From 7f8b6e4964818aa54c470995c586969efd574da0 Mon Sep 17 00:00:00 2001 From: John Vicondoa Date: Wed, 22 Jul 2026 20:09:19 -0700 Subject: [PATCH 001/115] docs: establish d2b 3.0 Provider control-plane foundation Freeze the shared Zone, resource, Host/Guest, Provider, reconciliation, process, session, and bootstrap contracts before parallel specification authoring. This keeps the pre-ADR45 v3 tree as the factual baseline while allowing exact, independently reviewed reuse from main. --- CHANGELOG.md | 7 + docs/adr/0046-d2b-3-provider-control-plane.md | 374 ++++++++++++++++ docs/adr/README.md | 1 + ...DR-046-components-processes-and-sandbox.md | 330 ++++++++++++++ .../specs/ADR-046-componentsession-and-bus.md | 359 ++++++++++++++++ docs/specs/ADR-046-core-controllers.md | 331 +++++++++++++++ docs/specs/ADR-046-decision-register.md | 99 +++++ .../ADR-046-primitive-resource-composition.md | 374 ++++++++++++++++ .../ADR-046-provider-model-and-packaging.md | 401 ++++++++++++++++++ .../ADR-046-resource-api-and-authorization.md | 339 +++++++++++++++ docs/specs/ADR-046-resource-object-model.md | 262 ++++++++++++ docs/specs/ADR-046-resource-reconciliation.md | 330 ++++++++++++++ docs/specs/ADR-046-resource-store-redb.md | 333 +++++++++++++++ .../ADR-046-terminology-and-identities.md | 300 +++++++++++++ docs/specs/README.md | 189 +++++++++ 15 files changed, 4029 insertions(+) create mode 100644 docs/adr/0046-d2b-3-provider-control-plane.md create mode 100644 docs/specs/ADR-046-components-processes-and-sandbox.md create mode 100644 docs/specs/ADR-046-componentsession-and-bus.md create mode 100644 docs/specs/ADR-046-core-controllers.md create mode 100644 docs/specs/ADR-046-decision-register.md create mode 100644 docs/specs/ADR-046-primitive-resource-composition.md create mode 100644 docs/specs/ADR-046-provider-model-and-packaging.md create mode 100644 docs/specs/ADR-046-resource-api-and-authorization.md create mode 100644 docs/specs/ADR-046-resource-object-model.md create mode 100644 docs/specs/ADR-046-resource-reconciliation.md create mode 100644 docs/specs/ADR-046-resource-store-redb.md create mode 100644 docs/specs/ADR-046-terminology-and-identities.md create mode 100644 docs/specs/README.md diff --git a/CHANGELOG.md b/CHANGELOG.md index b745d112a..c52538975 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -10,6 +10,13 @@ deprecations ship one minor release before removal. ## [Unreleased] +### Added + +- Proposed ADR 0046 and its normative specification set for the d2b 3.0 + Provider control plane: Zone-local ResourceSpecs, independently packaged + multi-process Providers, a lightweight redb resource store, asynchronous + owner-driven reconciliation, and Noise-protected d2b-bus control channels. + ## [1.4.1] - 2026-07-12 ### Added diff --git a/docs/adr/0046-d2b-3-provider-control-plane.md b/docs/adr/0046-d2b-3-provider-control-plane.md new file mode 100644 index 000000000..b4f6b328e --- /dev/null +++ b/docs/adr/0046-d2b-3-provider-control-plane.md @@ -0,0 +1,374 @@ +# ADR 0046: d2b 3.0 Provider control plane + +- Status: Proposed +- Date: 2026-07-22 +- Baseline: protected `v3` commit + `b5ddbed67867d9244bf33390868101bd9b053e49` +- Related: [ADR 0028](0028-guest-control-plane-over-vsock.md) + (typed guest control), [ADR 0034](0034-storage-lifecycle-restart-and-synchronization.md) + (storage/restart ownership), [ADR 0037](0037-local-hypervisor-runtime-seam.md) + (runtime Provider seam), [ADR 0043](0043-realm-native-control-plane.md) + (pre-v3 Realm architecture), and + [ADR 0044](0044-unsafe-local-runtime-provider.md) + (unsafe-local Provider) + +## Status and output + +This ADR defines the d2b v3 architecture from the protected pre-ADR-0045 +branch. Main-branch ADR 0045 is not ancestry or current v3 behavior, but its +code may be copied/adapted without restriction when an exact work item proves +the source, selected behavior, destination, integration, tests, and excluded +ADR 0045 assumptions. + +The decision is a concise parent for the normative +[`docs/specs/ADR-046-*`](../specs/README.md) specification set. The set, its +indexes, and this ADR are accepted as one unit. + +This decision PR delivers documentation only. It does not create v3 crates, +dependencies, Nix modules, services, controllers, Providers, state stores, or +reset behavior. Future implementation requires a separate request after the +set is Accepted. + +## Context + +The v3 baseline has useful foundations: + +- typed Provider traits in `d2b-realm-provider`; +- production ACA and Relay integrations; +- codec-neutral Realm operations, idempotency, targets, capabilities, and + stream state machines; +- typed guest control over ttrpc/vsock; +- typed process DAGs, minijail profiles, broker effects, pidfd adoption, and + generated storage/synchronization contracts; +- normalized Nix indexes and integrity-pinned bundle artifacts; +- user-session process/scope precedents in unsafe-local; +- separate journald/OTEL and authoritative audit paths. + +It does not have a live generic Provider registry, universal ComponentSession, +Provider toolkit/client, native resource API/store, generic Provider state, or +production Realm peer frontend. Several Provider/transport/codec/client crates +are test-only or unwired. Realm artifacts and the allocator remain explicitly +metadata-only. + +A kcp feasibility spike proved useful object semantics—typed spec/status, +revisions, watches, optimistic conflicts, owner/finalizer behavior, hierarchy, +and controller clients—but measured approximately 490 MiB RSS and a 176 MiB +executable. That footprint is unsuitable for recursive host/Guest/Zone use. + +## Decision + +### Zones and resources + +`Zone` replaces Realm as the v3 public isolation, policy, routing, resource, +state, and audit unit. Every Zone owns: + +- one Zone runtime; +- one embedded redb database and `d2b.resource.v3` service; +- one authoritative `Zone/` self resource; +- one fixed core-controller process; +- Zone-local Provider, Host, Guest, controller, policy, and ordinary resources. + +Every resource belongs to one Zone. A parent represents a child with a local +`ZoneLink/` resource and accesses the child's resources through the child +Zone API. Ordinary resource references never cross Zones. + +### Resource references and ownership + +Every `*Ref` field is: + +```text +/ +``` + +It resolves a named resource in the same Zone. Plain enums and inline values do +not use a `Ref` suffix. Standard ResourceTypes are short and Zone-unique; +vendor ResourceTypes are qualified; API binding rejects collisions. + +Every resource has zero or one `ownerRef`. Any committed child mutation +triggers reconciliation of the owner through a reverse owner index. The owner +controller relists its children and restores the complete desired child set and +configuration. Owner cycles fail, deletion is child-first/finalizer-aware, and +immutable UIDs prevent delete/recreate name confusion. + +Every resource uses native `ResourceType`/`ResourceSpec` terminology and has: + +```yaml +apiVersion: ... +type: ... +metadata: + name: ... + zone: ... + uid: ... + generation: ... + revision: ... + ownerRef: ... + createdAt: ... + updatedAt: ... +spec: {} +status: + observedGeneration: ... + phase: ... + conditions: [] + lastReconciledAt: ... + startedAt: ... + completedAt: ... + outcome: ... +``` + +`spec` and `status` are always present. Common identity, Zone, ownership, +revision, and timestamps remain in metadata. Status is a separately authorized +controller-owned subresource. It stores the latest bounded observation: +numeric `observedGeneration`, closed phase, conditions and transition/reconcile +datetimes, stable outcome code, optional process exit code, detailed bounded +redacted message, retryability, and ResourceType-specific fields. Earlier +status versions are revision-log history, not an embedded unbounded array. +The common phase is +`Pending|Ready|Succeeded|Degraded|Failed|Deleted|Unknown`; conditions carry +starting, deleting, retrying, and other transition detail. + +### Providers and controllers + +A Provider must be installed as a Zone-local `Provider/` resource before +it can be selected. Every Provider is one independently buildable crate and +signed package. It may contain several separately sandboxed controller, +service, and worker binaries, but it declares one Provider identity. + +Controller processes own their watch/coalescing queues and retry decisions. +Core validates signed watch plans, filters by API/scope/ownership/dependencies, +suppresses irrelevant or already-converged changes, coalesces revisions, and +delivers bounded reconcile hints immediately after durable commit. All +reconciliation APIs are asynchronous. A dedicated watch task keeps reading +while per-resource tasks reconcile; independent resources run in parallel and +long-running Process effects do not block the next ready Process. There is no +fixed poll, debounce, or inter-resource sleep. All controllers implement the +standard reconciliation contract and commit optimistic +`ResourceMutationBatch` values. + +### Hosts, Guests, and process placement + +Physical/local host execution contexts are `Host/` resources reconciled +by Provider/system-core. A Zone may declare several policy/budget-separated +Hosts. VM, sandbox, cloud-host, and remote execution contexts are +`Guest/` resources reconciled by their installed runtime Providers. + +Host and Guest share one `ExecutionPolicy`: + +- `providerRef`; +- `defaultDomain = system|user`; +- `allowedDomains`; +- `defaultUserRef` when its default domain is user. +- budgets and Volume/Network/Device attachment defaults. + +Current unsafe-local becomes a user-only Host under +Provider/system-core, not a v3 Provider. Its no-isolation warning remains +explicit in status and UI. + +Every ordinary `Process` declares: + +- `providerRef`, selecting an installed Process Provider; +- `executionRef`, selecting `Host/` or `Guest/`; +- optional `domain`, inheriting from the referenced ExecutionPolicy; +- `userRef` when required by user domain; +- exact sandbox, principal, budget, state, filesystem, endpoint, network, + device, config, and telemetry resource refs. + +There are no Host/Guest-specific Process ResourceTypes. Provider controller +descriptors declare which Host/Guest Provider capabilities and domains their +instances support. + +### System Providers and pidfd + +The initial system Provider family is: + +- `Provider/system-core` for Host reconciliation and local User + discovery/status only; +- `Provider/system-systemd` for systemd-backed Process/scope lifecycle; +- `Provider/system-minijail` for broker/minijail-backed Process lifecycle. + +The fixed core-controller process is also the `Provider/system-core` +controller. `Provider/system-minijail` is a second fixed bootstrap controller +because the first Process controller cannot be launched by itself. It +reconciles all later Process resources, including the system-systemd controller. +Every other Provider/controller is represented by a Process resource under a +Host or Guest. + +Both Process Providers implement the same common execution schema and +conformance for long-lived `Process` and one-shot `EphemeralProcess`. +EphemeralProcess reports its terminal outcome directly and does not reference a +Process child. It retains successful results for `successfulTtl` (default +`1h`) and failed results for `failedTtl` (default `24h`), measured from +status.completedAt, before revision/finalizer-safe cleanup. Every Process +Provider must obtain and locally retain a verified pidfd. +Minijail receives it from `clone3(CLONE_PIDFD)` and owns wait/reap. The systemd +implementation binds a non-forking transient unit/scope by +InvocationID+cgroup+MainPID/start-time and opens a pidfd, while systemd owns +wait/reap. Pidfds never persist or cross d2b-bus. + +### Resource plane + +Each Zone embeds one redb database in its Zone runtime. redb supplies ACID +transactions, one writer, concurrent MVCC readers, crash safety, and B-tree +storage. d2b owns: + +- ResourceType schemas/API exports/bindings; +- resources and derived indexes; +- native Role/Binding authorization; +- revisions/change log/watches; +- operations/idempotency; +- Zone-link cursors; +- compaction, backup, restore, upgrade, and quarantine; +- controller registration and reconcile hinting. + +Concurrent clients are supported. Writes serialize through a bounded fair +coordinator. Non-conflicting queued mutations may use bounded group commit: +one redb transaction/fsync and Zone revision with ordered mutation ordinals, +while each caller receives its own result. Every mutation has expected-revision +preconditions; stale writes return conflict and never merge silently. + +The aggregate Zone resource service/store plus mandatory system-core and +system-minijail controller processes must satisfy the normative footprint/ +startup/latency gates, including +p95 durable commit-to-controller-handler <=5 ms and p95 ready Process +commit-to-launch-attempt <=20 ms. + +### Communication + +All controller control-plane traffic uses: + +```text +ResourceClient + -> d2b-bus + -> ComponentSession + -> selected d2b transport + -> owning Zone d2b.resource.v3 + -> redb coordinator +``` + +This applies to local, user, guest, remote, and nested controller registration, +get/list/watch, mutations, status/finalizers, reconcile hints/checkpoints, and +conflicts. Providers/controllers never receive a redb handle/path, direct store +client, HTTP control plane, or alternate ambient socket. + +ComponentSession uses v3 Noise-based authentication and record protection. +Authenticated peers map to canonical Zone-local resource subjects. The same +native Role/RoleBinding engine authorizes session connect, service invoke, +stream open, and resource verbs. A handshake cannot self-assert roles; +authorization leases bind policy revisions and revoke when RBAC changes. + +### Primitive ResourceSpecs + +A primitive is a complete standard low-level ResourceSpec. The model is kept +small: Host; Guest; Process/EphemeralProcess; Volume; and only independently +lifecycle-managed/shared/substitutable User, Network, Device, and Credential +resources approved by the normative resource catalog. + +Budgets, cgroups, sandbox, namespaces, capabilities, environment, mounts, +endpoints, ports, and telemetry are fields of Host/Guest/Process specs. Files, +directories, state, ACLs, views, and mount lifecycle are one Volume spec. +Locks/leases remain internal transaction/controller mechanics unless a later +independent shared lifecycle proves a ResourceType is required. + +Volume preserves fine-grained storage policy: anchored relative layout entries, +owner/group refs, mode, access/default ACLs, no-follow/inheritance/repair/ +cleanup rules, named views, and Process mounts. Same-Zone Volume attachments +can bridge an authorized Host source to a Guest mount using virtiofs; the +Volume controller owns any virtiofsd Process and reports per-attachment status. + +Helpers, syscalls, broker operations, pidfds, and sandbox fragments are +implementation mechanisms, not primitives. + +Semantic Provider controllers compose behavior by creating owned primitive +resources. They do not call spawn, systemd, minijail, broker, filesystem, +network, or device effects directly. + +## Rejected alternatives + +### kcp/etcd/Kubernetes API + +Rejected for runtime footprint and unnecessary workload-cluster machinery. +Useful resource semantics are retained in the native API. + +### One global resource database + +Rejected because Zone ownership, backup, failure, authorization, and state +must remain independently bounded. + +### Separate Process ResourceTypes for Host and Guest + +Rejected because location belongs to executionRef/provider/domain selection. +Duplicated Process schemas would drift and prevent implementation substitution. + +### Direct controller store access + +Rejected because it bypasses ComponentSession identity, d2b-bus routing, native +authorization, audit, limits, and cross-Host/Guest transport parity. + +### One Provider process or dynamic library + +Rejected because components need distinct UIDs, Hosts/Guests, user domains, +sandboxes, resources, and failure boundaries. + +## Consequences + +Benefits: + +- one resource/controller model for physical hosts, users, guests, sandboxes, + remote hosts, and nested Zones; +- independently packaged and later independently hosted Provider repositories; +- exact ownership-driven reconciliation after child drift; +- implementation-neutral Process resources with systemd/minijail substitution; +- smaller control-plane target than kcp; +- language-neutral contracts and one authenticated communication channel. + +Costs: + +- d2b must implement and maintain resource storage, watches, RBAC, schemas, + backup/upgrade, controller scheduling support, and conformance; +- many current implementation-shaped roles require explicit migration work + items; +- every ResourceType, core controller, and Provider needs a complete normative + spec; +- more processes and controller/resource status surfaces increase operational + complexity; +- the d2b 3.0 cutover remains destructive. + +## Normative specifications + +The authoritative set is indexed by +[`docs/specs/README.md`](../specs/README.md). The decision register is +[`ADR-046-decision-register.md`](../specs/ADR-046-decision-register.md). + +Foundation specs are authored first. Once stable, all ready file-disjoint +resource, core-controller, cross-cutting, and Provider dossier specs are +authored in parallel. No dependent spec may invent a missing foundation choice. + +## Review and acceptance + +The same ADR/spec PR has two required human review gates: + +1. approval before the immutable final panel snapshot; +2. approval after unanimous panel signoff. + +Any content change invalidates validation and panel evidence. The changed +candidate is revalidated, repaneled, and reviewed again. + +The set cannot become Accepted while it contains: + +- an unresolved decision; +- a missing ResourceType/core-controller/Provider dossier; +- an undefined ref, owner, controller, process, state, limit, error, or test; +- a work item without exact v3 source and future destination paths; +- a claim that proposed v3 implementation is already live. + +## Current-code fit + +| Item | ADR 0046 treatment | +| --- | --- | +| Current anchor | ADR 0043 Realm model; `d2b-realm-*`; live CLI/daemon/guest-control; process DAG/broker; normalized Nix/bundle/storage contracts; ACA/Relay/gateway; unsafe-local user scopes | +| Evidence class | Mixed; exact classifications live in the authoring evidence ledger and owning specs | +| Behavior retained | Typed/fail-closed contracts, positive capabilities, idempotency, bounded streams, pidfd identity, broker mediation, generated storage ownership, argv/secret redaction, OTEL/audit separation | +| Required delta | Entire native Zone resource plane, ComponentSession/d2b-bus production path, Provider resources/toolkit, reconciliation, Primitive ResourceSpecs, Provider process packaging, Provider state, Zone routing, and reset | +| Reuse path | Copy/extract/adapt exact symbols named by each spec work item; current code is canon for current behavior | +| Replacement/deletion | No current path is removed until its resource/controller/Provider successor is integrated and tested | +| Feasibility proof | redb/resource/reconciliation/process/bus/package/state/route/security spikes specified by the normative set | +| Future owner | Exact `ADR046-*` work items in the spec-set manifest | diff --git a/docs/adr/README.md b/docs/adr/README.md index e97eb9fc1..a220f1d66 100644 --- a/docs/adr/README.md +++ b/docs/adr/README.md @@ -50,3 +50,4 @@ for the broader design narrative, see | [0043. Realm-native control plane](0043-realm-native-control-plane.md) | Accepted | 2026-07-05 | Supersedes ADR 0032 with realm-as-control-plane architecture: one `d2bd` plus broker/socket/state/audit boundary per realm, strict parent/child routing, dynamic relay discovery, realm-qualified VM addresses, and migration from current `home`/`dev`/`work` groups into first-class realms. | | [0044. Unsafe-local runtime provider](0044-unsafe-local-runtime-provider.md) | Accepted | 2026-07-09 | Adds an explicit `unsafe-local` provider for host-user/session/network workloads with Wayland-proxy identity rails, first-class desktop launch metadata, and host-local persistent shell UX while preserving the no-isolation security warning. | | [0045. Provider and transport framework](0045-provider-and-transport-framework.md) | Proposed | 2026-07-10 | Separates provider authorities from transport and protocol seams, selects Noise-authenticated component sessions with ttrpc/protobuf control services, models controllers as parent-owned workloads, and authorizes direct peer streams over shared transport fabrics without bypassing realm-tree policy. | +| [0046. d2b 3.0 Provider control plane](0046-d2b-3-provider-control-plane.md) | Proposed | 2026-07-22 | Rebuilds d2b 3.0 from the pre-ADR45 v3 baseline around Zone-local resources, external multi-process Providers, an embedded redb resource plane, asynchronous reconciliation, and d2b-bus/Noise ComponentSession control channels. | diff --git a/docs/specs/ADR-046-components-processes-and-sandbox.md b/docs/specs/ADR-046-components-processes-and-sandbox.md new file mode 100644 index 000000000..0587a3f6a --- /dev/null +++ b/docs/specs/ADR-046-components-processes-and-sandbox.md @@ -0,0 +1,330 @@ +# ADR 0046 components, processes, and sandbox + +| Field | Value | +| --- | --- | +| Spec ID | `ADR-046-components-processes-and-sandbox` | +| Parent | ADR 0046 | +| Status | Proposed | +| Version | 1 | +| Baseline | `b5ddbed67867d9244bf33390868101bd9b053e49` | +| Normative | Yes | +| Owners | Process Providers, ProviderSupervisor, broker/Host/Guest supervisors | +| Depends on | `ADR-046-provider-model-and-packaging`, `ADR-046-primitive-resource-composition`, `ADR-046-componentsession-and-bus` | +| Supersedes | Current ProcessRole/VmProcessDag as the public process model | + +## Bootstrap boundary + +Non-resource bootstrap is closed: + +- Zone runtime with embedded store/resource API/bus endpoint; +- Zone privileged broker where required; +- one fixed core-controller/Provider-system-core process; +- minimum Host/Guest supervisor/effect adapter; +- fixed user supervisor; +- fixed Provider/system-minijail controller process; +- transport/listener resources necessary to reach the owning Zone. + +Bootstrap mechanisms are not Primitive ResourceSpecs. They cannot be selected by +third-party Providers or widened by config. + +Provider/system-core and Provider/system-minijail are the only Provider +controllers without Process resources. system-minijail is fixed because no +Process controller exists to launch the first Process controller. It is +integrity-pinned, uses the compiled bootstrap authorization, and launches every +later Process controller, including Provider/system-systemd. + +After bootstrap: + +- every non-system-core controller is Process; +- every service is Process; +- every worker is Process or EphemeralProcess; +- every other Process Provider/controller is Process; +- every process has one executionRef/domain/user placement and selected Process + Provider. + +## ProviderSupervisor + +ProviderSupervisor is a fixed local effect adapter, not a Provider/resource +controller. It receives a Process-controller-authenticated LaunchTicket bound +to: + +- Process/EphemeralProcess ref/UID/revision/generation; +- owner Provider/component/template; +- executionRef/domain/userRef; +- selected Process Provider; +- compiled sandbox/budget/mount/device/network/endpoint configuration digests; +- exact inherited FD table; +- operation/deadline/cancellation; +- expected process identity/readiness. + +It: + +1. verifies ticket/current resource/controller lease; +2. resolves only trusted package/template/resource outputs; +3. asks the local broker/systemd/user/Host/Guest effect owner to launch; +4. returns provider-specific stable process identity and mandatory pidfd + evidence; +5. observes/adopts/signals/stops only that exact identity; +6. reports bounded effects/status to the Process controller. + +It never interprets Provider root settings, chooses dependencies, reads sibling +state, or registers services/commands. + +## Process common spec + +Common fields: + +- providerRef; +- executionRef; +- domain and conditional userRef; +- processClass controller/service/worker; +- owning Provider/component/template; +- sealed config and Credential refs; +- mounts by Volume/view/path/access; +- sandbox; +- budget; +- Network/Device refs and usage; +- endpoints/ports; +- bus/telemetry; +- readiness/deadlines; +- desired lifecycle. + +No caller-controlled executable path, UID/GID, host path, cgroup path, +capability, raw seccomp, minijail argv, systemd property, broker operation, +socket address, credential byte, or environment escapes the signed Provider/ +Process/Volume schema. + +## Sandbox compilation + +Inline Process sandbox declares semantic requirements: + +- namespace isolation classes; +- capability classes; +- seccomp classes; +- mount/Volume views; +- Device/Network access; +- read-only root/store posture; +- environment classes; +- LSM labels/profile references where supported; +- no-new-privileges/start-root requirements; +- fd classes; +- umask/rlimits/oom policy. + +The selected Process Provider compiles these into its implementation: + +- system-minijail → broker-validated minijail/clone3 plan; +- system-systemd → transient unit/scope hardening and any approved sandbox + wrapper; +- future Provider → same semantic conformance. + +Raw policy fragments are package/core-reviewed artifacts, never Provider config +strings. + +## Pidfd and wait/reap + +Every Process Provider has a local verified pidfd. + +### system-minijail + +- broker `clone3(CLONE_PIDFD)` preferred; +- reviewed fallback only where contract permits; +- d2b owns wait/reap; +- process born directly in final cgroup; +- adoption verifies pid/start-time/cgroup/executable/template/generation before + `pidfd_open`; +- ambiguity → Unknown/quarantine, never broad kill/reuse. + +### system-systemd + +- non-forking transient unit/scope; +- bind InvocationID, cgroup, MainPID, start-time, Provider/template/generation; +- open verified pidfd; +- systemd owns wait/reap; +- no daemonizing/forking type; +- unit-name alone is not identity; +- adoption revalidates all stable identity before pidfd open. + +Pidfd: + +- is not persisted; +- is not public status; +- never crosses d2b-bus/Zone/Host/Guest transport; +- is closed/reopened across controller restart after identity verification. + +## User domain + +User-domain Process uses the same ResourceType: + +- executionRef; +- domain=user; +- exact userRef or inherited Host/Guest defaultUserRef; +- the referenced ExecutionPolicy allows user; +- system-systemd uses verified transient user scope via fixed user supervisor; +- system-minijail may support user domain only if descriptor/conformance says so; +- UserPortal needs are inline typed refs/settings; +- same-UID non-isolation is explicit status/security posture. + +User supervisor is a fixed local effect adapter, not another Provider. + +## Host/Guest locality + +A Process controller instance declares compatible Host/Guest Provider +capabilities. The ResourceType does not change for: + +- physical Host; +- VM Guest; +- ACA sandbox Guest; +- Azure/full-host Guest; +- remote/nested Guest. + +The Host/Guest local supervisor/effect adapter performs launch and reports to +the owning Zone over d2b-bus/ComponentSession. A Guest does not need a Zone +store unless it separately hosts a child Zone. + +## Process naming and cgroups + +Diagnostic process names: + +```text +z-@ +s-@ +u--@ +``` + +Opaque short IDs are derived from immutable UIDs, not human labels. Executable/ +template/generation remain separate verified identity. + +System cgroup shape: + +```text +z-/ + controller/ + broker/ + executions/ + e-/ + system/ + providers/ + p-/ + components/ + c-/ + process/ + workers/ + w-/ +``` + +Intermediate nodes are process-free. User processes live in exact user scopes +and are not misrepresented as system cgroup children. + +## Process status + +Common status plus: + +- process implementation/provider; +- process identity digest; +- wait/reap owner; +- executionRef/domain/userRef; +- config/sandbox/resource revision digests; +- readiness/health/restart/backoff; +- last start/exit class; +- optional exitCode in outcome; +- adoption/quarantine condition. + +No PID, pidfd, unit name, path, argv, environment, terminal bytes, or raw +Provider diagnostic is public status/audit. + +## Fast launch + +After durable ready Process commit: + +- direct post-commit hint; +- p95 handler start <=5 ms; +- p95 launch-attempt start <=20 ms; +- Process controller launches in independent async task; +- watch loop dispatches the next independent resource concurrently; +- readiness wait does not hold controller-wide queue; +- status transitions are async expected-revision writes. + +## Current-role disposition rule + +Every v3 ProcessRole/helper/unit is classified as: + +- fixed bootstrap; +- Process/EphemeralProcess under exact Provider; +- non-process controller/probe; +- deleted after successor. + +Each disposition names: + +- current source/emitter/runtime caller; +- ResourceType owner/Provider; +- Process common/Provider-specific fields; +- executionRef/domain/userRef; +- Volume/Network/Device dependencies; +- readiness/restart/adoption/finalizer; +- current tests copied/adapted; +- exact old path removal. + +No generic Process conversion may lose role-specific watchdog behavior, +pre-start effects, fine-grained ACL/device policy, or restart semantics. + +## Representative baseline mapping + +| Current role | Future owner | +| --- | --- | +| CloudHypervisorRunner | Runtime Cloud Hypervisor Provider owns a Process under Host | +| QemuMediaRunner | Runtime QEMU Provider owns Process | +| Virtiofsd | Volume Provider owns Process per attachment | +| Swtpm | TPM Device Provider owns Process plus Volume state | +| GPU/GpuRenderNode/Video | Device GPU Provider owns Processes; Display Provider consumes Device/endpoints | +| Audio | Audio Provider owns Process | +| WaylandProxy | Display/Wayland Provider owns Process | +| VsockRelay | Transport Provider owns Process | +| OtelHostBridge | Observability Provider owns Process | +| Usbip | USB Device Provider owns Process/EphemeralProcess | +| GuestControlHealth/readiness probes | controller observation, not Process | +| HostReconcile/store preflight | controller logic, not Process | + +## Current-code fit + +| Item | Treatment | +| --- | --- | +| Current anchor | `d2b-core/src/processes.rs`, minijail/profile/runtime; d2bd DAG/pidfd/adoption; broker SpawnRunner; unsafe-local helper; guestd/exec runner; Nix system/user/guest units | +| Evidence class | Current launch paths are reachable; generic Process Providers/supervisor are ADR-only | +| Behavior retained | Typed argv/profile, broker privilege, pidfd identity/adoption, direct cgroup placement, user scope, guest locality, readiness/watchdogs, redaction | +| Required delta | Common Process/EphemeralProcess, selected Provider, Host/Guest executionRef/domain placement, generic supervisor/async controller/status | +| Reuse path | Exact current/main source mappings in role/Provider dossiers | +| Replacement/deletion | No ProcessRole/unit/helper removal until successor behavior/test parity | +| Feasibility proof | systemd/minijail shared conformance; Host system/user and Guest execution; fast parallel launch | +| Future owner | Work items below | + +## Implementation work items + +### ADR046-process-001 + +| Field | Value | +| --- | --- | +| Dependency/owner | W0/W2; Process contracts/supervisor | +| Current source | `d2b-core/src/{processes,process_builder,minijail_profile}.rs`; `d2bd/src/supervisor/*`; broker `spawn_runner.rs` | +| Reuse source | Useful main ProviderSupervisor/session/process code named by sub-items | +| Reuse action | extract and adapt | +| Destination | `packages/d2b-process/src/`, `packages/d2b-provider-supervisor/src/` | +| Detailed design | Common spec/status/tickets/pidfd/adoption/naming/cgroup/fast path | +| Integration | Process Provider controllers → supervisor/broker/systemd → async status | +| Data migration | Full reset; no role snapshot import | +| Validation | Shared conformance/fault/latency tests | +| Removal proof | Role/DAG path removed only per role disposition | + +### ADR046-process-002 + +| Field | Value | +| --- | --- | +| Dependency/owner | ADR046-process-001; systemd/minijail Provider owners | +| Current source | unsafe-local helper runtime/systemd; guest exec systemd-run; broker SpawnRunner/minijail | +| Reuse source | Main process/session helpers if selected by exact sub-items | +| Reuse action | extract/adapt | +| Destination | `packages/d2b-provider-system-systemd/`, `packages/d2b-provider-system-minijail/` | +| Detailed design | Two Process/EphemeralProcess implementations, pidfd/wait ownership, system/user domains | +| Integration | Zone-installed Providers/controller instances per Host/Guest | +| Data migration | None | +| Validation | Identical schema/status conformance plus provider-specific adoption | +| Removal proof | Old helpers retained until host/user/guest parity | diff --git a/docs/specs/ADR-046-componentsession-and-bus.md b/docs/specs/ADR-046-componentsession-and-bus.md new file mode 100644 index 000000000..2dff2d004 --- /dev/null +++ b/docs/specs/ADR-046-componentsession-and-bus.md @@ -0,0 +1,359 @@ +# ADR 0046 ComponentSession and d2b-bus + +| Field | Value | +| --- | --- | +| Spec ID | `ADR-046-componentsession-and-bus` | +| Parent | ADR 0046 | +| Status | Proposed | +| Version | 1 | +| Baseline | `b5ddbed67867d9244bf33390868101bd9b053e49` | +| Normative | Yes | +| Owners | `d2b-session`, transport adapters, `d2b-bus` | +| Depends on | `ADR-046-terminology-and-identities`, `ADR-046-resource-api-and-authorization` | +| Supersedes | Current v3 Realm PeerSession and ad hoc guest/user/public IPC | + +## Source and reuse policy + +The pre-ADR45 v3 baseline has no ComponentSession. This spec deliberately +copies/adapts the implementation from main commit: + +```text +a1cc0b2da4a08ca3240a770a972fe4da6f912bef +ADR-0045 W9: coordinate toolkit and sibling cutover (#314) +``` + +Selected main sources: + +| Main source | Selected behavior | +| --- | --- | +| `packages/d2b-session/src/handshake.rs` | Strict canonical offer/preface, Noise prologue binding, NN/KK/IKpsk2 profiles, generation discovery, credentials, transcript | +| `packages/d2b-session/src/bootstrap.rs` | Zeroizing single-use operation/nonce/expiry-bound bootstrap PSK | +| `packages/d2b-session/src/record.rs` | Directional encrypted records, sequence/reconnect generation, replay detection, bounds | +| `packages/d2b-session/src/engine.rs` | Async establish/reconnect, timeout, cancellation, ttrpc, named streams, attachments | +| `packages/d2b-session/src/scheduler.rs` | Bounded priority/fair scheduling and named-stream round robin | +| `packages/d2b-session/src/streams.rs` | Credit-based named stream state machine | +| `packages/d2b-session/src/lifecycle.rs` | Keepalive/reconnect/close state | +| `packages/d2b-session/src/transport.rs` | Async owned transport abstraction | +| `packages/d2b-session-unix/src/adapter.rs` | Unix peer identity, atomic attachments, descriptor validation/credits | +| `packages/d2b-session-unix/src/vsock.rs` | Async framed vsock transport, expected CID, no attachments | +| `packages/d2b-session/tests/noise_vectors.rs` | Exact snow 0.10 NN/KK/IKpsk2 vectors and rejection mutations | +| `packages/d2b-session/tests/component_session.rs` | Integrated strict negotiation/record/lifecycle/limits | +| `packages/d2b-session-unix/tests/unix_session.rs` | SO_PEERCRED/SCM_RIGHTS/pidfd/object identity/credit tests | + +Unrelated ADR 0045 Provider types, endpoint roles, service inventory, realm +process model, generated v2 DTO names, and delivery assumptions are not copied +as v3 architecture. + +## Layering + +```text +generated async ttrpc services / named streams + -> d2b-bus exact addressed route/admission + -> ComponentSession + Noise authentication + record protection + schema/purpose/transport/limits/reconnect binding + cancellation/deadlines/keepalive + fair control/stream scheduling + local typed attachment validation + -> owned d2b transport + inherited socketpair / Unix seqpacket / Unix stream / vsock / + enrolled Zone/provider transport +``` + +d2b-transport provides carriage/evidence only. ComponentSession authenticates +and protects a session. d2b-bus resolves an exact service/Zone/Provider/ +controller route. Native Role/RoleBinding authorizes connect/invoke/stream/ +resource verbs. No layer may widen the authority established below it. + +## Noise profiles + +### Local NN + +`Noise_NN_25519_ChaChaPoly_SHA256` is permitted only when: + +- purpose class is local; +- transport is inherited socketpair or Unix stream/seqpacket; +- transport provides directional peer identity evidence; +- endpoint policy is trusted/config-generated; +- authenticated Unix peer evidence maps to an exact canonical subject. + +NN supplies ephemeral encrypted record protection; peer authentication comes +from the bound Unix evidence, not a peer-supplied subject. + +### Enrolled KK + +`Noise_KK_25519_ChaChaPoly_SHA256` is used for enrolled peers: + +- both static public keys are known before handshake; +- local private keys are sealed/zeroizing; +- static key registry maps the authenticated remote key to one canonical + `Zone/*`, `Provider/*`, `Process/*`, `Host/*`, or `Guest/*` subject; +- route/purpose/service/schema/limits/channel binding are in the Noise prologue. + +### Bootstrap IKpsk2 + +`Noise_IKpsk2_25519_ChaChaPoly_SHA256` is used only for one-time bootstrap: + +- responder static key is known; +- single-use PSK is bound to operation ID, replay nonce, expected subject/ + Host/Guest/Provider/controller purpose, and expiry; +- PSK admission is consumed exactly once; +- successful enrollment replaces bootstrap with an enrolled identity/session; +- replay, expiry, wrong operation/subject/purpose fails closed. + +## Handshake policy + +The trusted endpoint policy binds: + +- endpoint purpose and purpose class; +- service package and exact schema fingerprint; +- initiator/responder evidence requirements; +- expected/authenticated subject constraints; +- selected Noise profile; +- transport class/locality/channel binding; +- reconnect generation; +- exact limit profile; +- attachment policy; +- authorization service/verb requirements. + +The canonical preface+offer is the Noise prologue. Any mismatch changes the +transcript and fails. There is no negotiation down to another service, schema, +profile, transport, purpose, role/evidence class, limit, or attachment policy. + +Generation discovery remains local-only, exact-policy-bound, and unauthenticated +only to the extent required to learn the nonzero reconnect generation; it +cannot admit a request or authorize a service. + +## Authenticated subject + +ComponentSession never trusts a subjectRef/role from the peer payload. + +Evidence mapping: + +| Evidence | Subject source | +| --- | --- | +| Unix pathname/socketpair | trusted endpoint config + verified SO_PEERCRED/process identity maps uid/pid to exact Zone-local subject | +| Enrolled KK key | Zone identity registry maps static public key to exact subject | +| Bootstrap IKpsk2 | one-time admission record contains expected subject and operation | +| Native vsock | expected CID/Guest bootstrap identity plus IKpsk2/enrolled key maps to exact subject | + +The established session carries the shared +`AuthenticatedSubjectContext` from the terminology/identity spec, plus an +authorization lease revision/expiry. This spec owns the evidence-to-context +mapping, not a second identity schema. + +It is redacted in Debug/logs and cannot be changed without a new handshake. + +## Native RBAC integration + +After authentication and before service dispatch, the same native +Role/RoleBinding evaluator used by the resource API checks: + +```text +subject +Zone +session purpose/service +verb = connect | invoke | open-stream | attach | cancel | observe +method/stream kind +target ResourceRef/Provider/Host/Guest +Provider/controller/session generations +``` + +Resource API methods add their resource verb attributes. + +Rules may authorize ComponentSession connect/service/stream attributes in the +same Role resource as resource verbs. There is no endpoint-local parallel RBAC +language. + +Authorization: + +- is required for the session itself and each method/stream; +- is revision-bound to Role/RoleBinding/Provider/API/Zone policy; +- is cached only under exact attributes/short expiry; +- invalidates immediately after relevant durable policy commit; +- grants long-lived streams a short lease; +- closes/refuses new work when lease revalidation fails; +- preserves already admitted bounded work only to its original deadline. + +Noise authentication proves identity/channel; it does not itself grant a role. + +## Records + +The copied RecordProtector behavior is retained: + +- directional Noise transport keys; +- bounded u16 length-prefixed encrypted record; +- authenticated header containing record kind, channel, sequence, reconnect + generation, payload length; +- strict send/receive sequence; +- replay digest rejection; +- reconnect-generation rejection; +- no plaintext/status/credential Debug. + +The v3 contract versions all record and canonical offer encodings separately +from v2. Protobuf numbers and binary tags are regenerated/frozen; v2 and v3 do +not interoperate. + +## Channels and fairness + +Closed channel classes: + +- session control; +- ttrpc control; +- attachment control; +- named stream. + +Session/control/cancel/keepalive/status health traffic has reserved bounded +capacity. Named streams use per-stream and aggregate byte credit plus round- +robin fairness. One blocked terminal/watch stream cannot starve resource status, +cancel, or health. + +Controller resource watches and reconcile hints are named streams. Their +delivery/ack cursor remains resource revision, not session sequence. + +## Async behavior + +All transport/session/service methods are async. + +- one driver owns session transport/protector/scheduler; +- service handlers run as independent bounded tasks; +- named streams expose async read/write/credit/cancel; +- blocking fd/kernel/filesystem operations use explicit adapters; +- no nested runtime/block_on; +- cancellation/deadline propagates from bus to handler/effect; +- Process reconcile fast path cannot wait for unrelated stream traffic. + +## Attachments + +Attachments are local Unix only. They require: + +- packet-atomic seqpacket policy; +- encrypted descriptor bound to service/method/request/operation/generation; +- exact object/access/purpose; +- CLOEXEC; +- SO_PEERCRED/pidfd/object identity validation; +- duplicate kernel-object rejection unless explicitly permitted; +- atomic multi-scope credits and cleanup on every failure. + +Unix stream/vsock/remote Zone transports carry no SCM_RIGHTS. Destination +controllers re-origin local resources/effects. + +## d2b-bus + +d2b-bus is an exact addressed router, not pub/sub. + +Route key: + +```text +(Zone, service package, method/stream, target ResourceRef or Provider, + schema fingerprint, Provider/controller/session generation) +``` + +It: + +- resolves local/Host/Guest/parent-child Zone transport; +- binds authenticated subject and operation/deadline; +- checks session connect/service/stream RBAC; +- invokes exact generated ttrpc client/server; +- bridges named streams with bounded credit; +- preserves pinned reverse route/cancellation; +- exposes no global subscriber socket/topic namespace/route table to Providers. + +ResourceClient always uses d2b-bus, even beside the Zone runtime. There is no +direct-store shortcut. + +## Lifecycle + +Retain/adapt main's: + +- Established/Disconnected/Reconnecting/Closing/Closed; +- monotonic keepalive; +- bounded reconnect window/attempts; +- reconnect generation increment; +- cancel all old-generation requests; +- new Noise handshake/protector after reconnect; +- explicit close reason/remediation. + +Resource/controller streams relist/resume using resource revisions after +reconnect; session generation alone is not durable state. + +## Errors and telemetry + +Stable errors include malformed/unsupported handshake, authentication, +transcript/schema/purpose/transport/evidence/subject mismatch, policy denial, +generation/replay/sequence, limit/backpressure, invalid attachment, timeout, +cancel, disconnect, and internal invariant. + +Metrics use closed labels: + +- purpose/service/transport/profile; +- operation class; +- outcome/error code; +- stream/attachment class. + +Never label subject/resource names, endpoint paths, keys, payload, terminal +bytes, credentials, PIDs, or Provider diagnostics. + +Audit records authenticated subject digest/ref where policy permits, endpoint/ +service/method, authorization revision/decision, operation/correlation, +transcript/session generation digest, route, and fixed outcome. + +## Current-code fit + +| Item | Treatment | +| --- | --- | +| v3 current anchor | `d2b-realm-router` PeerSession/SecurePeerSession/MuxSession and `d2b-realm-transport`; guest ttrpc/vsock/HMAC | +| v3 evidence class | Mostly implemented-but-unwired for Realm peer; guest control reachable; ComponentSession absent | +| Main reuse source | main `a1cc0b2d`, `d2b-session`, `d2b-session-unix`, v2 ComponentSession contracts/tests | +| Behavior retained | Strict Noise profiles/transcript, encrypted replay-safe records, async owned transports, fair streams, cancellation/reconnect, exact attachments | +| Required delta | v3 contract names/versions, shared AuthenticatedSubjectContext, Role/RoleBinding authorization, d2b-bus routing, Zone services | +| Excluded main assumptions | v2 EndpointRole/Realm/service inventory, Provider registry/process model, delivery/Nix ownership | +| Feasibility proof | Copy main tests, add subject/RBAC/revocation/resource-watch/latency integration on v3 | +| Future owner | Work items below | + +## Implementation work items + +### ADR046-session-001 + +| Field | Value | +| --- | --- | +| Dependency/owner | W0 shared contract root | +| Current source | v3 `d2b-realm-router/src/{session,secure_session,mux_session,lifecycle}.rs`, guest auth/transport | +| Reuse source | main `a1cc0b2d`: `d2b-contracts/src/v2_component_session.rs`, `d2b-session/src/{handshake,bootstrap,record,engine,scheduler,streams,lifecycle,transport}.rs`, Noise/component tests/vectors | +| Reuse action | copy and adapt | +| Destination | `packages/d2b-contracts/src/v3_component_session.rs`, `packages/d2b-session/` | +| Detailed design | Reversion canonical offer/records; retain NN/KK/IKpsk2; add canonical subject/authorization context hooks | +| Integration | d2b-bus, resource/controller/Provider services | +| Data migration | No v2 session compatibility; reconnect on v3 | +| Validation | Copied exact vectors/rejections plus subject/RBAC/revocation tests | +| Removal proof | v3 old Realm PeerSession removed only after all v3 peer routes move | + +### ADR046-session-002 + +| Field | Value | +| --- | --- | +| Dependency/owner | ADR046-session-001; transport owner | +| Current source | v3 guest vsock/local seqpacket implementations and d2b-realm-transport traits | +| Reuse source | main `a1cc0b2d`: `d2b-session-unix/src/{adapter,socket,descriptor,pidfd,vsock,systemd,credit}.rs`, `tests/unix_session.rs` | +| Reuse action | copy and adapt | +| Destination | `packages/d2b-session-unix/`, future enrolled transport adapter crates | +| Detailed design | Unix/socketpair/vsock owned transports, peer evidence, fd/pidfd/object validation, credits | +| Integration | ProviderSupervisor/Host/Guest/Zone listeners hand owned transports to session | +| Data migration | None | +| Validation | Copied fd/peer/credit tests plus Host/Guest subject mapping | +| Removal proof | Ad hoc guest/public/helper transport removed only per service cutover | + +### ADR046-bus-001 + +| Field | Value | +| --- | --- | +| Dependency/owner | Sessions + resource API; bus owner | +| Current source | v3 `d2b-realm-router`, target resolver, CLI routing, operation router | +| Reuse source | Any useful main d2b-client/provider/session routing symbols named by implementation sub-items | +| Reuse action | extract/adapt | +| Destination | `packages/d2b-bus/src/{router,registry,authorization,streams,operations}.rs` | +| Detailed design | Exact service/resource routes, RBAC, pinned reverse route, cancellation, named stream bridge, no wildcard pub/sub | +| Integration | Every ResourceClient/controller/Provider/CLI service | +| Data migration | None | +| Validation | Message isolation, route/auth revocation, fairness, reconnect, no direct-store path | +| Removal proof | Old direct dispatch branches removed only after route parity | diff --git a/docs/specs/ADR-046-core-controllers.md b/docs/specs/ADR-046-core-controllers.md new file mode 100644 index 000000000..e03c43b98 --- /dev/null +++ b/docs/specs/ADR-046-core-controllers.md @@ -0,0 +1,331 @@ +# ADR 0046 core controllers + +| Field | Value | +| --- | --- | +| Spec ID | `ADR-046-core-controllers` | +| Parent | ADR 0046 | +| Status | Proposed | +| Version | 1 | +| Baseline | `b5ddbed67867d9244bf33390868101bd9b053e49` | +| Normative | Yes | +| Owners | Fixed core-controller process, `Provider/system-core` | +| Depends on | `ADR-046-resource-store-redb`, `ADR-046-resource-api-and-authorization`, `ADR-046-resource-reconciliation`, `ADR-046-provider-model-and-packaging` | +| Supersedes | Current daemon-global hard-coded orchestration ownership | + +## Process model + +Each Zone has one fixed core-controller process. It hosts multiple isolated +controller handlers in one binary/runtime to reduce memory/process overhead. + +It is also the `Provider/system-core` controller. Provider/system-core and the +fixed Provider/system-minijail controller are the two Provider bootstrap +exceptions. It starts after the embedded store/resource API/bus endpoint is +ready and connects to those services through the same local +d2b-bus/ComponentSession path used by other controllers. Only the non-controller +store actor touches redb directly. + +The process: + +- has zero ambient host capability; +- cannot invoke arbitrary broker operations; +- owns no Provider payload state; +- uses typed internal core effect ports only for store lifecycle/emergency + operations that cannot be resources; +- has reserved resource API/bus capacity; +- runs handlers concurrently but serializes each resource; +- exposes aggregate health plus per-handler status. + +All other Provider controllers are Process resources under Hosts or Guests. + +## Handler catalog + +### Configuration publication + +Owns: + +- configured root resource generation; +- validation/staging/activation; +- prior generation rollback window. + +Algorithm: + +1. read integrity-pinned candidate bundle; +2. validate Provider packages/APIs/config/refs/owners/RBAC/budgets; +3. stage inactive resources in bounded transactions; +4. atomically activate one configuration revision; +5. trigger affected resources/providers/controllers; +6. retain prior generation for bounded rollback/drain; +7. prune only after ownership/finalizer checks. + +It does not overwrite controller-created children merely because root config +changes. + +### API catalog/binding + +Owns ResourceTypeSchema, ResourceApiExport, and ResourceApiBinding internal +control resources. + +It verifies: + +- Provider/package/signature; +- ResourceType short-name collision; +- exact schema/descriptor fingerprint; +- additive compatibility; +- permission claims narrowed by policy; +- controller implementation exists; +- API withdrawal has no unresolved resources/finalizers. + +It atomically swaps the in-memory validator/catalog after commit. + +### Authorization + +Owns Role/RoleBinding validation and revision-bound authorization indexes. + +It: + +- validates subjects/refs/scope/verbs; +- builds/switches indexes after durable commit; +- invalidates session/API authorization caches/leases immediately; +- triggers affected Provider/controller/resource reconcilers; +- never turns a deny into allow through fallback. + +### Provider lifecycle + +Owns Provider resources and aggregate status. + +It: + +- verifies package/trust/config/conformance; +- validates controller/service/worker graph; +- creates owned Volume/Process/EphemeralProcess and other required children; +- waits for required components/dependencies; +- publishes exported ResourceTypes/services only after ready; +- drains/withdraws/revokes components on update/disable/delete; +- aggregates exact child status without spawning directly. + +Provider/system-core is handled internally without a Process child. All others +use the same owned child-resource algorithm. + +### Controller registration and hints + +Owns: + +- signed descriptor validation; +- authenticated controller lease; +- ResourceType/provider/Host/Guest/domain binding; +- watch plan; +- reverse dependency indexes; +- hint filtering/coalescing; +- checkpoint/high-water tracking; +- lease withdrawal/replacement. + +It implements the <=5 ms commit-to-handler path with the store post-commit +dispatcher and d2b-bus. + +### Ownership/finalizer + +Owns generic owner graph integrity and deletion ordering, not Provider-specific +finalizer logic. + +It: + +- validates singular ownerRef/UID/cycle/depth; +- emits owner hints for every child mutation; +- tracks child-first deletion; +- dispatches finalizer owners; +- reports blocked/ambiguous conditions; +- never clears another controller's finalizer or invents cleanup success. + +### Revision/watch maintenance + +Owns: + +- watch registration/high-water handoff; +- revision-log compaction policy; +- expired-cursor/relist; +- watch/stream quotas; +- owner/dependency live dispatch. + +The store actor performs redb transactions; this controller schedules policy and +reports status. + +### EphemeralProcess cleanup + +Owns terminal retention cleanup: + +- Succeeded uses successfulTtl, default 1h; +- Failed uses failedTtl, default 24h; +- starts at completedAt; +- writes/validates cleanupEligibleAt; +- respects finalizers, owner deletion, incident holds, expected revision; +- calls normal Delete; does not remove rows directly. + +### Zone link/delegation + +Owns ZoneLink: + +- parent/child Zone identity and transport/session requirements; +- child-local RoleBinding/authorization expectation; +- route/resource cursor status; +- reconnect/resync; +- local intents while disconnected; +- disable/revocation. + +It never stores child resources or credentials in the parent. + +### Budget/emergency policy + +Budgets are shared Host/Guest ExecutionPolicy and Process fields, not +ResourceTypes. This handler: + +- validates hierarchical Host/Guest allocations against Zone capacity/policy; +- tracks aggregate reservations/observations; +- blocks overcommit; +- narrows child Zone capacity; +- handles digest/Provider/Host/Guest/Zone/global emergency disable; +- revokes routes/sessions/grants and stops child Processes via normal resources; +- preserves incident-held Volume/Provider state. + +### Store lifecycle + +Coordinates: + +- backup request/admission; +- staged restore/internal schema upgrade; +- compaction; +- corruption quarantine; +- health/metrics; +- reset inventory. + +Actual redb transaction/file publication stays in the embedded store actor/ +storage owner. Normal controllers cannot call these internals. + +### system-core Host + +Provider/system-core reconciles Host: + +- validates it represents the local OS attached to this Zone runtime; +- validates defaultDomain/allowedDomains/defaultUserRef/budget/policy; +- reports local host availability/capabilities; +- exposes Host Provider capabilities used by Process/Volume/Network/Device + controller descriptors; +- never starts a Process directly. + +A Zone may have multiple policy/budget-separated Hosts, all +mapping to the same physical OS but accepting different Process domains/users. + +### system-core User + +Provider/system-core reconciles local User: + +- configured identity/name; +- NSS/passwd/group lookup or explicitly supported local source; +- observed UID/GID/groups/home/session manager availability; +- status only, no credential bytes; +- detects UID/name/group drift; +- does not create arbitrary users unless a later reviewed Provider implements + that behavior. + +User status supports Process userRef and Volume ACL owner/group refs. + +## Controller health/status + +Each handler exposes: + +- observed configuration/API/policy generation; +- phase/conditions/outcome; +- queue/running counts; +- last watch revision/checkpoint; +- lastReconciledAt; +- degraded/failed stable code; +- retry/backpressure; +- latency histogram without resource-name labels. + +The core-controller aggregate becomes Ready only when mandatory store/API/auth/ +configuration/Provider/controller/ownership handlers are Ready. Optional link/ +backup cleanup work can report Degraded without false total failure. + +## Startup + +1. Zone runtime validates/opens store. +2. Resource service and local d2b-bus/ComponentSession endpoint start. +3. Fixed system-core and system-minijail controller processes start and + authenticate as their exact Provider subjects. +4. The compiled bootstrap authorization grants only the closed verbs to exact + Provider/system-core and Provider/system-minijail subjects. +5. Handlers list/recover/checkpoint concurrently. +6. Configuration publishes/recovers active generation. +7. system-core Hosts/Users reconcile. +8. Other Provider controllers/processes launch through resources. +9. Zone readiness publishes after mandatory handlers are current. + +No handler requires all optional Providers to be ready. + +## Restart + +The core controller stores no authoritative private ledger outside resources/ +operations/revision log. On restart it: + +- authenticates a new ComponentSession generation; +- relists owned resources; +- resumes from durable checkpoints where valid; +- revalidates Provider/controller leases; +- does not clean up before Process/Host/Guest/Volume owners observe/adopt; +- preserves Unknown/ambiguous states. + +## Security boundary + +Keeping handlers in one process does not union arbitrary Provider privilege: + +- only fixed core ResourceTypes/verbs; +- no Provider payload config/secrets/state; +- no broker socket/raw host paths; +- typed store-lifecycle/emergency internal port; +- all resource work still uses Role/RoleBinding and expected revisions; +- handler panic/failure is contained, surfaced, and may restart process, but + store remains independently owned by Zone runtime. + +## Current-code fit + +| Item | Treatment | +| --- | --- | +| Current anchor | d2bd startup/config/provider/process/operation/adoption owners; Realm access/allocator engines; Nix config generation; storage/sync contracts | +| Evidence class | Current owners are mixed reachable/generated; generic core-controller process is ADR-only | +| Behavior retained | Single owners, deterministic config, typed policy, idempotency, recover-before-cleanup, pidfd non-persistence, bounded audit | +| Required delta | Separate fixed core controller, ResourceType handlers, resource-only authority, async watches/checkpoints | +| Reuse path | Extract pure controller algorithms/validators from exact current/main sources per work item | +| Replacement/deletion | d2bd monolithic branches remain until each handler and Provider successor is integrated | +| Feasibility proof | One process with all handlers over local ComponentSession, fast hint path, crash/relist/revoke tests | +| Future owner | Work items below | + +## Implementation work items + +### ADR046-core-001 + +| Field | Value | +| --- | --- | +| Dependency/owner | W0/W1a; core-controller owner | +| Current source | `packages/d2bd/src/lib.rs`, `provider_registry.rs` if present on source ref, supervisor state, operations; `d2b-realm-core/src/{allocator_engine,identity_store}.rs` | +| Reuse source | Useful pure handler/toolkit code from main named in implementation sub-items | +| Reuse action | extract and adapt | +| Destination | `packages/d2b-core-controller/src/{main,configuration,api_catalog,authz,providers,controllers,ownership,watches,cleanup,zone_links,budgets,store}.rs` | +| Detailed design | One fixed process, isolated handlers, async ResourceClient, health/startup/restart | +| Integration | Zone runtime local bus/session; Provider/system-core resource identity | +| Data migration | Full reset | +| Validation | Per-handler unit/property tests plus multi-process startup/restart | +| Removal proof | Current daemon branches removed after handler/Provider parity | + +### ADR046-core-002 + +| Field | Value | +| --- | --- | +| Dependency/owner | ADR046-core-001; system-core Provider owner | +| Current source | Nix host/Realm options/index; host check/provider code; user/group lookup and unsafe-local eligibility | +| Reuse source | Any main local-host/user-provider code selected by exact sub-items | +| Reuse action | extract/adapt | +| Destination | `packages/d2b-provider-system-core/src/{host,user}.rs` linked into fixed core controller | +| Detailed design | Host and User schemas/reconcile/status/capabilities | +| Integration | Bootstrap Provider/system-core; other controllers target Host/Guest/User refs | +| Data migration | New v3 resources from Nix | +| Validation | Multiple Hosts, system/user restrictions, UID/session drift | +| Removal proof | Host grouping/user helper policy removed only after resource parity | diff --git a/docs/specs/ADR-046-decision-register.md b/docs/specs/ADR-046-decision-register.md new file mode 100644 index 000000000..d8982bdde --- /dev/null +++ b/docs/specs/ADR-046-decision-register.md @@ -0,0 +1,99 @@ +# ADR 0046 decision register + +| Field | Value | +| --- | --- | +| Spec ID | `ADR-046-decision-register` | +| Parent | ADR 0046 | +| Status | Proposed | +| Version | 1 | +| Baseline | `b5ddbed67867d9244bf33390868101bd9b053e49` | +| Normative | Yes | +| Owners | ADR 0046 integrator | +| Depends on | None | +| Supersedes | None | + +This register records resolved architecture choices for the ADR 0046 normative +set. It contains design decisions and rationale, not conversation transcripts, +review metadata, or implementation status. + +## Resolved decisions + +| ID | Decision | Rationale and consequences | Affected specs | +| --- | --- | --- | --- | +| D001 | ADR 0046 is authored and implemented from protected `v3` at `b5ddbed6`, the final pre-ADR-0045 commit. | Main is not ancestry or current-state evidence, but any useful main-branch implementation may be copied/adapted through explicit reuse work items and independently integrated/validated against v3 contracts. | All | +| D002 | Keep number 0046 for cross-branch historical clarity. | The v3 branch has no ADR 0045, but the number distinguishes this replacement design from main-branch ADR 0045. | Parent, all specs | +| D003 | Reject kcp as the runtime substrate. | The spike proved desired object semantics but measured about 490 MiB RSS and a 176 MiB executable. v3 retains semantics without Kubernetes wire compatibility. | Resource store/API/reconciliation | +| D004 | Use one redb database per Zone. | redb supplies embedded ACID transactions, one writer with concurrent MVCC readers, crash safety, and a pure-Rust deployment shape. d2b owns schemas, indexes, watches, RBAC, operations, and routing. | Resource store | +| D005 | Use a single writer with optimistic conflicts. | Concurrent clients are accepted, but commits serialize. Every mutation has an expected revision; a stale client receives conflict and may re-read/retry. | Resource object/store/API/reconciliation | +| D006 | Embed the redb store and resource service in the Zone runtime. | This minimizes memory/process overhead and gives one clear owner. Provider/controller processes never access redb directly. | Resource store, process model | +| D007 | One fixed core-controller process per Zone hosts all trusted core controller handlers. | Separate handlers keep contracts explicit while one process avoids per-controller memory/process overhead. It uses d2b-bus like every other controller. | Core controllers, process model | +| D008 | Resource-plane acceptance is aggregate idle RSS <=64 MiB, readiness <=500 ms, p95 local reads <=2 ms, and p95 crash-safe writes <=10 ms on the pinned fixture. | The aggregate includes Zone resource service/store plus mandatory system-core and system-minijail controller processes. Optional Provider processes have separate budgets. | Resource store, validation | +| D009 | Controllers own their watch/coalescing queue and retry decisions; core minimizes unnecessary runs. | Core validates watch plans, maintains reverse indexes, suppresses irrelevant/converged/self-status events, coalesces revisions, and emits bounded hints. Provider logic remains independent. | Reconciliation | +| D010 | Every primitive is a complete low-level ResourceSpec. | Helpers, broker ops, syscalls, and sandbox fragments are implementation mechanisms, not primitives. Provider controllers compose behavior by creating primitive resources. | Resource object, primitive composition | +| D011 | d2b-bus over ComponentSession and a d2b transport is the only controller control-plane channel. | All get/list/watch/mutate/status/hint/checkpoint traffic uses the same authenticated route. There is no direct store, HTTP, or ambient IPC shortcut. | Bus/session/transport, resource API | +| D012 | Every Provider is one independently buildable crate/package; a Provider crate may build multiple process binaries. | The boundary supports separate controller/service/worker processes now and later extraction to one GitHub repository per Provider. | Provider model, every Provider dossier | +| D013 | ADR 0046 is a concise parent plus a manifest-bound `docs/specs/ADR-046-*` set. | The design is too large for one reviewable ADR. All specs are normative and accepted together. | Parent, all specs | +| D014 | Use one ADR/spec PR with user review before the final panel and again after it. | The panel runs only after the first approval. Any content change invalidates validation/panel evidence and repeats the final review. | Validation/delivery | +| D015 | Every field ending in `Ref` names another same-Zone resource using `/`. | Plain enums use plain names. Standard ResourceTypes are Zone-unique; vendor types are qualified; API binding collisions fail. | Resource object/API, all resource specs | +| D016 | Every resource belongs to exactly one Zone. | Refs never resolve across Zones. A Provider must be installed as a Zone-local `Provider/` resource before use. Cross-Zone resource relationships require separate review. | Zone, Provider, routing, resource API | +| D017 | Every Zone store has exactly one authoritative `Zone/` self resource; parents use local `ZoneLink/` resources. | Parent access uses the child Zone API; a parent does not mount or mirror the child store. | Zone resources/routing | +| D018 | Physical/local host execution contexts are `Host/` resources; VM, sandbox, cloud, and remote execution contexts are `Guest/` resources. | A Zone may have multiple Hosts reconciled by Provider/system-core and multiple Guests reconciled by runtime Providers. Process/EphemeralProcess uses one executionRef to either type. | Host/Guest, Provider model, process placement | +| D019 | Host and Guest share one ExecutionPolicy with plain `system|user` domains. | `defaultDomain`, `allowedDomains`, `defaultUserRef`, budgets, and attachment defaults are common. Process uses executionRef, optional domain, and conditional userRef; user-only Hosts/Guests reject system processes. | Host/Guest/Process specs | +| D020 | Resource ownership is singular. | Every resource has zero or one ownerRef. Any child mutation triggers owner reconciliation; owner cycles fail; deletion is child-first; UID binding prevents name-reuse confusion. | Resource object/store/reconciliation | +| D021 | `Provider/system-core`, `Provider/system-systemd`, and `Provider/system-minijail` are distinct. | `system-core` implements Host plus local User discovery/status only. systemd and minijail are interchangeable Process/EphemeralProcess implementations for Host or Guest execution. Volume, Network, Device, and Credential use separate Providers. | Resource catalog, Provider dossiers | +| D022 | Pidfd is mandatory local Process Provider behavior, not a ResourceSpec. | Minijail receives pidfd from clone3 and owns wait/reap. systemd binds InvocationID/cgroup/MainPID/start-time, opens pidfd, while systemd owns wait/reap. Pidfds never persist or cross the bus. | Process spec, Process Providers | +| D023 | Provider/controller descriptors declare supported Host/Guest Provider capabilities and Process domains. | ResourceTypes are declared once. Controller instances may run under several executionRef targets without host/guest-specific Process schemas. | Provider model, reconciliation, primitive composition | +| D024 | The initial task output is documentation only. | ADR/specs, manifests, indexes, changelog/instructions, PR, validation, and panel evidence are delivered. Future W0-W10 implementation requires a separate request. | Validation/delivery | +| D025 | Spec writing is parallel only after the shared foundation is stable. | File-disjoint Resource, core-controller, cross-cutting, and Provider dossier agents launch concurrently. Agents return decision-required rather than inventing shared contracts. | Validation/delivery | +| D026 | Standardize on ResourceType/ResourceSpec terminology and top-level `type`; do not use Kubernetes-style ResourceKind/kind terminology. | The vocabulary matches the native d2b model and canonical `/` refs. | All resource/controller specs | +| D027 | Every resource has `metadata`, `spec`, and `status`; common identity/Zone/revision/timestamps live in metadata. | Even resources with no desired fields carry `spec: {}`. Status is a separately authorized controller-owned subresource. | Resource object/store/API, every ResourceType | +| D028 | Status stores the latest bounded observation, not an embedded history. | Common status has numeric observedGeneration, phase, conditions, RFC 3339 timestamps, stable outcome code, optional exitCode, detailed bounded/redacted message, retryability, and ResourceType-specific typed fields. Earlier values remain in revision_log until compaction. | Resource object/store/API/reconciliation | +| D029 | The parent title is `ADR 0046: d2b 3.0 Provider control plane`. | It names the release and central architecture without exposing implementation-specific storage terminology. | Parent ADR/index | +| D030 | Reconciliation APIs and controller loops are asynchronous with hard local reaction targets. | Durable commit-to-controller-handler p95 is <=5 ms and ready Process commit-to-launch-attempt p95 is <=20 ms. Watch reception never waits for a running effect; independent resources reconcile/start concurrently within budgets; no polling/debounce delay. | Resource store/API/reconciliation, Process Providers, validation | +| D031 | Use `Process` for long-lived processes and `EphemeralProcess` for one-shot asynchronous processes. | They share a common execution spec and pidfd contract. EphemeralProcess reports terminal status directly and never references/creates a Process child merely to run. | Process resources/Providers, reconciliation | +| D032 | Replace File/Directory/ACL/FilesystemView ResourceTypes with one Volume ResourceType. | Volume has anchored relative layout entries, fine-grained owner/mode/access/default ACLs and lifecycle rules, named views, and same-Zone Host/Guest attachments. Process mounts select a volumeRef/view. | Volume, Process, storage migration | +| D033 | Volume supports Host-to-Guest transports such as virtiofs. | The Volume source may use an explicitly authorized Host path under its Provider policy; attachments name target executionRef, transport, mount path, view/access, and settings. The controller may own a Host virtiofsd Process and reports per-attachment export/mount status. | Volume/Host/Guest/Process specs and Providers | +| D034 | EphemeralProcess has outcome-specific terminal retention. | `successfulTtl` defaults to `1h`; `failedTtl` defaults to `24h`. TTL begins at status.completedAt and cleanup remains revision/finalizer/incident-hold safe. | EphemeralProcess, core cleanup controller, validation | +| D035 | Freeze the minimal standard ResourceType catalog. | Standard types are Zone, ZoneLink, Provider, Role, RoleBinding, Host, Guest, Process, EphemeralProcess, Volume, Network, Device, User, and Credential. Other behavior is inline or Provider-specific. | All resource/provider specs | +| D036 | The fixed core-controller process is also the `Provider/system-core` controller. | It creates/reconciles the first Host and Users and is one of two fixed Provider bootstrap exceptions. | Core controllers, system-core Provider, process/bootstrap | +| D037 | The universal status phase enum is `Pending`, `Ready`, `Succeeded`, `Degraded`, `Failed`, `Deleted`, or `Unknown`. | Conditions carry starting/deleting/retrying and other detail without multiplying common phases. | Resource object/API/store, all controllers | +| D038 | Final deletion has no retained resource tombstone. | After finalizers complete, core emits a Deleted status revision and removes the resource immediately. revision_log is the only deletion history; GET returns not found. | Resource store/API/reconciliation | +| D039 | ComponentSession uses Noise-based authenticated/record-protected profiles. | Copy/adapt the proven `d2b-session` and `d2b-session-unix` implementation from main, then reversion, integrate, and validate it against v3 Zone/resource/RBAC contracts. | ComponentSession/bus/security | +| D041 | Main is an unrestricted implementation reuse source, not the v3 baseline. | Work items may copy/adapt any useful main code. Each names the exact main commit/file/symbol/tests, proves the selected behavior, maps it to exact v3 destinations, and excludes unrelated ADR 0045 architecture. | Every implementation work item | +| D042 | Unsafe-local is not a separate v3 Provider. | It becomes a user-only Host using Provider/system-core, defaultDomain=user, allowedDomains=[user], and defaultUserRef. Its explicit no-isolation posture/warnings remain in Host status/UI; Processes use normal Process Providers. | Host/system-core/Nix/UI/reset | +| D043 | Freeze four Guest Provider families. | runtime-cloud-hypervisor, runtime-qemu-media, runtime-azure-container-apps, and runtime-azure-virtual-machine implement Guest. | Guest/Provider dossiers | +| D044 | Freeze two Volume Provider families. | volume-local owns anchored local storage/layout/ACL/views; volume-virtiofs owns same-Zone host-source→Guest virtiofs attachments and virtiofsd Processes. | Volume/Provider dossiers | +| D045 | Freeze one initial Network Provider. | network-local owns shared local fabrics; Azure/ACA networking remains in their Guest Providers until independently shared networking is required. | Network/Provider dossier | +| D046 | Freeze four Device Provider families. | device-tpm, device-usbip, device-security-key, and combined device-gpu (GPU/video) implement Device. Security-key owns unprivileged relay/frontend Processes; fixed broker only opens/passes hidraw. | Device/Provider dossiers | +| D047 | Freeze five interaction Provider families. | display-wayland, audio-pipewire, clipboard-wayland, notification-desktop, and shell-terminal own semantic resources/processes. Exec uses EphemeralProcess directly. | Interaction/Provider dossiers | +| D048 | Freeze three Credential Provider families. | credential-secret-service, credential-entra, and credential-managed-identity retain secret/token bytes and expose opaque leases/typed operations. | Credential/Provider dossiers | +| D049 | Freeze transport/observability/activation Providers and omit an orchestrator. | transport-unix, transport-vsock, transport-azure-relay, observability-otel, and activation-nixos are initial Providers. Ordinary controllers perform composition; there is no orchestrator-standard Provider. | Provider dossiers, routing/telemetry/activation | +| D050 | Rename the non-host execution parent from Workload to Guest and add Host as a separate ResourceType. | Host excludes guests and is reconciled by system-core. Guest covers VM/sandbox/cloud/remote execution. Process/EphemeralProcess uses canonical executionRef to either Host or Guest; both share ExecutionPolicy. | All resource/provider/process/routing/Nix specs | +| D051 | Provider/system-minijail is the second fixed non-resource Provider/controller bootstrap process. | The first Process controller cannot itself depend on a Process controller. The fixed minijail controller launches/reconciles later Process resources, including the system-systemd controller. | Process/bootstrap/system Providers | +| D052 | Use compiled narrow bootstrap authorization before stored RBAC exists. | Only exact Provider/system-core and Provider/system-minijail subjects receive the closed initial recovery/config publication verbs. It is non-configurable and cannot be widened; normal stored Role/RoleBinding governs later work. | Resource API/authz/core startup/session | +| D053 | Use bounded group commit in the single writer. | Independently validated non-conflicting queued mutations may share one redb transaction/fsync and Zone revision with ordered ordinals; each caller receives its own result. Conflicting/dependent writes remain ordered/separate. | Resource store/watches/performance | +| D054 | Terminology/identity owns one shared AuthenticatedSubjectContext. | ComponentSession maps trusted transport/Noise/bootstrap evidence into it; d2b-bus/resource API consume it for Role/RoleBinding evaluation. No spec defines a parallel identity context. | Identity/session/bus/resource API | +| D040 | ComponentSession and resource control use one authorization system. | Session authentication maps to a canonical resource subject; native Role/RoleBinding authorizes connect, invoke, stream, and resource verbs. Handshakes cannot self-assert roles; revision-bound leases revoke on policy change. | ComponentSession/bus/resource API/RBAC | + +## Open decisions + +No unresolved foundation decision is currently recorded. New ambiguity +must be added here before an author selects a normative answer. + +## Implementation work items + +This decision register has no future production implementation owner. Its +authoring work item is: + +| Field | Value | +| --- | --- | +| Work item ID | `ADR046-decisions-001` | +| Dependency/owner | ADR 0046 integrator | +| Current source | v3 evidence baseline plus parent/spec contradictions | +| Reuse action | adapt | +| Destination | `docs/specs/ADR-046-decision-register.md` | +| Detailed design | Record every evidence-underdetermined choice before dependent spec work proceeds | +| Integration | Parent decision summary and all affected specs link the decision ID | +| Data migration | None | +| Validation | Zero unresolved entries before pre-panel review; manifest/link consistency | +| Removal proof | Not applicable | diff --git a/docs/specs/ADR-046-primitive-resource-composition.md b/docs/specs/ADR-046-primitive-resource-composition.md new file mode 100644 index 000000000..9391999ef --- /dev/null +++ b/docs/specs/ADR-046-primitive-resource-composition.md @@ -0,0 +1,374 @@ +# ADR 0046 primitive ResourceSpec composition + +| Field | Value | +| --- | --- | +| Spec ID | `ADR-046-primitive-resource-composition` | +| Parent | ADR 0046 | +| Status | Proposed | +| Version | 1 | +| Baseline | `b5ddbed67867d9244bf33390868101bd9b053e49` | +| Normative | Yes | +| Owners | System/Host/Guest/Process/Volume Provider contracts | +| Depends on | `ADR-046-resource-object-model`, `ADR-046-resource-reconciliation` | +| Supersedes | Current implementation-shaped ProcessRole composition | + +## ResourceType threshold + +A behavior is a separate ResourceType only when it needs at least one: + +- independent identity; +- independent lifecycle; +- independent controller/status; +- independent owner/finalizer; +- sharing by several parents/consumers; +- Provider substitution. + +Otherwise it is a field or usage edge inside its parent ResourceSpec. + +## Frozen standard catalog + +Core control: + +| ResourceType | Responsibility | +| --- | --- | +| Zone | Zone self identity, policy, API/store status | +| ZoneLink | Parent/child delegation, transport, cursor/health | +| Provider | Installed package/config/controllers/schemas/services/status | +| Role | Bounded native RBAC rules | +| RoleBinding | Subjects to Role with narrowing/expiry | + +Standard execution/shared: + +| ResourceType | Responsibility | +| --- | --- | +| Host | Physical/local host execution/policy/budget parent | +| Guest | VM, sandbox, cloud, or remote execution/policy/budget parent | +| Process | Long-lived supervised process | +| EphemeralProcess | One-shot asynchronous process with result retention | +| Volume | Shareable storage, layout/ACL/views, Host/Guest attachments | +| Network | Independently shared network fabric | +| Device | Inventoried/exclusive-or-shared device arbitration | +| User | Named identity, UID/session observations, ACL/process subject | +| Credential | Opaque rotating credential/lease lifecycle | + +Provider-specific semantic ResourceTypes may extend this set. + +## Folded fields + +### Host and Guest shared ExecutionPolicy + +Inline: + +- Host/Guest Provider-specific type/settings; +- defaultDomain/allowedDomains/defaultUserRef; +- hierarchical CPU/memory/pids/fds/I/O/storage/network budgets; +- network attachments; +- device attachments; +- Volume attachment defaults; +- provider-specific settings; +- boot/identity/capability requirements. + +### Process and EphemeralProcess + +Inline: + +- sandbox namespaces/seccomp/capabilities/LSM; +- CPU/memory/pids/fds/I/O/thread/concurrency budgets; +- command template and sealed config/env; +- Volume mounts by volumeRef/view/mountPath/access; +- Network/Device refs and usage settings; +- ports/endpoints; +- bus/telemetry bindings; +- readiness/health/deadlines; +- user-domain portals. + +### Volume + +Inline: + +- files/directories; +- state/tmp/cache; +- owner/group/mode; +- access/default ACLs; +- no-follow/inheritance/recursive; +- create/repair/cleanup/restart/adoption; +- named views; +- same-Zone Host/Guest attachments and transport settings. + +### Internal-only + +Not ResourceTypes: + +- controller instances; +- pidfds; +- redb/OFD/controller locks; +- transient leases that have no independent external lifecycle; +- broker operations/syscalls; +- sandbox compiler fragments; +- process-group/cgroup implementation handles; +- route/session implementation handles. + +## Host + +Physical/local execution is an explicit Host: + +```yaml +apiVersion: resources.d2b.io/v3 +type: Host +metadata: + name: host-system + zone: dev +spec: + providerRef: Provider/system-core + defaultDomain: system + allowedDomains: [system, user] + budget: { ... } +status: + observedGeneration: 1 + phase: Ready + ... +``` + +A Zone may have several Hosts: + +- system-default Host accepting system and selected user processes; +- user-default Host for one configured defaultUserRef that rejects + system processes; +- additional policy/budget-separated Hosts. + +The v3 successor to unsafe-local is a user-only Host: + +```yaml +providerRef: Provider/system-core +defaultDomain: user +allowedDomains: [user] +defaultUserRef: User/alice +``` + +Its status/UI preserves an explicit no-isolation posture. It cannot satisfy an +isolated/managed/credential-separated policy and never falls back from another +Host/Guest Provider. It is not a separate Provider. + +## Guest + +VM/sandbox/remote Guests select their own installed Provider. +They use the same ExecutionPolicy fields as Host and may add Provider-specific +boot/identity/runtime settings. + +## Process common execution spec + +Process and EphemeralProcess share: + +```yaml +providerRef: Provider/system-systemd +executionRef: Host/host-system +domain: system # optional; defaults from Host/Guest ExecutionPolicy +userRef: null # required/inherited for user +processClass: controller # controller | service | worker +packageRef: Provider/example # exact package artifact comes from owning Provider +template: controller-main +configRef: Volume/example-config +mounts: + - volumeRef: Volume/example-state + view: controller + mountPath: /state + access: read-write +sandbox: { ... } +budget: { ... } +network: { ... } +devices: [] +endpoints: [] +telemetry: { ... } +``` + +No free-form executable, raw host path, numeric UID/GID, raw seccomp program, +ambient capability list, caller-selected broker op, credential bytes, or +arbitrary socket address is accepted. Package/template/provider schemas resolve +those implementation details. + +## Process + +Adds: + +- desired lifecycle; +- readiness/health; +- restart/backoff; +- adoption/drain; +- long-lived retention; +- provider-specific process identity. + +Status includes mandatory locally held pidfd evidence (never the fd/PID in +public status), wait/reap owner, phase/conditions, ready/exit observations, and +resource revisions. + +## EphemeralProcess + +Adds: + +- one-shot input; +- start/runtime deadlines; +- terminal output handles/digests; +- successfulTtl default `1h`; +- failedTtl default `24h`. + +It is the one-shot process itself, not a Job that references a Process. +Pending/running does not expire. Terminal cleanup begins at completedAt, respects +finalizers/incident hold, and writes cleanupEligibleAt. + +## Process Providers + +### system-systemd + +- non-forking transient system unit/scope or user scope; +- unit InvocationID+cgroup+MainPID/start-time binding; +- opens/verifies mandatory pidfd; +- systemd owns wait/reap; +- exact userRef for user scope; +- no per-Provider static PID1 template unit. + +### system-minijail + +- broker/local supervisor resolves compiled sandbox/resources; +- clone3(CLONE_PIDFD); +- d2b owns wait/reap; +- pidfd/cgroup/process start identity; +- no direct Provider broker access. + +Both implement identical ResourceTypes and status/error conformance. + +## system-core bootstrap + +The fixed core-controller process is also Provider/system-core. It and the +fixed Provider/system-minijail controller are the two Provider bootstrap +exceptions. system-core: + +- reconciles Host resources; +- discovers/reconciles local User resources/status; +- runs before the first Host exists. + +It does not implement Process, EphemeralProcess, Volume, Network, Device, or +Credential. + +Every Provider/controller other than those two bootstrap controllers is +represented by a Process under a Host or Guest. + +## Volume + +Volume spec preserves fine-grained current storage policy: + +```yaml +providerRef: Provider/volume-virtiofs +source: + executionRef: Host/host-system + settings: + hostPath: +layout: + - path: state + type: directory + ownerRef: User/example-system + groupRef: User/example-system + mode: "0700" + accessAcl: [] + defaultAcl: [] + noFollow: true + recursive: false + sensitivity: private + createPolicy: create-if-never-provisioned + repairPolicy: exact-owner + cleanupPolicy: owner-controlled +views: + controller: + path: state + rights: [read, write, create, delete, traverse] +attachments: + - executionRef: Guest/dev-vm + transport: virtiofs + mountPath: /mnt/state + view: controller + access: read-write +``` + +All layout paths are relative to the anchored Volume root. Host path is accepted +only by an explicitly selected/configured Volume Provider and its allowlisted +root policy; it never appears in public status/audit. + +Virtiofs attachment controller may create: + +- owned Host EphemeralProcess/Process for setup/virtiofsd; +- target Guest Provider attachment update; +- status per export and guest mount. + +Process mounts independently select Volume view/access for sandbox exposure. + +## Composite ownership example + +Cloud Hypervisor Guest owns: + +- VMM Process under Host; +- virtiofsd Process per Volume attachment; +- audio/video/GPU worker Processes where required; +- Provider-specific endpoint/device resources only when they satisfy the + separate ResourceType threshold. + +If any child mutates, Guest owner reconcile runs and reasserts the complete +child graph. + +Leaf Process Providers create no child merely to represent their internal +systemd unit/minijail state; they write Process status. + +## Current-code fit + +| Item | Treatment | +| --- | --- | +| Current anchor | `d2b-core/src/processes.rs`, `storage.rs`, `sync.rs`; Nix processes/storage/sync emitters; d2bd DAG; broker SpawnRunner; unsafe-local scopes; guest exec | +| Evidence class | Current process/storage contracts are reachable/generated; unified minimal resources are ADR-only | +| Behavior retained | Fine-grained ownership/ACL/no-follow, minijail profiles, pidfd/adoption, systemd user scopes, typed role/readiness, state fail-closed | +| Required delta | Host/Guest parent model, common Process/EphemeralProcess, generic Volume, Provider implementations, owner child graphs | +| Reuse path | Exact ProcessRole/storage-path work items copy/adapt current algorithms/tests | +| Replacement/deletion | No current role/path is removed without a resource/Provider successor | +| Feasibility proof | Host system/user Process; non-Host Process; virtiofs host-path→guest-mount Volume | +| Future owner | Resource/Provider dossier work items | + +## Implementation work items + +### ADR046-primitives-001 + +| Field | Value | +| --- | --- | +| Dependency/owner | W0; resource contracts | +| Current source | `packages/d2b-core/src/processes.rs`, `minijail_profile.rs`, `storage.rs`; `d2b-contracts/src/broker_wire.rs` | +| Reuse action | extract and adapt | +| Destination | `packages/d2b-contracts/src/v3/host.rs`, `guest.rs`, `execution_policy.rs`, `process.rs`, `volume.rs`, `user.rs`, `network.rs`, `device.rs`, `credential.rs` | +| Detailed design | Complete minimal ResourceType schemas and shared execution/Volume sub-schemas | +| Integration | Provider dossiers/controller descriptors bind exact types | +| Data migration | Full reset | +| Validation | Schema vectors and folded-field/no-duplicate-type policy tests | +| Removal proof | Old DTOs removed only by owning future slices | + +### ADR046-primitives-002 + +| Field | Value | +| --- | --- | +| Dependency/owner | Process contracts; system Provider slices | +| Current source | broker SpawnRunner/pidfd; d2bd supervisor; unsafe-local helper; guest exec runner | +| Reuse action | extract and adapt | +| Destination | `packages/d2b-provider-system-systemd/`, `packages/d2b-provider-system-minijail/`, shared neutral process conformance library | +| Detailed design | Common Process/EphemeralProcess, provider-specific launch/pidfd/wait/adoption/status | +| Integration | Process controller registration under Host/Guest; d2b-bus ResourceClient/status | +| Data migration | Current ProcessRoles converted by exact disposition table | +| Validation | Shared conformance plus Host/Guest/user integration | +| Removal proof | Role branches removed only after successor Provider tests | + +### ADR046-primitives-003 + +| Field | Value | +| --- | --- | +| Dependency/owner | Volume contract; Volume Provider slices | +| Current source | `storage-json.nix`, `d2b-core/src/storage.rs`, store/TPM/runtime path owners, virtiofsd argv/ProcessRole | +| Reuse action | extract and adapt | +| Destination | `packages/d2b-provider-volume-*/`; `nixos-modules/resources-volume.nix` | +| Detailed design | Fine-grained Volume layout/views, host-path policy, virtiofs attachments/status/owned Process | +| Integration | Host/Guest/Process refs and Volume controller | +| Data migration | Full reset; Provider-specific state export only where separately specified | +| Validation | ACL/no-follow/marker, sharing/views, virtiofs host/guest mount tests | +| Removal proof | storage.json rows removed only after Volume successor parity | diff --git a/docs/specs/ADR-046-provider-model-and-packaging.md b/docs/specs/ADR-046-provider-model-and-packaging.md new file mode 100644 index 000000000..efde9d30c --- /dev/null +++ b/docs/specs/ADR-046-provider-model-and-packaging.md @@ -0,0 +1,401 @@ +# ADR 0046 Provider model and packaging + +| Field | Value | +| --- | --- | +| Spec ID | `ADR-046-provider-model-and-packaging` | +| Parent | ADR 0046 | +| Status | Proposed | +| Version | 1 | +| Baseline | `b5ddbed67867d9244bf33390868101bd9b053e49` | +| Normative | Yes | +| Owners | Provider contracts/toolkit, package catalog, Nix integration | +| Depends on | `ADR-046-resource-object-model`, `ADR-046-resource-api-and-authorization`, `ADR-046-resource-reconciliation`, `ADR-046-primitive-resource-composition` | +| Supersedes | Current direct Provider construction/composition | + +## Provider resource + +A Provider is installed in a Zone as: + +```text +Provider/ +``` + +Package presence alone is not installation. providerRef resolves only a Ready +Provider resource in the same Zone. + +Provider spec contains: + +- exact package/executable/manifest/config/schema/service digests; +- publisher/signature/trust/conformance/provenance/SBOM identity; +- support channel and compatibility range; +- root configuration validated against signed JSON Schema; +- exported ResourceTypes/schemas; +- controller component descriptors; +- service component descriptors; +- worker Process templates; +- dependency aliases; +- permission claims; +- CLI projection; +- events/telemetry/state contracts; +- component placement templates; +- upgrade/drain/restart policy. + +Provider status contains: + +- common resource status; +- package/trust/API/conformance result; +- required/optional component status; +- exported ResourceType/service readiness; +- dependency health; +- controller leases/watch status; +- state schema/migration health; +- disabled/quarantined condition; +- aggregate Provider generation. + +## Crate/package boundary + +Every Provider maps to one independently buildable crate and signed package. + +One Provider crate: + +- declares one Provider identity; +- may build several controller/service/worker binaries; +- may share an internal library among those binaries; +- depends only on public neutral contracts/toolkit/SDK crates and approved + ecosystem dependencies; +- does not import d2bd, broker, Zone-store, Nix-emitter, or another Provider's + implementation internals; +- has one Nix package/conformance output; +- has one `ADR-046-provider-.md` dossier. + +This boundary must allow moving the crate to its own GitHub repository without +splitting semantics or copying daemon internals. + +Common libraries are Provider-neutral. A common library cannot register a +second Provider identity or become a hidden multi-Provider composition binary. + +## Provider components + +Component types: + +| Type | Responsibility | +| --- | --- | +| controller | Owns one or more ResourceTypes and async reconcile loop | +| service | Serves typed runtime/internal ComponentSession methods; no ResourceType ownership | +| worker | Narrow Process/EphemeralProcess with no controller/bus/dependency/CLI authority | + +Every component is a separate Process except the fixed system-core and +system-minijail bootstrap controllers. + +Descriptor fields include: + +- component ID/type/binary/template; +- exported ResourceTypes/methods; +- supported Host/Guest Provider capabilities; +- allowed `system|user` domains; +- cardinality; +- config projection; +- required/optional dependencies; +- ResourceRefs/templates it may create/use; +- state/Volume views; +- Process Provider selection constraints; +- permission claims; +- readiness/health/drain; +- process/sandbox/budget maximums. + +The same ResourceType is declared once. Several controller instances may run +under different Hosts/Guests/domains without duplicate Process schemas. + +## system-core bootstrap + +The one fixed core-controller process per Zone is also +`Provider/system-core`. It and the fixed Provider/system-minijail controller are +the only Providers not represented by Process resources. + +It owns: + +- Host reconciliation; +- local User discovery/status. + +It does not own: + +- Process/EphemeralProcess (`system-systemd`, `system-minijail`); +- Volume; +- Network; +- Device; +- Credential; +- semantic runtime/desktop/cloud resources. + +After system-core creates the first Host, system-minijail launches every other +Provider/controller/service/worker as a Process under a Host or Guest. + +## Process Provider family + +### Provider/system-systemd + +Implements Process and EphemeralProcess for systemd-capable Hosts/Guests: + +- non-forking transient system service/scope; +- transient user scope through fixed user supervisor; +- InvocationID+cgroup+MainPID/start-time verification; +- mandatory local pidfd; +- systemd wait/reap ownership; +- no per-Provider static PID1 template units. + +### Provider/system-minijail + +Implements the same ResourceTypes: + +- compiled inline Process sandbox; +- broker/Host/Guest supervisor effect; +- clone3(CLONE_PIDFD); +- d2b wait/reap ownership; +- cgroup/namespace/FD/adoption validation. + +Future Process Providers pass the same conformance without schema changes. + +## Configuration projection + +One Provider-owned root JSON Schema is evaluated before launch. The signed +component graph defines deterministic projections: + +- fields visible to each component; +- defaults/validation; +- sensitivity; +- ResourceRef/dependency bindings; +- component schema digest. + +Components cannot read sibling config. Secrets are Credential refs, not config +values. Root/component digests bind Provider resource, Process resources, +ComponentSessions, state, status, and audit. + +## Provider dependencies + +Manifest declares aliases: + +```text +runtime +volume +network +credential +transport +``` + +Zone config binds each alias to an exact Provider ResourceRef/service +fingerprint. A component asks d2b-bus for an alias; it never receives a global +registry/route table or arbitrary Provider endpoint. + +Synchronous dependency cycles fail configuration. Optional dependencies produce +declared degraded behavior only. + +## Package catalog + +Nix compiles an offline sorted exact-digest catalog: + +- Provider/package/publisher/version; +- package/executable/manifest/component/descriptor/config digests; +- systems/platform; +- API/service compatibility; +- signature/root epoch/revocation/deny status; +- provenance/SBOM/license/vulnerability evidence; +- conformance attestation; +- support channel; +- support contact. + +Selection is exact digest. No runtime marketplace, download, PATH scan, +directory discovery, latest, or version-range solving. + +## Trust + +Production admission requires: + +- exact digest; +- trusted publisher/root epoch; +- valid signature/rotation/revocation; +- no emergency deny; +- accepted provenance/SBOM/license/vulnerability policy; +- exact package/API conformance attestation. + +First- and third-party Providers use the same admission and sandbox. Trust does +not bypass runtime restrictions. + +## Compatibility + +- Provider API major exact; +- minor additive only; +- protobuf numbers never reused; +- exact descriptor fingerprint selected before launch; +- no handshake downgrade/fallback; +- removal after deprecation window or new major; +- state schema compatibility/migration checked independently. + +## Distribution bundles + +A bundle is a signed package catalog only. It does not: + +- merge Providers into one process/sandbox; +- union config/permissions; +- apply last-wins overrides; +- provide runtime discovery. + +Duplicate Provider names, command namespaces, ResourceTypes, incompatible +fingerprints, or policy conflicts reject the generation. + +## Toolkit + +Official Rust toolkit provides: + +- async ResourceClient/Reconciler loop; +- ComponentSession/d2b-bus lifecycle; +- generated typed Provider/service clients/servers; +- config/schema projection; +- Volume/pidfd-free Provider state helpers; +- operation/checkpoint/event/telemetry helpers; +- fake core/store/bus/supervisor/effect clients; +- fault injection; +- black-box conformance; +- Provider flake/project templates. + +Wire/state-machine golden vectors remain language-neutral. + +## Provider dossier requirement + +Every Provider dossier specifies: + +- exact crate/package/providerRef; +- root config schema/defaults/bounds/secrets; +- ResourceTypes implemented/consumed; +- controller watch/reconcile/finalize; +- services/CLI/events; +- every binary Process template/placement; +- Volume/state/credential use; +- dependencies/permission claims; +- pidfd/wait/reap where Process Provider; +- telemetry/audit/doctor/support; +- failure/upgrade/migration; +- exact v3 source→future destination work items and tests. + +## Frozen initial Provider catalog + +Every row requires one Provider crate/package and one +`ADR-046-provider-.md` dossier. + +### System, Host, and Guest + +| Provider | Implements | Description/processes | +| --- | --- | --- | +| `system-core` | Host, User | Fixed core-controller bootstrap; reconciles one or more Hosts and local User discovery/status only | +| `system-systemd` | Process, EphemeralProcess | Transient non-forking system/user units/scopes, pidfd verification, systemd wait/reap | +| `system-minijail` | Process, EphemeralProcess | Broker/minijail/clone3 sandboxed process, local pidfd and d2b wait/reap | +| `runtime-cloud-hypervisor` | Guest | Local NixOS VM lifecycle; owns VMM and guest-bootstrap child resources/Processes | +| `runtime-qemu-media` | Guest | QEMU media/physical-media lifecycle and QMP-mediated child Processes | +| `runtime-azure-container-apps` | Guest | Azure Container Apps sandbox lifecycle and remote agent integration | +| `runtime-azure-virtual-machine` | Guest | Full-host Azure VM lifecycle, bootstrap, and optional child Zone hosting | + +Unsafe-local is not a Provider. It is a user-only Host under +system-core. + +### Storage/network/device + +| Provider | Implements | Description/processes | +| --- | --- | --- | +| `volume-local` | Volume | Anchored local durable/ephemeral storage, fine-grained layout/ACL/views, bind/tmpfs/local source behavior and store-view mode | +| `volume-virtiofs` | Volume attachment controller | Host source Volume to target Guest virtiofs export/mount; owns virtiofsd Processes and attachment status | +| `network-local` | Network | Local bridge/namespace/address/DHCP/DNS/NAT/firewall/egress and Host/Guest attachment | +| `device-tpm` | Device | TPM allocation, swtpm Process, persistent TPM Volume/state and identity | +| `device-usbip` | Device | USB inventory/arbitration/export/attach/firewall and USBIP Process/EphemeralProcess | +| `device-security-key` | Device | Security-key inventory/ceremony/CID/lease/session; unprivileged Host relay and Guest frontend Processes; fixed broker only opens/passes hidraw | +| `device-gpu` | Device | Combined GPU/render/VFIO/video/media arbitration and GPU/video worker Processes | + +Azure/ACA-specific network remains inside Guest Providers until an +independently shared Azure Network is required. + +### Interaction + +| Provider | Implements | Description/processes | +| --- | --- | --- | +| `display-wayland` | Provider-specific display/session types | Wayland/display policy, Host/Guest proxies, window identity/rails and endpoint Processes | +| `audio-pipewire` | Provider-specific audio/session types | PipeWire policy/session, Host/user components, vhost-user-sound Processes | +| `clipboard-wayland` | Provider-specific clipboard types | Selection/bridge/transfer/presentation and Host/user/Guest Processes | +| `notification-desktop` | Provider-specific notification types | Observe/project/action/ack/presentation Processes | +| `shell-terminal` | ShellSession | Persistent terminal session/supervisor, open/attach/detach/kill and named terminal streams | + +One-shot exec is EphemeralProcess, not an exec Provider. + +### Credentials + +| Provider | Implements | Description/processes | +| --- | --- | --- | +| `credential-secret-service` | Credential | Exact-user Secret Service/keyring leases and typed operations | +| `credential-entra` | Credential | Entra-bound credential leases/operations without token export | +| `credential-managed-identity` | Credential | Host/Guest cloud managed-identity leases/operations | + +### Transport/observability/activation + +| Provider | Implements | Description/processes | +| --- | --- | --- | +| `transport-unix` | ZoneLink/ComponentSession transport | Local Unix/socketpair endpoints, peer evidence, FD-capable local channels | +| `transport-vsock` | ZoneLink/ComponentSession transport | Host/Guest vsock channels, expected CID and no FD transfer | +| `transport-azure-relay` | ZoneLink/ComponentSession transport | Remote Azure Relay reachability; relay identity is carriage only | +| `observability-otel` | Provider-specific telemetry endpoint/export/status types | OTEL endpoint/export/collector integration and health | +| `activation-nixos` | Provider-specific activation types | NixOS generation plan/apply/inspect/adopt/rollback | + +Cross-resource composition is ordinary controller behavior. There is no +special orchestrator Provider. + +## Current-code fit + +| Item | Treatment | +| --- | --- | +| Current anchor | `d2b-realm-provider`; live ACA/Relay/gateway; unwired d2b-host-providers; direct d2bd construction; current Nix package outputs | +| Evidence class | Mixed: ACA/Relay/gateway reachable; host adapters/transport/codec/client mostly unwired; generic registry/toolkit absent | +| Behavior retained | Typed Provider traits/errors/capabilities, fail-closed absence, circuit breaker, credential planes, redaction, injected test seams | +| Required delta | Provider resource/catalog/trust, one crate per Provider, process components, toolkit/conformance, exact dependencies | +| Reuse path | Extract current semantic logic with evidence-specific work items; do not copy dead scaffolds as live | +| Replacement/deletion | Direct d2bd constructors/factories removed only after Provider resource/Process/service integration | +| Feasibility proof | Out-of-tree template, multi-binary Provider, signed package, exact process bootstrap and resource controller | +| Future owner | Work items below and Provider dossiers | + +## Implementation work items + +### ADR046-provider-001 + +| Field | Value | +| --- | --- | +| Dependency/owner | W0; Provider contract/catalog owner | +| Current source | `packages/d2b-realm-provider/src/{provider,capabilities,error,credential,rate_limit,conformance}.rs` | +| Reuse action | extract and adapt | +| Destination | `packages/d2b-contracts/src/v3/provider.rs`, `packages/d2b-provider/src/lib.rs`, `packages/d2b-provider-toolkit/` | +| Detailed design | Provider resource/manifest/components/dependencies/services/trust/compatibility/toolkit | +| Integration | Zone config/catalog → Provider resource → Process components → bus/resource routes | +| Data migration | Full reset | +| Validation | Contract vectors, fake/malicious Provider, one-crate/one-identity policy | +| Removal proof | Old trait crate retired only after all Provider dossiers migrate | + +### ADR046-provider-002 + +| Field | Value | +| --- | --- | +| Dependency/owner | Provider contract; package/Nix integrator | +| Current source | `packages/Cargo.toml`; `flake.nix`; `nixos-modules/host-daemon.nix`; current source package derivations | +| Reuse action | adapt | +| Destination | one `packages/d2b-provider--/` per Provider; generic Nix Provider package/catalog emitter | +| Detailed design | Split current combined/composition crates; exact outputs/manifests/conformance | +| Integration | Provider package installed/registered per Zone | +| Data migration | No package compatibility path | +| Validation | Workspace naming/dependency/output/dossier/catalog parity policy | +| Removal proof | Combined crate removed only after every live implementation has a Provider successor | + +### ADR046-provider-003 + +| Field | Value | +| --- | --- | +| Dependency/owner | Process contracts; system Provider owners | +| Current source | `d2bd` DAG/broker spawn; unsafe-local helper; guestd/exec runner; `d2b-host` runtime provider | +| Reuse action | extract and adapt | +| Destination | `packages/d2b-provider-system-core/`, `d2b-provider-system-systemd/`, `d2b-provider-system-minijail/` | +| Detailed design | Bootstrap system-core; common Process/EphemeralProcess providers and pidfd conformance | +| Integration | Host/Guest providerRef/domain/userRef, local supervisors, resource status | +| Data migration | Current roles converted under reset | +| Validation | Shared conformance and host/user/non-Host tests | +| Removal proof | Current role launch paths removed after parity | diff --git a/docs/specs/ADR-046-resource-api-and-authorization.md b/docs/specs/ADR-046-resource-api-and-authorization.md new file mode 100644 index 000000000..6d5c06d10 --- /dev/null +++ b/docs/specs/ADR-046-resource-api-and-authorization.md @@ -0,0 +1,339 @@ +# ADR 0046 resource API and authorization + +| Field | Value | +| --- | --- | +| Spec ID | `ADR-046-resource-api-and-authorization` | +| Parent | ADR 0046 | +| Status | Proposed | +| Version | 1 | +| Baseline | `b5ddbed67867d9244bf33390868101bd9b053e49` | +| Normative | Yes | +| Owners | `d2b-resource-api`, Zone authorization engine | +| Depends on | `ADR-046-terminology-and-identities`, `ADR-046-resource-object-model`, `ADR-046-resource-store-redb` | +| Supersedes | None | + +## Service + +`d2b.resource.v3` is a language-neutral protobuf/ttrpc service routed through +d2b-bus over ComponentSession and a d2b transport. + +The resource API is asynchronous end-to-end. Rust clients expose async methods; +synchronous redb/filesystem work remains behind bounded blocking adapters in +the Zone runtime. + +## Methods + +| Method | Semantics | +| --- | --- | +| `Get` | Exact ResourceRef; returns current envelope/revision | +| `List` | Exact ResourceTypes and bounded filters/pagination; returns one MVCC snapshot revision | +| `Watch` | Exact authorized filters after revision; returns named stream | +| `Create` | Create-absent precondition and complete envelope spec | +| `UpdateSpec` | Full spec replacement with expected revision | +| `UpdateStatus` | Full status replacement with expected revision; controller/status-owner only | +| `UpdateMetadata` | Bounded labels/annotations/ownerRef/deletion-neutral metadata with expected revision | +| `UpdateFinalizers` | Exact finalizer add/remove with expected revision and ownership checks | +| `Delete` | Request deletion with expected revision/preconditions | +| `CommitBatch` | Atomic bounded set of the above in one Zone transaction | +| `ResolveRef` | Validate canonical ref/type/UID and return bounded identity metadata | +| `InspectSchema` | Read bound ResourceType schema/export/Provider identity | + +There is no arbitrary path/header/query/table API, JSON patch, server-side +apply, direct redb access, exec/port-forward, secret byte read, or generic +Provider command. + +## Request context + +The caller cannot provide authenticated identity or authorization outcome. +d2b-bus/ComponentSession supplies the shared `AuthenticatedSubjectContext`. +The API derives request attributes from it plus: + +- operation/idempotency/correlation/trace IDs; +- issue/deadline/cancellation; +- policy/API/config/controller revisions. + +The resource payload supplies only target refs, desired body, preconditions, +pagination/watch filters, and method-specific options. + +## ResourceType schemas and API exports + +Every Provider resource binds: + +- package/signature/trust identity; +- exported ResourceType names/versions; +- exact ResourceTypeSchema digests; +- controller descriptors and supported methods; +- maximum permission claims; +- service descriptors. + +The Zone self resource binds accepted API exports and short ResourceType names. +Binding: + +1. verifies Provider/package/schema signatures; +2. rejects a short ResourceType collision; +3. validates compatibility/fingerprint; +4. intersects permission claims with Zone policy; +5. installs the schema into api_schemas; +6. advances API catalog revision. + +Providers cannot mutate api_schemas directly. + +## Native RBAC resources + +### Bootstrap authorization + +Before a reset/empty store has Role/RoleBinding resources, the Zone runtime has +one compiled, non-configurable bootstrap policy: + +- exact subjects: Provider/system-core and Provider/system-minijail; +- exact local ComponentSession purposes/services; +- only store recovery, schema/config publication, initial Host/User/Provider/ + Role/RoleBinding creation, and first Process-controller launch verbs; +- no wildcard Provider/resource/runtime authority; +- no config field can widen it; +- normal stored RBAC governs all non-bootstrap work after publication. + +Every bootstrap action remains structurally validated/audited. A different +subject, remote route, Provider generation, or method fails closed. + +### Role + +Role spec contains bounded rules: + +```yaml +rules: + - resourceTypes: [Process, Volume] + verbs: [get, list, watch, create, update-spec] + subresources: [] + resourceNames: [] + zones: [dev] + executionRefs: [Host/host-system] +``` + +Rules use exact values; no implicit wildcard is granted. A reviewed explicit +wildcard may exist only for fixed core-controller roles and remains narrowed by +Zone/Provider/controller structural checks. + +### RoleBinding + +RoleBinding spec contains: + +- `roleRef: Role/`; +- exact subjects as canonical same-Zone refs, such as User, Provider, Host, + Guest, + or Process; +- optional authenticated external-principal selector generated by trusted + enrollment/config; +- expiry/revocation; +- bounded scope narrowing. + +A request body cannot select/override its subject. + +## Authorization attributes + +Every decision evaluates: + +```text +Zone +subject +ResourceType +subresource/service +verb +resource name +executionRef/domain/userRef scope +Provider/controller generation +``` + +Resource verbs: + +- get; +- list; +- watch; +- create; +- update-spec; +- update-status; +- update-metadata; +- update-finalizers; +- delete. + +Runtime service verbs such as invoke/connect/attach/cancel/observe are mapped +through the same engine but are not resource mutations. + +Native RBAC allow is necessary but not sufficient. Core structural checks also +enforce: + +- correct Zone/session/route; +- installed Provider/API binding; +- ResourceType/controller/status-owner match; +- ownerRef/UID/ref integrity; +- Host/Guest executionRef/domain/user placement; +- generation/revision; +- budget/quota/cardinality; +- process/sandbox/resource policy; +- broker/FD/locality constraints. + +Structural checks may narrow an allow and never override a deny. + +## Status ownership + +Each ResourceType schema identifies: + +- spec writer roles/controllers; +- status owner Provider/controller; +- finalizer owners; +- fields core alone sets. + +Only the current exact controller lease/generation may update status. Status +updates carry expected revision and observedGeneration. A Host/Guest/link/ +controller disconnect cannot write success; status becomes Unknown through the +authorized observer/core rule. + +## OwnerRef authorization + +Create/reparent requires: + +- permission on the child; +- get/use permission on the owner; +- scope compatibility; +- no cycle/depth violation; +- permission for both old/new owners on reparent. + +A child mutation's owner hint is generated by core after commit and cannot be +suppressed by the child. + +## Authorization cache + +Positive decisions may be cached only under: + +- exact subject; +- exact authorization attributes; +- Role/RoleBinding/Provider/API/Zone policy revisions; +- short expiry. + +Relevant resource revisions invalidate caches immediately after durable commit. +Denials may be cached briefly but never become allows. + +If authorization/store state is unavailable: + +- no new resource/runtime operation is admitted; +- admitted bounded operations retain their original context until deadline; +- long-lived streams require short authorization leases and close on expiry; +- local emergency disable remains available through the fixed out-of-band + safety path. + +## Parent/child Zone access + +A parent: + +- authenticates over a ZoneLink ComponentSession; +- maps to a child-local subject/RoleBinding; +- calls the child d2b.resource.v3 service; +- receives only child-authorized data/status. + +The child commits to its own store. The parent receives no database handle, +credential, token, or cross-Zone ResourceRef. + +A disconnected parent may record a local ZoneLink intent but cannot claim the +child resource changed. On reconnect the child reauthorizes and applies/rejects +against current revision. + +## Limits + +The API spec freezes bounds for: + +- request/response/batch bytes; +- batch mutation/resource count; +- ResourceType/name/ref depth/length; +- list page/filter count; +- watch count/filter complexity/rate/credit; +- Role rules/bindings/subjects; +- conditions/status/error strings; +- finalizers/owner depth; +- concurrent reads/writes per principal/controller; +- deadlines and retry-after. + +Over-limit input is rejected before redb mutation or Provider invocation. + +## Errors + +Stable classes include: + +- resource-not-found; +- resource-already-exists; +- resource-conflict; +- resource-schema-invalid; +- resource-ref-invalid; +- resource-owner-cycle; +- resource-owner-depth; +- resource-finalizer-denied; +- resource-provider-unavailable; +- resource-controller-mismatch; +- resource-status-owner-mismatch; +- authorization-denied; +- revision-expired; +- backpressure; +- timeout; +- cancelled; +- resource-plane-unavailable; +- internal-integrity-failure. + +Error messages are bounded/redacted. Conflict returns current revision but does +not return an unauthorized resource body. + +## Audit + +Audit records: + +- subject/Zone; +- ResourceType/name or bounded digest; +- verb/subresource; +- expected/current/result revision; +- authorization decision and policy revisions; +- operation/correlation; +- fixed outcome/error/retry class. + +It excludes spec/status payloads, Provider diagnostics, host paths, credentials, +process data, and terminal bytes. + +## Current-code fit + +| Item | Treatment | +| --- | --- | +| Current anchor | Public daemon/broker seqpacket auth; `d2b-daemon-access` admission types; `d2b-realm-router` principal/capability/idempotency checks; Realm access resolver; strict DTOs | +| Evidence class | Local daemon auth is reachable; daemon-access/Realm peer abstractions are partly unwired; native RBAC/API are ADR-only | +| Behavior retained | SO_PEERCRED/local identity, typed denials, positive capabilities, no relay-to-local auth, strict bounds/unknown-field rejection | +| Required delta | Entire resource API, Provider API schemas/bindings, Role/RoleBinding engine, status ownership, parent resource routing | +| Reuse path | Extract exact admission/error/id/ref validators and router authorization derivation | +| Replacement/deletion | Old public wire remains until CLI/controllers consume new services | +| Feasibility proof | Multi-process local/vsock/Zone resource calls, immediate revocation, conflict/no-leak tests | +| Future owner | Work items below | + +## Implementation work items + +### ADR046-api-001 + +| Field | Value | +| --- | --- | +| Dependency/owner | W0; resource API integrator | +| Current source | `packages/d2b-contracts/src/public_wire.rs`, `broker_wire.rs`; `d2b-daemon-access/src/lib.rs`; `d2b-realm-router/src/lib.rs` | +| Reuse action | extract and adapt | +| Destination | `packages/d2b-contracts/proto/d2b-resource-v3.proto`, `packages/d2b-resource-api/src/service.rs`, `client.rs` | +| Detailed design | Async methods, contexts, preconditions, limits, errors, status/finalizer separation, batch API | +| Integration | d2b-bus exact service → Zone auth → redb actor | +| Data migration | None; v3 clean break | +| Validation | Protocol vectors; malformed/oversize/conflict/status-owner tests | +| Removal proof | Old command/resource-equivalent paths removed only per integration wave | + +### ADR046-api-002 + +| Field | Value | +| --- | --- | +| Dependency/owner | ADR046-api-001; authorization integrator | +| Current source | `d2bd` public admission; `d2b-daemon-access` policy evidence; `d2b-realm-core/src/access.rs`, `audit.rs` | +| Reuse action | adapt | +| Destination | `packages/d2b-resource-api/src/authz.rs`, `packages/d2b-core-controller/src/rbac.rs` | +| Detailed design | Role/RoleBinding schemas/evaluator/cache/revision invalidation, ComponentSession subject mapping, parent Zone access | +| Integration | Every resource/runtime method invokes one native evaluator before structural checks | +| Data migration | Generate initial Roles/Bindings from Nix v3 config | +| Validation | Decision matrix/property tests; revocation/cache/outage/parent-child tests | +| Removal proof | Legacy auth remains until every v3 route is covered | diff --git a/docs/specs/ADR-046-resource-object-model.md b/docs/specs/ADR-046-resource-object-model.md new file mode 100644 index 000000000..b68f7ca7e --- /dev/null +++ b/docs/specs/ADR-046-resource-object-model.md @@ -0,0 +1,262 @@ +# ADR 0046 resource object model + +| Field | Value | +| --- | --- | +| Spec ID | `ADR-046-resource-object-model` | +| Parent | ADR 0046 | +| Status | Proposed | +| Version | 1 | +| Baseline | `b5ddbed67867d9244bf33390868101bd9b053e49` | +| Normative | Yes | +| Owners | `d2b-contracts`, Zone resource API/store | +| Depends on | `ADR-046-decision-register`, `ADR-046-terminology-and-identities` | +| Supersedes | None | + +## Universal envelope + +Every resource has: + +```yaml +apiVersion: resources.d2b.io/v3 +type: Host +metadata: + name: host-system + zone: dev + uid: + generation: 1 + revision: + ownerRef: null + finalizers: [] + deletionRequestedAt: null + createdAt: 2026-07-22T00:00:00Z + updatedAt: 2026-07-22T00:00:00Z +spec: {} +status: + observedGeneration: 0 + phase: Pending + conditions: [] + lastReconciledAt: null + startedAt: null + completedAt: null + outcome: null +``` + +`metadata`, `spec`, and `status` are always present. A type with no desired +fields uses `spec: {}`. + +## Metadata + +| Field | Rules | +| --- | --- | +| `name` | Required ResourceName; unique for the bound ResourceType in this Zone | +| `zone` | Required plain Zone name; must equal the store's `Zone/` self resource | +| `uid` | Immutable, generated at create, never caller-selected | +| `generation` | Starts at 1; increments only when spec changes | +| `revision` | Opaque Zone-local latest-commit/watch token | +| `ownerRef` | Optional singular canonical same-Zone ResourceRef | +| `ownerUid` | Internal store binding, not caller-writable/portable JSON input | +| `finalizers` | Bounded unique typed finalizer IDs owned by installed controllers | +| `deletionRequestedAt` | Core-set RFC 3339 UTC timestamp; null before delete | +| `createdAt` | Core-set RFC 3339 UTC timestamp | +| `updatedAt` | Core-set RFC 3339 UTC timestamp on every mutation | + +Labels/annotations are optional bounded presentation metadata. They never +select authorization, provider/controller ownership, path, process identity, +or implicit relationships. A ResourceType may declare a closed set of indexed +exact-match metadata fields. + +## Spec + +Spec is the desired state. Rules: + +- strict schema with unknown fields denied unless the signed ResourceType + explicitly declares a bounded vendor extension object; +- deterministic defaults applied before storage; +- canonical JSON representation and digest; +- all references use canonical ResourceRef values; +- no secrets, raw credentials, or authority decisions; +- provider-specific settings are schema-bound to the exact installed Provider; +- spec mutation requires expected revision; +- full replacement is the default; no field-level server-side apply or silent + merge; +- a successful spec replacement increments generation and revision. + +## Status + +Status is observed state and is a separately authorized subresource. + +### Common fields + +| Field | Rules | +| --- | --- | +| `observedGeneration` | Numeric spec generation accounted for by this status | +| `phase` | `Pending`, `Ready`, `Succeeded`, `Degraded`, `Failed`, `Deleted`, or `Unknown` | +| `conditions` | Bounded latest condition set keyed by condition type | +| `lastReconciledAt` | RFC 3339 UTC; set on completed reconcile attempt | +| `startedAt` | Optional RFC 3339 UTC; resource/effect start | +| `completedAt` | Optional RFC 3339 UTC; terminal completion | +| `outcome` | Optional latest bounded outcome | + +Condition: + +```yaml +type: Ready +status: "True" # True | False | Unknown +reason: process-ready +message: bounded redacted operator detail +observedGeneration: 3 +lastTransitionAt: 2026-07-22T00:00:01Z +``` + +Outcome: + +```yaml +code: process-exited +exitCode: 1 +message: bounded redacted error detail +retryable: true +retryAfter: 5s +occurredAt: 2026-07-22T00:00:01Z +``` + +`code` and `reason` are stable lower-kebab-case machine values. `message` may +contain actionable Provider detail but is bounded, UTF-8/control-character +validated, and must not contain secrets, tokens, credential material, terminal +bytes, argv/environment, state contents, or host/provider paths. ResourceType +schemas add typed status fields; they do not replace the common fields. + +Status retains only the latest conditions/outcome. Prior status versions remain +in revision_log until compaction. + +### Phase use + +- `Pending`: desired state exists but is not yet ready/terminal. +- `Ready`: long-lived resource is healthy and current. +- `Succeeded`: one-shot/finite desired work completed successfully. +- `Degraded`: usable but one or more declared conditions are impaired. +- `Failed`: current desired generation cannot complete under the current + retry/terminal policy. +- `Deleted`: final status event after finalizers, immediately before row/index + removal. +- `Unknown`: owning controller/Host/Guest/link cannot currently prove state. + +Starting, reconciling, retrying, draining, and deleting are condition/reason +details, not additional common phases. + +## Ownership and child-triggered reconciliation + +Each resource has zero or one ownerRef. Create/reparent: + +1. resolves canonical owner type/name in the same Zone; +2. stores owner UID binding; +3. rejects self/cycles/excessive depth; +4. updates owner_index atomically with the resource and revision event. + +Every committed child spec/status/metadata/finalizer/delete mutation produces a +coalesced `owned-resource-changed` hint for the current owner. The owner +controller relists owner_index, compares children with desired state, creates +missing children, corrects drift through expected-revision writes, and removes +no-longer-desired children under finalizer policy. + +Child status updates still trigger the owner even when a controller's own +status-only event would otherwise be suppressed. + +## Generation and revision + +- create: generation 1, one Zone revision; +- status-only/metadata/finalizer update: generation unchanged, revision changes; +- spec update: generation increments exactly once, revision changes; +- a multi-resource mutation batch receives one Zone revision and ordered + per-resource ordinals; +- stale expected revision changes nothing and returns conflict/current revision. + +## Deletion + +1. Authorized delete sets deletionRequestedAt and emits a revision. +2. Controllers complete exact finalizers child-first. +3. Final transaction emits a `phase=Deleted` change event and removes the + resource plus indexes immediately. +4. GET returns not found. revision_log is the only deletion history. + +No retained resource tombstone exists. + +## Minimal standard ResourceType catalog + +Core control: + +- Zone; +- ZoneLink; +- Provider; +- Role; +- RoleBinding. + +Standard execution/shared: + +- Host; +- Guest; +- Process; +- EphemeralProcess; +- Volume; +- Network; +- Device; +- User; +- Credential. + +Provider-specific semantic ResourceTypes may extend the set through signed +schemas/API bindings. They use this same envelope/status/ownership contract. + +## Folded implementation detail + +The following are not standalone ResourceTypes: + +- budgets/cgroups; +- sandbox/namespace/seccomp/capability profiles; +- files/directories/ACLs/views/mounts outside Volume; +- process endpoints/ports/telemetry bindings; +- controller instances; +- pidfds; +- locks/leases internal to transactions/controllers; +- syscalls/broker operations. + +## Current-code fit + +| Item | Treatment | +| --- | --- | +| Current anchor | `d2b-realm-core/src/ids.rs`, `workload.rs`, `allocator.rs`; `d2b-core/src/storage.rs`, `processes.rs`; daemon operation/readiness/status DTOs | +| Evidence class | Current DTOs are mixed reachable/generated; universal resource envelope is ADR-only | +| Behavior retained | Strict serde, bounded IDs/messages, typed status/error enums, generation-bound exec/shell attach, storage owner/repair metadata | +| Required delta | Universal metadata/spec/status, ResourceType schemas, owner index/triggers, revisions, conditions/outcome, native deletion | +| Reuse path | Extract validators/redaction/error constants and storage lifecycle fields named in work items | +| Replacement/deletion | Existing manifest/process/storage DTOs remain until owning ResourceType integrations are live | +| Feasibility proof | Schema golden vectors, owner property tests, optimistic conflicts, status redaction, deletion event/removal | +| Future owner | Work items below | + +## Implementation work items + +### ADR046-object-001 + +| Field | Value | +| --- | --- | +| Dependency/owner | W0 shared contract root; `d2b-contracts` | +| Current source | `packages/d2b-realm-core/src/ids.rs`, `workload.rs`, `error.rs`; `packages/d2b-core/src/storage.rs`, `processes.rs` | +| Reuse action | extract and adapt | +| Destination | `packages/d2b-contracts/src/v3/resource.rs`, `resource_status.rs`, `resource_schema.rs` | +| Detailed design | Implement strict ResourceEnvelope, metadata, spec/status values, phase/condition/outcome, canonical JSON, bounds/redaction, ownerRef/UID fields | +| Integration | Store/API/SDK/Nix/codegen consume one contract | +| Data migration | Full d2b 3.0 reset; no v2 resource import | +| Validation | Golden JSON/protobuf vectors; serde unknown-field; status redaction/size/time/phase tests | +| Removal proof | Old DTOs removed per owning ResourceType wave only after rendered/runtime consumers move | + +### ADR046-object-002 + +| Field | Value | +| --- | --- | +| Dependency/owner | ADR046-object-001; native resource store | +| Current source | `packages/d2b-realm-core/src/allocator_engine.rs`, `d2b-realm-router/src/lib.rs` shared ownership/idempotency precedents | +| Reuse action | adapt | +| Destination | `packages/d2b-resource-store-redb/src/ownership.rs`, `packages/d2b-controller-toolkit/src/owner_hints.rs` | +| Detailed design | Singular ownerRef resolution/UID binding, cycle/depth property checks, reverse index, owner hints, child-first deletion | +| Integration | Every store mutation updates owner index and hint dispatcher atomically | +| Data migration | None after reset | +| Validation | Property tests for cycles/reparent/name reuse; integration tests for child drift repair and owner cascades | +| Removal proof | Not applicable | diff --git a/docs/specs/ADR-046-resource-reconciliation.md b/docs/specs/ADR-046-resource-reconciliation.md new file mode 100644 index 000000000..56b2b1904 --- /dev/null +++ b/docs/specs/ADR-046-resource-reconciliation.md @@ -0,0 +1,330 @@ +# ADR 0046 resource reconciliation + +| Field | Value | +| --- | --- | +| Spec ID | `ADR-046-resource-reconciliation` | +| Parent | ADR 0046 | +| Status | Proposed | +| Version | 1 | +| Baseline | `b5ddbed67867d9244bf33390868101bd9b053e49` | +| Normative | Yes | +| Owners | Core hint dispatcher, Provider controller toolkit | +| Depends on | `ADR-046-resource-object-model`, `ADR-046-resource-store-redb`, `ADR-046-resource-api-and-authorization` | +| Supersedes | None | + +## Ownership + +Provider controller processes own: + +- watch subscription; +- local pending/running map; +- per-resource serialization; +- cross-resource async concurrency; +- reconcile/observe/finalize handlers; +- retry/requeue decision; +- status/mutation batches. + +Core owns: + +- watch-plan validation; +- ResourceType/provider/controller ownership; +- type/owner/dependency reverse indexes; +- change filtering; +- converged/self-status suppression; +- owner triggers; +- coalesced high-water hints; +- controller lease/generation; +- fair API/watch budgets; +- durable revision/watch delivery. + +Core does not execute Provider domain logic. Providers do not poll the store or +open broad watches. + +## Controller descriptor + +Each signed controller descriptor declares: + +- Provider/component/controller ID and generation; +- ResourceTypes and versions; +- Host/Guest Provider capabilities supported; +- Process domains supported; +- spec/status/finalizer verbs; +- exact watch selectors; +- explicit dependency selectors; +- owner-child triggers consumed; +- reconcile/observe concurrency; +- maximum pending resources; +- observe/resync policy; +- finalizers owned; +- service/schema fingerprints; +- ResourceType-specific deadlines/retry classes. + +Runtime registration must match the installed Provider descriptor and +authenticated Process/Host/Guest identity. + +## Async interface + +Language-neutral semantic interface: + +```text +async describe() -> ControllerDescriptor +async validateSpec(context, resource) -> ValidationResult +async plan(context, resource, dependencies) -> ReconcilePlan +async reconcile(context, resource, dependencies) -> ReconcileResult +async observe(context, resource) -> ObservationResult +async finalize(context, deletingResource) -> FinalizeResult +async health() -> ControllerHealth +async drain(deadline) -> DrainResult +``` + +The official Rust toolkit exposes async traits and an async ResourceClient. +Non-Rust SDKs implement the same vectors/state machine. + +No handler holds a redb transaction or blocking kernel/systemd/filesystem call +across an await. Blocking effects use explicit bounded adapters. + +## Reconcile context + +Context contains: + +- controller/Provider/Process/Host/Guest/Zone identity and generations; +- target ResourceRef/UID/revision/generation; +- trigger reason set and high-water revision; +- operation/idempotency/correlation/trace IDs; +- attempt; +- monotonic deadline/cancellation; +- policy/API/config/controller revisions; +- capability-limited async ResourceClient. + +It contains no database handle, direct broker socket, reusable credential, raw +route table, or authority supplied by the resource payload. + +## Reconcile results + +Result contains: + +- processed revision/generation; +- zero or one ResourceMutationBatch; +- latest status/conditions/outcome; +- one disposition: + - `converged`; + - `pending`; + - `degraded`; + - `failed-retryable`; + - `failed-terminal`; + - `requeue-at`; + - `finalized`; +- next observe/requeue time where applicable. + +A stale mutation conflict discards the result; the toolkit re-reads and lets the +controller retry under policy. Core never merges stale Provider output. + +## Async loop + +1. Register descriptor/watch plan. +2. List resources requiring initial work and receive snapshot revision. +3. Open watch after snapshot revision. +4. Dedicated async receiver continuously consumes hints. +5. If a resource is idle, dispatch immediately. +6. If already queued/running, replace pending high-water revision and union + non-droppable reasons. +7. Each resource has one running handler; independent resources run in parallel + under semaphore/budget. +8. Handler reads one fresh resource/dependency snapshot. +9. Handler may asynchronously write Pending/starting status. +10. Handler starts effects in its own task/blocking adapter. +11. Receiver continues reading and dispatching other ready resources. +12. Handler commits mutation/status with expected revisions. +13. Toolkit acknowledges/checkpoints after commit or terminal no-mutation + outcome. +14. Disconnect/revision-expired relists and rebuilds the queue. + +There is no fixed polling interval, debounce window, or sleep between ready +resources. + +## Trigger reasons + +Closed common reasons: + +- spec-generation-changed; +- owned-resource-changed; +- dependency-changed; +- dependency-ready; +- deletion-requested; +- finalizer-required; +- controller-generation-changed; +- Provider-generation-changed; +- policy-changed; +- execution-status-changed; +- scheduled-observe; +- retry-due; +- manual-reconcile; +- startup-relist. + +Reasons are coalesced without dropping owner/deletion/finalizer/policy/ +generation causes. + +## Core suppression + +Core may suppress: + +- unbound ResourceTypes/scopes; +- irrelevant metadata/status fields; +- controller's own status-only event when no owner/dependency consumer needs + it; +- object whose generation equals observedGeneration, controller generation is + current, no dependency/owner/delete/observe/retry cause exists, and conditions + do not require work. + +Core may not suppress: + +- any child mutation's ownerRef trigger; +- deletion/finalizer; +- policy/Provider/controller generation; +- explicit dependency; +- due retry/observe; +- Unknown state requiring observation. + +## Owner reconciliation + +On any child mutation: + +1. store emits owned-resource-changed after durable commit; +2. owner hint includes child ref/UID/revision/event; +3. owner controller relists owner_index; +4. it compares complete desired children with observed children; +5. it creates missing children; +6. it repairs drift through expected-revision writes; +7. it requests deletion for children no longer desired; +8. child finalizers/status remain owned by their controllers. + +Propagation to ancestors is acyclic, depth/budget bounded, and coalesced. + +## Process fast path + +When a Process or EphemeralProcess durable commit completes and dependencies +are ready: + +- post-commit dispatcher pushes a matching hint immediately; +- p95 handler start is <=5 ms; +- Process Provider validates/starts launch attempt in a background task; +- p95 commit-to-launch-attempt is <=20 ms; +- status is written asynchronously; +- watch receiver continues reading; +- next independent Process may start before prior readiness/completion; +- only per-resource ordering, true dependencies, configured concurrency, or + backpressure may delay dispatch. + +The benchmark proves 1/10/100 ready Process resources and records queue/ +event-loop responsiveness. + +## Process status + +Typical transitions: + +```text +Pending (Queued/Starting condition) + -> Ready + -> Degraded | Failed | Unknown +``` + +Starting/retrying/draining are conditions/reasons, not common phases. + +EphemeralProcess: + +```text +Pending -> Succeeded | Failed | Unknown +``` + +It includes startedAt/completedAt/outcome/exitCode and: + +- successfulTtl default 1h; +- failedTtl default 24h; +- cleanupEligibleAt; +- finalizer/incident-hold-safe deletion. + +## Finalization + +The exact finalizer owner receives deletion-requested. It returns: + +- complete; +- pending with requeue-at; +- blocked with typed condition; +- ambiguous with no false success. + +A controller clears only its finalizer. Core removes the resource only after all +finalizers and owned-child deletion complete. + +## Resync and external drift + +Controllers do not poll by default. A ResourceType whose external state can +drift declares a bounded observe interval. Core schedules exactly that +reconcile reason. Missed watch events are recovered by revision replay/relist. + +## Backpressure and fairness + +- bounded watch stream credit; +- bounded pending map; +- per-controller/Provider/Host/Guest concurrency; +- fair ResourceClient queues; +- reserved health/cancel/status capacity; +- typed backpressure, never silent drop of an admitted mutation; +- latest high-water coalescing for the same resource; +- no cross-resource eviction. + +## Current-code fit + +| Item | Treatment | +| --- | --- | +| Current anchor | `d2b-realm-router` shared OperationRouter/mux/session lifecycle; d2bd DAG/topological executor/readiness/pidfd; role-specific state machines | +| Evidence class | Current route/DAG logic is tested; generic controller loop/hints are ADR-only | +| Behavior retained | Deterministic ordering, capability denial, idempotency, bounded queues, cancellation, fail-fast typed errors, pidfd adoption | +| Required delta | Async controller SDK/loop, store watches/hints, owner triggers, cross-resource concurrency, status batches | +| Reuse path | Extract pure state machines/limits and deterministic test clocks; replace role branches with ResourceType controllers | +| Replacement/deletion | DAG/role path remains until each successor controller/Process graph is integrated | +| Feasibility proof | Real multi-process controller over d2b-bus; latency/load/conflict/owner/finalizer tests | +| Future owner | Work items below | + +## Implementation work items + +### ADR046-reconcile-001 + +| Field | Value | +| --- | --- | +| Dependency/owner | W0/W1a; controller toolkit owner | +| Current source | `packages/d2b-realm-router/src/lib.rs`, `mux_session.rs`, `session_lifecycle.rs`; `packages/d2bd/src/supervisor/dag.rs` | +| Reuse action | extract and adapt | +| Destination | `packages/d2b-controller-toolkit/src/lib.rs`, `runner.rs`, `queue.rs`, `context.rs`, `result.rs` | +| Detailed design | Async ResourceReconciler, watch receiver, coalescing, per-resource serialization, parallel tasks, retry/checkpoint/finalize | +| Integration | Provider controller binaries wrap handlers with toolkit | +| Data migration | None | +| Validation | Golden state-machine vectors, deterministic clocks, conflict/restart/queue tests | +| Removal proof | Current per-role orchestration removed only after ResourceType successors | + +### ADR046-reconcile-002 + +| Field | Value | +| --- | --- | +| Dependency/owner | Store/API + ADR046-reconcile-001; core controller | +| Current source | `d2b-realm-core/src/route_engine.rs`, `allocator_engine.rs`; `d2b-realm-router/tests/transport_topology_harness.rs` | +| Reuse action | adapt | +| Destination | `packages/d2b-core-controller/src/hints.rs`, `dependencies.rs`, `owner_reconcile.rs` | +| Detailed design | Watch-plan validation, indexes, suppression, owner/dependency hints, leases, startup relist, fair admission | +| Integration | Store post-commit dispatcher → d2b-bus controller streams | +| Data migration | None | +| Validation | Owner/dependency chains, suppression/no-loss, restart/relist, lease withdrawal | +| Removal proof | Not applicable | + +### ADR046-reconcile-003 + +| Field | Value | +| --- | --- | +| Dependency/owner | Process Providers + benchmark owner | +| Current source | `d2bd/src/supervisor/dag.rs`, `pidfd.rs`, unsafe-local blocked supervisor, guest exec runner | +| Reuse action | adapt | +| Destination | `packages/d2b-controller-toolkit/benches/reaction.rs`, Process Provider integration tests | +| Detailed design | Commit-to-handler/launch fast path, nonblocking watch, parallel ready resources | +| Integration | Resource store → bus/session → controller → Process effect/status | +| Data migration | None | +| Validation | Hard <=5 ms/<=20 ms p95 gates and 1/10/100 Process concurrency | +| Removal proof | Not applicable | diff --git a/docs/specs/ADR-046-resource-store-redb.md b/docs/specs/ADR-046-resource-store-redb.md new file mode 100644 index 000000000..d3d0df5f3 --- /dev/null +++ b/docs/specs/ADR-046-resource-store-redb.md @@ -0,0 +1,333 @@ +# ADR 0046 redb Zone resource store + +| Field | Value | +| --- | --- | +| Spec ID | `ADR-046-resource-store-redb` | +| Parent | ADR 0046 | +| Status | Proposed | +| Version | 1 | +| Baseline | `b5ddbed67867d9244bf33390868101bd9b053e49` | +| Normative | Yes | +| Owners | Zone runtime, `d2b-resource-store-redb` | +| Depends on | `ADR-046-terminology-and-identities`, `ADR-046-resource-object-model` | +| Supersedes | None | + +## Ownership and process boundary + +Every Zone runtime embeds exactly one redb database and one resource service. +The database belongs to that Zone only. + +The generated storage contract gives one broker/Host/Guest-local storage owner +authority to create/validate: + +- database inode; +- identity marker; +- owner/group/mode/link count; +- filesystem/locking support; +- parent-directory fsync; +- replacement/missing-state detection. + +The owner passes one already-open regular `File` to the Zone runtime. The +pinned redb API must use `FileBackend::new(File)` or an equivalently reviewed +fd-backed API. The Zone runtime does not resolve a caller-controlled store +path. + +Providers/controllers never receive a redb handle, database file/dir fd, path, +table access, or direct store client. Only the resource service/store actor +touches redb. + +## Store identity + +One closed `store_meta` table binds: + +| Key | Value | +| --- | --- | +| `store_uuid` | Random immutable store identity | +| `zone_name` | Must match `Zone/` self resource | +| `zone_uid` | Immutable UID of self resource | +| `created_at` | RFC 3339 UTC | +| `schema_version` | Internal physical schema version | +| `current_revision` | Latest committed Zone revision | +| `compaction_floor` | Earliest replayable revision | +| `active_configuration_revision` | Active Nix/root configuration | +| `policy_revision` | Current authorization policy revision | +| `api_catalog_revision` | Current bound ResourceType catalog | +| `clean_shutdown` | Clean/crash-open marker | +| `backup_generation` | Latest validated logical backup | + +A previously provisioned database that is missing, replaced, bound to another +Zone/UID, newer than the binary schema, or internally inconsistent fails closed. +The runtime never silently creates an empty replacement. + +## Physical tables + +| Table | Key | Value | +| --- | --- | --- | +| `store_meta` | closed metadata key | Closed versioned scalar | +| `api_schemas` | canonical API/schema identity digest | Signed ResourceTypeSchema/ResourceApiExport, validator fingerprint, compatibility/provenance | +| `resources` | `(bound ResourceType, resource_name)` | Complete strict ResourceEnvelope plus internal owner UID | +| `type_index` | `(bound ResourceType, resource_name)` | Immutable resource UID | +| `owner_index` | `(owner UID, child UID)` | Child ResourceType/name and latest revision | +| `controller_index` | `(controller binding ID, ResourceType, name)` | Resource UID | +| `revision_log` | monotonically increasing u64 revision | Ordered bounded ChangeBatch | +| `operations` | OperationId | Idempotency/request digest, resources, phase/outcome, accepted/finished revisions, retention | +| `zone_link_cursors` | peer Zone UID | Link epoch and last sent/acked/received/applied revisions | + +Keys use versioned length-prefixed binary tuples, never delimiter-joined +caller strings. Dynamic spec/status is canonical JSON validated against the +exact signed schema before storage. Envelope/index/operation/change values use +one versioned deterministic encoding owned by d2b-contracts. + +Unknown table/encoding/schema versions fail closed. + +## Async storage adapter + +redb is synchronous. The Zone runtime exposes only async resource APIs. + +- one bounded fair async write queue feeds one dedicated blocking store actor; +- read requests execute as short-lived blocking MVCC read transactions through + a bounded adapter pool; +- async executor threads never call blocking redb/filesystem APIs; +- read transactions cannot survive an await or watch lifetime; +- per-principal/controller fair admission prevents one caller monopolizing the + writer; +- queue saturation returns typed backpressure before opening a transaction. + +The writer may perform bounded group commit. It takes a small immediately +available batch from the fair queue without sleeping/debouncing: + +- preserves per-principal fairness and per-resource order; +- validates each mutation/result independently in one write transaction; +- includes only non-conflicting/dependency-compatible mutations; +- assigns one Zone revision and ordered per-mutation ordinals; +- performs one crash-safe commit/fsync; +- returns each caller its own success/conflict/error; +- emits ordered ChangeBatch entries and controller hints after commit. + +A mutation that depends on/conflicts with another queued mutation is ordered +explicitly or committed separately. Group commit never silently changes atomic +batch semantics or shares authorization outcomes. + +## Write transaction + +For every mutation/bounded group: + +1. authenticate/admit request before queueing; +2. begin one redb write transaction; +3. recheck policy/API/controller generations; +4. resolve target refs/UIDs and expected revisions; +5. validate ResourceType schema, owner graph, finalizers, quotas, and controller + ownership; +6. reject conflicts with no mutation; +7. update resources plus every affected index; +8. update durable operation/idempotency state; +9. allocate exactly `current_revision + 1`; +10. append one ordered ChangeBatch; +11. update store metadata; +12. commit with the selected crash-safe redb durability; +13. only after commit, swap in-memory indexes and push matching watch/reconcile + events directly to d2b-bus. + +No success, status, watch event, reconcile hint, or effect starts before durable +commit returns. + +Two callers using the same expected resource revision cannot both succeed. The +first valid durable commit wins; the other gets `resource-conflict` with the +current revision and may re-read/retry. + +## Revision model + +Zone revision: + +- is a monotonically increasing u64 inside one Zone; +- increments once per successful write transaction; +- is not wall time or cross-Zone causal order; +- orders changes within a ChangeBatch by bounded ordinal. + +Resource metadata.revision is the Zone revision of its latest change. List +returns a consistent MVCC snapshot plus snapshot revision. + +## ChangeBatch + +Each event contains only: + +- revision and ordinal; +- ResourceType/name/UID; +- event `Created|SpecUpdated|StatusUpdated|MetadataUpdated|DeleteRequested|Deleted`; +- old/new generation where applicable; +- current ownerRef/owner UID; +- payload digest; +- complete bounded ResourceEnvelope when watch authorization permits; +- operation/correlation IDs. + +No secret, credential byte, terminal data, raw Provider state, pidfd, host path, +or process argv/environment enters revision_log. + +## Watches + +Watch is application-owned because redb has no native changefeed. + +1. Caller supplies exact authorized ResourceTypes/filters and `afterRevision`. +2. Service replays matching revision_log entries. +3. Under the watch coordinator it registers live delivery and rechecks the + high-water revision, preventing a replay/live gap. +4. Live committed changes are pushed immediately through d2b-bus named streams. +5. Caller acknowledges fully processed revisions. +6. Disconnect resumes from the last acknowledged revision. + +There is no fixed polling, debounce, or compaction-tick delivery delay. + +The log is bounded by bytes, count, and age. Slow clients cannot pin it forever. +Compaction advances a durable floor and deletes old batches in bounded write +transactions. A cursor below the floor receives `revision-expired` plus current +revision and must list/re-watch. + +## Owner triggers + +Every child mutation updates owner_index/ChangeBatch in the same transaction. +After commit, the dispatcher emits `owned-resource-changed` for the singular +owner with child ref/UID/revision/event. + +- repeated child changes coalesce only while owner is queued/running; +- child status-only updates still trigger the owner; +- propagation follows the validated acyclic owner chain; +- strict depth/work budgets prevent amplification; +- parent relists owner_index and reasserts its complete child set. + +## Deletion + +Delete flow: + +1. set metadata.deletionRequestedAt and trigger controller/finalizers; +2. reconcile children/finalizers child-first; +3. final transaction creates a `Deleted` change event, removes resource/type/ + owner/controller indexes, and commits; +4. GET returns not found immediately after commit. + +No resource tombstone is retained. revision_log is the deletion history. + +## EphemeralProcess cleanup + +Terminal EphemeralProcess status includes completedAt and cleanupEligibleAt. + +- Succeeded defaults successfulTtl to `1h`. +- Failed defaults failedTtl to `24h`. +- Pending/Ready/Unknown/Degraded never age out. +- explicit bounds are enforced by schema/policy; +- finalizers and incident holds block cleanup; +- cleanup uses expected revision and the normal delete transaction; +- owner deletion/reconcile notification is preserved. + +## Backup, restore, and physical schema upgrade + +Backup uses a bounded consistent logical read snapshot containing: + +- store identity/schema and revision boundary; +- API schemas/catalog; +- resources; +- operations; +- Zone-link cursors; +- index checksums/rebuild inputs. + +An open database file is never copied unless the pinned redb version explicitly +documents that operation as crash-safe. + +Restore/upgrade: + +1. stop Zone mutation admission; +2. validate source identity/digest/schema; +3. build a staged new redb database; +4. validate resources, refs, owners, indexes, revisions, operations; +5. fsync staged file and parent; +6. atomically publish while retaining prior file for the bounded rollback + window; +7. reopen/rebuild indexes before readiness. + +Corruption or ambiguous publication quarantines the Zone resource plane. It +does not create a fresh store or claim partial success. + +## Performance contract + +On the pinned reference host/release profile: + +| Metric | Hard target | +| --- | --- | +| Normal readiness | <=500 ms | +| Aggregate Zone resource service/store + fixed system-core and system-minijail controllers idle RSS | <=64 MiB | +| p95 local Get/bounded List | <=2 ms | +| p95 crash-safe single-resource mutation | <=10 ms | +| p95 durable commit → matching controller handler start | <=5 ms | +| p95 ready Process commit → launch-attempt start | <=20 ms | + +Benchmark fixtures include: + +- empty store; +- 10,000 resources; +- 100 live watches; +- 1/10/100 concurrently ready Process resources; +- expected-revision conflict storm; +- owner-trigger fan-in/chain; +- revision compaction; +- forced crash at every commit boundary; +- backup/restore/internal schema upgrade; +- repeated open/close and long-reader rejection. + +Failure to meet a hard target changes the Proposed design. Durability, +authorization, or audit cannot be weakened to pass. + +## Current-code fit + +| Item | Treatment | +| --- | --- | +| Current anchor | No generic store. Reuse `d2b-core/src/storage.rs`, `sync.rs`; daemon snapshot/operation records; `d2b-realm-router` idempotency; broker fd/path safety | +| Evidence class | Current storage/locks/ledgers are mixed reachable/generated; redb store is ADR-only | +| Behavior retained | Single repair owner, no-follow/fd-relative safety, atomic rename/fsync, OFD locks, bounded records, pidfd non-persistence, idempotency/quarantine | +| Required delta | Entire redb schema, store actor, revisions, indexes, watches, conflicts, backup/upgrade | +| Reuse path | Extract exact storage/atomic/idempotency validators named below; redb only supplies ACID B-trees | +| Replacement/deletion | No existing state file/ledger is removed until its owning resource/operation migration lands | +| Feasibility proof | Disposable redb crash/performance/watch/backup spike with exact pinned version | +| Future owner | Work items below | + +## Implementation work items + +### ADR046-store-001 + +| Field | Value | +| --- | --- | +| Dependency/owner | W0; store integrator | +| Current source | `packages/d2b-core/src/storage.rs`, `sync.rs`; `packages/d2bd/src/supervisor/state.rs`, `daemon_audit.rs`; `d2b-realm-router/src/lib.rs` | +| Reuse action | extract and adapt | +| Destination | `packages/d2b-resource-store/src/lib.rs`, `packages/d2b-resource-store-redb/src/lib.rs`, `schema.rs`, `keys.rs`, `transaction.rs` | +| Detailed design | redb tables/encodings, fd backend, store identity, fair actor, MVCC reads, atomic indexes/revisions/operations/conflicts | +| Integration | Zone runtime owns store; resource API is sole caller | +| Data migration | Full reset; logical backup only for v3 stores | +| Validation | Unit/property/fault tests and hard benchmark | +| Removal proof | Existing ledgers removed only by owning future work items | + +### ADR046-store-002 + +| Field | Value | +| --- | --- | +| Dependency/owner | ADR046-store-001; watch/reconciliation integrator | +| Current source | `packages/d2b-realm-core/src/mux.rs`, `d2b-realm-router/src/mux_session.rs`, `route_engine.rs` | +| Reuse action | adapt | +| Destination | `packages/d2b-resource-store-redb/src/revision_log.rs`, `packages/d2b-resource-api/src/watch.rs` | +| Detailed design | replay/live no-gap watch, cursors, owner hints, compaction floor, expired relist | +| Integration | d2b-bus named streams; controller toolkit | +| Data migration | None | +| Validation | deterministic watch/compaction/disconnect/fan-in tests | +| Removal proof | Not applicable | + +### ADR046-store-003 + +| Field | Value | +| --- | --- | +| Dependency/owner | ADR046-store-001; storage/broker integrator | +| Current source | `nixos-modules/storage-json.nix`, `packages/d2b-priv-broker/src/ops/storage_contract.rs`, existing marker/ownership tests | +| Reuse action | adapt | +| Destination | `packages/d2b-resource-store-redb/src/backup.rs`, `migration.rs`; generated v3 storage row | +| Detailed design | fd-backed provision/open, marker identity, logical backup, staged restore/upgrade, corruption quarantine | +| Integration | Broker/Host/Guest storage owner passes File to Zone runtime | +| Data migration | Destructive v3 bootstrap; v3-to-v3 logical restore | +| Validation | marker replacement, crash publication, backup/restore/upgrade tests | +| Removal proof | Not applicable | diff --git a/docs/specs/ADR-046-terminology-and-identities.md b/docs/specs/ADR-046-terminology-and-identities.md new file mode 100644 index 000000000..558838df3 --- /dev/null +++ b/docs/specs/ADR-046-terminology-and-identities.md @@ -0,0 +1,300 @@ +# ADR 0046 terminology and identities + +| Field | Value | +| --- | --- | +| Spec ID | `ADR-046-terminology-and-identities` | +| Parent | ADR 0046 | +| Status | Proposed | +| Version | 1 | +| Baseline | `b5ddbed67867d9244bf33390868101bd9b053e49` | +| Normative | Yes | +| Owners | `d2b-contracts`, Zone runtime, Nix resource compiler | +| Depends on | `ADR-046-decision-register` | +| Supersedes | Public Realm terminology selected by ADR 0043 for d2b v3 | + +## Purpose + +This spec defines the names, identities, references, and scoping rules used by +every ADR 0046 specification. + +## Terms + +### Zone + +A Zone is the d2b 3.0 resource, policy, authorization, routing, state, and audit +boundary. Every resource belongs to exactly one Zone and is stored in that +Zone's redb database. + +Every Zone store contains exactly one authoritative: + +```text +Zone/ +``` + +The resource's metadata.zone equals ``. A parent represents a child +with a parent-local `ZoneLink/` resource. Parent access uses the child +Zone API; resources and ordinary refs are not copied across Zones. + +`Realm` remains current v3 baseline terminology and migration evidence. New d2b +3.0 public schemas, CLI, APIs, errors, and docs use `Zone`. + +### ResourceType and ResourceSpec + +A ResourceType is a Zone-bound schema/controller contract. A ResourceSpec is +one resource's desired-state object under that type. + +The public envelope field is `type`. ADR 0046 does not use Kubernetes +ResourceKind/kind terminology. + +Standard ResourceTypes use a short Zone-unique name: + +```text +Host +Guest +Process +Volume +``` + +Vendor ResourceTypes use a qualified name: + +```text +acme.io.Widget +``` + +ResourceApiBinding rejects type-name collisions. A type cannot be selected by +an ambiguous short name. + +### Resource reference + +Every field ending in `Ref` contains: + +```text +/ +``` + +Examples: + +```text +Zone/dev +Provider/system-core +Host/host-system +Process/wayland-proxy +User/alice +Volume/work-state +``` + +Rules: + +- refs resolve only in the caller/resource's Zone; +- ResourceType and name are both required; +- `resource_name` matches `^[a-z][a-z0-9-]*$`; +- the serialized form has no scheme, Zone prefix, query, fragment, relative + segment, or implicit default type; +- a plain enum/inline value never has a `Ref` suffix; +- a ref resolves both canonical type/name and the target's immutable UID; +- a deleted/recreated object with the same type/name has a different UID and + does not silently inherit old ownership/operation state. + +Cross-Zone ResourceRefs do not exist in the initial contract. A future need is +a decision-required architecture change. + +### Provider + +A Provider is installed as: + +```text +Provider/ +``` + +Its resource binds exact package/config/schema/controller/service/process/ +state/trust generations and status. A providerRef cannot resolve merely because +a package exists in the Nix store or catalog; the Provider resource must exist +and be Ready in the Zone. + +One Provider maps to one independently buildable crate/package. It may contain +several separately sandboxed process binaries. + +### Host + +A Host is a physical/local host execution, policy, and budget parent: + +```text +Host/ +``` + +Provider/system-core reconciles Host and local User. A Zone may define several +Hosts for separate system/user policy and budgets. + +### Guest + +A Guest is a non-host VM, sandbox, cloud, or remote execution parent: + +```text +Guest/ +``` + +Each Guest selects an installed runtime Provider such as Cloud Hypervisor, +QEMU, ACA, or Azure VM. + +### ExecutionPolicy + +Host and Guest share: + +- `providerRef`; +- `defaultDomain: system|user`; +- `allowedDomains`; +- `defaultUserRef` when user is the default. +- budgets; +- Volume/Network/Device attachment defaults. + +### Process domain + +Process and EphemeralProcess placement consists of: + +- required `executionRef`; +- optional `domain: system|user`, defaulting from the referenced Host/Guest; +- `userRef` when user domain does not inherit its default. + +executionRef must resolve to Host or Guest. Remote/nested are Guest Provider/ +Zone properties, not Process domains or duplicate Process ResourceTypes. + +### Process implementation + +Process and EphemeralProcess use `providerRef` to select an installed +implementation such as: + +```text +Provider/system-systemd +Provider/system-minijail +``` + +Both implement one common execution schema and mandatory local pidfd +conformance. The pidfd is local ephemeral controller authority, not a resource +or ref. + +### AuthenticatedSubjectContext + +The shared identity/authorization seam is: + +```text +AuthenticatedSubjectContext { + subjectRef + subjectUid + zoneRef + evidenceClass + executionRef? + providerRef? + processRef? + controllerGeneration? + providerGeneration? + sessionPurpose + service + schemaFingerprint + transportBinding + reconnectGeneration + transcriptHash +} +``` + +This spec owns the field contract. ComponentSession owns mapping trusted +Unix/Noise/bootstrap/vsock/enrollment evidence into it. The resource API and +d2b-bus consume it when building native authorization attributes. Peers cannot +self-assert or mutate it. + +### Owner + +Each resource has zero or one `metadata.ownerRef`. It is same-Zone and resolves +to canonical ref plus immutable UID. + +Ownership means: + +- a child mutation triggers owner reconciliation; +- owner deletion orders child finalization first; +- owner cycles fail at commit; +- unrelated dependencies use ordinary typed refs, not ownerRef. + +### Generation, revision, and UID + +- `metadata.uid` is immutable store-generated identity. +- `metadata.generation` starts at 1 and increments only on spec change. +- `metadata.revision` is the opaque Zone-local optimistic concurrency/watch + token of the resource's latest committed mutation. +- `status.observedGeneration` is the latest spec generation the controller has + observed and accounted for. + +None is a timestamp. Human-readable generation names are not part of the common +contract. + +### Time + +Persistent datetimes are RFC 3339 UTC with bounded precision. Wall time never +extends an already-admitted monotonic deadline. Common metadata/status times +are described in +[`ADR-046-resource-object-model`](ADR-046-resource-object-model.md). + +## Canonical process identities + +Fixed Zone bootstrap process: + +```text +z-@ +``` + +System-domain Host/Guest Process: + +```text +s-@ +``` + +User-domain Host/Guest Process: + +```text +u--@ +``` + +The logical name is diagnostic only. Adoption also verifies Provider, +component/template, executable/config/sandbox generations, executionRef/domain, +cgroup/scope, and provider-specific process identity. + +## Current-code fit + +| Item | Treatment | +| --- | --- | +| Current anchor | `d2b-realm-core/src/ids.rs`, `realm.rs`, `target.rs`, `workload.rs`; `nixos-modules/options-realms*.nix`; `index.nix` | +| Evidence class | Current Realm/Workload IDs are implemented-and-reachable; Zone/Host/Guest/ResourceRef/Provider resources are ADR-only | +| Behavior retained | Bounded fail-closed IDs, canonical target parsing, opaque token redaction, stable current Workload identity | +| Required delta | Zone term/type, universal ResourceRef, UID/generation/revision, Host/Guest split, Process domains | +| Reuse path | Adapt current ID validators/serde/redaction; map current Workload/Realm only where evidence says reachable | +| Replacement/deletion | Realm public types/options remain until the v3 cutover work item supplies Zone successors | +| Feasibility proof | Golden ref/ID vectors shared by Rust/Nix/other SDKs; collision and UID-recreate tests | +| Future owner | Work items below | + +## Implementation work items + +### ADR046-identities-001 + +| Field | Value | +| --- | --- | +| Dependency/owner | W0 shared contract root; `d2b-contracts` | +| Current source | `packages/d2b-realm-core/src/ids.rs`, `realm.rs`, `target.rs`, `workload.rs` | +| Reuse action | extract and adapt | +| Destination | `packages/d2b-contracts/src/v3/identity.rs`, `packages/d2b-contracts/src/v3/resource_ref.rs` | +| Detailed design | Add ZoneId, ResourceTypeName, ResourceName, ResourceUid, ResourceRef, generation/revision newtypes, exact parsing/serde/Debug/redaction, and golden vectors | +| Integration | Resource API/store/controllers/SDK/Nix import only these canonical types | +| Data migration | Destructive d2b 3.0 reset; no RealmRef parser compatibility | +| Validation | Rust property/vector tests; pure-Nix vector parity; malformed/collision/UID-recreate tests | +| Removal proof | Old public Realm target types removed only after all v3 callers consume Zone/ResourceRef | + +### ADR046-identities-002 + +| Field | Value | +| --- | --- | +| Dependency/owner | ADR046-identities-001; Nix integrator | +| Current source | `nixos-modules/options-realms.nix`, `options-realms-workloads.nix`, `index.nix` | +| Reuse action | adapt | +| Destination | `nixos-modules/options-zones.nix`, `nixos-modules/resources.nix`, `nixos-modules/index.nix` | +| Detailed design | Validate Zone names, ResourceTypes/names/refs, shared Host/Guest ExecutionPolicy, and canonical sorted resource identities | +| Integration | Nix resource objects serialize exactly the Rust contract | +| Data migration | Full reset and new Zone declarations | +| Validation | nix-unit vectors and rendered contract tests | +| Removal proof | Realm-facing declarations removed only in the reset/purge wave | diff --git a/docs/specs/README.md b/docs/specs/README.md new file mode 100644 index 000000000..d717be3ee --- /dev/null +++ b/docs/specs/README.md @@ -0,0 +1,189 @@ +# ADR specification sets + +The files in this directory are focused normative specifications attached to an +architecture decision record. They keep implementation-level contracts +reviewable without turning the parent ADR into one monolithic document. + +## ADR 0046 set + +Every ADR 0046 specification is named: + +```text +ADR-046-.md +``` + +The parent decision is +[`docs/adr/0046-d2b-3-provider-control-plane.md`](../adr/0046-d2b-3-provider-control-plane.md). +The parent and every manifest-listed spec form one atomic normative set: + +- all files move from `Proposed` to `Accepted` together; +- a content change to any member invalidates validation and panel evidence for + the set; +- no spec may silently override another spec; +- cross-spec dependencies must be acyclic; +- one spec owns each serialized contract, state machine, ResourceType, + controller, Provider dossier, process model, and security invariant. + +`ADR-046-spec-set.json` and `ADR-046-work-items.json` are generated indexes. +They bind the exact member files, versions, statuses, dependency edges, content +digests, and implementation work items. They are generated only after the +initial member set exists. + +## Required metadata + +Each spec starts with this table: + +| Field | Meaning | +| --- | --- | +| Spec ID | Stable `ADR-046-` identifier | +| Parent | `ADR 0046` | +| Status | `Proposed` or `Accepted` | +| Version | Monotonic integer | +| Baseline | Exact v3 commit analyzed | +| Normative | `Yes` | +| Owners | Contract and future implementation owners | +| Depends on | Exact ADR 0046 spec IDs | +| Supersedes | Prior spec/version, if any | + +## Evidence and current-code fit + +Current behavior is cited by exact v3 file, symbol, and baseline commit. Every +claim is classified as: + +- `implemented-and-reachable`; +- `implemented-but-unwired`; +- `generated-or-eval-contract`; +- `test-only-or-preview`; +- `ADR-only`; +- `unknown-requires-spike`. + +The protected pre-ADR45 v3 tree remains the sole current-state and ancestry +baseline. Main may be used freely as an implementation reuse source. A work +item that borrows from main must separately record: + +- exact main commit, file, symbol, and tests; +- why that code is selected; +- which behavior is copied unchanged versus adapted; +- exact v3 destination and integration path; +- which surrounding ADR 0045 assumptions are deliberately excluded. + +Borrowed main code never changes a v3 claim from ADR-only/unwired to +implemented-and-reachable. + +Every design section ends with a current-code fit table: + +| Item | Required content | +| --- | --- | +| Current anchor | Exact current source/artifact and evidence class | +| Behavior retained | Tested semantics preserved | +| Required delta | Behavior absent from v3 | +| Reuse path | Exact code/symbol copied, extracted, adapted, or wrapped | +| Replacement/deletion | Old owner removed only after a live successor | +| Feasibility proof | Existing proof or pre-acceptance spike | +| Future owner | Exact work item/crate/component | + +## Resource terminology + +A **resource** belongs to exactly one Zone. + +A canonical resource reference is: + +```text +/ +``` + +Examples: + +```text +Zone/dev +Provider/system-core +Provider/system-systemd +Host/host-system +Guest/dev-vm +Process/wayland-proxy +User/alice +``` + +Rules: + +- every field ending in `Ref` contains one canonical same-Zone resource + reference; +- a plain enum or inline value never uses a `Ref` suffix; +- standard ResourceTypes use Zone-unique short names; +- vendor ResourceTypes use a qualified name such as `acme.io.Widget`; +- API binding rejects ResourceType collisions; +- cross-Zone resource references do not exist unless a later reviewed special + case explicitly adds one. + +Every resource has zero or one `ownerRef`. Any committed child mutation +triggers reconciliation of that owner through the reverse owner index. + +ADR 0046 uses `ResourceType`, `ResourceTypeSchema`, and `ResourceSpec` +terminology. It does not expose Kubernetes-style ResourceKind/kind vocabulary. + +Every resource has all three top-level objects: + +- `metadata`, containing common name, Zone, UID, generation, revision, + ownerRef/finalizer/deletion, and creation/update timestamps; +- `spec`, present as `{}` even when the ResourceType has no desired fields; +- `status`, present from creation and writable only through the authorized + status subresource. + +Common status contains numeric `observedGeneration`, phase +`Pending|Ready|Succeeded|Degraded|Failed|Deleted|Unknown`, bounded +conditions, RFC 3339 transition/reconcile/start/completion datetimes, and the +latest outcome with stable code, optional exitCode, detailed bounded/redacted +message, and retryability. ResourceType-specific status extends that shape. +Prior status is available from the Zone revision log until compaction rather +than retained in an unbounded per-resource history. + +After finalizers complete, deletion emits one `phase=Deleted` revision event +and removes the resource immediately. The revision log is the only deletion +history. + +## Decision-required protocol + +Evidence may determine one answer. If it does not, the author stops the affected +spec and records `decision-required` rather than selecting an implicit default. +The decision entry contains: + +- one focused question; +- the viable options and consequences; +- current-code evidence; +- a recommendation; +- blocked specs and work items. + +The resolved choice is recorded in +[`ADR-046-decision-register.md`](ADR-046-decision-register.md) and propagated to +all affected specs. The set cannot enter pre-panel review with unresolved +decisions, placeholders, or implementation-defined behavior. + +## Implementation work items + +Each spec contains an **Implementation work items** section. Every item has: + +| Field | Requirement | +| --- | --- | +| Work item ID | Stable `ADR046--` ID | +| Dependency/owner | Prerequisites, future wave, crate/component, shared owner | +| Current source | Exact v3 paths, symbols, call sites, artifacts, and tests | +| Reuse source | Optional exact main commit/paths/symbols/tests used for copy/adaptation | +| Reuse action | `copy-unchanged`, `extract`, `adapt`, `wrap`, `replace`, or `delete-after-cutover` | +| Destination | Exact future crate/module/file and binary targets | +| Detailed design | Types, APIs, algorithms, state, limits, errors, security | +| Integration | Complete producer-to-consumer call/resource/process chain | +| Data migration | State/config/artifact/reset behavior | +| Validation | Exact test files/selectors and measurable acceptance | +| Removal proof | Live successor path and tests required before deletion | + +Broad items such as “update d2bd” are invalid. A work item that copies current +behavior names the exact source symbols, the tests that move with them, the +adaptations required, and the exact point where callers switch. + +## Parallel authoring + +After the shared foundation is stable, file-disjoint specs are authored in +parallel. An agent owns only its assigned spec files and session evidence. +Shared parent/index/manifest/changelog/instruction files remain integrator-owned. +Agents return completed specs or `decision-required`; they do not make +cross-cutting choices or write implementation code. From 95aa75e873207b8c4ae9e8cf8a3625f82c706aeb Mon Sep 17 00:00:00 2001 From: John Vicondoa Date: Wed, 22 Jul 2026 22:18:43 -0700 Subject: [PATCH 002/115] docs: synchronize ADR 0046 foundation decisions Record the reviewed Host/Guest model, bootstrap authority, sensitive credential channel, minimal resource and Provider catalogs, Nix cleanup/artifact rules, and fixed performance/security bounds before integrating parallel specs. --- docs/adr/0046-d2b-3-provider-control-plane.md | 18 +++++++ .../specs/ADR-046-componentsession-and-bus.md | 23 ++++++++ docs/specs/ADR-046-core-controllers.md | 7 ++- docs/specs/ADR-046-decision-register.md | 24 ++++++++- .../ADR-046-primitive-resource-composition.md | 13 +++-- .../ADR-046-provider-model-and-packaging.md | 54 ++++++++++++++++--- docs/specs/ADR-046-resource-object-model.md | 30 +++++++++++ docs/specs/README.md | 37 +++++++++++++ 8 files changed, 191 insertions(+), 15 deletions(-) diff --git a/docs/adr/0046-d2b-3-provider-control-plane.md b/docs/adr/0046-d2b-3-provider-control-plane.md index b4f6b328e..d4045915b 100644 --- a/docs/adr/0046-d2b-3-provider-control-plane.md +++ b/docs/adr/0046-d2b-3-provider-control-plane.md @@ -29,6 +29,15 @@ dependencies, Nix modules, services, controllers, Providers, state stores, or reset behavior. Future implementation requires a separate request after the set is Accepted. +Every normative ResourceType/Provider spec defines its Nix authoring form, +canonical rendered ResourceSpec, and NixOS eval/build schema/reference +validation. Nix mirrors the ResourceSpec `spec` shape directly; only name, +Zone, and apiVersion are derived/defaulted, and status is controller-owned. Nix +emits an integrity-pinned per-Zone resource generation. +Removing a configured resource activates the new generation immediately and +requests asynchronous owner/finalizer-safe deletion, with visible Degraded +cleanup status; dynamic controller-owned resources are not broadly swept. + ## Context The v3 baseline has useful foundations: @@ -68,6 +77,9 @@ state, and audit unit. Every Zone owns: - one fixed core-controller process; - Zone-local Provider, Host, Guest, controller, policy, and ordinary resources. +Zone.spec is empty. Zone-wide ceilings and emergency controls are separate +Quota and EmergencyPolicy resources with their own controllers/status. + Every resource belongs to one Zone. A parent represents a child with a local `ZoneLink/` resource and accesses the child's resources through the child Zone API. Ordinary resource references never cross Zones. @@ -255,6 +267,12 @@ native Role/RoleBinding engine authorizes session connect, service invoke, stream open, and resource verbs. A handshake cannot self-assert roles; authorization leases bind policy revisions and revoke when RBAC changes. +Credential Providers may deliver raw token bytes only over a dedicated +end-to-end Noise_KK ComponentSession to a fully enrolled authorized consumer +Provider/component. Bus/Zone/relay intermediaries authorize and forward opaque +protected records without decrypting them. Tokens never enter resource +spec/status/store/revision/audit/telemetry, NN, or bootstrap sessions. + ### Primitive ResourceSpecs A primitive is a complete standard low-level ResourceSpec. The model is kept diff --git a/docs/specs/ADR-046-componentsession-and-bus.md b/docs/specs/ADR-046-componentsession-and-bus.md index 2dff2d004..81ef82f6d 100644 --- a/docs/specs/ADR-046-componentsession-and-bus.md +++ b/docs/specs/ADR-046-componentsession-and-bus.md @@ -262,6 +262,29 @@ It: ResourceClient always uses d2b-bus, even beside the Zone runtime. There is no direct-store shortcut. +## Sensitive credential delivery + +Credential resources/status/store/revision/audit/OTEL remain free of token +bytes. A Credential Provider may deliver a raw token only through a dedicated +end-to-end sensitive ComponentSession: + +- initiator/responder are fully enrolled Provider/component identities; +- Noise profile is KK; NN and IKpsk2 are forbidden; +- consumerRef may name Provider/; its signed component descriptor and + Role/RoleBinding resolve the exact receiving component/Process; +- the offer/prologue binds Credential ref/UID/generation, Credential Provider + and consumer Provider/component generations, audience/operation class, + route, schema, limits, expiry/deadline, and authorization revisions; +- d2b-bus/Zone/relay intermediaries authorize route establishment but forward + opaque protected records and cannot terminate/decrypt the inner session; +- token payload has a strict small bound, zeroizing buffers, redacted Debug, + replay-safe sequence, no logging/audit/metrics, and immediate close/zeroize; +- ambiguous delivery never becomes success and is not automatically replayed + outside the credential method's explicit idempotency contract. + +This is the only initial cross-process secret-byte channel. It grants no generic +raw HTTP, signing, endpoint, or credential forwarding authority. + ## Lifecycle Retain/adapt main's: diff --git a/docs/specs/ADR-046-core-controllers.md b/docs/specs/ADR-046-core-controllers.md index e03c43b98..465172762 100644 --- a/docs/specs/ADR-046-core-controllers.md +++ b/docs/specs/ADR-046-core-controllers.md @@ -54,8 +54,11 @@ Algorithm: 3. stage inactive resources in bounded transactions; 4. atomically activate one configuration revision; 5. trigger affected resources/providers/controllers; -6. retain prior generation for bounded rollback/drain; -7. prune only after ownership/finalizer checks. +6. request asynchronous Delete for prior configuration-owned resources omitted + from the new canonical set; +7. report Degraded/pending-cleanup without blocking activation; +8. retain prior generation for bounded rollback/drain; +9. prune only after ownership/finalizer checks. It does not overwrite controller-created children merely because root config changes. diff --git a/docs/specs/ADR-046-decision-register.md b/docs/specs/ADR-046-decision-register.md index d8982bdde..4e6315900 100644 --- a/docs/specs/ADR-046-decision-register.md +++ b/docs/specs/ADR-046-decision-register.md @@ -54,7 +54,7 @@ review metadata, or implementation status. | D032 | Replace File/Directory/ACL/FilesystemView ResourceTypes with one Volume ResourceType. | Volume has anchored relative layout entries, fine-grained owner/mode/access/default ACLs and lifecycle rules, named views, and same-Zone Host/Guest attachments. Process mounts select a volumeRef/view. | Volume, Process, storage migration | | D033 | Volume supports Host-to-Guest transports such as virtiofs. | The Volume source may use an explicitly authorized Host path under its Provider policy; attachments name target executionRef, transport, mount path, view/access, and settings. The controller may own a Host virtiofsd Process and reports per-attachment export/mount status. | Volume/Host/Guest/Process specs and Providers | | D034 | EphemeralProcess has outcome-specific terminal retention. | `successfulTtl` defaults to `1h`; `failedTtl` defaults to `24h`. TTL begins at status.completedAt and cleanup remains revision/finalizer/incident-hold safe. | EphemeralProcess, core cleanup controller, validation | -| D035 | Freeze the minimal standard ResourceType catalog. | Standard types are Zone, ZoneLink, Provider, Role, RoleBinding, Host, Guest, Process, EphemeralProcess, Volume, Network, Device, User, and Credential. Other behavior is inline or Provider-specific. | All resource/provider specs | +| D035 | Freeze the minimal standard ResourceType catalog. | Standard types are Zone, ZoneLink, Provider, Role, RoleBinding, Quota, EmergencyPolicy, Host, Guest, Process, EphemeralProcess, Volume, Network, Device, User, and Credential. Other behavior is inline or Provider-specific. | All resource/provider specs | | D036 | The fixed core-controller process is also the `Provider/system-core` controller. | It creates/reconciles the first Host and Users and is one of two fixed Provider bootstrap exceptions. | Core controllers, system-core Provider, process/bootstrap | | D037 | The universal status phase enum is `Pending`, `Ready`, `Succeeded`, `Degraded`, `Failed`, `Deleted`, or `Unknown`. | Conditions carry starting/deleting/retrying and other detail without multiplying common phases. | Resource object/API/store, all controllers | | D038 | Final deletion has no retained resource tombstone. | After finalizers complete, core emits a Deleted status revision and removes the resource immediately. revision_log is the only deletion history; GET returns not found. | Resource store/API/reconciliation | @@ -66,13 +66,33 @@ review metadata, or implementation status. | D045 | Freeze one initial Network Provider. | network-local owns shared local fabrics; Azure/ACA networking remains in their Guest Providers until independently shared networking is required. | Network/Provider dossier | | D046 | Freeze four Device Provider families. | device-tpm, device-usbip, device-security-key, and combined device-gpu (GPU/video) implement Device. Security-key owns unprivileged relay/frontend Processes; fixed broker only opens/passes hidraw. | Device/Provider dossiers | | D047 | Freeze five interaction Provider families. | display-wayland, audio-pipewire, clipboard-wayland, notification-desktop, and shell-terminal own semantic resources/processes. Exec uses EphemeralProcess directly. | Interaction/Provider dossiers | -| D048 | Freeze three Credential Provider families. | credential-secret-service, credential-entra, and credential-managed-identity retain secret/token bytes and expose opaque leases/typed operations. | Credential/Provider dossiers | +| D048 | Freeze three Credential Provider families. | credential-secret-service, credential-entra, and credential-managed-identity acquire/retain credentials and may deliver authorized token/signature bytes only through D055/D056 sensitive sessions. | Credential/Provider dossiers | | D049 | Freeze transport/observability/activation Providers and omit an orchestrator. | transport-unix, transport-vsock, transport-azure-relay, observability-otel, and activation-nixos are initial Providers. Ordinary controllers perform composition; there is no orchestrator-standard Provider. | Provider dossiers, routing/telemetry/activation | | D050 | Rename the non-host execution parent from Workload to Guest and add Host as a separate ResourceType. | Host excludes guests and is reconciled by system-core. Guest covers VM/sandbox/cloud/remote execution. Process/EphemeralProcess uses canonical executionRef to either Host or Guest; both share ExecutionPolicy. | All resource/provider/process/routing/Nix specs | | D051 | Provider/system-minijail is the second fixed non-resource Provider/controller bootstrap process. | The first Process controller cannot itself depend on a Process controller. The fixed minijail controller launches/reconciles later Process resources, including the system-systemd controller. | Process/bootstrap/system Providers | | D052 | Use compiled narrow bootstrap authorization before stored RBAC exists. | Only exact Provider/system-core and Provider/system-minijail subjects receive the closed initial recovery/config publication verbs. It is non-configurable and cannot be widened; normal stored Role/RoleBinding governs later work. | Resource API/authz/core startup/session | | D053 | Use bounded group commit in the single writer. | Independently validated non-conflicting queued mutations may share one redb transaction/fsync and Zone revision with ordered ordinals; each caller receives its own result. Conflicting/dependent writes remain ordered/separate. | Resource store/watches/performance | | D054 | Terminology/identity owns one shared AuthenticatedSubjectContext. | ComponentSession maps trusted transport/Noise/bootstrap evidence into it; d2b-bus/resource API consume it for Role/RoleBinding evaluation. No spec defines a parallel identity context. | Identity/session/bus/resource API | +| D055 | Credential Providers may deliver raw tokens or SignChallenge signatures to an authorized consumer Provider only over a dedicated end-to-end ComponentSession. | Sensitive bytes remain absent from resources/store/status/audit/telemetry. d2b-bus/Zone/relay intermediaries authorize then forward opaque protected records without decryption. Provider-level consumerRef is sufficient; its signed descriptor/RBAC selects the receiving component. | Credential/session/bus/security | +| D056 | Raw token delivery requires fully enrolled Noise_KK peers. | NN local and IKpsk2 bootstrap sessions cannot carry raw tokens. The session binds Credential/consumer Provider/component generations, audience/operation, deadline, route, schema, limits, and transcript. | Credential/session/enrollment | +| D057 | Every ResourceType/Provider spec includes Nix authoring, canonical rendering, and NixOS eval/build schema/reference validation. | Nix emits an integrity-pinned Zone generation. Removing a configuration-managed resource activates the new generation and deletes it asynchronously through owner/finalizers with visible Degraded cleanup status; controller-managed resources are not broadly swept. | All resource/provider/Nix/configuration specs | +| D058 | Nix authoring mirrors ResourceSpec directly. | Users set type, optional metadata.ownerRef/presentation metadata, and exact canonical spec. metadata.name/zone derive from the Nix attr path, apiVersion defaults, and status/UID/generation/revision/timestamps/finalizers/managedBy/configurationGeneration are omitted/read-only. | Every ResourceType/Provider/Nix spec | +| D059 | Every Provider crate has `src/`, `tests/`, `integration/`, and `README.md`. | src owns implementation/unit tests; tests owns hermetic Cargo/conformance tests; integration owns heavier Host/Guest/container scenarios; README documents complete Provider usage/contracts. Missing paths fail policy. | Provider model, every Provider dossier, validation | +| D060 | User resource name and OS username are separate. | metadata.name is the canonical Zone-local key; spec.osUsername is the actual bounded OS username resolved by system-core. | User/Process/Volume ACL/Nix specs | +| D061 | network-local reconciles runtime networks dynamically. | Closed broker operations create/delete bridges and apply state; mDNS is an owned Process; USBIP Processes remain device-usbip-owned. | Network/broker/device/Nix specs | +| D062 | Freeze Volume source/sharing/security defaults. | volume-local supports block-image and bounded tmpfs; requested quotas are hard/fail-if-unsupported; bounds are 1024 layout/64 views/64 attachments; virtiofs is single-writer by default with explicit supported shared-write; symlinks are relative/in-Volume only; typed User ACL refs and continuous repair apply. | Volume/Guest/Process specs | +| D063 | Freeze Device arbitration/status defaults. | First probe failure => Unknown, absent after 3; render-node may share, full/VFIO exclusive; Device attachment is desired state; security-key/frontend and other workers are Processes; conservative broker/FD limits apply. | Device/Process/broker specs | +| D064 | Freeze CLI context/loading/output/cutover. | Global --zone with host-command exemptions; local standard ResourceType validation and live vendor validation; lazy per-invocation Provider CLI; remove v2 aliases at cutover; non-TTY defaults JSON. | CLI/docs/tests | +| D065 | Full OTEL SDK/exporter runs only in optional Provider/observability-otel Process. | Mandatory Zone/core processes use lightweight bounded emitters; telemetry outage degrades/drops telemetry without blocking startup; audit remains authoritative. | Telemetry/Provider/process/performance | +| D066 | Freeze Zone/Nix trust and generation defaults. | ZoneLink transport providerRef is explicit/no default; built-in official signing root plus additional per-Zone roots; retain 3 prior generations by default, configurable 1–16. | ZoneLink/Provider trust/Nix/configuration | +| D067 | Unsafe-local successor isolation posture is not an OTEL dimension. | It remains explicit in Host status, CLI/UI, and authoritative audit only; no metric label/span attribute/log field. | Host/CLI/audit/telemetry | +| D068 | SignChallenge signatures use the same sensitive KK delivery channel as tokens. | Outer response carries only outcome metadata; signature bytes are end-to-end protected and opaque to intermediaries. | Credential/session/bus/security | +| D069 | Core-managed metadata.managedBy is `configuration`, `controller`, or `api`. | Configuration cleanup deletes only omitted configuration-managed resources; controller manages owned children; API-managed resources persist until explicit API deletion. | Resource object/API/Nix/cleanup | +| D070 | Nix derivations live in a separate named `d2b.artifacts` catalog. | Provider/Guest ResourceSpecs use plain artifactId/systemArtifactId fields. Nix builds/hashes closures and emits a private ID→type/digest/closure catalog; store paths never enter public resources. | Nix/Provider/Guest/package/trust specs | +| D071 | Zone.spec is empty; Zone-wide quota and emergency control are separate ResourceTypes. | Quota owns aggregate ceilings/usage and may be referenced by Host/Guest/Process; EmergencyPolicy owns disable scopes/actions/status. | Zone/Quota/EmergencyPolicy/core/Nix specs | +| D072 | ZoneLink uses explicit transportProviderRef plus signed settings/Credential refs. | It has no default/inferred transport. | ZoneLink/routing/Nix/transport Providers | +| D073 | Freeze Provider/RBAC bounds. | Provider: 8 controllers, 8 services, 32 worker templates, 16 ResourceTypes/controller. Role: 32 rules; rule max 16 types, 16 verbs, 64 names, 32 executionRefs. RoleBinding: 128 subjects. | Zone-control/Provider/RBAC schemas/tests | +| D074 | RoleBinding has no expiry field. | Revocation/lifecycle uses normal spec update or deletion, avoiding time-derived authorization state. | RoleBinding/authz/Nix/API | | D040 | ComponentSession and resource control use one authorization system. | Session authentication maps to a canonical resource subject; native Role/RoleBinding authorizes connect, invoke, stream, and resource verbs. Handshakes cannot self-assert roles; revision-bound leases revoke on policy change. | ComponentSession/bus/resource API/RBAC | ## Open decisions diff --git a/docs/specs/ADR-046-primitive-resource-composition.md b/docs/specs/ADR-046-primitive-resource-composition.md index 9391999ef..14a81a9c1 100644 --- a/docs/specs/ADR-046-primitive-resource-composition.md +++ b/docs/specs/ADR-046-primitive-resource-composition.md @@ -35,7 +35,9 @@ Core control: | ZoneLink | Parent/child delegation, transport, cursor/health | | Provider | Installed package/config/controllers/schemas/services/status | | Role | Bounded native RBAC rules | -| RoleBinding | Subjects to Role with narrowing/expiry | +| RoleBinding | Subjects to Role with narrowing; no time-based expiry | +| Quota | Zone-wide/shared aggregate ceilings and observed usage | +| EmergencyPolicy | Disable scopes/actions and emergency status | Standard execution/shared: @@ -61,7 +63,8 @@ Inline: - Host/Guest Provider-specific type/settings; - defaultDomain/allowedDomains/defaultUserRef; -- hierarchical CPU/memory/pids/fds/I/O/storage/network budgets; +- optional `quotaRef` plus inline requested CPU/memory/pids/fds/I/O/storage/ + network amounts; - network attachments; - device attachments; - Volume attachment defaults; @@ -165,7 +168,6 @@ executionRef: Host/host-system domain: system # optional; defaults from Host/Guest ExecutionPolicy userRef: null # required/inherited for user processClass: controller # controller | service | worker -packageRef: Provider/example # exact package artifact comes from owning Provider template: controller-main configRef: Volume/example-config mounts: @@ -181,7 +183,10 @@ endpoints: [] telemetry: { ... } ``` -No free-form executable, raw host path, numeric UID/GID, raw seccomp program, +The owning semantic Provider is metadata.ownerRef; its signed component/ +process template supplies the executable package/digest. `template` is a plain +bounded ID, not a ResourceRef. No free-form executable, raw host path, numeric +UID/GID, raw seccomp program, ambient capability list, caller-selected broker op, credential bytes, or arbitrary socket address is accepted. Package/template/provider schemas resolve those implementation details. diff --git a/docs/specs/ADR-046-provider-model-and-packaging.md b/docs/specs/ADR-046-provider-model-and-packaging.md index efde9d30c..1abb2138a 100644 --- a/docs/specs/ADR-046-provider-model-and-packaging.md +++ b/docs/specs/ADR-046-provider-model-and-packaging.md @@ -25,7 +25,9 @@ Provider resource in the same Zone. Provider spec contains: -- exact package/executable/manifest/config/schema/service digests; +- plain `artifactId` selecting a named Nix artifact-catalog entry; +- exact package/executable/manifest/config/schema/service digests resolved from + that private artifact entry; - publisher/signature/trust/conformance/provenance/SBOM identity; - support channel and compatibility range; - root configuration validated against signed JSON Schema; @@ -68,6 +70,28 @@ One Provider crate: - has one Nix package/conformance output; - has one `ADR-046-provider-.md` dossier. +Every Provider crate contains: + +```text +d2b-provider--/ + src/ + tests/ + integration/ + README.md +``` + +- `src/`: implementation, component binaries, internal modules, and colocated + unit tests; +- `tests/`: hermetic Cargo integration, ResourceType/controller conformance, + fault, redaction, schema, and fake-port tests; +- `integration/`: heavier container/Host/Guest/cross-process/provider-system + fixtures and scenarios invoked by existing repository test orchestration; +- `README.md`: Provider identity/config, ResourceTypes, controllers/services/ + workers/binaries, placement, dependencies/RBAC, security/state/telemetry, + build/test/integration commands, and standalone-repository consumption. + +Workspace policy rejects a Provider crate missing any of these paths. + This boundary must allow moving the crate to its own GitHub repository without splitting semantics or copying daemon internals. @@ -165,9 +189,11 @@ component graph defines deterministic projections: - ResourceRef/dependency bindings; - component schema digest. -Components cannot read sibling config. Secrets are Credential refs, not config -values. Root/component digests bind Provider resource, Process resources, -ComponentSessions, state, status, and audit. +Components cannot read sibling config. Secrets are Credential refs, not config values. A signed Provider component may +be selected as a raw-token consumer only through the Credential spec/RBAC and +the KK end-to-end sensitive ComponentSession contract. Root/component digests +bind Provider resource, Process resources, ComponentSessions, state, status, +and audit. ## Provider dependencies @@ -190,7 +216,17 @@ declared degraded behavior only. ## Package catalog -Nix compiles an offline sorted exact-digest catalog: +Nix authoring first declares derivations separately: + +```nix +d2b.artifacts.provider-wayland = { + package = inputs.wayland-provider.packages.${system}.default; + type = "provider"; +}; +``` + +The Provider ResourceSpec then uses `artifactId = "provider-wayland"`. Nix +compiles an offline sorted exact-digest catalog: - Provider/package/publisher/version; - package/executable/manifest/component/descriptor/config digests; @@ -205,6 +241,10 @@ Nix compiles an offline sorted exact-digest catalog: Selection is exact digest. No runtime marketplace, download, PATH scan, directory discovery, latest, or version-range solving. +Artifact is not a ResourceType; `artifactId` is a plain bounded ID, not a +ResourceRef. The private catalog may retain a Nix store path for activation, +but resource spec/status/audit never expose it. + ## Trust Production admission requires: @@ -379,8 +419,8 @@ special orchestrator Provider. | Dependency/owner | Provider contract; package/Nix integrator | | Current source | `packages/Cargo.toml`; `flake.nix`; `nixos-modules/host-daemon.nix`; current source package derivations | | Reuse action | adapt | -| Destination | one `packages/d2b-provider--/` per Provider; generic Nix Provider package/catalog emitter | -| Detailed design | Split current combined/composition crates; exact outputs/manifests/conformance | +| Destination | one `packages/d2b-provider--/` per Provider with mandatory src/, tests/, integration/, README.md; generic Nix Provider package/catalog emitter | +| Detailed design | Split current combined/composition crates; exact outputs/manifests/conformance/layout/documentation | | Integration | Provider package installed/registered per Zone | | Data migration | No package compatibility path | | Validation | Workspace naming/dependency/output/dossier/catalog parity policy | diff --git a/docs/specs/ADR-046-resource-object-model.md b/docs/specs/ADR-046-resource-object-model.md index b68f7ca7e..c7203512c 100644 --- a/docs/specs/ADR-046-resource-object-model.md +++ b/docs/specs/ADR-046-resource-object-model.md @@ -60,6 +60,19 @@ fields uses `spec: {}`. | `createdAt` | Core-set RFC 3339 UTC timestamp | | `updatedAt` | Core-set RFC 3339 UTC timestamp on every mutation | +Internal non-user-writable management metadata also records: + +- `managedBy = configuration|controller|api`; +- optional `configurationGeneration` for Nix-owned roots; +- controller/Provider generation for dynamic children. + +These fields are authority inputs set by the configuration/resource service, +not labels/annotations or caller-selected spec values. + +`api` is assigned to resources created directly by an authorized API client; +they persist until explicit API deletion and are never swept by configuration +generation cleanup. + Labels/annotations are optional bounded presentation metadata. They never select authorization, provider/controller ownership, path, process identity, or implicit relationships. A ResourceType may declare a closed set of indexed @@ -180,6 +193,21 @@ status-only event would otherwise be suppressed. No retained resource tombstone exists. +### Removed Nix configuration + +After a newly validated Zone configuration generation activates, core diffs its +canonical configured resource set against the prior active set. Every prior +`managedBy=configuration` resource omitted from the new set receives normal +asynchronous Delete: + +- activation succeeds without waiting for cleanup; +- generation status becomes Degraded/pending-cleanup while removals remain; +- owner children/finalizers complete through normal reconciliation; +- controller-created resources are never swept merely because absent from Nix; +- prior generation remains retained until cleanup/rollback policy permits + pruning; +- failures are visible/audited and never reported as deleted. + ## Minimal standard ResourceType catalog Core control: @@ -189,6 +217,8 @@ Core control: - Provider; - Role; - RoleBinding. +- Quota; +- EmergencyPolicy. Standard execution/shared: diff --git a/docs/specs/README.md b/docs/specs/README.md index d717be3ee..fc0cd1ff7 100644 --- a/docs/specs/README.md +++ b/docs/specs/README.md @@ -82,6 +82,43 @@ Every design section ends with a current-code fit table: | Feasibility proof | Existing proof or pre-acceptance spike | | Future owner | Exact work item/crate/component | +Every ResourceType/Provider spec also contains a **Nix authoring and +configuration cleanup** section: + +- user-facing direct schema mirror: + `d2b.zones..resources. = { type = "..."; spec = { ... }; };`; +- exact canonical rendered ResourceSpec JSON; +- generated/committed ResourceTypeSchema and Provider settings schema; +- Nix eval/build validation of fields, bounds, ResourceRefs, Provider presence, + Host/Guest/domain policy, ownership, conflicts, and schema fingerprints; +- canonical sorted integrity-pinned per-Zone resource bundle/generation; +- activation/publication path; +- removed configured-resource cleanup, status, audit, and tests. + +Nix does not define a second resource vocabulary. `spec` field names, nesting, +types, defaults, bounds, and Provider extensions match the canonical +ResourceTypeSchema directly. metadata.name, metadata.zone, and apiVersion are +derived/defaulted. Users may author only metadata.ownerRef and bounded +presentation labels/annotations; status, UID, generation, revision, timestamps, +finalizers, managedBy, and configurationGeneration are core/controller-managed. +managedBy is `configuration`, `controller`, or `api`; API-managed resources +persist until explicitly deleted. + +Nix derivations are the one value class that cannot appear in JSON +ResourceSpecs. They live in a separate named `d2b.artifacts.` catalog. +ResourceSpecs use plain `artifactId`/`systemArtifactId` fields. Nix builds and +hashes each derivation and emits a private integrity-pinned ID-to-digest/closure +catalog; store paths never enter public spec/status/audit. + +After a new generation activates, a previously Nix-owned resource absent from +the new configured set enters normal asynchronous finalizer-safe deletion. The +generation reports Degraded/pending-cleanup until deletion completes. Activation +does not block. Controller-created resources are not deleted merely because +they are absent from Nix; their owner controller governs them. + +Every Provider dossier requires its crate's `src/`, `tests/`, `integration/`, +and `README.md` layout and assigns exact work items/files to each path. + ## Resource terminology A **resource** belongs to exactly one Zone. From 20e3be55074a473edf0ef2c15bff04af628d65c1 Mon Sep 17 00:00:00 2001 From: John Vicondoa Date: Wed, 22 Jul 2026 23:09:29 -0700 Subject: [PATCH 003/115] docs: define d2b 3.0 control and operator surfaces Specify Zone-local control resources, direct Nix ResourceSpec authoring and artifact resolution, configuration cleanup, and the complete CLI contract on top of the ADR 0046 foundation. --- docs/specs/ADR-046-cli-and-operations.md | 2231 +++++++++++ docs/specs/ADR-046-nix-configuration.md | 2324 +++++++++++ docs/specs/ADR-046-resources-zone-control.md | 3725 ++++++++++++++++++ 3 files changed, 8280 insertions(+) create mode 100644 docs/specs/ADR-046-cli-and-operations.md create mode 100644 docs/specs/ADR-046-nix-configuration.md create mode 100644 docs/specs/ADR-046-resources-zone-control.md diff --git a/docs/specs/ADR-046-cli-and-operations.md b/docs/specs/ADR-046-cli-and-operations.md new file mode 100644 index 000000000..e83657883 --- /dev/null +++ b/docs/specs/ADR-046-cli-and-operations.md @@ -0,0 +1,2231 @@ +# ADR 0046 CLI and operations + +| Field | Value | +| --- | --- | +| Spec ID | `ADR-046-cli-and-operations` | +| Parent | ADR 0046 | +| Status | Proposed | +| Version | 1 | +| Baseline | `b5ddbed67867d9244bf33390868101bd9b053e49` | +| Normative | Yes | +| Owners | `packages/d2b` binary crate, Zone runtime client layer | +| Depends on | `ADR-046-terminology-and-identities`, `ADR-046-resource-object-model`, `ADR-046-resource-api-and-authorization`, `ADR-046-provider-model-and-packaging`, `ADR-046-components-processes-and-sandbox`, `ADR-046-componentsession-and-bus` | +| Supersedes | Current v3 `d2b` CLI contract (`packages/d2b/src/lib.rs` at baseline) | + +## Purpose + +This spec defines every public-facing CLI command, argument, output format, exit +code, stream/TTY contract, completion/help surface, and dynamic descriptor +behavior for the d2b 3.0 CLI binary. It maps every current v3 CLI verb and +target to its retained, successor, or deletion outcome. It specifies how the CLI +discovers the nearest Zone runtime, constructs canonical ResourceRefs, routes +through the resource API, handles async operations and their lifecycle, and +enforces all limits without embedding Provider logic. + +## Guiding invariants + +1. The CLI is a typed client. It constructs resource API requests and interprets + resource/operation responses. It contains no Provider code, no controller + logic, no broker operations, and no sandbox compilation. +2. Every mutating verb routes through the Zone resource API over ComponentSession + and d2b-bus. Every read-only verb prefers the Zone resource API and documents + its graceful fallback. +3. ResourceRefs are canonical Zone-local references of the form + `/`. The CLI parses, validates, and serializes + them but does not invent, guess, or alias them. +4. Output is stable: `--json` emits newline-terminated JSON objects with a + frozen schema; human output is for terminals only and is not machine-parseable. +5. Exit codes are stable. The guest process exit code passes through `vm exec` + transparently; the `--json` envelope disambiguates collisions. +6. Dynamic descriptors (shell completion, help, Provider-advertised commands) + are bounded and fetched with hard deadlines. A slow or absent Zone runtime + never blocks the CLI binary startup. +7. Provider code never runs in the CLI binary or subprocess at CLI invocation + time. +8. No SSH, no bash fallback, no realm/workload terminology in new surfaces. + +## Baseline terminology mapping + +The v3 codebase at baseline `b5ddbed6` uses pre-ADR 0046 names. This table +maps every cited current symbol to its ADR 0046 target. Current-source evidence +citations throughout this spec use the old symbol names; target names appear in +the "target:" annotation. + +| Current baseline symbol | Package | Target name | +| --- | --- | --- | +| `RealmId`, `RealmPath`, `TargetName` | `d2b-realm-core/src/ids.rs`, `realm.rs`, `target.rs` | `Zone/` ResourceRef; multi-level path → `ZoneLink` hierarchy | +| `RealmControllerPlacement`, `EntrypointMode` | `d2b-realm-core/src/realm.rs` | Zone runtime placement/mode (ZoneLink `mode` field) | +| `RealmEntrypointDocument`, `RealmEntrypointConfig` | `packages/d2b/src/lib.rs:5163` | Nix-generated static `realm-entrypoints.json`; target: Zone self resource + ZoneLink resources in redb store | +| `RealmPolicyOutputV1 { realm, mode, gateway_vm, gateway_target, gateway_state, cross_realm_policy, credential_boundary }` | `d2b-contracts/src/cli_output.rs:345` | ZoneLink resource fields; `gateway_vm` → ZoneLink `gatewayGuestRef`; `mode` → ZoneLink `placement` | +| `RealmListOutputV1 { realms: Vec }` | `d2b-contracts/src/cli_output.rs:285` | Target: `d2b zone list` response listing `ZoneLink` resources | +| `RealmInspectOutputV1 { realm: RealmPolicyOutputV1 }` | `d2b-contracts/src/cli_output.rs:292` | Target: `d2b zone get ` response | +| `WorkloadId`, `WorkloadTarget` (`= RealmTarget`) | `d2b-realm-core/src/ids.rs`, `d2b-core/src/workload_identity.rs` | `Guest/` ResourceRef for VM/sandbox/cloud/remote; `Host/` for unsafe-local (NEVER `Guest`) | +| `WorkloadProviderKind::LocalVm` | `d2b-realm-core/src/workload.rs:16` | `Guest` under `Provider/runtime-cloud-hypervisor` | +| `WorkloadProviderKind::QemuMedia` | `d2b-realm-core/src/workload.rs:19` | `Guest` under `Provider/runtime-qemu-media` | +| `WorkloadProviderKind::UnsafeLocal` | `d2b-realm-core/src/workload.rs:22`; Nix option `kind = "unsafe-local"` in `nixos-modules/options-realms-workloads.nix:221`; Nix emitter `nixos-modules/unsafe-local-workloads-json.nix`; helper crate `packages/d2b-unsafe-local-helper/`; wire protocol `packages/d2b-contracts/src/unsafe_local_wire.rs` | Target: `Host/` resource with `defaultDomain=user`, `allowedDomains=[user]`, `defaultUserRef=User/`; reconciled by `Provider/system-core` (NOT itself a Provider and NOT a Guest); child processes use normal Process Providers; no-isolation posture (`IsolationPosture::UnsafeLocal`) MUST be preserved as explicit warnings in Host status, CLI output, UI, and audit/telemetry | +| `WorkloadProviderKind::ProviderManaged` | `d2b-realm-core/src/workload.rs:21` | `Guest` or `Host` depending on Provider type; decision-required per provider dossier | +| `IsolationPosture::UnsafeLocal` | `d2b-realm-core/src/workload.rs:33`; `WorkloadExecutionPosture { isolation: IsolationPosture::UnsafeLocal, environment: EnvironmentPosture::SystemdUserManagerAmbient, execution_identity: ExecutionIdentityPosture::AuthenticatedRequesterUid }` (test at workload.rs:207); emitted in `WorkloadPublicSummary.execution_posture` via `public_wire.rs:267` | Target: `Host` resource `status.isolationPosture: "none"` field; this field MUST be present and non-empty in all Host status outputs, `--json` envelopes, CLI `--human` tables, and audit events for unsafe-local Hosts; a missing or silent posture field is a correctness violation | +| `WorkloadSummary`, `WorkloadState` | `d2b-realm-core/src/workload.rs:130,156` | Guest resource status/phase (LocalVm/QemuMedia/ProviderManaged); for UnsafeLocal → Host resource status/phase | +| `ListEntry { vm: String, lifecycle: VmLifecycle, workload_identity: Option, … }` | `d2b-contracts/src/public_wire.rs:2495` | Guest resource envelope; `ListEntry.vm` = `WorkloadId`; `workload_identity` = current realm-native canonical target | +| `VmStatus { vm: String, lifecycle: VmLifecycle, … }` | `d2b-contracts/src/public_wire.rs:2530` | Guest resource status | +| `VmLifecycleState::Stopped/Starting/Booted/Running/Stopping/Restarting/Failed/Unknown` | `d2b-contracts/src/public_wire.rs:2605` | Guest resource `phase`; target phases are `Pending\|Ready\|Succeeded\|Degraded\|Failed\|Deleted\|Unknown`; `Starting`/`Stopping`/`Restarting` map to conditions/reasons on `Pending`/`Degraded`, not separate phases | +| `VmTargetRoute::Local { vm }` | `packages/d2b/src/lib.rs:5577` | `Guest/` ResourceRef in the current Zone | +| `VmTargetRoute::Gateway { realm, gateway_vm }` | `packages/d2b/src/lib.rs:5578` | Cross-Zone via `ZoneLink/` ComponentSession; `gateway_vm` = `Guest/` | +| `ProcessRole::Virtiofsd` | `d2b-core/src/processes.rs:199` | `Process` under volume-virtiofs Provider | +| `ProcessRole::Swtpm` | `d2b-core/src/processes.rs:203` | `Process` under device-tpm Provider | +| `ProcessRole::CloudHypervisorRunner` | `d2b-core/src/processes.rs:213` | `Process` under runtime-cloud-hypervisor Provider | +| `ProcessRole::QemuMediaRunner` | `d2b-core/src/processes.rs:215` | `Process` under runtime-qemu-media Provider | +| `ProcessRole::WaylandProxy` | `d2b-core/src/processes.rs:249` | `Process` under display-wayland Provider | +| `ProcessRole::Gpu`/`GpuRenderNode` | `d2b-core/src/processes.rs:209,211` | `Process` under `Provider/device-gpu` | +| `ProcessRole::Audio` | `d2b-core/src/processes.rs:213` | `Process` under audio-pipewire Provider | +| `ProcessRole::HostReconcile` | `d2b-core/src/processes.rs:196` | Realm controller bootstrap step; not a standalone CLI-visible Process | +| `ProcessRole::GuestControlHealth` | `d2b-core/src/processes.rs:235` | Guest readiness probe integrated into Guest controller reconcile loop; not a standalone Process ResourceType | +| `ProcessRole::GuestSshReadiness` | `d2b-core/src/processes.rs:228` | Deleted at v3 clean cutover; no compatibility retention | +| `VmProcessDag` | `d2b-core/src/processes.rs` | Per-Guest/Provider `Process` resource DAG; target managed by Provider controller | +| `RealmControllersJson`, `RealmControllerLocalWorkload { vm_name, identity }` | `d2b-core/src/realm_controller_config.rs` | Zone Nix-authored Guest resources; `vm_name` → `Guest/` resource name; `identity.canonical_target` → `WorkloadId` = `Guest/` resource name | +| `ShellAttachArgs { vm: String }`, `ShellOp`, `ShellOpResponse` | `d2b-contracts/src/public_wire.rs:1319,1394,1527` | Target: ShellSession resource + named stream over ComponentSession; `vm` arg = current `WorkloadId` (= target `executionRef: Host/\|Guest/`) | + +## Zone context and nearest-runtime discovery + +### Zone context selection + +The CLI selects a Zone context in this priority order: + +1. `--zone ` flag on any command where it applies. +2. `D2B_ZONE` environment variable. +3. Nearest-runtime discovery: the CLI walks the runtime socket hierarchy + (see below) and selects the first reachable Zone runtime whose + `Zone/` self resource is Ready. +4. If no Zone runtime is reachable, read-only verbs that accept static sources + fall back to those sources with a visible degraded warning; mutating verbs + fail closed with exit code 1 and a stable `zone-unavailable` error class. + +The selected zone name and socket path are visible in every `--json` output +envelope under `zoneRef` (e.g. `"zoneRef": "Zone/dev"`). + +### Nearest-runtime socket paths + +For a system-domain (default) context, the CLI probes these paths in order, +stopping at the first socket that accepts a local ComponentSession connect: + +```text +/run/d2b/zones//public.sock # per-Zone socket (multi-Zone host) +/run/d2b/public.sock # v3 canonical single-Zone socket +``` + +For a user-domain context (`--domain user` or inferred from `$XDG_RUNTIME_DIR` +availability and the target ResourceRef): + +```text +$XDG_RUNTIME_DIR/d2b/zones//public.sock +$XDG_RUNTIME_DIR/d2b/public.sock +``` + +Environment overrides (retained from v3 baseline for compatibility): + +| Variable | Purpose | +| --- | --- | +| `D2B_ZONE` | Override zone name selection | +| `D2B_PUBLIC_SOCKET` | Override public socket path directly | +| `D2B_BROKER_SOCKET` | Override broker socket path (host prepare/destroy only) | + +Discovery never falls back from a zone-qualified socket to the single-Zone +socket when `--zone` or `D2B_ZONE` is set. + +`--zone` is a global option available to all commands. Purely local +host-maintenance commands (`host prepare`, `host destroy`, `host install`, +`host reconcile`, `host doctor`, `host check`) do not consult Zone context; +they operate on the local host only and explicitly document this in their +`--help` text. Providing `--zone` to these commands is not an error; the flag +is accepted and documented as having no effect on host-maintenance operations. + +## Canonical ResourceRef argument parsing + +### Accepted forms + +Standard ResourceRef positional or `--` flag argument: + +```text +/ # canonical full form + # short form for commands that declare a default ResourceType +``` + +Short form rules: + +- The command declares one and only one default ResourceType. There is no + implicit disambiguation across types. +- Short form is accepted only when the command's `--help` documents the default + type. +- If the name contains a `/`, the full form is required. + +Validation: + +- `resource_name` matches `^[a-z][a-z0-9-]*$`. +- `ResourceType` is a known Zone-registered type or a qualified vendor name. +- Validation failure yields exit code 2 with a `ref-invalid` error class. + +ResourceRef values that name resources that do not exist return exit code 1 with +a `resource-not-found` error class. + +**ResourceType validation:** The frozen standard ResourceType set (Zone, +ZoneLink, Provider, Role, RoleBinding, Host, Guest, Process, EphemeralProcess, +Volume, Network, Device, User, Credential, Quota, EmergencyPolicy) is validated +locally at compile time against the names defined in this spec. A syntactically +valid type name that contains a vendor qualifier (e.g. `acme.corp/FooResource`) +is passed through to the live Zone catalog; the API returns +`resource-schema-invalid` if the Zone does not recognize it. Any other +unrecognized type name fails locally with exit code 2 and class `ref-invalid` +without a Zone round-trip. + +## Nix configuration, ResourceSpec JSON, and generation lifecycle + +> Full Nix option surface, eval/build validation rules, bundle emission format, +> schema-digest binding, and assertion catalogue are owned by the Nix +> configuration spec (ADR-046-nix-configuration.md). This section covers the +> canonical resource envelope shape as exposed by the CLI and the CLI-visible +> activation/cleanup commands. + +### Resource shape and Nix authoring + +Zone resources are declared in NixOS modules through a unified structure that +mirrors the canonical ResourceSpec schema directly: + +``` +d2b.zones..resources. = { + type = ""; + metadata = { # optional user-authored fields only + ownerRef = null; # optional; names a declared resource in the same bundle + labels = { }; # optional; presentation/selector labels only + }; + spec = { /* exact ResourceTypeSchema field names — no renaming or extra nesting */ }; +}; +``` + +**Derived on emit:** `metadata.name` from `` attr key; +`metadata.zone` from `` attr key; `apiVersion` defaults to +`"resources.d2b.io/v3"`. + +**Omitted in authoring (runtime-filled):** `status`, `uid`, `revision`, +`generation`, `finalizers`, `deletionRequestedAt`, `createdAt`, `updatedAt`, +`managedBy`, `configurationGeneration`. The Nix module rejects these as unknown +options if authored. + +`spec` sub-options per `type` are generated from the same ResourceTypeSchema +(and signed Provider schema for `type = "Provider"`) that governs the API. +Field names in `spec` are identical to the schema; there is no second vocabulary +and no extra nesting. Vendor-qualified types (containing `.`) are admitted when +a matching schema is installed. + +**Minimal multi-resource example:** + +```nix +# Derivations are declared in d2b.artifacts; ResourceSpecs reference them by id only. +d2b.artifacts."cloud-hv-pkg" = { package = pkgs.d2b-provider-cloud-hv; type = "provider"; }; + +d2b.zones.main.resources = { + "main" = { type = "Zone"; spec = {}; }; # Zone.spec is empty + "work-vm" = { type = "Guest"; spec = { providerRef = "Provider/cloud-hv"; executionPolicy = { cores = 4; memoryMiB = 8192; }; networkRefs = [ "Network/default" ]; }; }; + "local-user" = { type = "Host"; spec.defaultUserRef = "User/alice"; }; + "default" = { type = "Network"; spec = { cidr = "10.100.0.0/24"; gatewayAddress = "10.100.0.1"; }; }; + "cloud-hv" = { type = "Provider"; spec.artifactId = "cloud-hv-pkg"; }; # plain id, no derivation in spec + "ssh-host-key" = { type = "Credential"; spec = { credentialType = "ssh-ed25519-host-key"; credentialSource = "systemd-credential:d2b-ssh-key"; }; }; + "alice" = { type = "User"; spec = { uid = 1001; homeDir = "/home/alice"; }; }; + "default" = { type = "Quota"; spec = { /* quota fields per Quota schema */ }; }; + "lockdown" = { type = "EmergencyPolicy"; spec = { /* policy fields per EmergencyPolicy schema */ }; }; +}; +``` + +**Evidence class:** `ADR-only` — v3 Nix modules do not exist at baseline +`b5ddbed6`. Current source: `nixos-modules/options-realms-workloads.nix`, +`nixos-modules/options-realms.nix`, `nixos-modules/unsafe-local-workloads-json.nix` +(pre-ADR 0046 names). Target implemented by work item ADR046-cli-011. + +### Canonical ResourceSpec JSON shape + +Every `d2b get`, `d2b list` element, and `d2b watch` event carries the +canonical ResourceSpec envelope inside the outer `--json` response. The +exact shape (all fields always present; null where empty): + +```json +{ + "apiVersion": "resources.d2b.io/v3", + "type": "Guest", + "metadata": { + "name": "work-vm", + "zone": "main", + "uid": "01930a4f-2b3d-7e8f-9c1a-4d5e6f708192", + "generation": 3, + "revision": "", + "ownerRef": null, + "finalizers": [], + "deletionRequestedAt": null, + "createdAt": "2026-07-22T00:00:00Z", + "updatedAt": "2026-07-22T10:30:00Z", + "managedBy": "nix", + "configurationGeneration": 7, + "labels": { + "d2b.io/nix-path": "d2b.zones.main.resources.work-vm" + } + }, + "spec": { + "providerRef": "Provider/cloud-hv", + "executionPolicy": { "cores": 4, "memoryMiB": 8192 }, + "networkRefs": ["Network/default"], + "providerSettings": {} + }, + "status": { + "observedGeneration": 3, + "phase": "Ready", + "conditions": [], + "lastReconciledAt": "2026-07-22T10:30:05Z", + "startedAt": "2026-07-22T10:30:05Z", + "completedAt": null, + "outcome": null + } +} +``` + +**Core management metadata** (runtime-filled; never authored in Nix): + +| Field | Value | Meaning | +| --- | --- | --- | +| `managedBy` | `"nix"` | Set by Zone runtime on bundle apply; absent on dynamic resources | +| `configurationGeneration` | `7` (integer) | Bundle generation that last created/updated this resource; absent on dynamic resources | +| `labels["d2b.io/nix-path"]` | `"d2b.zones.main.resources.work-vm"` | Presentation/tracing only — not authoritative for cleanup decisions | + +**Cleanup authority:** The Zone runtime identifies resources eligible for +deletion from a new bundle by `managedBy == "nix"` AND absence from the new +bundle. `managedBy` absent or `"controller"` (dynamic resources) are **never** +deleted by bundle application, regardless of whether their type appears in the +bundle. This invariant is a correctness requirement. + +**Owner controller responsibility:** When a Nix-owned parent resource is +deleted, its owning controller enqueues deletion of declared children (child +Processes, EphemeralProcess sessions, Volume attachments). It does not delete +unrelated resources. + +**List response** (`d2b list Guest`): + +```json +{ + "ok": true, + "zoneRef": "Zone/main", + "operationId": "", + "schemaVersion": 1, + "items": [ { /* ResourceSpec envelope */ }, ... ], + "nextPageToken": null, + "totalCount": 3 +} +``` + +**Status-only response** (`d2b status Guest/work-vm`): + +```json +{ + "ok": true, + "zoneRef": "Zone/main", + "operationId": "", + "schemaVersion": 1, + "resourceRef": "Guest/work-vm", + "status": { /* status sub-object only */ } +} +``` + +### CLI-visible activation and cleanup + +Full activation-provider command surface is in +[§ `d2b activation`](#d2b-activation--activation-nixos-provider-commands). + +**Apply a new bundle generation:** + +``` +d2b activation apply [--zone ] [--dry-run] [--json | --human] +``` + +Sends the built bundle (`/etc/d2b/zone-resources.json`) to the Zone runtime. +Exits 0 when the apply phase completes (creates/updates present resources). +Does not wait for deletion of absent Nix resources. Output includes applied +resource count and pending-cleanup count. + +**Absent-resource deletion:** Resources with `managedBy == "nix"` absent from +the new bundle receive asynchronous Delete. The Zone enters +`Degraded/pending-cleanup` until all complete. + +**Observe cleanup progress:** + +``` +d2b zone status [] [--zone ] [--watch] [--deadline ] +``` + +With `--watch`, streams `pending-cleanup` condition transitions: + +``` +$ d2b zone status main --watch +Zone/main Degraded pending-cleanup: 2 resources pending deletion +Zone/main Degraded pending-cleanup: 1 remaining (Guest/old-vm done) +Zone/main Ready (cleanup complete) +``` + +**Per-resource deletion progress:** + +``` +d2b status / [--watch] +``` + +**Force-remove stuck finalizer (explicit operator escape hatch):** + +``` +d2b delete / --force-remove-finalizers +``` + +Never triggered automatically. + +**Prior generation retention:** count-based; default 3; range 1–16. The +retention count is configured via the Zone resource; the exact field name is +established by ADR-046-nix-configuration. Generations beyond the count become +eligible for pruning after cleanup completes. Generations within the count are +retained for rollback regardless of elapsed time; there is no TTL. + +``` +d2b activation gc [--zone ] [--dry-run] [--json | --human] +``` + +Prunes generation bundles and hardlink-farm snapshots beyond the retention count. + +**Rollback:** + +``` +d2b activation rollback [--to-generation ] [--zone ] [--dry-run] [--apply] +``` + +Re-applies a retained prior generation bundle as a new (higher) +`bundleGeneration`. The generation counter is never reversed. + +**Audit:** Each deletion dispatched from bundle application emits +`resource-delete-dispatched`; each completion emits +`resource-delete-completed` or `resource-delete-failed`. + +### CLI-visible tests for activation and cleanup + +| Layer | Test | What it proves | +| --- | --- | --- | +| Runtime (integration) | Apply bundle → absent `managedBy=nix` resource receives async Delete | Cleanup dispatch uses `managedBy` | +| Runtime (integration) | Dynamic resource (`managedBy` absent) absent from bundle → NOT deleted | `managedBy` cleanup invariant | +| Runtime (integration) | Zone status = `Degraded/pending-cleanup` during outstanding deletions | Status accuracy | +| Runtime (integration) | Zone status = `Ready` after all cleanup completes | Status accuracy | +| Runtime (integration) | Generations within retention count retained (no TTL) | Count-based retention | +| Runtime (integration) | `d2b activation gc` prunes beyond retention count | GC correctness | +| Runtime (integration) | `d2b activation rollback` re-applies prior bundle as new bundleGeneration | Rollback increments counter | +| Runtime (integration) | Forced delete of stuck finalizer completes | Force-delete path | +| Runtime (integration) | Old `bundleGeneration` replay rejected | Generation monotonicity | + +## Standard resource verbs + +The following subcommand structure applies to all standard ResourceTypes that +declare a CLI surface. The exact set of verbs implemented per ResourceType is +specified in the per-ResourceType section below. + +### `d2b get /` + +Fetch the current resource envelope. + +``` +d2b get / [--zone ] [--json | --human] +``` + +Returns the complete resource envelope (`metadata`, `spec`, `status`). + +**Output format:** JSON envelope or human summary table row with phase/condition +summary. + +**Exit codes:** +- 0: resource returned +- 1: resource not found or zone unavailable (`resource-not-found`, + `zone-unavailable`) +- 2: usage/validation error + +### `d2b list [filters]` + +List resources of one type. + +``` +d2b list [--zone ] + [--phase ] + [--label-selector =] + [--page-token ] + [--limit ] + [--json | --human] +``` + +Returns a bounded page of matching resources. Pagination uses `--page-token` +from the previous response. + +**Output format:** JSON array envelope with `items`, `nextPageToken` +(absent if last page), `snapshotRevision`; human table with status column. + +**Exit codes:** +- 0: list returned (may be empty) +- 1: zone unavailable or filter error +- 2: usage error + +### `d2b watch [filters]` + +Stream resource change events after a starting revision. + +``` +d2b watch [--zone ] + [--since-revision ] + [--phase ] + [--label-selector =] + [--json] + [--deadline ] +``` + +Streams newline-separated JSON change envelopes (`type`, `object`) to stdout. + +**Output format:** JSON change stream only (`--human` is not valid for watch). + +**Deadline:** `--deadline ` (e.g. `30s`, `5m`) sets a hard wall +deadline. Exit code 0 on clean close; 1 on deadline expiry with +`deadline-exceeded` class; 3 on signal/cancel. + +**Exit codes:** +- 0: clean close by server +- 1: stream error, zone unavailable +- 2: usage error +- 3: cancelled by SIGINT/SIGTERM + +### `d2b create [--spec-file | --spec-stdin]` + +Create a resource from a JSON spec file or stdin. + +``` +d2b create [--zone ] + [--spec-file | --spec-stdin] + [--json | --human] +``` + +**Exit codes:** +- 0: created +- 1: already exists (`resource-already-exists`), validation error, zone + unavailable +- 2: usage error + +### `d2b update-spec /` + +Full spec replacement. + +``` +d2b update-spec / [--zone ] + [--revision ] + [--spec-file | --spec-stdin] + [--json | --human] +``` + +**Exit codes:** +- 0: updated +- 1: conflict, not found, validation error, zone unavailable +- 2: usage error + +### `d2b delete /` + +Request deletion. + +``` +d2b delete / [--zone ] + [--revision ] + [--json | --human] +``` + +Triggers deletion (sets `deletionRequestedAt`). Does not wait for finalizers to +complete unless `--wait` is given. + +**Exit codes:** +- 0: deletion accepted +- 1: not found, conflict, zone unavailable +- 2: usage error + +### `d2b status /` + +Fetch the status subresource only. + +``` +d2b status / [--zone ] + [--watch] + [--deadline ] + [--json | --human] +``` + +With `--watch`, streams status change events until Ready/Succeeded/Failed/ +Deleted or deadline. + +**Exit codes (without `--watch`):** +- 0: returned +- 1: not found, zone unavailable +- 2: usage error + +**Exit codes (with `--watch`):** +- 0: phase reached Ready or Succeeded +- 1: phase reached Failed, Deleted, or zone unavailable +- 2: usage error +- 3: cancelled or deadline exceeded + +## `d2b host` — Host resource commands + +Maps to the `Host` ResourceType. Default ResourceType context for positional +name argument. + +### `d2b host get ` + +Equivalent to `d2b get Host/`. + +### `d2b host list` + +Equivalent to `d2b list Host`. + +**Current v3 source:** `cmd_list`/`cmd_status` read `ListResponse { vms: Vec }` and `StatusResponse { entries: Vec }` from `packages/d2b-contracts/src/public_wire.rs:2152,2158`. Each `ListEntry.vm: String` is a `WorkloadId` (old terminology); `ListEntry.lifecycle.state: VmLifecycleState` (old: `Stopped/Starting/Booted/Running/Stopping/Restarting/Failed/Unknown`) maps to target `Host` phase. `Host` ResourceType is new — there is no current separate host-listing command. Evidence class: `implemented-and-reachable` for the list/status daemon paths; `ADR-only` for `Host` as a distinct ResourceType. + +### `d2b host status ` + +Equivalent to `d2b status Host/`. + +### `d2b host prepare` + +Reconcile host-side infrastructure (bridges, nftables, sysctls, cgroups). +Routes through the Zone resource API's Host reconcile operation. `--dry-run` +returns a plan; `--apply` executes. + +``` +d2b host prepare [--zone ] [--dry-run | --apply] [--json | --human] +``` + +**Current v3 source:** `cmd_host_prepare` at +`packages/d2b/src/lib.rs:4468`; dispatches through `public.sock`/broker. +Evidence class: `implemented-and-reachable`. + +**Retained behavior:** dry-run/apply distinction, broker-mediated mutation, +ownership markers, fail-closed on foreign markers. + +**Required delta:** Route through Zone resource API Host reconcile operation +instead of raw broker seqpacket. + +### `d2b host destroy` + +``` +d2b host destroy [--zone ] [--dry-run | --apply] [--json | --human] +``` + +**Current v3 source:** `cmd_host_destroy` at `packages/d2b/src/lib.rs:4549`. +Evidence class: `implemented-and-reachable`. + +### `d2b host doctor` + +Read-only deep diagnostics. + +``` +d2b host doctor [--zone ] [--read-only] [--json | --human] +``` + +**Current v3 source:** `cmd_host_doctor` at `packages/d2b/src/lib.rs:4752`. +Evidence class: `implemented-and-reachable`. + +**Retained behavior:** reads daemon state dir, metrics URL reachability, +pidfd-table, kernel-module-report, autostart-report, storage-lifecycle-report. +Daemon-state-dir and metrics-URL overrides retained via `D2B_DAEMON_STATE_DIR` +and `D2B_METRICS_URL`. + +**Required delta:** Read from Zone runtime status resources rather than local +files where available; fall back to local state files when Zone API is +unavailable. + +### `d2b host check` + +``` +d2b host check [--read-only] [--json | --human] +``` + +**Current v3 source:** `cmd_host_check` at `packages/d2b/src/lib.rs:4271`. +Evidence class: `implemented-and-reachable`. Host-check exit code 3 retained +for compatibility. + +### `d2b host install` + +``` +d2b host install [--dry-run | --apply] [--enable] [--start] [--no-start] + [--json | --human] +``` + +**Current v3 source:** `cmd_host_install` at `packages/d2b/src/lib.rs:5007`. +Evidence class: `implemented-and-reachable`. + +### `d2b host reconcile` + +``` +d2b host reconcile [--dry-run | --apply] [--json | --human] +``` + +**Current v3 source:** `cmd_host_reconcile` at `packages/d2b/src/lib.rs:5070`. +Evidence class: `implemented-and-reachable`. + +### `d2b host validate` + +``` +d2b host validate [--dry-run | --apply] [--wave ] + [--evidence-dir ] [--scripts-dir ] + [--operator-signature ] + [--json | --human] +``` + +**Current v3 source:** `cmd_host_validate` at `packages/d2b/src/lib.rs:4941`. +Evidence class: `implemented-and-reachable`. + +### Unsafe-local Host resources — isolation posture requirements + +`Host` resources with `WorkloadProviderKind::UnsafeLocal` lineage have +`defaultDomain=user`, `allowedDomains=[user]`, and `defaultUserRef=User/`. +They are reconciled by `Provider/system-core`; they are NOT Provider resources +themselves, and they are NEVER `Guest` resources. Their child processes run +through normal Process Providers. + +The no-isolation posture (`IsolationPosture::UnsafeLocal` in current baseline, +`status.isolationPosture: "none"` in target Host resource) MUST be preserved +explicitly in: + +1. **Host status** (`d2b host status`, `d2b host get`, `d2b host list`): the + `status.isolationPosture` field MUST be `"none"` in `--json` output and + presented as a visible `[no isolation]` annotation in `--human` table rows. +2. **CLI/UI**: `d2b shell open Host/` and `d2b exec run Host/ -- ...` + MUST emit a one-line `warning: no isolation boundary — this process runs as + your host user` line to stderr before attaching. The warning has NO + suppression flag; it appears unconditionally in human output. JSON output + carries `isolationPosture: "none"` in the response envelope. +3. **Audit**: every `Shell` and `EphemeralProcess` event on a Host resource + MUST carry `isolationPosture: "none"` as a fixed closed label in audit + records. This posture MUST NOT appear in OTEL metric labels, span attributes, + or structured log fields; it is status/UI/audit-surface only. + +A missing or silent posture field is a correctness violation. Reviewers MUST +reject any diff that adds a code path serving a Host/unsafe-local resource +without propagating the isolation posture through all three surfaces above. + +**Current v3 evidence:** +- `IsolationPosture::UnsafeLocal` at `d2b-realm-core/src/workload.rs:33` +- `WorkloadExecutionPosture { isolation: IsolationPosture::UnsafeLocal, environment: EnvironmentPosture::SystemdUserManagerAmbient, execution_identity: ExecutionIdentityPosture::AuthenticatedRequesterUid }` at `workload.rs:207` +- `WorkloadPublicSummary.execution_posture: WorkloadExecutionPosture` at `packages/d2b-contracts/src/public_wire.rs:267` (public inventory carries isolation posture today) +- `nixos-modules/options-realms-workloads.nix:233` — "Host-user process runtime with no isolation boundary. Requires explicit realm policy opt-in." +- `nixos-modules/unsafe-local-workloads-json.nix` — emits `runtimeKind = "unsafe-local"` and `providerId = "unsafe-local"` in private bundle +- `packages/d2b-unsafe-local-helper/src/` — runtime helper; `packages/d2b-contracts/src/unsafe_local_wire.rs` — wire protocol + +Evidence class: `implemented-and-reachable` for isolation posture data path; +`ADR-only` for the `Host` resource API shape and the `Provider/system-core` reconciler. + +## `d2b guest` — Guest resource commands + +Maps to the `Guest` ResourceType. Unsafe-local workloads are `Host` resources, +never `Guest` — see §Unsafe-local Host resources above. + +### `d2b guest get ` + +Equivalent to `d2b get Guest/`. + +### `d2b guest list` + +Equivalent to `d2b list Guest`. + +**Current v3 source:** `cmd_list`/`cmd_vm_list` at `packages/d2b/src/lib.rs:3560`; returns +`ListResponse { vms: Vec }` from `packages/d2b-contracts/src/public_wire.rs:2152`. +Each `ListEntry` has `vm: String` (= current `WorkloadId`; target: `Guest/` resource +name), `lifecycle.state: VmLifecycleState`, `workload_identity: Option` (realm- +native canonical target, additive field tolerated absent by old daemons), and `runtime_capabilities: +Vec` (current: `WorkloadProviderKind` encoded; target: Provider family). Evidence class: +`implemented-and-reachable` for the list/lifecycle data path; `ADR-only` for `Guest` resource API. + +### `d2b guest status ` + +Equivalent to `d2b status Guest/`. With `--watch`, streams status events. + +**Successor to:** `d2b vm status ` (see migration table below). + +**Current v3 source:** `cmd_vm_status`/`cmd_status` at `packages/d2b/src/lib.rs:6868,3587`; +returns `StatusResponse { entries: Vec }` from `public_wire.rs:2158`. `VmStatus.vm: +String` = current `WorkloadId`; `VmStatus.lifecycle.state: VmLifecycleState` maps to target +`Guest` phase (`Stopped` → `Pending`, `Booted`/`Running` → `Ready`, `Failed` → `Failed`, +`Starting`/`Stopping`/`Restarting` → conditions/reasons on `Pending`/`Degraded`, +not separate phases). Evidence class: +`implemented-and-reachable` for status/lifecycle data path; `ADR-only` for `Guest` resource API. + +### `d2b guest start ` + +Request that the Guest reach Ready phase. + +``` +d2b guest start [--zone ] + [--dry-run | --apply] + [--no-wait-ready] + [--json | --human] +``` + +`--no-wait-ready` exits 0 on accepted (not waiting for Ready phase). +Default behavior waits for phase Ready or Failed with exit codes 0/1. + +**Successor to:** `d2b vm start `, `d2b up `. + +**Current v3 source:** `cmd_vm_start` at `packages/d2b/src/lib.rs:6675`; +dry-run/apply/no-wait-api pattern at `VmStartArgs`. +Evidence class: `implemented-and-reachable`. + +**Required delta:** Route through Zone resource API Guest lifecycle instead +of raw daemon verb; replace `EXIT_API_TIMEOUT` (33) with `deadline-exceeded` +error class. + +### `d2b guest stop ` + +``` +d2b guest stop [--zone ] + [--dry-run | --apply] + [-f | --force] + [--json | --human] +``` + +**Successor to:** `d2b vm stop `, `d2b down `. + +**Current v3 source:** `cmd_vm_stop` at `packages/d2b/src/lib.rs:6690`. +Evidence class: `implemented-and-reachable`. + +### `d2b guest restart ` + +``` +d2b guest restart [--zone ] + [--dry-run | --apply] + [-f | --force] + [--json | --human] +``` + +**Successor to:** `d2b vm restart `, `d2b restart `. + +### `d2b guest create` + +``` +d2b guest create [--zone ] + [--spec-file | --spec-stdin] + [--json | --human] +``` + +### `d2b guest update-spec ` + +### `d2b guest delete ` + +**Note:** The following v3 Guest verbs (`build`, `generations`, `switch`, +`boot`, `test`, `rollback`) are Guest-activation operations that interact with +the activation Provider (`activation-nixos`). They are specified under +`d2b activation` below. + +## `d2b process` — Process resource commands + +Maps to the `Process` ResourceType. + +### `d2b process get ` + +Equivalent to `d2b get Process/`. + +### `d2b process list` + +Equivalent to `d2b list Process [--execution-ref /] + [--domain ]`. + +### `d2b process status ` + +Equivalent to `d2b status Process/`. With `--watch`, streams until Ready +or terminal. + +### `d2b process start ` + +Request that the Process reach Ready phase (sets desired lifecycle). + +``` +d2b process start [--zone ] + [--dry-run | --apply] + [--no-wait-ready] + [--json | --human] +``` + +### `d2b process stop ` + +``` +d2b process stop [--zone ] + [--dry-run | --apply] + [-f | --force] + [--json | --human] +``` + +### `d2b process create`, `update-spec`, `delete` + +Standard resource verbs. + +## `d2b exec` — EphemeralProcess (one-shot exec) + +`d2b exec` creates an `EphemeralProcess` resource and manages its lifecycle. +This replaces the `d2b vm exec` sub-verb for the one-shot asynchronous exec +use case. + +### `d2b exec run -- [args...]` + +Create a detached `EphemeralProcess` and print its resource name. + +``` +d2b exec run [--zone ] + [--name ] + [--domain ] + [--user ] + [--provider ] + [--env KEY=VALUE]... + [--cwd ] + [--deadline ] + [--json | --human] + -- [args...] +``` + +`executionRef` is a canonical ResourceRef of type `Host` or `Guest`. + +Returns the created `EphemeralProcess/` resource ref and initial status. + +**Exit codes:** +- 0: EphemeralProcess created and accepted +- 1: execution target unavailable, authorization denied, zone unavailable +- 2: usage error + +**Successor to:** `d2b vm exec -d -- ` (detached form). + +**Current v3 source:** `VmExecArgs.detach` in `cmd_vm_exec` at +`packages/d2b/src/lib.rs:7166`; `exec_client::EXIT_EXEC_*` exit codes at +`packages/d2b/src/exec_client.rs`. +Evidence class: `implemented-and-reachable` for detached vm exec. + +### `d2b exec attach /` + +Attach stdin/stdout/stderr to a running EphemeralProcess. + +``` +d2b exec attach / [--zone ] + [-i | --interactive] + [-t | --tty] + [--json] + [--deadline ] +``` + +`-t` puts the host terminal in raw mode. `-i` forwards host stdin. `-it` +together yields an interactive shell session. `--tty` is incompatible with +`--json`. + +**Successor to:** `d2b vm exec -it -- bash` (attached interactive form) and +`d2b vm exec -- ` (attached non-interactive form). + +**Current v3 source:** `cmd_vm_exec` attached path at +`packages/d2b/src/lib.rs:7166`; `exec_client.rs` FSM; `terminal_client.rs` +TTY abstractions. Evidence class: `implemented-and-reachable`. + +**Retained behavior:** host terminal raw mode; RAII termios restore on every +exit/error/panic; guest owns PTY; host only flips termios; `--json` envelope +with `source`/`reason`/`guestExitCode`/`signal`/`exitCode` fields; +`--json` is non-interactive (incompatible with `-t`). + +### `d2b exec wait /` + +Wait for completion and exit with the process exit code. + +``` +d2b exec wait / [--zone ] + [--deadline ] + [--json] +``` + +**Exit codes:** guest `WIFEXITED` code passes through (0–255). Reserved exit +codes (see table below) are only generated by transport/protocol failures. + +### `d2b exec status /` + +``` +d2b exec status / [--zone ] + [--watch] + [--deadline ] + [--json | --human] +``` + +**Successor to:** `d2b vm exec status `. + +### `d2b exec list []` + +``` +d2b exec list [] [--zone ] + [--phase ] + [--json | --human] +``` + +**Successor to:** `d2b vm exec list`. + +### `d2b exec logs /` + +``` +d2b exec logs / [--zone ] + [--stdout-offset ] + [--stderr-offset ] + [--max-len ] + [--json] +``` + +**Successor to:** `d2b vm exec logs `. + +**Current v3 source:** `VmExecLogsArgs` in `packages/d2b/src/lib.rs:789`; +`cmd_vm_exec_management` at line 7397. Evidence class: `implemented-and-reachable`. + +### `d2b exec kill /` + +``` +d2b exec kill / [--zone ] + [--signal ] + [--json | --human] +``` + +Sends `ExecCancel` SIGTERM/grace/SIGKILL sequence. + +**Successor to:** `d2b vm exec kill `. + +## `d2b shell` — ShellSession (persistent terminal sessions) + +Shell commands interact with the `shell-terminal` Provider's `ShellSession` +ResourceType and attach to persistent named sessions. + +### `d2b shell open ` + +Open a new persistent shell session or attach to an existing one. + +``` +d2b shell open [--zone ] + [--name ] + [--force] + [--json | --human] +``` + +Puts the host terminal in raw mode for the attached session. With `--json`, +emits the session resource ref and initial status without attaching. + +**Successor to:** `d2b shell ` / `d2b shell attach`. + +**Current v3 source:** `cmd_shell` and `cmd_shell_attach` at +`packages/d2b/src/lib.rs:1788,1957`; shell FSM in `run_shell_fsm` at line +2092; `ShellOwnerTransport`/`TerminalTransport` traits; `exec_client.rs` +signal/TTY machinery. Evidence class: `implemented-and-reachable`. + +**Current target argument:** The current `ShellArgs.vm: String` accepts a +`WorkloadId` (local VM name) or a `RealmTarget` of the form +`..d2b`. The CLI dispatches through `route_vm_target()` (at +`lib.rs:5544`) which resolves to `VmTargetRoute::Local { vm }` (local shell) or +`VmTargetRoute::Gateway { realm, gateway_vm }` (cross-realm shell). The target +v3 arg is `executionRef: Host/ | Guest/`. + +**Gateway shell fails closed:** `cmd_gateway_shell` at `lib.rs:1770` returns +`shell_gateway_attach_failure()` (error class `gateway-shell-attach-unavailable`) +for `ShellAction::Attach`; only management verbs (list/detach/kill) route through +the gateway today. The v3 target removes this restriction via cross-Zone +ComponentSession. + +**Retained behavior:** host terminal raw mode; RAII termios restore; SIGINT/ +SIGTERM/SIGSTOP/SIGHUP signal forwarding; SIGWINCH window resize; shell session +supervision in a verified transient user scope; disconnect detaches but does not +kill; kill targets only the exact re-verified transient scope. + +### `d2b shell attach /` + +Attach to an existing named session. + +``` +d2b shell attach / [--zone ] + [--force] +``` + +**Successor to:** `d2b shell attach --name `. + +### `d2b shell list []` + +``` +d2b shell list [] [--zone ] + [--json | --human] +``` + +**Successor to:** `d2b shell list`. + +**Current v3 source:** `ShellAction::List` in `cmd_shell`; shell list dispatch +at `packages/d2b/src/lib.rs:1788`. Evidence class: `implemented-and-reachable`. + +### `d2b shell detach /` + +``` +d2b shell detach / [--zone ] + [--json | --human] +``` + +**Successor to:** `d2b shell detach --name `. + +### `d2b shell kill /` + +``` +d2b shell kill / [--zone ] + [--json | --human] +``` + +Terminates the session's transient user scope. + +**Successor to:** `d2b shell kill --name `. + +**Current v3 source:** `ShellAction::Kill` in `cmd_shell`. Exit code 1 when +`--name` is absent for kill retained. Evidence class: `implemented-and-reachable`. + +### `d2b shell status /` + +``` +d2b shell status / [--zone ] + [--watch] + [--deadline ] + [--json | --human] +``` + +## `d2b volume` — Volume resource commands + +Maps to the `Volume` ResourceType. + +``` +d2b volume get +d2b volume list +d2b volume status [--watch] [--deadline ] +d2b volume create [--spec-file | --spec-stdin] +d2b volume update-spec [--revision ] [--spec-file | --spec-stdin] +d2b volume delete [--revision ] +``` + +All accept `[--zone ] [--json | --human]`. + +### `d2b volume verify ` + +Verify the Volume's backing state (hardlink farm integrity check for +`volume-local`). + +``` +d2b volume verify [--zone ] + [--repair] + [--json | --human] +``` + +**Successor to:** `d2b store verify `. + +**Current v3 source:** `cmd_store_verify`; `StoreVerifyArgs` at +`packages/d2b/src/lib.rs:973`. Evidence class: `implemented-and-reachable`. + +**Retained behavior:** non-destructive by default; `--repair` opts in to +repairs; exit code reflects verification result. + +## `d2b network` — Network resource commands + +Maps to the `Network` ResourceType. + +``` +d2b network get +d2b network list +d2b network status [--watch] [--deadline ] +d2b network create [--spec-file | --spec-stdin] +d2b network update-spec [--revision ] [--spec-file | --spec-stdin] +d2b network delete [--revision ] +``` + +All accept `[--zone ] [--json | --human]`. + +## `d2b device` — Device resource commands + +Maps to the `Device` ResourceType. + +``` +d2b device get +d2b device list [--type ] +d2b device status [--watch] [--deadline ] +``` + +All accept `[--zone ] [--json | --human]`. + +### `d2b device usb attach ` + +Bind a host USB busid to a declared Device resource. + +``` +d2b device usb attach / + [--zone ] + [--dry-run | --apply] + [--json | --human] +``` + +**Successor to:** `d2b usb attach `. + +**Current v3 source:** `cmd_usb_attach` at `packages/d2b/src/lib.rs`; UsbAttachArgs. +Evidence class: `implemented-and-reachable`. + +### `d2b device usb detach ` + +**Successor to:** `d2b usb detach `. + +**Current v3 source:** `cmd_usb_detach`. Evidence class: `implemented-and-reachable`. + +### `d2b device usb probe` + +``` +d2b device usb probe [--zone ] [--json | --human] +``` + +**Successor to:** `d2b usb probe`. + +**Current v3 source:** `cmd_usb_probe`. Evidence class: `implemented-and-reachable`. + +### `d2b device security-key status` + +``` +d2b device security-key status [--zone ] [--json | --human] +``` + +**Successor to:** `d2b usb security-key status`. + +**Current v3 source:** `cmd_usb_sk_status`. Evidence class: `implemented-and-reachable`. + +### `d2b device security-key sessions` + +**Successor to:** `d2b usb security-key sessions`. + +### `d2b device security-key cancel` + +``` +d2b device security-key cancel [ | --current] + [--dry-run | --apply] + [--json | --human] +``` + +**Successor to:** `d2b usb security-key cancel`. + +### `d2b device security-key test ` + +``` +d2b device security-key test / [--zone ] + [--dry-run] [--json | --human] +``` + +**Successor to:** `d2b usb security-key test `. + +## `d2b user` — User resource commands + +Maps to the `User` ResourceType. + +``` +d2b user get +d2b user list +d2b user status +``` + +All accept `[--zone ] [--json | --human]`. + +## `d2b credential` — Credential resource commands + +Maps to the `Credential` ResourceType. Credential bytes are never surfaced +through the CLI; only opaque status/lease metadata is returned. + +``` +d2b credential get +d2b credential list +d2b credential status [--watch] [--deadline ] +d2b credential delete +``` + +All accept `[--zone ] [--json | --human]`. + +**Current v3 source:** No direct current CLI mapping. +Evidence class: `ADR-only`. + +## `d2b provider` — Provider resource commands + +### `d2b provider list` + +``` +d2b provider list [--zone ] + [--package-only] + [--json | --human] +``` + +`--package-only` lists the offline package catalog without contacting the Zone +runtime. + +**Current v3 source:** No direct mapping; closest is `cmd_list` which lists +runtime VMs. Evidence class: `ADR-only`. + +### `d2b provider get ` + +``` +d2b provider get [--zone ] + [--json | --human] +``` + +Returns the Provider resource envelope including component/service status. + +### `d2b provider status ` + +``` +d2b provider status [--zone ] + [--watch] + [--deadline ] + [--json | --human] +``` + +### `d2b provider inspect ` + +Return full Provider descriptor: exported ResourceTypes, schemas, services, +process templates, permission claims, CLI projection, dependency aliases. + +``` +d2b provider inspect [--zone ] + [--json | --human] +``` + +**Relationship to dynamic descriptors:** The CLI fetches the CLI projection +sub-field of the Provider descriptor and renders the custom commands it +declares. See §Custom provider commands below. + +**Current v3 source:** No direct mapping. Evidence class: `ADR-only`. + +### Custom provider commands + +A Provider may declare a bounded `cliProjection` in its descriptor. The +projection contains: + +- a top-level subcommand name (e.g. `audio`, `display`); +- one or more sub-verb descriptors, each with: name, description, arguments + (typed, bounded), required/optional flags, output schema; +- maximum total projection byte size: 64 KiB per Provider. + +The CLI discovers projections by calling `InspectSchema` on each Ready +Provider with a `--deadline 2s` hard deadline per call. Missing or slow +Providers produce a visible warning but do not block CLI startup. + +Custom commands are rendered as: + +``` +d2b [args...] +``` + +or, for Providers whose top-level name matches a built-in command name, as: + +``` +d2b provider run [args...] +``` + +**Built-in name collision rule:** A Provider-projected command name that +collides with a built-in top-level verb is always rejected at Provider +install/bind time. Providers cannot shadow `get`, `list`, `watch`, +`create`, `update-spec`, `delete`, `status`, `host`, `guest`, `process`, +`exec`, `shell`, `volume`, `network`, `device`, `user`, `credential`, +`provider`, `zone`, `activation`, `audit`, `op`, `auth`, or `complete`. + +**Retained built-in Provider commands:** The following current v3 Provider- +specific verbs are retained as first-class built-in commands because they have +stable existing tests and wire contracts: + +| Retained built-in | Provider | Successor | +| --- | --- | --- | +| `d2b audio status/mic/speaker/off` | `audio-pipewire` | Retained via Provider CLI projection | +| `d2b clipboard arm` | `clipboard-wayland` | Retained via Provider CLI projection | +| `d2b console ` | Guest serial console | `d2b guest console ` | +| `d2b vm display list/close` | `display-wayland` | Retained via Provider CLI projection | + +**Current v3 source (audio):** `cmd_audio` at `packages/d2b/src/lib.rs`; `AudioArgs`, +`AudioCommand`. Evidence class: `implemented-and-reachable`. + +**Current v3 source (clipboard):** `cmd_clipboard_arm` at line 2555. Evidence +class: `implemented-and-reachable`. + +**Provider projection loading:** Provider CLI projections are loaded lazily on +demand. A projection is fetched from the Provider's InspectSchema endpoint the +first time that Provider's projected subcommand is invoked or included in +`--help` output for `d2b provider`. Each fetch is bounded by the per-Provider +2-second deadline. The result is cached for the duration of the current CLI +invocation only. No disk cache or cross-invocation cache is maintained. Startup +latency for non-provider commands is zero. + +## `d2b zone` — Zone resource commands + +``` +d2b zone get [] # omitting name fetches the current Zone self resource +d2b zone list # local ZoneLink resources (child Zones) +d2b zone status [] [--watch] [--deadline ] +``` + +All accept `[--json | --human]`. + +**Successor to:** `d2b realm list`, `d2b realm inspect `. + +**Current v3 source:** `cmd_realm_list`, `cmd_realm_inspect` at +`packages/d2b/src/lib.rs:5942,5958`. These read the **static Nix-generated file** +`realm-entrypoints.json` (path constant in `lib.rs`) via `realm_policy_rows_raw()` — +NOT a live daemon API call. The file contains a `RealmEntrypointDocument { entries: +BTreeMap }` (from `d2b-realm-router` crate); entries +have `mode: host-resident|gateway-backed` and optional `gateway` VM name. +`cmd_realm_list` emits `RealmListOutputV1 { realms: Vec }`; +`cmd_realm_inspect` emits `RealmInspectOutputV1 { realm: RealmPolicyOutputV1 }`. +`RealmPolicyOutputV1` fields: `realm` (= `RealmId`), `mode`, `gateway_vm`, `gateway_target`, +`gateway_state`, `cross_realm_policy`, `credential_boundary` (from +`packages/d2b-contracts/src/cli_output.rs:345`). + +Evidence class: `implemented-and-reachable` for realm list/inspect (static file read); +`ADR-only` for Zone/ZoneLink resource API (live daemon query replacing static file). + +**Replacement/deletion:** `d2b realm enter` and `d2b realm run` are replaced by +`d2b guest start ` plus `d2b exec run -- `. +See migration table. + +## `d2b quota` — Quota resource commands + +Maps to the `Quota` ResourceType. Quota resources define resource-consumption +limits for a Zone or a subset of its resources. Quota and EmergencyPolicy are +separate resources; quota commands do not operate on `Zone.spec` (which is empty). + +``` +d2b quota get [--zone ] [--json | --human] +d2b quota list [--zone ] [--json | --human] +d2b quota status [--zone ] [--watch] [--deadline ] [--json | --human] +d2b quota create [--zone ] [--spec-file | --spec-stdin] [--json | --human] +d2b quota update-spec [--zone ] [--revision ] [--spec-file | --spec-stdin] [--json | --human] +d2b quota delete [--zone ] [--revision ] [--json | --human] +``` + +**Successor to:** no current v3 equivalent. + +**Evidence class:** `ADR-only`. Target implemented by standard resource verb +infrastructure (ADR046-cli-002). + +## `d2b emergency-policy` — EmergencyPolicy resource commands + +Maps to the `EmergencyPolicy` ResourceType. EmergencyPolicy resources define +Zone-wide emergency operational modes (e.g. forced shutdown, isolation, +credential revocation). They are separate resources from Zone, Host, and Guest; +`Zone.spec` is empty and carries no policy fields. + +``` +d2b emergency-policy get [--zone ] [--json | --human] +d2b emergency-policy list [--zone ] [--json | --human] +d2b emergency-policy status [--zone ] [--watch] [--deadline ] [--json | --human] +d2b emergency-policy create [--zone ] [--spec-file | --spec-stdin] [--json | --human] +d2b emergency-policy update-spec [--zone ] [--revision ] [--spec-file | --spec-stdin] [--json | --human] +d2b emergency-policy delete [--zone ] [--revision ] [--json | --human] +``` + +**Successor to:** no current v3 equivalent. + +**Evidence class:** `ADR-only`. Target implemented by standard resource verb +infrastructure (ADR046-cli-002). + +**Help text** for both `d2b quota` and `d2b emergency-policy` MUST note that +`Zone.spec` is empty; quota limits and emergency policy are configured via +their own resource types, not through Zone fields. + +**Tests:** + +| Layer | Test | What it proves | +| --- | --- | --- | +| Unit | `d2b quota get/list/status/create/update-spec/delete` parse and route correctly | Quota resource noun wired | +| Unit | `d2b emergency-policy get/list/status/create/update-spec/delete` parse and route correctly | EmergencyPolicy resource noun wired | +| Unit | `Quota` and `EmergencyPolicy` accepted as frozen standard types (local validation) | Compile-time type list complete | +| Unit | `d2b get Quota/default` routes to Zone catalog without a round-trip for type validation | Local type validation path | +| Unit | `d2b get EmergencyPolicy/lockdown` routes correctly | Local type validation path | + +## `d2b activation` — activation-nixos Provider commands + +The `activation-nixos` Provider projects these CLI commands. They operate on +the `activation-nixos`-specific ResourceTypes. + +### `d2b activation build ` + +Non-destructive eval + build of the Guest NixOS toplevel. + +``` +d2b activation build / [--zone ] + [--json | --human] +``` + +**Successor to:** `d2b build `. + +**Current v3 source:** `cmd_build` at `packages/d2b/src/lib.rs`. Evidence +class: `implemented-and-reachable`. + +### `d2b activation generations ` + +``` +d2b activation generations / [--zone ] + [--json | --human] +``` + +**Successor to:** `d2b generations `. + +### `d2b activation switch ` + +Atomically activate a new closure. + +``` +d2b activation switch / [--zone ] + [--dry-run | --apply] + [--json | --human] +``` + +**Successor to:** `d2b switch `. + +### `d2b activation boot ` + +Stage a closure for next boot only. + +``` +d2b activation boot / [--zone ] + [--dry-run | --apply] + [--json | --human] +``` + +**Successor to:** `d2b boot `. + +### `d2b activation test ` + +Activate with rollback on reboot. + +``` +d2b activation test / [--zone ] + [--dry-run | --apply] + [--json | --human] +``` + +**Successor to:** `d2b test `. + +### `d2b activation rollback ` + +Roll back to previous generation. + +``` +d2b activation rollback / [--zone ] + [--dry-run | --apply] + [--json | --human] +``` + +**Successor to:** `d2b rollback `. + +### `d2b activation gc` + +Garbage-collect hardlink farms. + +``` +d2b activation gc [--zone ] + [--dry-run | --apply] + [--json | --human] +``` + +**Successor to:** `d2b gc`. + +### `d2b activation migrate` + +``` +d2b activation migrate [--zone ] + [--dry-run | --apply] + [--json | --human] +``` + +**Successor to:** `d2b migrate`. + +### `d2b activation keys` — managed SSH key lifecycle + +``` +d2b activation keys list [--json | --human] +d2b activation keys show [--json | --human] +d2b activation keys rotate [--dry-run | --apply] [--json | --human] +d2b activation trust [--json | --human] +d2b activation rotate-known-host [--json | --human] +``` + +**Successor to:** `d2b keys list/show/rotate`, `d2b trust `, +`d2b rotate-known-host `. + +**Current v3 source:** `cmd_keys_list`, `cmd_keys_show`, `cmd_keys_rotate`, +`cmd_keys_trust`, `cmd_keys_rotate_known_host` at +`packages/d2b/src/lib.rs:986ff`. Evidence class: `implemented-and-reachable`. + +### `d2b activation config` — guest-editable config lifecycle + +``` +d2b activation config sync [--dry-run] [--json] +d2b activation config diff --against [--json] +d2b activation config approve --to [--json] +d2b activation config reject [--json] +d2b activation config status [--json] +``` + +**Successor to:** `d2b config sync/diff/approve/reject/status`. + +**Current v3 source:** `ConfigCommand` variants; `cmd_config_sync` and +related at `packages/d2b/src/lib.rs:2699ff`. Evidence class: +`implemented-and-reachable`. + +**Retained behavior:** guest-control transport for sync (no SSH); staging file +lifecycle; diff/approve/reject workflow. + +### `d2b guest console ` + +Foreground serial console bridge for headless Guests. + +``` +d2b guest console [--zone ] +``` + +**Successor to:** `d2b console `. + +**Current v3 source:** `cmd_console` at `packages/d2b/src/lib.rs`. Evidence +class: `implemented-and-reachable`. + +## `d2b audit` — audit log streaming + +``` +d2b audit [--zone ] + [--strict] + [--deadline ] + [--json | --human] +``` + +**Successor to:** `d2b audit`. + +**Current v3 source:** `cmd_audit` at `packages/d2b/src/lib.rs`. Evidence +class: `implemented-and-reachable`. + +**Retained behavior:** streaming audit lines; `--strict` (exit non-zero on +parse error); bounded line lengths; redacted payloads. + +## `d2b op` — operation inspection + +``` +d2b op inspect [--zone ] + [--operation-id ] + [--trace-id ] + [--json | --human] +``` + +**Successor to:** `d2b op inspect`. + +**Current v3 source:** `cmd_op_inspect` at `packages/d2b/src/lib.rs:6098`. +Evidence class: `implemented-and-reachable`. + +## `d2b auth` — authorization status + +``` +d2b auth status [--zone ] + [--json | --human] +``` + +**Successor to:** `d2b auth status`. + +**Current v3 source:** `cmd_auth_status` at `packages/d2b/src/lib.rs`. Evidence +class: `implemented-and-reachable`. + +**Retained behavior:** SO_PEERCRED group-membership check display; no test-uid +argument exposed in v3. + +## `d2b complete` — shell completion + +``` +d2b complete # emit completion script (bash | zsh | fish) +d2b complete --list-commands # emit available top-level commands as JSON +``` + +Completion discovery fetches Provider CLI projections with a hard 2s aggregate +deadline. Missing Providers produce a stable degraded list without error. +Completion output is bounded: maximum 256 KiB total. + +**Current v3 source:** No shell completion implemented in v3 baseline +(`packages/d2b/src/lib.rs` has no `clap_complete` reference). Evidence +class: `ADR-only`. + +## v2 command surface removed at 3.0 clean break + +All v2 aliases and predecessor commands are deleted at the d2b 3.0 clean break. +There are no executable aliases in 3.0. A `d2b migrate-check` diagnostic +command may explain replacements, but it does not dispatch to v2 behavior. +The table below records each removed command and its v3 successor for +documentation and test-removal tracking only. + +| Removed v2 command | v3 successor | +| --- | --- | +| `d2b up ` | `d2b guest start ` | +| `d2b down ` | `d2b guest stop ` | +| `d2b restart ` | `d2b guest restart ` | +| `d2b list` | `d2b guest list` | +| `d2b status []` | `d2b guest status ` | +| `d2b vm start/stop/restart/list/status` | `d2b guest start/stop/restart/list/status` | +| `d2b vm exec -- ` | `d2b exec run/attach` | +| `d2b vm exec list/logs/status/kill` | `d2b exec list/logs/status/kill` | +| `d2b realm list` | `d2b zone list` | +| `d2b realm inspect ` | `d2b zone get ` | +| `d2b realm enter ` | `d2b shell open ` | +| `d2b realm run -- ` | `d2b exec run -- ` | +| `d2b usb attach/detach/probe` | `d2b device usb attach/detach/probe` | +| `d2b usb security-key ` | `d2b device security-key ` | +| `d2b store verify ` | `d2b volume verify /` | +| `d2b keys list/show/rotate` | `d2b activation keys list/show/rotate` | +| `d2b trust ` | `d2b activation trust ` | +| `d2b rotate-known-host ` | `d2b activation rotate-known-host ` | +| `d2b build ` | `d2b activation build ` | +| `d2b generations ` | `d2b activation generations ` | +| `d2b switch/boot/test/rollback ` | `d2b activation switch/boot/test/rollback ` | +| `d2b gc` | `d2b activation gc` | +| `d2b migrate` | `d2b activation migrate` | +| `d2b config sync/diff/approve/reject/status` | `d2b activation config sync/...` | +| `d2b console ` | `d2b guest console ` | +| `d2b vm display list/close` | `d2b provider run display-wayland display list/close` | + +## Async operation lifecycle, status watch, and cancel + +### `d2b op inspect` and operation IDs + +Every mutating resource API call returns an operation ID in the JSON output +envelope as `operationId`. Long-running operations (Guest start/stop, Provider +lifecycle, activation switch) also emit it in human output. + +``` +d2b op inspect --operation-id [--zone ] + [--watch] + [--deadline ] + [--json | --human] +``` + +### Deadline and cancel flags + +All commands that interact with the Zone runtime accept: + +``` +--deadline # wall deadline (e.g. 30s, 5m); default per-command +--no-deadline # suppress default deadline (use with care) +``` + +Deadline exhausted: exit code 1, `deadline-exceeded` error class, `--json` +envelope with `errorClass: "deadline-exceeded"`. + +SIGINT / SIGTERM: attempt graceful cancel of the in-flight operation, emit a +cancel event envelope, restore terminal state, then exit with code 3. + +SIGHUP: detach from interactive sessions (shell/exec attach) without killing +the remote session. + +**Current v3 source:** Deadline partially implemented: `EXIT_API_TIMEOUT` (33) +for vm-start; exec transport deadline at `EXIT_EXEC_TRANSPORT` (69). Evidence +class: `implemented-but-unwired` for general deadline; `implemented-and-reachable` +for specific paths. + +**Required delta:** Unified `deadline-exceeded` error class across all resource +API paths; SIGINT/SIGTERM cancel dispatch; `--deadline` flag on all API-backed +commands. + +## Output format + +### `--json` envelope + +Every command's `--json` output is a single JSON object (for non-streaming +commands) or a stream of newline-separated JSON objects (for `watch`, `exec +attach`, `audit`). + +Common fields in every `--json` envelope: + +```json +{ + "ok": true, + "zoneRef": "Zone/dev", + "operationId": "", + "schemaVersion": 1, + "resource": { /* canonical ResourceSpec envelope — see §Canonical ResourceSpec JSON shape */ } +} +``` + +Error envelopes: + +```json +{ + "ok": false, + "zoneRef": "Zone/dev", + "errorClass": "resource-not-found", + "message": "bounded redacted operator detail", + "remediation": "optional actionable hint" +} +``` + +The `message` and `remediation` fields are bounded (max 4096 bytes each), +UTF-8 validated, and must not contain secrets, tokens, credential bytes, argv/ +environment, terminal bytes, or host paths. + +**Stability:** JSON field names are stable across patch and minor versions. +Additive new fields may appear; a consumer must use `deny_unknown_fields = false` +on JSON deserialization. + +**Versioning:** The `--json` envelope carries `"schemaVersion": 1` in the root. +Minor bumps add optional fields; major bumps increment the version. + +### `--human` output + +Human output is for interactive terminals only. Format is not machine-parseable +and may change between patch versions. + +**Format defaulting rule:** `--json` and `--human` are mutually exclusive. When +neither is given: if stdout is a TTY, human output is produced; if stdout is +not a TTY, JSON is produced (script-friendly default). `--human` explicitly +overrides to human output even in non-TTY contexts; `--json` explicitly +overrides to JSON even in TTY contexts. + +**Current v3 source:** `stdout_is_tty()` at +`packages/d2b/src/lib.rs:10302`. Evidence class: `implemented-and-reachable`. + +## Error classes and exit codes + +### Stable error classes + +All error class strings are stable lower-kebab-case machine values. + +| Error class | Meaning | +| --- | --- | +| `resource-not-found` | Target resource does not exist | +| `resource-already-exists` | Create precondition: resource exists | +| `resource-conflict` | Optimistic concurrency mismatch | +| `resource-schema-invalid` | Spec/status payload fails schema validation | +| `ref-invalid` | ResourceRef syntax or resolution failure | +| `authorization-denied` | RBAC or structural check denied the request | +| `zone-unavailable` | Zone runtime not reachable | +| `deadline-exceeded` | Wall deadline elapsed | +| `operation-cancelled` | SIGINT/SIGTERM graceful cancel | +| `provider-unavailable` | Target Provider is not Ready | +| `exec-transport-error` | Exec vsock/session transport unreachable or deadline | +| `exec-old-generation` | VM generation does not support guest-control exec | +| `exec-capacity` | Exec session table at capacity or rate-limited | +| `exec-protocol-error` | Malformed/out-of-contract guest response | +| `exec-auth-error` | Guest-control handshake rejected | +| `exec-internal-error` | CLI/daemon-internal failure | +| `shell-transport-error` | Shell session transport failure | +| `not-implemented` | Requested verb is not implemented in this runtime version | +| `internal-error` | Unexpected CLI/runtime internal failure | +| `bundle-integrity-failure` | Zone resource bundle SHA256 pin mismatch | +| `bundle-generation-replay` | Submitted bundleGeneration ≤ last applied generation | +| `bundle-schema-mismatch` | Bundle resourceTypeSchemaDigests do not match installed schemas | +| `resource-pending-cleanup` | Resource has outstanding deletion that must complete or be force-removed | + +### Stable exit codes + +| Exit code | Class | Condition | +| --- | --- | --- | +| 0 | success | Command completed successfully | +| 1 | error | Operational failure (not-found, unavailable, zone-down, etc.) | +| 2 | usage | Argument/parsing/validation error | +| 3 | cancelled | Cancelled by SIGINT/SIGTERM/deadline (interactive streams) | +| 33 | `deadline-exceeded` | API-ready wait timeout (retained from v3: `EXIT_API_TIMEOUT`) | +| 42 | `exec-internal-error` | CLI/daemon internal exec failure (`EXIT_EXEC_INTERNAL`) | +| 69 | `exec-transport-error` | Exec transport unreachable or deadline (`EXIT_EXEC_TRANSPORT`) | +| 70 | `exec-old-generation` | Old VM generation (`EXIT_EXEC_OLD_GENERATION`, `EXIT_GUEST_CONTROL_CONFIG`) | +| 75 | `exec-capacity` | Exec session table at capacity (`EXIT_EXEC_CAPACITY`) | +| 76 | `exec-protocol-error` | Malformed guest response (`EXIT_EXEC_PROTOCOL`) | +| 77 | `exec-auth-error` | Guest-control handshake rejected (`EXIT_EXEC_AUTH`) | +| 78 | `not-implemented` | Runtime returned `not-yet-implemented` | +| 128+N | signal | Terminated by signal N (non-TTY context only) | + +Guest WIFEXITED 0–255 pass through `exec attach`/`exec wait` and can collide +with the reserved codes above. The `--json` envelope disambiguates via +`source`/`reason`/`guestExitCode`/`transportExitCode`. + +**Host-check exit code 3:** `d2b host check` uses exit code 3 for usage errors +(retained from v3 baseline `is_host_usage` check at +`packages/d2b/src/lib.rs:2405`). This is the one documented exception to the +standard exit code 2 usage rule. + +**Current v3 source:** `EXIT_API_TIMEOUT=33`, `EXIT_GUEST_CONTROL_CONFIG=70`, +`EXIT_EXEC_TRANSPORT=69`, `EXIT_EXEC_OLD_GENERATION=70`, +`EXIT_EXEC_CAPACITY=75`, `EXIT_EXEC_PROTOCOL=76`, `EXIT_EXEC_AUTH=77`, +`EXIT_EXEC_INTERNAL=42` in +`packages/d2b/src/lib.rs:92,98` and +`packages/d2b/src/exec_client.rs:32-46`. +Evidence class: `implemented-and-reachable`. + +## Streams and TTY contract + +### Attached exec/shell streams + +When `-t` / `--tty` is given: + +1. The CLI verifies stdout is a TTY (`stdout.is_terminal()`). +2. The CLI enters raw mode on the host terminal (`tcsetattr(STDIN, TCSANOW)`). +3. An RAII guard restores termios on every code path (success, error, panic, + signal). +4. The guest owns the PTY. The host streams bytes bidirectionally without + interpretation. +5. SIGWINCH triggers a resize notification to the guest. +6. SIGINT/SIGTERM/SIGHUP are forwarded as typed signals to the guest session. +7. SIGHUP in a shell context detaches without killing. +8. On disconnect, raw mode is restored before any error message is printed to + stderr. + +`--tty` is incompatible with `--json` (the JSON envelope cannot capture raw +terminal bytes). + +**Current v3 source:** `exec_client::FdStateGuard`, `exec_client::install_signals`, +`exec_client::current_window_size`, `run_shell_fsm`, `ShellOwnerTransport` in +`packages/d2b/src/lib.rs:1957,2005,2092` and +`packages/d2b/src/exec_client.rs`. Evidence class: `implemented-and-reachable`. + +### Named streams + +The Zone resource API Watch and exec/shell attach operations use named streams +over ComponentSession. The CLI does not manage credit flow directly; that is +the ComponentSession layer's responsibility. + +### Audit and watch streams + +Non-TTY streaming commands (`d2b audit`, `d2b watch`, `d2b exec logs`) +write newline-terminated JSON objects to stdout continuously. Each object is +valid JSON on its own line. Buffering is flushed after each object. The stream +closes when the server closes, the deadline is reached, or a signal is received. + +## Completion and help + +### `--help` output + +Every command and subcommand emits a usage block followed by a description. +The description is bounded (max 2048 chars per command). Process-markers, wave +tags, or internal identifiers are not present in help text. + +Dynamic Provider-projected commands appear in the top-level `--help` listing +under a `PROVIDER COMMANDS` section with a `(provider: )` annotation and +a bounded description. + +### Shell completion + +Shell completion scripts (bash, zsh, fish) are generated by `d2b complete +`. Completion discovery: + +1. Fetches the list of Ready Providers from the Zone runtime (2s deadline). +2. For each Provider with a `cliProjection`, fetches the projection (2s + deadline per call). +3. Generates completion for all built-in + projected commands. +4. Result is bounded at 256 KiB. + +A timeout or unreachable Zone runtime produces completion for built-in commands +only, with no error. + +**Current v3 source:** No completion exists in v3 baseline. Evidence class: +`ADR-only`. + +## Dynamic descriptors — safety bounds + +Provider CLI projections are Provider-supplied content. The CLI applies these +bounds before rendering any projection: + +| Bound | Limit | +| --- | --- | +| Total bytes per projection | 64 KiB | +| Top-level subcommand name length | 32 bytes; `^[a-z][a-z0-9-]*$` | +| Sub-verb name length | 32 bytes; `^[a-z][a-z0-9-]*$` | +| Number of sub-verbs per Provider | 32 | +| Argument name length | 64 bytes | +| Number of arguments per sub-verb | 16 | +| Description length per sub-verb | 512 bytes | +| Fetch deadline per Provider | 2 s | +| Total fetch deadline (all Providers) | 10 s | + +A projection that violates any bound is silently skipped with a single +`d2b: provider cli-projection exceeded limit` line to stderr. No +Provider projection can cause the CLI to crash, loop, allocate unbounded memory, +or emit shell-injectable text. + +Completion strings are HTML/shell-escaped before inclusion in completion +scripts. Newlines in dynamic strings are replaced with a space. + +## No provider code in CLI + +The CLI binary must not: + +- import Provider implementation crates; +- import `d2bd`, broker, or Zone-store implementation crates; +- invoke Provider subprocesses; +- read Provider-specific configuration files (only the standard Zone socket and + optional env overrides); +- perform sandbox compilation or minijail argument construction; +- hold or transmit credential bytes. + +The CLI may import `d2b-contracts` for shared DTO types, `d2b-resource-api` for +the typed async client, and `d2b-session`/`d2b-session-unix` for +ComponentSession transport. + +## Current verb/target/contract migration table + +The following table maps every current v3 CLI verb to its v3 status. + +| Current verb | Status | v3 successor | Current source | Evidence class | +| --- | --- | --- | --- | --- | +| `d2b list` | Deleted at 3.0 | `d2b guest list` | `cmd_list` @ lib.rs:3560 | reachable | +| `d2b status []` | Deleted at 3.0 | `d2b guest status ` | `cmd_status` @ lib.rs:3587 | reachable | +| `d2b up ` | Deleted at 3.0 | `d2b guest start ` | `cmd_vm_start` via alias | reachable | +| `d2b down ` | Deleted at 3.0 | `d2b guest stop ` | `cmd_vm_stop` via alias | reachable | +| `d2b restart ` | Deleted at 3.0 | `d2b guest restart ` | `cmd_vm_restart` via alias | reachable | +| `d2b vm start ` | Deleted at 3.0 | `d2b guest start ` | `cmd_vm_start` @ lib.rs:6675 | reachable | +| `d2b vm stop ` | Deleted at 3.0 | `d2b guest stop ` | `cmd_vm_stop` @ lib.rs:6690 | reachable | +| `d2b vm restart ` | Deleted at 3.0 | `d2b guest restart ` | `cmd_vm_restart` @ lib.rs:6690 | reachable | +| `d2b vm list` | Deleted at 3.0 | `d2b guest list` | `cmd_vm_list` @ lib.rs | reachable | +| `d2b vm status ` | Deleted at 3.0 | `d2b guest status ` | `cmd_vm_status` @ lib.rs:6868 | reachable | +| `d2b vm exec -- ` | Deleted at 3.0 | `d2b exec run/attach` | `cmd_vm_exec` @ lib.rs:7166 | reachable | +| `d2b vm exec list` | Deleted at 3.0 | `d2b exec list` | `cmd_vm_exec_management` @ lib.rs:7397 | reachable | +| `d2b vm exec logs ` | Deleted at 3.0 | `d2b exec logs` | same | reachable | +| `d2b vm exec status ` | Deleted at 3.0 | `d2b exec status` | same | reachable | +| `d2b vm exec kill ` | Deleted at 3.0 | `d2b exec kill` | same | reachable | +| `d2b vm display list/close` | Provider-projected | `audio-pipewire`/`display-wayland` projection | `cmd_vm_display` @ lib.rs | reachable | +| `d2b shell [attach]` | Deleted at 3.0 | `d2b shell open ` | `cmd_shell` @ lib.rs:1788 | reachable | +| `d2b shell list` | Deleted at 3.0 | `d2b shell list` | same | reachable | +| `d2b shell detach` | Deleted at 3.0 | `d2b shell detach` | same | reachable | +| `d2b shell kill` | Deleted at 3.0 | `d2b shell kill` | same | reachable | +| `d2b usb attach ` | Deleted at 3.0 | `d2b device usb attach` | `cmd_usb_attach` @ lib.rs | reachable | +| `d2b usb detach ` | Deleted at 3.0 | `d2b device usb detach` | `cmd_usb_detach` | reachable | +| `d2b usb probe` | Deleted at 3.0 | `d2b device usb probe` | `cmd_usb_probe` | reachable | +| `d2b usb security-key status` | Deleted at 3.0 | `d2b device security-key status` | `cmd_usb_sk_status` | reachable | +| `d2b usb security-key sessions` | Deleted at 3.0 | `d2b device security-key sessions` | `cmd_usb_sk_sessions` | reachable | +| `d2b usb security-key cancel` | Deleted at 3.0 | `d2b device security-key cancel` | `cmd_usb_sk_cancel` | reachable | +| `d2b usb security-key test ` | Deleted at 3.0 | `d2b device security-key test` | `cmd_usb_sk_test` | reachable | +| `d2b console ` | Deleted at 3.0 | `d2b guest console ` | `cmd_console` | reachable | +| `d2b audio status/mic/speaker/off` | Provider-projected | `audio-pipewire` projection | `cmd_audio` @ lib.rs | reachable | +| `d2b audit` | **Retained** | `d2b audit` | `cmd_audit` @ lib.rs | reachable | +| `d2b host check` | **Retained** | `d2b host check` | `cmd_host_check` @ lib.rs:4271 | reachable | +| `d2b host prepare` | **Retained** | `d2b host prepare` | `cmd_host_prepare` @ lib.rs:4468 | reachable | +| `d2b host destroy` | **Retained** | `d2b host destroy` | `cmd_host_destroy` @ lib.rs:4549 | reachable | +| `d2b host doctor` | **Retained** | `d2b host doctor` | `cmd_host_doctor` @ lib.rs:4752 | reachable | +| `d2b host migrate-storage` | Deleted after reset | *(none; storage ADR 0034 reset)* | `cmd_host_migrate_storage` @ lib.rs:4867 | reachable | +| `d2b host install` | **Retained** | `d2b host install` | `cmd_host_install` @ lib.rs:5007 | reachable | +| `d2b host reconcile` | **Retained** | `d2b host reconcile` | `cmd_host_reconcile` @ lib.rs:5070 | reachable | +| `d2b host validate` | **Retained** | `d2b host validate` | `cmd_host_validate` @ lib.rs:4941 | reachable | +| `d2b host shutdown-hook` | **Retained** internal | `d2b host shutdown-hook` (hidden) | `cmd_host_shutdown_hook` @ lib.rs:4644 | reachable | +| `d2b realm list` | Deleted at 3.0 | `d2b zone list` | `cmd_realm_list` @ lib.rs:5942 | reachable | +| `d2b realm inspect ` | Deleted at 3.0 | `d2b zone get ` | `cmd_realm_inspect` @ lib.rs:5958 | reachable | +| `d2b realm enter ` | Deleted at 3.0 | `d2b shell open ` | `cmd_realm_enter` @ lib.rs:6129 | reachable | +| `d2b realm run -- ` | Deleted at 3.0 | `d2b exec run -- ` | `cmd_realm_run` @ lib.rs:6143 | reachable | +| `d2b op inspect` | **Retained** | `d2b op inspect` | `cmd_op_inspect` @ lib.rs:6098 | reachable | +| `d2b auth status` | **Retained** | `d2b auth status` | `cmd_auth_status` @ lib.rs | reachable | +| `d2b store verify ` | Deleted at 3.0 | `d2b volume verify` | `cmd_store_verify` @ lib.rs | reachable | +| `d2b keys list/show/rotate` | Deleted at 3.0 | `d2b activation keys list/show/rotate` | `cmd_keys_*` @ lib.rs:986 | reachable | +| `d2b trust ` | Deleted at 3.0 | `d2b activation trust ` | `cmd_keys_trust` | reachable | +| `d2b rotate-known-host ` | Deleted at 3.0 | `d2b activation rotate-known-host` | `cmd_keys_rotate_known_host` | reachable | +| `d2b build ` | Deleted at 3.0 | `d2b activation build` | `cmd_build` | reachable | +| `d2b generations ` | Deleted at 3.0 | `d2b activation generations` | `cmd_generations` | reachable | +| `d2b switch ` | Deleted at 3.0 | `d2b activation switch` | `cmd_switch` | reachable | +| `d2b boot ` | Deleted at 3.0 | `d2b activation boot` | `cmd_boot` | reachable | +| `d2b test ` | Deleted at 3.0 | `d2b activation test` | `cmd_test` | reachable | +| `d2b rollback ` | Deleted at 3.0 | `d2b activation rollback` | `cmd_rollback` | reachable | +| `d2b gc` | Deleted at 3.0 | `d2b activation gc` | `cmd_gc` | reachable | +| `d2b migrate` | Deleted at 3.0 | `d2b activation migrate` | `cmd_migrate` | reachable | +| `d2b config sync/diff/approve/reject/status` | Deleted at 3.0 | `d2b activation config *` | `ConfigCommand` @ lib.rs:2705 | reachable | +| `d2b clipboard arm` | Provider-projected | `clipboard-wayland` projection | `cmd_clipboard_arm` @ lib.rs | reachable | +| `d2b clipboard picker` | **Deleted** (was already deprecated) | *(none)* | deprecated notice @ lib.rs:2424 | reachable | + +### Removal notes + +All commands listed as "Deleted at 3.0" in the full verb/target table +below are deleted at the 3.0 clean break. The removal criterion for each is: +its live v3 successor is implemented and tested, and the v2 source path +(`cmd_*` function) is deleted in the same wave. + +`d2b host migrate-storage` is deleted after the v3 storage reset completes; it +has no v3 successor because the layout cutover it served is a one-time v1→v2 +migration. + +`d2b clipboard picker` is already removed from the dispatch table in the v3 +baseline (only a deprecation notice remains at `lib.rs:2424`). + +## Current-code fit + +| Item | Treatment | +| --- | --- | +| Current anchor | `packages/d2b/src/lib.rs` (18 599 lines), `exec_client.rs`, `terminal_client.rs`, `target_routing.rs`, `human_render.rs`, `status_read_model.rs` at baseline `b5ddbed6`; supplementary: `packages/d2b-contracts/src/public_wire.rs` (`ListEntry`, `VmStatus`, `VmLifecycleState`, `ShellOp`, `ShellOpResponse`, `WorkloadPublicSummary.execution_posture`), `packages/d2b-contracts/src/cli_output.rs` (`RealmListOutputV1`, `RealmInspectOutputV1`, `RealmPolicyOutputV1`), `packages/d2b-contracts/src/unsafe_local_wire.rs` (unsafe-local helper wire protocol), `packages/d2b-unsafe-local-helper/src/` (runtime helper), `packages/d2b-realm-core/src/workload.rs` (`WorkloadProviderKind`, `IsolationPosture::UnsafeLocal`, `WorkloadExecutionPosture`), `packages/d2b-core/src/processes.rs` (`ProcessRole`, `VmProcessDag`), `nixos-modules/options-realms-workloads.nix` (`kind = "unsafe-local"`), `nixos-modules/unsafe-local-workloads-json.nix`. Note: baseline uses pre-ADR 0046 terminology throughout (Realm/WorkloadId/ProcessRole/VmProcessDag etc.); see "Baseline terminology mapping" above. Unsafe-local workloads are `Host` resources NEVER `Guest`; see §Unsafe-local Host resources. | +| Evidence class | All dispatched NativeCommand variants are `implemented-and-reachable`; Zone/resource API routing, Provider CLI projection, `d2b complete`, `d2b exec run/attach/wait`, `d2b shell open/attach`, `d2b guest/process/volume/network/device/user/credential/provider/zone/activation` are `ADR-only` | +| Behavior retained | All documented exit codes; `--json`/`--human` mutual exclusion with non-TTY JSON default and `--human` explicit override; TTY raw-mode RAII; SIGWINCH/SIGINT/SIGTERM forwarding; dry-run/apply pattern; daemon-down/not-implemented envelopes; host-check exit 3; bounded output; no bash fallback; no SSH for exec/shell | +| Required delta | Zone context discovery; ResourceRef argument parsing; resource API client routing; standard `get/list/watch/create/update-spec/delete/status` verb set; `d2b exec`, `d2b shell`, `d2b guest`, `d2b process`, `d2b volume`, `d2b network`, `d2b device`, `d2b user`, `d2b credential`, `d2b provider`, `d2b zone`, `d2b quota`, `d2b emergency-policy`, `d2b activation`, `d2b complete`; Provider CLI projection loading; unified deadline/cancel; JSON schema version field; `--zone` flag | +| Reuse path | Retain current `run()` entry, clap dispatch, `CliFailure`/`report_failure`, `exec_client.rs` FSM/signals/TTY, `terminal_client.rs` traits; adapt `Context` for Zone/socket discovery; replace seqpacket with ComponentSession resource API client | +| Replacement/deletion | Raw seqpacket send/recv helpers replaced only after resource API client paths are live and tested; old `public_wire` / `broker_wire` wire envelopes retired per owning resource ResourceType wave | +| Feasibility proof | Existing exec FSM/terminal/signal test suite; clap dispatch tests; all 150+ unit tests passing at baseline | +| Future owner | Work items below | + +## Implementation work items + +### ADR046-cli-001 + +| Field | Value | +| --- | --- | +| Work item ID | `ADR046-cli-001` | +| Dependency/owner | ADR046-identities-001, ADR046-api-001; CLI crate owner | +| Current source | `packages/d2b/src/lib.rs`: `Context::from_env` (reads `D2B_PUBLIC_SOCKET` env or `/run/d2b/public.sock` default; single flat socket path — no zone-qualified path, no `ZoneContext`), `NativeCli`, `NativeCommand`, `dispatch`, `CliFailure`, `report_failure`, `stdout_is_tty`; socket path model: old `Context { public_socket, broker_socket }` → target: `ZoneContext { zone_name, socket_path, session_client }` | +| Reuse source | main `a1cc0b2d` — copy/adapt these exact symbols: (1) `packages/d2b-client/src/client.rs` `Client`, `ConnectedClient`, `CallOptions`, `CancellationToken`, `MetadataInput`, `RetryPolicy`, `Response` — copy unchanged as the async client foundation; (2) `packages/d2b-client/src/host_socket.rs` `HostSocketConnector::from_seqpacket_fd`, `local_daemon_endpoint_identity` — adapt: replace fixed `d2b.daemon.v2` service lookup with zone-scoped service identity; replace hardcoded `RealmPath::parse("local-root")` with `Zone/`-derived target; (3) `packages/d2b-daemon-access/src/component_session.rs` `LocalUnixDaemonAccess::connect_component_session()` connect chain — adapt: replace `TargetInput::LocalRoot(realm)` with the v3 Zone target variant; adapt socket path discovery for per-Zone paths; (4) `packages/d2b-client/src/session.rs` `ComponentSessionConnector`, `ConnectedSession`, `NamedStream`, `SessionCall`, `SessionReply`, `SharedDriver` — copy unchanged; (5) `packages/d2b-client/src/target.rs` `RouteTable`, `RouteRecord`, `TargetInput`, `ResolvedTarget`, `TransportKind`, `TransportSelection` — copy unchanged, excluding `TargetInput::Realm/Workload/Provider` variants which carry ADR 0045 assumptions; (6) `packages/d2b-contracts/src/v2_component_session.rs` `LimitProfile::local_default()` constants: `MAX_REQUEST_LIFETIME_MS=900000`, `LOCAL_HANDSHAKE_DEADLINE_MS=5000`, `MAX_RECONNECT_ATTEMPTS=10`, `MAX_ACTIVE_NAMED_STREAMS=128`, `MAX_LOGICAL_MESSAGE_BYTES=1048576`, `named_stream_queue_bytes=262144`, `aggregate_named_stream_queue_bytes=4194304` — copy unchanged; these bound every CLI deadline and stream operation | +| Reuse action | adapt | +| Destination | `packages/d2b/src/lib.rs`, `packages/d2b/src/context.rs`, `packages/d2b/src/dispatch.rs` | +| Detailed design | Introduce `ZoneContext` (zone name, socket path, ComponentSession client); implement `--zone`/`D2B_ZONE`/nearest-socket discovery using adapted `LocalUnixDaemonAccess::connect_component_session()` chain; introduce `ResourceRef` argument parser; introduce unified `--json`/`--human`/`--deadline` flag infrastructure bounded by `MAX_REQUEST_LIFETIME_MS=900s`; freeze `--json` schema version 1; stabilize exit code table. Excluded ADR 0045 assumptions: `TargetInput::Realm`, `TargetInput::Workload`, `TargetInput::Provider` variants; `RealmPath::parse("local-root")` / `RealmId::derive` pattern; `RealmPath`-based service owner types. | +| Integration | All command functions receive `ZoneContext`; resource API client (`Client`) is injected for testing | +| Data migration | None; context discovery replaces env-var path lookups | +| Validation | Zone-unavailable/fallback tests; ResourceRef parse/reject vectors; exit-code round-trip tests; TTY detection tests; adapt `client.rs:typed_routes_select_exact_transport_without_fallback` (line 1053), `connector_discovers_and_authenticates_the_driver_generation` (line 1254), `daemon_transport_rejects_ancillary_data_and_oversized_packets` (line 1312) | +| Removal proof | Old `Context` struct removed only after all command functions use `ZoneContext` | + +### ADR046-cli-002 + +| Field | Value | +| --- | --- | +| Work item ID | `ADR046-cli-002` | +| Dependency/owner | ADR046-cli-001, ADR046-api-001; CLI crate owner | +| Current source | `packages/d2b/src/lib.rs`: `cmd_vm_start`, `cmd_vm_stop`, `cmd_vm_restart`, `cmd_vm_status`, `cmd_vm_list`, `cmd_list`, `cmd_status`; wire types: `ListResponse { vms: Vec }`, `StatusResponse { entries: Vec }`, `VmLifecycleState` (old: Stopped/Starting/Booted/Running/Stopping/Restarting/Failed/Unknown) from `packages/d2b-contracts/src/public_wire.rs:2152,2158,2605`; `ListEntry.vm: String` = `WorkloadId`; `VmStatus.lifecycle.state: VmLifecycleState` → target Guest `phase` (Pending\|Ready\|Succeeded\|Degraded\|Failed\|Deleted\|Unknown; Starting/Stopping/Restarting → conditions/reasons); `WorkloadPublicSummary.execution_posture: WorkloadExecutionPosture` from `public_wire.rs:267` (carries `IsolationPosture`; unsafe-local entries have `IsolationPosture::UnsafeLocal`) | +| Reuse source | main `a1cc0b2d` — copy/adapt: (1) `packages/d2b-client/src/daemon_service.rs` `DaemonClient::lifecycle()` (line 210), `DaemonClient::list_workloads()` (line 148), `DaemonClient::inspect()` (line 179); `DaemonMethod::Apply/Start/Stop/Restart/ListWorkloads` variants (lines 31-46) — adapt: replace `WorkloadLifecycleRequest`/`WorkloadName` with `Guest/` ResourceRef; replace `TargetInput::Workload`-scoped calls with zone-root resource API calls; (2) `packages/d2b-contracts/src/generated_v2_services/daemon.rs` `WorkloadLifecycleProjection`, `DeploymentProjection`, `RuntimeProjection` — adapt field mapping to Guest resource spec/status; (3) `packages/d2b/src/lib.rs` `cmd_launch` (`LaunchArgs`) — adapt: the typed ComponentSession target resolution pattern applies but realm/workload-model types (`RealmPath`, `WorkloadName`) are excluded; behavior selected: idempotent apply with dry-run/apply precondition | +| Reuse action | adapt | +| Destination | `packages/d2b/src/guest.rs` (`d2b guest start/stop/restart/list/status`); unsafe-local workloads go to `packages/d2b/src/host.rs` (`d2b host list/status/get`), NOT guest.rs | +| Detailed design | Route Guest lifecycle (WorkloadProviderKind: LocalVm/QemuMedia/ProviderManaged) through `d2b.resource.v3` Get/UpdateSpec/Watch; map dry-run/apply to resource API precondition; `--no-wait-ready` exits on accepted; with-wait uses `d2b status --watch` loop. WorkloadProviderKind::UnsafeLocal entries MUST route to `d2b host` commands only; any code path that would return an unsafe-local entry from `d2b guest list` is a correctness violation. v2 commands (`d2b up/down/restart/list/status`, `d2b vm start/stop/restart/list/status`) are deleted at 3.0; `d2b migrate-check` explains replacements. | +| Integration | ZoneContext → resource API client → Guest resource; status watch uses Watch stream | +| Data migration | None | +| Validation | Dry-run/apply/wait/no-wait-ready tests; zone-unavailable degraded path; JSON output schema tests; confirm v2 command paths are absent (compilation failure if any cmd_vm_start/stop alias re-introduced) | +| Removal proof | Old `cmd_vm_start/stop/restart` seqpacket paths removed after Guest resource API paths are live with full test coverage | + +### ADR046-cli-003 + +| Field | Value | +| --- | --- | +| Work item ID | `ADR046-cli-003` | +| Dependency/owner | ADR046-cli-001, ADR046-api-001; CLI crate owner | +| Current source | `packages/d2b/src/exec_client.rs` (entire FSM); `packages/d2b/src/terminal_client.rs`; `packages/d2b/src/lib.rs`: `cmd_vm_exec`, `cmd_vm_exec_management`, `VmExecArgs` | +| Reuse source | main `a1cc0b2d` — copy/adapt: (1) `packages/d2b-client/src/daemon_service.rs` `DaemonClient::open_terminal(method, resource_id, operation_id, selection, options, cancellation)` returning `DaemonTerminal` (line 248) — copy-then-adapt: replaces the existing seqpacket Exec call; `DaemonMethod::Exec` (line 40) maps to `d2b exec run`; (2) `packages/d2b-contracts/src/generated_v2_services/terminal.rs` `TerminalOpenRequest`, `TerminalOpenResponse`, `TerminalStreamFrame`, `TerminalSelection`, `TerminalKind` — copy unchanged as the named-stream terminal wire protocol; (3) `packages/d2b-client/src/session.rs` `NamedStream` (`send`, `receive`, `cancel`, `close`, `is_terminal`) — copy unchanged; provides async stdio routing and cancel on disconnect; (4) `packages/d2b-client/src/daemon_service.rs` `GuestClient::inspect_exec()`, `cancel_exec()`, `open_exec_retained_log()` — adapt: rename from `WorkloadName`/`GuestClient` to `EphemeralProcess/` resource API; (5) `packages/d2b-session/src/streams.rs` `NamedStreamMux` limits (`MAX_ACTIVE_NAMED_STREAMS=128`, `named_stream_queue_bytes=262144`) — copy unchanged; bounds the exec stream pipeline; (6) `packages/d2b-session/src/cancellation.rs` `Cancellation`, `RequestRegistry` — copy unchanged; provides generation-bound per-request cancel; tests to adapt/import: `client.rs:terminal_uses_server_stream_and_validates_bidirectional_lifecycle`, `terminal_rejects_response_generation_and_non_server_stream_ids`, `invalid_terminal_selection_is_rejected_before_open_rpc`, `guest_exec_management_preserves_typed_state_and_cancel_correlation`, `guest_retained_log_open_binds_range_resource_and_selection`, `named_stream_fragments_over_queue_credit_and_has_terminal_actions`, `named_stream_grants_only_consumed_data_and_releases_blocked_sender`; excluded ADR 0045 assumptions: `GuestClient` internal `TargetInput::Workload`-scoped vsock routing (guest-control proxy uses old `WorkloadName`/`RealmPath` — these are excluded; v3 routes through resource API only) | +| Reuse action | copy-then-adapt | +| Destination | `packages/d2b/src/exec.rs` (`d2b exec run/attach/wait/status/list/logs/kill`) | +| Detailed design | Map EphemeralProcess resource lifecycle; `exec run` creates resource and returns ref; `exec attach` opens named stream via adapted `DaemonClient::open_terminal(DaemonMethod::Exec, ...)` → `DaemonTerminal`; retain full `exec_client.rs` FSM and TTY machinery from baseline; retain `--json` envelope fields `source`/`reason`/`guestExitCode`/`signal`/`transportExitCode`; retain reserved exit codes 42/69/70/75/76/77. v2 commands (`d2b vm exec *`) are deleted at 3.0; no dispatch wiring. Excluded ADR 0045: `GuestClient` vsock/guest-control proxy path; `TargetInput::Workload`; old `WorkloadName`-keyed exec management. | +| Integration | ZoneContext → EphemeralProcess Create → named stream attach via `DaemonClient::open_terminal` | +| Data migration | None | +| Validation | Full `exec_client.rs` test suite migrated; adapted tests from main `client.rs:terminal_*` and `guest_exec_*`; TTY/raw-mode/RAII/signal tests; `--json` envelope/disambiguation tests; capacity/transport/auth/protocol exit-code tests; confirm v2 `cmd_vm_exec` path is absent | +| Removal proof | Old `cmd_vm_exec` seqpacket path removed after `d2b exec` paths have equivalent coverage | + +### ADR046-cli-004 + +| Field | Value | +| --- | --- | +| Work item ID | `ADR046-cli-004` | +| Dependency/owner | ADR046-cli-001, `shell-terminal` Provider dossier; CLI crate owner | +| Current source | `packages/d2b/src/lib.rs`: `cmd_shell` (`ShellArgs.vm: String` = `WorkloadId` or `RealmTarget`; routes through `route_vm_target()` → `VmTargetRoute::Local\|Gateway`; gateway `Attach` fails closed via `shell_gateway_attach_failure()` with error class `gateway-shell-attach-unavailable` at lib.rs:1697,1780), `cmd_shell_attach`, `run_shell_fsm`, `ShellOwnerTransport`; wire: `ShellOp`, `ShellOpResponse`, `ShellAttachArgs { vm: String }`, `ShellListEntry`, `ShellSessionState` from `packages/d2b-contracts/src/public_wire.rs:1319,1394,1452,1409`; `exec_client.rs` signal/TTY machinery | +| Reuse source | main `a1cc0b2d` — copy/adapt: (1) `packages/d2b-client/src/daemon_service.rs` `DaemonClient::open_terminal(DaemonMethod::Shell, ...)` returning `DaemonTerminal` — copy-then-adapt; same `TerminalOpenRequest`/`TerminalOpenResponse`/`DaemonTerminal` flow as cli-003, applied to shell open/attach; (2) `packages/d2b-contracts/src/generated_v2_services/shell.rs` and `shell_ttrpc.rs` `ShellService` methods: `ShellCreate`, `ShellAttach`, `ShellDetach`, `ShellList`, `ShellInspect`, `ShellKill`, `ShellCancel` (service definition); `ShellCreateRequest`/`ShellAttachRequest`/`ShellListResponse`/`ShellInspectResponse` — copy-then-adapt: these are the target ShellSession resource CRUD wire types; adapt field names from `workload_id`/`shell_name` to `Guest/` ResourceRef; (3) `packages/d2b-client/src/session.rs` `NamedStream` — copy unchanged; used for shell I/O stream; (4) `packages/d2b-session/src/cancellation.rs` `Cancellation`, `RequestRegistry` — copy unchanged; (5) `packages/d2b-session/src/deadline.rs` `DeadlineBudget` — copy unchanged; shell sessions use per-operation deadline tracking; tests to adapt/import: `client.rs:shell_management_uses_typed_selection_result_and_terminal_outcome`, `named_stream_fragments_over_queue_credit_and_has_terminal_actions`, `named_stream_grants_only_consumed_data_and_releases_blocked_sender`, `concurrent_named_streams_route_events_without_cross_consumption`; excluded ADR 0045 assumptions: `VmTargetRoute::Gateway` shell routing and `realm_router` relay path; old `ShellOp`/`ShellOpResponse` seqpacket wire; unsafe-local helper shell protocol v2 | +| Reuse action | copy-then-adapt | +| Destination | `packages/d2b/src/shell.rs` (`d2b shell open/attach/list/detach/kill/status`) | +| Detailed design | Route ShellSession resource lifecycle through resource API using adapted `ShellService` generated types; `shell open` → `ShellCreate` → `DaemonClient::open_terminal(Shell)` → `DaemonTerminal`; retain FSM/TTY/signal/RAII behavior from `run_shell_fsm`; `--name` required for kill; SIGHUP detaches without kill. v2 commands (`d2b shell *`) are deleted at 3.0; no dispatch wiring. Excluded: gateway relay path (`VmTargetRoute::Gateway`); old `ShellOp`/`ShellOpResponse` seqpacket protocol; `TargetInput::Workload`-keyed realm routing. | +| Integration | ZoneContext → ShellSession Create via `DaemonClient::open_terminal` → `NamedStream` I/O | +| Data migration | None | +| Validation | Shell list/detach/kill/attach unit tests (adapted from existing); adapted `client.rs:shell_management_*` and `named_stream_*` tests; TTY RAII/signal tests; confirm v2 `cmd_shell` path is absent | +| Removal proof | Old `cmd_shell` seqpacket path removed after new shell commands have equivalent coverage | + +### ADR046-cli-005 + +| Field | Value | +| --- | --- | +| Work item ID | `ADR046-cli-005` | +| Dependency/owner | ADR046-cli-001, Provider model spec; CLI crate owner | +| Current source | `packages/d2b/src/lib.rs`: `cmd_audio`, `cmd_clipboard_arm`, `cmd_vm_display` | +| Reuse source | main `a1cc0b2d` — copy/adapt: (1) `packages/d2b-provider/src/rpc.rs` `RpcProviderProxy` — adapt: the CLI uses the inverse side (client calling provider via `ConnectedClient::invoke`); `RpcCall`, `RpcPayload`, `RpcResponse`, `RpcOperation` define the typed call shape for dynamic provider commands; (2) `packages/d2b-contracts/src/generated_v2_services/provider_runtime.rs`, `provider_display.rs`, `provider_audio.rs`, `provider_infrastructure.rs` — adapt: the generated service method types show what CLI projection verbs can be mapped to typed service calls; use as shape reference for the first audio/clipboard/display migration; (3) `packages/d2b-provider-toolkit/src/conformance.rs` `check_provider_conformance`, `check_descriptor_conformance` — copy-then-adapt into CLI-side projection conformance validation (bounds: 64 KiB, 32 sub-verbs, 2s deadline, shell-escape, newline strip); (4) `packages/d2b-provider-toolkit/src/server.rs` `GeneratedProviderServiceServer::generated_services()` — server-side only; use as reference for what CLI InspectSchema receives; excluded ADR 0045 assumptions: `ProviderRegistry`/`ProviderAgentAdapter` are server-side and not used in CLI; `RpcProviderProxy` internal `AuthenticatedProviderRpc` pattern is server-side; tests to adapt: `conformance.rs:every_axis_passes_identical_in_process_and_rpc_conformance`, `generated_server_dispatches_closed_methods_over_authenticated_session` | +| Reuse action | adapt | +| Destination | `packages/d2b/src/provider.rs` (`d2b provider list/get/status/inspect`; dynamic projection loading) | +| Detailed design | `d2b provider list/get/status/inspect`; InspectSchema call returns dynamic projection descriptor using `ConnectedClient::invoke` with generated provider service types; projection bounds enforcement (64 KiB, 32 sub-verbs, 2s deadline, shell-escape, newline strip); built-in name collision guard; audio/clipboard/display as first providers to migrate their projections | +| Integration | ZoneContext → Provider resource + InspectSchema via `ConnectedClient::invoke` | +| Data migration | None | +| Validation | Projection size/name/collision/timeout bounds tests; audio/clipboard/display projection conformance tests; completion script safety tests; adapted `conformance.rs` tests | +| Removal proof | Built-in `cmd_audio`/`cmd_clipboard_arm`/`cmd_vm_display` removed only after Provider projection paths pass equivalence tests | + +### ADR046-cli-006 + +| Field | Value | +| --- | --- | +| Work item ID | `ADR046-cli-006` | +| Dependency/owner | ADR046-cli-001; CLI crate owner | +| Current source | None (no completion exists in v3 baseline) | +| Reuse source | Optional: clap_complete crate (version to be pinned); no main-branch source | +| Reuse action | copy-unchanged (clap_complete) | +| Destination | `packages/d2b/src/complete.rs` (`d2b complete bash/zsh/fish`) | +| Detailed design | `d2b complete ` emits completion script; uses clap `CommandFactory::command()` plus dynamic projection fetch (2s per-Provider, 10s total); result bounded at 256 KiB; shell-escaped; newlines stripped | +| Integration | Standalone command; no Zone API required for static completion; Zone API used for dynamic Provider projection | +| Data migration | None | +| Validation | Completion script tests (bash/zsh/fish syntax valid); projection injection safety tests; deadline/partial-Provider tests | +| Removal proof | Not applicable | + +### ADR046-cli-007 + +| Field | Value | +| --- | --- | +| Work item ID | `ADR046-cli-007` | +| Dependency/owner | ADR046-cli-001; CLI/activation Provider owner | +| Current source | `packages/d2b/src/lib.rs`: `cmd_build`, `cmd_generations`, `cmd_switch`, `cmd_boot`, `cmd_test`, `cmd_rollback`, `cmd_gc`, `cmd_migrate`, `cmd_keys_*`, `cmd_keys_trust`, `cmd_keys_rotate_known_host`, `ConfigCommand` variants | +| Reuse source | main `a1cc0b2d` — copy/adapt: (1) `packages/d2b-client/src/daemon_service.rs` `DaemonClient::lifecycle()` with `DaemonMethod::Apply`, `DaemonMethod::Start`, `DaemonMethod::Stop`, `DaemonMethod::Restart` — copy-then-adapt: the apply/lifecycle dispatch pattern maps cleanly to `d2b activation switch/boot/test/rollback`; retain idempotency token and dry-run/apply precondition from `DaemonMethod::Apply`; (2) `packages/d2b-contracts/src/generated_v2_services/activation.rs` activation service method types — adapt: map `ActivationBuildRequest`, `ActivationSwitchRequest`, `ActivationGenerationsRequest` to typed CLI args; excluded ADR 0045 assumptions: `DaemonMethod::ListRealms` / `DaemonMethod::ListWorkloads` are not used; old `WorkloadName`-keyed dispatch is excluded | +| Reuse action | adapt | +| Destination | `packages/d2b/src/activation.rs` (`d2b activation build/generations/switch/boot/test/rollback/gc/migrate/keys/trust/rotate-known-host/config`) | +| Detailed design | Route through `activation-nixos` Provider service via `ConnectedClient::invoke` using adapted `DaemonMethod::Apply`/lifecycle dispatch pattern; retain dry-run/apply; retain guest-control transport for config sync (no SSH). v2 top-level activation commands (`d2b build/switch/boot/test/rollback/gc/migrate/keys/trust/rotate-known-host/config`) are deleted at 3.0; no dispatch wiring. | +| Integration | ZoneContext → activation-nixos Provider service → resource API via `ConnectedClient::invoke` | +| Data migration | None | +| Validation | All existing switch/boot/test/rollback/keys tests adapted; config sync/diff/approve/reject tests; confirm v2 top-level activation paths are absent; adapted `client.rs:daemon_typed_list_preserves_projection_and_truncation` apply pattern | +| Removal proof | Old top-level activation verbs removed only after `d2b activation *` paths have equivalent coverage | + +### ADR046-cli-008 + +| Field | Value | +| --- | --- | +| Work item ID | `ADR046-cli-008` | +| Dependency/owner | ADR046-cli-001; CLI crate owner | +| Current source | `packages/d2b/src/lib.rs`: `cmd_host_check`, `cmd_host_prepare`, `cmd_host_destroy`, `cmd_host_doctor`, `cmd_host_install`, `cmd_host_reconcile`, `cmd_host_validate`; `host_validate.rs` | +| Reuse source | main `a1cc0b2d` — copy/adapt: (1) `packages/d2b-daemon-access/src/component_session.rs` `LocalUnixDaemonAccess::connect_component_session()` connect chain — adapt: the zone-local host commands use the same connect chain as cli-001; `d2b host prepare` and `d2b host doctor` both require a live `ZoneContext`; (2) `packages/d2b-client/src/client.rs` `ConnectedClient::invoke()` with `CallOptions` and `CancellationToken` — copy unchanged; used for Host resource Get/UpdateSpec/Status calls and broker-op dispatch; (3) `packages/d2b-contracts/src/generated_v2_services/broker.rs` broker operation request/response types — adapt: `BrokerHostPrepareRequest`, `BrokerHostDestroyRequest`, `BrokerHostDoctorRequest` (or equivalent) types define the CLI argument shape; retain broker-mediated ownership-marker semantics from baseline; excluded ADR 0045 assumptions: `TargetInput::Workload`-scoped broker routing is excluded; broker operation routing uses zone-root LocalRoot pattern only | +| Reuse action | adapt | +| Destination | `packages/d2b/src/host.rs` (all `d2b host` subcommands) | +| Detailed design | Route `host prepare/destroy` through Zone resource API Host reconcile operation via `ConnectedClient::invoke`; retain broker-mediated mutation and ownership-marker semantics; `host doctor` prefers Zone resource API status, falls back to local state files; `host check` retains exit-code 3; `host validate` retains wave/evidence-dir/scripts-dir/signature | +| Integration | ZoneContext → Host resource; broker op path retained for emergency/shutdown-hook | +| Data migration | None | +| Validation | All existing host-check/prepare/destroy/doctor/install/reconcile/validate tests; exit-code 3 regression; doctor Zone-fallback/local-state-fallback tests | +| Removal proof | Raw broker-socket paths removed only after Host resource API routes have equivalent coverage | + +### ADR046-cli-009 + +| Field | Value | +| --- | --- | +| Work item ID | `ADR046-cli-009` | +| Dependency/owner | ADR046-cli-001; CLI crate owner | +| Current source | `packages/d2b/src/lib.rs`: `cmd_realm_list` (reads static `realm-entrypoints.json` via `realm_policy_rows_raw()`), `cmd_realm_inspect`, `cmd_realm_enter` (→ `realm_gateway_exec_args` → `cmd_vm_exec` with `-it bash -l`), `cmd_realm_run` (→ `cmd_vm_exec` with caller argv); wire output types: `RealmListOutputV1 { realms: Vec }`, `RealmInspectOutputV1 { realm: RealmPolicyOutputV1 }` from `packages/d2b-contracts/src/cli_output.rs:285,292,345`; `RealmPolicyOutputV1` fields: `realm` (= `RealmId`), `mode`, `gateway_vm`, `gateway_target`, `gateway_state`, `cross_realm_policy`, `credential_boundary`; `target_routing.rs`: `Route::Local { vm }`, `Route::Gateway { gateway, target }`, `resolve_access_route()`, `VmTargetRoute`; `d2b-realm-router::RealmEntrypointTable` | +| Reuse source | main `a1cc0b2d` — reference only (no copy): `packages/d2b-realm-router/src/service_v2.rs` `RealmServiceServer`, `RealmServiceProcess`, `RealmMethod::Inspect`, `RealmMethod::ResolveRoute` — server-side multi-realm routing; this is the ADR 0045 multi-Zone topology and is **excluded** from v3 CLI as a direct reuse source; `packages/d2b-realm-router/src/remote_node.rs` `RemoteNodeRegistration`, `RemoteNodeEntry` — constellation remote routing; also excluded; note: `packages/d2b-client/src/daemon_service.rs` `DaemonClient::list_workloads()` and `DaemonMethod::ListRealms` are the closest live list-call patterns, but their zone/workload scoping uses `RealmPath`/`RealmId` types that are ADR 0045-specific; adapt `ConnectedClient::invoke()` with a v3 Zone List request type instead; no main symbols are copied unchanged for cli-009; the zone resource API type design is an ADR-only deliverable pending Zone resource spec | +| Reuse action | adapt | +| Destination | `packages/d2b/src/zone.rs` (`d2b zone get/list/status`) | +| Detailed design | `d2b zone get []` fetches Zone self resource via `ConnectedClient::invoke`; `d2b zone list` lists ZoneLink resources. v2 commands (`d2b realm list/inspect/enter/run`) are deleted at 3.0; no dispatch wiring. Excluded ADR 0045: `RealmServiceServer`/`RealmServiceProcess` multi-realm service; `RemoteNodeRegistration` constellation routing; `TargetInput::Realm`; `RealmMethod::ResolveRoute`/`AuthorizeShortcut`/`RevokeShortcut`. | +| Integration | ZoneContext → Zone resource Get/List via `ConnectedClient::invoke` | +| Data migration | None | +| Validation | Zone get/list tests; confirm v2 `cmd_realm_*` paths are absent | +| Removal proof | `cmd_realm_*` and `target_routing.rs` removed only after zone routes pass equivalence tests | + +### ADR046-cli-010 + +| Field | Value | +| --- | --- | +| Work item ID | `ADR046-cli-010` | +| Dependency/owner | ADR046-cli-001; CLI crate owner | +| Current source | None | +| Reuse source | main `a1cc0b2d` — copy/adapt: (1) `packages/d2b-client/src/client.rs` `ConnectedClient::invoke()`, `ConnectedClient::invoke_with_attachments()`, `ConnectedClient::open_server_stream()` — copy unchanged; these are the three primitives for resource Get/List/Watch respectively; (2) `packages/d2b-client/src/session.rs` `NamedStream` (`send`, `receive`, `cancel`, `close`, `is_terminal`) — copy unchanged; Watch stream output arrives over a named stream; (3) `packages/d2b-session/src/deadline.rs` `DeadlineBudget` — copy unchanged; `--deadline` flag maps to `DeadlineBudget::admit_metadata` wall deadline; `MAX_REQUEST_LIFETIME_MS=900000` caps all Watch/List deadlines; (4) `packages/d2b-client/src/client.rs` `CancellationToken::cancel()` — copy unchanged; `SIGINT`/SIGTERM → `CancellationToken::cancel()` → propagated to `ConnectedClient::invoke` and `NamedStream`; (5) `packages/d2b-client/src/client.rs` `MetadataInput`, `RetryPolicy`, `CallOptions` — copy unchanged; `--idempotency-token` maps to `MetadataInput`; `RetryPolicy::mutating_once()` is the default for Create/UpdateSpec/Delete; tests to adapt/import: `client.rs:metadata_retries_and_cancellation_use_canonical_driver`, `mutating_retries_require_stable_idempotency`, `concurrent_named_streams_route_events_without_cross_consumption`, `named_stream_grants_only_consumed_data_and_releases_blocked_sender`; excluded ADR 0045 assumptions: `TargetInput::Workload/Realm/Provider` routing variants; `GuestClient` cross-realm proxy routing; old `DeploymentProjection`/`RuntimeProjection` ADR 0045-specific field types | +| Reuse action | copy-then-adapt | +| Destination | `packages/d2b/src/resource.rs` (standard `d2b get/list/watch/create/update-spec/delete/status` top-level verbs) | +| Detailed design | Generic typed dispatch to resource API Get/List/Watch/Create/UpdateSpec/Delete using `ConnectedClient::invoke` (Get/List/Create/UpdateSpec/Delete) and `ConnectedClient::open_server_stream` + `NamedStream` (Watch); ResourceRef argument parsing and validation; page token pagination; `--phase`/`--label-selector` filters; `--deadline` bounded by `MAX_REQUEST_LIFETIME_MS=900s` via `DeadlineBudget`; Watch output streams resource events as JSON lines; JSON schema version field; `CancellationToken` wired to process signal handlers. Excluded: `GuestClient` vsock exec/shell routing; `TargetInput` realm/workload/provider variants. | +| Integration | ZoneContext → `ConnectedClient` → resource API | +| Data migration | None | +| Validation | Get/list/watch/create/update-spec/delete tests per ResourceType; pagination/filter/watch-deadline tests; error-class/exit-code tests; adapted `client.rs:metadata_retries_*` and `mutating_retries_*` and `concurrent_named_streams_*` tests | +| Removal proof | Not applicable (new surface) | + +### ADR046-cli-011 + +| Field | Value | +| --- | --- | +| Work item ID | `ADR046-cli-011` | +| Dependency/owner | ADR046-identities-002, ADR046-cli-001, ADR046-cli-002, ADR046-cli-007; Nix module owner + Zone runtime owner | +| Current source | Nix emitters: `nixos-modules/options-realms-workloads.nix` (current `d2b.envs..vms..*`), `nixos-modules/options-realms.nix` (`d2b.realms.*`), `nixos-modules/unsafe-local-workloads-json.nix` (unsafe-local source), `nixos-modules/bundle-artifacts.nix`, `nixos-modules/manifest.nix`, `nixos-modules/assertions.nix`; JSON output: `/etc/d2b/processes.json` (old bundle), `/etc/d2b/realm-entrypoints.json` (static realm index); Zone runtime apply path: `packages/d2bd/src/` (activation apply handler — pre-ADR 0046 path through `cmd_host_prepare`/broker; no live resource bundle apply); cleanup: no current resource-deletion-on-bundle-apply path at baseline | +| Reuse source | None (new implementation; no main `a1cc0b2d` reuse — this is the Nix/Zone side, not the CLI client side) | +| Reuse action | replace | +| Destination | Nix: `nixos-modules/options-zones.nix` (unified `d2b.zones..resources` attrset; per-type `spec` sub-options generated from ResourceTypeSchema/Provider schema), `nixos-modules/bundle-emit.nix` (canonical JSON emit + SHA256 pin), `nixos-modules/assertions.nix` (updated); Zone runtime: `packages/d2bd/src/bundle_apply.rs` (new), `packages/d2bd/src/cleanup.rs` (new); Contracts: `packages/d2b-contracts/src/zone_bundle.rs` (new) | +| Detailed design | **Nix shape:** `d2b.zones..resources` is `attrsOf (submodule { type; optional metadata { ownerRef; labels }; spec })`. `spec` sub-options per `type` generated from ResourceTypeSchema (and signed Provider schema for type=Provider); field names identical to schema. `metadata.name`/`metadata.zone`/`apiVersion` derived; `status`/`uid`/`revision`/`generation`/`finalizers`/`deletionRequestedAt`/`createdAt`/`updatedAt`/`managedBy`/`configurationGeneration` are module-rejected unknown fields. Vendor-qualified types admitted when schema installed. Eval-time assertions: ref resolution, Host `spec.defaultDomain`/`spec.allowedDomains` locked to "user"/["user"], secret policy, conflict/bounds (max 1024). **Nix emit:** `bundle-emit.nix` emits `/etc/d2b/zone-resources.json` + SHA256 pin: canonical sort (type asc, name asc), `bundleGeneration` per derivation hash, `resourceTypeSchemaDigests` bound to installed schemas; build validation renders each entry to canonical JSON and verifies round-trip against ResourceTypeSchema (mismatch = hard build error). **Zone runtime apply:** `bundle_apply.rs` verifies integrity pin and schema digests, rejects old `bundleGeneration`, applies each resource (create/update/no-op), computes absent-set by `managedBy == "nix"` AND absence from new bundle, dispatches async Delete per absent resource, sets `pending-cleanup` Zone condition, returns immediately. Never deletes `managedBy` absent or "controller" resources. **Cleanup tracking:** `cleanup.rs` watches dispatched deletion completion, clears condition when empty. **Prior generation retention:** count-based; default 3; range 1–16; configured via the Zone resource (field name established by ADR-046-nix-configuration); no TTL. `d2b activation gc` prunes beyond count. Rollback re-applies retained bundle as new higher `bundleGeneration`. **Force delete:** `d2b delete --force-remove-finalizers` operator escape hatch. **Audit:** `resource-delete-dispatched` and `resource-delete-completed`/`resource-delete-failed` per absent resource. | +| Integration | Nix build → `/etc/d2b/zone-resources.json` + pin → `d2b activation switch` → Zone runtime `bundle_apply` → resource API Create/Update/Delete → owner controllers → finalizer cascade → `cleanup` watcher → Zone status update | +| Data migration | Full reset from current manifest/processes/realm-entrypoints JSON format; prior Nix-generated artifacts (`/etc/d2b/processes.json`, `/etc/d2b/realm-entrypoints.json`) deleted after Zone resource bundle activates | +| Validation | Runtime integration: all CLI-visible cleanup/status/rollback/gc/force-delete/audit tests (§CLI-visible tests for activation and cleanup); Nix unit and build tests owned by ADR-046-nix-configuration spec | +| Removal proof | Old `nixos-modules/manifest.nix`, `nixos-modules/bundle-artifacts.nix` emitters removed only after `bundle-emit.nix` produces equivalent-or-superseding output and all downstream consumers of the old bundle format are migrated | diff --git a/docs/specs/ADR-046-nix-configuration.md b/docs/specs/ADR-046-nix-configuration.md new file mode 100644 index 000000000..108557a63 --- /dev/null +++ b/docs/specs/ADR-046-nix-configuration.md @@ -0,0 +1,2324 @@ +# ADR 0046 Nix configuration and resource compilation + +| Field | Value | +| --- | --- | +| Spec ID | `ADR-046-nix-configuration` | +| Parent | ADR 0046 | +| Status | Proposed | +| Version | 2 | +| Baseline | `b5ddbed67867d9244bf33390868101bd9b053e49` | +| Normative | Yes | +| Owners | `nixos-modules/`, Nix resource compiler, generated bundle artifacts | +| Depends on | `ADR-046-terminology-and-identities`, `ADR-046-resource-object-model`, `ADR-046-primitive-resource-composition`, `ADR-046-provider-model-and-packaging`, `ADR-046-core-controllers` | +| Supersedes | Current `nixos-modules/options-realms*.nix`, `options-envs.nix`, `options-vms.nix`, `index.nix`, `bundle*.nix`, `*-json.nix`, and generated `/etc/d2b/*.json` | + +## Source, reuse, and evidence policy + +This spec uses `b5ddbed6` as its authoritative factual baseline. + +**Old current names versus new target names.** The baseline uses `Realm`/ +`RealmId`/`RealmPath`/`WorkloadId`/`NodeId` everywhere. These are the current +live symbols. The target names `Zone`, `Host`, `Guest`, `ResourceRef` do not +exist in the baseline. A current source search must use the old names. +Specifically: + +- `RealmId` / `RealmPath` / `d2b.realms.*` → target `Zone` / `d2b.zones.*` +- `WorkloadId` / `d2b.realms..workloads.*` → target `Guest` (for VM/sandbox/ + cloud/remote execution), or target user-only `Host` (for unsafe-local workloads). + The target physical/local-execution parent `Host` is a new ResourceType. +- `NodeId` / `NodeKind` / `WorkloadPlacement` → target `Host` or `Guest` + depending on NodeKind value (see classification table below). +- `ProcessRole` / `VmProcessDag` / runner / systemd helper → target `Process`, + `EphemeralProcess`, or controller/probe/bootstrap per the disposition table below. +- `StoragePathSpec` / `storage.json` / `store-view` / filesystem-view → + target `Volume` where independent lifecycle; locks/internal handles remain + implementation mechanisms. +- `d2b-realm-provider` traits/adapters → target Zone-local `Provider` resource + plus controller/service/worker Process components. Reachability varies by + adapter (see reachability section). +- `d2b-realm-router` / `d2b-realm-transport` → target `d2b-bus` and + `ComponentSession` (new; may copy/adapt main commit `a1cc0b2d`). These crates + are compile-checked in `d2bd` via `realm_stubs.rs` (explicitly + `dead_code`-allowed, not called from the running daemon at baseline). +- `RealmControllersJson` / `realm-controllers.json` → target Zone self-resource + in `zones//zone.json`. +- `RealmWorkloadsLauncherV2Json` / `realm-workloads-launcher-v2.json` → + target Process resource annotations in `zones//processes.json`. +- `Capability` enum → see capability disposition table below (mapping resolved; + verb set owned by resource-api/authz foundation spec). + +No main-branch behavior is assumed live. Every reuse work item names an +exact baseline source file and a precise v3 destination. + +## Purpose + +This spec defines how operators declare d2b 3.0 resource objects in Nix, how +those declarations compile into Zone resource stores, and how generated bundle +artifacts are produced, staged, and rolled back. It covers: + +- Zone self-resource declaration and naming; +- Provider package catalog and `Provider/` install resources; +- `Host`, `Guest`, and shared `ExecutionPolicy` specs; +- `Process`, `EphemeralProcess`, `Volume`, `Network`, `Device`, `User`, and + `Credential` specs; +- `Role` and `RoleBinding` resources and RBAC compilation; +- controller placement templates; +- ref validation at eval time; +- exact schemas and defaults for every folded field; +- the universal Nix authoring and validation contract: canonical ResourceSpec + JSON shape, ResourceTypeSchema and Provider-schema validation, Credential ref + enforcement, and bundle integrity; +- the required `src/`/`tests/`/`integration/`/`README.md` layout for every + `packages/d2b-provider--/` crate and the workspace + policy gate that enforces it; +- active-configuration generation, staging, and rollback across Zones; +- the resource cleanup contract: configuration-owned vs controller-created + classification, async absent-resource deletion, finalizer-safe owner-child + cascade, prior-generation retention, status/audit/tests; +- normalized index and bundle artifacts; +- package closures into Hosts and Guests; +- prohibited fields (secrets, freeform paths); +- conflict detection and rejection; +- the artifact catalog (`d2b.artifacts`) separating derivation-valued inputs + from ResourceSpecs, with `artifactId`/`systemArtifactId` as plain bounded + identifiers validated at build time; +- exact mapping from current option paths and Rust symbols to future files; and +- complete work items. + +## Architectural invariants + +These invariants must be enforced at eval time (Nix assertion or type system), +not deferred to runtime: + +1. Every `*Ref` field follows `/`. No scheme, + Zone prefix, query string, relative segment, or implicit type is accepted. +2. `resource_name` matches `^[a-z][a-z0-9-]*$` — the same LABEL_PATTERN as + `RealmId`/`WorkloadId` in `d2b-realm-core/src/ids.rs`. +3. Every intra-Zone ref resolves to a declared resource of the stated type in + the same Zone. Cross-Zone refs are rejected unless routed through an + explicit `ZoneLink`. +4. Owner cycles fail at eval time. +5. No secret value, credential bytes, raw numeric UID/GID, freeform host path, + ambient capability list, raw seccomp program, or arbitrary socket address + appears in any Nix-emitted resource spec or generated artifact. +6. The Provider package catalog is offline and sorted by exact content digest. + No version ranges, `latest`, PATH scan, or runtime marketplace. +7. Configuration generation is hermetic and deterministic. Running the same + Nix derivation twice with the same inputs produces byte-identical output. +8. Staged configuration becomes active only after eval-time validation passes. + No partial activation is possible. +9. Every generated artifact in `/etc/d2b/` is owned `root:d2bd` mode `0640` + unless the type table below specifies otherwise. +10. No current behavior is removed until the equivalent resource/Provider + successor is integrated and tested. + +## Current-symbol classification tables + +### NodeKind → Host or Guest + +Current source: `packages/d2b-realm-core/src/node.rs`, `NodeKind` enum. + +| Current `NodeKind` variant | Current meaning | v3 target ResourceType | +| --- | --- | --- | +| `FullHost` | A full d2b host (KVM, broker, vsock, device control) | `Host` resource | +| `Gateway` | A realm gateway guest VM | `Guest` with explicitly selected existing runtime Provider (e.g., `Provider/runtime-cloud-hypervisor`) | +| `ProviderManaged` | Limited-capability provider-managed node | `Guest` under the managing Provider | + +`Host` is a new ResourceType with no current equivalent. Its Nix declaration is +the v3 successor to `d2b.realms.` with `placement = "host-local"` plus the +per-realm path-partition entry in `allocator.json`. + +`NodeKind::Gateway` maps to a `Guest` resource whose `providerRef` is explicitly +selected by the operator from the existing catalog (e.g., `Provider/runtime-cloud-hypervisor` +or `Provider/runtime-qemu-media`). There is no dedicated gateway runtime Provider +entry in the initial catalog. Gateway functionality is a declaration pattern (a +Guest whose role is to mediate realm/zone bridging), not a distinct ResourceType +or Provider kind. + +### WorkloadProviderKind → Guest or Host ExecutionPolicy + +Current source: `packages/d2b-realm-core/src/workload.rs`, `WorkloadProviderKind` +and `IsolationPosture` enums. These drive current `WorkloadExecutionPosture`. + +| Current value | Current meaning | v3 target | +| --- | --- | --- | +| `LocalVm` | Locally supervised NixOS VM (Cloud Hypervisor) | `Guest` + `providerRef: Provider/runtime-cloud-hypervisor` | +| `QemuMedia` | Locally supervised QEMU external-media runner | `Guest` + `providerRef: Provider/runtime-qemu-media` | +| `ProviderManaged` | Runtime owned by a provider adapter | `Guest` + exact frozen `providerRef` selected in config (e.g., `Provider/runtime-azure-container-apps`, `Provider/runtime-azure-virtual-machine`) | +| `UnsafeLocal` | Host-user process, no isolation boundary | User-only `Host` under `Provider/system-core` with `isolationPolicy: "none"` | + +For current `WorkloadProviderKind::ProviderManaged` workloads, the `providerRef` +in the compiled `Guest` resource is the exact frozen catalog entry selected by +the operator in their Nix config. Current live ACA paths use +`WorkloadProviderKind::ProviderManaged` backed by the ACA adapter +(`d2b-realm-provider/src/provider.rs`); these map to +`Provider/runtime-azure-container-apps`. Current Azure VM paths map to +`Provider/runtime-azure-virtual-machine`. The operator must select the exact +catalog entry name; the compiler never infers `providerRef` from a current +`ProviderId` value. + +### ProcessRole disposition + +Current source: `packages/d2b-core/src/processes.rs`, `ProcessRole` enum. +Every `VmProcessDag` node carries one `ProcessRole`. The target +`Process`/`EphemeralProcess` classification is determined per variant. + +Evaluation note: a ProcessRole variant that has no independent lifecycle, no +independent owner, and is purely an implementation mechanism (e.g., a transient +health probe) may become a non-resource controller action. The threshold from +`ADR-046-primitive-resource-composition` applies. + +| Current `ProcessRole` | Current description | v3 classification | Target Provider | +| --- | --- | --- | --- | +| `HostReconcile` | Host reconciliation before VM-specific startup | Controller action (not a Process resource; owned by `Provider/system-core`) | `Provider/system-core` | +| `StoreVirtiofsPreflight` | Store and virtiofs preflight validation | `EphemeralProcess` | `Provider/volume-virtiofs` | +| `SwtpmPreStartFlush` | swtpm pre-start flush step | `EphemeralProcess` | `Provider/device-tpm` | +| `Swtpm` | swtpm sidecar (long-lived) | `Process` | `Provider/device-tpm` | +| `Virtiofsd` | virtiofsd sidecar (long-lived) | `Process` | `Provider/volume-virtiofs` | +| `Video` | Optional video sidecar | `Process` | `Provider/device-gpu` | +| `Gpu` | GPU/graphics sidecar | `Process` | `Provider/device-gpu` | +| `GpuRenderNode` | GPU render-node-only mode | `Process` | `Provider/device-gpu` | +| `Audio` | Audio sidecar | `Process` | `Provider/audio-pipewire` | +| `CloudHypervisorRunner` | Cloud Hypervisor VMM | `Process` (owned by Guest) | `Provider/runtime-cloud-hypervisor` | +| `QemuMediaRunner` | QEMU media runner | `Process` (owned by Guest) | `Provider/runtime-qemu-media` | +| `VsockRelay` | vsock relay sidecar | `Process` | `Provider/transport-vsock` | +| `OtelHostBridge` | Host-to-observability-VM OTLP bridge | `Process` | `Provider/observability-otel` | +| `GuestSshReadiness` | Legacy SSH readiness probe (compat window) | Retired at v3 cutover; no v3 Process equivalent; no compatibility period | — | +| `GuestControlHealth` | Authenticated guest-control Health probe | `EphemeralProcess` | `Provider/system-core` | +| `Usbip` (long-lived backend/proxy) | USBIP long-lived proxy/backend | `Process` | `Provider/device-usbip` | +| `Usbip` (per-busid attach/detach) | USBIP per-busid attach/detach helper | `EphemeralProcess` | `Provider/device-usbip` | +| `SecurityKeyFrontend` | Guest CTAPHID relay frontend | `Process` | `Provider/device-security-key` | +| `WaylandProxy` | Host-jailed Wayland proxy | `Process` | `Provider/display-wayland` | + +`ProcessRole::VsockRelay` becomes a `Process` resource owned by +`Provider/transport-vsock`. It is not an implementation mechanism internal to +`Provider/runtime-cloud-hypervisor`; it has an independent lifecycle (shared +across Guests using vsock transport) and therefore satisfies the +primitive-resource-composition threshold for an independent Process resource. + +`ProcessRole::GuestSshReadiness` is retired at the v3 clean cutover. The +baseline comment explicitly marks it as "Replaced by GuestControlHealth"; +there is no v3 `EphemeralProcess` equivalent and no compatibility period. +Operators with SSH-dependent tooling must migrate to the authenticated +guest-control path (`GuestControlHealth` / `ProcessRole::GuestControlHealth`) +before adopting v3. + +`ProcessRole::Usbip` covers two distinct resource kinds. The long-lived +USBIP backend/proxy (runs continuously) becomes a `Process` resource. Each +per-busid attach or detach operation (one-shot) becomes an `EphemeralProcess` +resource. Both are owned by `Provider/device-usbip`. The `Process` controller +starts `EphemeralProcess` instances for individual attach/detach operations; +they are not polymorphic on a single resource instance. + +### Capability enum disposition + +Current source: `packages/d2b-realm-core/src/capability.rs`, `Capability` +enum. Current capabilities are positive-assertion advertisement values. + +The `Capability` enum must not be preserved as a Nix option or ResourceType +field merely because of the name. Each value must be individually classified. + +| Current `Capability` value | Current purpose | v3 target | +| --- | --- | --- | +| `Lifecycle` | Workload create/start/stop/inspect | Implicit grant from `Role` binding with `Host`/`Guest` verbs | +| `Exec` | Command execution (admin-only) | Role verb `exec` on `Process` or `EphemeralProcess` | +| `Pty` | Interactive pseudo-terminal | Role verb on `Process`/`EphemeralProcess`; exact verb name per resource-api/authz foundation spec | +| `Logs` | Durable execution logs with cursors | Role verb on `EphemeralProcess`; exact verb name per resource-api/authz foundation spec | +| `FileCopy` | Bounded file copy | Not in initial verb set; reimplemented as Volume view copy op if needed | +| `PortForward` | One stream per connection | Not in initial verb set | +| `PersistentShell` | Named shell operations | Service capability of `Provider/shell-terminal` | +| `Vsock`, `Virtiofs` | Transport availability | Provider descriptor capability field (not a Role verb) | +| `WindowForwarding`, `DisplayStreaming`, `Clipboard` | Display/clipboard transport | Provider descriptor capability field for `Provider/display-wayland`/`Provider/clipboard-wayland` | +| `AudioPlayback`, `AudioCapture` | Audio | Provider descriptor capability field for `Provider/audio-pipewire` | +| `Hid` | Named HID device operations | Provider descriptor capability field for security-key Provider | +| `Usb` | Named USB device operations | Provider descriptor capability field for `Provider/device-usbip` | +| `GpuAccel` | Local GPU acceleration | Provider descriptor capability field for `Provider/device-gpu` | +| `Snapshots` | Snapshots | Typed Provider descriptor capability field; absent means fail closed; no Nix option | +| `Hotplug` | Device hotplug | Typed Provider descriptor capability field; absent means fail closed; no Nix option | +| `EphemeralSessions` | Provider-managed ephemeral sessions | Provider descriptor capability field; not a Role verb | +| `ProviderManagedIsolation` | Non-host-owned isolation boundary | Typed Provider descriptor capability field; absent means fail closed; no Nix option | +| `ConfiguredLaunch` | Execute a configured launcher item | Role verb on `EphemeralProcess` or `Process`; exact verb name per resource-api/authz foundation spec | + +The verb set for `Role.rules[*].verbs` is owned by the resource-api and authz +foundation spec. The compiler validates that every declared verb is in the +closed set published by that spec; any verb not in that set is rejected at eval +time. Core-reserved verbs (verbs bound to internal controller identity and not +grantable to operator principals) cannot be declared in Nix RoleBindings; the +compiler rejects them with a structured eval error. Cross-reference: +`ADR-046-resource-api-and-authz`. + +`Capability::Snapshots`, `Hotplug`, and `ProviderManagedIsolation` are retained +as typed Provider descriptor capability fields, declared in the Provider's +dossier/manifest. A Provider that does not declare one of these fields is +treated as not supporting that capability; dependent resources fail closed +(e.g., a Guest requesting a snapshot on a Provider without `Snapshots` in its +capability descriptor is a config-publication error). No Nix option forces or +defaults these fields; they are set exclusively by the Provider author in the +Provider dossier. + +### Current reachability summary + +This table records what is wired versus compile-only at baseline `b5ddbed6`. +Evidence class per `ADR-046-terminology-and-identities.md`. + +| Component | Reachability at baseline | Evidence source | +| --- | --- | --- | +| `d2b-realm-core` identifiers, routing metadata, capability model | Compiled into `d2bd`; `RealmId`/`WorkloadId`/`RealmPath`/`RealmTarget`/`CapabilitySet` used in live access-resolver and identity-config paths | `packages/d2bd/src/realm_access_resolver.rs`, `lib.rs` | +| `d2b-realm-core` pure engines (`allocator_engine.rs`, `route_engine.rs`, `identity_store.rs`) | Compile-tested; pure in-memory — no live mutation | Inline module docs: "performs no netlink… live host mutation" / "never generates keys, signs data, writes files" | +| `d2b-realm-provider` `RuntimeProvider`/`WorkloadProvider`/`HostSubstrateProvider` traits | Implemented by live local-vm/ACA providers wired into `d2bd` | `packages/d2bd/src/lib.rs`: `WorkloadProvider` used; `realm_stubs.rs` documents that future gateway wiring is NOT called | +| `d2b-realm-router` `OperationRouter`, `DurableExecTable` | Imported by `d2bd` via `Cargo.toml` but used ONLY in `realm_stubs.rs` which is `dead_code`-allowed and explicitly "NOT called from the running daemon" | `packages/d2bd/src/realm_stubs.rs` header comment | +| `d2b-realm-transport` `LocalTcpTransport`, loopback | Compile-check and conformance tests only; no live socket opened | `packages/d2b-realm-transport/src/lib.rs` docs: "loopback connects… no real socket is opened" | +| `d2b-realm-codec-protobuf` | Compile-check only at baseline (no caller found in daemon) | `packages/d2b-realm-codec-protobuf/src/lib.rs` | +| `realm_access_resolver.rs` in `d2bd` | Live and wired: reads `/etc/d2b/realm-controllers.json`, resolves `RealmPath`/`WorkloadId` targets for auth | `packages/d2bd/src/realm_access_resolver.rs` | +| `d2b-realm-core` `RealmIdentityConfigJson`/`RealmIdentityConfigSummary` | Live: loaded by `d2bd` from `/etc/d2b/realm-identity.json` | `packages/d2bd/src/lib.rs`: `DEFAULT_REALM_IDENTITY_CONFIG_PATH` | +| ACA/Relay/gateway Providers | Live paths exist (implemented `WorkloadProvider`); not covered further in this spec | `d2b-realm-provider` implementors wired into `d2bd` | +| Per-VM process DAG (`VmProcessDag`, `ProcessNode`, `ProcessRole`) | Live: generated by Nix, read by broker/daemon at runtime | `packages/d2b-core/src/processes.rs`; `nixos-modules/processes-json.nix` | +| `StorageJson` / `SyncJson` / `AllocatorJson` | Live: generated by Nix, read by broker | `nixos-modules/storage-json.nix`, `sync-json.nix`, `allocator-json.nix` | +| `RealmWorkloadsLauncherV2Json` | Live: generated by Nix, read by launcher | `packages/d2b-core/src/realm_workloads_launcher.rs`; `nixos-modules/realm-workloads-launcher-v2-json.nix` | + +## Zone declaration + +Each Zone is declared under `d2b.zones.`. The option path replaces +`d2b.realms.` (current: `nixos-modules/options-realms.nix`). The Zone +`` matches `^[a-z][a-z0-9-]*$` — the same LABEL_PATTERN as `RealmId`. + +```nix +d2b.zones.dev = { + label = "Development"; # optional; defaults to attribute name +}; +``` + +Zone-level Nix options (`label`, `retainedGenerations`, `trustedPublishers`) are +compiler/configuration-service settings. They control build behavior and are +not emitted into the Zone ResourceSpec. Parent/child hierarchy is represented +exclusively by a ZoneLink resource in the parent Zone; there is no `parentRef` +in the Zone spec. + +Every declared Zone compiles to exactly one `Zone/` self-resource with +an empty spec: + +```yaml +apiVersion: resources.d2b.io/v3 +type: Zone +metadata: + name: dev + zone: dev + uid: + generation: 1 + revision: + ownerRef: null + finalizers: [] +spec: {} +status: + phase: Ready +``` + +`uid` is never specified in Nix. It is assigned by the Zone runtime at first +activation. A Nix redeclaration of the same name must not invent a new UID. + +Zone UIDs are assigned by the Zone runtime store at first activation. Nix +binds a Zone exclusively by its declared name and config identity. A UID is +never pinned, predicted, or validated in Nix; bundle manifests bind Zone +_name_ only and UIDs are resolved at runtime. A Nix redeclaration of an +existing Zone name must not invent or specify a UID. The +`d2b-activation-helper` reads the runtime-assigned UID from the Zone store +after first activation; subsequent generations are linked to that store-held +UID automatically. + +### ZoneLink + +A `ZoneLink` resource connects a parent Zone to a child Zone. Because a +ZoneLink lives in the PARENT Zone's resource bundle, it is declared in the +parent zone's `resources` set. Parent/child relationships are represented +only by the parent-local ZoneLink; there is no `parentRef` in any Zone's spec +or cross-Zone ResourceRef. Current source for zone-hierarchy: +`d2b-realm-core/src/realm.rs` (`RealmPath` dotted hierarchy — e.g., +`work.personal`); `d2b.realms..parent` option in +`nixos-modules/options-realms.nix`. + +```nix +# Declared in the PARENT zone's resources: +d2b.zones.root.resources.link-dev = { + type = "ZoneLink"; + spec = { + childZoneName = "dev"; # plain Zone name, not a ResourceRef + transportProviderRef = "Provider/transport-unix"; # always explicit; no default + }; +}; +``` + +An eval assertion verifies that for every `ZoneLink` resource in +`d2b.zones.

.resources` with `spec.childZoneName = ""`, the Zone `` is +declared in `d2b.zones`. There is no bidirectional `parentRef` requirement on +the child Zone; the ZoneLink in the parent is the sole declaration. + +`ZoneLink.spec.transportProviderRef` is always explicit. There is no default +transport and no inference. Omitting `transportProviderRef` from a `ZoneLink` +spec is a structured eval error. The operator must select a declared Provider +(e.g., `Provider/transport-unix` for local-host ZoneLinks or +`Provider/transport-vsock` for cross-host links). + +Compiled ZoneLink bundle entry: + +```yaml +apiVersion: resources.d2b.io/v3 +type: ZoneLink +metadata: + name: link-dev + zone: root + ownerRef: null +spec: + childZoneName: dev + transportProviderRef: Provider/transport-unix +``` + +## Artifact catalog + +Derivation-valued inputs — Provider package closures, NixOS system closures, +and any other Nix build output a resource spec must reference — are declared +in a separate named catalog. ResourceSpecs remain pure schema mirrors containing +only plain bounded string IDs; no embedded Nix derivation handles or +convenience wrappers appear inside any spec field. + +### Declaration + +```nix +d2b.artifacts. = { + package = ; # required Nix derivation + type = "provider" # closed type tag + | "nixos-system" + | "nixos-module-set" + | "config-bundle"; +}; +``` + +`` follows `^[a-z][a-z0-9-]*$`. Two entries sharing the same `` is a +NixOS eval error (Nix attrset uniqueness). A spec field referencing an `` +absent from `d2b.artifacts` fails the NixOS build. A spec field referencing +an `` whose `type` does not match what the field expects (e.g., a +`"provider"` artifact in a `systemArtifactId` field that expects +`"nixos-system"`) fails the build with a structured type-mismatch error. + +`Artifact` is not a ResourceType; artifact IDs are NOT ResourceRefs. Fields +that hold artifact IDs use plain unambiguous names (`artifactId`, +`systemArtifactId`) with no `*Ref` suffix. + +Example declarations: + +```nix +d2b.artifacts = { + system-core = { package = pkgs.d2b-provider-system-core; type = "provider"; }; + system-systemd = { package = pkgs.d2b-provider-system-systemd; type = "provider"; }; + runtime-ch = { package = pkgs.d2b-provider-runtime-cloud-hypervisor; type = "provider"; }; + dev-vm-system = { package = pkgs.nixosSystem { modules = [ ... ]; }; type = "nixos-system"; }; +}; +``` + +### Emitted artifact catalog + +The NixOS build emits a private, integrity-pinned `artifact-catalog.json` +(installed `root:d2bd` 0640) alongside the Zone bundles. Store paths are +restricted from PUBLIC ResourceSpecs, status fields, audit records, and OTEL +telemetry. The private `artifact-catalog.json` must contain sufficient Nix +store location data — directly as `storePath` fields, or via a private +integrity-bound locator — for `d2b-activation-helper` to resolve and stage +each built artifact: + +```json +{ + "schemaVersion": "v1", + "entries": [ + { + "artifactId": "system-core", + "type": "provider", + "storePath": "/nix/store/aabbcc...-d2b-provider-system-core", + "packageDigest": "sha256:aabbcc...", + "closureDigest": "sha256:ddeeff...", + "closureSize": 12345678 + }, + { + "artifactId": "dev-vm-system", + "type": "nixos-system", + "storePath": "/nix/store/112233...-nixos-system", + "packageDigest": "sha256:112233...", + "closureDigest": "sha256:445566...", + "closureSize": 876543210 + } + ] +} +``` + +`storePath` is a private field read only by `d2b-activation-helper` and the +Zone runtime for staging. It never appears in any public ResourceSpec, status +field, audit record, or OTEL telemetry export. `d2b-activation-helper` verifies +the artifact catalog digest against a `bundle.json`-sibling manifest entry +before staging. The catalog is content-addressed: same derivation inputs → +byte-identical output. + +### Validation + +| Rule | Layer | +| --- | --- | +| `` matches `^[a-z][a-z0-9-]*$` | Eval | +| No duplicate `d2b.artifacts.` keys | Eval (Nix attrset uniqueness) | +| Every `artifactId` / `systemArtifactId` in any spec exists in `d2b.artifacts` | Build | +| `type` of the artifact matches the expected type for the spec field | Build | +| Provider `artifactId` has `type = "provider"`; trust root validated | Build | +| `systemArtifactId` / `source.systemArtifactId` has `type = "nixos-system"` | Build | +| Store paths absent from all public ResourceSpecs, status fields, audit records, and OTEL telemetry | Build/Runtime | + +## Provider package catalog + +The catalog replaces the ad hoc per-crate package outputs in `flake.nix` and +`nixos-modules/host-daemon.nix`. Current provider construction is implicit +(direct Rust construction in `d2bd`). The v3 catalog is an offline sorted +exact-digest artifact. + +### Catalog declaration + +```nix +d2b.providerCatalog = { + system-core = { + artifactId = "system-core"; # must exist in d2b.artifacts with type = "provider" + trust = { publisherRef = "d2b-official"; }; + }; + system-systemd = { + artifactId = "system-systemd"; + trust = { publisherRef = "d2b-official"; }; + }; + runtime-cloud-hypervisor = { + artifactId = "runtime-ch"; # artifact ID may differ from catalog entry name + trust = { publisherRef = "d2b-official"; }; + }; + # ... one entry per Provider in the frozen initial catalog +}; +``` + +### Catalog artifact + +Emits `/etc/d2b/provider-catalog.json` (sorted by `providerName`). The +`artifactId` field links each Provider catalog entry to the corresponding +artifact catalog entry. `packageDigest` and `settingsSchemaDigest` are +populated by the build from the artifact catalog and Provider package closure. +Store paths do not appear in this public catalog; staging data lives in the +private `artifact-catalog.json`: + +```json +{ + "schemaVersion": "v1", + "entries": [ + { + "providerName": "system-core", + "artifactId": "system-core", + "packageDigest": "sha256:aabbcc...", + "executableDigest": "sha256:...", + "manifestDigest": "sha256:...", + "settingsSchemaDigest": "sha256:...", + "publisherRef": "d2b-official", + "systems": ["x86_64-linux"], + "apiMajor": 3, + "apiMinor": 0 + } + ] +} +``` + +The trust root model uses a built-in `d2b-official` signing root embedded in +the Nix module (covers all initial catalog entries). Additional trusted +publisher roots are per-Zone Nix compiler settings, not Zone ResourceSpec +fields. They are declared at the Zone option level and consumed only during +the NixOS build: + +```nix +d2b.zones.dev.trustedPublishers = { + acme-corp = { signingKey = ""; }; +}; +``` + +A Provider entry whose `publisherRef` is not the built-in `d2b-official` root +and is not registered in `d2b.zones..trustedPublishers` fails install +closed. An absent or unrecognized `publisherRef` is never a warning; it is a +hard failure at catalog resolution time. + +### Provider install resource + +Catalog presence does not install a Provider. Each Zone declares installed +Providers separately: + +```nix +d2b.zones.dev.resources.system-core = { + type = "Provider"; + spec = { + artifactId = "system-core"; # plain bounded ID; must exist in d2b.artifacts with type="provider" + rootConfig = {}; # validated against Provider's signed settings schema + }; +}; +``` + +Compiles to a `Provider` resource in that Zone's generated bundle: + +```yaml +apiVersion: resources.d2b.io/v3 +type: Provider +metadata: + name: system-core + zone: dev + ownerRef: null +spec: + artifactId: system-core + packageDigest: sha256:aabbcc... + rootConfig: {} +status: + phase: Pending +``` + +`artifactId` is a plain bounded string (not a ResourceRef; `Artifact` is not a +ResourceType). The build resolves `artifactId` against `d2b.artifacts` (type +must be `"provider"`) and `d2b.providerCatalog` (trust validation); the catalog +is frozen with no `ProviderCatalogEntry` ResourceType. `packageDigest` is +populated by the compiler from the resolved artifact catalog entry and never +specified directly in Nix. + +### Provider crate layout + +Every `packages/d2b-provider--/` Rust crate in the +workspace must contain exactly the following four paths. Missing any path — or +having an empty `tests/` or `integration/` directory with no Rust source file — +fails the workspace policy check (`make test-policy`): + +| Path | Required contents | +| --- | --- | +| `src/` | Crate implementation source and compiled binaries (one per process role declared in the Provider dossier). Colocated `#[cfg(test)]` unit tests are permitted here. No integration or provider-system tests. | +| `tests/` | Hermetic Cargo integration tests: ResourceType schema round-trips; controller/service/worker lifecycle; conformance via `d2b-provider-toolkit::conformance` (all declared provider-type axes); fault injection. No container, Host, Guest, or cross-process fixtures. | +| `integration/` | Heavier fixtures and scenarios requiring container launch, Host/Guest interaction, cross-process rendezvous, or provider-system state. Invoked by existing test orchestration (`make test-integration`, `make test-host-integration`). Must contain at least one `.rs` source file or fixture. | +| `README.md` | Provider identity and config schema; declared ResourceTypes and their spec/status fields; controllers, services, workers, and binaries with their process roles; placement constraints; dependencies and RBAC requirements; security posture, state lifecycle, and telemetry labels/cardinality; build, test, and integration commands; future standalone-repo usage notes. Minimum 200 bytes. | + +The workspace policy test +(`packages/d2b-contract-tests/tests/provider-crate-layout.rs`) scans every +workspace member matching `packages/d2b-provider-*-*` and fails for any +missing or empty path. The gate runs as part of `make test-policy`. + +Every work item in any spec that introduces a new +`packages/d2b-provider--/` crate must: +- list all four required paths in its `Destination` field; +- include the layout policy gate (`make test-policy`) in its `Tests` field; and +- include a `README.md` stub commit in its first commit before any other + implementation lands. + + + +`Host` is a new ResourceType; there is no current equivalent. The closest +current concepts are `NodeKind::FullHost` (`d2b-realm-core/src/node.rs`), +`RealmControllerPlacement::HostLocal` (`d2b-realm-core/src/realm.rs`), and +the per-realm path-partition in `allocator-json.nix`. The option +`d2b.realms..placement = "host-local"` is the current declaration of a +host-resident realm. + +```nix +d2b.zones.dev.resources.host-system = { + type = "Host"; + spec = { + providerRef = "Provider/system-core"; + defaultDomain = "system"; + allowedDomains = ["system" "user"]; + budget = { cpu = {}; memory = {}; pids = {}; fds = {}; io = {}; storage = {}; network = {}; }; + }; +}; +``` + +Compiles to: + +```yaml +apiVersion: resources.d2b.io/v3 +type: Host +metadata: + name: host-system + zone: dev + ownerRef: null +spec: + providerRef: Provider/system-core + defaultDomain: system + allowedDomains: [system, user] + defaultUserRef: null + budget: { ... } + networkAttachments: [] + deviceAttachments: [] + volumeDefaults: {} +``` + +### Unsafe-local Host + +The current `d2b.realms..workloads..kind = "unsafe-local"` workload +(current type: `WorkloadProviderKind::UnsafeLocal`, current isolation: +`IsolationPosture::UnsafeLocal`, current files: +`nixos-modules/unsafe-local-workloads-json.nix`, +`nixos-modules/unsafe-local-helper.nix`, +`packages/d2b-core/src/unsafe_local_workloads.rs`) maps to a user-only `Host` +resource in v3. It is never a `Guest` and is not a v3 Provider. + +The target shape is a `Host` resource reconciled by `Provider/system-core` with +`defaultDomain: user`, `allowedDomains: [user]`, and `defaultUserRef: User/`. +Child processes are normal `Process` resources selecting any installed Process +Provider (`Provider/system-systemd`, `Provider/system-minijail`). No special +Provider or process type exists for unsafe-local execution. + +The no-isolation posture is preserved explicitly across all surfaces: + +- **Host status** — `Provider/system-core` sets a stable `NoIsolation` condition + on `Host` status. The condition is present whenever `spec.isolationPolicy` is + `"none"`; it is never absent or cleared by a later upgrade. +- **CLI/UI** — `d2b host inspect` and any status display always render the + no-isolation warning when the Host carries the `NoIsolation` condition. + The warning text is not suppressible by operator flag. +- **Audit/telemetry** — every process start, session open, and lifecycle + event under this Host carries a closed `isolation: none` label in its audit + record and telemetry attributes. + +```nix +d2b.zones.dev.resources.host-unsafe-local = { + type = "Host"; + spec = { + providerRef = "Provider/system-core"; + defaultDomain = "user"; + allowedDomains = ["user"]; + defaultUserRef = "User/alice"; + isolationPolicy = "none"; # required common Host spec field for user-only + # system-core Hosts; cannot be set to any other value + }; +}; +``` + +Eval assertions: + +- `isolationPolicy` set to any value other than `"none"` for a user-only + (`defaultDomain: user`, `allowedDomains: [user]`) `Provider/system-core` Host + is rejected at eval time. +- A Process with `executionRef: Host/host-unsafe-local` and `domain: system` + is rejected at eval time. +- No `Guest` ref is emitted for an unsafe-local declaration. + +`isolationPolicy: "none"` is a common field of the `Host` spec (visible to all +controllers and inspectable via the resource API). It is not a +Provider-specific extension. `status.isolationPosture` reflects `none` when +`spec.isolationPolicy` is `none`. The `NoIsolation` status condition and +`isolation: none` audit/telemetry label are required whenever this posture is +in effect. + +## Guest resource + +`Guest` is the v3 successor to `d2b.vms.` (current: `nixos-modules/options-vms.nix`) +for NixOS VM and QEMU media workloads, and to `d2b.realms..workloads.` +with `kind = "local-vm"` or `"qemu-media"` (current: +`nixos-modules/options-realms-workloads.nix`). The current `WorkloadId` maps +to `Guest/` and the current `RealmTarget` address +`..d2b` maps to `Zone//Guest/`. + +```nix +d2b.zones.dev.resources.dev-vm = { + type = "Guest"; + spec = { + providerRef = "Provider/runtime-cloud-hypervisor"; + systemArtifactId = "dev-vm-system"; # plain bounded ID; type="nixos-system" in d2b.artifacts + defaultDomain = "system"; + allowedDomains = ["system"]; + budget = { cpu = { cores = 4; }; memory = { bytes = 4294967296; }; }; + networkAttachments = [{ networkRef = "Network/dev-lan"; }]; + deviceAttachments = [{ deviceRef = "Device/dev-tpm"; }]; + # providerSettings validated against Provider's signed JSON Schema. + # No raw host paths; named closure-entry IDs only. + providerSettings = { + vsockCid = 42; + memoryBacking = "shared"; + }; + }; +}; +``` + +`providerSettings` is validated against the installed Provider's exported JSON +Schema. Values that reference Nix derivation outputs (e.g., a closure, a +kernel module path) are serialized as named closure-entry identifiers or +content digests validated against the package manifest. Raw Nix store path +strings (e.g., `/nix/store/-foo`) are rejected at eval time and must +never appear in emitted resource spec JSON. + +### Package closures into Guests + +Current source: `nixos-modules/closures-json.nix` — `pkgs.closureInfo` per VM, +outputs `/etc/d2b/closures/.json`. Current `VmProcessDag.nodes` contains +a `ProcessRole::Virtiofsd` node whose `share.source` sentinel +`"/nix/store"` (string literal) is the eval-time marker for the per-VM +hardlink-farm share path; see `nixos-modules/processes-json.nix` and +`nixos-modules/store.nix`. + +In v3, every Guest that runs a closure-based OS pins its system artifact in +the artifact catalog, then references it by plain ID in the Guest spec: + +```nix +# Step 1: declare the NixOS system derivation in the artifact catalog +d2b.artifacts.dev-vm-system = { + package = pkgs.nixosSystem { modules = [ ... ]; }; + type = "nixos-system"; +}; + +# Step 2: reference it by ID in the Guest spec (mirrors the canonical schema) +d2b.zones.dev.resources.dev-vm = { + type = "Guest"; + spec = { + providerRef = "Provider/runtime-cloud-hypervisor"; + systemArtifactId = "dev-vm-system"; # validated type="nixos-system" at build + # ... other spec fields + }; +}; +``` + +The compiler: + +1. Resolves `systemArtifactId = "dev-vm-system"` from `d2b.artifacts`; validates + `type = "nixos-system"`. +2. Computes `pkgs.closureInfo { rootPaths = [artifacts.dev-vm-system.package]; }`. +3. Emits a `Volume/-nix-store` resource with + `source.kind = "nix-closure"` and `source.systemArtifactId = "dev-vm-system"`. +4. Emits a virtiofs attachment from that Volume to the Guest with + `mountPath: /nix/store`. +5. Records closure digest, closure size, and private store path in the artifact + catalog entry for `"dev-vm-system"`. The store path is a private field in + `artifact-catalog.json`; it is never emitted in public ResourceSpecs, status + fields, audit records, or OTEL telemetry. + +The per-VM hardlink farm path is derived by `Provider/volume-virtiofs` at +runtime from the artifact catalog entry and the Zone's `stateDir`; it never +appears in any resource spec or status field. The current `share.source == +"/nix/store"` sentinel in `nixos-modules/processes-json.nix` is the eval-time +equivalent; its exact migration mapping is covered in ADR046-nix-017. + +## Process and EphemeralProcess + +Current source: `packages/d2b-core/src/processes.rs` — `ProcessesJson`, +`VmProcessDag`, `ProcessNode`, `ProcessRole`. The per-VM DAG drives +`nixos-modules/processes-json.nix` which emits `/etc/d2b/processes.json`. + +### Common spec + +```nix +d2b.zones.dev.resources.wayland-proxy = { + type = "Process"; + spec = { + providerRef = "Provider/system-systemd"; # replaces ProcessRole + minijail profile selection + executionRef = "Host/host-system"; # replaces per-VM DAG node host/VM assignment + domain = "user"; + userRef = "User/alice"; + processClass = "service"; + packageRef = "Provider/display-wayland"; # replaces binaryPath in ProcessNode + template = "wayland-proxy-host"; # replaces ProcessRole for template dispatch + configRef = "Volume/wayland-proxy-config"; + mounts = [ + { volumeRef = "Volume/wayland-proxy-state"; + view = "proxy"; + mountPath = "/state"; + access = "read-write"; } + ]; + budget = { memory = { bytes = 134217728; }; }; + sandbox = { + # Named profile only — replaces raw seccomp program and capability list. + seccompProfile = "system-systemd-default"; + capabilities = []; + noNewPrivs = true; + }; + network = { networkRef = null; ports = []; }; + devices = []; + endpoints = [{ name = "wayland-host-socket"; transport = "unix"; }]; + readiness = { kind = "unix-socket-accept"; }; + restart = { policy = "on-failure"; maxRestarts = 5; backoffMs = 1000; }; + }; +}; +``` + +### Prohibited fields + +The following are never accepted in any Process or EphemeralProcess Nix +declaration. They replace the free-form fields that current `ProcessNode` +carries (current: `unit`, `binary_path`, `argv`, numeric UID/GID in sandbox): + +- Raw executable path (current `ProcessNode.binary_path` — now resolved by + Provider from signed package manifest); +- Raw environment variable map (current `SpawnRunnerPlanOp` env fields); +- Numeric UID or GID (now `userRef: User/`); +- Raw seccomp BPF program (now named profile ref); +- Ambient capability bitmask; +- Arbitrary socket path; +- Credential or secret bytes. + +### EphemeralProcess + +Current source: `ProcessRole::StoreVirtiofsPreflight`, `SwtpmPreStartFlush`, +`GuestControlHealth`, `GuestSshReadiness` (disposition table above). + +```nix +d2b.zones.dev.resources.store-sync-dev-vm = { + type = "EphemeralProcess"; + spec = { + providerRef = "Provider/system-minijail"; + executionRef = "Host/host-system"; + domain = "system"; + processClass = "worker"; + packageRef = "Provider/volume-virtiofs"; + template = "store-sync"; + configRef = "Volume/store-sync-config"; + successfulTtl = "1h"; # default; explicit for clarity + failedTtl = "24h"; # default + startDeadline = "30s"; + mounts = [ + { volumeRef = "Volume/dev-vm-nix-store"; + view = "sync-source"; mountPath = "/source"; access = "read-only"; } + { volumeRef = "Volume/dev-vm-store-farm"; + view = "sync-target"; mountPath = "/target"; access = "read-write"; } + ]; + }; +}; +``` + +`startDeadline` and `runDeadline` use Go-style bounded duration strings. +Unbounded deadlines are rejected at eval time. + +## Volume resource + +Current source: `packages/d2b-core/src/storage.rs` — `StorageJson`, +`StorageRoot`, `StoragePathSpec` with `owner`/`group`/`mode`/`accessAcl`/ +`defaultAcl`/`noFollow`/`createPolicy`/`repairPolicy`/`cleanupPolicy`. +The current `nixos-modules/storage-json.nix` emitter generates these rows +per VM using the `mkPath` helper. The v3 Volume layout/views replaces this +with a single ResourceType that carries the same fine-grained policy. + +```nix +d2b.zones.dev.resources.wayland-proxy-state = { + type = "Volume"; + spec = { + providerRef = "Provider/volume-local"; + source = { kind = "local-durable"; }; + layout = [ + { path = "socket-dir"; + type = "directory"; + ownerRef = "User/alice"; # replaces PrincipalRef "uid"/"gid" in StoragePathSpec + groupRef = "User/alice"; + mode = "0700"; + noFollow = true; + createPolicy = "create-if-never-provisioned"; + repairPolicy = "exact-owner"; + cleanupPolicy = "owner-controlled"; } + ]; + views = { + proxy = { path = "."; rights = ["read" "write" "create" "delete" "traverse"]; }; + }; + attachments = []; + }; +}; +``` + +`layout[*].ownerRef` and `layout[*].groupRef` accept only `User/` +typed Zone refs. Numeric UID/GID strings (e.g., `"1000"`) are rejected at +eval time. There is no legacy numeric ref migration period: current +`PrincipalRef { kind: "uid", value: "..." }` entries are a clean-reset +migration; operators must declare corresponding `User` resources before +migrating storage layout declarations (tracked in ADR046-nix-009). + +### Virtiofs Volume + +Current source: `ProcessRole::Virtiofsd` in `VmProcessDag`; the current +`share.source` sentinel; `store.nix` for the per-VM hardlink farm. + +```nix +d2b.zones.dev.resources.dev-vm-nix-store = { + type = "Volume"; + spec = { + providerRef = "Provider/volume-virtiofs"; + source = { + kind = "nix-closure"; # desired-state marker + executionRef = "Host/host-system"; + systemArtifactId = "dev-vm-system"; # references artifact catalog; type="nixos-system" + }; + views = { + guest-ro = { path = "."; rights = ["read" "traverse"]; }; + }; + attachments = [ + { executionRef = "Guest/dev-vm"; + transport = "virtiofs"; + mountPath = "/nix/store"; + view = "guest-ro"; + access = "read-only"; } + ]; + }; +}; +``` + +## Network resource + +Current source: `nixos-modules/options-envs.nix` — `d2b.envs..*` with +`lanSubnet`, `uplinkSubnet`, `mtu`, `mssClamp`, `externalNetwork.*`, +`portForwards`. Current `index.nix` (`netMeta`) computes derived bridge names, +IP addresses, and DHCP ranges from these options. The `lib.mkForce` +neutralizer in `net.nix` is the live DHCP/NAT-prevention mechanism for the +net VM's uplink interface. + +```nix +d2b.zones.dev.resources.dev-lan = { + type = "Network"; + spec = { + providerRef = "Provider/network-local"; + lanSubnet = "10.100.0.0/24"; + uplinkSubnet = "10.100.1.0/30"; + mtu = 1500; + mssClamp = true; + dhcp = { enable = true; rangeStart = "10.100.0.200"; rangeEnd = "10.100.0.250"; }; + dns = { enable = true; }; + nat = { enable = true; }; + eastWest = { allow = false; }; + externalNetwork = { enable = false; }; + }; +}; +``` + +CIDR ranges must not overlap with any other Network in the same Zone. The eval +assertion is the v3 successor to the current CIDR-overlap assertion in +`nixos-modules/assertions.nix`. + +CIDR/ref/conflict validation is dual-layer: (1) a pure Nix eval assertion runs +at `nix flake check` for all statically known inputs — this is the primary +shift-left gate; (2) the configuration-publication controller repeats the same +validation before staging a new generation. Dynamic allocations that are not +known at eval time (e.g., DHCP ranges assigned by an external IPAM system) +are validated only at the runtime layer (2). Both layers are required; neither +may be omitted. + +## Device resource + +Current sources: `nixos-modules/components/tpm.nix` (swtpm; `ProcessRole::Swtpm` +and `SwtpmPreStartFlush`), `components/usbip.nix` (`ProcessRole::Usbip`), +`components/graphics.nix` (`ProcessRole::Gpu`, `GpuRenderNode`, `Video`). +Current per-VM toggle: `d2b.vms..tpm.enable`, `d2b.vms..usbip.*`, +`d2b.vms..graphics.*`. + +```nix +d2b.zones.dev.resources.dev-tpm = { + type = "Device"; + spec = { + providerRef = "Provider/device-tpm"; + stateVolumeRef = "Volume/dev-vm-tpm-state"; + # No raw device path. The Provider enumerates TPM hardware and swtpm state. + }; +}; + +d2b.zones.dev.resources.dev-usbip = { + type = "Device"; + spec = { + providerRef = "Provider/device-usbip"; + deviceRef = null; # opaque ref resolved by Provider at runtime + }; +}; +``` + +Raw device node paths (`/dev/tpm0`, hidraw nodes) never appear in emitted +resource specs. The Provider is the sole authority for device enumeration. + +## User resource + +Current source: `nixos-modules/host-users.nix` — d2b system user/group +declarations; `d2b-realm-core/src/identity_store.rs` — `IdentityStore` owns +UID/session lifecycle metadata (pure in-memory). Current per-realm +`d2b.realms..allowedUsers`/`allowedGroups` options. + +```nix +d2b.zones.dev.resources.alice = { + type = "User"; + spec = { + source = "nss"; + groups = []; + }; +}; +``` + +Compiles to: + +```yaml +apiVersion: resources.d2b.io/v3 +type: User +metadata: + name: alice + zone: dev + ownerRef: null +spec: + source: nss + groups: [] +status: + phase: Unknown + observedUid: null + observedGid: null + observedHome: null + sessionManagerAvailable: false +``` + +`observedUid` and `observedGid` are status observations, never spec fields. +The current `PrincipalRef { kind: "uid", value: "..." }` form in storage +contracts must not appear in v3 User spec; it is a pre-migration form only. + +## Credential resource + +Current source: `packages/d2b-realm-core/src/identity_config.rs` +(`RealmIdentityConfigJson`, `IdentityConfigEntry`) — live in `d2bd` at +`/etc/d2b/realm-identity.json`. Current `d2b-realm-provider/src/credential.rs` +(`CredentialProvider` trait, `CredentialPlane`). + +```nix +d2b.zones.dev.resources.work-entra = { + type = "Credential"; + spec = { + providerRef = "Provider/credential-entra"; + # No secret bytes, token values, or key material in spec. + # identity_config fields that are directory metadata (not secrets) only. + providerSettings = { + tenantId = "..."; # directory metadata only + applicationRef = "..."; + }; + }; +}; +``` + +`providerSettings` is validated against the Provider's exported schema. +The emitted `Credential` spec never contains key material, token bytes, or PEM. +Current `identity_config.rs` values that carry credential bytes are forbidden +from the emitted spec; they remain inside the Provider's external secret service. + +## Role and RoleBinding + +Current source: `packages/d2b-realm-core/src/access.rs` — +`RealmAccessBinding`, `RealmAccessClientBinding`, `RealmAccessClientContract`, +and `CapabilityPreflightStatus` model current access rules. Current live access +resolution is in `d2bd/src/realm_access_resolver.rs`. + +```nix +d2b.zones.dev.resources.process-operator = { + type = "Role"; + spec = { + rules = [ + { resourceTypes = ["Process"]; + verbs = ["get" "list" "watch" "update-status"]; } + { resourceTypes = ["Host" "Guest"]; + verbs = ["get" "list"]; } + ]; + }; +}; + +d2b.zones.dev.resources.process-operator-binding = { + type = "RoleBinding"; + spec = { + roleRef = "Role/process-operator"; + subjects = [ + "Provider/system-systemd" + "Provider/system-minijail" + ]; + expiresAt = null; + narrowing = null; + }; +}; +``` + +Eval assertions: + +- `roleRef` must resolve a declared `Role` in the same Zone. +- Every `subjects` entry must be a `/` canonical ref string. + The initial closed subject ResourceType set is: `Zone`, `Provider`, `Host`, + `Guest`, `Process`, `User`. `Group` is not a subject ResourceType; user group + membership may narrow User admission at runtime but is not declared as a + RoleBinding subject. +- A subject referencing a `Provider`, `Host`, `Guest`, `Process`, or `User` + must resolve a declared resource of that type in the same Zone. +- Verbs must be from the closed set published by the resource-api and authz + foundation spec; the compiler rejects any verb not in that set at eval time. + +The initial closed subject ResourceType set (`Zone`, `Provider`, `Host`, +`Guest`, `Process`, `User`) is extended only by a future foundation spec +update. No `Group` ResourceType exists in the initial set; user group facts +(e.g., supplementary groups) may narrow User admission within the runtime +authz layer but are never RoleBinding subjects. + +## Controller placement templates + +Current source: `nixos-modules/options-realms.nix` — `d2b.realms..providers` +attrset with `kind`, `placement`, `capabilityRefs`, `configRef`, +`providerSpecificPlacement`. The `placementKinds` list +(`host-local`, `gateway-vm`, `cloud-full-host`, `provider-controller`, +`provider-agent`, `provider-specific`) in `options-realms.nix` is the current +list; these collapse into `executionRef: Host/` or `Guest/` per the +NodeKind table above. + +```nix +d2b.zones.dev.resources.display-wayland = { + type = "Provider"; + spec = { + catalogEntryId = "display-wayland"; + componentPlacements = { + wayland-proxy-host = { + executionRef = "Host/host-system"; + domain = "user"; + userRef = "User/alice"; + }; + wayland-proxy-guest = { + executionRef = "Guest/dev-vm"; + domain = "system"; + }; + }; + }; +}; +``` + +`componentPlacements` narrows Provider template defaults. It may not add new +templates. Eval asserts every key names an existing template in the Provider's +catalog entry. + +If an operator-declared `componentPlacements` key names a template that no +longer exists in the Provider's current catalog entry (removed or renamed in a +Provider version upgrade), config generation fails with a structured error +identifying the missing template name and the Provider catalog entry. There is +no warning-only or silent-drop path; the operator must either remove the stale +key or pin the prior Provider version. + +## Ref validation + +All ref validation runs at Nix eval time: + +| Rule | Assertion | +| --- | --- | +| ResourceRef format | `/` where name matches `^[a-z][a-z0-9-]*$` | +| Type exists in catalog | Type is in the closed standard catalog or an approved vendor type | +| Intra-Zone resolution | The named resource is declared in `d2b.zones..resources` | +| Cross-Zone ref rejection | Any ref containing a Zone component is rejected; no cross-Zone ResourceRef allowed | +| `providerRef` resolution | The named Provider is declared in `d2b.zones..resources` with `type = "Provider"` | +| `executionRef` resolution | The named Host or Guest is declared in `d2b.zones..resources` with `type = "Host"` or `"Guest"` | +| `userRef` resolution | The named User is declared in `d2b.zones..resources` with `type = "User"` | +| `ownerRef` resolution | The owning resource is declared in `d2b.zones..resources` and is not the resource itself | +| `ownerRef` acyclicity | No chain of `ownerRef`s forms a cycle | +| `roleRef` resolution | The named Role is declared in `d2b.zones..resources` with `type = "Role"` | +| `subjects` entries | Each entry is a canonical `/` ref string resolving to a declared resource of the stated type in the same Zone; type must be in the closed subject set | +| `transportProviderRef` resolution | The named Provider is declared in `d2b.zones..resources` with `type = "Provider"`; required on every ZoneLink; no default | +| `childZoneName` check | The named child Zone is declared in `d2b.zones`; plain Zone name, not a ResourceRef | +| `artifactId` / `systemArtifactId` format | Plain bounded string `^[a-z][a-z0-9-]*$`; not a `/` ResourceRef; no `*Ref` suffix | +| `catalogEntryId` check | The named entry exists in `d2b.providerCatalog`; resolved to its `artifactId` | + +Failed validation emits a structured eval error identifying the exact option +path and rejected value. + +Vendor ResourceType names (e.g., `acme.io.Widget`) appearing in any Nix ref +field before the exporting Provider is installed reject at eval time and block +config publication. There is no deferred-warning or allow-with-warning path. +The compiler's closed ResourceType set is extended only when a Provider +declares the type in its installed dossier. + +## Nix authoring and validation contract + +### Universal resource spec shape + +Every Zone resource is declared under the unified `d2b.zones..resources` +attribute set using a `type`/`spec` envelope that mirrors the canonical +ResourceSpec JSON schema directly: + +```nix +d2b.zones..resources. = { + type = ""; # string discriminator matching a known ResourceType + spec = { + # Exact ResourceType spec fields — identical names to the canonical JSON schema. + # No field renaming; no parallel bespoke vocabulary. + providerRef = "Provider/..."; + # ... + }; +}; +``` + +`metadata.name` is derived from the attribute key (``). +`metadata.zone` is derived from the enclosing zone attribute key (``). +`apiVersion` is defaulted to `"resources.d2b.io/v3"` — never specified in Nix. +`status` is omitted from all emitted artifacts and is read-only; the Zone +runtime fills `uid`, `generation`, `revision`, `timestamps`, and management +metadata at first activation and on subsequent reconciles. + +The `managedBy` field (`configuration | controller | api`) is a core-set +management metadata field set exclusively by the core runtime. It is not +specified in Nix, not emitted by the Nix build, and not accepted in any +resource spec input. The Nix compiler rejects any resource whose `spec` +contains a `managedBy` key. + +The `spec` field uses the **exact same field names and nesting** as the +ResourceTypeSchema JSON — there is no second bespoke Nix vocabulary. A spec +field called `providerRef` in the JSON schema is `providerRef` in Nix; a +nested struct called `budget.cpu.cores` maps to `budget.cpu.cores` in Nix. + +Generated Nix option types, defaults, allowed values, and inline documentation +are derived from the same committed ResourceTypeSchema +(`docs/reference/schemas/v3/.json`) and Provider schema +(`settingsSchemaDigest` in `provider-catalog.json`) — the module system and the +build validator use the same source of truth. + +There are no Nix-only fields inside a resource declaration. The `type`/`spec` +envelope is the complete authoring shape; `type` is the ResourceType +discriminator and `spec` is emitted verbatim. To disable a resource, omit it +from `d2b.zones..resources` or use the ResourceType's own desired-state +fields if that type defines them. + +All spec fields are emitted verbatim into the canonical JSON envelope. +Derivation references and NixOS system closures belong in `d2b.artifacts`, not +inside any resource spec field. + +```json +{ + "apiVersion": "resources.d2b.io/v3", + "type": "", + "metadata": { + "name": "", + "zone": "", + "ownerRef": null, + "finalizers": [] + }, + "spec": { /* exact spec fields, identical to Nix input */ } +} +``` + +`uid`, `generation`, `revision`, and `timestamp` fields are absent from +Nix-emitted artifacts. `ownerRef` defaults to `null`; it may be set in `spec` +only for resources that are explicitly owner-attributed at authoring time (not +for dynamically controller-created resources). `finalizers` defaults to `[]` in +emitted bundles and is managed exclusively at runtime. + + +### ResourceTypeSchema validation + +Every ResourceType has a committed JSON Schema under +`docs/reference/schemas/v3/.json` generated from canonical Rust +DTOs in `d2b-contracts` by `cargo xtask gen-schemas`. The Nix build derivation +validates every emitted `spec` against this schema before the derivation +succeeds. The drift gate `make test-drift` enforces `xtask gen-schemas` + +`git diff --exit-code` to prevent silent schema/code drift. + +| Validation rule | Layer | +| --- | --- | +| Every `spec` field type-checks against committed JSON Schema | Build | +| All required fields present; no unknown top-level `spec` fields | Build | +| `spec` must not contain `managedBy` (core-set runtime field) | Eval | +| `resource_name` matches `^[a-z][a-z0-9-]*$` | Eval | +| All `*Ref` fields follow `/` | Eval | +| All refs resolve within the same Zone | Eval | +| `ownerRef` acyclicity | Eval | +| `domain` ∈ `allowedDomains` of the target Host/Guest | Eval | +| CIDR ranges non-overlapping within Zone | Eval | +| Vendor ResourceType installed before use in any ref | Eval | +| `artifactId` / `systemArtifactId` exists in `d2b.artifacts` | Build | +| `artifactId` artifact has `type = "provider"` + trust validated | Build | +| `systemArtifactId` artifact has `type = "nixos-system"` | Build | +| `source.systemArtifactId` artifact has `type = "nixos-system"` | Build | +| Numeric/string bounds (e.g., vsockCid range) | Eval | +| `providerSettings` matches Provider's signed `settingsSchemaDigest` | Build | +| Store paths absent from all public ResourceSpecs, status, audit, and OTEL telemetry | Build/Runtime | + +A structured eval error identifies the exact NixOS option path and rejected +value for every rule violation. + +### Provider-specific settings validation + +`providerSettings` in `Guest`, `Host`, `Process`, and `EphemeralProcess` specs +is validated at build time against the exact signed JSON Schema embedded in the +Provider's package closure. Validation is offline; no network access occurs +during the build. The schema fingerprint is recorded in `provider-catalog.json` +under `settingsSchemaDigest`; a `providerSettings` schema whose digest does not +match the catalog entry is a build error. + +Rules: +- Additional fields not declared in the Provider schema are rejected + (`additionalProperties: false`). +- Settings values referencing Nix derivation outputs are serialized as named + closure-entry identifiers validated against the package manifest; raw Nix + store path strings are rejected at eval time. +- Numeric, string, and boolean bounds declared in the Provider schema are + enforced at build time; out-of-bounds values fail the derivation. + +### Credentials and secrets + +No secret value (credential bytes, token, PSK material, key PEM, password, +certificate DER/PEM, bearer token, HMAC key) may appear in any Nix spec field, +`providerSettings` value, or generated artifact. See "Prohibited fields +summary" below for the complete list. Provider-required secrets are always +declared as `Credential/` refs. The `Credential` resource spec carries +only: + +- the credential `type` (e.g., `tls-client-cert`, `mtls-keypair`, `psk`); +- the owning `ownerRef`; +- `providerRef` pointing at the Provider consuming it; and +- `domain` (`system` or `user`). + +Actual secret bytes are injected at runtime via the broker's `StoreCredential` +op, which is never invoked from Nix. An eval assertion rejects any string field +matching known secret patterns (PEM `-----BEGIN` headers, JWT `eyJ...` prefix, +hex strings ≥ 32 bytes in secret-typed spec fields). + +### Bundle integrity + +The Zone resource bundle and the artifact catalog emitted by each Nix derivation +build are: + +1. **Sorted**: every `*.json` file contains resources sorted by + `metadata.name`. `bundle.json` file entries are sorted by filename. + `artifact-catalog.json` entries are sorted by `artifactId`. +2. **Content-addressed**: `candidateId` is the sha256 of the concatenation of + all per-file digests (Zone bundle files plus artifact catalog) in canonical + sort order. `contentId` is the sha256 of canonical sorted resource content, + stable across runtime-only metadata mutations (`uid`, `generation`, + `revision`) applied after activation. +3. **Integrity-pinned**: `d2b-activation-helper` verifies every per-file digest + against `bundle.json` before staging, including `artifact-catalog.json`; + any mismatch fails activation closed. +4. **Hermetic**: same Nix inputs → byte-identical derivation output. No + timestamps, randomness, or network access inside the derivation. +5. **Offline**: all required Provider JSON Schemas are in Provider package + closures already in the Nix store at build time; no Provider dossier network + fetch occurs during derivation evaluation or build. +6. **D070-compliant**: store paths are excluded from all public ResourceSpecs, + status fields, audit records, and OTEL telemetry. The private + `artifact-catalog.json` (root:d2bd 0640) contains `storePath` fields that + are readable only by the Zone runtime and activation helper for staging. + + + +### Generation compilation + +Current source: `nixos-modules/bundle.nix` — monolithic bundle derivation, +`SHA256SUMS`, integrity chain. Current `d2b._bundle` attrset in +`bundle-artifacts.nix`. The v3 design replaces the monolithic bundle with +one content-addressed derivation per Zone. + +Each Nix evaluation produces one immutable configuration generation: a closed +set of Zone resource bundles plus the provider catalog. Each bundle is a +content-addressed derivation: + +``` +/nix/store/-d2b-config-zone-dev/ + zone.json — Zone self-resource + providers.json — Provider resources for this Zone + hosts.json — Host resources + guests.json — Guest resources + processes.json — Process and EphemeralProcess resources + volumes.json — Volume resources + networks.json — Network resources + devices.json — Device/User/Credential resources + roles.json — Role and RoleBinding resources + index.json — cross-resource index for this Zone + bundle.json — manifest of all files + content digests + +/nix/store/-d2b-artifact-catalog/ + artifact-catalog.json — private integrity-pinned catalog; storePath per entry for staging +``` + +### Activation path + +``` +Nix eval + → derivation build (hermetic, offline) + → /nix/store/-d2b-config-zone-/ (immutable) + → system activation (d2b-activation-helper) + 1. verify bundle.json digest chain; + 2. validate resource refs, owners, RBAC cross-checks; + 3. stage new generation into Zone runtime (not yet active); + 4. atomically swap active pointer; + 5. trigger configuration-publication controller handler; + 6. record prior generation pointer for rollback window. +``` + +Steps 1–3 are fail-closed. Step 4 is atomic. + +### Rollback + +```bash +d2b zone rollback dev --generation +``` + +Re-stages the prior generation's bundle and swaps the active pointer. The +configuration-publication controller reconciles affected resources. + +Generation retention is a per-Zone Nix compiler setting outside the Zone +ResourceSpec. It is declared at the Zone option level: + +```nix +d2b.zones.dev.retainedGenerations = 3; # default 3; range 1..16 +``` + +An eval assertion enforces `1 ≤ retainedGenerations ≤ 16`. The default is 3. +The minimum of 1 ensures at least one prior generation is always available for +rollback. The maximum of 16 is eval-enforced; values above 16 are rejected with +a structured error. `retainedGenerations` is consumed only during build and +runtime generation bookkeeping; it is never emitted into any Zone ResourceSpec +or bundle JSON. + +### Cross-Zone generation ordering + +When Zone A has a `ZoneLink` to Zone B, Zone A's bundle includes a `cursorRef` +pointing at the expected Zone B generation revision at compilation time. The +configuration-publication controller verifies Zone B revision before activating +Zone A. + +Zone activations are independent. When Zone A activates and Zone B (referenced +via `ZoneLink`) has not yet reached the `cursorRef` revision recorded in +Zone A's bundle, Zone A activates independently and the `ZoneLink` resource +(and any Zone A resources that depend on Zone B state) enter `Degraded` status. +They reconcile asynchronously when Zone B becomes reachable and reaches the +expected revision. Zone A never claims a commit on behalf of Zone B. There is +no cross-Zone atomic activation and no option to block Zone A activation on +Zone B readiness. + +### Resource cleanup contract + +#### Configuration-owned vs controller-created resources + +The configuration-publication controller classifies every resource in the Zone +runtime store using the core-set `managedBy` and `configurationGeneration` +fields set by the runtime at activation time: + +- **Configuration-owned** (`managedBy=configuration`): resources whose + `managedBy` field is `configuration`. The `configurationGeneration` field + records the generation index at which the resource was last reconciled by + config publication. The controller diffs `configurationGeneration` + name + against the new generation's bundle to identify absent resources. +- **Controller-managed** (`managedBy=controller`) and **API-managed** + (`managedBy=api`): resources set by runtime controllers or the resource API. + These are **never** touched by the configuration-publication controller. No + `ownerRef` inference, no label matching, and no "absent from emitted files" + logic is used to determine cleanup eligibility for these resources. + +The configuration-publication controller **only** enqueues for Delete the +resources that carry `managedBy=configuration` and whose `name`+`type` pair is +absent from the new generation's bundle. All other resources are untouched. + +#### Absent-resource deletion + +When a new configuration generation activates: + +1. The configuration-publication controller reads the new generation's bundle + `*.json` files to form the new configuration-owned name set. +2. Resources carrying `managedBy=configuration` whose name+type is absent from + the new set are enqueued for asynchronous `Delete`. +3. **Activation does not block on cleanup.** Step 4 of the activation path + (atomic pointer swap) completes before cleanup begins. +4. Each resource enqueued for Delete transitions to `status.phase: Pending` + with a `PendingDeletion` condition (`reason: AbsentFromConfiguration`). The + Zone's aggregate `status.phase` becomes `Degraded` until all pending deletes + complete. +5. Deletion is finalizer-safe: + - A resource with active finalizers receives a `DeletionBlocked` condition. + The finalizer-holding controller must remove its finalizer before deletion + proceeds; the cleanup controller waits and does not forcibly strip + finalizers. + - When a configuration-owned parent is enqueued for Delete, the parent's + controller is responsible for observing the parent's `PendingDeletion` + condition and reconciling owned children before clearing its finalizer. + The cleanup controller cascades only to resources that also carry + `managedBy=configuration`; controller-managed children of a deleted + configuration-owned parent are handled by the parent's controller. +6. When all finalizers are clear and reconciliation is complete, the resource + transitions to `status.phase: Deleted`. The runtime then removes the row + from the Zone store. + +#### Prior generation retention and pruning + +Prior generation bundles are retained according to `retainedGenerations` +(default 3, range 1..16). Pruning rules: + +- A generation is eligible for pruning when it has been superseded by at least + `retainedGenerations` newer activated generations. +- Pruning removes the generation's bundle pointer from the Zone runtime store. + It does not forcibly interrupt in-flight deletions from that generation. +- On `d2b zone rollback dev --generation N`: configuration-owned resources + absent from generation N that are undergoing cleanup are re-adopted into + generation N's configuration-owned set. Resources that have already reached + `Deleted` and been removed are re-created by the configuration-publication + controller reconciler. + +#### Status, errors, and audit + +| Field | Values | +| --- | --- | +| `Zone.status.phase` | `Ready` — all configuration-owned resources reconciled; `Degraded` — deletion pending or ZoneLink lagging; `Pending` — new generation staged and pointer swapped, reconciliation in progress | +| `Resource.status.phase` | `Pending` — awaiting deletion completion; `Deleted` — deletion complete, row being removed | +| `Resource.status.conditions[PendingDeletion]` | Present when resource is enqueued for deletion; `reason: AbsentFromConfiguration` | +| `Resource.status.conditions[DeletionBlocked]` | Present when a finalizer prevents deletion completion | +| `Resource.status.conditions[ReconcileError]` | Present on reconciliation failure | + +Every absent-resource deletion initiated by a generation change emits a +structured audit event: + +```json +{ + "kind": "ResourceDelete", + "source": "ConfigurationPublicationController", + "zone": "", + "resourceType": "", + "resourceName": "", + "generationIndex": , + "configurationGeneration": , + "reason": "AbsentFromConfiguration" +} +``` + +#### Tests for removed-resource cleanup + +| Test | Tier | Description | +| --- | --- | --- | +| Two-generation bundle diff | nix-unit | Generation 1 declares resource R; generation 2 omits R. Verify R absent from generation 2 bundle `*.json`; generation 1 bundle retains R. | +| Async cleanup activation | Integration | Activate generation 1 (R present, `managedBy=configuration`, phase Ready). Activate generation 2 (R absent). Verify R enters Pending/PendingDeletion; Zone phase Degraded. Complete cleanup. Verify R phase Deleted and row removed; Zone phase Ready. | +| Audit record | Integration | After async cleanup: verify structured `ResourceDelete` event with correct zone/type/name/generationIndex/configurationGeneration/reason fields. | +| Finalizer-blocked deletion | Integration | R holds active finalizer. Activate generation 2 (R absent). Verify R enters DeletionBlocked condition. Remove finalizer. Verify deletion completes (phase Deleted, row removed) and Zone phase returns Ready. | +| Controller-managed preservation | Integration | A resource carrying `managedBy=controller` exists. Activate generation 2. Verify the config-publication controller never touches that resource, regardless of ownerRef or bundle absence. | +| API-managed preservation | Integration | A resource carrying `managedBy=api` exists. Activate generation 2. Verify the config-publication controller never enqueues it for deletion. | +| Rollback after partial cleanup | Integration | Activate generation 2 (R absent, cleanup in progress). Before cleanup completes, roll back to generation 1. Verify R is re-adopted or re-created and returns to Ready. | +| Retention window enforcement | Integration | Activate generations 1–5 with `retainedGenerations=3`. Verify generation 1 is pruned and no longer available for rollback after generation 4 activates. | + + + +Current source: `nixos-modules/index.nix` — `cfg._index.*` attribute tree +(`enabledEnvs`, `enabledVms`, `netMeta`, `declaredRealms`, `enabledRealms`, +`runtimeRows`). The `_index` is used by all other Nix emitters to avoid +repeated `filterAttrs` passes. + +The v3 normalized index is a single cross-Zone artifact emitted to +`/etc/d2b/index.json`: + +```json +{ + "schemaVersion": "v1", + "zones": { + "dev": { + "hosts": ["host-system"], + "guests": ["dev-vm"], + "networks": ["dev-lan"], + "providers": ["system-core", "system-systemd", "runtime-cloud-hypervisor"] + } + }, + "executionIndex": { + "Host/host-system": { + "zone": "dev", + "providerRef": "Provider/system-core", + "processes": ["wayland-proxy"] + }, + "Guest/dev-vm": { + "zone": "dev", + "providerRef": "Provider/runtime-cloud-hypervisor", + "processes": [] + } + }, + "networkIndex": { + "Network/dev-lan": { + "zone": "dev", + "lanSubnet": "10.100.0.0/24", + "attachedGuests": ["Guest/dev-vm"] + } + }, + "closureIndex": { + "Guest/dev-vm": { + "closureArtifact": "/etc/d2b/closures/dev-vm.json" + } + } +} +``` + +The index is derived; it is never edited directly. If it disagrees with the +resource bundle files the activation tool rejects the generation. The drift +gate enforces `xtask gen-index` + `git diff --exit-code`. + +## Bundle artifacts + +### `/etc/d2b/` layout + +| File | Owner | Description | +| --- | --- | --- | +| `provider-catalog.json` | `root:d2bd 0640` | Offline Provider catalog | +| `index.json` | `root:d2bd 0640` | Cross-Zone normalized index | +| `zones//bundle.json` | `root:d2bd 0640` | Zone bundle manifest + digest chain | +| `zones//zone.json` | `root:d2bd 0640` | Zone self-resource | +| `zones//providers.json` | `root:d2bd 0640` | Provider resources | +| `zones//hosts.json` | `root:d2bd 0640` | Host resources | +| `zones//guests.json` | `root:d2bd 0640` | Guest resources | +| `zones//processes.json` | `root:d2bd 0640` | Process/EphemeralProcess resources | +| `zones//volumes.json` | `root:d2bd 0640` | Volume resources | +| `zones//networks.json` | `root:d2bd 0640` | Network resources | +| `zones//devices.json` | `root:d2bd 0640` | Device/User/Credential resources | +| `zones//roles.json` | `root:d2bd 0640` | Role/RoleBinding resources | +| `closures/.json` | `root:d2bd 0640` | Per-Guest closure map | +| `minijail-profiles/.json` | `root:d2b-priv-broker 0640` | Minijail sandbox profiles | +| `privileges.json` | `root:d2bd 0640` | Broker op catalog (retained site-wide) | +| `realm-controllers.json` | `root:d2bd 0640` | Retained during migration; see ADR046-nix-008 | +| `realm-identity.json` | `root:d2bd 0640` | Retained during migration; see ADR046-nix-009 | + +### Bundle manifest format + +`zones//bundle.json`: + +```json +{ + "schemaVersion": "v1", + "candidateId": "", + "contentId": "", + "generationIndex": 1, + "files": [ + { "name": "zone.json", "digest": "sha256:..." }, + { "name": "providers.json", "digest": "sha256:..." }, + { "name": "hosts.json", "digest": "sha256:..." }, + { "name": "guests.json", "digest": "sha256:..." }, + { "name": "processes.json", "digest": "sha256:..." }, + { "name": "volumes.json", "digest": "sha256:..." }, + { "name": "networks.json", "digest": "sha256:..." }, + { "name": "devices.json", "digest": "sha256:..." }, + { "name": "roles.json", "digest": "sha256:..." } + ] +} +``` + +`candidateId`/`contentId` serve the same binding role as the current +`d2b._bundle` integrity chain (current source: `nixos-modules/bundle.nix`), +scoped per Zone. + +## Conflict detection + +The Nix compiler detects and rejects at eval time: + +| Conflict | Rule | +| --- | --- | +| Duplicate Zone name | Two `d2b.zones.` entries with the same `id` | +| ZoneLink cycle | A chain of `childZoneName` references in `ZoneLink` resources that loops | +| CIDR overlap | Two Networks in the same Zone with overlapping subnets | +| Guest name reserved prefix | A `Guest/` starting with `sys-` | +| Owner cycle | Any `ownerRef` chain that loops | +| Type collision | Two `d2b.zones..resources` entries with the same attribute key (Nix prevents this by construction) or two entries with different keys but emitting the same `/` pair (eval-checked for cross-type uniqueness) | +| Provider already installed | Two `d2b.zones..resources` entries of `type = "Provider"` with the same `catalogEntryId` | +| Catalog entry absent | A `catalogEntryId` not in `d2b.providerCatalog`, or an `artifactId`/`systemArtifactId` not in `d2b.artifacts` | +| Artifact type mismatch | An artifact ID used in a field that expects a different `type` (e.g., `"nixos-system"` where `"provider"` is required) | +| Duplicate artifact ID | Two `d2b.artifacts.` entries with the same key | +| Missing ref target | Any `*Ref` field whose target is not declared | +| Role verb unknown | A verb not in the initial closed set | + +## Current-code mapping + +Every current v3 baseline Nix source path and Rust symbol is mapped below to +its v3 destination. Sources are at `b5ddbed6`. No source is removed until the +destination is integrated and tested. + +### Nix modules + +| Current source | Current purpose | Current live? | v3 destination | Work item | +| --- | --- | --- | --- | --- | +| `nixos-modules/options-realms.nix` | `d2b.realms..*` — `RealmId`/`RealmPath`/`RealmControllerPlacement`/`EntrypointMode` options | Yes | `nixos-modules/options-zones.nix` (Zone/Host/Guest/Provider options) | ADR046-nix-001 | +| `nixos-modules/options-realms-workloads.nix` | `d2b.realms..workloads.*` — `WorkloadId`/`WorkloadProviderKind`/`IsolationPosture` options | Yes | `nixos-modules/options-zones-resources.nix` (Guest/Host/Process) | ADR046-nix-001 | +| `nixos-modules/options-realms-network.nix` | `d2b.realms..network.*` | Yes | Network resource spec in `options-zones-resources.nix` | ADR046-nix-001 | +| `nixos-modules/options-envs.nix` | `d2b.envs..*` — env isolation substrate | Yes | Network resource spec; Env concept retired | ADR046-nix-002 | +| `nixos-modules/options-vms.nix` | `d2b.vms..*` — per-VM options | Yes | Guest resource spec; per-VM toggles become Device resources | ADR046-nix-002 | +| `nixos-modules/options-daemon.nix` | `d2b.site.*` daemon options | Yes | `options-site.nix` (retained + extended) | ADR046-nix-003 | +| `nixos-modules/index.nix` | `cfg._index.*` — `netMeta`/`enabledVms`/`realmRows` | Yes | `index.nix` (rewritten); emits `/etc/d2b/index.json` | ADR046-nix-004 | +| `nixos-modules/bundle-artifacts.nix` | `d2b._bundle.*` internal artifact table | Yes | `bundle-zones.nix` (per-Zone) + `bundle-artifacts.nix` (helpers retained) | ADR046-nix-005 | +| `nixos-modules/bundle.nix` | Bundle derivation + SHA256SUMS + integrity chain | Yes | Rewritten per-Zone; `bundle-zones.nix` | ADR046-nix-005 | +| `nixos-modules/processes-json.nix` | `VmProcessDag`/`ProcessRole`/`binaryPath`/argv | Yes | `resources-zones-processes.nix`; emits `zones//processes.json` | ADR046-nix-006 | +| `nixos-modules/storage-json.nix` | `StorageJson`/`StoragePathSpec`/ownership/ACL contract rows | Yes | `resources-zones-volumes.nix`; rows migrate to Volume resources | ADR046-nix-007 | +| `nixos-modules/sync-json.nix` | `SyncJson` OFD lock contract rows | Yes | Internal to `d2b-contracts`; removed from Nix artifacts | ADR046-nix-007 | +| `nixos-modules/allocator-json.nix` | `AllocatorJson` — realm/env bridge assignments, socket paths | Yes | Folded into Zone/Network/Host resources | ADR046-nix-008 | +| `nixos-modules/realm-controller-config-json.nix` | `RealmControllersJson`/`realm-controllers.json` | Yes (read by live d2bd `realm_access_resolver`) | Zone bootstrap bundle `zones//zone.json` | ADR046-nix-008 | +| `nixos-modules/realm-workloads-launcher-v2-json.nix` | `RealmWorkloadsLauncherV2Json`/`realm-workloads-launcher-v2.json` | Yes | Provider/display-wayland + Provider/shell-terminal Process configs in `zones//processes.json` | ADR046-nix-009 | +| `nixos-modules/realm-identity-config-json.nix` | `RealmIdentityConfigJson`/`realm-identity.json` | Yes (read by live d2bd) | Credential resource specs; identity config inside Provider resources | ADR046-nix-009 | +| `nixos-modules/unsafe-local-workloads-json.nix` | `unsafe-local-workloads.json` | Yes | User-only Host + Process resources | ADR046-nix-010 | +| `nixos-modules/privileges-json.nix` | `privileges.json` broker op catalog | Yes | Retained at `/etc/d2b/privileges.json` | ADR046-nix-011 | +| `nixos-modules/closures-json.nix` | `closures/.json` per-VM closure maps | Yes | Retained at `/etc/d2b/closures/.json`; emitter rewritten | ADR046-nix-012 | +| `nixos-modules/minijail-profiles.nix` | `minijail-profiles/.json` | Yes | Retained at same path; emitter adapted to Zone Guest refs | ADR046-nix-012 | +| `nixos-modules/manifest.nix` | `manifest.json` `manifestVersion` contract | Yes | `zones//bundle.json`; `manifestVersion` → `schemaVersion` | ADR046-nix-013 | +| `nixos-modules/host-json.nix` | `host.json` host-side config | Yes | Folded into Host resource in `zones//hosts.json` | ADR046-nix-013 | +| `nixos-modules/assertions.nix` | Eval-time invariants (CIDR, platform, VM names) | Yes | Retained and extended | ADR046-nix-014 | +| `nixos-modules/host.nix`, `host-daemon.nix`, `host-activation.nix`, `host-users.nix` | Host NixOS modules, `d2bd`/`d2b-priv-broker` units, activation helper, users/groups | Yes | Retained and adapted to Zone bundle activation | ADR046-nix-015 | +| `nixos-modules/network.nix`, `net.nix` | Bridge/NAT/DHCP systemd-networkd units | Yes | Reconciled by `Provider/network-local`; retained until Provider successor | ADR046-nix-016 | +| `nixos-modules/store.nix` | Per-VM `/nix/store` hardlink farm; `share.source == "/nix/store"` sentinel | Yes | Reconciled by `Provider/volume-virtiofs`; retained until Provider successor | ADR046-nix-017 | +| `nixos-modules/components/` (graphics, tpm, usbip, audio) | Per-VM toggleable features | Yes | Each becomes a Provider install resource + Device/Guest spec | ADR046-nix-018 | + +### Rust symbols + +The following current Rust symbols need explicit v3 destination assignments +because their names carry old terminology. Each is a current-live path unless +marked compile-only. + +| Current symbol | Current crate/file | Current live? | v3 destination | Work item | +| --- | --- | --- | --- | --- | +| `RealmId`, `RealmPath` | `d2b-realm-core/src/ids.rs`, `realm.rs` | Live in d2bd | `ZoneId` (`^[a-z][a-z0-9-]*$`), `ZonePath` in `d2b-contracts/src/v3/identity.rs` | ADR046-identities-001 | +| `WorkloadId` | `d2b-realm-core/src/ids.rs` | Live | `ResourceName` + `ResourceRef` for `Guest/` or `Process/` | ADR046-identities-001 | +| `NodeId`, `NodeSummary`, `NodeKind` | `d2b-realm-core/src/node.rs` | Live in metadata | `Host` / `Guest` ResourceType (see NodeKind table) | ADR046-identities-001 | +| `ProviderId` | `d2b-realm-core/src/ids.rs` | Live | `ResourceName` for `Provider/` | ADR046-identities-001 | +| `RealmTarget` / `WorkloadTarget` (`..d2b`) | `d2b-realm-core/src/target.rs`, `d2b-core/src/workload_identity.rs` | Live in resolver | `Zone/` + `Guest/` ResourceRef | ADR046-identities-001 | +| `RealmControllerPlacement` (`HostLocal`, `GatewayVm`, `CloudFullHost`, `ProviderController`, `ProviderAgent`) | `d2b-realm-core/src/realm.rs` | Metadata only | `Host.providerRef` + `Guest.providerRef` per NodeKind table | ADR046-nix-001 | +| `EntrypointMode` (`HostResident`, `GatewayBacked`) | `d2b-realm-core/src/realm.rs` | Metadata only | `Host` vs `Guest` ExecutionPolicy distinction | ADR046-nix-001 | +| `VmProcessDag`, `ProcessNode`, `ProcessRole` | `d2b-core/src/processes.rs` | Live (processes.json consumed by broker) | `Process`/`EphemeralProcess` per disposition table | ADR046-nix-006 | +| `ProcessesJson`, `ProcessRole.CloudHypervisorRunner` | `d2b-core/src/processes.rs`, `nixos-modules/processes-json.nix` | Live | `Process` under `Provider/runtime-cloud-hypervisor` owned by `Guest` | ADR046-nix-006 | +| `ProcessRole.Virtiofsd` + `share.source == "/nix/store"` sentinel | `d2b-core/src/processes.rs`, `nixos-modules/processes-json.nix` | Live | `Process` under `Provider/volume-virtiofs` + Volume nix-closure source | ADR046-nix-006 | +| `StorageJson`, `StoragePathSpec`, `PrincipalRef { kind: "uid"|"user" }` | `d2b-core/src/storage.rs`, `nixos-modules/storage-json.nix` | Live (storage.json consumed by broker) | `Volume` layout/views; `PrincipalRef` uid-kind → `User/` ref | ADR046-nix-007 | +| `SyncJson`, OFD lock rows | `d2b-core/src/sync.rs`, `nixos-modules/sync-json.nix` | Live | Internal `d2b-contracts` implementation mechanism; removed from Nix artifacts | ADR046-nix-007 | +| `RealmControllersJson`, `RealmControllerMetadataSummary` | `d2b-core/src/realm_controller_config.rs`; read live by d2bd `realm_access_resolver` | Live | Zone self-resource + ZoneLink bootstrap; `realm-controllers.json` retained during migration | ADR046-nix-008 | +| `RealmWorkloadsLauncherV2Json`, `LauncherWorkloadSummary` | `d2b-core/src/realm_workloads_launcher.rs` | Live | Process resource annotations in `zones//processes.json` | ADR046-nix-009 | +| `RealmIdentityConfigJson` | `d2b-realm-core/src/identity_config.rs`; loaded live by d2bd | Live | Credential resource providerSettings; `realm-identity.json` retained during migration | ADR046-nix-009 | +| `WorkloadProviderKind`, `IsolationPosture`, `WorkloadExecutionPosture` | `d2b-realm-core/src/workload.rs` | Live in launcher metadata | `LocalVm`/`QemuMedia`/`ProviderManaged` → `Guest.providerRef` per table; `UnsafeLocal` → user-only `Host` with `noIsolationWarning: true` (never `Guest`; not a v3 Provider) | ADR046-nix-001 | +| `Capability` enum, `CapabilitySet` | `d2b-realm-core/src/capability.rs` | Live in provider advertisement | Role verbs / Provider descriptor fields per Capability disposition table | ADR046-nix-001 | +| `RuntimeProvider`, `WorkloadProvider`, `HostSubstrateProvider` traits | `d2b-realm-provider/src/provider.rs` | Live (ACA/local-vm implement these) | Provider component descriptors + `ADR-046-provider-.md` dossiers | ADR046-provider-001 | +| `OperationRouter`, `DurableExecTable`, `TargetResolver` | `d2b-realm-router/src/`; `d2bd/src/realm_stubs.rs` | COMPILE-ONLY at baseline (`dead_code`-allowed, not called) | `d2b-bus` routing; adapt `RealmSessionAuthority`/`CredentialCustody`/`RealmServiceLimits` from `main:packages/d2b-realm-router/src/service_v2.rs` | ADR046-bus-010 | +| `TransportProvider`, `loopback`, `LocalTcpTransport` | `d2b-realm-transport/src/lib.rs`, `local_tcp.rs` | COMPILE-ONLY / conformance tests only | `Provider/transport-unix` (Unix seqpacket); `Provider/transport-vsock`; transport primitives adapt `main:packages/d2b-session-unix/src/` | ADR046-bus-004 | + +### Systemd unit mapping + +No current unit is removed until the resource/Provider successor is integrated. + +| Current unit | Current role | v3 treatment | +| --- | --- | --- | +| `d2bd.service` | PID1 local-root controller | Retained; becomes fixed Zone core controller launcher | +| `d2bd.socket` | Local-root public socket | Retained unchanged | +| `d2b-priv-broker.service` | Privileged broker | Retained unchanged | +| `d2b-priv-broker.socket` | Broker socket activation | Retained unchanged | +| Per-env bridge units (`br--lan`, `br--up`) | Network bridge, systemd-networkd | Owned by `Provider/network-local` Process resources after migration | +| `sys--net` VM unit | Auto-declared net VM | Replaced by `Network` + `Guest/sys--net` under `Provider/network-local` | +| Per-VM store-sync | Store hardlink farm sync | Replaced by `EphemeralProcess` under `Provider/volume-virtiofs` | +| Per-VM swtpm | TPM state (`ProcessRole::Swtpm`) | Replaced by `Process` under `Provider/device-tpm` | +| Per-VM GPU sidecar | Cloud Hypervisor VMM (`ProcessRole::CloudHypervisorRunner`) | Replaced by `Process` under `Provider/runtime-cloud-hypervisor` | +| `d2b@` | Legacy per-workload template | Not created in v3 | + +## Prohibited fields summary + +Never accepted in any Nix-authored resource spec or generated artifact: + +- Credential/secret bytes, token values, PSK material, key PEM, passwords; +- Freeform host paths outside closure-backed derivation outputs; +- Raw numeric UID or GID in spec fields; +- Ambient capability bitmasks or named capability lists in Process spec; +- Raw seccomp BPF programs (only named Provider-owned profile refs); +- Arbitrary socket addresses or raw file descriptor numbers; +- Provider-internal `argv` or environment variable maps; +- `eval` or `builtins.exec` in Nix resource compiler expressions; +- `RealmTarget` format strings (`..d2b`) in any spec field. + +## Feasibility proof required + +| Proof | Description | +| --- | --- | +| Zone self-resource round-trip | Nix → `zone.json` → Zone runtime → resource API GET returns matching spec | +| Provider install resource | Nix catalog + Provider resource → core controller lifecycle → Ready status | +| Host system/user Process | Process under Host/host-system with system-systemd; locally held pidfd | +| Guest with closure Volume | Guest + virtiofs Volume → per-VM store farm → guest `/nix/store` without direct host store export | +| Cross-Zone ZoneLink | Parent Zone declares unidirectional ZoneLink with `childZoneName`/`transportProviderRef`; activates with child cursor; child update propagates to parent; no `parentRef` in child spec | +| Configuration rollback | Two-generation rollback restores prior Zone resource state | +| Ref validation rejection | Malformed or missing ref fails eval with structured error | +| Conflict detection | CIDR overlap, owner cycle, and duplicate type/name rejection at eval time | +| ProcessRole parity | Every `ProcessRole` variant has a corresponding test case in the Process/EphemeralProcess resource schema | +| Unsafe-local Host | User-only `Host` with `isolationPolicy: "none"` reconciled by `Provider/system-core`; child Processes use normal Process Providers; `NoIsolation` condition present in Host status; `isolation: none` label in audit record; CLI/UI warning non-suppressible; no `Guest` emitted | +| ResourceTypeSchema validation | Every emitted `spec` validates against committed JSON Schema at build time; schema drift gate passes; unknown field in providerSettings fails build | +| Credential ref enforcement | PEM header in spec field fails eval; `Credential/` ref accepted; no secret bytes in any emitted artifact | +| Bundle integrity | Byte-identical rebuild from identical inputs; `candidateId`/`contentId` match computed values; file digest mismatch fails activation | +| Absent-resource async Delete | Generation 1 has resource R (`managedBy=configuration`); generation 2 omits R; R enters Pending/PendingDeletion; Zone Degraded; R reaches Deleted phase and row removed; Zone Ready; audit event emitted | +| Controller-managed preserved | Resource with `managedBy=controller` untouched by config-publication controller; never enqueued for deletion regardless of bundle absence | +| Finalizer-safe deletion | Resource with active finalizer enters DeletionBlocked condition; stays Pending; deletion completes after finalizer removed; transitions to Deleted, row removed | +| Provider crate layout gate | Stub `d2b-provider-test-missing-integration/` missing `integration/` fails `make test-policy`; complete stub with all four paths passes | +| Artifact catalog resolution | Declare `d2b.artifacts.dev-vm-system = { package = …; type = "nixos-system"; }`; build succeeds; `artifact-catalog.json` contains matching entry with correct type/digests/storePath; `storePath` absent from all public ResourceSpecs, status, audit, OTEL; reference with wrong type fails build; absent artifact ID fails build | + +## Current-code fit + +| Item | Treatment | +| --- | --- | +| Current anchor | `nixos-modules/options-realms*.nix`; `options-vms.nix`; `options-envs.nix`; `index.nix`; `bundle*.nix`; `*-json.nix`; `d2b-realm-core` ids/realm/workload/capability/allocation; `d2b-core` processes/storage/workload-identity; live `realm_access_resolver`; live `realm-controllers.json`/`realm-identity.json` | +| Evidence class | Nix schemas and generated artifacts are live/reachable; `d2b-realm-router`/`d2b-realm-transport` are compile-only stubs at baseline; Zone/Host/Guest/ResourceRef/Provider resources are ADR-only | +| Behavior retained | Hermetic deterministic eval, offline derivations, integrity-pinned artifacts, no secrets in generated JSON, CIDR/platform/name assertions, fine-grained storage ownership/ACL/no-follow, minijail profiles, pidfd/adoption | +| Required delta | Zone/Resource option schema, per-Zone bundle layout, normalized index rewrite, configuration-publication controller hook, ZoneLink cursor, rollback activation path, Host ResourceType (new), ProcessRole disposition implementation | +| Reuse path | Extract pure schema validators, CIDR/name assertion logic, closure-info pattern, and index helpers per work item; `d2b-realm-core` ID validators adapted to `ZoneId`/`ResourceName`; main `a1cc0b2d` ComponentSession/Provider implementation adapted per ADR046-bus-* items | +| Replacement/deletion | No current options or artifact emitters are removed until the named destination is integrated and all tests pass; `realm-controllers.json` and `realm-identity.json` retained during migration | +| Feasibility proof | Items in the proof table above | +| Future owner | Work items below; Provider dossiers; `ADR-046-core-controllers.md` configuration-publication handler | + +## Main-commit ComponentSession and Provider reuse inventory + +Exact reuse sources are at `main:a1cc0b2da4a08ca3240a770a972fe4da6f912bef` (W9 +commit "coordinate toolkit and sibling cutover"). None of these paths are +implemented on the v3 baseline `b5ddbed6`; they are reuse sources, not current +evidence. Every item specifies the exact main-commit file/symbol/tests, the +selected behavior, the v3 destination, and the ADR45 assumptions that must be +excluded or adapted before integration. + +### Reuse summary + +| Main package | Key files/symbols | v3 destination | Work item | +| --- | --- | --- | --- | +| `d2b-session` | `engine.rs`, `handshake.rs`, `lifecycle.rs`, `scheduler.rs`, `record.rs`, `fragmentation.rs`, `transport.rs`, `driver.rs`, `server.rs`, `metrics.rs` | `packages/d2b-bus/src/session/` | ADR046-bus-001 | +| `d2b-session` | `attachment.rs`, `streams.rs`, `cancellation.rs`, `deadline.rs`, `bootstrap.rs` | `packages/d2b-bus/src/session/` | ADR046-bus-002, ADR046-bus-003 | +| `d2b-contracts` | `v2_component_session.rs` — all wire constants + types | `packages/d2b-contracts/src/v3/component_session.rs` | ADR046-bus-005 | +| `d2b-contracts` | `generated_v2_services/` (24 service + 24 ttrpc files) | `packages/d2b-contracts/src/v3/services/` | ADR046-bus-006 | +| `d2b-session-unix` | `adapter.rs`, `socket.rs`, `descriptor.rs`, `credit.rs`, `pidfd.rs`, `systemd.rs`, `vsock.rs` | `packages/d2b-bus/src/transport/unix/` | ADR046-bus-004 | +| `d2b-provider` | `registry.rs`, `rpc.rs`, `instance.rs`, `context.rs` | `packages/d2b-provider/src/` (adapt in place) | ADR046-bus-007 | +| `d2b-provider-toolkit` | `adapter.rs`, `server.rs`, `conformance.rs`, `registration.rs` | `packages/d2b-provider-toolkit/src/` (adapt in place) | ADR046-bus-008 | +| `d2b-client` | `client.rs`, `session.rs`, `target.rs`, `service.rs`, `daemon_service.rs`, `guest_service.rs`, `host_socket.rs` | `packages/d2b-client/src/` (adapt in place) | ADR046-bus-009 | +| `d2b-realm-router` | `service_v2.rs` | `packages/d2b-bus/src/routing/zone_service.rs` | ADR046-bus-010 | +| `d2bd` | `provider_registry.rs` | `packages/d2bd/src/provider_registry.rs` (adapt in place) | ADR046-bus-011 | +| `d2bd` | `provider_effects.rs` | `packages/d2bd/src/provider_effects.rs` (adapt in place) | ADR046-bus-012 | + +**ComponentSession wire contract — cross-reference only:** The choices for +`EndpointRole`, `ServicePackage`, `EndpointPurpose`, `PurposeClass`, and +`Locality` variant naming (including any rename of `RealmController`, +`RealmBroker`, `RealmV2`, `RealmPeer`, `RealmBootstrap` to Zone-prefixed +names), the exact Zone service name replacing `"d2b.realm.v2.RealmService"`, +the v3 `TargetInput` variant shape for Zone/Resource addressing, the v3 +`PROVIDER_BUNDLE_VERSION` and `PROVIDER_BUNDLE_SCHEMA_VERSION`, and the v3 +wire operation replacing `VmLifecycleRequest` — are all owned by the +ComponentSession/d2b-bus foundation spec and the provider-registry contract +work item. These are not defined here. + +Invariants that apply regardless of naming decisions: wire tag values (numeric) +are stable and must not change regardless of string/identifier renames. Zone +service target is addressed as `ResourceRef/Zone`; Guest lifecycle operations +address resources as `ResourceRef` (`Guest/`) with a Zone context +(`Zone/`). Bundle/schema version constants are generated by the owning +contract work item (ADR046-bus-011/ADR046-bus-012). Cross-reference: +`ADR-046-componentsession-and-bus` and the d2b-contracts v3 work item. + +## Implementation work items + +### ADR046-nix-001 + +| Field | Value | +| --- | --- | +| Dependency/owner | W0; `d2b-contracts` identities (ADR046-identities-001, ADR046-identities-002) | +| Current source | `nixos-modules/options-realms.nix` (`RealmId`/`RealmPath`/`RealmControllerPlacement`/`EntrypointMode` labels); `options-realms-workloads.nix` (`WorkloadId`/`WorkloadProviderKind`/`IsolationPosture`/`WorkloadExecutionPosture`); `options-realms-network.nix`; `d2b-realm-core/src/realm.rs`, `workload.rs`, `capability.rs`, `ids.rs` (symbols to adapt) | +| Reuse action | adapt | +| Destination | `nixos-modules/options-zones.nix` (Zone-level options: `label`, `retainedGenerations`, `trustedPublishers` — compiler settings, not Zone spec fields); `nixos-modules/options-zones-resources.nix` (unified `resources` attrset) | +| Detailed design | `d2b.zones..resources. = { type = ""; spec = { ... }; }` — single attrset covering all ResourceTypes; `type` discriminates dispatch; `spec` fields mirror exact ResourceTypeSchema field names and nesting; Nix option types/defaults/docs generated from `docs/reference/schemas/v3/.json`; no Nix-only fields inside resource declarations; `metadata.name` derives from attr key; `metadata.zone` derives from enclosing zone attr key; `apiVersion` defaulted; `uid`/`generation`/`revision`/`status`/`managedBy` never in Nix; `resource_name` regex `^[a-z][a-z0-9-]*$`; ref validation assertions; `WorkloadProviderKind` → Guest/Host mapping per disposition table above; `Capability` → Role verb mapping per resource-api/authz foundation spec; Zone self-resource spec is `{}`; `retainedGenerations`/`trustedPublishers` are Zone-level compiler settings not emitted in Zone spec | +| Integration | `nixos-modules/default.nix` imports new options files; old realms options coexist until ADR046-nix-002 | +| Data migration | Operator configs migrate `d2b.realms.*` → `d2b.zones.*`; `d2b.vms.*` → `d2b.zones..resources.*` with `type = "Guest"` | +| Validation | nix-unit vectors for each ResourceType; ref-validation rejection vectors; malformed ref error shape; ZoneLink `childZoneName` resolves declared Zone; missing `transportProviderRef` fails eval; `managedBy` in spec rejected at eval; Zone spec is `{}` (no `parentRef`, `retainedGenerations`, etc.) | +| Tests | `tests/unit/nix/cases/zones-options.nix`, `tests/unit/nix/cases/zones-ref-validation.nix`, `tests/unit/nix/cases/zones-zonelink.nix` | +| Drift pin | `make nix-unit-pin` after adding cases | +| Removal proof | `options-realms*.nix` removed after `options-zones*.nix` achieves parity and parity drift test passes | + +### ADR046-nix-002 + +| Field | Value | +| --- | --- | +| Dependency/owner | ADR046-nix-001; env/VM migration | +| Current source | `nixos-modules/options-envs.nix` (`lanSubnet`/`uplinkSubnet`/`mtu`/`mssClamp`/`externalNetwork.*`); `nixos-modules/options-vms.nix` (`d2b.vms..*`; `ProcessRole` toggle options for components) | +| Reuse action | adapt | +| Destination | `Network` resource fields in `nixos-modules/options-zones-resources.nix`; `Guest` resource fields | +| Detailed design | `d2b.envs.work.lanSubnet` → `d2b.zones.work.resources.work-lan = { type = "Network"; spec = { lanSubnet = "..."; ... }; }`; CIDR overlap assertion migrated; `sys-` reserved prefix and VM-name regex retained; `d2b.vms..tpm.enable` → `d2b.zones..resources.vm-tpm = { type = "Device"; spec = { providerRef = "Provider/device-tpm"; ... }; }` | +| Validation | nix-unit CIDR rejection; eval assertion for `sys-` prefix; VM-name regex | +| Tests | `tests/unit/nix/cases/zones-network.nix`, `tests/assertions-eval.sh` extended | +| Drift pin | `make nix-unit-pin` | +| Removal proof | `options-envs.nix`, `options-vms.nix` removed after migration parity test passes | + +### ADR046-nix-003 + +| Field | Value | +| --- | --- | +| Dependency/owner | ADR046-nix-001; site options | +| Current source | `nixos-modules/options-daemon.nix`, `options-site.nix`, `options-host.nix` | +| Reuse action | retain and extend | +| Destination | `nixos-modules/options-site.nix` (retained); per-Zone options in `options-zones.nix` | +| Detailed design | `d2b.zones..retainedGenerations` (default 3, range 1..16, compiler setting — not emitted in Zone spec); `d2b.site.stateDir` maps to Zone storage roots; `d2b.site.usePrebuiltHostTools` retained | +| Tests | `tests/unit/nix/cases/site-options.nix` | +| Drift pin | `make nix-unit-pin` | +| Removal proof | No removal; file extended only | + +### ADR046-nix-004 + +| Field | Value | +| --- | --- | +| Dependency/owner | ADR046-nix-001, ADR046-nix-002 | +| Current source | `nixos-modules/index.nix` — `cfg._index.*`: `enabledEnvs`, `enabledVms`, `netMeta` (derives bridge names/IPs from `lanSubnet`/`uplinkSubnet`), `declaredRealms`, `enabledRealms`, `workloadsInEnv`, `runtimeRows` | +| Reuse action | rewrite | +| Destination | `nixos-modules/index.nix` (rewritten); emits `/etc/d2b/index.json` | +| Detailed design | Cross-Zone normalized index: zone/host/guest/network/closure entries; executionIndex; networkIndex; closureIndex; sorted output; `cfg._index` attribute tree retained as internal helper during migration | +| Validation | nix-unit golden vectors for index shape; drift gate: `xtask gen-index` round-trip | +| Tests | `tests/unit/nix/cases/index-zones.nix`; `tests/unit/gates/drift-check.sh` extended | +| Drift pin | `make nix-unit-pin`; `make flake-matrix-pin` if flake checks change | +| Removal proof | `cfg._index.envMeta`, `cfg._index.realms.*` sub-trees removed after all callers migrate to Zone resource lookups | + +### ADR046-nix-005 + +| Field | Value | +| --- | --- | +| Dependency/owner | ADR046-nix-004 | +| Current source | `nixos-modules/bundle-artifacts.nix` (`artifactModule` submodule, `installFileName`, `mode 0640` ownership); `nixos-modules/bundle.nix` (bundle derivation, SHA256SUMS, `d2b._bundle` integrity chain) | +| Reuse action | extend and rewrite | +| Destination | `nixos-modules/bundle-zones.nix` (per-Zone bundle derivation); common helpers retained in `bundle-artifacts.nix` | +| Detailed design | Per-Zone `bundle.json` with `candidateId`/`contentId` binding; SHA256 digest chain; `generationIndex`; atomic activation pointer; `manifestVersion` → `schemaVersion` rename | +| Integration | `d2b-activation-helper` reads `bundle.json` per Zone; validates digest chain before staging | +| Validation | Artifact-shape contract tests in `packages/d2b-contract-tests/tests/`; determinism test (build twice, diff outputs) | +| Tests | `tests/unit/nix/cases/bundle-zones.nix`; `tests/unit/gates/drift-check.sh` for schema drift | +| Drift pin | `make test-drift` | +| Removal proof | Monolithic `bundle.json` and `d2b._bundle` artifact table retired after all Zone bundle tests pass | + +### ADR046-nix-006 + +| Field | Value | +| --- | --- | +| Dependency/owner | ADR046-nix-005; Process Provider work items (ADR046-primitives-002) | +| Current source | `nixos-modules/processes-json.nix` (`VmProcessDag`, `ProcessRole`, `binaryPath`, `argv`, `share.source == "/nix/store"` sentinel); `packages/d2b-core/src/processes.rs` (`ProcessRole` enum — all variants in disposition table above) | +| Reuse action | extract and adapt | +| Destination | `nixos-modules/resources-zones-processes.nix`; emits `zones//processes.json` | +| Detailed design | Process/EphemeralProcess resource serialization per disposition table; no free-form `binaryPath` or `argv`; template refs; mounts from `volumeRef`; sandbox from named profile; VsockRelay → `Process` under `Provider/transport-vsock`; GuestSshReadiness retired at v3 cutover; Usbip long-lived backend/proxy → `Process`, Usbip per-busid attach/detach → `EphemeralProcess`, all owned by `Provider/device-usbip` | +| Integration | `processes.json` replaces `cfg._bundle.processesJson`; Process Providers read the new format | +| Validation | Process resource schema vectors; no-raw-path assertion; ProcessRole parity test (every variant has a test case) | +| Tests | `tests/unit/nix/cases/zones-processes.nix`; `packages/d2b-contract-tests/tests/processes-schema.rs` | +| Drift pin | `make test-drift` after schema changes | +| Removal proof | `processes-json.nix` and current `processes.json` schema removed after all Process Providers consume `zones//processes.json` | + +### ADR046-nix-007 + +| Field | Value | +| --- | --- | +| Dependency/owner | ADR046-nix-005; Volume Provider work items (ADR046-primitives-003) | +| Current source | `nixos-modules/storage-json.nix` (all `mkPath` calls, `PrincipalRef` `uid`/`gid`/`user` kinds, `StorageRoot`/`StoragePathSpec`/`repairPolicy`/`cleanupPolicy`); `packages/d2b-core/src/storage.rs` (`StorageJson`, `StoragePathSpec`); `nixos-modules/sync-json.nix` (`SyncJson` OFD lock rows); `packages/d2b-core/src/sync.rs` | +| Reuse action | extract storage policy → adapt; retire sync rows | +| Destination | `nixos-modules/resources-zones-volumes.nix`; emits `zones//volumes.json`; OFD lock rows move to `d2b-contracts` internals | +| Detailed design | Volume layout/views/ACL/no-follow/repair preserving current policy; `PrincipalRef { kind: "uid" }` → `User/` typed ref only; OFD rows removed from Nix artifacts | +| Validation | Volume schema vectors; ACL/no-follow/view policy tests | +| Tests | `tests/unit/nix/cases/zones-volumes.nix`; `packages/d2b-contract-tests/tests/volumes-schema.rs` | +| Drift pin | `make test-drift` | +| Removal proof | `storage-json.nix`, `sync-json.nix`, and `/etc/d2b/storage.json`/`sync.json` removed after Volume controller parity | + +### ADR046-nix-008 + +| Field | Value | +| --- | --- | +| Dependency/owner | ADR046-nix-004; Zone/Network migration | +| Current source | `nixos-modules/allocator-json.nix` (realm/env bridge assignments, `allocatorStateDir`, socket paths, `providerPlacement`); `nixos-modules/realm-controller-config-json.nix` (emits `realm-controllers.json`); `packages/d2b-core/src/realm_controller_config.rs` (`RealmControllersJson`, `RealmControllerMetadataSummary`; read live by `d2bd/src/realm_access_resolver.rs` from `/etc/d2b/realm-controllers.json`) | +| Reuse action | adapt and retire | +| Destination | Zone self-resource in `zones//zone.json`; allocator concept retired from Nix; socket paths in Zone resource spec; `realm-controllers.json` RETAINED during migration (live d2bd reads it) | +| Detailed design | Zone runtime derives socket paths from Zone name; broker row → Network/Host resource; `realm-controllers.json` must remain published until `realm_access_resolver` is replaced by Zone resource API | +| Validation | Zone resource round-trip; bootstrap socket path regression tests | +| Tests | `tests/unit/nix/cases/zones-bootstrap.nix`; `realm_access_resolver` contract test | +| Drift pin | `make nix-unit-pin` | +| Removal proof | `allocator-json.nix`, `realm-controller-config-json.nix`, and `/etc/d2b/allocator.json`/`realm-controllers.json` removed ONLY after `realm_access_resolver` is replaced by Zone bundle reader | + +### ADR046-nix-009 + +| Field | Value | +| --- | --- | +| Dependency/owner | ADR046-nix-006; display/credential Provider work items | +| Current source | `nixos-modules/realm-workloads-launcher-v2-json.nix` (`RealmWorkloadsLauncherV2Json`, `LauncherWorkloadSummary`; live); `nixos-modules/realm-identity-config-json.nix` (`RealmIdentityConfigJson`; live, read by d2bd from `/etc/d2b/realm-identity.json`); `packages/d2b-core/src/realm_workloads_launcher.rs`; `packages/d2b-realm-core/src/identity_config.rs` | +| Reuse action | adapt | +| Destination | Provider/display-wayland and Provider/shell-terminal Process configs in `zones//processes.json`; `Provider/credential-entra` Credential resource; `realm-identity.json` RETAINED during migration | +| Detailed design | Launcher metadata folded into Process resource annotations; identity config → Credential resource providerSettings (no secret bytes); `realm-identity.json` must remain until d2bd `RealmIdentityConfigJson` loading is replaced by Credential resource reader | +| Validation | Launcher metadata shape regression; no-secret assertion vectors | +| Tests | `tests/unit/nix/cases/zones-launcher-metadata.nix`; no-secret vectors | +| Drift pin | `make nix-unit-pin` | +| Removal proof | `realm-workloads-launcher-v2-json.nix`/`realm-identity-config-json.nix` and `/etc/d2b/realm-workloads-launcher-v2.json`/`realm-identity.json` removed ONLY after display/credential Providers read resource configs | + +### ADR046-nix-010 + +| Field | Value | +| --- | --- | +| Dependency/owner | ADR046-nix-001; unsafe-local migration | +| Current source | `nixos-modules/unsafe-local-workloads-json.nix` (`WorkloadProviderKind::UnsafeLocal`/`IsolationPosture::UnsafeLocal`; current `unsafe-local-workloads.json` artifact); `nixos-modules/unsafe-local-helper.nix` (user-domain process/helper definitions); `packages/d2b-core/src/unsafe_local_workloads.rs` | +| Reuse action | adapt | +| Destination | User-only `Host` resource in `zones//hosts.json` (`isolationPolicy: "none"`, `defaultDomain: user`, `allowedDomains: [user]`, `defaultUserRef: User/`); child `Process` resources in `zones//processes.json` using normal Process Providers; shell session supervisor → `Process` under `Provider/shell-terminal`; never a `Guest`; not a v3 Provider | +| Detailed design | `isolationPolicy: "none"` is the common Host spec field (not providerSettings); enforced at eval time; user-only Host rejects system-domain Process refs; `NoIsolation` condition in Host status; `status.isolationPosture: none`; `isolation: none` label in audit/telemetry for all events under this Host; CLI/UI warning non-suppressible | +| Validation | User-only Host rejection of system-domain Process refs; `isolationPolicy != "none"` assertion rejection for user-only system-core Hosts; `NoIsolation` condition present in status; `isolation: none` in audit record; no Guest emitted for unsafe-local declaration | +| Tests | `tests/unit/nix/cases/zones-unsafe-local.nix`; `tests/host-integration/unsafe-local-helper.nix` extended | +| Drift pin | `make nix-unit-pin` | +| Removal proof | `unsafe-local-workloads-json.nix` and unsafe-local-specific Nix code removed after user-only Host/Process resources pass all `tests/host-integration/unsafe-local-helper.nix` tests | + +### ADR046-nix-011 + +| Field | Value | +| --- | --- | +| Dependency/owner | Broker privileges owner | +| Current source | `nixos-modules/privileges-json.nix` | +| Reuse action | retain | +| Destination | `nixos-modules/privileges-json.nix` (retained); `/etc/d2b/privileges.json` (retained, site-wide) | +| Detailed design | Broker op catalog is not Zone-scoped; no structural change required | +| Validation | Existing `tests/unit/gates/drift-check.sh` | +| Removal proof | Not removed in this spec | + +### ADR046-nix-012 + +| Field | Value | +| --- | --- | +| Dependency/owner | ADR046-nix-005; ADR046-nix-022 (artifact catalog emitter); Provider/volume-virtiofs | +| Current source | `nixos-modules/closures-json.nix` (keyed by `d2b.vms.`); `nixos-modules/minijail-profiles.nix` | +| Reuse action | adapt | +| Destination | `nixos-modules/closures-json.nix` (rewritten, keyed by artifact ID from `d2b.artifacts` with `type = "nixos-system"`); `nixos-modules/minijail-profiles.nix` (retained, adapted to reference Zone Guests) | +| Detailed design | Closure emitter iterates `d2b.artifacts` entries with `type = "nixos-system"`, computes `pkgs.closureInfo`, records `storePath`/digest/size in artifact catalog (private root:d2bd 0640 field; absent from all public ResourceSpecs/status/audit/OTEL); `Guest.spec.systemArtifactId` links Guest to artifact; `Volume.source.systemArtifactId` links Volume to artifact; minijail profile emitter structurally unchanged; old `d2b.vms.` keying retired | +| Validation | Closure map round-trip; per-VM store hardlink integrity; `storePath` present in private catalog; `storePath` absent from all emitted public ResourceSpecs and status/audit/OTEL surfaces | +| Tests | `tests/unit/nix/cases/closures-zones.nix`; `tests/unit/nix/cases/artifact-catalog-store-path-public-absent.nix` | +| Drift pin | `make nix-unit-pin` | +| Removal proof | Old `d2b.vms.*`-keyed closure entries removed after all Guests use `zones//guests.json` and artifact catalog | + +### ADR046-nix-013 + +| Field | Value | +| --- | --- | +| Dependency/owner | ADR046-nix-005; manifest contract | +| Current source | `nixos-modules/manifest.nix` (`manifestVersion` pinned contract); `nixos-modules/host-json.nix` | +| Reuse action | replace | +| Destination | Per-Zone `zones//bundle.json` (`schemaVersion`); Host resource in `zones//hosts.json` | +| Detailed design | `manifestVersion` → `schemaVersion`; `host.json` host config folded into Host resource spec; CHANGELOG entry for rename required | +| Validation | Schema drift gate; CHANGELOG enforcement | +| Tests | `tests/unit/gates/drift-check.sh` extended for `schemaVersion` | +| Drift pin | `make test-drift` | +| Removal proof | `manifest.nix` and `host.json` emitters removed after Zone bundle activation path passes | + +### ADR046-nix-014 + +| Field | Value | +| --- | --- | +| Dependency/owner | ADR046-nix-001, ADR046-nix-002 | +| Current source | `nixos-modules/assertions.nix` (CIDR overlap, `sys-` prefix, VM-name regex, platform gate) | +| Reuse action | extend in place | +| Destination | `nixos-modules/assertions.nix` | +| Detailed design | Migrate existing assertions to Zone/Resource terminology; add ref validation, owner cycles, CIDR overlap (Zones), provider resolution, RoleBinding verb set assertions | +| Validation | Each new assertion has a failing-config test vector | +| Tests | `tests/assertions-eval.sh` extended; `tests/unit/nix/cases/assertions-zones.nix` | +| Drift pin | `make nix-unit-pin` | +| Removal proof | No removal; extended only | + +### ADR046-nix-015 + +| Field | Value | +| --- | --- | +| Dependency/owner | ADR046-nix-001; host activation | +| Current source | `nixos-modules/host.nix`, `host-daemon.nix` (fixed local-root endpoint set), `host-activation.nix`, `host-users.nix` | +| Reuse action | retain and adapt | +| Destination | Same files; updated to use Zone bundle activation path and Zone resource state dirs | +| Detailed design | `d2b-activation-helper` updated to validate/stage per-Zone bundles; `d2bd.service` updated to read Zone bundle; `d2b` group retained for `SO_PEERCRED` | +| Validation | Host-integration test with Zone bundle activation and daemon readiness | +| Tests | `tests/host-integration/` extended for Zone activation | +| Removal proof | No removal; adapted in place | + +### ADR046-nix-016 + +| Field | Value | +| --- | --- | +| Dependency/owner | ADR046-nix-002; Provider/network-local dossier | +| Current source | `nixos-modules/network.nix`, `nixos-modules/net.nix` (including `lib.mkForce` DHCP neutralizer and per-env MTU/MSS/east-west wiring) | +| Reuse action | retain until Provider successor | +| Destination | Network reconciliation by `Provider/network-local` Process resources | +| Detailed design | Current bridge/NAT/DHCP/firewall Nix units retained; `Provider/network-local` controller emits equivalent configuration from Network resources; `lib.mkForce` neutralization preserved | +| Validation | `tests/net-vm-network-eval.sh` passes against Network resource spec | +| Tests | `tests/unit/nix/cases/zones-network-parity.nix` | +| Drift pin | `make nix-unit-pin` | +| Removal proof | `network.nix`/`net.nix` removed after `Provider/network-local` parity and `tests/net-vm-network-eval.sh` passes | + +### ADR046-nix-017 + +| Field | Value | +| --- | --- | +| Dependency/owner | ADR046-nix-012; Provider/volume-virtiofs | +| Current source | `nixos-modules/store.nix` (`ProcessRole::Virtiofsd` and `share.source == "/nix/store"` sentinel; per-VM hardlink farm) | +| Reuse action | retain until Provider successor | +| Destination | Per-VM store reconciliation by `Provider/volume-virtiofs` EphemeralProcess/Process resources | +| Detailed design | `store.nix` retained; `Provider/volume-virtiofs` controller creates equivalent EphemeralProcess; per-VM store path derived from Zone stateDir + Guest name via Provider, not raw path in spec | +| Validation | Store hardlink integrity; no direct `/nix/store` export | +| Tests | Existing store integrity tests extended with Zone/Guest resource fixture | +| Removal proof | `store.nix` removed after `Provider/volume-virtiofs` manages farm lifecycle and existing store tests pass | + +### ADR046-nix-018 + +| Field | Value | +| --- | --- | +| Dependency/owner | ADR046-nix-002; Provider dossiers for graphics, tpm, usbip, audio | +| Current source | `nixos-modules/components/graphics.nix` (`ProcessRole::Gpu`, `GpuRenderNode`, `Video`); `components/tpm.nix` (`ProcessRole::Swtpm`, `SwtpmPreStartFlush`); `components/usbip.nix` (`ProcessRole::Usbip`); `components/audio/` (`ProcessRole::Audio`) | +| Reuse action | each component becomes a Provider install resource + Device/Guest spec field | +| Destination | `Provider/device-tpm`, `Provider/device-usbip`, `Provider/device-gpu`, `Provider/audio-pipewire` resource install declarations in `options-zones-resources.nix` | +| Detailed design | `d2b.vms..tpm.enable = true` → `d2b.zones..resources.vm-tpm = { type = "Device"; spec = { providerRef = "Provider/device-tpm"; ... }; }`; all component eval assertions migrated to `assertions.nix`; GuestSshReadiness retired at v3 cutover; Usbip long-lived backend/proxy → `Process`, per-busid attach/detach → `EphemeralProcess`, both owned by `Provider/device-usbip` | +| Validation | Existing component eval tests; `tests/usbip-gating-eval.sh`; `tests/video-contract-eval.sh` | +| Tests | `tests/unit/nix/cases/zones-devices.nix` | +| Drift pin | `make nix-unit-pin` | +| Removal proof | `components/` Nix units removed after Provider resource install achieves parity and all component eval tests pass against Zone resource configs | + +### ADR046-nix-019 + +| Field | Value | +| --- | --- | +| Dependency/owner | ADR046-nix-005; ADR046-nix-001; `d2b-contracts` schema generation (ADR046-bus-005) | +| Current source | `nixos-modules/bundle-artifacts.nix` (`artifactModule` submodule, mode/ownership); `nixos-modules/bundle.nix` (digest chain, SHA256SUMS); `packages/xtask/src/main.rs` (`gen-schemas`); no current per-ResourceType JSON Schema under `docs/reference/schemas/v3/` | +| Reuse action | extend xtask schema generation; new Nix eval/build validation hooks | +| Destination | `docs/reference/schemas/v3/.json` for each ResourceType; `nixos-modules/resource-schema-validation.nix` (validates emitted spec against committed JSON Schema at build time); `nixos-modules/provider-settings-validation.nix` (validates `providerSettings` against Provider-embedded schema at build time); `nixos-modules/assertions.nix` (Credential ref enforcement, secret-pattern rejection) | +| Detailed design | `cargo xtask gen-schemas` emits one JSON Schema per ResourceType under `docs/reference/schemas/v3/`; Nix derivation reads these schemas from `pkgs.d2b-resource-schemas` and validates every emitted `spec` JSON before producing the Zone bundle; Provider-settings validation reads `settingsSchemaDigest` from `provider-catalog.json` and resolves the schema from the Provider package closure; Credential ref enforcement: eval assertion rejects any `spec` string field matching `-----BEGIN`, `eyJ`, or a hex string ≥ 32 bytes in a secret-typed field; `managedBy` in any input spec rejected at eval (core-set runtime field, never in Nix input); bundle integrity: `candidateId`/`contentId` computed over canonical sorted output | +| Integration | Validation hooks wired into `bundle-zones.nix` derivation; `d2b-activation-helper` re-verifies digest chain at staging | +| Validation | Schema round-trip: emit spec, validate against schema, verify byte-identical re-emit; providerSettings rejection test (unknown field, out-of-bounds value, raw store path); Credential ref enforcement: PEM-in-spec rejected; secret-pattern-in-spec rejected; valid `Credential/` ref accepted; `managedBy` in spec input rejected at eval | +| Tests | `tests/unit/nix/cases/resource-schema-validation.nix`; `tests/unit/nix/cases/provider-settings-validation.nix`; `tests/unit/nix/cases/credential-ref-enforcement.nix`; `tests/unit/nix/cases/managed-by-rejection.nix`; `packages/d2b-contract-tests/tests/resource-schema-round-trip.rs` | +| Drift pin | `make test-drift` after any `gen-schemas` run; `make nix-unit-pin` after adding cases | +| Removal proof | Not removed; extended as new ResourceTypes are added | + +### ADR046-nix-020 + +| Field | Value | +| --- | --- | +| Dependency/owner | ADR046-nix-005; ADR046-nix-001; configuration-publication controller (ADR-046-core-controllers) | +| Current source | No current equivalent; current `bundle.nix` replaces all artifacts atomically with no per-resource cleanup tracking | +| Reuse action | new | +| Destination | Configuration-publication controller handler in `packages/d2bd/src/config_publication.rs`; `ConfigurationOwnedClassifier`; `AbsentResourceReaper`; `Zone` status conditions in `d2b-contracts/src/v3/zone_status.rs`; cleanup audit emitter in `d2b-state/src/audit_segments.rs` | +| Detailed design | `ConfigurationOwnedClassifier`: classify resources by core-set `managedBy` field only — `managedBy=configuration` resources are owned by config publication; `managedBy=controller` and `managedBy=api` resources are never touched. At activation, diff new-generation bundle name+type set against all resources with `managedBy=configuration` in the Zone store; resources absent from the new bundle are enqueued for Delete. Never infer ownership from `ownerRef`, labels, or absence from emitted files. `AbsentResourceReaper`: processes the Absent queue asynchronously; does not block pointer swap (step 4); sets `status.phase=Pending` + `PendingDeletion` condition (`reason: AbsentFromConfiguration`); waits for all finalizers to clear before transitioning to `status.phase=Deleted`; runtime removes the row on `Deleted`. Zone phase: `Pending` during pointer-swap-to-first-reconcile window; `Degraded` while any `managedBy=configuration` resource carries `PendingDeletion` or a ZoneLink lags; `Ready` when all reconciled. Generation pruning: prune when `generationIndex ≤ activeIndex - retainedGenerations` AND all enqueued resources from that generation have reached `Deleted`. Rollback: re-adopt `managedBy=configuration` resources in `Pending/PendingDeletion` back to the rollback target generation's owned set | +| Integration | `d2b-activation-helper` sets `managedBy=configuration` + `configurationGeneration` on every resource it activates; controller reads these fields to determine owned set — never `ownerRef` or bundle membership alone | +| Validation | Classification: `managedBy=controller` resource never enqueued (even if absent from bundle). `managedBy=api` resource never enqueued. `managedBy=configuration` resource absent from new bundle always enqueued. Finalizer safety: resource with active finalizer enters DeletionBlocked; not force-deleted; stays in `Pending`. Final deletion: resource reaches `Deleted` phase and row is removed from Zone store. Zone status: `Pending` during activation; `Degraded` while PendingDeletion outstanding; `Ready` when clean. Audit: `ResourceDelete` event includes `configurationGeneration` field. | +| Tests | `tests/unit/nix/cases/cleanup-two-generation.nix` (bundle diff); `tests/host-integration/cleanup-activation.nix` (async cleanup, Pending→Deleted, Zone Pending→Degraded→Ready, audit record); `tests/host-integration/cleanup-finalizer.nix` (DeletionBlocked, stays Pending, cleared on finalizer removal); `tests/host-integration/cleanup-controller-managed.nix` (managedBy=controller preserved); `tests/host-integration/cleanup-api-managed.nix` (managedBy=api preserved); `tests/host-integration/cleanup-rollback.nix` (rollback re-adoption); `tests/host-integration/cleanup-retention-window.nix` (generation pruning) | +| Drift pin | `make nix-unit-pin`; `make test-drift` if status/audit schema changes | +| Removal proof | Not removed; extended as new ResourceTypes are added | + +### ADR046-nix-021 + +| Field | Value | +| --- | --- | +| Dependency/owner | ADR046-nix-001; `d2b-contracts` workspace policy tests | +| Current source | `packages/d2b-contract-tests/tests/` (existing workspace policy lints: `tests/workspace-member-sort.rs`, `tests/crate-naming.rs`); no current Provider crate layout gate exists | +| Reuse action | new | +| Destination | `packages/d2b-contract-tests/tests/provider-crate-layout.rs`; workspace scan in `packages/xtask/src/main.rs` (extend `check-workspace` or add `check-provider-layout` subcommand) | +| Detailed design | Parse the root `packages/Cargo.toml` workspace member list; for every member path matching `packages/d2b-provider-*-*`: assert (1) `src/` directory exists and contains at least one `.rs` file; (2) `tests/` directory exists and contains at least one `.rs` file; (3) `integration/` directory exists and contains at least one `.rs` or fixture file; (4) `README.md` exists and is ≥ 200 bytes. All four conditions required; any single failure fails the test with a structured message naming the crate and missing path. Test runs as `cargo test -p d2b-contract-tests provider_crate_layout`; wired into `make test-policy`. | +| Integration | Wired into `make test-policy` (same gate family as existing workspace policy tests); no new `Makefile` target needed unless `test-policy` does not yet exist | +| Validation | Fixture: add a stub `packages/d2b-provider-test-missing-integration/` with `src/lib.rs` and `tests/smoke.rs` but no `integration/` and no `README.md`; assert test fails naming both missing paths. Add complete stub with all four paths; assert test passes. | +| Tests | `packages/d2b-contract-tests/tests/provider-crate-layout.rs` with fixture sub-directories under `packages/d2b-contract-tests/fixtures/`; included in `make test-policy` | +| Drift pin | `make test-policy`; re-run after any new `d2b-provider-*-*` crate is added to the workspace | +| Removal proof | Not removed; extended as new Provider crates are added to the workspace | + +### ADR046-nix-022 + +| Field | Value | +| --- | --- | +| Dependency/owner | ADR046-nix-005 (bundle derivation); `d2b-contracts` schema generation (ADR046-bus-005) | +| Current source | No current equivalent for a separate artifact catalog. Current `nixos-modules/bundle.nix` embeds package derivation references inline. Current `nixos-modules/closures-json.nix` uses `pkgs.closureInfo` keyed by `d2b.vms.`. | +| Reuse action | new | +| Destination | `nixos-modules/artifact-catalog.nix` (new emitter); `nixos-modules/options-artifacts.nix` (new option: `d2b.artifacts. = { package; type; }`); `/etc/d2b/artifact-catalog.json` (output artifact, `root:d2bd` 0640); `nixos-modules/bundle-zones.nix` (extend to include artifact catalog digest in `bundle.json`); `nixos-modules/options-zones-resources.nix` (replace `closureRef` / `nixosSystem` helpers with `systemArtifactId` validation) | +| Detailed design | `d2b.artifacts.` attrset option: `id` matches `^[a-z][a-z0-9-]*$`; `type ∈ { "provider", "nixos-system", "nixos-module-set", "config-bundle" }`; no other fields. Emitter computes `pkgs.closureInfo` for each entry and writes `artifact-catalog.json` with sorted entries (by `artifactId`) containing `artifactId`, `type`, `storePath` (private, for activation-helper staging), `packageDigest`, `closureDigest`, `closureSize`. `storePath` is a private field of the root:d2bd 0640 file; it is never emitted in public ResourceSpecs, status fields, audit records, or OTEL telemetry. The `bundle.json` manifest includes the artifact catalog file entry and its SHA256 digest. `d2b-activation-helper` reads `storePath` from the catalog to resolve and stage each artifact; verifies catalog digest before staging. Build-time validation: `artifactId` / `systemArtifactId` / `source.systemArtifactId` fields in resource specs resolve against `d2b.artifacts`; type-mismatch fails with a structured error. `d2b.providerCatalog..package` option is removed; replaced by `d2b.providerCatalog..artifactId`. `Guest.spec.systemArtifactId` replaces the former `nixosSystem` Nix-only helper. `Volume.source.systemArtifactId` replaces `source.closureRef`. | +| Integration | `nixos-modules/closures-json.nix` rewritten (ADR046-nix-012) to key by artifact ID; `nixos-modules/bundle-zones.nix` includes artifact catalog in integrity chain | +| Validation | Artifact catalog round-trip: declare artifact, build, verify JSON entry present with correct type/digests/storePath; missing artifact ID fails build with structured error; wrong-type artifact fails build; `storePath` absent from all public ResourceSpecs and status/audit/OTEL surfaces | +| Tests | `tests/unit/nix/cases/artifact-catalog.nix` (declaration, resolution, storePath present in private catalog); `tests/unit/nix/cases/artifact-catalog-type-mismatch.nix` (build failure for wrong type); `tests/unit/nix/cases/artifact-catalog-missing-id.nix` (build failure for absent ID); `tests/unit/nix/cases/artifact-catalog-public-surfaces.nix` (storePath absent from all emitted ResourceSpecs); `packages/d2b-contract-tests/tests/artifact-catalog-schema.rs` | +| Drift pin | `make test-drift` (artifact catalog schema); `make nix-unit-pin` | +| Removal proof | Not removed; extended as new artifact types are added | + + +### ADR046-bus-001 + +| Field | Value | +| --- | --- | +| Dependency/owner | ADR-046-componentsession-and-bus spec; ADR046-bus-005 (wire contract) must land first | +| Main commit source | `packages/d2b-session/src/engine.rs` (`SessionEngine`, `SessionEvent`); `packages/d2b-session/src/handshake.rs` (`NoiseHandshake`, `HandshakeCredentials` Nn/Kk/IkPsk2 variants, `HandshakeRole`, `NegotiatedOffer`, `EstablishedHandshake`, `encode_offer`, `negotiate_offer`, `x25519_public_key`, `encode_generation_discovery_request`, `accept_generation_discovery_request`, `encode_generation_discovery_response`, `decode_generation_discovery_response`); `packages/d2b-session/src/lifecycle.rs` (`SessionLifecycle`, `SessionPhase`, `KeepaliveAction`); `packages/d2b-session/src/scheduler.rs` (`FairScheduler`, `QueueClass`, `OutboundFrame`); `packages/d2b-session/src/record.rs` (`RecordProtector`, `ProtectedRecord`; replay cache 1024 entries); `packages/d2b-session/src/fragmentation.rs` (`Fragmenter`, `Reassembler`, `Fragment`); `packages/d2b-session/src/transport.rs` (`OwnedTransport`, `TransportDescriptor`, `TransportPacket`, `TransportError`); `packages/d2b-session/src/driver.rs` (`ComponentSessionDriver` trait with 20 async methods, `SessionDriverHandle`); `packages/d2b-session/src/server.rs` (`serve_ttrpc_services`, `SessionServerError`); `packages/d2b-session/src/metrics.rs` (`MetricEvent`, `MetricsSink`, `NoopMetrics`) | +| Tests at main | `packages/d2b-session/tests/component_session.rs` — full Nn/Kk/IkPsk2 session lifecycle, fragmentation, attachments, named streams, cancellation, keepalive; `packages/d2b-session/tests/noise_vectors.rs` — KAT against `docs/reference/component-session-v2-vectors.json` | +| Selected behavior | Complete session protocol: preface negotiation, Noise handshake (all three profiles), record protection with 1024-entry replay cache, fair two-class scheduler, fragmentation/reassembly, keepalive (ping/timeout/close), ttrpc bridge (`serve_ttrpc_services`), generation discovery (pre-handshake version probe), `ComponentSessionDriver` as the sole application-layer control surface | +| v3 destination | `packages/d2b-bus/src/session/` (new crate `d2b-bus`); `ComponentSessionDriver` becomes the central abstraction for all Zone bus sessions (local-root controller ↔ broker, controller ↔ guest agent, controller ↔ provider agent) | +| ADR45 exclusions | `HandshakeOffer` fields `purpose: EndpointPurpose` and `service: ServicePackage` reference ADR45 enum values (`RealmPeer`, `RealmBootstrap`, `RealmV2`); adapt variant names per ADR-046-componentsession-and-bus owning spec; wire tag values are stable and must not change; `Locality::GuestLocal` remains valid for vsock guest sessions | +| Drift pin | `make test-rust` (session crate); Noise KAT vectors must pass after copy | + +### ADR046-bus-002 + +| Field | Value | +| --- | --- | +| Dependency/owner | ADR046-bus-001; ADR046-bus-005 | +| Main commit source | `packages/d2b-session/src/attachment.rs` (`AttachmentPayload` trait with `validate_descriptor()`, `close()`, `as_any()`, `into_any()`; `OwnedAttachment` with `unbound()`/`bind_received()`/`bind_outbound()`/`validate_payload_descriptor()`/`into_payload()`; `AttachmentValidationError` enum: Kind/ObjectType/Access/CloseOnExec/Other); `packages/d2b-session/src/streams.rs` (`NamedStreamMux`, `StreamId`, `StreamEvent` enum: Data/RemoteClosed/Reset, `StreamPhase` enum: Open/HalfClosedLocal/HalfClosedRemote/Closed/Reset) | +| Tests at main | `packages/d2b-session/tests/component_session.rs` (attachment ownership, stream mux, credit accounting) | +| Selected behavior | Attachment lifecycle: `OwnedAttachment::unbound()` for transport-received before descriptor auth; `validate_descriptor()` called only after authenticated decryption; `into_payload()` transfers ownership without close; `Drop` closes remaining payload. `NamedStreamMux` with per-stream and aggregate queue byte limits; half-close semantics; credit-based flow control | +| v3 destination | `packages/d2b-bus/src/session/` (same crate as ADR046-bus-001); `AttachmentPayload` and `OwnedAttachment` are transport-neutral and require no ADR45 adaptation | +| ADR45 exclusions | `AttachmentDescriptor` from `v2_component_session` has `kind: AttachmentKind` and `object_type: KernelObjectType`; these values are wire-stable and carry no ADR45 realm naming; no exclusions for this item | + +### ADR046-bus-003 + +| Field | Value | +| --- | --- | +| Dependency/owner | ADR046-bus-001; ADR046-bus-005 | +| Main commit source | `packages/d2b-session/src/cancellation.rs` (`Cancellation` with `cancel()`/`is_cancelled()`/`cancelled()` async notify; `RequestRegistry` with generation scoping, `register()`/`mark_dispatched()`/`cancel()`/`cancel_all()`/`cancel_generated()`/`complete()`/`remove()`/`signal()`/`active()`); `packages/d2b-session/src/deadline.rs` (`DeadlineBudget` with `admit_metadata()` validating clock skew ≤30 s, request lifetime ≤15 min, idempotency key, peer ttrpc timeout); `packages/d2b-session/src/bootstrap.rs` (`Secret32` zeroizing 32-byte key; `BootstrapPsk`; `AdmittedBootstrapPsk`; `BootstrapAdmission` single-use PSK with operation-ID + replay-nonce check) | +| Tests at main | `packages/d2b-session/tests/component_session.rs` (cancel-before-dispatch, cancel-after-dispatch, generation-mismatch cancel, deadline admit) | +| Selected behavior | `RequestRegistry` is per-generation; calling `cancel_all()` cancels every outstanding request; `DeadlineBudget::admit_metadata()` is the single gate for all inbound request metadata; `BootstrapAdmission::consume()` single-use PSK prevents replay | +| v3 destination | `packages/d2b-bus/src/session/` | +| ADR45 exclusions | `cancel_generated()` calls `common::CancelRequest`/`CancelResponse` from `v2_services`; the `common.rs` proto type paths may change when services are versioned to v3 (ADR046-bus-006); the underlying `CancelRequest`/`CancelAck`/`CancelResult` contract from `v2_component_session` is wire-stable and requires no change | + +### ADR046-bus-004 + +| Field | Value | +| --- | --- | +| Dependency/owner | ADR046-bus-001 | +| Main commit source | `packages/d2b-session-unix/src/adapter.rs` (feature `host-socket`) — `UnixSeqpacketTransport`, `UnixStreamTransport`, `UnixAttachmentPayload`, `OwnedUnixAttachment`, `PeerIdentityPolicy` (Pathname/InheritedSocketpair), `DescriptorPolicyResolver`, `PathnamePeerVerifier`; `packages/d2b-session-unix/src/credit.rs` — `CreditBundle`, `CreditError`, `CreditPool`, `CreditScope`, `CreditScopeSet`, `ProcessCreditLimit`; `packages/d2b-session-unix/src/descriptor.rs` — `ReceivedPacket`, `AcceptedAttachment`, `DescriptorPolicy`, `FirstPacketCredentials`, `ObjectIdentity`, `PeerCredentials`, `PidfdIdentityPolicy`, `VerifiedPacket`; `packages/d2b-session-unix/src/pidfd.rs` — `DigestEvidenceCallback`, `PidfdEvidence`, `PidfdIdentityVerifier`, `PidfdInfoSource`, `ProcPidfdIdentityVerifier`, `ProcSelfFdInfoSource`, `parse_pidfd_fdinfo`; `packages/d2b-session-unix/src/socket.rs` — `AncillaryCapacity`, `OutboundPacket`, `PacketBurst`, `SendBurst`, `SentPacket`, `SeqpacketSocket`, `StreamRead`, `StreamSocket`, `prearmed_seqpacket_pair`; `packages/d2b-session-unix/src/systemd.rs` — `ActivatedSeqpacketListener`, `ActivatedSeqpacketListeners`, `SystemdActivationError`; `packages/d2b-session-unix/src/vsock.rs` (feature `native-vsock`) — `FramedVsockTransport`, `NativeVsockListener`, `NativeVsockTransport` | +| Tests at main | `packages/d2b-session-unix/tests/unix_session.rs` — seqpacket pair, Unix stream, pidfd identity, FD attachment validation, credit pool exhaustion and recovery | +| Selected behavior | Audited Unix seqpacket and stream transports implementing `OwnedTransport`; pidfd identity verification reads `/proc//fdinfo/` for `st_dev`/`st_ino`; 6-scope credit pool with per-process and per-host limits (`MAX_PROCESS_ATTACHMENT_CREDITS=2048`, `MAX_HOST_ATTACHMENT_CREDITS=8192`); systemd `SD_LISTEN_FDS` seqpacket activation; vsock framing with length-prefixed records | +| v3 destination | `packages/d2b-bus/src/transport/unix/` — adapt as the Unix transport backend for `d2b-bus` sessions; keep the `host-socket`/`native-vsock` feature gates intact; the transport code itself has no ADR45 realm bindings | +| ADR45 exclusions | `ActivatedSeqpacketListeners` reads socket names from `SD_LISTEN_FDS`; socket names are bound to current `d2bd.socket` / `d2b-priv-broker.socket` unit names; v3 socket paths come from Zone bootstrap config — activation code is reusable, socket name strings are not. `PeerIdentityPolicy::accepted()` is transport-layer code and has no ADR45 binding | + +### ADR046-bus-005 + +| Field | Value | +| --- | --- | +| Dependency/owner | ADR-046-componentsession-and-bus; naming and wire enumeration decisions per ADR-046-componentsession-and-bus owning spec before final adoption | +| Main commit source | `packages/d2b-contracts/src/v2_component_session.rs` (entire file, 2500+ lines at `a1cc0b2d`): wire constants — `PREFACE_MAGIC=*b"D2BCS2\r\n"`, `COMPONENT_SESSION_MAJOR=2`, `COMPONENT_SESSION_MINOR=0`, all `MAX_*` limits, `FRAGMENT_HEADER_LEN=24`, `RECORD_HEADER_LEN=24`; structs — `BoundedVec`, `ComponentSessionPreface`, `HandshakeOffer`, `EndpointPolicy`, `EndpointPolicyIdentity`, `LimitProfile`, `TransportBinding`, `AttachmentPolicy`, `RequestEnvelope`, `AdmittedDeadline`, `RecordHeader`, `FragmentHeader`, `SendSequence`, `ReceiveSequence`, `KeepaliveRecord`, `CloseRecord`, `GuestSessionCredentialV1`, `GuestBootstrapCredentialV1`, `GuestBootstrapPsk`, `BootstrapPskBinding`, `BootstrapPskState`; enums — `EndpointPurpose`(19 variants), `PurposeClass`(3), `EndpointRole`(19), `ServicePackage`(15), `NoiseProfile`(3), `Locality`(4), `TransportClass`(5+), `AttachmentKind`, `KernelObjectType`, `CloseReason`, `ContractError`, `BinaryError`; closed-enum wire tag codec | +| Tests at main | `packages/d2b-session/tests/component_session.rs` (wire protocol round-trip); `packages/d2b-session/tests/noise_vectors.rs` (KAT from `docs/reference/component-session-v2-vectors.json`) | +| Selected behavior | Canonical wire values and fail-closed validation for the session protocol; `BoundedVec` serde+JsonSchema; all binary size constants are stable wire commitments and must not change without a `COMPONENT_SESSION_MAJOR` bump | +| v3 destination | `packages/d2b-contracts/src/v3/component_session.rs`; `COMPONENT_SESSION_MAJOR` stays 2 unless the wire handshake format changes; KAT vectors in `docs/reference/component-session-v2-vectors.json` must still pass after copy | +| ADR45 exclusions | `EndpointRole` variants `RealmController`(3), `RealmBroker`(5): may rename per ADR-046-componentsession-and-bus owning spec; wire tag values 3 and 5 are stable and must not change. `ServicePackage` variants `RealmV2`(2, `"d2b.realm.v2"`) and `DaemonV2`(1): `RealmV2` may rename per owning spec; wire tag 2 is stable. `EndpointPurpose` variants `RealmPeer`(3), `RealmBootstrap`(4): may rename per owning spec; wire tags 3 and 4 are stable. `PurposeClass` and `Locality` variant names: confirm per owning spec; no "realm" prefix in either enum so rename is unlikely but must be verified before adoption | + +### ADR046-bus-006 + +| Field | Value | +| --- | --- | +| Dependency/owner | ADR046-bus-005; Zone service naming per ADR-046-componentsession-and-bus owning spec | +| Main commit source | `packages/d2b-contracts/src/generated_v2_services/` (48 files at `a1cc0b2d`): `activation.rs`+`_ttrpc.rs`, `broker.rs`+`_ttrpc.rs`, `clipboard.rs`+`_ttrpc.rs`, `clipboard_picker.rs`+`_ttrpc.rs`, `daemon.rs`+`_ttrpc.rs`, `guest.rs`+`_ttrpc.rs`, `notify.rs`+`_ttrpc.rs`, `provider_audio.rs`+`_ttrpc.rs`, `provider_credential.rs`+`_ttrpc.rs`, `provider_device.rs`+`_ttrpc.rs`, `provider_display.rs`+`_ttrpc.rs`, `provider_infrastructure.rs`+`_ttrpc.rs`, `provider_network.rs`+`_ttrpc.rs`, `provider_observability.rs`+`_ttrpc.rs`, `provider_runtime.rs`+`_ttrpc.rs`, `provider_storage.rs`+`_ttrpc.rs`, `provider_substrate.rs`+`_ttrpc.rs`, `provider_transport.rs`+`_ttrpc.rs`, `realm.rs`+`_ttrpc.rs`, `runtime_systemd_user.rs`+`_ttrpc.rs`, `security_key.rs`+`_ttrpc.rs`, `shell.rs`+`_ttrpc.rs`, `terminal.rs`, `tty.rs`+`_ttrpc.rs`, `user.rs`+`_ttrpc.rs`, `wayland.rs`+`_ttrpc.rs`, `common.rs`, `mod.rs`; also `packages/d2b-contracts/src/v2_guest_services.rs`, `v2_component_session.rs`'s `SERVICE_INVENTORY` + fingerprint functions | +| Tests at main | `packages/d2b-provider-toolkit/tests/conformance.rs` (all 11 provider-type axes); `packages/d2b-session/tests/component_session.rs` (service fingerprint assertions) | +| Selected behavior | Each `*_ttrpc.rs` defines the ttrpc `Arc` server type and method dispatch table; `common.rs` defines shared `RequestMetadata`, `CancelRequest`/`CancelResponse`, `Outcome`, `ErrorKind`; `SERVICE_INVENTORY` indexes all services for schema-fingerprint verification | +| v3 destination | `packages/d2b-contracts/src/v3/services/` (versioned sub-path); service interfaces adopted as-is initially; breaking method changes require a new proto major version | +| ADR45 exclusions | `realm.rs` service `"d2b.realm.v2.RealmService"` and its `RealmId`-typed fields → rename per ADR-046-componentsession-and-bus owning spec; `user.rs` and `runtime_systemd_user.rs` reference `WorkloadId` in some method contexts → adapt to `ResourceName` on copy; the 11 provider service files (`provider_*.rs`) carry no realm naming and require no ADR45 adaptation | + +### ADR046-bus-007 + +| Field | Value | +| --- | --- | +| Dependency/owner | ADR046-bus-005; ADR046-bus-006; EndpointRole naming per ADR-046-componentsession-and-bus owning spec | +| Main commit source | `packages/d2b-provider/src/registry.rs` (`ProviderRegistry`, `ProviderRegistryBuilder`, `RegistryLimits` defaults: total_in_flight=256, per_provider_in_flight=32, `AdmissionOptions`); `packages/d2b-provider/src/rpc.rs` (`AuthenticatedProviderRpc` trait, `RpcProviderProxy`, `RpcCall`, `RpcOperation` enum: Health/Capabilities/Method, `RpcPayload` enum: None/Operation/Plan/Adoption/LeaseRequest/Lease, `RpcResponse` enum: Health/Capabilities/Plan/Handle/Observation/ObservabilityQuery/Mutation/Lease, `SessionIdentity`, `ProviderClock`, `SystemProviderClock`); `packages/d2b-provider/src/instance.rs` (`ProviderInstance` enum, 11 variants: Runtime/Infrastructure/Transport/Substrate/Credential/Display/Network/Storage/Device/Audio/Observability); `packages/d2b-provider/src/context.rs` (`OwnedOperationContext`, `CancellationToken`) | +| Tests at main | `packages/d2b-provider/tests/runtime.rs` | +| Selected behavior | `ProviderRegistry` admits sessions against a versioned generational snapshot and drains before rotation; per-provider in-flight cap prevents one slow provider from consuming all capacity; `RpcProviderProxy` converts typed `RpcCall` into the correct per-ProviderType ttrpc service invocation through `AuthenticatedProviderRpc`; `RegistryLimits` validated at build time | +| v3 destination | `packages/d2b-provider/src/` (adapt in place); `ProviderRegistry` becomes the Zone controller's active Provider registry; `ProviderInstance` enum extended with new Provider types as dossiers are ratified | +| ADR45 exclusions | `AdmissionOptions.peer_role: EndpointRole` contains ADR45 role values — update variant names per ADR-046-componentsession-and-bus owning spec; `SessionIdentity` contains `provider_generation` and `service: ServicePackage` — the generation type is stable; `ServicePackage::ProviderV2` may rename per owning spec; `v2_identity::ProviderId`/`ProviderType` carry no realm naming and require no adaptation | + +### ADR046-bus-008 + +| Field | Value | +| --- | --- | +| Dependency/owner | ADR046-bus-007; ADR046-bus-006; EndpointRole and ServicePackage naming per ADR-046-componentsession-and-bus owning spec | +| Main commit source | `packages/d2b-provider-toolkit/src/adapter.rs` (`ProviderAgentAdapter::new()` validates session identity — checks `peer_role == EndpointRole::ProviderAgent`, `service == ServicePackage::ProviderV2`, `binding.agent_generation == identity.provider_generation`; `invoke_session()` checks attachment index ordering); `packages/d2b-provider-toolkit/src/server.rs` (`GeneratedProviderServiceServer` with per-session object stores — `MAX_SESSION_PLANS=256`, `MAX_SESSION_HANDLES=1024`, `MAX_SESSION_LEASES=1024`, `MAX_AGENT_IN_FLIGHT=64`; atomic `accepting`/`in_flight`; `idle: Notify`; routes all 11 ProviderType ttrpc method families); `packages/d2b-provider-toolkit/src/conformance.rs` (`check_provider_conformance`, `check_descriptor_conformance`, `ConformanceError` enum); `packages/d2b-provider-toolkit/src/registration.rs` (`register_exact_instances`, `ToolkitError` enum) | +| Tests at main | `packages/d2b-provider-toolkit/tests/conformance.rs` — `every_axis_passes_identical_in_process_and_rpc_conformance` tests all 11 `ProviderType` variants both in-process and through the full RPC path | +| Selected behavior | `ProviderAgentAdapter` is the descriptor-bound validation gate between a ComponentSession and a provider instance; `GeneratedProviderServiceServer` is the agent-side ttrpc dispatch engine; conformance kit provides a reference test harness for every Provider implementation; `register_exact_instances` is the canonical pattern for building a test registry from static descriptors | +| v3 destination | `packages/d2b-provider-toolkit/src/` (adapt in place); conformance tests in `packages/d2b-provider-toolkit/tests/conformance.rs` must pass unchanged after the ADR45 exclusions are adapted | +| ADR45 exclusions | `ProviderAgentAdapter::new()` hard-checks `peer_role == EndpointRole::ProviderAgent` (tag 7) and `service == ServicePackage::ProviderV2` (tag 4) — update the Rust enum variant names if the owning spec renames them per ADR-046-componentsession-and-bus; wire tag values 7 and 4 must not change. `v2_identity::{RealmId, WorkloadId}` appear in test context imports — adapt `RealmId` → `ZoneId`, `WorkloadId` → `ResourceName` on copy | + +### ADR046-bus-009 + +| Field | Value | +| --- | --- | +| Dependency/owner | ADR046-bus-007; ADR046-bus-005; TargetInput v3 shape per ADR-046-componentsession-and-bus owning spec | +| Main commit source | `packages/d2b-client/src/client.rs` (`Client`, `ConnectedClient`, `MetadataInput`, `CallOptions`, `CancellationToken`, `Response`, `RetryPolicy`, `SystemClock`, `WallClock`); `packages/d2b-client/src/session.rs` (`ComponentSessionConnector` trait, `ConnectedSession` with `driver: SharedDriver` + `ttrpc_socket: Socket` + `limits: LimitProfile`, `SessionCall`, `SessionReply`, `SessionFailure` enum: BeforeDispatch/Retryable/Ambiguous/Disconnected/Deadline/Cancelled/Protocol, `NamedStream`, `SharedDriver`); `packages/d2b-client/src/target.rs` (`TargetInput` enum, `ServiceOwner`, `ResolvedTarget`, `RouteRecord`, `RouteTable`, `TransportKind`, `TransportSelection`); `packages/d2b-client/src/service.rs` (`GeneratedClient`, `MethodHandle`, `ServiceHandle`, `ServiceKind`); `packages/d2b-client/src/daemon_service.rs` (`DaemonClient`, `DaemonMethod`, `DaemonLifecycleRequest`, `DaemonTerminal`, `daemon_call_options`); `packages/d2b-client/src/guest_service.rs` (`GuestClient`, `GuestOperation`, `GuestCancelCall`, `GuestInspectCall`, `GuestRetainedLogCall`); `packages/d2b-client/src/host_socket.rs` (feature `host-socket`) — `HostSocketConnector`, `local_daemon_endpoint_identity` | +| Tests at main | `packages/d2b-client/tests/client.rs` | +| Selected behavior | Transport-neutral typed async client; `ComponentSessionConnector` abstracts connection setup; `SessionFailure` provides precise failure classification for retry policy; `MetadataInput` constructs signed request envelopes with clock-bounded lifetimes; `NamedStream` exposes named-stream channel as a client-side abstraction; `HostSocketConnector` is the reference Unix socket connection implementation | +| v3 destination | `packages/d2b-client/src/` (adapt in place); client becomes the primary CLI and controller access path for Zone-local and cross-Zone ComponentSession services | +| ADR45 exclusions | `TargetInput::Workload { realm: RealmId, workload: WorkloadId }` → v3 shape addresses resources as `ResourceRef` (e.g., `Zone/`, `Guest/`) per ADR-046-componentsession-and-bus owning spec; `TargetInput::Realm(RealmId)` → `TargetInput::Zone(ZoneId)`; `ServiceOwner::Workload { realm, workload }` → `ServiceOwner::Resource { zone: ZoneId, resource: ResourceName }`. `HostSocketConnector::local_daemon_endpoint_identity()` returns identity pinned to current `d2bd.socket` path — v3 socket path comes from Zone bootstrap config and must not be hard-coded. `DaemonClient`/`DaemonMethod` verb set per resource-api/authz foundation spec | + +### ADR046-bus-010 + +| Field | Value | +| --- | --- | +| Dependency/owner | ADR046-bus-001; ADR046-bus-005; naming and service name per ADR-046-componentsession-and-bus owning spec | +| Main commit source | `packages/d2b-realm-router/src/service_v2.rs` (`RealmSessionAuthority` struct with `realm: RealmId`, `peer_role: EndpointRole`, `locality: Locality`, `purpose: PurposeClass`, `custody: CredentialCustody`; `CredentialCustody` enum: None/GatewayGuest; constructors `local_controller()`/`gateway_peer()`/`new()`; `REALM_SERVICE_NAME = "d2b.realm.v2.RealmService"`; constants `DEFAULT_MAX_REALM_BINDINGS=256`, `DEFAULT_MAX_SHORTCUTS=256`, `DEFAULT_MAX_MUTATION_RECORDS=1024`, `DEFAULT_AUDIT_CAPACITY=1024`; internal `MAX_DISPATCH_IN_FLIGHT=64`, `SHUTDOWN_TIMEOUT=5s`; `RealmServiceLimits`; dispatch using `Arc(MAX_DISPATCH_IN_FLIGHT)` and `JoinSet<()>`); `packages/d2b-realm-router/tests/realm_service_v2.rs`; `packages/d2b-realm-router/tests/transport_topology_harness.rs` | +| Tests at main | `packages/d2b-realm-router/tests/realm_service_v2.rs` — routing service tests; `packages/d2b-realm-router/tests/transport_topology_harness.rs` — topology harness | +| Selected behavior | `RealmSessionAuthority` enforces that host-local sessions hold no realm credentials (`CredentialCustody::None`) while gateway sessions hold `GatewayGuest` custody — this is the runtime enforcement of ADR 0032 "relay identity is not local auth"; concurrent dispatch with `Semaphore(64)` bound; 5-second graceful shutdown via `JoinSet` | +| v3 destination | `packages/d2b-bus/src/routing/zone_service.rs`; `RealmSessionAuthority` renames to `ZoneSessionAuthority`; `CredentialCustody` is behavior-stable and requires no rename; `REALM_SERVICE_NAME` updates per ADR-046-componentsession-and-bus owning spec | +| ADR45 exclusions | `realm: RealmId` field in `RealmSessionAuthority` → `zone: ZoneId`; `REALM_SERVICE_NAME = "d2b.realm.v2.RealmService"` → v3 service name per ADR-046-componentsession-and-bus owning spec; `EndpointRole::LocalRootController`, `RealmController`, `RemotePeer` used in `new()` validation — may rename per owning spec; wire tags remain stable; `PurposeClass::Local`/`Enrolled`/`Bootstrap` — confirm per owning spec | + +### ADR046-bus-011 + +| Field | Value | +| --- | --- | +| Dependency/owner | ADR046-bus-007; ADR046-bus-006; ADR046-bus-005; PROVIDER_BUNDLE_VERSION bump required on any bundle artifact format change | +| Main commit source | `packages/d2bd/src/provider_registry.rs` (`ProviderCompositionError` enum with 26 named variants including `AzureVmForbidden`, `LegacyRunnerForbidden`, `NondispatchableCapability`, `ProcessIdentityMismatch`, `LifecycleBudgetExceeded`; all first-party factory instantiations: `PipewireVhostUserAudioFactory`, `HostMediatedDeviceFactory`, `WaylandDisplayFactory`, `LocalRealmNetworkFactory`, `LocalObservabilityFactory`, `AzureContainerAppsRuntimeProviderFactory`, `LocalRuntimeProviderFactory` CH/QEMU/systemd-user, `LocalStorageFactory`, `HostSubstrateProviderFactory` Linux/NixOS, `AzureRelayProviderFactory`, `LocalTransportFactory`; constants `PROVIDER_BUNDLE_VERSION: u32 = 13`, `PROVIDER_BUNDLE_SCHEMA_VERSION: &str = "v2"`, `AZURE_VM_IMPLEMENTATION_ID: &str = "azure-vm"`, static `NEXT_LIFECYCLE_OPERATION_ID: AtomicU64`) | +| Tests at main | `packages/d2bd/src/` integration tests exercising composition (search `#[cfg(test)]` blocks in `provider_registry.rs`) | +| Selected behavior | Fail-closed composition: every error is named; `AzureVmForbidden` explicitly rejects non-production implementations; bundle loaded through `load_bundle_resolver()` and validated against `PROVIDER_BUNDLE_VERSION`; `NEXT_LIFECYCLE_OPERATION_ID` provides monotone IDs across restarts | +| v3 destination | `packages/d2bd/src/provider_registry.rs` (adapt in place); `PROVIDER_BUNDLE_VERSION` bumps when bundle artifact format changes; `PROVIDER_BUNDLE_SCHEMA_VERSION` updates from `"v2"` to `"v3"`; `ProviderCompositionError` variants retained with v3-specific variants added | +| ADR45 exclusions | Uses `d2b_contracts::v2_identity::{RealmId, WorkloadId, RealmPath as ProviderRealmPath}` in binding contexts → adapt to `ZoneId`/`ResourceName`; `d2b_contracts::provider_registry_v2` module types (`ProviderBindingV2ConsumerView`, `ProviderRegistryEntryV2`, `ProviderRegistryV2`) are ADR45 bundle artifact types → v3 replaces with `d2b_contracts::v3::provider_registry`; `PROVIDER_BUNDLE_VERSION = 13` is the ADR45 pinned version — a bump is required before v3 adoption; numeric value is determined when the v3 bundle format is finalized in this work item | + +### ADR046-bus-012 + +| Field | Value | +| --- | --- | +| Dependency/owner | ADR046-bus-011; ADR046-bus-007; GuestLifecycleRequest ResourceRef addressing per ADR-046-componentsession-and-bus owning spec | +| Main commit source | `packages/d2bd/src/provider_effects.rs` (`DaemonEffectAdapters` struct binding all semantic port traits; `ProviderLifecycleDispatch` with `MAX_TRACKED_LIFECYCLE_MUTATIONS=256` and `BTreeMap`; all effect port imports: `AudioEffectPort`/`AudioQueryPort`, `DeviceEffectPort`/`DeviceQueryPort`, `DisplayEffectPort`, `NetworkEffectPort`, `BoundedExportSink`/`ObservabilityExportPort`/`ObservabilityQueryPort`, `RuntimeAdoptionControl`/`RuntimeConfiguredItemControl`/`RuntimeControlPort`/`RuntimeEnsureControl`/`RuntimeOperationControl`/`RuntimePlanDecision`, `StorageEffectPort`, `HostSubstratePort`, `LocalEndpointPort`; lifecycle dispatch functions `dispatch_broker_vm_start_on_blocking_adapter`, `dispatch_broker_vm_stop_on_blocking_adapter`; test helpers `reset_test_runtime_lifecycle_calls()`, `test_runtime_lifecycle_calls()`, `TEST_RUNTIME_START_CALLS`/`TEST_RUNTIME_STOP_CALLS` thread-locals) | +| Tests at main | `packages/d2bd/src/provider_effects.rs` `#[cfg(test)]` blocks; `packages/d2bd/src/provider_registry.rs` composition tests | +| Selected behavior | Each effect adapter is descriptor-bound at composition time in `provider_registry.rs`; `ProviderLifecycleDispatch` tracks in-flight lifecycle mutations with a bounded BTreeMap and idempotency-keyed deduplication; `dispatch_broker_vm_start/stop_on_blocking_adapter` routes to the broker via a blocking task adapter; test helpers provide per-test reset of lifecycle call counters | +| v3 destination | `packages/d2bd/src/provider_effects.rs` (adapt in place); effect port bindings retained; lifecycle dispatch updated from `VmLifecycleRequest` to a v3 Guest lifecycle op addressed by `ResourceRef` (`Guest/`) with Zone context (`Zone/`); exact wire type per ADR-046-componentsession-and-bus owning spec | +| ADR45 exclusions | `VmLifecycleRequest` from `d2b-contracts/src/public_wire.rs` uses `vm_name: String` (ADR45 daemon wire); v3 requires a `GuestLifecycleRequest` or equivalent with `ResourceRef` addressing; `BrokerCallerRole` from `broker_wire.rs` is an ADR45 broker identity type — keep in place and flag for broker wire update when broker contract is versioned; `DaemonAuditSinkStatus` references current audit shape — keep until v3 audit contract is defined | diff --git a/docs/specs/ADR-046-resources-zone-control.md b/docs/specs/ADR-046-resources-zone-control.md new file mode 100644 index 000000000..b660609f8 --- /dev/null +++ b/docs/specs/ADR-046-resources-zone-control.md @@ -0,0 +1,3725 @@ +# ADR 0046 ResourceTypes: Zone, ZoneLink, Provider, Role, RoleBinding, Quota, and EmergencyPolicy + +| Field | Value | +| --- | --- | +| Spec ID | `ADR-046-resources-zone-control` | +| Parent | ADR 0046 | +| Status | Proposed | +| Version | 1 | +| Baseline | `b5ddbed67867d9244bf33390868101bd9b053e49` | +| Normative | Yes | +| Owners | `d2b-contracts` (schemas), `d2b-core-controller` (handlers), Zone runtime, Nix resource compiler | +| Depends on | `ADR-046-decision-register`, `ADR-046-terminology-and-identities`, `ADR-046-resource-object-model`, `ADR-046-resource-api-and-authorization`, `ADR-046-resource-store-redb`, `ADR-046-core-controllers`, `ADR-046-provider-model-and-packaging`, `ADR-046-resource-reconciliation` | +| Supersedes | None | + +## 1. Scope + +This spec defines the complete normative contract for the seven core control +ResourceTypes: `Zone`, `ZoneLink`, `Provider`, `Role`, `RoleBinding`, `Quota`, +and `EmergencyPolicy`. + +For each type it provides: + +- complete `metadata`/`spec`/`status` field schemas with bounds and defaults; +- provider/controller ownership; +- phase/condition/outcome transitions and timestamps; +- ownerRef/finalizer/deletion behavior; +- native RBAC and bootstrap authorization; +- Zone self-resource and parent/child ZoneLink semantics; +- core controller algorithms and async reconciliation triggers; +- security, audit, OTEL, and error requirements; +- normative Nix authoring examples; +- conformance requirements and exact tests; +- current-code fit tables; +- implementation work items. + +This revision resolves all design choices for the covered ResourceTypes. New +design decisions are tracked in +[`docs/specs/ADR-046-decision-register.md`](ADR-046-decision-register.md). + +All common resource fields (universal envelope, generation, revision, UID, +condition and outcome shapes, phase values, deletion protocol) are governed by +[`ADR-046-resource-object-model`](ADR-046-resource-object-model.md). This spec +extends that contract for these seven types only. + +--- + +## 2. Zone + +### 2.1 Role + +`Zone` is the self-resource of a Zone store. Every Zone store contains exactly +one authoritative: + +```text +Zone/ +``` + +`` must equal the redb `store_meta.zone_name` key and the resource's +own `metadata.zone` field. There is no cross-Zone resource reference; a +`Zone/` resource resolves only inside the store where `` equals +the store's own `zone_name`. The Zone resource is not replicated, shadowed, or +referenced from other Zones. + +**Controller/owner**: The `configuration publication` handler of the fixed +core-controller process (Provider/system-core) owns Zone reconciliation. No +other controller may update Zone spec or status. + +### 2.2 Metadata + +Zone metadata follows the common envelope. Additional Zone-specific rules: + +| Field | Rule | +| --- | --- | +| `metadata.name` | Must equal `store_meta.zone_name`; validated at every open/upgrade; immutable after first commit | +| `metadata.zone` | Must equal `metadata.name` for the self resource | +| `metadata.uid` | Immutable; equals `store_meta.zone_uid`; generated once at store creation | +| `metadata.generation` | Starts at 1; increments only on spec change | +| `metadata.ownerRef` | Must be `null`; Zone cannot be owned by another resource | +| `metadata.finalizers` | Core may add `core.zone-drain` finalizer during shutdown/reset; no other controller may add a Zone finalizer | +| `metadata.deletionRequestedAt` | Set only by deliberate reset/shutdown; normal operations never delete Zone | + +### 2.3 Spec + +`Zone.spec` is `{}` — an empty object. Zone identity, API catalog, policy +revision, and configuration revision are entirely derived from the store +metadata and other installed resources (Provider, Role, RoleBinding, Quota, +EmergencyPolicy). The Zone resource is a pure identity anchor. + +```yaml +apiVersion: resources.d2b.io/v3 +type: Zone +metadata: + name: dev + zone: dev + uid: + generation: 1 + revision: + ownerRef: null + finalizers: [] + deletionRequestedAt: null + createdAt: 2026-07-22T00:00:00Z + updatedAt: 2026-07-22T00:00:00Z +spec: {} +status: + observedGeneration: 1 + phase: Ready + conditions: [] + lastReconciledAt: 2026-07-22T00:00:01Z + startedAt: 2026-07-22T00:00:00Z + completedAt: null + outcome: null +``` + +### 2.4 Status + + +Zone.status extends common status with: + +| Field | Type | Rules | +| --- | --- | --- | +| `apiCatalogRevision` | u64 | Current Zone api_catalog revision from store_meta | +| `policyRevision` | u64 | Current authorization policy revision from store_meta | +| `configurationRevision` | u64 | Active configuration revision from store_meta | +| `coreControllerPhase` | phase enum | Aggregate phase of all mandatory core handlers | +| `handlers` | bounded list | Per-handler name/phase/lastReconciledAt (no resource names, no secrets) | +| `installedProviderCount` | u32 | Count of installed (non-Deleted) Provider resources | +| `readyProviderCount` | u32 | Count of Ready Provider resources | +| `totalResourceCount` | u32 | Total non-deleted resource count | +| `activeConfigurationGeneration` | u64 | Generation number of the active Nix-authored resource bundle | +| `generationCleanupPending` | bool | True while prior-generation config-owned resources are completing deletion | +| `cleanupPendingCount` | u32 | Count of config-owned resources from prior generations awaiting deletion | + +`coreControllerPhase` is the stricter of all mandatory handler phases. Optional +handlers (ZoneLink maintenance, backup cleanup) may report Degraded without +changing `coreControllerPhase` to Failed. + +`handlers` entries contain only: handler name (stable closed enum), phase, and +lastReconciledAt. No resource names, counts by type, provider diagnostics, or +store paths appear here. + +### 2.5 Phase and conditions + +| Phase | Meaning | +| --- | --- | +| `Pending` | Store open; core handlers initializing | +| `Ready` | All mandatory handlers Ready; Zone fully operational | +| `Degraded` | Core handlers Ready but optional handlers impaired, or config-owned resources are awaiting deletion (`GenerationCleanupPending=True`) | +| `Failed` | One or more mandatory handlers in Failed; Zone non-operational | +| `Unknown` | Store unavailable or Zone runtime cannot determine state | +| `Deleted` | Final revision event emitted; store closing/closed | + +Zone never reaches `Succeeded` (it is long-lived, not a one-shot resource). + +Closed condition types for Zone: + +| Condition type | Meaning | +| --- | --- | +| `StoreReady` | Underlying redb database is open and healthy | +| `ConfigurationCurrent` | Active configuration generation is current | +| `ApiCatalogReady` | API catalog bound and valid | +| `AuthorizationReady` | Role/RoleBinding index ready and authorization operational | +| `ProvidersHealthy` | All required Provider processes are Ready | +| `CoreHandlerReady` | Fixed core-controller process is fully initialized | +| `GenerationCleanupPending` | Prior-generation config-owned resources have not yet completed deletion | +| `GenerationCleanupFailed` | One or more prior-generation config-owned resources are stuck awaiting deletion beyond threshold | + +Condition `status` values follow the common `True|False|Unknown` rule. +Transition times are RFC 3339 UTC. + +`GenerationCleanupPending=True` changes the Zone `phase` to `Degraded` while +any prior-generation config-owned resources are awaiting deletion; this is the +normal pending-cleanup posture and does not indicate a fault. `GenerationCleanupFailed=True` +is additionally set when a candidate is stuck beyond `cleanupStuckThreshold` (default +5 minutes) with no controller progress. + +### 2.6 ownerRef, finalizers, and deletion + +`metadata.ownerRef` is always `null`. Zone is the root resource; it cannot be +owned. + +Core adds `core.zone-drain` finalizer to Zone only when an explicit reset/ +shutdown request is in progress. During drain: + +1. `metadata.deletionRequestedAt` is set. +2. Core stops admitting new resource/service requests. +3. Each non-Zone resource receives a delete request in reverse dependency order + under finalizer protocol. +4. After all other resources are deleted, `core.zone-drain` is cleared. +5. Final transaction emits `phase=Deleted` event and closes the store. + +A Zone resource is never deleted except during a deliberate destructive reset. +Normal daemon restart, upgrade, or Provider lifecycle changes do not delete Zone. + +--- + +## 3. ZoneLink + +### 3.1 Role + +`ZoneLink/` represents one parent/child Zone delegation. It is stored in +the **parent** Zone. It carries the child Zone's identity, transport/session +requirements, cursor tracking for resource synchronization, and connection +health. + +A parent accesses child Zone resources exclusively through the child's +`d2b.resource.v3` service over a ComponentSession routed via the transport +Provider named in ZoneLink. The parent never receives a database handle, +credential, token, or cross-Zone ResourceRef. + +**Controller/owner**: The `zone link/delegation` handler of the fixed +core-controller process owns ZoneLink reconciliation. No other controller may +update ZoneLink spec or status. + +### 3.2 Metadata + +| Field | Rule | +| --- | --- | +| `metadata.name` | Operator-assigned; locally unique in the parent Zone; no structural constraint beyond ResourceName regex | +| `metadata.zone` | Parent Zone name; ZoneLink is always in the parent | +| `metadata.ownerRef` | Optional; may be owned by a managing Provider (e.g. a runtime Guest that hosts the child Zone) | +| `metadata.finalizers` | Core adds `core.zone-link-drain` before deletion; transport Provider may add its own finalizer | +| `metadata.deletionRequestedAt` | Normal deletion by operator or owning resource | + +### 3.3 Spec + +```yaml +apiVersion: resources.d2b.io/v3 +type: ZoneLink +metadata: + name: guest-link + zone: parent + uid: + generation: 1 + revision: + ownerRef: null + finalizers: + - core.zone-link-drain + deletionRequestedAt: null + createdAt: 2026-07-22T00:00:00Z + updatedAt: 2026-07-22T00:00:00Z +spec: + childZoneName: guest + transportProviderRef: "Provider/transport-unix" + transportSettings: {} + transportCredentials: [] + disabled: false + limits: + maxPendingIntents: 256 + maxActiveStreams: 32 + reconnectMaxAttempts: 10 + reconnectWindowSecs: 300 +``` + +**`spec.transportProviderRef`** (required; no default): + +- Must match `^Provider/transport-[a-z][a-z0-9-]*$`; any other form is rejected at admission. +- References a Provider installed in the same Zone whose name begins with `transport-`; that Provider must be in `Ready` phase before any connection is attempted. +- The named transport Provider exports a ZoneLink schema extension (`transportSettings` schema); the resource compiler validates `spec.transportSettings` against that schema at build time. +- No transport Provider is pre-installed; an operator must install one before creating ZoneLinks that require it. + +**`spec.transportSettings`** (required; default `{}`): + +- Provider-specific configuration object; validated against the transport Provider's ZoneLink schema extension at build time (Phase 2) and at admission time (runtime). +- Must not contain credential bytes; use `spec.transportCredentials` for secrets. + +**`spec.transportCredentials`** (list of Credential refs; default `[]`): + +- Each entry is a string `"Credential/"` in the same Zone. +- Max 8 entries; evaluated at ComponentSession establishment time. +- Referenced Credentials must be declared in `d2b.zones..resources.*` with `type = "Credential"`. + +**`spec.limits`**: ZoneLink connection and queue limits. All fields have normative defaults (shown above). Bounds enforced at admission: +- `maxPendingIntents`: max 1024 +- `maxActiveStreams`: max 128 + +### 3.4 Status + + +ZoneLink.status extends common status with: + +| Field | Type | Rules | +| --- | --- | --- | +| `childZoneUid` | UID string or null | Observed immutable UID of child Zone resource; null until first successful connection | +| `connected` | bool | Whether a ComponentSession to the child Zone API is currently established | +| `lastConnectedAt` | RFC 3339 UTC or null | Most recent successful session establishment | +| `lastDisconnectedAt` | RFC 3339 UTC or null | Most recent disconnect | +| `lastSentRevision` | u64 or null | Highest parent-applied revision sent to child | +| `lastAckedRevision` | u64 or null | Highest revision acknowledged by child | +| `lastReceivedRevision` | u64 or null | Highest child-source revision received | +| `lastAppliedRevision` | u64 or null | Highest revision applied to parent store | +| `linkEpoch` | u64 | Monotonic counter; increments on every reconnect establishing a new session generation | +| `pendingLocalIntents` | u32 | Count of locally queued intents while disconnected; bounded | +| `childAuthorized` | bool | Whether parent's mapped subject is authorized in the child Zone | + +`childZoneUid` allows the parent to detect that a child Zone was destroyed and +recreated. If `childZoneUid` changes across reconnects, the ZoneLink handler +must clear cursor state and relist/rewatch from revision 0 in the child. + +Cursor fields (`lastSentRevision`, `lastAckedRevision`, `lastReceivedRevision`, +`lastAppliedRevision`) reflect the `zone_link_cursors` redb table entry for +this ZoneLink. They are bounded u64 monotonic values. Their exact semantics +are defined by the ZoneLink session and cursor protocol. + +### 3.5 Phase and conditions + +| Phase | Meaning | +| --- | --- | +| `Pending` | Transport not yet connected or child not yet authorized | +| `Ready` | ComponentSession established; child authorized; resources accessible | +| `Degraded` | Connected but some capability unavailable (e.g. watch quota exceeded) | +| `Failed` | Cannot connect after retry policy exhausted, or child permanently denied | +| `Unknown` | Transport state cannot be determined | + +Zone never participates in ZoneLink phase (ZoneLink belongs to the parent Zone). + +Closed condition types for ZoneLink: + +| Condition type | Meaning | +| --- | --- | +| `TransportReachable` | Transport provider reports the endpoint is reachable | +| `SessionEstablished` | ComponentSession handshake completed successfully | +| `ChildAuthorized` | Parent subject has required authorization in child Zone | +| `CursorSynchronized` | Parent/child revision cursors are within expected bounds | +| `LocalIntentsDrained` | No pending locally queued intents remain | +| `DisabledByOperator` | `spec.disabled: true`; link is intentionally inactive | + +### 3.6 Local intents while disconnected + +When the child Zone is unreachable, the parent may record bounded local +ZoneLink intents — operator intent that cannot be applied until reconnection. +These are not resource writes in the child. They are persisted locally as +ZoneLink-owned metadata-only entries. + +Rules: + +- bounded count: `pendingLocalIntents` must not exceed 256; new intents are + rejected with `backpressure` when at limit; +- intents are ordered FIFO; no deduplication or priority; +- on reconnect the ZoneLink handler applies pending intents in order under + expected-revision preconditions; +- failed intent application clears the intent queue and records a + `IntentApplicationFailed` condition; the operator must relist and retry; +- the queue is cleared on ZoneLink deletion or successful relist/resync. + +### 3.7 ownerRef, finalizers, and deletion + +`metadata.ownerRef` is optional. A Guest runtime Provider may own the ZoneLink +for the child Zone it hosts. When owner is deleted, ZoneLink deletion follows +standard child-first finalizer protocol. + +Core adds `core.zone-link-drain` to `metadata.finalizers` before processing a +delete request. The ZoneLink handler: + +1. Closes the ComponentSession to the child Zone. +2. Clears the cursor table entry. +3. Removes any pending local intents. +4. Clears `core.zone-link-drain`. +5. Clears any transport-Provider finalizer (by calling delete on the provider's + owned endpoint resource if applicable under the resolved transport Provider + contract). + +After all finalizers clear, core removes the ZoneLink resource and its +`zone_link_cursors` entry in one transaction. + +--- + +## 4. Provider + +### 4.1 Role + +`Provider/` is the installed representation of one independently +buildable Provider package in a Zone. A `providerRef` resolves only if the +named Provider resource exists and is `Ready` in the same Zone. + +Provider resources are always Zone-local. There are no cross-Zone Provider +refs. Provider installation is a Zone-local administrative act. + +> **`unsafe-local` is NOT a Provider.** The current `kind = "unsafe-local"` Nix +> workload option (and the baseline `WorkloadProviderKind::UnsafeLocal` / +> `IsolationPosture::UnsafeLocal` in `d2b-realm-core/src/workload.rs`) maps to +> a **Host** resource with `spec.defaultDomain=user`, `spec.allowedDomains=[user]`, +> and `spec.defaultUserRef=User/`, reconciled by `Provider/system-core`. +> No Provider resource with name or type `unsafe-local` exists in ADR 0046. +> Child processes of an unsafe-local Host use normal Process Providers. No-isolation +> posture and warnings are preserved in Host status, CLI output, and audit records. +> They must not appear in OTEL metric label values or structured log labels. See ADR046-zone-control-008 (§17) and §16.2 subsection +> "Workload posture and unsafe-local types". + +**Controller/owner**: The `provider lifecycle` handler of the fixed +core-controller process owns Provider reconciliation. Only Provider/system-core +and Provider/system-minijail are fixed bootstrap exceptions with no Process +resource — all other Provider controllers are Process resources owned by their +Provider resources. + +### 4.2 Metadata + +| Field | Rule | +| --- | --- | +| `metadata.name` | Must equal the Provider's declared identity; no installer can select an arbitrary name independent of the package | +| `metadata.zone` | Zone where Provider is installed | +| `metadata.ownerRef` | `null` for directly installed Providers; may point to a managing resource for dependent Providers | +| `metadata.finalizers` | Core adds `core.provider-api-binding` before Provider deletion begins | +| `metadata.deletionRequestedAt` | Set by operator delete or owning resource delete | + +Finalizer `core.provider-api-binding` ensures the API catalog handler +withdraws all exported ResourceType schemas and verifies no resources of those +types remain before the Provider resource is removed. + +### 4.3 Spec + +Provider.spec carries the complete desired installation. All digests are +`sha256:` prefixed lowercase hex. An empty or missing digest field is +rejected at admission. + +```yaml +spec: + artifactId: "runtime-cloud-hypervisor" # bounded ID; references d2b.artifacts catalog + # PackageIdentity fields (digest, manifestDigest, configSchemaDigest, signatureId, + # trustEpoch, conformanceAttestationDigest, compatibility, support, etc.) are + # resolved from the artifact catalog by the resource compiler; they do not appear + # in operator-authored spec or in the canonical JSON envelope. + config: {} # root config; validated against catalog's configSchemaDigest schema + exports: + resourceTypes: + - name: Host # short Zone-unique ResourceType name (or vendor.qualified.Name) + version: 1 + schemaDigest: "sha256:<64 hex>" + - name: User + version: 1 + schemaDigest: "sha256:<64 hex>" + components: + controllers: + - id: host-controller + binaryRef: d2b-core-controller + resourceTypes: [Host, User] + supportedHostProviderCapabilities: [] + supportedGuestProviderCapabilities: [] + allowedDomains: [system] + specVerbs: [create, update-spec] + statusVerbs: [update-status] + finalizerIds: [core.host-teardown] + watchSelectors: + - resourceType: Host + - resourceType: User + dependencySelectors: [] + ownerChildTriggers: [owned-resource-changed] + reconcileConcurrency: 8 + observeConcurrency: 4 + maxPendingResources: 256 + observeIntervalSeconds: null # null = no periodic observe; non-null = seconds + resyncPolicy: on-dependency-change + resourceTypeDeadlineSeconds: + Host: 30 + User: 10 + services: [] + workers: [] + dependencies: + # aliases bound to Provider names; closed set defined per Provider manifest + # example entries (actual aliases declared in signed manifest): + # runtime: Provider/runtime-cloud-hypervisor + # volume: Provider/volume-local + permissionClaims: + - verb: create + resourceType: Host + - verb: update-status + resourceType: Host + - verb: create + resourceType: User + - verb: update-status + resourceType: User + lifecycle: + upgradePolicy: drain-then-replace # drain-then-replace | rolling | immediate + drainTimeoutSeconds: 120 + restartPolicy: on-failure + maxRestarts: 5 + restartBackoffSeconds: 10 +``` + +#### 4.3.1 Artifact identity and catalog fields + +`spec.artifactId` is a bounded label (`^[a-z][a-z0-9-]*$`, max 128 chars) +referencing the `d2b.artifacts.` catalog entry with `type = "provider"`. +All PackageIdentity sub-fields are resolved from the artifact catalog at build +time and are **not** present in the operator-authored spec or canonical JSON +envelope. They are validated by the resource compiler and stored in the private +artifact catalog; the resource store holds only `artifactId`. + +| Artifact catalog field | Type | Rules | +| --- | --- | --- | +| `name` (from manifest) | string | Must equal Provider identity declared in manifest; must equal `metadata.name` | +| `version` (from manifest) | string | Semver `major.minor.patch`; informational; exact digest is binding | +| `digest` | sha256 | Content digest of signed Provider package; required; validated at build | +| `executableDigests` | map[name]sha256 | One entry per built binary; validated at build | +| `manifestDigest` | sha256 | Digest of the Provider's signed manifest artifact; validated at build | +| `configSchemaDigest` | sha256 | Digest of the root config JSON Schema; used to validate `spec.config` | +| `publisher` | string | Stable publisher/organization label; `^[a-z][a-z0-9-]*$` | +| `signatureId` | string | Stable opaque signature reference; non-empty | +| `trustEpoch` | u32 | Trust root epoch; must not be revoked | +| `revocationRef` | null or string | Stable revocation check token; null if no revocation mechanism | +| `conformanceAttestationDigest` | sha256 | Digest of Provider API conformance attestation | +| `compatibility.apiVersion` | u32 | Provider API major version; exact match required; no downgrade | + +Unknown artifact catalog fields are rejected at build time. Store paths and +Nix closure metadata are private catalog implementation data and do not appear +in resource spec, status, audit records, or OTEL attributes. + +#### 4.3.2 Root configuration + +`spec.config` is an object validated against the JSON Schema identified by +the artifact catalog's `configSchemaDigest` for `spec.artifactId`. Validation +occurs at build time (resource compiler) and is re-validated at Provider spec +admission by the Zone runtime. + +Rules: + +- all validation is schema-strict; unknown fields in config are rejected unless + the schema explicitly permits `additionalProperties: true` in a bounded + vendor extension object; +- no credential bytes, raw host paths, PIDs, process arguments, or ambient + authority values are permitted in `config`; +- secrets are `Credential/` ResourceRefs; config values that need + resolution reference Credentials or Volumes; +- config digest (derived from canonical JSON representation of `config`) is + bound to every Process resource spawned from this Provider's components; +- config replacement (spec.generation increment) triggers all owned Process + resources to be drained and replaced; +- the empty config `{}` is valid for Providers with no config. + +#### 4.3.3 Component descriptors + +A Provider may declare at most one set of each component type (controllers, +services, workers). Component IDs are unique within the Provider. + +The following component bounds are normative and enforced at admission: + +| Field | Maximum | +| --- | --- | +| Controllers per Provider | 8 | +| Services per Provider | 8 | +| Worker templates per Provider | 32 | +| ResourceTypes per controller | 16 | +| Watch selectors per controller | 32 | +| Dependency selectors per controller | 16 | +| Finalizer IDs per controller | 8 | + +**Controller descriptor fields** (normative): + + +| Field | Type | Rules | +| --- | --- | --- | +| `id` | ComponentId | `^[a-z][a-z0-9-]*$`; unique within Provider | +| `binaryRef` | string | Key in `package.executableDigests`; identifies the built binary | +| `resourceTypes` | list of ResourceType names | ResourceTypes this controller reconciles; declared in `exports.resourceTypes`; max 16 | +| `supportedHostProviderCapabilities` | list of capability tokens | Host Provider capabilities required for this controller to operate | +| `supportedGuestProviderCapabilities` | list of capability tokens | Guest Provider capabilities required | +| `allowedDomains` | list of `system|user` | Process domains this controller instance supports | +| `specVerbs` | list of resource verbs | Verbs on spec subresource; bounded set from common verb enum | +| `statusVerbs` | list of resource verbs | Verbs on status subresource; controller is status owner if `update-status` present | +| `finalizerIds` | list of FinalizerId | Finalizers this controller adds/clears; `^[a-z][a-z0-9.-]*$` | +| `watchSelectors` | list of selector objects | Exact ResourceTypes and optional name/owner/zone filters this controller watches | +| `dependencySelectors` | list of selector objects | ResourceTypes watched as read-only dependencies | +| `ownerChildTriggers` | list of trigger reason tokens | Subset of common triggers; must include `owned-resource-changed` if controller uses owner index | +| `reconcileConcurrency` | u32 ≥ 1 | Max concurrent reconcile handlers for distinct resources | +| `observeConcurrency` | u32 ≥ 1 | Max concurrent observe handlers | +| `maxPendingResources` | u32 ≥ 1 | Max resources queued for reconcile before backpressure | +| `observeIntervalSeconds` | null or u32 ≥ 1 | Periodic observe period; null means no periodic observe | +| `resyncPolicy` | enum | `on-dependency-change | manual | period-only` | +| `resourceTypeDeadlineSeconds` | map[ResourceType]u32 | Per-type per-reconcile deadline; 0 means use global default | + +**Service descriptor fields**: + +| Field | Type | Rules | +| --- | --- | --- | +| `id` | ComponentId | Unique within Provider | +| `binaryRef` | string | Key in `package.executableDigests` | +| `serviceMethods` | list of method IDs | ttrpc method names this service exports | +| `serviceSchemaDigest` | sha256 | Digest of ttrpc service descriptor | +| `allowedDomains` | list of `system|user` | | +| `endpointRequirements` | object | ComponentSession purpose class/transport class required | +| `subjectConstraints` | list | Closed set of ResourceType subject classes allowed to connect | + +**Worker descriptor fields**: + +| Field | Type | Rules | +| --- | --- | --- | +| `id` | ComponentId | Unique within Provider | +| `binaryRef` | string | Key in `package.executableDigests` | +| `templateName` | string | Name of the Process template used for this worker | +| `allowedDomains` | list of `system|user` | | +| `configProjection` | list of config field paths | Config fields visible to this worker; bounded | +| `maxInstances` | u32 ≥ 1 | Cardinality; owner/controller enforces | +| `executionConstraints` | object | Allowed executionRef ResourceTypes (Host or Guest) for this worker | + +#### 4.3.4 Dependencies + +`spec.dependencies` is a flat map from alias to Provider ResourceRef: + +```yaml +dependencies: + runtime: Provider/runtime-cloud-hypervisor + volume: Provider/volume-local + network: Provider/network-local + credential: Provider/credential-secret-service + transport: Provider/transport-unix +``` + +Rules: + +- alias names are defined in the Provider's signed manifest; the Zone config + binds each alias to an exact installed Provider; +- alias resolution is request-time; the Provider lifecycle handler re-checks + all aliases when a referenced Provider enters or leaves `Ready`; +- synchronous dependency cycles fail Provider configuration; +- an optional dependency's absence produces `Degraded` in the Provider's + component that requires it; the Provider may still publish other components; +- a required dependency's absence prevents the Provider reaching `Ready`; +- alias keys must be `^[a-z][a-z0-9-]*$`; max 16 aliases per Provider. + +#### 4.3.5 Permission claims + +`spec.permissionClaims` is a bounded list of operations the Provider controller +or service requests. Each claim has: + +```yaml +- verb: create # resource verb or runtime verb + resourceType: Host # ResourceType name + subresource: null # null or bounded subresource name + resourceNames: [] # [] = all; non-empty = explicit names + zones: [] # [] = this Zone only; non-empty = named linked Zones + executionRefs: [] # [] = all; non-empty = explicit Host/Guest refs +``` + +Rules: + +- the Zone API binding handler intersects permission claims with Zone policy + derived from installed resources and static admission rules before granting + any Role; +- a Provider cannot claim permissions not declared in its signed manifest; +- cross-Zone claims require an explicit Zone in `zones`; a ZoneLink must exist; +- wildcard permission claims (empty `resourceNames` and `executionRefs`) are + only permitted for verified system Providers with explicit review; +- permission claims are inputs to generated Roles; they are not + self-executing grants. + +#### 4.3.6 Upgrade and lifecycle policy + +| Field | Values | Default | +| --- | --- | --- | +| `lifecycle.upgradePolicy` | `drain-then-replace | rolling | immediate` | `drain-then-replace` | +| `lifecycle.drainTimeoutSeconds` | u32; 0 < t ≤ 3600 | 120 | +| `lifecycle.restartPolicy` | `on-failure | always | never` | `on-failure` | +| `lifecycle.maxRestarts` | u32; ≤ 20 | 5 | +| `lifecycle.restartBackoffSeconds` | u32; 1 ≤ b ≤ 3600 | 10 | + +`drain-then-replace`: All controlled processes are gracefully stopped before +new version starts. No overlap window. Safe for stateful controllers. + +`rolling`: Controller reconciliation continues; workers are replaced +concurrently under cardinality constraints. Not safe for controllers that hold +exclusive locks. + +`immediate`: Old processes stop concurrently with new start. Only for +stateless workers. + +### 4.4 Status + +Provider.status extends common status with: + +| Field | Type | Rules | +| --- | --- | --- | +| `trustResult` | enum or null | `trusted | revoked | expired-epoch | attestation-failed | conformance-failed`; null until first check | +| `conformanceResult` | enum or null | `passed | failed | skipped`; null until first check | +| `lastTrustCheckedAt` | RFC 3339 UTC or null | | +| `activePackageDigest` | sha256 or null | Currently running package digest; differs from spec during upgrade | +| `activeConfigDigest` | sha256 or null | Currently running config digest | +| `providerGeneration` | u64 | Monotonic counter incremented by core on every trust+config+component re-validation | +| `components` | bounded list | Per-component status entries | +| `exportedResourceTypes` | list | ResourceTypes currently published to API catalog | +| `dependencyHealth` | map | alias → `Ready|Degraded|Missing`; no Provider details | +| `controllerLeases` | bounded list | Per-controller lease ID / phase / lastCheckpointAt | +| `stateSchemaMigration` | enum | `current | pending | in-progress | failed`; null if no state schema | +| `disabled` | bool | Whether Provider is operator-disabled | +| `quarantined` | bool | Whether core has quarantined Provider due to trust/conformance failure | + +**Component status entry**: + +```yaml +- componentId: host-controller + type: controller # controller | service | worker + phase: Ready # common phases + processRef: Process/system-core-host-controller # null for bootstrap exceptions + lastReconciledAt: 2026-07-22T00:00:01Z + conditions: [] +``` + +`processRef` is null for `Provider/system-core` and `Provider/system-minijail` +components only. All other component `processRef` values must resolve to a +current Process resource owned by this Provider. + +### 4.5 Phase and conditions + +| Phase | Meaning | +| --- | --- | +| `Pending` | Provider spec admitted; trust/conformance/component checks not yet complete | +| `Ready` | All required components Ready; all exported ResourceTypes published | +| `Degraded` | Optional components impaired; required components Ready; exported types published | +| `Failed` | One or more required components Failed; exported types withdrawn | +| `Unknown` | Core cannot determine Provider state | + +Provider never reaches `Succeeded` (long-lived resource). + +Closed condition types for Provider: + +| Condition type | Meaning | +| --- | --- | +| `PackageTrusted` | Package digest/signature/epoch/conformance verified | +| `ConfigValid` | Root config validated against configSchemaDigest schema | +| `DependenciesReady` | All required dependency aliases resolved to Ready Providers | +| `ComponentsReady` | All required component Processes in Ready phase | +| `ApiPublished` | All exported ResourceTypes bound in Zone API catalog | +| `ControllerLeaseActive` | At least one active controller lease exists | +| `DrainComplete` | Upgrade drain complete; `True` only during drain cycle | +| `Quarantined` | Trust/conformance failure has caused quarantine | + +### 4.6 ownerRef, finalizers, and deletion + +**ownerRef**: null for directly installed Providers. A managing resource +(e.g. a future Provider catalog manager) may own a Provider, making it a +generated child that is deleted when the owner is deleted. + +**Finalizer `core.provider-api-binding`**: Added by core when a delete request +arrives. The API catalog handler: + +1. Marks all exported ResourceTypes as `withdrawing`. +2. Verifies no resources of those types remain (returns Pending if they do, + with a `PendingResourceDeletion` condition). +3. Atomically unbinds types from api_schemas. +4. Clears `core.provider-api-binding`. + +**Additional Provider finalizers**: A Provider controller may add finalization +logic through its own finalizer ID (declared in the controller descriptor +`finalizerIds`). Core only coordinates finalizer ordering; it does not execute +Provider-specific finalization logic. + +**Deletion sequence**: + +1. Operator requests delete; `deletionRequestedAt` is set. +2. All owned child resources (Process, Volume, etc.) receive delete requests + child-first. +3. `core.provider-api-binding` awaits ResourceType draining. +4. Provider-specific controller finalizers execute. +5. All finalizers cleared; `phase=Deleted` emitted; resource removed. + +### 4.7 Bootstrap exceptions + +`Provider/system-core` and `Provider/system-minijail` are the two Provider +bootstrap exceptions: + +- they have no `processRef` in their component status; +- their Process binaries are embedded in or co-launched with the Zone runtime; +- they are pre-created in the store during zone initialization before any + other resource exists; +- they are granted only the compiled bootstrap authorization (§6); +- they cannot be deleted while the Zone is operational; +- their trust/package/conformance fields must still pass validation; they + are not exempt from trust checks, only from having Process resource children. + +`Provider/system-minijail` is the fixed second bootstrap exception because: + +- the first Process controller (`system-minijail`) cannot depend on a Process + controller to launch itself; +- `system-minijail` reconciles all later Process resources including + `system-systemd`; it is not a general-purpose minijail executor outside + this bootstrap role. + +After bootstrap both Providers share the same core-controller process process +boundary but use distinct authenticated subjects and closed RBAC grants. + +### 4.8 Provider crate layout (normative) + +Every Provider implementation lives in its own workspace member crate under +`packages/` with the name `d2b-provider--` (following +AGENTS.md naming: base before implementation, alphanumerically sorted in the +workspace member list). The crate **MUST** contain all four of the following +paths or the workspace/package policy check fails the build: + +#### 4.8.1 Required paths + +| Path | Required contents | Missing → | +| --- | --- | --- | +| `src/` | All implementation source files and binaries. Colocated unit tests (`#[cfg(test)]` modules) live here. Must contain at least one `lib.rs`, `main.rs`, or named binary entry point. | policy failure | +| `tests/` | Hermetic Cargo integration tests. Must cover: ResourceType admission/lifecycle, controller reconcile and phase transitions, conformance checks, and one fault-injection test (e.g. trust failure, config-schema mismatch, component crash). No external I/O or container dependencies; runs inside the Nix sandbox build. | policy failure | +| `integration/` | Heavier fixtures and scenarios invoked by the existing test orchestration (`make test-integration` / `make test-host-integration`). Must cover at least one cross-process scenario (Provider process spawn + component lifecycle) and, if the Provider creates Host or Guest resources, one Host or Guest attachment scenario. Scenarios must declare which orchestration target invokes them (container or host-integration). May not import symbols from `src/` directly; communicates through the Zone API or test harness fixtures. | policy failure | +| `README.md` | Provider identity (name, publisher, version, trust/conformance attestation pointers); full `spec.config` schema with field descriptions and defaults; list of exported ResourceTypes with brief description; list of controllers, services, workers, and binaries (role, placement, resource quotas); placement constraints and dependencies; RBAC requirements (Roles and RoleBindings the Provider requires pre-installed); security posture (capabilities, namespace isolation, state roots, credential handling); state and telemetry (audit events emitted, OTEL span/metric names); build, unit-test, integration-test commands; note on future standalone-repo packaging if applicable. | policy failure | + +#### 4.8.2 Policy enforcement + +The workspace policy check (`packages/d2b-contract-tests/tests/policy_provider_crate_layout.rs`, +implemented in work item ADR046-pkg-001) walks every `packages/d2b-provider-*` +crate directory in the workspace and asserts all four paths exist. A missing +`src/`, `tests/`, `integration/`, or `README.md` in any Provider crate is a +**hard policy failure** that blocks `make test-policy` (and therefore +`make check`) on the same basis as a workspace member sort violation or a +`Command::new("bash")` site. + +The check is gated by the same `D2B_FIXTURES` step in +`tests/tools/rust-workspace-checks.sh` as the existing crate-naming and +member-sort checks. It does not require building the crate; it inspects +the filesystem only. + +#### 4.8.3 README minimum sections + +A Provider `README.md` must contain all of the following section headings +(exact casing not required; matched case-insensitively after stripping `#` +whitespace): + +1. `Provider identity` +2. `Config schema` +3. `Exported resource types` +4. `Controllers / services / workers / binaries` +5. `Placement and dependencies` +6. `RBAC requirements` +7. `Security posture` +8. `State and telemetry` +9. `Build and test` + +The policy check verifies all nine headings are present. A missing heading +is a policy failure with the message: +`"d2b-provider-/README.md: missing required section ''"`. + +#### 4.8.4 Integration scenario declaration + +Each file under `integration/` must declare its orchestration target in a +top-level comment or doc attribute within the first 20 lines: + +```rust +//! integration-target: container // runs under make test-integration +//! integration-target: host-integration // runs under make test-host-integration +``` + +The policy check verifies every `integration/*.rs` file carries exactly one +such declaration. Missing or multiple declarations are a policy failure. + +#### 4.8.5 Example skeleton + +``` +packages/d2b-provider-runtime-cloud-hypervisor/ +├── Cargo.toml +├── README.md # §4.8.3 sections required +├── src/ +│ ├── lib.rs # provider registration, component factory +│ ├── guest.rs # Guest ResourceType controller +│ ├── process.rs # Process ResourceType controller +│ └── bin/ +│ └── d2b-provider-runtime-cloud-hypervisor # provider agent binary +├── tests/ +│ ├── guest_lifecycle.rs # admission → Pending → Ready → deletion +│ ├── process_lifecycle.rs # Process component phase transitions +│ ├── conformance.rs # check_provider_conformance/check_descriptor_conformance +│ └── fault_trust_failure.rs # quarantine on trust failure +└── integration/ + ├── guest_spawn.rs # integration-target: host-integration + └── guest_network_attach.rs # integration-target: container +``` + +--- + +## 5. Role + +### 5.1 Role and ownership + +`Role/` declares a bounded set of permission rules. RoleBindings grant +one Role to one or more subjects. + +**Controller/owner**: The `authorization` handler of the fixed core-controller +process owns Role reconciliation. No other controller may update Role spec or +status. + +### 5.2 Metadata + +| Field | Rule | +| --- | --- | +| `metadata.name` | Zone-unique ResourceName | +| `metadata.ownerRef` | Optional; may be owned by a Provider (for generated Roles) or null for operator-created Roles | +| `metadata.finalizers` | Core adds `core.role-binding-drain` when a Role with active RoleBindings is deleted | +| `metadata.deletionRequestedAt` | Set on operator delete or owning resource delete | + +### 5.3 Spec + +The complete Role spec is the `rules` array: + +```yaml +apiVersion: resources.d2b.io/v3 +type: Role +metadata: + name: process-controller + zone: dev + uid: + generation: 1 + revision: + ownerRef: null + finalizers: [] + deletionRequestedAt: null + createdAt: 2026-07-22T00:00:00Z + updatedAt: 2026-07-22T00:00:00Z +spec: + rules: + - resourceTypes: [Process, EphemeralProcess] + verbs: [get, list, watch, create, update-spec, update-status, update-finalizers, delete] + subresources: [] + resourceNames: [] + zones: [] + executionRefs: [] + sessionVerbs: [] +status: + observedGeneration: 1 + phase: Ready + conditions: [] + lastReconciledAt: 2026-07-22T00:00:01Z + startedAt: null + completedAt: null + outcome: null +``` + +#### 5.3.1 Rule schema + +Each rule in `spec.rules` has: + +| Field | Type | Default | Semantics | +| --- | --- | --- | --- | +| `resourceTypes` | list of ResourceType names | required, non-empty | ResourceTypes this rule covers; short Zone-unique names or qualified vendor names | +| `verbs` | list of resource verb tokens | required, non-empty | Resource operation verbs (see §5.3.2) | +| `subresources` | list of subresource names | `[]` = no subresource restriction | Empty means all subresources; non-empty means exactly those subresources | +| `resourceNames` | list of ResourceName | `[]` = all | Empty means all names; non-empty means exactly those names; max 64 | +| `zones` | list of Zone names | `[]` = this Zone only | Empty means only the evaluating Zone; non-empty means any listed Zone | +| `executionRefs` | list of ResourceRefs | `[]` = no restriction | Restricts to operations whose target resource has one of these executionRefs; `[]` means unrestricted | +| `sessionVerbs` | list of session verb tokens | `[]` = no session verbs | ComponentSession/service verbs granted to this rule (see §5.3.2) | + +All list fields are deduplicated at admission. Order is not significant. + +#### 5.3.2 Verb sets + +**Resource verbs** (exact closed set): + +| Verb | Semantics | +| --- | --- | +| `get` | Single resource read | +| `list` | Multi-resource read with pagination | +| `watch` | Streaming change subscription | +| `create` | Create a new resource | +| `update-spec` | Full spec replacement | +| `update-status` | Full status replacement (controller-only) | +| `update-metadata` | Bounded labels/annotations/ownerRef metadata change | +| `update-finalizers` | Add/remove finalizers (ownership constrained) | +| `delete` | Request resource deletion | + +Unknown verbs are rejected at admission. + +**Session verbs** (exact closed set): + +| Verb | Semantics | +| --- | --- | +| `connect` | Open a ComponentSession to a service | +| `invoke` | Call a ttrpc method | +| `open-stream` | Open a named stream | +| `attach` | Transfer a local file descriptor | +| `cancel` | Cancel an in-progress operation | +| `observe` | Subscribe to service health/event notifications | + +Session verbs in a Role rule grant ComponentSession/d2b-bus access to the +services bound by the same rule's `resourceTypes` and `zones` constraints. +They are evaluated by the same native RBAC engine as resource verbs. + +#### 5.3.3 Explicit wildcard form + +The foundation specifies: "no implicit wildcard is granted; a reviewed explicit +wildcard may exist only for fixed core-controller roles." + +An explicit wildcard in `resourceNames` is represented as a single-element +list `["*"]`. This form is: + +- permitted only for fixed core-controller generated Roles; +- prohibited in operator-created or Provider-generated Roles (admission + rejects at creation time); +- evaluated strictly as "all names in the current Zone catalog"; it does not + grant future ResourceTypes not yet bound; +- recorded in the authorization audit log with the wildcard marker. + +No other wildcard form exists. `[]` (empty list) means "all" for +`resourceNames` — this is equivalent to a wildcard for names but is the +default form available to all subjects. An `executionRefs: []` means +"unrestricted by executionRef", not a wildcard requiring `["*"]`. + +The distinction between `resourceNames: []` (unrestricted, default) and +`resourceNames: ["*"]` (explicit reviewed wildcard, only for core-controller +Roles) must be enforced at admission. For non-core-controller Roles, +`resourceNames: ["*"]` is rejected with `resource-schema-invalid`. + +#### 5.3.4 Bounds + +| Field | Maximum | +| --- | --- | +| Rules per Role | 32 | +| `resourceTypes` per rule | 16 | +| `verbs` per rule | 16 (bounded by verb enum: currently 9 verbs) | +| `sessionVerbs` per rule | 6 (bounded by session verb enum: currently 6 verbs) | +| `subresources` per rule | 16 | +| `resourceNames` per rule | 64 | +| `executionRefs` per rule | 32 | +| `zones` per rule | 8 | + +All bounds are enforced at admission. Exceeding any bound returns `resource-schema-invalid`. + +### 5.4 Status + + +Role.status contains only common fields. There are no Role-specific status +fields beyond: + +| Field | Type | Rules | +| --- | --- | --- | +| `activeBindingCount` | u32 | Number of RoleBindings currently referencing this Role; maintained by authorization handler | +| `lastValidatedAt` | RFC 3339 UTC or null | Most recent time the authorization handler validated this Role | + +`activeBindingCount` is informational only. Deletion is blocked (via finalizer) +when this count > 0. + +### 5.5 Phase and conditions + +| Phase | Meaning | +| --- | --- | +| `Pending` | Role spec admitted; authorization handler has not yet built index entry | +| `Ready` | Role validated; index built; authorization operational | +| `Degraded` | Role valid but one or more rules reference unresolvable executionRefs (warn only) | +| `Failed` | Role spec fails validation (invalid verb, unknown ResourceType, bad ref) | + +Closed condition types for Role: + +| Condition type | Meaning | +| --- | --- | +| `RuleSetValid` | All rules have valid verbs, ResourceType names, and ref formats | +| `IndexBuilt` | Authorization index entry is current | +| `ActiveBindings` | `True` when activeBindingCount > 0; informational | +| `PendingBindingDrain` | `True` during deletion while active bindings exist | + +### 5.6 ownerRef, finalizers, and deletion + +**ownerRef**: null for operator-created Roles; may be a Provider for +generated core-controller Roles. + +**Finalizer `core.role-binding-drain`**: Added when deletion is requested and +`activeBindingCount > 0`. The authorization handler: + +1. Sets `PendingBindingDrain=True` condition; the Role remains in its current phase (typically `Ready`) while awaiting binding drain. +2. Awaits all RoleBindings referencing this Role to be deleted first. +3. Clears the index entry. +4. Clears `core.role-binding-drain`. + +A Role with active bindings cannot be deleted until all bindings are removed. +This prevents authorization gaps from dangling RoleBinding references. + +--- + +## 6. RoleBinding + +### 6.1 Role and ownership + +`RoleBinding/` grants a Role to one or more subjects. It may optionally +narrow the Role's rules and carries revocation state. + +**Controller/owner**: The `authorization` handler of the fixed core-controller +process owns RoleBinding reconciliation. No other controller may update +RoleBinding spec or status. + +### 6.2 Metadata + +| Field | Rule | +| --- | --- | +| `metadata.name` | Zone-unique ResourceName | +| `metadata.ownerRef` | Optional; may be owned by the Provider that generates this binding | +| `metadata.finalizers` | No standard finalizer; core clears the binding on deletion without deferral (no downstream finalizer chain) | +| `metadata.deletionRequestedAt` | Set on operator delete or owning resource delete | + +### 6.3 Spec + +```yaml +apiVersion: resources.d2b.io/v3 +type: RoleBinding +metadata: + name: process-controller-binding + zone: dev + uid: + generation: 1 + revision: + ownerRef: null + finalizers: [] + deletionRequestedAt: null + createdAt: 2026-07-22T00:00:00Z + updatedAt: 2026-07-22T00:00:00Z +spec: + roleRef: Role/process-controller + subjects: + - Provider/system-minijail + externalPrincipalSelector: null + scopeNarrowing: null # see §6.3.5 +status: + observedGeneration: 1 + phase: Ready + conditions: [] + lastReconciledAt: 2026-07-22T00:00:01Z + startedAt: null + completedAt: null + outcome: null +``` + +#### 6.3.1 roleRef + +`spec.roleRef` is a canonical ResourceRef `Role/` in the same Zone. + +Rules: + +- `roleRef` is required; an absent or empty roleRef is rejected at admission; +- the referenced Role must exist; if it does not, RoleBinding enters `Failed` + with condition `RoleNotFound`; +- `roleRef` is immutable after creation; changing the role requires deleting + and recreating the RoleBinding; +- authorization evaluates the referenced Role's current generation at decision + time; the authorization cache is invalidated when the Role changes. + +#### 6.3.2 Subjects + +`spec.subjects` is a bounded ordered list of canonical same-Zone ResourceRefs: + +```yaml +subjects: + - Provider/system-minijail + - User/alice + - Host/host-system + - Process/wayland-proxy + - Guest/dev-vm +``` + +Rules: + +- each subject is a `/` ResourceRef in this Zone; +- supported subject ResourceTypes: `User`, `Provider`, `Host`, `Guest`, + `Process`; other ResourceTypes are rejected at admission with + `resource-schema-invalid`; +- subjects list must be non-empty; +- duplicate subjects are rejected at admission; +- a subject that does not currently exist as a resource causes `SubjectNotFound` + condition (warning only, not Failed; the subject may be created later); +- the resolved subject UID is stored for change detection; if a subject is + deleted and a new resource of the same name is created, the authorization + handler detects the UID change, removes the old binding entry, and emits a + `SubjectIdentityChanged` condition; +- max subjects per RoleBinding: 128. + +#### 6.3.3 External principal selector + +`spec.externalPrincipalSelector` is `null` or a bounded object generated by +trusted enrollment/config. It selects ComponentSession subjects identified by +external identity evidence (e.g. enrolled Noise KK keys, vsock CID, enrollment +token digests) rather than by local ResourceRef. + +Rules: + +- external selectors may only appear in RoleBindings generated by trusted + enrollment config (configuration publication or Provider bootstrap); + operator-created RoleBindings may use external selectors only when the + operator has permission to create enrollment records; +- an external selector contains no credential bytes; it contains only opaque + enrollment digests or stable external identity tokens; +- external selectors are evaluated by the ComponentSession authentication step + before d2b-bus routing; they never appear in `subjects`; +- external selectors are bounded in size (max 512 bytes canonical JSON); +- `externalPrincipalSelector` and `subjects` may both be present; a request + satisfies the binding if it matches either. + +#### 6.3.4 (removed — no expiry field) + +`RoleBinding` has no `spec.expiry` field. Revoke, update, or delete a +RoleBinding via normal resource lifecycle operations. Time-limited grants +are implemented by operator-scheduled deletion of the RoleBinding resource, +not by an expiry field inside the spec. + +#### 6.3.5 Scope narrowing + + +`spec.scopeNarrowing` is null or a subset of the referenced Role's rules: + +```yaml +scopeNarrowing: + rules: + - resourceTypes: [Process] + verbs: [get, list, watch] + executionRefs: [Host/host-system] +``` + +Rules: + +- scope narrowing may only restrict; it cannot grant verbs or resourceTypes + not present in the referenced Role; +- an attempt to grant a verb absent from the Role is rejected at admission + with `resource-schema-invalid`; +- narrowed rules are the intersection of the Role rules and the narrowing + rules; +- `scopeNarrowing: null` means the full Role is granted without restriction; +- scope narrowing affects only this RoleBinding; the referenced Role is + unchanged. + +### 6.4 Status + +| Field | Type | Rules | +| --- | --- | --- | +| `roleResolved` | bool | Whether `roleRef` currently resolves to a Ready Role | +| `subjectCount` | u32 | Number of `subjects` entries | +| `unresolvedSubjects` | bounded list | Subject refs that currently have no matching resource; max 8 entries shown; overflow shown as count | +| `revoked` | bool | Whether operator revocation is set | +| `revokedAt` | RFC 3339 UTC or null | When revocation was set | + +### 6.5 Phase and conditions + +| Phase | Meaning | +| --- | --- | +| `Pending` | Admitted; authorization handler not yet built index entry | +| `Ready` | Role resolved; at least one subject matches; index entry built | +| `Degraded` | Role resolved but some subjects unresolvable (warning) | +| `Failed` | Role not found or revocation set | + +Closed condition types for RoleBinding: + +| Condition type | Meaning | +| --- | --- | +| `RoleFound` | Referenced Role exists and is Ready | +| `SubjectsResolved` | All listed subjects resolve to current resources | +| `SubjectNotFound` | One or more listed subjects do not exist (warning) | +| `SubjectIdentityChanged` | A subject UID changed due to delete/recreate | +| `IndexBuilt` | Authorization index entry is current | +| `RoleBindingRevoked` | Operator revocation set | + +### 6.6 ownerRef, finalizers, and deletion + +**ownerRef**: null for operator-created bindings; may point to a Provider +that generated the binding. + +**No standard finalizer**: RoleBinding deletion is immediate. When +`deletionRequestedAt` is set: + +1. Authorization handler drains in-flight request contexts: ongoing requests + that were admitted under this binding retain their original context until + their deadline; no new authorizations are granted to these subjects under + this binding. +2. Authorization caches for all subjects in this binding are invalidated. +3. Role's `activeBindingCount` decrements. +4. **Final atomic transaction**: remove the RBAC index entry, emit the + `Deleted` revision event, and remove the resource row from the Zone store + as one redb write transaction. No observable intermediate state exists + between index removal and row removal. + +There is no RoleBinding finalizer chain; the binding is not needed to safely +remove. + +--- + +## 7. Quota + +### 7.1 Role and ownership + +`Quota/` declares Zone-wide aggregate resource ceilings and optionally +per-resource-type limits. Host, Guest, and Process resources may carry a +`spec.quotaRef: "Quota/"` pointing to the governing Quota, and may +declare inline resource requests (cpu, memory, storage) within that Quota's +bounds. + +**Controller/owner**: The `quota` handler of the fixed core-controller process +owns Quota reconciliation. No other controller may update Quota spec or status. + +### 7.2 Metadata + +| Field | Rule | +| --- | --- | +| `metadata.name` | Zone-unique ResourceName | +| `metadata.ownerRef` | null for operator-created Quotas | +| `metadata.finalizers` | Core adds `core.quota-drain` when Quota is the subject of a quotaRef before deletion | +| `metadata.deletionRequestedAt` | Set on operator delete | + +### 7.3 Spec + +```yaml +apiVersion: resources.d2b.io/v3 +type: Quota +metadata: + name: default-quota + zone: dev + uid: + generation: 1 + revision: + ownerRef: null + finalizers: [] + deletionRequestedAt: null + createdAt: 2026-07-22T00:00:00Z + updatedAt: 2026-07-22T00:00:00Z +spec: + ceilings: + maxResources: 4096 # max total non-Deleted resources in Zone + maxResourcesPerType: 512 # max non-Deleted resources per ResourceType + maxOwnerDepth: 8 # max owner chain depth + maxCpu: null # optional: aggregate vCPU ceiling across all quotaRef resources (null = unlimited) + maxMemoryMib: null # optional: aggregate memory MiB ceiling (null = unlimited) + maxStorageGib: null # optional: aggregate storage GiB ceiling (null = unlimited) + perTypeCeilings: {} # map ResourceType → {maxResources, maxCpu, maxMemoryMib, maxStorageGib}; empty = use global ceilings + scope: zone # "zone" (only value in v3; reserved for future sub-Zone scopes) + enforcementPolicy: hard # "hard" (reject over-quota) | "soft" (warn only, still admit) +``` + +**Spec field rules**: + +- `ceilings.maxResources`: `1..=65536`; default 4096 +- `ceilings.maxResourcesPerType`: `1..=65536`; default 512 +- `ceilings.maxOwnerDepth`: `1..=32`; default 8 +- `ceilings.maxCpu`, `ceilings.maxMemoryMib`, `ceilings.maxStorageGib`: null or positive integer; null means no ceiling +- `perTypeCeilings`: max 64 entries; ResourceType names must be resolvable in the Zone API catalog +- `scope`: exactly `"zone"` in v3 +- `enforcementPolicy`: `"hard"` | `"soft"` + +Multiple Quota resources may coexist in a Zone; individual Host/Guest/Process +resources reference exactly one Quota via `spec.quotaRef` (or none). The quota +handler aggregates usage by quotaRef independently. A resource with no quotaRef +is counted against all `scope: zone` Quotas for the `maxResources` ceiling check +but is not subject to cpu/memory/storage ceilings. + +### 7.4 Status + +| Field | Type | Rules | +| --- | --- | --- | +| `usedResources` | u32 | Count of non-Deleted resources referencing this Quota | +| `usedCpu` | u32 or null | Aggregate vCPU in use; null if no cpu ceiling | +| `usedMemoryMib` | u32 or null | Aggregate memory MiB in use; null if no memory ceiling | +| `usedStorageGib` | u32 or null | Aggregate storage GiB in use; null if no storage ceiling | +| `overQuota` | bool | True if any ceiling is currently exceeded (only possible with enforcementPolicy=soft) | +| `overQuotaTypes` | bounded list | ResourceTypes currently over their perTypeCeiling; max 16 shown | +| `lastCheckedAt` | RFC 3339 UTC or null | Most recent quota check | +| `dependentCount` | u32 | Count of resources referencing this Quota via `spec.quotaRef`; 0 when safe to delete | + +### 7.5 Phase and conditions + +| Phase | Meaning | +| --- | --- | +| `Pending` | Admitted; quota handler initializing | +| `Ready` | Quota active; ceilings enforced | +| `Degraded` | Quota over soft limit (enforcementPolicy=soft), or deletion requested with dependents remaining | +| `Failed` | Spec invalid (bad ceiling value, unknown ResourceType in perTypeCeilings) | + +Closed condition types: + +| Condition type | Meaning | +| --- | --- | +| `CeilingsValid` | All ceiling values and perTypeCeilings entries are valid | +| `OverQuota` | One or more ceilings exceeded (soft enforcement only) | +| `QuotaDrainPending` | Deletion in progress; `dependentCount` resources still reference this Quota; waiting for authorized owners/operators to reassign or delete them | + +### 7.6 ownerRef, finalizers, and deletion + +Quota deletion with active `quotaRef` assignments uses `core.quota-drain`: + +1. `deletionRequestedAt` set; Zone transitions to `Degraded` with `QuotaDrainPending` condition. +2. Quota controller sets `QuotaDrainPending=True` condition with message + `" resources still reference this Quota; reassign or delete them before deletion completes"`. +3. `status.dependentCount` reflects the current count of resources with `spec.quotaRef` pointing to this Quota. +4. The quota controller does NOT mutate any other resource's `spec.quotaRef`. + Authorized owners/operators must manually reassign dependents to another Quota or + delete them; the controller blocks `core.quota-drain` clearance until `dependentCount = 0`. +5. The quota controller re-checks `dependentCount` on every reconcile triggered by + resource creation/deletion/update; it emits `quota-drain-complete` audit record when + the count reaches 0, then clears `core.quota-drain`. +6. Final atomic transaction: emit `Deleted` revision event; remove resource row and index. + +A Quota with `enforcementPolicy=hard` and active resource ceilings blocks +resource creation over-quota; it never blocks deletion. + +### 7.7 Nix authoring + +```nix +d2b.zones.dev.resources.default-quota = { + type = "Quota"; + spec = { + ceilings = { + maxResources = 4096; + maxResourcesPerType = 512; + maxOwnerDepth = 8; + maxCpu = null; + maxMemoryMib = null; + maxStorageGib = null; + }; + perTypeCeilings = {}; + scope = "zone"; + enforcementPolicy = "hard"; + }; +}; +``` + +Eval-time assertions: `ceilings.maxResources` in `1..=65536`; `ceilings.maxOwnerDepth` in `1..=32`; `scope` equals `"zone"`; `enforcementPolicy` in `{"hard","soft"}`. Phase 2: `perTypeCeilings` ResourceType names resolved against Zone Provider catalogs. + +### 7.8 Controller algorithm + +1. **Validate** `spec.ceilings` and `spec.perTypeCeilings`; set `CeilingsValid`. +2. **Build usage index**: scan all non-Deleted Zone resources; aggregate by `quotaRef` and ResourceType. +3. **Enforce**: on every resource admission (`create` verb), check current usage + 1 against ceilings; reject with `quota-exceeded` if over ceiling and `enforcementPolicy=hard`; warn if `soft`. +4. **Update status**: write `usedResources`, `usedCpu`, `usedMemoryMib`, `usedStorageGib`, `overQuota`, `overQuotaTypes`. +5. **Trigger**: on any resource creation, deletion, or `spec.quotaRef` change, trigger quota reconcile. + +### 7.9 RBAC and audit + +Quota creates/updates generate audit event kind `resource-mutated` with redacted spec. Quota admission checks generate `quota-check` audit event with resource type, name, result (admitted/rejected), and usage snapshot. No subject names, resource names, or store paths in audit payload. + +--- + +## 8. EmergencyPolicy + +### 8.1 Role and ownership + +`EmergencyPolicy/` declares a Zone-wide emergency disable scope, +permitted actions during disable, and current disable status. Multiple +EmergencyPolicy resources may coexist; their scopes are evaluated as +a union (most restrictive wins per-action). + +**Controller/owner**: The `emergency-policy` handler of the fixed +core-controller process owns EmergencyPolicy reconciliation. Multiple +EmergencyPolicy resources with `enabled=true` may coexist; the handler +computes the most-restrictive union of all active scope flags: any scope +flag set to `true` in any enabled policy applies Zone-wide. The effective +`drainDeadlineSeconds` is the minimum across all enabled policies. + +### 8.2 Metadata + +| Field | Rule | +| --- | --- | +| `metadata.name` | Zone-unique ResourceName | +| `metadata.ownerRef` | null for operator-created EmergencyPolicies | +| `metadata.finalizers` | `core.emergency-drain` added when policy has `active=true` at deletion time | +| `metadata.deletionRequestedAt` | Set on operator delete | + +### 8.3 Spec + +```yaml +apiVersion: resources.d2b.io/v3 +type: EmergencyPolicy +metadata: + name: zone-lockdown + zone: dev + uid: + generation: 1 + revision: + ownerRef: null + finalizers: [] + deletionRequestedAt: null + createdAt: 2026-07-22T00:00:00Z + updatedAt: 2026-07-22T00:00:00Z +spec: + enabled: false # operator activates/deactivates disable + scope: + stopNewAdmissions: true # block all new resource API admissions + disconnectZoneLinks: true # gracefully disconnect all ZoneLinks + stopProviderProcesses: false # stop all Provider Process resources + drainOngoingOperations: true # drain in-flight operations to deadline + drainDeadlineSeconds: 30 # max seconds to drain; default 30 + reason: "" # operator-supplied rationale (max 256 chars); visible in resource spec and audit; never emitted in status, OTEL labels, or log labels +``` + +**Spec field rules**: + +- `enabled`: boolean; `false` means policy exists but is not active +- `scope.stopNewAdmissions`: boolean; when true, new resource API admissions are rejected while active +- `scope.disconnectZoneLinks`: boolean; when true, ZoneLinks receive graceful disconnect signal +- `scope.stopProviderProcesses`: boolean; when true, the runtime suppresses new Provider component Process launches and signals running non-bootstrap Provider component Processes to stop; Process resources are **not** deleted and no `deletionRequestedAt` is set; on deactivation the provider lifecycle controller resumes reconciliation and re-launches stopped Processes +- `scope.drainOngoingOperations`: boolean; when true, in-flight operations drain to deadline before service stops +- `drainDeadlineSeconds`: `1..=300`; default 30 +- `reason`: string max 256 chars; visible in resource spec; included in audit record body; must not appear in status fields, OTEL metric label values, or structured log labels + +### 8.4 Status + +| Field | Type | Rules | +| --- | --- | --- | +| `active` | bool | Whether this policy is currently enforced (`enabled=true` and effects applied) | +| `activatedAt` | RFC 3339 UTC or null | When most recently activated | +| `deactivatedAt` | RFC 3339 UTC or null | When most recently deactivated | +| `drainCompletedAt` | RFC 3339 UTC or null | When in-flight drain completed after most recent activation | + +### 8.5 Phase and conditions + +| Phase | Meaning | +| --- | --- | +| `Pending` | Admitted; handler initializing | +| `Ready` | Policy spec valid; handler operational (regardless of `enabled` state; see `status.active` for enforcement state) | +| `Degraded` | Policy enabled and drain in progress, or partial scope effect failure | +| `Failed` | Spec invalid | +| `Unknown` | Handler cannot determine state | + +Closed condition types: + +| Condition type | Meaning | +| --- | --- | +| `PolicyValid` | Spec fields are valid | +| `Enforced` | `True` when `enabled=true` and all effective scope actions are applied | +| `DrainComplete` | Post-activation drain completed; `True` after drain, reset on next activation | +| `EmergencyDrainPending` | Deletion is requested and `active=true`; drain must complete before finalizer clearance | + +### 8.6 ownerRef, finalizers, and deletion + +`core.emergency-drain` is added when deletion is requested while `status.active=true`: + +1. The controller sets `enabled=false` internally to begin deactivation and scope-effect reversal. +2. Drain completes per the effective `drainDeadlineSeconds` (minimum across all enabled policies, or this policy's own value if alone). +3. `core.emergency-drain` cleared; final atomic transaction emits `Deleted` revision event; resource row removed. + +Deletion of an `enabled=false` EmergencyPolicy has no finalizer; the final +atomic transaction emits `Deleted` and removes the row immediately. + +### 8.7 Nix authoring + +```nix +d2b.zones.dev.resources.zone-lockdown = { + type = "EmergencyPolicy"; + spec = { + enabled = false; + scope = { + stopNewAdmissions = true; + disconnectZoneLinks = true; + stopProviderProcesses = false; + drainOngoingOperations = true; + }; + drainDeadlineSeconds = 30; + reason = ""; + }; +}; +``` + +Eval-time assertions: `drainDeadlineSeconds` in `1..=300`; `reason` max 256 chars. `reason` is visible in the resource spec; it must not appear in status fields, OTEL metric label values, or structured log labels. + +### 8.8 Controller algorithm + +1. **Validate** spec; set `PolicyValid`. +2. **Compute effective scope union**: at each reconcile, iterate all EmergencyPolicy resources with `enabled=true` (including self). For each scope flag, set the effective flag to `true` if ANY enabled policy has it `true`. Effective `drainDeadlineSeconds` = minimum across all enabled policies. Apply/maintain the effective scope set. +3. **On `enabled` transition to `true`**: set `active=true`, `activatedAt=now()`, `Enforced=True`; apply this policy's contribution to the scope union: + - `stopNewAdmissions`: signal API admission gate to reject new requests (if not already gated by another policy) + - `disconnectZoneLinks`: emit graceful disconnect to all active ZoneLinks (if not already disconnected) + - `stopProviderProcesses`: suppress new Provider component Process launches; signal running non-bootstrap Provider component Processes to stop without setting `deletionRequestedAt` on their Process resources + - `drainOngoingOperations`: set effective drain deadline; await in-flight drain completion or deadline +4. **On `enabled` transition to `false`**: set `active=false`, `deactivatedAt=now()`, `Enforced=False`; recompute scope union from remaining enabled policies; if no other policy gates an action, restore that action (admit new requests, allow ZoneLinks to reconnect, resume Provider lifecycle reconciliation for stopped Processes). +5. **Update status** on every reconcile. + +### 8.9 RBAC and audit + +EmergencyPolicy activation/deactivation generates `emergency-policy-activated` / +`emergency-policy-deactivated` audit events carrying: policy ResourceRef/UID, +effective scope flags (union or individual), and outcome. `reason` is included +in the audit record body (it is a resource spec field); it must not appear in +audit span attribute labels, OTEL metric label values, or structured log labels. +Principal identity is carried at the ComponentSession authentication layer and +is not duplicated in the audit payload. + +--- + +## 9. Bootstrap authorization + +Before any Role/RoleBinding resources exist, the Zone runtime has one compiled +non-configurable bootstrap authorization policy. + +### 9.1 Subjects + +Exactly two subjects are permitted under bootstrap: + +- `Provider/system-core` +- `Provider/system-minijail` + +Any other subject is denied. There is no bootstrap fallback for any other +Provider, controller, service, operator, or CLI connection. + +### 9.2 Verbs + +Bootstrap grants only: + +| Verb | ResourceTypes | Scope | +| --- | --- | --- | +| `get`, `list` | all | Store recovery read | +| `create` | Zone | Initial self-resource creation | +| `create` | Provider | Initial system-core and system-minijail records | +| `create` | Host | First Host creation | +| `create` | User | Local User creation | +| `create` | Role | Initial core-controller Roles | +| `create` | RoleBinding | Initial core-controller RoleBindings | +| `update-spec`, `update-status` | Zone, Provider, Host, User | Initial reconciliation | +| `update-status` | Role, RoleBinding | Initial authorization index publication | +| `connect`, `invoke` | store-lifecycle, core-controller services | Core process launch only | + +No wildcard Provider, resource, runtime authority, or cross-Zone access is +granted. No config field widens bootstrap authorization. + +### 9.3 Properties + +- Bootstrap policy is compiled into the Zone runtime binary; it is not + configurable, overridable, or loaded from any file or resource at runtime. +- Bootstrap admission checks all structural constraints (Zone/session/route + correctness, installed Provider/API binding, ref integrity, generation/revision, + budget) as if they were normal requests. +- Every bootstrap action is structurally audited. +- Bootstrap authorization is superseded completely and immediately after the + first full set of Role/RoleBinding resources is published and the + authorization handler reports `IndexBuilt=True` for all initial bindings. +- After bootstrap supersession, any request using only bootstrap subjects/verbs + that are not covered by stored Roles is denied. +- The transition from bootstrap to stored RBAC is atomic: the authorization + handler swaps its in-memory policy in one transaction; there is no window + where both policies are active. + +### 9.4 Recovery + +If the authorization store is corrupted or the Role/RoleBinding index is +unreadable: + +- no new requests are admitted (fail-closed); +- the out-of-band safety path (see §12.6) allows a privileged local operator + to trigger a destructive reset that re-enters bootstrap for a fresh + initialization; +- the out-of-band path is authenticated by OS-level mechanisms (uid=0 or + equivalent); it cannot be invoked remotely or through d2b-bus. + +--- + +## 10. Zone self-resource and ZoneLink relationship + +### 10.1 Zone self-resource invariant + +Every Zone store contains exactly one `Zone/` resource where +`` equals `store_meta.zone_name`. This invariant is enforced: + +- at store open: if `Zone/` does not exist, bootstrap creates it; +- at every mutation: create/update requests cannot change `metadata.name` or + `metadata.zone` to violate the self-resource constraint; +- at every open: the Zone runtime reads and verifies the self resource before + admitting any API request; +- at upgrade: if `store_meta.zone_uid` differs from the Zone resource UID, + the store is quarantined (this indicates replacement or corruption); +- a second `Zone/` resource in the same store cannot be + created; the ResourceType `Zone` is restricted to cardinality 1 within a + store. + +### 10.2 No cross-Zone resource references + +Ordinary resource refs (`/`) always resolve in +the evaluating resource's own Zone. There is no URI scheme, Zone prefix, or +cross-Zone address in any ref field. + +Prohibited: + +```yaml +ownerRef: Zone/other-zone::Process/foo # REJECTED +subjectRef: other-zone:User/alice # REJECTED +``` + +A parent Zone accesses child Zone resources by calling the child Zone's +`d2b.resource.v3` API over the ZoneLink ComponentSession. The parent's +local resources (e.g. a local `Process` that represents a child Zone workload) +do not contain cross-Zone ResourceRefs; they contain only local refs valid in +the parent Zone. + +### 10.3 ZoneLink parent/child access model + +``` +Parent Zone Child Zone + ZoneLink/guest-link Zone/guest + (stores cursor, link state) (authoritative resource store) + | | + | ComponentSession | + | (via transport Provider) | + |------- d2b.resource.v3 Get ----------> | + |<------- response (child data) --------- | +``` + +The parent receives only data the child's authorization engine permits. The +parent's mapped subject in the child Zone is established through the +transport Provider's authenticated ComponentSession using +`spec.transportProviderRef`, `spec.transportSettings`, and resolved +`spec.transportCredentials`. The parent cannot read child resources it is not +authorized for; the child authorization engine is the sole arbiter. + +A disconnected parent: + +- records local ZoneLink intents but does not claim child resource states + changed; +- on reconnect, re-authenticates and applies/rejects pending intents against + current child revision; +- never uses cached child resource state for authorization decisions. + +--- + +## 11. Core controller algorithms + +### 11.1 Zone controller algorithm + +The `configuration publication` handler reconciles Zone: + +1. **Startup relist**: List `Zone/`; verify `metadata.uid` matches + `store_meta.zone_uid`; quarantine if mismatch. +2. **Spec validation**: Validate that `Zone.spec` is exactly `{}`; any + non-empty object is rejected with `zone-spec-invalid` and `ConfigValid=False` condition (handler sets Phase `Failed` for this resource). +3. **Configuration activation**: Read integrity-pinned candidate bundle; + validate all Provider packages/APIs/config/refs/owners/RBAC/budgets; + stage inactive resources in bounded transactions; atomically activate one + configuration revision; trigger affected resources/providers/controllers. +4. **Status update**: Write current `apiCatalogRevision`, `policyRevision`, + `configurationRevision` from store_meta; aggregate mandatory handler phases + into `coreControllerPhase`. +5. **Reconcile result**: `converged` when store_meta revisions match status and + all mandatory handlers are Ready; `pending` during activation; `degraded` if + optional handlers impaired. + +The Zone controller never directly touches Provider, Role, RoleBinding, Host, +Guest, or Process resources. Those are owned by their respective handlers. + +### 11.2 ZoneLink controller algorithm + +The `zone link/delegation` handler reconciles ZoneLink: + +1. **Startup relist**: List all ZoneLink resources; for each, read cursor from + `zone_link_cursors` table; verify `childZoneUid` field against last known + child UID. +2. **Transport resolution**: Resolve `spec.transportProviderRef`; wait for + that Provider to be `Ready`. Open a ComponentSession to the child Zone's + `d2b.resource.v3` service endpoint using the transport Provider's session + factory with `spec.transportSettings` and resolved + `spec.transportCredentials`. +3. **Authentication**: Establish ComponentSession using the transport; perform + Noise handshake; map to child-local subject/RoleBinding. +4. **Child authorization check**: Verify the parent's mapped subject has at + least `get|list` permissions in the child Zone; set `ChildAuthorized`. +5. **Cursor recovery**: Compare `childZoneUid` with `last_child_zone_uid`; if + changed, reset cursor to 0. +6. **Watch/relist**: Open watch from `lastAppliedRevision`; if cursor expired, + relist and re-watch. +7. **Status update**: Write connection state, cursor values, link epoch. +8. **Reconnect loop**: On disconnect, attempt reconnect with exponential backoff; + write `lastDisconnectedAt`, `connected=false`, `Unknown` phase until + reconnected. +9. **Drain on delete**: On `deletionRequestedAt`, gracefully close session, + apply drain finalizer, update status to reflect drain. + +ZoneLink does not store child resources in the parent store. Parent caches +are session-scoped only. + +### 11.3 Provider lifecycle algorithm + +The `provider lifecycle` handler reconciles Provider: + +1. **Trust/conformance**: Resolve the artifact catalog entry for `spec.artifactId`; + verify `digest`, `signatureId`, `trustEpoch` against the installed trust store; + verify `conformanceAttestationDigest` against known attestation store. + Set `PackageTrusted` and `trustResult`. +2. **Config validation**: Validate `spec.config` against the JSON Schema + identified by the artifact catalog's `configSchemaDigest`. Set `ConfigValid`. +3. **Dependency resolution**: For each alias in `spec.dependencies`, resolve + to a Ready Provider; verify service fingerprint compatibility. Set + `DependenciesReady`. +4. **API binding**: Pass component descriptors and exported ResourceTypes to + the API catalog handler; verify no name collisions; intersect permission + claims with Zone policy; install schemas. +5. **Component launch**: For each component in `spec.components`, create or + update the owned Process resource with the corresponding template/placement. + Bootstrap exception components (`system-core`, `system-minijail`) skip this + step. +6. **Readiness**: Once all required component Processes reach `Ready`, write + Provider status to `Ready` with `ApiPublished=True`. Write `providerGeneration`. +7. **Upgrade**: On spec change (generation increment), execute lifecycle policy + (`drainAndReplace`, `rolling`, `immediate`); update component Processes. +8. **Quarantine**: On trust or conformance failure, set `quarantined=true`; stop + all component Processes; withdraw exported ResourceTypes; leave state Volumes + intact for incident investigation. + +### 11.4 Authorization algorithm (Role and RoleBinding) + +The `authorization` handler reconciles Role and RoleBinding: + +1. **Role reconciliation**: + a. Validate rules (verbs, ResourceType names, ref formats, explicit wildcard + restriction). + b. Build index entry: for each (subject, Zone, ResourceType, verb, subresource, + resourceNames, executionRefs) tuple, add an allow entry. + c. Write `IndexBuilt=True`, `phase=Ready`. + d. Invalidate authorization caches for all subjects covered by changed rules. + +2. **RoleBinding reconciliation**: + a. Resolve `roleRef` to current Role UID and generation. + b. Resolve each subject in `subjects` to current UID; note unresolved. + c. Resolve `externalPrincipalSelector` if present. + d. Apply `scopeNarrowing` as an intersection with Role rules. + e. Honor operator revocation state and any pending deletion request. + f. Build index entry: for each subject × (narrowed rule) add allow entry. + g. Increment Role `activeBindingCount`. + h. Write `phase=Ready`, `IndexBuilt=True`, `roleResolved=true`. + i. Invalidate authorization caches for all covered subjects. + +3. **Index swap**: After every batch of Role/RoleBinding commits, the + authorization handler atomically swaps the in-memory evaluator to the new + generation. The swap is MVCC-safe: in-flight requests retain their original + context; new requests use the new index. + +4. **Cache invalidation**: The authorization cache (positive decisions cached + under exact attributes and short expiry) is invalidated per subject upon: + a. Any Role change affecting that subject. + b. Any RoleBinding change affecting that subject. + c. Zone policy revision change. + d. Provider/API catalog revision change. + Denials are never cached as allows. + +5. **Deletion handling**: + - **Role deletion**: Blocked by `core.role-binding-drain` until all + RoleBindings referencing this Role are deleted. The handler decrements + `activeBindingCount` on each binding deletion; clears the finalizer when + count reaches 0. + - **RoleBinding deletion**: The final atomic transaction removes the RBAC + index entry, emits the `Deleted` revision event, and removes the resource + row from the Zone store in one redb write; no observable intermediate state + exists between index removal and row removal (§6.6). + +### 11.5 Configuration generation cleanup algorithm + +The `configuration publication` handler executes the following sequence when +activating a new Nix-authored resource bundle generation. See §14.11 for the +full normative cleanup contract including status/conditions/audit. + +1. **Diff computation**: Compare the new bundle's config-owned resource set + against all config-owned resources currently in the store. Resources present + in the new bundle are upserted (created if new; spec-updated if changed). + Resources that were config-owned in any prior active generation but absent + from the new bundle are "cleanup candidates". + +2. **Immediate generation activation**: The new bundle generation is written as + the active configuration revision in `store_meta` atomically. The Zone runtime + begins serving requests under the new generation without waiting for cleanup. + `Zone.status.activeConfigurationGeneration` updates to the new value. + `ConfigurationCurrent=True` is set. + +3. **Async Delete for absent config-owned resources**: For each cleanup candidate: + a. If `metadata.deletionRequestedAt` is null, set it to the current timestamp. + b. Add the resource-type-specific core finalizer if not already present. + c. Emit an async reconcile trigger to the owning handler. + Deletes are issued concurrently, bounded by the store transaction limit. + Resources with `managedBy=controller` (including bootstrap-created resources + such as `Provider/system-core`, `Provider/system-minijail`, and the Zone + self-resource) or `managedBy=api` are never cleanup-deleted by generation change. + +4. **Controller-created resource preservation**: The handler tracks only bundle + records. Controller-created children — process instances, ephemeral records, + dynamic volumes — have no bundle entry and are never deleted by generation + change. The owning controller reconciles its dynamic children when the parent + resource's spec or phase changes via watch trigger. + +5. **Owner controller child cascade**: When a config-owned resource has + `deletionRequestedAt` set, its owning controller cascades deletion to owned children: + - `Provider` awaiting deletion: drains component Processes per `upgradePolicy`; state + Volumes preserved unless the policy specifies destruction. + - `Role` awaiting deletion: blocked by `core.role-binding-drain` until all dependent + RoleBindings are deleted or re-bound to another Role. + - `RoleBinding` awaiting deletion: atomic RBAC index removal (see §6.6); resource row + removed in same transaction. + - `ZoneLink` awaiting deletion: graceful session close; pending-intent drain; + `core.zone-link-drain` finalizer cleared. + +6. **Cleanup status tracking**: The configuration publication handler tracks + resources pending deletion via the standard watch mechanism: + - While any remain: `generationCleanupPending=true`, + `cleanupPendingCount=`, `GenerationCleanupPending=True` condition with + reason `PendingCleanup`; Zone.status.phase = `Degraded`. + - When all complete: fields reset, condition cleared, Zone.status.phase + reverts to the aggregate mandatory handler phase. + +7. **Stuck-cleanup Degraded**: If any resource awaiting deletion has not + completed finalizer drain within `cleanupStuckThreshold` (default 5 minutes), + `GenerationCleanupFailed=True` is additionally set. Zone.status.phase remains + `Degraded` until the stuck resource clears. The condition message names the + ResourceType only (not the resource name or any spec content). The operator + inspects the resource's finalizer list and owning controller conditions. + +8. **Prior generation retention**: Prior bundle files are retained in the Zone + store bundle directory up to the configured retention count (default 3, range + 1..16). When the count is exceeded, the oldest retained prior bundle beyond + the retention window is de-referenced from the rollback target (resources + with `deletionRequestedAt` set from that generation continue their individual + finalizer drain but lose rollback association). + +9. **Rollback**: An operator with `verb=zone.config-rollback` may restore the + retained prior generation bundle atomically: re-creates deleted resources from + the prior spec, issues async Deletes for superseded-generation additions. The + rollback itself triggers a new cleanup cycle. + +--- + +## 12. Async reconciliation triggers + +### 12.1 Trigger reasons + +These trigger reasons are relevant for Zone control ResourceTypes. All are +from the common closed set in `ADR-046-resource-reconciliation`. + +| Trigger | Zone | ZoneLink | Provider | Role | RoleBinding | +| --- | --- | --- | --- | --- | --- | +| `spec-generation-changed` | ✓ | ✓ | ✓ | ✓ | ✓ | +| `owned-resource-changed` | ✓ | — | ✓ | — | — | +| `dependency-changed` | — | ✓ | ✓ | — | ✓ | +| `dependency-ready` | — | ✓ | ✓ | — | — | +| `deletion-requested` | ✓ | ✓ | ✓ | ✓ | ✓ | +| `finalizer-required` | ✓ | ✓ | ✓ | ✓ | — | +| `controller-generation-changed` | ✓ | ✓ | ✓ | ✓ | ✓ | +| `Provider-generation-changed` | — | — | — | — | — | +| `policy-changed` | ✓ | — | ✓ | — | — | +| `execution-status-changed` | — | — | — | — | — | +| `scheduled-observe` | ✓ | ✓ | ✓ | — | ✓ | +| `retry-due` | — | ✓ | ✓ | ✓ | ✓ | +| `manual-reconcile` | ✓ | ✓ | ✓ | ✓ | ✓ | +| `startup-relist` | ✓ | ✓ | ✓ | ✓ | ✓ | + +`dependency-changed` for RoleBinding is triggered when the referenced Role +changes (subject resolved status changes). `dependency-ready` for Provider +is triggered when any alias dependency Provider transitions to Ready. + +### 12.2 Cross-type triggers + +| Event | Triggers reconcile of | +| --- | --- | +| Zone spec/status change | No automatic cross-type trigger; handlers watch their own types | +| ZoneLink status Connected | Provider lifecycle handler if transport Provider is ZoneLink-dependent | +| Provider reaches Ready | Every RoleBinding that includes this Provider as a subject | +| Provider quarantined | Zone status update; no cascade to Roles/RoleBindings | +| Role spec change | All RoleBindings referencing this Role (`dependency-changed`) | +| RoleBinding subject UID change | RoleBinding itself (`scheduled-observe` detects UID mismatch) | +| Role deletion finalizer drain | RoleBindings referencing it receive `deletion-requested` from core | + +### 12.3 Convergence and suppression + +Core may suppress a reconcile hint for a Zone control resource when: + +- spec generation equals status `observedGeneration`; +- controller generation is current; +- no dependency/provider/policy generation change is pending; +- no deletion/finalizer/owner/retry cause exists; +- conditions do not require re-evaluation. + +Core must NOT suppress: +- any trigger for `policy-changed` affecting Role/RoleBinding; +- any `deletion-requested` or `finalizer-required`; +- any scheduled-observe for RoleBinding subject re-resolution or revocation state refresh; +- any Provider trust expiry check. + +### 12.4 Observe intervals + +| ResourceType | `observeIntervalSeconds` | Rationale | +| --- | --- | --- | +| Zone | 60 | Periodic store health snapshot | +| ZoneLink | 30 | Transport reachability and cursor health | +| Provider | 300 | Trust epoch and conformance re-check | +| Role | null | No external drift; reconcile only on change | +| RoleBinding | 300 | Subject UID drift detection; revocation state refresh | + +These are the declared `observeIntervalSeconds` values for core-controller +handlers. Provider controllers declare their own intervals. + +--- + +## 13. Security, audit, OTEL, and errors + +### 13.1 Authorization attributes for Zone control types + +Every decision on a Zone/ZoneLink/Provider/Role/RoleBinding resource evaluates: + +```text +Zone (from authenticated session) +subject (from AuthenticatedSubjectContext.subjectRef) +ResourceType (Zone | ZoneLink | Provider | Role | RoleBinding) +subresource (spec | status | metadata | finalizers | deletion) +verb (get | list | watch | create | update-spec | update-status | ...) +resource name +executionRef / domain / userRef scope (not applicable to Zone control types) +Provider/controller generation +``` + +Zone control type modifications are restricted: + +| Operation | Permitted subjects | +| --- | --- | +| Create/delete Zone | Bootstrap only (zone-drain is core-initiated) | +| Update Zone spec | Bootstrap; then stored RBAC via core-controller Role | +| Create/delete ZoneLink | Admin subjects per Zone policy | +| Create/delete Provider | Admin subjects per Zone policy | +| Create/delete Role | Admin subjects per Zone policy; core-generated Roles owned by system-core | +| Create/delete RoleBinding | Admin subjects per Zone policy; core-generated bindings owned by system-core | +| Update-status (any) | Owning core-controller handler only | +| Update-finalizers | Core only (no external finalizer owners for Zone control types except Provider controllers per §4.3.3) | + +### 13.2 Audit records + +Audit records for Zone control type operations contain: + +- `subject` (ResourceRef/UID of the authenticated subject) +- `zone` (name) +- `resourceType` (Zone | ZoneLink | Provider | Role | RoleBinding) +- `resourceRef` (`ResourceType/name`) and `resourceUid` of the affected resource +- `verb`/`subresource` +- `expectedRevision`/`currentRevision`/`resultRevision` +- authorization decision (`allowed | denied`) and policy revisions consulted +- `operationId`/`correlationId` +- fixed outcome/error class + +Audit records for these types MUST NOT contain: + +- Role rule payloads (verbs/resourceTypes/resourceNames); +- RoleBinding subject lists; +- Provider package digests or config; +- Zone policy fields; +- ZoneLink transport credentials or cursor values; +- any credential byte, process data, or terminal byte. + +The audit sink is the same fixed OTEL-separate audit path described in the +parent ADR. Zone control type audit records share no sink with runtime +operational audit. + +### 13.3 OTEL telemetry + +Metrics for Zone control types use closed label sets: + +| Metric | Labels | +| --- | --- | +| `d2b.zone.reconcile.duration_seconds` | `handler`, `resource_type`, `outcome` | +| `d2b.zone.authorization.decisions_total` | `resource_type`, `verb`, `decision` | +| `d2b.zone.provider.component_phase` | `component_type`, `phase` | +| `d2b.zone.zoneLink.connected` | (no label; gauge per link reported as aggregate) | + +Never label: subject names, resource names, provider package digests, Zone +names, RoleBinding subject counts, config values, or any credential-adjacent +string. + +Spans for Zone control reconciliation: + +- one span per reconcile attempt: `d2b.zone.reconcile` +- child spans for trust check, config validation, component launch, API binding +- no resource payload in span attributes; only stable codes and durations +- parent trace propagated from d2b-bus into reconcile context + +### 13.4 Errors + +Stable error codes for Zone control types: + +| Error code | Meaning | +| --- | --- | +| `zone-self-resource-mismatch` | Store zone_name/zone_uid differs from Zone resource | +| `zone-spec-invalid` | Zone spec is not exactly `{}` | +| `zone-link-transport-unavailable` | Transport Provider unavailable or unreachable | +| `zone-link-child-auth-denied` | Parent subject not authorized in child Zone | +| `zone-link-child-uid-changed` | Child Zone UID changed; cursor reset required | +| `provider-package-digest-mismatch` | Package content differs from declared digest | +| `provider-trust-revoked` | Trust epoch revoked or signature invalid | +| `provider-conformance-failed` | API conformance attestation check failed | +| `provider-config-invalid` | Config failed schema validation | +| `provider-api-name-collision` | Exported ResourceType name already bound by another Provider | +| `provider-dependency-cycle` | Provider dependency alias creates a cycle | +| `role-wildcard-denied` | Explicit wildcard attempted by non-core-controller Role | +| `role-unknown-verb` | Rule contains an unknown verb token | +| `role-unknown-resource-type` | Rule names a ResourceType not in Zone API catalog | +| `rolebinding-role-not-found` | `roleRef` does not resolve to an existing Role | +| `rolebinding-subject-type-invalid` | Subject ResourceType is not a permitted subject type | +| `rolebinding-scope-exceeds-role` | scopeNarrowing attempts to grant more than Role allows | +| `bootstrap-subject-denied` | Non-bootstrap subject attempted a bootstrap-only verb | + +All error messages are bounded (max 512 bytes), UTF-8 validated, +control-character sanitized, and must not contain secrets, credentials, paths, +process data, or any other sensitive content. + +### 13.5 Authentication boundary + +Zone control type mutations require: + +- authenticated ComponentSession with verified `AuthenticatedSubjectContext`; +- the subject must be current (its resource UID must match the last seen UID + for that subject ref in the authorization index); +- subject UID changes invalidate existing sessions — any session whose subject + resource UID has changed is denied on next method call and must re-establish; +- bootstrap Providers (system-core, system-minijail) are authenticated by + the compiled bootstrap policy through their fixed process identity, not by + an external token. + +### 13.6 Emergency disable + +An emergency disable is declared by creating or updating an `EmergencyPolicy` +resource (§8) with `spec.enabled=true`. See §8 for the full EmergencyPolicy +spec and controller algorithm. When active, it applies to Zone control types as +follows: + +- Zone emergency disable stops all new resource API admissions; ongoing bounded + operations drain to their effective deadline (minimum `drainDeadlineSeconds` across + all enabled policies); +- active ZoneLinks receive a graceful disconnect signal; +- Provider component Processes are stopped without deleting their Process resources; + reconciliation resumes when all effective `stopProviderProcesses` policies are deactivated; +- Role/RoleBinding evaluation continues for ongoing admitted operations until + deadline; +- Zone status reflects the admission stop via a fixed condition; it is not a Zone + spec change and does not directly set Zone `phase`. + +--- + +## 14. Nix authoring and resource bundle + +This section is normative for the d2b 3.0 Nix configuration surface, including Quota and EmergencyPolicy resources. + +**Unified authoring syntax**: All Zone control ResourceTypes use a single +uniform structure. `metadata.name` is derived from the resource attrset key, +`metadata.zone` from the Zone attrset key, and `apiVersion` defaults to +`"d2b.dev/v3"`. `status` is omitted — it is read-only; the Zone runtime fills +all status fields. Core fills `uid`, `generation`, `revision`, `createdAt`, +`updatedAt`, `ownerRef` (where applicable), `finalizers`, and +`deletionRequestedAt`: + +```nix +d2b.zones..resources. = { + type = ""; # "Zone"|"ZoneLink"|"Provider"|"Role"|"RoleBinding"|"Quota"|"EmergencyPolicy"|"Credential"|... + spec = { + # Exact ResourceType spec fields — mirrors ResourceSpec.spec for this type. + # No renaming or re-nesting of field names. + }; +}; +``` + +**Artifact catalog**: Derivation-valued inputs (Provider packages, NixOS +system closures, and similar build outputs) are configured exclusively in the +separate artifact catalog: + +```nix +d2b.artifacts. = { + package = ; # Nix derivation; required + type = "provider" # "provider" | "nixos-system" | ... + | "nixos-system" | ...; # closed enum; further types added via ADR +}; +``` + +`` is a bounded label (`^[a-z][a-z0-9-]*$`, max 128 chars). The resource +compiler builds/includes/hashes each derivation, validates the catalog for +type consistency and ID uniqueness, checks trust/conformance, and emits a +**private integrity-pinned artifact catalog** mapping each ID to its type, +content digest, and closure metadata. Store paths are private catalog +implementation data and are **never** exposed in resource spec fields, status +fields, audit records, or OTEL attributes. + +ResourceSpecs remain **pure direct schema mirrors**: they reference artifacts +by plain bounded IDs. Provider spec uses `artifactId` (string); Guest system +spec uses `systemArtifactId` (string). These are not `*Ref` fields because +`Artifact` is not a ResourceType. A missing or wrong-type artifact ID is a +NixOS build failure. + +**Schema-driven Nix options**: For each registered ResourceType, the Nix module +generator reads the corresponding ResourceTypeSchema JSON and emits Nix option +declarations (types, defaults, documentation strings) for every field in `spec`. +There is no second hand-authored Nix option vocabulary for resource spec fields. + +**Build validation**: The resource compiler serializes each Nix `spec` attrset +to canonical JSON and validates it against the ResourceTypeSchema for the given +`type`. Provider `spec.config` is additionally validated against the exact signed +Provider's config schema (located via the artifact catalog's `configSchemaDigest` +for the named `artifactId`). The validated canonical JSON is the input to +per-resource digest computation and the Zone resource bundle. + +See §14.8 for the canonical envelope format, §14.9 for the bundle format, and +§14.10 for the full three-phase validation pipeline. + +### 14.1 Zone declaration + +```nix +d2b.zones.dev.resources.dev = { + type = "Zone"; + spec = {}; +}; +``` + + +The resource attrset key (`dev`) must equal the Zone attrset key; authoring +`type = "Zone"` with a name that differs from the Zone key is an eval assertion +error. The Zone self-resource is created by the Zone runtime on first initialization +with `managedBy=controller` and is never included in the resource bundle. + +### 14.2 ZoneLink declaration + +```nix +d2b.artifacts.transport-unix = { + package = pkgs.d2b-provider-transport-unix; + type = "provider"; +}; + +d2b.zones.parent.resources.transport-unix = { + type = "Provider"; + spec = { artifactId = "transport-unix"; config = {}; }; +}; + +d2b.zones.parent.resources.guest-link = { + type = "ZoneLink"; + spec = { + childZoneName = "guest"; + transportProviderRef = "Provider/transport-unix"; + transportSettings = {}; + transportCredentials = []; + disabled = false; + }; +}; +``` + + +`metadata.name` (`guest-link`) and `metadata.zone` (`parent`) derive from the +attrset keys. Eval-time checks: `spec.childZoneName` matches +`^[a-z][a-z0-9-]*$`; `spec.childZoneName ≠ "parent"`; `spec.limits.*` within +bounds. + +### 14.3 Provider installation + +```nix +# Step 1: declare the derivation in the artifact catalog (global, not zone-local). +# Artifact IDs are zone-scoped at use, but the catalog is a top-level Nix attrset. +d2b.artifacts.runtime-cloud-hypervisor = { + package = pkgs.d2b-provider-runtime-cloud-hypervisor; + type = "provider"; +}; + +# Step 2: install the Provider in a Zone; spec.artifactId references the catalog +# by ID. All PackageIdentity sub-fields (digest, signatureId, trustEpoch, +# manifestDigest, conformanceAttestationDigest, configSchemaDigest) are resolved +# from the artifact catalog at build time — they do not appear in the spec. +d2b.zones.dev.resources.runtime-cloud-hypervisor = { + type = "Provider"; + spec = { + artifactId = "runtime-cloud-hypervisor"; # plain bounded ID; NOT a ResourceRef + + # spec.config: operator-authored; validated at build time against the config + # schema identified by the artifact catalog's configSchemaDigest for this ID. + config = { + defaultCpuCount = 2; + defaultMemoryMib = 1024; + # Secrets via Credential ref: + # apiToken = d2b.zones.dev.credentialRef "api-token"; + }; + + # spec.exports, spec.components, spec.dependencies, spec.permissionClaims, + # spec.upgradePolicy, spec.restartPolicy: populated from the signed manifest + # embedded in the artifact at build time. These are NOT writable Nix options; + # the Nix module generator does not emit settable options for them. + # Attempting to set them is an eval assertion error. + }; +}; +``` + +Bootstrap Providers (`system-core`, `system-minijail`) are auto-generated by +the resource compiler from the Zone runtime's artifact catalog entries (type +`"provider"`, IDs `"system-core"` and `"system-minijail"`). Authoring +`d2b.zones..resources.system-core` or `system-minijail` with +`type = "Provider"` is an eval assertion error: +`"system-core and system-minijail are bootstrap-only providers and cannot be +hand-authored"`. + +### 14.4 Role authoring + +```nix +d2b.zones.dev.resources.process-controller = { + type = "Role"; + spec = { + rules = [ + { + resourceTypes = [ "EphemeralProcess" "Process" ]; + verbs = [ "create" "delete" "get" "list" "update-finalizers" + "update-spec" "update-status" "watch" ]; + subresources = []; + resourceNames = []; # empty = all names + zones = []; # empty = this Zone only + executionRefs = []; # empty = unrestricted + sessionVerbs = [ "connect" "invoke" "open-stream" ]; + } + ]; + }; +}; + +d2b.zones.dev.resources.zone-reader = { + type = "Role"; + spec = { + rules = [ + { + resourceTypes = [ "Guest" "Host" "Process" "Provider" "Zone" ]; + verbs = [ "get" "list" "watch" ]; + subresources = []; + resourceNames = []; + zones = []; + executionRefs = []; + sessionVerbs = []; + } + ]; + }; +}; +``` + +Eval-time validations: `spec.rules[*].verbs` against the closed verb enum; +`spec.rules[*].sessionVerbs` against the closed session verb enum; +`spec.rules[*].executionRefs` format if non-empty. `spec.rules[*].resourceTypes` +validation against the Zone API catalog is deferred to Phase 2 (catalog requires +loading Provider manifests; core types are known at eval time). + +### 14.5 RoleBinding authoring + +```nix +d2b.zones.dev.resources.process-controller-binding = { + type = "RoleBinding"; + spec = { + roleRef = "Role/process-controller"; + subjects = [ "Provider/system-minijail" ]; + # spec.externalPrincipalSelector: null (default; omit) + # spec.scopeNarrowing: null (full role scope; omit) + }; +}; + +d2b.zones.dev.resources.zone-reader-alice = { + type = "RoleBinding"; + spec = { + roleRef = "Role/zone-reader"; + subjects = [ "User/alice" ]; + }; +}; + +d2b.zones.dev.resources.zone-reader-bob = { + type = "RoleBinding"; + spec = { + roleRef = "Role/zone-reader"; + subjects = [ "User/bob" ]; + }; +}; +``` + +Eval-time validations: `spec.roleRef` format; each `spec.subjects[*]` format +and ResourceType; no duplicate subjects. + +### 14.6 Bootstrap provider records + +`Provider/system-core` and `Provider/system-minijail` are generated +automatically by the Zone runtime on first initialization as `managedBy=controller` +resources. They are not included in the resource bundle and are never subject to +generation cleanup deletion. + +Attempting to author `d2b.zones..resources.system-core` or +`d2b.zones..resources.system-minijail` with `type = "Provider"` emits the +eval assertion: +`"system-core and system-minijail are bootstrap-only providers and cannot be +hand-authored"`. + +--- + +### 14.7 Credential references and secret handling + +Secrets must never appear as inline string values in the resource bundle. The +Nix authoring surface uses Credential resources to hold secret references; any +Provider `config` field that requires a secret is expressed using the canonical +Credential ref object: + +```json +{ "$credentialRef": "Credential/" } +``` + +Nix authoring syntax: + +```nix +# Declare a Credential resource using the unified syntax: +d2b.zones.dev.resources.api-token = { + type = "Credential"; + spec = { + source = "systemd-credential"; # "systemd-credential"|"host-secret"|"derivation-secret" + credentialName = "d2b-api-token"; + }; +}; + +# Reference it in a Provider spec.config: +d2b.zones.dev.resources.some-provider = { + type = "Provider"; + spec = { + artifactId = "some-provider"; + config = { + apiToken = d2b.zones.dev.credentialRef "api-token"; + # ↑ compiles to: { "$credentialRef": "Credential/api-token" } + }; + }; +}; +``` + +Normative rules: + +- `$credentialRef` is the only permitted `$`-prefixed key in config JSON; any + other `$`-prefixed key is rejected at build time. +- The referenced Credential must be declared in `d2b.zones..resources.*` + with `type = "Credential"` within the same Zone; cross-Zone Credential refs + are rejected at eval time. +- Credential resources are operator-authored and appear in the resource bundle; + the Zone runtime creates them as `managedBy=configuration` resources subject to + the generation cleanup contract (§14.11). +- The Zone runtime resolves Credential refs at activation time by reading the + declared secret source. Resolved secret values are never written to the redb + store, emitted in logs, audit records, or status fields. +- The resource compiler applies a heuristic inline-secret lint (PEM headers, + base64 payload > 32 bytes, `sk-*`/`ecdsa-*` SSH key prefixes, UUIDs ≥ 128 bits + as raw hex) and rejects the build when `--strict-secrets` is set or emits a + warning otherwise. This lint is heuristic and does not replace the requirement + to use Credential refs for all actual secrets. +- When a Credential is deleted by generation cleanup, the Zone runtime revokes + the resolved secret binding before clearing the finalizer. + +### 14.8 Canonical ResourceSpec JSON envelope format + +All resources emitted by the Nix resource compiler follow this exact envelope +structure (normative). + +**Nix-to-envelope mapping**: `type` in the Nix record maps to `resourceType` +in the envelope; `spec` maps to `spec`. `metadata.name` derives from the attrset +key; `metadata.zone` derives from the Zone attrset key; `apiVersion` defaults to +`"d2b.dev/v3"`. `metadata.generation` is initialized to 1 for new resources and +incremented by the compiler whenever `spec` changes. All other metadata fields +and `status` are filled by the Zone runtime (null in the bundle). + +``` +{ + "apiVersion": "d2b.dev/v3", // fixed; not per-type versioned + "resourceType": "", // PascalCase registered type name + "metadata": { + "name": , // ResourceName; ^[a-z][a-z0-9-]*$; ≤128 chars + "zone": , // equals the Zone name this bundle is for + "uid": null, // null in bundle; set by runtime at first create + "generation": , // 1 for new resources; compiler-incremented on spec change + "revision": null, // null in bundle; set by runtime on each write + "labels": {}, // string→string; empty by default + "annotations": {}, // string→string; empty by default + "ownerRef": , // "ResourceType/name" or null + "finalizers": [], // pre-set core finalizer strings (see per-type rules) + "deletionRequestedAt": null, // null in bundle; set by runtime on deletion + "createdAt": null, // null in bundle; set by runtime on first create + "updatedAt": null // null in bundle; set by runtime on each write + }, + "spec": { ... }, // type-specific; fully determined by Nix; see per-type examples + "status": null // always null in bundle; written by runtime only +} +``` + +Invariants: + +- `metadata.zone` must equal the Zone name of the enclosing bundle. +- Runtime-assigned fields (`uid`, `revision`, `createdAt`, `updatedAt`) are `null` + in the bundle; the runtime fills them on first activation and preserves stored + values on subsequent generations. +- `metadata.generation` in the bundle is the spec-generation counter maintained + by the resource compiler: starts at 1, incremented each time the operator + changes the spec for that resource. The runtime preserves and independently + increments this counter after activation. +- `metadata.finalizers` pre-populated in the bundle instruct the runtime to add + those finalizers when the resource is first created. Values must be from the + closed per-type core finalizer set. +- `spec` is serialized to canonical JSON (RFC 8785 key ordering: UTF-16 code-unit + sort; no insignificant whitespace) by the resource compiler. +- Per-resource `digest` = `sha256()` in lowercase hex + prefixed `sha256:`. + +#### Zone canonical envelope + +```json +{ + "apiVersion": "d2b.dev/v3", + "resourceType": "Zone", + "metadata": { + "name": "dev", "zone": "dev", "uid": null, "generation": 1, + "revision": null, "labels": {}, "annotations": {}, + "ownerRef": null, "finalizers": [], + "deletionRequestedAt": null, "createdAt": null, "updatedAt": null + }, + "spec": {}, + "status": null +} +``` + +Zone is the self-resource; it is created by the Zone runtime on initialization +(`managedBy=controller`) and is not included in the resource bundle. + +#### ZoneLink canonical envelope + +```json +{ + "apiVersion": "d2b.dev/v3", + "resourceType": "ZoneLink", + "metadata": { + "name": "guest-link", "zone": "parent", "uid": null, "generation": 1, + "revision": null, "labels": {}, "annotations": {}, + "ownerRef": null, "finalizers": [], + "deletionRequestedAt": null, "createdAt": null, "updatedAt": null + }, + "spec": { + "childZoneName": "guest", + "transportProviderRef": "Provider/transport-unix", + "transportSettings": {}, + "transportCredentials": [], + "disabled": false, + "limits": { + "maxPendingIntents": 256, + "maxActiveStreams": 32, + "reconnectMaxAttempts": 10, + "reconnectWindowSecs": 300 + } + }, + "status": null +} +``` + +`childZoneUid` is `null` in the bundle; the ZoneLink controller writes it on +first successful connection and uses it to detect UID changes. + +#### Provider canonical envelope + +```json +{ + "apiVersion": "d2b.dev/v3", + "resourceType": "Provider", + "metadata": { + "name": "runtime-cloud-hypervisor", "zone": "dev", "uid": null, "generation": 1, + "revision": null, "labels": {}, "annotations": {}, + "ownerRef": null, "finalizers": ["core.provider-drain"], + "deletionRequestedAt": null, "createdAt": null, "updatedAt": null + }, + "spec": { + "artifactId": "runtime-cloud-hypervisor", + "config": { "defaultCpuCount": 2, "defaultMemoryMib": 1024 }, + "exports": [ + { "resourceType": "Guest", "schemaDigest": "sha256:445566..." } + ], + "components": [ + { + "name": "primary", "role": "controller", "placement": "HostLocal", + "processTemplate": { + "executableRef": "d2b-provider-runtime-cloud-hypervisor", + "args": ["--zone-bus-fd", "3"], + "environmentPolicy": "isolated" + }, + "required": true + } + ], + "dependencies": [], + "permissionClaims": [ + { "resourceType": "Guest", + "verbs": ["create","update-spec","update-status","delete"], + "level": "standard" } + ], + "upgradePolicy": "drain-then-replace", + "restartPolicy": "on-failure" + }, + "status": null +} +``` + +`exports`, `components`, `dependencies`, `permissionClaims`, `upgradePolicy`, and +`restartPolicy` are loaded from the signed manifest embedded in the artifact +identified by `artifactId`. The operator supplies only `artifactId` (a plain +bounded ID referencing the `d2b.artifacts.*` catalog) and `config`. The resource +compiler resolves all manifest-derived fields and PackageIdentity sub-fields from +the artifact catalog at build time. Store paths and raw closure metadata are +**private catalog implementation data** and do not appear in the spec, status, +audit records, or OTEL attributes. + +#### Role canonical envelope + +```json +{ + "apiVersion": "d2b.dev/v3", + "resourceType": "Role", + "metadata": { + "name": "process-controller", "zone": "dev", "uid": null, "generation": 1, + "revision": null, "labels": {}, "annotations": {}, + "ownerRef": "Provider/system-minijail", + "finalizers": ["core.role-binding-drain"], + "deletionRequestedAt": null, "createdAt": null, "updatedAt": null + }, + "spec": { + "rules": [ + { + "resourceTypes": ["EphemeralProcess", "Process"], + "verbs": ["create","delete","get","list","update-finalizers", + "update-spec","update-status","watch"], + "subresources": [], + "resourceNames": [], + "zones": [], + "executionRefs": [], + "sessionVerbs": ["connect","invoke","open-stream"] + } + ] + }, + "status": null +} +``` + +Note: `verbs` and `resourceTypes` arrays in the canonical envelope are sorted +ascending by the resource compiler (RFC 8785 array-of-string sort). + +#### RoleBinding canonical envelope + +```json +{ + "apiVersion": "d2b.dev/v3", + "resourceType": "RoleBinding", + "metadata": { + "name": "process-controller-binding", "zone": "dev", "uid": null, "generation": 1, + "revision": null, "labels": {}, "annotations": {}, + "ownerRef": null, "finalizers": [], + "deletionRequestedAt": null, "createdAt": null, "updatedAt": null + }, + "spec": { + "externalPrincipalSelector": null, + "roleRef": "Role/process-controller", + "scopeNarrowing": null, + "subjects": ["Provider/system-minijail"] + }, + "status": null +} +``` + + +### 14.9 Zone resource bundle: generation, canonical sort, and integrity pinning + +The Nix resource compiler emits one `zone-resources.json` per configured Zone +per build. This is the Zone resource bundle. + +```json +{ + "bundleVersion": "1", + "zoneUid": null, + "zoneName": "dev", + "generation": 7, + "generatedAt": "2026-07-22T21:25:43Z", + "nixRevision": "abc123def456", + "resources": [ + { + "resourceType": "Credential", "name": "api-token", + "digest": "sha256:...", + "envelope": { "..." } + }, + { + "resourceType": "Provider", "name": "runtime-cloud-hypervisor", + "digest": "sha256:...", + "envelope": { "..." } + }, + { + "resourceType": "Role", "name": "process-controller", + "digest": "sha256:...", + "envelope": { "..." } + }, + { + "resourceType": "RoleBinding", "name": "process-controller-binding", + "digest": "sha256:...", + "envelope": { "..." } + } + ], + "bundleDigest": "sha256:..." +} +``` + +| Bundle field | Type | Rules | +| --- | --- | --- | +| `bundleVersion` | string | Fixed `"1"`; the resource compiler's bundle schema version | +| `zoneUid` | string\|null | `null` at build time; the runtime validates against `store_meta.zone_uid` at activation; first activation stores the newly generated UID back into the bundle record in the store | +| `zoneName` | string | Must match the Zone self-resource `metadata.name` | +| `generation` | u64 | Monotonically increasing; compiler-incremented on every config change; starts at 1; rejected at runtime if not strictly greater than `store_meta.active_configuration_revision` | +| `generatedAt` | RFC 3339 UTC | Build-time timestamp from `builtins.currentTime` (impure); not used for security decisions | +| `nixRevision` | string | Git rev of the NixOS config, if available; opaque; traceability only | +| `resources` | array | Sorted ascending by `(resourceType, name)` lexicographically; contains only operator-authored resources (`managedBy=configuration`); bootstrap resources (`Provider/system-core`, `Provider/system-minijail`, `Zone/`) are runtime-created and never present in the bundle | +| `digest` | sha256 hex | `sha256()`; lowercase hex prefixed `sha256:` | +| `bundleDigest` | sha256 hex | `sha256()` over the sorted array; computed by the compiler; verified by the runtime before applying any resource | + +**Canonical sort order**: `(resourceType, name)` lexicographic ascending. Examples: +`Credential/api-token` < `Provider/runtime-cloud-hypervisor` +< `Role/process-controller` < `RoleBinding/process-controller-binding`. + +**Integrity pinning**: The runtime verifies `bundleDigest` against the `resources` +array before applying any resource from the bundle. A digest mismatch rejects the +entire bundle; the prior generation remains active. + +**Bundle file location**: Installed at +`/var/lib/d2b/zones//bundles/generation-.json` (path managed by the +Zone runtime's state directory). The `store_meta.active_configuration_revision` +pointer names the active generation. Retained prior generation bundles use the +same path scheme. + +### 14.10 Nix eval and build-time validation pipeline + +Three ordered phases validate the Nix configuration before a bundle is activated. + +#### Phase 1 — NixOS eval (pure; runs on `nixos-rebuild` evaluation and `nix flake check`) + +| Check | Mechanism | Failure mode | +| --- | --- | --- | +| Resource attrset key (→ `metadata.name`) matches `^[a-z][a-z0-9-]*$` | Nix `assert` | eval error | +| Name length ≤ 128 chars | Nix `assert` | eval error | +| `type` is a recognized core ResourceType or a non-empty string (non-core types validated at Phase 2) | Nix `assert` | eval error if empty or non-string | +| `spec.rules[*].verbs` tokens in closed verb enum (§5.3.2) — Role only | Nix `assert` | eval error | +| `spec.rules[*].sessionVerbs` tokens in closed session verb enum — Role only | Nix `assert` | eval error | +| `spec.roleRef` format `^Role/[a-z][a-z0-9-]*$` — RoleBinding only | Nix `assert` | eval error | +| `spec.subjects[*]` format `^[A-Za-z][A-Za-z0-9]*/[a-z][a-z0-9-]*$` — RoleBinding only | Nix `assert` | eval error | +| `spec.subjects[*]` ResourceType in permitted subject set (§6.3.2) — RoleBinding only | Nix `assert` | eval error | +| No duplicate entries in `spec.subjects` — RoleBinding only | Nix `assert` | eval error | +| No cross-Zone refs anywhere in `spec` (no `/ResourceType/name` form) | Nix `assert` | eval error | +| `type = "Provider"` and name `system-core` or `system-minijail` rejected | Nix `assert` | eval error with message | +| `type = "Zone"` and name ≠ Zone attrset key rejected | Nix `assert` | eval error | +| ZoneLink: `spec.childZoneName ≠` Zone attrset key | Nix `assert` | eval error | +| `spec.transportProviderRef` matches `^Provider/transport-[a-z][a-z0-9-]*$` — ZoneLink only | Nix `assert` | eval error | +| `$credentialRef` target in any `spec.*` string must name a `type = "Credential"` resource in the same Zone | Nix `assert` | eval error | +| No `$`-prefixed keys in `spec` other than `$credentialRef` | Nix `assert` | eval error | +| `spec.limits.maxPendingIntents ≤ 1024` — ZoneLink only | Nix `assert` | eval error | +| `spec.limits.maxActiveStreams ≤ 128` — ZoneLink only | Nix `assert` | eval error | +| `spec.artifactId` format `^[a-z][a-z0-9-]*$`, max 128 chars — Provider only | Nix `assert` | eval error | +| `type = "Provider"` without a `spec.artifactId` field is rejected | Nix `assert` | eval error | +| `spec.artifactId` value must name an entry in `d2b.artifacts.*` (attrset key lookup at eval time) with `type = "provider"` — Provider only | Nix `assert` | eval error if ID absent from catalog | +| Manifest-derived Provider spec fields (`spec.exports`, `spec.components`, `spec.dependencies`, `spec.permissionClaims`, `spec.upgradePolicy`, `spec.restartPolicy`) not set by operator | Nix `assert` | eval error with message | + +#### Phase 2 — Nix build (impure; runs on `nixos-rebuild build` and `nix build`) + +| Check | Mechanism | Failure mode | +| --- | --- | --- | +| Each resource's `spec` validated against the ResourceTypeSchema for its `type`; canonical JSON compared against schema (`genSchemaOptions` emits Phase 1 Nix options; Phase 2 performs the definitive comparison against the committed schema JSON) | Resource compiler | build failure naming type and failing field | +| `spec.artifactId` names an existing `d2b.artifacts.` catalog entry with `type = "provider"` — Provider only | Resource compiler | build failure: "artifact ID not found or wrong type" | +| Artifact catalog entry has required derivation outputs (manifest, config schema, executable) — Provider only | Resource compiler | build failure | +| Artifact catalog `configSchemaDigest` matches SHA-256 of schema file in derivation output — Provider only | Resource compiler | build failure | +| Operator `spec.config` passes JSON Schema validation against catalog-resolved config schema — Provider only | Resource compiler | build failure naming failing field and schema path | +| Artifact catalog `manifestDigest` matches SHA-256 of manifest file in derivation output — Provider only | Resource compiler | build failure | +| Artifact manifest signature chain valid against installed trust store — Provider only | Resource compiler | build failure | +| Artifact `conformanceAttestationDigest` present in known attestation store — Provider only | Resource compiler | build failure | +| No duplicate `d2b.artifacts.` entries (IDs are unique across the catalog) | Resource compiler | build failure naming duplicate ID | +| All declared dependency aliases resolve within the same Zone's Providers — Provider only | Resource compiler | build failure naming unresolved alias | +| ResourceType short-name collision among installed Providers in the same Zone | Resource compiler | build failure naming conflicting Providers | +| `spec.rules[*].resourceTypes` resolved against installed Provider catalogs in the Zone bundle — Role only | Resource compiler | build failure | +| `spec.roleRef` names an existing `type = "Role"` resource in the same Zone bundle — RoleBinding only | Resource compiler | build failure | +| `spec.subjects[*]` names resolve to declared resources or known external principal types in the same Zone bundle — RoleBinding only | Resource compiler | build failure | +| Inline-secret heuristic lint on config string values | Resource compiler | build warning; build failure with `--strict-secrets` | +| `resources` array sorted by `(resourceType, name)` | Resource compiler | auto-sorted; not an operator error | +| Per-resource `digest` = `sha256(canonical-JSON-of-spec)` | Resource compiler | computed and written; mismatch is a compiler bug | +| `bundleDigest` over sorted `resources` array | Resource compiler | computed and written | + +#### Phase 3 — Runtime activation (on daemon restart or `d2b zone config apply`) + +| Check | Mechanism | Failure mode | +| --- | --- | --- | +| `bundleDigest` integrity re-verified against `resources` array | Zone runtime | bundle rejected; prior generation stays active | +| `zoneUid` consistency: null = first activation; non-null must match `store_meta.zone_uid` | Zone runtime | bundle rejected | +| `zoneName` must match Zone self-resource `metadata.name` | Zone runtime | bundle rejected | +| `generation` strictly greater than `store_meta.active_configuration_revision` | Zone runtime | bundle rejected (prevents replay/downgrade) | +| All resource `digest` values re-verified against `envelope.spec` | Zone runtime | bundle rejected | +| Provider `config` re-validated against the config schema identified by the artifact catalog entry for `spec.artifactId` | Zone runtime | affected Provider set to `Failed` with `ConfigValid=False` condition; generation proceeds for other resources | +| `zoneUid=null` only on store's first activation (no prior Zone UID stored) | Zone runtime | bundle rejected if non-null UID expected | + +If Phase 3 rejects the bundle, no resources are applied atomically and the prior +generation remains active. A per-Provider `config` failure does not block other +resources from activating; the affected Provider is set to `Failed` with `ConfigValid=False` condition. + +### 14.11 Configuration generation lifecycle and cleanup contract + +This section is the normative specification for resource lifecycle across Nix +configuration generation changes. + +#### Configuration ownership + +The Zone runtime stores two internal resource metadata fields on every resource +in the redb store (not exposed in the public resource API envelope): +- `managedBy`: one of `configuration | controller | api` — set by the runtime at create time +- `configurationGeneration`: the bundle generation when this resource was last confirmed present in the bundle + +Every resource present in the bundle is created (or updated) by the runtime as +`managedBy=configuration`. The Zone runtime also creates bootstrap resources +(`Provider/system-core`, `Provider/system-minijail`, and the Zone self-resource) +directly on initialization as `managedBy=controller`; these are not included in +the bundle. The cleanup authority is the stored `managedBy` and +`configurationGeneration` values on the resource, **never** inferred from bundle +absence, `ownerRef`, labels, or any other field. + +| Class | `managedBy` | In bundle | Cleanup eligible | +| --- | --- | --- | --- | +| Config-owned | `configuration` | Yes | Yes — absent from new bundle (`configurationGeneration < new generation`) → async Delete | +| Bootstrap-created | `controller` | No | No — `managedBy=controller` resources untouched by generation cleanup | +| Controller-created | `controller` | No | No — `managedBy=controller` resources untouched by generation cleanup | +| API-created | `api` | No | No — `managedBy=api` resources untouched by generation cleanup | + +The resource compiler must reject any operator attempt to name a controller-created +resource in config (e.g., placing `Process/provider-primary-0` under `d2b.zones.*`). +This is enforced by a build-time exclusion list of reserved name prefixes per +controller-managed ResourceType. + +#### Generation activation sequence (non-blocking) + +1. Resource compiler emits bundle with `generation=N+1`. +2. Zone runtime reads and integrity-verifies the bundle (Phase 3 checks, §14.10). +3. `store_meta.active_configuration_revision` advances to `N+1` atomically. +4. The Zone begins serving all requests under generation `N+1` immediately. + No cleanup gate; no operator wait. +5. `Zone.status.activeConfigurationGeneration = N+1`. +6. `Zone.status.conditions[ConfigurationCurrent].status = "True"`. +7. Absent config-owned resources are identified and queued for async Delete + (see below). Activation does not wait for them. + +#### Absent resource deletion (normative) + +After generation activation, for each resource in the store with +`managedBy=configuration` whose `configurationGeneration` does not match the +new bundle generation (i.e., absent from the new bundle): + +1. `metadata.deletionRequestedAt` is set to the activation timestamp (if null). +2. The resource-type-specific core finalizer is added if absent: + - `Provider`: `core.provider-drain` + - `Role`: `core.role-binding-drain` + - `ZoneLink`: `core.zone-link-drain` + - `Credential`: `core.credential-revoke` + - `RoleBinding`: no pre-set finalizer (immediate atomic deletion) +3. An async reconcile trigger is emitted to the owning controller handler. +4. `Zone.status.cleanupPendingCount` is incremented per candidate. +5. `Zone.status.generationCleanupPending = true`. +6. `Zone.status.conditions[GenerationCleanupPending]` is set with reason + `PendingCleanup` and message `" config-owned resources from generation + completing deletion"`. +7. Zone.status.phase transitions to `Degraded` (remains until cleanup completes). + +The Zone runtime never force-removes finalizers. If an owning controller fails +to clear its finalizer, `GenerationCleanupFailed` is eventually set and Zone +remains `Degraded`. Resources with `managedBy=controller` or `managedBy=api` +are never touched by the generation cleanup path. + +#### Controller-created child preservation (normative) + +Controllers reconcile their dynamic children in response to parent resource +deletion requests (`deletionRequestedAt` set) and phase changes, not in response +to generation changes directly: + +- A `Provider` with `deletionRequestedAt` set triggers the provider lifecycle + handler to stop owned component Processes. State Volumes are preserved unless + `upgradePolicy=immediate`. +- A `Role` with `deletionRequestedAt` set is blocked by `core.role-binding-drain` + until all dependent RoleBindings are deleted or re-bound to another Role. + The authorization handler triggers dependent RoleBinding deletion automatically. +- `RoleBinding` deletion is the atomic final transaction (§6.6): RBAC index + removal + row removal + `Deleted` event in one redb write. +- `ZoneLink` deletion closes the session gracefully, drains pending intents, then + clears `core.zone-link-drain`. +- Controller-created resources with `managedBy=controller` (e.g., `Process/provider-primary-0`, + `Volume/provider-state`) are **never** automatically deleted on generation change, + even if absent from the new Nix config. They are managed exclusively by their + owning controller in response to parent resource phase transitions. + +#### Cleanup status, conditions, and audit (normative) + +**While cleanup is pending**: + +``` +Zone.status.generationCleanupPending = true +Zone.status.cleanupPendingCount = +Zone.status.conditions: + GenerationCleanupPending: + status: "True" + reason: "PendingCleanup" + message: " config-owned resources from generation completing deletion" + lastTransitionTime: +Zone.status.phase: "Degraded" (transitions to Degraded while any cleanup is pending) +``` + +**After all cleanup candidates complete**: + +``` +Zone.status.generationCleanupPending = false +Zone.status.cleanupPendingCount = 0 +Zone.status.conditions[GenerationCleanupPending].status: "False" +Zone.status.conditions[GenerationCleanupFailed]: absent or status="False" +Zone.status.phase: reverts to aggregate mandatory handler phase (typically "Ready") +``` + +**On stuck cleanup** (resource awaiting deletion with no controller progress for +`cleanupStuckThreshold`, default 5 minutes): + +``` +Zone.status.phase: "Degraded" (already set from pending cleanup; remains Degraded) +Zone.status.conditions: + GenerationCleanupFailed: + status: "True" + reason: "CleanupStuck" + message: " resource has been awaiting deletion beyond threshold" + lastTransitionTime: +``` + +The message names the ResourceType only; it does not include the resource name, +spec content, or any configuration value. + +**Audit records** (all carry `zone_name` and `generation`; no resource names, +spec content, or secret values): + +| Audit kind | Emitted when | +| --- | --- | +| `zone.config.generation.activate` | New bundle generation becomes active | +| `zone.config.cleanup.complete` | All cleanup candidates for a generation complete deletion | +| `zone.config.cleanup.stuck` | A cleanup candidate exceeds `cleanupStuckThreshold` | +| `zone.config.generation.rollback` | Prior generation is restored by operator | + +**Error handling**: The configuration publication handler retries stuck finalizer +notifications with exponential backoff, bounded by `cleanupStuckThreshold`. After +threshold, it records the `GenerationCleanupFailed` condition without further +retries. The runtime never force-removes finalizers. An operator may resolve a +stuck cleanup by: (a) fixing or restarting the owning controller so it can +complete its finalizer, or (b) performing a full Zone reset. + +#### Prior generation retention and rollback + +Prior generation bundle files are retained in the Zone store bundle directory +up to the configured `retainedPriorGenerationCount` (default 3, range 1..16). +A bundle file for generation M is eligible for pruning when: + +1. All resources from generation M with `managedBy=configuration` that were + absent from generation M+1 have completed deletion; AND +2. No rollback lock is outstanding (`d2b zone config rollback-lock set`); AND +3. Retaining this file would exceed `retainedPriorGenerationCount`. + +When the count is exceeded, the oldest eligible prior bundle file is pruned; +resources with `deletionRequestedAt` already set from that generation continue +their individual finalizer drain but lose rollback association. + +An operator with `verb=zone.config-rollback` may restore the retained prior +generation: + +``` +d2b zone config rollback --zone dev --to-generation +``` + +Rollback atomically: +1. Re-activates the prior bundle as the new active generation. +2. Re-creates any config-owned resources deleted by the superseded generation's + cleanup, using the prior bundle's resource envelopes. +3. Issues async Deletes for resources that appeared in the superseded generation + but not the prior generation. +4. Emits `zone.config.generation.rollback` audit record. +5. Triggers a new cleanup cycle for the superseded-generation additions. + +--- + +## 15. Conformance and tests + +### 15.1 Zone self-resource tests + +| Test | Assertion | +| --- | --- | +| `zone-self-resource-enforced` | Store with no Zone resource fails to open | +| `zone-uid-mismatch-quarantine` | Opening a store where Zone UID differs from store_meta.zone_uid quarantines the store | +| `zone-name-mismatch-rejected` | Zone resource with `metadata.name != store_meta.zone_name` is rejected | +| `zone-cardinality-one` | Attempt to create a second Zone resource is rejected with `resource-already-exists` | +| `zone-cross-zone-ref-rejected` | Any resource with a ref containing a Zone prefix is rejected | +| `zone-owner-rejected` | Create request with non-null Zone `ownerRef` is rejected | +| `zone-deletion-only-on-drain` | Zone deletion without `core.zone-drain` finalizer path is rejected | + +### 15.2 ZoneLink tests + +| Test | Assertion | +| --- | --- | +| `zonelink-reconnect-child-uid-change` | On reconnect with changed child UID, cursor resets to 0 and `childZoneUid` updates | +| `zonelink-disconnect-unknown-phase` | On transport disconnect ZoneLink phase becomes Unknown within one reconcile | +| `zonelink-intent-queue-limit` | Queuing more than 256 local intents returns `backpressure` | +| `zonelink-disabled-no-reconnect` | `spec.disabled=true` prevents reconnection and sets `DisabledByOperator` condition | +| `zonelink-child-auth-denied-failed` | Child Zone rejecting parent subject sets `Failed` phase and `ChildAuthorizationDenied` condition | +| `zonelink-drain-closes-session` | Deletion drains local intents and closes session before removing resource | +| `zonelink-transport-provider-ref-required` | ZoneLink without `spec.transportProviderRef` fails admission | +| `zonelink-transport-ref-pattern-enforced` | `spec.transportProviderRef = "Provider/network-local"` fails admission because it does not match the required `transport-*` pattern | +| `zonelink-transport-credentials-max` | ZoneLink with 9 `transportCredentials` entries fails admission | + +### 15.3 Provider tests + +| Test | Assertion | +| --- | --- | +| `provider-trust-check-required` | Provider with invalid signature is rejected before any component launch | +| `provider-conformance-check-required` | Provider without attestation digest is rejected | +| `provider-config-schema-validation` | Config object not matching configSchemaDigest schema is rejected | +| `provider-api-name-collision-rejected` | Two Providers exporting the same short ResourceType name: second rejected | +| `provider-dependency-cycle-rejected` | Provider with cyclic alias dependency is rejected | +| `provider-quarantine-on-trust-failure` | Trust check failure after install quarantines Provider and stops components | +| `provider-bootstrap-no-process` | system-core and system-minijail report no processRef; all others require processRef | +| `provider-wildcard-permission-restricted` | Non-bootstrap Provider with wildcard permission claim is rejected | +| `provider-upgrade-drain-then-replace` | `upgradePolicy=drain-then-replace` completes old component drain before new launch | +| `provider-component-bound-limits` | Provider with 9 controllers fails admission; 8 is the maximum | +| `provider-crate-layout-src-required` | A `d2b-provider-*` workspace crate without `src/` fails `make test-policy` with message naming the crate and missing path | +| `provider-crate-layout-tests-required` | A `d2b-provider-*` workspace crate without `tests/` fails `make test-policy` | +| `provider-crate-layout-integration-required` | A `d2b-provider-*` workspace crate without `integration/` fails `make test-policy` | +| `provider-crate-layout-readme-required` | A `d2b-provider-*` workspace crate without `README.md` fails `make test-policy` | +| `provider-readme-sections-all-present` | A Provider `README.md` missing any of the nine required headings (§4.8.3) fails policy with the exact missing heading name | +| `provider-readme-sections-partial-missing` | A Provider `README.md` with 8 of 9 sections fails policy; message names the one missing section | +| `provider-integration-target-declared` | An `integration/*.rs` file without an `integration-target:` declaration in the first 20 lines fails policy | +| `provider-integration-target-unique` | An `integration/*.rs` file with two `integration-target:` declarations fails policy | +| `provider-integration-target-valid-values` | An `integration-target:` value other than `container` or `host-integration` fails policy | +| `provider-crate-naming-convention` | A crate named `d2b--` (implementation before base) fails the workspace member name policy | +| `provider-crate-layout-non-provider-exempt` | A non-`d2b-provider-*` workspace crate is exempt from the §4.8 layout check | + +### 15.4 Role tests + +| Test | Assertion | +| --- | --- | +| `role-unknown-verb-rejected` | Rule with an unknown verb token fails admission | +| `role-unknown-resource-type-rejected` | Rule naming a ResourceType not in Zone API catalog is rejected | +| `role-wildcard-non-core-rejected` | Non-core-controller Role with `resourceNames: ["*"]` is rejected | +| `role-wildcard-core-permitted` | Core-generated Role with `resourceNames: ["*"]` is admitted | +| `role-index-built-before-ready` | Role does not reach Ready until `IndexBuilt=True` | +| `role-deletion-blocked-by-bindings` | Role with active bindings cannot be deleted until all bindings removed | +| `role-bounds-enforced` | Role with 33 rules fails admission; 32 is the maximum | + +### 15.5 RoleBinding tests + +| Test | Assertion | +| --- | --- | +| `rolebinding-role-not-found-failed` | RoleBinding with non-existent `roleRef` reaches `Failed` phase | +| `rolebinding-invalid-subject-type-rejected` | Subject with non-permitted ResourceType rejected at admission | +| `rolebinding-scope-exceeds-role-rejected` | `scopeNarrowing` with verb absent from Role rejected at admission | +| `rolebinding-subject-uid-change-detected` | Subject deleted/recreated triggers `SubjectIdentityChanged` condition | +| `rolebinding-deletion-immediate` | RoleBinding deletion is one atomic transaction: RBAC index removal, `Deleted` revision event, and row removal occur simultaneously; no intermediate state is observable | +| `rolebinding-subject-bounds-enforced` | RoleBinding with 129 subjects fails admission | + +### 15.6 Bootstrap tests + +| Test | Assertion | +| --- | --- | +| `bootstrap-only-system-core-minijail` | Any subject other than system-core/system-minijail is denied under bootstrap | +| `bootstrap-no-runtime-authority` | Bootstrap cannot grant exec/shell/process-outside-spec operations | +| `bootstrap-non-configurable` | No config field widens bootstrap authorization | +| `bootstrap-supersession-atomic` | After stored RBAC publishes, bootstrap is fully superseded with no overlap window | +| `bootstrap-recovery-out-of-band` | Corrupt authorization store triggers fail-closed; reset requires privileged local operator | + +### 15.7 Cross-cutting tests + +| Test | Assertion | +| --- | --- | +| `zone-control-audit-no-payload` | Audit records for Zone control type mutations contain no rule/subject/config/digest content | +| `zone-control-otel-no-sensitive-labels` | OTEL metrics carry no subject names, resource names, or credential-adjacent labels | +| `zone-control-error-bounded` | All Zone control error messages ≤ 512 bytes, UTF-8, no secrets | +| `zone-control-status-owner-only` | Only core-controller handler may update-status for any Zone control resource | +| `zone-control-cross-zone-ref-rejected` | Any ref with cross-Zone notation is rejected at admission | + +### 15.8 Configuration generation and cleanup tests + +#### Phase 1 — Nix eval tests + +| Test | Assertion | +| --- | --- | +| `nix-eval-name-regex-enforced` | Zone/Provider/Role/RoleBinding with name `"Bad_Name"` fails eval with assertion error | +| `nix-eval-name-length-enforced` | Resource name of 129 characters fails eval | +| `nix-eval-verb-closed-enum` | Rule with verb `"delete-all"` (unknown) fails eval | +| `nix-eval-session-verb-closed-enum` | Rule with `sessionVerbs=["sudo"]` fails eval | +| `nix-eval-roleref-format` | `roleRef="role/foo"` (wrong case) fails eval | +| `nix-eval-subject-type-restricted` | Subject `"Process/foo"` (non-permitted type) fails eval | +| `nix-eval-no-duplicate-subjects` | Two identical subjects in one RoleBinding fails eval | +| `nix-eval-no-cross-zone-ref` | Subject `"dev/Role/foo"` (Zone-prefixed) fails eval | +| `nix-eval-bootstrap-provider-rejected` | `d2b.zones.dev.resources.system-core = { type = "Provider"; ... }` fails eval with named assertion | +| `nix-eval-provider-missing-artifact-id` | `d2b.zones.dev.resources.p = { type = "Provider"; spec = { config = {}; }; }` (no `artifactId`) fails eval | +| `nix-eval-artifact-id-not-in-catalog` | `spec.artifactId = "nonexistent"` for a Provider where `d2b.artifacts` has no `nonexistent` entry fails eval | +| `nix-eval-artifact-wrong-type` | `d2b.artifacts.foo = { package = pkgs.hello; type = "nixos-system"; }` used as `spec.artifactId` in a Provider fails eval (type mismatch) | +| `nix-eval-artifact-id-format` | `spec.artifactId = "Bad_ID"` fails eval with label regex assertion | +| `nix-eval-credentialref-declared` | `d2b.zones.dev.credentialRef "missing"` for undeclared Credential fails eval | +| `nix-eval-dollar-key-rejected` | Config `{ "$secret" = "x"; }` fails eval | +| `nix-eval-zonelink-self-loop-rejected` | ZoneLink with `childZoneName` equal to parent Zone name fails eval | +| `nix-eval-zonelink-limits-maxpendingintents-bound` | `maxPendingIntents = 1025` fails eval | + +#### Phase 2 — Build tests + +| Test | Assertion | +| --- | --- | +| `nix-build-artifact-id-missing-from-catalog` | Provider `spec.artifactId = "unknown"` fails build with "artifact ID not found" | +| `nix-build-artifact-wrong-type-rejected` | Provider `spec.artifactId` pointing to a `nixos-system` catalog entry fails build | +| `nix-build-duplicate-artifact-id` | Two `d2b.artifacts` entries with the same ID fail build with duplicate error | +| `nix-build-artifact-store-path-absent-from-bundle` | Emitted bundle JSON contains no Nix store path strings | +| `nix-build-artifact-store-path-absent-from-config` | Emitted config JSON in bundle contains no Nix store path strings | +| `nix-build-config-schema-failure` | Provider config field of wrong type fails build with field path in error | +| `nix-build-schema-digest-mismatch` | Schema file with SHA-256 not matching `configSchemaDigest` in artifact catalog fails build | +| `nix-build-manifest-digest-mismatch` | Manifest file with SHA-256 not matching `manifestDigest` in artifact catalog fails build | +| `nix-build-resourcetype-collision` | Two Providers in the same Zone exporting the same short ResourceType name fail build | +| `nix-build-bundle-sorted` | Emitted `resources` array is sorted by `(resourceType, name)` ascending | +| `nix-build-bundle-digest-stable` | Same Nix config on two builds produces identical `bundleDigest` | +| `nix-build-per-resource-digest-correct` | Per-resource `digest` matches `sha256(canonical-JSON-of-spec)` | +| `nix-build-credential-ref-survives-build` | `{ "$credentialRef": "Credential/api-token" }` appears verbatim in emitted bundle | +| `nix-build-inline-secret-lint-warning` | Config string matching PEM header emits build warning | +| `nix-build-inline-secret-strict-failure` | Same with `--strict-secrets` fails build | + +#### Phase 3 — Runtime activation tests + +| Test | Assertion | +| --- | --- | +| `nix-runtime-bundledigest-integrity` | Bundle with tampered `bundleDigest` is rejected; prior generation stays active | +| `nix-runtime-generation-monotone` | Bundle with `generation ≤ active_configuration_revision` is rejected | +| `nix-runtime-zoneuid-mismatch-rejected` | Bundle with `zoneUid` not matching `store_meta.zone_uid` is rejected | +| `nix-runtime-zonename-mismatch-rejected` | Bundle with `zoneName != Zone self-resource name` is rejected | +| `nix-runtime-activation-nonblocking` | New generation activates and serves requests before cleanup of prior generation completes | +| `nix-runtime-provider-config-invalid-continues` | Provider `config` failing schema re-check sets `Failed` with `ConfigValid=False` condition but does not block other resources from activating | + +#### Cleanup tests + +| Test | Assertion | +| --- | --- | +| `cleanup-config-owned-absent-resource-deleted` | Provider present in generation N but absent from N+1 receives async Delete | +| `cleanup-controller-created-resource-preserved` | Controller-created `Process/provider-primary-0` (not in bundle) is NOT deleted on generation change | +| `cleanup-bootstrap-provider-preserved` | `Provider/system-core` absent from operator config is NOT deleted on any generation change | +| `cleanup-role-deletion-blocked-by-binding` | Config-owned Role awaiting cleanup is blocked by `core.role-binding-drain` until dependent RoleBinding is deleted | +| `cleanup-rolebinding-auto-deleted-when-role-deleted` | Authorization handler triggers RoleBinding deletion when parent Role has `deletionRequestedAt` set and is awaiting `core.role-binding-drain` clearance | +| `cleanup-provider-stops-processes-on-delete` | Config-owned Provider with `deletionRequestedAt` set stops owned component Processes via provider lifecycle handler | +| `cleanup-credential-revoke-on-delete` | Credential with `deletionRequestedAt` set triggers runtime to revoke resolved secret binding before finalizer clearance | +| `cleanup-status-pending-count-accurate` | `Zone.status.cleanupPendingCount` equals exact count of cleanup candidates with `deletionRequestedAt` set | +| `cleanup-zone-degraded-while-pending` | Zone.status.phase is Degraded while any cleanup candidate has `deletionRequestedAt` set; reverts when all complete | +| `cleanup-condition-clears-on-completion` | `GenerationCleanupPending` condition clears, count resets to 0, and Zone.status.phase reverts after all cleanup candidates complete | +| `cleanup-stuck-sets-degraded` | Resource awaiting deletion beyond `cleanupStuckThreshold` keeps Zone.status.phase=Degraded and additionally sets `GenerationCleanupFailed=True` | +| `cleanup-stuck-message-no-content` | `GenerationCleanupFailed` condition message contains ResourceType but no resource name, spec content, or secret value | +| `cleanup-audit-activate-emitted` | Generation activation emits `zone.config.generation.activate` audit record | +| `cleanup-audit-complete-emitted` | Cleanup completion emits `zone.config.cleanup.complete` audit record | +| `cleanup-audit-stuck-emitted` | Stuck cleanup emits `zone.config.cleanup.stuck` audit record | +| `cleanup-audit-no-resource-names` | All cleanup audit records contain no resource names, spec content, or secret values | +| `rollback-restores-prior-generation` | `d2b zone config rollback` re-creates deleted config-owned resources from prior bundle | +| `rollback-deletes-superseded-additions` | Rollback issues async Delete for resources added by the superseded generation but absent from prior bundle | +| `rollback-requires-prior-generation-retained` | Rollback fails if prior generation bundle has been pruned | +| `rollback-emits-audit-record` | Rollback emits `zone.config.generation.rollback` audit record | + + +### 15.9 Quota and EmergencyPolicy tests + +| Test | Assertion | +| --- | --- | +| `quota-ceiling-hard-reject` | Resource creation over `maxResources` ceiling with `enforcementPolicy=hard` rejected with `quota-exceeded` | +| `quota-ceiling-soft-warn` | Same but `enforcementPolicy=soft`; resource admitted; `overQuota=true` in status | +| `quota-ceiling-pertype` | Resource creation over `maxResourcesPerType` ceiling for a specific ResourceType rejected | +| `quota-drain-blocks-on-dependents` | Quota deletion with `dependentCount > 0` keeps `core.quota-drain`; `QuotaDrainPending=True` condition message names the count; no quotaRef on other resources is modified | +| `quota-over-quota-status` | Quota status `usedResources` and `overQuota` reflect current usage | +| `quota-nix-eval-bounds` | `ceilings.maxOwnerDepth = 33` fails eval; `33 > 32` | +| `quota-nix-build-pertype-unknown-type` | `perTypeCeilings` entry for a ResourceType not in Zone API catalog fails build | +| `emergency-policy-activates-gate` | EmergencyPolicy with `enabled=true` and `stopNewAdmissions=true` causes new resource admissions to return admission-denied | +| `emergency-policy-disconnects-zonelinks` | EmergencyPolicy activation with `disconnectZoneLinks=true` triggers ZoneLink graceful disconnect | +| `emergency-policy-multiple-enabled-union` | Two enabled EmergencyPolicy resources with `stopNewAdmissions=true` and `disconnectZoneLinks=true` respectively produce a combined union where both effects apply Zone-wide | +| `emergency-policy-union-deactivate-partial` | Deactivating one of two enabled EmergencyPolicy resources removes only its contribution; the other policy's effects remain | +| `emergency-policy-deactivation-restores-gate` | Setting `enabled=false` restores normal admissions | +| `emergency-policy-stop-processes-no-delete` | EmergencyPolicy with `stopProviderProcesses=true` stops running Provider component Processes without setting `deletionRequestedAt`; Process resources remain; reconciliation resumes on deactivation | +| `emergency-policy-drain-finalizer-on-active-delete` | Deleting an EmergencyPolicy with `active=true` adds `core.emergency-drain`; drain completes before final atomic deletion | +| `emergency-reason-visible-in-spec` | EmergencyPolicy `reason` is readable in the resource spec via API; it does not appear in `status.*`, OTEL metric label values, or structured log labels | + +--- + +## 16. Current-code fit + +All seven Zone control ResourceTypes as stored resource objects are **`ADR-only`**: +no Zone, ZoneLink, Provider, Role, or RoleBinding resource schema, store table, +or controller handler exists in the v3 baseline (`b5ddbed6`). The subsections +below record every Realm-related baseline symbol that maps to Zone control +concepts, with exact evidence class and architectural mapping notes. + +### 16.1 Why Realm→Zone is not a textual rename + +ADR 0043 (`docs/adr/0043-realm-native-control-plane.md`, Accepted) defines a +Realm as a **runtime control-plane process pair**: each active realm boundary is +served by its own `d2bd` instance and its own privileged broker, with a distinct +socket, state directory, audit log, cgroup slice, nftables partition, and +identity key. A Realm is a running process boundary, not a data record. + +A **Zone** (ADR 0046) is a typed ResourceType stored in a redb object store, +reconciled by the core controller. It is a resource object representing the +local isolation and policy domain of the store that owns it — one Zone per +store, cardinality-1 enforced. It is not itself a process; the Zone runtime +that hosts the store and core controller is the process, but the Zone resource +is just the authoritative metadata record in that runtime. + +The key architectural differences that implementations must track: + +| Dimension | Baseline Realm (ADR 0043) | Target Zone (ADR 0046) | +| --- | --- | --- | +| Identity model | `RealmPath(Vec)`, a tree path; realms may nest up to `MAX_REALM_LABELS=16` | `metadata.name` (single label); parent/child expressed via ZoneLink resources | +| Process model | Each realm = one `d2bd` + one broker (multiple pairs on one host) | Zone is a resource record; Zone runtime is the surrounding process | +| Hierarchy representation | Tree path embedded in identity; parent realm is a routing ancestor in `RouteTreeEngine`/`RealmEntrypointTable` | Parent/child is a stored `ZoneLink` resource in the child Zone's store; no path encoding | +| Config loading | `realm-controllers.json` loaded at daemon startup (`load_realm_controllers_config` at `d2bd/src/lib.rs:1408`); routing remains "inert" but metadata is live | Zone self resource read from redb store; no external JSON config file for Zone identity | +| Access resolution | `RealmAccessResolverRequest/Response` + `RealmEntrypointTable` loaded from `/run/current-system/sw/share/d2b/realm-entrypoints.json`; reachable in CLI routing path (`d2b/src/target_routing.rs`) | ZoneLink resource with ComponentSession transport binding; access resolution is a resource-lifecycle operation | +| Provider placement | `RealmControllerPlacement` enum (`HostLocal`, `GatewayVm`, `CloudFullHost`, `ProviderController`, `ProviderAgent`) — runtime property of where realm's d2bd runs | Provider component `placement` field — descriptor in Provider resource spec; different semantics (component placement, not realm process placement) | +| Host-resource allocation | `LocalRootAllocatorEngine` in `d2b-realm-core/src/allocator_engine.rs:332`; typed leases for cgroup subtrees, nftables partitions, socket paths, bridges; leases tied to realm broker lifecycle | Resource store lifecycle + broker operations; allocator concepts move into Zone runtime internal mechanics, not ResourceTypes | +| Routing | `RouteTreeEngine`/`DescendantRoute`/`TreeRoutePath`/`RouteAdvertisement` (tree discovery); `OperationRouter` dedup; `RemoteNodeRegistry` (full-host nodes) — all `implemented-but-unwired` | Not scope of Zone control types; ZoneLink establishes point-to-point connection, not tree route advertisements | +| Auth/admission | Coarse `PeerRole::{Admin,Launcher,HostShutdown}` per-connection; `DaemonAccessPolicyRole::RealmAdmin` | Native RBAC: Role resource (rule table) + RoleBinding resource (subject binding); per-operation rather than per-connection | + +### 16.2 Exhaustive symbol evidence matrix + +Evidence classes: +- `implemented-and-reachable` — in live call path, not gated by dead-code allow +- `implemented-but-unwired` — compiles and is tested but not called from live daemon/CLI code +- `generated-or-eval-contract` — Nix emitter or generated JSON artifact; consumed at runtime +- `test-only-or-preview` — only in test harnesses + +#### Identity and path types + +| Symbol | File | Evidence class | ADR-0046 mapping | +| --- | --- | --- | --- | +| `RealmId` (label-shaped, `is_label()`) | `d2b-realm-core/src/ids.rs:211` | `implemented-and-reachable` | Zone `metadata.name`; ResourceName shape | +| `WorkloadId` (label-shaped) | `d2b-realm-core/src/ids.rs:225` | `implemented-and-reachable` | `metadata.name` for Guest or Host resources; `WorkloadProviderKind::LocalVm`/`QemuMedia` → Guest `metadata.name`; **`WorkloadProviderKind::UnsafeLocal` → Host `metadata.name` (user-only, `defaultDomain=user`, reconciled by `Provider/system-core`)** — NOT a Guest; `WorkloadProviderKind::ProviderManaged` → Guest or Host per provider semantics | +| `ProviderId` (label-shaped) | `d2b-realm-core/src/ids.rs:232` | `implemented-and-reachable` | Provider `metadata.name` | +| `NodeId` (label-shaped) | `d2b-realm-core/src/ids.rs:218` | `implemented-and-reachable` | Host `metadata.name` | +| `LABEL_PATTERN = "^[a-z][a-z0-9-]*$"`, `is_label()`, `MAX_ID_LEN = 128`, `IdError` | `d2b-realm-core/src/ids.rs:31,76,28,54` | `implemented-and-reachable` | ResourceName validator; extracted for ResourceRef parser | +| `RealmIdentityRef` (opaque, redacts debug) | `d2b-realm-core/src/ids.rs:312` | `implemented-and-reachable` | Opaque identity ref pattern; maps to Zone key-ref in ZoneLink key-pin model | +| `RealmPath(Vec)` | `d2b-realm-core/src/realm.rs:64` | `implemented-and-reachable` | **Not** a target type; encodes hierarchy structurally (path-in-identity). ZoneLink replaces this with a stored resource. `RealmPath` depth (`MAX_REALM_LABELS=16`) maps to ZoneLink depth bound | +| `MAX_REALM_LABELS = 16` | `d2b-realm-core/src/realm.rs:67` | `implemented-and-reachable` | ZoneLink tree depth bound | +| `RealmControllerPlacement::{HostLocal, GatewayVm, CloudFullHost, ProviderController, ProviderAgent}` | `d2b-realm-core/src/realm.rs:26` | `implemented-and-reachable` | **Partially reused**: these placement labels map to Provider component `placement` descriptor semantics, but the target is a Provider resource field, not a realm process property. Not a 1:1 rename. | +| `EntrypointMode::{HostResident, GatewayBacked}` | `d2b-realm-core/src/realm.rs:12` | `implemented-and-reachable` | Informs ZoneLink transport binding (host-local socket vs. remote transport); maps to the resolved `transportProviderRef` selector semantics | +| `RealmTarget { workload: WorkloadId, realm: RealmPath }` | `d2b-realm-core/src/target.rs:39` | `implemented-and-reachable` (CLI routing path) | `RealmTarget` is the current addressable unit (`..d2b`); maps to `ResourceRef` (target scoped to Zone); NOT a Zone identity. `WorkloadTarget = RealmTarget` alias in `d2b-core/src/workload_identity.rs:55` | +| `TargetName`, `RealmTargetParser`, `RealmTargetParseError` | `d2b-realm-core/src/target.rs:122,373,280` | `implemented-and-reachable` | Maps to ResourceRef `/` parser in ADR046-identities-001 | +| `LegacyNodeQualifiedTarget` | `d2b-realm-core/src/target.rs:242` | `implemented-and-reachable` | Migration artifact; removed after target format normalizes to ResourceRef | + +#### Routing and access resolution types + +| Symbol | File | Evidence class | ADR-0046 mapping | +| --- | --- | --- | --- | +| `RealmTreeEdge { parent: RealmPath, child: RealmPath }` | `d2b-realm-core/src/routing.rs:543` | `implemented-but-unwired` | Tree edge between two zones; ZoneLink resource replaces this structural edge with a stored resource object | +| `DescendantRoute`, `TreeRoutePath`, `TreeRouteHop` | `d2b-realm-core/src/routing.rs:581,854,798` | `implemented-but-unwired` | Multi-hop tree routing metadata; ADR 0046 replaces with ZoneLink cursor model (no multi-hop discovery in Zone control types) | +| `RouteAdvertisement`, `RouteAdvertisementEnvelope`, `RouteNamespaceAllocation` | `d2b-realm-core/src/routing.rs:609,697,716` | `implemented-but-unwired` | Route advertisement for peer discovery; no equivalent in Zone control types (ZoneLink uses ComponentSession, not route advertisements) | +| `RouteRealmClass::{LocalRoot, StaticConfigured, HostLocalPeer, GatewayBacked, CloudFullHost, ProviderManaged, EphemeralDiscovered, Unknown}` | `d2b-realm-core/src/routing.rs:1170` | `implemented-but-unwired` | Telemetry/audit realm class. Maps to ZoneLink low-cardinality transport class labels in OTEL metrics (§13); NOT a stored resource field | +| `RoutePlacementClass` | `d2b-realm-core/src/routing.rs:1201` | `implemented-but-unwired` | Low-cardinality placement label for telemetry; maps to Provider component placement OTEL label | +| `RouteAuditLabels`, `RouteTelemetryLabels`, `RouteTelemetrySample`, `RouteTelemetryBatch` | `d2b-realm-core/src/routing.rs:1244,1300,1313,1322` | `implemented-but-unwired` | Audit/telemetry shape precedent; maps to ZoneLink OTEL metrics label constraints in §13.2 | +| `RouteTreeEngine`, `RouteEngineEvent`, `RouteAdvertisementAdmission`, `DiscoveryQueueDecision`, `RoutePruneReport`, `DirectShortcutAuthorizationRequest` | `d2b-realm-core/src/route_engine.rs:161,30,37,51,70,78` | `implemented-but-unwired` | Live routing engine with dedup, pruning, shortcut auth; exported from `d2b_realm_core::lib.rs:122` but has **zero call sites in live daemon or CLI code** — only used in tests within `route_engine.rs`. ZoneLink does not use route advertisements; the `decide_route` algorithm is an unwired precedent only | +| `RealmEntrypointTable`, `RealmEntrypoint`, `DispatchTarget::{HostResident, GatewayBacked}`, `ResolveError` | `d2b-realm-router/src/target_resolver.rs:103,25,54` | `implemented-and-reachable` (CLI only) | The CLI (`d2b/src/lib.rs:5239`, `target_routing.rs:430`) loads the realm entrypoints file from `/run/current-system/sw/share/d2b/realm-entrypoints.json` (Nix-generated at `nixos-modules/host-daemon.nix:385`) and resolves targets. The daemon (`d2bd`) only uses it in `realm_stubs.rs` (dead code). ZoneLink replaces the entrypoint table with stored resource + ComponentSession transport | +| `OperationRouter`, `RouteDecision`, `OperationRoutePlan`, `ReconcilableLease` | `d2b-realm-router/src/lib.rs:306,95,179,636` | `implemented-but-unwired` | In `d2bd` only via `realm_stubs.rs` (`#![allow(dead_code)]`; comment: "not called from the running daemon"); `OperationRouter` dedup model maps to resource API idempotency layer | +| `RemoteNodeRegistry`, `RemoteFullHostAdapter`, `RemotePeerClient`, `RemoteDispatchOutcome` | `d2b-realm-router/src/remote_node.rs:252,644,608,627` | `implemented-but-unwired` | Remote full-host node management; in d2bd only via realm_stubs dead-code. No Zone control type equivalent; future Host/node resource design | +| `RealmAccessTargetInput`, `RealmAccessAliasBinding`, `RealmAccessResolverRequest`, `RealmAccessResolverResponse`, `RealmAccessBinding`, `RealmTransportBinding`, `HostLocalPeerCredentialSemantics`, `AccessBindingRef` | `d2b-realm-core/src/access.rs:170,285,322,542,597,569,364,96` | `implemented-and-reachable` (CLI path) | Used actively in `d2b/src/target_routing.rs:21–26` which is called from live CLI routing. `RealmTransportBinding::{LocalUnixSocket,RemoteRealmTransport,ProviderRealmTransport}` maps to the resolved ZoneLink transport binding variants | +| `realm_access_resolver` module (`resolve_local_root_realm_access`, `host_local_capability_preflight`, `realm_controllers_config_generation`) | `d2bd/src/realm_access_resolver.rs:20,159,190` | `implemented-but-unwired` | `pub mod realm_access_resolver` in `d2bd/src/lib.rs:117`; no call sites in live daemon code (only within the module itself). Capability preflight model maps to Zone/ZoneLink capability surface in §13 | +| `realm_stubs` module (`ApiFrontend`, `ApiService`, `TargetResolver`, `PeerOperationRouter`, `ProviderExecutor`, `PeerDaemon`, `DaemonMode`, `SharedRouter`) | `d2bd/src/realm_stubs.rs` | `implemented-but-unwired` | Explicitly `#![allow(dead_code)]`; module comment: "not called from the running daemon — the local CLI→daemon path is unchanged (zero behavior change)". Seam for future gateway work. Maps to: `ApiFrontend`→ComponentSession framing; `ApiService`→Zone runtime API surface; `TargetResolver`→ZoneLink access | + +#### Session types + +| Symbol | File | Evidence class | ADR-0046 mapping | +| --- | --- | --- | --- | +| `PeerSession` | `d2b-realm-router/src/session.rs` | `implemented-but-unwired` | No call sites in live d2bd/d2b code (d2bd imports `d2b_realm_router` only for `realm_stubs` dead code; d2b CLI imports only `RealmEntrypointTable` from router). Noise handshake/session framing model; reuse source for ComponentSession | +| `SecurePeerSession` (Noise-based) | `d2b-realm-router/src/secure_session.rs` | `implemented-but-unwired` | Same reachability as `PeerSession`; Noise KK handshake implementation; **design precedent** for ComponentSession Noise KK (copy/adapt from main `a1cc0b2d`) | +| `MuxSession` | `d2b-realm-router/src/mux_session.rs` | `implemented-but-unwired` | Stream multiplexing over a session; maps to ZoneLink named-stream framing model (copy/adapt from main `a1cc0b2d` d2b-bus) | +| `SessionLifecycle`, `SessionPhase` | `d2b-realm-router/src/session_lifecycle.rs` | `implemented-but-unwired` | FSM phases (current evidence: `Unknown`, `Connecting`, `Established`, `Failed`); design precedent for ZoneLink session reconnect loop; `Connecting`/`Established` are current baseline FSM states that map to ZoneLink `status.connected` detail field, not to `status.phase`; ZoneLink `status.phase` uses common Resource phases (`Pending`/`Ready`/`Degraded`/`Failed`/`Unknown`) | + +#### Identity store and enrollment types + +| Symbol | File | Evidence class | ADR-0046 mapping | +| --- | --- | --- | --- | +| `RealmIdentityStore`, `EnrollmentRecord`, `ChildKeyPin`, `ControllerGenerationId`, `RevocationList`, `RecoveryProcedure` | `d2b-realm-core/src/identity_store.rs:141,207,177,182,197,202` | `implemented-and-reachable` (d2b-realm-core is a live dependency) | Zone identity anchoring and child enrollment model. The identity store tracks enrolled realm key-pins, controller generations, and revocations. ZoneLink child key-pin binding adapts `ChildKeyPin` + `EnrollmentRecord`. **Not** a ResourceType; internal Zone runtime mechanism | +| `RealmIdentityConfigJson`, `RealmIdentityConfigEntry`, `RealmIdentityConfigInvariants`, `RealmIdentityConfigError` | `d2b-realm-core/src/identity_config.rs:19,69,89,124` | `implemented-and-reachable` | Deserialized by `load_realm_identity_config()` in live daemon startup (`d2bd/src/lib.rs:1425`); runtime trust sessions logged as "inert". Maps to Zone identity key-ref fields loaded from bundle | +| `RealmIdentityConfigRuntimeState::MetadataOnly` | `d2b-realm-core/src/identity_config.rs:63` | `implemented-and-reachable` | `runtimeState = "metadata-only"` invariant retained; all identity config validated as non-secret at startup | + +#### Realm controller config types (generated-and-consumed) + +| Symbol | File | Evidence class | ADR-0046 mapping | +| --- | --- | --- | --- | +| `RealmControllersJson`, `RealmControllerConfig`, `RealmControllerPlacement`, `RealmControllerLocalRuntime`, `RealmControllerLocalWorkload`, `RealmAllocatorBinding`, `RealmDaemonConfig`, `RealmBrokerConfig` | `d2b-core/src/realm_controller_config.rs:144,190,256,278,434,541,497,511` | `implemented-and-reachable` | Loaded in live daemon startup at `d2bd/src/lib.rs:1408` and in serve path at line `16741`; `WorkloadTargetIndex::build_from_controllers` is called live. Contains per-realm socket/state paths, placement, providers, workloads. Maps to: Zone self resource (the realm metadata row); ZoneLink (per-realm parent socket ref); Provider (per-realm provider config rows); NOT replaced by a single file — replaced by resource store entries | +| `WorkloadTargetIndex`, `TargetResolution`, `TargetResolutionError` | `d2bd/src/workload_target_index.rs:93,28,56` | `implemented-and-reachable` | Built from `RealmControllersJson` in live serve path (`d2bd/src/lib.rs:16745`); resolves `..d2b` to VM name. Maps to resource API `resourceRef` lookup table in ADR 0046 API layer | + +#### Allocator types + +| Symbol | File | Evidence class | ADR-0046 mapping | +| --- | --- | --- | --- | +| `AllocatorLease`, `LeaseAllocationRequest`, `AllocatorLeaseState`, `LeaseOwner`, `AllocatorReasonCode`, `HostResourceKind`, `ResourceShareMode` | `d2b-realm-core/src/allocator.rs:274,151,241,71,310,41,84` | `implemented-and-reachable` | Core allocator data model. Leases typed by `HostResourceKind` (`HostFilePartition`, `CgroupSubtree`, `NftablesPartition`, `Bridge`, `NamespaceBoundary`). Maps to Zone runtime internal resource management; these are **mechanisms, not ResourceTypes** | +| `LocalRootAllocatorEngine` | `d2b-realm-core/src/allocator_engine.rs:332` | `implemented-and-reachable` | Test-facing fake engine; drives idempotency/reconciliation proof. Maps to Zone runtime allocator internals; NOT a ResourceType or Zone store table | +| `AllocatorEngineDecision`, `AllocatorEngineOutcome`, `AllocatorAllocationDecision`, `AllocatorReconciliationAction` | `d2b-realm-core/src/allocator_engine.rs:22,78,152,170` | `implemented-and-reachable` | Allocation decision types; maps to Zone runtime startup allocation invariants | + +#### Provider trait types + +| Symbol | File | Evidence class | ADR-0046 mapping | +| --- | --- | --- | --- | +| `HostSubstrateProvider`, `RuntimeProvider`, `WorkloadProvider`, `DurableExecutionProvider`, `InfrastructureProvider`, `NodeProvider`, `TransportProvider`, `RelayProvider`, `CredentialProvider`, `PersistentShellProvider`, `DisplayProvider` traits | `d2b-realm-provider/src/provider.rs:31,41,65,89,268,333,197,324,301,137,169` | `implemented-and-reachable` (`WorkloadProvider` is imported in live `d2bd/src/lib.rs:93` for ACA) | Provider trait surface. Each trait axis maps to a Provider component service definition; `WorkloadProvider` is the only one with a live call path (ACA gateway). Remaining traits are reachable via `d2b-realm-provider` dependency but have no live non-ACA call sites | +| `RuntimeCapabilitySet`, `WorkloadCapabilitySet`, `NodeCapabilitySet` | `d2b-realm-provider/src/capabilities.rs` | `implemented-and-reachable` | Maps to Provider `spec.components[].supportedCapabilities` | +| `workload_lists_and_advertises()`, `display_fails_closed_when_unsupported()` | `d2b-realm-provider/src/conformance.rs` | `implemented-and-reachable` | Provider conformance check behavior; reused in Provider trust check (ADR046-zone-control-003) | +| `ProviderError`, `ErrorKind`, `RetryHint`, `ProviderDiagnostic` | `d2b-realm-provider/src/error.rs` | `implemented-and-reachable` | Provider lifecycle error/retry schema; maps to Provider `status.conditions[].message` and error bounds | + +#### Process and supervisor types + +| Symbol | File | Evidence class | ADR-0046 mapping | +| --- | --- | --- | --- | +| `ProcessRole::{CloudHypervisorRunner, Virtiofsd, Swtpm, GpuRenderNode, Audio, Video, QemuMediaRunner, VsockRelay, OtelHostBridge, Usbip}` | `d2b-core/src/processes.rs:194` | `implemented-and-reachable` | Each `ProcessRole` variant maps to a declared Process or EphemeralProcess resource under a Provider component | +| `VmProcessDag`, `ProcessNode` | `d2b-core/src/processes.rs:20,57` | `implemented-and-reachable` | Provider component DAG shape; maps to Process/EphemeralProcess dependency graph | +| `DagExecutor`, `NodeRunner`, `topo_sort()`, `NodeOutcome`, `NodeBudget` | `d2bd/src/supervisor/dag.rs:302,181,222,38,148` | `implemented-and-reachable` | DAG lifecycle executor; maps to Process/EphemeralProcess launch ordering in Provider component lifecycle | + +#### Authorization and admission types + +| Symbol | File | Evidence class | ADR-0046 mapping | +| --- | --- | --- | --- | +| `PeerRole::{Admin, Launcher, HostShutdown}`, `PeerIdentity`, `authorize_peer()`, `verb_requires_admin()`, `verb_allowed_for_host_shutdown()` | `d2bd/src/admission.rs:16,10,37,123,157` | `implemented-and-reachable` | **Current bootstrap auth system**. Called in live daemon request path. Maps to: `PeerRole::Admin`→`system-core` subject; `PeerRole::Launcher`→`system-minijail` subject; `PeerRole::HostShutdown`→narrow bootstrap exception; verb tables → Role rule verb enum | +| `LocalUnixAllowlistRole::{Admin, Launcher, Denied}`, `DaemonAccessPolicyRole::RealmAdmin`, `DaemonAccessDecision::{Authorized,Denied}`, `DaemonAccessAdmissionSource`, `MappedDaemonAccessPrincipal::{LocalAdmin,LocalLauncher,LocalDenied}`, `map_local_unix_daemon_access()`, `map_remote_daemon_access()` | `d2b-daemon-access/src/lib.rs:361,370,390,415,468,487` | `implemented-and-reachable` | **Current local auth surface**. `LocalUnixAllowlistRole` determines `PeerRole` by group membership (`Admin`→`LocalAdmin`, `Launcher`→`LocalLauncher`). Maps to Role/RoleBinding RBAC engine (§6–§9). `DaemonAccessPolicyRole::RealmAdmin` maps to a future Role grant for realm operators; `MappedDaemonAccessPrincipal` maps to RoleBinding subject identity model | + +#### Nix authoring and generated artifacts + +| Symbol | File | Evidence class | ADR-0046 mapping | +| --- | --- | --- | --- | +| `d2b.realms..*`, `providerType` submodule, `providerKind = "^[a-z][a-z0-9-]*$"`, `placementProvider`, `providerSpecificPlacement` | `nixos-modules/options-realms.nix:26,39,55,65,170` | `generated-or-eval-contract` | Authoring interface for realm/provider declarations. Label regex identical to `LABEL_PATTERN` in `ids.rs`. Adapted to `d2b.zones..providers.*` in ADR046-zone-control-007 | +| `d2b.realms..workloads.*`, `kind`, `placement`, `legacyVmName` | `nixos-modules/options-realms-workloads.nix` | `generated-or-eval-contract` | Workload authoring submodule; `kind = "local-vm"` / `"qemu-media"` → Guest resource Nix options; **`kind = "unsafe-local"` → Host resource Nix options** (`defaultDomain=user`, `allowedDomains=[user]`); `kind = "provider-placeholder"` → Guest/Host per provider semantics | +| `d2b.realms..network.*` | `nixos-modules/options-realms-network.nix` | `generated-or-eval-contract` | Network/bridge authoring; maps to Network resource (not Zone control types) | +| `realm-controllers.json` (`RealmControllersJson`), `schemaVersion = "v2"`, `runtimeState = "metadata-only"`, per-realm rows | `nixos-modules/realm-controller-config-json.nix` (emitter); `d2b-core/src/realm_controller_config.rs:144` (deserializer) | `generated-or-eval-contract` (Nix); `implemented-and-reachable` (consumption in daemon) | **Loaded live in daemon** at `d2bd/src/lib.rs:1408,16741`. Runtime routing "remains inert" but `WorkloadTargetIndex` is built from it. Maps to: Zone self resource (identity/path row); ZoneLink (parent socket ref); Provider (provider config rows). The bundle artifact is **replaced** by resource store entries; until then, the schema is the live authority | +| `realm-identity.json` (`RealmIdentityConfigJson`), identity refs, fingerprints | `nixos-modules/realm-identity-config-json.nix` (emitter); `d2b-realm-core/src/identity_config.rs:19` (deserializer) | `generated-or-eval-contract` (Nix); `implemented-and-reachable` (consumption in daemon) | Loaded live at `d2bd/src/lib.rs:1425`; trust sessions "remain inert". Maps to Zone runtime identity key-ref state and store metadata, not Zone.spec | +| `allocator.json` resource request rows (per-realm: cgroup, nftables, bridges, socket paths), `LocalRootAllocatorEngine` | `nixos-modules/allocator-json.nix` (emitter); `d2b-realm-core/src/allocator_engine.rs:332` (engine) | `generated-or-eval-contract` (Nix); `implemented-and-reachable` (engine) | Allocator resource requests are generated per realm from `d2b.realms.*` Nix config and consumed by the allocator at runtime. Maps to Zone runtime startup resource claiming. **Mechanism, not a ResourceType** | +| `realm-entrypoints.json`, loaded from `/run/current-system/sw/share/d2b/realm-entrypoints.json` | `nixos-modules/host-daemon.nix:385` (emitter); `d2b/src/lib.rs:5239` (loader) | `generated-or-eval-contract` (Nix); `implemented-and-reachable` (CLI consumption) | Loaded by `d2b` CLI in live routing path for realm-qualified targets. Maps to ZoneLink access binding (CLI consumes it to find realm socket; replaced by ZoneLink resource + ComponentSession lookup) | + +#### Workload posture and unsafe-local types + +> These symbols ground the `unsafe-local` → **Host** ResourceType mapping. +> `WorkloadProviderKind::UnsafeLocal` / `IsolationPosture::UnsafeLocal` do NOT +> map to a Guest resource or a Provider resource; they map exclusively to a +> user-domain Host resource reconciled by `Provider/system-core`. + +| Symbol | File | Evidence class | ADR-0046 mapping | +| --- | --- | --- | --- | +| `WorkloadProviderKind::{LocalVm, QemuMedia, ProviderManaged, UnsafeLocal}` | `d2b-realm-core/src/workload.rs:13` | `implemented-and-reachable` | `LocalVm`/`QemuMedia` → Guest resource kind; **`UnsafeLocal` → Host resource (`defaultDomain=user`, `allowedDomains=[user]`)** — NOT a Provider, NOT a Guest; `ProviderManaged` → Guest or Host per provider semantics | +| `IsolationPosture::{VirtualMachine, ProviderManaged, UnsafeLocal}` | `d2b-realm-core/src/workload.rs:27` | `implemented-and-reachable` | `VirtualMachine` → Guest isolation; **`UnsafeLocal` → Host `spec.isolationPosture=no-isolation`**; no-isolation posture is preserved as a mandatory warning in Host status conditions, CLI/UI output, and audit records (`isolation=no-isolation`); it is never emitted as an OTEL metric label or span attribute | +| `WorkloadExecutionPosture { isolation, environment, display_environment, execution_identity, session_persistence }` | `d2b-realm-core/src/workload.rs:83` | `implemented-and-reachable` | Typed posture carried by launcher entries for CLI/desktop display. Canonical unsafe-local tuple at line 206–211: `isolation=unsafe-local`, `environment=systemd-user-manager-ambient`, `displayEnvironment=wayland-proxy-only`, `executionIdentity=authenticated-requester-uid`, `sessionPersistence=user-manager-lifetime`. Tuple maps to Host `status.observedPosture` and mandatory CLI/UI warnings; posture details appear in audit record body but are not OTEL labels | +| `EnvironmentPosture::{RuntimeManaged, SystemdUserManagerAmbient}` | `d2b-realm-core/src/workload.rs:39` | `implemented-and-reachable` | `SystemdUserManagerAmbient` is the unsafe-local environment posture; maps to Host `status.observedPosture.environment` field and audit record body; not emitted as an OTEL label | +| `ExecutionIdentityPosture::{WorkloadUser, ProviderManaged, AuthenticatedRequesterUid}` | `d2b-realm-core/src/workload.rs:61` | `implemented-and-reachable` | `AuthenticatedRequesterUid` is the unsafe-local identity posture; maps to Host `spec.defaultUserRef=User/` and audit label `execution_identity=authenticated-requester-uid` | +| `SessionPersistencePosture::{RuntimeManaged, UserManagerLifetime}` | `d2b-realm-core/src/workload.rs:73` | `implemented-and-reachable` | `UserManagerLifetime` is the unsafe-local session posture; maps to Host status field noting session lifetime bound to the systemd user manager | +| `UnsafeLocalWorkloadsJson`, `UnsafeLocalWorkload`, `UnsafeLocalLauncherItem::{Exec, Shell}`, `UnsafeLocalExecItem`, `UnsafeLocalShellItem`, `UnsafeLocalShellPolicy` | `d2b-core/src/unsafe_local_workloads.rs:16,47,106,129,141,150` | `implemented-and-reachable` (consumed by `d2b-core/src/bundle_resolver.rs:85,106`) | Private configured-item contract loaded via bundle resolver. `UnsafeLocalWorkload.identity` → Host `metadata.name` + `spec.defaultUserRef`; `UnsafeLocalShellPolicy.{defaultName, maxSessions}` → Host `spec.shellPolicy`; `UnsafeLocalExecItem` → Host launcher item contract. Constants: `MAX_UNSAFE_LOCAL_WORKLOADS=256`, `MAX_LAUNCHER_ITEMS_PER_WORKLOAD=64`, `MAX_UNSAFE_LOCAL_SHELL_SESSIONS=64` → Host cardinality bounds | +| `HelperRegistry`, `bind_helper_socket`, `dispatch_launch`, `HelperReply`, `HelperSnapshot`, `active_generation()` | `d2bd/src/unsafe_local_helper.rs:41,62,149,167,176,208,221` | `implemented-and-reachable` (live in `d2bd/src/lib.rs:1346–1468`) | Per-uid launch broker for unsafe-local helper sessions, bound at daemon startup (`d2bd/src/lib.rs:1356`). Maps to Zone runtime Host/Process broker: `dispatch_launch` → Process launch request; `HelperRegistry.allowed_uids` → Host subject UID allowlist derived from `spec.defaultUserRef` | +| `d2b.realms..policy.allowUnsafeLocal` | `nixos-modules/options-realms.nix:346` | `generated-or-eval-contract` | Gate option that permits `kind = "unsafe-local"` workloads in a realm; assertion at `nixos-modules/assertions.nix:730` blocks unsafe-local without this flag. Maps to a separate Host admission gate for user-domain unsafe-local resources, not Zone.spec | +| `kind = "unsafe-local"` enum value, doc "Host-user process runtime with no isolation boundary" | `nixos-modules/options-realms-workloads.nix:221,233` | `generated-or-eval-contract` | Nix workload `kind` enum value for unsafe-local Host resources. Maps to Host resource with `spec.defaultDomain=user` authored via `d2b.zones..resources. = { type = "Host"; spec = { ... }; };` (ADR046-zone-control-008) | +| Assertion: `!unsafeLocal \|\| realm.policy.allowUnsafeLocal` | `nixos-modules/assertions.nix:730` | `generated-or-eval-contract` | Eval-time gate; maps to Host admission enforcement for unsafe-local creation outside Zone.spec | + +### 16.3 Required delta + +None of the following exist in baseline: + +- Zone, ZoneLink, Provider, Role, RoleBinding resource schemas (Rust structs + JSON schemas) +- redb store physical tables for all seven types +- Core-controller handlers for all seven types +- Native RBAC engine (Role index + RoleBinding evaluation) +- Bootstrap authorization (compiled constant policy superseded by stored RBAC) +- `d2b.zones.*` Nix options and resource compiler +- Audit/OTEL instrumentation per §13 + +### 16.4 Reuse path summary + +| Baseline symbol | Reuse action | Destination | +| --- | --- | --- | +| `is_label()`, `LABEL_PATTERN`, `MAX_ID_LEN`, `IdError` | extract | `d2b-contracts/src/v3/resource_ref.rs` ResourceName validator | +| `RealmControllerPlacement` label set | adapt (semantics change: process placement → component placement) | Provider `spec.components[].placement` field enum | +| `EntrypointMode::{HostResident,GatewayBacked}` | adapt | ZoneLink transport binding selector | +| `RealmTransportBinding::{LocalUnixSocket,RemoteRealmTransport,ProviderRealmTransport}` | adapt | ZoneLink `spec.transportProviderRef` + transport settings variants | +| `SecurePeerSession` (Noise KK) | copy/adapt via main `a1cc0b2d` | ComponentSession Noise KK handshake (ADR046-session-001) | +| `SessionLifecycle`, `SessionPhase` | adapt | ZoneLink session reconnect loop and connection detail fields (`status.connected`, `status.lastConnectedAt`); `Connecting`/`Established` current evidence phases do not become `ZoneLink.status.phase` values | +| `RouteRealmClass`/`RoutePlacementClass` label strings | reuse label values | ZoneLink OTEL telemetry labels (§13.2) | +| `workload_lists_and_advertises()`, `display_fails_closed_when_unsupported()` | adapt | Provider trust/conformance check (ADR046-zone-control-003) | +| `RuntimeCapabilitySet`, `WorkloadCapabilitySet`, `NodeCapabilitySet` | adapt | Provider component `supportedCapabilities` fields | +| `ProviderError`, `ErrorKind`, `RetryHint` | adapt | Provider `status.conditions[].message` + error bounds | +| `ProcessRole` variants | adapt (each → Process/EphemeralProcess resource `spec.role`) | Provider component type identifiers | +| `authorize_peer()` verb table, `PeerRole` two-role model | adapt | Bootstrap authorization constant policy (ADR046-zone-control-006) | +| `map_local_unix_daemon_access()` SO_PEERCRED derivation | adapt | Role/RoleBinding subject identity derivation | +| `providerType` submodule, label regex, `placement` option | adapt | `d2b.zones..providers.*` Nix option submodule | +| `WorkloadExecutionPosture` unsafe-local posture tuple | adapt | Host `status.observedPosture`; mandatory CLI/UI posture warnings; audit record body (no OTEL posture labels) | +| `UnsafeLocalShellPolicy.{defaultName, maxSessions}`, `MAX_UNSAFE_LOCAL_SHELL_SESSIONS=64`, `MAX_UNSAFE_LOCAL_WORKLOADS=256` | adapt | Host `spec.shellPolicy` bounds and per-Zone Host cardinality bounds | +| `HelperRegistry.dispatch_launch`, `bind_helper_socket`, `allowed_uids` | adapt | Zone runtime Host process launch broker; subject UID allowlist derived from `spec.defaultUserRef` | +| `d2b.realms..policy.allowUnsafeLocal` option | adapt | `d2b.zones..resources.*` Host ResourceType admission gate (separate opt-in required before `type = "Host"` with `defaultDomain=user` is permitted) | + +### 16.5 Replacement and deletion + +| Baseline artifact | Replacement trigger | Retention period | +| --- | --- | --- | +| `realm_stubs.rs` dead-code module | ADR046-session-001 ComponentSession integration | Until gateway-mode work integrates live paths | +| `realm_access_resolver.rs` unwired module | ZoneLink resource + ComponentSession replaces entrypoint-table resolution | Same wave as ADR046-zone-control-002 | +| `PeerRole` coarse per-connection auth (`d2bd/admission.rs`) | Role/RoleBinding RBAC engine covers all verb gates | ADR046-zone-control-004/006 complete | +| `d2b-realm-provider` trait crate (all traits) | Provider resource + ComponentSession service definitions | ADR046-provider-001 integration complete | +| `realm-controllers.json` + `realm-identity.json` bundle artifacts | Zone self resource + ZoneLink resources in store | ADR046-zone-control-001/002 integration + Nix compiler (ADR046-zone-control-007) | +| `options-realms.nix`, `options-realms-workloads.nix`, `options-realms-network.nix` | `d2b.zones.*` Nix options | Purge wave (after all consumers migrate) | +| `realm-entrypoints.json` CLI config | ZoneLink access resolution via resource API | ADR046-zone-control-002 + CLI routing update | +| `unsafe-local-workloads.json` bundle artifact (`UnsafeLocalWorkloadsJson`) | Host resource store entries + Process Providers | ADR046-zone-control-008 complete | +| `d2bd/src/unsafe_local_helper.rs` `HelperRegistry` / `bind_helper_socket` | Zone runtime Host/Process broker | ADR046-zone-control-008 integration | +| `kind = "unsafe-local"` Nix enum value in `options-realms-workloads.nix` | `d2b.zones..resources. = { type = "Host"; spec = { defaultDomain = "user"; ... }; }` authoring | Purge wave (with full `options-realms*.nix` removal) | + +--- + +## 17. Implementation work items + +### ADR046-zone-control-001 + +| Field | Value | +| --- | --- | +| Work item ID | `ADR046-zone-control-001` | +| Dependency/owner | ADR046-object-001 (resource envelope); ADR046-store-001 (redb store); ADR046-identities-001 (types) | +| Current source | `packages/d2b-realm-core/src/ids.rs` (`RealmId`, `LABEL_PATTERN = "^[a-z][a-z0-9-]*$"`, `is_label()`, `MAX_ID_LEN = 128`, `IdError` — `implemented-and-reachable`, baseline `b5ddbed6`); `packages/d2b-realm-core/src/realm.rs` (`RealmPath`, `MAX_REALM_LABELS = 16`, `RealmControllerPlacement`, `EntrypointMode` — `implemented-and-reachable`, baseline `b5ddbed6`); Zone resource schema: `ADR-only` | +| Reuse action | extract and adapt (`RealmId`→`ResourceName` validators, `RealmPath` depth bound→ZoneLink depth, `RealmControllerPlacement`→Provider component placement); new (Zone resource schema/store table/handler) | +| Destination | `packages/d2b-contracts/src/v3/zone.rs`; `packages/d2b-core-controller/src/zone.rs` | +| Detailed design | Zone ResourceType schema with `spec = {}`; self-resource enforcement; store_meta binding checks; phase/conditions; cardinality-1 enforcement; `zone-drain` finalizer; Nix Zone options; canonical JSON schema | +| Integration | Zone runtime open/upgrade verifies `Zone/` self resource; configuration publication handler creates/reconciles Zone; resource API rejects cross-Zone refs | +| Data migration | Destructive d2b 3.0 reset; no v2/Realm Zone resource import | +| Validation | `zone-self-resource-enforced`, `zone-uid-mismatch-quarantine`, `zone-name-mismatch-rejected`, `zone-cardinality-one`, `zone-cross-zone-ref-rejected`, `zone-owner-rejected`, `zone-deletion-only-on-drain` | +| Removal proof | `d2b-realm-core` Realm struct removed only after Zone resource integration is live in Zone runtime | + +### ADR046-zone-control-002 + +| Field | Value | +| --- | --- | +| Work item ID | `ADR046-zone-control-002` | +| Dependency/owner | ADR046-zone-control-001; ADR046-session-001 (ComponentSession) | +| Current source | `packages/d2b-realm-router/src/session.rs` (`PeerSession` — `implemented-but-unwired`, baseline `b5ddbed6`; no live call sites in d2bd or d2b — only reachable through `realm_stubs.rs` dead code in d2bd); `packages/d2b-realm-router/src/secure_session.rs` (`SecurePeerSession`, Noise-based — `implemented-but-unwired`); `packages/d2b-realm-router/src/mux_session.rs` (`MuxSession` — `implemented-but-unwired`); `packages/d2b-realm-router/src/session_lifecycle.rs` (`SessionLifecycle`, `SessionPhase` — `implemented-but-unwired`); `packages/d2b-realm-core/src/route_engine.rs` (`RouteTreeEngine`, `admit_advertisement()`, `decide_route()`, `RoutePruneReport` — `implemented-but-unwired`; exported from `d2b_realm_core::lib.rs:122` but zero call sites in live daemon or CLI code, tests only); `packages/d2b-realm-core/src/identity_store.rs` (`RealmIdentityStore`, `EnrollmentRecord`, `ChildKeyPin` — `implemented-and-reachable`); `packages/d2b-realm-core/src/realm.rs` (`RealmPath`, `MAX_REALM_LABELS = 16` — `implemented-and-reachable`); `packages/d2b-realm-core/src/access.rs` (`RealmTransportBinding::{LocalUnixSocket,RemoteRealmTransport,ProviderRealmTransport}` — `implemented-and-reachable` in CLI routing path); ZoneLink resource schema and cursor tables: `ADR-only` | +| Reuse source | main `a1cc0b2d`: `packages/d2b-session/src/lifecycle.rs`, `d2b-session-unix/src/adapter.rs` for reconnect/transport precedents | +| Reuse action | extract and adapt (`SecurePeerSession` Noise model → ComponentSession Noise KK; `SessionLifecycle`/`SessionPhase` → ZoneLink session reconnect loop and connection detail fields; `Connecting`/`Established` current evidence phases drive `status.connected` and `status.phase` transitions to `Pending`/`Ready`, not direct phase values; `RouteTreeEngine.decide_route()` → cursor tracking; `RealmIdentityStore` enrollment → ZoneLink child key-pin) | +| Destination | `packages/d2b-contracts/src/v3/zone_link.rs`; `packages/d2b-core-controller/src/zone_link.rs` | +| Detailed design | ZoneLink schema with resolved `spec.transportProviderRef`, `spec.transportSettings`, and `spec.transportCredentials` fields; cursor tracking; reconnect loop with exponential backoff; local intent queue (max 256 entries); child UID change detection; drain finalizer; Nix ZoneLink options | +| Integration | core-controller zone_link handler; redb `zone_link_cursors` table; d2b-bus transport resolver; ComponentSession lifecycle | +| Data migration | Destructive reset; no v2 Realm peer migration | +| Validation | `zonelink-reconnect-child-uid-change`, `zonelink-disconnect-unknown-phase`, `zonelink-intent-queue-limit`, `zonelink-disabled-no-reconnect`, `zonelink-child-auth-denied-failed`, `zonelink-drain-closes-session` | +| Removal proof | `realm_stubs.rs` (`ApiFrontend`, `PeerOperationRouter`, `TargetResolver`) removed after ComponentSession integration (ADR046-session-001); `realm_access_resolver.rs` module removed after ZoneLink replaces entrypoint-table resolution; gateway `PeerSession`/`SecurePeerSession` session types remain as dead code in d2b-realm-router until Provider session migration wave | + +### ADR046-zone-control-003 + +| Field | Value | +| --- | --- | +| Work item ID | `ADR046-zone-control-003` | +| Dependency/owner | ADR046-zone-control-001; ADR046-api-001; ADR046-provider-001 | +| Current source | `packages/d2b-realm-provider/src/provider.rs` (`HostSubstrateProvider`, `RuntimeProvider`, `WorkloadProvider`, `DurableExecutionProvider`, `InfrastructureProvider`, `NodeProvider` traits — `implemented-and-reachable`, baseline `b5ddbed6`); `packages/d2b-realm-provider/src/capabilities.rs` (`RuntimeCapabilitySet`, `WorkloadCapabilitySet`, `NodeCapabilitySet` — `implemented-and-reachable`); `packages/d2b-realm-provider/src/conformance.rs` (`workload_lists_and_advertises`, `display_fails_closed_when_unsupported` — `implemented-and-reachable`); `packages/d2b-realm-provider/src/error.rs` (`ProviderError`, `ErrorKind`, `RetryHint`, `ProviderDiagnostic` — `implemented-and-reachable`); `packages/d2b-realm-core/src/ids.rs` (`ProviderId`, label-shaped — `implemented-and-reachable`); `packages/d2b-core/src/processes.rs` (`ProcessRole::{CloudHypervisorRunner, Virtiofsd, Swtpm, GpuRenderNode, Audio, Video, QemuMediaRunner, VsockRelay, OtelHostBridge, Usbip}`, `VmProcessDag`, `ProcessNode` — `implemented-and-reachable`); Provider resource schema and API catalog: `ADR-only` | +| Reuse source | main `a1cc0b2d`: any `d2b-provider-toolkit` registry/descriptor patterns named in ADR046-provider-001 sub-items | +| Reuse action | extract and adapt (`workload_lists_and_advertises`/`display_fails_closed_when_unsupported` conformance behavior; `RuntimeCapabilitySet`/`WorkloadCapabilitySet`/`NodeCapabilitySet` → Provider component `supportedCapabilities`; `ProviderError`/`RetryHint` → Provider lifecycle error schema; `ProviderId` → Provider `metadata.name` validator; `ProcessRole` variants → Provider component type identifiers) | +| Destination | `packages/d2b-contracts/src/v3/provider.rs`; `packages/d2b-core-controller/src/provider_lifecycle.rs`; `packages/d2b-core-controller/src/api_catalog.rs` | +| Detailed design | Provider resource schema with all spec fields from §4.3, including resolved component bounds (max 8 controllers, 8 services, 32 worker templates, 16 ResourceTypes per controller); trust/conformance/config validation; component descriptor validation; dependency alias resolution; API binding with permission intersection; lifecycle policies; Nix Provider installation options; Provider crate layout enforcement per §4.8 (see ADR046-pkg-001) | +| Integration | Zone config publication installs Provider resources; API catalog handler binds exported ResourceTypes; Provider/system-core and Provider/system-minijail are bootstrap exceptions with pre-created records | +| Data migration | Full reset; Provider packages recompiled and re-registered per new schema | +| Validation | All §15.3 Provider tests including the resolved bounds checks | +| Removal proof | `d2b-realm-provider` trait crate removed per ADR046-provider-001 after Provider resource integration | + +### ADR046-zone-control-004 + +| Field | Value | +| --- | --- | +| Work item ID | `ADR046-zone-control-004` | +| Dependency/owner | ADR046-zone-control-001; ADR046-api-002 | +| Current source | `packages/d2bd/src/admission.rs` (`PeerRole::{Admin, Launcher, HostShutdown}`, `PeerIdentity`, `authorize_peer()`, `verb_requires_admin()`, `verb_allowed_for_host_shutdown()` — `implemented-and-reachable`, baseline `b5ddbed6`); `packages/d2b-daemon-access/src/lib.rs` (`LocalUnixAllowlistRole::{Admin, Launcher}`, `DaemonAccessPolicyRole::RealmAdmin`, `DaemonAccessDecision`, `MappedDaemonAccessPrincipal`, `map_local_unix_daemon_access()` — `implemented-and-reachable`); Role resource schema and RBAC index: `ADR-only` | +| Reuse action | extract and adapt (`verb_requires_admin()` verb table → Role verb enum; `DaemonAccessDecision` error types → Role admission errors; `MappedDaemonAccessPrincipal` → subject identity model) | +| Destination | `packages/d2b-contracts/src/v3/role.rs`; `packages/d2b-core-controller/src/authz.rs` | +| Detailed design | Role resource schema with rule schema from §5.3 and resolved bounds (32 rules, 16 resourceTypes per rule, 64 resourceNames, 32 executionRefs); verb enum; explicit wildcard enforcement; index builder; phase/conditions; `role-binding-drain` finalizer; Nix Role options; audit/OTEL instrumentation | +| Integration | Authorization evaluator (ADR046-api-002) reads Role index entries; core `authz` handler owns reconcile loop | +| Data migration | Initial Roles generated from Nix config; no v2 Role resource import | +| Validation | All §15.4 Role tests including the resolved bounds checks | +| Removal proof | `daemon-access` capability enum removed after RBAC Role engine covers all access decisions | + +### ADR046-zone-control-005 + +| Field | Value | +| --- | --- | +| Work item ID | `ADR046-zone-control-005` | +| Dependency/owner | ADR046-zone-control-004 | +| Current source | `packages/d2b-daemon-access/src/lib.rs` (`DaemonAccessDecision::Authorized { role: DaemonAccessPolicyRole }`, `DaemonAccessAdmissionSource`, `map_remote_daemon_access()` — `implemented-and-reachable`, baseline `b5ddbed6`; these implement the current coarse binding decision per-connection); RoleBinding resource schema: `ADR-only` | +| Reuse action | new (RoleBinding resource schema/store table/handler); adapt (`DaemonAccessAdmissionSource` identity fields → subject selector; `map_remote_daemon_access()` logic → subject UID-binding behavior) | +| Destination | `packages/d2b-contracts/src/v3/role_binding.rs`; `packages/d2b-core-controller/src/authz.rs` (shared with Role handler) | +| Detailed design | RoleBinding resource schema with no expiry field and max 128 subjects; subject resolution and UID binding; external principal selector; scope narrowing intersection; revocation; immediate deletion with cache invalidation; Nix RoleBinding options | +| Integration | Subject resolution uses store owner index; authorization evaluator reads combined Role+narrowing entry; revocation/update/delete flow uses the normal resource lifecycle | +| Data migration | Initial RoleBindings generated from Nix config | +| Validation | All §15.5 RoleBinding tests, including the 128-subject admission bound and no-expiry lifecycle model | +| Removal proof | Not applicable; new type | + +### ADR046-zone-control-006 + +| Field | Value | +| --- | --- | +| Work item ID | `ADR046-zone-control-006` | +| Dependency/owner | ADR046-zone-control-004, ADR046-zone-control-005; Zone runtime | +| Current source | `packages/d2bd/src/admission.rs` (`authorize_peer()`, `verb_requires_admin()`, `verb_allowed_for_host_shutdown()`, `PeerRole::{Admin, Launcher, HostShutdown}` — `implemented-and-reachable`, baseline `b5ddbed6`); `packages/d2b-daemon-access/src/lib.rs` (`LocalUnixAllowlistRole`, `DaemonAccessDecision::Authorized/Denied`, `DaemonAccessAdmissionSource`, `MappedDaemonAccessPrincipal`, `map_local_unix_daemon_access()` — `implemented-and-reachable`); `packages/d2bd/src/lib.rs` (`LoadedRealmControllersConfig`, `LoadedRealmIdentityConfig` startup state — `implemented-and-reachable`); compiled bootstrap constant policy: `ADR-only` | +| Reuse action | extract and adapt (`authorize_peer()`/`verb_requires_admin()` verb table → bootstrap constant policy verb table; `PeerRole` two-variant model → system-core/system-minijail bootstrap subjects; `map_local_unix_daemon_access()` SO_PEERCRED mapping → bootstrap subject derivation; `LoadedRealmControllersConfig` startup path → Zone runtime startup bootstrap init sequence) | +| Destination | `packages/d2b-resource-api/src/bootstrap_authz.rs`; Zone runtime startup path | +| Detailed design | Compiled bootstrap authorization as described in §9; exact subjects (system-core, system-minijail); closed verb table; non-configurable enforcement; atomic supersession after stored RBAC publishes; out-of-band reset path | +| Integration | Resource API authorization layer checks bootstrap policy before stored RBAC; supersession is triggered by first `IndexBuilt=True` event from authorization handler | +| Data migration | Bootstrap is always freshly compiled; no migration | +| Validation | All §15.6 bootstrap tests | +| Removal proof | `daemon-access` bootstrap stub removed after Zone runtime bootstrap authz integrates | + +### ADR046-zone-control-007 + +| Field | Value | +| --- | --- | +| Work item ID | `ADR046-zone-control-007` | +| Dependency/owner | ADR046-identities-002; ADR046-zone-control-001; ADR046-zone-control-002; ADR046-zone-control-003; ADR046-zone-control-004; ADR046-zone-control-005; ADR046-zone-control-009; ADR046-zone-control-010 | +| Current source | `nixos-modules/options-realms.nix` (`providerKind = "^[a-z][a-z0-9-]*$"` label regex matching `LABEL_PATTERN` in `ids.rs`; `providerType` submodule with `enable`/`kind`/`placement`/`freeformType` fields; `d2b.realms..providers.*` attrset — `generated-or-eval-contract`, baseline `b5ddbed6`); `nixos-modules/options-realms-workloads.nix` (`d2b.realms..workloads.*` submodule shape — `generated-or-eval-contract`); `d2b.zones.*` Nix options: `ADR-only` | +| Reuse action | adapt (`providerType` submodule → Provider option submodule; label regex `^[a-z][a-z0-9-]*$` retained unchanged; `placement` option → Provider component placement; `d2b.realms.*` option namespace → `d2b.zones.*` option namespace) | +| Destination | `nixos-modules/options-zones.nix`; `nixos-modules/resources-zone-control.nix`; extended `nixos-modules/index.nix` | +| Detailed design | `d2b.zones..*` Nix options for Zone/ZoneLink/Provider/Role/RoleBinding/Quota/EmergencyPolicy authoring; Nix eval-time validation of ResourceRefs, verb enums, digest format, subject types, resolved ZoneLink transport ref constraints, and the no-expiry RoleBinding model; canonical JSON serialization; generation-bound resource bundle output | +| Integration | Nix resource compiler → configuration publication handler → Zone store; bootstrap Provider records auto-generated | +| Data migration | Full reset; Nix realm options (`d2b.realms.*`) remain until purge wave | +| Validation | nix-unit vectors for each Zone control type schema; cross-field constraint tests; rendered JSON contract tests (`make test-drift`) | +| Removal proof | `nixos-modules/options-realms.nix` and related realm options removed only after Zone resource Nix integration is live | + +### ADR046-zone-control-008 + +| Field | Value | +| --- | --- | +| Work item ID | `ADR046-zone-control-008` | +| Dependency/owner | ADR046-zone-control-001 (Zone resource); ADR046-zone-control-003 (Provider/system-core installed) | +| Current source | `d2b-realm-core/src/workload.rs:13,27,83` (`WorkloadProviderKind::UnsafeLocal`, `IsolationPosture::UnsafeLocal`, `WorkloadExecutionPosture` with canonical unsafe-local tuple at lines 206–211: `isolation=unsafe-local`, `environment=systemd-user-manager-ambient`, `executionIdentity=authenticated-requester-uid`, `sessionPersistence=user-manager-lifetime` — `implemented-and-reachable`, baseline `b5ddbed6`); `d2b-core/src/unsafe_local_workloads.rs:16,47,106,150` (`UnsafeLocalWorkloadsJson`, `UnsafeLocalWorkload`, `UnsafeLocalShellPolicy`, `MAX_UNSAFE_LOCAL_SHELL_SESSIONS=64`, `MAX_UNSAFE_LOCAL_WORKLOADS=256` — `implemented-and-reachable`); `d2bd/src/unsafe_local_helper.rs` (`HelperRegistry`, `bind_helper_socket`, `dispatch_launch` — `implemented-and-reachable`, live in `d2bd/src/lib.rs:1346–1468`); `nixos-modules/options-realms.nix:346` (`policy.allowUnsafeLocal` — `generated-or-eval-contract`); `nixos-modules/options-realms-workloads.nix:221,233` (`kind = "unsafe-local"`, doc "no isolation boundary" — `generated-or-eval-contract`) | +| Reuse action | adapt (`WorkloadExecutionPosture` unsafe-local posture tuple → Host `status.observedPosture`; posture details in audit record body, not OTEL labels; `UnsafeLocalShellPolicy.{defaultName,maxSessions}` → Host `spec.shellPolicy`; `HelperRegistry.dispatch_launch` → Zone runtime Host process launch broker; `policy.allowUnsafeLocal` → dedicated Host admission gate); new (Host ResourceType user-domain schema with `defaultDomain`, `allowedDomains`, `defaultUserRef`, `shellPolicy`, cardinality bounds) | +| Destination | `packages/d2b-contracts/src/v3/host.rs` (Host resource schema, user-domain variant); `packages/d2b-core-controller/src/host_user.rs` (reconciler owned by Provider/system-core); Nix Host authoring via `d2b.zones..resources. = { type = "Host"; spec = { ... }; };` (validated per ResourceTypeSchema; no separate `options-zones-hosts.nix` submodule) | +| Detailed design | Host ResourceType schema for user-domain variant: `spec.defaultDomain=user`, `spec.allowedDomains=[user]`, `spec.defaultUserRef=User/`, `spec.shellPolicy` (adapted from `UnsafeLocalShellPolicy`), `spec.launcherItems` (adapted from `UnsafeLocalLauncherItem`). No-isolation posture recorded in `status.observedPosture`. A dedicated Host admission gate blocks unsafe-local Host creation without opt-in. Mandatory no-isolation warning in Host `status.conditions[0].message` and CLI/UI output for all Host commands. No-isolation posture included in audit record body (`isolation=no-isolation`); it is never emitted as an OTEL metric label value or span attribute. Child processes use standard Process Providers — no Provider resource with name or kind `unsafe-local` is created. Cardinality bounds: max 256 user-domain Hosts per Zone, max 64 launcher items per Host, max 64 shell sessions per Host. | +| Integration | Provider/system-core bootstrap exception creates Host resources from Zone config publication; Host controller reconciles `defaultUserRef` via User resource lookup; `HelperRegistry` in Zone runtime becomes per-uid launch broker for Host Process launch; Host admission layer enforces the `allowUnsafeLocal` gate before Host creation | +| Data migration | Destructive reset; `unsafe-local-workloads.json` bundle artifact and `HelperRegistry` replaced by Host resource store entries + Process Provider launch | +| Validation | `host-user-domain-no-isolation-warning-required`, `host-user-only-disallows-system-domain`, `host-allowUnsafeLocal-gates-creation`, `host-defaultUserRef-user-type-required`, `host-shell-policy-max-sessions-bound`, `host-launcher-item-max-count-bound`, `host-audit-body-isolation-label-present`, `host-otel-no-posture-label`, `host-cli-no-isolation-warning-present` | +| Removal proof | `d2bd/src/unsafe_local_helper.rs` `HelperRegistry` removed after Zone runtime Host/Process broker integration; `unsafe-local-workloads.json` bundle artifact removed after Host resource store replaces it; `kind = "unsafe-local"` Nix enum value removed in purge wave | + + +### ADR046-zone-control-009 + +| Field | Value | +| --- | --- | +| Work item ID | `ADR046-zone-control-009` | +| Dependency/owner | ADR046-zone-control-001; Zone store (ADR046-store-001); Quota handler owner | +| Current source | `packages/d2b-realm-core/src/ids.rs` (`LABEL_PATTERN`, `MAX_ID_LEN` — ResourceName validation for quotaRef); `packages/d2b-core/src/unsafe_local_workloads.rs:16–164` (`MAX_UNSAFE_LOCAL_WORKLOADS=256`, etc. — bound evidence for quota ceiling defaults); no current quota ResourceType exists (`ADR-only`) | +| Reuse source | None from main; Quota is a new ResourceType with no main-branch analog | +| Reuse action | n/a | +| Destination | `packages/d2b-contracts/src/v3/quota.rs`; `packages/d2b-core-controller/src/quota.rs`; `packages/d2b-resource-api/src/quota_gate.rs` | +| Detailed design | Quota resource schema with all spec/status fields from §7; ceiling bounds enforcement at admission (hard policy: reject over-quota with `quota-exceeded`; soft policy: warn); usage index built from resource scan with `quotaRef` field; per-ResourceType ceiling in `perTypeCeilings`; `core.quota-drain` finalizer that blocks Quota deletion until all dependent resources with `spec.quotaRef` pointing to this Quota are reassigned or deleted by authorized owners/operators — the controller never issues spec-updates to clear `quotaRef` on other resources; `dependentCount` status field updated from dependency index; audit event `quota-check` per admission; Nix Quota options per §7.7 | +| Integration | Resource API admission gate (`packages/d2b-resource-api/src/quota_gate.rs`) called for every `create` verb; Zone controller triggers quota reconcile on resource-created/deleted/quotaRef-changed events; quota handler registered in core-controller process | +| Data migration | Full reset; no prior Quota resources exist | +| Validation | `quota-ceiling-hard-reject`, `quota-ceiling-soft-warn`, `quota-ceiling-pertype`, `quota-drain-blocks-on-dependents`, `quota-over-quota-status`, `quota-nix-eval-bounds`, `quota-nix-build-pertype-unknown-type` | +| Removal proof | Additive; no existing code removed | + +### ADR046-zone-control-010 + +| Field | Value | +| --- | --- | +| Work item ID | `ADR046-zone-control-010` | +| Dependency/owner | ADR046-zone-control-001; Zone store (ADR046-store-001); EmergencyPolicy handler owner | +| Current source | `packages/d2bd/src/lib.rs` admission gate (`authorize_peer()` — `implemented-and-reachable`, baseline `b5ddbed6`); `packages/d2b-daemon-access/src/lib.rs` (`DaemonAccessDecision::Denied` — basis for admission rejection pattern); no EmergencyPolicy ResourceType exists (`ADR-only`) | +| Reuse source | None from main; EmergencyPolicy is a new ResourceType | +| Reuse action | n/a | +| Destination | `packages/d2b-contracts/src/v3/emergency_policy.rs`; `packages/d2b-core-controller/src/emergency_policy.rs`; `packages/d2b-resource-api/src/emergency_gate.rs` | +| Detailed design | EmergencyPolicy resource schema from §8; union semantics: multiple `enabled=true` policies allowed simultaneously; effective scope = OR of all enabled policies' scope flags; effective `drainDeadlineSeconds` = minimum across enabled policies; scope flag evaluation: `stopNewAdmissions` signal to API admission gate, `disconnectZoneLinks` graceful signal to ZoneLink handler, `stopProviderProcesses` suppresses Process launch and sends stop signal to running Provider component Processes (does NOT set `deletionRequestedAt` on Process resources; reconciliation resumes on deactivation), `drainOngoingOperations` deadline drain; `core.emergency-drain` finalizer for enabled policies; audit events `emergency-policy-activated` / `emergency-policy-deactivated`; `reason` field stored in spec and included in audit record body (never in OTEL metric label values, structured log labels, or status fields); Nix EmergencyPolicy options per §8.7 | +| Integration | API admission gate checks union of enabled EmergencyPolicies before every admitted request; ZoneLink handler subscribes to EmergencyPolicy watch triggers; Provider process lifecycle listens for effective `stopProviderProcesses` and resumes launch on deactivation | +| Data migration | Full reset; no prior EmergencyPolicy resources exist | +| Validation | `emergency-policy-activates-gate`, `emergency-policy-disconnects-zonelinks`, `emergency-policy-union-most-restrictive`, `emergency-policy-multi-enabled-combined-scope`, `emergency-policy-stop-processes-no-delete`, `emergency-policy-deactivation-restores-gate`, `emergency-policy-drain-finalizer`, `emergency-nix-eval-drain-deadline-bound-tightest` | +| Removal proof | Replaces the inline `emergencyDisable` field from the proposed Zone.spec option B; that option was not implemented | + +--- + +All items in this subsection copy or adapt from main commit `a1cc0b2da4a08ca3240a770a972fe4da6f912bef` +(the ADR 0045 in-progress implementation). Main is **not** current pre-ADR45 v3 behavior. +Citations use the prefix `main a1cc0b2d:` to distinguish from baseline `b5ddbed6` citations. +Evidence class for all: `main-reuse-source`. + +### ADR046-bus-001 + +| Field | Value | +| --- | --- | +| Work item ID | `ADR046-bus-001` | +| Dependency/owner | ADR046-identities-001; ADR046-store-001 | +| Main source | main `a1cc0b2d`: `packages/d2b-session/src/lifecycle.rs` (`SessionPhase::{Established,Disconnected,Reconnecting,Closing,Closed}`, `KeepaliveAction::{None,SendPing,Close}`, `SessionLifecycle::new/phase/on_activity/poll_keepalive/receive_pong/disconnect/begin_reconnect/reconnect_established/close` — lines 9–195); `packages/d2b-session/src/engine.rs` (`SessionEngine`, `SessionEvent`, `establish_initiator/responder`, `reconnect_initiator/responder`, `call/complete_call/cancel_call`, `send_ttrpc/receive`, `open/send/grant_credit/close/reset_named_stream`, `send_attachments`, `drive_keepalive`, `fail_closed`, encode/decode helpers for keepalive/cancel/close/stream-control/attachment — lines 33–995); `packages/d2b-session/src/driver.rs` (`ComponentSessionDriver` trait 18 methods, `SessionDriverHandle`, `DriverQueues` 4 event queues + named-send FIFO, `DRIVER_COMMAND_CAPACITY=128`, `DRIVER_EVENT_CAPACITY=128`, backpressure on enqueue and dequeue, test `cancelled_immediate_receiver_restores_queued_event` — lines 20–718); `packages/d2b-session/src/streams.rs` (`StreamId`, `StreamPhase::{Open,HalfClosedLocal,HalfClosedRemote,Closed,Reset}`, `StreamEvent::{Data,RemoteClosed,Reset}`, `NamedStreamMux::open/reserve_send/grant_send_credit/refund_send_credit/receive_data/close_local/receive_close/reset/remove_terminal/active` — lines 7–237); `packages/d2b-session/src/transport.rs` (`OwnedTransport` trait, `TransportDescriptor`, `TransportPacket`, `TransportError::{Disconnected,Truncated,LimitExceeded,InvalidAttachment,Other}` — lines 9–106); `packages/d2b-session/src/error.rs` (`SessionError`, all `From<>` mappings covering `ContractError`, `BinaryError`, `SequenceError`, `FragmentSequenceError`, `HandshakeRejectReason`, `TransportError` — lines 10–143); supporting modules: `attachment.rs`, `bootstrap.rs`, `cancellation.rs`, `deadline.rs`, `fragmentation.rs`, `metrics.rs`, `record.rs`, `scheduler.rs`, `server.rs` | +| Reuse action | copy + adapt | +| Destination | `packages/d2b-bus/src/{lifecycle,engine,driver,streams,transport,error}.rs` (new crate `d2b-bus`) | +| Detailed design | **Selected**: full `SessionLifecycle` FSM including keepalive ping-nonce/timeout close (`poll_keepalive` lines 81–124), nonce-exhaustion close (`NonceExhausted` remediation `ReplaceGeneration`), reconnect attempt counting + window expiry (`begin_reconnect` lines 147–174), generation increment on reconnect; `ComponentSessionDriver` 18-method trait as the d2b-bus driver contract; `SessionEngine` full frame encode/decode, keepalive, named-stream credit/half-close/reset; `NamedStreamMux` credit model with send/receive credit, phase transitions, terminal removal; `DriverQueues` backpressure (`QueueBackpressure`) on both deliver and receive paths; all `SessionError`/`From<>` mapping chains. **Excluded ADR45 assumptions**: `establish_initiator_with_generation_discovery()` (lines 102–123): ADR45 initiator-probes-server for current generation; in v3 the generation lives in the Zone resource store — use `establish_initiator()` directly. OTEL labels in `metrics.rs` reference ADR45 realm/workload dimensions — replace with Zone/ZoneLink labels per §13.2. | +| Integration | `d2b-bus` crate is the sole session-transport dependency for ZoneLink (ADR046-session-001), Provider component Processes (ADR046-provider-001), and the Zone resource API service layer (ADR046-api-001); `ComponentSessionDriver` is the interface ZoneLink uses to send/receive ttrpc frames and named streams | +| Data migration | Not applicable; new crate | +| Validation | `session-lifecycle-reconnect-attempts-exhausted`, `session-lifecycle-keepalive-timeout-closes`, `session-lifecycle-nonce-exhausted-close-with-replace-generation-remediation`, `session-driver-cancelled-receiver-restores-event` (ported from test at line 707), `named-stream-credit-half-close-then-remote-close-transitions-closed`, `named-stream-reset-cancels-pending-send` | +| Removal proof | `d2b-realm-router/src/{session.rs,secure_session.rs,mux_session.rs,session_lifecycle.rs}` removed after d2b-bus integration wave | + +### ADR046-bus-002 + +| Field | Value | +| --- | --- | +| Work item ID | `ADR046-bus-002` | +| Dependency/owner | ADR046-bus-001 | +| Main source | main `a1cc0b2d`: `packages/d2b-session-unix/src/adapter.rs` (`UnixSeqpacketTransport::new`, `UnixStreamTransport::new`, `PeerIdentityPolicy::{Pathname{verifier},InheritedSocketpair{expected_peer}}`, `OwnedUnixAttachment::file`, `UnixAttachmentPayload`, `consume_peer_credentials()` credential strip+verify, `map_validation_error`/`map_transport_error`, tests `inherited_first_packet_requires_credentials`/`inherited_first_packet_rejects_wrong_credentials` — lines 23–852); `packages/d2b-session-unix/src/socket.rs` (`AncillaryCapacity::from_policy`, `OutboundPacket::new`, `SeqpacketSocket`, `prearmed_seqpacket_pair()`, `SendBurst`/`PacketBurst`/`SentPacket`, `recv_one` with `DONTWAIT|CMSG_CLOEXEC`, `send_one` with `DONTWAIT|NOSIGNAL`, SCM_RIGHTS+SCM_CREDENTIALS, fairness-budget burst loops, tests `raw_control_scanner_rejects_unknown_and_partial_headers`/`raw_control_scanner_accepts_exact_rights_shape` — lines 35–698); `packages/d2b-session-unix/src/pidfd.rs` (`PidfdEvidence::new`, `ProcPidfdIdentityVerifier::verify` with double-read race guard, `ProcSelfFdInfoSource::read_pid_from_fdinfo`, `DigestEvidenceCallback` — lines 8–159); `packages/d2b-session-unix/src/credit.rs` (`CreditPool::new/reserve`, `CreditScope::{Packet,Request,Operation,Session,Process,Host}`, `CreditScopeSet::reserve/reserve_ingress`, `CreditBundle::acquire_dispatch`, `ProcessCreditLimit::derive` from `RLIMIT_NOFILE` — lines 13–286); `packages/d2b-session-unix/src/descriptor.rs` (`ReceivedPacket::verify/verify_first_packet_credentials`, `DescriptorPolicy`, `ObjectIdentity`, `PeerCredentials`, `FirstPacketCredentials`, sealed memfd `F_SEAL_WRITE|GROW|SHRINK|SEAL` — lines 21–617); `packages/d2b-session-unix/src/error.rs` (`UnixSessionError` 21 variants, Display strings — lines 4–92); `packages/d2b-session-unix/src/systemd.rs` (`ActivatedSeqpacketListener`, `SystemdActivationError`) | +| Reuse action | copy + adapt | +| Destination | `packages/d2b-bus-unix/src/{adapter,socket,pidfd,credit,descriptor,error,systemd}.rs` (new crate `d2b-bus-unix`) | +| Detailed design | **Selected**: `UnixSeqpacketTransport` seqpacket adapter implementing `OwnedTransport`; `PeerIdentityPolicy` SO_PEERCRED verification (pathname + inherited socketpair); `prearmed_seqpacket_pair()` for bootstrap inherited-socketpair path; `CreditPool`/`CreditScopeSet` six-scope credit model with per-packet/request/operation/session/process/host reservation; `ProcessCreditLimit::derive()` from `RLIMIT_NOFILE` minus `RESERVED_CONTROL_FDS`; `ProcPidfdIdentityVerifier` double-read race guard on `/proc/self/fdinfo/` with executable + cgroup digest callbacks; sealed memfd four-seal enforcement; `AncillaryCapacity` derived from `AttachmentPolicy`; `UnixSessionError` 21-variant Display strings preserved verbatim for audit log compatibility; `consume_peer_credentials()` strips-and-verifies SCM_CREDENTIALS exactly once on first packet. **Excluded ADR45 assumptions**: `vsock.rs` (`FramedVsockTransport`, `NativeVsockListener`, `NativeVsockTransport`): ADR45 guest-control vsock transport; v3 ZoneLink uses Unix sockets for host-local; vsock for Guest connections is separate work. Specific socket paths (`PUBLIC_SOCKET_PATH`, `BROKER_SOCKET_PATH`) are replaced by Zone-resource-managed paths. | +| Integration | `d2b-bus-unix` provides `OwnedTransport` impl consumed by `d2b-bus` `SessionEngine`; Zone runtime passes accepted seqpacket socket to `UnixSeqpacketTransport::new`; Process pidfd identity verifier integrates with Host/Process broker launch | +| Data migration | Not applicable; new crate | +| Validation | `seqpacket-inherited-missing-credentials-rejected` (ported from `inherited_first_packet_requires_credentials`), `seqpacket-inherited-wrong-credentials-rejected` (ported from `inherited_first_packet_rejects_wrong_credentials`), `pidfd-double-read-race-guard-detects-pid-reuse`, `sealed-memfd-partial-seal-rejected`, `credit-scope-six-levels-ordered`, `ancillary-capacity-from-disabled-policy-is-zero`, `raw-control-unknown-header-rejected` (ported from `raw_control_scanner_rejects_unknown_and_partial_headers`), `raw-control-valid-rights-parsed` (ported from `raw_control_scanner_accepts_exact_rights_shape`) | +| Removal proof | `d2b-realm-router/src/secure_session.rs` `SecurePeerSession` seqpacket layer removed after d2b-bus-unix provides the replacement | + +### ADR046-bus-003 + +| Field | Value | +| --- | --- | +| Work item ID | `ADR046-bus-003` | +| Dependency/owner | ADR046-identities-001 | +| Main source | main `a1cc0b2d`: `packages/d2b-contracts/src/v2_component_session.rs` (all protocol constants: `PREFACE_LEN=16`, `PREFACE_MAGIC=b"D2BCS2\r\n"`, `COMPONENT_SESSION_MAJOR=2`, `COMPONENT_SESSION_MINOR=0`, `MAX_HANDSHAKE_OFFER_BYTES=16384`, `HANDSHAKE_OFFER_CANONICAL_LEN=148`, `ENDPOINT_POLICY_IDENTITY_CANONICAL_LEN=140`, `MAX_PROTECTED_CIPHERTEXT_BYTES=65535`, `NOISE_TAG_BYTES=16`, `RECORD_LENGTH_BYTES=2`, `MAX_PROTECTED_PLAINTEXT_BYTES=65519`, `MAX_LOGICAL_MESSAGE_BYTES=1_048_576`, `MAX_ACTIVE_NAMED_STREAMS=128`, `MAX_PACKET_ATTACHMENTS=32`, `MAX_REQUEST_ATTACHMENTS=64`, `MAX_OPERATION_ATTACHMENTS=128`, `MAX_SESSION_ATTACHMENTS=256`, `MAX_PROCESS_ATTACHMENT_CREDITS=2048`, `MAX_HOST_ATTACHMENT_CREDITS=8192`, named-stream/control queue limits, credential size/flag constants; closed enums: `EndpointPurpose` 19 tags, `PurposeClass` 3 tags, `EndpointRole` 19 tags, `ServicePackage` 15 tags, `NoiseProfile` 3 tags, `IdentityEvidenceRequirement` 3 tags, `Locality` 4 tags, `TransportClass` 7 tags, `AttachmentPolicyKind`; structs: `ComponentSessionPreface::parse/encode`, `HandshakeOffer` 11 fields, `HandshakeAccept`, `HandshakeReject`, `HandshakeRejectReason` 21 tags, `LimitProfile` 15 fields with `local_default()`, `AttachmentPolicy::validate/disabled`, `BoundedVec` — throughout); `packages/d2b-session/src/handshake.rs` (`HandshakeRole::{Initiator,Responder}`, `HandshakeCredentials::{Nn,Kk,IkPsk2Initiator,IkPsk2Responder}`, `NegotiatedOffer`, `NoiseHandshake::new/write_next/read_next/finish`, `EstablishedHandshake::transcript_hash/generation`, `encode_offer/negotiate_offer`, generation-discovery magics `b"D2BGD2Q\n"`/`b"D2BGD2A\n"`, `encode_generation_discovery_request/accept/response/decode_generation_discovery_response` — lines 39–433) | +| Reuse action | copy + adapt | +| Destination | `packages/d2b-contracts/src/v3/component_session.rs` (new v3 namespace in existing contracts crate) | +| Detailed design | **Selected**: all numeric constants verbatim; all 21 `HandshakeRejectReason` tags; `ComponentSessionPreface::parse/encode` with exact magic/length layout; `HandshakeOffer` 11-field shape; `AttachmentPolicy::validate` transport constraints (packet-atomic requires seqpacket or inherited-socketpair); `LimitProfile` 15-field profile; `NoiseHandshake` for Nn/Kk/IkPsk2 profiles; `EstablishedHandshake::transcript_hash`; `BoundedVec`. **Excluded ADR45 assumptions**: `EndpointPurpose` 19 tags and `ServicePackage` 15 tags encode ADR45-specific service families — v3 will append new tags for Zone API endpoints without renumbering existing ones. `IdentityEvidenceRequirement` + `GuestSessionCredentialV1` / `GuestBootstrapPsk` / `GUEST_SESSION_CREDENTIAL_MAGIC`: ADR45 guest bootstrap credential formats, excluded until v3 Guest bootstrap work item. Generation-discovery protocol (`encode_generation_discovery_request` lines 138–149): ADR45 initiator probes server for current generation; v3 generation lives in Zone resource store — generation-discovery excluded from initial d2b-bus copy. | +| Integration | All d2b-bus, d2b-bus-unix, Provider registry, and client packages import v3 protocol constants; v3 Zone API service layer uses `EndpointPurpose`/`ServicePackage` tags for Zone API endpoints | +| Data migration | Additive: new v3 tags appended to existing closed enums; ADR45 v2 tag values remain valid during coexistence | +| Validation | `preface-magic-exact-16-bytes`, `preface-offer-len-zero-rejected`, `preface-offer-len-over-max-rejected`, `preface-offer-len-canonical-accepted`, `handshake-21-reject-reasons-all-covered`, `attachment-policy-packet-atomic-requires-seqpacket-or-inherited`, `limit-profile-local-default-all-fields-positive` | +| Removal proof | `d2b-contracts/src/v2_component_session.rs` ADR45-specific tags retired after all v2 sessions decommissioned | + +### ADR046-session-001 + +| Field | Value | +| --- | --- | +| Work item ID | `ADR046-session-001` | +| Dependency/owner | ADR046-bus-001, ADR046-bus-002, ADR046-bus-003, ADR046-zone-control-002 | +| Main source | main `a1cc0b2d`: `packages/d2b-session/src/lifecycle.rs` (`SessionPhase` 5-phase FSM, `begin_reconnect` window/attempt bounds — lines 9–195); `packages/d2b-realm-router/src/service_v2.rs` (`RealmSessionAuthority::local_controller/gateway_peer`, authority validation rejecting invalid combinations — lines 53–123; `RealmServiceServer::call` wire/request/generation/lifetime/attachment validation — lines 415–497; `RealmServiceLimits` 15 fields — lines 147–181; `RealmAuditEvent`/`RealmAuditOutcome` — lines 236–245); `packages/d2b-realm-router/src/session_lifecycle.rs` (`SessionPhase::{Allocating,TokenMinting,RelayConnecting,DisplayOpening,Running,Stopping,Stopped}`, `fail`/`stop`/`finish_stop`, tests `forward_sequence_reaches_running_then_refuses_to_advance`, `failure_mid_establishment_rolls_into_teardown_and_records_phase`, `stop_is_idempotent`, `finish_stop_without_stopping_is_a_no_op` — lines 31–220); `packages/d2b-client/src/session.rs` (`ComponentSessionConnector` trait — lines 79–86; `NamedStream` lifecycle/close/reset/send/receive — lines 426–576); `packages/d2b-daemon-access/src/component_session.rs` (`connect_component_session()` peer-UID verify + `HandshakeCredentials::Nn` + `TransportKind::LocalUnix` — lines 53–85) | +| Reuse action | adapt | +| Destination | `packages/d2b-core-controller/src/zone_link.rs` (ZoneLink handler); `packages/d2b-resource-api/src/admission.rs` (request admission) | +| Detailed design | **Selected**: `SessionPhase` 5-phase FSM (from main `d2b-session`) drives ZoneLink session state; `Established` state → `status.connected=true` + ZoneLink `status.phase=Ready`; `Disconnected`/`Reconnecting` states → `status.connected=false` + ZoneLink `status.phase=Pending` (or `Degraded` if degraded capability); session-internal phases are not exposed as `ZoneLink.status.phase` values — only the common Resource phases (`Pending|Ready|Degraded|Failed|Unknown`) appear in `status.phase` (§3.5); `begin_reconnect` window/attempt logic → ZoneLink reconnect loop; `RealmSessionAuthority` local vs gateway pattern → ZoneLink authority types for host-local vs transport-bridge sessions; `RealmServiceServer::call` wire validation (generation, request lifetime, attachment) → Zone API request admission; `RealmServiceLimits` 15 fields → ZoneLink `spec.limits`; `connect_component_session()` Nn peer-UID path → Zone runtime bootstrap ComponentSession; `NamedStream` lifecycle → ZoneLink named-stream operations; session-lifecycle tests ported as ZoneLink phase regression tests. **Excluded ADR45 assumptions**: `RealmSessionAuthority::gateway_peer()` (lines 72–87): gateway custody and `Locality::Remote` + `CredentialCustody::GatewayGuest` are ADR45 realm-gateway patterns; v3 ZoneLink transport is bound by the resolved `spec.transportProviderRef`, `spec.transportSettings`, and `spec.transportCredentials` contract instead. Realm 7-phase `SessionLifecycle` (`Allocating→…→Running→Stopping→Stopped`) is the ADR45 realm-specific lifecycle; ZoneLink uses the 5-phase d2b-session model. `GuestBootstrapPsk`/`GuestSessionCredentialV1`: ADR45 guest bootstrap, excluded. `realm_stubs.rs` `ApiService`/`ApiFrontend` dead code excluded (§16.2). | +| Integration | Zone runtime startup creates bootstrap ComponentSession using `HandshakeCredentials::Nn` + local domain socket + peer-UID verification (adapted from `connect_component_session`); ZoneLink handler opens sessions for child Zone connections using d2b-bus `SessionEngine`; resource API admission layer validates requests using `RealmServiceServer::call` validation pattern | +| Data migration | Not applicable; new implementation | +| Validation | `zonelink-reconnect-child-uid-change`, `zonelink-disconnect-unknown-phase`, `zonelink-intent-queue-limit`, `zone-session-phase-forward-sequence-refuses-repeat` (ported from `forward_sequence_reaches_running_then_refuses_to_advance`), `zone-session-failure-records-phase` (ported from `failure_mid_establishment_rolls_into_teardown_and_records_phase`), `zone-session-stop-is-idempotent` (ported), `zone-bootstrap-session-nn-peer-uid-verified` | +| Removal proof | `d2b-realm-router/src/service_v2.rs` `RealmServiceServer` removed after Zone API service layer replaces realm v2 service; `d2b-realm-router/src/session_lifecycle.rs` removed after d2b-bus lifecycle replaces realm-specific one | + +### ADR046-provider-001 + +| Field | Value | +| --- | --- | +| Work item ID | `ADR046-provider-001` | +| Dependency/owner | ADR046-bus-001, ADR046-bus-003, ADR046-zone-control-003 | +| Main source | main `a1cc0b2d`: `packages/d2b-provider/src/registry.rs` (`RegistryLimits`, `AdmissionOptions`, `ProviderRegistryBuilder::new/limits/register_factory/register_instance/register_constructed/finish`, `ProviderRegistry::lifecycle/snapshot/instance/admit/shutdown`, `InFlightPermit`, `AdmittedProvider`, `ProviderRegistryManager::new/current/publish` — lines 33–568; tests `shutdown_closes_final_permit_notify_race`, `finish_drain_closes_final_permit_notify_race` — lines 683–691); `packages/d2b-provider/src/rpc.rs` (`SessionIdentity`, `ProviderClock`/`SystemProviderClock`, `RpcCall`, `RpcPayload`, `RpcResponse`, `AuthenticatedProviderRpc`, `RpcProviderProxy::new/preflight/call*`, `session_identity_matches_placement` — lines 27–895; test `provider_and_user_agent_session_identities_are_placement_exact` — lines 923–959); `packages/d2b-provider-toolkit/src/adapter.rs` (`ProviderAgentAdapter::new/invoke_session/invoke` — lines 18–194); `packages/d2b-provider-toolkit/src/conformance.rs` (`ConformanceError`, `check_descriptor_conformance`, `check_provider_conformance` — lines 8–126); `packages/d2b-provider-toolkit/src/fixture.rs` (`DeterministicClock`, `Fixture::new/from_descriptor/operation/request/call_context/session_identity`, `FakeProvider`, `sample_lease_request` — lines 39–262); `packages/d2b-provider-toolkit/src/registration.rs` (`ToolkitError`, `register_exact_instances` — lines 12–107); `packages/d2b-provider-toolkit/src/server.rs` (`GeneratedProviderServiceServer::from_session_handle/new/shutdown/generated_services` — lines 59–176; test `rpc_statuses_retain_closed_actionable_reasons`); `packages/d2b-provider-toolkit/src/values.rs` (`ProviderValues::new/descriptor/health/plan/handle_from_request/handle_from_plan/observation` — lines 18–192); `packages/d2b-provider-toolkit/src/redaction.rs` (`Redacted`, `Secret` — lines 3–37) | +| Reuse action | copy + adapt | +| Destination | `packages/d2b-provider/src/{registry,rpc}.rs` (new v3 Provider package); `packages/d2b-provider-toolkit/src/{adapter,conformance,fixture,registration,server,values,redaction}.rs` (new v3 toolkit) | +| Detailed design | **Selected**: `ProviderRegistry` lifecycle `Accepting→Draining→Retired` with drain-waiter notify-race safety (ported from `shutdown_closes_final_permit_notify_race` / `finish_drain_closes_final_permit_notify_race` tests); `InFlightPermit` + global + per-provider in-flight quota; `ProviderRegistryManager::publish` validates snapshot before swap; `RpcProviderProxy::preflight` cancellation-check, deadline-check, method/capability match, session-identity/placement exactness; `ProviderAgentAdapter` rejects attachments with missing descriptors or non-increasing indexes (lines 79–99); `GeneratedProviderServiceServer::new` single-service-per-agent requirement + shutdown via atomic accept-flag + idle notify + timeout; `check_provider_conformance` health/inspection/observability check sequence; `Fixture`/`FakeProvider`/`DeterministicClock` as conformance harness; `Redacted`/`Secret` retained unchanged. **Excluded ADR45 assumptions**: `TrustedFirstPartyInProcess` as the only accepted placement (in `session_identity_matches_placement` lines 577–598): v3 Provider resources support multiple placements per `RealmControllerPlacement` mapping (§16.2); in-process placement is retained but not exclusive. Generated ttrpc stubs in `d2b-contracts/src/generated_v2_services/` are v2-service-specific; v3 Provider processes compile their own stubs from v3 proto files. ACA workload adapter (`d2b-gateway-runtime/src/aca_workload.rs`) excluded. | +| Integration | Provider/system-core and Provider/system-minijail use `ProviderRegistryBuilder::register_constructed` as bootstrap exceptions; other Providers register instances from Process-spawned agents via `ProviderAgentAdapter`; Zone runtime hosts one `ProviderRegistry` per installed Provider component; `ProviderRegistryManager::publish` swaps registry on Provider update | +| Data migration | Provider descriptors re-registered on Zone store bootstrap; no v2 provider state migration | +| Validation | `provider-registry-drain-waiter-race-safe` (ported from both notify-race tests), `provider-registry-publish-validates-snapshot-before-swap`, `provider-rpc-proxy-placement-exact` (ported from `provider_and_user_agent_session_identities_are_placement_exact`), `provider-agent-adapter-rejects-non-monotone-attachment-indexes`, `provider-server-shutdown-drains-in-flight-requests`, `provider-conformance-health-inspection-observability-sequence` | +| Removal proof | `d2b-realm-provider` trait crate removed after v3 Provider resource + registry integration (§16.5) | + +### ADR046-provider-agent-001 + +| Field | Value | +| --- | --- | +| Work item ID | `ADR046-provider-agent-001` | +| Dependency/owner | ADR046-bus-001, ADR046-provider-001, ADR046-session-001 | +| Main source | main `a1cc0b2d`: `packages/d2b-gateway-runtime/src/provider_agent.rs` (`ProviderAgentError::{UnregisteredAdapter,RegistryNotAccepting,RegistrationRejected,InvalidAuditCapacity,SessionClosed,ProtocolViolation}`, `ProviderAgentAuditOutcome`, `ProviderAgentAuditEvent`, `ProviderAgentProcess::from_registry/from_registry_with/provider_type/service_names/audit_snapshot/serve`, `run_registered`, bounded in-memory audit ring, frame dispatch loop: semaphore in-flight limit, service/method routing, negative-timeout guard, `SessionClosed` termination, `ProtocolViolation` audit + terminate — lines 31–452; tests `standalone_entrypoint_fails_without_registration`, `audit_capacity_is_bounded` — lines 454–486); `packages/d2b-contracts/src/provider_registry_v2.rs` (`ProviderRegistryV2` wire contract, `ProviderRegistryEntryV2::validate` with provider-id derivation rule, schema fingerprint, scope-digest, generation exactness, `TrustedFirstPartyInProcess` placement requirement, `ProviderIntentId` label rules `max 128 bytes`, `MAX_PROVIDER_REGISTRY_ENTRIES`, `MAX_PROVIDER_MAPPING_IDS=64`, `ProviderBindingV2` non-exhaustive + `UnsupportedProviderBindingV2` fallback, `ProviderRegistryV2::validate` sort/unique/count checks — lines 23–566; tests `validates_closed_local_runtime_mapping`, `validates_closed_local_observability_mapping`, `serializes_declared_mapping_axes_as_closed_variants`, `rejects_duplicate_or_unbounded_mapping_ids`, `local_storage_binding_realm_must_match_descriptor_placement`, `rejects_generation_and_exact_identity_mismatches`, `contradictory_binding_realm_json_is_unrepresentable`, `unknown_binding_axis_remains_rejected_on_the_wire`, `identity_mismatch_messages_name_the_failed_contract`, `accepts_explicit_empty_registry` — lines 722–1044) | +| Reuse action | copy + adapt | +| Destination | `packages/d2b-provider/src/agent.rs` (v3 provider agent dispatch); `packages/d2b-contracts/src/v3/provider_registry.rs` (v3 provider registry wire contract) | +| Detailed design | **Selected**: `ProviderAgentProcess::serve` dispatch loop with semaphore in-flight limit; unsupported-service/method → ttrpc error; negative-timeout rejection; `SessionClosed` clean termination; `ProtocolViolation` audit-and-terminate path; bounded audit ring; `GeneratedProviderServiceServer` single-service-per-agent requirement; `ProviderRegistryV2` entry validation: provider-id derivation, schema fingerprint, scope digest, generation exactness; `MAX_PROVIDER_MAPPING_IDS=64` → Provider component mapping bound; `ProviderBindingV2` non-exhaustive + explicit `UnsupportedProviderBindingV2` fallback (never panics on unknown axis); `ProviderIntentId` `max 128 bytes` label rules → Provider component `spec.intentRef`; all 10 `provider_registry_v2.rs` tests ported. **Excluded ADR45 assumptions**: `aca_workload.rs` (`AcaGatewayWorkload`): ADR45 ACA external provider adapter, excluded entirely. `waypipe_display.rs` (`WaypipeDisplayProvider`): ADR45 display provider, excluded. `ProviderRegistryV2.registry_generation` / `configuration_fingerprint` bind to ADR45 bundle generation; v3 Provider resource version is tracked in redb store, not a JSON bundle. `run()` binary entrypoint uses the fixed `d2b-provider-agent` command; v3 provider processes use normal Zone runtime Process launch. `TrustedFirstPartyInProcess` is the only placement in v2; v3 Provider resources extend to `HostLocal`/`GatewayVm` etc. | +| Integration | Zone runtime spawns each Provider component Process via normal Process launch; Process binary calls `ProviderAgentProcess::from_registry` then `serve()` on established ComponentSession; on `SessionClosed` the process exits and Zone runtime observes `status.phase` transition | +| Data migration | Not applicable; new implementation | +| Validation | `provider-agent-dispatch-unsupported-service-returns-ttrpc-error`, `provider-agent-negative-timeout-rejected`, `provider-agent-session-closed-terminates-serve-loop`, `provider-agent-audit-ring-capacity-bounded` (ported from `audit_capacity_is_bounded`), `provider-registry-entry-fingerprint-generation-exact` (ported from `rejects_generation_and_exact_identity_mismatches`), `provider-registry-unknown-axis-fallback-non-exhaustive` (ported from `unknown_binding_axis_remains_rejected_on_the_wire`), `provider-registry-duplicate-ids-rejected` (ported from `rejects_duplicate_or_unbounded_mapping_ids`) | +| Removal proof | Not applicable; new implementation | + +### ADR046-client-001 + +| Field | Value | +| --- | --- | +| Work item ID | `ADR046-client-001` | +| Dependency/owner | ADR046-bus-001, ADR046-bus-002, ADR046-bus-003, ADR046-session-001 | +| Main source | main `a1cc0b2d`: `packages/d2b-client/src/client.rs` (`WallClock`, `MetadataInput`, `RetryPolicy` 1..8 attempts, `CallOptions`, `CancellationToken`, `Client::new/with_clock/connect`, `ConnectedClient` methods incl. `session_generation/session_limits/service/invoke/invoke_with_attachments/named_stream/open_server_stream`, `prepare_typed_request/prepare_operation_context`, `can_retry/retryable_failure/validate_outbound_attachments/validate_reply/service_package/map_remote_kind/map_retry` — lines 35–921); `packages/d2b-client/src/session.rs` (`ComponentSessionConnector` trait, `NamedStream` lifecycle, `StreamDispatcher`, `SharedDriver`, aggregate-queue-bound test — lines 24–626); `packages/d2b-client/src/target.rs` (`ServiceOwner`, `TargetInput`, `TransportKind`, `RouteRecord`, `ResolvedTarget`, `TargetResolver`, `RouteTable` — lines 7–228); `packages/d2b-client/src/service.rs` (`ServiceKind`, `GeneratedClient`, `MethodHandle`, `ServiceHandle::new/kind/generated/proxy/method/invoke` — lines 21–184); `packages/d2b-client/src/daemon_service.rs` (`DaemonClient::new/session_generation/connected/resolve/inspect/lifecycle/open_terminal`, `DaemonTerminal`, `daemon_call_options`, `ensure_daemon_outcome`, `map_ttrpc_error`, test `redacted_terminal_debug_payload` — lines 29–689); `packages/d2b-client/src/host_socket.rs` (`HostSocketConnector::new/from_seqpacket_fd`, `local_daemon_endpoint_identity`, `ComponentSessionConnector::connect` — lines 252–383); `packages/d2b-client/src/error.rs` (`RemoteErrorKind`, `RetryClass`, `ClientError` — lines 5–128) | +| Reuse action | copy + adapt | +| Destination | `packages/d2b-client/src/` (updated for v3 Zone API, replacing ADR45 daemon verbs with Zone resource operations) | +| Detailed design | **Selected**: `Client::connect()` target-resolve → ComponentSession-open → `ConnectedClient` lifecycle; `RetryPolicy` 1..8 bound + `retryable_failure()` safe-only retry detection; `NamedStream::send/receive/close_local/reset` lifecycle; `ComponentSessionConnector` trait as connector abstraction; `HostSocketConnector::from_seqpacket_fd` + `local_daemon_endpoint_identity` for Zone runtime socket connector; `RouteTable` ambiguous-route rejection; `ServiceHandle`/`GeneratedClient`/`MethodHandle` typed service client pattern; `map_ttrpc_error`/`validate_reply`/`map_retry` error-handling chain; `ClientError`/`RemoteErrorKind`/`RetryClass` error taxonomy; `DaemonClient` call-options and outcome helpers (infrastructure only). **Excluded ADR45 assumptions**: `DaemonMethod` enum (lines 29–56 of daemon_service.rs): ADR45 daemon verbs (`vm_start`, `vm_stop`, `list_realms`, etc.) — replaced with Zone API verbs. `GuestClient`/`guest_service.rs`: ADR45 guest operations; excluded until v3 Guest transport work item. Hardcoded socket path `PUBLIC_SOCKET_PATH = "/run/d2b/public.sock"` in `host_socket.rs`: replaced by Zone-resource-managed path. `TransportKind::LocalUnix` restriction in daemon-access: v3 allows multiple transport kinds per ZoneLink binding. | +| Integration | `d2b` CLI uses `d2b-client` to connect to Zone runtime via `HostSocketConnector`; Zone runtime uses `d2b-client` for internal child-Zone ZoneLink connections; Provider toolkit conformance tests use `Fixture`/`FakeProvider` with `d2b-client` service handles | +| Data migration | Not applicable; updated in place | +| Validation | `client-retry-policy-max-8-attempts-enforced`, `client-named-stream-close-local-then-remote-close-transitions-closed`, `client-route-table-ambiguous-route-rejected`, `client-host-socket-peer-uid-verified-on-connect`, `client-retryable-failure-only-safe-mutations` | +| Removal proof | `DaemonMethod` v2 verb enum retired after all v2 daemon operations migrated to Zone API | + +### ADR046-wire-001 + +| Field | Value | +| --- | --- | +| Work item ID | `ADR046-wire-001` | +| Dependency/owner | ADR046-bus-003 | +| Main source | main `a1cc0b2d`: `packages/d2b-contracts/src/v2_services.rs` (`MethodSpec{mutating,stream_kind,...}`, `ServiceSpec`, `SERVICE_INVENTORY` covering 20+ services and all provider services, `service_schema_fingerprint`, `public_daemon_schema_fingerprint`, `direct_guest_schema_fingerprint`, `StrictWireMessage`, `decode_strict`, `encode_strict`, `admit_metadata`, `TerminalStreamValidator`, `ServerStreamLease`, `RedactedTerminalFrame`, stream-name validators — lines 204–1004; tests `public_endpoint_fingerprint_binds_both_services_dependencies_and_order`, `public_endpoint_fingerprint_binds_daemon_and_guest_method_descriptors`, `direct_guest_fingerprint_binds_activation_and_remains_separate_from_public_endpoint` — lines 872–1030); `packages/d2b-contracts/src/v2_state.rs` (constants: `STATE_SCHEMA_VERSION=2`, `STATE_SCHEMA_GENERATION=1`, `MAX_JSON_DOCUMENT_BYTES=1_048_576`, `MAX_INVENTORY_ROWS=4096`, `MAX_LOCKS=1024`, `MAX_LOCK_DEPENDENCIES=32`, `MAX_DISCOVERY_OBSERVATIONS=4096`, `MAX_AUDIT_RECORD_BYTES=8192`, `MAX_AUDIT_RECORDS_PER_SEGMENT=16384`, `MAX_AUDIT_SEGMENT_BYTES=64*1024*1024`, `MAX_AUDIT_RETENTION_DAYS=14`, `MAX_LOCK_DEADLINE_MS=300_000`; types: `Digest`, `Generation`, `OwnershipEpoch`, `SafeJsonInteger`, `StorageCategory`, `StateEnvelope`, `CanonicalPayloadVerifier`, `AtomicWritePhase`, `RunnerEvidence`, audit types incl. `AuditRecord/Segment/Checkpoint/Gap`, `detect_audit_gap`, `AuditRetentionPolicy`, lock types: `LockClass`, `LockSpec`, `SyncInventory`, `LeaseRecord`); `packages/d2b-contracts/src/v2_identity.rs` (`IdentityError` 13 variants, canonical name rules `^[a-z][a-z0-9-]*$` max 63 bytes start-lowercase-letter, `RealmPath` label/separator rules, `ProviderType::ALL` 11 types + `as_str()` — lines 11–250); `packages/d2b-contracts/src/v2_provider.rs` (bounded opaque IDs `[a-z][a-z0-9-]{0,63}`: `ImplementationId`, `OperationId`, `IdempotencyKey`, `PlanId`, `HandleId`, `LeaseId`, `TransferId`, `PROVIDER_CONTRACT_FINGERPRINT`, `ProviderContractError` 34 variants, `Fingerprint` 64 lowercase-hex chars — lines 18–219); `packages/d2b-contracts/src/generated_v2_services/` (all 40+ generated ttrpc client/server stubs for v2 services) | +| Reuse action | adapt | +| Destination | `packages/d2b-contracts/src/v3/{services,state,identity,provider}.rs` (new v3 namespace); generated stubs regenerated from v3 proto files in `packages/d2b-contracts/proto/v3/` | +| Detailed design | **Selected**: `MethodSpec`/`ServiceSpec`/fingerprinting infrastructure → v3 Zone API service schema fingerprinting; `StrictWireMessage`/`decode_strict`/`encode_strict`/`admit_metadata` → v3 wire decode/admit for all resource API requests; `CanonicalPayloadVerifier` payload-digest binding → v3 resource store integrity checks; audit chain types `AuditRecord`/`AuditSegment`/`AuditCheckpoint`/`detect_audit_gap` + `MAX_AUDIT_RETENTION_DAYS=14`/`MAX_AUDIT_RECORD_BYTES=8192`/`MAX_AUDIT_SEGMENT_BYTES=64MiB` → v3 Zone audit (§13.3); lock types `LockSpec`/`SyncInventory`/`LeaseRecord` + `MAX_LOCKS=1024`/`MAX_LOCK_DEADLINE_MS=300_000` → v3 resource store lock layer; bounded opaque ID pattern `[a-z][a-z0-9-]{0,63}` → v3 `OperationId`/`HandleId`/`PlanId`/`LeaseId`; `Fingerprint` 64-hex-char → v3 Provider `spec.configFingerprint`; `ProviderContractError` 34 variants → v3 Provider operation error taxonomy; canonical name constraint `^[a-z][a-z0-9-]*$` max 63 bytes → same as `ids.rs::is_label()` shared validator (§16.2). **Excluded ADR45 assumptions**: `RealmLabel`/`WorkloadName`/`RealmPath` identity types: ADR45 workload/realm address format; replaced by `metadata.name` + Zone `ResourceRef`. `ProviderType::ALL` fixed 11-type closed enum: v3 Provider type is an open string field in the Provider resource spec. `STATE_SCHEMA_VERSION=2`/`STATE_SCHEMA_GENERATION=1`: v3 store schema uses redb table versioning, not a JSON schema version field. v2 service fingerprint tests reference ADR45-specific proto files; v3 fingerprints use different proto inputs but the same `service_schema_fingerprint` seeding mechanism. Generated stubs in `generated_v2_services/` are v2-specific and excluded; v3 uses regenerated stubs from `proto/v3/`. | +| Integration | v3 Zone API service layer uses `MethodSpec.mutating` + `StrictWireMessage` for admission and fingerprinting; `v3/state.rs` `Digest`/`StateEnvelope` integrate with redb store writes; `CanonicalPayloadVerifier` validates resource payloads loaded from store; audit types feed §13.3 Zone audit segment; lock types wire into ADR 0034 lock lifecycle | +| Data migration | v2 and v3 wire type namespaces coexist in the same contracts crate; no migration | +| Validation | `v3-canonical-name-matches-ids-is-label-regex`, `v3-service-fingerprint-changes-on-method-mutation` (behavior ported from `public_endpoint_fingerprint_binds_daemon_and_guest_method_descriptors`), `v3-audit-gap-detection-covers-missing-segment`, `v3-strict-wire-rejects-unknown-fields`, `v3-state-envelope-digest-mismatch-rejected`, `v3-canonical-payload-verifier-binding-holds-under-mutation` | +| Removal proof | `d2b-contracts/src/v2_{services,state,identity,provider}.rs` and `v2_component_session.rs` deprecated in contracts crate after all v2 clients decommissioned | + +--- + +### ADR046-nix-001 + +| Field | Value | +| --- | --- | +| Work item ID | `ADR046-nix-001` | +| Dependency/owner | ADR046-zone-control-001; Nix module owner | +| Current source | `nixos-modules/options-realms.nix` (option schema/assertion conventions); `nixos-modules/bundle-artifacts.nix` (bundle emit pattern); `nixos-modules/provider-registry-v2-json.nix` (provider-registry JSON emit); `nixos-modules/assertions.nix:730` (assertion style for unsafe-local); `packages/d2b-realm-core/src/ids.rs` (`LABEL_PATTERN`, `MAX_ID_LEN`, `is_label()` — eval-time name validation target) | +| Reuse source | None from main; eval-time validation is Nix-native | +| Reuse action | n/a | +| Destination | `nixos-modules/options-zones.nix`, `nixos-modules/resource-type-validators.nix` | +| Detailed design | Declare the unified `d2b.zones..resources.` option tree with two sub-options: `type` (string, required) and `spec` (attrset, required). Declare the `d2b.artifacts.` catalog option tree with `package` (types.package, required) and `type` (closed string enum, required); `d2b.artifacts` is a global option, not zone-local. For each core ResourceType (Zone, ZoneLink, Provider, Role, RoleBinding, Credential), a `genSchemaOptions` helper reads the corresponding ResourceTypeSchema JSON and emits per-type `spec` option declarations with correct Nix types, defaults, and documentation comments. Provider `spec.artifactId` is a plain string referencing the `d2b.artifacts` catalog; the `package` derivation is NOT a `spec` field. Non-core (Provider-registered) ResourceTypes are deferred to Phase 2 schema validation; Phase 1 accepts any non-empty `spec` attrset for non-core types. Implement all Phase 1 eval-time assertions (§14.10 Phase 1 table) via Nix `assert` and `lib.mkAssert`; `credentialRef` helper function producing `{ "$credentialRef": "Credential/" }` literals; `resourceRef` and `closedEnum` helpers; bootstrap provider exclusion assertions; eval assertion that Zone resource name must equal Zone attrset key; `system-core`/`system-minijail` record auto-generation from Zone runtime artifact catalog entries; `allowUnsafeLocal` option mapped to a dedicated Host admission gate (current `options-realms.nix:346`). Provider manifest-derived fields (`spec.exports`, `spec.components`, `spec.dependencies`, `spec.permissionClaims`, `spec.upgradePolicy`, `spec.restartPolicy`) are declared as read-only/not-settable options; setting them is an eval assertion error | +| Integration | Unified `d2b.zones..resources.*` option tree consumed by ADR046-nix-002 resource compiler; Zone controller (ADR046-zone-control-001) reads resulting bundle; Provider package conventions from ADR046-zone-control-003 | +| Data migration | Replace `nixos-modules/options-realms.nix`-derived option trees once Zone controller is live and has reached parity | +| Validation | All Phase 1 eval tests in §15.8 (`nix-eval-name-regex-enforced`, `nix-eval-verb-closed-enum`, `nix-eval-roleref-format`, `nix-eval-subject-type-restricted`, `nix-eval-no-duplicate-subjects`, `nix-eval-bootstrap-provider-rejected`, `nix-eval-provider-missing-artifact-id`, `nix-eval-artifact-id-not-in-catalog`, `nix-eval-artifact-wrong-type`, `nix-eval-artifact-id-format`, `nix-eval-credentialref-declared`, `nix-eval-dollar-key-rejected`, `nix-eval-zonelink-self-loop-rejected`, `nix-eval-zonelink-limits-maxpendingintents-bound`) | +| Removal proof | `nixos-modules/options-realms.nix`, `nixos-modules/realm-controller-config-json.nix`, `nixos-modules/realm-identity-config-json.nix` deleted after Zone controller and resource compiler reach full parity; `nixos-modules/assertions.nix` lines referencing `allowUnsafeLocal`/realm names removed after Host admission validation replaces them | + +### ADR046-nix-002 + +| Field | Value | +| --- | --- | +| Work item ID | `ADR046-nix-002` | +| Dependency/owner | ADR046-nix-001; resource compiler owner | +| Current source | `nixos-modules/bundle-artifacts.nix` (artifact emit pattern); `nixos-modules/processes-json.nix` (canonical JSON serialization conventions); `packages/xtask/src/main.rs` `gen-schemas` subcommand (schema-from-derivation pattern); `packages/d2b-core/src/bundle.rs` (current bundle DTO shape for adaptation reference) | +| Reuse source | None from main; the resource compiler is a new pure-Rust Nix derivation | +| Reuse action | n/a | +| Destination | `packages/d2b-resource-compiler/src/{main,bundle,schema,validator,digest,sort,secret_lint,generation}.rs`; exposed as `pkgs.d2b-resource-compiler`; called from `nixos-modules/resource-compiler.nix` | +| Detailed design | Implement all Phase 2 build-time checks (§14.10 Phase 2 table): dispatch on `type` to look up ResourceTypeSchema; validate each resource's `spec` canonical JSON against the committed schema (build validation compares canonical rendered JSON against ResourceTypeSchema for each core type); compile the `d2b.artifacts.*` catalog: for each entry, build/include the derivation, verify `type` is a recognized value, compute `digest` over the derivation output, extract and hash manifest and config schema files, validate signature chain and conformance attestation; detect duplicate artifact IDs; for each Provider resource, look up `spec.artifactId` in the compiled catalog (build failure if absent or wrong type), verify `configSchemaDigest` matches schema SHA-256, validate operator `spec.config` against loaded JSON Schema using a pure-Rust validator bundled in the derivation, verify `manifestDigest` and signature chain, load manifest-derived fields (`exports`, `components`, `dependencies`, `permissionClaims`, `upgradePolicy`, `restartPolicy`) into the bundle envelope; emit private integrity-pinned artifact catalog (ID → type/digest/closure metadata) as a separate private file (never merged into the public resource bundle); check `spec.rules[*].resourceTypes` against installed Provider catalogs in the bundle (Role); verify `spec.roleRef` names an existing Role in the bundle (RoleBinding); verify `spec.subjects[*]` names resolve in bundle (RoleBinding); check ResourceType short-name collision across all Zone Providers; RFC 8785 canonical JSON serialization; per-resource `digest` computation; `bundleDigest` computation over sorted `resources` array; inline-secret heuristic lint (`--strict-secrets` flag); `generation` counter persistence in Nix module state; emit `zone-resources.json` bundle with all fields per §14.9 | +| Integration | Reads from `d2b.zones..resources.*` (ADR046-nix-001); emits bundle consumed by ADR046-zone-control-001 configuration publication handler; generation counter stored as Nix module derivation input hash (hermetic) or in a NixOS state file (impure) — exact mechanism is implementation decision | +| Data migration | Full reset; no prior bundle state exists to carry forward | +| Validation | All Phase 2 build tests in §15.8 (`nix-build-artifact-id-missing-from-catalog`, `nix-build-artifact-wrong-type-rejected`, `nix-build-duplicate-artifact-id`, `nix-build-artifact-store-path-absent-from-bundle`, `nix-build-artifact-store-path-absent-from-config`, `nix-build-config-schema-failure`, `nix-build-schema-digest-mismatch`, `nix-build-manifest-digest-mismatch`, `nix-build-resourcetype-collision`, `nix-build-bundle-sorted`, `nix-build-bundle-digest-stable`, `nix-build-per-resource-digest-correct`, `nix-build-credential-ref-survives-build`, `nix-build-inline-secret-lint-warning`, `nix-build-inline-secret-strict-failure`) | +| Removal proof | No current equivalent; additive only; no prior code removed | + +### ADR046-nix-003 + +| Field | Value | +| --- | --- | +| Work item ID | `ADR046-nix-003` | +| Dependency/owner | ADR046-nix-002; ADR046-zone-control-001; configuration publication handler owner | +| Current source | `packages/d2bd/src/lib.rs` lines 1408 and 16741 (`RealmControllersJson` load — live active generation load pattern); `nixos-modules/realm-controller-config-json.nix` (current config bundle emit to `/etc/d2b/`); `packages/d2b-realm-core/src/allocator_engine.rs` (generation/activation pattern); `packages/d2b-core/src/unsafe_local_workloads.rs:16–164` (`MAX_UNSAFE_LOCAL_WORKLOADS=256`, etc. — bounds reference for Credential/Host cleanup) | +| Reuse source | main `a1cc0b2d`: `packages/d2b-session/src/lifecycle.rs` `begin_reconnect` exponential backoff logic (cleanup retry); `packages/d2b-state/` lock/lease types (ADR046-store-001 dependency for bundle file locking) | +| Reuse action | extract exponential backoff from `begin_reconnect` | +| Destination | `packages/d2b-core-controller/src/configuration.rs` (Phase 3 activation, diff, delete dispatch); `packages/d2b-core-controller/src/cleanup.rs` (pending tracking, status, stuck detection, rollback verb handler) | +| Detailed design | Implement all Phase 3 runtime activation checks (§14.10 Phase 3 table); `bundleDigest` integrity verify; `zoneUid` consistency check; `generation` monotone check; per-resource `digest` re-verify; atomic generation advance in `store_meta`; diff computation (resources with `managedBy=configuration` absent from new bundle → async Delete; `managedBy=controller`/`managedBy=api` resources untouched); `managedBy` and `configurationGeneration` field maintenance on resources in redb store; `cleanupPendingCount` and `generationCleanupPending` maintenance; Zone.status.phase → Degraded while cleanup pending, reverts on completion; `GenerationCleanupPending`/`GenerationCleanupFailed` condition management; stuck-cleanup `GenerationCleanupFailed=True` at `cleanupStuckThreshold` (default 5 min) with exponential backoff retry; prior generation bundle retention and pruning up to configured `retainedPriorGenerationCount` (default 3, range 1..16); audit emission for all four cleanup audit kinds (§14.11); `zone.config-rollback` verb handler | +| Integration | Zone store / redb (ADR046-store-001); core-controller watch/trigger bus (ADR046-bus-001); Zone status writer (ADR046-zone-control-001); audit emitter (§13.2); Credential revocation hook (triggered when `deletionRequestedAt` is set on a Credential and `core.credential-revoke` finalizer is present; revocation completes before finalizer clearance) | +| Data migration | Full reset; no prior bundle activation state exists to carry forward | +| Validation | All Phase 3 runtime and cleanup tests in §15.8 (`nix-runtime-bundledigest-integrity`, `nix-runtime-generation-monotone`, `nix-runtime-zoneuid-mismatch-rejected`, `nix-runtime-zonename-mismatch-rejected`, `nix-runtime-activation-nonblocking`, `nix-runtime-provider-config-invalid-continues`, all `cleanup-*` and `rollback-*` tests) | +| Removal proof | `d2bd/src/lib.rs` config-load at lines 1408 and 16741 removed after Zone configuration publication handler reaches parity; `realm-controller-config-json.nix` and `realm-identity-config-json.nix` Nix bundle-emit removed after resource compiler reaches parity | + +### ADR046-pkg-001 + +| Field | Value | +| --- | --- | +| Work item ID | `ADR046-pkg-001` | +| Dependency/owner | ADR046-zone-control-003; workspace policy owner | +| Current source | `packages/d2b-contract-tests/tests/policy_contracts.rs` lines 5–6 (D2B_FIXTURES gate / workspace-checks integration pattern — `implemented-and-reachable`, baseline `b5ddbed6`); `packages/d2b-contract-tests/tests/static_invariants.rs` (hermetic policy test structure — `implemented-and-reachable`); `tests/tools/rust-workspace-checks.sh` (D2B_FIXTURES step shell harness — `implemented-and-reachable`); AGENTS.md "Naming conventions" section (`-` workspace sort rules — `implemented-and-reachable`); `packages/d2b-realm-core/src/ids.rs` `LABEL_PATTERN` / `MAX_ID_LEN` (name regex reused for crate name token validation — `implemented-and-reachable`) | +| Reuse source | None from main; workspace policy tests are repo-specific | +| Reuse action | n/a | +| Destination | `packages/d2b-contract-tests/tests/policy_provider_crate_layout.rs` (new file; gated under D2B_FIXTURES in existing `tests/tools/rust-workspace-checks.sh`) | +| Detailed design | Implement `policy_provider_crate_layout.rs` with the following test functions: (1) `every_provider_crate_has_src` — walk `packages/d2b-provider-*/` directories in the workspace, assert each contains `src/`; failure names crate and missing path; (2) `every_provider_crate_has_tests` — assert `tests/` present; (3) `every_provider_crate_has_integration` — assert `integration/` present; (4) `every_provider_crate_has_readme` — assert `README.md` present; (5) `every_provider_readme_has_required_sections` — read `README.md`, check for all nine section headings from §4.8.3 (case-insensitive, after stripping `#` and whitespace); failure names the missing heading(s); (6) `every_integration_file_has_target_declaration` — for each `integration/*.rs` file, scan first 20 lines for exactly one `//! integration-target: (container|host-integration)` declaration; failure names the file and the violation (missing/multiple/invalid value); (7) `non_provider_crates_exempt` — verify the check does not run on non-`d2b-provider-*` crates. All checks are filesystem-only (no compilation). Workspace member list is discovered by parsing `packages/Cargo.toml` `[workspace].members`. Gate: add the new test file to `tests/tools/rust-workspace-checks.sh` D2B_FIXTURES list alongside existing policy tests | +| Integration | `make test-policy` and `make check` both fail if any provider crate violates §4.8; consistent with existing `no-bash-ast-walker` and workspace-sort gates; ADR046-zone-control-003 references §4.8 for Provider package conventions | +| Data migration | Additive; no existing `d2b-provider-*` crates in the pre-ADR45 baseline; first Provider crate created must comply from inception | +| Validation | §15.3 layout conformance tests: `provider-crate-layout-src-required`, `provider-crate-layout-tests-required`, `provider-crate-layout-integration-required`, `provider-crate-layout-readme-required`, `provider-readme-sections-all-present`, `provider-readme-sections-partial-missing`, `provider-integration-target-declared`, `provider-integration-target-unique`, `provider-integration-target-valid-values`, `provider-crate-naming-convention`, `provider-crate-layout-non-provider-exempt` | +| Removal proof | No existing code removed; additive policy test only | + +--- + +## 18. All decisions resolved + +All design decisions for Zone, ZoneLink, Provider, Role, RoleBinding, Quota, +and EmergencyPolicy are resolved as of this revision. No unresolved design +blocks remain. New design decisions are tracked in +`docs/specs/ADR-046-decision-register.md`. \ No newline at end of file From 6e314a79ffa2b72640757e68e3fcc6b26efe142a Mon Sep 17 00:00:00 2001 From: John Vicondoa Date: Wed, 22 Jul 2026 23:26:10 -0700 Subject: [PATCH 004/115] docs: align integrated ADR 0046 contracts Keep bootstrap resources runtime-owned, resolve Process templates through semantic owners without package references, and avoid treating current VM listings as Host resources. --- docs/specs/ADR-046-cli-and-operations.md | 5 ++++- docs/specs/ADR-046-nix-configuration.md | 4 ++-- docs/specs/ADR-046-resources-zone-control.md | 2 +- 3 files changed, 7 insertions(+), 4 deletions(-) diff --git a/docs/specs/ADR-046-cli-and-operations.md b/docs/specs/ADR-046-cli-and-operations.md index e83657883..8cff5e13d 100644 --- a/docs/specs/ADR-046-cli-and-operations.md +++ b/docs/specs/ADR-046-cli-and-operations.md @@ -583,7 +583,10 @@ Equivalent to `d2b get Host/`. Equivalent to `d2b list Host`. -**Current v3 source:** `cmd_list`/`cmd_status` read `ListResponse { vms: Vec }` and `StatusResponse { entries: Vec }` from `packages/d2b-contracts/src/public_wire.rs:2152,2158`. Each `ListEntry.vm: String` is a `WorkloadId` (old terminology); `ListEntry.lifecycle.state: VmLifecycleState` (old: `Stopped/Starting/Booted/Running/Stopping/Restarting/Failed/Unknown`) maps to target `Host` phase. `Host` ResourceType is new — there is no current separate host-listing command. Evidence class: `implemented-and-reachable` for the list/status daemon paths; `ADR-only` for `Host` as a distinct ResourceType. +**Current v3 source:** no current command lists physical/local Host contexts. +`cmd_list`/`cmd_status` list VM workloads and therefore inform the target +`Guest` command shape, not Host phase semantics. `Host` as a distinct +ResourceType and `d2b host list` are ADR-only. ### `d2b host status ` diff --git a/docs/specs/ADR-046-nix-configuration.md b/docs/specs/ADR-046-nix-configuration.md index 108557a63..e47c44833 100644 --- a/docs/specs/ADR-046-nix-configuration.md +++ b/docs/specs/ADR-046-nix-configuration.md @@ -802,13 +802,13 @@ Current source: `packages/d2b-core/src/processes.rs` — `ProcessesJson`, ```nix d2b.zones.dev.resources.wayland-proxy = { type = "Process"; + metadata.ownerRef = "Provider/display-wayland"; spec = { providerRef = "Provider/system-systemd"; # replaces ProcessRole + minijail profile selection executionRef = "Host/host-system"; # replaces per-VM DAG node host/VM assignment domain = "user"; userRef = "User/alice"; processClass = "service"; - packageRef = "Provider/display-wayland"; # replaces binaryPath in ProcessNode template = "wayland-proxy-host"; # replaces ProcessRole for template dispatch configRef = "Volume/wayland-proxy-config"; mounts = [ @@ -856,12 +856,12 @@ Current source: `ProcessRole::StoreVirtiofsPreflight`, `SwtpmPreStartFlush`, ```nix d2b.zones.dev.resources.store-sync-dev-vm = { type = "EphemeralProcess"; + metadata.ownerRef = "Volume/dev-vm-store-farm"; spec = { providerRef = "Provider/system-minijail"; executionRef = "Host/host-system"; domain = "system"; processClass = "worker"; - packageRef = "Provider/volume-virtiofs"; template = "store-sync"; configRef = "Volume/store-sync-config"; successfulTtl = "1h"; # default; explicit for clarity diff --git a/docs/specs/ADR-046-resources-zone-control.md b/docs/specs/ADR-046-resources-zone-control.md index b660609f8..306ecc436 100644 --- a/docs/specs/ADR-046-resources-zone-control.md +++ b/docs/specs/ADR-046-resources-zone-control.md @@ -3661,7 +3661,7 @@ Evidence class for all: `main-reuse-source`. | Reuse source | None from main; eval-time validation is Nix-native | | Reuse action | n/a | | Destination | `nixos-modules/options-zones.nix`, `nixos-modules/resource-type-validators.nix` | -| Detailed design | Declare the unified `d2b.zones..resources.` option tree with two sub-options: `type` (string, required) and `spec` (attrset, required). Declare the `d2b.artifacts.` catalog option tree with `package` (types.package, required) and `type` (closed string enum, required); `d2b.artifacts` is a global option, not zone-local. For each core ResourceType (Zone, ZoneLink, Provider, Role, RoleBinding, Credential), a `genSchemaOptions` helper reads the corresponding ResourceTypeSchema JSON and emits per-type `spec` option declarations with correct Nix types, defaults, and documentation comments. Provider `spec.artifactId` is a plain string referencing the `d2b.artifacts` catalog; the `package` derivation is NOT a `spec` field. Non-core (Provider-registered) ResourceTypes are deferred to Phase 2 schema validation; Phase 1 accepts any non-empty `spec` attrset for non-core types. Implement all Phase 1 eval-time assertions (§14.10 Phase 1 table) via Nix `assert` and `lib.mkAssert`; `credentialRef` helper function producing `{ "$credentialRef": "Credential/" }` literals; `resourceRef` and `closedEnum` helpers; bootstrap provider exclusion assertions; eval assertion that Zone resource name must equal Zone attrset key; `system-core`/`system-minijail` record auto-generation from Zone runtime artifact catalog entries; `allowUnsafeLocal` option mapped to a dedicated Host admission gate (current `options-realms.nix:346`). Provider manifest-derived fields (`spec.exports`, `spec.components`, `spec.dependencies`, `spec.permissionClaims`, `spec.upgradePolicy`, `spec.restartPolicy`) are declared as read-only/not-settable options; setting them is an eval assertion error | +| Detailed design | Declare the unified `d2b.zones..resources.` option tree with two sub-options: `type` (string, required) and `spec` (attrset, required). Declare the `d2b.artifacts.` catalog option tree with `package` (types.package, required) and `type` (closed string enum, required); `d2b.artifacts` is a global option, not zone-local. For each core ResourceType (Zone, ZoneLink, Provider, Role, RoleBinding, Credential), a `genSchemaOptions` helper reads the corresponding ResourceTypeSchema JSON and emits per-type `spec` option declarations with correct Nix types, defaults, and documentation comments. Provider `spec.artifactId` is a plain string referencing the `d2b.artifacts` catalog; the `package` derivation is NOT a `spec` field. Non-core (Provider-registered) ResourceTypes are deferred to Phase 2 schema validation; Phase 1 accepts any non-empty `spec` attrset for non-core types. Implement all Phase 1 eval-time assertions (§14.10 Phase 1 table) via Nix `assert` and `lib.mkAssert`; `credentialRef` helper function producing `{ "$credentialRef": "Credential/" }` literals; `resourceRef` and `closedEnum` helpers; bootstrap provider exclusion assertions; eval assertion that Zone resource name must equal Zone attrset key. The runtime creates `Zone/`, `Provider/system-core`, and `Provider/system-minijail` directly with `managedBy=controller`; none is emitted or inferred from the configuration bundle. `allowUnsafeLocal` maps to a dedicated Host admission gate (current `options-realms.nix:346`). Provider manifest-derived fields (`spec.exports`, `spec.components`, `spec.dependencies`, `spec.permissionClaims`, `spec.upgradePolicy`, `spec.restartPolicy`) are declared as read-only/not-settable options; setting them is an eval assertion error | | Integration | Unified `d2b.zones..resources.*` option tree consumed by ADR046-nix-002 resource compiler; Zone controller (ADR046-zone-control-001) reads resulting bundle; Provider package conventions from ADR046-zone-control-003 | | Data migration | Replace `nixos-modules/options-realms.nix`-derived option trees once Zone controller is live and has reached parity | | Validation | All Phase 1 eval tests in §15.8 (`nix-eval-name-regex-enforced`, `nix-eval-verb-closed-enum`, `nix-eval-roleref-format`, `nix-eval-subject-type-restricted`, `nix-eval-no-duplicate-subjects`, `nix-eval-bootstrap-provider-rejected`, `nix-eval-provider-missing-artifact-id`, `nix-eval-artifact-id-not-in-catalog`, `nix-eval-artifact-wrong-type`, `nix-eval-artifact-id-format`, `nix-eval-credentialref-declared`, `nix-eval-dollar-key-rejected`, `nix-eval-zonelink-self-loop-rejected`, `nix-eval-zonelink-limits-maxpendingintents-bound`) | From fdeb4b53264406fdae0e5163edc119056c4fa759 Mon Sep 17 00:00:00 2001 From: John Vicondoa Date: Wed, 22 Jul 2026 23:32:47 -0700 Subject: [PATCH 005/115] docs: specify d2b 3.0 Volume and Device resources Define fine-grained Volume layout, views, ACL repair, attachments, and quotas alongside external Device discovery, arbitration, claims, Provider packaging, and cleanup contracts. --- docs/specs/ADR-046-resources-device.md | 1969 ++++++++++++++++++++++++ docs/specs/ADR-046-resources-volume.md | 1522 ++++++++++++++++++ 2 files changed, 3491 insertions(+) create mode 100644 docs/specs/ADR-046-resources-device.md create mode 100644 docs/specs/ADR-046-resources-volume.md diff --git a/docs/specs/ADR-046-resources-device.md b/docs/specs/ADR-046-resources-device.md new file mode 100644 index 000000000..a62d3c82f --- /dev/null +++ b/docs/specs/ADR-046-resources-device.md @@ -0,0 +1,1969 @@ +# ADR 0046 Device resource and Provider dossiers + +| Field | Value | +| --- | --- | +| Spec ID | `ADR-046-resources-device` | +| Parent | ADR 0046 | +| Status | Proposed | +| Version | 1 | +| Baseline | `b5ddbed67867d9244bf33390868101bd9b053e49` | +| Normative | Yes | +| Owners | `d2b-provider-device-*`, Device controller contracts, Nix device emitters | +| Depends on | `ADR-046-resource-object-model`, `ADR-046-primitive-resource-composition`, `ADR-046-resource-api-and-authorization`, `ADR-046-resource-reconciliation`, `ADR-046-provider-model-and-packaging`, `ADR-046-components-processes-and-sandbox` | +| Supersedes | Current ProcessRole device sidecars (Swtpm, SwtpmPreStartFlush, Usbip, SecurityKeyFrontend, Gpu, GpuRenderNode, Video) and their Nix components | + +## Purpose + +Device is the single ResourceType for physical and emulated hardware device +inventory, arbitration, claim, and attachment in a Zone. It is the resource +contract for the four frozen Device Provider families: `device-tpm`, +`device-usbip`, `device-security-key`, and `device-gpu` (combined GPU/video). + +Audio (`device-audio`) is not part of this spec. The `audio-pipewire` Provider +is in the interaction Provider catalog and is independently specified. + +## Device ResourceType spec + +### Envelope example + +```yaml +apiVersion: resources.d2b.io/v3 +type: Device +metadata: + name: corp-vm-tpm + zone: dev + uid: + generation: 1 + revision: + ownerRef: Guest/corp-vm + finalizers: [device-tpm.d2b.io/state-preserved] + deletionRequestedAt: null + createdAt: 2026-07-22T00:00:00Z + updatedAt: 2026-07-22T00:00:00Z +spec: + providerRef: Provider/device-tpm + deviceClass: emulated + arbitration: exclusive + maxConcurrentClaims: 1 + inventory: + selector: {} # emulated devices carry no physical selector + settings: {} # Provider-specific settings +status: + observedGeneration: 1 + phase: Ready + conditions: [] + lastReconciledAt: 2026-07-22T00:00:01Z + device: + present: true + health: healthy + holderRefs: [Guest/corp-vm] + claims: [] + provisionedAt: 2026-07-22T00:00:01Z +``` + +### Device spec fields + +| Field | Type | Required | Default | Bounds | Notes | +| --- | --- | --- | --- | --- | --- | +| `providerRef` | ResourceRef | yes | — | must resolve to an installed Provider | Selects the device Provider | +| `deviceClass` | enum | yes | — | `physical` \| `emulated` | Physical devices exist in sysfs/udev; emulated devices are created by the Provider | +| `arbitration` | enum | yes | — | `exclusive` \| `shared` | Whether the device may be simultaneously claimed by more than one holder | +| `maxConcurrentClaims` | uint | no | 1 | 1–16 | Maximum simultaneous claimants; must equal 1 when `arbitration=exclusive` | +| `inventory` | object | yes | — | see below | Physical or emulated device selector | +| `settings` | object | no | `{}` | Provider-specific schema | Provider-validated device configuration | + +### Inventory selector + +An inventory selector is a discriminated union keyed on `busClass`. The +`busClass` field is the variant discriminant. Unknown fields for a given +`busClass` variant and unknown `busClass` values are rejected at admission +(strict unknown-field denial). No raw device path appears in the spec. + +```yaml +inventory: + # Emulated device — selector must be absent or {} + selector: {} + + # USB device + selector: + busClass: usb + label: yubikey-work # required; stable human label, max 63 chars + vendorId: "1050" # 4-hex lower-cased; optional + productId: "0407" # 4-hex lower-cased; optional + serial: null # max 128 chars; optional + + # HID/hidraw device + selector: + busClass: hidraw + label: yubikey-primary + vendorId: "1050" + productId: "0407" + serial: null + + # DRM/GPU device + selector: + busClass: drm + label: host-gpu + pciSlot: null # optional PCI slot filter; e.g. "0000:01:00.0"; max 31 chars + + # PCI device (non-GPU; reserved for future physical-passthrough Providers) + selector: + busClass: pci + label: host-pci-dev + slot: null # PCI slot; e.g. "0000:02:00.0"; max 31 chars + + # Physical TPM kernel device (rare; emulated TPM uses {})) + selector: + busClass: tpm + label: host-tpm + index: 0 # /dev/tpm; default 0 +``` + +Closed `busClass` values: `usb`, `hidraw`, `drm`, `pci`, `tpm`. Any other value +is rejected. For `deviceClass=emulated` the `selector` must be `{}` or absent. +For `deviceClass=physical` the `selector.label` field is required on every +variant and serves as the stable operator-defined identifier; the Provider +resolves the physical node using the label plus any provided filter fields. +Vendor and product IDs are stored lower-cased and must be exactly four ASCII hex +digits. A spec containing extra fields beyond a variant's declared field set is +rejected without admission. + +## Claims and attachments in Host/Guest/Process + +Device claims are declared inline on the execution context that holds them. + +### Host/Guest devices list + +`spec.devices` on a Host or Guest resource: + +```yaml +devices: + - deviceRef: Device/corp-vm-tpm + claim: exclusive + passthrough: tpm-socket # Provider-specific passthrough kind +``` + +| Field | Type | Required | Default | Notes | +| --- | --- | --- | --- | --- | +| `deviceRef` | ResourceRef | yes | — | Same-Zone Device resource | +| `claim` | enum | yes | — | `exclusive` \| `read-shared` \| `provider-managed` | +| `passthrough` | string | no | null | Provider-specific passthrough kind (e.g., `tpm-socket`, `usbip-export`, `virtiofs-hidraw`, `gpu-virtio`, `gpu-render-node`) | +| `settings` | object | no | `{}` | Provider-specific per-attachment settings | + +A Device with `arbitration=exclusive` rejects more than one claimed Host/Guest +attachment at a time. The controller writes a conflict condition and sets phase +Degraded/Pending on all but the first successful claimant. + +### Process devices list + +`spec.devices` on a Process or EphemeralProcess resource: + +```yaml +devices: + - deviceRef: Device/corp-vm-gpu + usage: gpu-socket + settings: {} +``` + +Process device entries express a usage dependency. The Process controller +verifies the Device is Ready and claimed by its owning Guest/Host before launch. +A Process that needs a device fd directly (e.g., a GPU worker Process that +receives a socket path) declares its dependency here; the fd handoff is +Provider-specific and is not in the resource spec. + +## Device status + +Status fields beyond the standard common envelope: + +```yaml +status: + phase: Ready | Pending | Degraded | Failed | Unknown + conditions: + - type: DevicePresent + status: "True" + reason: device-probed-present + - type: DeviceClaimed + status: "True" + reason: exclusive-claim-held + device: + present: true | false | null + health: healthy | degraded | failed | unknown + holderRefs: [] # ordered list of claimant ResourceRefs (bounded) + claims: [] # per-claimant claim entry + provisionedAt: null # RFC 3339; set when emulated device is created + lastProbedAt: null # RFC 3339; set on last physical probe + providerDiagnostic: null # bounded redacted one-line string; never paths/secrets +``` + +### Device claim entry + +```yaml +claims: + - holderRef: Guest/corp-vm + claim: exclusive + passthrough: tpm-socket + claimedAt: 2026-07-22T00:00:01Z + health: healthy | degraded | failed | unknown +``` + +### Phase semantics + +| Phase | Meaning | +| --- | --- | +| `Pending` | Device spec committed but not yet provisioned/claimed | +| `Ready` | Device is present/provisioned, claim(s) held, healthy | +| `Degraded` | Usable but one or more conditions impaired (e.g., probe uncertain) | +| `Failed` | Current spec generation cannot complete under retry policy | +| `Unknown` | Controller or Host/Guest cannot currently prove device state | + +`Succeeded` and `Deleted` follow the common phase contract. + +### Condition types + +| Type | Meaning | +| --- | --- | +| `DevicePresent` | Physical device is sysfs/udev-visible; always True for emulated; Unknown after first probe failure; False after three consecutive failures | +| `DeviceProvisioned` | Emulated device (swtpm, virtual HID) has been created | +| `DeviceClaimed` | At least one active claim is held | +| `DeviceHealthy` | Physical or emulated device is responsive | +| `ClaimConflict` | Exclusive device received a second concurrent claim request | +| `StateIntegrity` | Emulated device tamper-marker/state check passed | +| `BrokerAccessible` | Broker can open/pass the physical device fd | + +## Ownership and finalizer contract + +The Device resource's `ownerRef` is typically the Guest or Host that provisioned +it. The owning Provider installs one or more typed finalizers before starting any +external effect. + +### Finalizer IDs + +| Provider | Finalizer ID | +| --- | --- | +| `device-tpm` | `device-tpm.d2b.io/state-preserved` | +| `device-usbip` | `device-usbip.d2b.io/attachment-released` | +| `device-security-key` | `device-security-key.d2b.io/lease-released` | +| `device-gpu` | `device-gpu.d2b.io/worker-stopped` | + +Deletion sequence: +1. `deletionRequestedAt` is set. +2. The Device Provider finalizer handler terminates owned worker Processes, + releases OS resources (socket, udev rules, swtpm state if policy allows), + and clears the finalizer. +3. If a tamper-sensitive Volume exists (device-tpm only), the finalizer + completes but does NOT delete the persistent Volume; that Volume has its + own lifecycle and owner. +4. Core removes the Device resource after all finalizers clear. + +Persistent TPM state (swtpm NVRAM, EK seed) is never deleted by the Device +finalizer. The Volume resource that owns TPM state has an independent lifecycle +and its own `volume-local.d2b.io/tpm-state` finalizer. + +## Hotplug + +### Hotplug observation + +Device Providers that manage physical devices declare a bounded observe interval +in their controller descriptor. Core schedules a `scheduled-observe` trigger at +that interval. The controller probes the current device state and writes status. + +Hotplug state changes flow exclusively through Device status and the standard +resource/owner/dependency hint mechanism. The Device controller does not signal +the Host/Guest supervisor directly; it writes updated Device status, and +dependent Host/Guest controllers receive a `dependency-changed` trigger through +the normal resource watch path and react accordingly. There is no direct +supervisor signal bypass. + +### Physical probe failure semantics + +| Consecutive probe failures | Status transition | +| --- | --- | +| 1 (first failure) | phase → `Unknown`; condition `DevicePresent` status=`Unknown`, reason=`device-probe-failed` | +| 2 | condition `DevicePresent` remains `Unknown`; phase remains `Unknown` | +| 3 | phase → `Degraded`; condition `DevicePresent` status=`False`, reason=`device-consecutive-probe-failures-exceeded` | +| Device returns | phase → `Ready`; condition `DevicePresent` status=`True`, reason=`device-probed-present` | + +A single probe failure does not set `DevicePresent=False` or phase `Degraded`. +After three consecutive failures the full consequence chain fires: + +1. Condition `DevicePresent=False` and phase `Degraded`. +2. Claimant Host/Guest/Process controllers receive `dependency-changed` triggers + through the normal resource watch path. +3. If the device is essential (e.g., an exclusive TPM claim), the Guest + controller may set phase `Degraded` or stop the Guest. +4. When the device returns, the Device controller sets `DevicePresent=True` and + phase `Ready`, re-triggering claimants. + +### Hotplug limits + +- Maximum observe interval: 60 s (configurable per Provider, default 30 s). +- A probe failure must transition phase to `Unknown` within one observe period. +- Three consecutive probe failures are required before `DevicePresent=False`. + +## Security model + +### Broker effect limits + +The Device Provider is a Process under a Host. It interacts with the Zone +privileged broker for operations that require root: + +| Provider | Broker operation | Effect | Audit | +| --- | --- | --- | --- | +| `device-tpm` | `PrepareStateDir` (via `PrepareRuntimeDir`/`PrepareSwtpmDir` broker hook) | Provision/harden swtpm state dir, verify tamper marker | Yes | +| `device-tpm` | `SpawnRunner` (swtpm role) | Spawn swtpm Process in user namespace | Yes | +| `device-usbip` | `UsbipBindFirewallRule` | Add per-env/bus nftables rule | Yes | +| `device-usbip` | `SpawnRunner` (usbip role) | Spawn usbipd/bind Process | Yes | +| `device-security-key` | `SecurityKeyOpenDevice` | Open exact FIDO hidraw node; return fd via SCM_RIGHTS; never a path | Yes | +| `device-security-key` | `SecurityKeyApplyUdevRules` | Write udev rules for configured FIDO hidraw nodes | Yes | +| `device-gpu` | `SpawnRunner` (gpu/gpu-render-node/video role) | Spawn crosvm GPU or video-decoder Process | Yes | +| `device-gpu` | `OpenDevice` (kvm/dri/udmabuf/nvidia*) | Open GPU device fds in user namespace pre-spawn | Yes | + +No Device Provider receives a blanket device-path grant, raw socket address, or +ambient host capability. Broker operations are point-specific and audit-logged. + +### Security key specific invariants + +From D046 and the v3 baseline: + +1. The Zone privileged broker is the only entity that opens the physical hidraw + node. It never accepts a caller-supplied path; it derives the physical node + from the trusted bundle device table using only a stable label or session ID. +2. **Current implementation:** the relay is a daemon-internal async accept loop + inside d2bd (`packages/d2bd/src/lib.rs:start_sk_accept_loop`, + `packages/d2bd/src/security_key.rs`): the daemon calls the broker for the + hidraw fd via SCM_RIGHTS, binds a vsock-proxy Unix socket, and spawns an + async accept loop — there is no separate relay process. **v3 target:** this + relay logic is extracted into a dedicated unprivileged relay Process under + the device-security-key Provider. The relay Process receives the hidraw fd + from the broker over SCM_RIGHTS and proxies CTAP HID traffic to the Guest + frontend over AF_VSOCK. It never runs as root and has no further broker access. +3. **Current implementation:** the guest-side UHID virtual HID binary is + `packages/d2b-sk-frontend/src/` (static, implemented-and-reachable), running + inside the Guest VM as a guest systemd service (`d2b-sk-frontend.service`) + declared in `nixos-modules/components/security-key-guest.nix`. It is NOT + a current ProcessRole or Zone Process — the ProcessRole name + `SecurityKeyFrontend` refers to the HOST accept loop, not this guest binary. + **v3 target:** the `d2b-sk-frontend` binary becomes a Zone Process resource + (name `device--sk-frontend`, `executionRef: Guest/`) owned by + the device-security-key Provider controller, giving the controller lifecycle + visibility and replacing the untracked guest systemd unit. +4. At most one Guest may hold the exclusive hidraw lease at a time. A second + claim is rejected; the requesting Guest receives `ClaimConflict`. +5. Security-key proxy and USBIP YubiKey passthrough are mutually exclusive for + the same VM. This invariant is enforced at Nix eval time and at runtime by + the Device controller. + +### TPM state integrity + +The swtpm state directory (`/vms//swtpm`) is identity-bound: + +- Mode 0700, owner `d2b--swtpm`. +- Per-VM sticky 3770 root prevents non-owner rename/replace. +- Root-owned tamper-guard marker at `/var/lib/d2b/swtpm-markers/` records + `st_dev`/`st_ino` + first-provision stamp. +- A missing or mismatched marker fails VM start closed + (`previously-provisioned-swtpm-state-missing`). +- Wiping state is treated as device tampering by IdPs (Entra ID, Intune); + the Device finalizer never deletes swtpm NVRAM. + +These invariants are preserved exactly in the device-tpm Provider. + +### GPU security + +- Full GPU (virtio-gpu, card-node access, VFIO passthrough): always + `arbitration: exclusive`. Only one Guest may hold a full GPU claim at a time. +- Render-node-only mode (`settings.renderNodeOnly=true`) may use + `arbitration: shared` when the Device spec explicitly sets + `arbitration: shared`. A Device spec with `arbitration: shared` and + `settings.renderNodeOnly=false` is rejected at admission. The default + arbitration for GPU devices is `exclusive` regardless of mode; operators must + explicitly opt into shared render-node mode. +- Gpu and GpuRenderNode claim `kvm`, `dri`, `nvidia-ctl`, `nvidia-uvm`, + `nvidia-render`, and `udmabuf` broker device tokens. VFIO/SR-IOV is not + included in the standard GPU device claim. +- The GPU worker Process runs in a user namespace (ADR 0021 broker-pre-NS). + The broker opens GPU device fds before clone and passes them; the Process + has zero ambient host capabilities. +- Render-node-only mode passes only the DRM render node fd without a virtio-gpu + bind-mount. +- Video decode (crosvm video-decoder) runs as the per-Device Process principal, + needs `/dev/dri` and optionally NVIDIA devices. It is a separate Process from + the GPU sidecar but shares the GPU Device claim. + +### USBIP security + +- USBIP per-env/per-busid firewall rules use the ownership-marker pattern: + `comment "d2b managed: usbip:env::bus:"`. +- Broker operation `UsbipBindFirewallRule` is audit-logged and destructive. +- The bus ID validation is governed by `packages/d2b-contracts/src/usbip.rs`: + max 31 chars (`SYSFS_BUS_ID_MAX`), ASCII digits and separators only, no + shell metacharacters, no leading zeros on segments. +- Explicit-attach (`d2b usb attach --apply`) uses a separate + `UsbipExplicitBind` broker op path distinct from bundle-declared claims. + +## Async reconciliation + +Each Device Provider controller implements the standard async reconcile loop +from `ADR-046-resource-reconciliation`: + +1. **spec-generation-changed:** create/update inventory selector, provision + emulated device (swtpm, UHID virtual HID), apply udev rules. +2. **deletion-requested:** stop owned worker Processes, release OS resources, + uninstall udev rules, clear finalizer. Never delete persistent TPM state. +3. **dependency-changed / execution-status-changed:** if the owning Guest + stops, release active claims and set phase Degraded. +4. **scheduled-observe:** probe physical device presence; update status. +5. **owned-resource-changed:** if a worker Process fails, set DeviceHealthy=False + and optionally restart. + +### Fast-path process launch + +Worker Processes (swtpm, crosvm GPU/video, usbipd) follow the standard +Process fast-path (commit-to-handler ≤5 ms p95, launch-attempt ≤20 ms p95). +The Device controller creates the Process resource; the Process controller +manages the launch. + +### Pre-start EphemeralProcess pattern + +`SwtpmPreStartFlush` maps to an EphemeralProcess owned by the device-tpm +controller. The controller creates it before creating the long-lived swtpm +Process, waits for `Succeeded`, then creates the swtpm Process: + +```text +Device/corp-vm-tpm + └─ EphemeralProcess/device--flush (pre-start flush; successfulTtl=15m) + └─ Process/device--swtpm (long-lived swtpm socket) + └─ Volume/corp-vm-tpm-state (persistent TPM NVRAM; separate owner) +``` + +If the flush EphemeralProcess fails, the Device controller sets phase Failed +and does not launch the swtpm Process. + +## Provider: device-tpm + +### Identity + +```text +Provider/device-tpm +``` + +Crate: `packages/d2b-provider-device-tpm/` +Dossier: `docs/specs/ADR-046-provider-device-tpm.md` (separate, not yet authored) + +### Implements + +Device (emulated, exclusive, TPM class). + +### Root config schema + +| Field | Type | Default | Bounds | Notes | +| --- | --- | --- | --- | --- | +| `logLevel` | uint | 20 | 1–20 | swtpm `--log level` value | +| `startupClear` | bool | true | — | Emit `--flags startup-clear`; requires pre-start flush | +| `stateDirPath` | null | provider-derived | — | Overrides are rejected; path is always derived from Volume/state owner | + +swtpm and swtpm_ioctl binaries are dependencies bundled inside the +`d2b-provider-device-tpm` package closure. Their executable paths are resolved +from the signed component descriptor inside that closure; they are not +configurable fields in the Device spec. + +Config must not carry a raw state directory path; the Provider derives it from +the owned Volume resource. + +### Device spec + +```yaml +spec: + providerRef: Provider/device-tpm + deviceClass: emulated + arbitration: exclusive + maxConcurrentClaims: 1 + inventory: + selector: {} + settings: + logLevel: 20 + startupClear: true +``` + +### Worker processes + +| Process name | Role | Executable | Domain | Execution | Notes | +| --- | --- | --- | --- | --- | --- | +| `device--flush` | EphemeralProcess | `swtpm_ioctl -i --unix ` | system | Host | Pre-start flush; once per start cycle | +| `device--swtpm` | Process | `swtpm socket ...` | system | Host | Long-lived; supervised | + +Process resource names are derived deterministically from the owner Device +resource UID (`uid-short` = first 12 hex chars of the Device UID) combined with +a component template from the signed Provider dossier. The name never contains a +VM or Guest human name. The Device UID is stable across restarts. Foundation +canonical process identity format applies. + +### State/Volume + +The swtpm NVRAM Volume is a separate `volume-local` resource with independent +ownership: + +```yaml +type: Volume +metadata: + name: corp-vm-tpm-state +spec: + providerRef: Provider/volume-local + layout: + - path: swtpm + type: directory + ownerRef: User/corp-vm-swtpm-system + mode: "0700" + noFollow: true + sensitivity: private + createPolicy: create-if-never-provisioned + repairPolicy: exact-owner + cleanupPolicy: retain-forever + views: + swtpm-process: + path: swtpm + rights: [read, write, create] +``` + +The Device resource owns the swtpm Process via ownerRef. The Volume is the +TPM persistent state root; the Device does NOT own the Volume to prevent +accidental deletion cascade. + +### Broker operations consumed + +- `PrepareSwtpmDir` (hardening/tamper-marker) — once per start cycle. +- `SpawnRunner` (swtpm role) — for each long-lived swtpm Process. + +### Nix options (v3 successors) + +Current: `d2b.vms..tpm.enable = true` → guest `nixos-modules/components/tpm.nix` + +v3 successor: +```nix +d2b.zones..resources."-tpm" = { + type = "Device"; + metadata.ownerRef = "Guest/"; + spec = { + providerRef = "Provider/device-tpm"; + deviceClass = "emulated"; + arbitration = "exclusive"; + settings.startupClear = true; + }; +}; +``` + +The swtpm and swtpm_ioctl binaries are dependencies inside the +`d2b-provider-device-tpm` package closure (referenced by the Provider resource's +`spec.artifactId`); they require no configuration in the Device spec. + +The guest NixOS module wiring (`--tpm socket=...`, `tpm_crb` kernel module, +`tpm2-tools`, session-flush service) remains in the Guest Provider's +`runtime-cloud-hypervisor` Nix module. + +### Tests + +- `packages/d2b-contract-tests/tests/minijail_swtpm_video.rs`: swtpm profile + shape, user namespace propagation, zero host caps. +- `packages/d2b-contract-tests/tests/policy_swtpm_readiness.rs`: readiness contract. +- `packages/d2b-priv-broker/src/ops/swtpm_dir.rs` unit tests: tamper marker, + fresh/existing dir, symlink/mismatch fail-closed. +- `packages/d2b-host/src/swtpm_argv.rs` unit tests: argv golden vectors. +- New: Device reconcile state-machine test (flush → swtpm → Ready). +- New: Tamper-marker failure closes VM start (does not recreate empty TPM). +- New: Delete finalizer does not remove Volume/state. + +## Provider: device-usbip + +### Identity + +```text +Provider/device-usbip +``` + +Crate: `packages/d2b-provider-device-usbip/` +Dossier: `docs/specs/ADR-046-provider-device-usbip.md` (separate, not yet authored) + +### Implements + +Device (physical, exclusive per Guest attachment). + +### Root config schema + +| Field | Type | Default | Bounds | Notes | +| --- | --- | --- | --- | --- | +| `usbipHostKernelModule` | string | `usbip-host` | fixed | Host kernel module | +| `vhciHcdKernelModule` | string | `vhci_hcd` | fixed | Guest kernel module | +| `backendPort` | uint16 | provider-derived | 1–65535 | Per-env deterministic USBIP backend port | + +The usbip host and guest binaries are dependencies bundled inside the +`d2b-provider-device-usbip` package closure. Their executable paths are resolved +from the signed component descriptor inside that closure; they are not +configurable fields in the Device spec. + +### Device spec + +```yaml +spec: + providerRef: Provider/device-usbip + deviceClass: physical + arbitration: exclusive + maxConcurrentClaims: 1 + inventory: + selector: + busClass: usb + label: yubikey-work + vendorId: "1050" + productId: "0407" + serial: null + settings: + env: work +``` + +### Worker processes + +| Process name | Role | Executable | Domain | Execution | Notes | +| --- | --- | --- | --- | --- | --- | +| `device--bind` | EphemeralProcess | `usbip bind --busid ` | system | Host | Bind bus ID at attach | +| `device--unbind` | EphemeralProcess | `usbip unbind --busid ` | system | Host | Release bus ID at detach | +| `device--daemon` | Process | `usbipd --tcp-port ` | system | Host | Long-lived per-Device USBIP backend | + +The per-Device usbipd daemon and proxy Processes are owned by +`Provider/device-usbip`. The Network provider supplies only the +dependency/status/firewall interface: bridge membership, port allocation, and +the `UsbipBindFirewallRule` broker op surface. Network does not own or supervise +USBIP Processes. + +Process resource names follow the `device--` template +(`uid-short` = first 12 hex chars of the Device UID; component from the signed +Provider dossier). VM or Guest human names never appear in Process resource +names. + +### Bring-up sequence (from usbip_state_machine.rs) + +The canonical per-busid USBIP bring-up order is captured in +`packages/d2bd/src/usbip_state_machine.rs` (implemented-and-reachable): + +``` +modprobe → lock → withhold → firewall → backend → bind → proxy +``` + +This sequence is preserved in the device-usbip Provider reconcile loop. Each +step maps to a broker operation or host-side action: + +| Step | Action | Broker op | +| --- | --- | --- | +| `modprobe` | Load `usbip-host` kernel module | EphemeralProcess (modprobe) | +| `lock` | Acquire per-busid OFD lock | — | +| `withhold` | Prevent OS auto-claim of device | sysfs write via broker | +| `firewall` | Add nftables rule | `UsbipBindFirewallRule` | +| `backend` | Start per-env usbipd daemon | `SpawnRunner` (usbip role) | +| `bind` | Bind bus ID to usbip-host | EphemeralProcess (usbip bind) | +| `proxy` | Start TCP proxy on env-host IP | `SpawnRunner` (usbip proxy role) | + +### Broker operations consumed + +- `UsbipBindFirewallRule`: add nftables rule per env/bus; audited/destructive. +- `SpawnRunner` (usbip backend role, `usbip-host` device token): spawn per-env usbipd. +- `SpawnRunner` (usbip proxy role): spawn TCP proxy. + +### Validation + +Bus ID validation follows `packages/d2b-contracts/src/usbip.rs`: +- Max `SYSFS_BUS_ID_MAX` = 31 chars. +- Accepted form: `B`, `B-P`, `B-P.S[.S...]`. +- ASCII digits only. No leading zeros on segments. No metacharacters. + +Vendor/product ID: exactly 4 ASCII hex digits, lower-cased at storage. + +### Guest-side config (Nix) + +Current: `d2b.vms..usbip.yubikey = true` → `nixos-modules/components/usbip.nix` + +v3 guest module wires: +- `vhci_hcd` kernel module. +- `usbip` CLI tools. +- `d2b.guestControl.usbipPath` for guest-side import. + +The guest-side kernel module and tools remain in the Guest's `runtime-cloud-hypervisor` +Nix module under v3. + +### Tests + +- `packages/d2b-contract-tests/tests/usbip_json_contract.rs`: USBIP DTO + serde/schema round-trips, unknown field denial. +- `packages/d2b-contract-tests/tests/usb_network_scoping.rs`: per-env isolation. +- New: Device arbitration conflict test (two Guests claim same bus ID). +- New: Firewall rule ownership-marker preservation. +- New: Bus ID validation corpus (31-char max, metachar rejection, leading-zero segments). +- New: Explicit-attach (EphemeralProcess bind) vs declared (Process bind) path split. + +## Provider: device-security-key + +### Identity + +```text +Provider/device-security-key +``` + +Crate: `packages/d2b-provider-device-security-key/` +Dossier: `docs/specs/ADR-046-provider-device-security-key.md` (separate, not yet authored) + +### Implements + +Device (physical, exclusive per-session lease). + +### Key invariants (from D046) + +- The Provider owns unprivileged Host relay Processes and Guest frontend + Processes plus ceremony/CID/lease. +- The Zone privileged broker only opens/passes the hidraw fd; no path crosses + the public surface. + +### Root config schema + +| Field | Type | Default | Bounds | Notes | +| --- | --- | --- | --- | --- | +| `devices` | list | [] | max 16 | Per-device selector entries | +| `devices[].label` | string | — | `^[a-z][a-z0-9-]{0,62}$` | Stable selector label | +| `devices[].vendorId` | uint16 | — | — | USB vendor ID | +| `devices[].productId` | uint16 | — | — | USB product ID | +| `devices[].serial` | string \| null | null | max 128 chars | Optional serial filter | +| `vsockPort` | uint16 | 14320 | 1–65535 | AF_VSOCK port for host↔guest relay | +| `sessionRingSize` | uint | 32 | 8–256 | Bounded recent-session ring size | +| `leaseTimeoutSecs` | uint | 300 | 30–3600 | Session-level lease timeout | + +The `vsockPort` default 14320 is stable and matches `security-key-guest.nix` +option `d2b.securityKey.vsockPort` default. + +### Device spec + +```yaml +spec: + providerRef: Provider/device-security-key + deviceClass: physical + arbitration: exclusive + maxConcurrentClaims: 1 + inventory: + selector: + busClass: hidraw + label: yubikey-primary + vendorId: "1050" + productId: "0407" + serial: null + settings: + vsockPort: 14320 +``` + +### Process model + +The security-key Provider owns two process classes: + +**Host relay Process** (one per Device resource): + +```yaml +type: Process +metadata: + name: device--sk-relay +spec: + providerRef: Provider/system-minijail + executionRef: Host/host-system + domain: system + processClass: worker + # Package/template from device-security-key Provider dossier +``` + +The relay process: +- Runs unprivileged; receives the hidraw fd from the broker via SCM_RIGHTS. +- Proxies raw CTAP HID packets to the Guest frontend over AF_VSOCK. +- Has no further broker access. +- Restarts on disconnect; clears its lease on clean exit. + +**Current implementation note:** In the v3 baseline the relay is NOT a separate +spawned process — it is a daemon-internal async accept loop in d2bd +(`packages/d2bd/src/lib.rs:10456` `start_sk_accept_loop` and +`packages/d2bd/src/security_key.rs`). ProcessRole::SecurityKeyFrontend is +handled as a ReadinessOnly node that triggers this daemon coroutine rather than +spawning a runner through the broker. The v3 Provider target extracts this relay +logic into the `d2b-provider-device-security-key` crate running as a proper +unprivileged Process. + +**Guest frontend Process** (per opted-in Guest): + +The guest-side UHID virtual device binary is `packages/d2b-sk-frontend/` +(static binary; implemented-and-reachable). In v3 it becomes a Zone Process +resource owned by the device-security-key Provider controller: + +```yaml +type: Process +metadata: + name: device--sk-frontend +spec: + providerRef: Provider/system-minijail + executionRef: Guest/ + domain: user + processClass: worker + # Package/template from device-security-key Provider dossier +``` + +The `d2b-sk-frontend.service` guest systemd unit is removed from the Nix module +when the Process resource is live; the Process controller manages the lifecycle. +The guest-side Nix module (`nixos-modules/components/security-key-guest.nix`) +continues to wire: +- `uhid` kernel module. +- `plugdev` group + udev rule for `KERNEL=="hidraw*" … KERNELS=="0003:1050:0407.*"`. +- The `d2b-sk-frontend` static binary in the Guest store. + +### Ceremony, CID, and lease + +The security-key lease lifecycle: + +1. Guest firmware initiates a CTAP HID operation; the UHID frontend receives it. +2. The frontend sends an acquire-lease request to the host relay over AF_VSOCK. +3. The relay acquires the per-device OFD lock under `/run/d2b/locks/security-key/`. +4. The relay opens the physical hidraw fd via the broker (`SecurityKeyOpenDevice`). +5. The relay creates a session record (`SecurityKeySession`) with a new opaque + `SecurityKeySessionId` and updates Device status to reflect the active claim. +6. CTAP HID traffic is proxied through the relay until the operation ends or + times out. +7. On completion, the relay releases the OFD lock and updates session status. +8. The relay retains the session in a bounded ring (max `sessionRingSize`). + +The broker's `SecurityKeyOpenDevice` operation: +- Accepts only `device_label` and `session_id` (audit correlation); no path. +- Derives the physical hidraw node from the trusted bundle device table. +- Returns the fd over SCM_RIGHTS; never a path in the response body. +- Audit record: subject, device_label digest, session_id, outcome. + +### Status + +Device status `device.claims` carries at most one entry for the active session: + +```yaml +claims: + - holderRef: Guest/corp-vm + claim: exclusive + passthrough: hidraw-relay + claimedAt: 2026-07-22T00:05:00Z + sessionId: sk-corp-vm-42 # opaque; NOT the physical device descriptor + health: healthy +``` + +The `SecurityKeyStatusResponse` and `SecurityKeySessionsResponse` public wire +DTOs are reachable in `packages/d2b-contracts/src/security_key.rs` and are +preserved in v3 with adapted Zone/ResourceRef identifiers. + +### Mutual exclusion + +Security-key proxy and USBIP passthrough of the same physical USB device are +mutually exclusive. This is enforced: +- At Nix eval time in `assertions.nix`. +- At Device controller startup: if a Device/`

.resources` with `spec.childZoneName = ""`, the Zone `` is -declared in `d2b.zones`. There is no bidirectional `parentRef` requirement on -the child Zone; the ZoneLink in the parent is the sole declaration. +An eval assertion verifies that every `ZoneLink` in +`d2b.zones..resources` has `spec.childZoneName = ""`, that each non-root +Zone has at most one uplink resource (enabled or disabled), and that the +local-root Zone has none. +Private allocator bootstrap edges are checked for one parent per child, cycles, +and depth before publication. There is no bidirectional `parentRef` or +undefined reciprocal link; the child-local ZoneLink is the sole resource +declaration for that edge. `ZoneLink.spec.transportProviderRef` is always explicit. There is no default transport and no inference. Omitting `transportProviderRef` from a `ZoneLink` @@ -364,8 +375,8 @@ Compiled ZoneLink bundle entry: apiVersion: resources.d2bus.org/v3 type: ZoneLink metadata: - name: link-dev - zone: root + name: dev-uplink + zone: dev ownerRef: null spec: childZoneName: dev @@ -890,6 +901,16 @@ d2b.zones.local-root.resources.mic-export = { }; }; +# The consumer's uplink is a local resource. The parent allocator supplies the +# sealed route allocation and owns all privileged listener/routing effects. +d2b.zones.work.resources.work-uplink = { + type = "ZoneLink"; + spec = { + childZoneName = "work"; + transportProviderRef = "Provider/transport-unix"; + }; +}; + # Work Zone: import exactly one local AudioService projection. d2b.zones.work.resources.mic-import = { type = "ResourceImport"; @@ -1076,37 +1097,80 @@ Current source: `packages/d2b-core/src/processes.rs` — `ProcessesJson`, ```nix d2b.zones.dev.resources.wayland-proxy = { type = "Process"; + metadata.ownerRef = "Provider/display-wayland"; spec = { - providerRef = "Provider/system-systemd"; # replaces ProcessRole + minijail profile selection - executionRef = "Host/host-system"; # replaces per-VM DAG node host/VM assignment - domain = "user"; - userRef = "User/alice"; - processClass = "service"; - packageRef = "Provider/display-wayland"; # replaces binaryPath in ProcessNode - template = "wayland-proxy-host"; # replaces ProcessRole for template dispatch - configRef = "Volume/wayland-proxy-config"; + providerRef = "Provider/system-systemd"; + executionRef = "Host/host-system"; + domain = "user"; + userRef = "User/alice"; + processClass = "service"; + template = "wayland-proxy-host"; + configRef = "Volume/wayland-proxy-config"; + credentialRefs = []; mounts = [ { volumeRef = "Volume/wayland-proxy-state"; view = "proxy"; mountPath = "/state"; - access = "read-write"; } + access = "read-write"; + required = true; } ]; - budget = { memory = { bytes = 134217728; }; }; - sandbox = { - # Named profile only — replaces raw seccomp program and capability list. - seccompProfile = "system-systemd-default"; - capabilities = []; - noNewPrivs = true; + sandbox = {}; + budget = {}; + networkUsage = { + networkRef = null; + ports = []; + allowEgress = false; + }; + deviceUsage = []; + telemetry = {}; + desiredLifecycle = "running"; + restartPolicy = { + class = "on-failure"; + backoffBase = "1s"; + backoffMax = "60s"; + backoffMultiplier = 2.0; + maxRestarts = 5; + resetAfter = "300s"; + }; + readiness = { + initialDelay = "0s"; + timeout = "30s"; + failureThreshold = 3; + successThreshold = 1; + class = "ready-condition"; + }; + healthCheck = { + enabled = false; + interval = "30s"; + timeout = "5s"; + failureThreshold = 3; + class = "provider-defined"; }; - network = { networkRef = null; ports = []; }; - devices = []; - endpoints = [{ name = "wayland-host-socket"; transport = "unix"; }]; - readiness = { kind = "unix-socket-accept"; }; - restart = { policy = "on-failure"; maxRestarts = 5; backoffMs = 1000; }; + adoptionPolicy = "adopt-on-restart"; + drainTimeout = "30s"; + }; +}; + +# Stable endpoints are separate owned resources, never inline Process fields. +d2b.zones.dev.resources.wayland-host-socket = { + type = "Endpoint"; + metadata.ownerRef = "Process/wayland-proxy"; + spec = { + providerRef = "Provider/display-wayland"; + producerRef = "Process/wayland-proxy"; + endpointClass = "service"; + transport = "unix"; + purpose = "wayland-host-socket"; + locality = "host-local"; }; }; ``` +`providerRef` selects the Process implementation. `metadata.ownerRef` selects +the semantic owner whose signed component descriptor resolves `template` to an +exact executable and digest. No package reference or executable is accepted in +`ExecutionSpec`. + ### Prohibited fields The following are never accepted in any Process or EphemeralProcess Nix @@ -1130,28 +1194,39 @@ Current source: `ProcessRole::StoreVirtiofsPreflight`, `SwtpmPreStartFlush`, ```nix d2b.zones.dev.resources.store-sync-dev-vm = { type = "EphemeralProcess"; + metadata.ownerRef = "Provider/volume-virtiofs"; spec = { providerRef = "Provider/system-minijail"; executionRef = "Host/host-system"; domain = "system"; + userRef = null; processClass = "worker"; - packageRef = "Provider/volume-virtiofs"; template = "store-sync"; configRef = "Volume/store-sync-config"; - successfulTtl = "1h"; # default; explicit for clarity - failedTtl = "24h"; # default - startDeadline = "30s"; + credentialRefs = []; mounts = [ { volumeRef = "Volume/dev-vm-nix-store"; - view = "sync-source"; mountPath = "/source"; access = "read-only"; } + view = "sync-source"; mountPath = "/source"; access = "read-only"; + required = true; } { volumeRef = "Volume/dev-vm-store-farm"; - view = "sync-target"; mountPath = "/target"; access = "read-write"; } + view = "sync-target"; mountPath = "/target"; access = "read-write"; + required = true; } ]; + sandbox = {}; + budget = {}; + networkUsage = null; + deviceUsage = []; + telemetry = {}; + startDeadline = "30s"; + runtimeDeadline = "300s"; + successfulTtl = "1h"; + failedTtl = "24h"; + incidentHold = false; }; }; ``` -`startDeadline` and `runDeadline` use Go-style bounded duration strings. +`startDeadline` and `runtimeDeadline` use Go-style bounded duration strings. Unbounded deadlines are rejected at eval time. ## Volume resource @@ -1520,7 +1595,7 @@ All ref validation runs at Nix eval time: | `roleRef` resolution | The named Role is declared in `d2b.zones..resources` with `type = "Role"` | | `subjects` entries | Each entry is a canonical `/` ref string resolving to a declared resource of the stated type in the same Zone; type must be in the closed subject set | | `transportProviderRef` resolution | The named Provider is declared in `d2b.zones..resources` with `type = "Provider"`; required on every ZoneLink; no default | -| `childZoneName` check | The named child Zone is declared in `d2b.zones`; plain Zone name, not a ResourceRef | +| `childZoneName` check | Plain Zone name, not a ResourceRef; it equals the enclosing `d2b.zones.` key, each non-root Zone has at most one uplink resource, and local root has none | | `ResourceExport` Service-only ref | `providerRef` and `resourceRef` resolve locally; `resourceRef` has the factory's qualified `*Service` type; Device/Endpoint/Binding/backing and cross-Zone targets reject | | `ResourceImport` local route/factory | `providerRef` and `zoneLinkRef` resolve locally; `exportKey` is bounded/not a Ref; expected Service type and projection/factory fingerprints match the signed export and local factory | | Qualified Binding refs | `serviceRef` resolves to the same-Zone matching Service; target ref resolves to a factory-allowed same-Zone Guest/User/Zone; Binding spec is intent-only, observations are status-only, and Binding is non-exportable | @@ -1541,12 +1616,13 @@ declares the type in its installed dossier. ### Universal resource spec shape Every Zone resource is declared under the unified `d2b.zones..resources` -attribute set using a `type`/`spec` envelope that mirrors the canonical -ResourceSpec JSON schema directly: +attribute set using a `type`/optional-authored-`metadata`/`spec` envelope that +mirrors the canonical ResourceSpec JSON schema directly: ```nix d2b.zones..resources. = { type = ""; # string discriminator matching a known ResourceType + metadata.ownerRef = "Provider/owner"; # optional; canonical metadata, never spec spec = { # Exact ResourceType spec fields — identical names to the canonical JSON schema. # No field renaming; no parallel bespoke vocabulary. @@ -1567,7 +1643,8 @@ layers (D088) — the universal `ResourceStatus` base, the ResourceType-common observation and are never authored in Nix. The `status.provider.details` extension schema is signed into and registered with the **Provider package** (resolved via `spec.artifactId`), not authored or emitted by the Zone Nix -configuration; Nix authors only `type` + `spec`. +configuration. Nix authors `type`, optional permitted metadata such as +`ownerRef`, and `spec`; metadata never appears inside `spec`. The `managedBy` field (`configuration | controller | api`) is a core-set management metadata field set exclusively by the core runtime. It is not @@ -1586,11 +1663,11 @@ are derived from the same committed ResourceTypeSchema (`settingsSchemaDigest` in `provider-catalog.json`) — the module system and the build validator use the same source of truth. -There are no Nix-only fields inside a resource declaration. The `type`/`spec` -envelope is the complete authoring shape; `type` is the ResourceType -discriminator and `spec` is emitted verbatim. To disable a resource, omit it -from `d2b.zones..resources` or use the ResourceType's own desired-state -fields if that type defines them. +There are no Nix-only fields inside a resource declaration. `type` is the +ResourceType discriminator, permitted authored `metadata` fields map to the +canonical metadata envelope, and `spec` is emitted verbatim. To disable a +resource, omit it from `d2b.zones..resources` or use the ResourceType's +own desired-state fields if that type defines them. All spec fields are emitted verbatim into the canonical JSON envelope. Derivation references and NixOS system closures belong in `d2b.artifacts`, not @@ -1611,10 +1688,10 @@ inside any resource spec field. ``` `uid`, `generation`, `revision`, and `timestamp` fields are absent from -Nix-emitted artifacts. `ownerRef` defaults to `null`; it may be set in `spec` -only for resources that are explicitly owner-attributed at authoring time (not -for dynamically controller-created resources). `finalizers` defaults to `[]` in -emitted bundles and is managed exclusively at runtime. +Nix-emitted artifacts. `metadata.ownerRef` defaults to `null`; it may be +authored only for ResourceTypes that permit configuration-assigned ownership +and never appears in `spec`. `finalizers` defaults to `[]` in emitted bundles +and is managed exclusively at runtime. ### ResourceTypeSchema validation @@ -1800,19 +1877,17 @@ or bundle JSON. ### Cross-Zone generation ordering -When Zone A has a `ZoneLink` to Zone B, Zone A's bundle includes a `cursorRef` -pointing at the expected Zone B generation revision at compilation time. The -configuration-publication controller verifies Zone B revision before activating -Zone A. +When child Zone A has a local uplink `ZoneLink` to parent Zone B, neither +bundle carries the other Zone's generation or a cross-Zone `cursorRef`. +Advertisement cursors are runtime status owned by A's local ZoneLink handler +and the parent allocator/route engine. -Zone activations are independent. When Zone A activates and Zone B (referenced -via `ZoneLink`) has not yet reached the `cursorRef` revision recorded in -Zone A's bundle, Zone A activates independently and the `ZoneLink` resource -(and any Zone A resources that depend on Zone B state) enter `Degraded` status. -They reconcile asynchronously when Zone B becomes reachable and reaches the -expected revision. Zone A never claims a commit on behalf of Zone B. There is -no cross-Zone atomic activation and no option to block Zone A activation on -Zone B readiness. +Zone activations are independent. Zone A activates without waiting for Zone B. +Until its parent allocation is established and advertisements are current, A's +local `ZoneLink` (plus dependent imports) is `Degraded`. It reconciles +asynchronously when the parent route becomes reachable and current. Neither +Zone claims a commit on behalf of the other, and there is no cross-Zone atomic +activation. ### Resource cleanup contract @@ -2031,8 +2106,8 @@ The Nix compiler detects and rejects at eval time: | Conflict | Rule | | --- | --- | -| Duplicate Zone name | Two `d2b.zones.` entries with the same `id` | -| ZoneLink cycle | A chain of `childZoneName` references in `ZoneLink` resources that loops | +| Duplicate Zone name | Impossible by Nix construction: a Zone name is the unique `d2b.zones.` attr key; there is no Zone `id` field | +| ZoneLink topology cycle | The private allocator bootstrap parent edges bound to child-local ZoneLinks form a cycle | | CIDR overlap | Two Networks in the same Zone with overlapping subnets | | Guest name reserved prefix | A `Guest/` starting with `sys-` | | Owner cycle | Any `ownerRef` chain that loops | @@ -2139,6 +2214,9 @@ Never accepted in any Nix-authored resource spec or generated artifact: - Raw seccomp BPF programs (only named Provider-owned profile refs); - Arbitrary socket addresses or raw file descriptor numbers; - Provider-internal `argv` or environment variable maps; +- `packageRef`, renamed `network`/`devices`/`restart` aliases, or inline + Process `endpoints` (use owner-resolved `template`, canonical + `networkUsage`/`deviceUsage`/`restartPolicy`, and separate Endpoint resources); - `eval` or `builtins.exec` in Nix resource compiler expressions; - `RealmTarget` format strings (`..d2b`) in any spec field. @@ -2150,7 +2228,7 @@ Never accepted in any Nix-authored resource spec or generated artifact: | Provider install resource | Nix catalog + Provider resource → core controller lifecycle → Ready status | | Host system/user Process | Process under Host/host-system with system-systemd; locally held pidfd | | Guest with closure Volume | Guest + virtiofs Volume → per-VM store farm → guest `/nix/store` without direct host store export | -| Cross-Zone ZoneLink | Parent Zone declares unidirectional ZoneLink with `childZoneName`/`transportProviderRef`; activates with child cursor; child update propagates to parent; no `parentRef` in child spec | +| Cross-Zone ZoneLink | Child Zone declares one local uplink with self-matching `childZoneName` and local `transportProviderRef`; parent allocator binds the private edge and owns privileged route creation; no reciprocal resource or `parentRef` | | Configuration rollback | Two-generation rollback restores prior Zone resource state | | Ref validation rejection | Malformed or missing ref fails eval with structured error | | Conflict detection | CIDR overlap, owner cycle, and duplicate type/name rejection at eval time | @@ -2235,7 +2313,7 @@ contract work item (ADR046-bus-011/ADR046-bus-012). Cross-reference: | Detailed design | `d2b.zones..resources. = { type = ""; spec = { ... }; }` — single attrset covering all ResourceTypes; `type` discriminates dispatch; `spec` fields mirror exact ResourceTypeSchema field names and nesting; Nix option types/defaults/docs generated from `docs/reference/schemas/v3/.json`; no Nix-only fields inside resource declarations; `metadata.name` derives from attr key; `metadata.zone` derives from enclosing zone attr key; `apiVersion` defaulted; `uid`/`generation`/`revision`/`status`/`managedBy` never in Nix; `resource_name` regex `^[a-z][a-z0-9-]*$`; ref validation assertions; `WorkloadProviderKind` → Guest/Host mapping per disposition table above; `Capability` → Role verb mapping per resource-api/authz foundation spec; Zone self-resource spec is `{}`; `retainedGenerations`/`trustedPublishers` are Zone-level compiler settings not emitted in Zone spec | | Integration | `nixos-modules/default.nix` imports new options files; old realms options coexist until ADR046-nix-002 | | Data migration | Operator configs migrate `d2b.realms.*` → `d2b.zones.*`; `d2b.vms.*` → `d2b.zones..resources.*` with `type = "Guest"` | -| Validation | nix-unit vectors for each ResourceType; ref-validation rejection vectors; malformed ref error shape; ZoneLink `childZoneName` resolves declared Zone; missing `transportProviderRef` fails eval; `managedBy` in spec rejected at eval; Zone spec is `{}` (no `parentRef`, `retainedGenerations`, etc.) | +| Validation | nix-unit vectors for each ResourceType; ref-validation rejection vectors; malformed ref error shape; child-local ZoneLink `childZoneName` must equal its enclosing Zone key; a second uplink resource (even disabled) and a local-root uplink fail eval; missing/local-unresolved `transportProviderRef` fails eval; allocator bootstrap topology rejects multi-parent and cycles; `managedBy` in spec rejected at eval; Zone spec is `{}` (no `parentRef`, `retainedGenerations`, etc.) | | Tests | `tests/unit/nix/cases/zones-options.nix`, `tests/unit/nix/cases/zones-ref-validation.nix`, `tests/unit/nix/cases/zones-zonelink.nix` | | Drift pin | `make nix-unit-pin` after adding cases | | Removal proof | `options-realms*.nix` removed after `options-zones*.nix` achieves parity and parity drift test passes | @@ -2312,11 +2390,11 @@ contract work item (ADR046-bus-011/ADR046-bus-012). Cross-reference: | Current source | `nixos-modules/processes-json.nix` (`VmProcessDag`, `ProcessRole`, `binaryPath`, `argv`, `share.source == "/nix/store"` sentinel); `packages/d2b-core/src/processes.rs` (`ProcessRole` enum — all variants in disposition table above) | | Reuse action | extract and adapt | | Destination | `nixos-modules/resources-zones-processes.nix`; emits `zones//processes.json` | -| Detailed design | Process/EphemeralProcess resource serialization per disposition table; no free-form `binaryPath` or `argv`; template refs; mounts from `volumeRef`; sandbox from named profile; VsockRelay → `Process` under `Provider/transport-vsock`; GuestSshReadiness retired at v3 cutover; Usbip long-lived backend/proxy → `Process`, Usbip per-busid attach/detach → `EphemeralProcess`, all owned by `Provider/device-usbip` | +| Detailed design | Process/EphemeralProcess resource serialization per disposition table and the frozen `ExecutionSpec`: exact `providerRef`/`executionRef`/domain/user/processClass/template/config/credential/mount/sandbox/budget/`networkUsage`/`deviceUsage`/telemetry names; semantic owner from `metadata.ownerRef` with template fallback through the Process Provider; no `packageRef`, free-form `binaryPath`, `argv`, renamed network/device fields, or inline endpoints; stable produced endpoints are separate owned `Endpoint` resources with `producerRef`; Process uses canonical `restartPolicy` fields; EphemeralProcess uses `runtimeDeadline` and has no restart policy; VsockRelay → `Process` under `Provider/transport-vsock`; GuestSshReadiness retired at v3 cutover; Usbip long-lived backend/proxy → `Process`, Usbip per-busid attach/detach → `EphemeralProcess`, all owned by `Provider/device-usbip` | | Integration | `processes.json` replaces `cfg._bundle.processesJson`; Process Providers read the new format | | Data migration | Processes are emitted from v3 Process/EphemeralProcess resources; full d2b 3.0 reset; no v2 process artifact import. | -| Validation | Process resource schema vectors; no-raw-path assertion; ProcessRole parity test (every variant has a test case) | -| Tests | `tests/unit/nix/cases/zones-processes.nix`; `packages/d2b-contract-tests/tests/processes-schema.rs` | +| Validation | Process and EphemeralProcess exact-schema vectors; reject `packageRef`, `network`, `devices`, `endpoints`, `restart`, and `runDeadline`; accept canonical `networkUsage`, `deviceUsage`, `restartPolicy`, `runtimeDeadline`, and separately owned Endpoint with `producerRef`; no-raw-path assertion; ProcessRole parity test (every variant has a test case) | +| Tests | `tests/unit/nix/cases/zones-processes.nix`; `tests/unit/nix/cases/zones-process-exact-mirror.nix`; `packages/d2b-contract-tests/tests/processes-schema.rs` | | Drift pin | `make test-drift` after schema changes | | Removal proof | `processes-json.nix` and current `processes.json` schema removed after all Process Providers consume `zones//processes.json` | diff --git a/docs/specs/ADR-046-resources-zone-control.md b/docs/specs/ADR-046-resources-zone-control.md index 4ce75a683..860c5ae0a 100644 --- a/docs/specs/ADR-046-resources-zone-control.md +++ b/docs/specs/ADR-046-resources-zone-control.md @@ -258,27 +258,34 @@ Normal daemon restart, upgrade, or Provider lifecycle changes do not delete Zone ### 3.1 Role -`ZoneLink/` represents one parent/child Zone delegation. It is stored in -the **parent** Zone. It carries the child Zone's identity, transport/session -requirements, cursor tracking for resource synchronization, and connection -health. +`ZoneLink/` represents one parent/child Zone delegation as the child +Zone's local uplink. It is authored and stored in the **child** Zone. It carries +that local child identity, transport/session requirements, cursor tracking for +resource synchronization, and connection health. `spec.childZoneName` MUST +equal the enclosing Zone's self-name. A parent accesses child Zone resources exclusively through the child's `d2b.resource.v3` service over a ComponentSession routed via the transport Provider named in ZoneLink. The parent never receives a database handle, credential, token, or cross-Zone ResourceRef. -**Controller/owner**: The `zone link/delegation` handler of the fixed -core-controller process owns ZoneLink reconciliation. No other controller may -update ZoneLink spec or status. +The provisioning parent allocator owns privileged listener creation, placement, +and route-namespace effects. It binds the child-local ZoneLink UID and child +identity to exactly one parent edge in sealed bootstrap state and keeps only +allocator/route-engine state in the parent; it does not create a reciprocal +parent-store resource. **Controller/owner**: the child Zone's `zone +link/delegation` handler in the fixed core-controller process owns the +ZoneLink resource and its status. It consumes allocator observations through +the authenticated session and has no direct privileged-effect path. No other +controller may update ZoneLink spec or status. ### 3.2 Metadata | Field | Rule | | --- | --- | -| `metadata.name` | Operator-assigned; locally unique in the parent Zone; no structural constraint beyond ResourceName regex | -| `metadata.zone` | Parent Zone name; ZoneLink is always in the parent | -| `metadata.ownerRef` | Optional; may be owned by a managing Provider (e.g. a runtime Guest that hosts the child Zone) | +| `metadata.name` | Operator-assigned; locally unique in the child Zone; no structural constraint beyond ResourceName regex | +| `metadata.zone` | Child Zone self-name; ZoneLink is always local to that child | +| `metadata.ownerRef` | Optional same-Zone ref to the managing transport/runtime Provider; never a parent-Zone ref | | `metadata.finalizers` | Core adds `core.zone-link-drain` before deletion; transport Provider may add its own finalizer | | `metadata.deletionRequestedAt` | Normal deletion by operator or owning resource | @@ -288,8 +295,8 @@ update ZoneLink spec or status. apiVersion: resources.d2bus.org/v3 type: ZoneLink metadata: - name: guest-link - zone: parent + name: guest-uplink + zone: guest uid: generation: 1 revision: @@ -315,7 +322,7 @@ spec: **`spec.transportProviderRef`** (required; no default): - Must match `^Provider/transport-[a-z][a-z0-9-]*$`; any other form is rejected at admission. -- References a Provider installed in the same Zone whose name begins with `transport-`; that Provider must be in `Ready` phase before any connection is attempted. +- References a Provider installed in the same child Zone whose name begins with `transport-`; that Provider must be in `Ready` phase before any connection is attempted. - The named transport Provider exports a ZoneLink schema extension (`transportSettings` schema); the resource compiler validates `spec.transportSettings` against that schema at build time. - No transport Provider is pre-installed; an operator must install one before creating ZoneLinks that require it. @@ -365,21 +372,22 @@ ZoneLink.status extends common status with: | Field | Type | Rules | | --- | --- | --- | -| `childZoneUid` | UID string or null | Observed immutable UID of child Zone resource; null until first successful connection | -| `connected` | bool | Whether a ComponentSession to the child Zone API is currently established | +| `childZoneUid` | UID string or null | Immutable UID of this local child Zone as acknowledged by the parent allocation; null until first successful connection | +| `connected` | bool | Whether the allocator-bound ComponentSession to the parent route endpoint is established | | `lastConnectedAt` | RFC 3339 UTC or null | Most recent successful session establishment | | `lastDisconnectedAt` | RFC 3339 UTC or null | Most recent disconnect | -| `lastSentRevision` | u64 or null | Highest parent-applied revision sent to child | -| `lastAckedRevision` | u64 or null | Highest revision acknowledged by child | -| `lastReceivedRevision` | u64 or null | Highest child-source revision received | -| `lastAppliedRevision` | u64 or null | Highest revision applied to parent store | +| `lastSentRevision` | u64 or null | Highest child advertisement/local intent revision sent to the parent | +| `lastAckedRevision` | u64 or null | Highest child revision acknowledged by the parent | +| `lastReceivedRevision` | u64 or null | Highest parent route/export advertisement revision received | +| `lastAppliedRevision` | u64 or null | Highest parent advertisement revision applied to child-local routing/import state | | `linkEpoch` | u64 | Monotonic counter; increments on every reconnect establishing a new session generation | | `pendingLocalIntents` | u32 | Count of locally queued intents while disconnected; bounded | -| `childAuthorized` | bool | Whether parent's mapped subject is authorized in the child Zone | +| `childAuthorized` | bool | Whether the parent allocator accepted and authorized this child Zone subject | -`childZoneUid` allows the parent to detect that a child Zone was destroyed and -recreated. If `childZoneUid` changes across reconnects, the ZoneLink handler -must clear cursor state and relist/rewatch from revision 0 in the child. +`childZoneUid` lets both ends detect that this child Zone was destroyed and +recreated. If it changes across reconnects, the child-local ZoneLink handler +clears local cursor state and the parent allocator discards the corresponding +route allocation before both sides relist/rewatch from revision 0. Cursor fields (`lastSentRevision`, `lastAckedRevision`, `lastReceivedRevision`, `lastAppliedRevision`) reflect the `zone_link_cursors` redb table entry for @@ -396,7 +404,8 @@ are defined by the ZoneLink session and cursor protocol. | `Failed` | Cannot connect after retry policy exhausted, or child permanently denied | | `Unknown` | Transport state cannot be determined | -Zone never participates in ZoneLink phase (ZoneLink belongs to the parent Zone). +The parent Zone has no ZoneLink resource phase. The child-local ZoneLink alone +reports the edge's resource phase from authenticated allocator observations. Closed condition types for ZoneLink: @@ -404,17 +413,17 @@ Closed condition types for ZoneLink: | --- | --- | | `TransportReachable` | Transport provider reports the endpoint is reachable | | `SessionEstablished` | ComponentSession handshake completed successfully | -| `ChildAuthorized` | Parent subject has required authorization in child Zone | +| `ChildAuthorized` | Parent allocator accepted the authenticated child subject and route allocation | | `CursorSynchronized` | Parent/child revision cursors are within expected bounds | | `LocalIntentsDrained` | No pending locally queued intents remain | | `DisabledByOperator` | `spec.disabled: true`; link is intentionally inactive | ### 3.6 Local intents while disconnected -When the child Zone is unreachable, the parent may record bounded local -ZoneLink intents — operator intent that cannot be applied until reconnection. -These are not resource writes in the child. They are persisted locally as -ZoneLink-owned metadata-only entries. +When the parent route endpoint is unreachable, the child may record bounded +local ZoneLink intents — operator intent that cannot be forwarded until +reconnection. These are not resource writes in the parent. They are persisted +in the child store as ZoneLink-owned metadata-only entries. Rules: @@ -429,14 +438,16 @@ Rules: ### 3.7 ownerRef, finalizers, and deletion -`metadata.ownerRef` is optional. A Guest runtime Provider may own the ZoneLink -for the child Zone it hosts. When owner is deleted, ZoneLink deletion follows +`metadata.ownerRef` is optional and always same-Zone. A local transport/runtime +Provider may own the uplink it manages. A parent allocator, parent Provider, or +parent Zone resource can never be the owner because that would require a +cross-Zone ref. When the local owner is deleted, ZoneLink deletion follows standard child-first finalizer protocol. Core adds `core.zone-link-drain` to `metadata.finalizers` before processing a delete request. The ZoneLink handler: -1. Closes the ComponentSession to the child Zone. +1. Closes the allocator-bound ComponentSession to the parent route endpoint. 2. Clears the cursor table entry. 3. Removes any pending local intents. 4. Clears `core.zone-link-drain`. @@ -2016,6 +2027,19 @@ d2b.zones.local-root.resources.mic-export = { }; }; +# Consumer child Zone: declare the one local uplink used by imports. The +# provisioning parent allocator owns privileged listener and routing effects. +d2b.zones.work.resources.work-uplink = { + type = "ZoneLink"; + spec = { + childZoneName = "work"; + transportProviderRef = "Provider/transport-unix"; + transportSettings = {}; + transportCredentials = []; + disabled = false; + }; +}; + # Consumer Zone: import exactly one local AudioService projection. d2b.zones.work.resources.mic-import = { type = "ResourceImport"; @@ -2358,7 +2382,8 @@ subjectRef: other-zone:User/alice # REJECTED ``` A parent Zone accesses child Zone resources by calling the child Zone's -`d2b.resource.v3` API over the ZoneLink ComponentSession. The parent's +`d2b.resource.v3` API over the allocator-bound ComponentSession represented by +the child-local ZoneLink. The parent's local resources (e.g. a local `Process` that represents a child Zone workload) do not contain cross-Zone ResourceRefs; they contain only local refs valid in the parent Zone. @@ -2366,12 +2391,11 @@ the parent Zone. ### 10.3 ZoneLink parent/child access model ``` -Parent Zone Child Zone - ZoneLink/guest-link Zone/guest - (stores cursor, link state) (authoritative resource store) - | | - | ComponentSession | - | (via transport Provider) | +Parent Zone / allocator Child Zone + private route allocation Zone/guest + (privileged listeners/routes) ZoneLink/guest-uplink + | (resource, cursor, intent state) + | allocator-bound ComponentSession | |------- d2b.resource.v3 Get ----------> | |<------- response (child data) --------- | ``` @@ -2381,15 +2405,18 @@ parent's mapped subject in the child Zone is established through the transport Provider's authenticated ComponentSession using `spec.transportProviderRef`, `spec.transportSettings`, and resolved `spec.transportCredentials`. The parent cannot read child resources it is not -authorized for; the child authorization engine is the sole arbiter. +authorized for; the child authorization engine is the sole arbiter. The +ZoneLink spec and all of its refs resolve in the child store. Parent-side +allocation/route state is not a resource and is not a reciprocal ZoneLink. -A disconnected parent: +A disconnected child uplink: -- records local ZoneLink intents but does not claim child resource states - changed; +- records outbound local ZoneLink intents in the child but does not claim + parent resource state changed; - on reconnect, re-authenticates and applies/rejects pending intents against - current child revision; -- never uses cached child resource state for authorization decisions. + the current parent revision; +- never lets either endpoint use cached remote resource state for authorization + decisions. --- @@ -2419,24 +2446,27 @@ Guest, or Process resources. Those are owned by their respective handlers. ### 11.2 ZoneLink controller algorithm -The `zone link/delegation` handler reconciles ZoneLink: +The child Zone's `zone link/delegation` handler reconciles its local uplink: 1. **Startup relist**: List all ZoneLink resources; for each, read cursor from - `zone_link_cursors` table; verify `childZoneUid` field against last known - child UID. + the child-local `zone_link_cursors` table; verify `childZoneName` equals the + local Zone self-name and `childZoneUid` against the local Zone UID. 2. **Transport resolution**: Resolve `spec.transportProviderRef`; wait for - that Provider to be `Ready`. Open a ComponentSession to the child Zone's - `d2b.resource.v3` service endpoint using the transport Provider's session - factory with `spec.transportSettings` and resolved - `spec.transportCredentials`. -3. **Authentication**: Establish ComponentSession using the transport; perform - Noise handshake; map to child-local subject/RoleBinding. -4. **Child authorization check**: Verify the parent's mapped subject has at - least `get|list` permissions in the child Zone; set `ChildAuthorized`. -5. **Cursor recovery**: Compare `childZoneUid` with `last_child_zone_uid`; if - changed, reset cursor to 0. -6. **Watch/relist**: Open watch from `lastAppliedRevision`; if cursor expired, - relist and re-watch. + that same-child Provider to be `Ready`. Submit the ZoneLink UID, child + identity, and transport binding to the provisioning parent allocator. The + allocator alone creates privileged listeners/route namespace and returns + the pre-bound transport through sealed bootstrap authority. +3. **Authentication**: Establish the allocator-bound ComponentSession; perform + the Noise handshake; authenticate the parent allocator and present the + child-local subject. +4. **Child authorization check**: Require the parent to acknowledge that the + child subject and requested ceiling fit the sealed allocation; set + `ChildAuthorized`. Parent access to `d2b.resource.v3` is independently + checked by the child authorization engine on every call. +5. **Cursor recovery**: Compare `childZoneUid` with the local Zone UID; if it + changed, reset local cursor state and require a fresh parent allocation. +6. **Watch/relist**: Receive parent route/export advertisements from + `lastAppliedRevision`; if the cursor expired, relist and re-watch. 7. **Status update**: Write connection state, cursor values, link epoch. 8. **Reconnect loop**: On disconnect, attempt reconnect with exponential backoff; write `lastDisconnectedAt`, `connected=false`, `Unknown` phase until @@ -2444,8 +2474,9 @@ The `zone link/delegation` handler reconciles ZoneLink: 9. **Drain on delete**: On `deletionRequestedAt`, gracefully close session, apply drain finalizer, update status to reflect drain. -ZoneLink does not store child resources in the parent store. Parent caches -are session-scoped only. +ZoneLink does not copy parent or child resources across stores. The child store +contains only the local ZoneLink row/cursors/intents; parent allocator and +route-engine state is session/allocation scoped. ### 11.3 Provider lifecycle algorithm @@ -2753,7 +2784,7 @@ Stable error codes for Zone control types: | `zone-self-resource-mismatch` | Store zone_name/zone_uid differs from Zone resource | | `zone-spec-invalid` | Zone spec is not exactly `{}` | | `zone-link-transport-unavailable` | Transport Provider unavailable or unreachable | -| `zone-link-child-auth-denied` | Parent subject not authorized in child Zone | +| `zone-link-child-auth-denied` | Parent allocator rejected the authenticated child subject or requested allocation ceiling | | `zone-link-child-uid-changed` | Child Zone UID changed; cursor reset required | | `provider-package-digest-mismatch` | Package content differs from declared digest | | `provider-trust-revoked` | Trust epoch revoked or signature invalid | @@ -2892,12 +2923,12 @@ d2b.artifacts.transport-unix = { type = "provider"; }; -d2b.zones.parent.resources.transport-unix = { +d2b.zones.guest.resources.transport-unix = { type = "Provider"; spec = { artifactId = "transport-unix"; config = {}; }; }; -d2b.zones.parent.resources.guest-link = { +d2b.zones.guest.resources.guest-uplink = { type = "ZoneLink"; spec = { childZoneName = "guest"; @@ -2910,10 +2941,12 @@ d2b.zones.parent.resources.guest-link = { ``` -`metadata.name` (`guest-link`) and `metadata.zone` (`parent`) derive from the +`metadata.name` (`guest-uplink`) and `metadata.zone` (`guest`) derive from the attrset keys. Eval-time checks: `spec.childZoneName` matches -`^[a-z][a-z0-9-]*$`; `spec.childZoneName ≠ "parent"`; `spec.limits.*` within -bounds. +`^[a-z][a-z0-9-]*$` and equals the enclosing Zone key; the local root has no +uplink; at most one ZoneLink resource (enabled or disabled) exists per non-root +Zone; `spec.limits.*` remain within bounds. The parent edge comes only from the +provisioning allocator's sealed bootstrap state. ### 14.3 Provider installation @@ -3190,7 +3223,7 @@ Zone is the self-resource; it is created by the Zone runtime on initialization "apiVersion": "resources.d2bus.org/v3", "resourceType": "ZoneLink", "metadata": { - "name": "guest-link", "zone": "parent", "uid": null, "generation": 1, + "name": "guest-uplink", "zone": "guest", "uid": null, "generation": 1, "revision": null, "labels": {}, "annotations": {}, "ownerRef": null, "finalizers": [], "deletionRequestedAt": null, "createdAt": null, "updatedAt": null @@ -3212,8 +3245,9 @@ Zone is the self-resource; it is created by the Zone runtime on initialization } ``` -`childZoneUid` is `null` in the bundle; the ZoneLink controller writes it on -first successful connection and uses it to detect UID changes. +`childZoneUid` is `null` in the bundle; the child-local ZoneLink controller +writes the local Zone UID after the parent allocator acknowledges it and uses +it to detect replacement across reconnects. #### Provider canonical envelope @@ -3408,7 +3442,8 @@ Three ordered phases validate the Nix configuration before a bundle is activated | No cross-Zone refs anywhere in `spec` (no `/ResourceType/name` form) | Nix `assert` | eval error | | `type = "Provider"` and name `system-core` or `system-minijail` rejected | Nix `assert` | eval error with message | | `type = "Zone"` and name ≠ Zone attrset key rejected | Nix `assert` | eval error | -| ZoneLink: `spec.childZoneName ≠` Zone attrset key | Nix `assert` | eval error | +| ZoneLink: `spec.childZoneName ==` Zone attrset key | Nix `assert` | eval error | +| Non-root Zone has at most one ZoneLink resource; local root has none | Nix `assert` | eval error | | `spec.transportProviderRef` matches `^Provider/transport-[a-z][a-z0-9-]*$` — ZoneLink only | Nix `assert` | eval error | | `$credentialRef` target in any `spec.*` string must name a `type = "Credential"` resource in the same Zone | Nix `assert` | eval error | | No `$`-prefixed keys in `spec` other than `$credentialRef` | Nix `assert` | eval error | @@ -3659,15 +3694,18 @@ Rollback atomically: | Test | Assertion | | --- | --- | -| `zonelink-reconnect-child-uid-change` | On reconnect with changed child UID, cursor resets to 0 and `childZoneUid` updates | +| `zonelink-reconnect-child-uid-change` | On reconnect after local child Zone UID replacement, local cursor resets to 0, the parent allocation is recreated, and `childZoneUid` updates | | `zonelink-disconnect-unknown-phase` | On transport disconnect ZoneLink phase becomes Unknown within one reconcile | | `zonelink-intent-queue-limit` | Queuing more than 256 local intents returns `backpressure` | | `zonelink-disabled-no-reconnect` | `spec.disabled=true` prevents reconnection and sets `DisabledByOperator` condition | -| `zonelink-child-auth-denied-failed` | Child Zone rejecting parent subject sets `Failed` phase and `ChildAuthorizationDenied` condition | +| `zonelink-child-auth-denied-failed` | Parent allocator rejecting the child subject/allocation sets `Failed` phase and `ChildAuthorizationDenied` condition | | `zonelink-drain-closes-session` | Deletion drains local intents and closes session before removing resource | | `zonelink-transport-provider-ref-required` | ZoneLink without `spec.transportProviderRef` fails admission | | `zonelink-transport-ref-pattern-enforced` | `spec.transportProviderRef = "Provider/network-local"` fails admission because it does not match the required `transport-*` pattern | | `zonelink-transport-credentials-max` | ZoneLink with 9 `transportCredentials` entries fails admission | +| `zonelink-child-name-matches-store` | ZoneLink whose `childZoneName` differs from its enclosing child Zone is rejected | +| `zonelink-one-child-local-uplink` | A second ZoneLink (even disabled) in one child Zone and any uplink in local root are rejected | +| `zonelink-parent-has-no-reciprocal-row` | Reconcile creates parent allocator/route state but no parent-store ZoneLink resource | ### 15.3 Provider tests @@ -3759,7 +3797,8 @@ Rollback atomically: | `nix-eval-artifact-id-format` | `spec.artifactId = "Bad_ID"` fails eval with label regex assertion | | `nix-eval-credentialref-declared` | `d2b.zones.dev.credentialRef "missing"` for undeclared Credential fails eval | | `nix-eval-dollar-key-rejected` | Config `{ "$secret" = "x"; }` fails eval | -| `nix-eval-zonelink-self-loop-rejected` | ZoneLink with `childZoneName` equal to parent Zone name fails eval | +| `nix-eval-zonelink-child-name-mismatch-rejected` | Child-local ZoneLink with `childZoneName` unequal to its enclosing Zone key fails eval | +| `nix-eval-zonelink-second-uplink-rejected` | A second child-local uplink (even disabled), or any local-root uplink, fails eval | | `nix-eval-zonelink-limits-maxpendingintents-bound` | `maxPendingIntents = 1025` fails eval | #### Phase 2 — Build tests @@ -3943,7 +3982,7 @@ Evidence classes: | Symbol | File | Evidence class | ADR-0046 mapping | | --- | --- | --- | --- | -| `RealmControllersJson`, `RealmControllerConfig`, `RealmControllerPlacement`, `RealmControllerLocalRuntime`, `RealmControllerLocalWorkload`, `RealmAllocatorBinding`, `RealmDaemonConfig`, `RealmBrokerConfig` | `d2b-core/src/realm_controller_config.rs:144,190,256,278,434,541,497,511` | `implemented-and-reachable` | Loaded in live daemon startup at `d2bd/src/lib.rs:1408` and in serve path at line `16741`; `WorkloadTargetIndex::build_from_controllers` is called live. Contains per-realm socket/state paths, placement, providers, workloads. Maps to: Zone self resource (the realm metadata row); ZoneLink (per-realm parent socket ref); Provider (per-realm provider config rows); NOT replaced by a single file — replaced by resource store entries | +| `RealmControllersJson`, `RealmControllerConfig`, `RealmControllerPlacement`, `RealmControllerLocalRuntime`, `RealmControllerLocalWorkload`, `RealmAllocatorBinding`, `RealmDaemonConfig`, `RealmBrokerConfig` | `d2b-core/src/realm_controller_config.rs:144,190,256,278,434,541,497,511` | `implemented-and-reachable` | Loaded in live daemon startup at `d2bd/src/lib.rs:1408` and in serve path at line `16741`; `WorkloadTargetIndex::build_from_controllers` is called live. Contains per-realm socket/state paths, placement, providers, workloads. Maps to: Zone self resource (the realm metadata row); child-local ZoneLink plus private parent allocator binding (per-realm parent socket/edge data); Provider (per-realm provider config rows); NOT replaced by a single file — replaced by resource store entries plus sealed allocator state | | `WorkloadTargetIndex`, `TargetResolution`, `TargetResolutionError` | `d2bd/src/workload_target_index.rs:93,28,56` | `implemented-and-reachable` | Built from `RealmControllersJson` in live serve path (`d2bd/src/lib.rs:16745`); resolves `..d2b` to VM name. Maps to resource API `resourceRef` lookup table in ADR 0046 API layer | #### Allocator types @@ -3985,7 +4024,7 @@ Evidence classes: | `d2b.realms..*`, `providerType` submodule, `providerKind = "^[a-z][a-z0-9-]*$"`, `placementProvider`, `providerSpecificPlacement` | `nixos-modules/options-realms.nix:26,39,55,65,170` | `generated-or-eval-contract` | Authoring interface for realm/provider declarations. Label regex identical to `LABEL_PATTERN` in `ids.rs`. Adapted to `d2b.zones..providers.*` in ADR046-zone-control-007 | | `d2b.realms..workloads.*`, `kind`, `placement`, `legacyVmName` | `nixos-modules/options-realms-workloads.nix` | `generated-or-eval-contract` | Workload authoring submodule; `kind = "local-vm"` / `"qemu-media"` → Guest resource Nix options; **`kind = "unsafe-local"` → Host resource Nix options** (`defaultDomain=user`, `allowedDomains=[user]`); `kind = "provider-placeholder"` → Guest/Host per provider semantics | | `d2b.realms..network.*` | `nixos-modules/options-realms-network.nix` | `generated-or-eval-contract` | Network/bridge authoring; maps to Network resource (not Zone control types) | -| `realm-controllers.json` (`RealmControllersJson`), `schemaVersion = "v2"`, `runtimeState = "metadata-only"`, per-realm rows | `nixos-modules/realm-controller-config-json.nix` (emitter); `d2b-core/src/realm_controller_config.rs:144` (deserializer) | `generated-or-eval-contract` (Nix); `implemented-and-reachable` (consumption in daemon) | **Loaded live in daemon** at `d2bd/src/lib.rs:1408,16741`. Runtime routing "remains inert" but `WorkloadTargetIndex` is built from it. Maps to: Zone self resource (identity/path row); ZoneLink (parent socket ref); Provider (provider config rows). The bundle artifact is **replaced** by resource store entries; until then, the schema is the live authority | +| `realm-controllers.json` (`RealmControllersJson`), `schemaVersion = "v2"`, `runtimeState = "metadata-only"`, per-realm rows | `nixos-modules/realm-controller-config-json.nix` (emitter); `d2b-core/src/realm_controller_config.rs:144` (deserializer) | `generated-or-eval-contract` (Nix); `implemented-and-reachable` (consumption in daemon) | **Loaded live in daemon** at `d2bd/src/lib.rs:1408,16741`. Runtime routing "remains inert" but `WorkloadTargetIndex` is built from it. Maps to: Zone self resource (identity/path row); child-local ZoneLink plus private parent allocator binding (parent socket/edge data); Provider (provider config rows). The bundle artifact is replaced by child resource-store entries and sealed allocator state; until then, the schema is the live authority | | `realm-identity.json` (`RealmIdentityConfigJson`), identity refs, fingerprints | `nixos-modules/realm-identity-config-json.nix` (emitter); `d2b-realm-core/src/identity_config.rs:19` (deserializer) | `generated-or-eval-contract` (Nix); `implemented-and-reachable` (consumption in daemon) | Loaded live at `d2bd/src/lib.rs:1425`; trust sessions "remain inert". Maps to Zone runtime identity key-ref state and store metadata, not Zone.spec | | `allocator.json` resource request rows (per-realm: cgroup, nftables, bridges, socket paths), `LocalRootAllocatorEngine` | `nixos-modules/allocator-json.nix` (emitter); `d2b-realm-core/src/allocator_engine.rs:332` (engine) | `generated-or-eval-contract` (Nix); `implemented-and-reachable` (engine) | Allocator resource requests are generated per realm from `d2b.realms.*` Nix config and consumed by the allocator at runtime. Maps to Zone runtime startup resource claiming. **Mechanism, not a ResourceType** | | `realm-entrypoints.json`, loaded from `/run/current-system/sw/share/d2b/realm-entrypoints.json` | `nixos-modules/host-daemon.nix:385` (emitter); `d2b/src/lib.rs:5239` (loader) | `generated-or-eval-contract` (Nix); `implemented-and-reachable` (CLI consumption) | Loaded by `d2b` CLI in live routing path for realm-qualified targets. Maps to ZoneLink access binding (CLI consumes it to find realm socket; replaced by ZoneLink resource + ComponentSession lookup) | @@ -4090,10 +4129,10 @@ None of the following exist in baseline: | Reuse source | main `a1cc0b2d`: `packages/d2b-session/src/lifecycle.rs`, `d2b-session-unix/src/adapter.rs` for reconnect/transport precedents | | Reuse action | extract and adapt (`SecurePeerSession` Noise model → ComponentSession Noise KK; `SessionLifecycle`/`SessionPhase` → ZoneLink session reconnect loop and connection detail fields; `Connecting`/`Established` current evidence phases drive `status.connected` and `status.phase` transitions to `Pending`/`Ready`, not direct phase values; `RouteTreeEngine.decide_route()` → cursor tracking; `RealmIdentityStore` enrollment → ZoneLink child key-pin) | | Destination | `packages/d2b-contracts/src/v3/zone_link.rs`; `packages/d2b-core-controller/src/zone_link.rs` | -| Detailed design | ZoneLink schema with resolved `spec.transportProviderRef`, `spec.transportSettings`, and `spec.transportCredentials` fields; cursor tracking; reconnect loop with exponential backoff; local intent queue (max 256 entries); child UID change detection; drain finalizer; Nix ZoneLink options | -| Integration | core-controller zone_link handler; redb `zone_link_cursors` table; d2b-bus transport resolver; ComponentSession lifecycle | +| Detailed design | Child-local ZoneLink schema with self-matching `spec.childZoneName` plus same-Zone `spec.transportProviderRef`, `spec.transportSettings`, and `spec.transportCredentials`; at most one uplink resource per non-root Zone and none in local root; child-store cursor/intent state; reconnect loop with exponential backoff; local intent queue (max 256 entries); local child UID change detection; drain finalizer; no reciprocal parent-store resource; parent allocator owns privileged listener/placement/route allocation effects and exposes only a sealed bootstrap/allocation interface | +| Integration | child core-controller zone_link handler; child redb `zone_link_cursors` table; parent allocator and route engine through sealed allocation authority; d2b-bus transport resolver; ComponentSession lifecycle | | Data migration | Destructive reset; no v2 Realm peer migration | -| Validation | `zonelink-reconnect-child-uid-change`, `zonelink-disconnect-unknown-phase`, `zonelink-intent-queue-limit`, `zonelink-disabled-no-reconnect`, `zonelink-child-auth-denied-failed`, `zonelink-drain-closes-session` | +| Validation | `zonelink-reconnect-child-uid-change`, `zonelink-disconnect-unknown-phase`, `zonelink-intent-queue-limit`, `zonelink-disabled-no-reconnect`, `zonelink-child-auth-denied-failed`, `zonelink-drain-closes-session`, `zonelink-child-name-matches-store`, `zonelink-one-child-local-uplink`, `zonelink-parent-has-no-reciprocal-row` | | Removal proof | `realm_stubs.rs` (`ApiFrontend`, `PeerOperationRouter`, `TargetResolver`) removed after ComponentSession integration (ADR046-zone-control-018); `realm_access_resolver.rs` module removed after ZoneLink replaces entrypoint-table resolution; gateway `PeerSession`/`SecurePeerSession` session types remain as dead code in d2b-realm-router until Provider session migration wave | ### ADR046-zone-control-003 @@ -4166,7 +4205,7 @@ None of the following exist in baseline: | Current source | `nixos-modules/options-realms.nix` (`providerKind = "^[a-z][a-z0-9-]*$"` label regex matching `LABEL_PATTERN` in `ids.rs`; `providerType` submodule with `enable`/`kind`/`placement`/`freeformType` fields; `d2b.realms..providers.*` attrset — `generated-or-eval-contract`, baseline `b5ddbed6`); `nixos-modules/options-realms-workloads.nix` (`d2b.realms..workloads.*` submodule shape — `generated-or-eval-contract`); `d2b.zones.*` Nix options: `ADR-only` | | Reuse action | adapt (`providerType` submodule → Provider option submodule; label regex `^[a-z][a-z0-9-]*$` retained unchanged; `placement` option → Provider component placement; `d2b.realms.*` option namespace → `d2b.zones.*` option namespace) | | Destination | `nixos-modules/options-zones.nix`; `nixos-modules/resources-zone-control.nix`; extended `nixos-modules/index.nix` | -| Detailed design | `d2b.zones..*` Nix options for Zone/ZoneLink/Provider/Role/RoleBinding/Quota/EmergencyPolicy authoring; Nix eval-time validation of ResourceRefs, verb enums, digest format, subject types, resolved ZoneLink transport ref constraints, and the no-expiry RoleBinding model; canonical JSON serialization; generation-bound resource bundle output | +| Detailed design | `d2b.zones..*` Nix options for Zone/ZoneLink/Provider/Role/RoleBinding/Quota/EmergencyPolicy authoring; Nix eval-time validation of ResourceRefs, verb enums, digest format, subject types, child-local ZoneLink self-name/one-uplink/local transport-ref constraints, and the no-expiry RoleBinding model; canonical JSON serialization; generation-bound resource bundle output; allocator bootstrap topology validates one parent per child, cycles, and depth without emitting reciprocal resources | | Integration | Nix resource compiler → configuration publication handler → Zone store; bootstrap Provider records auto-generated | | Data migration | Full reset; Nix realm options (`d2b.realms.*`) remain until purge wave | | Validation | nix-unit vectors for each Zone control type schema; cross-field constraint tests; rendered JSON contract tests (`make test-drift`) | @@ -4282,7 +4321,7 @@ Evidence class for all: `main-reuse-source`. | Reuse action | adapt | | Destination | `packages/d2b-core-controller/src/zone_link.rs` (ZoneLink handler); `packages/d2b-resource-api/src/admission.rs` (request admission) | | Detailed design | **Selected**: `SessionPhase` 5-phase FSM (from main `d2b-session`) drives ZoneLink session state; `Established` state → `status.connected=true` + ZoneLink `status.phase=Ready`; `Disconnected`/`Reconnecting` states → `status.connected=false` + ZoneLink `status.phase=Pending` (or `Degraded` if degraded capability); session-internal phases are not exposed as `ZoneLink.status.phase` values — only the common Resource phases (`Pending|Ready|Degraded|Failed|Unknown`) appear in `status.phase` (§3.5); `begin_reconnect` window/attempt logic → ZoneLink reconnect loop; `RealmSessionAuthority` local vs gateway pattern → ZoneLink authority types for host-local vs transport-bridge sessions; `RealmServiceServer::call` wire validation (generation, request lifetime, attachment) → Zone API request admission; `RealmServiceLimits` 15 fields → ZoneLink `spec.limits`; `connect_component_session()` Nn peer-UID path → Zone runtime bootstrap ComponentSession; `NamedStream` lifecycle → ZoneLink named-stream operations; session-lifecycle tests ported as ZoneLink phase regression tests. **Excluded ADR45 assumptions**: `RealmSessionAuthority::gateway_peer()` (lines 72–87): gateway custody and `Locality::Remote` + `CredentialCustody::GatewayGuest` are ADR45 realm-gateway patterns; v3 ZoneLink transport is bound by the resolved `spec.transportProviderRef`, `spec.transportSettings`, and `spec.transportCredentials` contract instead. Realm 7-phase `SessionLifecycle` (`Allocating→…→Running→Stopping→Stopped`) is the ADR45 realm-specific lifecycle; ZoneLink uses the 5-phase d2b-session model. `GuestBootstrapPsk`/`GuestSessionCredentialV1`: ADR45 guest bootstrap, excluded. `realm_stubs.rs` `ApiService`/`ApiFrontend` dead code excluded (§16.2). | -| Integration | Zone runtime startup creates bootstrap ComponentSession using `HandshakeCredentials::Nn` + local domain socket + peer-UID verification (adapted from `connect_component_session`); ZoneLink handler opens sessions for child Zone connections using d2b-bus `SessionEngine`; resource API admission layer validates requests using `RealmServiceServer::call` validation pattern | +| Integration | Zone runtime startup creates bootstrap ComponentSession using `HandshakeCredentials::Nn` + local domain socket + peer-UID verification (adapted from `connect_component_session`); the child-local ZoneLink handler opens its allocator-bound uplink session using d2b-bus `SessionEngine`, and the parent routes child calls over that session; resource API admission validates requests using the `RealmServiceServer::call` pattern | | Data migration | Not applicable; new implementation | | Validation | `zonelink-reconnect-child-uid-change`, `zonelink-disconnect-unknown-phase`, `zonelink-intent-queue-limit`, `zone-session-phase-forward-sequence-refuses-repeat` (ported from `forward_sequence_reaches_running_then_refuses_to_advance`), `zone-session-failure-records-phase` (ported from `failure_mid_establishment_rolls_into_teardown_and_records_phase`), `zone-session-stop-is-idempotent` (ported), `zone-bootstrap-session-nn-peer-uid-verified` | | Removal proof | `d2b-realm-router/src/service_v2.rs` `RealmServiceServer` removed after Zone API service layer replaces realm v2 service; `d2b-realm-router/src/session_lifecycle.rs` removed after d2b-bus lifecycle replaces realm-specific one | @@ -4327,7 +4366,7 @@ Evidence class for all: `main-reuse-source`. | Reuse action | copy + adapt | | Destination | `packages/d2b-client/src/` (updated for v3 Zone API, replacing ADR45 daemon verbs with Zone resource operations) | | Detailed design | **Selected**: `Client::connect()` target-resolve → ComponentSession-open → `ConnectedClient` lifecycle; `RetryPolicy` 1..8 bound + `retryable_failure()` safe-only retry detection; `NamedStream::send/receive/close_local/reset` lifecycle; `ComponentSessionConnector` trait as connector abstraction; `HostSocketConnector::from_seqpacket_fd` + `local_daemon_endpoint_identity` for Zone runtime socket connector; `RouteTable` ambiguous-route rejection; `ServiceHandle`/`GeneratedClient`/`MethodHandle` typed service client pattern; `map_ttrpc_error`/`validate_reply`/`map_retry` error-handling chain; `ClientError`/`RemoteErrorKind`/`RetryClass` error taxonomy; `DaemonClient` call-options and outcome helpers (infrastructure only). **Excluded ADR45 assumptions**: `DaemonMethod` enum (lines 29–56 of daemon_service.rs): ADR45 daemon verbs (`vm_start`, `vm_stop`, `list_realms`, etc.) — replaced with Zone API verbs. `GuestClient`/`guest_service.rs`: ADR45 guest operations; excluded until v3 Guest transport work item. Hardcoded socket path `PUBLIC_SOCKET_PATH = "/run/d2b/public.sock"` in `host_socket.rs`: replaced by Zone-resource-managed path. `TransportKind::LocalUnix` restriction in daemon-access: v3 allows multiple transport kinds per ZoneLink binding. | -| Integration | `d2b` CLI uses `d2b-client` to connect to Zone runtime via `HostSocketConnector`; Zone runtime uses `d2b-client` for internal child-Zone ZoneLink connections; Provider toolkit conformance tests use `Fixture`/`FakeProvider` with `d2b-client` service handles | +| Integration | `d2b` CLI uses `d2b-client` to connect to Zone runtime via `HostSocketConnector`; a child Zone runtime uses `d2b-client` for its allocator-bound uplink while the parent route engine uses the established session for child calls; Provider toolkit conformance tests use `Fixture`/`FakeProvider` with `d2b-client` service handles | | Data migration | Not applicable; updated in place | | Validation | `client-retry-policy-max-8-attempts-enforced`, `client-named-stream-close-local-then-remote-close-transitions-closed`, `client-route-table-ambiguous-route-rejected`, `client-host-socket-peer-uid-verified-on-connect`, `client-retryable-failure-only-safe-mutations` | | Removal proof | `DaemonMethod` v2 verb enum retired after all v2 daemon operations migrated to Zone API | @@ -4359,10 +4398,10 @@ Evidence class for all: `main-reuse-source`. | Reuse source | None from main; eval-time validation is Nix-native | | Reuse action | net-new | | Destination | `nixos-modules/options-zones.nix`, `nixos-modules/resource-type-validators.nix` | -| Detailed design | Declare the unified `d2b.zones..resources.` option tree with two sub-options: `type` (string, required) and `spec` (attrset, required). Declare the `d2b.artifacts.` catalog option tree with `package` (types.package, required) and `type` (closed string enum, required); `d2b.artifacts` is a global option, not zone-local. For each core ResourceType (Zone, ZoneLink, Provider, Role, RoleBinding, Credential), a `genSchemaOptions` helper reads the corresponding ResourceTypeSchema JSON and emits per-type `spec` option declarations with correct Nix types, defaults, and documentation comments. Provider `spec.artifactId` is a plain string referencing the `d2b.artifacts` catalog; the `package` derivation is NOT a `spec` field. Non-core (Provider-registered) ResourceTypes are deferred to Phase 2 schema validation; Phase 1 accepts any non-empty `spec` attrset for non-core types. Implement all Phase 1 eval-time assertions (§14.10 Phase 1 table) via Nix `assert` and `lib.mkAssert`; `credentialRef` helper function producing `{ "$credentialRef": "Credential/" }` literals; `resourceRef` and `closedEnum` helpers; bootstrap provider exclusion assertions; eval assertion that Zone resource name must equal Zone attrset key. The runtime creates `Zone/`, `Provider/system-core`, and `Provider/system-minijail` directly with `managedBy=controller`; none is emitted or inferred from the configuration bundle. `allowUnsafeLocal` maps to a dedicated Host admission gate (current `options-realms.nix:346`). Provider manifest-derived fields (`spec.exports`, `spec.components`, `spec.dependencies`, `spec.permissionClaims`, `spec.upgradePolicy`, `spec.restartPolicy`) are declared as read-only/not-settable options; setting them is an eval assertion error | +| Detailed design | Declare the unified `d2b.zones..resources.` option tree with two sub-options: `type` (string, required) and `spec` (attrset, required). Declare the `d2b.artifacts.` catalog option tree with `package` (types.package, required) and `type` (closed string enum, required); `d2b.artifacts` is a global option, not zone-local. For each core ResourceType (Zone, ZoneLink, Provider, Role, RoleBinding, Credential), a `genSchemaOptions` helper reads the corresponding ResourceTypeSchema JSON and emits per-type `spec` option declarations with correct Nix types, defaults, and documentation comments. Provider `spec.artifactId` is a plain string referencing the `d2b.artifacts` catalog; the `package` derivation is NOT a `spec` field. Non-core (Provider-registered) ResourceTypes are deferred to Phase 2 schema validation; Phase 1 accepts any non-empty `spec` attrset for non-core types. Implement all Phase 1 eval-time assertions (§14.10 Phase 1 table) via Nix `assert` and `lib.mkAssert`; `credentialRef` helper function producing `{ "$credentialRef": "Credential/" }` literals; `resourceRef` and `closedEnum` helpers; bootstrap provider exclusion assertions; eval assertion that Zone resource name must equal Zone attrset key; child-local ZoneLink assertions require `childZoneName` to equal that key, reject a second uplink resource even when disabled, and reject any local-root uplink. The runtime creates `Zone/`, `Provider/system-core`, and `Provider/system-minijail` directly with `managedBy=controller`; none is emitted or inferred from the configuration bundle. `allowUnsafeLocal` maps to a dedicated Host admission gate (current `options-realms.nix:346`). Provider manifest-derived fields (`spec.exports`, `spec.components`, `spec.dependencies`, `spec.permissionClaims`, `spec.upgradePolicy`, `spec.restartPolicy`) are declared as read-only/not-settable options; setting them is an eval assertion error | | Integration | Unified `d2b.zones..resources.*` option tree consumed by ADR046-zone-control-015 resource compiler; Zone controller (ADR046-zone-control-001) reads resulting bundle; Provider package conventions from ADR046-zone-control-003 | | Data migration | Replace `nixos-modules/options-realms.nix`-derived option trees once Zone controller is live and has reached parity | -| Validation | All Phase 1 eval tests in §15.8 (`nix-eval-name-regex-enforced`, `nix-eval-verb-closed-enum`, `nix-eval-roleref-format`, `nix-eval-subject-type-restricted`, `nix-eval-no-duplicate-subjects`, `nix-eval-bootstrap-provider-rejected`, `nix-eval-provider-missing-artifact-id`, `nix-eval-artifact-id-not-in-catalog`, `nix-eval-artifact-wrong-type`, `nix-eval-artifact-id-format`, `nix-eval-credentialref-declared`, `nix-eval-dollar-key-rejected`, `nix-eval-zonelink-self-loop-rejected`, `nix-eval-zonelink-limits-maxpendingintents-bound`) | +| Validation | All Phase 1 eval tests in §15.8 (`nix-eval-name-regex-enforced`, `nix-eval-verb-closed-enum`, `nix-eval-roleref-format`, `nix-eval-subject-type-restricted`, `nix-eval-no-duplicate-subjects`, `nix-eval-bootstrap-provider-rejected`, `nix-eval-provider-missing-artifact-id`, `nix-eval-artifact-id-not-in-catalog`, `nix-eval-artifact-wrong-type`, `nix-eval-artifact-id-format`, `nix-eval-credentialref-declared`, `nix-eval-dollar-key-rejected`, `nix-eval-zonelink-child-name-mismatch-rejected`, `nix-eval-zonelink-second-uplink-rejected`, `nix-eval-zonelink-limits-maxpendingintents-bound`) | | Removal proof | `nixos-modules/options-realms.nix`, `nixos-modules/realm-controller-config-json.nix`, `nixos-modules/realm-identity-config-json.nix` deleted after Zone controller and resource compiler reach full parity; `nixos-modules/assertions.nix` lines referencing `allowUnsafeLocal`/realm names removed after Host admission validation replaces them | ### ADR046-zone-control-015 @@ -4426,7 +4465,7 @@ Evidence class for all: `main-reuse-source`. | Detailed design | Implement the `ResourceExport` and `ResourceImport` standard ResourceTypes per §8A plus signed Provider `ProjectionFactory` metadata binding qualified Service type, qualified Binding type, allowed owner-Service backing refs, allowed Binding target refs, projection schema/fingerprint, and aggregate factory fingerprint. Admission accepts only an owner Service as `ResourceExport.resourceRef`; matches export/import/local-factory type and fingerprints; and creates exactly one same-qualified-type projection Service (`ownerRef: ResourceImport/`). It never projects Device/Endpoint/Binding and never creates Binding. Binding spec is desired consumer intent only; observations belong only in status. No cross-Zone Ref, FD, secret, path, locator, or resource grant crosses a Zone; payload bytes use bounded encrypted named streams and high-churn sessions/streams remain internal. Export removal/ZoneLink loss revokes leases and degrades the projection Service; reconnect revalidates generation and both fingerprints. D091 currency propagates Service → export → import → projection Service → authored Binding → children. | | Integration | Zone store/redb (ADR046-store-001); shared D098 semantic base catalog (ADR046-provider-004); ZoneLink reconcile (§3); ComponentSession bounded encrypted named streams; signed projection factories/adapters for audio-pipewire, device-security-key, observability-otel, and policy-gated device-usbip; CLI graph rendering | | Data migration | None — full d2b 3.0 reset; no prior cross-Zone sharing state | -| Validation | §8A.7: fast hermetic factory absent/mismatch/tamper, Service-only export target, exactly-one same-type projection Service, no Device/Endpoint/Binding projection, no auto-Binding, intent-only spec/status-only observations, backing/target allowlists, finalizer/update propagation, Provider classification, canonical Nix stability, quotas/reconnect/revoke, and no FD/secret/path tests; slower real encrypted-stream integration for audio/security-key/observability/policy-gated USBIP | +| Validation | §8A.7: fast hermetic factory absent/mismatch/tamper, Service-only export target, exactly-one same-type projection Service, no Device/Endpoint/Binding projection, no auto-Binding, intent-only spec/status-only observations, backing/target allowlists, finalizer/update propagation, Provider classification, canonical Nix stability including child-local `ZoneLink/work-uplink` in `d2b.zones.work.resources`, local `zoneLinkRef` resolution, quotas/reconnect/revoke, and no FD/secret/path tests; slower real encrypted-stream integration for audio/security-key/observability/policy-gated USBIP | | Removal proof | Not applicable (new surface) | ### ADR046-zone-control-020 diff --git a/docs/specs/ADR-046-zone-routing.md b/docs/specs/ADR-046-zone-routing.md index e71a7af0d..2e3c87935 100644 --- a/docs/specs/ADR-046-zone-routing.md +++ b/docs/specs/ADR-046-zone-routing.md @@ -414,15 +414,17 @@ within a `Realm`. | Current variant | ADR 0046 Zone mapping | | --- | --- | | `HostLocal` | Private Zone runtime bootstrap: local controller, no external transport needed | -| `GatewayVm` | Private Zone runtime bootstrap: gateway-VM host; child identity via parent ZoneLink `childZoneName`/`transportProviderRef` | -| `CloudFullHost` | Private Zone runtime bootstrap: cloud/remote host; child identity via parent ZoneLink | -| `ProviderController { provider }` | Private Zone runtime bootstrap: Provider-managed controller; child identity via parent ZoneLink | -| `ProviderAgent { provider }` | Private Zone runtime bootstrap: Provider agent; child identity via parent ZoneLink | -| `ProviderSpecific { provider, placement }` | Private Zone runtime bootstrap: provider-schema-validated placement; child identity via parent ZoneLink | +| `GatewayVm` | Private Zone runtime bootstrap: gateway-VM host; child identity via the child-local uplink ZoneLink plus allocator binding | +| `CloudFullHost` | Private Zone runtime bootstrap: cloud/remote host; child identity via the child-local uplink ZoneLink | +| `ProviderController { provider }` | Private Zone runtime bootstrap: Provider-managed controller; child identity via the child-local uplink ZoneLink | +| `ProviderAgent { provider }` | Private Zone runtime bootstrap: Provider agent; child identity via the child-local uplink ZoneLink | +| `ProviderSpecific { provider, placement }` | Private Zone runtime bootstrap: provider-schema-validated placement; child identity via the child-local uplink ZoneLink | This is an **architectural change**: the static placement enum is replaced by -private per-Zone bootstrap configuration plus the parent ZoneLink's -`childZoneName` and `transportProviderRef`. Placement is NOT a public field in +private per-Zone bootstrap configuration plus the child-local uplink +ZoneLink's `childZoneName` and `transportProviderRef`. The provisioning parent +allocator binds the ZoneLink UID and child identity to the private parent edge; +no reciprocal parent-store ZoneLink exists. Placement is NOT a public field in `Zone.spec`. `Zone.spec` is `{}` — it carries no authored fields. `EntrypointMode` (`HostResident`/`GatewayBacked`) is used in the CLI @@ -558,8 +560,9 @@ homed in a different Zone. The d2b v3 model is intentionally constrained: - Every resource belongs to exactly one Zone. Resources never migrate. - Ordinary `*Ref` fields never cross Zone boundaries. -- A parent Zone may declare a `ZoneLink/` resource that represents a - direct parent/child relationship with another Zone. +- Every non-root child Zone declares at most one local `ZoneLink/` + uplink resource, enabled or disabled. Its provisioning parent allocator binds + that resource to the direct parent/child edge in sealed bootstrap state. - A parent calls the child Zone's `d2b.resource.v3` service through an authenticated ZoneLink ComponentSession; it does not obtain a database handle, process credential, host path, or cross-Zone resource reference. @@ -570,9 +573,9 @@ homed in a different Zone. The d2b v3 model is intentionally constrained: enforces native RBAC at each hop. ``` -Zone/K0 (parent) - ZoneLink/k1-dev -> Zone/K1 (child) - ZoneLink/k2-guest -> Zone/K2 (grandchild) +Zone/K0 (parent allocator/route state) + -> Zone/K1 (child): ZoneLink/k1-uplink + -> Zone/K2 (grandchild): ZoneLink/k2-uplink ``` A call from a process in K0 that targets K2 traverses K0→K1→K2. Each @@ -587,10 +590,10 @@ authority from prior hops beyond what its own enrolled RoleBinding grants. apiVersion: resources.d2bus.org/v3 type: ZoneLink metadata: - name: k1-dev - zone: k0 + name: k1-uplink + zone: k1 spec: - # Required: stable name of the child Zone as it knows itself. + # Required: stable local child name; must equal metadata.zone. childZoneName: k1 # Required: transport Provider that carries the link ComponentSession. transportProviderRef: Provider/transport-unix @@ -625,11 +628,14 @@ spec: Rules: -- `childZoneName` must satisfy `^[a-z][a-z0-9-]*$`. -- `transportProviderRef` must resolve to a Ready Provider in this Zone. -- `capabilityCeiling` may only include capabilities the parent's own - Role/RoleBinding already grants the linking principal; the intersection - is enforced structurally before the link becomes Ready. +- `childZoneName` must satisfy `^[a-z][a-z0-9-]*$` and equal the enclosing + child Zone name. Local root has no uplink; a non-root Zone has at most one + ZoneLink resource, enabled or disabled. +- `transportProviderRef` must resolve to a Ready Provider in this same child + Zone. +- `capabilityCeiling` may only include capabilities granted by the sealed + parent allocator binding; the allocator intersection is enforced + structurally before the link becomes Ready. - `childStaticKeyFingerprint` is the enrolled KK key fingerprint; the handshake fails closed if the child presents a different static key. - The `transportSettings` object is validated against the transport @@ -686,17 +692,21 @@ Status phases: - `Failed`: session cannot be established and retry policy is exhausted. - `Unknown`: controller cannot currently prove session state. -The core-controller ZoneLink handler owns status; it never writes -`Ready` without a successful probe of the child `d2b.resource.v3` -service within the current session generation. +The child-local core-controller ZoneLink handler owns status. It never writes +`Ready` until the parent allocator has authenticated the child, acknowledged +the route allocation, and successfully probed the child's `d2b.resource.v3` +service within the current session generation. The parent keeps private +allocation/route state, not a second resource row. ## Zone tree identity and prefix naming Each Zone has exactly one authoritative `Zone/` resource in -its own store. The name is the Zone's local self-name. When represented -in a tree context the parent assigns a `ZoneLink/` which need -not equal the child's self-name. The child's self-name is verified during -KK enrollment and recorded as `status.zoneLink.childZoneUid`. +its own store. The name is the Zone's local self-name. A non-root child +authors its local `ZoneLink/` uplink; the link name need not equal +the child's self-name, while `spec.childZoneName` must. The provisioning +parent allocator assigns the private tree edge and verifies the child self-name +during KK enrollment. The acknowledged UID is recorded as +`status.zoneLink.childZoneUid`. Zone tree positions are described as ordered label paths from the local root, most-specific first, matching the `RealmPath` grammar from @@ -898,7 +908,7 @@ option types from `xtask gen-zone-nix-options` and are not repeated here. | `` key matches `^[a-z][a-z0-9-]*$` | `zones: zone key must match ^[a-z][a-z0-9-]*$` | | `` key not `sys-*` or `launcher` | `zones: zone key uses reserved prefix or exact name` | | `` key matches `^[a-z][a-z0-9-]*$` | `zones..resources: resource key must match ^[a-z][a-z0-9-]*$` | -| For `type = "ZoneLink"`: no two resources in the same `` have `spec.childZoneName` equal to the same value | `zones.: two ZoneLink resources declare the same childZoneName — would create multi-parent` | +| For `type = "ZoneLink"`: `spec.childZoneName` equals ``; at most one uplink resource exists in a non-root Zone; local root has none | `zones.: ZoneLink must be the sole child-local uplink and childZoneName must equal its Zone` | | For `type = "ZoneLink"`: `spec.transportProviderRef` resolves to a declared `Provider` resource in the same `` | `zones..resources.: transportProviderRef does not resolve to a declared Provider resource` | | Total `d2b.zones` keys ≤ 64 | `zones: zone count exceeds host limit of 64` | | `resources` count per `` ≤ 1024 | `zones..resources: resource count exceeds zone limit of 1024` | @@ -916,8 +926,9 @@ d2b.zones.k0.resources.k0 = { spec = {}; }; -# Unix transport Provider for the K0→K1 link -d2b.zones.k0.resources.transport-unix = { +# Unix transport Provider is local to child K1 because the ZoneLink and all of +# its refs resolve in K1. +d2b.zones.k1.resources.transport-unix = { type = "Provider"; spec = { kind = "transport-unix"; @@ -925,8 +936,8 @@ d2b.zones.k0.resources.transport-unix = { }; }; -# ZoneLink from K0 to K1 — spec fields mirror ZoneLink.schema.json exactly -d2b.zones.k0.resources.k1-dev = { +# K1's local uplink to its provisioning K0 allocator. +d2b.zones.k1.resources.k1-uplink = { type = "ZoneLink"; spec = { childZoneName = "k1"; @@ -944,17 +955,17 @@ d2b.zones.k0.resources.k1-dev = { }; ``` -**K1 Zone self-resource plus K1→K2 link via Azure Relay transport**: +**K2 child-local uplink to K1 via Azure Relay transport**: ```nix -# K1 self (also a local controller on this host) -d2b.zones.k1.resources.k1 = { +# K2 self. +d2b.zones.k2.resources.k2 = { type = "Zone"; spec = {}; # Zone.spec is exactly {} }; -# Azure Relay transport Provider (binding validated against its signed Provider schema) -d2b.zones.k1.resources.transport-azure-relay = { +# Azure Relay transport Provider is local to K2. +d2b.zones.k2.resources.transport-azure-relay = { type = "Provider"; spec = { kind = "transport-azure-relay"; @@ -962,8 +973,8 @@ d2b.zones.k1.resources.transport-azure-relay = { }; }; -# Credential ref for the relay SAS token — no inline secret -d2b.zones.k1.resources.relay-sas-k2 = { +# Credential ref for the relay SAS token — no inline secret. +d2b.zones.k2.resources.relay-sas-k2 = { type = "Credential"; spec = { kind = "opaque"; @@ -972,8 +983,8 @@ d2b.zones.k1.resources.relay-sas-k2 = { }; }; -# ZoneLink K1→K2; spec fields identical to ZoneLink canonical JSON -d2b.zones.k1.resources.k2-guest = { +# K2's local uplink to its provisioning K1 allocator. +d2b.zones.k2.resources.k2-uplink = { type = "ZoneLink"; spec = { childZoneName = "k2"; @@ -1025,9 +1036,9 @@ The Nix build phase runs `xtask gen-zone-resources` which: `"credentialRef": true` must be `"Credential/"` strings. 3. **Capability ceiling subset check**: the emitter verifies that every - ZoneLink `capabilityCeiling` is a structural subset of the parent Zone's - own declared capability grant. Capabilities wider than the grant are a - build error. + child-local ZoneLink `capabilityCeiling` is a structural subset of the + provisioning parent's private allocator grant. Capabilities wider than the + sealed grant are a build error. 4. **Ref resolution**: all `*Ref` and `*ProviderRef` values are resolved against resources declared in the same `d2b.zones..resources` and @@ -1064,15 +1075,15 @@ core-populated fields — e.g. `metadata.managedBy`, `metadata.configurationGene bundle JSON and must not be authored. **ZoneLink resource** — emitted from -`d2b.zones.k0.resources.k1-dev = { type = "ZoneLink"; spec = { ... }; };`: +`d2b.zones.k1.resources.k1-uplink = { type = "ZoneLink"; spec = { ... }; };`: ```json { "apiVersion": "resources.d2bus.org/v3", "type": "ZoneLink", "metadata": { - "name": "k1-dev", - "zone": "k0" + "name": "k1-uplink", + "zone": "k1" }, "spec": { "childZoneName": "k1", @@ -1107,18 +1118,18 @@ omission. The `status` object is absent from the emitted bundle entirely. ### Zone resource bundle format -The build installs `/etc/d2b/zones//resource-bundle.json` (root:d2bd 0640): +The build installs `/etc/d2b/zones//resource-bundle.json` (root:d2bd +0640). For child K1, the local bundle contains its Provider and uplink. The +controller-created Zone self-resource is not emitted: ```json { "schemaVersion": 1, "generationId": "", - "resourceCount": 4, + "resourceCount": 2, "resources": [ - { "type": "Provider", "metadata": { "name": "transport-unix", "zone": "k0" }, "...": "..." }, - { "type": "Zone", "metadata": { "name": "k0", "zone": "k0" }, "...": "..." }, - { "type": "ZoneLink", "metadata": { "name": "k1-dev", "zone": "k0" }, "...": "..." }, - { "type": "Zone", "metadata": { "name": "k1", "zone": "k1" }, "...": "..." } + { "type": "Provider", "metadata": { "name": "transport-unix", "zone": "k1" }, "...": "..." }, + { "type": "ZoneLink", "metadata": { "name": "k1-uplink", "zone": "k1" }, "...": "..." } ], "integrity": "sha256-" } @@ -1327,7 +1338,8 @@ Required before ADR046-routing-011 through ADR046-routing-013 are complete: | --- | --- | --- | | `nix-unit: zone-name-regex` | nix-unit eval | Zone name regex and reserved-name assertions fire | | `nix-unit: zone-link-fingerprint` | nix-unit eval | 63-char and non-hex fingerprints rejected at eval | -| `nix-unit: zone-link-multi-parent` | nix-unit eval | Two ZoneLinks with identical `childZoneName` rejected | +| `nix-unit: zone-link-child-name` | nix-unit eval | `childZoneName` unequal to enclosing child Zone rejected | +| `nix-unit: zone-link-one-uplink` | nix-unit eval | Second uplink (even disabled) and any local-root uplink rejected | | `nix-unit: capability-ceiling-unknown-verb` | nix-unit eval | Unknown verb in `capabilityCeiling` rejected | | `nix-unit: transport-binding-secret-key` | nix-unit eval | Binding with `key =` rejected at eval | | `drift: zone-resource-schema` | `make test-drift` | `xtask gen-zone-schemas && git diff --exit-code` passes | @@ -1337,7 +1349,8 @@ Required before ADR046-routing-011 through ADR046-routing-013 are complete: | `build: missing-transport-provider` | flake check | Unresolvable `transportProviderRef` fails build | | `host-integration: cleanup-removed-zonelink` | NixOS test | Switch removes ZoneLink; assert `deletionRequestedAt` set; store transaction commits `Deleted` revision and removes row/index; audit record appended from committed revision with exactly-once recovery; generation reaches Ready; dynamic route entries deleted by ZoneLink controller teardown, not by generation diff | | `host-integration: rollback-restores-zonelink` | NixOS test | After cleanup switch, rollback re-activates ZoneLink; generation diff reverses | -| `host-integration: dynamic-child-not-deleted` | NixOS test | Route entries under a ZoneLink are NOT deleted by generation diff; only ZoneLink is diff-deleted via controller teardown | +| `host-integration: dynamic-child-not-deleted` | NixOS test | Parent allocator/route entries are NOT diff-owned resources; removing the child-local ZoneLink invokes controller teardown and allocation release | +| `host-integration: zonelink-no-reciprocal-row` | NixOS test | Activating a child-local uplink creates no ZoneLink row in the parent store | ## Authenticated advertisements, withdrawal, and renewal its enrolled KK key. The parent validates the signature before admitting @@ -1426,7 +1439,7 @@ route is treated as a new advertisement. ### Namespace allocation A parent allocates a `ZoneLinkNamespaceAllocation` to the child at link -creation and when the parent's `capabilityCeiling` changes: +creation and when the child-local ZoneLink's `capabilityCeiling` changes: ```text ZoneLinkNamespaceAllocation { @@ -1446,8 +1459,8 @@ a new advertisement under the new generation. The `ZoneLinkNamespaceAllocation` above **is** the explicit ZoneLink range capacity/quota (D097 hardware-audit finding): `allowedPrefixes` (1–16) and `maxRoutes` (1–64) are the bounded per-edge capacity; a child exceeding either -bound is rejected, so a parent's ZoneLink namespace cannot be exhausted by one -child edge. +bound is rejected, so the parent allocator's route namespace cannot be +exhausted by one child edge. ### Global vsock CID and fixed-port authority (D097) @@ -1651,9 +1664,9 @@ A parent ResourceClient targeting a child Zone routes through d2b-bus: ``` Parent ResourceClient - -> parent d2b-bus (route decision: local or ZoneLink) + -> parent d2b-bus (route decision: local or allocator-bound child edge) |-- local Zone: direct to local d2b.resource.v3 - |-- child Zone: ZoneLink ComponentSession + |-- child Zone: ComponentSession represented by child's local ZoneLink -> intermediate Zone d2b-bus (hop relay) -> child d2b.resource.v3 ``` @@ -1664,7 +1677,8 @@ At each intermediate hop, d2b-bus: 2. Evaluates local RBAC for the `relay` verb with the forwarded ResourceType and target Zone name. 3. Decrements the hop counter; refuses if zero. -4. Opens an outbound ZoneLink ComponentSession to the next hop. +4. Opens the allocator-bound ComponentSession represented by the next child + Zone's local uplink. 5. Re-serializes the request with the decremented hop counter; preserves the original operation/idempotency/correlation/trace IDs unchanged. 6. Returns the child response to the inbound caller. @@ -1697,8 +1711,8 @@ child's own revision cursors: ZoneLink ComponentSession. 2. Child streams `ResourceWatchEvent` items, each carrying the child's revision token. -3. If the ZoneLink session disconnects, the parent controller records the - last seen child revision. +3. If the ZoneLink session disconnects, parent route-session state records the + last seen child revision; no parent-store ZoneLink row is created. 4. On reconnect, the parent re-issues `Watch(afterRevision=)`. 5. If the child reports `revision-expired` (cursor too old), the parent re-issues `List` to obtain a fresh snapshot, then re-opens `Watch` @@ -1891,15 +1905,16 @@ serialization boundary, not a runtime policy decision. ### Link failure and restart -When a ZoneLink ComponentSession disconnects: +When a child-local ZoneLink ComponentSession disconnects: 1. The ZoneLink controller sets the link's `SessionEstablished` condition to `False` and phase to `Degraded` (or `Unknown` if no session was ever established this generation). 2. In-flight operations with a pinned path through this link fail immediately with `zone-link-disconnected`. -3. Queued intents accumulate per `localIntentPolicy`. -4. The controller schedules a reconnect attempt using the +3. Child-to-parent intents may accumulate in the child store per + `localIntentPolicy`; parent-to-child operations fail immediately. +4. The child-local controller schedules a reconnect attempt using the `reconnectPolicy` backoff. 5. On reconnect: - a new KK handshake is performed; the child static key is @@ -1909,15 +1924,16 @@ When a ZoneLink ComponentSession disconnects: - queued intents are replayed (with original idempotency keys if the queued mutation is younger than the retention window, or as new operations if older); - - the ZoneLink handler re-issues `Watch` subscriptions from the last - known child revision or triggers `List` + reopen if the cursor - expired. + - the child-local ZoneLink handler re-issues parent route/export + advertisement watches from the last known parent revision or triggers + `List` + reopen if the cursor expired. 6. After reconnect, `status.zoneLink.reconnectCount` is incremented and `lastConnectedAt` is updated. -The ZoneLink controller does not modify child resources during -disconnected recovery. It does not perform cleanup, garbage collection, -or status correction in the child Zone without a live session. +During disconnected recovery, the ZoneLink controller updates only its own +local status and outbound intent queue. It does not infer or mutate other +child-local resources from stale parent state, and it performs no remote +cleanup or status correction without a live session. ### Revocation @@ -1927,7 +1943,7 @@ A ZoneLink may be administratively revoked by setting its spec's Revocation sequence: 1. Spec update: `capabilityCeiling` set to empty. -2. ZoneLink controller issues a route withdrawal for all advertised +2. Child-local ZoneLink controller issues a route withdrawal for all advertised `routeId` values. 3. The parent's `RouteTreeEngine` removes all entries for the child. 4. d2b-bus immediately returns `zone-link-revoked` for any new call @@ -1936,8 +1952,9 @@ Revocation sequence: 5. Long-lived streams on this link receive `zone-link-revoked` and close. 6. In-flight operations are cancelled (best-effort); already-committed child-Zone operations are not rolled back. -7. The ZoneLink resource deletion proceeds through normal finalizer policy; - the child Zone is not notified of the deletion directly. +7. The child-local ZoneLink resource deletion proceeds through normal finalizer + policy and releases the parent allocator binding; no reciprocal parent + resource requires deletion. Authorization lease revocation: when a Role or RoleBinding governing cross-Zone access changes, the parent's d2b-bus authorization engine @@ -1978,12 +1995,14 @@ Hop counter enforcement: ## Local intents while disconnected -When `localIntentPolicy: queue` is set, the ZoneLink handler may enqueue -`UpdateSpec`, `Create`, and `Delete` intents for child-Zone resources -while the session is disconnected. Behavior: +When `localIntentPolicy: queue` is set, the child-local ZoneLink handler may +enqueue `UpdateSpec`, `Create`, and `Delete` intents directed from the child to +parent/ancestor resources while the uplink is disconnected. Parent-to-child +mutations are not queued in the child and fail at the parent route boundary. +Behavior: -- Intents are stored locally as bounded `ZoneLinkIntent` records, not - resource mutations. No child resource state is assumed. +- Intents are stored in the child Zone as bounded `ZoneLinkIntent` records, not + resource mutations. No parent resource state is assumed. - `maxQueuedIntents` limits the queue. When the queue is full, new intents fail with `zone-link-intent-queue-full`. - Intent records carry the original operation/idempotency/correlation IDs @@ -1993,7 +2012,7 @@ while the session is disconnected. Behavior: replayed with fresh operation IDs and marked as `late-replay`. - A replayed intent that receives `resource-conflict` (stale revision) is not retried automatically; the caller is notified with the conflict. -- A `Get`, `List`, or `Watch` call targeting a disconnected Zone always +- A `Get`, `List`, or `Watch` call traversing a disconnected uplink always returns `zone-link-disconnected` immediately; it is never queued. - `localIntentPolicy: drop` silently discards all intents during disconnect and returns success to the caller (callers are not informed @@ -2002,8 +2021,8 @@ while the session is disconnected. Behavior: - `localIntentPolicy: fail` returns `zone-link-disconnected` immediately for all mutating calls; no intent is queued or dropped. -Local intent queueing does not constitute a claim of child-Zone state. -The ZoneLink handler does not infer child resource phase, condition, or +Local intent queueing does not constitute a claim of parent-Zone state. +The ZoneLink handler does not infer parent resource phase, condition, or status from locally queued intents. ## Topology diagrams @@ -2013,15 +2032,14 @@ status from locally queued intents. ``` K0: parent Zone (Host-based, runs on physical host) Host/host-system - ZoneLink/k1-dev -> K1 (transport: unix socket) - ZoneLink/k1-work -> K1-work (transport: vsock) K1: child Zone (Guest VM, cloud-hypervisor) + ZoneLink/k1-uplink -> K0 allocator (transport: unix socket) Guest/dev-vm Host/host-system (accessible only within K1) - ZoneLink/k2-guest -> K2 (transport: vsock CID 5) K2: grandchild Zone (nested VM or ACA container) + ZoneLink/k2-uplink -> K1 allocator (transport: vsock CID 5) Guest/work-container Process/web-server (K2-local only) ``` @@ -2029,14 +2047,15 @@ K2: grandchild Zone (nested VM or ACA container) Route tree from K0's perspective: ``` -K0 (local root) - └── K1 (ZoneLink/k1-dev, advertises itself + K2) - └── K2 (advertised by K1 as K2.K1.K0, next-hop K1) +K0 (local root allocator) + └── K1 (child-local ZoneLink/k1-uplink; advertises itself + K2) + └── K2 (child-local ZoneLink/k2-uplink; next-hop K1) ``` A call from K0 to `Process/web-server` in K2: 1. K0 d2b-bus: NCA=K0, path K0→K1→K2, 2 hops. -2. K0 opens ZoneLink ComponentSession to K1. +2. K0 opens the allocator-bound ComponentSession represented by K1's local + `ZoneLink/k1-uplink`. 3. K1 d2b-bus: hop count decremented (1 remaining), relays to K2. 4. K2 d2b-bus: hop count decremented (0 remaining), dispatches to local `d2b.resource.v3`. @@ -2057,9 +2076,9 @@ K2: subject=K1-link-principal verb=get resourceType=Process name=web-server ``` K0 (Host/host-system, local Zone) - ZoneLink/k1-workvm -> K1 K1 (Guest/workvm, cloud-hypervisor VM) + ZoneLink/k1-uplink -> K0 allocator Guest/workvm Process/wayland-proxy Process/shell-session @@ -2069,7 +2088,8 @@ A watch from K0 on K1 Processes: ``` 1. K0 ResourceClient: Watch(Process, zone=K1, afterRevision=none) -2. K0 d2b-bus: route to K1 via ZoneLink/k1-workvm +2. K0 d2b-bus: route to K1 via the allocation represented by K1's + ZoneLink/k1-uplink 3. K1: List snapshot → revision R1; Watch(afterRevision=R1) 4. K1 streams events to K0 named-stream (credit-bounded) 5. Disconnect: K0 records lastRevision=R1 @@ -2214,7 +2234,7 @@ Evidence classes: **A** = implemented-and-reachable from production binary, | `NodeId` | `ids.rs` | **A** | d2b-realm-core, d2b-gateway | → Host resource name or Zone-local implicit address | | `ProviderId`, `ExecutionId`, `StreamId`, `PrincipalId`, etc. | `ids.rs` | **A** | Throughout | → preserved with v3 names; secret-marker reject logic retained | | `EntrypointMode` (`HostResident`/`GatewayBacked`) | `realm.rs` | **A** | `d2b/src/target_routing.rs` | → subsumed into ZoneLink `spec.transportProviderRef` | -| `RealmControllerPlacement` (6 variants) | `realm.rs` | **A** | `d2b-realm-core`, bundle artifacts | → private Zone runtime bootstrap placement + parent ZoneLink child identity/transportProviderRef (not a public Zone.spec field) | +| `RealmControllerPlacement` (6 variants) | `realm.rs` | **A** | `d2b-realm-core`, bundle artifacts | → private Zone runtime bootstrap placement + child-local uplink identity/transportProviderRef bound by the parent allocator (not a public Zone.spec field) | | `TargetName` (DNS-form target string) | `target.rs` | **A** | d2bd, d2b-gateway | → v3 resource ref path | | `RealmTarget` (workload + realm path) | `target.rs` | **A** | d2bd/realm_access_resolver.rs | → Zone-local resource lookup | @@ -2286,7 +2306,7 @@ Evidence classes: **A** = implemented-and-reachable from production binary, The following transitions are NOT simple textual renames: -1. **`RealmControllerPlacement` enum → private Zone runtime bootstrap placement + parent ZoneLink child identity**: 6 variants collapse into per-Zone bootstrap configuration plus the parent ZoneLink's `childZoneName`/`transportProviderRef`. Placement is not a public `Zone.spec` field; `Zone.spec` is `{}`. +1. **`RealmControllerPlacement` enum → private Zone runtime bootstrap placement + child-local uplink identity**: 6 variants collapse into per-Zone bootstrap configuration plus the child-local ZoneLink's `childZoneName`/`transportProviderRef`, bound to its parent by sealed allocator state. Placement is not a public `Zone.spec` field; `Zone.spec` is `{}`. 2. **`WorkloadId` → Guest/Host split**: VM/sandbox workloads become `Guest`; local/bare-metal become `Host`. Classification is semantic, not mechanical. 3. **`CapabilitySet`-only authz → RBAC + capability ceiling**: current engine checks capability ceiling only. Per-hop `relay` verb RBAC is new. 4. **`RealmPath` DNS target form → Zone resource path**: grammar preserved; wire address format changes. @@ -2347,7 +2367,7 @@ The following transitions are NOT simple textual renames: | Reuse source | Same v3 baseline commit `b5ddbed6` | | Reuse action | extract and adapt | | Destination | `packages/d2b-contracts/src/v3/zone_link.rs` (ZoneLink spec/status types, intent types, namespace allocation); `packages/d2b-zone-routing/src/resolver.rs` (ZoneEntrypointResolver) | -| Detailed design | ZoneLink spec/status fields; ZoneLinkIntent record; ZoneLinkNamespaceAllocation; ZoneEntrypointResolver with longest-suffix match over ZonePath (adapted from `RealmEntrypointTable::resolve`); fail-closed on unknown Zone | +| Detailed design | Child-local ZoneLink spec/status fields; self-name and one-uplink invariants; ZoneLinkIntent record stored in the child; ZoneLinkNamespaceAllocation issued by the parent allocator; ZoneEntrypointResolver with longest-suffix match over ZonePath (adapted from `RealmEntrypointTable::resolve`); no reciprocal parent-store resource; fail-closed on unknown Zone | | Integration | Core-controller ZoneLink handler manages ZoneLink resources; ZoneEntrypointResolver in d2b-bus for per-call dispatch decision | | Data migration | None; ZoneLink resources created from Nix configuration at v3 reset | | Validation | Longest-suffix match vectors; ZoneLink spec validation tests; resolver fail-closed test | @@ -2364,8 +2384,8 @@ The following transitions are NOT simple textual renames: | Reuse source | Same v3 baseline commit `b5ddbed6` | | Reuse action | extract and adapt | | Destination | `packages/d2b-core-controller/src/zone_links.rs` | -| Detailed design | ZoneLink handler in core-controller: manages ZoneLink ResourceSpec→session→advertisement lifecycle; session state machine (Pending/Established/Disconnected/Reconnecting/Revoked); reconnect backoff per `reconnectPolicy`; advertisement issuance/renewal/withdrawal using enrolled KK ComponentSession; route cursor tracking; intent queue; capability ceiling change handling; status writer; no storage of child resource content | -| Integration | Core-controller process → d2b-bus ZoneLink ComponentSession → child Zone runtime; ZoneLink handler triggers ZoneRouteEngine advertisement admission/withdrawal | +| Detailed design | Child-local ZoneLink handler in core-controller: manages local ResourceSpec→allocator-bound session→advertisement lifecycle; session state machine (Pending/Established/Disconnected/Reconnecting/Revoked); reconnect backoff per `reconnectPolicy`; advertisement issuance/renewal/withdrawal using enrolled KK ComponentSession; child-store route cursor and outbound intent queue; capability ceiling change handling; status writer; no copied parent/child resource content; parent allocator alone owns privileged listeners, placement, and route namespace and creates no reciprocal resource | +| Integration | Child core-controller process → local transport Provider → sealed parent allocator binding → d2b-bus ComponentSession; child ZoneLink handler exchanges advertisements while the parent ZoneRouteEngine admits/withdraws them | | Data migration | New ZoneLink resources from Nix configuration; no prior enrollment compatibility | | Validation | Session lifecycle tests; reconnect/revocation/ceiling-change; intent queue drain; cursor resync; advertisement renewal timing; fake-child tests | | Removal proof | `RemoteNodeRegistry` retired after all enrolled peer routing moves to ZoneLink handler | @@ -2525,10 +2545,10 @@ The following transitions are NOT simple textual renames: | Reuse source | Same v3 baseline `b5ddbed6`; `assertions.nix` pattern reused for Zone assertions; `realm-controller-config-json.nix` is the structural template | | Reuse action | new module following same pattern | | Destination | `nixos-modules/options-zones.nix` (new structural base), `nixos-modules/generated/options-zones-.nix` (generated per ResourceType by `xtask gen-zone-nix-options`), `nixos-modules/assertions.nix` (new Zone assertions) | -| Detailed design | Declare the structural base option `d2b.zones..resources. = { type = ...; spec = {}; }` as specified in the "Option schema" section above. Wire `options-zones.nix` and all `generated/options-zones-*.nix` files into `nixos-modules/default.nix`. Add a new `xtask gen-zone-nix-options` command that reads `docs/reference/schemas/v3/.schema.json` for each ResourceType and emits a generated submodule overlaying typed spec options (types, bounds, enum constraints, defaults, docs) onto `d2b.zones..resources..spec`. These generated modules are committed and kept in sync by `xtask gen-zone-nix-options && git diff --exit-code` wired into `make test-drift`. Because the generated options carry field-level type constraints, field-level eval errors (wrong enum, out-of-range int, non-hex fingerprint) are caught without explicit assertions. Explicit assertions in `nixos-modules/assertions.nix` cover cross-resource invariants only: zone/resource key name regex, reserved names, multi-parent detection, ref resolution, count limits, and transportSettings secret-key exclusion (listed in the eval-time assertions table). Each new assertion must have a matching case in `tests/unit/nix/cases/zone-assertions.nix` (nix-unit auto-discovered). `d2b.realms` option namespace is NOT removed in this work item. | +| Detailed design | Declare the structural base option `d2b.zones..resources. = { type = ...; spec = {}; }` as specified in the "Option schema" section above. Wire `options-zones.nix` and all `generated/options-zones-*.nix` files into `nixos-modules/default.nix`. Add a new `xtask gen-zone-nix-options` command that reads `docs/reference/schemas/v3/.schema.json` for each ResourceType and emits a generated submodule overlaying typed spec options (types, bounds, enum constraints, defaults, docs) onto `d2b.zones..resources..spec`. These generated modules are committed and kept in sync by `xtask gen-zone-nix-options && git diff --exit-code` wired into `make test-drift`. Because the generated options carry field-level type constraints, field-level eval errors (wrong enum, out-of-range int, non-hex fingerprint) are caught without explicit assertions. Explicit assertions in `nixos-modules/assertions.nix` cover cross-resource invariants only: zone/resource key name regex, reserved names, child-local `childZoneName == zone`, at most one uplink resource per non-root Zone and none in local root, allocator bootstrap multi-parent/cycle rejection, ref resolution, count limits, and transportSettings secret-key exclusion (listed in the eval-time assertions table). Each new assertion must have a matching case in `tests/unit/nix/cases/zone-assertions.nix` (nix-unit auto-discovered). `d2b.realms` option namespace is NOT removed in this work item. | | Integration | `nixos-modules/zone-resources-json.nix` (ADR046-routing-012) iterates `d2b.zones..resources.*` to emit the bundle | | Data migration | None; Zone options are new; Realm options retained until migration PR | -| Validation | `nix-unit: zone-name-regex`, `nix-unit: zone-link-fingerprint`, `nix-unit: zone-link-multi-parent`, `nix-unit: capability-ceiling-unknown-verb`, `nix-unit: transport-settings-secret-key`; add `drift: zone-nix-options` (`xtask gen-zone-nix-options && git diff --exit-code`); run `make nix-unit-pin` after adding eval cases | +| Validation | `nix-unit: zone-name-regex`, `nix-unit: zone-link-fingerprint`, `nix-unit: zone-link-child-name`, `nix-unit: zone-link-one-uplink`, `nix-unit: capability-ceiling-unknown-verb`, `nix-unit: transport-settings-secret-key`; add `drift: zone-nix-options` (`xtask gen-zone-nix-options && git diff --exit-code`); run `make nix-unit-pin` after adding eval cases | | Removal proof | `nixos-modules/options-realms-workloads.nix` `d2b.realms` namespace retires after all hosts migrate to `d2b.zones` | ### ADR046-routing-012 @@ -2541,7 +2561,7 @@ The following transitions are NOT simple textual renames: | Reuse source | `realm-controller-config-json.nix` structural template; `xtask gen-schemas` extension point (main `a1cc0b2d` unchanged in this area) | | Reuse action | extend and adapt | | Destination | `nixos-modules/zone-resources-json.nix` (new), `nixos-modules/bundle-artifacts.nix` (new row for per-Zone `resource-bundle.json`), `packages/xtask/src/main.rs` (`gen-zone-schemas` subcommand emitting `docs/reference/schemas/v3/.schema.json` for Zone and ZoneLink; `gen-zone-nix-options` subcommand emitting `nixos-modules/generated/options-zones-.nix`) | -| Detailed design | `zone-resources-json.nix` iterates `d2b.zones..resources.*` to produce the canonical sorted resource list: for each entry, render `{ apiVersion, type, metadata: { name, zone, ownerRef: , labels: , annotations: }, spec: }`. The bundle JSON omits `managedBy` and `configurationGeneration`; the configuration service/core sets those fields when activating the validated bundle. Sort all resources by `(type, zone, name)`. Compute `generationId` as SHA-256 (lower hex) of the UTF-8 bytes of the sorted `resources` array JSON. Compute `integrity` as SHA-256 (base64url, no padding) of the full bundle JSON with integrity field zeroed. Install at `/etc/d2b/zones//resource-bundle.json` root:d2bd 0640. Canonical form: all object keys sorted lexicographically; order-significant arrays preserved; schema-declared set-like arrays sorted lexicographically; all optional fields emitted with defaults; no field renaming or restructuring. Build-time validation runs in a Nix derivation: (1) validate each resource against the committed JSON Schema; (2) validate `transportSettings` for each ZoneLink against the Provider's `transportSettingsSchema` — `transportProviderRef` is always explicit, never inferred or defaulted; (3) verify capability ceilings are subsets of parent grants; (4) check for duplicate `(type, zone, name)` tuples. Providers MUST commit their `transportSettingsSchema` before any ZoneLink can reference them. Drift gates: `xtask gen-zone-schemas && git diff --exit-code` and `xtask gen-zone-nix-options && git diff --exit-code` both wired into `make test-drift`. Add `checks.${system}.zone-schema-drift` to `flake.nix`. | +| Detailed design | `zone-resources-json.nix` iterates `d2b.zones..resources.*` to produce the canonical sorted resource list: for each entry, render `{ apiVersion, type, metadata: { name, zone, ownerRef: , labels: , annotations: }, spec: }`. The bundle JSON omits `managedBy` and `configurationGeneration`; the configuration service/core sets those fields when activating the validated bundle. Sort all resources by `(type, zone, name)`. Compute `generationId` as SHA-256 (lower hex) of the UTF-8 bytes of the sorted `resources` array JSON. Compute `integrity` as SHA-256 (base64url, no padding) of the full bundle JSON with integrity field zeroed. Install at `/etc/d2b/zones//resource-bundle.json` root:d2bd 0640. Canonical form: all object keys sorted lexicographically; order-significant arrays preserved; schema-declared set-like arrays sorted lexicographically; all optional fields emitted with defaults; no field renaming or restructuring. Build-time validation runs in a Nix derivation: (1) validate each resource against the committed JSON Schema; (2) validate `transportSettings` for each child-local ZoneLink against its same-Zone Provider's `transportSettingsSchema` — `transportProviderRef` is always explicit, never inferred or defaulted; (3) verify capability ceilings are subsets of the sealed parent allocator grants; (4) verify `childZoneName == metadata.zone`, at most one uplink resource per non-root Zone, no local-root uplink, and no multi-parent/cycle in private bootstrap edges; (5) check for duplicate `(type, zone, name)` tuples. Providers MUST commit their `transportSettingsSchema` before any ZoneLink can reference them. Drift gates: `xtask gen-zone-schemas && git diff --exit-code` and `xtask gen-zone-nix-options && git diff --exit-code` both wired into `make test-drift`. Add `checks.${system}.zone-schema-drift` to `flake.nix`. | | Integration | `nixos-modules/bundle-artifacts.nix` installs per-Zone `resource-bundle.json`; ADR046-routing-013 Zone runtime reads it on startup | | Data migration | None; new artifact file | | Validation | `drift: zone-resource-schema`, `drift: zone-nix-options`, `build: zone-bundle-deterministic`, `build: transport-settings-unknown-field`, `build: capability-ceiling-superset`, `build: missing-transport-provider`; run `make flake-matrix-pin` after adding flake checks | @@ -2560,5 +2580,5 @@ The following transitions are NOT simple textual renames: | Detailed design | Implement the configuration ownership and cleanup contract from the "Configuration ownership and cleanup contract" section. `configuration.rs` owns: (1) reading and integrity-verifying `/etc/d2b/zones//resource-bundle.json` on startup and SIGHUP; (2) diffing against active generation by `generationId` (no-op if unchanged); (3) queuing Create/UpdateSpec/Delete intents — core sets `configurationGeneration` and `managedBy` when applying Create/UpdateSpec; Delete targets only resources where BOTH `managedBy` equals the configuration service's value AND `configurationGeneration` matches the prior bundle — resources with `managedBy=controller` or `managedBy=api` are never seized; (4) setting `deletionRequestedAt` on pending-delete resources immediately and adding a Pending condition; (5) writing the prior bundle into the capped ring at `/var/lib/d2b/zones//configuration/prior/.json` (default retentionCount=3, range 1..16, no TTL; prune oldest when count would exceed limit); (6) enforcing boundary invariants (no diff-delete for absent `configurationGeneration`, `managedBy` collision guard, live controller-child teardown guard); (7) driving finalizer drain + controller-child cascade before completing a Delete; (8) on successful deletion: one store transaction writes the `Deleted` revision/change event and removes the resource row and all index entries; the authoritative audit record (`zone-resource-cleanup`) is appended from the committed revision with dedup/exactly-once recovery and is NOT part of the store transaction; (9) tracking `deletionRequestedAt`/`cleanupConfigGeneration`/`cleanupError`/`cleanupAttempt` per resource; (10) on rollback: clearing `deletionRequestedAt` and Pending condition for revived resources; (11) never pruning a prior bundle while a Delete intent from its `configurationGeneration` is in flight. OFD lock on the bundle file prevents concurrent activation races. Generation state persisted atomically at `/var/lib/d2b/zones//configuration/generation.json` (root:d2bd 0640). The `spec` object comparison for UpdateSpec detection uses the canonical JSON form so two identical specs always compare equal regardless of Nix rendering order. Resource phase transitions: Pending while Create/UpdateSpec in-flight; Degraded while cleanup pending; Ready when clean; Failed on permanent error. | | Integration | `d2b-core-controller` configuration service activates on bundle install and SIGHUP; zone-controller reconcile loops in `d2b-core-controller` consume the queued intents; d2b-bus resource API exposes `status.phase` and `pendingCleanup` via Get/Watch on the active generation resource | | Data migration | None; new runtime component | -| Validation | `host-integration: cleanup-removed-zonelink`, `host-integration: rollback-restores-zonelink`, `host-integration: dynamic-child-not-deleted`; unit tests: deterministic generationId, no-op on same generationId, cross-ownership invariant enforcement, prior-bundle write/prune cycle, UpdateSpec canonical comparison, store-transaction-then-audit-append ordering, exactly-once audit dedup | +| Validation | `host-integration: cleanup-removed-zonelink`, `host-integration: rollback-restores-zonelink`, `host-integration: dynamic-child-not-deleted`, `host-integration: zonelink-no-reciprocal-row`; unit tests: deterministic generationId, no-op on same generationId, cross-ownership invariant enforcement, prior-bundle write/prune cycle, UpdateSpec canonical comparison, store-transaction-then-audit-append ordering, exactly-once audit dedup | | Removal proof | `realm_access_resolver.rs` (B) retires after `d2b-core-controller` configuration tracking is live; `RealmControllersJson` (C) retires after all hosts migrated to Zone bundles | From d7cfc5fe39d917e008ac43be4a16a51d9a50317e Mon Sep 17 00:00:00 2001 From: John Vicondoa Date: Fri, 24 Jul 2026 02:21:40 -0700 Subject: [PATCH 082/115] docs: define ADR46 Zone parent topology Make parent ownership an explicit compiler-only Nix input so sealed allocator bootstrap edges are reproducible without changing Zone.spec or introducing cross-Zone refs. Bind child-local ZoneLinks to the selected parent while keeping transport and route state local. --- docs/specs/ADR-046-nix-configuration.md | 103 +++++++----- docs/specs/ADR-046-resources-zone-control.md | 116 ++++++++----- docs/specs/ADR-046-zone-routing.md | 166 ++++++++++++------- 3 files changed, 248 insertions(+), 137 deletions(-) diff --git a/docs/specs/ADR-046-nix-configuration.md b/docs/specs/ADR-046-nix-configuration.md index 53f1e9fd5..fe804eaae 100644 --- a/docs/specs/ADR-046-nix-configuration.md +++ b/docs/specs/ADR-046-nix-configuration.md @@ -40,8 +40,9 @@ Specifically: `ComponentSession` (new; may copy/adapt main commit `a1cc0b2d`). These crates are compile-checked in `d2bd` via `realm_stubs.rs` (explicitly `dead_code`-allowed, not called from the running daemon at baseline). -- `RealmControllersJson` / `realm-controllers.json` → target Zone self-resource - in `zones//zone.json`. +- `RealmControllersJson` / `realm-controllers.json` → runtime-created Zone + self-resource, compiler-only `parentZone` topology sealed into allocator + bootstrap state, and child-local ZoneLink transport/route state. - `RealmWorkloadsLauncherV2Json` / `realm-workloads-launcher-v2.json` → target Process resource annotations in `zones//processes.json`. - `Capability` enum → see capability disposition table below (mapping resolved; @@ -283,18 +284,31 @@ Each Zone is declared under `d2b.zones.`. The option path replaces `` matches `^[a-z][a-z0-9-]*$` — the same LABEL_PATTERN as `RealmId`. ```nix +d2b.zones.local-root = { + label = "Local root"; # parentZone is forbidden on this Zone +}; + d2b.zones.dev = { - label = "Development"; # optional; defaults to attribute name + label = "Development"; # optional; defaults to attribute name + parentZone = "local-root"; # required for every non-root Zone }; ``` -Zone-level Nix options (`label`, `retainedGenerations`, `trustedPublishers`) are -compiler/configuration-service settings. They control build behavior and are -not emitted into the Zone ResourceSpec. Parent/child hierarchy is represented -by one child-local uplink `ZoneLink` in each non-root Zone. The allocator that -provisions that child supplies the parent edge through sealed private bootstrap -state; there is no `parentRef` in the Zone spec and no reciprocal parent-store -ZoneLink resource. +Zone-level Nix options (`label`, `parentZone`, `retainedGenerations`, +`trustedPublishers`) are compiler/configuration-service settings. They control +build behavior and are not emitted into the Zone ResourceSpec. `parentZone` is +a plain Zone name, not a `ResourceRef`. It is required for every non-root Zone, +must resolve to another declared `d2b.zones.`, and is forbidden on the +distinguished local-root Zone. The compiler turns the resulting child→parent +map into the sealed private allocator-bootstrap topology; it never emits +`parentZone` into a Zone resource bundle or `Zone.spec`, which remains exactly +`{}`. + +The scalar `parentZone` setting selects exactly one allocator owner for the +child. One optional child-local uplink `ZoneLink` supplies that edge's transport +binding and local route/session state. A non-root Zone with no enabled ZoneLink +still has an authored parent but remains disconnected. There is no `parentRef` +in the Zone spec and no reciprocal parent-store ZoneLink resource. Every declared Zone causes its runtime to maintain exactly one `Zone/` self-resource with an empty spec. The self-resource is @@ -336,9 +350,10 @@ declared in that child Zone's `resources` set. `spec.childZoneName` is an identity anchor and MUST equal the enclosing child Zone name. The parent allocator owns privileged listener creation, namespace/cgroup placement, and route allocation, but it does not create a reciprocal resource in the parent -store. The sealed bootstrap allocation binds this local ZoneLink UID and child -identity to exactly one parent edge. There is no `parentRef` in either Zone's -spec and no cross-Zone ResourceRef. Current source for zone hierarchy: +store. The compiler resolves the enclosing Zone's `parentZone`, and the sealed +bootstrap allocation binds this local ZoneLink UID and child identity to that +one selected parent edge. There is no `parentRef` in either Zone's spec and no +cross-Zone ResourceRef. Current source for zone hierarchy: `d2b-realm-core/src/realm.rs` (`RealmPath` dotted hierarchy — e.g., `work.personal`); `d2b.realms..parent` option in `nixos-modules/options-realms.nix`. @@ -358,10 +373,15 @@ An eval assertion verifies that every `ZoneLink` in `d2b.zones..resources` has `spec.childZoneName = ""`, that each non-root Zone has at most one uplink resource (enabled or disabled), and that the local-root Zone has none. -Private allocator bootstrap edges are checked for one parent per child, cycles, -and depth before publication. There is no bidirectional `parentRef` or -undefined reciprocal link; the child-local ZoneLink is the sole resource -declaration for that edge. +The compiler validates the `parentZone` map before publication: `local-root` +must omit the setting, every other Zone must have one resolved scalar parent, +no Zone may name itself, the graph must be acyclic, and every ancestry path +may contain at most 16 Zone names (the retained `MAX_REALM_LABELS` bound). +Conflicting scalar definitions fail through normal Nix module merging, so one +child cannot acquire two parents. There is no bidirectional `parentRef` or +undefined reciprocal link; `parentZone` is the topology input and the +child-local ZoneLink is the sole resource declaration for its transport and +local route state. `ZoneLink.spec.transportProviderRef` is always explicit. There is no default transport and no inference. Omitting `transportProviderRef` from a `ZoneLink` @@ -901,8 +921,12 @@ d2b.zones.local-root.resources.mic-export = { }; }; -# The consumer's uplink is a local resource. The parent allocator supplies the -# sealed route allocation and owns all privileged listener/routing effects. +# Compiler-only topology selects the allocator owner; this is not emitted into +# Zone.spec or any ResourceSpec. +d2b.zones.work.parentZone = "local-root"; + +# The consumer's uplink is a local resource. It supplies transport/local route +# state while the selected parent allocator owns all privileged effects. d2b.zones.work.resources.work-uplink = { type = "ZoneLink"; spec = { @@ -1596,6 +1620,7 @@ All ref validation runs at Nix eval time: | `subjects` entries | Each entry is a canonical `/` ref string resolving to a declared resource of the stated type in the same Zone; type must be in the closed subject set | | `transportProviderRef` resolution | The named Provider is declared in `d2b.zones..resources` with `type = "Provider"`; required on every ZoneLink; no default | | `childZoneName` check | Plain Zone name, not a ResourceRef; it equals the enclosing `d2b.zones.` key, each non-root Zone has at most one uplink resource, and local root has none | +| `parentZone` topology | Compiler-only plain Zone name, not a ResourceRef; omitted on `local-root`, required everywhere else, resolves to one declared Zone, is not self, forms no cycle, and keeps every ancestry path within 16 Zone names | | `ResourceExport` Service-only ref | `providerRef` and `resourceRef` resolve locally; `resourceRef` has the factory's qualified `*Service` type; Device/Endpoint/Binding/backing and cross-Zone targets reject | | `ResourceImport` local route/factory | `providerRef` and `zoneLinkRef` resolve locally; `exportKey` is bounded/not a Ref; expected Service type and projection/factory fingerprints match the signed export and local factory | | Qualified Binding refs | `serviceRef` resolves to the same-Zone matching Service; target ref resolves to a factory-allowed same-Zone Guest/User/Zone; Binding spec is intent-only, observations are status-only, and Binding is non-exportable | @@ -1877,8 +1902,9 @@ or bundle JSON. ### Cross-Zone generation ordering -When child Zone A has a local uplink `ZoneLink` to parent Zone B, neither -bundle carries the other Zone's generation or a cross-Zone `cursorRef`. +When child Zone A selects parent Zone B with `A.parentZone` and has a local +uplink `ZoneLink`, neither bundle carries the other Zone's generation or a +cross-Zone `cursorRef`. Advertisement cursors are runtime status owned by A's local ZoneLink handler and the parent allocator/route engine. @@ -2107,7 +2133,10 @@ The Nix compiler detects and rejects at eval time: | Conflict | Rule | | --- | --- | | Duplicate Zone name | Impossible by Nix construction: a Zone name is the unique `d2b.zones.` attr key; there is no Zone `id` field | -| ZoneLink topology cycle | The private allocator bootstrap parent edges bound to child-local ZoneLinks form a cycle | +| Missing/extra Zone parent | Any non-root Zone omits `parentZone`, or `local-root` defines it | +| Conflicting Zone parent | Separate module definitions resolve one Zone's scalar `parentZone` to different values; normal Nix option merging rejects the conflict | +| Unknown/self Zone parent | `parentZone` does not resolve to a declared Zone or equals the child Zone | +| Zone topology cycle/depth | The compiler-only `parentZone` graph cycles or an ancestry path exceeds 16 Zone names | | CIDR overlap | Two Networks in the same Zone with overlapping subnets | | Guest name reserved prefix | A `Guest/` starting with `sys-` | | Owner cycle | Any `ownerRef` chain that loops | @@ -2141,8 +2170,8 @@ destination is integrated and tested. | `nixos-modules/processes-json.nix` | `VmProcessDag`/`ProcessRole`/`binaryPath`/argv | Yes | `resources-zones-processes.nix`; emits `zones//processes.json` | ADR046-nix-006 | | `nixos-modules/storage-json.nix` | `StorageJson`/`StoragePathSpec`/ownership/ACL contract rows | Yes | `resources-zones-volumes.nix`; rows migrate to Volume resources | ADR046-nix-007 | | `nixos-modules/sync-json.nix` | `SyncJson` OFD lock contract rows | Yes | Internal to `d2b-contracts`; removed from Nix artifacts | ADR046-nix-007 | -| `nixos-modules/allocator-json.nix` | `AllocatorJson` — realm/env bridge assignments, socket paths | Yes | Folded into Zone/Network/Host resources | ADR046-nix-008 | -| `nixos-modules/realm-controller-config-json.nix` | `RealmControllersJson`/`realm-controllers.json` | Yes (read by live d2bd `realm_access_resolver`) | Zone bootstrap bundle `zones//zone.json` | ADR046-nix-008 | +| `nixos-modules/allocator-json.nix` | `AllocatorJson` — realm/env bridge assignments, socket paths | Yes | Privileged effects remain allocator-owned; parent edges come from compiler-only `parentZone`; Network/Host resources carry declarative intent | ADR046-nix-008 | +| `nixos-modules/realm-controller-config-json.nix` | `RealmControllersJson`/`realm-controllers.json` | Yes (read by live d2bd `realm_access_resolver`) | Runtime-created Zone self-resource + sealed `parentZone` allocator topology + child-local ZoneLink | ADR046-nix-008 | | `nixos-modules/realm-workloads-launcher-v2-json.nix` | `RealmWorkloadsLauncherV2Json`/`realm-workloads-launcher-v2.json` | Yes | Provider/display-wayland + Provider/shell-terminal Process configs in `zones//processes.json` | ADR046-nix-009 | | `nixos-modules/realm-identity-config-json.nix` | `RealmIdentityConfigJson`/`realm-identity.json` | Yes (read by live d2bd) | Credential resource specs; identity config inside Provider resources | ADR046-nix-009 | | `nixos-modules/unsafe-local-workloads-json.nix` | `unsafe-local-workloads.json` | Yes | User-only Host + Process resources | ADR046-nix-010 | @@ -2177,7 +2206,7 @@ marked compile-only. | `ProcessRole.Virtiofsd` + `share.source == "/nix/store"` sentinel | `d2b-core/src/processes.rs`, `nixos-modules/processes-json.nix` | Live | `Process` under `Provider/volume-virtiofs` + Volume nix-closure source | ADR046-nix-006 | | `StorageJson`, `StoragePathSpec`, `PrincipalRef { kind: "uid"|"user" }` | `d2b-core/src/storage.rs`, `nixos-modules/storage-json.nix` | Live (storage.json consumed by broker) | `Volume` layout/views; `PrincipalRef` uid-kind → `User/` ref | ADR046-nix-007 | | `SyncJson`, OFD lock rows | `d2b-core/src/sync.rs`, `nixos-modules/sync-json.nix` | Live | Internal `d2b-contracts` implementation mechanism; removed from Nix artifacts | ADR046-nix-007 | -| `RealmControllersJson`, `RealmControllerMetadataSummary` | `d2b-core/src/realm_controller_config.rs`; read live by d2bd `realm_access_resolver` | Live | Zone self-resource + ZoneLink bootstrap; `realm-controllers.json` retained during migration | ADR046-nix-008 | +| `RealmControllersJson`, `RealmControllerMetadataSummary` | `d2b-core/src/realm_controller_config.rs`; read live by d2bd `realm_access_resolver` | Live | Runtime Zone self-resource + compiler-only `parentZone` allocator topology + child-local ZoneLink transport/route state; `realm-controllers.json` retained during migration | ADR046-nix-008 | | `RealmWorkloadsLauncherV2Json`, `LauncherWorkloadSummary` | `d2b-core/src/realm_workloads_launcher.rs` | Live | Process resource annotations in `zones//processes.json` | ADR046-nix-009 | | `RealmIdentityConfigJson` | `d2b-realm-core/src/identity_config.rs`; loaded live by d2bd | Live | Credential resource `scope`/`audience`/`allowedOperations`/`spec.provider.settings` fields; `realm-identity.json` retained during migration | ADR046-nix-009 | | `WorkloadProviderKind`, `IsolationPosture`, `WorkloadExecutionPosture` | `d2b-realm-core/src/workload.rs` | Live in launcher metadata | `LocalVm`/`QemuMedia`/`ProviderManaged` → `Guest.providerRef` per table; `UnsafeLocal` → user-only `Host` with `spec.isolationPosture: "none"` (never `Guest`; not a v3 Provider) | ADR046-nix-001 | @@ -2224,14 +2253,14 @@ Never accepted in any Nix-authored resource spec or generated artifact: | Proof | Description | | --- | --- | -| Zone self-resource round-trip | Nix → `zone.json` → Zone runtime → resource API GET returns matching spec | +| Zone self-resource round-trip | Nix Zone declaration → runtime-created Zone self-resource → resource API GET returns `spec = {}` with no compiler-only settings | | Provider install resource | Nix catalog + Provider resource → core controller lifecycle → Ready status | | Host system/user Process | Process under Host/host-system with system-systemd; locally held pidfd | | Guest with closure Volume | Guest + virtiofs Volume → per-VM store farm → guest `/nix/store` without direct host store export | -| Cross-Zone ZoneLink | Child Zone declares one local uplink with self-matching `childZoneName` and local `transportProviderRef`; parent allocator binds the private edge and owns privileged route creation; no reciprocal resource or `parentRef` | +| Cross-Zone ZoneLink | Child Zone's `parentZone` selects one allocator owner; its local uplink has self-matching `childZoneName` and local `transportProviderRef`; the selected allocator binds the private edge and owns privileged route creation; no reciprocal resource, `parentRef`, or emitted `parentZone` | | Configuration rollback | Two-generation rollback restores prior Zone resource state | | Ref validation rejection | Malformed or missing ref fails eval with structured error | -| Conflict detection | CIDR overlap, owner cycle, and duplicate type/name rejection at eval time | +| Conflict detection | `parentZone` missing/unknown/self/cycle/depth, CIDR overlap, owner cycle, and duplicate type/name rejection at eval time | | ProcessRole parity | Every `ProcessRole` variant has a corresponding test case in the Process/EphemeralProcess resource schema | | Unsafe-local Host | User-only `Host` with `spec.isolationPosture: "none"` reconciled by `Provider/system-core`; child Processes use normal Process Providers; `NoIsolation` condition present in Host status; `no_isolation=true` in `ProcessEffect` audit events; OTEL never labels isolation; CLI/UI warning non-suppressible; no `Guest` emitted | | ResourceTypeSchema validation | Every emitted `spec` validates against committed JSON Schema at build time; schema drift gate passes; unknown field in `spec.provider.settings` (where schema declares it) or Provider `config` fails build | @@ -2309,11 +2338,11 @@ contract work item (ADR046-bus-011/ADR046-bus-012). Cross-reference: | Dependency/owner | W0; `d2b-contracts` identities (ADR046-identities-001, ADR046-identities-002) | | Current source | `nixos-modules/options-realms.nix` (`RealmId`/`RealmPath`/`RealmControllerPlacement`/`EntrypointMode` labels); `options-realms-workloads.nix` (`WorkloadId`/`WorkloadProviderKind`/`IsolationPosture`/`WorkloadExecutionPosture`); `options-realms-network.nix`; `d2b-realm-core/src/realm.rs`, `workload.rs`, `capability.rs`, `ids.rs` (symbols to adapt) | | Reuse action | adapt | -| Destination | `nixos-modules/options-zones.nix` (Zone-level options: `label`, `retainedGenerations`, `trustedPublishers` — compiler settings, not Zone spec fields); `nixos-modules/options-zones-resources.nix` (unified `resources` attrset) | -| Detailed design | `d2b.zones..resources. = { type = ""; spec = { ... }; }` — single attrset covering all ResourceTypes; `type` discriminates dispatch; `spec` fields mirror exact ResourceTypeSchema field names and nesting; Nix option types/defaults/docs generated from `docs/reference/schemas/v3/.json`; no Nix-only fields inside resource declarations; `metadata.name` derives from attr key; `metadata.zone` derives from enclosing zone attr key; `apiVersion` defaulted; `uid`/`generation`/`revision`/`status`/`managedBy` never in Nix; `resource_name` regex `^[a-z][a-z0-9-]*$`; ref validation assertions; `WorkloadProviderKind` → Guest/Host mapping per disposition table above; `Capability` → Role verb mapping per resource-api/authz foundation spec; Zone self-resource spec is `{}`; `retainedGenerations`/`trustedPublishers` are Zone-level compiler settings not emitted in Zone spec | +| Destination | `nixos-modules/options-zones.nix` (Zone-level options: `label`, `parentZone`, `retainedGenerations`, `trustedPublishers` — compiler settings, not Zone spec fields); `nixos-modules/options-zones-resources.nix` (unified `resources` attrset) | +| Detailed design | `d2b.zones..resources. = { type = ""; spec = { ... }; }` — single attrset covering all ResourceTypes; `type` discriminates dispatch; `spec` fields mirror exact ResourceTypeSchema field names and nesting; Nix option types/defaults/docs generated from `docs/reference/schemas/v3/.json`; no Nix-only fields inside resource declarations; `metadata.name` derives from attr key; `metadata.zone` derives from enclosing zone attr key; `apiVersion` defaulted; `uid`/`generation`/`revision`/`status`/`managedBy` never in Nix; `resource_name` regex `^[a-z][a-z0-9-]*$`; ref validation assertions; `WorkloadProviderKind` → Guest/Host mapping per disposition table above; `Capability` → Role verb mapping per resource-api/authz foundation spec; Zone self-resource spec is `{}`; `parentZone` is a required non-root/forbidden-root compiler-only plain Zone name compiled into sealed allocator topology; `retainedGenerations`/`trustedPublishers` are likewise Zone-level compiler settings not emitted in Zone spec | | Integration | `nixos-modules/default.nix` imports new options files; old realms options coexist until ADR046-nix-002 | | Data migration | Operator configs migrate `d2b.realms.*` → `d2b.zones.*`; `d2b.vms.*` → `d2b.zones..resources.*` with `type = "Guest"` | -| Validation | nix-unit vectors for each ResourceType; ref-validation rejection vectors; malformed ref error shape; child-local ZoneLink `childZoneName` must equal its enclosing Zone key; a second uplink resource (even disabled) and a local-root uplink fail eval; missing/local-unresolved `transportProviderRef` fails eval; allocator bootstrap topology rejects multi-parent and cycles; `managedBy` in spec rejected at eval; Zone spec is `{}` (no `parentRef`, `retainedGenerations`, etc.) | +| Validation | nix-unit vectors for each ResourceType; ref-validation rejection vectors; malformed ref error shape; `parentZone` is missing on non-root/forbidden on local-root/unknown/self/cyclic/over-depth rejection; scalar module conflicts prove one parent; child-local ZoneLink `childZoneName` must equal its enclosing Zone key; a second uplink resource (even disabled) and a local-root uplink fail eval; missing/local-unresolved `transportProviderRef` fails eval; `managedBy` in spec rejected at eval; Zone spec is `{}` (no `parentZone`, `parentRef`, `retainedGenerations`, etc.) | | Tests | `tests/unit/nix/cases/zones-options.nix`, `tests/unit/nix/cases/zones-ref-validation.nix`, `tests/unit/nix/cases/zones-zonelink.nix` | | Drift pin | `make nix-unit-pin` after adding cases | | Removal proof | `options-realms*.nix` removed after `options-zones*.nix` achieves parity and parity drift test passes | @@ -2421,14 +2450,14 @@ contract work item (ADR046-bus-011/ADR046-bus-012). Cross-reference: | Dependency/owner | ADR046-nix-004; Zone/Network migration | | Current source | `nixos-modules/allocator-json.nix` (realm/env bridge assignments, `allocatorStateDir`, socket paths, `providerPlacement`); `nixos-modules/realm-controller-config-json.nix` (emits `realm-controllers.json`); `packages/d2b-core/src/realm_controller_config.rs` (`RealmControllersJson`, `RealmControllerMetadataSummary`; read live by `d2bd/src/realm_access_resolver.rs` from `/etc/d2b/realm-controllers.json`) | | Reuse action | adapt and retire | -| Destination | Zone self-resource in `zones//zone.json`; allocator concept retired from Nix; socket paths in Zone resource spec; `realm-controllers.json` RETAINED during migration (live d2bd reads it) | -| Detailed design | Zone runtime derives socket paths from Zone name; broker row → Network/Host resource; `realm-controllers.json` must remain published until `realm_access_resolver` is replaced by Zone resource API | -| Integration | Zone bootstrap config publishes socket paths consumed by the Zone runtime and `realm_access_resolver` during migration; the replacement Zone resource API takes over before legacy artifacts are removed. | -| Data migration | Zone bootstrap data is regenerated from v3 Zone resources; full d2b 3.0 reset; no allocator/realm-controller state import. | -| Validation | Zone resource round-trip; bootstrap socket path regression tests | +| Destination | Compiler-only `parentZone` map in `nixos-modules/options-zones.nix`; local-root allocator bootstrap compiler/sealer; runtime-created Zone self-resource with `spec = {}`; child-local ZoneLink resource; `realm-controllers.json` RETAINED during migration (live d2bd reads it) | +| Detailed design | Resolve each non-root `parentZone` to one declared Zone; reject a root value, missing/unknown/self/conflicting/cyclic parent, and ancestry deeper than 16 names. Canonicalize sorted child→parent rows and feed them to the local-root allocator's private bootstrap sealer. Parent allocators derive/pre-bind privileged socket/listener state from that topology; no socket path or `parentZone` enters `Zone.spec` or a ResourceRef. Child-local ZoneLinks provide transport/local route state. `realm-controllers.json` remains published until `realm_access_resolver` is replaced. | +| Integration | Nix Zone options → validated topology compiler → local-root allocator bootstrap sealer; ZoneLink controller consumes only its sealed selected-parent binding. During migration, `realm-controllers.json` remains available to the legacy resolver until the Zone topology/ZoneLink path takes over. | +| Data migration | Topology is regenerated from `d2b.zones..parentZone`; full d2b 3.0 reset; no allocator/realm-controller state import. | +| Validation | Zone self-resource round-trip with exact empty spec; `parentZone` required/root-forbidden/existence/one-parent/self/cycle/16-name-depth vectors; topology seal excludes `parentZone` from Zone/resource bundles; bootstrap socket/listener regression tests | | Tests | `tests/unit/nix/cases/zones-bootstrap.nix`; `realm_access_resolver` contract test | | Drift pin | `make nix-unit-pin` | -| Removal proof | `allocator-json.nix`, `realm-controller-config-json.nix`, and `/etc/d2b/allocator.json`/`realm-controllers.json` removed ONLY after `realm_access_resolver` is replaced by Zone bundle reader | +| Removal proof | Legacy `allocator-json.nix`, `realm-controller-config-json.nix`, and `/etc/d2b/allocator.json`/`realm-controllers.json` removed ONLY after the sealed `parentZone` topology and ZoneLink resolver are live; the allocator bootstrap mechanism itself remains | ### ADR046-nix-009 diff --git a/docs/specs/ADR-046-resources-zone-control.md b/docs/specs/ADR-046-resources-zone-control.md index 860c5ae0a..fea190bf8 100644 --- a/docs/specs/ADR-046-resources-zone-control.md +++ b/docs/specs/ADR-046-resources-zone-control.md @@ -270,10 +270,12 @@ Provider named in ZoneLink. The parent never receives a database handle, credential, token, or cross-Zone ResourceRef. The provisioning parent allocator owns privileged listener creation, placement, -and route-namespace effects. It binds the child-local ZoneLink UID and child -identity to exactly one parent edge in sealed bootstrap state and keeps only -allocator/route-engine state in the parent; it does not create a reciprocal -parent-store resource. **Controller/owner**: the child Zone's `zone +and route-namespace effects. The Nix compiler selects that allocator through +the enclosing Zone's compiler-only `parentZone` setting. The allocator binds +the child-local ZoneLink UID and child identity to that one parent edge in +sealed bootstrap state and keeps only allocator/route-engine state in the +parent; it does not create a reciprocal parent-store resource. +**Controller/owner**: the child Zone's `zone link/delegation` handler in the fixed core-controller process owns the ZoneLink resource and its status. It consumes allocator observations through the authenticated session and has no direct privileged-effect path. No other @@ -1816,10 +1818,11 @@ three-layer status like every other standard type. Its Provider controller creates owned `Process`/`Endpoint` children. Binding is never exported and never auto-created, owned, or deleted by the import controller. -- Sharing is **opt-in on both sides**, over the **same parent/child Zone - hierarchy and ZoneLink only**. Every hop applies RBAC and a capability ceiling - and requires an enrolled Noise_KK session. **No FD or resource grant crosses a - Zone.** Payload bytes flow only over bounded, encrypted named streams with a +- Sharing is **opt-in on both sides**, over the **same Nix-compiled + `parentZone` hierarchy and child-local ZoneLink only**. Every hop applies RBAC + and a capability ceiling and requires an enrolled Noise_KK session. **No FD + or resource grant crosses a Zone.** Payload bytes flow only over bounded, + encrypted named streams with a per-import session generation, credits/backpressure, cancel, deadline, and idempotency (D096, ComponentSession/bus spec). Intermediate controllers see ciphertext only. @@ -2028,7 +2031,12 @@ d2b.zones.local-root.resources.mic-export = { }; # Consumer child Zone: declare the one local uplink used by imports. The -# provisioning parent allocator owns privileged listener and routing effects. +# compiler-only setting chooses the allocator owner but is never emitted into +# Zone.spec or the resource bundle. +d2b.zones.work.parentZone = "local-root"; + +# The child-local ZoneLink supplies transport and local route/session state; +# the selected parent allocator owns privileged listener and routing effects. d2b.zones.work.resources.work-uplink = { type = "ZoneLink"; spec = { @@ -2408,6 +2416,9 @@ transport Provider's authenticated ComponentSession using authorized for; the child authorization engine is the sole arbiter. The ZoneLink spec and all of its refs resolve in the child store. Parent-side allocation/route state is not a resource and is not a reciprocal ZoneLink. +The enclosing child's compiler-only `parentZone` chooses which parent allocator +appears on the left side of the diagram; no ZoneLink field can select or +override that owner. A disconnected child uplink: @@ -2453,9 +2464,10 @@ The child Zone's `zone link/delegation` handler reconciles its local uplink: local Zone self-name and `childZoneUid` against the local Zone UID. 2. **Transport resolution**: Resolve `spec.transportProviderRef`; wait for that same-child Provider to be `Ready`. Submit the ZoneLink UID, child - identity, and transport binding to the provisioning parent allocator. The - allocator alone creates privileged listeners/route namespace and returns - the pre-bound transport through sealed bootstrap authority. + identity, and transport binding to the parent allocator selected by the + compiler-only `parentZone` map. The allocator alone creates privileged + listeners/route namespace and returns the pre-bound transport through + sealed bootstrap authority. 3. **Authentication**: Establish the allocator-bound ComponentSession; perform the Noise handshake; authenticate the parent allocator and present the child-local subject. @@ -2903,21 +2915,32 @@ See §14.8 for the canonical envelope format, §14.9 for the bundle format, and ### 14.1 Zone declaration ```nix -d2b.zones.dev.resources.dev = { - type = "Zone"; - spec = {}; +d2b.zones.local-root = {}; # parentZone is forbidden on this Zone + +d2b.zones.dev = { + parentZone = "local-root"; + resources = {}; }; ``` +`parentZone` is a compiler-only plain Zone name, not a `ResourceRef`. It is +required on every non-root Zone, forbidden on `local-root`, and never emitted +into a ResourceEnvelope or `Zone.spec`, which remains exactly `{}`. The +compiler resolves it to one declared parent and writes that edge only into the +sealed private allocator-bootstrap topology. It rejects a missing or unknown +parent, self-parenting, conflicting scalar definitions, cycles, and ancestry +paths longer than 16 Zone names. -The resource attrset key (`dev`) must equal the Zone attrset key; authoring -`type = "Zone"` with a name that differs from the Zone key is an eval assertion -error. The Zone self-resource is created by the Zone runtime on first initialization -with `managedBy=controller` and is never included in the resource bundle. +The Zone self-resource name derives from the Zone attrset key. It is created by +the Zone runtime on first initialization with `managedBy=controller` and is +never included in the resource bundle. Authors do not declare a `type = "Zone"` +resource under `resources`. ### 14.2 ZoneLink declaration ```nix +d2b.zones.guest.parentZone = "local-root"; + d2b.artifacts.transport-unix = { package = pkgs.d2b-provider-transport-unix; type = "provider"; @@ -2945,8 +2968,9 @@ d2b.zones.guest.resources.guest-uplink = { attrset keys. Eval-time checks: `spec.childZoneName` matches `^[a-z][a-z0-9-]*$` and equals the enclosing Zone key; the local root has no uplink; at most one ZoneLink resource (enabled or disabled) exists per non-root -Zone; `spec.limits.*` remain within bounds. The parent edge comes only from the -provisioning allocator's sealed bootstrap state. +Zone; `spec.limits.*` remain within bounds. `parentZone` selects the allocator +owner and is compiled into sealed bootstrap topology; the child-local ZoneLink +supplies transport and local route/session state for that selected edge. ### 14.3 Provider installation @@ -3441,7 +3465,11 @@ Three ordered phases validate the Nix configuration before a bundle is activated | No duplicate entries in `spec.subjects` — RoleBinding only | Nix `assert` | eval error | | No cross-Zone refs anywhere in `spec` (no `/ResourceType/name` form) | Nix `assert` | eval error | | `type = "Provider"` and name `system-core` or `system-minijail` rejected | Nix `assert` | eval error with message | -| `type = "Zone"` and name ≠ Zone attrset key rejected | Nix `assert` | eval error | +| Any authored `type = "Zone"` entry under `resources` | Nix `assert`; Zone self-resource is runtime-created | eval error | +| `parentZone` omitted on a non-root Zone or set on `local-root` | Nix `assert` over Zone options | eval error | +| `parentZone` does not resolve to a declared Zone or equals the child | Nix `assert` over Zone options | eval error | +| `parentZone` graph cycles or exceeds 16 Zone names on an ancestry path | Nix graph walk using retained `MAX_REALM_LABELS` bound | eval error | +| conflicting `parentZone` scalar definitions | Standard Nix module merge | eval error | | ZoneLink: `spec.childZoneName ==` Zone attrset key | Nix `assert` | eval error | | Non-root Zone has at most one ZoneLink resource; local root has none | Nix `assert` | eval error | | `spec.transportProviderRef` matches `^Provider/transport-[a-z][a-z0-9-]*$` — ZoneLink only | Nix `assert` | eval error | @@ -3705,6 +3733,7 @@ Rollback atomically: | `zonelink-transport-credentials-max` | ZoneLink with 9 `transportCredentials` entries fails admission | | `zonelink-child-name-matches-store` | ZoneLink whose `childZoneName` differs from its enclosing child Zone is rejected | | `zonelink-one-child-local-uplink` | A second ZoneLink (even disabled) in one child Zone and any uplink in local root are rejected | +| `zonelink-parent-bootstrap-binding` | Child ZoneLink allocation uses exactly the parent chosen by compiler-only `parentZone`; no transport setting can override it | | `zonelink-parent-has-no-reciprocal-row` | Reconcile creates parent allocator/route state but no parent-store ZoneLink resource | ### 15.3 Provider tests @@ -3797,6 +3826,11 @@ Rollback atomically: | `nix-eval-artifact-id-format` | `spec.artifactId = "Bad_ID"` fails eval with label regex assertion | | `nix-eval-credentialref-declared` | `d2b.zones.dev.credentialRef "missing"` for undeclared Credential fails eval | | `nix-eval-dollar-key-rejected` | Config `{ "$secret" = "x"; }` fails eval | +| `nix-eval-parent-zone-required-root-forbidden` | Missing `parentZone` on a non-root Zone and any definition on `local-root` fail eval | +| `nix-eval-parent-zone-resolves` | Unknown and self-valued `parentZone` settings fail eval | +| `nix-eval-parent-zone-one-parent` | Conflicting scalar `parentZone` module definitions fail through Nix merging | +| `nix-eval-parent-zone-cycle-rejected` | A two-Zone or longer `parentZone` cycle fails eval | +| `nix-eval-parent-zone-depth-bound` | A topology path containing 17 Zone names fails eval; 16 succeeds | | `nix-eval-zonelink-child-name-mismatch-rejected` | Child-local ZoneLink with `childZoneName` unequal to its enclosing Zone key fails eval | | `nix-eval-zonelink-second-uplink-rejected` | A second child-local uplink (even disabled), or any local-root uplink, fails eval | | `nix-eval-zonelink-limits-maxpendingintents-bound` | `maxPendingIntents = 1025` fails eval | @@ -3907,9 +3941,9 @@ The key architectural differences that implementations must track: | Dimension | Baseline Realm (ADR 0043) | Target Zone (ADR 0046) | | --- | --- | --- | -| Identity model | `RealmPath(Vec)`, a tree path; realms may nest up to `MAX_REALM_LABELS=16` | `metadata.name` (single label); parent/child expressed via ZoneLink resources | +| Identity model | `RealmPath(Vec)`, a tree path; realms may nest up to `MAX_REALM_LABELS=16` | `metadata.name` (single label); compiler-only `parentZone` selects the parent while a child-local ZoneLink supplies transport/route state | | Process model | Each realm = one `d2bd` + one broker (multiple pairs on one host) | Zone is a resource record; Zone runtime is the surrounding process | -| Hierarchy representation | Tree path embedded in identity; parent realm is a routing ancestor in `RouteTreeEngine`/`RealmEntrypointTable` | Parent/child is a stored `ZoneLink` resource in the child Zone's store; no path encoding | +| Hierarchy representation | Tree path embedded in identity; parent realm is a routing ancestor in `RouteTreeEngine`/`RealmEntrypointTable` | `parentZone` compiles to sealed private allocator topology; the child store holds only its local ZoneLink; no path encoding or reciprocal parent row | | Config loading | `realm-controllers.json` loaded at daemon startup (`load_realm_controllers_config` at `d2bd/src/lib.rs:1408`); routing remains "inert" but metadata is live | Zone self resource read from redb store; no external JSON config file for Zone identity | | Access resolution | `RealmAccessResolverRequest/Response` + `RealmEntrypointTable` loaded from `/run/current-system/sw/share/d2b/realm-entrypoints.json`; reachable in CLI routing path (`d2b/src/target_routing.rs`) | ZoneLink resource with ComponentSession transport binding; access resolution is a resource-lifecycle operation | | Provider placement | `RealmControllerPlacement` enum (`HostLocal`, `GatewayVm`, `CloudFullHost`, `ProviderController`, `ProviderAgent`) — runtime property of where realm's d2bd runs | Provider component `placement` field — descriptor in Provider resource spec; different semantics (component placement, not realm process placement) | @@ -3935,8 +3969,8 @@ Evidence classes: | `NodeId` (label-shaped) | `d2b-realm-core/src/ids.rs:218` | `implemented-and-reachable` | Host `metadata.name` | | `LABEL_PATTERN = "^[a-z][a-z0-9-]*$"`, `is_label()`, `MAX_ID_LEN = 128`, `IdError` | `d2b-realm-core/src/ids.rs:31,76,28,54` | `implemented-and-reachable` | ResourceName validator; extracted for ResourceRef parser | | `RealmIdentityRef` (opaque, redacts debug) | `d2b-realm-core/src/ids.rs:312` | `implemented-and-reachable` | Opaque identity ref pattern; maps to Zone key-ref in ZoneLink key-pin model | -| `RealmPath(Vec)` | `d2b-realm-core/src/realm.rs:64` | `implemented-and-reachable` | **Not** a target type; encodes hierarchy structurally (path-in-identity). ZoneLink replaces this with a stored resource. `RealmPath` depth (`MAX_REALM_LABELS=16`) maps to ZoneLink depth bound | -| `MAX_REALM_LABELS = 16` | `d2b-realm-core/src/realm.rs:67` | `implemented-and-reachable` | ZoneLink tree depth bound | +| `RealmPath(Vec)` | `d2b-realm-core/src/realm.rs:64` | `implemented-and-reachable` | **Not** a target type; encodes hierarchy structurally (path-in-identity). Compiler-only `parentZone` replaces the path topology; the child-local ZoneLink carries transport/route state. `RealmPath` depth (`MAX_REALM_LABELS=16`) maps to the compiled parent graph's ancestry bound | +| `MAX_REALM_LABELS = 16` | `d2b-realm-core/src/realm.rs:67` | `implemented-and-reachable` | Compiler-only `parentZone` ancestry bound | | `RealmControllerPlacement::{HostLocal, GatewayVm, CloudFullHost, ProviderController, ProviderAgent}` | `d2b-realm-core/src/realm.rs:26` | `implemented-and-reachable` | **Partially reused**: these placement labels map to Provider component `placement` descriptor semantics, but the target is a Provider resource field, not a realm process property. Not a 1:1 rename. | | `EntrypointMode::{HostResident, GatewayBacked}` | `d2b-realm-core/src/realm.rs:12` | `implemented-and-reachable` | Informs ZoneLink transport binding (host-local socket vs. remote transport); maps to the resolved `transportProviderRef` selector semantics | | `RealmTarget { workload: WorkloadId, realm: RealmPath }` | `d2b-realm-core/src/target.rs:39` | `implemented-and-reachable` (CLI routing path) | `RealmTarget` is the current addressable unit (`..d2b`); maps to `ResourceRef` (target scoped to Zone); NOT a Zone identity. `WorkloadTarget = RealmTarget` alias in `d2b-core/src/workload_identity.rs:55` | @@ -3954,7 +3988,7 @@ Evidence classes: | `RoutePlacementClass` | `d2b-realm-core/src/routing.rs:1201` | `implemented-but-unwired` | Low-cardinality placement label for telemetry; maps to Provider component placement OTEL label | | `RouteAuditLabels`, `RouteTelemetryLabels`, `RouteTelemetrySample`, `RouteTelemetryBatch` | `d2b-realm-core/src/routing.rs:1244,1300,1313,1322` | `implemented-but-unwired` | Audit/telemetry shape precedent; maps to ZoneLink OTEL metrics label constraints in §13.2 | | `RouteTreeEngine`, `RouteEngineEvent`, `RouteAdvertisementAdmission`, `DiscoveryQueueDecision`, `RoutePruneReport`, `DirectShortcutAuthorizationRequest` | `d2b-realm-core/src/route_engine.rs:161,30,37,51,70,78` | `implemented-but-unwired` | Live routing engine with dedup, pruning, shortcut auth; exported from `d2b_realm_core::lib.rs:122` but has **zero call sites in live daemon or CLI code** — only used in tests within `route_engine.rs`. ZoneLink does not use route advertisements; the `decide_route` algorithm is an unwired precedent only | -| `RealmEntrypointTable`, `RealmEntrypoint`, `DispatchTarget::{HostResident, GatewayBacked}`, `ResolveError` | `d2b-realm-router/src/target_resolver.rs:103,25,54` | `implemented-and-reachable` (CLI only) | The CLI (`d2b/src/lib.rs:5239`, `target_routing.rs:430`) loads the realm entrypoints file from `/run/current-system/sw/share/d2b/realm-entrypoints.json` (Nix-generated at `nixos-modules/host-daemon.nix:385`) and resolves targets. The daemon (`d2bd`) only uses it in `realm_stubs.rs` (dead code). ZoneLink replaces the entrypoint table with stored resource + ComponentSession transport | +| `RealmEntrypointTable`, `RealmEntrypoint`, `DispatchTarget::{HostResident, GatewayBacked}`, `ResolveError` | `d2b-realm-router/src/target_resolver.rs:103,25,54` | `implemented-and-reachable` (CLI only) | The CLI (`d2b/src/lib.rs:5239`, `target_routing.rs:430`) loads the realm entrypoints file from `/run/current-system/sw/share/d2b/realm-entrypoints.json` (Nix-generated at `nixos-modules/host-daemon.nix:385`) and resolves targets. The daemon (`d2bd`) only uses it in `realm_stubs.rs` (dead code). Compiler-only `parentZone` topology plus child-local ZoneLink ComponentSession transport replace the entrypoint table | | `OperationRouter`, `RouteDecision`, `OperationRoutePlan`, `ReconcilableLease` | `d2b-realm-router/src/lib.rs:306,95,179,636` | `implemented-but-unwired` | In `d2bd` only via `realm_stubs.rs` (`#![allow(dead_code)]`; comment: "not called from the running daemon"); `OperationRouter` dedup model maps to resource API idempotency layer | | `RemoteNodeRegistry`, `RemoteFullHostAdapter`, `RemotePeerClient`, `RemoteDispatchOutcome` | `d2b-realm-router/src/remote_node.rs:252,644,608,627` | `implemented-but-unwired` | Remote full-host node management; in d2bd only via realm_stubs dead-code. No Zone control type equivalent; future Host/node resource design | | `RealmAccessTargetInput`, `RealmAccessAliasBinding`, `RealmAccessResolverRequest`, `RealmAccessResolverResponse`, `RealmAccessBinding`, `RealmTransportBinding`, `HostLocalPeerCredentialSemantics`, `AccessBindingRef` | `d2b-realm-core/src/access.rs:170,285,322,542,597,569,364,96` | `implemented-and-reachable` (CLI path) | Used actively in `d2b/src/target_routing.rs:21–26` which is called from live CLI routing. `RealmTransportBinding::{LocalUnixSocket,RemoteRealmTransport,ProviderRealmTransport}` maps to the resolved ZoneLink transport binding variants | @@ -3982,7 +4016,7 @@ Evidence classes: | Symbol | File | Evidence class | ADR-0046 mapping | | --- | --- | --- | --- | -| `RealmControllersJson`, `RealmControllerConfig`, `RealmControllerPlacement`, `RealmControllerLocalRuntime`, `RealmControllerLocalWorkload`, `RealmAllocatorBinding`, `RealmDaemonConfig`, `RealmBrokerConfig` | `d2b-core/src/realm_controller_config.rs:144,190,256,278,434,541,497,511` | `implemented-and-reachable` | Loaded in live daemon startup at `d2bd/src/lib.rs:1408` and in serve path at line `16741`; `WorkloadTargetIndex::build_from_controllers` is called live. Contains per-realm socket/state paths, placement, providers, workloads. Maps to: Zone self resource (the realm metadata row); child-local ZoneLink plus private parent allocator binding (per-realm parent socket/edge data); Provider (per-realm provider config rows); NOT replaced by a single file — replaced by resource store entries plus sealed allocator state | +| `RealmControllersJson`, `RealmControllerConfig`, `RealmControllerPlacement`, `RealmControllerLocalRuntime`, `RealmControllerLocalWorkload`, `RealmAllocatorBinding`, `RealmDaemonConfig`, `RealmBrokerConfig` | `d2b-core/src/realm_controller_config.rs:144,190,256,278,434,541,497,511` | `implemented-and-reachable` | Loaded in live daemon startup at `d2bd/src/lib.rs:1408` and in serve path at line `16741`; `WorkloadTargetIndex::build_from_controllers` is called live. Contains per-realm socket/state paths, placement, providers, workloads. Maps to: Zone self resource (the realm metadata row); compiler-only `parentZone` compiled into the private allocator binding (per-realm parent socket/edge data); child-local ZoneLink transport/route state; Provider (per-realm provider config rows); NOT replaced by a single file — replaced by resource store entries plus sealed allocator state | | `WorkloadTargetIndex`, `TargetResolution`, `TargetResolutionError` | `d2bd/src/workload_target_index.rs:93,28,56` | `implemented-and-reachable` | Built from `RealmControllersJson` in live serve path (`d2bd/src/lib.rs:16745`); resolves `..d2b` to VM name. Maps to resource API `resourceRef` lookup table in ADR 0046 API layer | #### Allocator types @@ -4024,10 +4058,10 @@ Evidence classes: | `d2b.realms..*`, `providerType` submodule, `providerKind = "^[a-z][a-z0-9-]*$"`, `placementProvider`, `providerSpecificPlacement` | `nixos-modules/options-realms.nix:26,39,55,65,170` | `generated-or-eval-contract` | Authoring interface for realm/provider declarations. Label regex identical to `LABEL_PATTERN` in `ids.rs`. Adapted to `d2b.zones..providers.*` in ADR046-zone-control-007 | | `d2b.realms..workloads.*`, `kind`, `placement`, `legacyVmName` | `nixos-modules/options-realms-workloads.nix` | `generated-or-eval-contract` | Workload authoring submodule; `kind = "local-vm"` / `"qemu-media"` → Guest resource Nix options; **`kind = "unsafe-local"` → Host resource Nix options** (`defaultDomain=user`, `allowedDomains=[user]`); `kind = "provider-placeholder"` → Guest/Host per provider semantics | | `d2b.realms..network.*` | `nixos-modules/options-realms-network.nix` | `generated-or-eval-contract` | Network/bridge authoring; maps to Network resource (not Zone control types) | -| `realm-controllers.json` (`RealmControllersJson`), `schemaVersion = "v2"`, `runtimeState = "metadata-only"`, per-realm rows | `nixos-modules/realm-controller-config-json.nix` (emitter); `d2b-core/src/realm_controller_config.rs:144` (deserializer) | `generated-or-eval-contract` (Nix); `implemented-and-reachable` (consumption in daemon) | **Loaded live in daemon** at `d2bd/src/lib.rs:1408,16741`. Runtime routing "remains inert" but `WorkloadTargetIndex` is built from it. Maps to: Zone self resource (identity/path row); child-local ZoneLink plus private parent allocator binding (parent socket/edge data); Provider (provider config rows). The bundle artifact is replaced by child resource-store entries and sealed allocator state; until then, the schema is the live authority | +| `realm-controllers.json` (`RealmControllersJson`), `schemaVersion = "v2"`, `runtimeState = "metadata-only"`, per-realm rows | `nixos-modules/realm-controller-config-json.nix` (emitter); `d2b-core/src/realm_controller_config.rs:144` (deserializer) | `generated-or-eval-contract` (Nix); `implemented-and-reachable` (consumption in daemon) | **Loaded live in daemon** at `d2bd/src/lib.rs:1408,16741`. Runtime routing "remains inert" but `WorkloadTargetIndex` is built from it. Maps to: Zone self resource (identity row); compiler-only `parentZone` plus private parent allocator binding (topology/socket data); child-local ZoneLink transport/route state; Provider (provider config rows). The bundle artifact is replaced by child resource-store entries and sealed allocator state; until then, the schema is the live authority | | `realm-identity.json` (`RealmIdentityConfigJson`), identity refs, fingerprints | `nixos-modules/realm-identity-config-json.nix` (emitter); `d2b-realm-core/src/identity_config.rs:19` (deserializer) | `generated-or-eval-contract` (Nix); `implemented-and-reachable` (consumption in daemon) | Loaded live at `d2bd/src/lib.rs:1425`; trust sessions "remain inert". Maps to Zone runtime identity key-ref state and store metadata, not Zone.spec | -| `allocator.json` resource request rows (per-realm: cgroup, nftables, bridges, socket paths), `LocalRootAllocatorEngine` | `nixos-modules/allocator-json.nix` (emitter); `d2b-realm-core/src/allocator_engine.rs:332` (engine) | `generated-or-eval-contract` (Nix); `implemented-and-reachable` (engine) | Allocator resource requests are generated per realm from `d2b.realms.*` Nix config and consumed by the allocator at runtime. Maps to Zone runtime startup resource claiming. **Mechanism, not a ResourceType** | -| `realm-entrypoints.json`, loaded from `/run/current-system/sw/share/d2b/realm-entrypoints.json` | `nixos-modules/host-daemon.nix:385` (emitter); `d2b/src/lib.rs:5239` (loader) | `generated-or-eval-contract` (Nix); `implemented-and-reachable` (CLI consumption) | Loaded by `d2b` CLI in live routing path for realm-qualified targets. Maps to ZoneLink access binding (CLI consumes it to find realm socket; replaced by ZoneLink resource + ComponentSession lookup) | +| `allocator.json` resource request rows (per-realm: cgroup, nftables, bridges, socket paths), `LocalRootAllocatorEngine` | `nixos-modules/allocator-json.nix` (emitter); `d2b-realm-core/src/allocator_engine.rs:332` (engine) | `generated-or-eval-contract` (Nix); `implemented-and-reachable` (engine) | Allocator resource requests are generated per realm from `d2b.realms.*` Nix config and consumed by the allocator at runtime. Maps to compiler-only `parentZone` topology plus Zone runtime startup resource claiming. **Mechanism, not a ResourceType** | +| `realm-entrypoints.json`, loaded from `/run/current-system/sw/share/d2b/realm-entrypoints.json` | `nixos-modules/host-daemon.nix:385` (emitter); `d2b/src/lib.rs:5239` (loader) | `generated-or-eval-contract` (Nix); `implemented-and-reachable` (CLI consumption) | Loaded by `d2b` CLI in live routing path for realm-qualified targets. Replaced by the sealed `parentZone` topology plus child-local ZoneLink Resource/ComponentSession lookup | #### Workload posture and unsafe-local types @@ -4113,7 +4147,7 @@ None of the following exist in baseline: | Current source | `packages/d2b-realm-core/src/ids.rs` (`RealmId`, `LABEL_PATTERN = "^[a-z][a-z0-9-]*$"`, `is_label()`, `MAX_ID_LEN = 128`, `IdError` — `implemented-and-reachable`, baseline `b5ddbed6`); `packages/d2b-realm-core/src/realm.rs` (`RealmPath`, `MAX_REALM_LABELS = 16`, `RealmControllerPlacement`, `EntrypointMode` — `implemented-and-reachable`, baseline `b5ddbed6`); Zone resource schema: `ADR-only` | | Reuse action | extract and adapt (`RealmId`→`ResourceName` validators, `RealmPath` depth bound→ZoneLink depth, `RealmControllerPlacement`→Provider component placement); new (Zone resource schema/store table/handler) | | Destination | `packages/d2b-contracts/src/v3/zone.rs`; `packages/d2b-core-controller/src/zone.rs` | -| Detailed design | Zone ResourceType schema with `spec = {}`; self-resource enforcement; store_meta binding checks; phase/conditions; cardinality-1 enforcement; `zone-drain` finalizer; Nix Zone options; canonical JSON schema | +| Detailed design | Zone ResourceType schema with `spec = {}`; self-resource enforcement; store_meta binding checks; phase/conditions; cardinality-1 enforcement; `zone-drain` finalizer; canonical JSON schema. Nix Zone options include compiler-only `parentZone`: required for every non-root Zone, forbidden on `local-root`, and never emitted into `Zone.spec` | | Integration | Zone runtime open/upgrade verifies `Zone/` self resource; configuration publication handler creates/reconciles Zone; resource API rejects cross-Zone refs | | Data migration | Destructive d2b 3.0 reset; no v2/Realm Zone resource import | | Validation | `zone-self-resource-enforced`, `zone-uid-mismatch-quarantine`, `zone-name-mismatch-rejected`, `zone-cardinality-one`, `zone-cross-zone-ref-rejected`, `zone-owner-rejected`, `zone-deletion-only-on-drain` | @@ -4129,10 +4163,10 @@ None of the following exist in baseline: | Reuse source | main `a1cc0b2d`: `packages/d2b-session/src/lifecycle.rs`, `d2b-session-unix/src/adapter.rs` for reconnect/transport precedents | | Reuse action | extract and adapt (`SecurePeerSession` Noise model → ComponentSession Noise KK; `SessionLifecycle`/`SessionPhase` → ZoneLink session reconnect loop and connection detail fields; `Connecting`/`Established` current evidence phases drive `status.connected` and `status.phase` transitions to `Pending`/`Ready`, not direct phase values; `RouteTreeEngine.decide_route()` → cursor tracking; `RealmIdentityStore` enrollment → ZoneLink child key-pin) | | Destination | `packages/d2b-contracts/src/v3/zone_link.rs`; `packages/d2b-core-controller/src/zone_link.rs` | -| Detailed design | Child-local ZoneLink schema with self-matching `spec.childZoneName` plus same-Zone `spec.transportProviderRef`, `spec.transportSettings`, and `spec.transportCredentials`; at most one uplink resource per non-root Zone and none in local root; child-store cursor/intent state; reconnect loop with exponential backoff; local intent queue (max 256 entries); local child UID change detection; drain finalizer; no reciprocal parent-store resource; parent allocator owns privileged listener/placement/route allocation effects and exposes only a sealed bootstrap/allocation interface | -| Integration | child core-controller zone_link handler; child redb `zone_link_cursors` table; parent allocator and route engine through sealed allocation authority; d2b-bus transport resolver; ComponentSession lifecycle | +| Detailed design | Child-local ZoneLink schema with self-matching `spec.childZoneName` plus same-Zone `spec.transportProviderRef`, `spec.transportSettings`, and `spec.transportCredentials`; at most one uplink resource per non-root Zone and none in local root; child-store cursor/intent state; reconnect loop with exponential backoff; local intent queue (max 256 entries); local child UID change detection; drain finalizer; no reciprocal parent-store resource; compiler-only `parentZone` selects the one allocator owner, while that allocator owns privileged listener/placement/route allocation effects and exposes only a sealed bootstrap/allocation interface | +| Integration | child core-controller zone_link handler; child redb `zone_link_cursors` table; Nix-compiled `parentZone` bootstrap topology; selected parent allocator and route engine through sealed allocation authority; d2b-bus transport resolver; ComponentSession lifecycle | | Data migration | Destructive reset; no v2 Realm peer migration | -| Validation | `zonelink-reconnect-child-uid-change`, `zonelink-disconnect-unknown-phase`, `zonelink-intent-queue-limit`, `zonelink-disabled-no-reconnect`, `zonelink-child-auth-denied-failed`, `zonelink-drain-closes-session`, `zonelink-child-name-matches-store`, `zonelink-one-child-local-uplink`, `zonelink-parent-has-no-reciprocal-row` | +| Validation | `zonelink-reconnect-child-uid-change`, `zonelink-disconnect-unknown-phase`, `zonelink-intent-queue-limit`, `zonelink-disabled-no-reconnect`, `zonelink-child-auth-denied-failed`, `zonelink-drain-closes-session`, `zonelink-child-name-matches-store`, `zonelink-one-child-local-uplink`, `zonelink-parent-bootstrap-binding`, `zonelink-parent-has-no-reciprocal-row` | | Removal proof | `realm_stubs.rs` (`ApiFrontend`, `PeerOperationRouter`, `TargetResolver`) removed after ComponentSession integration (ADR046-zone-control-018); `realm_access_resolver.rs` module removed after ZoneLink replaces entrypoint-table resolution; gateway `PeerSession`/`SecurePeerSession` session types remain as dead code in d2b-realm-router until Provider session migration wave | ### ADR046-zone-control-003 @@ -4205,8 +4239,8 @@ None of the following exist in baseline: | Current source | `nixos-modules/options-realms.nix` (`providerKind = "^[a-z][a-z0-9-]*$"` label regex matching `LABEL_PATTERN` in `ids.rs`; `providerType` submodule with `enable`/`kind`/`placement`/`freeformType` fields; `d2b.realms..providers.*` attrset — `generated-or-eval-contract`, baseline `b5ddbed6`); `nixos-modules/options-realms-workloads.nix` (`d2b.realms..workloads.*` submodule shape — `generated-or-eval-contract`); `d2b.zones.*` Nix options: `ADR-only` | | Reuse action | adapt (`providerType` submodule → Provider option submodule; label regex `^[a-z][a-z0-9-]*$` retained unchanged; `placement` option → Provider component placement; `d2b.realms.*` option namespace → `d2b.zones.*` option namespace) | | Destination | `nixos-modules/options-zones.nix`; `nixos-modules/resources-zone-control.nix`; extended `nixos-modules/index.nix` | -| Detailed design | `d2b.zones..*` Nix options for Zone/ZoneLink/Provider/Role/RoleBinding/Quota/EmergencyPolicy authoring; Nix eval-time validation of ResourceRefs, verb enums, digest format, subject types, child-local ZoneLink self-name/one-uplink/local transport-ref constraints, and the no-expiry RoleBinding model; canonical JSON serialization; generation-bound resource bundle output; allocator bootstrap topology validates one parent per child, cycles, and depth without emitting reciprocal resources | -| Integration | Nix resource compiler → configuration publication handler → Zone store; bootstrap Provider records auto-generated | +| Detailed design | `d2b.zones..*` Nix options for Zone/ZoneLink/Provider/Role/RoleBinding/Quota/EmergencyPolicy authoring; compiler-only scalar `parentZone` required on non-root Zones and forbidden on `local-root`; Nix eval-time validation of parent existence, one resolved parent, self/cycle rejection, 16-name ancestry depth, ResourceRefs, verb enums, digest format, subject types, child-local ZoneLink self-name/one-uplink/local transport-ref constraints, and the no-expiry RoleBinding model; canonical JSON serialization; generation-bound resource bundle output; the compiler seals the validated parent map into allocator bootstrap topology without emitting `parentZone` into `Zone.spec` or reciprocal resources | +| Integration | Nix compiler → sealed allocator topology for `parentZone`; resource compiler → configuration publication handler → Zone store; bootstrap Provider records auto-generated | | Data migration | Full reset; Nix realm options (`d2b.realms.*`) remain until purge wave | | Validation | nix-unit vectors for each Zone control type schema; cross-field constraint tests; rendered JSON contract tests (`make test-drift`) | | Removal proof | `nixos-modules/options-realms.nix` and related realm options removed only after Zone resource Nix integration is live | @@ -4398,10 +4432,10 @@ Evidence class for all: `main-reuse-source`. | Reuse source | None from main; eval-time validation is Nix-native | | Reuse action | net-new | | Destination | `nixos-modules/options-zones.nix`, `nixos-modules/resource-type-validators.nix` | -| Detailed design | Declare the unified `d2b.zones..resources.` option tree with two sub-options: `type` (string, required) and `spec` (attrset, required). Declare the `d2b.artifacts.` catalog option tree with `package` (types.package, required) and `type` (closed string enum, required); `d2b.artifacts` is a global option, not zone-local. For each core ResourceType (Zone, ZoneLink, Provider, Role, RoleBinding, Credential), a `genSchemaOptions` helper reads the corresponding ResourceTypeSchema JSON and emits per-type `spec` option declarations with correct Nix types, defaults, and documentation comments. Provider `spec.artifactId` is a plain string referencing the `d2b.artifacts` catalog; the `package` derivation is NOT a `spec` field. Non-core (Provider-registered) ResourceTypes are deferred to Phase 2 schema validation; Phase 1 accepts any non-empty `spec` attrset for non-core types. Implement all Phase 1 eval-time assertions (§14.10 Phase 1 table) via Nix `assert` and `lib.mkAssert`; `credentialRef` helper function producing `{ "$credentialRef": "Credential/" }` literals; `resourceRef` and `closedEnum` helpers; bootstrap provider exclusion assertions; eval assertion that Zone resource name must equal Zone attrset key; child-local ZoneLink assertions require `childZoneName` to equal that key, reject a second uplink resource even when disabled, and reject any local-root uplink. The runtime creates `Zone/`, `Provider/system-core`, and `Provider/system-minijail` directly with `managedBy=controller`; none is emitted or inferred from the configuration bundle. `allowUnsafeLocal` maps to a dedicated Host admission gate (current `options-realms.nix:346`). Provider manifest-derived fields (`spec.exports`, `spec.components`, `spec.dependencies`, `spec.permissionClaims`, `spec.upgradePolicy`, `spec.restartPolicy`) are declared as read-only/not-settable options; setting them is an eval assertion error | -| Integration | Unified `d2b.zones..resources.*` option tree consumed by ADR046-zone-control-015 resource compiler; Zone controller (ADR046-zone-control-001) reads resulting bundle; Provider package conventions from ADR046-zone-control-003 | +| Detailed design | Declare the unified `d2b.zones..resources.` option tree with two sub-options: `type` (string, required) and `spec` (attrset, required), plus the Zone-level compiler-only `parentZone` scalar (plain Zone name, no default). Require `parentZone` on every non-root Zone and forbid it on `local-root`; resolve it against declared Zone keys, reject self-parent/conflicting module definitions/cycles, and cap each ancestry path at 16 Zone names. Compile the validated map into sealed allocator bootstrap topology; never emit it into the resource bundle or `Zone.spec`. Declare the `d2b.artifacts.` catalog option tree with `package` (types.package, required) and `type` (closed string enum, required); `d2b.artifacts` is a global option, not zone-local. For each core ResourceType (Zone, ZoneLink, Provider, Role, RoleBinding, Credential), a `genSchemaOptions` helper reads the corresponding ResourceTypeSchema JSON and emits per-type `spec` option declarations with correct Nix types, defaults, and documentation comments. Provider `spec.artifactId` is a plain string referencing the `d2b.artifacts` catalog; the `package` derivation is NOT a `spec` field. Non-core (Provider-registered) ResourceTypes are deferred to Phase 2 schema validation; Phase 1 accepts any non-empty `spec` attrset for non-core types. Implement all Phase 1 eval-time assertions (§14.10 Phase 1 table) via Nix `assert` and `lib.mkAssert`; `credentialRef` helper function producing `{ "$credentialRef": "Credential/" }` literals; `resourceRef` and `closedEnum` helpers; bootstrap provider exclusion assertions; reject operator-authored `type = "Zone"` resources; child-local ZoneLink assertions require `childZoneName` to equal the enclosing Zone key, reject a second uplink resource even when disabled, and reject any local-root uplink. The runtime creates `Zone/`, `Provider/system-core`, and `Provider/system-minijail` directly with `managedBy=controller`; none is emitted or inferred from the configuration bundle. `allowUnsafeLocal` maps to a dedicated Host admission gate (current `options-realms.nix:346`). Provider manifest-derived fields (`spec.exports`, `spec.components`, `spec.dependencies`, `spec.permissionClaims`, `spec.upgradePolicy`, `spec.restartPolicy`) are declared as read-only/not-settable options; setting them is an eval assertion error | +| Integration | Validated `parentZone` map feeds the allocator bootstrap sealer; unified `d2b.zones..resources.*` option tree is consumed by ADR046-zone-control-015 resource compiler; Zone controller (ADR046-zone-control-001) reads the resulting bundle; Provider package conventions come from ADR046-zone-control-003 | | Data migration | Replace `nixos-modules/options-realms.nix`-derived option trees once Zone controller is live and has reached parity | -| Validation | All Phase 1 eval tests in §15.8 (`nix-eval-name-regex-enforced`, `nix-eval-verb-closed-enum`, `nix-eval-roleref-format`, `nix-eval-subject-type-restricted`, `nix-eval-no-duplicate-subjects`, `nix-eval-bootstrap-provider-rejected`, `nix-eval-provider-missing-artifact-id`, `nix-eval-artifact-id-not-in-catalog`, `nix-eval-artifact-wrong-type`, `nix-eval-artifact-id-format`, `nix-eval-credentialref-declared`, `nix-eval-dollar-key-rejected`, `nix-eval-zonelink-child-name-mismatch-rejected`, `nix-eval-zonelink-second-uplink-rejected`, `nix-eval-zonelink-limits-maxpendingintents-bound`) | +| Validation | All Phase 1 eval tests in §15.8 (`nix-eval-name-regex-enforced`, `nix-eval-verb-closed-enum`, `nix-eval-roleref-format`, `nix-eval-subject-type-restricted`, `nix-eval-no-duplicate-subjects`, `nix-eval-bootstrap-provider-rejected`, `nix-eval-provider-missing-artifact-id`, `nix-eval-artifact-id-not-in-catalog`, `nix-eval-artifact-wrong-type`, `nix-eval-artifact-id-format`, `nix-eval-credentialref-declared`, `nix-eval-dollar-key-rejected`, all five `nix-eval-parent-zone-*` vectors, `nix-eval-zonelink-child-name-mismatch-rejected`, `nix-eval-zonelink-second-uplink-rejected`, `nix-eval-zonelink-limits-maxpendingintents-bound`) | | Removal proof | `nixos-modules/options-realms.nix`, `nixos-modules/realm-controller-config-json.nix`, `nixos-modules/realm-identity-config-json.nix` deleted after Zone controller and resource compiler reach full parity; `nixos-modules/assertions.nix` lines referencing `allowUnsafeLocal`/realm names removed after Host admission validation replaces them | ### ADR046-zone-control-015 @@ -4465,7 +4499,7 @@ Evidence class for all: `main-reuse-source`. | Detailed design | Implement the `ResourceExport` and `ResourceImport` standard ResourceTypes per §8A plus signed Provider `ProjectionFactory` metadata binding qualified Service type, qualified Binding type, allowed owner-Service backing refs, allowed Binding target refs, projection schema/fingerprint, and aggregate factory fingerprint. Admission accepts only an owner Service as `ResourceExport.resourceRef`; matches export/import/local-factory type and fingerprints; and creates exactly one same-qualified-type projection Service (`ownerRef: ResourceImport/`). It never projects Device/Endpoint/Binding and never creates Binding. Binding spec is desired consumer intent only; observations belong only in status. No cross-Zone Ref, FD, secret, path, locator, or resource grant crosses a Zone; payload bytes use bounded encrypted named streams and high-churn sessions/streams remain internal. Export removal/ZoneLink loss revokes leases and degrades the projection Service; reconnect revalidates generation and both fingerprints. D091 currency propagates Service → export → import → projection Service → authored Binding → children. | | Integration | Zone store/redb (ADR046-store-001); shared D098 semantic base catalog (ADR046-provider-004); ZoneLink reconcile (§3); ComponentSession bounded encrypted named streams; signed projection factories/adapters for audio-pipewire, device-security-key, observability-otel, and policy-gated device-usbip; CLI graph rendering | | Data migration | None — full d2b 3.0 reset; no prior cross-Zone sharing state | -| Validation | §8A.7: fast hermetic factory absent/mismatch/tamper, Service-only export target, exactly-one same-type projection Service, no Device/Endpoint/Binding projection, no auto-Binding, intent-only spec/status-only observations, backing/target allowlists, finalizer/update propagation, Provider classification, canonical Nix stability including child-local `ZoneLink/work-uplink` in `d2b.zones.work.resources`, local `zoneLinkRef` resolution, quotas/reconnect/revoke, and no FD/secret/path tests; slower real encrypted-stream integration for audio/security-key/observability/policy-gated USBIP | +| Validation | §8A.7: fast hermetic factory absent/mismatch/tamper, Service-only export target, exactly-one same-type projection Service, no Device/Endpoint/Binding projection, no auto-Binding, intent-only spec/status-only observations, backing/target allowlists, finalizer/update propagation, Provider classification, canonical Nix stability including compiler-only `d2b.zones.work.parentZone = "local-root"`, child-local `ZoneLink/work-uplink` in `d2b.zones.work.resources`, local `zoneLinkRef` resolution, quotas/reconnect/revoke, and no FD/secret/path tests; slower real encrypted-stream integration for audio/security-key/observability/policy-gated USBIP | | Removal proof | Not applicable (new surface) | ### ADR046-zone-control-020 diff --git a/docs/specs/ADR-046-zone-routing.md b/docs/specs/ADR-046-zone-routing.md index 2e3c87935..e510853a1 100644 --- a/docs/specs/ADR-046-zone-routing.md +++ b/docs/specs/ADR-046-zone-routing.md @@ -10,7 +10,7 @@ | Normative | Yes | | Owners | `d2b-zone-routing`, `d2b-bus` Zone route engine, core-controller ZoneLink handler | | Depends on | `ADR-046-terminology-and-identities`, `ADR-046-componentsession-and-bus`, `ADR-046-resource-api-and-authorization`, `ADR-046-resource-object-model`, `ADR-046-resource-reconciliation` | -| Supersedes | `RealmEntrypointTable`/`EntrypointMode`/`RealmControllerPlacement` enum (→ ZoneLink transport + private Zone runtime bootstrap placement); `RouteTreeEngine`/`routing.rs` route contracts (adapted, not deleted); `RemoteNodeRegistry` (→ ZoneLink controller); `OperationRouter` (→ ZoneLinkIdempotencyKey in d2b-bus); `realm_access_resolver` module (→ ZoneEntrypointResolver); `realm-controllers.json`/`realm-identity.json` bundle artifacts (→ Nix-authored Zone/ZoneLink resources); `d2b.realms.*` Nix option namespace (→ `d2b.zones.*`); `WorkloadId` routing targets (→ typed Guest/Host resource refs); `CapabilitySet`-only routing authz (→ per-hop RBAC narrowing + capability ceiling) | +| Supersedes | `RealmEntrypointTable`/`EntrypointMode`/`RealmControllerPlacement` enum (→ compiler-only `parentZone` topology + ZoneLink transport + private Zone runtime bootstrap placement); `RouteTreeEngine`/`routing.rs` route contracts (adapted, not deleted); `RemoteNodeRegistry` (→ ZoneLink controller); `OperationRouter` (→ ZoneLinkIdempotencyKey in d2b-bus); `realm_access_resolver` module (→ ZoneEntrypointResolver); `realm-controllers.json`/`realm-identity.json` bundle artifacts (→ Nix-authored Zone settings/ZoneLink resources); `d2b.realms.*` Nix option namespace (→ `d2b.zones.*`); `WorkloadId` routing targets (→ typed Guest/Host resource refs); `CapabilitySet`-only routing authz (→ per-hop RBAC narrowing + capability ceiling) | ## Source and reuse policy @@ -421,11 +421,14 @@ within a `Realm`. | `ProviderSpecific { provider, placement }` | Private Zone runtime bootstrap: provider-schema-validated placement; child identity via the child-local uplink ZoneLink | This is an **architectural change**: the static placement enum is replaced by -private per-Zone bootstrap configuration plus the child-local uplink -ZoneLink's `childZoneName` and `transportProviderRef`. The provisioning parent -allocator binds the ZoneLink UID and child identity to the private parent edge; -no reciprocal parent-store ZoneLink exists. Placement is NOT a public field in -`Zone.spec`. `Zone.spec` is `{}` — it carries no authored fields. +private per-Zone bootstrap configuration plus two explicit Nix inputs: +compiler-only `d2b.zones..parentZone` selects the allocator owner, while +the child-local uplink ZoneLink's `childZoneName` and `transportProviderRef` +supply transport and local route/session state. The selected parent allocator +binds the ZoneLink UID and child identity to that private parent edge; no +reciprocal parent-store ZoneLink exists. Neither placement nor `parentZone` is +a public field in `Zone.spec`. `Zone.spec` is `{}` — it carries no authored +fields. `EntrypointMode` (`HostResident`/`GatewayBacked`) is used in the CLI target router (`d2b/src/target_routing.rs`) to classify routing decisions. @@ -486,9 +489,10 @@ resources will supersede; the daemon/broker never act on routing or trust-session operations based on them today. **ADR 0046 mapping:** -- `realm-controllers.json` → superseded by Nix-authored ZoneLink resources - and the Zone self-resource. The `RealmControllersJson` schema retires - when Zone resources own placement and path. +- `realm-controllers.json` → superseded by compiler-only `parentZone` topology, + Nix-authored child-local ZoneLink resources, and the runtime-created Zone + self-resource. The `RealmControllersJson` schema retires when sealed allocator + topology owns placement/path and ZoneLinks own transport/route state. - `realm-identity.json` → superseded by ZoneLink `spec.childStaticKeyFingerprint`. The `RealmIdentityConfigJson` schema retires when ZoneLink enrollment handles key pinning. @@ -560,9 +564,13 @@ homed in a different Zone. The d2b v3 model is intentionally constrained: - Every resource belongs to exactly one Zone. Resources never migrate. - Ordinary `*Ref` fields never cross Zone boundaries. +- Every non-root Zone declares exactly one compiler-only scalar `parentZone`; + `local-root` declares none. The value is a plain declared Zone name, not a + ResourceRef, and compiles into the sealed allocator-bootstrap topology. - Every non-root child Zone declares at most one local `ZoneLink/` - uplink resource, enabled or disabled. Its provisioning parent allocator binds - that resource to the direct parent/child edge in sealed bootstrap state. + uplink resource, enabled or disabled. It supplies transport and local + route/session state; the allocator selected by `parentZone` binds that + resource to the direct parent/child edge in sealed bootstrap state. - A parent calls the child Zone's `d2b.resource.v3` service through an authenticated ZoneLink ComponentSession; it does not obtain a database handle, process credential, host path, or cross-Zone resource reference. @@ -573,9 +581,11 @@ homed in a different Zone. The d2b v3 model is intentionally constrained: enforces native RBAC at each hop. ``` -Zone/K0 (parent allocator/route state) - -> Zone/K1 (child): ZoneLink/k1-uplink - -> Zone/K2 (grandchild): ZoneLink/k2-uplink +Nix topology: k1.parentZone = local-root; k2.parentZone = k1 + +Zone/local-root (K0 parent allocator/route state) + -> Zone/k1 (child): child-local ZoneLink/k1-uplink + -> Zone/k2 (grandchild): child-local ZoneLink/k2-uplink ``` A call from a process in K0 that targets K2 traverses K0→K1→K2. Each @@ -742,11 +752,15 @@ performs no I/O; callers supply all metadata. - Max 32 hops per decision path (`MAX_ROUTE_PATH_HOPS`). - Max 4096 parent entries per engine (`MAX_PARENT_ENTRIES`). - Max 4096 route entries per engine (`MAX_ROUTE_ENTRIES`). -- Max 16 labels per Zone tree path (`MAX_REALM_LABELS`). +- Max 16 Zone names per compiler-authored ancestry path + (`MAX_REALM_LABELS`; local root counts as one). - Sibling/parent route advertisements are rejected with `sibling-or-parent-route-advert`. -- Cycles are detected and rejected with `loop`. -- Multiple parents for one child are rejected with `multi-parent`. +- Nix rejects missing/unknown/self parents, cycles, and over-depth topology + before sealing bootstrap state. +- Conflicting `parentZone` scalar definitions fail through normal Nix module + merging; runtime advertisements that claim a conflicting parent are rejected + with `multi-parent`. ## Nix configuration @@ -759,6 +773,15 @@ second bespoke Nix vocabulary, no field renaming, and no additional nesting beyond what the canonical schema has. `status` is omitted — it is read-only and filled by the Zone runtime. +`d2b.zones..parentZone` is the one deliberate Zone-level compiler input, +not part of that schema mirror. It is a plain Zone attrset key: required for +every non-root Zone, forbidden on `local-root`, and never emitted into a +ResourceSpec or `Zone.spec`. The compiler canonicalizes the resolved +child→parent rows, validates the complete graph, and seals them into the private +allocator-bootstrap topology. A parent change updates that private topology and +forces release/reallocation of the affected edge even though the Zone resource +bundle remains unchanged. + ### Metadata derivation The Nix emitter serializes core-derived metadata (`name`, `zone`, `apiVersion`) @@ -786,6 +809,15 @@ The base Nix option type for every resource is structural: # nixos-modules/options-zones.nix (structural base; type-specific options are # generated by xtask gen-zone-nix-options from ResourceTypeSchema JSON) { + d2b.zones..parentZone = mkOption { + # No default. Required for non-root Zones and forbidden on local-root. + type = types.strMatching "^[a-z][a-z0-9-]*$"; + description = '' + Compiler-only parent Zone name. This is not a ResourceRef and is emitted + only into sealed allocator bootstrap topology, never Zone.spec. + ''; + }; + d2b.zones..resources. = { type = mkOption { type = types.enum [ @@ -907,7 +939,11 @@ option types from `xtask gen-zone-nix-options` and are not repeated here. | --- | --- | | `` key matches `^[a-z][a-z0-9-]*$` | `zones: zone key must match ^[a-z][a-z0-9-]*$` | | `` key not `sys-*` or `launcher` | `zones: zone key uses reserved prefix or exact name` | +| `parentZone` omitted on `local-root` and defined once on every other Zone | `zones.: parentZone is required for non-root Zones and forbidden on local-root` | +| `parentZone` resolves to a declared Zone and does not equal `` | `zones..parentZone: parent must exist and differ from child` | +| Complete `parentZone` graph is acyclic and each ancestry path contains at most 16 Zone names | `zones: parentZone topology has a cycle or exceeds depth 16` | | `` key matches `^[a-z][a-z0-9-]*$` | `zones..resources: resource key must match ^[a-z][a-z0-9-]*$` | +| No operator-authored `type = "Zone"` under `resources` | `zones..resources.: Zone self-resource is runtime-created` | | For `type = "ZoneLink"`: `spec.childZoneName` equals ``; at most one uplink resource exists in a non-root Zone; local root has none | `zones.: ZoneLink must be the sole child-local uplink and childZoneName must equal its Zone` | | For `type = "ZoneLink"`: `spec.transportProviderRef` resolves to a declared `Provider` resource in the same `` | `zones..resources.: transportProviderRef does not resolve to a declared Provider resource` | | Total `d2b.zones` keys ≤ 64 | `zones: zone count exceeds host limit of 64` | @@ -919,15 +955,14 @@ option types from `xtask gen-zone-nix-options` and are not repeated here. **K0 with local Unix-transport child K1 (K0 = Host, K1 = Guest)**: ```nix -# K0 Zone self-resource (local root; runtime managed by this host) -# Zone.spec is exactly {}; no authored spec fields. -d2b.zones.k0.resources.k0 = { - type = "Zone"; - spec = {}; -}; +# K0 is the distinguished local-root Zone. parentZone is forbidden here. +# The runtime creates Zone/local-root with spec = {}; it is not bundle-authored. +d2b.zones.local-root = {}; # Unix transport Provider is local to child K1 because the ZoneLink and all of # its refs resolve in K1. +d2b.zones.k1.parentZone = "local-root"; + d2b.zones.k1.resources.transport-unix = { type = "Provider"; spec = { @@ -936,7 +971,8 @@ d2b.zones.k1.resources.transport-unix = { }; }; -# K1's local uplink to its provisioning K0 allocator. +# K1's local uplink supplies transport/route state for the allocator selected +# by k1.parentZone. d2b.zones.k1.resources.k1-uplink = { type = "ZoneLink"; spec = { @@ -958,11 +994,9 @@ d2b.zones.k1.resources.k1-uplink = { **K2 child-local uplink to K1 via Azure Relay transport**: ```nix -# K2 self. -d2b.zones.k2.resources.k2 = { - type = "Zone"; - spec = {}; # Zone.spec is exactly {} -}; +# Compiler-only topology makes K1 the one allocator owner for K2. The runtime +# creates Zone/k2 with spec = {}; parentZone is not emitted there. +d2b.zones.k2.parentZone = "k1"; # Azure Relay transport Provider is local to K2. d2b.zones.k2.resources.transport-azure-relay = { @@ -983,7 +1017,8 @@ d2b.zones.k2.resources.relay-sas-k2 = { }; }; -# K2's local uplink to its provisioning K1 allocator. +# K2's local uplink supplies transport/route state for the allocator selected +# by k2.parentZone. d2b.zones.k2.resources.k2-uplink = { type = "ZoneLink"; spec = { @@ -1018,7 +1053,13 @@ secret material arrive only as `Credential/` refs. The Nix build phase runs `xtask gen-zone-resources` which: -1. **Schema validation**: validates every emitted resource object against +1. **Topology validation and compilation**: resolves every non-root + `parentZone` against declared Zones; rejects a value on `local-root`, + missing, unknown, self, conflicting, cyclic, or over-16-name ancestry; and + canonicalizes the child→parent map into sealed private allocator-bootstrap + topology. `parentZone` is never copied into the resource bundle. + +2. **Schema validation**: validates every emitted resource object against the committed JSON Schema files: - `docs/reference/schemas/v3/Zone.schema.json` - `docs/reference/schemas/v3/ZoneLink.schema.json` @@ -1028,23 +1069,23 @@ The Nix build phase runs `xtask gen-zone-resources` which: in sync. A separate `xtask gen-zone-nix-options && git diff --exit-code` step ensures the generated Nix option modules match the current schema. -2. **Provider binding validation**: for each `ZoneLink`, the emitter fetches +3. **Provider binding validation**: for each `ZoneLink`, the emitter fetches the transport Provider's `transportSettingsSchema` (signed, committed under `docs/reference/schemas/v3/providers/.transport-binding.json`) and validates `spec.transportSettings` against it. Unknown keys are rejected. Fields annotated `"secret": true` are rejected; those annotated `"credentialRef": true` must be `"Credential/"` strings. -3. **Capability ceiling subset check**: the emitter verifies that every +4. **Capability ceiling subset check**: the emitter verifies that every child-local ZoneLink `capabilityCeiling` is a structural subset of the provisioning parent's private allocator grant. Capabilities wider than the sealed grant are a build error. -4. **Ref resolution**: all `*Ref` and `*ProviderRef` values are resolved +5. **Ref resolution**: all `*Ref` and `*ProviderRef` values are resolved against resources declared in the same `d2b.zones..resources` and fail the build if unresolvable. -5. **Conflict check**: duplicate `(type, zone, name)` tuples across the +6. **Conflict check**: duplicate `(type, zone, name)` tuples across the entire emitted bundle fail the build. ### Canonical ResourceSpec JSON shapes @@ -1054,25 +1095,26 @@ names, nesting, and defaults. The emitter does not rename or restructure any spec field. Resources are sorted by `(type, zone, name)` for determinism before the integrity digest is computed. -**Zone resource** — emitted from -`d2b.zones.k0.resources.k0 = { type = "Zone"; spec = {}; };`: +**Zone resource** — runtime-created from the declaration +`d2b.zones.local-root = {};`, not emitted in the resource bundle: ```json { "apiVersion": "resources.d2bus.org/v3", "type": "Zone", "metadata": { - "name": "k0", - "zone": "k0" + "name": "local-root", + "zone": "local-root" }, "spec": {} } ``` -After core activates the bundle, persisted store resources gain additional -core-populated fields — e.g. `metadata.managedBy`, `metadata.configurationGeneration`, -`metadata.uid`, `metadata.generation`. These are absent from the Nix-rendered -bundle JSON and must not be authored. +The runtime gives this self-resource core-populated fields such as +`metadata.managedBy`, `metadata.uid`, and `metadata.generation`. +`metadata.configurationGeneration` is absent because the resource is +controller-created. Compiler-only `parentZone` is absent from both this object +and the Nix-rendered resource bundle. **ZoneLink resource** — emitted from `d2b.zones.k1.resources.k1-uplink = { type = "ZoneLink"; spec = { ... }; };`: @@ -1337,6 +1379,11 @@ Required before ADR046-routing-011 through ADR046-routing-013 are complete: | Test ID | Kind | What it proves | | --- | --- | --- | | `nix-unit: zone-name-regex` | nix-unit eval | Zone name regex and reserved-name assertions fire | +| `nix-unit: zone-parent-required-root-forbidden` | nix-unit eval | Every non-root Zone requires `parentZone`; `local-root` rejects it | +| `nix-unit: zone-parent-resolves` | nix-unit eval | Unknown and self-valued `parentZone` settings reject | +| `nix-unit: zone-parent-one-parent` | nix-unit eval | Conflicting scalar definitions reject through normal Nix module merging | +| `nix-unit: zone-parent-cycle` | nix-unit eval | Two-node and longer `parentZone` cycles reject before bootstrap publication | +| `nix-unit: zone-parent-depth` | nix-unit eval | Sixteen-name ancestry succeeds and seventeen-name ancestry rejects | | `nix-unit: zone-link-fingerprint` | nix-unit eval | 63-char and non-hex fingerprints rejected at eval | | `nix-unit: zone-link-child-name` | nix-unit eval | `childZoneName` unequal to enclosing child Zone rejected | | `nix-unit: zone-link-one-uplink` | nix-unit eval | Second uplink (even disabled) and any local-root uplink rejected | @@ -1347,6 +1394,7 @@ Required before ADR046-routing-011 through ADR046-routing-013 are complete: | `build: transport-binding-unknown-field` | flake check | Unknown Provider binding key fails build | | `build: capability-ceiling-superset` | flake check | Ceiling wider than parent grant fails build | | `build: missing-transport-provider` | flake check | Unresolvable `transportProviderRef` fails build | +| `build: parent-topology-sealed` | flake check | Valid sorted `parentZone` rows compile only into the sealed allocator bootstrap input and never into `Zone.spec` or resource bundles | | `host-integration: cleanup-removed-zonelink` | NixOS test | Switch removes ZoneLink; assert `deletionRequestedAt` set; store transaction commits `Deleted` revision and removes row/index; audit record appended from committed revision with exactly-once recovery; generation reaches Ready; dynamic route entries deleted by ZoneLink controller teardown, not by generation diff | | `host-integration: rollback-restores-zonelink` | NixOS test | After cleanup switch, rollback re-activates ZoneLink; generation diff reverses | | `host-integration: dynamic-child-not-deleted` | NixOS test | Parent allocator/route entries are NOT diff-owned resources; removing the child-local ZoneLink invokes controller teardown and allocation release | @@ -2234,7 +2282,7 @@ Evidence classes: **A** = implemented-and-reachable from production binary, | `NodeId` | `ids.rs` | **A** | d2b-realm-core, d2b-gateway | → Host resource name or Zone-local implicit address | | `ProviderId`, `ExecutionId`, `StreamId`, `PrincipalId`, etc. | `ids.rs` | **A** | Throughout | → preserved with v3 names; secret-marker reject logic retained | | `EntrypointMode` (`HostResident`/`GatewayBacked`) | `realm.rs` | **A** | `d2b/src/target_routing.rs` | → subsumed into ZoneLink `spec.transportProviderRef` | -| `RealmControllerPlacement` (6 variants) | `realm.rs` | **A** | `d2b-realm-core`, bundle artifacts | → private Zone runtime bootstrap placement + child-local uplink identity/transportProviderRef bound by the parent allocator (not a public Zone.spec field) | +| `RealmControllerPlacement` (6 variants) | `realm.rs` | **A** | `d2b-realm-core`, bundle artifacts | → private Zone runtime bootstrap placement; compiler-only `parentZone` selects the allocator owner while child-local uplink identity/transportProviderRef supplies transport state (none is a public Zone.spec parent field) | | `TargetName` (DNS-form target string) | `target.rs` | **A** | d2bd, d2b-gateway | → v3 resource ref path | | `RealmTarget` (workload + realm path) | `target.rs` | **A** | d2bd/realm_access_resolver.rs | → Zone-local resource lookup | @@ -2296,17 +2344,17 @@ Evidence classes: **A** = implemented-and-reachable from production binary, | Symbol | File | Class | Production callers | ADR 0046 mapping | | --- | --- | --- | --- | --- | -| `RealmControllersJson` (schema v2) | `d2b-core/src/realm_controller_config.rs` | **C** | Loaded at d2bd startup (lib.rs:1408) and priv-broker (runtime.rs:687); logs "runtime routing remains inert" | → Nix-authored Zone/ZoneLink resources; artifact retires | +| `RealmControllersJson` (schema v2) | `d2b-core/src/realm_controller_config.rs` | **C** | Loaded at d2bd startup (lib.rs:1408) and priv-broker (runtime.rs:687); logs "runtime routing remains inert" | → compiler-only `parentZone` bootstrap topology + runtime Zone self-resource + child-local ZoneLink resources; artifact retires | | `WorkloadTargetIndex` | `d2bd/src/workload_target_index.rs` | **A** | d2bd `lib.rs:16745` in `PublicRequestArtifacts`; maps canonical targets → VM names for exec dispatch | → Guest/Host resource lookup; retires with legacy VM dispatch | | `realm-workloads-launcher-v2.json` | `nixos-modules/realm-workloads-launcher-v2-json.nix` | **C** | Active bundle artifact (installed root:d2bd 0640); consumed by launcher clients | → v3 workload catalog from Zone resources | | `realm-workloads-launcher.json` (v1) | `nixos-modules/realm-workloads-launcher-json.nix` | **C** | **NOT installed** by `bundle.nix`; declared artifact but dead | Retire | -| `d2b.realms.*` Nix options | `nixos-modules/options-realms.nix`, `options-realms-workloads.nix`, `options-realms-network.nix` | **C** | Eval-time; drives JSON artifact emission | → `d2b.zones.*` with richer Zone/ZoneLink spec | +| `d2b.realms.*` Nix options | `nixos-modules/options-realms.nix`, `options-realms-workloads.nix`, `options-realms-network.nix` | **C** | Eval-time; drives JSON artifact emission | → `d2b.zones.*` with compiler-only `parentZone` plus schema-mirrored resources | ### Architecture change summary The following transitions are NOT simple textual renames: -1. **`RealmControllerPlacement` enum → private Zone runtime bootstrap placement + child-local uplink identity**: 6 variants collapse into per-Zone bootstrap configuration plus the child-local ZoneLink's `childZoneName`/`transportProviderRef`, bound to its parent by sealed allocator state. Placement is not a public `Zone.spec` field; `Zone.spec` is `{}`. +1. **`RealmControllerPlacement` enum → private Zone runtime bootstrap placement + explicit parent topology + child-local uplink identity**: 6 variants collapse into per-Zone bootstrap configuration; compiler-only `parentZone` selects the allocator owner, and the child-local ZoneLink's `childZoneName`/`transportProviderRef` supplies transport state. The compiler seals the parent edge into allocator state. Placement and `parentZone` are not public `Zone.spec` fields; `Zone.spec` is `{}`. 2. **`WorkloadId` → Guest/Host split**: VM/sandbox workloads become `Guest`; local/bare-metal become `Host`. Classification is semantic, not mechanical. 3. **`CapabilitySet`-only authz → RBAC + capability ceiling**: current engine checks capability ceiling only. Per-hop `relay` verb RBAC is new. 4. **`RealmPath` DNS target form → Zone resource path**: grammar preserved; wire address format changes. @@ -2317,9 +2365,9 @@ The following transitions are NOT simple textual renames: | Item | Treatment | | --- | --- | | Behavior retained | Pure in-memory NCA tree-walk; loop/multi-parent detection; advertisement replay-window; capability ceiling propagation; idempotency dedup full 6-tuple `(realm, principal, node, operation_kind, idempotency_key)`; fail-closed on unknown realm/route; bounded audit label cardinality; `TreeRoutePath`/`TreeRouteHop` already exist (rename to ZoneRoutePath/ZoneRouteHop); `DirectShortcut*` machinery already exists; `RouteAuditEventKind` event set already exists | -| Required delta | ZoneLink ResourceType spec/status/intent queue (ADR-only); RBAC `relay` verb check per intermediate hop; watch cursor resync over ZoneLink; named-stream credit forwarding; hop counter byte in wire frames; no-FD/credential structural rejection at serialization boundary; private Zone runtime bootstrap placement (replaces placement enum, not a public spec field); per-hop subject narrowing | +| Required delta | ZoneLink ResourceType spec/status/intent queue (ADR-only); compiler-only validated `parentZone` map sealed into allocator bootstrap topology; RBAC `relay` verb check per intermediate hop; watch cursor resync over ZoneLink; named-stream credit forwarding; hop counter byte in wire frames; no-FD/credential structural rejection at serialization boundary; private Zone runtime bootstrap placement (replaces placement enum, not a public spec field); per-hop subject narrowing | | Reuse path | Copy and adapt `RouteTreeEngine` (rename RealmPath→ZonePath, RouteId→ZoneRouteId); copy `RouteAdvertisement`/`RouteNamespaceAllocation`/`TreeRoutePath`/`TreeRouteHop`/`RouteFailClosedReason`/`DirectShortcut*`/`RouteAuditEventKind`; copy `OperationRouter` idempotency dedup; extract `RealmEntrypointTable` suffix-match into ZoneEntrypointResolver; adapt KK handshake from `SecurePeerSession`/`PeerSession` | -| Replacement/deletion | `RealmEntrypointTable`/`RouteTreeEngine` on RealmPath types retire after ZoneRouteEngine live; `RemoteNodeRegistry` retires when ZoneLink controller live; `WorkloadTargetIndex` retires when Guest/Host resource lookup live; CLI `Route::GatewayBacked` retires when ZoneLink handles all cross-Zone routing; `realm-controllers.json`/`realm-identity.json` retire when Zone resources own placement/identity | +| Replacement/deletion | `RealmEntrypointTable`/`RouteTreeEngine` on RealmPath types retire after ZoneRouteEngine live; `RemoteNodeRegistry` retires when ZoneLink controller live; `WorkloadTargetIndex` retires when Guest/Host resource lookup live; CLI `Route::GatewayBacked` retires when ZoneLink handles all cross-Zone routing; `realm-controllers.json`/`realm-identity.json` retire when sealed `parentZone` topology, runtime Zone identity, and ZoneLink transport state replace them | | Feasibility proof | `route_engine.rs` inline test suite (45 functions at line 1202) proves NCA, advertisement, loop, capability, replay, DirectShortcut; `target_resolver.rs` tests prove suffix match; `lib.rs` tests prove idempotency dedup namespace | | Future owner | Work items below | @@ -2367,7 +2415,7 @@ The following transitions are NOT simple textual renames: | Reuse source | Same v3 baseline commit `b5ddbed6` | | Reuse action | extract and adapt | | Destination | `packages/d2b-contracts/src/v3/zone_link.rs` (ZoneLink spec/status types, intent types, namespace allocation); `packages/d2b-zone-routing/src/resolver.rs` (ZoneEntrypointResolver) | -| Detailed design | Child-local ZoneLink spec/status fields; self-name and one-uplink invariants; ZoneLinkIntent record stored in the child; ZoneLinkNamespaceAllocation issued by the parent allocator; ZoneEntrypointResolver with longest-suffix match over ZonePath (adapted from `RealmEntrypointTable::resolve`); no reciprocal parent-store resource; fail-closed on unknown Zone | +| Detailed design | Child-local ZoneLink spec/status fields; self-name and one-uplink invariants; ZoneLinkIntent record stored in the child; ZoneLinkNamespaceAllocation issued by the exact parent allocator selected in sealed Nix-compiled `parentZone` topology; ZoneEntrypointResolver with longest-suffix match over ZonePath (adapted from `RealmEntrypointTable::resolve`); no reciprocal parent-store resource; fail-closed on unknown Zone | | Integration | Core-controller ZoneLink handler manages ZoneLink resources; ZoneEntrypointResolver in d2b-bus for per-call dispatch decision | | Data migration | None; ZoneLink resources created from Nix configuration at v3 reset | | Validation | Longest-suffix match vectors; ZoneLink spec validation tests; resolver fail-closed test | @@ -2384,8 +2432,8 @@ The following transitions are NOT simple textual renames: | Reuse source | Same v3 baseline commit `b5ddbed6` | | Reuse action | extract and adapt | | Destination | `packages/d2b-core-controller/src/zone_links.rs` | -| Detailed design | Child-local ZoneLink handler in core-controller: manages local ResourceSpec→allocator-bound session→advertisement lifecycle; session state machine (Pending/Established/Disconnected/Reconnecting/Revoked); reconnect backoff per `reconnectPolicy`; advertisement issuance/renewal/withdrawal using enrolled KK ComponentSession; child-store route cursor and outbound intent queue; capability ceiling change handling; status writer; no copied parent/child resource content; parent allocator alone owns privileged listeners, placement, and route namespace and creates no reciprocal resource | -| Integration | Child core-controller process → local transport Provider → sealed parent allocator binding → d2b-bus ComponentSession; child ZoneLink handler exchanges advertisements while the parent ZoneRouteEngine admits/withdraws them | +| Detailed design | Child-local ZoneLink handler in core-controller: manages local ResourceSpec→allocator-bound session→advertisement lifecycle; session state machine (Pending/Established/Disconnected/Reconnecting/Revoked); reconnect backoff per `reconnectPolicy`; advertisement issuance/renewal/withdrawal using enrolled KK ComponentSession; child-store route cursor and outbound intent queue; capability ceiling change handling; status writer; no copied parent/child resource content; Nix-compiled `parentZone` selects the parent allocator, which alone owns privileged listeners, placement, and route namespace and creates no reciprocal resource | +| Integration | Child core-controller process → local transport Provider → sealed binding for the allocator selected by `parentZone` → d2b-bus ComponentSession; child ZoneLink handler exchanges advertisements while that parent ZoneRouteEngine admits/withdraws them | | Data migration | New ZoneLink resources from Nix configuration; no prior enrollment compatibility | | Validation | Session lifecycle tests; reconnect/revocation/ceiling-change; intent queue drain; cursor resync; advertisement renewal timing; fake-child tests | | Removal proof | `RemoteNodeRegistry` retired after all enrolled peer routing moves to ZoneLink handler | @@ -2545,10 +2593,10 @@ The following transitions are NOT simple textual renames: | Reuse source | Same v3 baseline `b5ddbed6`; `assertions.nix` pattern reused for Zone assertions; `realm-controller-config-json.nix` is the structural template | | Reuse action | new module following same pattern | | Destination | `nixos-modules/options-zones.nix` (new structural base), `nixos-modules/generated/options-zones-.nix` (generated per ResourceType by `xtask gen-zone-nix-options`), `nixos-modules/assertions.nix` (new Zone assertions) | -| Detailed design | Declare the structural base option `d2b.zones..resources. = { type = ...; spec = {}; }` as specified in the "Option schema" section above. Wire `options-zones.nix` and all `generated/options-zones-*.nix` files into `nixos-modules/default.nix`. Add a new `xtask gen-zone-nix-options` command that reads `docs/reference/schemas/v3/.schema.json` for each ResourceType and emits a generated submodule overlaying typed spec options (types, bounds, enum constraints, defaults, docs) onto `d2b.zones..resources..spec`. These generated modules are committed and kept in sync by `xtask gen-zone-nix-options && git diff --exit-code` wired into `make test-drift`. Because the generated options carry field-level type constraints, field-level eval errors (wrong enum, out-of-range int, non-hex fingerprint) are caught without explicit assertions. Explicit assertions in `nixos-modules/assertions.nix` cover cross-resource invariants only: zone/resource key name regex, reserved names, child-local `childZoneName == zone`, at most one uplink resource per non-root Zone and none in local root, allocator bootstrap multi-parent/cycle rejection, ref resolution, count limits, and transportSettings secret-key exclusion (listed in the eval-time assertions table). Each new assertion must have a matching case in `tests/unit/nix/cases/zone-assertions.nix` (nix-unit auto-discovered). `d2b.realms` option namespace is NOT removed in this work item. | -| Integration | `nixos-modules/zone-resources-json.nix` (ADR046-routing-012) iterates `d2b.zones..resources.*` to emit the bundle | +| Detailed design | Declare compiler-only scalar `d2b.zones..parentZone` plus structural `d2b.zones..resources. = { type = ...; spec = {}; }` as specified in the "Option schema" section above. `parentZone` has no default, is required for every non-root Zone, forbidden on `local-root`, resolves to one declared Zone, and never enters a ResourceSpec. Wire `options-zones.nix` and all `generated/options-zones-*.nix` files into `nixos-modules/default.nix`. Add a new `xtask gen-zone-nix-options` command that reads `docs/reference/schemas/v3/.schema.json` for each ResourceType and emits a generated submodule overlaying typed spec options (types, bounds, enum constraints, defaults, docs) onto `d2b.zones..resources..spec`. These generated modules are committed and kept in sync by `xtask gen-zone-nix-options && git diff --exit-code` wired into `make test-drift`. Because the generated options carry field-level type constraints, field-level eval errors (wrong enum, out-of-range int, non-hex fingerprint) are caught without explicit assertions. Explicit assertions in `nixos-modules/assertions.nix` cover cross-resource invariants only: zone/resource key name regex, reserved names, `parentZone` required/forbidden/existence/self/cycle/16-name-depth constraints, child-local `childZoneName == zone`, at most one uplink resource per non-root Zone and none in local root, ref resolution, count limits, and transportSettings secret-key exclusion (listed in the eval-time assertions table). Conflicting parent scalar definitions fail through standard Nix module merging. Each new assertion must have a matching case in `tests/unit/nix/cases/zone-assertions.nix` (nix-unit auto-discovered). `d2b.realms` option namespace is NOT removed in this work item. | +| Integration | ADR046-routing-012 consumes the validated `parentZone` map for private allocator bootstrap sealing and iterates `d2b.zones..resources.*` for resource-bundle emission | | Data migration | None; Zone options are new; Realm options retained until migration PR | -| Validation | `nix-unit: zone-name-regex`, `nix-unit: zone-link-fingerprint`, `nix-unit: zone-link-child-name`, `nix-unit: zone-link-one-uplink`, `nix-unit: capability-ceiling-unknown-verb`, `nix-unit: transport-settings-secret-key`; add `drift: zone-nix-options` (`xtask gen-zone-nix-options && git diff --exit-code`); run `make nix-unit-pin` after adding eval cases | +| Validation | `nix-unit: zone-name-regex`, all five `nix-unit: zone-parent-*` vectors, `nix-unit: zone-link-fingerprint`, `nix-unit: zone-link-child-name`, `nix-unit: zone-link-one-uplink`, `nix-unit: capability-ceiling-unknown-verb`, `nix-unit: transport-settings-secret-key`; add `drift: zone-nix-options` (`xtask gen-zone-nix-options && git diff --exit-code`); run `make nix-unit-pin` after adding eval cases | | Removal proof | `nixos-modules/options-realms-workloads.nix` `d2b.realms` namespace retires after all hosts migrate to `d2b.zones` | ### ADR046-routing-012 @@ -2560,11 +2608,11 @@ The following transitions are NOT simple textual renames: | Current source | `nixos-modules/realm-controller-config-json.nix` (v3 baseline `b5ddbed6`): `builtins.toJSON` emitter for `realm-controllers.json` (bundle.nix:59); `nixos-modules/bundle-artifacts.nix`: install table (root:d2bd 0640); `nixos-modules/bundle.nix`: artifact wiring (evidence: **C**); `packages/xtask/src/main.rs` `gen-schemas` subcommand (evidence: **A** — wired into `make test-drift`) | | Reuse source | `realm-controller-config-json.nix` structural template; `xtask gen-schemas` extension point (main `a1cc0b2d` unchanged in this area) | | Reuse action | extend and adapt | -| Destination | `nixos-modules/zone-resources-json.nix` (new), `nixos-modules/bundle-artifacts.nix` (new row for per-Zone `resource-bundle.json`), `packages/xtask/src/main.rs` (`gen-zone-schemas` subcommand emitting `docs/reference/schemas/v3/.schema.json` for Zone and ZoneLink; `gen-zone-nix-options` subcommand emitting `nixos-modules/generated/options-zones-.nix`) | -| Detailed design | `zone-resources-json.nix` iterates `d2b.zones..resources.*` to produce the canonical sorted resource list: for each entry, render `{ apiVersion, type, metadata: { name, zone, ownerRef: , labels: , annotations: }, spec: }`. The bundle JSON omits `managedBy` and `configurationGeneration`; the configuration service/core sets those fields when activating the validated bundle. Sort all resources by `(type, zone, name)`. Compute `generationId` as SHA-256 (lower hex) of the UTF-8 bytes of the sorted `resources` array JSON. Compute `integrity` as SHA-256 (base64url, no padding) of the full bundle JSON with integrity field zeroed. Install at `/etc/d2b/zones//resource-bundle.json` root:d2bd 0640. Canonical form: all object keys sorted lexicographically; order-significant arrays preserved; schema-declared set-like arrays sorted lexicographically; all optional fields emitted with defaults; no field renaming or restructuring. Build-time validation runs in a Nix derivation: (1) validate each resource against the committed JSON Schema; (2) validate `transportSettings` for each child-local ZoneLink against its same-Zone Provider's `transportSettingsSchema` — `transportProviderRef` is always explicit, never inferred or defaulted; (3) verify capability ceilings are subsets of the sealed parent allocator grants; (4) verify `childZoneName == metadata.zone`, at most one uplink resource per non-root Zone, no local-root uplink, and no multi-parent/cycle in private bootstrap edges; (5) check for duplicate `(type, zone, name)` tuples. Providers MUST commit their `transportSettingsSchema` before any ZoneLink can reference them. Drift gates: `xtask gen-zone-schemas && git diff --exit-code` and `xtask gen-zone-nix-options && git diff --exit-code` both wired into `make test-drift`. Add `checks.${system}.zone-schema-drift` to `flake.nix`. | -| Integration | `nixos-modules/bundle-artifacts.nix` installs per-Zone `resource-bundle.json`; ADR046-routing-013 Zone runtime reads it on startup | +| Destination | `nixos-modules/zone-resources-json.nix` (new), private local-root allocator bootstrap compiler/sealer input (not a ResourceSpec or public bundle), `nixos-modules/bundle-artifacts.nix` (new row for per-Zone `resource-bundle.json`), `packages/xtask/src/main.rs` (`gen-zone-schemas` subcommand emitting `docs/reference/schemas/v3/.schema.json` for Zone and ZoneLink; `gen-zone-nix-options` subcommand emitting `nixos-modules/generated/options-zones-.nix`) | +| Detailed design | `zone-resources-json.nix` iterates `d2b.zones..resources.*` to produce the canonical sorted resource list: for each entry, render `{ apiVersion, type, metadata: { name, zone, ownerRef: , labels: , annotations: }, spec: }`. Separately canonicalize sorted `{ childZone, parentZone }` rows from the compiler-only topology and seal them into the private allocator bootstrap input; `parentZone` never enters a resource bundle or `Zone.spec`, and a topology digest change releases/reallocates affected edges independently of resource `generationId`. The bundle JSON omits `managedBy` and `configurationGeneration`; the configuration service/core sets those fields when activating the validated bundle. Sort all resources by `(type, zone, name)`. Compute `generationId` as SHA-256 (lower hex) of the UTF-8 bytes of the sorted `resources` array JSON. Compute `integrity` as SHA-256 (base64url, no padding) of the full bundle JSON with integrity field zeroed. Install at `/etc/d2b/zones//resource-bundle.json` root:d2bd 0640. Canonical form: all object keys sorted lexicographically; order-significant arrays preserved; schema-declared set-like arrays sorted lexicographically; all optional fields emitted with defaults; no field renaming or restructuring. Build-time validation runs in a Nix derivation: (1) validate the complete parent map (non-root required, local-root forbidden, declared target, one scalar parent, not self, acyclic, max 16 names); (2) validate each resource against the committed JSON Schema; (3) validate `transportSettings` for each child-local ZoneLink against its same-Zone Provider's `transportSettingsSchema` — `transportProviderRef` is always explicit, never inferred or defaulted; (4) verify capability ceilings are subsets of the sealed selected-parent allocator grants; (5) verify `childZoneName == metadata.zone`, at most one uplink resource per non-root Zone, and no local-root uplink; (6) check for duplicate `(type, zone, name)` tuples. Providers MUST commit their `transportSettingsSchema` before any ZoneLink can reference them. Drift gates: `xtask gen-zone-schemas && git diff --exit-code` and `xtask gen-zone-nix-options && git diff --exit-code` both wired into `make test-drift`. Add `checks.${system}.zone-schema-drift` to `flake.nix`. | +| Integration | The local-root allocator consumes sealed parent topology independently of resource bundles; `nixos-modules/bundle-artifacts.nix` installs each per-Zone `resource-bundle.json`; ADR046-routing-013 Zone runtime reads it on startup | | Data migration | None; new artifact file | -| Validation | `drift: zone-resource-schema`, `drift: zone-nix-options`, `build: zone-bundle-deterministic`, `build: transport-settings-unknown-field`, `build: capability-ceiling-superset`, `build: missing-transport-provider`; run `make flake-matrix-pin` after adding flake checks | +| Validation | `drift: zone-resource-schema`, `drift: zone-nix-options`, `build: zone-bundle-deterministic`, `build: parent-topology-sealed`, `build: transport-settings-unknown-field`, `build: capability-ceiling-superset`, `build: missing-transport-provider`; run `make flake-matrix-pin` after adding flake checks | | Removal proof | `realm-controllers.json` artifact retires after Zone runtime is live and all hosts migrated | ### ADR046-routing-013 From 2eab0cd816ac3aeb2fe4192a5f6eded8017e3e77 Mon Sep 17 00:00:00 2001 From: John Vicondoa Date: Fri, 24 Jul 2026 03:19:04 -0700 Subject: [PATCH 083/115] docs: close ADR 0046 work-item contract gaps Register stable per-spec work-item prefixes, normalize declared IDs and reuse dispositions, and require a complete Markdown-to-manifest bijection. Regenerate the spec set, work-item manifest, and acyclic single-wave implementation graph from the corrected documentation. --- docs/specs/ADR-046-cli-and-operations.md | 16 +- ...DR-046-components-processes-and-sandbox.md | 8 +- .../specs/ADR-046-componentsession-and-bus.md | 14 +- docs/specs/ADR-046-core-controllers.md | 8 +- docs/specs/ADR-046-feasibility-and-spikes.md | 36 +- docs/specs/ADR-046-implementation-graph.json | 28551 ++++++++-------- docs/specs/ADR-046-implementation-graph.md | 79 +- docs/specs/ADR-046-nix-configuration.md | 130 +- .../ADR-046-primitive-resource-composition.md | 12 +- .../ADR-046-provider-model-and-packaging.md | 10 +- docs/specs/ADR-046-provider-state.md | 12 +- docs/specs/ADR-046-reset-and-cutover.md | 20 +- .../ADR-046-resource-api-and-authorization.md | 4 +- docs/specs/ADR-046-resource-object-model.md | 4 +- docs/specs/ADR-046-resource-reconciliation.md | 4 +- docs/specs/ADR-046-resource-store-redb.md | 4 +- docs/specs/ADR-046-resources-credential.md | 18 +- docs/specs/ADR-046-resources-device.md | 26 +- ...R-046-resources-host-guest-process-user.md | 72 +- docs/specs/ADR-046-resources-network.md | 18 +- docs/specs/ADR-046-resources-volume.md | 16 +- docs/specs/ADR-046-resources-zone-control.md | 128 +- .../ADR-046-security-and-threat-model.md | 68 +- docs/specs/ADR-046-spec-set.json | 868 +- docs/specs/ADR-046-streamline.md | 94 +- .../ADR-046-telemetry-audit-and-support.md | 204 +- .../ADR-046-terminology-and-identities.md | 4 +- docs/specs/ADR-046-validation-and-delivery.md | 81 +- docs/specs/ADR-046-work-items.json | 16225 ++++----- docs/specs/ADR-046-zone-routing.md | 60 +- docs/specs/README.md | 102 +- .../ADR-046-provider-activation-nixos.md | 28 +- .../ADR-046-provider-audio-pipewire.md | 44 +- .../ADR-046-provider-clipboard-wayland.md | 44 +- .../ADR-046-provider-credential-entra.md | 4 +- ...46-provider-credential-managed-identity.md | 32 +- ...-046-provider-credential-secret-service.md | 24 +- .../providers/ADR-046-provider-device-gpu.md | 86 +- .../ADR-046-provider-device-security-key.md | 280 +- .../providers/ADR-046-provider-device-tpm.md | 52 +- .../ADR-046-provider-device-usbip.md | 36 +- .../ADR-046-provider-display-wayland.md | 10 +- .../ADR-046-provider-network-local.md | 98 +- .../ADR-046-provider-notification-desktop.md | 14 +- .../ADR-046-provider-observability-otel.md | 24 +- ...6-provider-runtime-azure-container-apps.md | 26 +- ...-provider-runtime-azure-virtual-machine.md | 34 +- ...R-046-provider-runtime-cloud-hypervisor.md | 28 +- .../ADR-046-provider-runtime-qemu-media.md | 148 +- .../ADR-046-provider-shell-terminal.md | 48 +- .../providers/ADR-046-provider-system-core.md | 12 +- .../ADR-046-provider-system-minijail.md | 20 +- .../ADR-046-provider-system-systemd.md | 8 +- .../ADR-046-provider-transport-azure-relay.md | 14 +- .../ADR-046-provider-transport-unix.md | 40 +- .../ADR-046-provider-transport-vsock.md | 36 +- .../ADR-046-provider-volume-local.md | 22 +- .../ADR-046-provider-volume-virtiofs.md | 24 +- 58 files changed, 24851 insertions(+), 23281 deletions(-) diff --git a/docs/specs/ADR-046-cli-and-operations.md b/docs/specs/ADR-046-cli-and-operations.md index 1e31c1feb..494f438c6 100644 --- a/docs/specs/ADR-046-cli-and-operations.md +++ b/docs/specs/ADR-046-cli-and-operations.md @@ -2355,9 +2355,9 @@ baseline (only a deprecation notice remains at `lib.rs:2424`). | Dependency/owner | ADR046-cli-001, ADR046-api-001; CLI crate owner | | Current source | `packages/d2b/src/exec_client.rs` (entire FSM); `packages/d2b/src/terminal_client.rs`; `packages/d2b/src/lib.rs`: `cmd_vm_exec`, `cmd_vm_exec_management`, `VmExecArgs` | | Reuse source | main `a1cc0b2d` — copy/adapt: (1) `packages/d2b-client/src/daemon_service.rs` `DaemonClient::open_terminal(method, resource_id, operation_id, selection, options, cancellation)` returning `DaemonTerminal` (line 248) — copy-then-adapt: replaces the existing seqpacket Exec call; `DaemonMethod::Exec` (line 40) maps to `d2b exec run`; (2) `packages/d2b-contracts/src/generated_v2_services/terminal.rs` `TerminalOpenRequest`, `TerminalOpenResponse`, `TerminalStreamFrame`, `TerminalSelection`, `TerminalKind` — copy unchanged as the named-stream terminal wire protocol; (3) `packages/d2b-client/src/session.rs` `NamedStream` (`send`, `receive`, `cancel`, `close`, `is_terminal`) — copy unchanged; provides async stdio routing and cancel on disconnect; (4) `packages/d2b-client/src/daemon_service.rs` `GuestClient::inspect_exec()`, `cancel_exec()`, `open_exec_retained_log()` — adapt: rename from `WorkloadName`/`GuestClient` to `EphemeralProcess/` resource API; (5) `packages/d2b-session/src/streams.rs` `NamedStreamMux` limits (`MAX_ACTIVE_NAMED_STREAMS=128`, `named_stream_queue_bytes=262144`) — copy unchanged; bounds the exec stream pipeline; (6) `packages/d2b-session/src/cancellation.rs` `Cancellation`, `RequestRegistry` — copy unchanged; provides generation-bound per-request cancel; tests to adapt/import: `client.rs:terminal_uses_server_stream_and_validates_bidirectional_lifecycle`, `terminal_rejects_response_generation_and_non_server_stream_ids`, `invalid_terminal_selection_is_rejected_before_open_rpc`, `guest_exec_management_preserves_typed_state_and_cancel_correlation`, `guest_retained_log_open_binds_range_resource_and_selection`, `named_stream_fragments_over_queue_credit_and_has_terminal_actions`, `named_stream_grants_only_consumed_data_and_releases_blocked_sender`; excluded ADR 0045 assumptions: `GuestClient` internal `TargetInput::Workload`-scoped vsock routing (guest-control proxy uses old `WorkloadName`/`RealmPath` — these are excluded; v3 routes through resource API only) | -| Reuse action | copy-then-adapt | +| Reuse action | adapt | | Destination | `packages/d2b/src/exec.rs` (`d2b exec run/attach/wait/status/list/logs/kill`) | -| Detailed design | Map EphemeralProcess resource lifecycle; `exec run` creates resource and returns ref; `exec attach` opens named stream via adapted `DaemonClient::open_terminal(DaemonMethod::Exec, ...)` → `DaemonTerminal`; retain full `exec_client.rs` FSM and TTY machinery from baseline; retain `--json` envelope fields `source`/`reason`/`guestExitCode`/`signal`/`transportExitCode`; retain reserved exit codes 42/69/70/75/76/77. v2 commands (`d2b vm exec *`) are deleted at 3.0; no dispatch wiring. Excluded ADR 0045: `GuestClient` vsock/guest-control proxy path; `TargetInput::Workload`; old `WorkloadName`-keyed exec management. | +| Detailed design | Map EphemeralProcess resource lifecycle; `exec run` creates resource and returns ref; `exec attach` opens named stream via adapted `DaemonClient::open_terminal(DaemonMethod::Exec, ...)` → `DaemonTerminal`; retain full `exec_client.rs` FSM and TTY machinery from baseline; retain `--json` envelope fields `source`/`reason`/`guestExitCode`/`signal`/`transportExitCode`; retain reserved exit codes 42/69/70/75/76/77. v2 commands (`d2b vm exec *`) are deleted at 3.0; no dispatch wiring. Excluded ADR 0045: `GuestClient` vsock/guest-control proxy path; `TargetInput::Workload`; old `WorkloadName`-keyed exec management. Primary reuse disposition: `adapt`. Preserved source-plan detail: copy-then-adapt. | | Integration | ZoneContext → EphemeralProcess Create → named stream attach via `DaemonClient::open_terminal` | | Data migration | None — full d2b 3.0 reset; no prior state to migrate | | Validation | Full `exec_client.rs` test suite migrated; adapted tests from main `client.rs:terminal_*` and `guest_exec_*`; TTY/raw-mode/RAII/signal tests; `--json` envelope/disambiguation tests; capacity/transport/auth/protocol exit-code tests; confirm v2 `cmd_vm_exec` path is absent | @@ -2371,9 +2371,9 @@ baseline (only a deprecation notice remains at `lib.rs:2424`). | Dependency/owner | ADR046-cli-001, `shell-terminal` Provider dossier; CLI crate owner | | Current source | `packages/d2b/src/lib.rs`: `cmd_shell` (`ShellArgs.vm: String` = `WorkloadId` or `RealmTarget`; routes through `route_vm_target()` → `VmTargetRoute::Local\|Gateway`; gateway `Attach` fails closed via `shell_gateway_attach_failure()` with error class `gateway-shell-attach-unavailable` at lib.rs:1697,1780), `cmd_shell_attach`, `run_shell_fsm`, `ShellOwnerTransport`; wire: `ShellOp`, `ShellOpResponse`, `ShellAttachArgs { vm: String }`, `ShellListEntry`, `ShellSessionState` from `packages/d2b-contracts/src/public_wire.rs:1319,1394,1452,1409`; `exec_client.rs` signal/TTY machinery | | Reuse source | main `a1cc0b2d` — copy/adapt: (1) `packages/d2b-client/src/daemon_service.rs` `DaemonClient::open_terminal(DaemonMethod::Shell, ...)` returning `DaemonTerminal` — copy-then-adapt; same `TerminalOpenRequest`/`TerminalOpenResponse`/`DaemonTerminal` flow as cli-003, applied to shell open/attach; (2) `packages/d2b-contracts/src/generated_v2_services/shell.rs` and `shell_ttrpc.rs` `ShellService` methods: `ShellCreate`, `ShellAttach`, `ShellDetach`, `ShellList`, `ShellInspect`, `ShellKill`, `ShellCancel` (service definition); `ShellCreateRequest`/`ShellAttachRequest`/`ShellListResponse`/`ShellInspectResponse` — copy-then-adapt: these are the target ShellSession resource CRUD wire types; adapt field names from `workload_id`/`shell_name` to `Guest/` ResourceRef; (3) `packages/d2b-client/src/session.rs` `NamedStream` — copy unchanged; used for shell I/O stream; (4) `packages/d2b-session/src/cancellation.rs` `Cancellation`, `RequestRegistry` — copy unchanged; (5) `packages/d2b-session/src/deadline.rs` `DeadlineBudget` — copy unchanged; shell sessions use per-operation deadline tracking; tests to adapt/import: `client.rs:shell_management_uses_typed_selection_result_and_terminal_outcome`, `named_stream_fragments_over_queue_credit_and_has_terminal_actions`, `named_stream_grants_only_consumed_data_and_releases_blocked_sender`, `concurrent_named_streams_route_events_without_cross_consumption`; excluded ADR 0045 assumptions: `VmTargetRoute::Gateway` shell routing and `realm_router` relay path; old `ShellOp`/`ShellOpResponse` seqpacket wire; unsafe-local helper shell protocol v2 | -| Reuse action | copy-then-adapt | +| Reuse action | adapt | | Destination | `packages/d2b/src/shell.rs` (`d2b shell open/attach/list/detach/kill/status`) | -| Detailed design | Route ShellSession resource lifecycle through resource API using adapted `ShellService` generated types; `shell open` → `ShellCreate` → `DaemonClient::open_terminal(Shell)` → `DaemonTerminal`; retain FSM/TTY/signal/RAII behavior from `run_shell_fsm`; `--name` required for kill; SIGHUP detaches without kill. v2 commands (`d2b shell *`) are deleted at 3.0; no dispatch wiring. Excluded: gateway relay path (`VmTargetRoute::Gateway`); old `ShellOp`/`ShellOpResponse` seqpacket protocol; `TargetInput::Workload`-keyed realm routing. | +| Detailed design | Route ShellSession resource lifecycle through resource API using adapted `ShellService` generated types; `shell open` → `ShellCreate` → `DaemonClient::open_terminal(Shell)` → `DaemonTerminal`; retain FSM/TTY/signal/RAII behavior from `run_shell_fsm`; `--name` required for kill; SIGHUP detaches without kill. v2 commands (`d2b shell *`) are deleted at 3.0; no dispatch wiring. Excluded: gateway relay path (`VmTargetRoute::Gateway`); old `ShellOp`/`ShellOpResponse` seqpacket protocol; `TargetInput::Workload`-keyed realm routing. Primary reuse disposition: `adapt`. Preserved source-plan detail: copy-then-adapt. | | Integration | ZoneContext → ShellSession Create via `DaemonClient::open_terminal` → `NamedStream` I/O | | Data migration | None — full d2b 3.0 reset; no prior state to migrate | | Validation | Shell list/detach/kill/attach unit tests (adapted from existing); adapted `client.rs:shell_management_*` and `named_stream_*` tests; TTY RAII/signal tests; confirm v2 `cmd_shell` path is absent | @@ -2403,9 +2403,9 @@ baseline (only a deprecation notice remains at `lib.rs:2424`). | Dependency/owner | ADR046-cli-001; CLI crate owner | | Current source | None (no completion exists in v3 baseline) | | Reuse source | Optional: clap_complete crate (version to be pinned); no main-branch source | -| Reuse action | copy-unchanged (clap_complete) | +| Reuse action | adapt | | Destination | `packages/d2b/src/complete.rs` (`d2b complete bash/zsh/fish`) | -| Detailed design | `d2b complete ` emits completion script; uses clap `CommandFactory::command()` plus dynamic projection fetch (2s per-Provider, 10s total); result bounded at 256 KiB; shell-escaped; newlines stripped | +| Detailed design | `d2b complete ` emits completion script; uses clap `CommandFactory::command()` plus dynamic projection fetch (2s per-Provider, 10s total); result bounded at 256 KiB; shell-escaped; newlines stripped Primary reuse disposition: `adapt`. Preserved source-plan detail: copy-unchanged (clap_complete). | | Integration | Standalone command; no Zone API required for static completion; Zone API used for dynamic Provider projection | | Data migration | None — full d2b 3.0 reset; no prior state to migrate | | Validation | Completion script tests (bash/zsh/fish syntax valid); projection injection safety tests; deadline/partial-Provider tests | @@ -2467,9 +2467,9 @@ baseline (only a deprecation notice remains at `lib.rs:2424`). | Dependency/owner | ADR046-cli-001; CLI crate owner | | Current source | None — net-new v3 work; no pre-ADR45 baseline equivalent | | Reuse source | main `a1cc0b2d` — copy/adapt: (1) `packages/d2b-client/src/client.rs` `ConnectedClient::invoke()`, `ConnectedClient::invoke_with_attachments()`, `ConnectedClient::open_server_stream()` — copy unchanged; these are the three primitives for resource Get/List/Watch respectively; (2) `packages/d2b-client/src/session.rs` `NamedStream` (`send`, `receive`, `cancel`, `close`, `is_terminal`) — copy unchanged; Watch stream output arrives over a named stream; (3) `packages/d2b-session/src/deadline.rs` `DeadlineBudget` — copy unchanged; `--deadline` flag maps to `DeadlineBudget::admit_metadata` wall deadline; `MAX_REQUEST_LIFETIME_MS=900000` caps all Watch/List deadlines; (4) `packages/d2b-client/src/client.rs` `CancellationToken::cancel()` — copy unchanged; `SIGINT`/SIGTERM → `CancellationToken::cancel()` → propagated to `ConnectedClient::invoke` and `NamedStream`; (5) `packages/d2b-client/src/client.rs` `MetadataInput`, `RetryPolicy`, `CallOptions` — copy unchanged; `--idempotency-token` maps to `MetadataInput`; `RetryPolicy::mutating_once()` is the default for Create/UpdateSpec/Delete; tests to adapt/import: `client.rs:metadata_retries_and_cancellation_use_canonical_driver`, `mutating_retries_require_stable_idempotency`, `concurrent_named_streams_route_events_without_cross_consumption`, `named_stream_grants_only_consumed_data_and_releases_blocked_sender`; excluded ADR 0045 assumptions: `TargetInput::Workload/Realm/Provider` routing variants; `GuestClient` cross-realm proxy routing; old `DeploymentProjection`/`RuntimeProjection` ADR 0045-specific field types | -| Reuse action | copy-then-adapt | +| Reuse action | adapt | | Destination | `packages/d2b/src/resource.rs` (standard `d2b get/list/watch/create/update-spec/delete/status` top-level verbs) | -| Detailed design | Generic typed dispatch to resource API Get/List/Watch/Create/UpdateSpec/Delete using `ConnectedClient::invoke` (Get/List/Create/UpdateSpec/Delete) and `ConnectedClient::open_server_stream` + `NamedStream` (Watch); ResourceRef argument parsing and validation; page token pagination; `--phase`/`--label-selector` filters; `--deadline` bounded by `MAX_REQUEST_LIFETIME_MS=900s` via `DeadlineBudget`; Watch output streams resource events as JSON lines; JSON schema version field; `CancellationToken` wired to process signal handlers. Excluded: `GuestClient` vsock exec/shell routing; `TargetInput` realm/workload/provider variants. | +| Detailed design | Generic typed dispatch to resource API Get/List/Watch/Create/UpdateSpec/Delete using `ConnectedClient::invoke` (Get/List/Create/UpdateSpec/Delete) and `ConnectedClient::open_server_stream` + `NamedStream` (Watch); ResourceRef argument parsing and validation; page token pagination; `--phase`/`--label-selector` filters; `--deadline` bounded by `MAX_REQUEST_LIFETIME_MS=900s` via `DeadlineBudget`; Watch output streams resource events as JSON lines; JSON schema version field; `CancellationToken` wired to process signal handlers. Excluded: `GuestClient` vsock exec/shell routing; `TargetInput` realm/workload/provider variants. Primary reuse disposition: `adapt`. Preserved source-plan detail: copy-then-adapt. | | Integration | ZoneContext → `ConnectedClient` → resource API | | Data migration | None — full d2b 3.0 reset; no prior state to migrate | | Validation | Get/list/watch/create/update-spec/delete tests per ResourceType; pagination/filter/watch-deadline tests; error-class/exit-code tests; adapted `client.rs:metadata_retries_*` and `mutating_retries_*` and `concurrent_named_streams_*` tests | diff --git a/docs/specs/ADR-046-components-processes-and-sandbox.md b/docs/specs/ADR-046-components-processes-and-sandbox.md index cb8be20b3..0bcdc54c4 100644 --- a/docs/specs/ADR-046-components-processes-and-sandbox.md +++ b/docs/specs/ADR-046-components-processes-and-sandbox.md @@ -436,9 +436,9 @@ pre-start effects, fine-grained ACL/device policy, or restart semantics. | Dependency/owner | W0/W2; Process contracts/supervisor | | Current source | `d2b-core/src/{processes,process_builder,minijail_profile}.rs`; `d2bd/src/supervisor/*`; broker `spawn_runner.rs` | | Reuse source | Useful main ProviderSupervisor/session/process code named by sub-items | -| Reuse action | extract and adapt | +| Reuse action | adapt | | Destination | `packages/d2b-process/src/`, `packages/d2b-provider-supervisor/src/` | -| Detailed design | Common spec/status/tickets/pidfd/adoption/naming/cgroup/fast path | +| Detailed design | Common spec/status/tickets/pidfd/adoption/naming/cgroup/fast path Primary reuse disposition: `adapt`. Preserved source-plan detail: extract and adapt. | | Integration | Process Provider controllers → supervisor/broker/systemd → async status | | Data migration | Full reset; no role snapshot import | | Validation | Shared conformance/fault/latency tests | @@ -451,9 +451,9 @@ pre-start effects, fine-grained ACL/device policy, or restart semantics. | Dependency/owner | ADR046-process-001; systemd/minijail Provider owners | | Current source | unsafe-local helper runtime/systemd; guest exec systemd-run; broker SpawnRunner/minijail | | Reuse source | Main process/session helpers if selected by exact sub-items | -| Reuse action | extract/adapt | +| Reuse action | adapt | | Destination | `packages/d2b-provider-system-systemd/`, `packages/d2b-provider-system-minijail/` | -| Detailed design | Two Process/EphemeralProcess implementations, pidfd/wait ownership, system/user domains | +| Detailed design | Two Process/EphemeralProcess implementations, pidfd/wait ownership, system/user domains Primary reuse disposition: `adapt`. Preserved source-plan detail: extract/adapt. | | Integration | Zone-installed Providers/controller instances per Host/Guest | | Data migration | None — full d2b 3.0 reset; no prior state to migrate | | Validation | Identical schema/status conformance plus provider-specific adoption | diff --git a/docs/specs/ADR-046-componentsession-and-bus.md b/docs/specs/ADR-046-componentsession-and-bus.md index 880f78a13..73cf888fb 100644 --- a/docs/specs/ADR-046-componentsession-and-bus.md +++ b/docs/specs/ADR-046-componentsession-and-bus.md @@ -367,9 +367,9 @@ transcript/session generation digest, route, and fixed outcome. | Dependency/owner | W0 shared contract root | | Current source | v3 `d2b-realm-router/src/{session,secure_session,mux_session,lifecycle}.rs`, guest auth/transport | | Reuse source | main `a1cc0b2d`: `d2b-contracts/src/v2_component_session.rs`, `d2b-session/src/{handshake,bootstrap,record,engine,scheduler,streams,lifecycle,transport}.rs`, Noise/component tests/vectors | -| Reuse action | copy and adapt | +| Reuse action | adapt | | Destination | `packages/d2b-contracts/src/v3/component_session.rs`, `packages/d2b-session/` | -| Detailed design | Reversion canonical offer/records; retain NN/KK/IKpsk2; add canonical subject/authorization context hooks | +| Detailed design | Reversion canonical offer/records; retain NN/KK/IKpsk2; add canonical subject/authorization context hooks Primary reuse disposition: `adapt`. Preserved source-plan detail: copy and adapt. | | Integration | d2b-bus, resource/controller/Provider services | | Data migration | No v2 session compatibility; reconnect on v3 | | Validation | Copied exact vectors/rejections plus subject/RBAC/revocation tests | @@ -382,24 +382,24 @@ transcript/session generation digest, route, and fixed outcome. | Dependency/owner | ADR046-session-001; transport owner | | Current source | v3 guest vsock/local seqpacket implementations and d2b-realm-transport traits | | Reuse source | main `a1cc0b2d`: `d2b-session-unix/src/{adapter,socket,descriptor,pidfd,vsock,systemd,credit}.rs`, `tests/unix_session.rs` | -| Reuse action | copy and adapt | +| Reuse action | adapt | | Destination | `packages/d2b-session-unix/`, future enrolled transport adapter crates | -| Detailed design | Unix/socketpair/vsock owned transports, peer evidence, fd/pidfd/object validation, credits | +| Detailed design | Unix/socketpair/vsock owned transports, peer evidence, fd/pidfd/object validation, credits Primary reuse disposition: `adapt`. Preserved source-plan detail: copy and adapt. | | Integration | ProviderSupervisor/Host/Guest/Zone listeners hand owned transports to session | | Data migration | None — full d2b 3.0 reset; no prior state to migrate | | Validation | Copied fd/peer/credit tests plus Host/Guest subject mapping | | Removal proof | Ad hoc guest/public/helper transport removed only per service cutover | -### ADR046-bus-001 +### ADR046-session-003 | Field | Value | | --- | --- | | Dependency/owner | Sessions + resource API; bus owner | | Current source | v3 `d2b-realm-router`, target resolver, CLI routing, operation router | | Reuse source | Any useful main d2b-client/provider/session routing symbols named by implementation sub-items | -| Reuse action | extract/adapt | +| Reuse action | adapt | | Destination | `packages/d2b-bus/src/{router,registry,authorization,streams,operations}.rs` | -| Detailed design | Exact service/resource routes, RBAC, pinned reverse route, cancellation, named stream bridge, no wildcard pub/sub | +| Detailed design | Exact service/resource routes, RBAC, pinned reverse route, cancellation, named stream bridge, no wildcard pub/sub Primary reuse disposition: `adapt`. Preserved source-plan detail: extract/adapt. | | Integration | Every ResourceClient/controller/Provider/CLI service | | Data migration | None — full d2b 3.0 reset; no prior state to migrate | | Validation | Message isolation, route/auth revocation, fairness, reconnect, no direct-store path | diff --git a/docs/specs/ADR-046-core-controllers.md b/docs/specs/ADR-046-core-controllers.md index 800323f89..1329bcf78 100644 --- a/docs/specs/ADR-046-core-controllers.md +++ b/docs/specs/ADR-046-core-controllers.md @@ -339,9 +339,9 @@ Keeping handlers in one process does not union arbitrary Provider privilege: | Dependency/owner | W0/W1a; core-controller owner | | Current source | `packages/d2bd/src/lib.rs`, `provider_registry.rs` if present on source ref, supervisor state, operations; `d2b-realm-core/src/{allocator_engine,identity_store}.rs` | | Reuse source | Useful pure handler/toolkit code from main named in implementation sub-items | -| Reuse action | extract and adapt | +| Reuse action | adapt | | Destination | `packages/d2b-core-controller/src/{main,configuration,api_catalog,authz,providers,controllers,ownership,watches,cleanup,zone_links,budgets,store}.rs` | -| Detailed design | One fixed process, isolated handlers, async ResourceClient, health/startup/restart | +| Detailed design | One fixed process, isolated handlers, async ResourceClient, health/startup/restart Primary reuse disposition: `adapt`. Preserved source-plan detail: extract and adapt. | | Integration | Zone runtime local bus/session; Provider/system-core resource identity | | Data migration | Full reset | | Validation | Per-handler unit/property tests plus multi-process startup/restart | @@ -354,9 +354,9 @@ Keeping handlers in one process does not union arbitrary Provider privilege: | Dependency/owner | ADR046-core-001; system-core Provider owner | | Current source | Nix host/Realm options/index; host check/provider code; user/group lookup and unsafe-local eligibility | | Reuse source | Any main local-host/user-provider code selected by exact sub-items | -| Reuse action | extract/adapt | +| Reuse action | adapt | | Destination | `packages/d2b-provider-system-core/src/{host,user}.rs` linked into fixed core controller | -| Detailed design | Host and User schemas/reconcile/status/capabilities | +| Detailed design | Host and User schemas/reconcile/status/capabilities Primary reuse disposition: `adapt`. Preserved source-plan detail: extract/adapt. | | Integration | Bootstrap Provider/system-core; other controllers target Host/Guest/User refs | | Data migration | New v3 resources from Nix | | Validation | Multiple Hosts, system/user restrictions, UID/session drift | diff --git a/docs/specs/ADR-046-feasibility-and-spikes.md b/docs/specs/ADR-046-feasibility-and-spikes.md index e46f0a463..6bfeed5ac 100644 --- a/docs/specs/ADR-046-feasibility-and-spikes.md +++ b/docs/specs/ADR-046-feasibility-and-spikes.md @@ -381,9 +381,9 @@ performed by this documentation-only spec. | Metrics | (1) every one of the 500 calls is delivered to exactly the recipient named by its route key, verified by a per-recipient received-call counter; (2) each recipient's session transcript hash and record sequence counter are independent (no shared state, verified by asserting the three sessions' internal sequence counters never reference each other's session object); (3) the deliberate cross-wiring case is rejected before any record is exchanged. | | Pass/fail threshold | 500/500 correct routing, 0 cross-recipient session-state leakage, 1/1 cross-wiring case rejected with a stable typed error (not a panic, not a silent no-op). | | Expected resource budget | ≤2 minutes wall time; ≤64 MiB RSS (three lightweight Noise sessions plus the fake router). | -| Failure interpretation | A misrouted call or shared-session-state finding blocks `ADR046-bus-001` from starting until the route-key resolution or per-session isolation in `ADR-046-componentsession-and-bus` §"d2b-bus" is corrected; per anti-claim rule 3, the fix is never "widen the route key to fail open." | -| Affected decisions/work items | D011, D039, D040, D054; `ADR046-session-001`, `ADR046-session-002`, `ADR046-bus-001`. | -| Cleanup | Deleted once `packages/d2b-bus/src/router.rs` (the real `ADR046-bus-001` destination) carries an in-tree message-isolation/route-authorization/no-direct-store-path conformance test with equal or stricter coverage, per that work item's own Validation column. | +| Failure interpretation | A misrouted call or shared-session-state finding blocks `ADR046-session-003` from starting until the route-key resolution or per-session isolation in `ADR-046-componentsession-and-bus` §"d2b-bus" is corrected; per anti-claim rule 3, the fix is never "widen the route key to fail open." | +| Affected decisions/work items | D011, D039, D040, D054; `ADR046-session-001`, `ADR046-session-002`, `ADR046-session-003`. | +| Cleanup | Deleted once `packages/d2b-bus/src/router.rs` (the real `ADR046-session-003` destination) carries an in-tree message-isolation/route-authorization/no-direct-store-path conformance test with equal or stricter coverage, per that work item's own Validation column. | | Status | Specified — not yet executed. | ### SPIKE-07 — Unix/vsock/Azure-Relay transports carry only opaque Noise record bytes @@ -697,9 +697,9 @@ decisions/work items" row. | Dependency/owner | W0 shared contract root; store/reconciliation integrator | | Current source | `packages/d2b-core/src/{storage,sync}.rs` (atomic/idempotency reference shape, E4); no redb usage exists anywhere in this repository at any inspected commit | | Reuse source | None (redb is a new external dependency; no main or v3 code implements it) | -| Reuse action | `adapt` (the atomic-write/idempotency discipline in `storage.rs`/`sync.rs` is adapted into the spike's write-transaction algorithm; redb itself is used unmodified) | +| Reuse action | adapt | | Destination | `proofs/redb-resource-store-spike/` | -| Detailed design | Implements SPIKE-01 and SPIKE-02: the eight-table schema, fair write queue, blocking store-actor, watch registrar, and hint bus described in those two spike entries | +| Detailed design | Implements SPIKE-01 and SPIKE-02: the eight-table schema, fair write queue, blocking store-actor, watch registrar, and hint bus described in those two spike entries Primary reuse disposition: `adapt`. Preserved source-plan detail: `adapt` (the atomic-write/idempotency discipline in `storage.rs`/`sync.rs` is adapted into the spike's write-transaction algorithm; redb itself is used unmodified). | | Integration | None (standalone; no d2b-bus/ComponentSession/broker dependency) | | Data migration | None (disposable fixture data only) | | Validation | SPIKE-01 metrics (1)-(5) and SPIKE-02 metrics (1) across all 3 concurrency profiles, per those entries' exact pass/fail thresholds | @@ -713,9 +713,9 @@ decisions/work items" row. | Dependency/owner | `ADR046-feasibility-001`; reconciliation/process integrator | | Current source | `packages/d2bd/src/supervisor/{dag,pidfd}.rs` (current DAG/pidfd reference shape, E4) | | Reuse source | None (the generic async controller loop is ADR-only per `ADR-046-resource-reconciliation`'s own current-code-fit row) | -| Reuse action | `adapt` (current DAG ordering/readiness concepts are adapted into the spike's per-resource single-flight/parallel-semaphore loop) | +| Reuse action | adapt | | Destination | `proofs/process-fastlaunch-spike/` | -| Detailed design | Implements SPIKE-03: the fake Process controller loop, fake `ProcessLaunchEffectPort`, and the 1/10/100-concurrency commit-to-launch-attempt and next-dispatch-independence benchmarks | +| Detailed design | Implements SPIKE-03: the fake Process controller loop, fake `ProcessLaunchEffectPort`, and the 1/10/100-concurrency commit-to-launch-attempt and next-dispatch-independence benchmarks Primary reuse disposition: `adapt`. Preserved source-plan detail: `adapt` (current DAG ordering/readiness concepts are adapted into the spike's per-resource single-flight/parallel-semaphore loop). | | Integration | Consumes `ADR046-feasibility-001`'s hint-bus shape as its watch-receiver input | | Data migration | None — full d2b 3.0 reset; no prior state to migrate | | Validation | SPIKE-03 metrics (1)-(3) and thresholds | @@ -729,7 +729,7 @@ decisions/work items" row. | Dependency/owner | Independent of `-001`/`-002`; EffectPort/ProviderSupervisor integrator | | Current source | `packages/d2b-priv-broker/src/ops/spawn_runner.rs` and `tests/pidfd_real_spawner.rs` (current blocking-call reference shape, E4) | | Reuse source | None | -| Reuse action | `adapt` | +| Reuse action | adapt | | Destination | `proofs/effectport-async-spike/` | | Detailed design | Implements SPIKE-04: the four fake EffectPort traits, the deliberately slow blocking-primitive backends, and the current-thread-runtime heartbeat-jitter detector | | Integration | None (standalone) | @@ -745,9 +745,9 @@ decisions/work items" row. | Dependency/owner | Independent of `-001`/`-002`/`-003`; Provider packaging/toolkit integrator | | Current source | None in v3 at `b5ddbed6` (no generic Provider registry exists; per parent ADR context, this is explicitly listed as missing) | | Reuse source | None | -| Reuse action | `adapt` (the crate-layout policy check reuses the same `src/`/`tests/`/`integration/`/`README.md` structure already enforced elsewhere in this repository's workspace policy tests) | +| Reuse action | adapt | | Destination | `proofs/provider-packaging-spike/` | -| Detailed design | Implements SPIKE-05: the two-binary crate, hand-authored manifest, fake `ProviderDeployment`, and the `cargo metadata` dependency-edge check | +| Detailed design | Implements SPIKE-05: the two-binary crate, hand-authored manifest, fake `ProviderDeployment`, and the `cargo metadata` dependency-edge check Primary reuse disposition: `adapt`. Preserved source-plan detail: `adapt` (the crate-layout policy check reuses the same `src/`/`tests/`/`integration/`/`README.md` structure already enforced elsewhere in this repository's workspace policy tests). | | Integration | None (standalone) | | Data migration | None — full d2b 3.0 reset; no prior state to migrate | | Validation | SPIKE-05 metrics (1)-(4) across 20 repeated randomized-order manifest loads | @@ -761,9 +761,9 @@ decisions/work items" row. | Dependency/owner | Independent of `-001` through `-004`; bus/session/transport/credential integrator | | Current source | `packages/d2b-realm-router/src/{session,secure_session,mux_session}.rs` (current routing reference shape, E4); main `a1cc0b2d` `packages/d2b-session/**`, `packages/d2b-session-unix/**` (re-verified green in E3) | | Reuse source | main `a1cc0b2d`: `packages/d2b-session/src/{handshake,bootstrap,record,engine,scheduler,streams,lifecycle,transport}.rs`, `packages/d2b-session-unix/src/{adapter,vsock,pidfd,socket,systemd,credit,descriptor}.rs`, and the exact test files listed in E3's table | -| Reuse action | `copy-unchanged` for the Noise/record/transport machinery (path-dependency on a pinned local checkout of `a1cc0b2d`); `adapt` for the fake router/relay/credential-delivery wrapper code that SPIKE-06/07/08 add on top | +| Reuse action | adapt | | Destination | `proofs/bus-routing-noise-spike/`, `proofs/transport-opaque-streams-spike/`, `proofs/credential-kk-e2e-spike/` | -| Detailed design | Implements SPIKE-06 (exact-addressed routing + per-recipient Noise isolation), SPIKE-07 (Unix/vsock/relay-shaped opaque byte-stream conformance across 3 backends), and SPIKE-08 (Credential Provider → consumer Provider KK delivery with the 13-field binding contract) | +| Detailed design | Implements SPIKE-06 (exact-addressed routing + per-recipient Noise isolation), SPIKE-07 (Unix/vsock/relay-shaped opaque byte-stream conformance across 3 backends), and SPIKE-08 (Credential Provider → consumer Provider KK delivery with the 13-field binding contract) Primary reuse disposition: `adapt`. Preserved source-plan detail: `copy-unchanged` for the Noise/record/transport machinery (path-dependency on a pinned local checkout of `a1cc0b2d`); `adapt` for the fake router/relay/credential-delivery wrapper code that SPIKE-06/07/08 add on top. | | Integration | SPIKE-07's Unix backend and SPIKE-08's session machinery both depend on the same pinned `a1cc0b2d` path-dependency established for SPIKE-06 | | Data migration | None — full d2b 3.0 reset; no prior state to migrate | | Validation | SPIKE-06 metrics (1)-(3), SPIKE-07 metrics (1)-(4) against the exact numeric gates already committed in the transport-unix/vsock dossiers, SPIKE-08 metrics (1)-(6) | @@ -777,7 +777,7 @@ decisions/work items" row. | Dependency/owner | Independent of `-001` through `-005`; Provider-state/Volume integrator | | Current source | None in v3 at `b5ddbed6` (ProviderStateSet and the generalized Volume ResourceType are both ADR-only) | | Reuse source | None | -| Reuse action | `adapt` | +| Reuse action | adapt | | Destination | `proofs/provider-state-export-spike/`, `proofs/volume-policy-spike/` | | Detailed design | Implements SPIKE-09 (optional declared state-Volume creation order, guest-local/host-backed-guest placement, virtiofs Export child ownership) and SPIKE-10 (Volume ACL/`sourcePolicyId`/quota/lifecycle-marker policy conformance) | | Integration | None between the two spikes beyond sharing the same fake resource-store oracle shape | @@ -793,7 +793,7 @@ decisions/work items" row. | Dependency/owner | Independent of `-001` through `-006`; Process Provider integrator | | Current source | `packages/d2b-priv-broker/src/ops/spawn_runner.rs` (real-spawn reference shape, E4); current unsafe-local helper runtime/systemd invocation shape | | Reuse source | None | -| Reuse action | `adapt` | +| Reuse action | adapt | | Destination | `proofs/process-provider-conformance-spike/` | | Detailed design | Implements SPIKE-11: the shared `ProcessProviderHarness` trait, the minijail-shaped `clone3(CLONE_PIDFD)` launcher, and the systemd transient-user-scope launcher, plus the identity-drift/quarantine and clean-exit cases | | Integration | None (standalone; requires a Linux host with `clone3`/`pidfd_open`, and optionally a running `systemd --user` instance behind the `systemd-user` feature) | @@ -809,7 +809,7 @@ decisions/work items" row. | Dependency/owner | Independent of `-001` through `-007`; Nix/xtask integrator | | Current source | `nixos-modules/assertions.nix` pattern, `packages/xtask` `gen-schemas` pattern, `make test-drift` gate (existing generated-or-eval-contract precedent) | | Reuse source | None | -| Reuse action | `adapt` | +| Reuse action | adapt | | Destination | `proofs/nix-authoring-spike/` | | Detailed design | Implements SPIKE-12: the minimal flake, the two synthetic ResourceTypes, the hand-written committed schemas, the standalone `gen-schemas`-shaped drift check, and the two-generation removed-resource cleanup simulation | | Integration | None (standalone flake; no dependency on the main `flake.nix`) | @@ -825,7 +825,7 @@ decisions/work items" row. | Dependency/owner | Independent of `-001` through `-008`; CLI integrator | | Current source | `packages/d2b/src/lib.rs` `cmd_audio`/`cmd_clipboard_arm` and the current command-table shape (current CLI reference shape) | | Reuse source | None | -| Reuse action | `adapt` | +| Reuse action | adapt | | Destination | `proofs/cli-discovery-spike/`, `proofs/clean-cutover-spike/` | | Detailed design | Implements SPIKE-13 (dynamic Provider-projection discovery, bounds, latency isolation) and SPIKE-14 (zero v2 dispatch, fresh Zone bootstrap ignoring legacy state) | | Integration | None between the two spikes beyond sharing the same fixture command-table shape | @@ -841,7 +841,7 @@ decisions/work items" row. | Dependency/owner | `ADR046-feasibility-001` through `ADR046-feasibility-009` (integrates their fakes; must run last) | | Current source | None (this is a pure integration of the other nine work items' fixtures) | | Reuse source | None beyond what `-001` through `-009` already reuse | -| Reuse action | `adapt` | +| Reuse action | adapt | | Destination | `proofs/e2e-composition-spike/` | | Detailed design | Implements SPIKE-15: the three representative compositions (local/cloud-hypervisor, cloud/azure, interaction/shell-terminal-or-wayland), wired from the fakes built by `-001` through `-009`, plus the combined 3-Zone aggregate RSS measurement | | Integration | Depends on and imports the fake shapes from `proofs/redb-resource-store-spike/`, `proofs/process-fastlaunch-spike/`, `proofs/effectport-async-spike/`, `proofs/provider-packaging-spike/`, `proofs/bus-routing-noise-spike/`, `proofs/transport-opaque-streams-spike/`, `proofs/credential-kk-e2e-spike/`, `proofs/provider-state-export-spike/`, `proofs/volume-policy-spike/`, and `proofs/process-provider-conformance-spike/` | @@ -857,7 +857,7 @@ decisions/work items" row. | Dependency/owner | `ADR046-delivery-007`; delivery/test-tooling integrator | | Current source | this codebase's ad hoc `tests/tools/` timing logs (`d2b-static-timing.$$/`), which are not a candidate-bound, reference-runner-recorded ledger | | Reuse source | existing `libtest --format=json` timing output and `xtask` (no new test framework) | -| Reuse action | `adapt` | +| Reuse action | adapt | | Destination | `proofs/test-runtime-budget-spike/`; the committed baseline ledger consumed by `ADR046-delivery-007` | | Detailed design | Establishes the D094 measurement baseline: records the reference runner class, repetition count, and per-test/crate/shard p95 for a representative hermetic crate; proves the §10.16 budgets (individual normal test p95 ≤50 ms, per-crate `--lib --tests` ≤2 s, Layer-1 hermetic shard ≤60 s) are met on the reference runner and that an injected slow/sleeping test is detected as a regression | | Integration | Output ledger shape is consumed by the runtime ledger/timing gate; establishes the historical threshold seed | diff --git a/docs/specs/ADR-046-implementation-graph.json b/docs/specs/ADR-046-implementation-graph.json index 69f093130..b4d8a7636 100644 --- a/docs/specs/ADR-046-implementation-graph.json +++ b/docs/specs/ADR-046-implementation-graph.json @@ -1,15 +1,15 @@ { - "adr": "0046", - "artifactKind": "d2b-adr-implementation-graph", - "counts": { - "edges": 1677, - "maxTopologicalRank": 19, - "nodes": 593, - "specNodes": 55, - "waves": 8, - "workItemNodes": 538 + "adr" : "0046", + "artifactKind" : "d2b-adr-implementation-graph", + "counts" : { + "edges" : 1896, + "maxTopologicalRank" : 22, + "nodes" : 598, + "specNodes" : 55, + "waves" : 8, + "workItemNodes" : 543 }, - "criticalPath": [ + "criticalPath" : [ "ADR-046-decision-register", "ADR-046-terminology-and-identities", "ADR-046-resource-object-model", @@ -18,8425 +18,9523 @@ "ADR-046-resource-reconciliation", "ADR-046-primitive-resource-composition", "ADR-046-provider-model-and-packaging", - "ADR-046-components-processes-and-sandbox", - "ADR-046-cli-and-operations", - "ADR-046-reset-and-cutover", - "ADR046-reset-001", - "ADR046-reset-002", - "ADR046-reset-003", - "ADR046-reset-004", - "ADR046-reset-005", - "ADR046-reset-006", - "ADR046-reset-007", - "ADR046-reset-010", - "ADR046-reset-011" + "ADR-046-core-controllers", + "ADR-046-nix-configuration", + "ADR-046-provider-runtime-qemu-media", + "ADR046-qemu-media-001", + "ADR046-qemu-media-003", + "ADR046-qemu-media-005", + "ADR046-qemu-media-006", + "ADR046-qemu-media-009", + "ADR046-qemu-media-010", + "ADR046-qemu-media-011", + "ADR046-qemu-media-013", + "ADR046-qemu-media-014", + "ADR046-qemu-media-015", + "ADR046-qemu-media-018", + "ADR046-qemu-media-019" ], - "edgeTypes": [ + "edgeTypes" : [ "file-overlap-order", "implements-spec", "shared-contract", "spec-depends-on", "work-item-depends-on" ], - "edges": [ + "edges" : [ { - "from": "ADR-046-cli-and-operations", - "to": "ADR-046-components-processes-and-sandbox", - "type": "spec-depends-on" + "from" : "ADR046-device-007", + "to" : "ADR046-core-001", + "type" : "file-overlap-order" }, { - "from": "ADR-046-cli-and-operations", - "to": "ADR-046-componentsession-and-bus", - "type": "spec-depends-on" + "from" : "ADR046-exec-013", + "to" : "ADR046-core-001", + "type" : "file-overlap-order" }, { - "from": "ADR-046-cli-and-operations", - "to": "ADR-046-provider-model-and-packaging", - "type": "spec-depends-on" + "from" : "ADR046-exec-015", + "to" : "ADR046-core-001", + "type" : "file-overlap-order" }, { - "from": "ADR-046-cli-and-operations", - "to": "ADR-046-resource-api-and-authorization", - "type": "spec-depends-on" + "from" : "ADR046-network-008", + "to" : "ADR046-core-001", + "type" : "file-overlap-order" }, { - "from": "ADR-046-cli-and-operations", - "to": "ADR-046-resource-object-model", - "type": "spec-depends-on" + "from" : "ADR046-telem-011", + "to" : "ADR046-core-001", + "type" : "file-overlap-order" }, { - "from": "ADR-046-cli-and-operations", - "to": "ADR-046-terminology-and-identities", - "type": "spec-depends-on" + "from" : "ADR046-zone-control-016", + "to" : "ADR046-core-001", + "type" : "file-overlap-order" }, { - "from": "ADR-046-components-processes-and-sandbox", - "to": "ADR-046-componentsession-and-bus", - "type": "spec-depends-on" + "from" : "ADR046-zone-control-021", + "to" : "ADR046-core-001", + "type" : "file-overlap-order" }, { - "from": "ADR-046-components-processes-and-sandbox", - "to": "ADR-046-primitive-resource-composition", - "type": "spec-depends-on" + "from" : "ADR046-aca-001", + "to" : "ADR-046-provider-runtime-azure-container-apps", + "type" : "implements-spec" }, { - "from": "ADR-046-components-processes-and-sandbox", - "to": "ADR-046-provider-model-and-packaging", - "type": "spec-depends-on" + "from" : "ADR046-aca-002", + "to" : "ADR-046-provider-runtime-azure-container-apps", + "type" : "implements-spec" }, { - "from": "ADR-046-componentsession-and-bus", - "to": "ADR-046-resource-api-and-authorization", - "type": "spec-depends-on" + "from" : "ADR046-aca-003", + "to" : "ADR-046-provider-runtime-azure-container-apps", + "type" : "implements-spec" }, { - "from": "ADR-046-componentsession-and-bus", - "to": "ADR-046-terminology-and-identities", - "type": "spec-depends-on" + "from" : "ADR046-aca-004", + "to" : "ADR-046-provider-runtime-azure-container-apps", + "type" : "implements-spec" }, { - "from": "ADR-046-core-controllers", - "to": "ADR-046-provider-model-and-packaging", - "type": "spec-depends-on" + "from" : "ADR046-aca-005", + "to" : "ADR-046-provider-runtime-azure-container-apps", + "type" : "implements-spec" }, { - "from": "ADR-046-core-controllers", - "to": "ADR-046-resource-api-and-authorization", - "type": "spec-depends-on" + "from" : "ADR046-aca-006", + "to" : "ADR-046-provider-runtime-azure-container-apps", + "type" : "implements-spec" }, { - "from": "ADR-046-core-controllers", - "to": "ADR-046-resource-reconciliation", - "type": "spec-depends-on" + "from" : "ADR046-aca-007", + "to" : "ADR-046-provider-runtime-azure-container-apps", + "type" : "implements-spec" }, { - "from": "ADR-046-core-controllers", - "to": "ADR-046-resource-store-redb", - "type": "spec-depends-on" + "from" : "ADR046-activation-001", + "to" : "ADR-046-provider-activation-nixos", + "type" : "implements-spec" }, { - "from": "ADR-046-feasibility-and-spikes", - "to": "ADR-046-cli-and-operations", - "type": "spec-depends-on" + "from" : "ADR046-activation-002", + "to" : "ADR-046-provider-activation-nixos", + "type" : "implements-spec" }, { - "from": "ADR-046-feasibility-and-spikes", - "to": "ADR-046-components-processes-and-sandbox", - "type": "spec-depends-on" + "from" : "ADR046-activation-003", + "to" : "ADR-046-provider-activation-nixos", + "type" : "implements-spec" }, { - "from": "ADR-046-feasibility-and-spikes", - "to": "ADR-046-componentsession-and-bus", - "type": "spec-depends-on" + "from" : "ADR046-activation-004", + "to" : "ADR-046-provider-activation-nixos", + "type" : "implements-spec" }, { - "from": "ADR-046-feasibility-and-spikes", - "to": "ADR-046-current-code-migration-map", - "type": "spec-depends-on" + "from" : "ADR046-activation-005", + "to" : "ADR-046-provider-activation-nixos", + "type" : "implements-spec" }, { - "from": "ADR-046-feasibility-and-spikes", - "to": "ADR-046-decision-register", - "type": "spec-depends-on" + "from" : "ADR046-activation-006", + "to" : "ADR-046-provider-activation-nixos", + "type" : "implements-spec" }, { - "from": "ADR-046-feasibility-and-spikes", - "to": "ADR-046-nix-configuration", - "type": "spec-depends-on" + "from" : "ADR046-activation-007", + "to" : "ADR-046-provider-activation-nixos", + "type" : "implements-spec" }, { - "from": "ADR-046-feasibility-and-spikes", - "to": "ADR-046-provider-model-and-packaging", - "type": "spec-depends-on" + "from" : "ADR046-api-001", + "to" : "ADR-046-resource-api-and-authorization", + "type" : "implements-spec" }, { - "from": "ADR-046-feasibility-and-spikes", - "to": "ADR-046-provider-state", - "type": "spec-depends-on" + "from" : "ADR046-api-002", + "to" : "ADR-046-resource-api-and-authorization", + "type" : "implements-spec" }, { - "from": "ADR-046-feasibility-and-spikes", - "to": "ADR-046-resource-reconciliation", - "type": "spec-depends-on" + "from" : "ADR046-audio-001", + "to" : "ADR-046-provider-audio-pipewire", + "type" : "implements-spec" }, { - "from": "ADR-046-feasibility-and-spikes", - "to": "ADR-046-resource-store-redb", - "type": "spec-depends-on" + "from" : "ADR046-audio-002", + "to" : "ADR-046-provider-audio-pipewire", + "type" : "implements-spec" }, { - "from": "ADR-046-feasibility-and-spikes", - "to": "ADR-046-resources-credential", - "type": "spec-depends-on" + "from" : "ADR046-audio-004", + "to" : "ADR-046-provider-audio-pipewire", + "type" : "implements-spec" }, { - "from": "ADR-046-feasibility-and-spikes", - "to": "ADR-046-resources-host-guest-process-user", - "type": "spec-depends-on" + "from" : "ADR046-audio-005", + "to" : "ADR-046-provider-audio-pipewire", + "type" : "implements-spec" }, { - "from": "ADR-046-feasibility-and-spikes", - "to": "ADR-046-resources-volume", - "type": "spec-depends-on" + "from" : "ADR046-audio-006", + "to" : "ADR-046-provider-audio-pipewire", + "type" : "implements-spec" }, { - "from": "ADR-046-feasibility-and-spikes", - "to": "ADR-046-terminology-and-identities", - "type": "spec-depends-on" + "from" : "ADR046-audio-007", + "to" : "ADR-046-provider-audio-pipewire", + "type" : "implements-spec" }, { - "from": "ADR-046-feasibility-and-spikes", - "to": "ADR-046-zone-routing", - "type": "spec-depends-on" + "from" : "ADR046-audio-008", + "to" : "ADR-046-provider-audio-pipewire", + "type" : "implements-spec" }, { - "from": "ADR-046-nix-configuration", - "to": "ADR-046-core-controllers", - "type": "spec-depends-on" + "from" : "ADR046-audio-009", + "to" : "ADR-046-provider-audio-pipewire", + "type" : "implements-spec" }, { - "from": "ADR-046-nix-configuration", - "to": "ADR-046-primitive-resource-composition", - "type": "spec-depends-on" + "from" : "ADR046-audio-010", + "to" : "ADR-046-provider-audio-pipewire", + "type" : "implements-spec" }, { - "from": "ADR-046-nix-configuration", - "to": "ADR-046-provider-model-and-packaging", - "type": "spec-depends-on" + "from" : "ADR046-audio-011", + "to" : "ADR-046-provider-audio-pipewire", + "type" : "implements-spec" }, { - "from": "ADR-046-nix-configuration", - "to": "ADR-046-resource-object-model", - "type": "spec-depends-on" + "from" : "ADR046-audio-012", + "to" : "ADR-046-provider-audio-pipewire", + "type" : "implements-spec" }, { - "from": "ADR-046-nix-configuration", - "to": "ADR-046-terminology-and-identities", - "type": "spec-depends-on" + "from" : "ADR046-audio-013", + "to" : "ADR-046-provider-audio-pipewire", + "type" : "implements-spec" }, { - "from": "ADR-046-primitive-resource-composition", - "to": "ADR-046-resource-object-model", - "type": "spec-depends-on" + "from" : "ADR046-audio-014", + "to" : "ADR-046-provider-audio-pipewire", + "type" : "implements-spec" }, { - "from": "ADR-046-primitive-resource-composition", - "to": "ADR-046-resource-reconciliation", - "type": "spec-depends-on" + "from" : "ADR046-azure-vm-001", + "to" : "ADR-046-provider-runtime-azure-virtual-machine", + "type" : "implements-spec" }, { - "from": "ADR-046-provider-activation-nixos", - "to": "ADR-046-cli-and-operations", - "type": "spec-depends-on" + "from" : "ADR046-azure-vm-002", + "to" : "ADR-046-provider-runtime-azure-virtual-machine", + "type" : "implements-spec" }, { - "from": "ADR-046-provider-activation-nixos", - "to": "ADR-046-componentsession-and-bus", - "type": "spec-depends-on" + "from" : "ADR046-azure-vm-003", + "to" : "ADR-046-provider-runtime-azure-virtual-machine", + "type" : "implements-spec" }, { - "from": "ADR-046-provider-activation-nixos", - "to": "ADR-046-core-controllers", - "type": "spec-depends-on" + "from" : "ADR046-azure-vm-004", + "to" : "ADR-046-provider-runtime-azure-virtual-machine", + "type" : "implements-spec" }, { - "from": "ADR-046-provider-activation-nixos", - "to": "ADR-046-nix-configuration", - "type": "spec-depends-on" + "from" : "ADR046-azure-vm-005", + "to" : "ADR-046-provider-runtime-azure-virtual-machine", + "type" : "implements-spec" }, { - "from": "ADR-046-provider-activation-nixos", - "to": "ADR-046-provider-model-and-packaging", - "type": "spec-depends-on" + "from" : "ADR046-azure-vm-006", + "to" : "ADR-046-provider-runtime-azure-virtual-machine", + "type" : "implements-spec" }, { - "from": "ADR-046-provider-activation-nixos", - "to": "ADR-046-provider-state", - "type": "spec-depends-on" + "from" : "ADR046-azure-vm-007", + "to" : "ADR-046-provider-runtime-azure-virtual-machine", + "type" : "implements-spec" }, { - "from": "ADR-046-provider-activation-nixos", - "to": "ADR-046-resource-api-and-authorization", - "type": "spec-depends-on" + "from" : "ADR046-azure-vm-008", + "to" : "ADR-046-provider-runtime-azure-virtual-machine", + "type" : "implements-spec" }, { - "from": "ADR-046-provider-activation-nixos", - "to": "ADR-046-resource-object-model", - "type": "spec-depends-on" + "from" : "ADR046-azure-vm-009", + "to" : "ADR-046-provider-runtime-azure-virtual-machine", + "type" : "implements-spec" }, { - "from": "ADR-046-provider-activation-nixos", - "to": "ADR-046-resources-host-guest-process-user", - "type": "spec-depends-on" + "from" : "ADR046-ch-001", + "to" : "ADR-046-provider-runtime-cloud-hypervisor", + "type" : "implements-spec" }, { - "from": "ADR-046-provider-activation-nixos", - "to": "ADR-046-resources-zone-control", - "type": "spec-depends-on" + "from" : "ADR046-ch-002", + "to" : "ADR-046-provider-runtime-cloud-hypervisor", + "type" : "implements-spec" }, { - "from": "ADR-046-provider-activation-nixos", - "to": "ADR-046-telemetry-audit-and-support", - "type": "spec-depends-on" + "from" : "ADR046-ch-003", + "to" : "ADR-046-provider-runtime-cloud-hypervisor", + "type" : "implements-spec" }, { - "from": "ADR-046-provider-audio-pipewire", - "to": "ADR-046-components-processes-and-sandbox", - "type": "spec-depends-on" + "from" : "ADR046-ch-004", + "to" : "ADR-046-provider-runtime-cloud-hypervisor", + "type" : "implements-spec" }, { - "from": "ADR-046-provider-audio-pipewire", - "to": "ADR-046-componentsession-and-bus", - "type": "spec-depends-on" + "from" : "ADR046-ch-005", + "to" : "ADR-046-provider-runtime-cloud-hypervisor", + "type" : "implements-spec" }, { - "from": "ADR-046-provider-audio-pipewire", - "to": "ADR-046-nix-configuration", - "type": "spec-depends-on" + "from" : "ADR046-ch-006", + "to" : "ADR-046-provider-runtime-cloud-hypervisor", + "type" : "implements-spec" }, { - "from": "ADR-046-provider-audio-pipewire", - "to": "ADR-046-primitive-resource-composition", - "type": "spec-depends-on" + "from" : "ADR046-ch-007", + "to" : "ADR-046-provider-runtime-cloud-hypervisor", + "type" : "implements-spec" }, { - "from": "ADR-046-provider-audio-pipewire", - "to": "ADR-046-provider-model-and-packaging", - "type": "spec-depends-on" + "from" : "ADR046-cli-001", + "to" : "ADR-046-cli-and-operations", + "type" : "implements-spec" }, { - "from": "ADR-046-provider-audio-pipewire", - "to": "ADR-046-provider-state", - "type": "spec-depends-on" + "from" : "ADR046-cli-002", + "to" : "ADR-046-cli-and-operations", + "type" : "implements-spec" }, { - "from": "ADR-046-provider-audio-pipewire", - "to": "ADR-046-resource-api-and-authorization", - "type": "spec-depends-on" + "from" : "ADR046-cli-003", + "to" : "ADR-046-cli-and-operations", + "type" : "implements-spec" }, { - "from": "ADR-046-provider-audio-pipewire", - "to": "ADR-046-resource-object-model", - "type": "spec-depends-on" + "from" : "ADR046-cli-004", + "to" : "ADR-046-cli-and-operations", + "type" : "implements-spec" }, { - "from": "ADR-046-provider-audio-pipewire", - "to": "ADR-046-resource-reconciliation", - "type": "spec-depends-on" + "from" : "ADR046-cli-005", + "to" : "ADR-046-cli-and-operations", + "type" : "implements-spec" }, { - "from": "ADR-046-provider-audio-pipewire", - "to": "ADR-046-resources-host-guest-process-user", - "type": "spec-depends-on" + "from" : "ADR046-cli-006", + "to" : "ADR-046-cli-and-operations", + "type" : "implements-spec" }, { - "from": "ADR-046-provider-audio-pipewire", - "to": "ADR-046-resources-volume", - "type": "spec-depends-on" + "from" : "ADR046-cli-007", + "to" : "ADR-046-cli-and-operations", + "type" : "implements-spec" }, { - "from": "ADR-046-provider-audio-pipewire", - "to": "ADR-046-telemetry-audit-and-support", - "type": "spec-depends-on" + "from" : "ADR046-cli-008", + "to" : "ADR-046-cli-and-operations", + "type" : "implements-spec" }, { - "from": "ADR-046-provider-clipboard-wayland", - "to": "ADR-046-componentsession-and-bus", - "type": "spec-depends-on" + "from" : "ADR046-cli-009", + "to" : "ADR-046-cli-and-operations", + "type" : "implements-spec" }, { - "from": "ADR-046-provider-clipboard-wayland", - "to": "ADR-046-core-controllers", - "type": "spec-depends-on" + "from" : "ADR046-cli-010", + "to" : "ADR-046-cli-and-operations", + "type" : "implements-spec" }, { - "from": "ADR-046-provider-clipboard-wayland", - "to": "ADR-046-nix-configuration", - "type": "spec-depends-on" + "from" : "ADR046-cli-011", + "to" : "ADR-046-cli-and-operations", + "type" : "implements-spec" }, { - "from": "ADR-046-provider-clipboard-wayland", - "to": "ADR-046-provider-model-and-packaging", - "type": "spec-depends-on" + "from" : "ADR046-cli-012", + "to" : "ADR-046-cli-and-operations", + "type" : "implements-spec" }, { - "from": "ADR-046-provider-clipboard-wayland", - "to": "ADR-046-provider-state", - "type": "spec-depends-on" + "from" : "ADR046-cli-013", + "to" : "ADR-046-cli-and-operations", + "type" : "implements-spec" }, { - "from": "ADR-046-provider-clipboard-wayland", - "to": "ADR-046-resource-reconciliation", - "type": "spec-depends-on" + "from" : "ADR046-clipboard-001", + "to" : "ADR-046-provider-clipboard-wayland", + "type" : "implements-spec" }, { - "from": "ADR-046-provider-clipboard-wayland", - "to": "ADR-046-resources-host-guest-process-user", - "type": "spec-depends-on" + "from" : "ADR046-clipboard-002", + "to" : "ADR-046-provider-clipboard-wayland", + "type" : "implements-spec" }, { - "from": "ADR-046-provider-clipboard-wayland", - "to": "ADR-046-telemetry-audit-and-support", - "type": "spec-depends-on" + "from" : "ADR046-clipboard-003", + "to" : "ADR-046-provider-clipboard-wayland", + "type" : "implements-spec" }, { - "from": "ADR-046-provider-credential-entra", - "to": "ADR-046-componentsession-and-bus", - "type": "spec-depends-on" + "from" : "ADR046-clipboard-004", + "to" : "ADR-046-provider-clipboard-wayland", + "type" : "implements-spec" }, { - "from": "ADR-046-provider-credential-entra", - "to": "ADR-046-nix-configuration", - "type": "spec-depends-on" + "from" : "ADR046-clipboard-005", + "to" : "ADR-046-provider-clipboard-wayland", + "type" : "implements-spec" }, { - "from": "ADR-046-provider-credential-entra", - "to": "ADR-046-provider-model-and-packaging", - "type": "spec-depends-on" + "from" : "ADR046-clipboard-006", + "to" : "ADR-046-provider-clipboard-wayland", + "type" : "implements-spec" }, { - "from": "ADR-046-provider-credential-entra", - "to": "ADR-046-resource-reconciliation", - "type": "spec-depends-on" + "from" : "ADR046-clipboard-007", + "to" : "ADR-046-provider-clipboard-wayland", + "type" : "implements-spec" }, { - "from": "ADR-046-provider-credential-entra", - "to": "ADR-046-resources-credential", - "type": "spec-depends-on" + "from" : "ADR046-clipboard-008", + "to" : "ADR-046-provider-clipboard-wayland", + "type" : "implements-spec" }, { - "from": "ADR-046-provider-credential-entra", - "to": "ADR-046-resources-host-guest-process-user", - "type": "spec-depends-on" + "from" : "ADR046-clipboard-009", + "to" : "ADR-046-provider-clipboard-wayland", + "type" : "implements-spec" }, { - "from": "ADR-046-provider-credential-entra", - "to": "ADR-046-telemetry-audit-and-support", - "type": "spec-depends-on" + "from" : "ADR046-clipboard-010", + "to" : "ADR-046-provider-clipboard-wayland", + "type" : "implements-spec" }, { - "from": "ADR-046-provider-credential-managed-identity", - "to": "ADR-046-componentsession-and-bus", - "type": "spec-depends-on" + "from" : "ADR046-clipboard-011", + "to" : "ADR-046-provider-clipboard-wayland", + "type" : "implements-spec" }, { - "from": "ADR-046-provider-credential-managed-identity", - "to": "ADR-046-nix-configuration", - "type": "spec-depends-on" + "from" : "ADR046-clipboard-012", + "to" : "ADR-046-provider-clipboard-wayland", + "type" : "implements-spec" }, { - "from": "ADR-046-provider-credential-managed-identity", - "to": "ADR-046-provider-model-and-packaging", - "type": "spec-depends-on" + "from" : "ADR046-core-001", + "to" : "ADR-046-core-controllers", + "type" : "implements-spec" }, { - "from": "ADR-046-provider-credential-managed-identity", - "to": "ADR-046-resource-reconciliation", - "type": "spec-depends-on" + "from" : "ADR046-core-002", + "to" : "ADR-046-core-controllers", + "type" : "implements-spec" }, { - "from": "ADR-046-provider-credential-managed-identity", - "to": "ADR-046-resources-credential", - "type": "spec-depends-on" + "from" : "ADR046-cred-entra-001", + "to" : "ADR-046-provider-credential-entra", + "type" : "implements-spec" }, { - "from": "ADR-046-provider-credential-managed-identity", - "to": "ADR-046-telemetry-audit-and-support", - "type": "spec-depends-on" + "from" : "ADR046-cred-mi-001", + "to" : "ADR-046-provider-credential-managed-identity", + "type" : "implements-spec" }, { - "from": "ADR-046-provider-credential-secret-service", - "to": "ADR-046-components-processes-and-sandbox", - "type": "spec-depends-on" + "from" : "ADR046-cred-mi-002", + "to" : "ADR-046-provider-credential-managed-identity", + "type" : "implements-spec" }, { - "from": "ADR-046-provider-credential-secret-service", - "to": "ADR-046-componentsession-and-bus", - "type": "spec-depends-on" + "from" : "ADR046-cred-mi-003", + "to" : "ADR-046-provider-credential-managed-identity", + "type" : "implements-spec" }, { - "from": "ADR-046-provider-credential-secret-service", - "to": "ADR-046-nix-configuration", - "type": "spec-depends-on" + "from" : "ADR046-cred-mi-004", + "to" : "ADR-046-provider-credential-managed-identity", + "type" : "implements-spec" }, { - "from": "ADR-046-provider-credential-secret-service", - "to": "ADR-046-provider-model-and-packaging", - "type": "spec-depends-on" + "from" : "ADR046-cred-mi-005", + "to" : "ADR-046-provider-credential-managed-identity", + "type" : "implements-spec" }, { - "from": "ADR-046-provider-credential-secret-service", - "to": "ADR-046-resource-api-and-authorization", - "type": "spec-depends-on" + "from" : "ADR046-cred-ss-001", + "to" : "ADR-046-provider-credential-secret-service", + "type" : "implements-spec" }, { - "from": "ADR-046-provider-credential-secret-service", - "to": "ADR-046-resource-reconciliation", - "type": "spec-depends-on" + "from" : "ADR046-cred-ss-002", + "to" : "ADR-046-provider-credential-secret-service", + "type" : "implements-spec" }, { - "from": "ADR-046-provider-credential-secret-service", - "to": "ADR-046-resources-credential", - "type": "spec-depends-on" + "from" : "ADR046-cred-ss-003", + "to" : "ADR-046-provider-credential-secret-service", + "type" : "implements-spec" }, { - "from": "ADR-046-provider-credential-secret-service", - "to": "ADR-046-telemetry-audit-and-support", - "type": "spec-depends-on" + "from" : "ADR046-cred-ss-004", + "to" : "ADR-046-provider-credential-secret-service", + "type" : "implements-spec" }, { - "from": "ADR-046-provider-device-gpu", - "to": "ADR-046-components-processes-and-sandbox", - "type": "spec-depends-on" + "from" : "ADR046-cred-ss-005", + "to" : "ADR-046-provider-credential-secret-service", + "type" : "implements-spec" }, { - "from": "ADR-046-provider-device-gpu", - "to": "ADR-046-primitive-resource-composition", - "type": "spec-depends-on" + "from" : "ADR046-cred-ss-006", + "to" : "ADR-046-provider-credential-secret-service", + "type" : "implements-spec" }, { - "from": "ADR-046-provider-device-gpu", - "to": "ADR-046-provider-model-and-packaging", - "type": "spec-depends-on" + "from" : "ADR046-credential-001", + "to" : "ADR-046-resources-credential", + "type" : "implements-spec" }, { - "from": "ADR-046-provider-device-gpu", - "to": "ADR-046-resource-api-and-authorization", - "type": "spec-depends-on" + "from" : "ADR046-credential-002", + "to" : "ADR-046-resources-credential", + "type" : "implements-spec" }, { - "from": "ADR-046-provider-device-gpu", - "to": "ADR-046-resource-object-model", - "type": "spec-depends-on" + "from" : "ADR046-credential-003", + "to" : "ADR-046-resources-credential", + "type" : "implements-spec" }, { - "from": "ADR-046-provider-device-gpu", - "to": "ADR-046-resource-reconciliation", - "type": "spec-depends-on" + "from" : "ADR046-credential-004", + "to" : "ADR-046-resources-credential", + "type" : "implements-spec" }, { - "from": "ADR-046-provider-device-gpu", - "to": "ADR-046-resources-device", - "type": "spec-depends-on" + "from" : "ADR046-credential-005", + "to" : "ADR-046-resources-credential", + "type" : "implements-spec" }, { - "from": "ADR-046-provider-device-gpu", - "to": "ADR-046-telemetry-audit-and-support", - "type": "spec-depends-on" + "from" : "ADR046-credential-006", + "to" : "ADR-046-resources-credential", + "type" : "implements-spec" }, { - "from": "ADR-046-provider-device-security-key", - "to": "ADR-046-components-processes-and-sandbox", - "type": "spec-depends-on" + "from" : "ADR046-credential-007", + "to" : "ADR-046-resources-credential", + "type" : "implements-spec" }, { - "from": "ADR-046-provider-device-security-key", - "to": "ADR-046-componentsession-and-bus", - "type": "spec-depends-on" + "from" : "ADR046-credential-008", + "to" : "ADR-046-resources-credential", + "type" : "implements-spec" }, { - "from": "ADR-046-provider-device-security-key", - "to": "ADR-046-nix-configuration", - "type": "spec-depends-on" + "from" : "ADR046-decisions-001", + "to" : "ADR-046-decision-register", + "type" : "implements-spec" }, { - "from": "ADR-046-provider-device-security-key", - "to": "ADR-046-primitive-resource-composition", - "type": "spec-depends-on" + "from" : "ADR046-delivery-001", + "to" : "ADR-046-validation-and-delivery", + "type" : "implements-spec" }, { - "from": "ADR-046-provider-device-security-key", - "to": "ADR-046-provider-model-and-packaging", - "type": "spec-depends-on" + "from" : "ADR046-delivery-002", + "to" : "ADR-046-validation-and-delivery", + "type" : "implements-spec" }, { - "from": "ADR-046-provider-device-security-key", - "to": "ADR-046-provider-state", - "type": "spec-depends-on" + "from" : "ADR046-delivery-003", + "to" : "ADR-046-validation-and-delivery", + "type" : "implements-spec" }, { - "from": "ADR-046-provider-device-security-key", - "to": "ADR-046-resource-api-and-authorization", - "type": "spec-depends-on" + "from" : "ADR046-delivery-004", + "to" : "ADR-046-validation-and-delivery", + "type" : "implements-spec" }, { - "from": "ADR-046-provider-device-security-key", - "to": "ADR-046-resource-object-model", - "type": "spec-depends-on" + "from" : "ADR046-delivery-005", + "to" : "ADR-046-validation-and-delivery", + "type" : "implements-spec" }, { - "from": "ADR-046-provider-device-security-key", - "to": "ADR-046-resource-reconciliation", - "type": "spec-depends-on" + "from" : "ADR046-delivery-006", + "to" : "ADR-046-validation-and-delivery", + "type" : "implements-spec" }, { - "from": "ADR-046-provider-device-security-key", - "to": "ADR-046-resources-device", - "type": "spec-depends-on" + "from" : "ADR046-delivery-007", + "to" : "ADR-046-validation-and-delivery", + "type" : "implements-spec" }, { - "from": "ADR-046-provider-device-security-key", - "to": "ADR-046-telemetry-audit-and-support", - "type": "spec-depends-on" + "from" : "ADR046-delivery-008", + "to" : "ADR-046-validation-and-delivery", + "type" : "implements-spec" }, { - "from": "ADR-046-provider-device-tpm", - "to": "ADR-046-primitive-resource-composition", - "type": "spec-depends-on" + "from" : "ADR046-delivery-009", + "to" : "ADR-046-validation-and-delivery", + "type" : "implements-spec" }, { - "from": "ADR-046-provider-device-tpm", - "to": "ADR-046-provider-model-and-packaging", - "type": "spec-depends-on" + "from" : "ADR046-device-001", + "to" : "ADR-046-resources-device", + "type" : "implements-spec" }, { - "from": "ADR-046-provider-device-tpm", - "to": "ADR-046-provider-state", - "type": "spec-depends-on" + "from" : "ADR046-device-002", + "to" : "ADR-046-resources-device", + "type" : "implements-spec" }, { - "from": "ADR-046-provider-device-tpm", - "to": "ADR-046-resource-object-model", - "type": "spec-depends-on" + "from" : "ADR046-device-003", + "to" : "ADR-046-resources-device", + "type" : "implements-spec" }, { - "from": "ADR-046-provider-device-tpm", - "to": "ADR-046-resource-reconciliation", - "type": "spec-depends-on" + "from" : "ADR046-device-004", + "to" : "ADR-046-resources-device", + "type" : "implements-spec" }, { - "from": "ADR-046-provider-device-tpm", - "to": "ADR-046-resources-device", - "type": "spec-depends-on" + "from" : "ADR046-device-005", + "to" : "ADR-046-resources-device", + "type" : "implements-spec" }, { - "from": "ADR-046-provider-device-tpm", - "to": "ADR-046-resources-host-guest-process-user", - "type": "spec-depends-on" + "from" : "ADR046-device-006", + "to" : "ADR-046-resources-device", + "type" : "implements-spec" }, { - "from": "ADR-046-provider-device-tpm", - "to": "ADR-046-resources-volume", - "type": "spec-depends-on" + "from" : "ADR046-device-007", + "to" : "ADR-046-resources-device", + "type" : "implements-spec" }, { - "from": "ADR-046-provider-device-usbip", - "to": "ADR-046-components-processes-and-sandbox", - "type": "spec-depends-on" + "from" : "ADR046-device-008", + "to" : "ADR-046-resources-device", + "type" : "implements-spec" }, { - "from": "ADR-046-provider-device-usbip", - "to": "ADR-046-componentsession-and-bus", - "type": "spec-depends-on" + "from" : "ADR046-device-tpm-001", + "to" : "ADR-046-provider-device-tpm", + "type" : "implements-spec" }, { - "from": "ADR-046-provider-device-usbip", - "to": "ADR-046-nix-configuration", - "type": "spec-depends-on" + "from" : "ADR046-device-tpm-002", + "to" : "ADR-046-provider-device-tpm", + "type" : "implements-spec" }, { - "from": "ADR-046-provider-device-usbip", - "to": "ADR-046-provider-model-and-packaging", - "type": "spec-depends-on" + "from" : "ADR046-device-tpm-003", + "to" : "ADR-046-provider-device-tpm", + "type" : "implements-spec" }, { - "from": "ADR-046-provider-device-usbip", - "to": "ADR-046-provider-state", - "type": "spec-depends-on" + "from" : "ADR046-device-tpm-004", + "to" : "ADR-046-provider-device-tpm", + "type" : "implements-spec" }, { - "from": "ADR-046-provider-device-usbip", - "to": "ADR-046-resource-api-and-authorization", - "type": "spec-depends-on" + "from" : "ADR046-device-tpm-005", + "to" : "ADR-046-provider-device-tpm", + "type" : "implements-spec" }, { - "from": "ADR-046-provider-device-usbip", - "to": "ADR-046-resource-reconciliation", - "type": "spec-depends-on" + "from" : "ADR046-device-tpm-006", + "to" : "ADR-046-provider-device-tpm", + "type" : "implements-spec" }, { - "from": "ADR-046-provider-device-usbip", - "to": "ADR-046-resources-device", - "type": "spec-depends-on" + "from" : "ADR046-device-tpm-007", + "to" : "ADR-046-provider-device-tpm", + "type" : "implements-spec" }, { - "from": "ADR-046-provider-device-usbip", - "to": "ADR-046-resources-network", - "type": "spec-depends-on" + "from" : "ADR046-device-tpm-008", + "to" : "ADR-046-provider-device-tpm", + "type" : "implements-spec" }, { - "from": "ADR-046-provider-device-usbip", - "to": "ADR-046-resources-zone-control", - "type": "spec-depends-on" + "from" : "ADR046-device-tpm-009", + "to" : "ADR-046-provider-device-tpm", + "type" : "implements-spec" }, { - "from": "ADR-046-provider-device-usbip", - "to": "ADR-046-telemetry-audit-and-support", - "type": "spec-depends-on" + "from" : "ADR046-device-tpm-010", + "to" : "ADR-046-provider-device-tpm", + "type" : "implements-spec" }, { - "from": "ADR-046-provider-device-usbip", - "to": "ADR-046-zone-routing", - "type": "spec-depends-on" + "from" : "ADR046-device-tpm-011", + "to" : "ADR-046-provider-device-tpm", + "type" : "implements-spec" }, { - "from": "ADR-046-provider-display-wayland", - "to": "ADR-046-components-processes-and-sandbox", - "type": "spec-depends-on" + "from" : "ADR046-device-tpm-012", + "to" : "ADR-046-provider-device-tpm", + "type" : "implements-spec" }, { - "from": "ADR-046-provider-display-wayland", - "to": "ADR-046-componentsession-and-bus", - "type": "spec-depends-on" + "from" : "ADR046-device-tpm-013", + "to" : "ADR-046-provider-device-tpm", + "type" : "implements-spec" }, { - "from": "ADR-046-provider-display-wayland", - "to": "ADR-046-nix-configuration", - "type": "spec-depends-on" + "from" : "ADR046-display-001", + "to" : "ADR-046-provider-display-wayland", + "type" : "implements-spec" }, { - "from": "ADR-046-provider-display-wayland", - "to": "ADR-046-provider-model-and-packaging", - "type": "spec-depends-on" + "from" : "ADR046-display-002", + "to" : "ADR-046-provider-display-wayland", + "type" : "implements-spec" }, { - "from": "ADR-046-provider-display-wayland", - "to": "ADR-046-resource-api-and-authorization", - "type": "spec-depends-on" + "from" : "ADR046-display-003", + "to" : "ADR-046-provider-display-wayland", + "type" : "implements-spec" }, { - "from": "ADR-046-provider-display-wayland", - "to": "ADR-046-resource-reconciliation", - "type": "spec-depends-on" + "from" : "ADR046-display-004", + "to" : "ADR-046-provider-display-wayland", + "type" : "implements-spec" }, { - "from": "ADR-046-provider-display-wayland", - "to": "ADR-046-resources-device", - "type": "spec-depends-on" + "from" : "ADR046-exec-001", + "to" : "ADR-046-resources-host-guest-process-user", + "type" : "implements-spec" }, { - "from": "ADR-046-provider-display-wayland", - "to": "ADR-046-resources-host-guest-process-user", - "type": "spec-depends-on" + "from" : "ADR046-exec-002", + "to" : "ADR-046-resources-host-guest-process-user", + "type" : "implements-spec" }, { - "from": "ADR-046-provider-display-wayland", - "to": "ADR-046-resources-volume", - "type": "spec-depends-on" + "from" : "ADR046-exec-003", + "to" : "ADR-046-resources-host-guest-process-user", + "type" : "implements-spec" }, { - "from": "ADR-046-provider-display-wayland", - "to": "ADR-046-telemetry-audit-and-support", - "type": "spec-depends-on" + "from" : "ADR046-exec-004", + "to" : "ADR-046-resources-host-guest-process-user", + "type" : "implements-spec" }, { - "from": "ADR-046-provider-model-and-packaging", - "to": "ADR-046-primitive-resource-composition", - "type": "spec-depends-on" + "from" : "ADR046-exec-005", + "to" : "ADR-046-resources-host-guest-process-user", + "type" : "implements-spec" }, { - "from": "ADR-046-provider-model-and-packaging", - "to": "ADR-046-resource-api-and-authorization", - "type": "spec-depends-on" + "from" : "ADR046-exec-006", + "to" : "ADR-046-resources-host-guest-process-user", + "type" : "implements-spec" }, { - "from": "ADR-046-provider-model-and-packaging", - "to": "ADR-046-resource-object-model", - "type": "spec-depends-on" + "from" : "ADR046-exec-007", + "to" : "ADR-046-resources-host-guest-process-user", + "type" : "implements-spec" }, { - "from": "ADR-046-provider-model-and-packaging", - "to": "ADR-046-resource-reconciliation", - "type": "spec-depends-on" + "from" : "ADR046-exec-008", + "to" : "ADR-046-resources-host-guest-process-user", + "type" : "implements-spec" }, { - "from": "ADR-046-provider-network-local", - "to": "ADR-046-componentsession-and-bus", - "type": "spec-depends-on" + "from" : "ADR046-exec-009", + "to" : "ADR-046-resources-host-guest-process-user", + "type" : "implements-spec" }, { - "from": "ADR-046-provider-network-local", - "to": "ADR-046-current-code-migration-map", - "type": "spec-depends-on" + "from" : "ADR046-exec-010", + "to" : "ADR-046-resources-host-guest-process-user", + "type" : "implements-spec" }, { - "from": "ADR-046-provider-network-local", - "to": "ADR-046-nix-configuration", - "type": "spec-depends-on" + "from" : "ADR046-exec-011", + "to" : "ADR-046-resources-host-guest-process-user", + "type" : "implements-spec" }, { - "from": "ADR-046-provider-network-local", - "to": "ADR-046-provider-model-and-packaging", - "type": "spec-depends-on" + "from" : "ADR046-exec-012", + "to" : "ADR-046-resources-host-guest-process-user", + "type" : "implements-spec" }, { - "from": "ADR-046-provider-network-local", - "to": "ADR-046-resource-reconciliation", - "type": "spec-depends-on" + "from" : "ADR046-exec-013", + "to" : "ADR-046-resources-host-guest-process-user", + "type" : "implements-spec" }, { - "from": "ADR-046-provider-network-local", - "to": "ADR-046-resources-host-guest-process-user", - "type": "spec-depends-on" + "from" : "ADR046-exec-014", + "to" : "ADR-046-resources-host-guest-process-user", + "type" : "implements-spec" }, { - "from": "ADR-046-provider-network-local", - "to": "ADR-046-resources-network", - "type": "spec-depends-on" + "from" : "ADR046-exec-015", + "to" : "ADR-046-resources-host-guest-process-user", + "type" : "implements-spec" }, { - "from": "ADR-046-provider-network-local", - "to": "ADR-046-resources-volume", - "type": "spec-depends-on" + "from" : "ADR046-exec-016", + "to" : "ADR-046-resources-host-guest-process-user", + "type" : "implements-spec" }, { - "from": "ADR-046-provider-network-local", - "to": "ADR-046-telemetry-audit-and-support", - "type": "spec-depends-on" + "from" : "ADR046-exec-017", + "to" : "ADR-046-resources-host-guest-process-user", + "type" : "implements-spec" }, { - "from": "ADR-046-provider-notification-desktop", - "to": "ADR-046-componentsession-and-bus", - "type": "spec-depends-on" + "from" : "ADR046-exec-018", + "to" : "ADR-046-resources-host-guest-process-user", + "type" : "implements-spec" }, { - "from": "ADR-046-provider-notification-desktop", - "to": "ADR-046-nix-configuration", - "type": "spec-depends-on" + "from" : "ADR046-exec-019", + "to" : "ADR-046-resources-host-guest-process-user", + "type" : "implements-spec" }, { - "from": "ADR-046-provider-notification-desktop", - "to": "ADR-046-primitive-resource-composition", - "type": "spec-depends-on" + "from" : "ADR046-exec-020", + "to" : "ADR-046-resources-host-guest-process-user", + "type" : "implements-spec" }, { - "from": "ADR-046-provider-notification-desktop", - "to": "ADR-046-provider-model-and-packaging", - "type": "spec-depends-on" + "from" : "ADR046-exec-021", + "to" : "ADR-046-resources-host-guest-process-user", + "type" : "implements-spec" }, { - "from": "ADR-046-provider-notification-desktop", - "to": "ADR-046-provider-state", - "type": "spec-depends-on" + "from" : "ADR046-exec-022", + "to" : "ADR-046-resources-host-guest-process-user", + "type" : "implements-spec" }, { - "from": "ADR-046-provider-notification-desktop", - "to": "ADR-046-resource-reconciliation", - "type": "spec-depends-on" + "from" : "ADR046-exec-023", + "to" : "ADR-046-resources-host-guest-process-user", + "type" : "implements-spec" }, { - "from": "ADR-046-provider-notification-desktop", - "to": "ADR-046-telemetry-audit-and-support", - "type": "spec-depends-on" + "from" : "ADR046-exec-024", + "to" : "ADR-046-resources-host-guest-process-user", + "type" : "implements-spec" }, { - "from": "ADR-046-provider-observability-otel", - "to": "ADR-046-components-processes-and-sandbox", - "type": "spec-depends-on" + "from" : "ADR046-feasibility-001", + "to" : "ADR-046-feasibility-and-spikes", + "type" : "implements-spec" }, { - "from": "ADR-046-provider-observability-otel", - "to": "ADR-046-componentsession-and-bus", - "type": "spec-depends-on" + "from" : "ADR046-feasibility-002", + "to" : "ADR-046-feasibility-and-spikes", + "type" : "implements-spec" }, { - "from": "ADR-046-provider-observability-otel", - "to": "ADR-046-nix-configuration", - "type": "spec-depends-on" + "from" : "ADR046-feasibility-003", + "to" : "ADR-046-feasibility-and-spikes", + "type" : "implements-spec" }, { - "from": "ADR-046-provider-observability-otel", - "to": "ADR-046-primitive-resource-composition", - "type": "spec-depends-on" + "from" : "ADR046-feasibility-004", + "to" : "ADR-046-feasibility-and-spikes", + "type" : "implements-spec" }, { - "from": "ADR-046-provider-observability-otel", - "to": "ADR-046-provider-model-and-packaging", - "type": "spec-depends-on" + "from" : "ADR046-feasibility-005", + "to" : "ADR-046-feasibility-and-spikes", + "type" : "implements-spec" }, { - "from": "ADR-046-provider-observability-otel", - "to": "ADR-046-resource-api-and-authorization", - "type": "spec-depends-on" + "from" : "ADR046-feasibility-006", + "to" : "ADR-046-feasibility-and-spikes", + "type" : "implements-spec" }, { - "from": "ADR-046-provider-observability-otel", - "to": "ADR-046-resource-object-model", - "type": "spec-depends-on" + "from" : "ADR046-feasibility-007", + "to" : "ADR-046-feasibility-and-spikes", + "type" : "implements-spec" }, { - "from": "ADR-046-provider-observability-otel", - "to": "ADR-046-resource-reconciliation", - "type": "spec-depends-on" + "from" : "ADR046-feasibility-008", + "to" : "ADR-046-feasibility-and-spikes", + "type" : "implements-spec" }, { - "from": "ADR-046-provider-observability-otel", - "to": "ADR-046-resources-credential", - "type": "spec-depends-on" + "from" : "ADR046-feasibility-009", + "to" : "ADR-046-feasibility-and-spikes", + "type" : "implements-spec" }, { - "from": "ADR-046-provider-observability-otel", - "to": "ADR-046-resources-volume", - "type": "spec-depends-on" + "from" : "ADR046-feasibility-010", + "to" : "ADR-046-feasibility-and-spikes", + "type" : "implements-spec" }, { - "from": "ADR-046-provider-observability-otel", - "to": "ADR-046-telemetry-audit-and-support", - "type": "spec-depends-on" + "from" : "ADR046-feasibility-011", + "to" : "ADR-046-feasibility-and-spikes", + "type" : "implements-spec" }, { - "from": "ADR-046-provider-observability-otel", - "to": "ADR-046-terminology-and-identities", - "type": "spec-depends-on" + "from" : "ADR046-gpu-001", + "to" : "ADR-046-provider-device-gpu", + "type" : "implements-spec" }, { - "from": "ADR-046-provider-runtime-azure-container-apps", - "to": "ADR-046-componentsession-and-bus", - "type": "spec-depends-on" + "from" : "ADR046-gpu-002", + "to" : "ADR-046-provider-device-gpu", + "type" : "implements-spec" }, { - "from": "ADR-046-provider-runtime-azure-container-apps", - "to": "ADR-046-nix-configuration", - "type": "spec-depends-on" + "from" : "ADR046-gpu-003", + "to" : "ADR-046-provider-device-gpu", + "type" : "implements-spec" }, { - "from": "ADR-046-provider-runtime-azure-container-apps", - "to": "ADR-046-provider-model-and-packaging", - "type": "spec-depends-on" + "from" : "ADR046-gpu-004", + "to" : "ADR-046-provider-device-gpu", + "type" : "implements-spec" }, { - "from": "ADR-046-provider-runtime-azure-container-apps", - "to": "ADR-046-provider-state", - "type": "spec-depends-on" + "from" : "ADR046-gpu-005", + "to" : "ADR-046-provider-device-gpu", + "type" : "implements-spec" }, { - "from": "ADR-046-provider-runtime-azure-container-apps", - "to": "ADR-046-resources-credential", - "type": "spec-depends-on" + "from" : "ADR046-gpu-006", + "to" : "ADR-046-provider-device-gpu", + "type" : "implements-spec" }, { - "from": "ADR-046-provider-runtime-azure-container-apps", - "to": "ADR-046-resources-host-guest-process-user", - "type": "spec-depends-on" + "from" : "ADR046-gpu-007", + "to" : "ADR-046-provider-device-gpu", + "type" : "implements-spec" }, { - "from": "ADR-046-provider-runtime-azure-container-apps", - "to": "ADR-046-telemetry-audit-and-support", - "type": "spec-depends-on" + "from" : "ADR046-gpu-008", + "to" : "ADR-046-provider-device-gpu", + "type" : "implements-spec" }, { - "from": "ADR-046-provider-runtime-azure-container-apps", - "to": "ADR-046-zone-routing", - "type": "spec-depends-on" + "from" : "ADR046-gpu-009", + "to" : "ADR-046-provider-device-gpu", + "type" : "implements-spec" }, { - "from": "ADR-046-provider-runtime-azure-virtual-machine", - "to": "ADR-046-components-processes-and-sandbox", - "type": "spec-depends-on" + "from" : "ADR046-identities-001", + "to" : "ADR-046-terminology-and-identities", + "type" : "implements-spec" }, { - "from": "ADR-046-provider-runtime-azure-virtual-machine", - "to": "ADR-046-componentsession-and-bus", - "type": "spec-depends-on" + "from" : "ADR046-identities-002", + "to" : "ADR-046-terminology-and-identities", + "type" : "implements-spec" }, { - "from": "ADR-046-provider-runtime-azure-virtual-machine", - "to": "ADR-046-nix-configuration", - "type": "spec-depends-on" + "from" : "ADR046-minijail-001", + "to" : "ADR-046-provider-system-minijail", + "type" : "implements-spec" }, { - "from": "ADR-046-provider-runtime-azure-virtual-machine", - "to": "ADR-046-primitive-resource-composition", - "type": "spec-depends-on" + "from" : "ADR046-minijail-002", + "to" : "ADR-046-provider-system-minijail", + "type" : "implements-spec" }, { - "from": "ADR-046-provider-runtime-azure-virtual-machine", - "to": "ADR-046-provider-model-and-packaging", - "type": "spec-depends-on" + "from" : "ADR046-minijail-003", + "to" : "ADR-046-provider-system-minijail", + "type" : "implements-spec" }, { - "from": "ADR-046-provider-runtime-azure-virtual-machine", - "to": "ADR-046-provider-state", - "type": "spec-depends-on" + "from" : "ADR046-minijail-004", + "to" : "ADR-046-provider-system-minijail", + "type" : "implements-spec" }, { - "from": "ADR-046-provider-runtime-azure-virtual-machine", - "to": "ADR-046-resource-api-and-authorization", - "type": "spec-depends-on" + "from" : "ADR046-minijail-005", + "to" : "ADR-046-provider-system-minijail", + "type" : "implements-spec" }, { - "from": "ADR-046-provider-runtime-azure-virtual-machine", - "to": "ADR-046-resource-object-model", - "type": "spec-depends-on" + "from" : "ADR046-minijail-006", + "to" : "ADR-046-provider-system-minijail", + "type" : "implements-spec" }, { - "from": "ADR-046-provider-runtime-azure-virtual-machine", - "to": "ADR-046-resource-reconciliation", - "type": "spec-depends-on" + "from" : "ADR046-network-001", + "to" : "ADR-046-resources-network", + "type" : "implements-spec" }, { - "from": "ADR-046-provider-runtime-azure-virtual-machine", - "to": "ADR-046-resources-credential", - "type": "spec-depends-on" + "from" : "ADR046-network-002", + "to" : "ADR-046-resources-network", + "type" : "implements-spec" }, { - "from": "ADR-046-provider-runtime-azure-virtual-machine", - "to": "ADR-046-resources-host-guest-process-user", - "type": "spec-depends-on" + "from" : "ADR046-network-003", + "to" : "ADR-046-resources-network", + "type" : "implements-spec" }, { - "from": "ADR-046-provider-runtime-azure-virtual-machine", - "to": "ADR-046-resources-network", - "type": "spec-depends-on" + "from" : "ADR046-network-004", + "to" : "ADR-046-resources-network", + "type" : "implements-spec" }, { - "from": "ADR-046-provider-runtime-azure-virtual-machine", - "to": "ADR-046-telemetry-audit-and-support", - "type": "spec-depends-on" + "from" : "ADR046-network-005", + "to" : "ADR-046-resources-network", + "type" : "implements-spec" }, { - "from": "ADR-046-provider-runtime-azure-virtual-machine", - "to": "ADR-046-terminology-and-identities", - "type": "spec-depends-on" + "from" : "ADR046-network-006", + "to" : "ADR-046-resources-network", + "type" : "implements-spec" }, { - "from": "ADR-046-provider-runtime-azure-virtual-machine", - "to": "ADR-046-zone-routing", - "type": "spec-depends-on" + "from" : "ADR046-network-007", + "to" : "ADR-046-resources-network", + "type" : "implements-spec" }, { - "from": "ADR-046-provider-runtime-cloud-hypervisor", - "to": "ADR-046-components-processes-and-sandbox", - "type": "spec-depends-on" + "from" : "ADR046-network-008", + "to" : "ADR-046-resources-network", + "type" : "implements-spec" }, { - "from": "ADR-046-provider-runtime-cloud-hypervisor", - "to": "ADR-046-componentsession-and-bus", - "type": "spec-depends-on" + "from" : "ADR046-network-009", + "to" : "ADR-046-resources-network", + "type" : "implements-spec" }, { - "from": "ADR-046-provider-runtime-cloud-hypervisor", - "to": "ADR-046-nix-configuration", - "type": "spec-depends-on" + "from" : "ADR046-nix-001", + "to" : "ADR-046-nix-configuration", + "type" : "implements-spec" }, { - "from": "ADR-046-provider-runtime-cloud-hypervisor", - "to": "ADR-046-primitive-resource-composition", - "type": "spec-depends-on" + "from" : "ADR046-nix-002", + "to" : "ADR-046-nix-configuration", + "type" : "implements-spec" }, { - "from": "ADR-046-provider-runtime-cloud-hypervisor", - "to": "ADR-046-provider-model-and-packaging", - "type": "spec-depends-on" + "from" : "ADR046-nix-003", + "to" : "ADR-046-nix-configuration", + "type" : "implements-spec" }, { - "from": "ADR-046-provider-runtime-cloud-hypervisor", - "to": "ADR-046-provider-state", - "type": "spec-depends-on" + "from" : "ADR046-nix-004", + "to" : "ADR-046-nix-configuration", + "type" : "implements-spec" }, { - "from": "ADR-046-provider-runtime-cloud-hypervisor", - "to": "ADR-046-resource-object-model", - "type": "spec-depends-on" + "from" : "ADR046-nix-005", + "to" : "ADR-046-nix-configuration", + "type" : "implements-spec" }, { - "from": "ADR-046-provider-runtime-cloud-hypervisor", - "to": "ADR-046-resource-reconciliation", - "type": "spec-depends-on" + "from" : "ADR046-nix-006", + "to" : "ADR-046-nix-configuration", + "type" : "implements-spec" }, { - "from": "ADR-046-provider-runtime-cloud-hypervisor", - "to": "ADR-046-resources-device", - "type": "spec-depends-on" + "from" : "ADR046-nix-007", + "to" : "ADR-046-nix-configuration", + "type" : "implements-spec" }, { - "from": "ADR-046-provider-runtime-cloud-hypervisor", - "to": "ADR-046-resources-host-guest-process-user", - "type": "spec-depends-on" + "from" : "ADR046-nix-008", + "to" : "ADR-046-nix-configuration", + "type" : "implements-spec" }, { - "from": "ADR-046-provider-runtime-cloud-hypervisor", - "to": "ADR-046-resources-network", - "type": "spec-depends-on" + "from" : "ADR046-nix-009", + "to" : "ADR-046-nix-configuration", + "type" : "implements-spec" }, { - "from": "ADR-046-provider-runtime-cloud-hypervisor", - "to": "ADR-046-resources-volume", - "type": "spec-depends-on" + "from" : "ADR046-nix-010", + "to" : "ADR-046-nix-configuration", + "type" : "implements-spec" }, { - "from": "ADR-046-provider-runtime-cloud-hypervisor", - "to": "ADR-046-telemetry-audit-and-support", - "type": "spec-depends-on" + "from" : "ADR046-nix-011", + "to" : "ADR-046-nix-configuration", + "type" : "implements-spec" }, { - "from": "ADR-046-provider-runtime-qemu-media", - "to": "ADR-046-components-processes-and-sandbox", - "type": "spec-depends-on" + "from" : "ADR046-nix-012", + "to" : "ADR-046-nix-configuration", + "type" : "implements-spec" }, { - "from": "ADR-046-provider-runtime-qemu-media", - "to": "ADR-046-componentsession-and-bus", - "type": "spec-depends-on" + "from" : "ADR046-nix-013", + "to" : "ADR-046-nix-configuration", + "type" : "implements-spec" }, { - "from": "ADR-046-provider-runtime-qemu-media", - "to": "ADR-046-current-code-migration-map", - "type": "spec-depends-on" + "from" : "ADR046-nix-014", + "to" : "ADR-046-nix-configuration", + "type" : "implements-spec" }, { - "from": "ADR-046-provider-runtime-qemu-media", - "to": "ADR-046-nix-configuration", - "type": "spec-depends-on" + "from" : "ADR046-nix-015", + "to" : "ADR-046-nix-configuration", + "type" : "implements-spec" }, { - "from": "ADR-046-provider-runtime-qemu-media", - "to": "ADR-046-provider-model-and-packaging", - "type": "spec-depends-on" + "from" : "ADR046-nix-016", + "to" : "ADR-046-nix-configuration", + "type" : "implements-spec" }, { - "from": "ADR-046-provider-runtime-qemu-media", - "to": "ADR-046-resource-reconciliation", - "type": "spec-depends-on" + "from" : "ADR046-nix-017", + "to" : "ADR-046-nix-configuration", + "type" : "implements-spec" }, { - "from": "ADR-046-provider-runtime-qemu-media", - "to": "ADR-046-resources-device", - "type": "spec-depends-on" + "from" : "ADR046-nix-018", + "to" : "ADR-046-nix-configuration", + "type" : "implements-spec" }, { - "from": "ADR-046-provider-runtime-qemu-media", - "to": "ADR-046-resources-host-guest-process-user", - "type": "spec-depends-on" + "from" : "ADR046-nix-019", + "to" : "ADR-046-nix-configuration", + "type" : "implements-spec" }, { - "from": "ADR-046-provider-runtime-qemu-media", - "to": "ADR-046-resources-network", - "type": "spec-depends-on" + "from" : "ADR046-nix-020", + "to" : "ADR-046-nix-configuration", + "type" : "implements-spec" }, { - "from": "ADR-046-provider-runtime-qemu-media", - "to": "ADR-046-resources-volume", - "type": "spec-depends-on" + "from" : "ADR046-nix-021", + "to" : "ADR-046-nix-configuration", + "type" : "implements-spec" }, { - "from": "ADR-046-provider-runtime-qemu-media", - "to": "ADR-046-telemetry-audit-and-support", - "type": "spec-depends-on" + "from" : "ADR046-nix-022", + "to" : "ADR-046-nix-configuration", + "type" : "implements-spec" }, { - "from": "ADR-046-provider-shell-terminal", - "to": "ADR-046-components-processes-and-sandbox", - "type": "spec-depends-on" + "from" : "ADR046-nix-023", + "to" : "ADR-046-nix-configuration", + "type" : "implements-spec" }, { - "from": "ADR-046-provider-shell-terminal", - "to": "ADR-046-componentsession-and-bus", - "type": "spec-depends-on" + "from" : "ADR046-nix-024", + "to" : "ADR-046-nix-configuration", + "type" : "implements-spec" }, { - "from": "ADR-046-provider-shell-terminal", - "to": "ADR-046-core-controllers", - "type": "spec-depends-on" + "from" : "ADR046-nix-025", + "to" : "ADR-046-nix-configuration", + "type" : "implements-spec" }, { - "from": "ADR-046-provider-shell-terminal", - "to": "ADR-046-decision-register", - "type": "spec-depends-on" + "from" : "ADR046-nix-026", + "to" : "ADR-046-nix-configuration", + "type" : "implements-spec" }, { - "from": "ADR-046-provider-shell-terminal", - "to": "ADR-046-nix-configuration", - "type": "spec-depends-on" + "from" : "ADR046-nix-027", + "to" : "ADR-046-nix-configuration", + "type" : "implements-spec" }, { - "from": "ADR-046-provider-shell-terminal", - "to": "ADR-046-primitive-resource-composition", - "type": "spec-depends-on" + "from" : "ADR046-nix-028", + "to" : "ADR-046-nix-configuration", + "type" : "implements-spec" }, { - "from": "ADR-046-provider-shell-terminal", - "to": "ADR-046-provider-model-and-packaging", - "type": "spec-depends-on" + "from" : "ADR046-nix-029", + "to" : "ADR-046-nix-configuration", + "type" : "implements-spec" }, { - "from": "ADR-046-provider-shell-terminal", - "to": "ADR-046-provider-state", - "type": "spec-depends-on" + "from" : "ADR046-nix-030", + "to" : "ADR-046-nix-configuration", + "type" : "implements-spec" }, { - "from": "ADR-046-provider-shell-terminal", - "to": "ADR-046-resource-api-and-authorization", - "type": "spec-depends-on" + "from" : "ADR046-nix-031", + "to" : "ADR-046-nix-configuration", + "type" : "implements-spec" }, { - "from": "ADR-046-provider-shell-terminal", - "to": "ADR-046-resource-object-model", - "type": "spec-depends-on" + "from" : "ADR046-nix-032", + "to" : "ADR-046-nix-configuration", + "type" : "implements-spec" }, { - "from": "ADR-046-provider-shell-terminal", - "to": "ADR-046-resource-reconciliation", - "type": "spec-depends-on" + "from" : "ADR046-nix-033", + "to" : "ADR-046-nix-configuration", + "type" : "implements-spec" }, { - "from": "ADR-046-provider-shell-terminal", - "to": "ADR-046-resources-credential", - "type": "spec-depends-on" + "from" : "ADR046-nix-034", + "to" : "ADR-046-nix-configuration", + "type" : "implements-spec" }, { - "from": "ADR-046-provider-shell-terminal", - "to": "ADR-046-resources-host-guest-process-user", - "type": "spec-depends-on" + "from" : "ADR046-nix-035", + "to" : "ADR-046-nix-configuration", + "type" : "implements-spec" }, { - "from": "ADR-046-provider-shell-terminal", - "to": "ADR-046-telemetry-audit-and-support", - "type": "spec-depends-on" + "from" : "ADR046-nl-001", + "to" : "ADR-046-provider-network-local", + "type" : "implements-spec" }, { - "from": "ADR-046-provider-shell-terminal", - "to": "ADR-046-terminology-and-identities", - "type": "spec-depends-on" + "from" : "ADR046-nl-002", + "to" : "ADR-046-provider-network-local", + "type" : "implements-spec" }, { - "from": "ADR-046-provider-shell-terminal", - "to": "ADR-046-zone-routing", - "type": "spec-depends-on" + "from" : "ADR046-nl-003", + "to" : "ADR-046-provider-network-local", + "type" : "implements-spec" }, { - "from": "ADR-046-provider-state", - "to": "ADR-046-componentsession-and-bus", - "type": "spec-depends-on" + "from" : "ADR046-nl-004", + "to" : "ADR-046-provider-network-local", + "type" : "implements-spec" }, { - "from": "ADR-046-provider-state", - "to": "ADR-046-primitive-resource-composition", - "type": "spec-depends-on" + "from" : "ADR046-nl-005", + "to" : "ADR-046-provider-network-local", + "type" : "implements-spec" }, { - "from": "ADR-046-provider-state", - "to": "ADR-046-provider-model-and-packaging", - "type": "spec-depends-on" + "from" : "ADR046-nl-006", + "to" : "ADR-046-provider-network-local", + "type" : "implements-spec" }, { - "from": "ADR-046-provider-state", - "to": "ADR-046-resource-object-model", - "type": "spec-depends-on" + "from" : "ADR046-nl-007", + "to" : "ADR-046-provider-network-local", + "type" : "implements-spec" }, { - "from": "ADR-046-provider-state", - "to": "ADR-046-resource-store-redb", - "type": "spec-depends-on" + "from" : "ADR046-nl-008", + "to" : "ADR-046-provider-network-local", + "type" : "implements-spec" }, { - "from": "ADR-046-provider-state", - "to": "ADR-046-terminology-and-identities", - "type": "spec-depends-on" + "from" : "ADR046-nl-009", + "to" : "ADR-046-provider-network-local", + "type" : "implements-spec" }, { - "from": "ADR-046-provider-system-core", - "to": "ADR-046-componentsession-and-bus", - "type": "spec-depends-on" + "from" : "ADR046-nl-010", + "to" : "ADR-046-provider-network-local", + "type" : "implements-spec" }, { - "from": "ADR-046-provider-system-core", - "to": "ADR-046-core-controllers", - "type": "spec-depends-on" + "from" : "ADR046-nl-011", + "to" : "ADR-046-provider-network-local", + "type" : "implements-spec" }, { - "from": "ADR-046-provider-system-core", - "to": "ADR-046-current-code-migration-map", - "type": "spec-depends-on" + "from" : "ADR046-nl-012", + "to" : "ADR-046-provider-network-local", + "type" : "implements-spec" }, { - "from": "ADR-046-provider-system-core", - "to": "ADR-046-nix-configuration", - "type": "spec-depends-on" + "from" : "ADR046-nl-013", + "to" : "ADR-046-provider-network-local", + "type" : "implements-spec" }, { - "from": "ADR-046-provider-system-core", - "to": "ADR-046-provider-model-and-packaging", - "type": "spec-depends-on" + "from" : "ADR046-nl-014", + "to" : "ADR-046-provider-network-local", + "type" : "implements-spec" }, { - "from": "ADR-046-provider-system-core", - "to": "ADR-046-resources-host-guest-process-user", - "type": "spec-depends-on" + "from" : "ADR046-nl-015", + "to" : "ADR-046-provider-network-local", + "type" : "implements-spec" }, { - "from": "ADR-046-provider-system-core", - "to": "ADR-046-resources-zone-control", - "type": "spec-depends-on" + "from" : "ADR046-nl-016", + "to" : "ADR-046-provider-network-local", + "type" : "implements-spec" }, { - "from": "ADR-046-provider-system-core", - "to": "ADR-046-telemetry-audit-and-support", - "type": "spec-depends-on" + "from" : "ADR046-nl-017", + "to" : "ADR-046-provider-network-local", + "type" : "implements-spec" }, { - "from": "ADR-046-provider-system-minijail", - "to": "ADR-046-components-processes-and-sandbox", - "type": "spec-depends-on" + "from" : "ADR046-nl-018", + "to" : "ADR-046-provider-network-local", + "type" : "implements-spec" }, { - "from": "ADR-046-provider-system-minijail", - "to": "ADR-046-componentsession-and-bus", - "type": "spec-depends-on" + "from" : "ADR046-nl-019", + "to" : "ADR-046-provider-network-local", + "type" : "implements-spec" }, { - "from": "ADR-046-provider-system-minijail", - "to": "ADR-046-core-controllers", - "type": "spec-depends-on" + "from" : "ADR046-nl-020", + "to" : "ADR-046-provider-network-local", + "type" : "implements-spec" }, { - "from": "ADR-046-provider-system-minijail", - "to": "ADR-046-nix-configuration", - "type": "spec-depends-on" + "from" : "ADR046-notify-001", + "to" : "ADR-046-provider-notification-desktop", + "type" : "implements-spec" }, { - "from": "ADR-046-provider-system-minijail", - "to": "ADR-046-primitive-resource-composition", - "type": "spec-depends-on" + "from" : "ADR046-notify-002", + "to" : "ADR-046-provider-notification-desktop", + "type" : "implements-spec" }, { - "from": "ADR-046-provider-system-minijail", - "to": "ADR-046-provider-model-and-packaging", - "type": "spec-depends-on" + "from" : "ADR046-notify-003", + "to" : "ADR-046-provider-notification-desktop", + "type" : "implements-spec" }, { - "from": "ADR-046-provider-system-minijail", - "to": "ADR-046-resource-api-and-authorization", - "type": "spec-depends-on" + "from" : "ADR046-notify-004", + "to" : "ADR-046-provider-notification-desktop", + "type" : "implements-spec" }, { - "from": "ADR-046-provider-system-minijail", - "to": "ADR-046-resource-object-model", - "type": "spec-depends-on" + "from" : "ADR046-notify-005", + "to" : "ADR-046-provider-notification-desktop", + "type" : "implements-spec" }, { - "from": "ADR-046-provider-system-minijail", - "to": "ADR-046-resource-reconciliation", - "type": "spec-depends-on" + "from" : "ADR046-notify-006", + "to" : "ADR-046-provider-notification-desktop", + "type" : "implements-spec" }, { - "from": "ADR-046-provider-system-minijail", - "to": "ADR-046-resources-host-guest-process-user", - "type": "spec-depends-on" + "from" : "ADR046-object-001", + "to" : "ADR-046-resource-object-model", + "type" : "implements-spec" }, { - "from": "ADR-046-provider-system-minijail", - "to": "ADR-046-telemetry-audit-and-support", - "type": "spec-depends-on" + "from" : "ADR046-object-002", + "to" : "ADR-046-resource-object-model", + "type" : "implements-spec" }, { - "from": "ADR-046-provider-system-minijail", - "to": "ADR-046-terminology-and-identities", - "type": "spec-depends-on" + "from" : "ADR046-otel-001", + "to" : "ADR-046-provider-observability-otel", + "type" : "implements-spec" }, { - "from": "ADR-046-provider-system-systemd", - "to": "ADR-046-components-processes-and-sandbox", - "type": "spec-depends-on" + "from" : "ADR046-otel-002", + "to" : "ADR-046-provider-observability-otel", + "type" : "implements-spec" }, { - "from": "ADR-046-provider-system-systemd", - "to": "ADR-046-componentsession-and-bus", - "type": "spec-depends-on" + "from" : "ADR046-otel-003", + "to" : "ADR-046-provider-observability-otel", + "type" : "implements-spec" }, { - "from": "ADR-046-provider-system-systemd", - "to": "ADR-046-current-code-migration-map", - "type": "spec-depends-on" + "from" : "ADR046-otel-004", + "to" : "ADR-046-provider-observability-otel", + "type" : "implements-spec" }, { - "from": "ADR-046-provider-system-systemd", - "to": "ADR-046-nix-configuration", - "type": "spec-depends-on" + "from" : "ADR046-otel-005", + "to" : "ADR-046-provider-observability-otel", + "type" : "implements-spec" }, { - "from": "ADR-046-provider-system-systemd", - "to": "ADR-046-provider-model-and-packaging", - "type": "spec-depends-on" + "from" : "ADR046-otel-006", + "to" : "ADR-046-provider-observability-otel", + "type" : "implements-spec" }, { - "from": "ADR-046-provider-system-systemd", - "to": "ADR-046-resources-host-guest-process-user", - "type": "spec-depends-on" + "from" : "ADR046-primitives-001", + "to" : "ADR-046-primitive-resource-composition", + "type" : "implements-spec" }, { - "from": "ADR-046-provider-system-systemd", - "to": "ADR-046-telemetry-audit-and-support", - "type": "spec-depends-on" + "from" : "ADR046-primitives-002", + "to" : "ADR-046-primitive-resource-composition", + "type" : "implements-spec" }, { - "from": "ADR-046-provider-transport-azure-relay", - "to": "ADR-046-componentsession-and-bus", - "type": "spec-depends-on" + "from" : "ADR046-primitives-003", + "to" : "ADR-046-primitive-resource-composition", + "type" : "implements-spec" }, { - "from": "ADR-046-provider-transport-azure-relay", - "to": "ADR-046-nix-configuration", - "type": "spec-depends-on" + "from" : "ADR046-process-001", + "to" : "ADR-046-components-processes-and-sandbox", + "type" : "implements-spec" }, { - "from": "ADR-046-provider-transport-azure-relay", - "to": "ADR-046-provider-model-and-packaging", - "type": "spec-depends-on" + "from" : "ADR046-process-002", + "to" : "ADR-046-components-processes-and-sandbox", + "type" : "implements-spec" }, { - "from": "ADR-046-provider-transport-azure-relay", - "to": "ADR-046-resources-credential", - "type": "spec-depends-on" + "from" : "ADR046-provider-001", + "to" : "ADR-046-provider-model-and-packaging", + "type" : "implements-spec" }, { - "from": "ADR-046-provider-transport-azure-relay", - "to": "ADR-046-telemetry-audit-and-support", - "type": "spec-depends-on" + "from" : "ADR046-provider-002", + "to" : "ADR-046-provider-model-and-packaging", + "type" : "implements-spec" }, { - "from": "ADR-046-provider-transport-azure-relay", - "to": "ADR-046-terminology-and-identities", - "type": "spec-depends-on" + "from" : "ADR046-provider-003", + "to" : "ADR-046-provider-model-and-packaging", + "type" : "implements-spec" }, { - "from": "ADR-046-provider-transport-azure-relay", - "to": "ADR-046-zone-routing", - "type": "spec-depends-on" + "from" : "ADR046-provider-004", + "to" : "ADR-046-provider-model-and-packaging", + "type" : "implements-spec" }, { - "from": "ADR-046-provider-transport-unix", - "to": "ADR-046-components-processes-and-sandbox", - "type": "spec-depends-on" + "from" : "ADR046-pstate-001", + "to" : "ADR-046-provider-state", + "type" : "implements-spec" }, { - "from": "ADR-046-provider-transport-unix", - "to": "ADR-046-componentsession-and-bus", - "type": "spec-depends-on" + "from" : "ADR046-pstate-002", + "to" : "ADR-046-provider-state", + "type" : "implements-spec" }, { - "from": "ADR-046-provider-transport-unix", - "to": "ADR-046-core-controllers", - "type": "spec-depends-on" + "from" : "ADR046-pstate-003", + "to" : "ADR-046-provider-state", + "type" : "implements-spec" }, { - "from": "ADR-046-provider-transport-unix", - "to": "ADR-046-provider-model-and-packaging", - "type": "spec-depends-on" + "from" : "ADR046-pstate-004", + "to" : "ADR-046-provider-state", + "type" : "implements-spec" }, { - "from": "ADR-046-provider-transport-unix", - "to": "ADR-046-provider-state", - "type": "spec-depends-on" + "from" : "ADR046-pstate-005", + "to" : "ADR-046-provider-state", + "type" : "implements-spec" }, { - "from": "ADR-046-provider-transport-unix", - "to": "ADR-046-resource-api-and-authorization", - "type": "spec-depends-on" + "from" : "ADR046-pstate-006", + "to" : "ADR-046-provider-state", + "type" : "implements-spec" }, { - "from": "ADR-046-provider-transport-unix", - "to": "ADR-046-resource-object-model", - "type": "spec-depends-on" + "from" : "ADR046-pstate-007", + "to" : "ADR-046-provider-state", + "type" : "implements-spec" }, { - "from": "ADR-046-provider-transport-unix", - "to": "ADR-046-resources-host-guest-process-user", - "type": "spec-depends-on" + "from" : "ADR046-pstate-008", + "to" : "ADR-046-provider-state", + "type" : "implements-spec" }, { - "from": "ADR-046-provider-transport-unix", - "to": "ADR-046-resources-zone-control", - "type": "spec-depends-on" + "from" : "ADR046-pstate-009", + "to" : "ADR-046-provider-state", + "type" : "implements-spec" }, { - "from": "ADR-046-provider-transport-unix", - "to": "ADR-046-terminology-and-identities", - "type": "spec-depends-on" + "from" : "ADR046-pstate-010", + "to" : "ADR-046-provider-state", + "type" : "implements-spec" }, { - "from": "ADR-046-provider-transport-unix", - "to": "ADR-046-zone-routing", - "type": "spec-depends-on" + "from" : "ADR046-pstate-011", + "to" : "ADR-046-provider-state", + "type" : "implements-spec" }, { - "from": "ADR-046-provider-transport-vsock", - "to": "ADR-046-componentsession-and-bus", - "type": "spec-depends-on" + "from" : "ADR046-pstate-012", + "to" : "ADR-046-provider-state", + "type" : "implements-spec" }, { - "from": "ADR-046-provider-transport-vsock", - "to": "ADR-046-provider-model-and-packaging", - "type": "spec-depends-on" + "from" : "ADR046-qemu-media-001", + "to" : "ADR-046-provider-runtime-qemu-media", + "type" : "implements-spec" }, { - "from": "ADR-046-provider-transport-vsock", - "to": "ADR-046-provider-state", - "type": "spec-depends-on" + "from" : "ADR046-qemu-media-002", + "to" : "ADR-046-provider-runtime-qemu-media", + "type" : "implements-spec" }, { - "from": "ADR-046-provider-transport-vsock", - "to": "ADR-046-resource-api-and-authorization", - "type": "spec-depends-on" + "from" : "ADR046-qemu-media-003", + "to" : "ADR-046-provider-runtime-qemu-media", + "type" : "implements-spec" }, { - "from": "ADR-046-provider-transport-vsock", - "to": "ADR-046-resources-host-guest-process-user", - "type": "spec-depends-on" + "from" : "ADR046-qemu-media-004", + "to" : "ADR-046-provider-runtime-qemu-media", + "type" : "implements-spec" }, { - "from": "ADR-046-provider-transport-vsock", - "to": "ADR-046-resources-zone-control", - "type": "spec-depends-on" + "from" : "ADR046-qemu-media-005", + "to" : "ADR-046-provider-runtime-qemu-media", + "type" : "implements-spec" }, { - "from": "ADR-046-provider-transport-vsock", - "to": "ADR-046-terminology-and-identities", - "type": "spec-depends-on" + "from" : "ADR046-qemu-media-006", + "to" : "ADR-046-provider-runtime-qemu-media", + "type" : "implements-spec" }, { - "from": "ADR-046-provider-transport-vsock", - "to": "ADR-046-zone-routing", - "type": "spec-depends-on" + "from" : "ADR046-qemu-media-007", + "to" : "ADR-046-provider-runtime-qemu-media", + "type" : "implements-spec" }, { - "from": "ADR-046-provider-volume-local", - "to": "ADR-046-components-processes-and-sandbox", - "type": "spec-depends-on" + "from" : "ADR046-qemu-media-008", + "to" : "ADR-046-provider-runtime-qemu-media", + "type" : "implements-spec" }, { - "from": "ADR-046-provider-volume-local", - "to": "ADR-046-componentsession-and-bus", - "type": "spec-depends-on" + "from" : "ADR046-qemu-media-009", + "to" : "ADR-046-provider-runtime-qemu-media", + "type" : "implements-spec" }, { - "from": "ADR-046-provider-volume-local", - "to": "ADR-046-nix-configuration", - "type": "spec-depends-on" + "from" : "ADR046-qemu-media-010", + "to" : "ADR-046-provider-runtime-qemu-media", + "type" : "implements-spec" }, { - "from": "ADR-046-provider-volume-local", - "to": "ADR-046-primitive-resource-composition", - "type": "spec-depends-on" + "from" : "ADR046-qemu-media-011", + "to" : "ADR-046-provider-runtime-qemu-media", + "type" : "implements-spec" }, { - "from": "ADR-046-provider-volume-local", - "to": "ADR-046-provider-state", - "type": "spec-depends-on" + "from" : "ADR046-qemu-media-012", + "to" : "ADR-046-provider-runtime-qemu-media", + "type" : "implements-spec" }, { - "from": "ADR-046-provider-volume-local", - "to": "ADR-046-resource-api-and-authorization", - "type": "spec-depends-on" + "from" : "ADR046-qemu-media-013", + "to" : "ADR-046-provider-runtime-qemu-media", + "type" : "implements-spec" }, { - "from": "ADR-046-provider-volume-local", - "to": "ADR-046-resources-volume", - "type": "spec-depends-on" + "from" : "ADR046-qemu-media-014", + "to" : "ADR-046-provider-runtime-qemu-media", + "type" : "implements-spec" }, { - "from": "ADR-046-provider-volume-local", - "to": "ADR-046-telemetry-audit-and-support", - "type": "spec-depends-on" + "from" : "ADR046-qemu-media-015", + "to" : "ADR-046-provider-runtime-qemu-media", + "type" : "implements-spec" }, { - "from": "ADR-046-provider-volume-virtiofs", - "to": "ADR-046-components-processes-and-sandbox", - "type": "spec-depends-on" + "from" : "ADR046-qemu-media-016", + "to" : "ADR-046-provider-runtime-qemu-media", + "type" : "implements-spec" }, { - "from": "ADR-046-provider-volume-virtiofs", - "to": "ADR-046-componentsession-and-bus", - "type": "spec-depends-on" + "from" : "ADR046-qemu-media-017", + "to" : "ADR-046-provider-runtime-qemu-media", + "type" : "implements-spec" }, { - "from": "ADR-046-provider-volume-virtiofs", - "to": "ADR-046-nix-configuration", - "type": "spec-depends-on" + "from" : "ADR046-qemu-media-018", + "to" : "ADR-046-provider-runtime-qemu-media", + "type" : "implements-spec" }, { - "from": "ADR-046-provider-volume-virtiofs", - "to": "ADR-046-provider-model-and-packaging", - "type": "spec-depends-on" + "from" : "ADR046-qemu-media-019", + "to" : "ADR-046-provider-runtime-qemu-media", + "type" : "implements-spec" }, { - "from": "ADR-046-provider-volume-virtiofs", - "to": "ADR-046-provider-state", - "type": "spec-depends-on" + "from" : "ADR046-reconcile-001", + "to" : "ADR-046-resource-reconciliation", + "type" : "implements-spec" }, { - "from": "ADR-046-provider-volume-virtiofs", - "to": "ADR-046-resource-api-and-authorization", - "type": "spec-depends-on" + "from" : "ADR046-reconcile-002", + "to" : "ADR-046-resource-reconciliation", + "type" : "implements-spec" }, { - "from": "ADR-046-provider-volume-virtiofs", - "to": "ADR-046-resource-reconciliation", - "type": "spec-depends-on" + "from" : "ADR046-reconcile-003", + "to" : "ADR-046-resource-reconciliation", + "type" : "implements-spec" }, { - "from": "ADR-046-provider-volume-virtiofs", - "to": "ADR-046-resources-host-guest-process-user", - "type": "spec-depends-on" + "from" : "ADR046-reset-001", + "to" : "ADR-046-reset-and-cutover", + "type" : "implements-spec" }, { - "from": "ADR-046-provider-volume-virtiofs", - "to": "ADR-046-resources-volume", - "type": "spec-depends-on" + "from" : "ADR046-reset-002", + "to" : "ADR-046-reset-and-cutover", + "type" : "implements-spec" }, { - "from": "ADR-046-provider-volume-virtiofs", - "to": "ADR-046-telemetry-audit-and-support", - "type": "spec-depends-on" + "from" : "ADR046-reset-003", + "to" : "ADR-046-reset-and-cutover", + "type" : "implements-spec" }, { - "from": "ADR-046-reset-and-cutover", - "to": "ADR-046-cli-and-operations", - "type": "spec-depends-on" + "from" : "ADR046-reset-004", + "to" : "ADR-046-reset-and-cutover", + "type" : "implements-spec" }, { - "from": "ADR-046-reset-and-cutover", - "to": "ADR-046-components-processes-and-sandbox", - "type": "spec-depends-on" + "from" : "ADR046-reset-005", + "to" : "ADR-046-reset-and-cutover", + "type" : "implements-spec" }, { - "from": "ADR-046-reset-and-cutover", - "to": "ADR-046-componentsession-and-bus", - "type": "spec-depends-on" + "from" : "ADR046-reset-006", + "to" : "ADR-046-reset-and-cutover", + "type" : "implements-spec" }, { - "from": "ADR-046-reset-and-cutover", - "to": "ADR-046-core-controllers", - "type": "spec-depends-on" + "from" : "ADR046-reset-007", + "to" : "ADR-046-reset-and-cutover", + "type" : "implements-spec" }, { - "from": "ADR-046-reset-and-cutover", - "to": "ADR-046-current-code-migration-map", - "type": "spec-depends-on" + "from" : "ADR046-reset-008", + "to" : "ADR-046-reset-and-cutover", + "type" : "implements-spec" }, { - "from": "ADR-046-reset-and-cutover", - "to": "ADR-046-decision-register", - "type": "spec-depends-on" + "from" : "ADR046-reset-009", + "to" : "ADR-046-reset-and-cutover", + "type" : "implements-spec" }, { - "from": "ADR-046-reset-and-cutover", - "to": "ADR-046-nix-configuration", - "type": "spec-depends-on" + "from" : "ADR046-reset-010", + "to" : "ADR-046-reset-and-cutover", + "type" : "implements-spec" }, { - "from": "ADR-046-reset-and-cutover", - "to": "ADR-046-primitive-resource-composition", - "type": "spec-depends-on" + "from" : "ADR046-reset-011", + "to" : "ADR-046-reset-and-cutover", + "type" : "implements-spec" }, { - "from": "ADR-046-reset-and-cutover", - "to": "ADR-046-provider-model-and-packaging", - "type": "spec-depends-on" + "from" : "ADR046-routing-001", + "to" : "ADR-046-zone-routing", + "type" : "implements-spec" }, { - "from": "ADR-046-reset-and-cutover", - "to": "ADR-046-provider-state", - "type": "spec-depends-on" + "from" : "ADR046-routing-002", + "to" : "ADR-046-zone-routing", + "type" : "implements-spec" }, { - "from": "ADR-046-reset-and-cutover", - "to": "ADR-046-resource-api-and-authorization", - "type": "spec-depends-on" + "from" : "ADR046-routing-003", + "to" : "ADR-046-zone-routing", + "type" : "implements-spec" }, { - "from": "ADR-046-reset-and-cutover", - "to": "ADR-046-resource-object-model", - "type": "spec-depends-on" + "from" : "ADR046-routing-004", + "to" : "ADR-046-zone-routing", + "type" : "implements-spec" }, { - "from": "ADR-046-reset-and-cutover", - "to": "ADR-046-resource-reconciliation", - "type": "spec-depends-on" + "from" : "ADR046-routing-005", + "to" : "ADR-046-zone-routing", + "type" : "implements-spec" }, { - "from": "ADR-046-reset-and-cutover", - "to": "ADR-046-resource-store-redb", - "type": "spec-depends-on" + "from" : "ADR046-routing-006", + "to" : "ADR-046-zone-routing", + "type" : "implements-spec" }, { - "from": "ADR-046-reset-and-cutover", - "to": "ADR-046-resources-credential", - "type": "spec-depends-on" + "from" : "ADR046-routing-007", + "to" : "ADR-046-zone-routing", + "type" : "implements-spec" }, { - "from": "ADR-046-reset-and-cutover", - "to": "ADR-046-resources-device", - "type": "spec-depends-on" + "from" : "ADR046-routing-008", + "to" : "ADR-046-zone-routing", + "type" : "implements-spec" }, { - "from": "ADR-046-reset-and-cutover", - "to": "ADR-046-resources-host-guest-process-user", - "type": "spec-depends-on" + "from" : "ADR046-routing-009", + "to" : "ADR-046-zone-routing", + "type" : "implements-spec" }, { - "from": "ADR-046-reset-and-cutover", - "to": "ADR-046-resources-network", - "type": "spec-depends-on" + "from" : "ADR046-routing-010", + "to" : "ADR-046-zone-routing", + "type" : "implements-spec" }, { - "from": "ADR-046-reset-and-cutover", - "to": "ADR-046-resources-volume", - "type": "spec-depends-on" + "from" : "ADR046-routing-011", + "to" : "ADR-046-zone-routing", + "type" : "implements-spec" }, { - "from": "ADR-046-reset-and-cutover", - "to": "ADR-046-resources-zone-control", - "type": "spec-depends-on" + "from" : "ADR046-routing-012", + "to" : "ADR-046-zone-routing", + "type" : "implements-spec" }, { - "from": "ADR-046-reset-and-cutover", - "to": "ADR-046-telemetry-audit-and-support", - "type": "spec-depends-on" + "from" : "ADR046-routing-013", + "to" : "ADR-046-zone-routing", + "type" : "implements-spec" }, { - "from": "ADR-046-reset-and-cutover", - "to": "ADR-046-terminology-and-identities", - "type": "spec-depends-on" + "from" : "ADR046-routing-014", + "to" : "ADR-046-zone-routing", + "type" : "implements-spec" }, { - "from": "ADR-046-reset-and-cutover", - "to": "ADR-046-zone-routing", - "type": "spec-depends-on" + "from" : "ADR046-routing-015", + "to" : "ADR-046-zone-routing", + "type" : "implements-spec" }, { - "from": "ADR-046-resource-api-and-authorization", - "to": "ADR-046-resource-object-model", - "type": "shared-contract" + "from" : "ADR046-routing-016", + "to" : "ADR-046-zone-routing", + "type" : "implements-spec" }, { - "from": "ADR-046-resource-api-and-authorization", - "to": "ADR-046-resource-store-redb", - "type": "shared-contract" + "from" : "ADR046-security-001", + "to" : "ADR-046-security-and-threat-model", + "type" : "implements-spec" }, { - "from": "ADR-046-resource-api-and-authorization", - "to": "ADR-046-terminology-and-identities", - "type": "shared-contract" + "from" : "ADR046-security-002", + "to" : "ADR-046-security-and-threat-model", + "type" : "implements-spec" }, { - "from": "ADR-046-resource-object-model", - "to": "ADR-046-decision-register", - "type": "shared-contract" + "from" : "ADR046-security-003", + "to" : "ADR-046-security-and-threat-model", + "type" : "implements-spec" }, { - "from": "ADR-046-resource-object-model", - "to": "ADR-046-terminology-and-identities", - "type": "shared-contract" + "from" : "ADR046-security-004", + "to" : "ADR-046-security-and-threat-model", + "type" : "implements-spec" }, { - "from": "ADR-046-resource-reconciliation", - "to": "ADR-046-resource-api-and-authorization", - "type": "spec-depends-on" + "from" : "ADR046-security-005", + "to" : "ADR-046-security-and-threat-model", + "type" : "implements-spec" }, { - "from": "ADR-046-resource-reconciliation", - "to": "ADR-046-resource-object-model", - "type": "spec-depends-on" + "from" : "ADR046-security-006", + "to" : "ADR-046-security-and-threat-model", + "type" : "implements-spec" }, { - "from": "ADR-046-resource-reconciliation", - "to": "ADR-046-resource-store-redb", - "type": "spec-depends-on" + "from" : "ADR046-security-007", + "to" : "ADR-046-security-and-threat-model", + "type" : "implements-spec" }, { - "from": "ADR-046-resource-store-redb", - "to": "ADR-046-resource-object-model", - "type": "shared-contract" + "from" : "ADR046-security-008", + "to" : "ADR-046-security-and-threat-model", + "type" : "implements-spec" }, { - "from": "ADR-046-resource-store-redb", - "to": "ADR-046-terminology-and-identities", - "type": "shared-contract" + "from" : "ADR046-security-009", + "to" : "ADR-046-security-and-threat-model", + "type" : "implements-spec" }, { - "from": "ADR-046-resources-credential", - "to": "ADR-046-componentsession-and-bus", - "type": "spec-depends-on" + "from" : "ADR046-security-010", + "to" : "ADR-046-security-and-threat-model", + "type" : "implements-spec" }, { - "from": "ADR-046-resources-credential", - "to": "ADR-046-primitive-resource-composition", - "type": "spec-depends-on" + "from" : "ADR046-security-011", + "to" : "ADR-046-security-and-threat-model", + "type" : "implements-spec" }, { - "from": "ADR-046-resources-credential", - "to": "ADR-046-provider-model-and-packaging", - "type": "spec-depends-on" + "from" : "ADR046-security-012", + "to" : "ADR-046-security-and-threat-model", + "type" : "implements-spec" }, { - "from": "ADR-046-resources-credential", - "to": "ADR-046-resource-api-and-authorization", - "type": "spec-depends-on" + "from" : "ADR046-security-013", + "to" : "ADR-046-security-and-threat-model", + "type" : "implements-spec" }, { - "from": "ADR-046-resources-credential", - "to": "ADR-046-resource-object-model", - "type": "spec-depends-on" + "from" : "ADR046-security-014", + "to" : "ADR-046-security-and-threat-model", + "type" : "implements-spec" }, { - "from": "ADR-046-resources-credential", - "to": "ADR-046-resource-reconciliation", - "type": "spec-depends-on" + "from" : "ADR046-security-015", + "to" : "ADR-046-security-and-threat-model", + "type" : "implements-spec" }, { - "from": "ADR-046-resources-credential", - "to": "ADR-046-terminology-and-identities", - "type": "spec-depends-on" + "from" : "ADR046-security-016", + "to" : "ADR-046-security-and-threat-model", + "type" : "implements-spec" }, { - "from": "ADR-046-resources-device", - "to": "ADR-046-components-processes-and-sandbox", - "type": "spec-depends-on" + "from" : "ADR046-security-017", + "to" : "ADR-046-security-and-threat-model", + "type" : "implements-spec" }, { - "from": "ADR-046-resources-device", - "to": "ADR-046-primitive-resource-composition", - "type": "spec-depends-on" + "from" : "ADR046-security-018", + "to" : "ADR-046-security-and-threat-model", + "type" : "implements-spec" }, { - "from": "ADR-046-resources-device", - "to": "ADR-046-provider-model-and-packaging", - "type": "spec-depends-on" + "from" : "ADR046-security-019", + "to" : "ADR-046-security-and-threat-model", + "type" : "implements-spec" }, { - "from": "ADR-046-resources-device", - "to": "ADR-046-resource-api-and-authorization", - "type": "spec-depends-on" + "from" : "ADR046-security-key-001", + "to" : "ADR-046-provider-device-security-key", + "type" : "implements-spec" }, { - "from": "ADR-046-resources-device", - "to": "ADR-046-resource-object-model", - "type": "spec-depends-on" + "from" : "ADR046-security-key-002", + "to" : "ADR-046-provider-device-security-key", + "type" : "implements-spec" }, { - "from": "ADR-046-resources-device", - "to": "ADR-046-resource-reconciliation", - "type": "spec-depends-on" + "from" : "ADR046-security-key-003", + "to" : "ADR-046-provider-device-security-key", + "type" : "implements-spec" }, { - "from": "ADR-046-resources-host-guest-process-user", - "to": "ADR-046-components-processes-and-sandbox", - "type": "spec-depends-on" + "from" : "ADR046-security-key-004", + "to" : "ADR-046-provider-device-security-key", + "type" : "implements-spec" }, { - "from": "ADR-046-resources-host-guest-process-user", - "to": "ADR-046-core-controllers", - "type": "spec-depends-on" + "from" : "ADR046-security-key-005", + "to" : "ADR-046-provider-device-security-key", + "type" : "implements-spec" }, { - "from": "ADR-046-resources-host-guest-process-user", - "to": "ADR-046-decision-register", - "type": "spec-depends-on" + "from" : "ADR046-security-key-006", + "to" : "ADR-046-provider-device-security-key", + "type" : "implements-spec" }, { - "from": "ADR-046-resources-host-guest-process-user", - "to": "ADR-046-primitive-resource-composition", - "type": "spec-depends-on" + "from" : "ADR046-security-key-007", + "to" : "ADR-046-provider-device-security-key", + "type" : "implements-spec" }, { - "from": "ADR-046-resources-host-guest-process-user", - "to": "ADR-046-provider-model-and-packaging", - "type": "spec-depends-on" + "from" : "ADR046-security-key-008", + "to" : "ADR-046-provider-device-security-key", + "type" : "implements-spec" }, { - "from": "ADR-046-resources-host-guest-process-user", - "to": "ADR-046-resource-api-and-authorization", - "type": "spec-depends-on" + "from" : "ADR046-security-key-009", + "to" : "ADR-046-provider-device-security-key", + "type" : "implements-spec" }, { - "from": "ADR-046-resources-host-guest-process-user", - "to": "ADR-046-resource-object-model", - "type": "spec-depends-on" + "from" : "ADR046-security-key-010", + "to" : "ADR-046-provider-device-security-key", + "type" : "implements-spec" }, { - "from": "ADR-046-resources-host-guest-process-user", - "to": "ADR-046-resource-reconciliation", - "type": "spec-depends-on" + "from" : "ADR046-security-key-011", + "to" : "ADR-046-provider-device-security-key", + "type" : "implements-spec" }, { - "from": "ADR-046-resources-host-guest-process-user", - "to": "ADR-046-terminology-and-identities", - "type": "spec-depends-on" + "from" : "ADR046-security-key-012", + "to" : "ADR-046-provider-device-security-key", + "type" : "implements-spec" }, { - "from": "ADR-046-resources-network", - "to": "ADR-046-primitive-resource-composition", - "type": "spec-depends-on" + "from" : "ADR046-security-key-013", + "to" : "ADR-046-provider-device-security-key", + "type" : "implements-spec" }, { - "from": "ADR-046-resources-network", - "to": "ADR-046-provider-model-and-packaging", - "type": "spec-depends-on" + "from" : "ADR046-security-key-014", + "to" : "ADR-046-provider-device-security-key", + "type" : "implements-spec" }, { - "from": "ADR-046-resources-network", - "to": "ADR-046-resource-object-model", - "type": "spec-depends-on" + "from" : "ADR046-security-key-015", + "to" : "ADR-046-provider-device-security-key", + "type" : "implements-spec" }, { - "from": "ADR-046-resources-network", - "to": "ADR-046-resource-reconciliation", - "type": "spec-depends-on" + "from" : "ADR046-security-key-016", + "to" : "ADR-046-provider-device-security-key", + "type" : "implements-spec" }, { - "from": "ADR-046-resources-network", - "to": "ADR-046-terminology-and-identities", - "type": "spec-depends-on" + "from" : "ADR046-security-key-017", + "to" : "ADR-046-provider-device-security-key", + "type" : "implements-spec" }, { - "from": "ADR-046-resources-volume", - "to": "ADR-046-components-processes-and-sandbox", - "type": "spec-depends-on" + "from" : "ADR046-security-key-018", + "to" : "ADR-046-provider-device-security-key", + "type" : "implements-spec" }, { - "from": "ADR-046-resources-volume", - "to": "ADR-046-primitive-resource-composition", - "type": "spec-depends-on" + "from" : "ADR046-security-key-019", + "to" : "ADR-046-provider-device-security-key", + "type" : "implements-spec" }, { - "from": "ADR-046-resources-volume", - "to": "ADR-046-provider-model-and-packaging", - "type": "spec-depends-on" + "from" : "ADR046-security-key-020", + "to" : "ADR-046-provider-device-security-key", + "type" : "implements-spec" }, { - "from": "ADR-046-resources-volume", - "to": "ADR-046-resource-object-model", - "type": "spec-depends-on" + "from" : "ADR046-security-key-021", + "to" : "ADR-046-provider-device-security-key", + "type" : "implements-spec" }, { - "from": "ADR-046-resources-volume", - "to": "ADR-046-resource-reconciliation", - "type": "spec-depends-on" + "from" : "ADR046-security-key-022", + "to" : "ADR-046-provider-device-security-key", + "type" : "implements-spec" }, { - "from": "ADR-046-resources-zone-control", - "to": "ADR-046-core-controllers", - "type": "spec-depends-on" + "from" : "ADR046-security-key-023", + "to" : "ADR-046-provider-device-security-key", + "type" : "implements-spec" }, { - "from": "ADR-046-resources-zone-control", - "to": "ADR-046-decision-register", - "type": "spec-depends-on" + "from" : "ADR046-security-key-024", + "to" : "ADR-046-provider-device-security-key", + "type" : "implements-spec" }, { - "from": "ADR-046-resources-zone-control", - "to": "ADR-046-provider-model-and-packaging", - "type": "spec-depends-on" + "from" : "ADR046-security-key-025", + "to" : "ADR-046-provider-device-security-key", + "type" : "implements-spec" }, { - "from": "ADR-046-resources-zone-control", - "to": "ADR-046-resource-api-and-authorization", - "type": "spec-depends-on" + "from" : "ADR046-security-key-026", + "to" : "ADR-046-provider-device-security-key", + "type" : "implements-spec" }, { - "from": "ADR-046-resources-zone-control", - "to": "ADR-046-resource-object-model", - "type": "spec-depends-on" + "from" : "ADR046-security-key-027", + "to" : "ADR-046-provider-device-security-key", + "type" : "implements-spec" }, { - "from": "ADR-046-resources-zone-control", - "to": "ADR-046-resource-reconciliation", - "type": "spec-depends-on" + "from" : "ADR046-security-key-028", + "to" : "ADR-046-provider-device-security-key", + "type" : "implements-spec" }, { - "from": "ADR-046-resources-zone-control", - "to": "ADR-046-resource-store-redb", - "type": "spec-depends-on" + "from" : "ADR046-security-key-029", + "to" : "ADR-046-provider-device-security-key", + "type" : "implements-spec" }, { - "from": "ADR-046-resources-zone-control", - "to": "ADR-046-terminology-and-identities", - "type": "spec-depends-on" + "from" : "ADR046-security-key-030", + "to" : "ADR-046-provider-device-security-key", + "type" : "implements-spec" }, { - "from": "ADR-046-security-and-threat-model", - "to": "ADR-046-cli-and-operations", - "type": "spec-depends-on" + "from" : "ADR046-security-key-031", + "to" : "ADR-046-provider-device-security-key", + "type" : "implements-spec" }, { - "from": "ADR-046-security-and-threat-model", - "to": "ADR-046-components-processes-and-sandbox", - "type": "spec-depends-on" + "from" : "ADR046-security-key-032", + "to" : "ADR-046-provider-device-security-key", + "type" : "implements-spec" }, { - "from": "ADR-046-security-and-threat-model", - "to": "ADR-046-componentsession-and-bus", - "type": "spec-depends-on" + "from" : "ADR046-security-key-033", + "to" : "ADR-046-provider-device-security-key", + "type" : "implements-spec" }, { - "from": "ADR-046-security-and-threat-model", - "to": "ADR-046-core-controllers", - "type": "spec-depends-on" + "from" : "ADR046-security-key-034", + "to" : "ADR-046-provider-device-security-key", + "type" : "implements-spec" }, { - "from": "ADR-046-security-and-threat-model", - "to": "ADR-046-current-code-migration-map", - "type": "spec-depends-on" + "from" : "ADR046-security-key-035", + "to" : "ADR-046-provider-device-security-key", + "type" : "implements-spec" }, { - "from": "ADR-046-security-and-threat-model", - "to": "ADR-046-decision-register", - "type": "spec-depends-on" + "from" : "ADR046-session-001", + "to" : "ADR-046-componentsession-and-bus", + "type" : "implements-spec" }, { - "from": "ADR-046-security-and-threat-model", - "to": "ADR-046-nix-configuration", - "type": "spec-depends-on" + "from" : "ADR046-session-002", + "to" : "ADR-046-componentsession-and-bus", + "type" : "implements-spec" }, { - "from": "ADR-046-security-and-threat-model", - "to": "ADR-046-primitive-resource-composition", - "type": "spec-depends-on" + "from" : "ADR046-session-003", + "to" : "ADR-046-componentsession-and-bus", + "type" : "implements-spec" }, { - "from": "ADR-046-security-and-threat-model", - "to": "ADR-046-provider-activation-nixos", - "type": "spec-depends-on" + "from" : "ADR046-sterm-001", + "to" : "ADR-046-provider-shell-terminal", + "type" : "implements-spec" }, { - "from": "ADR-046-security-and-threat-model", - "to": "ADR-046-provider-audio-pipewire", - "type": "spec-depends-on" + "from" : "ADR046-sterm-002", + "to" : "ADR-046-provider-shell-terminal", + "type" : "implements-spec" }, { - "from": "ADR-046-security-and-threat-model", - "to": "ADR-046-provider-clipboard-wayland", - "type": "spec-depends-on" + "from" : "ADR046-sterm-003", + "to" : "ADR-046-provider-shell-terminal", + "type" : "implements-spec" }, { - "from": "ADR-046-security-and-threat-model", - "to": "ADR-046-provider-credential-entra", - "type": "spec-depends-on" + "from" : "ADR046-sterm-004", + "to" : "ADR-046-provider-shell-terminal", + "type" : "implements-spec" }, { - "from": "ADR-046-security-and-threat-model", - "to": "ADR-046-provider-credential-managed-identity", - "type": "spec-depends-on" + "from" : "ADR046-sterm-005", + "to" : "ADR-046-provider-shell-terminal", + "type" : "implements-spec" }, { - "from": "ADR-046-security-and-threat-model", - "to": "ADR-046-provider-credential-secret-service", - "type": "spec-depends-on" + "from" : "ADR046-sterm-006", + "to" : "ADR-046-provider-shell-terminal", + "type" : "implements-spec" }, { - "from": "ADR-046-security-and-threat-model", - "to": "ADR-046-provider-device-gpu", - "type": "spec-depends-on" + "from" : "ADR046-sterm-007", + "to" : "ADR-046-provider-shell-terminal", + "type" : "implements-spec" }, { - "from": "ADR-046-security-and-threat-model", - "to": "ADR-046-provider-device-security-key", - "type": "spec-depends-on" + "from" : "ADR046-sterm-008", + "to" : "ADR-046-provider-shell-terminal", + "type" : "implements-spec" }, { - "from": "ADR-046-security-and-threat-model", - "to": "ADR-046-provider-device-tpm", - "type": "spec-depends-on" + "from" : "ADR046-sterm-009", + "to" : "ADR-046-provider-shell-terminal", + "type" : "implements-spec" }, { - "from": "ADR-046-security-and-threat-model", - "to": "ADR-046-provider-device-usbip", - "type": "spec-depends-on" + "from" : "ADR046-sterm-010", + "to" : "ADR-046-provider-shell-terminal", + "type" : "implements-spec" }, { - "from": "ADR-046-security-and-threat-model", - "to": "ADR-046-provider-display-wayland", - "type": "spec-depends-on" + "from" : "ADR046-sterm-011", + "to" : "ADR-046-provider-shell-terminal", + "type" : "implements-spec" }, { - "from": "ADR-046-security-and-threat-model", - "to": "ADR-046-provider-model-and-packaging", - "type": "spec-depends-on" + "from" : "ADR046-sterm-012", + "to" : "ADR-046-provider-shell-terminal", + "type" : "implements-spec" }, { - "from": "ADR-046-security-and-threat-model", - "to": "ADR-046-provider-network-local", - "type": "spec-depends-on" + "from" : "ADR046-sterm-013", + "to" : "ADR-046-provider-shell-terminal", + "type" : "implements-spec" }, { - "from": "ADR-046-security-and-threat-model", - "to": "ADR-046-provider-notification-desktop", - "type": "spec-depends-on" + "from" : "ADR046-store-001", + "to" : "ADR-046-resource-store-redb", + "type" : "implements-spec" }, { - "from": "ADR-046-security-and-threat-model", - "to": "ADR-046-provider-observability-otel", - "type": "spec-depends-on" + "from" : "ADR046-store-002", + "to" : "ADR-046-resource-store-redb", + "type" : "implements-spec" }, { - "from": "ADR-046-security-and-threat-model", - "to": "ADR-046-provider-runtime-azure-container-apps", - "type": "spec-depends-on" + "from" : "ADR046-store-003", + "to" : "ADR-046-resource-store-redb", + "type" : "implements-spec" }, { - "from": "ADR-046-security-and-threat-model", - "to": "ADR-046-provider-runtime-azure-virtual-machine", - "type": "spec-depends-on" + "from" : "ADR046-streamline-001", + "to" : "ADR-046-streamline", + "type" : "implements-spec" }, { - "from": "ADR-046-security-and-threat-model", - "to": "ADR-046-provider-runtime-cloud-hypervisor", - "type": "spec-depends-on" + "from" : "ADR046-streamline-002", + "to" : "ADR-046-streamline", + "type" : "implements-spec" }, { - "from": "ADR-046-security-and-threat-model", - "to": "ADR-046-provider-runtime-qemu-media", - "type": "spec-depends-on" + "from" : "ADR046-streamline-003", + "to" : "ADR-046-streamline", + "type" : "implements-spec" }, { - "from": "ADR-046-security-and-threat-model", - "to": "ADR-046-provider-shell-terminal", - "type": "spec-depends-on" + "from" : "ADR046-streamline-004", + "to" : "ADR-046-streamline", + "type" : "implements-spec" }, { - "from": "ADR-046-security-and-threat-model", - "to": "ADR-046-provider-state", - "type": "spec-depends-on" + "from" : "ADR046-streamline-005", + "to" : "ADR-046-streamline", + "type" : "implements-spec" }, { - "from": "ADR-046-security-and-threat-model", - "to": "ADR-046-provider-system-core", - "type": "spec-depends-on" + "from" : "ADR046-streamline-006", + "to" : "ADR-046-streamline", + "type" : "implements-spec" }, { - "from": "ADR-046-security-and-threat-model", - "to": "ADR-046-provider-system-minijail", - "type": "spec-depends-on" + "from" : "ADR046-streamline-007", + "to" : "ADR-046-streamline", + "type" : "implements-spec" }, { - "from": "ADR-046-security-and-threat-model", - "to": "ADR-046-provider-system-systemd", - "type": "spec-depends-on" + "from" : "ADR046-streamline-008", + "to" : "ADR-046-streamline", + "type" : "implements-spec" }, { - "from": "ADR-046-security-and-threat-model", - "to": "ADR-046-provider-transport-azure-relay", - "type": "spec-depends-on" + "from" : "ADR046-streamline-009", + "to" : "ADR-046-streamline", + "type" : "implements-spec" }, { - "from": "ADR-046-security-and-threat-model", - "to": "ADR-046-provider-transport-unix", - "type": "spec-depends-on" + "from" : "ADR046-streamline-010", + "to" : "ADR-046-streamline", + "type" : "implements-spec" }, { - "from": "ADR-046-security-and-threat-model", - "to": "ADR-046-provider-transport-vsock", - "type": "spec-depends-on" + "from" : "ADR046-streamline-011", + "to" : "ADR-046-streamline", + "type" : "implements-spec" }, { - "from": "ADR-046-security-and-threat-model", - "to": "ADR-046-provider-volume-local", - "type": "spec-depends-on" + "from" : "ADR046-streamline-012", + "to" : "ADR-046-streamline", + "type" : "implements-spec" }, { - "from": "ADR-046-security-and-threat-model", - "to": "ADR-046-provider-volume-virtiofs", - "type": "spec-depends-on" + "from" : "ADR046-streamline-013", + "to" : "ADR-046-streamline", + "type" : "implements-spec" }, { - "from": "ADR-046-security-and-threat-model", - "to": "ADR-046-resource-api-and-authorization", - "type": "spec-depends-on" + "from" : "ADR046-streamline-014", + "to" : "ADR-046-streamline", + "type" : "implements-spec" }, { - "from": "ADR-046-security-and-threat-model", - "to": "ADR-046-resource-object-model", - "type": "spec-depends-on" + "from" : "ADR046-streamline-015", + "to" : "ADR-046-streamline", + "type" : "implements-spec" }, { - "from": "ADR-046-security-and-threat-model", - "to": "ADR-046-resource-reconciliation", - "type": "spec-depends-on" + "from" : "ADR046-streamline-016", + "to" : "ADR-046-streamline", + "type" : "implements-spec" }, { - "from": "ADR-046-security-and-threat-model", - "to": "ADR-046-resource-store-redb", - "type": "spec-depends-on" + "from" : "ADR046-streamline-017", + "to" : "ADR-046-streamline", + "type" : "implements-spec" }, { - "from": "ADR-046-security-and-threat-model", - "to": "ADR-046-resources-credential", - "type": "spec-depends-on" + "from" : "ADR046-streamline-018", + "to" : "ADR-046-streamline", + "type" : "implements-spec" }, { - "from": "ADR-046-security-and-threat-model", - "to": "ADR-046-resources-device", - "type": "spec-depends-on" + "from" : "ADR046-streamline-019", + "to" : "ADR-046-streamline", + "type" : "implements-spec" }, { - "from": "ADR-046-security-and-threat-model", - "to": "ADR-046-resources-host-guest-process-user", - "type": "spec-depends-on" + "from" : "ADR046-streamline-020", + "to" : "ADR-046-streamline", + "type" : "implements-spec" }, { - "from": "ADR-046-security-and-threat-model", - "to": "ADR-046-resources-network", - "type": "spec-depends-on" + "from" : "ADR046-streamline-021", + "to" : "ADR-046-streamline", + "type" : "implements-spec" }, { - "from": "ADR-046-security-and-threat-model", - "to": "ADR-046-resources-volume", - "type": "spec-depends-on" + "from" : "ADR046-streamline-022", + "to" : "ADR-046-streamline", + "type" : "implements-spec" }, { - "from": "ADR-046-security-and-threat-model", - "to": "ADR-046-resources-zone-control", - "type": "spec-depends-on" + "from" : "ADR046-streamline-023", + "to" : "ADR-046-streamline", + "type" : "implements-spec" }, { - "from": "ADR-046-security-and-threat-model", - "to": "ADR-046-telemetry-audit-and-support", - "type": "spec-depends-on" + "from" : "ADR046-streamline-024", + "to" : "ADR-046-streamline", + "type" : "implements-spec" }, { - "from": "ADR-046-security-and-threat-model", - "to": "ADR-046-terminology-and-identities", - "type": "spec-depends-on" + "from" : "ADR046-system-core-001", + "to" : "ADR-046-provider-system-core", + "type" : "implements-spec" }, { - "from": "ADR-046-security-and-threat-model", - "to": "ADR-046-zone-routing", - "type": "spec-depends-on" + "from" : "ADR046-systemd-001", + "to" : "ADR-046-provider-system-systemd", + "type" : "implements-spec" }, { - "from": "ADR-046-streamline", - "to": "ADR-046-current-code-migration-map", - "type": "spec-depends-on" + "from" : "ADR046-systemd-002", + "to" : "ADR-046-provider-system-systemd", + "type" : "implements-spec" }, { - "from": "ADR-046-streamline", - "to": "ADR-046-decision-register", - "type": "spec-depends-on" + "from" : "ADR046-systemd-003", + "to" : "ADR-046-provider-system-systemd", + "type" : "implements-spec" }, { - "from": "ADR-046-streamline", - "to": "ADR-046-primitive-resource-composition", - "type": "spec-depends-on" + "from" : "ADR046-telem-001", + "to" : "ADR-046-telemetry-audit-and-support", + "type" : "implements-spec" }, { - "from": "ADR-046-streamline", - "to": "ADR-046-provider-model-and-packaging", - "type": "spec-depends-on" + "from" : "ADR046-telem-002", + "to" : "ADR-046-telemetry-audit-and-support", + "type" : "implements-spec" }, { - "from": "ADR-046-streamline", - "to": "ADR-046-provider-state", - "type": "spec-depends-on" + "from" : "ADR046-telem-003", + "to" : "ADR-046-telemetry-audit-and-support", + "type" : "implements-spec" }, { - "from": "ADR-046-streamline", - "to": "ADR-046-reset-and-cutover", - "type": "shared-contract" + "from" : "ADR046-telem-004", + "to" : "ADR-046-telemetry-audit-and-support", + "type" : "implements-spec" }, { - "from": "ADR-046-streamline", - "to": "ADR-046-resources-volume", - "type": "spec-depends-on" + "from" : "ADR046-telem-005", + "to" : "ADR-046-telemetry-audit-and-support", + "type" : "implements-spec" }, { - "from": "ADR-046-streamline", - "to": "ADR-046-terminology-and-identities", - "type": "spec-depends-on" + "from" : "ADR046-telem-006", + "to" : "ADR-046-telemetry-audit-and-support", + "type" : "implements-spec" }, { - "from": "ADR-046-streamline", - "to": "ADR-046-zone-routing", - "type": "spec-depends-on" + "from" : "ADR046-telem-007", + "to" : "ADR-046-telemetry-audit-and-support", + "type" : "implements-spec" }, { - "from": "ADR-046-telemetry-audit-and-support", - "to": "ADR-046-components-processes-and-sandbox", - "type": "spec-depends-on" + "from" : "ADR046-telem-008", + "to" : "ADR-046-telemetry-audit-and-support", + "type" : "implements-spec" }, { - "from": "ADR-046-telemetry-audit-and-support", - "to": "ADR-046-componentsession-and-bus", - "type": "spec-depends-on" + "from" : "ADR046-telem-009", + "to" : "ADR-046-telemetry-audit-and-support", + "type" : "implements-spec" }, { - "from": "ADR-046-telemetry-audit-and-support", - "to": "ADR-046-core-controllers", - "type": "spec-depends-on" + "from" : "ADR046-telem-010", + "to" : "ADR-046-telemetry-audit-and-support", + "type" : "implements-spec" }, { - "from": "ADR-046-telemetry-audit-and-support", - "to": "ADR-046-provider-model-and-packaging", - "type": "spec-depends-on" + "from" : "ADR046-telem-011", + "to" : "ADR-046-telemetry-audit-and-support", + "type" : "implements-spec" }, { - "from": "ADR-046-telemetry-audit-and-support", - "to": "ADR-046-resource-object-model", - "type": "spec-depends-on" + "from" : "ADR046-telem-012", + "to" : "ADR-046-telemetry-audit-and-support", + "type" : "implements-spec" }, { - "from": "ADR-046-telemetry-audit-and-support", - "to": "ADR-046-resource-store-redb", - "type": "spec-depends-on" + "from" : "ADR046-telem-013", + "to" : "ADR-046-telemetry-audit-and-support", + "type" : "implements-spec" }, { - "from": "ADR-046-telemetry-audit-and-support", - "to": "ADR-046-terminology-and-identities", - "type": "spec-depends-on" + "from" : "ADR046-telem-014", + "to" : "ADR-046-telemetry-audit-and-support", + "type" : "implements-spec" }, { - "from": "ADR-046-terminology-and-identities", - "to": "ADR-046-decision-register", - "type": "shared-contract" + "from" : "ADR046-telem-015", + "to" : "ADR-046-telemetry-audit-and-support", + "type" : "implements-spec" }, { - "from": "ADR-046-validation-and-delivery", - "to": "ADR-046-cli-and-operations", - "type": "spec-depends-on" + "from" : "ADR046-telem-016", + "to" : "ADR-046-telemetry-audit-and-support", + "type" : "implements-spec" }, { - "from": "ADR-046-validation-and-delivery", - "to": "ADR-046-components-processes-and-sandbox", - "type": "spec-depends-on" + "from" : "ADR046-telem-017", + "to" : "ADR-046-telemetry-audit-and-support", + "type" : "implements-spec" }, { - "from": "ADR-046-validation-and-delivery", - "to": "ADR-046-componentsession-and-bus", - "type": "spec-depends-on" + "from" : "ADR046-telem-018", + "to" : "ADR-046-telemetry-audit-and-support", + "type" : "implements-spec" }, { - "from": "ADR-046-validation-and-delivery", - "to": "ADR-046-core-controllers", - "type": "spec-depends-on" + "from" : "ADR046-telem-019", + "to" : "ADR-046-telemetry-audit-and-support", + "type" : "implements-spec" }, { - "from": "ADR-046-validation-and-delivery", - "to": "ADR-046-current-code-migration-map", - "type": "spec-depends-on" + "from" : "ADR046-telem-020", + "to" : "ADR-046-telemetry-audit-and-support", + "type" : "implements-spec" }, { - "from": "ADR-046-validation-and-delivery", - "to": "ADR-046-decision-register", - "type": "spec-depends-on" + "from" : "ADR046-telem-021", + "to" : "ADR-046-telemetry-audit-and-support", + "type" : "implements-spec" }, { - "from": "ADR-046-validation-and-delivery", - "to": "ADR-046-feasibility-and-spikes", - "type": "shared-contract" + "from" : "ADR046-telem-022", + "to" : "ADR-046-telemetry-audit-and-support", + "type" : "implements-spec" }, { - "from": "ADR-046-validation-and-delivery", - "to": "ADR-046-nix-configuration", - "type": "spec-depends-on" + "from" : "ADR046-telem-023", + "to" : "ADR-046-telemetry-audit-and-support", + "type" : "implements-spec" }, { - "from": "ADR-046-validation-and-delivery", - "to": "ADR-046-primitive-resource-composition", - "type": "spec-depends-on" + "from" : "ADR046-telem-024", + "to" : "ADR-046-telemetry-audit-and-support", + "type" : "implements-spec" }, { - "from": "ADR-046-validation-and-delivery", - "to": "ADR-046-provider-activation-nixos", - "type": "spec-depends-on" + "from" : "ADR046-telem-025", + "to" : "ADR-046-telemetry-audit-and-support", + "type" : "implements-spec" }, { - "from": "ADR-046-validation-and-delivery", - "to": "ADR-046-provider-audio-pipewire", - "type": "spec-depends-on" + "from" : "ADR046-telem-026", + "to" : "ADR-046-telemetry-audit-and-support", + "type" : "implements-spec" }, { - "from": "ADR-046-validation-and-delivery", - "to": "ADR-046-provider-clipboard-wayland", - "type": "spec-depends-on" + "from" : "ADR046-telem-027", + "to" : "ADR-046-telemetry-audit-and-support", + "type" : "implements-spec" }, { - "from": "ADR-046-validation-and-delivery", - "to": "ADR-046-provider-credential-entra", - "type": "spec-depends-on" + "from" : "ADR046-transport-relay-001", + "to" : "ADR-046-provider-transport-azure-relay", + "type" : "implements-spec" }, { - "from": "ADR-046-validation-and-delivery", - "to": "ADR-046-provider-credential-managed-identity", - "type": "spec-depends-on" + "from" : "ADR046-transport-relay-002", + "to" : "ADR-046-provider-transport-azure-relay", + "type" : "implements-spec" }, { - "from": "ADR-046-validation-and-delivery", - "to": "ADR-046-provider-credential-secret-service", - "type": "spec-depends-on" + "from" : "ADR046-transport-relay-003", + "to" : "ADR-046-provider-transport-azure-relay", + "type" : "implements-spec" }, { - "from": "ADR-046-validation-and-delivery", - "to": "ADR-046-provider-device-gpu", - "type": "spec-depends-on" + "from" : "ADR046-transport-relay-004", + "to" : "ADR-046-provider-transport-azure-relay", + "type" : "implements-spec" }, { - "from": "ADR-046-validation-and-delivery", - "to": "ADR-046-provider-device-security-key", - "type": "spec-depends-on" + "from" : "ADR046-transport-relay-005", + "to" : "ADR-046-provider-transport-azure-relay", + "type" : "implements-spec" }, { - "from": "ADR-046-validation-and-delivery", - "to": "ADR-046-provider-device-tpm", - "type": "spec-depends-on" + "from" : "ADR046-transport-relay-006", + "to" : "ADR-046-provider-transport-azure-relay", + "type" : "implements-spec" }, { - "from": "ADR-046-validation-and-delivery", - "to": "ADR-046-provider-device-usbip", - "type": "spec-depends-on" + "from" : "ADR046-transport-relay-007", + "to" : "ADR-046-provider-transport-azure-relay", + "type" : "implements-spec" }, { - "from": "ADR-046-validation-and-delivery", - "to": "ADR-046-provider-display-wayland", - "type": "spec-depends-on" + "from" : "ADR046-transport-unix-001", + "to" : "ADR-046-provider-transport-unix", + "type" : "implements-spec" }, { - "from": "ADR-046-validation-and-delivery", - "to": "ADR-046-provider-model-and-packaging", - "type": "spec-depends-on" + "from" : "ADR046-transport-unix-002", + "to" : "ADR-046-provider-transport-unix", + "type" : "implements-spec" }, { - "from": "ADR-046-validation-and-delivery", - "to": "ADR-046-provider-network-local", - "type": "spec-depends-on" + "from" : "ADR046-transport-unix-003", + "to" : "ADR-046-provider-transport-unix", + "type" : "implements-spec" }, { - "from": "ADR-046-validation-and-delivery", - "to": "ADR-046-provider-notification-desktop", - "type": "spec-depends-on" + "from" : "ADR046-transport-unix-004", + "to" : "ADR-046-provider-transport-unix", + "type" : "implements-spec" }, { - "from": "ADR-046-validation-and-delivery", - "to": "ADR-046-provider-observability-otel", - "type": "spec-depends-on" + "from" : "ADR046-transport-unix-005", + "to" : "ADR-046-provider-transport-unix", + "type" : "implements-spec" }, { - "from": "ADR-046-validation-and-delivery", - "to": "ADR-046-provider-runtime-azure-container-apps", - "type": "spec-depends-on" + "from" : "ADR046-transport-unix-006", + "to" : "ADR-046-provider-transport-unix", + "type" : "implements-spec" }, { - "from": "ADR-046-validation-and-delivery", - "to": "ADR-046-provider-runtime-azure-virtual-machine", - "type": "spec-depends-on" + "from" : "ADR046-transport-unix-007", + "to" : "ADR-046-provider-transport-unix", + "type" : "implements-spec" }, { - "from": "ADR-046-validation-and-delivery", - "to": "ADR-046-provider-runtime-cloud-hypervisor", - "type": "spec-depends-on" + "from" : "ADR046-transport-unix-008", + "to" : "ADR-046-provider-transport-unix", + "type" : "implements-spec" }, { - "from": "ADR-046-validation-and-delivery", - "to": "ADR-046-provider-runtime-qemu-media", - "type": "spec-depends-on" + "from" : "ADR046-transport-unix-009", + "to" : "ADR-046-provider-transport-unix", + "type" : "implements-spec" }, { - "from": "ADR-046-validation-and-delivery", - "to": "ADR-046-provider-shell-terminal", - "type": "spec-depends-on" + "from" : "ADR046-transport-unix-010", + "to" : "ADR-046-provider-transport-unix", + "type" : "implements-spec" }, { - "from": "ADR-046-validation-and-delivery", - "to": "ADR-046-provider-state", - "type": "spec-depends-on" + "from" : "ADR046-transport-unix-011", + "to" : "ADR-046-provider-transport-unix", + "type" : "implements-spec" }, { - "from": "ADR-046-validation-and-delivery", - "to": "ADR-046-provider-system-core", - "type": "spec-depends-on" + "from" : "ADR046-usbip-001", + "to" : "ADR-046-provider-device-usbip", + "type" : "implements-spec" }, { - "from": "ADR-046-validation-and-delivery", - "to": "ADR-046-provider-system-minijail", - "type": "spec-depends-on" + "from" : "ADR046-usbip-002", + "to" : "ADR-046-provider-device-usbip", + "type" : "implements-spec" }, { - "from": "ADR-046-validation-and-delivery", - "to": "ADR-046-provider-system-systemd", - "type": "spec-depends-on" + "from" : "ADR046-usbip-003", + "to" : "ADR-046-provider-device-usbip", + "type" : "implements-spec" }, { - "from": "ADR-046-validation-and-delivery", - "to": "ADR-046-provider-transport-azure-relay", - "type": "spec-depends-on" + "from" : "ADR046-usbip-004", + "to" : "ADR-046-provider-device-usbip", + "type" : "implements-spec" }, { - "from": "ADR-046-validation-and-delivery", - "to": "ADR-046-provider-transport-unix", - "type": "spec-depends-on" + "from" : "ADR046-usbip-005", + "to" : "ADR-046-provider-device-usbip", + "type" : "implements-spec" }, { - "from": "ADR-046-validation-and-delivery", - "to": "ADR-046-provider-transport-vsock", - "type": "spec-depends-on" + "from" : "ADR046-usbip-006", + "to" : "ADR-046-provider-device-usbip", + "type" : "implements-spec" }, { - "from": "ADR-046-validation-and-delivery", - "to": "ADR-046-provider-volume-local", - "type": "spec-depends-on" + "from" : "ADR046-usbip-007", + "to" : "ADR-046-provider-device-usbip", + "type" : "implements-spec" }, { - "from": "ADR-046-validation-and-delivery", - "to": "ADR-046-provider-volume-virtiofs", - "type": "spec-depends-on" + "from" : "ADR046-usbip-008", + "to" : "ADR-046-provider-device-usbip", + "type" : "implements-spec" }, { - "from": "ADR-046-validation-and-delivery", - "to": "ADR-046-reset-and-cutover", - "type": "shared-contract" + "from" : "ADR046-usbip-009", + "to" : "ADR-046-provider-device-usbip", + "type" : "implements-spec" }, { - "from": "ADR-046-validation-and-delivery", - "to": "ADR-046-resource-api-and-authorization", - "type": "spec-depends-on" + "from" : "ADR046-vl-001", + "to" : "ADR-046-provider-volume-local", + "type" : "implements-spec" }, { - "from": "ADR-046-validation-and-delivery", - "to": "ADR-046-resource-object-model", - "type": "spec-depends-on" + "from" : "ADR046-vl-002", + "to" : "ADR-046-provider-volume-local", + "type" : "implements-spec" }, { - "from": "ADR-046-validation-and-delivery", - "to": "ADR-046-resource-reconciliation", - "type": "spec-depends-on" + "from" : "ADR046-vl-003", + "to" : "ADR-046-provider-volume-local", + "type" : "implements-spec" }, { - "from": "ADR-046-validation-and-delivery", - "to": "ADR-046-resource-store-redb", - "type": "spec-depends-on" + "from" : "ADR046-vl-004", + "to" : "ADR-046-provider-volume-local", + "type" : "implements-spec" }, { - "from": "ADR-046-validation-and-delivery", - "to": "ADR-046-resources-credential", - "type": "spec-depends-on" + "from" : "ADR046-vl-005", + "to" : "ADR-046-provider-volume-local", + "type" : "implements-spec" }, { - "from": "ADR-046-validation-and-delivery", - "to": "ADR-046-resources-device", - "type": "spec-depends-on" + "from" : "ADR046-vl-006", + "to" : "ADR-046-provider-volume-local", + "type" : "implements-spec" }, { - "from": "ADR-046-validation-and-delivery", - "to": "ADR-046-resources-host-guest-process-user", - "type": "spec-depends-on" + "from" : "ADR046-vl-007", + "to" : "ADR-046-provider-volume-local", + "type" : "implements-spec" }, { - "from": "ADR-046-validation-and-delivery", - "to": "ADR-046-resources-network", - "type": "spec-depends-on" + "from" : "ADR046-vl-008", + "to" : "ADR-046-provider-volume-local", + "type" : "implements-spec" }, { - "from": "ADR-046-validation-and-delivery", - "to": "ADR-046-resources-volume", - "type": "spec-depends-on" + "from" : "ADR046-vl-009", + "to" : "ADR-046-provider-volume-local", + "type" : "implements-spec" }, { - "from": "ADR-046-validation-and-delivery", - "to": "ADR-046-resources-zone-control", - "type": "spec-depends-on" + "from" : "ADR046-vl-010", + "to" : "ADR-046-provider-volume-local", + "type" : "implements-spec" }, { - "from": "ADR-046-validation-and-delivery", - "to": "ADR-046-security-and-threat-model", - "type": "shared-contract" + "from" : "ADR046-vl-011", + "to" : "ADR-046-provider-volume-local", + "type" : "implements-spec" }, { - "from": "ADR-046-validation-and-delivery", - "to": "ADR-046-streamline", - "type": "shared-contract" + "from" : "ADR046-vl-012", + "to" : "ADR-046-provider-volume-local", + "type" : "implements-spec" }, { - "from": "ADR-046-validation-and-delivery", - "to": "ADR-046-telemetry-audit-and-support", - "type": "spec-depends-on" + "from" : "ADR046-vl-013", + "to" : "ADR-046-provider-volume-local", + "type" : "implements-spec" }, { - "from": "ADR-046-validation-and-delivery", - "to": "ADR-046-terminology-and-identities", - "type": "spec-depends-on" + "from" : "ADR046-volume-001", + "to" : "ADR-046-resources-volume", + "type" : "implements-spec" }, { - "from": "ADR-046-validation-and-delivery", - "to": "ADR-046-zone-routing", - "type": "spec-depends-on" + "from" : "ADR046-volume-002", + "to" : "ADR-046-resources-volume", + "type" : "implements-spec" }, { - "from": "ADR-046-zone-routing", - "to": "ADR-046-componentsession-and-bus", - "type": "spec-depends-on" + "from" : "ADR046-volume-003", + "to" : "ADR-046-resources-volume", + "type" : "implements-spec" }, { - "from": "ADR-046-zone-routing", - "to": "ADR-046-resource-api-and-authorization", - "type": "spec-depends-on" + "from" : "ADR046-volume-004", + "to" : "ADR-046-resources-volume", + "type" : "implements-spec" }, { - "from": "ADR-046-zone-routing", - "to": "ADR-046-resource-object-model", - "type": "spec-depends-on" + "from" : "ADR046-volume-005", + "to" : "ADR-046-resources-volume", + "type" : "implements-spec" }, { - "from": "ADR-046-zone-routing", - "to": "ADR-046-resource-reconciliation", - "type": "spec-depends-on" + "from" : "ADR046-volume-006", + "to" : "ADR-046-resources-volume", + "type" : "implements-spec" }, { - "from": "ADR-046-zone-routing", - "to": "ADR-046-terminology-and-identities", - "type": "spec-depends-on" + "from" : "ADR046-vsock-001", + "to" : "ADR-046-provider-transport-vsock", + "type" : "implements-spec" }, { - "from": "ADR046-aca-001", - "to": "ADR-046-provider-runtime-azure-container-apps", - "type": "implements-spec" + "from" : "ADR046-vsock-002", + "to" : "ADR-046-provider-transport-vsock", + "type" : "implements-spec" }, { - "from": "ADR046-aca-001", - "to": "ADR046-provider-001", - "type": "work-item-depends-on" + "from" : "ADR046-vsock-003", + "to" : "ADR-046-provider-transport-vsock", + "type" : "implements-spec" }, { - "from": "ADR046-aca-002", - "to": "ADR-046-provider-runtime-azure-container-apps", - "type": "implements-spec" + "from" : "ADR046-vsock-004", + "to" : "ADR-046-provider-transport-vsock", + "type" : "implements-spec" }, { - "from": "ADR046-aca-002", - "to": "ADR046-aca-001", - "type": "work-item-depends-on" + "from" : "ADR046-vsock-005", + "to" : "ADR-046-provider-transport-vsock", + "type" : "implements-spec" }, { - "from": "ADR046-aca-003", - "to": "ADR-046-provider-runtime-azure-container-apps", - "type": "implements-spec" + "from" : "ADR046-vsock-006", + "to" : "ADR-046-provider-transport-vsock", + "type" : "implements-spec" }, { - "from": "ADR046-aca-003", - "to": "ADR046-aca-001", - "type": "work-item-depends-on" + "from" : "ADR046-vsock-007", + "to" : "ADR-046-provider-transport-vsock", + "type" : "implements-spec" }, { - "from": "ADR046-aca-004", - "to": "ADR-046-provider-runtime-azure-container-apps", - "type": "implements-spec" + "from" : "ADR046-vvfs-001", + "to" : "ADR-046-provider-volume-virtiofs", + "type" : "implements-spec" }, { - "from": "ADR046-aca-004", - "to": "ADR046-session-001", - "type": "work-item-depends-on" + "from" : "ADR046-vvfs-002", + "to" : "ADR-046-provider-volume-virtiofs", + "type" : "implements-spec" }, { - "from": "ADR046-aca-005", - "to": "ADR-046-provider-runtime-azure-container-apps", - "type": "implements-spec" + "from" : "ADR046-vvfs-003", + "to" : "ADR-046-provider-volume-virtiofs", + "type" : "implements-spec" }, { - "from": "ADR046-aca-005", - "to": "ADR046-aca-001", - "type": "work-item-depends-on" + "from" : "ADR046-vvfs-004", + "to" : "ADR-046-provider-volume-virtiofs", + "type" : "implements-spec" }, { - "from": "ADR046-aca-006", - "to": "ADR-046-provider-runtime-azure-container-apps", - "type": "implements-spec" + "from" : "ADR046-vvfs-005", + "to" : "ADR-046-provider-volume-virtiofs", + "type" : "implements-spec" }, { - "from": "ADR046-aca-006", - "to": "ADR046-aca-001", - "type": "work-item-depends-on" + "from" : "ADR046-vvfs-006", + "to" : "ADR-046-provider-volume-virtiofs", + "type" : "implements-spec" }, { - "from": "ADR046-aca-007", - "to": "ADR-046-provider-runtime-azure-container-apps", - "type": "implements-spec" + "from" : "ADR046-vvfs-007", + "to" : "ADR-046-provider-volume-virtiofs", + "type" : "implements-spec" }, { - "from": "ADR046-aca-007", - "to": "ADR046-aca-001", - "type": "work-item-depends-on" + "from" : "ADR046-zone-control-001", + "to" : "ADR-046-resources-zone-control", + "type" : "implements-spec" }, { - "from": "ADR046-activation-001", - "to": "ADR-046-provider-activation-nixos", - "type": "implements-spec" + "from" : "ADR046-zone-control-002", + "to" : "ADR-046-resources-zone-control", + "type" : "implements-spec" }, { - "from": "ADR046-activation-002", - "to": "ADR-046-provider-activation-nixos", - "type": "implements-spec" + "from" : "ADR046-zone-control-003", + "to" : "ADR-046-resources-zone-control", + "type" : "implements-spec" }, { - "from": "ADR046-activation-003", - "to": "ADR-046-provider-activation-nixos", - "type": "implements-spec" + "from" : "ADR046-zone-control-004", + "to" : "ADR-046-resources-zone-control", + "type" : "implements-spec" }, { - "from": "ADR046-activation-003", - "to": "ADR046-activation-002", - "type": "work-item-depends-on" + "from" : "ADR046-zone-control-005", + "to" : "ADR-046-resources-zone-control", + "type" : "implements-spec" }, { - "from": "ADR046-activation-004", - "to": "ADR-046-provider-activation-nixos", - "type": "implements-spec" + "from" : "ADR046-zone-control-006", + "to" : "ADR-046-resources-zone-control", + "type" : "implements-spec" }, { - "from": "ADR046-activation-004", - "to": "ADR046-activation-003", - "type": "work-item-depends-on" + "from" : "ADR046-zone-control-007", + "to" : "ADR-046-resources-zone-control", + "type" : "implements-spec" }, { - "from": "ADR046-activation-005", - "to": "ADR-046-provider-activation-nixos", - "type": "implements-spec" + "from" : "ADR046-zone-control-008", + "to" : "ADR-046-resources-zone-control", + "type" : "implements-spec" }, { - "from": "ADR046-activation-006", - "to": "ADR-046-provider-activation-nixos", - "type": "implements-spec" + "from" : "ADR046-zone-control-009", + "to" : "ADR-046-resources-zone-control", + "type" : "implements-spec" }, { - "from": "ADR046-activation-007", - "to": "ADR-046-provider-activation-nixos", - "type": "implements-spec" + "from" : "ADR046-zone-control-010", + "to" : "ADR-046-resources-zone-control", + "type" : "implements-spec" }, { - "from": "ADR046-activation-007", - "to": "ADR046-activation-005", - "type": "work-item-depends-on" + "from" : "ADR046-zone-control-011", + "to" : "ADR-046-resources-zone-control", + "type" : "implements-spec" }, { - "from": "ADR046-api-001", - "to": "ADR-046-resource-api-and-authorization", - "type": "implements-spec" + "from" : "ADR046-zone-control-012", + "to" : "ADR-046-resources-zone-control", + "type" : "implements-spec" }, { - "from": "ADR046-api-002", - "to": "ADR-046-resource-api-and-authorization", - "type": "implements-spec" + "from" : "ADR046-zone-control-013", + "to" : "ADR-046-resources-zone-control", + "type" : "implements-spec" }, { - "from": "ADR046-api-002", - "to": "ADR046-api-001", - "type": "work-item-depends-on" + "from" : "ADR046-zone-control-014", + "to" : "ADR-046-resources-zone-control", + "type" : "implements-spec" }, { - "from": "ADR046-audio-001", - "to": "ADR-046-provider-audio-pipewire", - "type": "implements-spec" + "from" : "ADR046-zone-control-015", + "to" : "ADR-046-resources-zone-control", + "type" : "implements-spec" }, { - "from": "ADR046-audio-002", - "to": "ADR-046-provider-audio-pipewire", - "type": "implements-spec" + "from" : "ADR046-zone-control-016", + "to" : "ADR-046-resources-zone-control", + "type" : "implements-spec" }, { - "from": "ADR046-audio-002", - "to": "ADR046-audio-001", - "type": "work-item-depends-on" + "from" : "ADR046-zone-control-017", + "to" : "ADR-046-resources-zone-control", + "type" : "implements-spec" }, { - "from": "ADR046-audio-004", - "to": "ADR-046-provider-audio-pipewire", - "type": "implements-spec" + "from" : "ADR046-zone-control-018", + "to" : "ADR-046-resources-zone-control", + "type" : "implements-spec" }, { - "from": "ADR046-audio-004", - "to": "ADR046-audio-001", - "type": "work-item-depends-on" + "from" : "ADR046-zone-control-019", + "to" : "ADR-046-resources-zone-control", + "type" : "implements-spec" }, { - "from": "ADR046-audio-005", - "to": "ADR-046-provider-audio-pipewire", - "type": "implements-spec" + "from" : "ADR046-zone-control-020", + "to" : "ADR-046-resources-zone-control", + "type" : "implements-spec" }, { - "from": "ADR046-audio-005", - "to": "ADR046-provider-004", - "type": "work-item-depends-on" + "from" : "ADR046-zone-control-021", + "to" : "ADR-046-resources-zone-control", + "type" : "implements-spec" }, { - "from": "ADR046-audio-006", - "to": "ADR-046-provider-audio-pipewire", - "type": "implements-spec" + "from" : "ADR046-zone-control-022", + "to" : "ADR-046-resources-zone-control", + "type" : "implements-spec" }, { - "from": "ADR046-audio-006", - "to": "ADR046-audio-001", - "type": "work-item-depends-on" + "from" : "ADR046-zone-control-023", + "to" : "ADR-046-resources-zone-control", + "type" : "implements-spec" }, { - "from": "ADR046-audio-006", - "to": "ADR046-audio-005", - "type": "work-item-depends-on" + "from" : "ADR046-zone-control-024", + "to" : "ADR-046-resources-zone-control", + "type" : "implements-spec" }, { - "from": "ADR046-audio-006", - "to": "ADR046-audio-007", - "type": "work-item-depends-on" + "from" : "ADR046-zone-control-025", + "to" : "ADR-046-resources-zone-control", + "type" : "implements-spec" }, { - "from": "ADR046-audio-006", - "to": "ADR046-audio-011", - "type": "work-item-depends-on" + "from" : "ADR046-zone-control-026", + "to" : "ADR-046-resources-zone-control", + "type" : "implements-spec" }, { - "from": "ADR046-audio-007", - "to": "ADR-046-provider-audio-pipewire", - "type": "implements-spec" + "from" : "ADR046-zone-control-027", + "to" : "ADR-046-resources-zone-control", + "type" : "implements-spec" }, { - "from": "ADR046-audio-007", - "to": "ADR046-audio-004", - "type": "work-item-depends-on" + "from" : "ADR046-zone-control-028", + "to" : "ADR-046-resources-zone-control", + "type" : "implements-spec" }, { - "from": "ADR046-audio-008", - "to": "ADR-046-provider-audio-pipewire", - "type": "implements-spec" + "from" : "ADR-046-resource-api-and-authorization", + "to" : "ADR-046-resource-object-model", + "type" : "shared-contract" }, { - "from": "ADR046-audio-008", - "to": "ADR046-audio-005", - "type": "work-item-depends-on" + "from" : "ADR-046-resource-api-and-authorization", + "to" : "ADR-046-resource-store-redb", + "type" : "shared-contract" }, { - "from": "ADR046-audio-009", - "to": "ADR-046-provider-audio-pipewire", - "type": "implements-spec" + "from" : "ADR-046-resource-api-and-authorization", + "to" : "ADR-046-terminology-and-identities", + "type" : "shared-contract" }, { - "from": "ADR046-audio-009", - "to": "ADR046-audio-005", - "type": "work-item-depends-on" + "from" : "ADR-046-resource-object-model", + "to" : "ADR-046-decision-register", + "type" : "shared-contract" }, { - "from": "ADR046-audio-010", - "to": "ADR-046-provider-audio-pipewire", - "type": "implements-spec" + "from" : "ADR-046-resource-object-model", + "to" : "ADR-046-terminology-and-identities", + "type" : "shared-contract" }, { - "from": "ADR046-audio-010", - "to": "ADR046-audio-006", - "type": "work-item-depends-on" + "from" : "ADR-046-resource-store-redb", + "to" : "ADR-046-resource-object-model", + "type" : "shared-contract" }, { - "from": "ADR046-audio-010", - "to": "ADR046-audio-007", - "type": "work-item-depends-on" + "from" : "ADR-046-resource-store-redb", + "to" : "ADR-046-terminology-and-identities", + "type" : "shared-contract" }, { - "from": "ADR046-audio-010", - "to": "ADR046-audio-011", - "type": "work-item-depends-on" + "from" : "ADR-046-streamline", + "to" : "ADR-046-reset-and-cutover", + "type" : "shared-contract" }, { - "from": "ADR046-audio-011", - "to": "ADR-046-provider-audio-pipewire", - "type": "implements-spec" + "from" : "ADR-046-terminology-and-identities", + "to" : "ADR-046-decision-register", + "type" : "shared-contract" }, { - "from": "ADR046-audio-011", - "to": "ADR046-audio-004", - "type": "work-item-depends-on" + "from" : "ADR-046-validation-and-delivery", + "to" : "ADR-046-feasibility-and-spikes", + "type" : "shared-contract" }, { - "from": "ADR046-audio-012", - "to": "ADR-046-provider-audio-pipewire", - "type": "implements-spec" + "from" : "ADR-046-validation-and-delivery", + "to" : "ADR-046-reset-and-cutover", + "type" : "shared-contract" }, { - "from": "ADR046-audio-012", - "to": "ADR046-zone-control-019", - "type": "work-item-depends-on" + "from" : "ADR-046-validation-and-delivery", + "to" : "ADR-046-security-and-threat-model", + "type" : "shared-contract" }, { - "from": "ADR046-audio-012", - "to": "ADR046-zone-control-020", - "type": "work-item-depends-on" + "from" : "ADR-046-validation-and-delivery", + "to" : "ADR-046-streamline", + "type" : "shared-contract" }, { - "from": "ADR046-audio-013", - "to": "ADR-046-provider-audio-pipewire", - "type": "implements-spec" + "from" : "ADR-046-cli-and-operations", + "to" : "ADR-046-components-processes-and-sandbox", + "type" : "spec-depends-on" }, { - "from": "ADR046-audio-013", - "to": "ADR046-audio-001", - "type": "work-item-depends-on" + "from" : "ADR-046-cli-and-operations", + "to" : "ADR-046-componentsession-and-bus", + "type" : "spec-depends-on" }, { - "from": "ADR046-audio-013", - "to": "ADR046-audio-004", - "type": "work-item-depends-on" + "from" : "ADR-046-cli-and-operations", + "to" : "ADR-046-provider-model-and-packaging", + "type" : "spec-depends-on" }, { - "from": "ADR046-audio-013", - "to": "ADR046-zone-control-019", - "type": "work-item-depends-on" + "from" : "ADR-046-cli-and-operations", + "to" : "ADR-046-resource-api-and-authorization", + "type" : "spec-depends-on" }, { - "from": "ADR046-audio-014", - "to": "ADR-046-provider-audio-pipewire", - "type": "implements-spec" + "from" : "ADR-046-cli-and-operations", + "to" : "ADR-046-resource-object-model", + "type" : "spec-depends-on" }, { - "from": "ADR046-audio-014", - "to": "ADR046-audio-013", - "type": "work-item-depends-on" + "from" : "ADR-046-cli-and-operations", + "to" : "ADR-046-terminology-and-identities", + "type" : "spec-depends-on" }, { - "from": "ADR046-audio-014", - "to": "ADR046-zone-control-019", - "type": "work-item-depends-on" + "from" : "ADR-046-components-processes-and-sandbox", + "to" : "ADR-046-componentsession-and-bus", + "type" : "spec-depends-on" }, { - "from": "ADR046-audit-001", - "to": "ADR-046-telemetry-audit-and-support", - "type": "implements-spec" + "from" : "ADR-046-components-processes-and-sandbox", + "to" : "ADR-046-primitive-resource-composition", + "type" : "spec-depends-on" }, { - "from": "ADR046-audit-002", - "to": "ADR-046-telemetry-audit-and-support", - "type": "implements-spec" + "from" : "ADR-046-components-processes-and-sandbox", + "to" : "ADR-046-provider-model-and-packaging", + "type" : "spec-depends-on" }, { - "from": "ADR046-audit-002", - "to": "ADR046-audit-001", - "type": "work-item-depends-on" + "from" : "ADR-046-componentsession-and-bus", + "to" : "ADR-046-resource-api-and-authorization", + "type" : "spec-depends-on" }, { - "from": "ADR046-audit-002", - "to": "ADR046-store-001", - "type": "work-item-depends-on" + "from" : "ADR-046-componentsession-and-bus", + "to" : "ADR-046-terminology-and-identities", + "type" : "spec-depends-on" }, { - "from": "ADR046-audit-003", - "to": "ADR-046-telemetry-audit-and-support", - "type": "implements-spec" + "from" : "ADR-046-core-controllers", + "to" : "ADR-046-provider-model-and-packaging", + "type" : "spec-depends-on" }, { - "from": "ADR046-audit-003", - "to": "ADR046-audit-001", - "type": "work-item-depends-on" + "from" : "ADR-046-core-controllers", + "to" : "ADR-046-resource-api-and-authorization", + "type" : "spec-depends-on" }, { - "from": "ADR046-audit-003", - "to": "ADR046-bus-001", - "type": "work-item-depends-on" + "from" : "ADR-046-core-controllers", + "to" : "ADR-046-resource-reconciliation", + "type" : "spec-depends-on" }, { - "from": "ADR046-audit-003", - "to": "ADR046-session-001", - "type": "work-item-depends-on" + "from" : "ADR-046-core-controllers", + "to" : "ADR-046-resource-store-redb", + "type" : "spec-depends-on" }, { - "from": "ADR046-audit-004", - "to": "ADR-046-telemetry-audit-and-support", - "type": "implements-spec" + "from" : "ADR-046-feasibility-and-spikes", + "to" : "ADR-046-cli-and-operations", + "type" : "spec-depends-on" }, { - "from": "ADR046-audit-004", - "to": "ADR046-audit-001", - "type": "work-item-depends-on" + "from" : "ADR-046-feasibility-and-spikes", + "to" : "ADR-046-components-processes-and-sandbox", + "type" : "spec-depends-on" }, { - "from": "ADR046-azure-vm-001", - "to": "ADR-046-provider-runtime-azure-virtual-machine", - "type": "implements-spec" + "from" : "ADR-046-feasibility-and-spikes", + "to" : "ADR-046-componentsession-and-bus", + "type" : "spec-depends-on" }, { - "from": "ADR046-azure-vm-002", - "to": "ADR-046-provider-runtime-azure-virtual-machine", - "type": "implements-spec" + "from" : "ADR-046-feasibility-and-spikes", + "to" : "ADR-046-current-code-migration-map", + "type" : "spec-depends-on" }, { - "from": "ADR046-azure-vm-003", - "to": "ADR-046-provider-runtime-azure-virtual-machine", - "type": "implements-spec" + "from" : "ADR-046-feasibility-and-spikes", + "to" : "ADR-046-decision-register", + "type" : "spec-depends-on" }, { - "from": "ADR046-azure-vm-004", - "to": "ADR-046-provider-runtime-azure-virtual-machine", - "type": "implements-spec" + "from" : "ADR-046-feasibility-and-spikes", + "to" : "ADR-046-nix-configuration", + "type" : "spec-depends-on" }, { - "from": "ADR046-azure-vm-005", - "to": "ADR-046-provider-runtime-azure-virtual-machine", - "type": "implements-spec" + "from" : "ADR-046-feasibility-and-spikes", + "to" : "ADR-046-provider-model-and-packaging", + "type" : "spec-depends-on" }, { - "from": "ADR046-azure-vm-006", - "to": "ADR-046-provider-runtime-azure-virtual-machine", - "type": "implements-spec" + "from" : "ADR-046-feasibility-and-spikes", + "to" : "ADR-046-provider-state", + "type" : "spec-depends-on" }, { - "from": "ADR046-azure-vm-007", - "to": "ADR-046-provider-runtime-azure-virtual-machine", - "type": "implements-spec" + "from" : "ADR-046-feasibility-and-spikes", + "to" : "ADR-046-resource-reconciliation", + "type" : "spec-depends-on" }, { - "from": "ADR046-azure-vm-008", - "to": "ADR-046-provider-runtime-azure-virtual-machine", - "type": "implements-spec" + "from" : "ADR-046-feasibility-and-spikes", + "to" : "ADR-046-resource-store-redb", + "type" : "spec-depends-on" }, { - "from": "ADR046-azure-vm-009", - "to": "ADR-046-provider-runtime-azure-virtual-machine", - "type": "implements-spec" + "from" : "ADR-046-feasibility-and-spikes", + "to" : "ADR-046-resources-credential", + "type" : "spec-depends-on" }, { - "from": "ADR046-bus-001", - "to": "ADR-046-componentsession-and-bus", - "type": "implements-spec" + "from" : "ADR-046-feasibility-and-spikes", + "to" : "ADR-046-resources-host-guest-process-user", + "type" : "spec-depends-on" }, { - "from": "ADR046-bus-009", - "to": "ADR-046-nix-configuration", - "type": "implements-spec" + "from" : "ADR-046-feasibility-and-spikes", + "to" : "ADR-046-resources-volume", + "type" : "spec-depends-on" }, { - "from": "ADR046-bus-009", - "to": "ADR046-nix-027", - "type": "work-item-depends-on" + "from" : "ADR-046-feasibility-and-spikes", + "to" : "ADR-046-terminology-and-identities", + "type" : "spec-depends-on" }, { - "from": "ADR046-bus-009", - "to": "ADR046-nix-029", - "type": "work-item-depends-on" + "from" : "ADR-046-feasibility-and-spikes", + "to" : "ADR-046-zone-routing", + "type" : "spec-depends-on" }, { - "from": "ADR046-bus-010", - "to": "ADR-046-nix-configuration", - "type": "implements-spec" + "from" : "ADR-046-nix-configuration", + "to" : "ADR-046-core-controllers", + "type" : "spec-depends-on" }, { - "from": "ADR046-bus-010", - "to": "ADR046-nix-023", - "type": "work-item-depends-on" + "from" : "ADR-046-nix-configuration", + "to" : "ADR-046-primitive-resource-composition", + "type" : "spec-depends-on" }, { - "from": "ADR046-bus-010", - "to": "ADR046-nix-027", - "type": "work-item-depends-on" + "from" : "ADR-046-nix-configuration", + "to" : "ADR-046-provider-model-and-packaging", + "type" : "spec-depends-on" }, { - "from": "ADR046-bus-011", - "to": "ADR-046-nix-configuration", - "type": "implements-spec" + "from" : "ADR-046-nix-configuration", + "to" : "ADR-046-resource-object-model", + "type" : "spec-depends-on" }, { - "from": "ADR046-bus-011", - "to": "ADR046-nix-027", - "type": "work-item-depends-on" + "from" : "ADR-046-nix-configuration", + "to" : "ADR-046-terminology-and-identities", + "type" : "spec-depends-on" }, { - "from": "ADR046-bus-011", - "to": "ADR046-nix-028", - "type": "work-item-depends-on" + "from" : "ADR-046-primitive-resource-composition", + "to" : "ADR-046-resource-object-model", + "type" : "spec-depends-on" }, { - "from": "ADR046-bus-011", - "to": "ADR046-nix-029", - "type": "work-item-depends-on" + "from" : "ADR-046-primitive-resource-composition", + "to" : "ADR-046-resource-reconciliation", + "type" : "spec-depends-on" }, { - "from": "ADR046-bus-012", - "to": "ADR-046-nix-configuration", - "type": "implements-spec" + "from" : "ADR-046-provider-activation-nixos", + "to" : "ADR-046-cli-and-operations", + "type" : "spec-depends-on" }, { - "from": "ADR046-bus-012", - "to": "ADR046-bus-011", - "type": "work-item-depends-on" + "from" : "ADR-046-provider-activation-nixos", + "to" : "ADR-046-componentsession-and-bus", + "type" : "spec-depends-on" }, { - "from": "ADR046-bus-012", - "to": "ADR046-nix-029", - "type": "work-item-depends-on" + "from" : "ADR-046-provider-activation-nixos", + "to" : "ADR-046-core-controllers", + "type" : "spec-depends-on" }, { - "from": "ADR046-ch-001", - "to": "ADR-046-provider-runtime-cloud-hypervisor", - "type": "implements-spec" + "from" : "ADR-046-provider-activation-nixos", + "to" : "ADR-046-nix-configuration", + "type" : "spec-depends-on" }, { - "from": "ADR046-ch-002", - "to": "ADR-046-provider-runtime-cloud-hypervisor", - "type": "implements-spec" + "from" : "ADR-046-provider-activation-nixos", + "to" : "ADR-046-provider-model-and-packaging", + "type" : "spec-depends-on" }, { - "from": "ADR046-ch-002", - "to": "ADR046-ch-001", - "type": "work-item-depends-on" + "from" : "ADR-046-provider-activation-nixos", + "to" : "ADR-046-provider-state", + "type" : "spec-depends-on" }, { - "from": "ADR046-ch-003", - "to": "ADR-046-provider-runtime-cloud-hypervisor", - "type": "implements-spec" + "from" : "ADR-046-provider-activation-nixos", + "to" : "ADR-046-resource-api-and-authorization", + "type" : "spec-depends-on" }, { - "from": "ADR046-ch-003", - "to": "ADR046-ch-002", - "type": "work-item-depends-on" + "from" : "ADR-046-provider-activation-nixos", + "to" : "ADR-046-resource-object-model", + "type" : "spec-depends-on" }, { - "from": "ADR046-ch-004", - "to": "ADR-046-provider-runtime-cloud-hypervisor", - "type": "implements-spec" + "from" : "ADR-046-provider-activation-nixos", + "to" : "ADR-046-resources-host-guest-process-user", + "type" : "spec-depends-on" }, { - "from": "ADR046-ch-004", - "to": "ADR046-ch-002", - "type": "work-item-depends-on" + "from" : "ADR-046-provider-activation-nixos", + "to" : "ADR-046-resources-zone-control", + "type" : "spec-depends-on" }, { - "from": "ADR046-ch-004", - "to": "ADR046-identities-002", - "type": "work-item-depends-on" + "from" : "ADR-046-provider-activation-nixos", + "to" : "ADR-046-telemetry-audit-and-support", + "type" : "spec-depends-on" }, { - "from": "ADR046-ch-005", - "to": "ADR-046-provider-runtime-cloud-hypervisor", - "type": "implements-spec" + "from" : "ADR-046-provider-audio-pipewire", + "to" : "ADR-046-components-processes-and-sandbox", + "type" : "spec-depends-on" }, { - "from": "ADR046-ch-005", - "to": "ADR046-ch-001", - "type": "work-item-depends-on" + "from" : "ADR-046-provider-audio-pipewire", + "to" : "ADR-046-componentsession-and-bus", + "type" : "spec-depends-on" }, { - "from": "ADR046-ch-005", - "to": "ADR046-session-001", - "type": "work-item-depends-on" + "from" : "ADR-046-provider-audio-pipewire", + "to" : "ADR-046-nix-configuration", + "type" : "spec-depends-on" }, { - "from": "ADR046-ch-006", - "to": "ADR-046-provider-runtime-cloud-hypervisor", - "type": "implements-spec" + "from" : "ADR-046-provider-audio-pipewire", + "to" : "ADR-046-primitive-resource-composition", + "type" : "spec-depends-on" }, { - "from": "ADR046-ch-006", - "to": "ADR046-ch-001", - "type": "work-item-depends-on" + "from" : "ADR-046-provider-audio-pipewire", + "to" : "ADR-046-provider-model-and-packaging", + "type" : "spec-depends-on" }, { - "from": "ADR046-ch-006", - "to": "ADR046-telem-001", - "type": "work-item-depends-on" + "from" : "ADR-046-provider-audio-pipewire", + "to" : "ADR-046-provider-state", + "type" : "spec-depends-on" }, { - "from": "ADR046-ch-007", - "to": "ADR-046-provider-runtime-cloud-hypervisor", - "type": "implements-spec" + "from" : "ADR-046-provider-audio-pipewire", + "to" : "ADR-046-resource-api-and-authorization", + "type" : "spec-depends-on" }, { - "from": "ADR046-ch-007", - "to": "ADR046-ch-001", - "type": "work-item-depends-on" + "from" : "ADR-046-provider-audio-pipewire", + "to" : "ADR-046-resource-object-model", + "type" : "spec-depends-on" }, { - "from": "ADR046-ch-007", - "to": "ADR046-pstate-001", - "type": "work-item-depends-on" + "from" : "ADR-046-provider-audio-pipewire", + "to" : "ADR-046-resource-reconciliation", + "type" : "spec-depends-on" }, { - "from": "ADR046-cli-001", - "to": "ADR-046-cli-and-operations", - "type": "implements-spec" + "from" : "ADR-046-provider-audio-pipewire", + "to" : "ADR-046-resources-host-guest-process-user", + "type" : "spec-depends-on" }, { - "from": "ADR046-cli-001", - "to": "ADR046-api-001", - "type": "work-item-depends-on" + "from" : "ADR-046-provider-audio-pipewire", + "to" : "ADR-046-resources-volume", + "type" : "spec-depends-on" }, { - "from": "ADR046-cli-001", - "to": "ADR046-identities-001", - "type": "work-item-depends-on" + "from" : "ADR-046-provider-audio-pipewire", + "to" : "ADR-046-telemetry-audit-and-support", + "type" : "spec-depends-on" }, { - "from": "ADR046-cli-002", - "to": "ADR-046-cli-and-operations", - "type": "implements-spec" + "from" : "ADR-046-provider-clipboard-wayland", + "to" : "ADR-046-componentsession-and-bus", + "type" : "spec-depends-on" }, { - "from": "ADR046-cli-002", - "to": "ADR046-api-001", - "type": "work-item-depends-on" + "from" : "ADR-046-provider-clipboard-wayland", + "to" : "ADR-046-core-controllers", + "type" : "spec-depends-on" }, { - "from": "ADR046-cli-002", - "to": "ADR046-cli-001", - "type": "work-item-depends-on" + "from" : "ADR-046-provider-clipboard-wayland", + "to" : "ADR-046-nix-configuration", + "type" : "spec-depends-on" }, { - "from": "ADR046-cli-003", - "to": "ADR-046-cli-and-operations", - "type": "implements-spec" + "from" : "ADR-046-provider-clipboard-wayland", + "to" : "ADR-046-provider-model-and-packaging", + "type" : "spec-depends-on" }, { - "from": "ADR046-cli-003", - "to": "ADR046-api-001", - "type": "work-item-depends-on" + "from" : "ADR-046-provider-clipboard-wayland", + "to" : "ADR-046-provider-state", + "type" : "spec-depends-on" }, { - "from": "ADR046-cli-003", - "to": "ADR046-cli-001", - "type": "work-item-depends-on" + "from" : "ADR-046-provider-clipboard-wayland", + "to" : "ADR-046-resource-reconciliation", + "type" : "spec-depends-on" }, { - "from": "ADR046-cli-004", - "to": "ADR-046-cli-and-operations", - "type": "implements-spec" + "from" : "ADR-046-provider-clipboard-wayland", + "to" : "ADR-046-resources-host-guest-process-user", + "type" : "spec-depends-on" }, { - "from": "ADR046-cli-004", - "to": "ADR046-cli-001", - "type": "work-item-depends-on" + "from" : "ADR-046-provider-clipboard-wayland", + "to" : "ADR-046-telemetry-audit-and-support", + "type" : "spec-depends-on" }, { - "from": "ADR046-cli-005", - "to": "ADR-046-cli-and-operations", - "type": "implements-spec" + "from" : "ADR-046-provider-credential-entra", + "to" : "ADR-046-componentsession-and-bus", + "type" : "spec-depends-on" }, { - "from": "ADR046-cli-005", - "to": "ADR046-cli-001", - "type": "work-item-depends-on" + "from" : "ADR-046-provider-credential-entra", + "to" : "ADR-046-nix-configuration", + "type" : "spec-depends-on" }, { - "from": "ADR046-cli-006", - "to": "ADR-046-cli-and-operations", - "type": "implements-spec" + "from" : "ADR-046-provider-credential-entra", + "to" : "ADR-046-provider-model-and-packaging", + "type" : "spec-depends-on" }, { - "from": "ADR046-cli-006", - "to": "ADR046-cli-001", - "type": "work-item-depends-on" + "from" : "ADR-046-provider-credential-entra", + "to" : "ADR-046-resource-reconciliation", + "type" : "spec-depends-on" }, { - "from": "ADR046-cli-007", - "to": "ADR-046-cli-and-operations", - "type": "implements-spec" + "from" : "ADR-046-provider-credential-entra", + "to" : "ADR-046-resources-credential", + "type" : "spec-depends-on" }, { - "from": "ADR046-cli-007", - "to": "ADR046-cli-001", - "type": "work-item-depends-on" + "from" : "ADR-046-provider-credential-entra", + "to" : "ADR-046-resources-host-guest-process-user", + "type" : "spec-depends-on" }, { - "from": "ADR046-cli-008", - "to": "ADR-046-cli-and-operations", - "type": "implements-spec" + "from" : "ADR-046-provider-credential-entra", + "to" : "ADR-046-telemetry-audit-and-support", + "type" : "spec-depends-on" }, { - "from": "ADR046-cli-008", - "to": "ADR046-cli-001", - "type": "work-item-depends-on" + "from" : "ADR-046-provider-credential-managed-identity", + "to" : "ADR-046-componentsession-and-bus", + "type" : "spec-depends-on" }, { - "from": "ADR046-cli-009", - "to": "ADR-046-cli-and-operations", - "type": "implements-spec" + "from" : "ADR-046-provider-credential-managed-identity", + "to" : "ADR-046-nix-configuration", + "type" : "spec-depends-on" }, { - "from": "ADR046-cli-009", - "to": "ADR046-cli-001", - "type": "work-item-depends-on" + "from" : "ADR-046-provider-credential-managed-identity", + "to" : "ADR-046-provider-model-and-packaging", + "type" : "spec-depends-on" }, { - "from": "ADR046-cli-010", - "to": "ADR-046-cli-and-operations", - "type": "implements-spec" + "from" : "ADR-046-provider-credential-managed-identity", + "to" : "ADR-046-resource-reconciliation", + "type" : "spec-depends-on" }, { - "from": "ADR046-cli-010", - "to": "ADR046-cli-001", - "type": "work-item-depends-on" + "from" : "ADR-046-provider-credential-managed-identity", + "to" : "ADR-046-resources-credential", + "type" : "spec-depends-on" }, { - "from": "ADR046-cli-011", - "to": "ADR-046-cli-and-operations", - "type": "implements-spec" + "from" : "ADR-046-provider-credential-managed-identity", + "to" : "ADR-046-telemetry-audit-and-support", + "type" : "spec-depends-on" }, { - "from": "ADR046-cli-011", - "to": "ADR046-cli-001", - "type": "work-item-depends-on" + "from" : "ADR-046-provider-credential-secret-service", + "to" : "ADR-046-components-processes-and-sandbox", + "type" : "spec-depends-on" }, { - "from": "ADR046-cli-011", - "to": "ADR046-cli-002", - "type": "work-item-depends-on" + "from" : "ADR-046-provider-credential-secret-service", + "to" : "ADR-046-componentsession-and-bus", + "type" : "spec-depends-on" }, { - "from": "ADR046-cli-011", - "to": "ADR046-cli-007", - "type": "work-item-depends-on" + "from" : "ADR-046-provider-credential-secret-service", + "to" : "ADR-046-nix-configuration", + "type" : "spec-depends-on" }, { - "from": "ADR046-cli-011", - "to": "ADR046-identities-002", - "type": "work-item-depends-on" + "from" : "ADR-046-provider-credential-secret-service", + "to" : "ADR-046-provider-model-and-packaging", + "type" : "spec-depends-on" }, { - "from": "ADR046-cli-012", - "to": "ADR-046-cli-and-operations", - "type": "implements-spec" + "from" : "ADR-046-provider-credential-secret-service", + "to" : "ADR-046-resource-api-and-authorization", + "type" : "spec-depends-on" }, { - "from": "ADR046-cli-012", - "to": "ADR046-cli-001", - "type": "work-item-depends-on" + "from" : "ADR-046-provider-credential-secret-service", + "to" : "ADR-046-resource-reconciliation", + "type" : "spec-depends-on" }, { - "from": "ADR046-cli-012", - "to": "ADR046-cli-010", - "type": "work-item-depends-on" + "from" : "ADR-046-provider-credential-secret-service", + "to" : "ADR-046-resources-credential", + "type" : "spec-depends-on" }, { - "from": "ADR046-cli-013", - "to": "ADR-046-cli-and-operations", - "type": "implements-spec" + "from" : "ADR-046-provider-credential-secret-service", + "to" : "ADR-046-telemetry-audit-and-support", + "type" : "spec-depends-on" }, { - "from": "ADR046-cli-013", - "to": "ADR046-cli-001", - "type": "work-item-depends-on" + "from" : "ADR-046-provider-device-gpu", + "to" : "ADR-046-components-processes-and-sandbox", + "type" : "spec-depends-on" }, { - "from": "ADR046-cli-013", - "to": "ADR046-cli-010", - "type": "work-item-depends-on" + "from" : "ADR-046-provider-device-gpu", + "to" : "ADR-046-primitive-resource-composition", + "type" : "spec-depends-on" }, { - "from": "ADR046-client-001", - "to": "ADR-046-resources-zone-control", - "type": "implements-spec" + "from" : "ADR-046-provider-device-gpu", + "to" : "ADR-046-provider-model-and-packaging", + "type" : "spec-depends-on" }, { - "from": "ADR046-clipboard-001", - "to": "ADR-046-provider-clipboard-wayland", - "type": "implements-spec" + "from" : "ADR-046-provider-device-gpu", + "to" : "ADR-046-resource-api-and-authorization", + "type" : "spec-depends-on" }, { - "from": "ADR046-clipboard-002", - "to": "ADR-046-provider-clipboard-wayland", - "type": "implements-spec" + "from" : "ADR-046-provider-device-gpu", + "to" : "ADR-046-resource-object-model", + "type" : "spec-depends-on" }, { - "from": "ADR046-clipboard-002", - "to": "ADR046-clipboard-001", - "type": "work-item-depends-on" + "from" : "ADR-046-provider-device-gpu", + "to" : "ADR-046-resource-reconciliation", + "type" : "spec-depends-on" }, { - "from": "ADR046-clipboard-003", - "to": "ADR-046-provider-clipboard-wayland", - "type": "implements-spec" + "from" : "ADR-046-provider-device-gpu", + "to" : "ADR-046-resources-device", + "type" : "spec-depends-on" }, { - "from": "ADR046-clipboard-003", - "to": "ADR046-clipboard-001", - "type": "work-item-depends-on" + "from" : "ADR-046-provider-device-gpu", + "to" : "ADR-046-telemetry-audit-and-support", + "type" : "spec-depends-on" }, { - "from": "ADR046-clipboard-004", - "to": "ADR-046-provider-clipboard-wayland", - "type": "implements-spec" + "from" : "ADR-046-provider-device-security-key", + "to" : "ADR-046-components-processes-and-sandbox", + "type" : "spec-depends-on" }, { - "from": "ADR046-clipboard-004", - "to": "ADR046-clipboard-001", - "type": "work-item-depends-on" + "from" : "ADR-046-provider-device-security-key", + "to" : "ADR-046-componentsession-and-bus", + "type" : "spec-depends-on" }, { - "from": "ADR046-clipboard-004", - "to": "ADR046-clipboard-003", - "type": "work-item-depends-on" + "from" : "ADR-046-provider-device-security-key", + "to" : "ADR-046-nix-configuration", + "type" : "spec-depends-on" }, { - "from": "ADR046-clipboard-005", - "to": "ADR-046-provider-clipboard-wayland", - "type": "implements-spec" + "from" : "ADR-046-provider-device-security-key", + "to" : "ADR-046-primitive-resource-composition", + "type" : "spec-depends-on" }, { - "from": "ADR046-clipboard-005", - "to": "ADR046-clipboard-001", - "type": "work-item-depends-on" + "from" : "ADR-046-provider-device-security-key", + "to" : "ADR-046-provider-model-and-packaging", + "type" : "spec-depends-on" }, { - "from": "ADR046-clipboard-006", - "to": "ADR-046-provider-clipboard-wayland", - "type": "implements-spec" + "from" : "ADR-046-provider-device-security-key", + "to" : "ADR-046-provider-state", + "type" : "spec-depends-on" }, { - "from": "ADR046-clipboard-007", - "to": "ADR-046-provider-clipboard-wayland", - "type": "implements-spec" + "from" : "ADR-046-provider-device-security-key", + "to" : "ADR-046-resource-api-and-authorization", + "type" : "spec-depends-on" }, { - "from": "ADR046-clipboard-007", - "to": "ADR046-clipboard-003", - "type": "work-item-depends-on" + "from" : "ADR-046-provider-device-security-key", + "to" : "ADR-046-resource-object-model", + "type" : "spec-depends-on" }, { - "from": "ADR046-clipboard-008", - "to": "ADR-046-provider-clipboard-wayland", - "type": "implements-spec" + "from" : "ADR-046-provider-device-security-key", + "to" : "ADR-046-resource-reconciliation", + "type" : "spec-depends-on" }, { - "from": "ADR046-clipboard-008", - "to": "ADR046-clipboard-002", - "type": "work-item-depends-on" + "from" : "ADR-046-provider-device-security-key", + "to" : "ADR-046-resources-device", + "type" : "spec-depends-on" }, { - "from": "ADR046-clipboard-009", - "to": "ADR-046-provider-clipboard-wayland", - "type": "implements-spec" + "from" : "ADR-046-provider-device-security-key", + "to" : "ADR-046-telemetry-audit-and-support", + "type" : "spec-depends-on" }, { - "from": "ADR046-clipboard-009", - "to": "ADR046-clipboard-001", - "type": "work-item-depends-on" + "from" : "ADR-046-provider-device-tpm", + "to" : "ADR-046-primitive-resource-composition", + "type" : "spec-depends-on" }, { - "from": "ADR046-clipboard-009", - "to": "ADR046-clipboard-008", - "type": "work-item-depends-on" + "from" : "ADR-046-provider-device-tpm", + "to" : "ADR-046-provider-model-and-packaging", + "type" : "spec-depends-on" }, { - "from": "ADR046-clipboard-010", - "to": "ADR-046-provider-clipboard-wayland", - "type": "implements-spec" + "from" : "ADR-046-provider-device-tpm", + "to" : "ADR-046-provider-state", + "type" : "spec-depends-on" }, { - "from": "ADR046-clipboard-010", - "to": "ADR046-clipboard-009", - "type": "work-item-depends-on" + "from" : "ADR-046-provider-device-tpm", + "to" : "ADR-046-resource-object-model", + "type" : "spec-depends-on" }, { - "from": "ADR046-clipboard-011", - "to": "ADR-046-provider-clipboard-wayland", - "type": "implements-spec" + "from" : "ADR-046-provider-device-tpm", + "to" : "ADR-046-resource-reconciliation", + "type" : "spec-depends-on" }, { - "from": "ADR046-clipboard-011", - "to": "ADR046-clipboard-005", - "type": "work-item-depends-on" + "from" : "ADR-046-provider-device-tpm", + "to" : "ADR-046-resources-device", + "type" : "spec-depends-on" }, { - "from": "ADR046-clipboard-012", - "to": "ADR-046-provider-clipboard-wayland", - "type": "implements-spec" + "from" : "ADR-046-provider-device-tpm", + "to" : "ADR-046-resources-host-guest-process-user", + "type" : "spec-depends-on" }, { - "from": "ADR046-clipboard-012", - "to": "ADR046-clipboard-006", - "type": "work-item-depends-on" + "from" : "ADR-046-provider-device-tpm", + "to" : "ADR-046-resources-volume", + "type" : "spec-depends-on" }, { - "from": "ADR046-core-001", - "to": "ADR-046-core-controllers", - "type": "implements-spec" + "from" : "ADR-046-provider-device-usbip", + "to" : "ADR-046-components-processes-and-sandbox", + "type" : "spec-depends-on" }, { - "from": "ADR046-core-002", - "to": "ADR-046-core-controllers", - "type": "implements-spec" + "from" : "ADR-046-provider-device-usbip", + "to" : "ADR-046-componentsession-and-bus", + "type" : "spec-depends-on" }, { - "from": "ADR046-core-002", - "to": "ADR046-core-001", - "type": "work-item-depends-on" + "from" : "ADR-046-provider-device-usbip", + "to" : "ADR-046-nix-configuration", + "type" : "spec-depends-on" }, { - "from": "ADR046-cred-entra-001", - "to": "ADR-046-provider-credential-entra", - "type": "implements-spec" + "from" : "ADR-046-provider-device-usbip", + "to" : "ADR-046-provider-model-and-packaging", + "type" : "spec-depends-on" }, { - "from": "ADR046-cred-entra-001", - "to": "ADR046-credential-001", - "type": "work-item-depends-on" + "from" : "ADR-046-provider-device-usbip", + "to" : "ADR-046-provider-state", + "type" : "spec-depends-on" }, { - "from": "ADR046-cred-entra-001", - "to": "ADR046-credential-002", - "type": "work-item-depends-on" + "from" : "ADR-046-provider-device-usbip", + "to" : "ADR-046-resource-api-and-authorization", + "type" : "spec-depends-on" }, { - "from": "ADR046-cred-mi-001", - "to": "ADR-046-provider-credential-managed-identity", - "type": "implements-spec" + "from" : "ADR-046-provider-device-usbip", + "to" : "ADR-046-resource-reconciliation", + "type" : "spec-depends-on" }, { - "from": "ADR046-cred-mi-001", - "to": "ADR046-credential-001", - "type": "work-item-depends-on" + "from" : "ADR-046-provider-device-usbip", + "to" : "ADR-046-resources-device", + "type" : "spec-depends-on" }, { - "from": "ADR046-cred-mi-001", - "to": "ADR046-credential-002", - "type": "work-item-depends-on" + "from" : "ADR-046-provider-device-usbip", + "to" : "ADR-046-resources-network", + "type" : "spec-depends-on" }, { - "from": "ADR046-cred-mi-001", - "to": "ADR046-reconcile-001", - "type": "work-item-depends-on" + "from" : "ADR-046-provider-device-usbip", + "to" : "ADR-046-resources-zone-control", + "type" : "spec-depends-on" }, { - "from": "ADR046-cred-mi-002", - "to": "ADR-046-provider-credential-managed-identity", - "type": "implements-spec" + "from" : "ADR-046-provider-device-usbip", + "to" : "ADR-046-telemetry-audit-and-support", + "type" : "spec-depends-on" }, { - "from": "ADR046-cred-mi-002", - "to": "ADR046-credential-001", - "type": "work-item-depends-on" + "from" : "ADR-046-provider-device-usbip", + "to" : "ADR-046-zone-routing", + "type" : "spec-depends-on" }, { - "from": "ADR046-cred-mi-002", - "to": "ADR046-credential-002", - "type": "work-item-depends-on" + "from" : "ADR-046-provider-display-wayland", + "to" : "ADR-046-components-processes-and-sandbox", + "type" : "spec-depends-on" }, { - "from": "ADR046-cred-mi-002", - "to": "ADR046-reconcile-001", - "type": "work-item-depends-on" + "from" : "ADR-046-provider-display-wayland", + "to" : "ADR-046-componentsession-and-bus", + "type" : "spec-depends-on" }, { - "from": "ADR046-cred-mi-002", - "to": "ADR046-reconcile-002", - "type": "work-item-depends-on" + "from" : "ADR-046-provider-display-wayland", + "to" : "ADR-046-nix-configuration", + "type" : "spec-depends-on" }, { - "from": "ADR046-cred-mi-003", - "to": "ADR-046-provider-credential-managed-identity", - "type": "implements-spec" + "from" : "ADR-046-provider-display-wayland", + "to" : "ADR-046-provider-model-and-packaging", + "type" : "spec-depends-on" }, { - "from": "ADR046-cred-mi-003", - "to": "ADR046-credential-001", - "type": "work-item-depends-on" + "from" : "ADR-046-provider-display-wayland", + "to" : "ADR-046-resource-api-and-authorization", + "type" : "spec-depends-on" }, { - "from": "ADR046-cred-mi-003", - "to": "ADR046-credential-002", - "type": "work-item-depends-on" + "from" : "ADR-046-provider-display-wayland", + "to" : "ADR-046-resource-reconciliation", + "type" : "spec-depends-on" }, { - "from": "ADR046-cred-mi-004", - "to": "ADR-046-provider-credential-managed-identity", - "type": "implements-spec" + "from" : "ADR-046-provider-display-wayland", + "to" : "ADR-046-resources-device", + "type" : "spec-depends-on" }, { - "from": "ADR046-cred-ss-001", - "to": "ADR-046-provider-credential-secret-service", - "type": "implements-spec" + "from" : "ADR-046-provider-display-wayland", + "to" : "ADR-046-resources-host-guest-process-user", + "type" : "spec-depends-on" }, { - "from": "ADR046-cred-ss-002", - "to": "ADR-046-provider-credential-secret-service", - "type": "implements-spec" + "from" : "ADR-046-provider-display-wayland", + "to" : "ADR-046-resources-volume", + "type" : "spec-depends-on" }, { - "from": "ADR046-cred-ss-003", - "to": "ADR-046-provider-credential-secret-service", - "type": "implements-spec" + "from" : "ADR-046-provider-display-wayland", + "to" : "ADR-046-telemetry-audit-and-support", + "type" : "spec-depends-on" }, { - "from": "ADR046-cred-ss-003", - "to": "ADR046-reconcile-001", - "type": "work-item-depends-on" + "from" : "ADR-046-provider-model-and-packaging", + "to" : "ADR-046-primitive-resource-composition", + "type" : "spec-depends-on" }, { - "from": "ADR046-cred-ss-004", - "to": "ADR-046-provider-credential-secret-service", - "type": "implements-spec" + "from" : "ADR-046-provider-model-and-packaging", + "to" : "ADR-046-resource-api-and-authorization", + "type" : "spec-depends-on" }, { - "from": "ADR046-cred-ss-005", - "to": "ADR-046-provider-credential-secret-service", - "type": "implements-spec" + "from" : "ADR-046-provider-model-and-packaging", + "to" : "ADR-046-resource-object-model", + "type" : "spec-depends-on" }, { - "from": "ADR046-cred-ss-006", - "to": "ADR-046-provider-credential-secret-service", - "type": "implements-spec" + "from" : "ADR-046-provider-model-and-packaging", + "to" : "ADR-046-resource-reconciliation", + "type" : "spec-depends-on" }, { - "from": "ADR046-credential-001", - "to": "ADR-046-resources-credential", - "type": "implements-spec" + "from" : "ADR-046-provider-network-local", + "to" : "ADR-046-componentsession-and-bus", + "type" : "spec-depends-on" }, { - "from": "ADR046-credential-001", - "to": "ADR046-identities-001", - "type": "work-item-depends-on" + "from" : "ADR-046-provider-network-local", + "to" : "ADR-046-current-code-migration-map", + "type" : "spec-depends-on" }, { - "from": "ADR046-credential-001", - "to": "ADR046-object-001", - "type": "work-item-depends-on" + "from" : "ADR-046-provider-network-local", + "to" : "ADR-046-nix-configuration", + "type" : "spec-depends-on" }, { - "from": "ADR046-credential-002", - "to": "ADR-046-resources-credential", - "type": "implements-spec" + "from" : "ADR-046-provider-network-local", + "to" : "ADR-046-provider-model-and-packaging", + "type" : "spec-depends-on" }, { - "from": "ADR046-credential-002", - "to": "ADR046-api-001", - "type": "work-item-depends-on" + "from" : "ADR-046-provider-network-local", + "to" : "ADR-046-resource-reconciliation", + "type" : "spec-depends-on" }, { - "from": "ADR046-credential-002", - "to": "ADR046-bus-001", - "type": "work-item-depends-on" + "from" : "ADR-046-provider-network-local", + "to" : "ADR-046-resources-host-guest-process-user", + "type" : "spec-depends-on" }, { - "from": "ADR046-credential-002", - "to": "ADR046-credential-001", - "type": "work-item-depends-on" + "from" : "ADR-046-provider-network-local", + "to" : "ADR-046-resources-network", + "type" : "spec-depends-on" }, { - "from": "ADR046-credential-003", - "to": "ADR-046-resources-credential", - "type": "implements-spec" + "from" : "ADR-046-provider-network-local", + "to" : "ADR-046-resources-volume", + "type" : "spec-depends-on" }, { - "from": "ADR046-credential-003", - "to": "ADR046-credential-001", - "type": "work-item-depends-on" + "from" : "ADR-046-provider-network-local", + "to" : "ADR-046-telemetry-audit-and-support", + "type" : "spec-depends-on" }, { - "from": "ADR046-credential-003", - "to": "ADR046-credential-002", - "type": "work-item-depends-on" + "from" : "ADR-046-provider-notification-desktop", + "to" : "ADR-046-componentsession-and-bus", + "type" : "spec-depends-on" }, { - "from": "ADR046-credential-003", - "to": "ADR046-reconcile-001", - "type": "work-item-depends-on" + "from" : "ADR-046-provider-notification-desktop", + "to" : "ADR-046-nix-configuration", + "type" : "spec-depends-on" }, { - "from": "ADR046-credential-004", - "to": "ADR-046-resources-credential", - "type": "implements-spec" + "from" : "ADR-046-provider-notification-desktop", + "to" : "ADR-046-primitive-resource-composition", + "type" : "spec-depends-on" }, { - "from": "ADR046-credential-004", - "to": "ADR046-credential-001", - "type": "work-item-depends-on" + "from" : "ADR-046-provider-notification-desktop", + "to" : "ADR-046-provider-model-and-packaging", + "type" : "spec-depends-on" }, { - "from": "ADR046-credential-004", - "to": "ADR046-credential-002", - "type": "work-item-depends-on" + "from" : "ADR-046-provider-notification-desktop", + "to" : "ADR-046-provider-state", + "type" : "spec-depends-on" }, { - "from": "ADR046-credential-004", - "to": "ADR046-reconcile-001", - "type": "work-item-depends-on" + "from" : "ADR-046-provider-notification-desktop", + "to" : "ADR-046-resource-reconciliation", + "type" : "spec-depends-on" }, { - "from": "ADR046-credential-005", - "to": "ADR-046-resources-credential", - "type": "implements-spec" + "from" : "ADR-046-provider-notification-desktop", + "to" : "ADR-046-telemetry-audit-and-support", + "type" : "spec-depends-on" }, { - "from": "ADR046-credential-005", - "to": "ADR046-credential-001", - "type": "work-item-depends-on" + "from" : "ADR-046-provider-observability-otel", + "to" : "ADR-046-components-processes-and-sandbox", + "type" : "spec-depends-on" }, { - "from": "ADR046-credential-005", - "to": "ADR046-credential-002", - "type": "work-item-depends-on" + "from" : "ADR-046-provider-observability-otel", + "to" : "ADR-046-componentsession-and-bus", + "type" : "spec-depends-on" }, { - "from": "ADR046-credential-005", - "to": "ADR046-reconcile-001", - "type": "work-item-depends-on" + "from" : "ADR-046-provider-observability-otel", + "to" : "ADR-046-nix-configuration", + "type" : "spec-depends-on" }, { - "from": "ADR046-credential-006", - "to": "ADR-046-resources-credential", - "type": "implements-spec" + "from" : "ADR-046-provider-observability-otel", + "to" : "ADR-046-primitive-resource-composition", + "type" : "spec-depends-on" }, { - "from": "ADR046-credential-006", - "to": "ADR046-credential-001", - "type": "work-item-depends-on" + "from" : "ADR-046-provider-observability-otel", + "to" : "ADR-046-provider-model-and-packaging", + "type" : "spec-depends-on" }, { - "from": "ADR046-credential-006", - "to": "ADR046-credential-002", - "type": "work-item-depends-on" + "from" : "ADR-046-provider-observability-otel", + "to" : "ADR-046-resource-api-and-authorization", + "type" : "spec-depends-on" }, { - "from": "ADR046-credential-006", - "to": "ADR046-reconcile-001", - "type": "work-item-depends-on" + "from" : "ADR-046-provider-observability-otel", + "to" : "ADR-046-resource-object-model", + "type" : "spec-depends-on" }, { - "from": "ADR046-credential-006", - "to": "ADR046-reconcile-002", - "type": "work-item-depends-on" + "from" : "ADR-046-provider-observability-otel", + "to" : "ADR-046-resource-reconciliation", + "type" : "spec-depends-on" }, { - "from": "ADR046-credential-007", - "to": "ADR-046-resources-credential", - "type": "implements-spec" + "from" : "ADR-046-provider-observability-otel", + "to" : "ADR-046-resources-credential", + "type" : "spec-depends-on" }, { - "from": "ADR046-credential-007", - "to": "ADR046-api-001", - "type": "work-item-depends-on" + "from" : "ADR-046-provider-observability-otel", + "to" : "ADR-046-resources-volume", + "type" : "spec-depends-on" }, { - "from": "ADR046-credential-007", - "to": "ADR046-credential-001", - "type": "work-item-depends-on" + "from" : "ADR-046-provider-observability-otel", + "to" : "ADR-046-telemetry-audit-and-support", + "type" : "spec-depends-on" }, { - "from": "ADR046-credential-007", - "to": "ADR046-identities-002", - "type": "work-item-depends-on" + "from" : "ADR-046-provider-observability-otel", + "to" : "ADR-046-terminology-and-identities", + "type" : "spec-depends-on" }, { - "from": "ADR046-credential-007", - "to": "ADR046-reconcile-001", - "type": "work-item-depends-on" + "from" : "ADR-046-provider-runtime-azure-container-apps", + "to" : "ADR-046-componentsession-and-bus", + "type" : "spec-depends-on" }, { - "from": "ADR046-credential-008", - "to": "ADR-046-resources-credential", - "type": "implements-spec" + "from" : "ADR-046-provider-runtime-azure-container-apps", + "to" : "ADR-046-nix-configuration", + "type" : "spec-depends-on" }, { - "from": "ADR046-credential-008", - "to": "ADR046-credential-001", - "type": "work-item-depends-on" + "from" : "ADR-046-provider-runtime-azure-container-apps", + "to" : "ADR-046-provider-model-and-packaging", + "type" : "spec-depends-on" }, { - "from": "ADR046-credential-008", - "to": "ADR046-credential-006", - "type": "work-item-depends-on" + "from" : "ADR-046-provider-runtime-azure-container-apps", + "to" : "ADR-046-provider-state", + "type" : "spec-depends-on" }, { - "from": "ADR046-decisions-001", - "to": "ADR-046-decision-register", - "type": "implements-spec" + "from" : "ADR-046-provider-runtime-azure-container-apps", + "to" : "ADR-046-resources-credential", + "type" : "spec-depends-on" }, { - "from": "ADR046-delivery-001", - "to": "ADR-046-validation-and-delivery", - "type": "implements-spec" + "from" : "ADR-046-provider-runtime-azure-container-apps", + "to" : "ADR-046-resources-host-guest-process-user", + "type" : "spec-depends-on" }, { - "from": "ADR046-delivery-002", - "to": "ADR-046-validation-and-delivery", - "type": "implements-spec" + "from" : "ADR-046-provider-runtime-azure-container-apps", + "to" : "ADR-046-telemetry-audit-and-support", + "type" : "spec-depends-on" }, { - "from": "ADR046-delivery-003", - "to": "ADR-046-validation-and-delivery", - "type": "implements-spec" + "from" : "ADR-046-provider-runtime-azure-container-apps", + "to" : "ADR-046-zone-routing", + "type" : "spec-depends-on" }, { - "from": "ADR046-delivery-003", - "to": "ADR046-delivery-002", - "type": "work-item-depends-on" + "from" : "ADR-046-provider-runtime-azure-virtual-machine", + "to" : "ADR-046-components-processes-and-sandbox", + "type" : "spec-depends-on" }, { - "from": "ADR046-delivery-004", - "to": "ADR-046-validation-and-delivery", - "type": "implements-spec" + "from" : "ADR-046-provider-runtime-azure-virtual-machine", + "to" : "ADR-046-componentsession-and-bus", + "type" : "spec-depends-on" }, { - "from": "ADR046-delivery-004", - "to": "ADR046-delivery-002", - "type": "work-item-depends-on" + "from" : "ADR-046-provider-runtime-azure-virtual-machine", + "to" : "ADR-046-nix-configuration", + "type" : "spec-depends-on" }, { - "from": "ADR046-delivery-005", - "to": "ADR-046-validation-and-delivery", - "type": "implements-spec" + "from" : "ADR-046-provider-runtime-azure-virtual-machine", + "to" : "ADR-046-primitive-resource-composition", + "type" : "spec-depends-on" }, { - "from": "ADR046-delivery-005", - "to": "ADR046-delivery-002", - "type": "work-item-depends-on" + "from" : "ADR-046-provider-runtime-azure-virtual-machine", + "to" : "ADR-046-provider-model-and-packaging", + "type" : "spec-depends-on" }, { - "from": "ADR046-delivery-005", - "to": "ADR046-delivery-003", - "type": "work-item-depends-on" + "from" : "ADR-046-provider-runtime-azure-virtual-machine", + "to" : "ADR-046-provider-state", + "type" : "spec-depends-on" }, { - "from": "ADR046-delivery-006", - "to": "ADR-046-validation-and-delivery", - "type": "implements-spec" + "from" : "ADR-046-provider-runtime-azure-virtual-machine", + "to" : "ADR-046-resource-api-and-authorization", + "type" : "spec-depends-on" }, { - "from": "ADR046-delivery-006", - "to": "ADR046-delivery-002", - "type": "work-item-depends-on" + "from" : "ADR-046-provider-runtime-azure-virtual-machine", + "to" : "ADR-046-resource-object-model", + "type" : "spec-depends-on" }, { - "from": "ADR046-delivery-006", - "to": "ADR046-delivery-004", - "type": "work-item-depends-on" + "from" : "ADR-046-provider-runtime-azure-virtual-machine", + "to" : "ADR-046-resource-reconciliation", + "type" : "spec-depends-on" }, { - "from": "ADR046-delivery-006", - "to": "ADR046-delivery-005", - "type": "work-item-depends-on" + "from" : "ADR-046-provider-runtime-azure-virtual-machine", + "to" : "ADR-046-resources-credential", + "type" : "spec-depends-on" }, { - "from": "ADR046-delivery-007", - "to": "ADR-046-validation-and-delivery", - "type": "implements-spec" + "from" : "ADR-046-provider-runtime-azure-virtual-machine", + "to" : "ADR-046-resources-host-guest-process-user", + "type" : "spec-depends-on" }, { - "from": "ADR046-delivery-008", - "to": "ADR-046-validation-and-delivery", - "type": "implements-spec" + "from" : "ADR-046-provider-runtime-azure-virtual-machine", + "to" : "ADR-046-resources-network", + "type" : "spec-depends-on" }, { - "from": "ADR046-delivery-008", - "to": "ADR046-streamline-001", - "type": "work-item-depends-on" + "from" : "ADR-046-provider-runtime-azure-virtual-machine", + "to" : "ADR-046-telemetry-audit-and-support", + "type" : "spec-depends-on" }, { - "from": "ADR046-device-001", - "to": "ADR-046-resources-device", - "type": "implements-spec" + "from" : "ADR-046-provider-runtime-azure-virtual-machine", + "to" : "ADR-046-terminology-and-identities", + "type" : "spec-depends-on" }, { - "from": "ADR046-device-002", - "to": "ADR-046-resources-device", - "type": "implements-spec" + "from" : "ADR-046-provider-runtime-azure-virtual-machine", + "to" : "ADR-046-zone-routing", + "type" : "spec-depends-on" }, { - "from": "ADR046-device-002", - "to": "ADR046-device-001", - "type": "work-item-depends-on" + "from" : "ADR-046-provider-runtime-cloud-hypervisor", + "to" : "ADR-046-components-processes-and-sandbox", + "type" : "spec-depends-on" }, { - "from": "ADR046-device-003", - "to": "ADR-046-resources-device", - "type": "implements-spec" + "from" : "ADR-046-provider-runtime-cloud-hypervisor", + "to" : "ADR-046-componentsession-and-bus", + "type" : "spec-depends-on" }, { - "from": "ADR046-device-003", - "to": "ADR046-device-001", - "type": "work-item-depends-on" + "from" : "ADR-046-provider-runtime-cloud-hypervisor", + "to" : "ADR-046-nix-configuration", + "type" : "spec-depends-on" }, { - "from": "ADR046-device-004", - "to": "ADR-046-resources-device", - "type": "implements-spec" + "from" : "ADR-046-provider-runtime-cloud-hypervisor", + "to" : "ADR-046-primitive-resource-composition", + "type" : "spec-depends-on" }, { - "from": "ADR046-device-004", - "to": "ADR046-device-001", - "type": "work-item-depends-on" + "from" : "ADR-046-provider-runtime-cloud-hypervisor", + "to" : "ADR-046-provider-model-and-packaging", + "type" : "spec-depends-on" }, { - "from": "ADR046-device-005", - "to": "ADR-046-resources-device", - "type": "implements-spec" + "from" : "ADR-046-provider-runtime-cloud-hypervisor", + "to" : "ADR-046-provider-state", + "type" : "spec-depends-on" }, { - "from": "ADR046-device-005", - "to": "ADR046-device-001", - "type": "work-item-depends-on" + "from" : "ADR-046-provider-runtime-cloud-hypervisor", + "to" : "ADR-046-resource-object-model", + "type" : "spec-depends-on" }, { - "from": "ADR046-device-006", - "to": "ADR-046-resources-device", - "type": "implements-spec" + "from" : "ADR-046-provider-runtime-cloud-hypervisor", + "to" : "ADR-046-resource-reconciliation", + "type" : "spec-depends-on" }, { - "from": "ADR046-device-006", - "to": "ADR046-device-001", - "type": "work-item-depends-on" + "from" : "ADR-046-provider-runtime-cloud-hypervisor", + "to" : "ADR-046-resources-device", + "type" : "spec-depends-on" }, { - "from": "ADR046-device-006", - "to": "ADR046-device-005", - "type": "work-item-depends-on" + "from" : "ADR-046-provider-runtime-cloud-hypervisor", + "to" : "ADR-046-resources-host-guest-process-user", + "type" : "spec-depends-on" }, { - "from": "ADR046-device-007", - "to": "ADR-046-resources-device", - "type": "implements-spec" + "from" : "ADR-046-provider-runtime-cloud-hypervisor", + "to" : "ADR-046-resources-network", + "type" : "spec-depends-on" }, { - "from": "ADR046-device-007", - "to": "ADR046-core-001", - "type": "file-overlap-order" + "from" : "ADR-046-provider-runtime-cloud-hypervisor", + "to" : "ADR-046-resources-volume", + "type" : "spec-depends-on" }, { - "from": "ADR046-device-007", - "to": "ADR046-core-001", - "type": "work-item-depends-on" + "from" : "ADR-046-provider-runtime-cloud-hypervisor", + "to" : "ADR-046-telemetry-audit-and-support", + "type" : "spec-depends-on" }, { - "from": "ADR046-device-007", - "to": "ADR046-device-006", - "type": "work-item-depends-on" + "from" : "ADR-046-provider-runtime-qemu-media", + "to" : "ADR-046-components-processes-and-sandbox", + "type" : "spec-depends-on" }, { - "from": "ADR046-device-008", - "to": "ADR-046-resources-device", - "type": "implements-spec" + "from" : "ADR-046-provider-runtime-qemu-media", + "to" : "ADR-046-componentsession-and-bus", + "type" : "spec-depends-on" }, { - "from": "ADR046-device-008", - "to": "ADR046-device-002", - "type": "work-item-depends-on" + "from" : "ADR-046-provider-runtime-qemu-media", + "to" : "ADR-046-current-code-migration-map", + "type" : "spec-depends-on" }, { - "from": "ADR046-device-008", - "to": "ADR046-device-005", - "type": "work-item-depends-on" + "from" : "ADR-046-provider-runtime-qemu-media", + "to" : "ADR-046-nix-configuration", + "type" : "spec-depends-on" }, { - "from": "ADR046-device-tpm-001", - "to": "ADR-046-provider-device-tpm", - "type": "implements-spec" + "from" : "ADR-046-provider-runtime-qemu-media", + "to" : "ADR-046-provider-model-and-packaging", + "type" : "spec-depends-on" }, { - "from": "ADR046-device-tpm-002", - "to": "ADR-046-provider-device-tpm", - "type": "implements-spec" + "from" : "ADR-046-provider-runtime-qemu-media", + "to" : "ADR-046-resource-reconciliation", + "type" : "spec-depends-on" }, { - "from": "ADR046-device-tpm-003", - "to": "ADR-046-provider-device-tpm", - "type": "implements-spec" + "from" : "ADR-046-provider-runtime-qemu-media", + "to" : "ADR-046-resources-device", + "type" : "spec-depends-on" }, { - "from": "ADR046-device-tpm-004", - "to": "ADR-046-provider-device-tpm", - "type": "implements-spec" + "from" : "ADR-046-provider-runtime-qemu-media", + "to" : "ADR-046-resources-host-guest-process-user", + "type" : "spec-depends-on" }, { - "from": "ADR046-device-tpm-005", - "to": "ADR-046-provider-device-tpm", - "type": "implements-spec" + "from" : "ADR-046-provider-runtime-qemu-media", + "to" : "ADR-046-resources-network", + "type" : "spec-depends-on" }, { - "from": "ADR046-device-tpm-006", - "to": "ADR-046-provider-device-tpm", - "type": "implements-spec" + "from" : "ADR-046-provider-runtime-qemu-media", + "to" : "ADR-046-resources-volume", + "type" : "spec-depends-on" }, { - "from": "ADR046-device-tpm-007", - "to": "ADR-046-provider-device-tpm", - "type": "implements-spec" + "from" : "ADR-046-provider-runtime-qemu-media", + "to" : "ADR-046-telemetry-audit-and-support", + "type" : "spec-depends-on" }, { - "from": "ADR046-device-tpm-008", - "to": "ADR-046-provider-device-tpm", - "type": "implements-spec" + "from" : "ADR-046-provider-shell-terminal", + "to" : "ADR-046-components-processes-and-sandbox", + "type" : "spec-depends-on" }, { - "from": "ADR046-device-tpm-009", - "to": "ADR-046-provider-device-tpm", - "type": "implements-spec" + "from" : "ADR-046-provider-shell-terminal", + "to" : "ADR-046-componentsession-and-bus", + "type" : "spec-depends-on" }, { - "from": "ADR046-device-tpm-010", - "to": "ADR-046-provider-device-tpm", - "type": "implements-spec" + "from" : "ADR-046-provider-shell-terminal", + "to" : "ADR-046-core-controllers", + "type" : "spec-depends-on" }, { - "from": "ADR046-device-tpm-011", - "to": "ADR-046-provider-device-tpm", - "type": "implements-spec" + "from" : "ADR-046-provider-shell-terminal", + "to" : "ADR-046-decision-register", + "type" : "spec-depends-on" }, { - "from": "ADR046-device-tpm-012", - "to": "ADR-046-provider-device-tpm", - "type": "implements-spec" + "from" : "ADR-046-provider-shell-terminal", + "to" : "ADR-046-nix-configuration", + "type" : "spec-depends-on" }, { - "from": "ADR046-device-tpm-013", - "to": "ADR-046-provider-device-tpm", - "type": "implements-spec" + "from" : "ADR-046-provider-shell-terminal", + "to" : "ADR-046-primitive-resource-composition", + "type" : "spec-depends-on" }, { - "from": "ADR046-display-001", - "to": "ADR-046-provider-display-wayland", - "type": "implements-spec" + "from" : "ADR-046-provider-shell-terminal", + "to" : "ADR-046-provider-model-and-packaging", + "type" : "spec-depends-on" }, { - "from": "ADR046-display-001", - "to": "ADR046-process-001", - "type": "work-item-depends-on" + "from" : "ADR-046-provider-shell-terminal", + "to" : "ADR-046-provider-state", + "type" : "spec-depends-on" }, { - "from": "ADR046-display-001", - "to": "ADR046-provider-001", - "type": "work-item-depends-on" + "from" : "ADR-046-provider-shell-terminal", + "to" : "ADR-046-resource-api-and-authorization", + "type" : "spec-depends-on" }, { - "from": "ADR046-display-002", - "to": "ADR-046-provider-display-wayland", - "type": "implements-spec" + "from" : "ADR-046-provider-shell-terminal", + "to" : "ADR-046-resource-object-model", + "type" : "spec-depends-on" }, { - "from": "ADR046-display-002", - "to": "ADR046-display-001", - "type": "work-item-depends-on" + "from" : "ADR-046-provider-shell-terminal", + "to" : "ADR-046-resource-reconciliation", + "type" : "spec-depends-on" }, { - "from": "ADR046-display-003", - "to": "ADR-046-provider-display-wayland", - "type": "implements-spec" + "from" : "ADR-046-provider-shell-terminal", + "to" : "ADR-046-resources-credential", + "type" : "spec-depends-on" }, { - "from": "ADR046-display-003", - "to": "ADR046-display-001", - "type": "work-item-depends-on" + "from" : "ADR-046-provider-shell-terminal", + "to" : "ADR-046-resources-host-guest-process-user", + "type" : "spec-depends-on" }, { - "from": "ADR046-display-004", - "to": "ADR-046-provider-display-wayland", - "type": "implements-spec" + "from" : "ADR-046-provider-shell-terminal", + "to" : "ADR-046-telemetry-audit-and-support", + "type" : "spec-depends-on" }, { - "from": "ADR046-display-004", - "to": "ADR046-display-001", - "type": "work-item-depends-on" + "from" : "ADR-046-provider-shell-terminal", + "to" : "ADR-046-terminology-and-identities", + "type" : "spec-depends-on" }, { - "from": "ADR046-doctor-001", - "to": "ADR-046-telemetry-audit-and-support", - "type": "implements-spec" + "from" : "ADR-046-provider-shell-terminal", + "to" : "ADR-046-zone-routing", + "type" : "spec-depends-on" }, { - "from": "ADR046-doctor-001", - "to": "ADR046-audit-001", - "type": "work-item-depends-on" + "from" : "ADR-046-provider-state", + "to" : "ADR-046-componentsession-and-bus", + "type" : "spec-depends-on" }, { - "from": "ADR046-doctor-001", - "to": "ADR046-core-001", - "type": "work-item-depends-on" + "from" : "ADR-046-provider-state", + "to" : "ADR-046-primitive-resource-composition", + "type" : "spec-depends-on" }, { - "from": "ADR046-doctor-002", - "to": "ADR-046-telemetry-audit-and-support", - "type": "implements-spec" + "from" : "ADR-046-provider-state", + "to" : "ADR-046-provider-model-and-packaging", + "type" : "spec-depends-on" }, { - "from": "ADR046-doctor-002", - "to": "ADR046-doctor-001", - "type": "work-item-depends-on" + "from" : "ADR-046-provider-state", + "to" : "ADR-046-resource-object-model", + "type" : "spec-depends-on" }, { - "from": "ADR046-exec-001", - "to": "ADR-046-resources-host-guest-process-user", - "type": "implements-spec" + "from" : "ADR-046-provider-state", + "to" : "ADR-046-resource-store-redb", + "type" : "spec-depends-on" }, { - "from": "ADR046-exec-002", - "to": "ADR-046-resources-host-guest-process-user", - "type": "implements-spec" + "from" : "ADR-046-provider-state", + "to" : "ADR-046-terminology-and-identities", + "type" : "spec-depends-on" }, { - "from": "ADR046-exec-002", - "to": "ADR046-exec-001", - "type": "work-item-depends-on" + "from" : "ADR-046-provider-system-core", + "to" : "ADR-046-componentsession-and-bus", + "type" : "spec-depends-on" }, { - "from": "ADR046-exec-003", - "to": "ADR-046-resources-host-guest-process-user", - "type": "implements-spec" + "from" : "ADR-046-provider-system-core", + "to" : "ADR-046-core-controllers", + "type" : "spec-depends-on" }, { - "from": "ADR046-exec-003", - "to": "ADR046-exec-001", - "type": "work-item-depends-on" + "from" : "ADR-046-provider-system-core", + "to" : "ADR-046-current-code-migration-map", + "type" : "spec-depends-on" }, { - "from": "ADR046-exec-004", - "to": "ADR-046-resources-host-guest-process-user", - "type": "implements-spec" + "from" : "ADR-046-provider-system-core", + "to" : "ADR-046-nix-configuration", + "type" : "spec-depends-on" }, { - "from": "ADR046-exec-004", - "to": "ADR046-exec-001", - "type": "work-item-depends-on" + "from" : "ADR-046-provider-system-core", + "to" : "ADR-046-provider-model-and-packaging", + "type" : "spec-depends-on" }, { - "from": "ADR046-exec-005", - "to": "ADR-046-resources-host-guest-process-user", - "type": "implements-spec" + "from" : "ADR-046-provider-system-core", + "to" : "ADR-046-resources-host-guest-process-user", + "type" : "spec-depends-on" }, { - "from": "ADR046-exec-005", - "to": "ADR046-exec-001", - "type": "work-item-depends-on" + "from" : "ADR-046-provider-system-core", + "to" : "ADR-046-resources-zone-control", + "type" : "spec-depends-on" }, { - "from": "ADR046-exec-005", - "to": "ADR046-exec-004", - "type": "work-item-depends-on" + "from" : "ADR-046-provider-system-core", + "to" : "ADR-046-telemetry-audit-and-support", + "type" : "spec-depends-on" }, { - "from": "ADR046-exec-006", - "to": "ADR-046-resources-host-guest-process-user", - "type": "implements-spec" + "from" : "ADR-046-provider-system-minijail", + "to" : "ADR-046-components-processes-and-sandbox", + "type" : "spec-depends-on" }, { - "from": "ADR046-exec-006", - "to": "ADR046-exec-001", - "type": "work-item-depends-on" + "from" : "ADR-046-provider-system-minijail", + "to" : "ADR-046-componentsession-and-bus", + "type" : "spec-depends-on" }, { - "from": "ADR046-exec-006", - "to": "ADR046-exec-002", - "type": "work-item-depends-on" + "from" : "ADR-046-provider-system-minijail", + "to" : "ADR-046-core-controllers", + "type" : "spec-depends-on" }, { - "from": "ADR046-exec-007", - "to": "ADR-046-resources-host-guest-process-user", - "type": "implements-spec" + "from" : "ADR-046-provider-system-minijail", + "to" : "ADR-046-nix-configuration", + "type" : "spec-depends-on" }, { - "from": "ADR046-exec-007", - "to": "ADR046-exec-001", - "type": "work-item-depends-on" + "from" : "ADR-046-provider-system-minijail", + "to" : "ADR-046-primitive-resource-composition", + "type" : "spec-depends-on" }, { - "from": "ADR046-exec-007", - "to": "ADR046-exec-002", - "type": "work-item-depends-on" + "from" : "ADR-046-provider-system-minijail", + "to" : "ADR-046-provider-model-and-packaging", + "type" : "spec-depends-on" }, { - "from": "ADR046-exec-008", - "to": "ADR-046-resources-host-guest-process-user", - "type": "implements-spec" + "from" : "ADR-046-provider-system-minijail", + "to" : "ADR-046-resource-api-and-authorization", + "type" : "spec-depends-on" }, { - "from": "ADR046-exec-008", - "to": "ADR046-exec-006", - "type": "work-item-depends-on" + "from" : "ADR-046-provider-system-minijail", + "to" : "ADR-046-resource-object-model", + "type" : "spec-depends-on" }, { - "from": "ADR046-exec-008", - "to": "ADR046-exec-007", - "type": "work-item-depends-on" + "from" : "ADR-046-provider-system-minijail", + "to" : "ADR-046-resource-reconciliation", + "type" : "spec-depends-on" }, { - "from": "ADR046-exec-009", - "to": "ADR-046-resources-host-guest-process-user", - "type": "implements-spec" + "from" : "ADR-046-provider-system-minijail", + "to" : "ADR-046-resources-host-guest-process-user", + "type" : "spec-depends-on" }, { - "from": "ADR046-exec-009", - "to": "ADR046-exec-001", - "type": "work-item-depends-on" + "from" : "ADR-046-provider-system-minijail", + "to" : "ADR-046-telemetry-audit-and-support", + "type" : "spec-depends-on" }, { - "from": "ADR046-exec-010", - "to": "ADR-046-resources-host-guest-process-user", - "type": "implements-spec" + "from" : "ADR-046-provider-system-minijail", + "to" : "ADR-046-terminology-and-identities", + "type" : "spec-depends-on" }, { - "from": "ADR046-exec-010", - "to": "ADR046-exec-001", - "type": "work-item-depends-on" + "from" : "ADR-046-provider-system-systemd", + "to" : "ADR-046-components-processes-and-sandbox", + "type" : "spec-depends-on" }, { - "from": "ADR046-exec-010", - "to": "ADR046-exec-007", - "type": "work-item-depends-on" + "from" : "ADR-046-provider-system-systemd", + "to" : "ADR-046-componentsession-and-bus", + "type" : "spec-depends-on" }, { - "from": "ADR046-exec-011", - "to": "ADR-046-resources-host-guest-process-user", - "type": "implements-spec" + "from" : "ADR-046-provider-system-systemd", + "to" : "ADR-046-current-code-migration-map", + "type" : "spec-depends-on" }, { - "from": "ADR046-exec-011", - "to": "ADR046-exec-004", - "type": "work-item-depends-on" + "from" : "ADR-046-provider-system-systemd", + "to" : "ADR-046-nix-configuration", + "type" : "spec-depends-on" }, { - "from": "ADR046-exec-011", - "to": "ADR046-exec-010", - "type": "work-item-depends-on" + "from" : "ADR-046-provider-system-systemd", + "to" : "ADR-046-provider-model-and-packaging", + "type" : "spec-depends-on" }, { - "from": "ADR046-exec-012", - "to": "ADR-046-resources-host-guest-process-user", - "type": "implements-spec" + "from" : "ADR-046-provider-system-systemd", + "to" : "ADR-046-resources-host-guest-process-user", + "type" : "spec-depends-on" }, { - "from": "ADR046-exec-012", - "to": "ADR046-exec-001", - "type": "work-item-depends-on" + "from" : "ADR-046-provider-system-systemd", + "to" : "ADR-046-telemetry-audit-and-support", + "type" : "spec-depends-on" }, { - "from": "ADR046-exec-013", - "to": "ADR-046-resources-host-guest-process-user", - "type": "implements-spec" + "from" : "ADR-046-provider-transport-azure-relay", + "to" : "ADR-046-componentsession-and-bus", + "type" : "spec-depends-on" }, { - "from": "ADR046-exec-013", - "to": "ADR046-core-001", - "type": "file-overlap-order" + "from" : "ADR-046-provider-transport-azure-relay", + "to" : "ADR-046-nix-configuration", + "type" : "spec-depends-on" }, { - "from": "ADR046-exec-013", - "to": "ADR046-core-001", - "type": "work-item-depends-on" + "from" : "ADR-046-provider-transport-azure-relay", + "to" : "ADR-046-provider-model-and-packaging", + "type" : "spec-depends-on" }, { - "from": "ADR046-exec-013", - "to": "ADR046-exec-008", - "type": "work-item-depends-on" + "from" : "ADR-046-provider-transport-azure-relay", + "to" : "ADR-046-resources-credential", + "type" : "spec-depends-on" }, { - "from": "ADR046-exec-014", - "to": "ADR-046-resources-host-guest-process-user", - "type": "implements-spec" + "from" : "ADR-046-provider-transport-azure-relay", + "to" : "ADR-046-telemetry-audit-and-support", + "type" : "spec-depends-on" }, { - "from": "ADR046-exec-014", - "to": "ADR046-exec-001", - "type": "work-item-depends-on" + "from" : "ADR-046-provider-transport-azure-relay", + "to" : "ADR-046-terminology-and-identities", + "type" : "spec-depends-on" }, { - "from": "ADR046-exec-014", - "to": "ADR046-exec-012", - "type": "work-item-depends-on" + "from" : "ADR-046-provider-transport-azure-relay", + "to" : "ADR-046-zone-routing", + "type" : "spec-depends-on" }, { - "from": "ADR046-exec-015", - "to": "ADR-046-resources-host-guest-process-user", - "type": "implements-spec" + "from" : "ADR-046-provider-transport-unix", + "to" : "ADR-046-components-processes-and-sandbox", + "type" : "spec-depends-on" }, { - "from": "ADR046-exec-015", - "to": "ADR046-core-001", - "type": "file-overlap-order" + "from" : "ADR-046-provider-transport-unix", + "to" : "ADR-046-componentsession-and-bus", + "type" : "spec-depends-on" }, { - "from": "ADR046-exec-015", - "to": "ADR046-core-001", - "type": "work-item-depends-on" + "from" : "ADR-046-provider-transport-unix", + "to" : "ADR-046-core-controllers", + "type" : "spec-depends-on" }, { - "from": "ADR046-exec-015", - "to": "ADR046-exec-001", - "type": "work-item-depends-on" + "from" : "ADR-046-provider-transport-unix", + "to" : "ADR-046-provider-model-and-packaging", + "type" : "spec-depends-on" }, { - "from": "ADR046-exec-015", - "to": "ADR046-exec-014", - "type": "work-item-depends-on" + "from" : "ADR-046-provider-transport-unix", + "to" : "ADR-046-provider-state", + "type" : "spec-depends-on" }, { - "from": "ADR046-exec-015", - "to": "ADR046-exec-022", - "type": "work-item-depends-on" + "from" : "ADR-046-provider-transport-unix", + "to" : "ADR-046-resource-api-and-authorization", + "type" : "spec-depends-on" }, { - "from": "ADR046-exec-016", - "to": "ADR-046-resources-host-guest-process-user", - "type": "implements-spec" + "from" : "ADR-046-provider-transport-unix", + "to" : "ADR-046-resource-object-model", + "type" : "spec-depends-on" }, { - "from": "ADR046-exec-016", - "to": "ADR046-exec-001", - "type": "work-item-depends-on" + "from" : "ADR-046-provider-transport-unix", + "to" : "ADR-046-resources-host-guest-process-user", + "type" : "spec-depends-on" }, { - "from": "ADR046-exec-017", - "to": "ADR-046-resources-host-guest-process-user", - "type": "implements-spec" + "from" : "ADR-046-provider-transport-unix", + "to" : "ADR-046-resources-zone-control", + "type" : "spec-depends-on" }, { - "from": "ADR046-exec-017", - "to": "ADR046-exec-016", - "type": "work-item-depends-on" + "from" : "ADR-046-provider-transport-unix", + "to" : "ADR-046-terminology-and-identities", + "type" : "spec-depends-on" }, { - "from": "ADR046-exec-018", - "to": "ADR-046-resources-host-guest-process-user", - "type": "implements-spec" + "from" : "ADR-046-provider-transport-unix", + "to" : "ADR-046-zone-routing", + "type" : "spec-depends-on" }, { - "from": "ADR046-exec-018", - "to": "ADR046-exec-016", - "type": "work-item-depends-on" + "from" : "ADR-046-provider-transport-vsock", + "to" : "ADR-046-componentsession-and-bus", + "type" : "spec-depends-on" }, { - "from": "ADR046-exec-019", - "to": "ADR-046-resources-host-guest-process-user", - "type": "implements-spec" + "from" : "ADR-046-provider-transport-vsock", + "to" : "ADR-046-provider-model-and-packaging", + "type" : "spec-depends-on" }, { - "from": "ADR046-exec-019", - "to": "ADR046-exec-016", - "type": "work-item-depends-on" + "from" : "ADR-046-provider-transport-vsock", + "to" : "ADR-046-provider-state", + "type" : "spec-depends-on" }, { - "from": "ADR046-exec-019", - "to": "ADR046-exec-018", - "type": "work-item-depends-on" + "from" : "ADR-046-provider-transport-vsock", + "to" : "ADR-046-resource-api-and-authorization", + "type" : "spec-depends-on" }, { - "from": "ADR046-exec-020", - "to": "ADR-046-resources-host-guest-process-user", - "type": "implements-spec" + "from" : "ADR-046-provider-transport-vsock", + "to" : "ADR-046-resources-host-guest-process-user", + "type" : "spec-depends-on" }, { - "from": "ADR046-exec-020", - "to": "ADR046-exec-019", - "type": "work-item-depends-on" + "from" : "ADR-046-provider-transport-vsock", + "to" : "ADR-046-resources-zone-control", + "type" : "spec-depends-on" }, { - "from": "ADR046-exec-021", - "to": "ADR-046-resources-host-guest-process-user", - "type": "implements-spec" + "from" : "ADR-046-provider-transport-vsock", + "to" : "ADR-046-terminology-and-identities", + "type" : "spec-depends-on" }, { - "from": "ADR046-exec-021", - "to": "ADR046-exec-016", - "type": "work-item-depends-on" + "from" : "ADR-046-provider-transport-vsock", + "to" : "ADR-046-zone-routing", + "type" : "spec-depends-on" }, { - "from": "ADR046-exec-021", - "to": "ADR046-exec-018", - "type": "work-item-depends-on" + "from" : "ADR-046-provider-volume-local", + "to" : "ADR-046-components-processes-and-sandbox", + "type" : "spec-depends-on" }, { - "from": "ADR046-exec-022", - "to": "ADR-046-resources-host-guest-process-user", - "type": "implements-spec" + "from" : "ADR-046-provider-volume-local", + "to" : "ADR-046-componentsession-and-bus", + "type" : "spec-depends-on" }, { - "from": "ADR046-exec-022", - "to": "ADR046-exec-016", - "type": "work-item-depends-on" + "from" : "ADR-046-provider-volume-local", + "to" : "ADR-046-nix-configuration", + "type" : "spec-depends-on" }, { - "from": "ADR046-exec-022", - "to": "ADR046-exec-021", - "type": "work-item-depends-on" + "from" : "ADR-046-provider-volume-local", + "to" : "ADR-046-primitive-resource-composition", + "type" : "spec-depends-on" }, { - "from": "ADR046-exec-023", - "to": "ADR-046-resources-host-guest-process-user", - "type": "implements-spec" + "from" : "ADR-046-provider-volume-local", + "to" : "ADR-046-provider-state", + "type" : "spec-depends-on" }, { - "from": "ADR046-exec-023", - "to": "ADR046-exec-019", - "type": "work-item-depends-on" + "from" : "ADR-046-provider-volume-local", + "to" : "ADR-046-resource-api-and-authorization", + "type" : "spec-depends-on" }, { - "from": "ADR046-exec-023", - "to": "ADR046-exec-021", - "type": "work-item-depends-on" + "from" : "ADR-046-provider-volume-local", + "to" : "ADR-046-resources-volume", + "type" : "spec-depends-on" }, { - "from": "ADR046-feasibility-001", - "to": "ADR-046-feasibility-and-spikes", - "type": "implements-spec" + "from" : "ADR-046-provider-volume-local", + "to" : "ADR-046-telemetry-audit-and-support", + "type" : "spec-depends-on" }, { - "from": "ADR046-feasibility-002", - "to": "ADR-046-feasibility-and-spikes", - "type": "implements-spec" + "from" : "ADR-046-provider-volume-virtiofs", + "to" : "ADR-046-components-processes-and-sandbox", + "type" : "spec-depends-on" }, { - "from": "ADR046-feasibility-002", - "to": "ADR046-feasibility-001", - "type": "work-item-depends-on" + "from" : "ADR-046-provider-volume-virtiofs", + "to" : "ADR-046-componentsession-and-bus", + "type" : "spec-depends-on" }, { - "from": "ADR046-feasibility-003", - "to": "ADR-046-feasibility-and-spikes", - "type": "implements-spec" + "from" : "ADR-046-provider-volume-virtiofs", + "to" : "ADR-046-nix-configuration", + "type" : "spec-depends-on" }, { - "from": "ADR046-feasibility-004", - "to": "ADR-046-feasibility-and-spikes", - "type": "implements-spec" + "from" : "ADR-046-provider-volume-virtiofs", + "to" : "ADR-046-provider-model-and-packaging", + "type" : "spec-depends-on" }, { - "from": "ADR046-feasibility-005", - "to": "ADR-046-feasibility-and-spikes", - "type": "implements-spec" + "from" : "ADR-046-provider-volume-virtiofs", + "to" : "ADR-046-provider-state", + "type" : "spec-depends-on" }, { - "from": "ADR046-feasibility-006", - "to": "ADR-046-feasibility-and-spikes", - "type": "implements-spec" + "from" : "ADR-046-provider-volume-virtiofs", + "to" : "ADR-046-resource-api-and-authorization", + "type" : "spec-depends-on" }, { - "from": "ADR046-feasibility-007", - "to": "ADR-046-feasibility-and-spikes", - "type": "implements-spec" + "from" : "ADR-046-provider-volume-virtiofs", + "to" : "ADR-046-resource-reconciliation", + "type" : "spec-depends-on" }, { - "from": "ADR046-feasibility-008", - "to": "ADR-046-feasibility-and-spikes", - "type": "implements-spec" + "from" : "ADR-046-provider-volume-virtiofs", + "to" : "ADR-046-resources-host-guest-process-user", + "type" : "spec-depends-on" }, { - "from": "ADR046-feasibility-009", - "to": "ADR-046-feasibility-and-spikes", - "type": "implements-spec" + "from" : "ADR-046-provider-volume-virtiofs", + "to" : "ADR-046-resources-volume", + "type" : "spec-depends-on" }, { - "from": "ADR046-feasibility-010", - "to": "ADR-046-feasibility-and-spikes", - "type": "implements-spec" + "from" : "ADR-046-provider-volume-virtiofs", + "to" : "ADR-046-telemetry-audit-and-support", + "type" : "spec-depends-on" }, { - "from": "ADR046-feasibility-010", - "to": "ADR046-feasibility-001", - "type": "work-item-depends-on" + "from" : "ADR-046-reset-and-cutover", + "to" : "ADR-046-cli-and-operations", + "type" : "spec-depends-on" }, { - "from": "ADR046-feasibility-010", - "to": "ADR046-feasibility-009", - "type": "work-item-depends-on" + "from" : "ADR-046-reset-and-cutover", + "to" : "ADR-046-components-processes-and-sandbox", + "type" : "spec-depends-on" }, { - "from": "ADR046-feasibility-011", - "to": "ADR-046-feasibility-and-spikes", - "type": "implements-spec" + "from" : "ADR-046-reset-and-cutover", + "to" : "ADR-046-componentsession-and-bus", + "type" : "spec-depends-on" }, { - "from": "ADR046-feasibility-011", - "to": "ADR046-delivery-007", - "type": "work-item-depends-on" + "from" : "ADR-046-reset-and-cutover", + "to" : "ADR-046-core-controllers", + "type" : "spec-depends-on" }, { - "from": "ADR046-host-posture-001", - "to": "ADR-046-telemetry-audit-and-support", - "type": "implements-spec" + "from" : "ADR-046-reset-and-cutover", + "to" : "ADR-046-current-code-migration-map", + "type" : "spec-depends-on" }, { - "from": "ADR046-host-posture-001", - "to": "ADR046-audit-001", - "type": "work-item-depends-on" + "from" : "ADR-046-reset-and-cutover", + "to" : "ADR-046-decision-register", + "type" : "spec-depends-on" }, { - "from": "ADR046-host-posture-001", - "to": "ADR046-core-001", - "type": "work-item-depends-on" + "from" : "ADR-046-reset-and-cutover", + "to" : "ADR-046-nix-configuration", + "type" : "spec-depends-on" }, { - "from": "ADR046-identities-001", - "to": "ADR-046-terminology-and-identities", - "type": "implements-spec" + "from" : "ADR-046-reset-and-cutover", + "to" : "ADR-046-primitive-resource-composition", + "type" : "spec-depends-on" }, { - "from": "ADR046-identities-002", - "to": "ADR-046-terminology-and-identities", - "type": "implements-spec" + "from" : "ADR-046-reset-and-cutover", + "to" : "ADR-046-provider-model-and-packaging", + "type" : "spec-depends-on" }, { - "from": "ADR046-identities-002", - "to": "ADR046-identities-001", - "type": "work-item-depends-on" + "from" : "ADR-046-reset-and-cutover", + "to" : "ADR-046-provider-state", + "type" : "spec-depends-on" }, { - "from": "ADR046-mi-topology-001", - "to": "ADR-046-provider-credential-managed-identity", - "type": "implements-spec" + "from" : "ADR-046-reset-and-cutover", + "to" : "ADR-046-resource-api-and-authorization", + "type" : "spec-depends-on" }, { - "from": "ADR046-mi-topology-001", - "to": "ADR046-credential-001", - "type": "work-item-depends-on" + "from" : "ADR-046-reset-and-cutover", + "to" : "ADR-046-resource-object-model", + "type" : "spec-depends-on" }, { - "from": "ADR046-mi-topology-001", - "to": "ADR046-credential-002", - "type": "work-item-depends-on" + "from" : "ADR-046-reset-and-cutover", + "to" : "ADR-046-resource-reconciliation", + "type" : "spec-depends-on" }, { - "from": "ADR046-minijail-001", - "to": "ADR-046-provider-system-minijail", - "type": "implements-spec" + "from" : "ADR-046-reset-and-cutover", + "to" : "ADR-046-resource-store-redb", + "type" : "spec-depends-on" }, { - "from": "ADR046-minijail-001", - "to": "ADR046-process-001", - "type": "work-item-depends-on" + "from" : "ADR-046-reset-and-cutover", + "to" : "ADR-046-resources-credential", + "type" : "spec-depends-on" }, { - "from": "ADR046-minijail-001", - "to": "ADR046-provider-001", - "type": "work-item-depends-on" + "from" : "ADR-046-reset-and-cutover", + "to" : "ADR-046-resources-device", + "type" : "spec-depends-on" }, { - "from": "ADR046-minijail-002", - "to": "ADR-046-provider-system-minijail", - "type": "implements-spec" + "from" : "ADR-046-reset-and-cutover", + "to" : "ADR-046-resources-host-guest-process-user", + "type" : "spec-depends-on" }, { - "from": "ADR046-minijail-002", - "to": "ADR046-minijail-001", - "type": "work-item-depends-on" + "from" : "ADR-046-reset-and-cutover", + "to" : "ADR-046-resources-network", + "type" : "spec-depends-on" }, { - "from": "ADR046-minijail-003", - "to": "ADR-046-provider-system-minijail", - "type": "implements-spec" + "from" : "ADR-046-reset-and-cutover", + "to" : "ADR-046-resources-volume", + "type" : "spec-depends-on" }, { - "from": "ADR046-minijail-003", - "to": "ADR046-minijail-001", - "type": "work-item-depends-on" + "from" : "ADR-046-reset-and-cutover", + "to" : "ADR-046-resources-zone-control", + "type" : "spec-depends-on" }, { - "from": "ADR046-minijail-004", - "to": "ADR-046-provider-system-minijail", - "type": "implements-spec" + "from" : "ADR-046-reset-and-cutover", + "to" : "ADR-046-telemetry-audit-and-support", + "type" : "spec-depends-on" }, { - "from": "ADR046-minijail-004", - "to": "ADR046-minijail-003", - "type": "work-item-depends-on" + "from" : "ADR-046-reset-and-cutover", + "to" : "ADR-046-terminology-and-identities", + "type" : "spec-depends-on" }, { - "from": "ADR046-minijail-005", - "to": "ADR-046-provider-system-minijail", - "type": "implements-spec" + "from" : "ADR-046-reset-and-cutover", + "to" : "ADR-046-zone-routing", + "type" : "spec-depends-on" }, { - "from": "ADR046-minijail-005", - "to": "ADR046-bus-001", - "type": "work-item-depends-on" + "from" : "ADR-046-resource-api-and-authorization", + "to" : "ADR-046-resource-object-model", + "type" : "spec-depends-on" }, { - "from": "ADR046-minijail-005", - "to": "ADR046-minijail-001", - "type": "work-item-depends-on" + "from" : "ADR-046-resource-api-and-authorization", + "to" : "ADR-046-resource-store-redb", + "type" : "spec-depends-on" }, { - "from": "ADR046-minijail-005", - "to": "ADR046-minijail-004", - "type": "work-item-depends-on" + "from" : "ADR-046-resource-api-and-authorization", + "to" : "ADR-046-terminology-and-identities", + "type" : "spec-depends-on" }, { - "from": "ADR046-minijail-005", - "to": "ADR046-session-001", - "type": "work-item-depends-on" + "from" : "ADR-046-resource-object-model", + "to" : "ADR-046-decision-register", + "type" : "spec-depends-on" }, { - "from": "ADR046-minijail-006", - "to": "ADR-046-provider-system-minijail", - "type": "implements-spec" + "from" : "ADR-046-resource-object-model", + "to" : "ADR-046-terminology-and-identities", + "type" : "spec-depends-on" }, { - "from": "ADR046-minijail-006", - "to": "ADR046-minijail-005", - "type": "work-item-depends-on" + "from" : "ADR-046-resource-reconciliation", + "to" : "ADR-046-resource-api-and-authorization", + "type" : "spec-depends-on" }, { - "from": "ADR046-network-001", - "to": "ADR-046-resources-network", - "type": "implements-spec" + "from" : "ADR-046-resource-reconciliation", + "to" : "ADR-046-resource-object-model", + "type" : "spec-depends-on" }, { - "from": "ADR046-network-002", - "to": "ADR-046-resources-network", - "type": "implements-spec" + "from" : "ADR-046-resource-reconciliation", + "to" : "ADR-046-resource-store-redb", + "type" : "spec-depends-on" }, { - "from": "ADR046-network-002", - "to": "ADR046-network-001", - "type": "work-item-depends-on" + "from" : "ADR-046-resource-store-redb", + "to" : "ADR-046-resource-object-model", + "type" : "spec-depends-on" }, { - "from": "ADR046-network-003", - "to": "ADR-046-resources-network", - "type": "implements-spec" + "from" : "ADR-046-resource-store-redb", + "to" : "ADR-046-terminology-and-identities", + "type" : "spec-depends-on" }, { - "from": "ADR046-network-003", - "to": "ADR046-network-001", - "type": "work-item-depends-on" + "from" : "ADR-046-resources-credential", + "to" : "ADR-046-componentsession-and-bus", + "type" : "spec-depends-on" }, { - "from": "ADR046-network-003", - "to": "ADR046-network-002", - "type": "work-item-depends-on" + "from" : "ADR-046-resources-credential", + "to" : "ADR-046-primitive-resource-composition", + "type" : "spec-depends-on" }, { - "from": "ADR046-network-004", - "to": "ADR-046-resources-network", - "type": "implements-spec" + "from" : "ADR-046-resources-credential", + "to" : "ADR-046-provider-model-and-packaging", + "type" : "spec-depends-on" }, { - "from": "ADR046-network-004", - "to": "ADR046-network-001", - "type": "work-item-depends-on" + "from" : "ADR-046-resources-credential", + "to" : "ADR-046-resource-api-and-authorization", + "type" : "spec-depends-on" }, { - "from": "ADR046-network-004", - "to": "ADR046-network-002", - "type": "work-item-depends-on" + "from" : "ADR-046-resources-credential", + "to" : "ADR-046-resource-object-model", + "type" : "spec-depends-on" }, { - "from": "ADR046-network-004", - "to": "ADR046-network-003", - "type": "work-item-depends-on" + "from" : "ADR-046-resources-credential", + "to" : "ADR-046-resource-reconciliation", + "type" : "spec-depends-on" }, { - "from": "ADR046-network-005", - "to": "ADR-046-resources-network", - "type": "implements-spec" + "from" : "ADR-046-resources-credential", + "to" : "ADR-046-terminology-and-identities", + "type" : "spec-depends-on" }, { - "from": "ADR046-network-005", - "to": "ADR046-network-001", - "type": "work-item-depends-on" + "from" : "ADR-046-resources-device", + "to" : "ADR-046-components-processes-and-sandbox", + "type" : "spec-depends-on" }, { - "from": "ADR046-network-006", - "to": "ADR-046-resources-network", - "type": "implements-spec" + "from" : "ADR-046-resources-device", + "to" : "ADR-046-primitive-resource-composition", + "type" : "spec-depends-on" }, { - "from": "ADR046-network-006", - "to": "ADR046-network-001", - "type": "work-item-depends-on" + "from" : "ADR-046-resources-device", + "to" : "ADR-046-provider-model-and-packaging", + "type" : "spec-depends-on" }, { - "from": "ADR046-network-006", - "to": "ADR046-network-005", - "type": "work-item-depends-on" + "from" : "ADR-046-resources-device", + "to" : "ADR-046-resource-api-and-authorization", + "type" : "spec-depends-on" }, { - "from": "ADR046-network-007", - "to": "ADR-046-resources-network", - "type": "implements-spec" + "from" : "ADR-046-resources-device", + "to" : "ADR-046-resource-object-model", + "type" : "spec-depends-on" }, { - "from": "ADR046-network-007", - "to": "ADR046-network-005", - "type": "work-item-depends-on" + "from" : "ADR-046-resources-device", + "to" : "ADR-046-resource-reconciliation", + "type" : "spec-depends-on" }, { - "from": "ADR046-network-008", - "to": "ADR-046-resources-network", - "type": "implements-spec" + "from" : "ADR-046-resources-host-guest-process-user", + "to" : "ADR-046-components-processes-and-sandbox", + "type" : "spec-depends-on" }, { - "from": "ADR046-network-008", - "to": "ADR046-core-001", - "type": "file-overlap-order" + "from" : "ADR-046-resources-host-guest-process-user", + "to" : "ADR-046-core-controllers", + "type" : "spec-depends-on" }, { - "from": "ADR046-network-008", - "to": "ADR046-core-001", - "type": "work-item-depends-on" + "from" : "ADR-046-resources-host-guest-process-user", + "to" : "ADR-046-decision-register", + "type" : "spec-depends-on" }, { - "from": "ADR046-network-008", - "to": "ADR046-network-004", - "type": "work-item-depends-on" + "from" : "ADR-046-resources-host-guest-process-user", + "to" : "ADR-046-primitive-resource-composition", + "type" : "spec-depends-on" }, { - "from": "ADR046-network-008", - "to": "ADR046-network-005", - "type": "work-item-depends-on" + "from" : "ADR-046-resources-host-guest-process-user", + "to" : "ADR-046-provider-model-and-packaging", + "type" : "spec-depends-on" }, { - "from": "ADR046-network-009", - "to": "ADR-046-provider-network-local", - "type": "implements-spec" + "from" : "ADR-046-resources-host-guest-process-user", + "to" : "ADR-046-resource-api-and-authorization", + "type" : "spec-depends-on" }, { - "from": "ADR046-network-010", - "to": "ADR-046-provider-network-local", - "type": "implements-spec" + "from" : "ADR-046-resources-host-guest-process-user", + "to" : "ADR-046-resource-object-model", + "type" : "spec-depends-on" }, { - "from": "ADR046-network-011", - "to": "ADR-046-provider-network-local", - "type": "implements-spec" + "from" : "ADR-046-resources-host-guest-process-user", + "to" : "ADR-046-resource-reconciliation", + "type" : "spec-depends-on" }, { - "from": "ADR046-network-012", - "to": "ADR-046-provider-network-local", - "type": "implements-spec" + "from" : "ADR-046-resources-host-guest-process-user", + "to" : "ADR-046-terminology-and-identities", + "type" : "spec-depends-on" }, { - "from": "ADR046-network-013", - "to": "ADR-046-provider-network-local", - "type": "implements-spec" + "from" : "ADR-046-resources-network", + "to" : "ADR-046-primitive-resource-composition", + "type" : "spec-depends-on" }, { - "from": "ADR046-network-014", - "to": "ADR-046-provider-network-local", - "type": "implements-spec" + "from" : "ADR-046-resources-network", + "to" : "ADR-046-provider-model-and-packaging", + "type" : "spec-depends-on" }, { - "from": "ADR046-network-014", - "to": "ADR046-nl-006", - "type": "work-item-depends-on" + "from" : "ADR-046-resources-network", + "to" : "ADR-046-resource-object-model", + "type" : "spec-depends-on" }, { - "from": "ADR046-network-015", - "to": "ADR-046-provider-network-local", - "type": "implements-spec" + "from" : "ADR-046-resources-network", + "to" : "ADR-046-resource-reconciliation", + "type" : "spec-depends-on" }, { - "from": "ADR046-network-016", - "to": "ADR-046-provider-network-local", - "type": "implements-spec" + "from" : "ADR-046-resources-network", + "to" : "ADR-046-terminology-and-identities", + "type" : "spec-depends-on" }, { - "from": "ADR046-network-017", - "to": "ADR-046-provider-network-local", - "type": "implements-spec" + "from" : "ADR-046-resources-volume", + "to" : "ADR-046-components-processes-and-sandbox", + "type" : "spec-depends-on" }, { - "from": "ADR046-network-018", - "to": "ADR-046-provider-network-local", - "type": "implements-spec" + "from" : "ADR-046-resources-volume", + "to" : "ADR-046-primitive-resource-composition", + "type" : "spec-depends-on" }, { - "from": "ADR046-network-019", - "to": "ADR-046-provider-network-local", - "type": "implements-spec" + "from" : "ADR-046-resources-volume", + "to" : "ADR-046-provider-model-and-packaging", + "type" : "spec-depends-on" }, { - "from": "ADR046-nix-001", - "to": "ADR-046-nix-configuration", - "type": "implements-spec" + "from" : "ADR-046-resources-volume", + "to" : "ADR-046-resource-object-model", + "type" : "spec-depends-on" }, { - "from": "ADR046-nix-001", - "to": "ADR046-identities-001", - "type": "work-item-depends-on" + "from" : "ADR-046-resources-volume", + "to" : "ADR-046-resource-reconciliation", + "type" : "spec-depends-on" }, { - "from": "ADR046-nix-001", - "to": "ADR046-identities-002", - "type": "work-item-depends-on" + "from" : "ADR-046-resources-zone-control", + "to" : "ADR-046-core-controllers", + "type" : "spec-depends-on" }, { - "from": "ADR046-nix-002", - "to": "ADR-046-nix-configuration", - "type": "implements-spec" + "from" : "ADR-046-resources-zone-control", + "to" : "ADR-046-decision-register", + "type" : "spec-depends-on" }, { - "from": "ADR046-nix-002", - "to": "ADR046-nix-001", - "type": "work-item-depends-on" + "from" : "ADR-046-resources-zone-control", + "to" : "ADR-046-provider-model-and-packaging", + "type" : "spec-depends-on" }, { - "from": "ADR046-nix-003", - "to": "ADR-046-nix-configuration", - "type": "implements-spec" + "from" : "ADR-046-resources-zone-control", + "to" : "ADR-046-resource-api-and-authorization", + "type" : "spec-depends-on" }, { - "from": "ADR046-nix-003", - "to": "ADR046-nix-001", - "type": "work-item-depends-on" + "from" : "ADR-046-resources-zone-control", + "to" : "ADR-046-resource-object-model", + "type" : "spec-depends-on" }, { - "from": "ADR046-nix-004", - "to": "ADR-046-nix-configuration", - "type": "implements-spec" + "from" : "ADR-046-resources-zone-control", + "to" : "ADR-046-resource-reconciliation", + "type" : "spec-depends-on" }, { - "from": "ADR046-nix-004", - "to": "ADR046-nix-001", - "type": "work-item-depends-on" + "from" : "ADR-046-resources-zone-control", + "to" : "ADR-046-resource-store-redb", + "type" : "spec-depends-on" }, { - "from": "ADR046-nix-004", - "to": "ADR046-nix-002", - "type": "work-item-depends-on" + "from" : "ADR-046-resources-zone-control", + "to" : "ADR-046-terminology-and-identities", + "type" : "spec-depends-on" }, { - "from": "ADR046-nix-005", - "to": "ADR-046-nix-configuration", - "type": "implements-spec" + "from" : "ADR-046-security-and-threat-model", + "to" : "ADR-046-cli-and-operations", + "type" : "spec-depends-on" }, { - "from": "ADR046-nix-005", - "to": "ADR046-nix-004", - "type": "work-item-depends-on" + "from" : "ADR-046-security-and-threat-model", + "to" : "ADR-046-components-processes-and-sandbox", + "type" : "spec-depends-on" }, { - "from": "ADR046-nix-006", - "to": "ADR-046-nix-configuration", - "type": "implements-spec" + "from" : "ADR-046-security-and-threat-model", + "to" : "ADR-046-componentsession-and-bus", + "type" : "spec-depends-on" }, { - "from": "ADR046-nix-006", - "to": "ADR046-nix-005", - "type": "work-item-depends-on" + "from" : "ADR-046-security-and-threat-model", + "to" : "ADR-046-core-controllers", + "type" : "spec-depends-on" }, { - "from": "ADR046-nix-006", - "to": "ADR046-primitives-002", - "type": "work-item-depends-on" + "from" : "ADR-046-security-and-threat-model", + "to" : "ADR-046-current-code-migration-map", + "type" : "spec-depends-on" }, { - "from": "ADR046-nix-007", - "to": "ADR-046-nix-configuration", - "type": "implements-spec" + "from" : "ADR-046-security-and-threat-model", + "to" : "ADR-046-decision-register", + "type" : "spec-depends-on" }, { - "from": "ADR046-nix-007", - "to": "ADR046-nix-005", - "type": "work-item-depends-on" + "from" : "ADR-046-security-and-threat-model", + "to" : "ADR-046-nix-configuration", + "type" : "spec-depends-on" }, { - "from": "ADR046-nix-007", - "to": "ADR046-primitives-003", - "type": "work-item-depends-on" + "from" : "ADR-046-security-and-threat-model", + "to" : "ADR-046-primitive-resource-composition", + "type" : "spec-depends-on" }, { - "from": "ADR046-nix-008", - "to": "ADR-046-nix-configuration", - "type": "implements-spec" + "from" : "ADR-046-security-and-threat-model", + "to" : "ADR-046-provider-activation-nixos", + "type" : "spec-depends-on" }, { - "from": "ADR046-nix-008", - "to": "ADR046-nix-004", - "type": "work-item-depends-on" + "from" : "ADR-046-security-and-threat-model", + "to" : "ADR-046-provider-audio-pipewire", + "type" : "spec-depends-on" }, { - "from": "ADR046-nix-009", - "to": "ADR-046-nix-configuration", - "type": "implements-spec" + "from" : "ADR-046-security-and-threat-model", + "to" : "ADR-046-provider-clipboard-wayland", + "type" : "spec-depends-on" }, { - "from": "ADR046-nix-009", - "to": "ADR046-nix-006", - "type": "work-item-depends-on" + "from" : "ADR-046-security-and-threat-model", + "to" : "ADR-046-provider-credential-entra", + "type" : "spec-depends-on" }, { - "from": "ADR046-nix-010", - "to": "ADR-046-nix-configuration", - "type": "implements-spec" + "from" : "ADR-046-security-and-threat-model", + "to" : "ADR-046-provider-credential-managed-identity", + "type" : "spec-depends-on" }, { - "from": "ADR046-nix-010", - "to": "ADR046-nix-001", - "type": "work-item-depends-on" + "from" : "ADR-046-security-and-threat-model", + "to" : "ADR-046-provider-credential-secret-service", + "type" : "spec-depends-on" }, { - "from": "ADR046-nix-011", - "to": "ADR-046-nix-configuration", - "type": "implements-spec" + "from" : "ADR-046-security-and-threat-model", + "to" : "ADR-046-provider-device-gpu", + "type" : "spec-depends-on" }, { - "from": "ADR046-nix-012", - "to": "ADR-046-nix-configuration", - "type": "implements-spec" + "from" : "ADR-046-security-and-threat-model", + "to" : "ADR-046-provider-device-security-key", + "type" : "spec-depends-on" }, { - "from": "ADR046-nix-012", - "to": "ADR046-nix-005", - "type": "work-item-depends-on" + "from" : "ADR-046-security-and-threat-model", + "to" : "ADR-046-provider-device-tpm", + "type" : "spec-depends-on" }, { - "from": "ADR046-nix-012", - "to": "ADR046-nix-022", - "type": "work-item-depends-on" + "from" : "ADR-046-security-and-threat-model", + "to" : "ADR-046-provider-device-usbip", + "type" : "spec-depends-on" }, { - "from": "ADR046-nix-013", - "to": "ADR-046-nix-configuration", - "type": "implements-spec" + "from" : "ADR-046-security-and-threat-model", + "to" : "ADR-046-provider-display-wayland", + "type" : "spec-depends-on" }, { - "from": "ADR046-nix-013", - "to": "ADR046-nix-005", - "type": "work-item-depends-on" + "from" : "ADR-046-security-and-threat-model", + "to" : "ADR-046-provider-model-and-packaging", + "type" : "spec-depends-on" }, { - "from": "ADR046-nix-014", - "to": "ADR-046-nix-configuration", - "type": "implements-spec" + "from" : "ADR-046-security-and-threat-model", + "to" : "ADR-046-provider-network-local", + "type" : "spec-depends-on" }, { - "from": "ADR046-nix-014", - "to": "ADR046-nix-001", - "type": "work-item-depends-on" + "from" : "ADR-046-security-and-threat-model", + "to" : "ADR-046-provider-notification-desktop", + "type" : "spec-depends-on" }, { - "from": "ADR046-nix-014", - "to": "ADR046-nix-002", - "type": "work-item-depends-on" + "from" : "ADR-046-security-and-threat-model", + "to" : "ADR-046-provider-observability-otel", + "type" : "spec-depends-on" }, { - "from": "ADR046-nix-015", - "to": "ADR-046-nix-configuration", - "type": "implements-spec" + "from" : "ADR-046-security-and-threat-model", + "to" : "ADR-046-provider-runtime-azure-container-apps", + "type" : "spec-depends-on" }, { - "from": "ADR046-nix-015", - "to": "ADR046-nix-001", - "type": "work-item-depends-on" + "from" : "ADR-046-security-and-threat-model", + "to" : "ADR-046-provider-runtime-azure-virtual-machine", + "type" : "spec-depends-on" }, { - "from": "ADR046-nix-016", - "to": "ADR-046-nix-configuration", - "type": "implements-spec" + "from" : "ADR-046-security-and-threat-model", + "to" : "ADR-046-provider-runtime-cloud-hypervisor", + "type" : "spec-depends-on" }, { - "from": "ADR046-nix-016", - "to": "ADR046-nix-002", - "type": "work-item-depends-on" + "from" : "ADR-046-security-and-threat-model", + "to" : "ADR-046-provider-runtime-qemu-media", + "type" : "spec-depends-on" }, { - "from": "ADR046-nix-017", - "to": "ADR-046-nix-configuration", - "type": "implements-spec" + "from" : "ADR-046-security-and-threat-model", + "to" : "ADR-046-provider-shell-terminal", + "type" : "spec-depends-on" }, { - "from": "ADR046-nix-017", - "to": "ADR046-nix-012", - "type": "work-item-depends-on" + "from" : "ADR-046-security-and-threat-model", + "to" : "ADR-046-provider-state", + "type" : "spec-depends-on" }, { - "from": "ADR046-nix-018", - "to": "ADR-046-nix-configuration", - "type": "implements-spec" + "from" : "ADR-046-security-and-threat-model", + "to" : "ADR-046-provider-system-core", + "type" : "spec-depends-on" }, { - "from": "ADR046-nix-018", - "to": "ADR046-nix-002", - "type": "work-item-depends-on" + "from" : "ADR-046-security-and-threat-model", + "to" : "ADR-046-provider-system-minijail", + "type" : "spec-depends-on" }, { - "from": "ADR046-nix-019", - "to": "ADR-046-nix-configuration", - "type": "implements-spec" + "from" : "ADR-046-security-and-threat-model", + "to" : "ADR-046-provider-system-systemd", + "type" : "spec-depends-on" }, { - "from": "ADR046-nix-019", - "to": "ADR046-nix-001", - "type": "work-item-depends-on" + "from" : "ADR-046-security-and-threat-model", + "to" : "ADR-046-provider-transport-azure-relay", + "type" : "spec-depends-on" }, { - "from": "ADR046-nix-019", - "to": "ADR046-nix-005", - "type": "work-item-depends-on" + "from" : "ADR-046-security-and-threat-model", + "to" : "ADR-046-provider-transport-unix", + "type" : "spec-depends-on" }, { - "from": "ADR046-nix-019", - "to": "ADR046-nix-027", - "type": "work-item-depends-on" + "from" : "ADR-046-security-and-threat-model", + "to" : "ADR-046-provider-transport-vsock", + "type" : "spec-depends-on" }, { - "from": "ADR046-nix-020", - "to": "ADR-046-nix-configuration", - "type": "implements-spec" + "from" : "ADR-046-security-and-threat-model", + "to" : "ADR-046-provider-volume-local", + "type" : "spec-depends-on" }, { - "from": "ADR046-nix-020", - "to": "ADR046-nix-001", - "type": "work-item-depends-on" + "from" : "ADR-046-security-and-threat-model", + "to" : "ADR-046-provider-volume-virtiofs", + "type" : "spec-depends-on" }, { - "from": "ADR046-nix-020", - "to": "ADR046-nix-005", - "type": "work-item-depends-on" + "from" : "ADR-046-security-and-threat-model", + "to" : "ADR-046-resource-api-and-authorization", + "type" : "spec-depends-on" }, { - "from": "ADR046-nix-021", - "to": "ADR-046-nix-configuration", - "type": "implements-spec" + "from" : "ADR-046-security-and-threat-model", + "to" : "ADR-046-resource-object-model", + "type" : "spec-depends-on" }, { - "from": "ADR046-nix-021", - "to": "ADR046-nix-001", - "type": "work-item-depends-on" + "from" : "ADR-046-security-and-threat-model", + "to" : "ADR-046-resource-reconciliation", + "type" : "spec-depends-on" }, { - "from": "ADR046-nix-022", - "to": "ADR-046-nix-configuration", - "type": "implements-spec" + "from" : "ADR-046-security-and-threat-model", + "to" : "ADR-046-resource-store-redb", + "type" : "spec-depends-on" }, { - "from": "ADR046-nix-022", - "to": "ADR046-nix-005", - "type": "work-item-depends-on" + "from" : "ADR-046-security-and-threat-model", + "to" : "ADR-046-resources-credential", + "type" : "spec-depends-on" }, { - "from": "ADR046-nix-022", - "to": "ADR046-nix-027", - "type": "work-item-depends-on" + "from" : "ADR-046-security-and-threat-model", + "to" : "ADR-046-resources-device", + "type" : "spec-depends-on" }, { - "from": "ADR046-nix-023", - "to": "ADR-046-nix-configuration", - "type": "implements-spec" + "from" : "ADR-046-security-and-threat-model", + "to" : "ADR-046-resources-host-guest-process-user", + "type" : "spec-depends-on" }, { - "from": "ADR046-nix-023", - "to": "ADR046-nix-027", - "type": "work-item-depends-on" + "from" : "ADR-046-security-and-threat-model", + "to" : "ADR-046-resources-network", + "type" : "spec-depends-on" }, { - "from": "ADR046-nix-024", - "to": "ADR-046-nix-configuration", - "type": "implements-spec" + "from" : "ADR-046-security-and-threat-model", + "to" : "ADR-046-resources-volume", + "type" : "spec-depends-on" }, { - "from": "ADR046-nix-024", - "to": "ADR046-nix-023", - "type": "work-item-depends-on" + "from" : "ADR-046-security-and-threat-model", + "to" : "ADR-046-resources-zone-control", + "type" : "spec-depends-on" }, { - "from": "ADR046-nix-024", - "to": "ADR046-nix-027", - "type": "work-item-depends-on" + "from" : "ADR-046-security-and-threat-model", + "to" : "ADR-046-telemetry-audit-and-support", + "type" : "spec-depends-on" }, { - "from": "ADR046-nix-025", - "to": "ADR-046-nix-configuration", - "type": "implements-spec" + "from" : "ADR-046-security-and-threat-model", + "to" : "ADR-046-terminology-and-identities", + "type" : "spec-depends-on" }, { - "from": "ADR046-nix-025", - "to": "ADR046-nix-023", - "type": "work-item-depends-on" + "from" : "ADR-046-security-and-threat-model", + "to" : "ADR-046-zone-routing", + "type" : "spec-depends-on" }, { - "from": "ADR046-nix-025", - "to": "ADR046-nix-027", - "type": "work-item-depends-on" + "from" : "ADR-046-streamline", + "to" : "ADR-046-current-code-migration-map", + "type" : "spec-depends-on" }, { - "from": "ADR046-nix-026", - "to": "ADR-046-nix-configuration", - "type": "implements-spec" + "from" : "ADR-046-streamline", + "to" : "ADR-046-decision-register", + "type" : "spec-depends-on" }, { - "from": "ADR046-nix-026", - "to": "ADR046-nix-023", - "type": "work-item-depends-on" + "from" : "ADR-046-streamline", + "to" : "ADR-046-primitive-resource-composition", + "type" : "spec-depends-on" }, { - "from": "ADR046-nix-027", - "to": "ADR-046-nix-configuration", - "type": "implements-spec" + "from" : "ADR-046-streamline", + "to" : "ADR-046-provider-model-and-packaging", + "type" : "spec-depends-on" }, { - "from": "ADR046-nix-028", - "to": "ADR-046-nix-configuration", - "type": "implements-spec" + "from" : "ADR-046-streamline", + "to" : "ADR-046-provider-state", + "type" : "spec-depends-on" }, { - "from": "ADR046-nix-028", - "to": "ADR046-nix-027", - "type": "work-item-depends-on" + "from" : "ADR-046-streamline", + "to" : "ADR-046-reset-and-cutover", + "type" : "spec-depends-on" }, { - "from": "ADR046-nix-029", - "to": "ADR-046-nix-configuration", - "type": "implements-spec" + "from" : "ADR-046-streamline", + "to" : "ADR-046-resources-volume", + "type" : "spec-depends-on" }, { - "from": "ADR046-nix-029", - "to": "ADR046-nix-027", - "type": "work-item-depends-on" + "from" : "ADR-046-streamline", + "to" : "ADR-046-terminology-and-identities", + "type" : "spec-depends-on" }, { - "from": "ADR046-nix-029", - "to": "ADR046-nix-028", - "type": "work-item-depends-on" + "from" : "ADR-046-streamline", + "to" : "ADR-046-zone-routing", + "type" : "spec-depends-on" }, { - "from": "ADR046-nix-030", - "to": "ADR-046-nix-configuration", - "type": "implements-spec" + "from" : "ADR-046-telemetry-audit-and-support", + "to" : "ADR-046-components-processes-and-sandbox", + "type" : "spec-depends-on" }, { - "from": "ADR046-nix-030", - "to": "ADR046-nix-028", - "type": "work-item-depends-on" + "from" : "ADR-046-telemetry-audit-and-support", + "to" : "ADR-046-componentsession-and-bus", + "type" : "spec-depends-on" }, { - "from": "ADR046-nix-030", - "to": "ADR046-nix-029", - "type": "work-item-depends-on" + "from" : "ADR-046-telemetry-audit-and-support", + "to" : "ADR-046-core-controllers", + "type" : "spec-depends-on" }, { - "from": "ADR046-nix-031", - "to": "ADR-046-nix-configuration", - "type": "implements-spec" + "from" : "ADR-046-telemetry-audit-and-support", + "to" : "ADR-046-provider-model-and-packaging", + "type" : "spec-depends-on" }, { - "from": "ADR046-nix-031", - "to": "ADR046-provider-004", - "type": "work-item-depends-on" + "from" : "ADR-046-telemetry-audit-and-support", + "to" : "ADR-046-resource-object-model", + "type" : "spec-depends-on" }, { - "from": "ADR046-nix-031", - "to": "ADR046-zone-control-019", - "type": "work-item-depends-on" + "from" : "ADR-046-telemetry-audit-and-support", + "to" : "ADR-046-resource-store-redb", + "type" : "spec-depends-on" }, { - "from": "ADR046-nix-031", - "to": "ADR046-zone-control-020", - "type": "work-item-depends-on" + "from" : "ADR-046-telemetry-audit-and-support", + "to" : "ADR-046-terminology-and-identities", + "type" : "spec-depends-on" }, { - "from": "ADR046-nix-031", - "to": "ADR046-zone-control-024", - "type": "work-item-depends-on" + "from" : "ADR-046-terminology-and-identities", + "to" : "ADR-046-decision-register", + "type" : "spec-depends-on" }, { - "from": "ADR046-nl-001", - "to": "ADR-046-provider-network-local", - "type": "implements-spec" + "from" : "ADR-046-validation-and-delivery", + "to" : "ADR-046-cli-and-operations", + "type" : "spec-depends-on" }, { - "from": "ADR046-nl-002", - "to": "ADR-046-provider-network-local", - "type": "implements-spec" + "from" : "ADR-046-validation-and-delivery", + "to" : "ADR-046-components-processes-and-sandbox", + "type" : "spec-depends-on" }, { - "from": "ADR046-nl-002", - "to": "ADR046-nl-001", - "type": "work-item-depends-on" + "from" : "ADR-046-validation-and-delivery", + "to" : "ADR-046-componentsession-and-bus", + "type" : "spec-depends-on" }, { - "from": "ADR046-nl-003", - "to": "ADR-046-provider-network-local", - "type": "implements-spec" + "from" : "ADR-046-validation-and-delivery", + "to" : "ADR-046-core-controllers", + "type" : "spec-depends-on" }, { - "from": "ADR046-nl-004", - "to": "ADR-046-provider-network-local", - "type": "implements-spec" + "from" : "ADR-046-validation-and-delivery", + "to" : "ADR-046-current-code-migration-map", + "type" : "spec-depends-on" }, { - "from": "ADR046-nl-004", - "to": "ADR046-nl-003", - "type": "work-item-depends-on" + "from" : "ADR-046-validation-and-delivery", + "to" : "ADR-046-decision-register", + "type" : "spec-depends-on" }, { - "from": "ADR046-nl-005", - "to": "ADR-046-provider-network-local", - "type": "implements-spec" + "from" : "ADR-046-validation-and-delivery", + "to" : "ADR-046-feasibility-and-spikes", + "type" : "spec-depends-on" }, { - "from": "ADR046-nl-006", - "to": "ADR-046-provider-network-local", - "type": "implements-spec" + "from" : "ADR-046-validation-and-delivery", + "to" : "ADR-046-nix-configuration", + "type" : "spec-depends-on" }, { - "from": "ADR046-nl-006", - "to": "ADR046-nl-001", - "type": "work-item-depends-on" + "from" : "ADR-046-validation-and-delivery", + "to" : "ADR-046-primitive-resource-composition", + "type" : "spec-depends-on" }, { - "from": "ADR046-nl-006", - "to": "ADR046-nl-005", - "type": "work-item-depends-on" + "from" : "ADR-046-validation-and-delivery", + "to" : "ADR-046-provider-model-and-packaging", + "type" : "spec-depends-on" }, { - "from": "ADR046-nl-007", - "to": "ADR-046-provider-network-local", - "type": "implements-spec" + "from" : "ADR-046-validation-and-delivery", + "to" : "ADR-046-provider-state", + "type" : "spec-depends-on" }, { - "from": "ADR046-nl-008", - "to": "ADR-046-provider-network-local", - "type": "implements-spec" + "from" : "ADR-046-validation-and-delivery", + "to" : "ADR-046-reset-and-cutover", + "type" : "spec-depends-on" }, { - "from": "ADR046-notify-001", - "to": "ADR-046-provider-notification-desktop", - "type": "implements-spec" + "from" : "ADR-046-validation-and-delivery", + "to" : "ADR-046-resource-api-and-authorization", + "type" : "spec-depends-on" }, { - "from": "ADR046-notify-002", - "to": "ADR-046-provider-notification-desktop", - "type": "implements-spec" + "from" : "ADR-046-validation-and-delivery", + "to" : "ADR-046-resource-object-model", + "type" : "spec-depends-on" }, { - "from": "ADR046-notify-002", - "to": "ADR046-bus-001", - "type": "work-item-depends-on" + "from" : "ADR-046-validation-and-delivery", + "to" : "ADR-046-resource-reconciliation", + "type" : "spec-depends-on" }, { - "from": "ADR046-notify-002", - "to": "ADR046-session-001", - "type": "work-item-depends-on" + "from" : "ADR-046-validation-and-delivery", + "to" : "ADR-046-resource-store-redb", + "type" : "spec-depends-on" }, { - "from": "ADR046-notify-003", - "to": "ADR-046-provider-notification-desktop", - "type": "implements-spec" + "from" : "ADR-046-validation-and-delivery", + "to" : "ADR-046-resources-credential", + "type" : "spec-depends-on" }, { - "from": "ADR046-notify-003", - "to": "ADR046-notify-001", - "type": "work-item-depends-on" + "from" : "ADR-046-validation-and-delivery", + "to" : "ADR-046-resources-device", + "type" : "spec-depends-on" }, { - "from": "ADR046-notify-003", - "to": "ADR046-reconcile-001", - "type": "work-item-depends-on" + "from" : "ADR-046-validation-and-delivery", + "to" : "ADR-046-resources-host-guest-process-user", + "type" : "spec-depends-on" }, { - "from": "ADR046-notify-004", - "to": "ADR-046-provider-notification-desktop", - "type": "implements-spec" + "from" : "ADR-046-validation-and-delivery", + "to" : "ADR-046-resources-network", + "type" : "spec-depends-on" }, { - "from": "ADR046-notify-004", - "to": "ADR046-notify-002", - "type": "work-item-depends-on" + "from" : "ADR-046-validation-and-delivery", + "to" : "ADR-046-resources-volume", + "type" : "spec-depends-on" }, { - "from": "ADR046-notify-004", - "to": "ADR046-notify-003", - "type": "work-item-depends-on" + "from" : "ADR-046-validation-and-delivery", + "to" : "ADR-046-resources-zone-control", + "type" : "spec-depends-on" }, { - "from": "ADR046-notify-005", - "to": "ADR-046-provider-notification-desktop", - "type": "implements-spec" + "from" : "ADR-046-validation-and-delivery", + "to" : "ADR-046-security-and-threat-model", + "type" : "spec-depends-on" }, { - "from": "ADR046-notify-005", - "to": "ADR046-notify-002", - "type": "work-item-depends-on" + "from" : "ADR-046-validation-and-delivery", + "to" : "ADR-046-streamline", + "type" : "spec-depends-on" }, { - "from": "ADR046-notify-006", - "to": "ADR-046-provider-notification-desktop", - "type": "implements-spec" + "from" : "ADR-046-validation-and-delivery", + "to" : "ADR-046-telemetry-audit-and-support", + "type" : "spec-depends-on" }, { - "from": "ADR046-notify-006", - "to": "ADR046-notify-001", - "type": "work-item-depends-on" + "from" : "ADR-046-validation-and-delivery", + "to" : "ADR-046-terminology-and-identities", + "type" : "spec-depends-on" }, { - "from": "ADR046-object-001", - "to": "ADR-046-resource-object-model", - "type": "implements-spec" + "from" : "ADR-046-validation-and-delivery", + "to" : "ADR-046-zone-routing", + "type" : "spec-depends-on" }, { - "from": "ADR046-object-002", - "to": "ADR-046-resource-object-model", - "type": "implements-spec" + "from" : "ADR-046-zone-routing", + "to" : "ADR-046-componentsession-and-bus", + "type" : "spec-depends-on" }, { - "from": "ADR046-object-002", - "to": "ADR046-object-001", - "type": "work-item-depends-on" + "from" : "ADR-046-zone-routing", + "to" : "ADR-046-resource-api-and-authorization", + "type" : "spec-depends-on" }, { - "from": "ADR046-otel-001", - "to": "ADR-046-provider-observability-otel", - "type": "implements-spec" + "from" : "ADR-046-zone-routing", + "to" : "ADR-046-resource-object-model", + "type" : "spec-depends-on" }, { - "from": "ADR046-otel-001", - "to": "ADR046-telem-001", - "type": "work-item-depends-on" + "from" : "ADR-046-zone-routing", + "to" : "ADR-046-resource-reconciliation", + "type" : "spec-depends-on" }, { - "from": "ADR046-otel-002", - "to": "ADR-046-provider-observability-otel", - "type": "implements-spec" + "from" : "ADR-046-zone-routing", + "to" : "ADR-046-terminology-and-identities", + "type" : "spec-depends-on" }, { - "from": "ADR046-otel-002", - "to": "ADR046-otel-001", - "type": "work-item-depends-on" + "from" : "ADR046-aca-001", + "to" : "ADR046-provider-001", + "type" : "work-item-depends-on" }, { - "from": "ADR046-otel-002", - "to": "ADR046-provider-001", - "type": "work-item-depends-on" + "from" : "ADR046-aca-002", + "to" : "ADR046-aca-001", + "type" : "work-item-depends-on" }, { - "from": "ADR046-otel-002", - "to": "ADR046-provider-004", - "type": "work-item-depends-on" + "from" : "ADR046-aca-003", + "to" : "ADR046-aca-001", + "type" : "work-item-depends-on" }, { - "from": "ADR046-otel-002", - "to": "ADR046-telem-001", - "type": "work-item-depends-on" + "from" : "ADR046-aca-004", + "to" : "ADR046-session-001", + "type" : "work-item-depends-on" }, { - "from": "ADR046-otel-003", - "to": "ADR-046-provider-observability-otel", - "type": "implements-spec" + "from" : "ADR046-aca-005", + "to" : "ADR046-aca-001", + "type" : "work-item-depends-on" }, { - "from": "ADR046-otel-003", - "to": "ADR046-otel-002", - "type": "work-item-depends-on" + "from" : "ADR046-aca-006", + "to" : "ADR046-aca-001", + "type" : "work-item-depends-on" }, { - "from": "ADR046-otel-003", - "to": "ADR046-telem-001", - "type": "work-item-depends-on" + "from" : "ADR046-aca-007", + "to" : "ADR046-aca-001", + "type" : "work-item-depends-on" }, { - "from": "ADR046-otel-003", - "to": "ADR046-volume-001", - "type": "work-item-depends-on" + "from" : "ADR046-activation-003", + "to" : "ADR046-activation-002", + "type" : "work-item-depends-on" }, { - "from": "ADR046-otel-004", - "to": "ADR-046-provider-observability-otel", - "type": "implements-spec" + "from" : "ADR046-activation-004", + "to" : "ADR046-activation-003", + "type" : "work-item-depends-on" }, { - "from": "ADR046-otel-004", - "to": "ADR046-otel-002", - "type": "work-item-depends-on" + "from" : "ADR046-activation-007", + "to" : "ADR046-activation-005", + "type" : "work-item-depends-on" }, { - "from": "ADR046-otel-005", - "to": "ADR-046-provider-observability-otel", - "type": "implements-spec" + "from" : "ADR046-api-002", + "to" : "ADR046-api-001", + "type" : "work-item-depends-on" }, { - "from": "ADR046-otel-005", - "to": "ADR046-zone-control-019", - "type": "work-item-depends-on" + "from" : "ADR046-audio-002", + "to" : "ADR046-audio-001", + "type" : "work-item-depends-on" }, { - "from": "ADR046-otel-005", - "to": "ADR046-zone-control-020", - "type": "work-item-depends-on" + "from" : "ADR046-audio-004", + "to" : "ADR046-audio-001", + "type" : "work-item-depends-on" }, { - "from": "ADR046-otel-006", - "to": "ADR-046-provider-observability-otel", - "type": "implements-spec" + "from" : "ADR046-audio-005", + "to" : "ADR046-provider-004", + "type" : "work-item-depends-on" }, { - "from": "ADR046-otel-006", - "to": "ADR046-otel-005", - "type": "work-item-depends-on" + "from" : "ADR046-audio-006", + "to" : "ADR046-audio-001", + "type" : "work-item-depends-on" }, { - "from": "ADR046-otel-006", - "to": "ADR046-zone-control-019", - "type": "work-item-depends-on" + "from" : "ADR046-audio-006", + "to" : "ADR046-audio-005", + "type" : "work-item-depends-on" }, { - "from": "ADR046-otel-006", - "to": "ADR046-zone-control-020", - "type": "work-item-depends-on" + "from" : "ADR046-audio-006", + "to" : "ADR046-audio-007", + "type" : "work-item-depends-on" }, { - "from": "ADR046-pkg-001", - "to": "ADR-046-resources-zone-control", - "type": "implements-spec" + "from" : "ADR046-audio-006", + "to" : "ADR046-audio-011", + "type" : "work-item-depends-on" }, { - "from": "ADR046-primitives-001", - "to": "ADR-046-primitive-resource-composition", - "type": "implements-spec" + "from" : "ADR046-audio-007", + "to" : "ADR046-audio-004", + "type" : "work-item-depends-on" }, { - "from": "ADR046-primitives-002", - "to": "ADR-046-primitive-resource-composition", - "type": "implements-spec" + "from" : "ADR046-audio-008", + "to" : "ADR046-audio-005", + "type" : "work-item-depends-on" }, { - "from": "ADR046-primitives-003", - "to": "ADR-046-primitive-resource-composition", - "type": "implements-spec" + "from" : "ADR046-audio-009", + "to" : "ADR046-audio-005", + "type" : "work-item-depends-on" }, { - "from": "ADR046-process-001", - "to": "ADR-046-components-processes-and-sandbox", - "type": "implements-spec" + "from" : "ADR046-audio-010", + "to" : "ADR046-audio-006", + "type" : "work-item-depends-on" }, { - "from": "ADR046-process-002", - "to": "ADR-046-components-processes-and-sandbox", - "type": "implements-spec" + "from" : "ADR046-audio-010", + "to" : "ADR046-audio-007", + "type" : "work-item-depends-on" }, { - "from": "ADR046-process-002", - "to": "ADR046-process-001", - "type": "work-item-depends-on" + "from" : "ADR046-audio-010", + "to" : "ADR046-audio-011", + "type" : "work-item-depends-on" }, { - "from": "ADR046-provider-001", - "to": "ADR-046-provider-model-and-packaging", - "type": "implements-spec" + "from" : "ADR046-audio-011", + "to" : "ADR046-audio-004", + "type" : "work-item-depends-on" }, { - "from": "ADR046-provider-002", - "to": "ADR-046-provider-model-and-packaging", - "type": "implements-spec" + "from" : "ADR046-audio-012", + "to" : "ADR046-zone-control-019", + "type" : "work-item-depends-on" }, { - "from": "ADR046-provider-003", - "to": "ADR-046-provider-model-and-packaging", - "type": "implements-spec" + "from" : "ADR046-audio-012", + "to" : "ADR046-zone-control-020", + "type" : "work-item-depends-on" }, { - "from": "ADR046-provider-004", - "to": "ADR-046-provider-model-and-packaging", - "type": "implements-spec" + "from" : "ADR046-audio-013", + "to" : "ADR046-audio-001", + "type" : "work-item-depends-on" }, { - "from": "ADR046-provider-004", - "to": "ADR046-provider-001", - "type": "work-item-depends-on" + "from" : "ADR046-audio-013", + "to" : "ADR046-audio-004", + "type" : "work-item-depends-on" }, { - "from": "ADR046-provider-agent-001", - "to": "ADR-046-resources-zone-control", - "type": "implements-spec" + "from" : "ADR046-audio-013", + "to" : "ADR046-zone-control-019", + "type" : "work-item-depends-on" }, { - "from": "ADR046-provider-device-gpu-01", - "to": "ADR-046-provider-device-gpu", - "type": "implements-spec" + "from" : "ADR046-audio-014", + "to" : "ADR046-audio-013", + "type" : "work-item-depends-on" }, { - "from": "ADR046-provider-device-gpu-02", - "to": "ADR-046-provider-device-gpu", - "type": "implements-spec" + "from" : "ADR046-audio-014", + "to" : "ADR046-zone-control-019", + "type" : "work-item-depends-on" }, { - "from": "ADR046-provider-device-gpu-03", - "to": "ADR-046-provider-device-gpu", - "type": "implements-spec" + "from" : "ADR046-azure-vm-002", + "to" : "ADR046-azure-vm-001", + "type" : "work-item-depends-on" }, { - "from": "ADR046-provider-device-gpu-04", - "to": "ADR-046-provider-device-gpu", - "type": "implements-spec" + "from" : "ADR046-azure-vm-003", + "to" : "ADR046-azure-vm-001", + "type" : "work-item-depends-on" }, { - "from": "ADR046-provider-device-gpu-05", - "to": "ADR-046-provider-device-gpu", - "type": "implements-spec" + "from" : "ADR046-azure-vm-003", + "to" : "ADR046-azure-vm-002", + "type" : "work-item-depends-on" }, { - "from": "ADR046-provider-device-gpu-06", - "to": "ADR-046-provider-device-gpu", - "type": "implements-spec" + "from" : "ADR046-azure-vm-004", + "to" : "ADR046-azure-vm-001", + "type" : "work-item-depends-on" }, { - "from": "ADR046-provider-device-gpu-07", - "to": "ADR-046-provider-device-gpu", - "type": "implements-spec" + "from" : "ADR046-azure-vm-005", + "to" : "ADR046-azure-vm-003", + "type" : "work-item-depends-on" }, { - "from": "ADR046-provider-device-gpu-08", - "to": "ADR-046-provider-device-gpu", - "type": "implements-spec" + "from" : "ADR046-azure-vm-006", + "to" : "ADR046-azure-vm-003", + "type" : "work-item-depends-on" }, { - "from": "ADR046-provider-device-gpu-09", - "to": "ADR-046-provider-device-gpu", - "type": "implements-spec" + "from" : "ADR046-azure-vm-007", + "to" : "ADR046-azure-vm-001", + "type" : "work-item-depends-on" }, { - "from": "ADR046-pstate-001", - "to": "ADR-046-provider-state", - "type": "implements-spec" + "from" : "ADR046-azure-vm-008", + "to" : "ADR046-azure-vm-003", + "type" : "work-item-depends-on" }, { - "from": "ADR046-pstate-001", - "to": "ADR046-primitives-001", - "type": "work-item-depends-on" + "from" : "ADR046-ch-002", + "to" : "ADR046-ch-001", + "type" : "work-item-depends-on" }, { - "from": "ADR046-pstate-002", - "to": "ADR-046-provider-state", - "type": "implements-spec" + "from" : "ADR046-ch-003", + "to" : "ADR046-ch-002", + "type" : "work-item-depends-on" }, { - "from": "ADR046-pstate-002", - "to": "ADR046-pstate-001", - "type": "work-item-depends-on" + "from" : "ADR046-ch-004", + "to" : "ADR046-ch-002", + "type" : "work-item-depends-on" }, { - "from": "ADR046-pstate-003", - "to": "ADR-046-provider-state", - "type": "implements-spec" + "from" : "ADR046-ch-004", + "to" : "ADR046-identities-002", + "type" : "work-item-depends-on" }, { - "from": "ADR046-pstate-003", - "to": "ADR046-pstate-001", - "type": "work-item-depends-on" + "from" : "ADR046-ch-005", + "to" : "ADR046-ch-001", + "type" : "work-item-depends-on" }, { - "from": "ADR046-pstate-004", - "to": "ADR-046-provider-state", - "type": "implements-spec" + "from" : "ADR046-ch-005", + "to" : "ADR046-session-001", + "type" : "work-item-depends-on" }, { - "from": "ADR046-pstate-004", - "to": "ADR046-pstate-003", - "type": "work-item-depends-on" + "from" : "ADR046-ch-006", + "to" : "ADR046-ch-001", + "type" : "work-item-depends-on" }, { - "from": "ADR046-pstate-004", - "to": "ADR046-reconcile-001", - "type": "work-item-depends-on" + "from" : "ADR046-ch-006", + "to" : "ADR046-telem-001", + "type" : "work-item-depends-on" }, { - "from": "ADR046-pstate-005", - "to": "ADR-046-provider-state", - "type": "implements-spec" + "from" : "ADR046-ch-007", + "to" : "ADR046-ch-001", + "type" : "work-item-depends-on" }, { - "from": "ADR046-pstate-005", - "to": "ADR046-pstate-003", - "type": "work-item-depends-on" + "from" : "ADR046-ch-007", + "to" : "ADR046-pstate-001", + "type" : "work-item-depends-on" }, { - "from": "ADR046-pstate-006", - "to": "ADR-046-provider-state", - "type": "implements-spec" + "from" : "ADR046-cli-001", + "to" : "ADR046-api-001", + "type" : "work-item-depends-on" }, { - "from": "ADR046-pstate-006", - "to": "ADR046-pstate-003", - "type": "work-item-depends-on" + "from" : "ADR046-cli-001", + "to" : "ADR046-identities-001", + "type" : "work-item-depends-on" }, { - "from": "ADR046-pstate-006", - "to": "ADR046-pstate-004", - "type": "work-item-depends-on" + "from" : "ADR046-cli-002", + "to" : "ADR046-api-001", + "type" : "work-item-depends-on" }, { - "from": "ADR046-pstate-007", - "to": "ADR-046-provider-state", - "type": "implements-spec" + "from" : "ADR046-cli-002", + "to" : "ADR046-cli-001", + "type" : "work-item-depends-on" }, { - "from": "ADR046-pstate-007", - "to": "ADR046-pstate-003", - "type": "work-item-depends-on" + "from" : "ADR046-cli-003", + "to" : "ADR046-api-001", + "type" : "work-item-depends-on" }, { - "from": "ADR046-pstate-008", - "to": "ADR-046-provider-state", - "type": "implements-spec" + "from" : "ADR046-cli-003", + "to" : "ADR046-cli-001", + "type" : "work-item-depends-on" }, { - "from": "ADR046-pstate-008", - "to": "ADR046-pstate-001", - "type": "work-item-depends-on" + "from" : "ADR046-cli-004", + "to" : "ADR046-cli-001", + "type" : "work-item-depends-on" }, { - "from": "ADR046-pstate-009", - "to": "ADR-046-provider-state", - "type": "implements-spec" + "from" : "ADR046-cli-005", + "to" : "ADR046-cli-001", + "type" : "work-item-depends-on" }, { - "from": "ADR046-pstate-009", - "to": "ADR046-pstate-001", - "type": "work-item-depends-on" + "from" : "ADR046-cli-006", + "to" : "ADR046-cli-001", + "type" : "work-item-depends-on" }, { - "from": "ADR046-pstate-009", - "to": "ADR046-pstate-008", - "type": "work-item-depends-on" + "from" : "ADR046-cli-007", + "to" : "ADR046-cli-001", + "type" : "work-item-depends-on" }, { - "from": "ADR046-pstate-010", - "to": "ADR-046-provider-state", - "type": "implements-spec" + "from" : "ADR046-cli-008", + "to" : "ADR046-cli-001", + "type" : "work-item-depends-on" }, { - "from": "ADR046-pstate-010", - "to": "ADR046-pstate-001", - "type": "work-item-depends-on" + "from" : "ADR046-cli-009", + "to" : "ADR046-cli-001", + "type" : "work-item-depends-on" }, { - "from": "ADR046-pstate-011", - "to": "ADR-046-provider-state", - "type": "implements-spec" + "from" : "ADR046-cli-010", + "to" : "ADR046-cli-001", + "type" : "work-item-depends-on" }, { - "from": "ADR046-pstate-011", - "to": "ADR046-pstate-003", - "type": "work-item-depends-on" + "from" : "ADR046-cli-011", + "to" : "ADR046-cli-001", + "type" : "work-item-depends-on" }, { - "from": "ADR046-pstate-012", - "to": "ADR-046-provider-state", - "type": "implements-spec" + "from" : "ADR046-cli-011", + "to" : "ADR046-cli-002", + "type" : "work-item-depends-on" }, { - "from": "ADR046-pstate-012", - "to": "ADR046-pstate-001", - "type": "work-item-depends-on" + "from" : "ADR046-cli-011", + "to" : "ADR046-cli-007", + "type" : "work-item-depends-on" }, { - "from": "ADR046-pstate-012", - "to": "ADR046-pstate-002", - "type": "work-item-depends-on" + "from" : "ADR046-cli-011", + "to" : "ADR046-identities-002", + "type" : "work-item-depends-on" }, { - "from": "ADR046-reconcile-001", - "to": "ADR-046-resource-reconciliation", - "type": "implements-spec" + "from" : "ADR046-cli-012", + "to" : "ADR046-cli-001", + "type" : "work-item-depends-on" }, { - "from": "ADR046-reconcile-002", - "to": "ADR-046-resource-reconciliation", - "type": "implements-spec" + "from" : "ADR046-cli-012", + "to" : "ADR046-cli-010", + "type" : "work-item-depends-on" }, { - "from": "ADR046-reconcile-002", - "to": "ADR046-reconcile-001", - "type": "work-item-depends-on" + "from" : "ADR046-cli-013", + "to" : "ADR046-cli-001", + "type" : "work-item-depends-on" }, { - "from": "ADR046-reconcile-003", - "to": "ADR-046-resource-reconciliation", - "type": "implements-spec" + "from" : "ADR046-cli-013", + "to" : "ADR046-cli-010", + "type" : "work-item-depends-on" }, { - "from": "ADR046-reset-001", - "to": "ADR-046-reset-and-cutover", - "type": "implements-spec" + "from" : "ADR046-cli-013", + "to" : "ADR046-zone-control-019", + "type" : "work-item-depends-on" }, { - "from": "ADR046-reset-002", - "to": "ADR-046-reset-and-cutover", - "type": "implements-spec" + "from" : "ADR046-clipboard-002", + "to" : "ADR046-clipboard-001", + "type" : "work-item-depends-on" }, { - "from": "ADR046-reset-002", - "to": "ADR046-reset-001", - "type": "work-item-depends-on" + "from" : "ADR046-clipboard-003", + "to" : "ADR046-clipboard-001", + "type" : "work-item-depends-on" }, { - "from": "ADR046-reset-003", - "to": "ADR-046-reset-and-cutover", - "type": "implements-spec" + "from" : "ADR046-clipboard-004", + "to" : "ADR046-clipboard-001", + "type" : "work-item-depends-on" }, { - "from": "ADR046-reset-003", - "to": "ADR046-reset-001", - "type": "work-item-depends-on" + "from" : "ADR046-clipboard-004", + "to" : "ADR046-clipboard-003", + "type" : "work-item-depends-on" }, { - "from": "ADR046-reset-003", - "to": "ADR046-reset-002", - "type": "work-item-depends-on" + "from" : "ADR046-clipboard-005", + "to" : "ADR046-clipboard-001", + "type" : "work-item-depends-on" }, { - "from": "ADR046-reset-004", - "to": "ADR-046-reset-and-cutover", - "type": "implements-spec" + "from" : "ADR046-clipboard-007", + "to" : "ADR046-clipboard-003", + "type" : "work-item-depends-on" }, { - "from": "ADR046-reset-004", - "to": "ADR046-reset-003", - "type": "work-item-depends-on" + "from" : "ADR046-clipboard-008", + "to" : "ADR046-clipboard-002", + "type" : "work-item-depends-on" }, { - "from": "ADR046-reset-005", - "to": "ADR-046-reset-and-cutover", - "type": "implements-spec" + "from" : "ADR046-clipboard-009", + "to" : "ADR046-clipboard-001", + "type" : "work-item-depends-on" }, { - "from": "ADR046-reset-005", - "to": "ADR046-core-001", - "type": "work-item-depends-on" + "from" : "ADR046-clipboard-009", + "to" : "ADR046-clipboard-002", + "type" : "work-item-depends-on" }, { - "from": "ADR046-reset-005", - "to": "ADR046-reset-004", - "type": "work-item-depends-on" + "from" : "ADR046-clipboard-009", + "to" : "ADR046-clipboard-003", + "type" : "work-item-depends-on" }, { - "from": "ADR046-reset-005", - "to": "ADR046-store-003", - "type": "work-item-depends-on" + "from" : "ADR046-clipboard-009", + "to" : "ADR046-clipboard-004", + "type" : "work-item-depends-on" }, { - "from": "ADR046-reset-006", - "to": "ADR-046-reset-and-cutover", - "type": "implements-spec" + "from" : "ADR046-clipboard-009", + "to" : "ADR046-clipboard-005", + "type" : "work-item-depends-on" }, { - "from": "ADR046-reset-006", - "to": "ADR046-reset-005", - "type": "work-item-depends-on" + "from" : "ADR046-clipboard-009", + "to" : "ADR046-clipboard-006", + "type" : "work-item-depends-on" }, { - "from": "ADR046-reset-007", - "to": "ADR-046-reset-and-cutover", - "type": "implements-spec" + "from" : "ADR046-clipboard-009", + "to" : "ADR046-clipboard-007", + "type" : "work-item-depends-on" }, { - "from": "ADR046-reset-007", - "to": "ADR046-reset-006", - "type": "work-item-depends-on" + "from" : "ADR046-clipboard-009", + "to" : "ADR046-clipboard-008", + "type" : "work-item-depends-on" }, { - "from": "ADR046-reset-008", - "to": "ADR-046-reset-and-cutover", - "type": "implements-spec" + "from" : "ADR046-clipboard-010", + "to" : "ADR046-clipboard-009", + "type" : "work-item-depends-on" }, { - "from": "ADR046-reset-008", - "to": "ADR046-reset-007", - "type": "work-item-depends-on" + "from" : "ADR046-clipboard-011", + "to" : "ADR046-clipboard-005", + "type" : "work-item-depends-on" }, { - "from": "ADR046-reset-009", - "to": "ADR-046-reset-and-cutover", - "type": "implements-spec" + "from" : "ADR046-clipboard-012", + "to" : "ADR046-clipboard-006", + "type" : "work-item-depends-on" }, { - "from": "ADR046-reset-009", - "to": "ADR046-reset-003", - "type": "work-item-depends-on" + "from" : "ADR046-core-002", + "to" : "ADR046-core-001", + "type" : "work-item-depends-on" }, { - "from": "ADR046-reset-009", - "to": "ADR046-reset-006", - "type": "work-item-depends-on" + "from" : "ADR046-cred-entra-001", + "to" : "ADR046-credential-001", + "type" : "work-item-depends-on" }, { - "from": "ADR046-reset-010", - "to": "ADR-046-reset-and-cutover", - "type": "implements-spec" + "from" : "ADR046-cred-entra-001", + "to" : "ADR046-credential-002", + "type" : "work-item-depends-on" }, { - "from": "ADR046-reset-010", - "to": "ADR046-reset-005", - "type": "work-item-depends-on" + "from" : "ADR046-cred-mi-001", + "to" : "ADR046-cred-mi-005", + "type" : "work-item-depends-on" }, { - "from": "ADR046-reset-010", - "to": "ADR046-reset-007", - "type": "work-item-depends-on" + "from" : "ADR046-cred-mi-001", + "to" : "ADR046-credential-001", + "type" : "work-item-depends-on" }, { - "from": "ADR046-reset-011", - "to": "ADR-046-reset-and-cutover", - "type": "implements-spec" + "from" : "ADR046-cred-mi-001", + "to" : "ADR046-credential-002", + "type" : "work-item-depends-on" }, { - "from": "ADR046-reset-011", - "to": "ADR046-reset-001", - "type": "work-item-depends-on" + "from" : "ADR046-cred-mi-001", + "to" : "ADR046-reconcile-001", + "type" : "work-item-depends-on" }, { - "from": "ADR046-reset-011", - "to": "ADR046-reset-010", - "type": "work-item-depends-on" + "from" : "ADR046-cred-mi-002", + "to" : "ADR046-cred-mi-005", + "type" : "work-item-depends-on" }, { - "from": "ADR046-reuse-001", - "to": "ADR-046-telemetry-audit-and-support", - "type": "implements-spec" + "from" : "ADR046-cred-mi-002", + "to" : "ADR046-credential-001", + "type" : "work-item-depends-on" }, { - "from": "ADR046-reuse-001", - "to": "ADR046-telem-001", - "type": "work-item-depends-on" + "from" : "ADR046-cred-mi-002", + "to" : "ADR046-credential-002", + "type" : "work-item-depends-on" }, { - "from": "ADR046-reuse-002", - "to": "ADR-046-telemetry-audit-and-support", - "type": "implements-spec" + "from" : "ADR046-cred-mi-002", + "to" : "ADR046-reconcile-001", + "type" : "work-item-depends-on" }, { - "from": "ADR046-reuse-002", - "to": "ADR046-reuse-001", - "type": "work-item-depends-on" + "from" : "ADR046-cred-mi-002", + "to" : "ADR046-reconcile-002", + "type" : "work-item-depends-on" }, { - "from": "ADR046-reuse-003", - "to": "ADR-046-telemetry-audit-and-support", - "type": "implements-spec" + "from" : "ADR046-cred-mi-003", + "to" : "ADR046-cred-mi-001", + "type" : "work-item-depends-on" }, { - "from": "ADR046-reuse-003", - "to": "ADR046-reuse-001", - "type": "work-item-depends-on" + "from" : "ADR046-cred-mi-003", + "to" : "ADR046-credential-001", + "type" : "work-item-depends-on" }, { - "from": "ADR046-reuse-004", - "to": "ADR-046-telemetry-audit-and-support", - "type": "implements-spec" + "from" : "ADR046-cred-mi-003", + "to" : "ADR046-credential-002", + "type" : "work-item-depends-on" }, { - "from": "ADR046-reuse-004", - "to": "ADR046-reuse-001", - "type": "work-item-depends-on" + "from" : "ADR046-cred-mi-004", + "to" : "ADR046-cred-mi-001", + "type" : "work-item-depends-on" }, { - "from": "ADR046-reuse-004", - "to": "ADR046-reuse-003", - "type": "work-item-depends-on" + "from" : "ADR046-cred-mi-004", + "to" : "ADR046-cred-mi-005", + "type" : "work-item-depends-on" }, { - "from": "ADR046-reuse-005", - "to": "ADR-046-telemetry-audit-and-support", - "type": "implements-spec" + "from" : "ADR046-cred-mi-005", + "to" : "ADR046-credential-001", + "type" : "work-item-depends-on" }, { - "from": "ADR046-reuse-005", - "to": "ADR046-audit-003", - "type": "work-item-depends-on" + "from" : "ADR046-cred-mi-005", + "to" : "ADR046-credential-002", + "type" : "work-item-depends-on" }, { - "from": "ADR046-reuse-005", - "to": "ADR046-reuse-004", - "type": "work-item-depends-on" + "from" : "ADR046-cred-ss-003", + "to" : "ADR046-cred-ss-001", + "type" : "work-item-depends-on" }, { - "from": "ADR046-reuse-006", - "to": "ADR-046-telemetry-audit-and-support", - "type": "implements-spec" + "from" : "ADR046-cred-ss-003", + "to" : "ADR046-cred-ss-002", + "type" : "work-item-depends-on" }, { - "from": "ADR046-reuse-006", - "to": "ADR046-bus-001", - "type": "work-item-depends-on" + "from" : "ADR046-cred-ss-003", + "to" : "ADR046-reconcile-001", + "type" : "work-item-depends-on" }, { - "from": "ADR046-reuse-006", - "to": "ADR046-reuse-001", - "type": "work-item-depends-on" + "from" : "ADR046-credential-001", + "to" : "ADR046-identities-001", + "type" : "work-item-depends-on" }, { - "from": "ADR046-reuse-007", - "to": "ADR-046-telemetry-audit-and-support", - "type": "implements-spec" + "from" : "ADR046-credential-001", + "to" : "ADR046-object-001", + "type" : "work-item-depends-on" }, { - "from": "ADR046-reuse-007", - "to": "ADR046-reuse-004", - "type": "work-item-depends-on" + "from" : "ADR046-credential-002", + "to" : "ADR046-api-001", + "type" : "work-item-depends-on" }, { - "from": "ADR046-reuse-007", - "to": "ADR046-reuse-006", - "type": "work-item-depends-on" + "from" : "ADR046-credential-002", + "to" : "ADR046-credential-001", + "type" : "work-item-depends-on" }, { - "from": "ADR046-reuse-008", - "to": "ADR-046-telemetry-audit-and-support", - "type": "implements-spec" + "from" : "ADR046-credential-002", + "to" : "ADR046-session-003", + "type" : "work-item-depends-on" }, { - "from": "ADR046-reuse-008", - "to": "ADR046-reuse-001", - "type": "work-item-depends-on" + "from" : "ADR046-credential-003", + "to" : "ADR046-credential-001", + "type" : "work-item-depends-on" }, { - "from": "ADR046-reuse-009", - "to": "ADR-046-telemetry-audit-and-support", - "type": "implements-spec" + "from" : "ADR046-credential-003", + "to" : "ADR046-credential-002", + "type" : "work-item-depends-on" }, { - "from": "ADR046-reuse-009", - "to": "ADR046-reuse-001", - "type": "work-item-depends-on" + "from" : "ADR046-credential-003", + "to" : "ADR046-reconcile-001", + "type" : "work-item-depends-on" }, { - "from": "ADR046-reuse-009", - "to": "ADR046-telem-001", - "type": "work-item-depends-on" + "from" : "ADR046-credential-004", + "to" : "ADR046-credential-001", + "type" : "work-item-depends-on" }, { - "from": "ADR046-reuse-009", - "to": "ADR046-telem-003", - "type": "work-item-depends-on" + "from" : "ADR046-credential-004", + "to" : "ADR046-credential-002", + "type" : "work-item-depends-on" }, { - "from": "ADR046-routing-001", - "to": "ADR-046-zone-routing", - "type": "implements-spec" + "from" : "ADR046-credential-004", + "to" : "ADR046-reconcile-001", + "type" : "work-item-depends-on" }, { - "from": "ADR046-routing-002", - "to": "ADR-046-zone-routing", - "type": "implements-spec" + "from" : "ADR046-credential-005", + "to" : "ADR046-credential-001", + "type" : "work-item-depends-on" }, { - "from": "ADR046-routing-002", - "to": "ADR046-routing-001", - "type": "work-item-depends-on" + "from" : "ADR046-credential-005", + "to" : "ADR046-credential-002", + "type" : "work-item-depends-on" }, { - "from": "ADR046-routing-003", - "to": "ADR-046-zone-routing", - "type": "implements-spec" + "from" : "ADR046-credential-005", + "to" : "ADR046-reconcile-001", + "type" : "work-item-depends-on" }, { - "from": "ADR046-routing-003", - "to": "ADR046-routing-001", - "type": "work-item-depends-on" + "from" : "ADR046-credential-006", + "to" : "ADR046-credential-001", + "type" : "work-item-depends-on" }, { - "from": "ADR046-routing-003", - "to": "ADR046-routing-002", - "type": "work-item-depends-on" + "from" : "ADR046-credential-006", + "to" : "ADR046-credential-002", + "type" : "work-item-depends-on" }, { - "from": "ADR046-routing-004", - "to": "ADR-046-zone-routing", - "type": "implements-spec" + "from" : "ADR046-credential-006", + "to" : "ADR046-reconcile-001", + "type" : "work-item-depends-on" }, { - "from": "ADR046-routing-004", - "to": "ADR046-routing-003", - "type": "work-item-depends-on" + "from" : "ADR046-credential-006", + "to" : "ADR046-reconcile-002", + "type" : "work-item-depends-on" }, { - "from": "ADR046-routing-005", - "to": "ADR-046-zone-routing", - "type": "implements-spec" + "from" : "ADR046-credential-007", + "to" : "ADR046-api-001", + "type" : "work-item-depends-on" }, { - "from": "ADR046-routing-005", - "to": "ADR046-routing-002", - "type": "work-item-depends-on" + "from" : "ADR046-credential-007", + "to" : "ADR046-credential-001", + "type" : "work-item-depends-on" }, { - "from": "ADR046-routing-005", - "to": "ADR046-routing-007", - "type": "work-item-depends-on" + "from" : "ADR046-credential-007", + "to" : "ADR046-identities-002", + "type" : "work-item-depends-on" }, { - "from": "ADR046-routing-006", - "to": "ADR-046-zone-routing", - "type": "implements-spec" + "from" : "ADR046-credential-007", + "to" : "ADR046-reconcile-001", + "type" : "work-item-depends-on" }, { - "from": "ADR046-routing-006", - "to": "ADR046-routing-002", - "type": "work-item-depends-on" + "from" : "ADR046-credential-008", + "to" : "ADR046-credential-001", + "type" : "work-item-depends-on" }, { - "from": "ADR046-routing-007", - "to": "ADR-046-zone-routing", - "type": "implements-spec" + "from" : "ADR046-credential-008", + "to" : "ADR046-credential-006", + "type" : "work-item-depends-on" }, { - "from": "ADR046-routing-008", - "to": "ADR-046-zone-routing", - "type": "implements-spec" + "from" : "ADR046-delivery-003", + "to" : "ADR046-delivery-002", + "type" : "work-item-depends-on" }, { - "from": "ADR046-routing-008", - "to": "ADR046-routing-007", - "type": "work-item-depends-on" + "from" : "ADR046-delivery-004", + "to" : "ADR046-delivery-002", + "type" : "work-item-depends-on" }, { - "from": "ADR046-routing-009", - "to": "ADR-046-zone-routing", - "type": "implements-spec" + "from" : "ADR046-delivery-005", + "to" : "ADR046-delivery-002", + "type" : "work-item-depends-on" }, { - "from": "ADR046-routing-009", - "to": "ADR046-routing-007", - "type": "work-item-depends-on" + "from" : "ADR046-delivery-005", + "to" : "ADR046-delivery-003", + "type" : "work-item-depends-on" }, { - "from": "ADR046-routing-010", - "to": "ADR-046-zone-routing", - "type": "implements-spec" + "from" : "ADR046-delivery-006", + "to" : "ADR046-delivery-002", + "type" : "work-item-depends-on" }, { - "from": "ADR046-routing-010", - "to": "ADR046-routing-007", - "type": "work-item-depends-on" + "from" : "ADR046-delivery-006", + "to" : "ADR046-delivery-004", + "type" : "work-item-depends-on" }, { - "from": "ADR046-routing-010", - "to": "ADR046-routing-009", - "type": "work-item-depends-on" + "from" : "ADR046-delivery-006", + "to" : "ADR046-delivery-005", + "type" : "work-item-depends-on" }, { - "from": "ADR046-routing-011", - "to": "ADR-046-zone-routing", - "type": "implements-spec" + "from" : "ADR046-delivery-008", + "to" : "ADR046-streamline-001", + "type" : "work-item-depends-on" }, { - "from": "ADR046-routing-011", - "to": "ADR046-routing-001", - "type": "work-item-depends-on" + "from" : "ADR046-delivery-009", + "to" : "ADR046-delivery-004", + "type" : "work-item-depends-on" }, { - "from": "ADR046-routing-012", - "to": "ADR-046-zone-routing", - "type": "implements-spec" + "from" : "ADR046-delivery-009", + "to" : "ADR046-delivery-008", + "type" : "work-item-depends-on" }, { - "from": "ADR046-routing-012", - "to": "ADR046-routing-001", - "type": "work-item-depends-on" + "from" : "ADR046-device-002", + "to" : "ADR046-device-001", + "type" : "work-item-depends-on" }, { - "from": "ADR046-routing-012", - "to": "ADR046-routing-011", - "type": "work-item-depends-on" + "from" : "ADR046-device-003", + "to" : "ADR046-device-001", + "type" : "work-item-depends-on" }, { - "from": "ADR046-routing-013", - "to": "ADR-046-zone-routing", - "type": "implements-spec" + "from" : "ADR046-device-004", + "to" : "ADR046-device-001", + "type" : "work-item-depends-on" }, { - "from": "ADR046-routing-013", - "to": "ADR046-routing-003", - "type": "work-item-depends-on" + "from" : "ADR046-device-005", + "to" : "ADR046-device-001", + "type" : "work-item-depends-on" }, { - "from": "ADR046-routing-013", - "to": "ADR046-routing-012", - "type": "work-item-depends-on" + "from" : "ADR046-device-006", + "to" : "ADR046-device-001", + "type" : "work-item-depends-on" }, { - "from": "ADR046-routing-014", - "to": "ADR-046-zone-routing", - "type": "implements-spec" + "from" : "ADR046-device-006", + "to" : "ADR046-device-002", + "type" : "work-item-depends-on" }, { - "from": "ADR046-routing-014", - "to": "ADR046-routing-007", - "type": "work-item-depends-on" + "from" : "ADR046-device-006", + "to" : "ADR046-device-003", + "type" : "work-item-depends-on" }, { - "from": "ADR046-routing-015", - "to": "ADR-046-zone-routing", - "type": "implements-spec" + "from" : "ADR046-device-006", + "to" : "ADR046-device-004", + "type" : "work-item-depends-on" }, { - "from": "ADR046-routing-015", - "to": "ADR046-routing-014", - "type": "work-item-depends-on" + "from" : "ADR046-device-006", + "to" : "ADR046-device-005", + "type" : "work-item-depends-on" }, { - "from": "ADR046-routing-016", - "to": "ADR-046-zone-routing", - "type": "implements-spec" + "from" : "ADR046-device-007", + "to" : "ADR046-device-006", + "type" : "work-item-depends-on" }, { - "from": "ADR046-routing-016", - "to": "ADR046-routing-001", - "type": "work-item-depends-on" + "from" : "ADR046-device-008", + "to" : "ADR046-device-002", + "type" : "work-item-depends-on" }, { - "from": "ADR046-routing-016", - "to": "ADR046-routing-007", - "type": "work-item-depends-on" + "from" : "ADR046-device-008", + "to" : "ADR046-device-003", + "type" : "work-item-depends-on" }, { - "from": "ADR046-security-001", - "to": "ADR-046-security-and-threat-model", - "type": "implements-spec" + "from" : "ADR046-device-008", + "to" : "ADR046-device-004", + "type" : "work-item-depends-on" }, { - "from": "ADR046-security-001", - "to": "ADR046-telem-008", - "type": "work-item-depends-on" + "from" : "ADR046-device-008", + "to" : "ADR046-device-005", + "type" : "work-item-depends-on" }, { - "from": "ADR046-security-002", - "to": "ADR-046-security-and-threat-model", - "type": "implements-spec" + "from" : "ADR046-device-tpm-002", + "to" : "ADR046-device-tpm-001", + "type" : "work-item-depends-on" }, { - "from": "ADR046-security-002", - "to": "ADR046-bus-001", - "type": "work-item-depends-on" + "from" : "ADR046-device-tpm-003", + "to" : "ADR046-device-tpm-002", + "type" : "work-item-depends-on" }, { - "from": "ADR046-security-002", - "to": "ADR046-session-001", - "type": "work-item-depends-on" + "from" : "ADR046-device-tpm-004", + "to" : "ADR046-device-tpm-001", + "type" : "work-item-depends-on" }, { - "from": "ADR046-security-003", - "to": "ADR-046-security-and-threat-model", - "type": "implements-spec" + "from" : "ADR046-device-tpm-005", + "to" : "ADR046-device-tpm-004", + "type" : "work-item-depends-on" }, { - "from": "ADR046-security-004", - "to": "ADR-046-security-and-threat-model", - "type": "implements-spec" + "from" : "ADR046-device-tpm-006", + "to" : "ADR046-device-tpm-003", + "type" : "work-item-depends-on" }, { - "from": "ADR046-security-004", - "to": "ADR046-bus-001", - "type": "work-item-depends-on" + "from" : "ADR046-device-tpm-007", + "to" : "ADR046-device-tpm-003", + "type" : "work-item-depends-on" }, { - "from": "ADR046-security-004", - "to": "ADR046-routing-005", - "type": "work-item-depends-on" + "from" : "ADR046-device-tpm-008", + "to" : "ADR046-device-tpm-007", + "type" : "work-item-depends-on" }, { - "from": "ADR046-security-005", - "to": "ADR-046-security-and-threat-model", - "type": "implements-spec" + "from" : "ADR046-device-tpm-009", + "to" : "ADR046-device-tpm-004", + "type" : "work-item-depends-on" }, { - "from": "ADR046-security-006", - "to": "ADR-046-security-and-threat-model", - "type": "implements-spec" + "from" : "ADR046-device-tpm-010", + "to" : "ADR046-device-tpm-001", + "type" : "work-item-depends-on" }, { - "from": "ADR046-security-006", - "to": "ADR046-minijail-002", - "type": "work-item-depends-on" + "from" : "ADR046-device-tpm-011", + "to" : "ADR046-device-tpm-001", + "type" : "work-item-depends-on" }, { - "from": "ADR046-security-006", - "to": "ADR046-minijail-003", - "type": "work-item-depends-on" + "from" : "ADR046-device-tpm-012", + "to" : "ADR046-device-tpm-003", + "type" : "work-item-depends-on" }, { - "from": "ADR046-security-007", - "to": "ADR-046-security-and-threat-model", - "type": "implements-spec" + "from" : "ADR046-device-tpm-013", + "to" : "ADR046-device-tpm-002", + "type" : "work-item-depends-on" }, { - "from": "ADR046-security-007", - "to": "ADR046-aca-001", - "type": "work-item-depends-on" + "from" : "ADR046-display-001", + "to" : "ADR046-process-001", + "type" : "work-item-depends-on" }, { - "from": "ADR046-security-007", - "to": "ADR046-minijail-005", - "type": "work-item-depends-on" + "from" : "ADR046-display-001", + "to" : "ADR046-provider-001", + "type" : "work-item-depends-on" }, { - "from": "ADR046-security-007", - "to": "ADR046-systemd-002", - "type": "work-item-depends-on" + "from" : "ADR046-display-002", + "to" : "ADR046-display-001", + "type" : "work-item-depends-on" }, { - "from": "ADR046-security-007", - "to": "ADR046-volume-001", - "type": "work-item-depends-on" + "from" : "ADR046-display-003", + "to" : "ADR046-display-001", + "type" : "work-item-depends-on" }, { - "from": "ADR046-security-008", - "to": "ADR-046-security-and-threat-model", - "type": "implements-spec" + "from" : "ADR046-display-004", + "to" : "ADR046-display-001", + "type" : "work-item-depends-on" }, { - "from": "ADR046-security-008", - "to": "ADR046-exec-002", - "type": "work-item-depends-on" + "from" : "ADR046-exec-002", + "to" : "ADR046-exec-001", + "type" : "work-item-depends-on" }, { - "from": "ADR046-security-009", - "to": "ADR-046-security-and-threat-model", - "type": "implements-spec" + "from" : "ADR046-exec-003", + "to" : "ADR046-exec-001", + "type" : "work-item-depends-on" }, { - "from": "ADR046-security-009", - "to": "ADR046-pstate-003", - "type": "work-item-depends-on" + "from" : "ADR046-exec-004", + "to" : "ADR046-exec-001", + "type" : "work-item-depends-on" }, { - "from": "ADR046-security-010", - "to": "ADR-046-security-and-threat-model", - "type": "implements-spec" + "from" : "ADR046-exec-005", + "to" : "ADR046-exec-001", + "type" : "work-item-depends-on" }, { - "from": "ADR046-security-010", - "to": "ADR046-credential-001", - "type": "work-item-depends-on" + "from" : "ADR046-exec-005", + "to" : "ADR046-exec-004", + "type" : "work-item-depends-on" }, { - "from": "ADR046-security-010", - "to": "ADR046-credential-007", - "type": "work-item-depends-on" + "from" : "ADR046-exec-006", + "to" : "ADR046-exec-001", + "type" : "work-item-depends-on" }, { - "from": "ADR046-security-011", - "to": "ADR-046-security-and-threat-model", - "type": "implements-spec" + "from" : "ADR046-exec-006", + "to" : "ADR046-exec-002", + "type" : "work-item-depends-on" }, { - "from": "ADR046-security-011", - "to": "ADR046-notify-004", - "type": "work-item-depends-on" + "from" : "ADR046-exec-007", + "to" : "ADR046-exec-001", + "type" : "work-item-depends-on" }, { - "from": "ADR046-security-012", - "to": "ADR-046-security-and-threat-model", - "type": "implements-spec" + "from" : "ADR046-exec-007", + "to" : "ADR046-exec-002", + "type" : "work-item-depends-on" }, { - "from": "ADR046-security-012", - "to": "ADR046-audit-002", - "type": "work-item-depends-on" + "from" : "ADR046-exec-008", + "to" : "ADR046-exec-006", + "type" : "work-item-depends-on" }, { - "from": "ADR046-security-013", - "to": "ADR-046-security-and-threat-model", - "type": "implements-spec" + "from" : "ADR046-exec-008", + "to" : "ADR046-exec-007", + "type" : "work-item-depends-on" }, { - "from": "ADR046-security-013", - "to": "ADR046-session-001", - "type": "work-item-depends-on" + "from" : "ADR046-exec-009", + "to" : "ADR046-exec-001", + "type" : "work-item-depends-on" }, { - "from": "ADR046-security-014", - "to": "ADR-046-security-and-threat-model", - "type": "implements-spec" + "from" : "ADR046-exec-010", + "to" : "ADR046-exec-001", + "type" : "work-item-depends-on" }, { - "from": "ADR046-security-014", - "to": "ADR046-doctor-001", - "type": "work-item-depends-on" + "from" : "ADR046-exec-010", + "to" : "ADR046-exec-007", + "type" : "work-item-depends-on" }, { - "from": "ADR046-security-014", - "to": "ADR046-doctor-002", - "type": "work-item-depends-on" + "from" : "ADR046-exec-011", + "to" : "ADR046-exec-004", + "type" : "work-item-depends-on" }, { - "from": "ADR046-security-015", - "to": "ADR-046-security-and-threat-model", - "type": "implements-spec" + "from" : "ADR046-exec-011", + "to" : "ADR046-exec-010", + "type" : "work-item-depends-on" }, { - "from": "ADR046-security-015", - "to": "ADR046-audit-001", - "type": "work-item-depends-on" + "from" : "ADR046-exec-012", + "to" : "ADR046-exec-001", + "type" : "work-item-depends-on" }, { - "from": "ADR046-security-016", - "to": "ADR-046-security-and-threat-model", - "type": "implements-spec" + "from" : "ADR046-exec-013", + "to" : "ADR046-exec-008", + "type" : "work-item-depends-on" }, { - "from": "ADR046-security-017", - "to": "ADR-046-security-and-threat-model", - "type": "implements-spec" + "from" : "ADR046-exec-014", + "to" : "ADR046-exec-001", + "type" : "work-item-depends-on" }, { - "from": "ADR046-security-017", - "to": "ADR046-routing-004", - "type": "work-item-depends-on" + "from" : "ADR046-exec-014", + "to" : "ADR046-exec-012", + "type" : "work-item-depends-on" }, { - "from": "ADR046-security-018", - "to": "ADR-046-security-and-threat-model", - "type": "implements-spec" + "from" : "ADR046-exec-015", + "to" : "ADR046-exec-001", + "type" : "work-item-depends-on" }, { - "from": "ADR046-session-001", - "to": "ADR-046-componentsession-and-bus", - "type": "implements-spec" + "from" : "ADR046-exec-015", + "to" : "ADR046-exec-014", + "type" : "work-item-depends-on" }, { - "from": "ADR046-session-002", - "to": "ADR-046-componentsession-and-bus", - "type": "implements-spec" + "from" : "ADR046-exec-015", + "to" : "ADR046-exec-022", + "type" : "work-item-depends-on" }, { - "from": "ADR046-session-002", - "to": "ADR046-session-001", - "type": "work-item-depends-on" + "from" : "ADR046-exec-016", + "to" : "ADR046-exec-001", + "type" : "work-item-depends-on" }, { - "from": "ADR046-sterm-001", - "to": "ADR-046-provider-shell-terminal", - "type": "implements-spec" + "from" : "ADR046-exec-017", + "to" : "ADR046-exec-016", + "type" : "work-item-depends-on" }, { - "from": "ADR046-sterm-002", - "to": "ADR-046-provider-shell-terminal", - "type": "implements-spec" + "from" : "ADR046-exec-018", + "to" : "ADR046-exec-016", + "type" : "work-item-depends-on" }, { - "from": "ADR046-sterm-003", - "to": "ADR-046-provider-shell-terminal", - "type": "implements-spec" + "from" : "ADR046-exec-019", + "to" : "ADR046-exec-016", + "type" : "work-item-depends-on" }, { - "from": "ADR046-sterm-004", - "to": "ADR-046-provider-shell-terminal", - "type": "implements-spec" + "from" : "ADR046-exec-019", + "to" : "ADR046-exec-018", + "type" : "work-item-depends-on" }, { - "from": "ADR046-sterm-005", - "to": "ADR-046-provider-shell-terminal", - "type": "implements-spec" + "from" : "ADR046-exec-020", + "to" : "ADR046-exec-019", + "type" : "work-item-depends-on" }, { - "from": "ADR046-sterm-006", - "to": "ADR-046-provider-shell-terminal", - "type": "implements-spec" + "from" : "ADR046-exec-021", + "to" : "ADR046-exec-016", + "type" : "work-item-depends-on" }, { - "from": "ADR046-sterm-007", - "to": "ADR-046-provider-shell-terminal", - "type": "implements-spec" + "from" : "ADR046-exec-021", + "to" : "ADR046-exec-018", + "type" : "work-item-depends-on" }, { - "from": "ADR046-sterm-008", - "to": "ADR-046-provider-shell-terminal", - "type": "implements-spec" + "from" : "ADR046-exec-022", + "to" : "ADR046-exec-016", + "type" : "work-item-depends-on" }, { - "from": "ADR046-sterm-009", - "to": "ADR-046-provider-shell-terminal", - "type": "implements-spec" + "from" : "ADR046-exec-022", + "to" : "ADR046-exec-021", + "type" : "work-item-depends-on" }, { - "from": "ADR046-sterm-010", - "to": "ADR-046-provider-shell-terminal", - "type": "implements-spec" + "from" : "ADR046-exec-023", + "to" : "ADR046-exec-019", + "type" : "work-item-depends-on" }, { - "from": "ADR046-sterm-011", - "to": "ADR-046-provider-shell-terminal", - "type": "implements-spec" + "from" : "ADR046-exec-023", + "to" : "ADR046-exec-021", + "type" : "work-item-depends-on" }, { - "from": "ADR046-sterm-012", - "to": "ADR-046-provider-shell-terminal", - "type": "implements-spec" + "from" : "ADR046-exec-024", + "to" : "ADR046-zone-control-019", + "type" : "work-item-depends-on" }, { - "from": "ADR046-sterm-013", - "to": "ADR-046-provider-shell-terminal", - "type": "implements-spec" + "from" : "ADR046-feasibility-002", + "to" : "ADR046-feasibility-001", + "type" : "work-item-depends-on" }, { - "from": "ADR046-store-001", - "to": "ADR-046-resource-store-redb", - "type": "implements-spec" + "from" : "ADR046-feasibility-010", + "to" : "ADR046-feasibility-001", + "type" : "work-item-depends-on" }, { - "from": "ADR046-store-002", - "to": "ADR-046-resource-store-redb", - "type": "implements-spec" + "from" : "ADR046-feasibility-010", + "to" : "ADR046-feasibility-009", + "type" : "work-item-depends-on" }, { - "from": "ADR046-store-002", - "to": "ADR046-store-001", - "type": "work-item-depends-on" + "from" : "ADR046-feasibility-011", + "to" : "ADR046-delivery-007", + "type" : "work-item-depends-on" }, { - "from": "ADR046-store-003", - "to": "ADR-046-resource-store-redb", - "type": "implements-spec" + "from" : "ADR046-gpu-002", + "to" : "ADR046-gpu-001", + "type" : "work-item-depends-on" }, { - "from": "ADR046-store-003", - "to": "ADR046-store-001", - "type": "work-item-depends-on" + "from" : "ADR046-gpu-003", + "to" : "ADR046-gpu-002", + "type" : "work-item-depends-on" }, { - "from": "ADR046-streamline-001", - "to": "ADR-046-streamline", - "type": "implements-spec" + "from" : "ADR046-gpu-004", + "to" : "ADR046-gpu-002", + "type" : "work-item-depends-on" }, { - "from": "ADR046-streamline-002", - "to": "ADR-046-streamline", - "type": "implements-spec" + "from" : "ADR046-gpu-005", + "to" : "ADR046-gpu-002", + "type" : "work-item-depends-on" }, { - "from": "ADR046-streamline-002", - "to": "ADR046-streamline-001", - "type": "work-item-depends-on" + "from" : "ADR046-gpu-006", + "to" : "ADR046-gpu-005", + "type" : "work-item-depends-on" }, { - "from": "ADR046-streamline-003", - "to": "ADR-046-streamline", - "type": "implements-spec" + "from" : "ADR046-gpu-007", + "to" : "ADR046-gpu-001", + "type" : "work-item-depends-on" }, { - "from": "ADR046-streamline-003", - "to": "ADR046-streamline-001", - "type": "work-item-depends-on" + "from" : "ADR046-gpu-008", + "to" : "ADR046-gpu-001", + "type" : "work-item-depends-on" }, { - "from": "ADR046-streamline-003", - "to": "ADR046-streamline-002", - "type": "work-item-depends-on" + "from" : "ADR046-gpu-009", + "to" : "ADR046-gpu-001", + "type" : "work-item-depends-on" }, { - "from": "ADR046-streamline-004", - "to": "ADR-046-streamline", - "type": "implements-spec" + "from" : "ADR046-identities-002", + "to" : "ADR046-identities-001", + "type" : "work-item-depends-on" }, { - "from": "ADR046-streamline-004", - "to": "ADR046-streamline-001", - "type": "work-item-depends-on" + "from" : "ADR046-minijail-001", + "to" : "ADR046-process-001", + "type" : "work-item-depends-on" }, { - "from": "ADR046-streamline-004", - "to": "ADR046-streamline-002", - "type": "work-item-depends-on" + "from" : "ADR046-minijail-001", + "to" : "ADR046-provider-001", + "type" : "work-item-depends-on" }, { - "from": "ADR046-streamline-005", - "to": "ADR-046-streamline", - "type": "implements-spec" + "from" : "ADR046-minijail-002", + "to" : "ADR046-minijail-001", + "type" : "work-item-depends-on" }, { - "from": "ADR046-streamline-005", - "to": "ADR046-streamline-001", - "type": "work-item-depends-on" + "from" : "ADR046-minijail-003", + "to" : "ADR046-minijail-001", + "type" : "work-item-depends-on" }, { - "from": "ADR046-streamline-006", - "to": "ADR-046-streamline", - "type": "implements-spec" + "from" : "ADR046-minijail-004", + "to" : "ADR046-minijail-003", + "type" : "work-item-depends-on" }, { - "from": "ADR046-streamline-006", - "to": "ADR046-streamline-001", - "type": "work-item-depends-on" + "from" : "ADR046-minijail-005", + "to" : "ADR046-minijail-001", + "type" : "work-item-depends-on" }, { - "from": "ADR046-streamline-007", - "to": "ADR-046-streamline", - "type": "implements-spec" + "from" : "ADR046-minijail-005", + "to" : "ADR046-minijail-002", + "type" : "work-item-depends-on" }, { - "from": "ADR046-streamline-007", - "to": "ADR046-streamline-001", - "type": "work-item-depends-on" + "from" : "ADR046-minijail-005", + "to" : "ADR046-minijail-003", + "type" : "work-item-depends-on" }, { - "from": "ADR046-streamline-008", - "to": "ADR-046-streamline", - "type": "implements-spec" + "from" : "ADR046-minijail-005", + "to" : "ADR046-minijail-004", + "type" : "work-item-depends-on" }, { - "from": "ADR046-streamline-008", - "to": "ADR046-streamline-001", - "type": "work-item-depends-on" + "from" : "ADR046-minijail-005", + "to" : "ADR046-session-001", + "type" : "work-item-depends-on" }, { - "from": "ADR046-streamline-009", - "to": "ADR-046-streamline", - "type": "implements-spec" + "from" : "ADR046-minijail-005", + "to" : "ADR046-session-003", + "type" : "work-item-depends-on" }, { - "from": "ADR046-streamline-009", - "to": "ADR046-streamline-001", - "type": "work-item-depends-on" + "from" : "ADR046-minijail-006", + "to" : "ADR046-minijail-005", + "type" : "work-item-depends-on" }, { - "from": "ADR046-streamline-010", - "to": "ADR-046-streamline", - "type": "implements-spec" + "from" : "ADR046-network-002", + "to" : "ADR046-network-001", + "type" : "work-item-depends-on" }, { - "from": "ADR046-streamline-010", - "to": "ADR046-streamline-001", - "type": "work-item-depends-on" + "from" : "ADR046-network-003", + "to" : "ADR046-network-001", + "type" : "work-item-depends-on" }, { - "from": "ADR046-streamline-011", - "to": "ADR-046-streamline", - "type": "implements-spec" + "from" : "ADR046-network-003", + "to" : "ADR046-network-002", + "type" : "work-item-depends-on" }, { - "from": "ADR046-streamline-012", - "to": "ADR-046-streamline", - "type": "implements-spec" + "from" : "ADR046-network-004", + "to" : "ADR046-network-001", + "type" : "work-item-depends-on" }, { - "from": "ADR046-streamline-012", - "to": "ADR046-streamline-011", - "type": "work-item-depends-on" + "from" : "ADR046-network-004", + "to" : "ADR046-network-002", + "type" : "work-item-depends-on" }, { - "from": "ADR046-streamline-013", - "to": "ADR-046-streamline", - "type": "implements-spec" + "from" : "ADR046-network-004", + "to" : "ADR046-network-003", + "type" : "work-item-depends-on" }, { - "from": "ADR046-streamline-013", - "to": "ADR046-streamline-001", - "type": "work-item-depends-on" + "from" : "ADR046-network-005", + "to" : "ADR046-network-001", + "type" : "work-item-depends-on" }, { - "from": "ADR046-streamline-013", - "to": "ADR046-streamline-010", - "type": "work-item-depends-on" + "from" : "ADR046-network-006", + "to" : "ADR046-network-001", + "type" : "work-item-depends-on" }, { - "from": "ADR046-streamline-014", - "to": "ADR-046-streamline", - "type": "implements-spec" + "from" : "ADR046-network-006", + "to" : "ADR046-network-005", + "type" : "work-item-depends-on" }, { - "from": "ADR046-streamline-015", - "to": "ADR-046-streamline", - "type": "implements-spec" + "from" : "ADR046-network-007", + "to" : "ADR046-network-005", + "type" : "work-item-depends-on" }, { - "from": "ADR046-streamline-015", - "to": "ADR046-streamline-001", - "type": "work-item-depends-on" + "from" : "ADR046-network-008", + "to" : "ADR046-network-004", + "type" : "work-item-depends-on" }, { - "from": "ADR046-streamline-015", - "to": "ADR046-streamline-002", - "type": "work-item-depends-on" + "from" : "ADR046-network-008", + "to" : "ADR046-network-005", + "type" : "work-item-depends-on" }, { - "from": "ADR046-streamline-015", - "to": "ADR046-streamline-009", - "type": "work-item-depends-on" + "from" : "ADR046-network-009", + "to" : "ADR046-network-001", + "type" : "work-item-depends-on" }, { - "from": "ADR046-streamline-016", - "to": "ADR-046-streamline", - "type": "implements-spec" + "from" : "ADR046-network-009", + "to" : "ADR046-network-005", + "type" : "work-item-depends-on" }, { - "from": "ADR046-streamline-016", - "to": "ADR046-streamline-001", - "type": "work-item-depends-on" + "from" : "ADR046-nix-001", + "to" : "ADR046-identities-001", + "type" : "work-item-depends-on" }, { - "from": "ADR046-streamline-016", - "to": "ADR046-streamline-008", - "type": "work-item-depends-on" + "from" : "ADR046-nix-001", + "to" : "ADR046-identities-002", + "type" : "work-item-depends-on" }, { - "from": "ADR046-streamline-017", - "to": "ADR-046-streamline", - "type": "implements-spec" + "from" : "ADR046-nix-002", + "to" : "ADR046-nix-001", + "type" : "work-item-depends-on" }, { - "from": "ADR046-streamline-018", - "to": "ADR-046-streamline", - "type": "implements-spec" + "from" : "ADR046-nix-003", + "to" : "ADR046-nix-001", + "type" : "work-item-depends-on" }, { - "from": "ADR046-streamline-018", - "to": "ADR046-streamline-010", - "type": "work-item-depends-on" + "from" : "ADR046-nix-004", + "to" : "ADR046-nix-001", + "type" : "work-item-depends-on" }, { - "from": "ADR046-streamline-019", - "to": "ADR-046-streamline", - "type": "implements-spec" + "from" : "ADR046-nix-004", + "to" : "ADR046-nix-002", + "type" : "work-item-depends-on" }, { - "from": "ADR046-streamline-019", - "to": "ADR046-streamline-001", - "type": "work-item-depends-on" + "from" : "ADR046-nix-005", + "to" : "ADR046-nix-004", + "type" : "work-item-depends-on" }, { - "from": "ADR046-streamline-020", - "to": "ADR-046-streamline", - "type": "implements-spec" + "from" : "ADR046-nix-006", + "to" : "ADR046-nix-005", + "type" : "work-item-depends-on" }, { - "from": "ADR046-streamline-020", - "to": "ADR046-streamline-001", - "type": "work-item-depends-on" + "from" : "ADR046-nix-006", + "to" : "ADR046-primitives-002", + "type" : "work-item-depends-on" }, { - "from": "ADR046-streamline-021", - "to": "ADR-046-streamline", - "type": "implements-spec" + "from" : "ADR046-nix-007", + "to" : "ADR046-nix-005", + "type" : "work-item-depends-on" }, { - "from": "ADR046-streamline-021", - "to": "ADR046-streamline-020", - "type": "work-item-depends-on" + "from" : "ADR046-nix-007", + "to" : "ADR046-primitives-003", + "type" : "work-item-depends-on" }, { - "from": "ADR046-streamline-022", - "to": "ADR-046-streamline", - "type": "implements-spec" + "from" : "ADR046-nix-008", + "to" : "ADR046-nix-004", + "type" : "work-item-depends-on" }, { - "from": "ADR046-streamline-022", - "to": "ADR046-delivery-007", - "type": "work-item-depends-on" + "from" : "ADR046-nix-009", + "to" : "ADR046-nix-006", + "type" : "work-item-depends-on" }, { - "from": "ADR046-streamline-023", - "to": "ADR-046-streamline", - "type": "implements-spec" + "from" : "ADR046-nix-010", + "to" : "ADR046-nix-001", + "type" : "work-item-depends-on" }, { - "from": "ADR046-streamline-023", - "to": "ADR046-streamline-008", - "type": "work-item-depends-on" + "from" : "ADR046-nix-012", + "to" : "ADR046-nix-005", + "type" : "work-item-depends-on" }, { - "from": "ADR046-streamline-023", - "to": "ADR046-streamline-022", - "type": "work-item-depends-on" + "from" : "ADR046-nix-012", + "to" : "ADR046-nix-022", + "type" : "work-item-depends-on" }, { - "from": "ADR046-streamline-024", - "to": "ADR-046-streamline", - "type": "implements-spec" + "from" : "ADR046-nix-013", + "to" : "ADR046-nix-005", + "type" : "work-item-depends-on" }, { - "from": "ADR046-streamline-024", - "to": "ADR046-streamline-001", - "type": "work-item-depends-on" + "from" : "ADR046-nix-014", + "to" : "ADR046-nix-001", + "type" : "work-item-depends-on" }, { - "from": "ADR046-systemd-001", - "to": "ADR-046-provider-system-systemd", - "type": "implements-spec" + "from" : "ADR046-nix-014", + "to" : "ADR046-nix-002", + "type" : "work-item-depends-on" }, { - "from": "ADR046-systemd-001", - "to": "ADR046-process-002", - "type": "work-item-depends-on" + "from" : "ADR046-nix-015", + "to" : "ADR046-nix-001", + "type" : "work-item-depends-on" }, { - "from": "ADR046-systemd-002", - "to": "ADR-046-provider-system-systemd", - "type": "implements-spec" + "from" : "ADR046-nix-016", + "to" : "ADR046-nix-002", + "type" : "work-item-depends-on" }, { - "from": "ADR046-systemd-002", - "to": "ADR046-systemd-001", - "type": "work-item-depends-on" + "from" : "ADR046-nix-017", + "to" : "ADR046-nix-012", + "type" : "work-item-depends-on" }, { - "from": "ADR046-systemd-003", - "to": "ADR-046-provider-system-systemd", - "type": "implements-spec" + "from" : "ADR046-nix-018", + "to" : "ADR046-nix-002", + "type" : "work-item-depends-on" }, { - "from": "ADR046-systemd-003", - "to": "ADR046-systemd-001", - "type": "work-item-depends-on" + "from" : "ADR046-nix-019", + "to" : "ADR046-nix-001", + "type" : "work-item-depends-on" }, { - "from": "ADR046-telem-001", - "to": "ADR-046-telemetry-audit-and-support", - "type": "implements-spec" + "from" : "ADR046-nix-019", + "to" : "ADR046-nix-005", + "type" : "work-item-depends-on" }, { - "from": "ADR046-telem-002", - "to": "ADR-046-telemetry-audit-and-support", - "type": "implements-spec" + "from" : "ADR046-nix-019", + "to" : "ADR046-nix-027", + "type" : "work-item-depends-on" }, { - "from": "ADR046-telem-002", - "to": "ADR046-store-001", - "type": "work-item-depends-on" + "from" : "ADR046-nix-020", + "to" : "ADR046-nix-001", + "type" : "work-item-depends-on" }, { - "from": "ADR046-telem-002", - "to": "ADR046-telem-001", - "type": "work-item-depends-on" + "from" : "ADR046-nix-020", + "to" : "ADR046-nix-005", + "type" : "work-item-depends-on" }, { - "from": "ADR046-telem-003", - "to": "ADR-046-telemetry-audit-and-support", - "type": "implements-spec" + "from" : "ADR046-nix-021", + "to" : "ADR046-nix-001", + "type" : "work-item-depends-on" }, { - "from": "ADR046-telem-003", - "to": "ADR046-bus-001", - "type": "work-item-depends-on" + "from" : "ADR046-nix-022", + "to" : "ADR046-nix-005", + "type" : "work-item-depends-on" }, { - "from": "ADR046-telem-003", - "to": "ADR046-session-001", - "type": "work-item-depends-on" + "from" : "ADR046-nix-022", + "to" : "ADR046-nix-027", + "type" : "work-item-depends-on" }, { - "from": "ADR046-telem-003", - "to": "ADR046-telem-001", - "type": "work-item-depends-on" + "from" : "ADR046-nix-023", + "to" : "ADR046-nix-027", + "type" : "work-item-depends-on" }, { - "from": "ADR046-telem-004", - "to": "ADR-046-telemetry-audit-and-support", - "type": "implements-spec" + "from" : "ADR046-nix-024", + "to" : "ADR046-nix-023", + "type" : "work-item-depends-on" }, { - "from": "ADR046-telem-004", - "to": "ADR046-core-001", - "type": "work-item-depends-on" + "from" : "ADR046-nix-024", + "to" : "ADR046-nix-027", + "type" : "work-item-depends-on" }, { - "from": "ADR046-telem-004", - "to": "ADR046-telem-001", - "type": "work-item-depends-on" + "from" : "ADR046-nix-025", + "to" : "ADR046-nix-023", + "type" : "work-item-depends-on" }, { - "from": "ADR046-telem-005", - "to": "ADR-046-telemetry-audit-and-support", - "type": "implements-spec" + "from" : "ADR046-nix-025", + "to" : "ADR046-nix-027", + "type" : "work-item-depends-on" }, { - "from": "ADR046-telem-005", - "to": "ADR046-process-001", - "type": "work-item-depends-on" + "from" : "ADR046-nix-026", + "to" : "ADR046-nix-023", + "type" : "work-item-depends-on" }, { - "from": "ADR046-telem-005", - "to": "ADR046-telem-001", - "type": "work-item-depends-on" + "from" : "ADR046-nix-028", + "to" : "ADR046-nix-027", + "type" : "work-item-depends-on" }, { - "from": "ADR046-telem-006", - "to": "ADR-046-telemetry-audit-and-support", - "type": "implements-spec" + "from" : "ADR046-nix-029", + "to" : "ADR046-nix-027", + "type" : "work-item-depends-on" }, { - "from": "ADR046-telem-006", - "to": "ADR046-process-001", - "type": "work-item-depends-on" + "from" : "ADR046-nix-029", + "to" : "ADR046-nix-028", + "type" : "work-item-depends-on" }, { - "from": "ADR046-telem-006", - "to": "ADR046-provider-001", - "type": "work-item-depends-on" + "from" : "ADR046-nix-030", + "to" : "ADR046-nix-028", + "type" : "work-item-depends-on" }, { - "from": "ADR046-telem-007", - "to": "ADR-046-telemetry-audit-and-support", - "type": "implements-spec" + "from" : "ADR046-nix-030", + "to" : "ADR046-nix-029", + "type" : "work-item-depends-on" }, { - "from": "ADR046-telem-007", - "to": "ADR046-telem-006", - "type": "work-item-depends-on" + "from" : "ADR046-nix-031", + "to" : "ADR046-provider-004", + "type" : "work-item-depends-on" }, { - "from": "ADR046-telem-008", - "to": "ADR-046-telemetry-audit-and-support", - "type": "implements-spec" + "from" : "ADR046-nix-031", + "to" : "ADR046-zone-control-019", + "type" : "work-item-depends-on" }, { - "from": "ADR046-telem-008", - "to": "ADR046-telem-001", - "type": "work-item-depends-on" + "from" : "ADR046-nix-031", + "to" : "ADR046-zone-control-020", + "type" : "work-item-depends-on" }, { - "from": "ADR046-telem-009", - "to": "ADR-046-telemetry-audit-and-support", - "type": "implements-spec" + "from" : "ADR046-nix-031", + "to" : "ADR046-zone-control-024", + "type" : "work-item-depends-on" }, { - "from": "ADR046-telem-010", - "to": "ADR-046-telemetry-audit-and-support", - "type": "implements-spec" + "from" : "ADR046-nix-032", + "to" : "ADR046-nix-027", + "type" : "work-item-depends-on" }, { - "from": "ADR046-telem-010", - "to": "ADR046-store-001", - "type": "work-item-depends-on" + "from" : "ADR046-nix-032", + "to" : "ADR046-nix-029", + "type" : "work-item-depends-on" }, { - "from": "ADR046-telem-010", - "to": "ADR046-telem-006", - "type": "work-item-depends-on" + "from" : "ADR046-nix-033", + "to" : "ADR046-nix-023", + "type" : "work-item-depends-on" }, { - "from": "ADR046-telem-010", - "to": "ADR046-telem-009", - "type": "work-item-depends-on" + "from" : "ADR046-nix-033", + "to" : "ADR046-nix-027", + "type" : "work-item-depends-on" }, { - "from": "ADR046-telem-011", - "to": "ADR-046-telemetry-audit-and-support", - "type": "implements-spec" + "from" : "ADR046-nix-034", + "to" : "ADR046-nix-027", + "type" : "work-item-depends-on" }, { - "from": "ADR046-telem-011", - "to": "ADR046-audit-001", - "type": "work-item-depends-on" + "from" : "ADR046-nix-034", + "to" : "ADR046-nix-028", + "type" : "work-item-depends-on" }, { - "from": "ADR046-telem-011", - "to": "ADR046-core-001", - "type": "file-overlap-order" + "from" : "ADR046-nix-034", + "to" : "ADR046-nix-029", + "type" : "work-item-depends-on" }, { - "from": "ADR046-telem-011", - "to": "ADR046-core-001", - "type": "work-item-depends-on" + "from" : "ADR046-nix-035", + "to" : "ADR046-nix-029", + "type" : "work-item-depends-on" }, { - "from": "ADR046-telem-011", - "to": "ADR046-store-001", - "type": "work-item-depends-on" + "from" : "ADR046-nix-035", + "to" : "ADR046-nix-034", + "type" : "work-item-depends-on" }, { - "from": "ADR046-telem-011", - "to": "ADR046-telem-009", - "type": "work-item-depends-on" + "from" : "ADR046-nl-002", + "to" : "ADR046-nl-001", + "type" : "work-item-depends-on" }, { - "from": "ADR046-telem-011", - "to": "ADR046-telem-010", - "type": "work-item-depends-on" + "from" : "ADR046-nl-004", + "to" : "ADR046-nl-003", + "type" : "work-item-depends-on" }, { - "from": "ADR046-transport-relay-001", - "to": "ADR-046-provider-transport-azure-relay", - "type": "implements-spec" + "from" : "ADR046-nl-006", + "to" : "ADR046-nl-001", + "type" : "work-item-depends-on" }, { - "from": "ADR046-transport-relay-002", - "to": "ADR-046-provider-transport-azure-relay", - "type": "implements-spec" + "from" : "ADR046-nl-006", + "to" : "ADR046-nl-002", + "type" : "work-item-depends-on" }, { - "from": "ADR046-transport-relay-003", - "to": "ADR-046-provider-transport-azure-relay", - "type": "implements-spec" + "from" : "ADR046-nl-006", + "to" : "ADR046-nl-003", + "type" : "work-item-depends-on" }, { - "from": "ADR046-transport-relay-004", - "to": "ADR-046-provider-transport-azure-relay", - "type": "implements-spec" + "from" : "ADR046-nl-006", + "to" : "ADR046-nl-004", + "type" : "work-item-depends-on" }, { - "from": "ADR046-transport-relay-005", - "to": "ADR-046-provider-transport-azure-relay", - "type": "implements-spec" + "from" : "ADR046-nl-006", + "to" : "ADR046-nl-005", + "type" : "work-item-depends-on" }, { - "from": "ADR046-transport-relay-006", - "to": "ADR-046-provider-transport-azure-relay", - "type": "implements-spec" + "from" : "ADR046-nl-014", + "to" : "ADR046-nl-006", + "type" : "work-item-depends-on" }, { - "from": "ADR046-transport-relay-007", - "to": "ADR-046-provider-transport-azure-relay", - "type": "implements-spec" + "from" : "ADR046-notify-002", + "to" : "ADR046-session-001", + "type" : "work-item-depends-on" }, { - "from": "ADR046-transport-unix-001", - "to": "ADR-046-provider-transport-unix", - "type": "implements-spec" + "from" : "ADR046-notify-002", + "to" : "ADR046-session-003", + "type" : "work-item-depends-on" }, { - "from": "ADR046-transport-unix-001", - "to": "ADR046-session-001", - "type": "work-item-depends-on" + "from" : "ADR046-notify-003", + "to" : "ADR046-notify-001", + "type" : "work-item-depends-on" }, { - "from": "ADR046-transport-unix-002", - "to": "ADR-046-provider-transport-unix", - "type": "implements-spec" + "from" : "ADR046-notify-003", + "to" : "ADR046-reconcile-001", + "type" : "work-item-depends-on" }, { - "from": "ADR046-transport-unix-002", - "to": "ADR046-bus-001", - "type": "work-item-depends-on" + "from" : "ADR046-notify-004", + "to" : "ADR046-notify-002", + "type" : "work-item-depends-on" }, { - "from": "ADR046-transport-unix-003", - "to": "ADR-046-provider-transport-unix", - "type": "implements-spec" + "from" : "ADR046-notify-004", + "to" : "ADR046-notify-003", + "type" : "work-item-depends-on" }, { - "from": "ADR046-transport-unix-004", - "to": "ADR-046-provider-transport-unix", - "type": "implements-spec" + "from" : "ADR046-notify-005", + "to" : "ADR046-notify-002", + "type" : "work-item-depends-on" }, { - "from": "ADR046-transport-unix-005", - "to": "ADR-046-provider-transport-unix", - "type": "implements-spec" + "from" : "ADR046-notify-006", + "to" : "ADR046-notify-001", + "type" : "work-item-depends-on" }, { - "from": "ADR046-transport-unix-006", - "to": "ADR-046-provider-transport-unix", - "type": "implements-spec" + "from" : "ADR046-object-002", + "to" : "ADR046-object-001", + "type" : "work-item-depends-on" }, { - "from": "ADR046-transport-unix-007", - "to": "ADR-046-provider-transport-unix", - "type": "implements-spec" + "from" : "ADR046-otel-001", + "to" : "ADR046-telem-001", + "type" : "work-item-depends-on" }, { - "from": "ADR046-transport-unix-007", - "to": "ADR046-bus-001", - "type": "work-item-depends-on" + "from" : "ADR046-otel-002", + "to" : "ADR046-otel-001", + "type" : "work-item-depends-on" }, { - "from": "ADR046-transport-unix-007", - "to": "ADR046-session-001", - "type": "work-item-depends-on" + "from" : "ADR046-otel-002", + "to" : "ADR046-provider-001", + "type" : "work-item-depends-on" }, { - "from": "ADR046-transport-unix-008", - "to": "ADR-046-provider-transport-unix", - "type": "implements-spec" + "from" : "ADR046-otel-002", + "to" : "ADR046-provider-004", + "type" : "work-item-depends-on" }, { - "from": "ADR046-transport-unix-008", - "to": "ADR046-provider-003", - "type": "work-item-depends-on" + "from" : "ADR046-otel-002", + "to" : "ADR046-telem-001", + "type" : "work-item-depends-on" }, { - "from": "ADR046-transport-unix-009", - "to": "ADR-046-provider-transport-unix", - "type": "implements-spec" + "from" : "ADR046-otel-003", + "to" : "ADR046-otel-002", + "type" : "work-item-depends-on" }, { - "from": "ADR046-transport-unix-010", - "to": "ADR-046-provider-transport-unix", - "type": "implements-spec" + "from" : "ADR046-otel-003", + "to" : "ADR046-telem-001", + "type" : "work-item-depends-on" }, { - "from": "ADR046-transport-unix-011", - "to": "ADR-046-provider-transport-unix", - "type": "implements-spec" + "from" : "ADR046-otel-003", + "to" : "ADR046-volume-001", + "type" : "work-item-depends-on" }, { - "from": "ADR046-usbip-001", - "to": "ADR-046-provider-device-usbip", - "type": "implements-spec" + "from" : "ADR046-otel-004", + "to" : "ADR046-otel-002", + "type" : "work-item-depends-on" }, { - "from": "ADR046-usbip-002", - "to": "ADR-046-provider-device-usbip", - "type": "implements-spec" + "from" : "ADR046-otel-005", + "to" : "ADR046-zone-control-019", + "type" : "work-item-depends-on" }, { - "from": "ADR046-usbip-002", - "to": "ADR046-usbip-001", - "type": "work-item-depends-on" + "from" : "ADR046-otel-005", + "to" : "ADR046-zone-control-020", + "type" : "work-item-depends-on" }, { - "from": "ADR046-usbip-002", - "to": "ADR046-zone-control-024", - "type": "work-item-depends-on" + "from" : "ADR046-otel-006", + "to" : "ADR046-otel-005", + "type" : "work-item-depends-on" }, { - "from": "ADR046-usbip-003", - "to": "ADR-046-provider-device-usbip", - "type": "implements-spec" + "from" : "ADR046-otel-006", + "to" : "ADR046-zone-control-019", + "type" : "work-item-depends-on" }, { - "from": "ADR046-usbip-003", - "to": "ADR046-provider-004", - "type": "work-item-depends-on" + "from" : "ADR046-otel-006", + "to" : "ADR046-zone-control-020", + "type" : "work-item-depends-on" }, { - "from": "ADR046-usbip-003", - "to": "ADR046-usbip-001", - "type": "work-item-depends-on" + "from" : "ADR046-process-002", + "to" : "ADR046-process-001", + "type" : "work-item-depends-on" }, { - "from": "ADR046-usbip-004", - "to": "ADR-046-provider-device-usbip", - "type": "implements-spec" + "from" : "ADR046-provider-004", + "to" : "ADR046-provider-001", + "type" : "work-item-depends-on" }, { - "from": "ADR046-usbip-004", - "to": "ADR046-usbip-001", - "type": "work-item-depends-on" + "from" : "ADR046-pstate-001", + "to" : "ADR046-primitives-001", + "type" : "work-item-depends-on" }, { - "from": "ADR046-usbip-004", - "to": "ADR046-usbip-002", - "type": "work-item-depends-on" + "from" : "ADR046-pstate-002", + "to" : "ADR046-pstate-001", + "type" : "work-item-depends-on" }, { - "from": "ADR046-usbip-004", - "to": "ADR046-usbip-003", - "type": "work-item-depends-on" + "from" : "ADR046-pstate-003", + "to" : "ADR046-pstate-001", + "type" : "work-item-depends-on" }, { - "from": "ADR046-usbip-004", - "to": "ADR046-zone-control-024", - "type": "work-item-depends-on" + "from" : "ADR046-pstate-004", + "to" : "ADR046-pstate-003", + "type" : "work-item-depends-on" }, { - "from": "ADR046-usbip-005", - "to": "ADR-046-provider-device-usbip", - "type": "implements-spec" + "from" : "ADR046-pstate-004", + "to" : "ADR046-reconcile-001", + "type" : "work-item-depends-on" }, { - "from": "ADR046-usbip-005", - "to": "ADR046-usbip-003", - "type": "work-item-depends-on" + "from" : "ADR046-pstate-005", + "to" : "ADR046-pstate-003", + "type" : "work-item-depends-on" }, { - "from": "ADR046-usbip-006", - "to": "ADR-046-provider-device-usbip", - "type": "implements-spec" + "from" : "ADR046-pstate-006", + "to" : "ADR046-pstate-003", + "type" : "work-item-depends-on" }, { - "from": "ADR046-usbip-006", - "to": "ADR046-usbip-003", - "type": "work-item-depends-on" + "from" : "ADR046-pstate-006", + "to" : "ADR046-pstate-004", + "type" : "work-item-depends-on" }, { - "from": "ADR046-usbip-007", - "to": "ADR-046-provider-device-usbip", - "type": "implements-spec" + "from" : "ADR046-pstate-007", + "to" : "ADR046-pstate-003", + "type" : "work-item-depends-on" }, { - "from": "ADR046-usbip-007", - "to": "ADR046-usbip-004", - "type": "work-item-depends-on" + "from" : "ADR046-pstate-008", + "to" : "ADR046-pstate-001", + "type" : "work-item-depends-on" }, { - "from": "ADR046-usbip-007", - "to": "ADR046-usbip-005", - "type": "work-item-depends-on" + "from" : "ADR046-pstate-009", + "to" : "ADR046-pstate-001", + "type" : "work-item-depends-on" }, { - "from": "ADR046-usbip-008", - "to": "ADR-046-provider-device-usbip", - "type": "implements-spec" + "from" : "ADR046-pstate-009", + "to" : "ADR046-pstate-002", + "type" : "work-item-depends-on" }, { - "from": "ADR046-usbip-008", - "to": "ADR046-usbip-003", - "type": "work-item-depends-on" + "from" : "ADR046-pstate-009", + "to" : "ADR046-pstate-003", + "type" : "work-item-depends-on" }, { - "from": "ADR046-usbip-008", - "to": "ADR046-zone-control-024", - "type": "work-item-depends-on" + "from" : "ADR046-pstate-009", + "to" : "ADR046-pstate-004", + "type" : "work-item-depends-on" }, { - "from": "ADR046-usbip-009", - "to": "ADR-046-provider-device-usbip", - "type": "implements-spec" + "from" : "ADR046-pstate-009", + "to" : "ADR046-pstate-005", + "type" : "work-item-depends-on" }, { - "from": "ADR046-usbip-009", - "to": "ADR046-usbip-004", - "type": "work-item-depends-on" + "from" : "ADR046-pstate-009", + "to" : "ADR046-pstate-006", + "type" : "work-item-depends-on" }, { - "from": "ADR046-usbip-009", - "to": "ADR046-usbip-008", - "type": "work-item-depends-on" + "from" : "ADR046-pstate-009", + "to" : "ADR046-pstate-007", + "type" : "work-item-depends-on" }, { - "from": "ADR046-user-session-001", - "to": "ADR-046-resources-host-guest-process-user", - "type": "implements-spec" + "from" : "ADR046-pstate-009", + "to" : "ADR046-pstate-008", + "type" : "work-item-depends-on" }, { - "from": "ADR046-vl-001", - "to": "ADR-046-provider-volume-local", - "type": "implements-spec" + "from" : "ADR046-pstate-010", + "to" : "ADR046-pstate-001", + "type" : "work-item-depends-on" }, { - "from": "ADR046-vl-001", - "to": "ADR046-primitives-001", - "type": "work-item-depends-on" + "from" : "ADR046-pstate-011", + "to" : "ADR046-pstate-003", + "type" : "work-item-depends-on" }, { - "from": "ADR046-vl-002", - "to": "ADR-046-provider-volume-local", - "type": "implements-spec" + "from" : "ADR046-pstate-012", + "to" : "ADR046-pstate-001", + "type" : "work-item-depends-on" }, { - "from": "ADR046-vl-002", - "to": "ADR046-vl-001", - "type": "work-item-depends-on" + "from" : "ADR046-pstate-012", + "to" : "ADR046-pstate-002", + "type" : "work-item-depends-on" }, { - "from": "ADR046-vl-003", - "to": "ADR-046-provider-volume-local", - "type": "implements-spec" + "from" : "ADR046-qemu-media-002", + "to" : "ADR046-qemu-media-001", + "type" : "work-item-depends-on" }, { - "from": "ADR046-vl-003", - "to": "ADR046-reconcile-001", - "type": "work-item-depends-on" + "from" : "ADR046-qemu-media-003", + "to" : "ADR046-qemu-media-001", + "type" : "work-item-depends-on" }, { - "from": "ADR046-vl-003", - "to": "ADR046-vl-002", - "type": "work-item-depends-on" + "from" : "ADR046-qemu-media-004", + "to" : "ADR046-qemu-media-001", + "type" : "work-item-depends-on" }, { - "from": "ADR046-vl-004", - "to": "ADR-046-provider-volume-local", - "type": "implements-spec" + "from" : "ADR046-qemu-media-004", + "to" : "ADR046-qemu-media-003", + "type" : "work-item-depends-on" }, { - "from": "ADR046-vl-004", - "to": "ADR046-vl-003", - "type": "work-item-depends-on" + "from" : "ADR046-qemu-media-005", + "to" : "ADR046-qemu-media-001", + "type" : "work-item-depends-on" }, { - "from": "ADR046-vl-005", - "to": "ADR-046-provider-volume-local", - "type": "implements-spec" + "from" : "ADR046-qemu-media-005", + "to" : "ADR046-qemu-media-003", + "type" : "work-item-depends-on" }, { - "from": "ADR046-vl-005", - "to": "ADR046-vl-002", - "type": "work-item-depends-on" + "from" : "ADR046-qemu-media-006", + "to" : "ADR046-qemu-media-002", + "type" : "work-item-depends-on" }, { - "from": "ADR046-vl-006", - "to": "ADR-046-provider-volume-local", - "type": "implements-spec" + "from" : "ADR046-qemu-media-006", + "to" : "ADR046-qemu-media-005", + "type" : "work-item-depends-on" }, { - "from": "ADR046-vl-006", - "to": "ADR046-vl-003", - "type": "work-item-depends-on" + "from" : "ADR046-qemu-media-007", + "to" : "ADR046-qemu-media-002", + "type" : "work-item-depends-on" }, { - "from": "ADR046-vl-007", - "to": "ADR-046-provider-volume-local", - "type": "implements-spec" + "from" : "ADR046-qemu-media-008", + "to" : "ADR046-qemu-media-002", + "type" : "work-item-depends-on" }, { - "from": "ADR046-vl-007", - "to": "ADR046-pstate-004", - "type": "work-item-depends-on" + "from" : "ADR046-qemu-media-009", + "to" : "ADR046-qemu-media-002", + "type" : "work-item-depends-on" }, { - "from": "ADR046-vl-007", - "to": "ADR046-pstate-006", - "type": "work-item-depends-on" + "from" : "ADR046-qemu-media-009", + "to" : "ADR046-qemu-media-005", + "type" : "work-item-depends-on" }, { - "from": "ADR046-vl-007", - "to": "ADR046-vl-002", - "type": "work-item-depends-on" + "from" : "ADR046-qemu-media-009", + "to" : "ADR046-qemu-media-006", + "type" : "work-item-depends-on" }, { - "from": "ADR046-vl-007", - "to": "ADR046-vl-003", - "type": "work-item-depends-on" + "from" : "ADR046-qemu-media-009", + "to" : "ADR046-qemu-media-007", + "type" : "work-item-depends-on" }, { - "from": "ADR046-vl-008", - "to": "ADR-046-provider-volume-local", - "type": "implements-spec" + "from" : "ADR046-qemu-media-009", + "to" : "ADR046-qemu-media-008", + "type" : "work-item-depends-on" }, { - "from": "ADR046-vl-008", - "to": "ADR046-vl-003", - "type": "work-item-depends-on" + "from" : "ADR046-qemu-media-009", + "to" : "ADR046-qemu-media-012", + "type" : "work-item-depends-on" }, { - "from": "ADR046-vl-008", - "to": "ADR046-vl-007", - "type": "work-item-depends-on" + "from" : "ADR046-qemu-media-010", + "to" : "ADR046-qemu-media-009", + "type" : "work-item-depends-on" }, { - "from": "ADR046-vl-009", - "to": "ADR-046-provider-volume-local", - "type": "implements-spec" + "from" : "ADR046-qemu-media-011", + "to" : "ADR046-qemu-media-006", + "type" : "work-item-depends-on" }, { - "from": "ADR046-vl-009", - "to": "ADR046-vl-001", - "type": "work-item-depends-on" + "from" : "ADR046-qemu-media-011", + "to" : "ADR046-qemu-media-010", + "type" : "work-item-depends-on" }, { - "from": "ADR046-vl-010", - "to": "ADR-046-provider-volume-local", - "type": "implements-spec" + "from" : "ADR046-qemu-media-012", + "to" : "ADR046-qemu-media-002", + "type" : "work-item-depends-on" }, { - "from": "ADR046-vl-010", - "to": "ADR046-pstate-010", - "type": "work-item-depends-on" + "from" : "ADR046-qemu-media-013", + "to" : "ADR046-qemu-media-005", + "type" : "work-item-depends-on" }, { - "from": "ADR046-vl-010", - "to": "ADR046-vl-001", - "type": "work-item-depends-on" + "from" : "ADR046-qemu-media-013", + "to" : "ADR046-qemu-media-006", + "type" : "work-item-depends-on" }, { - "from": "ADR046-vl-011", - "to": "ADR-046-provider-volume-local", - "type": "implements-spec" + "from" : "ADR046-qemu-media-013", + "to" : "ADR046-qemu-media-007", + "type" : "work-item-depends-on" }, { - "from": "ADR046-vl-011", - "to": "ADR046-pstate-011", - "type": "work-item-depends-on" + "from" : "ADR046-qemu-media-013", + "to" : "ADR046-qemu-media-008", + "type" : "work-item-depends-on" }, { - "from": "ADR046-vl-011", - "to": "ADR046-vl-002", - "type": "work-item-depends-on" + "from" : "ADR046-qemu-media-013", + "to" : "ADR046-qemu-media-009", + "type" : "work-item-depends-on" }, { - "from": "ADR046-vl-012", - "to": "ADR-046-provider-volume-local", - "type": "implements-spec" + "from" : "ADR046-qemu-media-013", + "to" : "ADR046-qemu-media-010", + "type" : "work-item-depends-on" }, { - "from": "ADR046-vl-012", - "to": "ADR046-vl-001", - "type": "work-item-depends-on" + "from" : "ADR046-qemu-media-013", + "to" : "ADR046-qemu-media-011", + "type" : "work-item-depends-on" }, { - "from": "ADR046-vl-012", - "to": "ADR046-vl-002", - "type": "work-item-depends-on" + "from" : "ADR046-qemu-media-013", + "to" : "ADR046-qemu-media-012", + "type" : "work-item-depends-on" }, { - "from": "ADR046-vl-013", - "to": "ADR-046-provider-volume-local", - "type": "implements-spec" + "from" : "ADR046-qemu-media-014", + "to" : "ADR046-qemu-media-013", + "type" : "work-item-depends-on" }, { - "from": "ADR046-vl-013", - "to": "ADR046-vl-001", - "type": "work-item-depends-on" + "from" : "ADR046-qemu-media-015", + "to" : "ADR046-qemu-media-013", + "type" : "work-item-depends-on" }, { - "from": "ADR046-vl-013", - "to": "ADR046-vl-012", - "type": "work-item-depends-on" + "from" : "ADR046-qemu-media-015", + "to" : "ADR046-qemu-media-014", + "type" : "work-item-depends-on" }, { - "from": "ADR046-volume-001", - "to": "ADR-046-resources-volume", - "type": "implements-spec" + "from" : "ADR046-qemu-media-016", + "to" : "ADR046-qemu-media-013", + "type" : "work-item-depends-on" }, { - "from": "ADR046-volume-002", - "to": "ADR-046-resources-volume", - "type": "implements-spec" + "from" : "ADR046-qemu-media-016", + "to" : "ADR046-qemu-media-014", + "type" : "work-item-depends-on" }, { - "from": "ADR046-volume-002", - "to": "ADR046-volume-001", - "type": "work-item-depends-on" + "from" : "ADR046-qemu-media-017", + "to" : "ADR046-qemu-media-002", + "type" : "work-item-depends-on" }, { - "from": "ADR046-volume-003", - "to": "ADR-046-resources-volume", - "type": "implements-spec" + "from" : "ADR046-qemu-media-017", + "to" : "ADR046-qemu-media-003", + "type" : "work-item-depends-on" }, { - "from": "ADR046-volume-003", - "to": "ADR046-volume-001", - "type": "work-item-depends-on" + "from" : "ADR046-qemu-media-018", + "to" : "ADR046-qemu-media-013", + "type" : "work-item-depends-on" }, { - "from": "ADR046-volume-004", - "to": "ADR-046-resources-volume", - "type": "implements-spec" + "from" : "ADR046-qemu-media-018", + "to" : "ADR046-qemu-media-014", + "type" : "work-item-depends-on" }, { - "from": "ADR046-volume-004", - "to": "ADR046-volume-001", - "type": "work-item-depends-on" + "from" : "ADR046-qemu-media-018", + "to" : "ADR046-qemu-media-015", + "type" : "work-item-depends-on" }, { - "from": "ADR046-volume-005", - "to": "ADR-046-resources-volume", - "type": "implements-spec" + "from" : "ADR046-qemu-media-018", + "to" : "ADR046-qemu-media-016", + "type" : "work-item-depends-on" }, { - "from": "ADR046-volume-005", - "to": "ADR046-volume-002", - "type": "work-item-depends-on" + "from" : "ADR046-qemu-media-019", + "to" : "ADR046-qemu-media-005", + "type" : "work-item-depends-on" }, { - "from": "ADR046-volume-005", - "to": "ADR046-volume-003", - "type": "work-item-depends-on" + "from" : "ADR046-qemu-media-019", + "to" : "ADR046-qemu-media-006", + "type" : "work-item-depends-on" }, { - "from": "ADR046-volume-006", - "to": "ADR-046-resources-volume", - "type": "implements-spec" + "from" : "ADR046-qemu-media-019", + "to" : "ADR046-qemu-media-007", + "type" : "work-item-depends-on" }, { - "from": "ADR046-volume-006", - "to": "ADR046-volume-001", - "type": "work-item-depends-on" + "from" : "ADR046-qemu-media-019", + "to" : "ADR046-qemu-media-008", + "type" : "work-item-depends-on" }, { - "from": "ADR046-volume-006", - "to": "ADR046-volume-004", - "type": "work-item-depends-on" + "from" : "ADR046-qemu-media-019", + "to" : "ADR046-qemu-media-009", + "type" : "work-item-depends-on" }, { - "from": "ADR046-vsock-001", - "to": "ADR-046-provider-transport-vsock", - "type": "implements-spec" + "from" : "ADR046-qemu-media-019", + "to" : "ADR046-qemu-media-010", + "type" : "work-item-depends-on" }, { - "from": "ADR046-vsock-001", - "to": "ADR046-bus-001", - "type": "work-item-depends-on" + "from" : "ADR046-qemu-media-019", + "to" : "ADR046-qemu-media-011", + "type" : "work-item-depends-on" }, { - "from": "ADR046-vsock-002", - "to": "ADR-046-provider-transport-vsock", - "type": "implements-spec" + "from" : "ADR046-qemu-media-019", + "to" : "ADR046-qemu-media-012", + "type" : "work-item-depends-on" }, { - "from": "ADR046-vsock-002", - "to": "ADR046-vsock-001", - "type": "work-item-depends-on" + "from" : "ADR046-qemu-media-019", + "to" : "ADR046-qemu-media-013", + "type" : "work-item-depends-on" }, { - "from": "ADR046-vsock-003", - "to": "ADR-046-provider-transport-vsock", - "type": "implements-spec" + "from" : "ADR046-qemu-media-019", + "to" : "ADR046-qemu-media-014", + "type" : "work-item-depends-on" }, { - "from": "ADR046-vsock-003", - "to": "ADR046-bus-001", - "type": "work-item-depends-on" + "from" : "ADR046-qemu-media-019", + "to" : "ADR046-qemu-media-015", + "type" : "work-item-depends-on" }, { - "from": "ADR046-vsock-003", - "to": "ADR046-vsock-002", - "type": "work-item-depends-on" + "from" : "ADR046-qemu-media-019", + "to" : "ADR046-qemu-media-016", + "type" : "work-item-depends-on" }, { - "from": "ADR046-vsock-004", - "to": "ADR-046-provider-transport-vsock", - "type": "implements-spec" + "from" : "ADR046-qemu-media-019", + "to" : "ADR046-qemu-media-017", + "type" : "work-item-depends-on" }, { - "from": "ADR046-vsock-004", - "to": "ADR046-vsock-001", - "type": "work-item-depends-on" + "from" : "ADR046-qemu-media-019", + "to" : "ADR046-qemu-media-018", + "type" : "work-item-depends-on" }, { - "from": "ADR046-vsock-005", - "to": "ADR-046-provider-transport-vsock", - "type": "implements-spec" + "from" : "ADR046-reconcile-002", + "to" : "ADR046-reconcile-001", + "type" : "work-item-depends-on" }, { - "from": "ADR046-vsock-006", - "to": "ADR-046-provider-transport-vsock", - "type": "implements-spec" + "from" : "ADR046-reset-002", + "to" : "ADR046-reset-001", + "type" : "work-item-depends-on" }, { - "from": "ADR046-vsock-006", - "to": "ADR046-vsock-003", - "type": "work-item-depends-on" + "from" : "ADR046-reset-003", + "to" : "ADR046-reset-001", + "type" : "work-item-depends-on" }, { - "from": "ADR046-vsock-006", - "to": "ADR046-vsock-004", - "type": "work-item-depends-on" + "from" : "ADR046-reset-003", + "to" : "ADR046-reset-002", + "type" : "work-item-depends-on" }, { - "from": "ADR046-vsock-007", - "to": "ADR-046-provider-transport-vsock", - "type": "implements-spec" + "from" : "ADR046-reset-004", + "to" : "ADR046-reset-003", + "type" : "work-item-depends-on" }, { - "from": "ADR046-vvfs-001", - "to": "ADR-046-provider-volume-virtiofs", - "type": "implements-spec" + "from" : "ADR046-reset-005", + "to" : "ADR046-core-001", + "type" : "work-item-depends-on" }, { - "from": "ADR046-vvfs-001", - "to": "ADR046-volume-001", - "type": "work-item-depends-on" + "from" : "ADR046-reset-005", + "to" : "ADR046-reset-004", + "type" : "work-item-depends-on" }, { - "from": "ADR046-vvfs-002", - "to": "ADR-046-provider-volume-virtiofs", - "type": "implements-spec" + "from" : "ADR046-reset-005", + "to" : "ADR046-store-003", + "type" : "work-item-depends-on" }, { - "from": "ADR046-vvfs-002", - "to": "ADR046-volume-001", - "type": "work-item-depends-on" + "from" : "ADR046-reset-006", + "to" : "ADR046-reset-005", + "type" : "work-item-depends-on" }, { - "from": "ADR046-vvfs-002", - "to": "ADR046-vvfs-001", - "type": "work-item-depends-on" + "from" : "ADR046-reset-007", + "to" : "ADR046-reset-006", + "type" : "work-item-depends-on" }, { - "from": "ADR046-vvfs-003", - "to": "ADR-046-provider-volume-virtiofs", - "type": "implements-spec" + "from" : "ADR046-reset-008", + "to" : "ADR046-reset-007", + "type" : "work-item-depends-on" }, { - "from": "ADR046-vvfs-003", - "to": "ADR046-volume-001", - "type": "work-item-depends-on" + "from" : "ADR046-reset-009", + "to" : "ADR046-reset-003", + "type" : "work-item-depends-on" }, { - "from": "ADR046-vvfs-003", - "to": "ADR046-vvfs-001", - "type": "work-item-depends-on" + "from" : "ADR046-reset-009", + "to" : "ADR046-reset-004", + "type" : "work-item-depends-on" }, { - "from": "ADR046-vvfs-003", - "to": "ADR046-vvfs-002", - "type": "work-item-depends-on" + "from" : "ADR046-reset-009", + "to" : "ADR046-reset-005", + "type" : "work-item-depends-on" }, { - "from": "ADR046-vvfs-004", - "to": "ADR-046-provider-volume-virtiofs", - "type": "implements-spec" + "from" : "ADR046-reset-009", + "to" : "ADR046-reset-006", + "type" : "work-item-depends-on" }, { - "from": "ADR046-vvfs-004", - "to": "ADR046-vvfs-003", - "type": "work-item-depends-on" + "from" : "ADR046-reset-010", + "to" : "ADR046-reset-005", + "type" : "work-item-depends-on" }, { - "from": "ADR046-vvfs-005", - "to": "ADR-046-provider-volume-virtiofs", - "type": "implements-spec" + "from" : "ADR046-reset-010", + "to" : "ADR046-reset-006", + "type" : "work-item-depends-on" }, { - "from": "ADR046-vvfs-005", - "to": "ADR046-volume-002", - "type": "work-item-depends-on" + "from" : "ADR046-reset-010", + "to" : "ADR046-reset-007", + "type" : "work-item-depends-on" }, { - "from": "ADR046-vvfs-005", - "to": "ADR046-vvfs-003", - "type": "work-item-depends-on" + "from" : "ADR046-reset-011", + "to" : "ADR046-reset-001", + "type" : "work-item-depends-on" }, { - "from": "ADR046-vvfs-005", - "to": "ADR046-vvfs-004", - "type": "work-item-depends-on" + "from" : "ADR046-reset-011", + "to" : "ADR046-reset-002", + "type" : "work-item-depends-on" }, { - "from": "ADR046-vvfs-006", - "to": "ADR-046-provider-volume-virtiofs", - "type": "implements-spec" + "from" : "ADR046-reset-011", + "to" : "ADR046-reset-003", + "type" : "work-item-depends-on" }, { - "from": "ADR046-vvfs-006", - "to": "ADR046-volume-004", - "type": "work-item-depends-on" + "from" : "ADR046-reset-011", + "to" : "ADR046-reset-004", + "type" : "work-item-depends-on" }, { - "from": "ADR046-vvfs-006", - "to": "ADR046-vvfs-001", - "type": "work-item-depends-on" + "from" : "ADR046-reset-011", + "to" : "ADR046-reset-005", + "type" : "work-item-depends-on" }, { - "from": "ADR046-vvfs-export-001", - "to": "ADR-046-provider-volume-virtiofs", - "type": "implements-spec" + "from" : "ADR046-reset-011", + "to" : "ADR046-reset-006", + "type" : "work-item-depends-on" }, { - "from": "ADR046-vvfs-export-001", - "to": "ADR046-volume-001", - "type": "work-item-depends-on" + "from" : "ADR046-reset-011", + "to" : "ADR046-reset-007", + "type" : "work-item-depends-on" }, { - "from": "ADR046-wire-001", - "to": "ADR-046-resources-zone-control", - "type": "implements-spec" + "from" : "ADR046-reset-011", + "to" : "ADR046-reset-008", + "type" : "work-item-depends-on" }, { - "from": "ADR046-zone-control-001", - "to": "ADR-046-resources-zone-control", - "type": "implements-spec" + "from" : "ADR046-reset-011", + "to" : "ADR046-reset-009", + "type" : "work-item-depends-on" }, { - "from": "ADR046-zone-control-001", - "to": "ADR046-identities-001", - "type": "work-item-depends-on" + "from" : "ADR046-reset-011", + "to" : "ADR046-reset-010", + "type" : "work-item-depends-on" }, { - "from": "ADR046-zone-control-001", - "to": "ADR046-object-001", - "type": "work-item-depends-on" + "from" : "ADR046-routing-002", + "to" : "ADR046-routing-001", + "type" : "work-item-depends-on" }, { - "from": "ADR046-zone-control-001", - "to": "ADR046-store-001", - "type": "work-item-depends-on" + "from" : "ADR046-routing-003", + "to" : "ADR046-routing-001", + "type" : "work-item-depends-on" }, { - "from": "ADR046-zone-control-002", - "to": "ADR-046-resources-zone-control", - "type": "implements-spec" + "from" : "ADR046-routing-003", + "to" : "ADR046-routing-002", + "type" : "work-item-depends-on" }, { - "from": "ADR046-zone-control-003", - "to": "ADR-046-resources-zone-control", - "type": "implements-spec" + "from" : "ADR046-routing-004", + "to" : "ADR046-routing-003", + "type" : "work-item-depends-on" }, { - "from": "ADR046-zone-control-003", - "to": "ADR046-api-001", - "type": "work-item-depends-on" + "from" : "ADR046-routing-005", + "to" : "ADR046-routing-002", + "type" : "work-item-depends-on" }, { - "from": "ADR046-zone-control-004", - "to": "ADR-046-resources-zone-control", - "type": "implements-spec" + "from" : "ADR046-routing-005", + "to" : "ADR046-routing-007", + "type" : "work-item-depends-on" }, { - "from": "ADR046-zone-control-004", - "to": "ADR046-api-002", - "type": "work-item-depends-on" + "from" : "ADR046-routing-006", + "to" : "ADR046-routing-002", + "type" : "work-item-depends-on" }, { - "from": "ADR046-zone-control-005", - "to": "ADR-046-resources-zone-control", - "type": "implements-spec" + "from" : "ADR046-routing-008", + "to" : "ADR046-routing-007", + "type" : "work-item-depends-on" }, { - "from": "ADR046-zone-control-006", - "to": "ADR-046-resources-zone-control", - "type": "implements-spec" + "from" : "ADR046-routing-009", + "to" : "ADR046-routing-007", + "type" : "work-item-depends-on" }, { - "from": "ADR046-zone-control-007", - "to": "ADR-046-resources-zone-control", - "type": "implements-spec" + "from" : "ADR046-routing-010", + "to" : "ADR046-routing-007", + "type" : "work-item-depends-on" }, { - "from": "ADR046-zone-control-007", - "to": "ADR046-identities-002", - "type": "work-item-depends-on" + "from" : "ADR046-routing-010", + "to" : "ADR046-routing-009", + "type" : "work-item-depends-on" }, { - "from": "ADR046-zone-control-008", - "to": "ADR-046-resources-zone-control", - "type": "implements-spec" + "from" : "ADR046-routing-011", + "to" : "ADR046-routing-001", + "type" : "work-item-depends-on" }, { - "from": "ADR046-zone-control-009", - "to": "ADR-046-resources-zone-control", - "type": "implements-spec" + "from" : "ADR046-routing-012", + "to" : "ADR046-routing-001", + "type" : "work-item-depends-on" }, { - "from": "ADR046-zone-control-009", - "to": "ADR046-store-001", - "type": "work-item-depends-on" + "from" : "ADR046-routing-012", + "to" : "ADR046-routing-011", + "type" : "work-item-depends-on" }, { - "from": "ADR046-zone-control-010", - "to": "ADR-046-resources-zone-control", - "type": "implements-spec" + "from" : "ADR046-routing-013", + "to" : "ADR046-routing-003", + "type" : "work-item-depends-on" }, { - "from": "ADR046-zone-control-010", - "to": "ADR046-store-001", - "type": "work-item-depends-on" + "from" : "ADR046-routing-013", + "to" : "ADR046-routing-012", + "type" : "work-item-depends-on" }, { - "from": "ADR046-zone-control-011", - "to": "ADR-046-resources-zone-control", - "type": "implements-spec" + "from" : "ADR046-routing-014", + "to" : "ADR046-routing-007", + "type" : "work-item-depends-on" }, { - "from": "ADR046-zone-control-011", - "to": "ADR046-identities-001", - "type": "work-item-depends-on" + "from" : "ADR046-routing-015", + "to" : "ADR046-routing-014", + "type" : "work-item-depends-on" }, { - "from": "ADR046-zone-control-011", - "to": "ADR046-store-001", - "type": "work-item-depends-on" + "from" : "ADR046-routing-016", + "to" : "ADR046-routing-001", + "type" : "work-item-depends-on" }, { - "from": "ADR046-zone-control-012", - "to": "ADR-046-resources-zone-control", - "type": "implements-spec" + "from" : "ADR046-routing-016", + "to" : "ADR046-routing-007", + "type" : "work-item-depends-on" }, { - "from": "ADR046-zone-control-013", - "to": "ADR-046-resources-zone-control", - "type": "implements-spec" + "from" : "ADR046-security-001", + "to" : "ADR046-telem-008", + "type" : "work-item-depends-on" }, { - "from": "ADR046-zone-control-013", - "to": "ADR046-identities-001", - "type": "work-item-depends-on" + "from" : "ADR046-security-002", + "to" : "ADR046-session-001", + "type" : "work-item-depends-on" }, { - "from": "ADR046-zone-control-014", - "to": "ADR-046-resources-zone-control", - "type": "implements-spec" + "from" : "ADR046-security-002", + "to" : "ADR046-session-003", + "type" : "work-item-depends-on" }, { - "from": "ADR046-zone-control-015", - "to": "ADR-046-resources-zone-control", - "type": "implements-spec" + "from" : "ADR046-security-003", + "to" : "ADR046-zone-control-004", + "type" : "work-item-depends-on" }, { - "from": "ADR046-zone-control-016", - "to": "ADR-046-resources-zone-control", - "type": "implements-spec" + "from" : "ADR046-security-003", + "to" : "ADR046-zone-control-005", + "type" : "work-item-depends-on" }, { - "from": "ADR046-zone-control-016", - "to": "ADR046-core-001", - "type": "file-overlap-order" + "from" : "ADR046-security-004", + "to" : "ADR046-routing-005", + "type" : "work-item-depends-on" }, { - "from": "ADR046-zone-control-016", - "to": "ADR046-core-001", - "type": "work-item-depends-on" + "from" : "ADR046-security-004", + "to" : "ADR046-session-003", + "type" : "work-item-depends-on" }, { - "from": "ADR046-zone-control-017", - "to": "ADR-046-resources-zone-control", - "type": "implements-spec" + "from" : "ADR046-security-006", + "to" : "ADR046-minijail-002", + "type" : "work-item-depends-on" }, { - "from": "ADR046-zone-control-018", - "to": "ADR-046-resources-zone-control", - "type": "implements-spec" + "from" : "ADR046-security-006", + "to" : "ADR046-minijail-003", + "type" : "work-item-depends-on" }, { - "from": "ADR046-zone-control-019", - "to": "ADR-046-resources-zone-control", - "type": "implements-spec" + "from" : "ADR046-security-007", + "to" : "ADR046-aca-001", + "type" : "work-item-depends-on" }, { - "from": "ADR046-zone-control-020", - "to": "ADR-046-resources-zone-control", - "type": "implements-spec" + "from" : "ADR046-security-007", + "to" : "ADR046-minijail-005", + "type" : "work-item-depends-on" }, { - "from": "ADR046-zone-control-021", - "to": "ADR-046-resources-zone-control", - "type": "implements-spec" + "from" : "ADR046-security-007", + "to" : "ADR046-systemd-002", + "type" : "work-item-depends-on" }, { - "from": "ADR046-zone-control-021", - "to": "ADR046-core-001", - "type": "file-overlap-order" + "from" : "ADR046-security-007", + "to" : "ADR046-volume-001", + "type" : "work-item-depends-on" }, { - "from": "ADR046-zone-control-021", - "to": "ADR046-core-001", - "type": "work-item-depends-on" + "from" : "ADR046-security-008", + "to" : "ADR046-exec-002", + "type" : "work-item-depends-on" }, { - "from": "ADR046-zone-control-022", - "to": "ADR-046-resources-zone-control", - "type": "implements-spec" + "from" : "ADR046-security-009", + "to" : "ADR046-pstate-003", + "type" : "work-item-depends-on" }, { - "from": "ADR046-zone-control-022", - "to": "ADR046-api-001", - "type": "work-item-depends-on" + "from" : "ADR046-security-010", + "to" : "ADR046-credential-001", + "type" : "work-item-depends-on" }, { - "from": "ADR046-zone-control-023", - "to": "ADR-046-resources-zone-control", - "type": "implements-spec" + "from" : "ADR046-security-010", + "to" : "ADR046-credential-007", + "type" : "work-item-depends-on" }, { - "from": "ADR046-zone-control-024", - "to": "ADR-046-resources-zone-control", - "type": "implements-spec" + "from" : "ADR046-security-011", + "to" : "ADR046-notify-004", + "type" : "work-item-depends-on" }, { - "from": "W-N01", - "to": "ADR-046-provider-device-security-key", - "type": "implements-spec" + "from" : "ADR046-security-012", + "to" : "ADR046-telem-013", + "type" : "work-item-depends-on" }, { - "from": "W-N02", - "to": "ADR-046-provider-device-security-key", - "type": "implements-spec" + "from" : "ADR046-security-013", + "to" : "ADR046-session-001", + "type" : "work-item-depends-on" }, { - "from": "W-N02", - "to": "W-N06", - "type": "work-item-depends-on" + "from" : "ADR046-security-013", + "to" : "ADR046-zone-control-009", + "type" : "work-item-depends-on" }, { - "from": "W-N02", - "to": "W-N09", - "type": "work-item-depends-on" + "from" : "ADR046-security-013", + "to" : "ADR046-zone-control-010", + "type" : "work-item-depends-on" }, { - "from": "W-N02", - "to": "W-N18", - "type": "work-item-depends-on" + "from" : "ADR046-security-014", + "to" : "ADR046-telem-016", + "type" : "work-item-depends-on" }, { - "from": "W-N02", - "to": "W-N19", - "type": "work-item-depends-on" + "from" : "ADR046-security-014", + "to" : "ADR046-telem-017", + "type" : "work-item-depends-on" }, { - "from": "W-N02", - "to": "W-N20", - "type": "work-item-depends-on" + "from" : "ADR046-security-015", + "to" : "ADR046-telem-012", + "type" : "work-item-depends-on" }, { - "from": "W-N03", - "to": "ADR-046-provider-device-security-key", - "type": "implements-spec" + "from" : "ADR046-security-017", + "to" : "ADR046-routing-004", + "type" : "work-item-depends-on" }, { - "from": "W-N03", - "to": "W-N04", - "type": "work-item-depends-on" + "from" : "ADR046-security-019", + "to" : "ADR046-exec-003", + "type" : "work-item-depends-on" }, { - "from": "W-N03", - "to": "W-N05", - "type": "work-item-depends-on" + "from" : "ADR046-security-019", + "to" : "ADR046-exec-007", + "type" : "work-item-depends-on" }, { - "from": "W-N03", - "to": "W-N07", - "type": "work-item-depends-on" + "from" : "ADR046-security-019", + "to" : "ADR046-minijail-003", + "type" : "work-item-depends-on" }, { - "from": "W-N03", - "to": "W-N09", - "type": "work-item-depends-on" + "from" : "ADR046-security-019", + "to" : "ADR046-minijail-005", + "type" : "work-item-depends-on" }, { - "from": "W-N03", - "to": "W-N11", - "type": "work-item-depends-on" + "from" : "ADR046-security-key-001", + "to" : "ADR046-security-key-008", + "type" : "work-item-depends-on" }, { - "from": "W-N03", - "to": "W-R01", - "type": "work-item-depends-on" + "from" : "ADR046-security-key-002", + "to" : "ADR046-security-key-001", + "type" : "work-item-depends-on" }, { - "from": "W-N03", - "to": "W-R02", - "type": "work-item-depends-on" + "from" : "ADR046-security-key-002", + "to" : "ADR046-security-key-008", + "type" : "work-item-depends-on" }, { - "from": "W-N04", - "to": "ADR-046-provider-device-security-key", - "type": "implements-spec" + "from" : "ADR046-security-key-003", + "to" : "ADR046-security-key-008", + "type" : "work-item-depends-on" }, { - "from": "W-N04", - "to": "W-N01", - "type": "work-item-depends-on" + "from" : "ADR046-security-key-004", + "to" : "ADR046-security-key-013", + "type" : "work-item-depends-on" }, { - "from": "W-N04", - "to": "W-R01", - "type": "work-item-depends-on" + "from" : "ADR046-security-key-005", + "to" : "ADR046-provider-004", + "type" : "work-item-depends-on" }, { - "from": "W-N05", - "to": "ADR-046-provider-device-security-key", - "type": "implements-spec" + "from" : "ADR046-security-key-005", + "to" : "ADR046-security-key-008", + "type" : "work-item-depends-on" }, { - "from": "W-N05", - "to": "W-N01", - "type": "work-item-depends-on" + "from" : "ADR046-security-key-006", + "to" : "ADR046-security-key-005", + "type" : "work-item-depends-on" }, { - "from": "W-N05", - "to": "W-R01", - "type": "work-item-depends-on" + "from" : "ADR046-security-key-006", + "to" : "ADR046-security-key-008", + "type" : "work-item-depends-on" }, { - "from": "W-N06", - "to": "ADR-046-provider-device-security-key", - "type": "implements-spec" + "from" : "ADR046-security-key-007", + "to" : "ADR046-security-key-008", + "type" : "work-item-depends-on" }, { - "from": "W-N06", - "to": "W-N18", - "type": "work-item-depends-on" + "from" : "ADR046-security-key-009", + "to" : "ADR046-security-key-013", + "type" : "work-item-depends-on" }, { - "from": "W-N07", - "to": "ADR-046-provider-device-security-key", - "type": "implements-spec" + "from" : "ADR046-security-key-009", + "to" : "ADR046-security-key-016", + "type" : "work-item-depends-on" }, { - "from": "W-N07", - "to": "W-N01", - "type": "work-item-depends-on" + "from" : "ADR046-security-key-009", + "to" : "ADR046-security-key-025", + "type" : "work-item-depends-on" }, { - "from": "W-N07", - "to": "W-R05", - "type": "work-item-depends-on" + "from" : "ADR046-security-key-009", + "to" : "ADR046-security-key-026", + "type" : "work-item-depends-on" }, { - "from": "W-N08", - "to": "ADR-046-provider-device-security-key", - "type": "implements-spec" + "from" : "ADR046-security-key-009", + "to" : "ADR046-security-key-027", + "type" : "work-item-depends-on" }, { - "from": "W-N08", - "to": "W-N01", - "type": "work-item-depends-on" + "from" : "ADR046-security-key-010", + "to" : "ADR046-security-key-001", + "type" : "work-item-depends-on" }, { - "from": "W-N08", - "to": "W-R07", - "type": "work-item-depends-on" + "from" : "ADR046-security-key-010", + "to" : "ADR046-security-key-002", + "type" : "work-item-depends-on" }, { - "from": "W-N09", - "to": "ADR-046-provider-device-security-key", - "type": "implements-spec" + "from" : "ADR046-security-key-010", + "to" : "ADR046-security-key-011", + "type" : "work-item-depends-on" }, { - "from": "W-N09", - "to": "W-N01", - "type": "work-item-depends-on" + "from" : "ADR046-security-key-010", + "to" : "ADR046-security-key-012", + "type" : "work-item-depends-on" }, { - "from": "W-N09", - "to": "W-N08", - "type": "work-item-depends-on" + "from" : "ADR046-security-key-010", + "to" : "ADR046-security-key-014", + "type" : "work-item-depends-on" }, { - "from": "W-N09", - "to": "W-N19", - "type": "work-item-depends-on" + "from" : "ADR046-security-key-010", + "to" : "ADR046-security-key-016", + "type" : "work-item-depends-on" }, { - "from": "W-N10", - "to": "ADR-046-provider-device-security-key", - "type": "implements-spec" + "from" : "ADR046-security-key-010", + "to" : "ADR046-security-key-018", + "type" : "work-item-depends-on" }, { - "from": "W-N10", - "to": "W-N01", - "type": "work-item-depends-on" + "from" : "ADR046-security-key-011", + "to" : "ADR046-security-key-001", + "type" : "work-item-depends-on" }, { - "from": "W-N10", - "to": "W-N07", - "type": "work-item-depends-on" + "from" : "ADR046-security-key-011", + "to" : "ADR046-security-key-008", + "type" : "work-item-depends-on" }, { - "from": "W-N10", - "to": "W-N09", - "type": "work-item-depends-on" + "from" : "ADR046-security-key-012", + "to" : "ADR046-security-key-001", + "type" : "work-item-depends-on" }, { - "from": "W-N10", - "to": "W-N19", - "type": "work-item-depends-on" + "from" : "ADR046-security-key-012", + "to" : "ADR046-security-key-008", + "type" : "work-item-depends-on" }, { - "from": "W-N10", - "to": "W-N20", - "type": "work-item-depends-on" + "from" : "ADR046-security-key-013", + "to" : "ADR046-security-key-025", + "type" : "work-item-depends-on" }, { - "from": "W-N10", - "to": "W-R05", - "type": "work-item-depends-on" + "from" : "ADR046-security-key-014", + "to" : "ADR046-security-key-005", + "type" : "work-item-depends-on" }, { - "from": "W-N11", - "to": "ADR-046-provider-device-security-key", - "type": "implements-spec" + "from" : "ADR046-security-key-014", + "to" : "ADR046-security-key-008", + "type" : "work-item-depends-on" }, { - "from": "W-N11", - "to": "W-N06", - "type": "work-item-depends-on" + "from" : "ADR046-security-key-015", + "to" : "ADR046-security-key-007", + "type" : "work-item-depends-on" }, { - "from": "W-N11", - "to": "W-R04", - "type": "work-item-depends-on" + "from" : "ADR046-security-key-015", + "to" : "ADR046-security-key-008", + "type" : "work-item-depends-on" }, { - "from": "W-N11", - "to": "W-R05", - "type": "work-item-depends-on" + "from" : "ADR046-security-key-016", + "to" : "ADR046-security-key-008", + "type" : "work-item-depends-on" }, { - "from": "W-N12", - "to": "ADR-046-provider-device-security-key", - "type": "implements-spec" + "from" : "ADR046-security-key-016", + "to" : "ADR046-security-key-015", + "type" : "work-item-depends-on" }, { - "from": "W-N12", - "to": "ADR046-zone-control-024", - "type": "work-item-depends-on" + "from" : "ADR046-security-key-016", + "to" : "ADR046-security-key-026", + "type" : "work-item-depends-on" }, { - "from": "W-N12", - "to": "W-N10", - "type": "work-item-depends-on" + "from" : "ADR046-security-key-017", + "to" : "ADR046-security-key-005", + "type" : "work-item-depends-on" }, { - "from": "W-N12", - "to": "W-N19", - "type": "work-item-depends-on" + "from" : "ADR046-security-key-017", + "to" : "ADR046-security-key-008", + "type" : "work-item-depends-on" }, { - "from": "W-N13", - "to": "ADR-046-provider-device-security-key", - "type": "implements-spec" + "from" : "ADR046-security-key-017", + "to" : "ADR046-security-key-014", + "type" : "work-item-depends-on" }, { - "from": "W-N13", - "to": "W-N09", - "type": "work-item-depends-on" + "from" : "ADR046-security-key-017", + "to" : "ADR046-security-key-016", + "type" : "work-item-depends-on" }, { - "from": "W-N13", - "to": "W-N19", - "type": "work-item-depends-on" + "from" : "ADR046-security-key-017", + "to" : "ADR046-security-key-026", + "type" : "work-item-depends-on" }, { - "from": "W-N13", - "to": "W-R03", - "type": "work-item-depends-on" + "from" : "ADR046-security-key-017", + "to" : "ADR046-security-key-027", + "type" : "work-item-depends-on" }, { - "from": "W-N14", - "to": "ADR-046-provider-device-security-key", - "type": "implements-spec" + "from" : "ADR046-security-key-018", + "to" : "ADR046-security-key-004", + "type" : "work-item-depends-on" }, { - "from": "W-N14", - "to": "W-N02", - "type": "work-item-depends-on" + "from" : "ADR046-security-key-018", + "to" : "ADR046-security-key-005", + "type" : "work-item-depends-on" }, { - "from": "W-N14", - "to": "W-N11", - "type": "work-item-depends-on" + "from" : "ADR046-security-key-018", + "to" : "ADR046-security-key-013", + "type" : "work-item-depends-on" }, { - "from": "W-N15", - "to": "ADR-046-provider-device-security-key", - "type": "implements-spec" + "from" : "ADR046-security-key-019", + "to" : "ADR046-security-key-017", + "type" : "work-item-depends-on" }, { - "from": "W-N15", - "to": "W-N02", - "type": "work-item-depends-on" + "from" : "ADR046-security-key-019", + "to" : "ADR046-security-key-026", + "type" : "work-item-depends-on" }, { - "from": "W-N15", - "to": "W-N03", - "type": "work-item-depends-on" + "from" : "ADR046-security-key-019", + "to" : "ADR046-zone-control-024", + "type" : "work-item-depends-on" }, { - "from": "W-N16", - "to": "ADR-046-provider-device-security-key", - "type": "implements-spec" + "from" : "ADR046-security-key-020", + "to" : "ADR046-security-key-003", + "type" : "work-item-depends-on" }, { - "from": "W-N16", - "to": "W-N01", - "type": "work-item-depends-on" + "from" : "ADR046-security-key-020", + "to" : "ADR046-security-key-016", + "type" : "work-item-depends-on" }, { - "from": "W-N16", - "to": "W-N15", - "type": "work-item-depends-on" + "from" : "ADR046-security-key-020", + "to" : "ADR046-security-key-026", + "type" : "work-item-depends-on" }, { - "from": "W-N16", - "to": "W-N17", - "type": "work-item-depends-on" + "from" : "ADR046-security-key-021", + "to" : "ADR046-security-key-009", + "type" : "work-item-depends-on" }, { - "from": "W-N16", - "to": "W-N22", - "type": "work-item-depends-on" + "from" : "ADR046-security-key-021", + "to" : "ADR046-security-key-018", + "type" : "work-item-depends-on" }, { - "from": "W-N17", - "to": "ADR-046-provider-device-security-key", - "type": "implements-spec" + "from" : "ADR046-security-key-022", + "to" : "ADR046-security-key-009", + "type" : "work-item-depends-on" }, { - "from": "W-N17", - "to": "W-N03", - "type": "work-item-depends-on" + "from" : "ADR046-security-key-022", + "to" : "ADR046-security-key-010", + "type" : "work-item-depends-on" }, { - "from": "W-N17", - "to": "W-N07", - "type": "work-item-depends-on" + "from" : "ADR046-security-key-023", + "to" : "ADR046-security-key-008", + "type" : "work-item-depends-on" }, { - "from": "W-N17", - "to": "W-N09", - "type": "work-item-depends-on" + "from" : "ADR046-security-key-023", + "to" : "ADR046-security-key-009", + "type" : "work-item-depends-on" }, { - "from": "W-N17", - "to": "W-N19", - "type": "work-item-depends-on" + "from" : "ADR046-security-key-023", + "to" : "ADR046-security-key-010", + "type" : "work-item-depends-on" }, { - "from": "W-N17", - "to": "W-R03", - "type": "work-item-depends-on" + "from" : "ADR046-security-key-023", + "to" : "ADR046-security-key-011", + "type" : "work-item-depends-on" }, { - "from": "W-N18", - "to": "ADR-046-provider-device-security-key", - "type": "implements-spec" + "from" : "ADR046-security-key-023", + "to" : "ADR046-security-key-012", + "type" : "work-item-depends-on" }, { - "from": "W-N18", - "to": "W-N01", - "type": "work-item-depends-on" + "from" : "ADR046-security-key-023", + "to" : "ADR046-security-key-013", + "type" : "work-item-depends-on" }, { - "from": "W-N19", - "to": "ADR-046-provider-device-security-key", - "type": "implements-spec" + "from" : "ADR046-security-key-023", + "to" : "ADR046-security-key-014", + "type" : "work-item-depends-on" }, { - "from": "W-N19", - "to": "ADR046-provider-004", - "type": "work-item-depends-on" + "from" : "ADR046-security-key-023", + "to" : "ADR046-security-key-015", + "type" : "work-item-depends-on" }, { - "from": "W-N19", - "to": "W-N01", - "type": "work-item-depends-on" + "from" : "ADR046-security-key-023", + "to" : "ADR046-security-key-016", + "type" : "work-item-depends-on" }, { - "from": "W-N19", - "to": "W-R05", - "type": "work-item-depends-on" + "from" : "ADR046-security-key-023", + "to" : "ADR046-security-key-017", + "type" : "work-item-depends-on" }, { - "from": "W-N20", - "to": "ADR-046-provider-device-security-key", - "type": "implements-spec" + "from" : "ADR046-security-key-023", + "to" : "ADR046-security-key-018", + "type" : "work-item-depends-on" }, { - "from": "W-N20", - "to": "W-N01", - "type": "work-item-depends-on" + "from" : "ADR046-security-key-023", + "to" : "ADR046-security-key-019", + "type" : "work-item-depends-on" }, { - "from": "W-N20", - "to": "W-N19", - "type": "work-item-depends-on" + "from" : "ADR046-security-key-023", + "to" : "ADR046-security-key-020", + "type" : "work-item-depends-on" }, { - "from": "W-N21", - "to": "ADR-046-provider-device-security-key", - "type": "implements-spec" + "from" : "ADR046-security-key-023", + "to" : "ADR046-security-key-021", + "type" : "work-item-depends-on" }, { - "from": "W-N21", - "to": "ADR046-zone-control-019", - "type": "work-item-depends-on" + "from" : "ADR046-security-key-023", + "to" : "ADR046-security-key-022", + "type" : "work-item-depends-on" }, { - "from": "W-N21", - "to": "ADR046-zone-control-020", - "type": "work-item-depends-on" + "from" : "ADR046-security-key-023", + "to" : "ADR046-security-key-024", + "type" : "work-item-depends-on" }, { - "from": "W-N21", - "to": "W-N17", - "type": "work-item-depends-on" + "from" : "ADR046-security-key-023", + "to" : "ADR046-security-key-025", + "type" : "work-item-depends-on" }, { - "from": "W-N21", - "to": "W-N19", - "type": "work-item-depends-on" + "from" : "ADR046-security-key-023", + "to" : "ADR046-security-key-026", + "type" : "work-item-depends-on" }, { - "from": "W-N21", - "to": "W-N22", - "type": "work-item-depends-on" + "from" : "ADR046-security-key-023", + "to" : "ADR046-security-key-027", + "type" : "work-item-depends-on" }, { - "from": "W-N22", - "to": "ADR-046-provider-device-security-key", - "type": "implements-spec" + "from" : "ADR046-security-key-023", + "to" : "ADR046-security-key-028", + "type" : "work-item-depends-on" }, { - "from": "W-N22", - "to": "ADR046-zone-control-024", - "type": "work-item-depends-on" + "from" : "ADR046-security-key-023", + "to" : "ADR046-security-key-029", + "type" : "work-item-depends-on" }, { - "from": "W-N22", - "to": "W-N11", - "type": "work-item-depends-on" + "from" : "ADR046-security-key-024", + "to" : "ADR046-security-key-003", + "type" : "work-item-depends-on" }, { - "from": "W-N22", - "to": "W-N19", - "type": "work-item-depends-on" + "from" : "ADR046-security-key-024", + "to" : "ADR046-security-key-010", + "type" : "work-item-depends-on" }, { - "from": "W-N22", - "to": "W-R01", - "type": "work-item-depends-on" + "from" : "ADR046-security-key-024", + "to" : "ADR046-security-key-014", + "type" : "work-item-depends-on" }, { - "from": "W-N22", - "to": "W-R02", - "type": "work-item-depends-on" + "from" : "ADR046-security-key-024", + "to" : "ADR046-security-key-016", + "type" : "work-item-depends-on" }, { - "from": "W-N22", - "to": "W-R03", - "type": "work-item-depends-on" + "from" : "ADR046-security-key-024", + "to" : "ADR046-security-key-026", + "type" : "work-item-depends-on" }, { - "from": "W-N22", - "to": "W-R04", - "type": "work-item-depends-on" + "from" : "ADR046-security-key-025", + "to" : "ADR046-security-key-008", + "type" : "work-item-depends-on" }, { - "from": "W-R01", - "to": "ADR-046-provider-device-security-key", - "type": "implements-spec" + "from" : "ADR046-security-key-026", + "to" : "ADR046-provider-004", + "type" : "work-item-depends-on" }, { - "from": "W-R01", - "to": "W-N01", - "type": "work-item-depends-on" + "from" : "ADR046-security-key-026", + "to" : "ADR046-security-key-005", + "type" : "work-item-depends-on" }, { - "from": "W-R02", - "to": "ADR-046-provider-device-security-key", - "type": "implements-spec" + "from" : "ADR046-security-key-026", + "to" : "ADR046-security-key-008", + "type" : "work-item-depends-on" }, { - "from": "W-R02", - "to": "W-N01", - "type": "work-item-depends-on" + "from" : "ADR046-security-key-027", + "to" : "ADR046-security-key-008", + "type" : "work-item-depends-on" }, { - "from": "W-R02", - "to": "W-R01", - "type": "work-item-depends-on" + "from" : "ADR046-security-key-027", + "to" : "ADR046-security-key-026", + "type" : "work-item-depends-on" }, { - "from": "W-R03", - "to": "ADR-046-provider-device-security-key", - "type": "implements-spec" + "from" : "ADR046-security-key-028", + "to" : "ADR046-security-key-024", + "type" : "work-item-depends-on" }, { - "from": "W-R03", - "to": "W-N01", - "type": "work-item-depends-on" + "from" : "ADR046-security-key-028", + "to" : "ADR046-security-key-026", + "type" : "work-item-depends-on" }, { - "from": "W-R04", - "to": "ADR-046-provider-device-security-key", - "type": "implements-spec" + "from" : "ADR046-security-key-028", + "to" : "ADR046-security-key-029", + "type" : "work-item-depends-on" }, { - "from": "W-R04", - "to": "W-N06", - "type": "work-item-depends-on" + "from" : "ADR046-security-key-028", + "to" : "ADR046-zone-control-019", + "type" : "work-item-depends-on" }, { - "from": "W-R05", - "to": "ADR-046-provider-device-security-key", - "type": "implements-spec" + "from" : "ADR046-security-key-028", + "to" : "ADR046-zone-control-020", + "type" : "work-item-depends-on" }, { - "from": "W-R05", - "to": "ADR046-provider-004", - "type": "work-item-depends-on" + "from" : "ADR046-security-key-029", + "to" : "ADR046-security-key-001", + "type" : "work-item-depends-on" }, { - "from": "W-R05", - "to": "W-N01", - "type": "work-item-depends-on" + "from" : "ADR046-security-key-029", + "to" : "ADR046-security-key-002", + "type" : "work-item-depends-on" }, { - "from": "W-R06", - "to": "ADR-046-provider-device-security-key", - "type": "implements-spec" + "from" : "ADR046-security-key-029", + "to" : "ADR046-security-key-003", + "type" : "work-item-depends-on" }, { - "from": "W-R06", - "to": "W-N01", - "type": "work-item-depends-on" + "from" : "ADR046-security-key-029", + "to" : "ADR046-security-key-004", + "type" : "work-item-depends-on" }, { - "from": "W-R06", - "to": "W-R05", - "type": "work-item-depends-on" + "from" : "ADR046-security-key-029", + "to" : "ADR046-security-key-018", + "type" : "work-item-depends-on" }, { - "from": "W-R07", - "to": "ADR-046-provider-device-security-key", - "type": "implements-spec" + "from" : "ADR046-security-key-029", + "to" : "ADR046-security-key-026", + "type" : "work-item-depends-on" }, { - "from": "W-R07", - "to": "W-N01", - "type": "work-item-depends-on" + "from" : "ADR046-security-key-029", + "to" : "ADR046-zone-control-024", + "type" : "work-item-depends-on" }, { - "from": "W-X01", - "to": "ADR-046-provider-device-security-key", - "type": "implements-spec" + "from" : "ADR046-security-key-030", + "to" : "ADR046-security-key-001", + "type" : "work-item-depends-on" }, { - "from": "W-X01", - "to": "W-N03", - "type": "work-item-depends-on" + "from" : "ADR046-security-key-030", + "to" : "ADR046-security-key-002", + "type" : "work-item-depends-on" }, { - "from": "W-X01", - "to": "W-N04", - "type": "work-item-depends-on" + "from" : "ADR046-security-key-030", + "to" : "ADR046-security-key-010", + "type" : "work-item-depends-on" }, { - "from": "W-X01", - "to": "W-N05", - "type": "work-item-depends-on" + "from" : "ADR046-security-key-030", + "to" : "ADR046-security-key-011", + "type" : "work-item-depends-on" }, { - "from": "W-X01", - "to": "W-R01", - "type": "work-item-depends-on" + "from" : "ADR046-security-key-030", + "to" : "ADR046-security-key-012", + "type" : "work-item-depends-on" }, { - "from": "W-X01", - "to": "W-R02", - "type": "work-item-depends-on" + "from" : "ADR046-security-key-031", + "to" : "ADR046-security-key-030", + "type" : "work-item-depends-on" }, { - "from": "W-X02", - "to": "ADR-046-provider-device-security-key", - "type": "implements-spec" + "from" : "ADR046-security-key-032", + "to" : "ADR046-security-key-003", + "type" : "work-item-depends-on" }, { - "from": "W-X02", - "to": "W-X01", - "type": "work-item-depends-on" + "from" : "ADR046-security-key-032", + "to" : "ADR046-security-key-020", + "type" : "work-item-depends-on" }, { - "from": "W-X03", - "to": "ADR-046-provider-device-security-key", - "type": "implements-spec" + "from" : "ADR046-security-key-032", + "to" : "ADR046-security-key-026", + "type" : "work-item-depends-on" }, { - "from": "W-X03", - "to": "W-N13", - "type": "work-item-depends-on" + "from" : "ADR046-security-key-033", + "to" : "ADR046-security-key-006", + "type" : "work-item-depends-on" }, { - "from": "W-X03", - "to": "W-N19", - "type": "work-item-depends-on" + "from" : "ADR046-security-key-033", + "to" : "ADR046-security-key-007", + "type" : "work-item-depends-on" }, { - "from": "W-X03", - "to": "W-R03", - "type": "work-item-depends-on" + "from" : "ADR046-security-key-034", + "to" : "ADR046-security-key-015", + "type" : "work-item-depends-on" }, { - "from": "W-X04", - "to": "ADR-046-provider-device-security-key", - "type": "implements-spec" + "from" : "ADR046-security-key-034", + "to" : "ADR046-security-key-016", + "type" : "work-item-depends-on" }, { - "from": "W-X04", - "to": "W-R06", - "type": "work-item-depends-on" + "from" : "ADR046-security-key-035", + "to" : "ADR046-security-key-005", + "type" : "work-item-depends-on" }, { - "from": "W-X04", - "to": "W-R07", - "type": "work-item-depends-on" + "from" : "ADR046-security-key-035", + "to" : "ADR046-security-key-018", + "type" : "work-item-depends-on" }, { - "from": "W-X05", - "to": "ADR-046-provider-device-security-key", - "type": "implements-spec" + "from" : "ADR046-security-key-035", + "to" : "ADR046-security-key-020", + "type" : "work-item-depends-on" }, { - "from": "W-X05", - "to": "W-N08", - "type": "work-item-depends-on" + "from" : "ADR046-security-key-035", + "to" : "ADR046-security-key-026", + "type" : "work-item-depends-on" }, { - "from": "W-X05", - "to": "W-N09", - "type": "work-item-depends-on" + "from" : "ADR046-session-002", + "to" : "ADR046-session-001", + "type" : "work-item-depends-on" }, { - "from": "W-X06", - "to": "ADR-046-provider-device-security-key", - "type": "implements-spec" + "from" : "ADR046-store-002", + "to" : "ADR046-store-001", + "type" : "work-item-depends-on" }, { - "from": "W-X06", - "to": "W-N11", - "type": "work-item-depends-on" + "from" : "ADR046-store-003", + "to" : "ADR046-store-001", + "type" : "work-item-depends-on" }, { - "from": "W-X06", - "to": "W-N13", - "type": "work-item-depends-on" + "from" : "ADR046-streamline-002", + "to" : "ADR046-streamline-001", + "type" : "work-item-depends-on" }, { - "from": "W-X06", - "to": "W-N19", - "type": "work-item-depends-on" + "from" : "ADR046-streamline-003", + "to" : "ADR046-streamline-001", + "type" : "work-item-depends-on" }, { - "from": "W-X06", - "to": "W-R05", - "type": "work-item-depends-on" + "from" : "ADR046-streamline-003", + "to" : "ADR046-streamline-002", + "type" : "work-item-depends-on" }, { - "from": "WI-001", - "to": "ADR-046-provider-runtime-qemu-media", - "type": "implements-spec" + "from" : "ADR046-streamline-004", + "to" : "ADR046-streamline-001", + "type" : "work-item-depends-on" }, { - "from": "WI-002", - "to": "ADR-046-provider-runtime-qemu-media", - "type": "implements-spec" + "from" : "ADR046-streamline-004", + "to" : "ADR046-streamline-002", + "type" : "work-item-depends-on" }, { - "from": "WI-003", - "to": "ADR-046-provider-runtime-qemu-media", - "type": "implements-spec" + "from" : "ADR046-streamline-005", + "to" : "ADR046-streamline-001", + "type" : "work-item-depends-on" }, { - "from": "WI-003B", - "to": "ADR-046-provider-runtime-qemu-media", - "type": "implements-spec" + "from" : "ADR046-streamline-006", + "to" : "ADR046-streamline-001", + "type" : "work-item-depends-on" }, { - "from": "WI-004", - "to": "ADR-046-provider-runtime-qemu-media", - "type": "implements-spec" + "from" : "ADR046-streamline-007", + "to" : "ADR046-streamline-001", + "type" : "work-item-depends-on" }, { - "from": "WI-005", - "to": "ADR-046-provider-runtime-qemu-media", - "type": "implements-spec" + "from" : "ADR046-streamline-008", + "to" : "ADR046-streamline-001", + "type" : "work-item-depends-on" }, { - "from": "WI-006", - "to": "ADR-046-provider-runtime-qemu-media", - "type": "implements-spec" + "from" : "ADR046-streamline-009", + "to" : "ADR046-streamline-001", + "type" : "work-item-depends-on" }, { - "from": "WI-007", - "to": "ADR-046-provider-runtime-qemu-media", - "type": "implements-spec" + "from" : "ADR046-streamline-010", + "to" : "ADR046-streamline-001", + "type" : "work-item-depends-on" }, { - "from": "WI-008", - "to": "ADR-046-provider-runtime-qemu-media", - "type": "implements-spec" + "from" : "ADR046-streamline-012", + "to" : "ADR046-streamline-011", + "type" : "work-item-depends-on" }, { - "from": "WI-009", - "to": "ADR-046-provider-runtime-qemu-media", - "type": "implements-spec" + "from" : "ADR046-streamline-013", + "to" : "ADR046-streamline-001", + "type" : "work-item-depends-on" }, { - "from": "WI-010", - "to": "ADR-046-provider-runtime-qemu-media", - "type": "implements-spec" + "from" : "ADR046-streamline-013", + "to" : "ADR046-streamline-010", + "type" : "work-item-depends-on" }, { - "from": "WI-011", - "to": "ADR-046-provider-runtime-qemu-media", - "type": "implements-spec" + "from" : "ADR046-streamline-015", + "to" : "ADR046-streamline-001", + "type" : "work-item-depends-on" }, { - "from": "WI-012", - "to": "ADR-046-provider-runtime-qemu-media", - "type": "implements-spec" + "from" : "ADR046-streamline-015", + "to" : "ADR046-streamline-002", + "type" : "work-item-depends-on" }, { - "from": "WI-013", - "to": "ADR-046-provider-runtime-qemu-media", - "type": "implements-spec" + "from" : "ADR046-streamline-015", + "to" : "ADR046-streamline-009", + "type" : "work-item-depends-on" }, { - "from": "WI-014", - "to": "ADR-046-provider-runtime-qemu-media", - "type": "implements-spec" + "from" : "ADR046-streamline-016", + "to" : "ADR046-streamline-001", + "type" : "work-item-depends-on" }, { - "from": "WI-015", - "to": "ADR-046-provider-runtime-qemu-media", - "type": "implements-spec" + "from" : "ADR046-streamline-016", + "to" : "ADR046-streamline-008", + "type" : "work-item-depends-on" }, { - "from": "WI-016", - "to": "ADR-046-provider-runtime-qemu-media", - "type": "implements-spec" + "from" : "ADR046-streamline-018", + "to" : "ADR046-streamline-010", + "type" : "work-item-depends-on" }, { - "from": "WI-017", - "to": "ADR-046-provider-runtime-qemu-media", - "type": "implements-spec" + "from" : "ADR046-streamline-019", + "to" : "ADR046-streamline-001", + "type" : "work-item-depends-on" }, { - "from": "WI-018", - "to": "ADR-046-provider-runtime-qemu-media", - "type": "implements-spec" + "from" : "ADR046-streamline-020", + "to" : "ADR046-streamline-001", + "type" : "work-item-depends-on" + }, + { + "from" : "ADR046-streamline-021", + "to" : "ADR046-streamline-020", + "type" : "work-item-depends-on" + }, + { + "from" : "ADR046-streamline-022", + "to" : "ADR046-delivery-007", + "type" : "work-item-depends-on" + }, + { + "from" : "ADR046-streamline-023", + "to" : "ADR046-streamline-008", + "type" : "work-item-depends-on" + }, + { + "from" : "ADR046-streamline-023", + "to" : "ADR046-streamline-022", + "type" : "work-item-depends-on" + }, + { + "from" : "ADR046-streamline-024", + "to" : "ADR046-streamline-001", + "type" : "work-item-depends-on" + }, + { + "from" : "ADR046-system-core-001", + "to" : "ADR046-exec-003", + "type" : "work-item-depends-on" + }, + { + "from" : "ADR046-system-core-001", + "to" : "ADR046-exec-004", + "type" : "work-item-depends-on" + }, + { + "from" : "ADR046-system-core-001", + "to" : "ADR046-exec-005", + "type" : "work-item-depends-on" + }, + { + "from" : "ADR046-system-core-001", + "to" : "ADR046-provider-001", + "type" : "work-item-depends-on" + }, + { + "from" : "ADR046-system-core-001", + "to" : "ADR046-pstate-012", + "type" : "work-item-depends-on" + }, + { + "from" : "ADR046-system-core-001", + "to" : "ADR046-telem-001", + "type" : "work-item-depends-on" + }, + { + "from" : "ADR046-system-core-001", + "to" : "ADR046-telem-012", + "type" : "work-item-depends-on" + }, + { + "from" : "ADR046-systemd-001", + "to" : "ADR046-process-002", + "type" : "work-item-depends-on" + }, + { + "from" : "ADR046-systemd-002", + "to" : "ADR046-systemd-001", + "type" : "work-item-depends-on" + }, + { + "from" : "ADR046-systemd-003", + "to" : "ADR046-systemd-001", + "type" : "work-item-depends-on" + }, + { + "from" : "ADR046-telem-002", + "to" : "ADR046-store-001", + "type" : "work-item-depends-on" + }, + { + "from" : "ADR046-telem-002", + "to" : "ADR046-telem-001", + "type" : "work-item-depends-on" + }, + { + "from" : "ADR046-telem-003", + "to" : "ADR046-session-001", + "type" : "work-item-depends-on" + }, + { + "from" : "ADR046-telem-003", + "to" : "ADR046-session-003", + "type" : "work-item-depends-on" + }, + { + "from" : "ADR046-telem-003", + "to" : "ADR046-telem-001", + "type" : "work-item-depends-on" + }, + { + "from" : "ADR046-telem-004", + "to" : "ADR046-core-001", + "type" : "work-item-depends-on" + }, + { + "from" : "ADR046-telem-004", + "to" : "ADR046-telem-001", + "type" : "work-item-depends-on" + }, + { + "from" : "ADR046-telem-005", + "to" : "ADR046-process-001", + "type" : "work-item-depends-on" + }, + { + "from" : "ADR046-telem-005", + "to" : "ADR046-telem-001", + "type" : "work-item-depends-on" + }, + { + "from" : "ADR046-telem-006", + "to" : "ADR046-process-001", + "type" : "work-item-depends-on" + }, + { + "from" : "ADR046-telem-006", + "to" : "ADR046-provider-001", + "type" : "work-item-depends-on" + }, + { + "from" : "ADR046-telem-007", + "to" : "ADR046-telem-006", + "type" : "work-item-depends-on" + }, + { + "from" : "ADR046-telem-008", + "to" : "ADR046-telem-001", + "type" : "work-item-depends-on" + }, + { + "from" : "ADR046-telem-010", + "to" : "ADR046-store-001", + "type" : "work-item-depends-on" + }, + { + "from" : "ADR046-telem-010", + "to" : "ADR046-telem-006", + "type" : "work-item-depends-on" + }, + { + "from" : "ADR046-telem-010", + "to" : "ADR046-telem-009", + "type" : "work-item-depends-on" + }, + { + "from" : "ADR046-telem-011", + "to" : "ADR046-store-001", + "type" : "work-item-depends-on" + }, + { + "from" : "ADR046-telem-011", + "to" : "ADR046-telem-009", + "type" : "work-item-depends-on" + }, + { + "from" : "ADR046-telem-011", + "to" : "ADR046-telem-010", + "type" : "work-item-depends-on" + }, + { + "from" : "ADR046-telem-011", + "to" : "ADR046-telem-012", + "type" : "work-item-depends-on" + }, + { + "from" : "ADR046-telem-013", + "to" : "ADR046-store-001", + "type" : "work-item-depends-on" + }, + { + "from" : "ADR046-telem-013", + "to" : "ADR046-telem-012", + "type" : "work-item-depends-on" + }, + { + "from" : "ADR046-telem-014", + "to" : "ADR046-session-001", + "type" : "work-item-depends-on" + }, + { + "from" : "ADR046-telem-014", + "to" : "ADR046-session-003", + "type" : "work-item-depends-on" + }, + { + "from" : "ADR046-telem-014", + "to" : "ADR046-telem-012", + "type" : "work-item-depends-on" + }, + { + "from" : "ADR046-telem-015", + "to" : "ADR046-telem-012", + "type" : "work-item-depends-on" + }, + { + "from" : "ADR046-telem-016", + "to" : "ADR046-core-001", + "type" : "work-item-depends-on" + }, + { + "from" : "ADR046-telem-016", + "to" : "ADR046-telem-012", + "type" : "work-item-depends-on" + }, + { + "from" : "ADR046-telem-017", + "to" : "ADR046-telem-016", + "type" : "work-item-depends-on" + }, + { + "from" : "ADR046-telem-018", + "to" : "ADR046-core-001", + "type" : "work-item-depends-on" + }, + { + "from" : "ADR046-telem-018", + "to" : "ADR046-telem-012", + "type" : "work-item-depends-on" + }, + { + "from" : "ADR046-telem-019", + "to" : "ADR046-telem-001", + "type" : "work-item-depends-on" + }, + { + "from" : "ADR046-telem-020", + "to" : "ADR046-telem-019", + "type" : "work-item-depends-on" + }, + { + "from" : "ADR046-telem-021", + "to" : "ADR046-telem-019", + "type" : "work-item-depends-on" + }, + { + "from" : "ADR046-telem-022", + "to" : "ADR046-telem-019", + "type" : "work-item-depends-on" + }, + { + "from" : "ADR046-telem-022", + "to" : "ADR046-telem-021", + "type" : "work-item-depends-on" + }, + { + "from" : "ADR046-telem-023", + "to" : "ADR046-telem-014", + "type" : "work-item-depends-on" + }, + { + "from" : "ADR046-telem-023", + "to" : "ADR046-telem-022", + "type" : "work-item-depends-on" + }, + { + "from" : "ADR046-telem-024", + "to" : "ADR046-session-003", + "type" : "work-item-depends-on" + }, + { + "from" : "ADR046-telem-024", + "to" : "ADR046-telem-019", + "type" : "work-item-depends-on" + }, + { + "from" : "ADR046-telem-025", + "to" : "ADR046-telem-022", + "type" : "work-item-depends-on" + }, + { + "from" : "ADR046-telem-025", + "to" : "ADR046-telem-024", + "type" : "work-item-depends-on" + }, + { + "from" : "ADR046-telem-026", + "to" : "ADR046-telem-019", + "type" : "work-item-depends-on" + }, + { + "from" : "ADR046-telem-027", + "to" : "ADR046-telem-001", + "type" : "work-item-depends-on" + }, + { + "from" : "ADR046-telem-027", + "to" : "ADR046-telem-003", + "type" : "work-item-depends-on" + }, + { + "from" : "ADR046-telem-027", + "to" : "ADR046-telem-019", + "type" : "work-item-depends-on" + }, + { + "from" : "ADR046-transport-relay-002", + "to" : "ADR046-transport-relay-001", + "type" : "work-item-depends-on" + }, + { + "from" : "ADR046-transport-relay-003", + "to" : "ADR046-transport-relay-001", + "type" : "work-item-depends-on" + }, + { + "from" : "ADR046-transport-relay-004", + "to" : "ADR046-transport-relay-001", + "type" : "work-item-depends-on" + }, + { + "from" : "ADR046-transport-relay-005", + "to" : "ADR046-transport-relay-001", + "type" : "work-item-depends-on" + }, + { + "from" : "ADR046-transport-relay-006", + "to" : "ADR046-transport-relay-001", + "type" : "work-item-depends-on" + }, + { + "from" : "ADR046-transport-relay-006", + "to" : "ADR046-transport-relay-002", + "type" : "work-item-depends-on" + }, + { + "from" : "ADR046-transport-relay-006", + "to" : "ADR046-transport-relay-003", + "type" : "work-item-depends-on" + }, + { + "from" : "ADR046-transport-relay-006", + "to" : "ADR046-transport-relay-004", + "type" : "work-item-depends-on" + }, + { + "from" : "ADR046-transport-relay-006", + "to" : "ADR046-transport-relay-005", + "type" : "work-item-depends-on" + }, + { + "from" : "ADR046-transport-unix-001", + "to" : "ADR046-session-001", + "type" : "work-item-depends-on" + }, + { + "from" : "ADR046-transport-unix-002", + "to" : "ADR046-session-003", + "type" : "work-item-depends-on" + }, + { + "from" : "ADR046-transport-unix-002", + "to" : "ADR046-transport-unix-001", + "type" : "work-item-depends-on" + }, + { + "from" : "ADR046-transport-unix-003", + "to" : "ADR046-transport-unix-001", + "type" : "work-item-depends-on" + }, + { + "from" : "ADR046-transport-unix-004", + "to" : "ADR046-transport-unix-001", + "type" : "work-item-depends-on" + }, + { + "from" : "ADR046-transport-unix-005", + "to" : "ADR046-transport-unix-002", + "type" : "work-item-depends-on" + }, + { + "from" : "ADR046-transport-unix-006", + "to" : "ADR046-transport-unix-002", + "type" : "work-item-depends-on" + }, + { + "from" : "ADR046-transport-unix-007", + "to" : "ADR046-session-001", + "type" : "work-item-depends-on" + }, + { + "from" : "ADR046-transport-unix-007", + "to" : "ADR046-session-003", + "type" : "work-item-depends-on" + }, + { + "from" : "ADR046-transport-unix-007", + "to" : "ADR046-transport-unix-002", + "type" : "work-item-depends-on" + }, + { + "from" : "ADR046-transport-unix-007", + "to" : "ADR046-transport-unix-003", + "type" : "work-item-depends-on" + }, + { + "from" : "ADR046-transport-unix-007", + "to" : "ADR046-transport-unix-004", + "type" : "work-item-depends-on" + }, + { + "from" : "ADR046-transport-unix-007", + "to" : "ADR046-transport-unix-005", + "type" : "work-item-depends-on" + }, + { + "from" : "ADR046-transport-unix-007", + "to" : "ADR046-transport-unix-006", + "type" : "work-item-depends-on" + }, + { + "from" : "ADR046-transport-unix-008", + "to" : "ADR046-provider-003", + "type" : "work-item-depends-on" + }, + { + "from" : "ADR046-transport-unix-008", + "to" : "ADR046-transport-unix-007", + "type" : "work-item-depends-on" + }, + { + "from" : "ADR046-transport-unix-009", + "to" : "ADR046-transport-unix-001", + "type" : "work-item-depends-on" + }, + { + "from" : "ADR046-transport-unix-010", + "to" : "ADR046-transport-unix-007", + "type" : "work-item-depends-on" + }, + { + "from" : "ADR046-transport-unix-011", + "to" : "ADR046-transport-unix-007", + "type" : "work-item-depends-on" + }, + { + "from" : "ADR046-transport-unix-011", + "to" : "ADR046-transport-unix-008", + "type" : "work-item-depends-on" + }, + { + "from" : "ADR046-transport-unix-011", + "to" : "ADR046-transport-unix-009", + "type" : "work-item-depends-on" + }, + { + "from" : "ADR046-transport-unix-011", + "to" : "ADR046-transport-unix-010", + "type" : "work-item-depends-on" + }, + { + "from" : "ADR046-usbip-002", + "to" : "ADR046-usbip-001", + "type" : "work-item-depends-on" + }, + { + "from" : "ADR046-usbip-002", + "to" : "ADR046-zone-control-024", + "type" : "work-item-depends-on" + }, + { + "from" : "ADR046-usbip-003", + "to" : "ADR046-provider-004", + "type" : "work-item-depends-on" + }, + { + "from" : "ADR046-usbip-003", + "to" : "ADR046-usbip-001", + "type" : "work-item-depends-on" + }, + { + "from" : "ADR046-usbip-004", + "to" : "ADR046-usbip-001", + "type" : "work-item-depends-on" + }, + { + "from" : "ADR046-usbip-004", + "to" : "ADR046-usbip-002", + "type" : "work-item-depends-on" + }, + { + "from" : "ADR046-usbip-004", + "to" : "ADR046-usbip-003", + "type" : "work-item-depends-on" + }, + { + "from" : "ADR046-usbip-004", + "to" : "ADR046-zone-control-024", + "type" : "work-item-depends-on" + }, + { + "from" : "ADR046-usbip-005", + "to" : "ADR046-usbip-003", + "type" : "work-item-depends-on" + }, + { + "from" : "ADR046-usbip-006", + "to" : "ADR046-usbip-003", + "type" : "work-item-depends-on" + }, + { + "from" : "ADR046-usbip-007", + "to" : "ADR046-usbip-004", + "type" : "work-item-depends-on" + }, + { + "from" : "ADR046-usbip-007", + "to" : "ADR046-usbip-005", + "type" : "work-item-depends-on" + }, + { + "from" : "ADR046-usbip-008", + "to" : "ADR046-usbip-003", + "type" : "work-item-depends-on" + }, + { + "from" : "ADR046-usbip-008", + "to" : "ADR046-zone-control-024", + "type" : "work-item-depends-on" + }, + { + "from" : "ADR046-usbip-009", + "to" : "ADR046-usbip-004", + "type" : "work-item-depends-on" + }, + { + "from" : "ADR046-usbip-009", + "to" : "ADR046-usbip-008", + "type" : "work-item-depends-on" + }, + { + "from" : "ADR046-vl-001", + "to" : "ADR046-primitives-001", + "type" : "work-item-depends-on" + }, + { + "from" : "ADR046-vl-002", + "to" : "ADR046-vl-001", + "type" : "work-item-depends-on" + }, + { + "from" : "ADR046-vl-003", + "to" : "ADR046-reconcile-001", + "type" : "work-item-depends-on" + }, + { + "from" : "ADR046-vl-003", + "to" : "ADR046-vl-002", + "type" : "work-item-depends-on" + }, + { + "from" : "ADR046-vl-004", + "to" : "ADR046-vl-003", + "type" : "work-item-depends-on" + }, + { + "from" : "ADR046-vl-005", + "to" : "ADR046-vl-002", + "type" : "work-item-depends-on" + }, + { + "from" : "ADR046-vl-006", + "to" : "ADR046-vl-003", + "type" : "work-item-depends-on" + }, + { + "from" : "ADR046-vl-007", + "to" : "ADR046-pstate-004", + "type" : "work-item-depends-on" + }, + { + "from" : "ADR046-vl-007", + "to" : "ADR046-pstate-005", + "type" : "work-item-depends-on" + }, + { + "from" : "ADR046-vl-007", + "to" : "ADR046-pstate-006", + "type" : "work-item-depends-on" + }, + { + "from" : "ADR046-vl-007", + "to" : "ADR046-vl-002", + "type" : "work-item-depends-on" + }, + { + "from" : "ADR046-vl-007", + "to" : "ADR046-vl-003", + "type" : "work-item-depends-on" + }, + { + "from" : "ADR046-vl-008", + "to" : "ADR046-vl-003", + "type" : "work-item-depends-on" + }, + { + "from" : "ADR046-vl-008", + "to" : "ADR046-vl-007", + "type" : "work-item-depends-on" + }, + { + "from" : "ADR046-vl-009", + "to" : "ADR046-vl-001", + "type" : "work-item-depends-on" + }, + { + "from" : "ADR046-vl-010", + "to" : "ADR046-pstate-010", + "type" : "work-item-depends-on" + }, + { + "from" : "ADR046-vl-010", + "to" : "ADR046-vl-001", + "type" : "work-item-depends-on" + }, + { + "from" : "ADR046-vl-011", + "to" : "ADR046-pstate-011", + "type" : "work-item-depends-on" + }, + { + "from" : "ADR046-vl-011", + "to" : "ADR046-vl-002", + "type" : "work-item-depends-on" + }, + { + "from" : "ADR046-vl-012", + "to" : "ADR046-vl-001", + "type" : "work-item-depends-on" + }, + { + "from" : "ADR046-vl-012", + "to" : "ADR046-vl-002", + "type" : "work-item-depends-on" + }, + { + "from" : "ADR046-vl-013", + "to" : "ADR046-vl-001", + "type" : "work-item-depends-on" + }, + { + "from" : "ADR046-vl-013", + "to" : "ADR046-vl-012", + "type" : "work-item-depends-on" + }, + { + "from" : "ADR046-volume-002", + "to" : "ADR046-volume-001", + "type" : "work-item-depends-on" + }, + { + "from" : "ADR046-volume-003", + "to" : "ADR046-volume-001", + "type" : "work-item-depends-on" + }, + { + "from" : "ADR046-volume-004", + "to" : "ADR046-volume-001", + "type" : "work-item-depends-on" + }, + { + "from" : "ADR046-volume-005", + "to" : "ADR046-volume-002", + "type" : "work-item-depends-on" + }, + { + "from" : "ADR046-volume-005", + "to" : "ADR046-volume-003", + "type" : "work-item-depends-on" + }, + { + "from" : "ADR046-volume-006", + "to" : "ADR046-volume-001", + "type" : "work-item-depends-on" + }, + { + "from" : "ADR046-volume-006", + "to" : "ADR046-volume-004", + "type" : "work-item-depends-on" + }, + { + "from" : "ADR046-vsock-001", + "to" : "ADR046-session-003", + "type" : "work-item-depends-on" + }, + { + "from" : "ADR046-vsock-002", + "to" : "ADR046-vsock-001", + "type" : "work-item-depends-on" + }, + { + "from" : "ADR046-vsock-003", + "to" : "ADR046-session-003", + "type" : "work-item-depends-on" + }, + { + "from" : "ADR046-vsock-003", + "to" : "ADR046-vsock-002", + "type" : "work-item-depends-on" + }, + { + "from" : "ADR046-vsock-004", + "to" : "ADR046-vsock-001", + "type" : "work-item-depends-on" + }, + { + "from" : "ADR046-vsock-006", + "to" : "ADR046-vsock-003", + "type" : "work-item-depends-on" + }, + { + "from" : "ADR046-vsock-006", + "to" : "ADR046-vsock-004", + "type" : "work-item-depends-on" + }, + { + "from" : "ADR046-vvfs-001", + "to" : "ADR046-volume-001", + "type" : "work-item-depends-on" + }, + { + "from" : "ADR046-vvfs-001", + "to" : "ADR046-vvfs-007", + "type" : "work-item-depends-on" + }, + { + "from" : "ADR046-vvfs-002", + "to" : "ADR046-volume-001", + "type" : "work-item-depends-on" + }, + { + "from" : "ADR046-vvfs-002", + "to" : "ADR046-vvfs-001", + "type" : "work-item-depends-on" + }, + { + "from" : "ADR046-vvfs-003", + "to" : "ADR046-volume-001", + "type" : "work-item-depends-on" + }, + { + "from" : "ADR046-vvfs-003", + "to" : "ADR046-vvfs-001", + "type" : "work-item-depends-on" + }, + { + "from" : "ADR046-vvfs-003", + "to" : "ADR046-vvfs-002", + "type" : "work-item-depends-on" + }, + { + "from" : "ADR046-vvfs-003", + "to" : "ADR046-vvfs-007", + "type" : "work-item-depends-on" + }, + { + "from" : "ADR046-vvfs-004", + "to" : "ADR046-vvfs-003", + "type" : "work-item-depends-on" + }, + { + "from" : "ADR046-vvfs-005", + "to" : "ADR046-volume-002", + "type" : "work-item-depends-on" + }, + { + "from" : "ADR046-vvfs-005", + "to" : "ADR046-vvfs-003", + "type" : "work-item-depends-on" + }, + { + "from" : "ADR046-vvfs-005", + "to" : "ADR046-vvfs-004", + "type" : "work-item-depends-on" + }, + { + "from" : "ADR046-vvfs-006", + "to" : "ADR046-volume-004", + "type" : "work-item-depends-on" + }, + { + "from" : "ADR046-vvfs-006", + "to" : "ADR046-vvfs-001", + "type" : "work-item-depends-on" + }, + { + "from" : "ADR046-vvfs-007", + "to" : "ADR046-volume-001", + "type" : "work-item-depends-on" + }, + { + "from" : "ADR046-zone-control-001", + "to" : "ADR046-identities-001", + "type" : "work-item-depends-on" + }, + { + "from" : "ADR046-zone-control-001", + "to" : "ADR046-object-001", + "type" : "work-item-depends-on" + }, + { + "from" : "ADR046-zone-control-001", + "to" : "ADR046-store-001", + "type" : "work-item-depends-on" + }, + { + "from" : "ADR046-zone-control-002", + "to" : "ADR046-zone-control-001", + "type" : "work-item-depends-on" + }, + { + "from" : "ADR046-zone-control-002", + "to" : "ADR046-zone-control-018", + "type" : "work-item-depends-on" + }, + { + "from" : "ADR046-zone-control-003", + "to" : "ADR046-api-001", + "type" : "work-item-depends-on" + }, + { + "from" : "ADR046-zone-control-003", + "to" : "ADR046-zone-control-001", + "type" : "work-item-depends-on" + }, + { + "from" : "ADR046-zone-control-003", + "to" : "ADR046-zone-control-017", + "type" : "work-item-depends-on" + }, + { + "from" : "ADR046-zone-control-004", + "to" : "ADR046-api-002", + "type" : "work-item-depends-on" + }, + { + "from" : "ADR046-zone-control-004", + "to" : "ADR046-zone-control-001", + "type" : "work-item-depends-on" + }, + { + "from" : "ADR046-zone-control-005", + "to" : "ADR046-zone-control-004", + "type" : "work-item-depends-on" + }, + { + "from" : "ADR046-zone-control-006", + "to" : "ADR046-zone-control-004", + "type" : "work-item-depends-on" + }, + { + "from" : "ADR046-zone-control-006", + "to" : "ADR046-zone-control-005", + "type" : "work-item-depends-on" + }, + { + "from" : "ADR046-zone-control-007", + "to" : "ADR046-identities-002", + "type" : "work-item-depends-on" + }, + { + "from" : "ADR046-zone-control-007", + "to" : "ADR046-zone-control-001", + "type" : "work-item-depends-on" + }, + { + "from" : "ADR046-zone-control-007", + "to" : "ADR046-zone-control-002", + "type" : "work-item-depends-on" + }, + { + "from" : "ADR046-zone-control-007", + "to" : "ADR046-zone-control-003", + "type" : "work-item-depends-on" + }, + { + "from" : "ADR046-zone-control-007", + "to" : "ADR046-zone-control-004", + "type" : "work-item-depends-on" + }, + { + "from" : "ADR046-zone-control-007", + "to" : "ADR046-zone-control-005", + "type" : "work-item-depends-on" + }, + { + "from" : "ADR046-zone-control-007", + "to" : "ADR046-zone-control-009", + "type" : "work-item-depends-on" + }, + { + "from" : "ADR046-zone-control-007", + "to" : "ADR046-zone-control-010", + "type" : "work-item-depends-on" + }, + { + "from" : "ADR046-zone-control-008", + "to" : "ADR046-zone-control-001", + "type" : "work-item-depends-on" + }, + { + "from" : "ADR046-zone-control-008", + "to" : "ADR046-zone-control-003", + "type" : "work-item-depends-on" + }, + { + "from" : "ADR046-zone-control-009", + "to" : "ADR046-store-001", + "type" : "work-item-depends-on" + }, + { + "from" : "ADR046-zone-control-009", + "to" : "ADR046-zone-control-001", + "type" : "work-item-depends-on" + }, + { + "from" : "ADR046-zone-control-010", + "to" : "ADR046-store-001", + "type" : "work-item-depends-on" + }, + { + "from" : "ADR046-zone-control-010", + "to" : "ADR046-zone-control-001", + "type" : "work-item-depends-on" + }, + { + "from" : "ADR046-zone-control-011", + "to" : "ADR046-identities-001", + "type" : "work-item-depends-on" + }, + { + "from" : "ADR046-zone-control-011", + "to" : "ADR046-store-001", + "type" : "work-item-depends-on" + }, + { + "from" : "ADR046-zone-control-012", + "to" : "ADR046-zone-control-011", + "type" : "work-item-depends-on" + }, + { + "from" : "ADR046-zone-control-013", + "to" : "ADR046-identities-001", + "type" : "work-item-depends-on" + }, + { + "from" : "ADR046-zone-control-014", + "to" : "ADR046-zone-control-001", + "type" : "work-item-depends-on" + }, + { + "from" : "ADR046-zone-control-015", + "to" : "ADR046-zone-control-014", + "type" : "work-item-depends-on" + }, + { + "from" : "ADR046-zone-control-016", + "to" : "ADR046-zone-control-001", + "type" : "work-item-depends-on" + }, + { + "from" : "ADR046-zone-control-016", + "to" : "ADR046-zone-control-015", + "type" : "work-item-depends-on" + }, + { + "from" : "ADR046-zone-control-017", + "to" : "ADR046-zone-control-011", + "type" : "work-item-depends-on" + }, + { + "from" : "ADR046-zone-control-017", + "to" : "ADR046-zone-control-013", + "type" : "work-item-depends-on" + }, + { + "from" : "ADR046-zone-control-018", + "to" : "ADR046-zone-control-011", + "type" : "work-item-depends-on" + }, + { + "from" : "ADR046-zone-control-018", + "to" : "ADR046-zone-control-012", + "type" : "work-item-depends-on" + }, + { + "from" : "ADR046-zone-control-018", + "to" : "ADR046-zone-control-013", + "type" : "work-item-depends-on" + }, + { + "from" : "ADR046-zone-control-019", + "to" : "ADR046-provider-004", + "type" : "work-item-depends-on" + }, + { + "from" : "ADR046-zone-control-019", + "to" : "ADR046-zone-control-001", + "type" : "work-item-depends-on" + }, + { + "from" : "ADR046-zone-control-020", + "to" : "ADR046-zone-control-019", + "type" : "work-item-depends-on" + }, + { + "from" : "ADR046-zone-control-021", + "to" : "ADR046-zone-control-001", + "type" : "work-item-depends-on" + }, + { + "from" : "ADR046-zone-control-021", + "to" : "ADR046-zone-control-016", + "type" : "work-item-depends-on" + }, + { + "from" : "ADR046-zone-control-022", + "to" : "ADR046-api-001", + "type" : "work-item-depends-on" + }, + { + "from" : "ADR046-zone-control-022", + "to" : "ADR046-zone-control-019", + "type" : "work-item-depends-on" + }, + { + "from" : "ADR046-zone-control-023", + "to" : "ADR046-zone-control-019", + "type" : "work-item-depends-on" + }, + { + "from" : "ADR046-zone-control-024", + "to" : "ADR046-zone-control-019", + "type" : "work-item-depends-on" + }, + { + "from" : "ADR046-zone-control-024", + "to" : "ADR046-zone-control-022", + "type" : "work-item-depends-on" + }, + { + "from" : "ADR046-zone-control-025", + "to" : "ADR046-zone-control-011", + "type" : "work-item-depends-on" + }, + { + "from" : "ADR046-zone-control-025", + "to" : "ADR046-zone-control-012", + "type" : "work-item-depends-on" + }, + { + "from" : "ADR046-zone-control-025", + "to" : "ADR046-zone-control-013", + "type" : "work-item-depends-on" + }, + { + "from" : "ADR046-zone-control-025", + "to" : "ADR046-zone-control-018", + "type" : "work-item-depends-on" + }, + { + "from" : "ADR046-zone-control-026", + "to" : "ADR046-zone-control-003", + "type" : "work-item-depends-on" + }, + { + "from" : "ADR046-zone-control-027", + "to" : "ADR046-zone-control-011", + "type" : "work-item-depends-on" + }, + { + "from" : "ADR046-zone-control-027", + "to" : "ADR046-zone-control-017", + "type" : "work-item-depends-on" + }, + { + "from" : "ADR046-zone-control-027", + "to" : "ADR046-zone-control-018", + "type" : "work-item-depends-on" + }, + { + "from" : "ADR046-zone-control-028", + "to" : "ADR046-zone-control-013", + "type" : "work-item-depends-on" } ], - "generatedFrom": { - "specSet": "docs/specs/ADR-046-spec-set.json", - "waveTopology": "docs/specs/ADR-046-validation-and-delivery.md#3-delivery-wave-topology", - "workItems": "docs/specs/ADR-046-work-items.json" + "generatedFrom" : { + "specSet" : "docs/specs/ADR-046-spec-set.json", + "waveTopology" : "docs/specs/ADR-046-validation-and-delivery.md#3-delivery-wave-topology", + "workItems" : "docs/specs/ADR-046-work-items.json" }, - "nodes": [ + "nodes" : [ { - "blockers": [], - "destinations": [ + "blockers" : [], + "destinations" : [ "docs/specs/ADR-046-cli-and-operations.md" ], - "entryContracts": [ + "entryContracts" : [ "ADR-046-components-processes-and-sandbox", "ADR-046-componentsession-and-bus", "ADR-046-provider-model-and-packaging", @@ -8444,12 +9542,12 @@ "ADR-046-resource-object-model", "ADR-046-terminology-and-identities" ], - "exitGate": "ADR046-W5 exit criteria (ADR-046-validation-and-delivery §4): every spec/work item in this wave Merged with clean destinations, all validators green, and the ten-role panel seal recorded", - "id": "ADR-046-cli-and-operations", - "kind": "spec", - "owner": "ADR046-W5 wave (ADR-046-validation-and-delivery §3.2)", - "parallelGroup": "W5-parallel-specs", - "prerequisites": [ + "exitGate" : "ADR046-W5 exit criteria (ADR-046-validation-and-delivery §4): every spec/work item in this wave Merged with clean destinations, all validators green, and the ten-role panel seal recorded", + "id" : "ADR-046-cli-and-operations", + "kind" : "spec", + "owner" : "ADR046-W5 wave (ADR-046-validation-and-delivery §3.2)", + "parallelGroup" : "W5-parallel-specs", + "prerequisites" : [ "ADR-046-components-processes-and-sandbox", "ADR-046-componentsession-and-bus", "ADR-046-provider-model-and-packaging", @@ -8457,120 +9555,120 @@ "ADR-046-resource-object-model", "ADR-046-terminology-and-identities" ], - "specId": "ADR-046-cli-and-operations", - "topologicalRank": 9, - "wave": "W5" + "specId" : "ADR-046-cli-and-operations", + "topologicalRank" : 9, + "wave" : "W5" }, { - "blockers": [], - "destinations": [ + "blockers" : [], + "destinations" : [ "docs/specs/ADR-046-components-processes-and-sandbox.md" ], - "entryContracts": [ + "entryContracts" : [ "ADR-046-componentsession-and-bus", "ADR-046-primitive-resource-composition", "ADR-046-provider-model-and-packaging" ], - "exitGate": "ADR046-W4 exit criteria (ADR-046-validation-and-delivery §4): every spec/work item in this wave Merged with clean destinations, all validators green, and the ten-role panel seal recorded", - "id": "ADR-046-components-processes-and-sandbox", - "kind": "spec", - "owner": "ADR046-W4 wave (ADR-046-validation-and-delivery §3.2)", - "parallelGroup": "W4-parallel-specs", - "prerequisites": [ + "exitGate" : "ADR046-W4 exit criteria (ADR-046-validation-and-delivery §4): every spec/work item in this wave Merged with clean destinations, all validators green, and the ten-role panel seal recorded", + "id" : "ADR-046-components-processes-and-sandbox", + "kind" : "spec", + "owner" : "ADR046-W4 wave (ADR-046-validation-and-delivery §3.2)", + "parallelGroup" : "W4-parallel-specs", + "prerequisites" : [ "ADR-046-componentsession-and-bus", "ADR-046-primitive-resource-composition", "ADR-046-provider-model-and-packaging" ], - "specId": "ADR-046-components-processes-and-sandbox", - "topologicalRank": 8, - "wave": "W4" + "specId" : "ADR-046-components-processes-and-sandbox", + "topologicalRank" : 8, + "wave" : "W4" }, { - "blockers": [], - "destinations": [ + "blockers" : [], + "destinations" : [ "docs/specs/ADR-046-componentsession-and-bus.md" ], - "entryContracts": [ + "entryContracts" : [ "ADR-046-resource-api-and-authorization", "ADR-046-terminology-and-identities" ], - "exitGate": "ADR046-W1 exit criteria (ADR-046-validation-and-delivery §4): every spec/work item in this wave Merged with clean destinations, all validators green, and the ten-role panel seal recorded", - "id": "ADR-046-componentsession-and-bus", - "kind": "spec", - "owner": "ADR046-W1 wave (ADR-046-validation-and-delivery §3.2)", - "parallelGroup": "W1-reconcile-and-bus", - "prerequisites": [ + "exitGate" : "ADR046-W1 exit criteria (ADR-046-validation-and-delivery §4): every spec/work item in this wave Merged with clean destinations, all validators green, and the ten-role panel seal recorded", + "id" : "ADR-046-componentsession-and-bus", + "kind" : "spec", + "owner" : "ADR046-W1 wave (ADR-046-validation-and-delivery §3.2)", + "parallelGroup" : "W1-reconcile-and-bus", + "prerequisites" : [ "ADR-046-resource-api-and-authorization", "ADR-046-terminology-and-identities" ], - "specId": "ADR-046-componentsession-and-bus", - "topologicalRank": 5, - "wave": "W1" + "specId" : "ADR-046-componentsession-and-bus", + "topologicalRank" : 5, + "wave" : "W1" }, { - "blockers": [], - "destinations": [ + "blockers" : [], + "destinations" : [ "docs/specs/ADR-046-core-controllers.md" ], - "entryContracts": [ + "entryContracts" : [ "ADR-046-provider-model-and-packaging", "ADR-046-resource-api-and-authorization", "ADR-046-resource-reconciliation", "ADR-046-resource-store-redb" ], - "exitGate": "ADR046-W4 exit criteria (ADR-046-validation-and-delivery §4): every spec/work item in this wave Merged with clean destinations, all validators green, and the ten-role panel seal recorded", - "id": "ADR-046-core-controllers", - "kind": "spec", - "owner": "ADR046-W4 wave (ADR-046-validation-and-delivery §3.2)", - "parallelGroup": "W4-parallel-specs", - "prerequisites": [ + "exitGate" : "ADR046-W4 exit criteria (ADR-046-validation-and-delivery §4): every spec/work item in this wave Merged with clean destinations, all validators green, and the ten-role panel seal recorded", + "id" : "ADR-046-core-controllers", + "kind" : "spec", + "owner" : "ADR046-W4 wave (ADR-046-validation-and-delivery §3.2)", + "parallelGroup" : "W4-parallel-specs", + "prerequisites" : [ "ADR-046-provider-model-and-packaging", "ADR-046-resource-api-and-authorization", "ADR-046-resource-reconciliation", "ADR-046-resource-store-redb" ], - "specId": "ADR-046-core-controllers", - "topologicalRank": 8, - "wave": "W4" + "specId" : "ADR-046-core-controllers", + "topologicalRank" : 8, + "wave" : "W4" }, { - "blockers": [], - "destinations": [ + "blockers" : [], + "destinations" : [ "docs/specs/ADR-046-current-code-migration-map.md" ], - "entryContracts": [], - "exitGate": "ADR046-W0 exit criteria (ADR-046-validation-and-delivery §4): every spec/work item in this wave Merged with clean destinations, all validators green, and the ten-role panel seal recorded", - "id": "ADR-046-current-code-migration-map", - "kind": "spec", - "owner": "ADR046-W0 wave (ADR-046-validation-and-delivery §3.2)", - "parallelGroup": "W0-reference-docs", - "prerequisites": [], - "specId": "ADR-046-current-code-migration-map", - "topologicalRank": 0, - "wave": "W0" + "entryContracts" : [], + "exitGate" : "ADR046-W0 exit criteria (ADR-046-validation-and-delivery §4): every spec/work item in this wave Merged with clean destinations, all validators green, and the ten-role panel seal recorded", + "id" : "ADR-046-current-code-migration-map", + "kind" : "spec", + "owner" : "ADR046-W0 wave (ADR-046-validation-and-delivery §3.2)", + "parallelGroup" : "W0-reference-docs", + "prerequisites" : [], + "specId" : "ADR-046-current-code-migration-map", + "topologicalRank" : 0, + "wave" : "W0" }, { - "blockers": [], - "destinations": [ + "blockers" : [], + "destinations" : [ "docs/specs/ADR-046-decision-register.md" ], - "entryContracts": [], - "exitGate": "ADR046-W0 exit criteria (ADR-046-validation-and-delivery §4): every spec/work item in this wave Merged with clean destinations, all validators green, and the ten-role panel seal recorded", - "id": "ADR-046-decision-register", - "kind": "spec", - "owner": "ADR046-W0 wave (ADR-046-validation-and-delivery §3.2)", - "parallelGroup": "W0-reference-docs", - "prerequisites": [], - "specId": "ADR-046-decision-register", - "topologicalRank": 0, - "wave": "W0" + "entryContracts" : [], + "exitGate" : "ADR046-W0 exit criteria (ADR-046-validation-and-delivery §4): every spec/work item in this wave Merged with clean destinations, all validators green, and the ten-role panel seal recorded", + "id" : "ADR-046-decision-register", + "kind" : "spec", + "owner" : "ADR046-W0 wave (ADR-046-validation-and-delivery §3.2)", + "parallelGroup" : "W0-reference-docs", + "prerequisites" : [], + "specId" : "ADR-046-decision-register", + "topologicalRank" : 0, + "wave" : "W0" }, { - "blockers": [], - "destinations": [ + "blockers" : [], + "destinations" : [ "docs/specs/ADR-046-feasibility-and-spikes.md" ], - "entryContracts": [ + "entryContracts" : [ "ADR-046-cli-and-operations", "ADR-046-components-processes-and-sandbox", "ADR-046-componentsession-and-bus", @@ -8587,12 +9685,12 @@ "ADR-046-terminology-and-identities", "ADR-046-zone-routing" ], - "exitGate": "ADR046-W7 exit criteria (ADR-046-validation-and-delivery §4): every spec/work item in this wave Merged with clean destinations, all validators green, and the ten-role panel seal recorded", - "id": "ADR-046-feasibility-and-spikes", - "kind": "spec", - "owner": "ADR046-W7 wave (ADR-046-validation-and-delivery §3.2)", - "parallelGroup": "W7-closing", - "prerequisites": [ + "exitGate" : "ADR046-W7 exit criteria (ADR-046-validation-and-delivery §4): every spec/work item in this wave Merged with clean destinations, all validators green, and the ten-role panel seal recorded", + "id" : "ADR-046-feasibility-and-spikes", + "kind" : "spec", + "owner" : "ADR046-W7 wave (ADR-046-validation-and-delivery §3.2)", + "parallelGroup" : "W7-closing", + "prerequisites" : [ "ADR-046-cli-and-operations", "ADR-046-components-processes-and-sandbox", "ADR-046-componentsession-and-bus", @@ -8609,66 +9707,66 @@ "ADR-046-terminology-and-identities", "ADR-046-zone-routing" ], - "specId": "ADR-046-feasibility-and-spikes", - "topologicalRank": 10, - "wave": "W7" + "specId" : "ADR-046-feasibility-and-spikes", + "topologicalRank" : 10, + "wave" : "W7" }, { - "blockers": [], - "destinations": [ + "blockers" : [], + "destinations" : [ "docs/specs/ADR-046-nix-configuration.md" ], - "entryContracts": [ + "entryContracts" : [ "ADR-046-core-controllers", "ADR-046-primitive-resource-composition", "ADR-046-provider-model-and-packaging", "ADR-046-resource-object-model", "ADR-046-terminology-and-identities" ], - "exitGate": "ADR046-W5 exit criteria (ADR-046-validation-and-delivery §4): every spec/work item in this wave Merged with clean destinations, all validators green, and the ten-role panel seal recorded", - "id": "ADR-046-nix-configuration", - "kind": "spec", - "owner": "ADR046-W5 wave (ADR-046-validation-and-delivery §3.2)", - "parallelGroup": "W5-parallel-specs", - "prerequisites": [ + "exitGate" : "ADR046-W5 exit criteria (ADR-046-validation-and-delivery §4): every spec/work item in this wave Merged with clean destinations, all validators green, and the ten-role panel seal recorded", + "id" : "ADR-046-nix-configuration", + "kind" : "spec", + "owner" : "ADR046-W5 wave (ADR-046-validation-and-delivery §3.2)", + "parallelGroup" : "W5-parallel-specs", + "prerequisites" : [ "ADR-046-core-controllers", "ADR-046-primitive-resource-composition", "ADR-046-provider-model-and-packaging", "ADR-046-resource-object-model", "ADR-046-terminology-and-identities" ], - "specId": "ADR-046-nix-configuration", - "topologicalRank": 9, - "wave": "W5" + "specId" : "ADR-046-nix-configuration", + "topologicalRank" : 9, + "wave" : "W5" }, { - "blockers": [], - "destinations": [ + "blockers" : [], + "destinations" : [ "docs/specs/ADR-046-primitive-resource-composition.md" ], - "entryContracts": [ + "entryContracts" : [ "ADR-046-resource-object-model", "ADR-046-resource-reconciliation" ], - "exitGate": "ADR046-W2 exit criteria (ADR-046-validation-and-delivery §4): every spec/work item in this wave Merged with clean destinations, all validators green, and the ten-role panel seal recorded", - "id": "ADR-046-primitive-resource-composition", - "kind": "spec", - "owner": "ADR046-W2 wave (ADR-046-validation-and-delivery §3.2)", - "parallelGroup": "W2-composition-and-routing", - "prerequisites": [ + "exitGate" : "ADR046-W2 exit criteria (ADR-046-validation-and-delivery §4): every spec/work item in this wave Merged with clean destinations, all validators green, and the ten-role panel seal recorded", + "id" : "ADR-046-primitive-resource-composition", + "kind" : "spec", + "owner" : "ADR046-W2 wave (ADR-046-validation-and-delivery §3.2)", + "parallelGroup" : "W2-composition-and-routing", + "prerequisites" : [ "ADR-046-resource-object-model", "ADR-046-resource-reconciliation" ], - "specId": "ADR-046-primitive-resource-composition", - "topologicalRank": 6, - "wave": "W2" + "specId" : "ADR-046-primitive-resource-composition", + "topologicalRank" : 6, + "wave" : "W2" }, { - "blockers": [], - "destinations": [ + "blockers" : [], + "destinations" : [ "docs/specs/providers/ADR-046-provider-activation-nixos.md" ], - "entryContracts": [ + "entryContracts" : [ "ADR-046-cli-and-operations", "ADR-046-componentsession-and-bus", "ADR-046-core-controllers", @@ -8681,12 +9779,12 @@ "ADR-046-resources-zone-control", "ADR-046-telemetry-audit-and-support" ], - "exitGate": "ADR046-W6 exit criteria (ADR-046-validation-and-delivery §4): every spec/work item in this wave Merged with clean destinations, all validators green, and the ten-role panel seal recorded", - "id": "ADR-046-provider-activation-nixos", - "kind": "spec", - "owner": "ADR046-W6 wave (ADR-046-validation-and-delivery §3.2)", - "parallelGroup": "W6-transport-observability-activation", - "prerequisites": [ + "exitGate" : "ADR046-W6 exit criteria (ADR-046-validation-and-delivery §4): every spec/work item in this wave Merged with clean destinations, all validators green, and the ten-role panel seal recorded", + "id" : "ADR-046-provider-activation-nixos", + "kind" : "spec", + "owner" : "ADR046-W6 wave (ADR-046-validation-and-delivery §3.2)", + "parallelGroup" : "W6-transport-observability-activation", + "prerequisites" : [ "ADR-046-cli-and-operations", "ADR-046-componentsession-and-bus", "ADR-046-core-controllers", @@ -8699,16 +9797,16 @@ "ADR-046-resources-zone-control", "ADR-046-telemetry-audit-and-support" ], - "specId": "ADR-046-provider-activation-nixos", - "topologicalRank": 10, - "wave": "W6" + "specId" : "ADR-046-provider-activation-nixos", + "topologicalRank" : 10, + "wave" : "W6" }, { - "blockers": [], - "destinations": [ + "blockers" : [], + "destinations" : [ "docs/specs/providers/ADR-046-provider-audio-pipewire.md" ], - "entryContracts": [ + "entryContracts" : [ "ADR-046-components-processes-and-sandbox", "ADR-046-componentsession-and-bus", "ADR-046-nix-configuration", @@ -8722,12 +9820,12 @@ "ADR-046-resources-volume", "ADR-046-telemetry-audit-and-support" ], - "exitGate": "ADR046-W6 exit criteria (ADR-046-validation-and-delivery §4): every spec/work item in this wave Merged with clean destinations, all validators green, and the ten-role panel seal recorded", - "id": "ADR-046-provider-audio-pipewire", - "kind": "spec", - "owner": "ADR046-W6 wave (ADR-046-validation-and-delivery §3.2)", - "parallelGroup": "W6-interaction", - "prerequisites": [ + "exitGate" : "ADR046-W6 exit criteria (ADR-046-validation-and-delivery §4): every spec/work item in this wave Merged with clean destinations, all validators green, and the ten-role panel seal recorded", + "id" : "ADR-046-provider-audio-pipewire", + "kind" : "spec", + "owner" : "ADR046-W6 wave (ADR-046-validation-and-delivery §3.2)", + "parallelGroup" : "W6-interaction", + "prerequisites" : [ "ADR-046-components-processes-and-sandbox", "ADR-046-componentsession-and-bus", "ADR-046-nix-configuration", @@ -8741,16 +9839,16 @@ "ADR-046-resources-volume", "ADR-046-telemetry-audit-and-support" ], - "specId": "ADR-046-provider-audio-pipewire", - "topologicalRank": 10, - "wave": "W6" + "specId" : "ADR-046-provider-audio-pipewire", + "topologicalRank" : 10, + "wave" : "W6" }, { - "blockers": [], - "destinations": [ + "blockers" : [], + "destinations" : [ "docs/specs/providers/ADR-046-provider-clipboard-wayland.md" ], - "entryContracts": [ + "entryContracts" : [ "ADR-046-componentsession-and-bus", "ADR-046-core-controllers", "ADR-046-nix-configuration", @@ -8760,12 +9858,12 @@ "ADR-046-resources-host-guest-process-user", "ADR-046-telemetry-audit-and-support" ], - "exitGate": "ADR046-W6 exit criteria (ADR-046-validation-and-delivery §4): every spec/work item in this wave Merged with clean destinations, all validators green, and the ten-role panel seal recorded", - "id": "ADR-046-provider-clipboard-wayland", - "kind": "spec", - "owner": "ADR046-W6 wave (ADR-046-validation-and-delivery §3.2)", - "parallelGroup": "W6-interaction", - "prerequisites": [ + "exitGate" : "ADR046-W6 exit criteria (ADR-046-validation-and-delivery §4): every spec/work item in this wave Merged with clean destinations, all validators green, and the ten-role panel seal recorded", + "id" : "ADR-046-provider-clipboard-wayland", + "kind" : "spec", + "owner" : "ADR046-W6 wave (ADR-046-validation-and-delivery §3.2)", + "parallelGroup" : "W6-interaction", + "prerequisites" : [ "ADR-046-componentsession-and-bus", "ADR-046-core-controllers", "ADR-046-nix-configuration", @@ -8775,16 +9873,16 @@ "ADR-046-resources-host-guest-process-user", "ADR-046-telemetry-audit-and-support" ], - "specId": "ADR-046-provider-clipboard-wayland", - "topologicalRank": 10, - "wave": "W6" + "specId" : "ADR-046-provider-clipboard-wayland", + "topologicalRank" : 10, + "wave" : "W6" }, { - "blockers": [], - "destinations": [ + "blockers" : [], + "destinations" : [ "docs/specs/providers/ADR-046-provider-credential-entra.md" ], - "entryContracts": [ + "entryContracts" : [ "ADR-046-componentsession-and-bus", "ADR-046-nix-configuration", "ADR-046-provider-model-and-packaging", @@ -8793,12 +9891,12 @@ "ADR-046-resources-host-guest-process-user", "ADR-046-telemetry-audit-and-support" ], - "exitGate": "ADR046-W6 exit criteria (ADR-046-validation-and-delivery §4): every spec/work item in this wave Merged with clean destinations, all validators green, and the ten-role panel seal recorded", - "id": "ADR-046-provider-credential-entra", - "kind": "spec", - "owner": "ADR046-W6 wave (ADR-046-validation-and-delivery §3.2)", - "parallelGroup": "W6-credentials", - "prerequisites": [ + "exitGate" : "ADR046-W6 exit criteria (ADR-046-validation-and-delivery §4): every spec/work item in this wave Merged with clean destinations, all validators green, and the ten-role panel seal recorded", + "id" : "ADR-046-provider-credential-entra", + "kind" : "spec", + "owner" : "ADR046-W6 wave (ADR-046-validation-and-delivery §3.2)", + "parallelGroup" : "W6-credentials", + "prerequisites" : [ "ADR-046-componentsession-and-bus", "ADR-046-nix-configuration", "ADR-046-provider-model-and-packaging", @@ -8807,16 +9905,16 @@ "ADR-046-resources-host-guest-process-user", "ADR-046-telemetry-audit-and-support" ], - "specId": "ADR-046-provider-credential-entra", - "topologicalRank": 10, - "wave": "W6" + "specId" : "ADR-046-provider-credential-entra", + "topologicalRank" : 10, + "wave" : "W6" }, { - "blockers": [], - "destinations": [ + "blockers" : [], + "destinations" : [ "docs/specs/providers/ADR-046-provider-credential-managed-identity.md" ], - "entryContracts": [ + "entryContracts" : [ "ADR-046-componentsession-and-bus", "ADR-046-nix-configuration", "ADR-046-provider-model-and-packaging", @@ -8824,12 +9922,12 @@ "ADR-046-resources-credential", "ADR-046-telemetry-audit-and-support" ], - "exitGate": "ADR046-W6 exit criteria (ADR-046-validation-and-delivery §4): every spec/work item in this wave Merged with clean destinations, all validators green, and the ten-role panel seal recorded", - "id": "ADR-046-provider-credential-managed-identity", - "kind": "spec", - "owner": "ADR046-W6 wave (ADR-046-validation-and-delivery §3.2)", - "parallelGroup": "W6-credentials", - "prerequisites": [ + "exitGate" : "ADR046-W6 exit criteria (ADR-046-validation-and-delivery §4): every spec/work item in this wave Merged with clean destinations, all validators green, and the ten-role panel seal recorded", + "id" : "ADR-046-provider-credential-managed-identity", + "kind" : "spec", + "owner" : "ADR046-W6 wave (ADR-046-validation-and-delivery §3.2)", + "parallelGroup" : "W6-credentials", + "prerequisites" : [ "ADR-046-componentsession-and-bus", "ADR-046-nix-configuration", "ADR-046-provider-model-and-packaging", @@ -8837,16 +9935,16 @@ "ADR-046-resources-credential", "ADR-046-telemetry-audit-and-support" ], - "specId": "ADR-046-provider-credential-managed-identity", - "topologicalRank": 10, - "wave": "W6" + "specId" : "ADR-046-provider-credential-managed-identity", + "topologicalRank" : 10, + "wave" : "W6" }, { - "blockers": [], - "destinations": [ + "blockers" : [], + "destinations" : [ "docs/specs/providers/ADR-046-provider-credential-secret-service.md" ], - "entryContracts": [ + "entryContracts" : [ "ADR-046-components-processes-and-sandbox", "ADR-046-componentsession-and-bus", "ADR-046-nix-configuration", @@ -8856,12 +9954,12 @@ "ADR-046-resources-credential", "ADR-046-telemetry-audit-and-support" ], - "exitGate": "ADR046-W6 exit criteria (ADR-046-validation-and-delivery §4): every spec/work item in this wave Merged with clean destinations, all validators green, and the ten-role panel seal recorded", - "id": "ADR-046-provider-credential-secret-service", - "kind": "spec", - "owner": "ADR046-W6 wave (ADR-046-validation-and-delivery §3.2)", - "parallelGroup": "W6-credentials", - "prerequisites": [ + "exitGate" : "ADR046-W6 exit criteria (ADR-046-validation-and-delivery §4): every spec/work item in this wave Merged with clean destinations, all validators green, and the ten-role panel seal recorded", + "id" : "ADR-046-provider-credential-secret-service", + "kind" : "spec", + "owner" : "ADR046-W6 wave (ADR-046-validation-and-delivery §3.2)", + "parallelGroup" : "W6-credentials", + "prerequisites" : [ "ADR-046-components-processes-and-sandbox", "ADR-046-componentsession-and-bus", "ADR-046-nix-configuration", @@ -8871,16 +9969,16 @@ "ADR-046-resources-credential", "ADR-046-telemetry-audit-and-support" ], - "specId": "ADR-046-provider-credential-secret-service", - "topologicalRank": 10, - "wave": "W6" + "specId" : "ADR-046-provider-credential-secret-service", + "topologicalRank" : 10, + "wave" : "W6" }, { - "blockers": [], - "destinations": [ + "blockers" : [], + "destinations" : [ "docs/specs/providers/ADR-046-provider-device-gpu.md" ], - "entryContracts": [ + "entryContracts" : [ "ADR-046-components-processes-and-sandbox", "ADR-046-primitive-resource-composition", "ADR-046-provider-model-and-packaging", @@ -8890,12 +9988,12 @@ "ADR-046-resources-device", "ADR-046-telemetry-audit-and-support" ], - "exitGate": "ADR046-W6 exit criteria (ADR-046-validation-and-delivery §4): every spec/work item in this wave Merged with clean destinations, all validators green, and the ten-role panel seal recorded", - "id": "ADR-046-provider-device-gpu", - "kind": "spec", - "owner": "ADR046-W6 wave (ADR-046-validation-and-delivery §3.2)", - "parallelGroup": "W6-storage-network-device", - "prerequisites": [ + "exitGate" : "ADR046-W6 exit criteria (ADR-046-validation-and-delivery §4): every spec/work item in this wave Merged with clean destinations, all validators green, and the ten-role panel seal recorded", + "id" : "ADR-046-provider-device-gpu", + "kind" : "spec", + "owner" : "ADR046-W6 wave (ADR-046-validation-and-delivery §3.2)", + "parallelGroup" : "W6-storage-network-device", + "prerequisites" : [ "ADR-046-components-processes-and-sandbox", "ADR-046-primitive-resource-composition", "ADR-046-provider-model-and-packaging", @@ -8905,16 +10003,16 @@ "ADR-046-resources-device", "ADR-046-telemetry-audit-and-support" ], - "specId": "ADR-046-provider-device-gpu", - "topologicalRank": 10, - "wave": "W6" + "specId" : "ADR-046-provider-device-gpu", + "topologicalRank" : 10, + "wave" : "W6" }, { - "blockers": [], - "destinations": [ + "blockers" : [], + "destinations" : [ "docs/specs/providers/ADR-046-provider-device-security-key.md" ], - "entryContracts": [ + "entryContracts" : [ "ADR-046-components-processes-and-sandbox", "ADR-046-componentsession-and-bus", "ADR-046-nix-configuration", @@ -8927,12 +10025,12 @@ "ADR-046-resources-device", "ADR-046-telemetry-audit-and-support" ], - "exitGate": "ADR046-W6 exit criteria (ADR-046-validation-and-delivery §4): every spec/work item in this wave Merged with clean destinations, all validators green, and the ten-role panel seal recorded", - "id": "ADR-046-provider-device-security-key", - "kind": "spec", - "owner": "ADR046-W6 wave (ADR-046-validation-and-delivery §3.2)", - "parallelGroup": "W6-storage-network-device", - "prerequisites": [ + "exitGate" : "ADR046-W6 exit criteria (ADR-046-validation-and-delivery §4): every spec/work item in this wave Merged with clean destinations, all validators green, and the ten-role panel seal recorded", + "id" : "ADR-046-provider-device-security-key", + "kind" : "spec", + "owner" : "ADR046-W6 wave (ADR-046-validation-and-delivery §3.2)", + "parallelGroup" : "W6-storage-network-device", + "prerequisites" : [ "ADR-046-components-processes-and-sandbox", "ADR-046-componentsession-and-bus", "ADR-046-nix-configuration", @@ -8945,16 +10043,16 @@ "ADR-046-resources-device", "ADR-046-telemetry-audit-and-support" ], - "specId": "ADR-046-provider-device-security-key", - "topologicalRank": 10, - "wave": "W6" + "specId" : "ADR-046-provider-device-security-key", + "topologicalRank" : 10, + "wave" : "W6" }, { - "blockers": [], - "destinations": [ + "blockers" : [], + "destinations" : [ "docs/specs/providers/ADR-046-provider-device-tpm.md" ], - "entryContracts": [ + "entryContracts" : [ "ADR-046-primitive-resource-composition", "ADR-046-provider-model-and-packaging", "ADR-046-provider-state", @@ -8964,12 +10062,12 @@ "ADR-046-resources-host-guest-process-user", "ADR-046-resources-volume" ], - "exitGate": "ADR046-W6 exit criteria (ADR-046-validation-and-delivery §4): every spec/work item in this wave Merged with clean destinations, all validators green, and the ten-role panel seal recorded", - "id": "ADR-046-provider-device-tpm", - "kind": "spec", - "owner": "ADR046-W6 wave (ADR-046-validation-and-delivery §3.2)", - "parallelGroup": "W6-storage-network-device", - "prerequisites": [ + "exitGate" : "ADR046-W6 exit criteria (ADR-046-validation-and-delivery §4): every spec/work item in this wave Merged with clean destinations, all validators green, and the ten-role panel seal recorded", + "id" : "ADR-046-provider-device-tpm", + "kind" : "spec", + "owner" : "ADR046-W6 wave (ADR-046-validation-and-delivery §3.2)", + "parallelGroup" : "W6-storage-network-device", + "prerequisites" : [ "ADR-046-primitive-resource-composition", "ADR-046-provider-model-and-packaging", "ADR-046-provider-state", @@ -8979,16 +10077,16 @@ "ADR-046-resources-host-guest-process-user", "ADR-046-resources-volume" ], - "specId": "ADR-046-provider-device-tpm", - "topologicalRank": 10, - "wave": "W6" + "specId" : "ADR-046-provider-device-tpm", + "topologicalRank" : 10, + "wave" : "W6" }, { - "blockers": [], - "destinations": [ + "blockers" : [], + "destinations" : [ "docs/specs/providers/ADR-046-provider-device-usbip.md" ], - "entryContracts": [ + "entryContracts" : [ "ADR-046-components-processes-and-sandbox", "ADR-046-componentsession-and-bus", "ADR-046-nix-configuration", @@ -9002,12 +10100,12 @@ "ADR-046-telemetry-audit-and-support", "ADR-046-zone-routing" ], - "exitGate": "ADR046-W6 exit criteria (ADR-046-validation-and-delivery §4): every spec/work item in this wave Merged with clean destinations, all validators green, and the ten-role panel seal recorded", - "id": "ADR-046-provider-device-usbip", - "kind": "spec", - "owner": "ADR046-W6 wave (ADR-046-validation-and-delivery §3.2)", - "parallelGroup": "W6-storage-network-device", - "prerequisites": [ + "exitGate" : "ADR046-W6 exit criteria (ADR-046-validation-and-delivery §4): every spec/work item in this wave Merged with clean destinations, all validators green, and the ten-role panel seal recorded", + "id" : "ADR-046-provider-device-usbip", + "kind" : "spec", + "owner" : "ADR046-W6 wave (ADR-046-validation-and-delivery §3.2)", + "parallelGroup" : "W6-storage-network-device", + "prerequisites" : [ "ADR-046-components-processes-and-sandbox", "ADR-046-componentsession-and-bus", "ADR-046-nix-configuration", @@ -9021,16 +10119,16 @@ "ADR-046-telemetry-audit-and-support", "ADR-046-zone-routing" ], - "specId": "ADR-046-provider-device-usbip", - "topologicalRank": 10, - "wave": "W6" + "specId" : "ADR-046-provider-device-usbip", + "topologicalRank" : 10, + "wave" : "W6" }, { - "blockers": [], - "destinations": [ + "blockers" : [], + "destinations" : [ "docs/specs/providers/ADR-046-provider-display-wayland.md" ], - "entryContracts": [ + "entryContracts" : [ "ADR-046-components-processes-and-sandbox", "ADR-046-componentsession-and-bus", "ADR-046-nix-configuration", @@ -9042,12 +10140,12 @@ "ADR-046-resources-volume", "ADR-046-telemetry-audit-and-support" ], - "exitGate": "ADR046-W6 exit criteria (ADR-046-validation-and-delivery §4): every spec/work item in this wave Merged with clean destinations, all validators green, and the ten-role panel seal recorded", - "id": "ADR-046-provider-display-wayland", - "kind": "spec", - "owner": "ADR046-W6 wave (ADR-046-validation-and-delivery §3.2)", - "parallelGroup": "W6-interaction", - "prerequisites": [ + "exitGate" : "ADR046-W6 exit criteria (ADR-046-validation-and-delivery §4): every spec/work item in this wave Merged with clean destinations, all validators green, and the ten-role panel seal recorded", + "id" : "ADR-046-provider-display-wayland", + "kind" : "spec", + "owner" : "ADR046-W6 wave (ADR-046-validation-and-delivery §3.2)", + "parallelGroup" : "W6-interaction", + "prerequisites" : [ "ADR-046-components-processes-and-sandbox", "ADR-046-componentsession-and-bus", "ADR-046-nix-configuration", @@ -9059,42 +10157,42 @@ "ADR-046-resources-volume", "ADR-046-telemetry-audit-and-support" ], - "specId": "ADR-046-provider-display-wayland", - "topologicalRank": 10, - "wave": "W6" + "specId" : "ADR-046-provider-display-wayland", + "topologicalRank" : 10, + "wave" : "W6" }, { - "blockers": [], - "destinations": [ + "blockers" : [], + "destinations" : [ "docs/specs/ADR-046-provider-model-and-packaging.md" ], - "entryContracts": [ + "entryContracts" : [ "ADR-046-primitive-resource-composition", "ADR-046-resource-api-and-authorization", "ADR-046-resource-object-model", "ADR-046-resource-reconciliation" ], - "exitGate": "ADR046-W3 exit criteria (ADR-046-validation-and-delivery §4): every spec/work item in this wave Merged with clean destinations, all validators green, and the ten-role panel seal recorded", - "id": "ADR-046-provider-model-and-packaging", - "kind": "spec", - "owner": "ADR046-W3 wave (ADR-046-validation-and-delivery §3.2)", - "parallelGroup": "W3-provider-contract", - "prerequisites": [ + "exitGate" : "ADR046-W3 exit criteria (ADR-046-validation-and-delivery §4): every spec/work item in this wave Merged with clean destinations, all validators green, and the ten-role panel seal recorded", + "id" : "ADR-046-provider-model-and-packaging", + "kind" : "spec", + "owner" : "ADR046-W3 wave (ADR-046-validation-and-delivery §3.2)", + "parallelGroup" : "W3-provider-contract", + "prerequisites" : [ "ADR-046-primitive-resource-composition", "ADR-046-resource-api-and-authorization", "ADR-046-resource-object-model", "ADR-046-resource-reconciliation" ], - "specId": "ADR-046-provider-model-and-packaging", - "topologicalRank": 7, - "wave": "W3" + "specId" : "ADR-046-provider-model-and-packaging", + "topologicalRank" : 7, + "wave" : "W3" }, { - "blockers": [], - "destinations": [ + "blockers" : [], + "destinations" : [ "docs/specs/providers/ADR-046-provider-network-local.md" ], - "entryContracts": [ + "entryContracts" : [ "ADR-046-componentsession-and-bus", "ADR-046-current-code-migration-map", "ADR-046-nix-configuration", @@ -9105,12 +10203,12 @@ "ADR-046-resources-volume", "ADR-046-telemetry-audit-and-support" ], - "exitGate": "ADR046-W6 exit criteria (ADR-046-validation-and-delivery §4): every spec/work item in this wave Merged with clean destinations, all validators green, and the ten-role panel seal recorded", - "id": "ADR-046-provider-network-local", - "kind": "spec", - "owner": "ADR046-W6 wave (ADR-046-validation-and-delivery §3.2)", - "parallelGroup": "W6-storage-network-device", - "prerequisites": [ + "exitGate" : "ADR046-W6 exit criteria (ADR-046-validation-and-delivery §4): every spec/work item in this wave Merged with clean destinations, all validators green, and the ten-role panel seal recorded", + "id" : "ADR-046-provider-network-local", + "kind" : "spec", + "owner" : "ADR046-W6 wave (ADR-046-validation-and-delivery §3.2)", + "parallelGroup" : "W6-storage-network-device", + "prerequisites" : [ "ADR-046-componentsession-and-bus", "ADR-046-current-code-migration-map", "ADR-046-nix-configuration", @@ -9121,16 +10219,16 @@ "ADR-046-resources-volume", "ADR-046-telemetry-audit-and-support" ], - "specId": "ADR-046-provider-network-local", - "topologicalRank": 10, - "wave": "W6" + "specId" : "ADR-046-provider-network-local", + "topologicalRank" : 10, + "wave" : "W6" }, { - "blockers": [], - "destinations": [ + "blockers" : [], + "destinations" : [ "docs/specs/providers/ADR-046-provider-notification-desktop.md" ], - "entryContracts": [ + "entryContracts" : [ "ADR-046-componentsession-and-bus", "ADR-046-nix-configuration", "ADR-046-primitive-resource-composition", @@ -9139,12 +10237,12 @@ "ADR-046-resource-reconciliation", "ADR-046-telemetry-audit-and-support" ], - "exitGate": "ADR046-W6 exit criteria (ADR-046-validation-and-delivery §4): every spec/work item in this wave Merged with clean destinations, all validators green, and the ten-role panel seal recorded", - "id": "ADR-046-provider-notification-desktop", - "kind": "spec", - "owner": "ADR046-W6 wave (ADR-046-validation-and-delivery §3.2)", - "parallelGroup": "W6-interaction", - "prerequisites": [ + "exitGate" : "ADR046-W6 exit criteria (ADR-046-validation-and-delivery §4): every spec/work item in this wave Merged with clean destinations, all validators green, and the ten-role panel seal recorded", + "id" : "ADR-046-provider-notification-desktop", + "kind" : "spec", + "owner" : "ADR046-W6 wave (ADR-046-validation-and-delivery §3.2)", + "parallelGroup" : "W6-interaction", + "prerequisites" : [ "ADR-046-componentsession-and-bus", "ADR-046-nix-configuration", "ADR-046-primitive-resource-composition", @@ -9153,16 +10251,16 @@ "ADR-046-resource-reconciliation", "ADR-046-telemetry-audit-and-support" ], - "specId": "ADR-046-provider-notification-desktop", - "topologicalRank": 10, - "wave": "W6" + "specId" : "ADR-046-provider-notification-desktop", + "topologicalRank" : 10, + "wave" : "W6" }, { - "blockers": [], - "destinations": [ + "blockers" : [], + "destinations" : [ "docs/specs/providers/ADR-046-provider-observability-otel.md" ], - "entryContracts": [ + "entryContracts" : [ "ADR-046-components-processes-and-sandbox", "ADR-046-componentsession-and-bus", "ADR-046-nix-configuration", @@ -9176,12 +10274,12 @@ "ADR-046-telemetry-audit-and-support", "ADR-046-terminology-and-identities" ], - "exitGate": "ADR046-W6 exit criteria (ADR-046-validation-and-delivery §4): every spec/work item in this wave Merged with clean destinations, all validators green, and the ten-role panel seal recorded", - "id": "ADR-046-provider-observability-otel", - "kind": "spec", - "owner": "ADR046-W6 wave (ADR-046-validation-and-delivery §3.2)", - "parallelGroup": "W6-transport-observability-activation", - "prerequisites": [ + "exitGate" : "ADR046-W6 exit criteria (ADR-046-validation-and-delivery §4): every spec/work item in this wave Merged with clean destinations, all validators green, and the ten-role panel seal recorded", + "id" : "ADR-046-provider-observability-otel", + "kind" : "spec", + "owner" : "ADR046-W6 wave (ADR-046-validation-and-delivery §3.2)", + "parallelGroup" : "W6-transport-observability-activation", + "prerequisites" : [ "ADR-046-components-processes-and-sandbox", "ADR-046-componentsession-and-bus", "ADR-046-nix-configuration", @@ -9195,16 +10293,16 @@ "ADR-046-telemetry-audit-and-support", "ADR-046-terminology-and-identities" ], - "specId": "ADR-046-provider-observability-otel", - "topologicalRank": 10, - "wave": "W6" + "specId" : "ADR-046-provider-observability-otel", + "topologicalRank" : 10, + "wave" : "W6" }, { - "blockers": [], - "destinations": [ + "blockers" : [], + "destinations" : [ "docs/specs/providers/ADR-046-provider-runtime-azure-container-apps.md" ], - "entryContracts": [ + "entryContracts" : [ "ADR-046-componentsession-and-bus", "ADR-046-nix-configuration", "ADR-046-provider-model-and-packaging", @@ -9214,12 +10312,12 @@ "ADR-046-telemetry-audit-and-support", "ADR-046-zone-routing" ], - "exitGate": "ADR046-W6 exit criteria (ADR-046-validation-and-delivery §4): every spec/work item in this wave Merged with clean destinations, all validators green, and the ten-role panel seal recorded", - "id": "ADR-046-provider-runtime-azure-container-apps", - "kind": "spec", - "owner": "ADR046-W6 wave (ADR-046-validation-and-delivery §3.2)", - "parallelGroup": "W6-system-host-guest", - "prerequisites": [ + "exitGate" : "ADR046-W6 exit criteria (ADR-046-validation-and-delivery §4): every spec/work item in this wave Merged with clean destinations, all validators green, and the ten-role panel seal recorded", + "id" : "ADR-046-provider-runtime-azure-container-apps", + "kind" : "spec", + "owner" : "ADR046-W6 wave (ADR-046-validation-and-delivery §3.2)", + "parallelGroup" : "W6-system-host-guest", + "prerequisites" : [ "ADR-046-componentsession-and-bus", "ADR-046-nix-configuration", "ADR-046-provider-model-and-packaging", @@ -9229,16 +10327,16 @@ "ADR-046-telemetry-audit-and-support", "ADR-046-zone-routing" ], - "specId": "ADR-046-provider-runtime-azure-container-apps", - "topologicalRank": 10, - "wave": "W6" + "specId" : "ADR-046-provider-runtime-azure-container-apps", + "topologicalRank" : 10, + "wave" : "W6" }, { - "blockers": [], - "destinations": [ + "blockers" : [], + "destinations" : [ "docs/specs/providers/ADR-046-provider-runtime-azure-virtual-machine.md" ], - "entryContracts": [ + "entryContracts" : [ "ADR-046-components-processes-and-sandbox", "ADR-046-componentsession-and-bus", "ADR-046-nix-configuration", @@ -9255,12 +10353,12 @@ "ADR-046-terminology-and-identities", "ADR-046-zone-routing" ], - "exitGate": "ADR046-W6 exit criteria (ADR-046-validation-and-delivery §4): every spec/work item in this wave Merged with clean destinations, all validators green, and the ten-role panel seal recorded", - "id": "ADR-046-provider-runtime-azure-virtual-machine", - "kind": "spec", - "owner": "ADR046-W6 wave (ADR-046-validation-and-delivery §3.2)", - "parallelGroup": "W6-system-host-guest", - "prerequisites": [ + "exitGate" : "ADR046-W6 exit criteria (ADR-046-validation-and-delivery §4): every spec/work item in this wave Merged with clean destinations, all validators green, and the ten-role panel seal recorded", + "id" : "ADR-046-provider-runtime-azure-virtual-machine", + "kind" : "spec", + "owner" : "ADR046-W6 wave (ADR-046-validation-and-delivery §3.2)", + "parallelGroup" : "W6-system-host-guest", + "prerequisites" : [ "ADR-046-components-processes-and-sandbox", "ADR-046-componentsession-and-bus", "ADR-046-nix-configuration", @@ -9277,16 +10375,16 @@ "ADR-046-terminology-and-identities", "ADR-046-zone-routing" ], - "specId": "ADR-046-provider-runtime-azure-virtual-machine", - "topologicalRank": 10, - "wave": "W6" + "specId" : "ADR-046-provider-runtime-azure-virtual-machine", + "topologicalRank" : 10, + "wave" : "W6" }, { - "blockers": [], - "destinations": [ + "blockers" : [], + "destinations" : [ "docs/specs/providers/ADR-046-provider-runtime-cloud-hypervisor.md" ], - "entryContracts": [ + "entryContracts" : [ "ADR-046-components-processes-and-sandbox", "ADR-046-componentsession-and-bus", "ADR-046-nix-configuration", @@ -9301,12 +10399,12 @@ "ADR-046-resources-volume", "ADR-046-telemetry-audit-and-support" ], - "exitGate": "ADR046-W6 exit criteria (ADR-046-validation-and-delivery §4): every spec/work item in this wave Merged with clean destinations, all validators green, and the ten-role panel seal recorded", - "id": "ADR-046-provider-runtime-cloud-hypervisor", - "kind": "spec", - "owner": "ADR046-W6 wave (ADR-046-validation-and-delivery §3.2)", - "parallelGroup": "W6-system-host-guest", - "prerequisites": [ + "exitGate" : "ADR046-W6 exit criteria (ADR-046-validation-and-delivery §4): every spec/work item in this wave Merged with clean destinations, all validators green, and the ten-role panel seal recorded", + "id" : "ADR-046-provider-runtime-cloud-hypervisor", + "kind" : "spec", + "owner" : "ADR046-W6 wave (ADR-046-validation-and-delivery §3.2)", + "parallelGroup" : "W6-system-host-guest", + "prerequisites" : [ "ADR-046-components-processes-and-sandbox", "ADR-046-componentsession-and-bus", "ADR-046-nix-configuration", @@ -9321,16 +10419,16 @@ "ADR-046-resources-volume", "ADR-046-telemetry-audit-and-support" ], - "specId": "ADR-046-provider-runtime-cloud-hypervisor", - "topologicalRank": 10, - "wave": "W6" + "specId" : "ADR-046-provider-runtime-cloud-hypervisor", + "topologicalRank" : 10, + "wave" : "W6" }, { - "blockers": [], - "destinations": [ + "blockers" : [], + "destinations" : [ "docs/specs/providers/ADR-046-provider-runtime-qemu-media.md" ], - "entryContracts": [ + "entryContracts" : [ "ADR-046-components-processes-and-sandbox", "ADR-046-componentsession-and-bus", "ADR-046-current-code-migration-map", @@ -9343,12 +10441,12 @@ "ADR-046-resources-volume", "ADR-046-telemetry-audit-and-support" ], - "exitGate": "ADR046-W6 exit criteria (ADR-046-validation-and-delivery §4): every spec/work item in this wave Merged with clean destinations, all validators green, and the ten-role panel seal recorded", - "id": "ADR-046-provider-runtime-qemu-media", - "kind": "spec", - "owner": "ADR046-W6 wave (ADR-046-validation-and-delivery §3.2)", - "parallelGroup": "W6-system-host-guest", - "prerequisites": [ + "exitGate" : "ADR046-W6 exit criteria (ADR-046-validation-and-delivery §4): every spec/work item in this wave Merged with clean destinations, all validators green, and the ten-role panel seal recorded", + "id" : "ADR-046-provider-runtime-qemu-media", + "kind" : "spec", + "owner" : "ADR046-W6 wave (ADR-046-validation-and-delivery §3.2)", + "parallelGroup" : "W6-system-host-guest", + "prerequisites" : [ "ADR-046-components-processes-and-sandbox", "ADR-046-componentsession-and-bus", "ADR-046-current-code-migration-map", @@ -9361,16 +10459,16 @@ "ADR-046-resources-volume", "ADR-046-telemetry-audit-and-support" ], - "specId": "ADR-046-provider-runtime-qemu-media", - "topologicalRank": 10, - "wave": "W6" + "specId" : "ADR-046-provider-runtime-qemu-media", + "topologicalRank" : 10, + "wave" : "W6" }, { - "blockers": [], - "destinations": [ + "blockers" : [], + "destinations" : [ "docs/specs/providers/ADR-046-provider-shell-terminal.md" ], - "entryContracts": [ + "entryContracts" : [ "ADR-046-components-processes-and-sandbox", "ADR-046-componentsession-and-bus", "ADR-046-core-controllers", @@ -9388,12 +10486,12 @@ "ADR-046-terminology-and-identities", "ADR-046-zone-routing" ], - "exitGate": "ADR046-W6 exit criteria (ADR-046-validation-and-delivery §4): every spec/work item in this wave Merged with clean destinations, all validators green, and the ten-role panel seal recorded", - "id": "ADR-046-provider-shell-terminal", - "kind": "spec", - "owner": "ADR046-W6 wave (ADR-046-validation-and-delivery §3.2)", - "parallelGroup": "W6-interaction", - "prerequisites": [ + "exitGate" : "ADR046-W6 exit criteria (ADR-046-validation-and-delivery §4): every spec/work item in this wave Merged with clean destinations, all validators green, and the ten-role panel seal recorded", + "id" : "ADR-046-provider-shell-terminal", + "kind" : "spec", + "owner" : "ADR046-W6 wave (ADR-046-validation-and-delivery §3.2)", + "parallelGroup" : "W6-interaction", + "prerequisites" : [ "ADR-046-components-processes-and-sandbox", "ADR-046-componentsession-and-bus", "ADR-046-core-controllers", @@ -9411,16 +10509,16 @@ "ADR-046-terminology-and-identities", "ADR-046-zone-routing" ], - "specId": "ADR-046-provider-shell-terminal", - "topologicalRank": 10, - "wave": "W6" + "specId" : "ADR-046-provider-shell-terminal", + "topologicalRank" : 10, + "wave" : "W6" }, { - "blockers": [], - "destinations": [ + "blockers" : [], + "destinations" : [ "docs/specs/ADR-046-provider-state.md" ], - "entryContracts": [ + "entryContracts" : [ "ADR-046-componentsession-and-bus", "ADR-046-primitive-resource-composition", "ADR-046-provider-model-and-packaging", @@ -9428,12 +10526,12 @@ "ADR-046-resource-store-redb", "ADR-046-terminology-and-identities" ], - "exitGate": "ADR046-W4 exit criteria (ADR-046-validation-and-delivery §4): every spec/work item in this wave Merged with clean destinations, all validators green, and the ten-role panel seal recorded", - "id": "ADR-046-provider-state", - "kind": "spec", - "owner": "ADR046-W4 wave (ADR-046-validation-and-delivery §3.2)", - "parallelGroup": "W4-parallel-specs", - "prerequisites": [ + "exitGate" : "ADR046-W4 exit criteria (ADR-046-validation-and-delivery §4): every spec/work item in this wave Merged with clean destinations, all validators green, and the ten-role panel seal recorded", + "id" : "ADR-046-provider-state", + "kind" : "spec", + "owner" : "ADR046-W4 wave (ADR-046-validation-and-delivery §3.2)", + "parallelGroup" : "W4-parallel-specs", + "prerequisites" : [ "ADR-046-componentsession-and-bus", "ADR-046-primitive-resource-composition", "ADR-046-provider-model-and-packaging", @@ -9441,16 +10539,16 @@ "ADR-046-resource-store-redb", "ADR-046-terminology-and-identities" ], - "specId": "ADR-046-provider-state", - "topologicalRank": 8, - "wave": "W4" + "specId" : "ADR-046-provider-state", + "topologicalRank" : 8, + "wave" : "W4" }, { - "blockers": [], - "destinations": [ + "blockers" : [], + "destinations" : [ "docs/specs/providers/ADR-046-provider-system-core.md" ], - "entryContracts": [ + "entryContracts" : [ "ADR-046-componentsession-and-bus", "ADR-046-core-controllers", "ADR-046-current-code-migration-map", @@ -9460,12 +10558,12 @@ "ADR-046-resources-zone-control", "ADR-046-telemetry-audit-and-support" ], - "exitGate": "ADR046-W6 exit criteria (ADR-046-validation-and-delivery §4): every spec/work item in this wave Merged with clean destinations, all validators green, and the ten-role panel seal recorded", - "id": "ADR-046-provider-system-core", - "kind": "spec", - "owner": "ADR046-W6 wave (ADR-046-validation-and-delivery §3.2)", - "parallelGroup": "W6-system-host-guest", - "prerequisites": [ + "exitGate" : "ADR046-W6 exit criteria (ADR-046-validation-and-delivery §4): every spec/work item in this wave Merged with clean destinations, all validators green, and the ten-role panel seal recorded", + "id" : "ADR-046-provider-system-core", + "kind" : "spec", + "owner" : "ADR046-W6 wave (ADR-046-validation-and-delivery §3.2)", + "parallelGroup" : "W6-system-host-guest", + "prerequisites" : [ "ADR-046-componentsession-and-bus", "ADR-046-core-controllers", "ADR-046-current-code-migration-map", @@ -9475,16 +10573,16 @@ "ADR-046-resources-zone-control", "ADR-046-telemetry-audit-and-support" ], - "specId": "ADR-046-provider-system-core", - "topologicalRank": 10, - "wave": "W6" + "specId" : "ADR-046-provider-system-core", + "topologicalRank" : 10, + "wave" : "W6" }, { - "blockers": [], - "destinations": [ + "blockers" : [], + "destinations" : [ "docs/specs/providers/ADR-046-provider-system-minijail.md" ], - "entryContracts": [ + "entryContracts" : [ "ADR-046-components-processes-and-sandbox", "ADR-046-componentsession-and-bus", "ADR-046-core-controllers", @@ -9498,12 +10596,12 @@ "ADR-046-telemetry-audit-and-support", "ADR-046-terminology-and-identities" ], - "exitGate": "ADR046-W6 exit criteria (ADR-046-validation-and-delivery §4): every spec/work item in this wave Merged with clean destinations, all validators green, and the ten-role panel seal recorded", - "id": "ADR-046-provider-system-minijail", - "kind": "spec", - "owner": "ADR046-W6 wave (ADR-046-validation-and-delivery §3.2)", - "parallelGroup": "W6-system-host-guest", - "prerequisites": [ + "exitGate" : "ADR046-W6 exit criteria (ADR-046-validation-and-delivery §4): every spec/work item in this wave Merged with clean destinations, all validators green, and the ten-role panel seal recorded", + "id" : "ADR-046-provider-system-minijail", + "kind" : "spec", + "owner" : "ADR046-W6 wave (ADR-046-validation-and-delivery §3.2)", + "parallelGroup" : "W6-system-host-guest", + "prerequisites" : [ "ADR-046-components-processes-and-sandbox", "ADR-046-componentsession-and-bus", "ADR-046-core-controllers", @@ -9517,16 +10615,16 @@ "ADR-046-telemetry-audit-and-support", "ADR-046-terminology-and-identities" ], - "specId": "ADR-046-provider-system-minijail", - "topologicalRank": 10, - "wave": "W6" + "specId" : "ADR-046-provider-system-minijail", + "topologicalRank" : 10, + "wave" : "W6" }, { - "blockers": [], - "destinations": [ + "blockers" : [], + "destinations" : [ "docs/specs/providers/ADR-046-provider-system-systemd.md" ], - "entryContracts": [ + "entryContracts" : [ "ADR-046-components-processes-and-sandbox", "ADR-046-componentsession-and-bus", "ADR-046-current-code-migration-map", @@ -9535,12 +10633,12 @@ "ADR-046-resources-host-guest-process-user", "ADR-046-telemetry-audit-and-support" ], - "exitGate": "ADR046-W6 exit criteria (ADR-046-validation-and-delivery §4): every spec/work item in this wave Merged with clean destinations, all validators green, and the ten-role panel seal recorded", - "id": "ADR-046-provider-system-systemd", - "kind": "spec", - "owner": "ADR046-W6 wave (ADR-046-validation-and-delivery §3.2)", - "parallelGroup": "W6-system-host-guest", - "prerequisites": [ + "exitGate" : "ADR046-W6 exit criteria (ADR-046-validation-and-delivery §4): every spec/work item in this wave Merged with clean destinations, all validators green, and the ten-role panel seal recorded", + "id" : "ADR-046-provider-system-systemd", + "kind" : "spec", + "owner" : "ADR046-W6 wave (ADR-046-validation-and-delivery §3.2)", + "parallelGroup" : "W6-system-host-guest", + "prerequisites" : [ "ADR-046-components-processes-and-sandbox", "ADR-046-componentsession-and-bus", "ADR-046-current-code-migration-map", @@ -9549,16 +10647,16 @@ "ADR-046-resources-host-guest-process-user", "ADR-046-telemetry-audit-and-support" ], - "specId": "ADR-046-provider-system-systemd", - "topologicalRank": 10, - "wave": "W6" + "specId" : "ADR-046-provider-system-systemd", + "topologicalRank" : 10, + "wave" : "W6" }, { - "blockers": [], - "destinations": [ + "blockers" : [], + "destinations" : [ "docs/specs/providers/ADR-046-provider-transport-azure-relay.md" ], - "entryContracts": [ + "entryContracts" : [ "ADR-046-componentsession-and-bus", "ADR-046-nix-configuration", "ADR-046-provider-model-and-packaging", @@ -9567,12 +10665,12 @@ "ADR-046-terminology-and-identities", "ADR-046-zone-routing" ], - "exitGate": "ADR046-W6 exit criteria (ADR-046-validation-and-delivery §4): every spec/work item in this wave Merged with clean destinations, all validators green, and the ten-role panel seal recorded", - "id": "ADR-046-provider-transport-azure-relay", - "kind": "spec", - "owner": "ADR046-W6 wave (ADR-046-validation-and-delivery §3.2)", - "parallelGroup": "W6-transport-observability-activation", - "prerequisites": [ + "exitGate" : "ADR046-W6 exit criteria (ADR-046-validation-and-delivery §4): every spec/work item in this wave Merged with clean destinations, all validators green, and the ten-role panel seal recorded", + "id" : "ADR-046-provider-transport-azure-relay", + "kind" : "spec", + "owner" : "ADR046-W6 wave (ADR-046-validation-and-delivery §3.2)", + "parallelGroup" : "W6-transport-observability-activation", + "prerequisites" : [ "ADR-046-componentsession-and-bus", "ADR-046-nix-configuration", "ADR-046-provider-model-and-packaging", @@ -9581,16 +10679,16 @@ "ADR-046-terminology-and-identities", "ADR-046-zone-routing" ], - "specId": "ADR-046-provider-transport-azure-relay", - "topologicalRank": 10, - "wave": "W6" + "specId" : "ADR-046-provider-transport-azure-relay", + "topologicalRank" : 10, + "wave" : "W6" }, { - "blockers": [], - "destinations": [ + "blockers" : [], + "destinations" : [ "docs/specs/providers/ADR-046-provider-transport-unix.md" ], - "entryContracts": [ + "entryContracts" : [ "ADR-046-components-processes-and-sandbox", "ADR-046-componentsession-and-bus", "ADR-046-core-controllers", @@ -9603,12 +10701,12 @@ "ADR-046-terminology-and-identities", "ADR-046-zone-routing" ], - "exitGate": "ADR046-W6 exit criteria (ADR-046-validation-and-delivery §4): every spec/work item in this wave Merged with clean destinations, all validators green, and the ten-role panel seal recorded", - "id": "ADR-046-provider-transport-unix", - "kind": "spec", - "owner": "ADR046-W6 wave (ADR-046-validation-and-delivery §3.2)", - "parallelGroup": "W6-transport-observability-activation", - "prerequisites": [ + "exitGate" : "ADR046-W6 exit criteria (ADR-046-validation-and-delivery §4): every spec/work item in this wave Merged with clean destinations, all validators green, and the ten-role panel seal recorded", + "id" : "ADR-046-provider-transport-unix", + "kind" : "spec", + "owner" : "ADR046-W6 wave (ADR-046-validation-and-delivery §3.2)", + "parallelGroup" : "W6-transport-observability-activation", + "prerequisites" : [ "ADR-046-components-processes-and-sandbox", "ADR-046-componentsession-and-bus", "ADR-046-core-controllers", @@ -9621,16 +10719,16 @@ "ADR-046-terminology-and-identities", "ADR-046-zone-routing" ], - "specId": "ADR-046-provider-transport-unix", - "topologicalRank": 10, - "wave": "W6" + "specId" : "ADR-046-provider-transport-unix", + "topologicalRank" : 10, + "wave" : "W6" }, { - "blockers": [], - "destinations": [ + "blockers" : [], + "destinations" : [ "docs/specs/providers/ADR-046-provider-transport-vsock.md" ], - "entryContracts": [ + "entryContracts" : [ "ADR-046-componentsession-and-bus", "ADR-046-provider-model-and-packaging", "ADR-046-provider-state", @@ -9640,12 +10738,12 @@ "ADR-046-terminology-and-identities", "ADR-046-zone-routing" ], - "exitGate": "ADR046-W6 exit criteria (ADR-046-validation-and-delivery §4): every spec/work item in this wave Merged with clean destinations, all validators green, and the ten-role panel seal recorded", - "id": "ADR-046-provider-transport-vsock", - "kind": "spec", - "owner": "ADR046-W6 wave (ADR-046-validation-and-delivery §3.2)", - "parallelGroup": "W6-transport-observability-activation", - "prerequisites": [ + "exitGate" : "ADR046-W6 exit criteria (ADR-046-validation-and-delivery §4): every spec/work item in this wave Merged with clean destinations, all validators green, and the ten-role panel seal recorded", + "id" : "ADR-046-provider-transport-vsock", + "kind" : "spec", + "owner" : "ADR046-W6 wave (ADR-046-validation-and-delivery §3.2)", + "parallelGroup" : "W6-transport-observability-activation", + "prerequisites" : [ "ADR-046-componentsession-and-bus", "ADR-046-provider-model-and-packaging", "ADR-046-provider-state", @@ -9655,16 +10753,16 @@ "ADR-046-terminology-and-identities", "ADR-046-zone-routing" ], - "specId": "ADR-046-provider-transport-vsock", - "topologicalRank": 10, - "wave": "W6" + "specId" : "ADR-046-provider-transport-vsock", + "topologicalRank" : 10, + "wave" : "W6" }, { - "blockers": [], - "destinations": [ + "blockers" : [], + "destinations" : [ "docs/specs/providers/ADR-046-provider-volume-local.md" ], - "entryContracts": [ + "entryContracts" : [ "ADR-046-components-processes-and-sandbox", "ADR-046-componentsession-and-bus", "ADR-046-nix-configuration", @@ -9674,12 +10772,12 @@ "ADR-046-resources-volume", "ADR-046-telemetry-audit-and-support" ], - "exitGate": "ADR046-W6 exit criteria (ADR-046-validation-and-delivery §4): every spec/work item in this wave Merged with clean destinations, all validators green, and the ten-role panel seal recorded", - "id": "ADR-046-provider-volume-local", - "kind": "spec", - "owner": "ADR046-W6 wave (ADR-046-validation-and-delivery §3.2)", - "parallelGroup": "W6-storage-network-device", - "prerequisites": [ + "exitGate" : "ADR046-W6 exit criteria (ADR-046-validation-and-delivery §4): every spec/work item in this wave Merged with clean destinations, all validators green, and the ten-role panel seal recorded", + "id" : "ADR-046-provider-volume-local", + "kind" : "spec", + "owner" : "ADR046-W6 wave (ADR-046-validation-and-delivery §3.2)", + "parallelGroup" : "W6-storage-network-device", + "prerequisites" : [ "ADR-046-components-processes-and-sandbox", "ADR-046-componentsession-and-bus", "ADR-046-nix-configuration", @@ -9689,16 +10787,16 @@ "ADR-046-resources-volume", "ADR-046-telemetry-audit-and-support" ], - "specId": "ADR-046-provider-volume-local", - "topologicalRank": 10, - "wave": "W6" + "specId" : "ADR-046-provider-volume-local", + "topologicalRank" : 10, + "wave" : "W6" }, { - "blockers": [], - "destinations": [ + "blockers" : [], + "destinations" : [ "docs/specs/providers/ADR-046-provider-volume-virtiofs.md" ], - "entryContracts": [ + "entryContracts" : [ "ADR-046-components-processes-and-sandbox", "ADR-046-componentsession-and-bus", "ADR-046-nix-configuration", @@ -9710,12 +10808,12 @@ "ADR-046-resources-volume", "ADR-046-telemetry-audit-and-support" ], - "exitGate": "ADR046-W6 exit criteria (ADR-046-validation-and-delivery §4): every spec/work item in this wave Merged with clean destinations, all validators green, and the ten-role panel seal recorded", - "id": "ADR-046-provider-volume-virtiofs", - "kind": "spec", - "owner": "ADR046-W6 wave (ADR-046-validation-and-delivery §3.2)", - "parallelGroup": "W6-storage-network-device", - "prerequisites": [ + "exitGate" : "ADR046-W6 exit criteria (ADR-046-validation-and-delivery §4): every spec/work item in this wave Merged with clean destinations, all validators green, and the ten-role panel seal recorded", + "id" : "ADR-046-provider-volume-virtiofs", + "kind" : "spec", + "owner" : "ADR046-W6 wave (ADR-046-validation-and-delivery §3.2)", + "parallelGroup" : "W6-storage-network-device", + "prerequisites" : [ "ADR-046-components-processes-and-sandbox", "ADR-046-componentsession-and-bus", "ADR-046-nix-configuration", @@ -9727,16 +10825,16 @@ "ADR-046-resources-volume", "ADR-046-telemetry-audit-and-support" ], - "specId": "ADR-046-provider-volume-virtiofs", - "topologicalRank": 10, - "wave": "W6" + "specId" : "ADR-046-provider-volume-virtiofs", + "topologicalRank" : 10, + "wave" : "W6" }, { - "blockers": [], - "destinations": [ + "blockers" : [], + "destinations" : [ "docs/specs/ADR-046-reset-and-cutover.md" ], - "entryContracts": [ + "entryContracts" : [ "ADR-046-cli-and-operations", "ADR-046-components-processes-and-sandbox", "ADR-046-componentsession-and-bus", @@ -9761,12 +10859,12 @@ "ADR-046-terminology-and-identities", "ADR-046-zone-routing" ], - "exitGate": "ADR046-W7 exit criteria (ADR-046-validation-and-delivery §4): every spec/work item in this wave Merged with clean destinations, all validators green, and the ten-role panel seal recorded", - "id": "ADR-046-reset-and-cutover", - "kind": "spec", - "owner": "ADR046-W7 wave (ADR-046-validation-and-delivery §3.2)", - "parallelGroup": "W7-closing", - "prerequisites": [ + "exitGate" : "ADR046-W7 exit criteria (ADR-046-validation-and-delivery §4): every spec/work item in this wave Merged with clean destinations, all validators green, and the ten-role panel seal recorded", + "id" : "ADR-046-reset-and-cutover", + "kind" : "spec", + "owner" : "ADR046-W7 wave (ADR-046-validation-and-delivery §3.2)", + "parallelGroup" : "W7-closing", + "prerequisites" : [ "ADR-046-cli-and-operations", "ADR-046-components-processes-and-sandbox", "ADR-046-componentsession-and-bus", @@ -9791,108 +10889,108 @@ "ADR-046-terminology-and-identities", "ADR-046-zone-routing" ], - "specId": "ADR-046-reset-and-cutover", - "topologicalRank": 10, - "wave": "W7" + "specId" : "ADR-046-reset-and-cutover", + "topologicalRank" : 10, + "wave" : "W7" }, { - "blockers": [], - "destinations": [ + "blockers" : [], + "destinations" : [ "docs/specs/ADR-046-resource-api-and-authorization.md" ], - "entryContracts": [ + "entryContracts" : [ "ADR-046-resource-object-model", "ADR-046-resource-store-redb", "ADR-046-terminology-and-identities" ], - "exitGate": "ADR046-W0 exit criteria (ADR-046-validation-and-delivery §4): every spec/work item in this wave Merged with clean destinations, all validators green, and the ten-role panel seal recorded", - "id": "ADR-046-resource-api-and-authorization", - "kind": "spec", - "owner": "ADR046-W0 wave (ADR-046-validation-and-delivery §3.2)", - "parallelGroup": "W0-foundation-chain", - "prerequisites": [ + "exitGate" : "ADR046-W0 exit criteria (ADR-046-validation-and-delivery §4): every spec/work item in this wave Merged with clean destinations, all validators green, and the ten-role panel seal recorded", + "id" : "ADR-046-resource-api-and-authorization", + "kind" : "spec", + "owner" : "ADR046-W0 wave (ADR-046-validation-and-delivery §3.2)", + "parallelGroup" : "W0-foundation-chain", + "prerequisites" : [ "ADR-046-resource-object-model", "ADR-046-resource-store-redb", "ADR-046-terminology-and-identities" ], - "specId": "ADR-046-resource-api-and-authorization", - "topologicalRank": 4, - "wave": "W0" + "specId" : "ADR-046-resource-api-and-authorization", + "topologicalRank" : 4, + "wave" : "W0" }, { - "blockers": [], - "destinations": [ + "blockers" : [], + "destinations" : [ "docs/specs/ADR-046-resource-object-model.md" ], - "entryContracts": [ + "entryContracts" : [ "ADR-046-decision-register", "ADR-046-terminology-and-identities" ], - "exitGate": "ADR046-W0 exit criteria (ADR-046-validation-and-delivery §4): every spec/work item in this wave Merged with clean destinations, all validators green, and the ten-role panel seal recorded", - "id": "ADR-046-resource-object-model", - "kind": "spec", - "owner": "ADR046-W0 wave (ADR-046-validation-and-delivery §3.2)", - "parallelGroup": "W0-foundation-chain", - "prerequisites": [ + "exitGate" : "ADR046-W0 exit criteria (ADR-046-validation-and-delivery §4): every spec/work item in this wave Merged with clean destinations, all validators green, and the ten-role panel seal recorded", + "id" : "ADR-046-resource-object-model", + "kind" : "spec", + "owner" : "ADR046-W0 wave (ADR-046-validation-and-delivery §3.2)", + "parallelGroup" : "W0-foundation-chain", + "prerequisites" : [ "ADR-046-decision-register", "ADR-046-terminology-and-identities" ], - "specId": "ADR-046-resource-object-model", - "topologicalRank": 2, - "wave": "W0" + "specId" : "ADR-046-resource-object-model", + "topologicalRank" : 2, + "wave" : "W0" }, { - "blockers": [], - "destinations": [ + "blockers" : [], + "destinations" : [ "docs/specs/ADR-046-resource-reconciliation.md" ], - "entryContracts": [ + "entryContracts" : [ "ADR-046-resource-api-and-authorization", "ADR-046-resource-object-model", "ADR-046-resource-store-redb" ], - "exitGate": "ADR046-W1 exit criteria (ADR-046-validation-and-delivery §4): every spec/work item in this wave Merged with clean destinations, all validators green, and the ten-role panel seal recorded", - "id": "ADR-046-resource-reconciliation", - "kind": "spec", - "owner": "ADR046-W1 wave (ADR-046-validation-and-delivery §3.2)", - "parallelGroup": "W1-reconcile-and-bus", - "prerequisites": [ + "exitGate" : "ADR046-W1 exit criteria (ADR-046-validation-and-delivery §4): every spec/work item in this wave Merged with clean destinations, all validators green, and the ten-role panel seal recorded", + "id" : "ADR-046-resource-reconciliation", + "kind" : "spec", + "owner" : "ADR046-W1 wave (ADR-046-validation-and-delivery §3.2)", + "parallelGroup" : "W1-reconcile-and-bus", + "prerequisites" : [ "ADR-046-resource-api-and-authorization", "ADR-046-resource-object-model", "ADR-046-resource-store-redb" ], - "specId": "ADR-046-resource-reconciliation", - "topologicalRank": 5, - "wave": "W1" + "specId" : "ADR-046-resource-reconciliation", + "topologicalRank" : 5, + "wave" : "W1" }, { - "blockers": [], - "destinations": [ + "blockers" : [], + "destinations" : [ "docs/specs/ADR-046-resource-store-redb.md" ], - "entryContracts": [ + "entryContracts" : [ "ADR-046-resource-object-model", "ADR-046-terminology-and-identities" ], - "exitGate": "ADR046-W0 exit criteria (ADR-046-validation-and-delivery §4): every spec/work item in this wave Merged with clean destinations, all validators green, and the ten-role panel seal recorded", - "id": "ADR-046-resource-store-redb", - "kind": "spec", - "owner": "ADR046-W0 wave (ADR-046-validation-and-delivery §3.2)", - "parallelGroup": "W0-foundation-chain", - "prerequisites": [ + "exitGate" : "ADR046-W0 exit criteria (ADR-046-validation-and-delivery §4): every spec/work item in this wave Merged with clean destinations, all validators green, and the ten-role panel seal recorded", + "id" : "ADR-046-resource-store-redb", + "kind" : "spec", + "owner" : "ADR046-W0 wave (ADR-046-validation-and-delivery §3.2)", + "parallelGroup" : "W0-foundation-chain", + "prerequisites" : [ "ADR-046-resource-object-model", "ADR-046-terminology-and-identities" ], - "specId": "ADR-046-resource-store-redb", - "topologicalRank": 3, - "wave": "W0" + "specId" : "ADR-046-resource-store-redb", + "topologicalRank" : 3, + "wave" : "W0" }, { - "blockers": [], - "destinations": [ + "blockers" : [], + "destinations" : [ "docs/specs/ADR-046-resources-credential.md" ], - "entryContracts": [ + "entryContracts" : [ "ADR-046-componentsession-and-bus", "ADR-046-primitive-resource-composition", "ADR-046-provider-model-and-packaging", @@ -9901,12 +10999,12 @@ "ADR-046-resource-reconciliation", "ADR-046-terminology-and-identities" ], - "exitGate": "ADR046-W4 exit criteria (ADR-046-validation-and-delivery §4): every spec/work item in this wave Merged with clean destinations, all validators green, and the ten-role panel seal recorded", - "id": "ADR-046-resources-credential", - "kind": "spec", - "owner": "ADR046-W4 wave (ADR-046-validation-and-delivery §3.2)", - "parallelGroup": "W4-parallel-specs", - "prerequisites": [ + "exitGate" : "ADR046-W4 exit criteria (ADR-046-validation-and-delivery §4): every spec/work item in this wave Merged with clean destinations, all validators green, and the ten-role panel seal recorded", + "id" : "ADR-046-resources-credential", + "kind" : "spec", + "owner" : "ADR046-W4 wave (ADR-046-validation-and-delivery §3.2)", + "parallelGroup" : "W4-parallel-specs", + "prerequisites" : [ "ADR-046-componentsession-and-bus", "ADR-046-primitive-resource-composition", "ADR-046-provider-model-and-packaging", @@ -9915,16 +11013,16 @@ "ADR-046-resource-reconciliation", "ADR-046-terminology-and-identities" ], - "specId": "ADR-046-resources-credential", - "topologicalRank": 8, - "wave": "W4" + "specId" : "ADR-046-resources-credential", + "topologicalRank" : 8, + "wave" : "W4" }, { - "blockers": [], - "destinations": [ + "blockers" : [], + "destinations" : [ "docs/specs/ADR-046-resources-device.md" ], - "entryContracts": [ + "entryContracts" : [ "ADR-046-components-processes-and-sandbox", "ADR-046-primitive-resource-composition", "ADR-046-provider-model-and-packaging", @@ -9932,12 +11030,12 @@ "ADR-046-resource-object-model", "ADR-046-resource-reconciliation" ], - "exitGate": "ADR046-W5 exit criteria (ADR-046-validation-and-delivery §4): every spec/work item in this wave Merged with clean destinations, all validators green, and the ten-role panel seal recorded", - "id": "ADR-046-resources-device", - "kind": "spec", - "owner": "ADR046-W5 wave (ADR-046-validation-and-delivery §3.2)", - "parallelGroup": "W5-parallel-specs", - "prerequisites": [ + "exitGate" : "ADR046-W5 exit criteria (ADR-046-validation-and-delivery §4): every spec/work item in this wave Merged with clean destinations, all validators green, and the ten-role panel seal recorded", + "id" : "ADR-046-resources-device", + "kind" : "spec", + "owner" : "ADR046-W5 wave (ADR-046-validation-and-delivery §3.2)", + "parallelGroup" : "W5-parallel-specs", + "prerequisites" : [ "ADR-046-components-processes-and-sandbox", "ADR-046-primitive-resource-composition", "ADR-046-provider-model-and-packaging", @@ -9945,16 +11043,16 @@ "ADR-046-resource-object-model", "ADR-046-resource-reconciliation" ], - "specId": "ADR-046-resources-device", - "topologicalRank": 9, - "wave": "W5" + "specId" : "ADR-046-resources-device", + "topologicalRank" : 9, + "wave" : "W5" }, { - "blockers": [], - "destinations": [ + "blockers" : [], + "destinations" : [ "docs/specs/ADR-046-resources-host-guest-process-user.md" ], - "entryContracts": [ + "entryContracts" : [ "ADR-046-components-processes-and-sandbox", "ADR-046-core-controllers", "ADR-046-decision-register", @@ -9965,12 +11063,12 @@ "ADR-046-resource-reconciliation", "ADR-046-terminology-and-identities" ], - "exitGate": "ADR046-W5 exit criteria (ADR-046-validation-and-delivery §4): every spec/work item in this wave Merged with clean destinations, all validators green, and the ten-role panel seal recorded", - "id": "ADR-046-resources-host-guest-process-user", - "kind": "spec", - "owner": "ADR046-W5 wave (ADR-046-validation-and-delivery §3.2)", - "parallelGroup": "W5-parallel-specs", - "prerequisites": [ + "exitGate" : "ADR046-W5 exit criteria (ADR-046-validation-and-delivery §4): every spec/work item in this wave Merged with clean destinations, all validators green, and the ten-role panel seal recorded", + "id" : "ADR-046-resources-host-guest-process-user", + "kind" : "spec", + "owner" : "ADR046-W5 wave (ADR-046-validation-and-delivery §3.2)", + "parallelGroup" : "W5-parallel-specs", + "prerequisites" : [ "ADR-046-components-processes-and-sandbox", "ADR-046-core-controllers", "ADR-046-decision-register", @@ -9981,72 +11079,72 @@ "ADR-046-resource-reconciliation", "ADR-046-terminology-and-identities" ], - "specId": "ADR-046-resources-host-guest-process-user", - "topologicalRank": 9, - "wave": "W5" + "specId" : "ADR-046-resources-host-guest-process-user", + "topologicalRank" : 9, + "wave" : "W5" }, { - "blockers": [], - "destinations": [ + "blockers" : [], + "destinations" : [ "docs/specs/ADR-046-resources-network.md" ], - "entryContracts": [ + "entryContracts" : [ "ADR-046-primitive-resource-composition", "ADR-046-provider-model-and-packaging", "ADR-046-resource-object-model", "ADR-046-resource-reconciliation", "ADR-046-terminology-and-identities" ], - "exitGate": "ADR046-W4 exit criteria (ADR-046-validation-and-delivery §4): every spec/work item in this wave Merged with clean destinations, all validators green, and the ten-role panel seal recorded", - "id": "ADR-046-resources-network", - "kind": "spec", - "owner": "ADR046-W4 wave (ADR-046-validation-and-delivery §3.2)", - "parallelGroup": "W4-parallel-specs", - "prerequisites": [ + "exitGate" : "ADR046-W4 exit criteria (ADR-046-validation-and-delivery §4): every spec/work item in this wave Merged with clean destinations, all validators green, and the ten-role panel seal recorded", + "id" : "ADR-046-resources-network", + "kind" : "spec", + "owner" : "ADR046-W4 wave (ADR-046-validation-and-delivery §3.2)", + "parallelGroup" : "W4-parallel-specs", + "prerequisites" : [ "ADR-046-primitive-resource-composition", "ADR-046-provider-model-and-packaging", "ADR-046-resource-object-model", "ADR-046-resource-reconciliation", "ADR-046-terminology-and-identities" ], - "specId": "ADR-046-resources-network", - "topologicalRank": 8, - "wave": "W4" + "specId" : "ADR-046-resources-network", + "topologicalRank" : 8, + "wave" : "W4" }, { - "blockers": [], - "destinations": [ + "blockers" : [], + "destinations" : [ "docs/specs/ADR-046-resources-volume.md" ], - "entryContracts": [ + "entryContracts" : [ "ADR-046-components-processes-and-sandbox", "ADR-046-primitive-resource-composition", "ADR-046-provider-model-and-packaging", "ADR-046-resource-object-model", "ADR-046-resource-reconciliation" ], - "exitGate": "ADR046-W5 exit criteria (ADR-046-validation-and-delivery §4): every spec/work item in this wave Merged with clean destinations, all validators green, and the ten-role panel seal recorded", - "id": "ADR-046-resources-volume", - "kind": "spec", - "owner": "ADR046-W5 wave (ADR-046-validation-and-delivery §3.2)", - "parallelGroup": "W5-parallel-specs", - "prerequisites": [ + "exitGate" : "ADR046-W5 exit criteria (ADR-046-validation-and-delivery §4): every spec/work item in this wave Merged with clean destinations, all validators green, and the ten-role panel seal recorded", + "id" : "ADR-046-resources-volume", + "kind" : "spec", + "owner" : "ADR046-W5 wave (ADR-046-validation-and-delivery §3.2)", + "parallelGroup" : "W5-parallel-specs", + "prerequisites" : [ "ADR-046-components-processes-and-sandbox", "ADR-046-primitive-resource-composition", "ADR-046-provider-model-and-packaging", "ADR-046-resource-object-model", "ADR-046-resource-reconciliation" ], - "specId": "ADR-046-resources-volume", - "topologicalRank": 9, - "wave": "W5" + "specId" : "ADR-046-resources-volume", + "topologicalRank" : 9, + "wave" : "W5" }, { - "blockers": [], - "destinations": [ + "blockers" : [], + "destinations" : [ "docs/specs/ADR-046-resources-zone-control.md" ], - "entryContracts": [ + "entryContracts" : [ "ADR-046-core-controllers", "ADR-046-decision-register", "ADR-046-provider-model-and-packaging", @@ -10056,12 +11154,12 @@ "ADR-046-resource-store-redb", "ADR-046-terminology-and-identities" ], - "exitGate": "ADR046-W5 exit criteria (ADR-046-validation-and-delivery §4): every spec/work item in this wave Merged with clean destinations, all validators green, and the ten-role panel seal recorded", - "id": "ADR-046-resources-zone-control", - "kind": "spec", - "owner": "ADR046-W5 wave (ADR-046-validation-and-delivery §3.2)", - "parallelGroup": "W5-parallel-specs", - "prerequisites": [ + "exitGate" : "ADR046-W5 exit criteria (ADR-046-validation-and-delivery §4): every spec/work item in this wave Merged with clean destinations, all validators green, and the ten-role panel seal recorded", + "id" : "ADR-046-resources-zone-control", + "kind" : "spec", + "owner" : "ADR046-W5 wave (ADR-046-validation-and-delivery §3.2)", + "parallelGroup" : "W5-parallel-specs", + "prerequisites" : [ "ADR-046-core-controllers", "ADR-046-decision-register", "ADR-046-provider-model-and-packaging", @@ -10071,16 +11169,16 @@ "ADR-046-resource-store-redb", "ADR-046-terminology-and-identities" ], - "specId": "ADR-046-resources-zone-control", - "topologicalRank": 9, - "wave": "W5" + "specId" : "ADR-046-resources-zone-control", + "topologicalRank" : 9, + "wave" : "W5" }, { - "blockers": [], - "destinations": [ + "blockers" : [], + "destinations" : [ "docs/specs/ADR-046-security-and-threat-model.md" ], - "entryContracts": [ + "entryContracts" : [ "ADR-046-cli-and-operations", "ADR-046-components-processes-and-sandbox", "ADR-046-componentsession-and-bus", @@ -10132,12 +11230,12 @@ "ADR-046-terminology-and-identities", "ADR-046-zone-routing" ], - "exitGate": "ADR046-W7 exit criteria (ADR-046-validation-and-delivery §4): every spec/work item in this wave Merged with clean destinations, all validators green, and the ten-role panel seal recorded", - "id": "ADR-046-security-and-threat-model", - "kind": "spec", - "owner": "ADR046-W7 wave (ADR-046-validation-and-delivery §3.2)", - "parallelGroup": "W7-closing", - "prerequisites": [ + "exitGate" : "ADR046-W7 exit criteria (ADR-046-validation-and-delivery §4): every spec/work item in this wave Merged with clean destinations, all validators green, and the ten-role panel seal recorded", + "id" : "ADR-046-security-and-threat-model", + "kind" : "spec", + "owner" : "ADR046-W7 wave (ADR-046-validation-and-delivery §3.2)", + "parallelGroup" : "W7-closing", + "prerequisites" : [ "ADR-046-cli-and-operations", "ADR-046-components-processes-and-sandbox", "ADR-046-componentsession-and-bus", @@ -10189,16 +11287,16 @@ "ADR-046-terminology-and-identities", "ADR-046-zone-routing" ], - "specId": "ADR-046-security-and-threat-model", - "topologicalRank": 11, - "wave": "W7" + "specId" : "ADR-046-security-and-threat-model", + "topologicalRank" : 11, + "wave" : "W7" }, { - "blockers": [], - "destinations": [ + "blockers" : [], + "destinations" : [ "docs/specs/ADR-046-streamline.md" ], - "entryContracts": [ + "entryContracts" : [ "ADR-046-current-code-migration-map", "ADR-046-decision-register", "ADR-046-primitive-resource-composition", @@ -10209,12 +11307,12 @@ "ADR-046-terminology-and-identities", "ADR-046-zone-routing" ], - "exitGate": "ADR046-W7 exit criteria (ADR-046-validation-and-delivery §4): every spec/work item in this wave Merged with clean destinations, all validators green, and the ten-role panel seal recorded", - "id": "ADR-046-streamline", - "kind": "spec", - "owner": "ADR046-W7 wave (ADR-046-validation-and-delivery §3.2)", - "parallelGroup": "W7-closing", - "prerequisites": [ + "exitGate" : "ADR046-W7 exit criteria (ADR-046-validation-and-delivery §4): every spec/work item in this wave Merged with clean destinations, all validators green, and the ten-role panel seal recorded", + "id" : "ADR-046-streamline", + "kind" : "spec", + "owner" : "ADR046-W7 wave (ADR-046-validation-and-delivery §3.2)", + "parallelGroup" : "W7-closing", + "prerequisites" : [ "ADR-046-current-code-migration-map", "ADR-046-decision-register", "ADR-046-primitive-resource-composition", @@ -10225,16 +11323,16 @@ "ADR-046-terminology-and-identities", "ADR-046-zone-routing" ], - "specId": "ADR-046-streamline", - "topologicalRank": 11, - "wave": "W7" + "specId" : "ADR-046-streamline", + "topologicalRank" : 11, + "wave" : "W7" }, { - "blockers": [], - "destinations": [ + "blockers" : [], + "destinations" : [ "docs/specs/ADR-046-telemetry-audit-and-support.md" ], - "entryContracts": [ + "entryContracts" : [ "ADR-046-components-processes-and-sandbox", "ADR-046-componentsession-and-bus", "ADR-046-core-controllers", @@ -10243,12 +11341,12 @@ "ADR-046-resource-store-redb", "ADR-046-terminology-and-identities" ], - "exitGate": "ADR046-W5 exit criteria (ADR-046-validation-and-delivery §4): every spec/work item in this wave Merged with clean destinations, all validators green, and the ten-role panel seal recorded", - "id": "ADR-046-telemetry-audit-and-support", - "kind": "spec", - "owner": "ADR046-W5 wave (ADR-046-validation-and-delivery §3.2)", - "parallelGroup": "W5-parallel-specs", - "prerequisites": [ + "exitGate" : "ADR046-W5 exit criteria (ADR-046-validation-and-delivery §4): every spec/work item in this wave Merged with clean destinations, all validators green, and the ten-role panel seal recorded", + "id" : "ADR-046-telemetry-audit-and-support", + "kind" : "spec", + "owner" : "ADR046-W5 wave (ADR-046-validation-and-delivery §3.2)", + "parallelGroup" : "W5-parallel-specs", + "prerequisites" : [ "ADR-046-components-processes-and-sandbox", "ADR-046-componentsession-and-bus", "ADR-046-core-controllers", @@ -10257,36 +11355,36 @@ "ADR-046-resource-store-redb", "ADR-046-terminology-and-identities" ], - "specId": "ADR-046-telemetry-audit-and-support", - "topologicalRank": 9, - "wave": "W5" + "specId" : "ADR-046-telemetry-audit-and-support", + "topologicalRank" : 9, + "wave" : "W5" }, { - "blockers": [], - "destinations": [ + "blockers" : [], + "destinations" : [ "docs/specs/ADR-046-terminology-and-identities.md" ], - "entryContracts": [ + "entryContracts" : [ "ADR-046-decision-register" ], - "exitGate": "ADR046-W0 exit criteria (ADR-046-validation-and-delivery §4): every spec/work item in this wave Merged with clean destinations, all validators green, and the ten-role panel seal recorded", - "id": "ADR-046-terminology-and-identities", - "kind": "spec", - "owner": "ADR046-W0 wave (ADR-046-validation-and-delivery §3.2)", - "parallelGroup": "W0-foundation-chain", - "prerequisites": [ + "exitGate" : "ADR046-W0 exit criteria (ADR-046-validation-and-delivery §4): every spec/work item in this wave Merged with clean destinations, all validators green, and the ten-role panel seal recorded", + "id" : "ADR-046-terminology-and-identities", + "kind" : "spec", + "owner" : "ADR046-W0 wave (ADR-046-validation-and-delivery §3.2)", + "parallelGroup" : "W0-foundation-chain", + "prerequisites" : [ "ADR-046-decision-register" ], - "specId": "ADR-046-terminology-and-identities", - "topologicalRank": 1, - "wave": "W0" + "specId" : "ADR-046-terminology-and-identities", + "topologicalRank" : 1, + "wave" : "W0" }, { - "blockers": [], - "destinations": [ + "blockers" : [], + "destinations" : [ "docs/specs/ADR-046-validation-and-delivery.md" ], - "entryContracts": [ + "entryContracts" : [ "ADR-046-cli-and-operations", "ADR-046-components-processes-and-sandbox", "ADR-046-componentsession-and-bus", @@ -10296,35 +11394,8 @@ "ADR-046-feasibility-and-spikes", "ADR-046-nix-configuration", "ADR-046-primitive-resource-composition", - "ADR-046-provider-activation-nixos", - "ADR-046-provider-audio-pipewire", - "ADR-046-provider-clipboard-wayland", - "ADR-046-provider-credential-entra", - "ADR-046-provider-credential-managed-identity", - "ADR-046-provider-credential-secret-service", - "ADR-046-provider-device-gpu", - "ADR-046-provider-device-security-key", - "ADR-046-provider-device-tpm", - "ADR-046-provider-device-usbip", - "ADR-046-provider-display-wayland", "ADR-046-provider-model-and-packaging", - "ADR-046-provider-network-local", - "ADR-046-provider-notification-desktop", - "ADR-046-provider-observability-otel", - "ADR-046-provider-runtime-azure-container-apps", - "ADR-046-provider-runtime-azure-virtual-machine", - "ADR-046-provider-runtime-cloud-hypervisor", - "ADR-046-provider-runtime-qemu-media", - "ADR-046-provider-shell-terminal", "ADR-046-provider-state", - "ADR-046-provider-system-core", - "ADR-046-provider-system-minijail", - "ADR-046-provider-system-systemd", - "ADR-046-provider-transport-azure-relay", - "ADR-046-provider-transport-unix", - "ADR-046-provider-transport-vsock", - "ADR-046-provider-volume-local", - "ADR-046-provider-volume-virtiofs", "ADR-046-reset-and-cutover", "ADR-046-resource-api-and-authorization", "ADR-046-resource-object-model", @@ -10342,12 +11413,12 @@ "ADR-046-terminology-and-identities", "ADR-046-zone-routing" ], - "exitGate": "ADR046-W7 exit criteria (ADR-046-validation-and-delivery §4): every spec/work item in this wave Merged with clean destinations, all validators green, and the ten-role panel seal recorded", - "id": "ADR-046-validation-and-delivery", - "kind": "spec", - "owner": "ADR046-W7 wave (ADR-046-validation-and-delivery §3.2)", - "parallelGroup": "W7-closing", - "prerequisites": [ + "exitGate" : "ADR046-W7 exit criteria (ADR-046-validation-and-delivery §4): every spec/work item in this wave Merged with clean destinations, all validators green, and the ten-role panel seal recorded", + "id" : "ADR-046-validation-and-delivery", + "kind" : "spec", + "owner" : "ADR046-W7 wave (ADR-046-validation-and-delivery §3.2)", + "parallelGroup" : "W7-closing", + "prerequisites" : [ "ADR-046-cli-and-operations", "ADR-046-components-processes-and-sandbox", "ADR-046-componentsession-and-bus", @@ -10357,35 +11428,8 @@ "ADR-046-feasibility-and-spikes", "ADR-046-nix-configuration", "ADR-046-primitive-resource-composition", - "ADR-046-provider-activation-nixos", - "ADR-046-provider-audio-pipewire", - "ADR-046-provider-clipboard-wayland", - "ADR-046-provider-credential-entra", - "ADR-046-provider-credential-managed-identity", - "ADR-046-provider-credential-secret-service", - "ADR-046-provider-device-gpu", - "ADR-046-provider-device-security-key", - "ADR-046-provider-device-tpm", - "ADR-046-provider-device-usbip", - "ADR-046-provider-display-wayland", "ADR-046-provider-model-and-packaging", - "ADR-046-provider-network-local", - "ADR-046-provider-notification-desktop", - "ADR-046-provider-observability-otel", - "ADR-046-provider-runtime-azure-container-apps", - "ADR-046-provider-runtime-azure-virtual-machine", - "ADR-046-provider-runtime-cloud-hypervisor", - "ADR-046-provider-runtime-qemu-media", - "ADR-046-provider-shell-terminal", "ADR-046-provider-state", - "ADR-046-provider-system-core", - "ADR-046-provider-system-minijail", - "ADR-046-provider-system-systemd", - "ADR-046-provider-transport-azure-relay", - "ADR-046-provider-transport-unix", - "ADR-046-provider-transport-vsock", - "ADR-046-provider-volume-local", - "ADR-046-provider-volume-virtiofs", "ADR-046-reset-and-cutover", "ADR-046-resource-api-and-authorization", "ADR-046-resource-object-model", @@ -10403,11427 +11447,11755 @@ "ADR-046-terminology-and-identities", "ADR-046-zone-routing" ], - "specId": "ADR-046-validation-and-delivery", - "topologicalRank": 12, - "wave": "W7" + "specId" : "ADR-046-validation-and-delivery", + "topologicalRank" : 12, + "wave" : "W7" }, { - "blockers": [], - "destinations": [ + "blockers" : [], + "destinations" : [ "docs/specs/ADR-046-zone-routing.md" ], - "entryContracts": [ + "entryContracts" : [ "ADR-046-componentsession-and-bus", "ADR-046-resource-api-and-authorization", "ADR-046-resource-object-model", "ADR-046-resource-reconciliation", "ADR-046-terminology-and-identities" ], - "exitGate": "ADR046-W2 exit criteria (ADR-046-validation-and-delivery §4): every spec/work item in this wave Merged with clean destinations, all validators green, and the ten-role panel seal recorded", - "id": "ADR-046-zone-routing", - "kind": "spec", - "owner": "ADR046-W2 wave (ADR-046-validation-and-delivery §3.2)", - "parallelGroup": "W2-composition-and-routing", - "prerequisites": [ + "exitGate" : "ADR046-W2 exit criteria (ADR-046-validation-and-delivery §4): every spec/work item in this wave Merged with clean destinations, all validators green, and the ten-role panel seal recorded", + "id" : "ADR-046-zone-routing", + "kind" : "spec", + "owner" : "ADR046-W2 wave (ADR-046-validation-and-delivery §3.2)", + "parallelGroup" : "W2-composition-and-routing", + "prerequisites" : [ "ADR-046-componentsession-and-bus", "ADR-046-resource-api-and-authorization", "ADR-046-resource-object-model", "ADR-046-resource-reconciliation", "ADR-046-terminology-and-identities" ], - "specId": "ADR-046-zone-routing", - "topologicalRank": 6, - "wave": "W2" + "specId" : "ADR-046-zone-routing", + "topologicalRank" : 6, + "wave" : "W2" }, { - "blockers": [], - "destinations": [ + "blockers" : [], + "destinations" : [ "`packages/d2b-provider-runtime-azure-container-apps/src/controller.rs`" ], - "entryContracts": [ + "entryContracts" : [ "ADR-046-provider-runtime-azure-container-apps" ], - "exitGate": "ADR046-W6 exit criteria (ADR-046-validation-and-delivery §4): every spec/work item in this wave Merged with clean destinations, all validators green, and the ten-role panel seal recorded", - "id": "ADR046-aca-001", - "kind": "work-item", - "owner": "ADR046-provider-001; runtime-aca owner", - "parallelGroup": "wi:ADR-046-provider-runtime-azure-container-apps", - "prerequisites": [ + "exitGate" : "ADR046-W6 exit criteria (ADR-046-validation-and-delivery §4): every spec/work item in this wave Merged with clean destinations, all validators green, and the ten-role panel seal recorded", + "id" : "ADR046-aca-001", + "kind" : "work-item", + "owner" : "ADR046-provider-001; runtime-aca owner", + "parallelGroup" : "wi:ADR-046-provider-runtime-azure-container-apps", + "prerequisites" : [ "ADR-046-provider-runtime-azure-container-apps", "ADR046-provider-001" ], - "specId": "ADR-046-provider-runtime-azure-container-apps", - "topologicalRank": 11, - "wave": "W6" + "specId" : "ADR-046-provider-runtime-azure-container-apps", + "topologicalRank" : 11, + "wave" : "W6" }, { - "blockers": [], - "destinations": [ + "blockers" : [], + "destinations" : [ "`packages/d2b-provider-runtime-azure-container-apps/src/deployment_service.rs`" ], - "entryContracts": [ + "entryContracts" : [ "ADR-046-provider-runtime-azure-container-apps" ], - "exitGate": "ADR046-W6 exit criteria (ADR-046-validation-and-delivery §4): every spec/work item in this wave Merged with clean destinations, all validators green, and the ten-role panel seal recorded", - "id": "ADR046-aca-002", - "kind": "work-item", - "owner": "ADR046-aca-001; deployment service owner", - "parallelGroup": "wi:ADR-046-provider-runtime-azure-container-apps", - "prerequisites": [ + "exitGate" : "ADR046-W6 exit criteria (ADR-046-validation-and-delivery §4): every spec/work item in this wave Merged with clean destinations, all validators green, and the ten-role panel seal recorded", + "id" : "ADR046-aca-002", + "kind" : "work-item", + "owner" : "ADR046-aca-001; deployment service owner", + "parallelGroup" : "wi:ADR-046-provider-runtime-azure-container-apps", + "prerequisites" : [ "ADR-046-provider-runtime-azure-container-apps", "ADR046-aca-001" ], - "specId": "ADR-046-provider-runtime-azure-container-apps", - "topologicalRank": 12, - "wave": "W6" + "specId" : "ADR-046-provider-runtime-azure-container-apps", + "topologicalRank" : 12, + "wave" : "W6" }, { - "blockers": [], - "destinations": [ + "blockers" : [], + "destinations" : [ "`packages/d2b-contracts/src/provider_effects/aca.rs` (shared `d2b-contracts` provider-effects module; no new crate; provider crate remains one package)" ], - "entryContracts": [ + "entryContracts" : [ "ADR-046-provider-runtime-azure-container-apps" ], - "exitGate": "ADR046-W6 exit criteria (ADR-046-validation-and-delivery §4): every spec/work item in this wave Merged with clean destinations, all validators green, and the ten-role panel seal recorded", - "id": "ADR046-aca-003", - "kind": "work-item", - "owner": "ADR046-aca-001; credential integration owner", - "parallelGroup": "wi:ADR-046-provider-runtime-azure-container-apps", - "prerequisites": [ + "exitGate" : "ADR046-W6 exit criteria (ADR-046-validation-and-delivery §4): every spec/work item in this wave Merged with clean destinations, all validators green, and the ten-role panel seal recorded", + "id" : "ADR046-aca-003", + "kind" : "work-item", + "owner" : "ADR046-aca-001; credential integration owner", + "parallelGroup" : "wi:ADR-046-provider-runtime-azure-container-apps", + "prerequisites" : [ "ADR-046-provider-runtime-azure-container-apps", "ADR046-aca-001" ], - "specId": "ADR-046-provider-runtime-azure-container-apps", - "topologicalRank": 12, - "wave": "W6" + "specId" : "ADR-046-provider-runtime-azure-container-apps", + "topologicalRank" : 12, + "wave" : "W6" }, { - "blockers": [], - "destinations": [ + "blockers" : [], + "destinations" : [ "ZoneLink resource `spec.transportSettings` (§15.4); `Provider/transport-azure-relay` dossier (separate)" ], - "entryContracts": [ + "entryContracts" : [ "ADR-046-provider-runtime-azure-container-apps" ], - "exitGate": "ADR046-W6 exit criteria (ADR-046-validation-and-delivery §4): every spec/work item in this wave Merged with clean destinations, all validators green, and the ten-role panel seal recorded", - "id": "ADR046-aca-004", - "kind": "work-item", - "owner": "ADR046-session-001; ZoneLink transport owner", - "parallelGroup": "wi:ADR-046-provider-runtime-azure-container-apps", - "prerequisites": [ + "exitGate" : "ADR046-W6 exit criteria (ADR-046-validation-and-delivery §4): every spec/work item in this wave Merged with clean destinations, all validators green, and the ten-role panel seal recorded", + "id" : "ADR046-aca-004", + "kind" : "work-item", + "owner" : "ADR046-session-001; ZoneLink transport owner", + "parallelGroup" : "wi:ADR-046-provider-runtime-azure-container-apps", + "prerequisites" : [ "ADR-046-provider-runtime-azure-container-apps", "ADR046-session-001" ], - "specId": "ADR-046-provider-runtime-azure-container-apps", - "topologicalRank": 11, - "wave": "W6" + "specId" : "ADR-046-provider-runtime-azure-container-apps", + "topologicalRank" : 11, + "wave" : "W6" }, { - "blockers": [], - "destinations": [ + "blockers" : [], + "destinations" : [ "`packages/d2b-provider-runtime-azure-container-apps/src/types.rs`" ], - "entryContracts": [ + "entryContracts" : [ "ADR-046-provider-runtime-azure-container-apps" ], - "exitGate": "ADR046-W6 exit criteria (ADR-046-validation-and-delivery §4): every spec/work item in this wave Merged with clean destinations, all validators green, and the ten-role panel seal recorded", - "id": "ADR046-aca-005", - "kind": "work-item", - "owner": "ADR046-aca-001; state/migration owner", - "parallelGroup": "wi:ADR-046-provider-runtime-azure-container-apps", - "prerequisites": [ + "exitGate" : "ADR046-W6 exit criteria (ADR-046-validation-and-delivery §4): every spec/work item in this wave Merged with clean destinations, all validators green, and the ten-role panel seal recorded", + "id" : "ADR046-aca-005", + "kind" : "work-item", + "owner" : "ADR046-aca-001; state/migration owner", + "parallelGroup" : "wi:ADR-046-provider-runtime-azure-container-apps", + "prerequisites" : [ "ADR-046-provider-runtime-azure-container-apps", "ADR046-aca-001" ], - "specId": "ADR-046-provider-runtime-azure-container-apps", - "topologicalRank": 12, - "wave": "W6" + "specId" : "ADR-046-provider-runtime-azure-container-apps", + "topologicalRank" : 12, + "wave" : "W6" }, { - "blockers": [], - "destinations": [ + "blockers" : [], + "destinations" : [ "`nixos-modules/` (generated Guest resource options); `packages/d2b-provider-runtime-azure-container-apps/src/{audit,metrics}.rs`" ], - "entryContracts": [ + "entryContracts" : [ "ADR-046-provider-runtime-azure-container-apps" ], - "exitGate": "ADR046-W6 exit criteria (ADR-046-validation-and-delivery §4): every spec/work item in this wave Merged with clean destinations, all validators green, and the ten-role panel seal recorded", - "id": "ADR046-aca-006", - "kind": "work-item", - "owner": "ADR046-aca-001; Nix/telemetry owner", - "parallelGroup": "wi:ADR-046-provider-runtime-azure-container-apps", - "prerequisites": [ + "exitGate" : "ADR046-W6 exit criteria (ADR-046-validation-and-delivery §4): every spec/work item in this wave Merged with clean destinations, all validators green, and the ten-role panel seal recorded", + "id" : "ADR046-aca-006", + "kind" : "work-item", + "owner" : "ADR046-aca-001; Nix/telemetry owner", + "parallelGroup" : "wi:ADR-046-provider-runtime-azure-container-apps", + "prerequisites" : [ "ADR-046-provider-runtime-azure-container-apps", "ADR046-aca-001" ], - "specId": "ADR-046-provider-runtime-azure-container-apps", - "topologicalRank": 12, - "wave": "W6" + "specId" : "ADR-046-provider-runtime-azure-container-apps", + "topologicalRank" : 12, + "wave" : "W6" }, { - "blockers": [], - "destinations": [ + "blockers" : [], + "destinations" : [ "`nixos-modules/` (gateway Guest declaration, Process template wiring, Credential scope assertion); eval-time validation module" ], - "entryContracts": [ + "entryContracts" : [ "ADR-046-provider-runtime-azure-container-apps" ], - "exitGate": "ADR046-W6 exit criteria (ADR-046-validation-and-delivery §4): every spec/work item in this wave Merged with clean destinations, all validators green, and the ten-role panel seal recorded", - "id": "ADR046-aca-007", - "kind": "work-item", - "owner": "ADR046-aca-001; Nix/gateway wiring owner", - "parallelGroup": "wi:ADR-046-provider-runtime-azure-container-apps", - "prerequisites": [ + "exitGate" : "ADR046-W6 exit criteria (ADR-046-validation-and-delivery §4): every spec/work item in this wave Merged with clean destinations, all validators green, and the ten-role panel seal recorded", + "id" : "ADR046-aca-007", + "kind" : "work-item", + "owner" : "ADR046-aca-001; Nix/gateway wiring owner", + "parallelGroup" : "wi:ADR-046-provider-runtime-azure-container-apps", + "prerequisites" : [ "ADR-046-provider-runtime-azure-container-apps", "ADR046-aca-001" ], - "specId": "ADR-046-provider-runtime-azure-container-apps", - "topologicalRank": 12, - "wave": "W6" + "specId" : "ADR-046-provider-runtime-azure-container-apps", + "topologicalRank" : 12, + "wave" : "W6" }, { - "blockers": [], - "destinations": [ + "blockers" : [], + "destinations" : [ "packages/d2b-host/src/bin/d2b-activation-helper.rs" ], - "entryContracts": [ + "entryContracts" : [ "ADR-046-provider-activation-nixos" ], - "exitGate": "ADR046-W6 exit criteria (ADR-046-validation-and-delivery §4): every spec/work item in this wave Merged with clean destinations, all validators green, and the ten-role panel seal recorded", - "id": "ADR046-activation-001", - "kind": "work-item", - "owner": "Provider/activation-nixos runner owner; reused helper owner in d2b-host", - "parallelGroup": "wi:ADR-046-provider-activation-nixos", - "prerequisites": [ + "exitGate" : "ADR046-W6 exit criteria (ADR-046-validation-and-delivery §4): every spec/work item in this wave Merged with clean destinations, all validators green, and the ten-role panel seal recorded", + "id" : "ADR046-activation-001", + "kind" : "work-item", + "owner" : "Provider/activation-nixos runner owner; reused helper owner in d2b-host", + "parallelGroup" : "wi:ADR-046-provider-activation-nixos", + "prerequisites" : [ "ADR-046-provider-activation-nixos" ], - "specId": "ADR-046-provider-activation-nixos", - "topologicalRank": 11, - "wave": "W6" + "specId" : "ADR-046-provider-activation-nixos", + "topologicalRank" : 11, + "wave" : "W6" }, { - "blockers": [], - "destinations": [ + "blockers" : [], + "destinations" : [ "docs/reference/schemas/v3/activation-nixos.d2bus.org.NixosGeneration.json and packages/d2b-contracts/src/activation_nixos.rs" ], - "entryContracts": [ + "entryContracts" : [ "ADR-046-provider-activation-nixos" ], - "exitGate": "ADR046-W6 exit criteria (ADR-046-validation-and-delivery §4): every spec/work item in this wave Merged with clean destinations, all validators green, and the ten-role panel seal recorded", - "id": "ADR046-activation-002", - "kind": "work-item", - "owner": "ADR-046-resource-object-model and ADR-046-core-controllers; d2b-contracts activation-nixos owner", - "parallelGroup": "wi:ADR-046-provider-activation-nixos", - "prerequisites": [ + "exitGate" : "ADR046-W6 exit criteria (ADR-046-validation-and-delivery §4): every spec/work item in this wave Merged with clean destinations, all validators green, and the ten-role panel seal recorded", + "id" : "ADR046-activation-002", + "kind" : "work-item", + "owner" : "ADR-046-resource-object-model and ADR-046-core-controllers; d2b-contracts activation-nixos owner", + "parallelGroup" : "wi:ADR-046-provider-activation-nixos", + "prerequisites" : [ "ADR-046-provider-activation-nixos" ], - "specId": "ADR-046-provider-activation-nixos", - "topologicalRank": 11, - "wave": "W6" + "specId" : "ADR-046-provider-activation-nixos", + "topologicalRank" : 11, + "wave" : "W6" }, { - "blockers": [], - "destinations": [ + "blockers" : [], + "destinations" : [ "packages/d2b-provider-activation-nixos/src/controller/" ], - "entryContracts": [ + "entryContracts" : [ "ADR-046-provider-activation-nixos" ], - "exitGate": "ADR046-W6 exit criteria (ADR-046-validation-and-delivery §4): every spec/work item in this wave Merged with clean destinations, all validators green, and the ten-role panel seal recorded", - "id": "ADR046-activation-003", - "kind": "work-item", - "owner": "ADR046-activation-002; activation-nixos controller owner", - "parallelGroup": "wi:ADR-046-provider-activation-nixos", - "prerequisites": [ + "exitGate" : "ADR046-W6 exit criteria (ADR-046-validation-and-delivery §4): every spec/work item in this wave Merged with clean destinations, all validators green, and the ten-role panel seal recorded", + "id" : "ADR046-activation-003", + "kind" : "work-item", + "owner" : "ADR046-activation-002; activation-nixos controller owner", + "parallelGroup" : "wi:ADR-046-provider-activation-nixos", + "prerequisites" : [ "ADR-046-provider-activation-nixos", "ADR046-activation-002" ], - "specId": "ADR-046-provider-activation-nixos", - "topologicalRank": 12, - "wave": "W6" + "specId" : "ADR-046-provider-activation-nixos", + "topologicalRank" : 12, + "wave" : "W6" }, { - "blockers": [], - "destinations": [ + "blockers" : [], + "destinations" : [ "packages/d2b-provider-activation-nixos/src/runner/" ], - "entryContracts": [ + "entryContracts" : [ "ADR-046-provider-activation-nixos" ], - "exitGate": "ADR046-W6 exit criteria (ADR-046-validation-and-delivery §4): every spec/work item in this wave Merged with clean destinations, all validators green, and the ten-role panel seal recorded", - "id": "ADR046-activation-004", - "kind": "work-item", - "owner": "ADR046-activation-003; activation-runner owner", - "parallelGroup": "wi:ADR-046-provider-activation-nixos", - "prerequisites": [ + "exitGate" : "ADR046-W6 exit criteria (ADR-046-validation-and-delivery §4): every spec/work item in this wave Merged with clean destinations, all validators green, and the ten-role panel seal recorded", + "id" : "ADR046-activation-004", + "kind" : "work-item", + "owner" : "ADR046-activation-003; activation-runner owner", + "parallelGroup" : "wi:ADR-046-provider-activation-nixos", + "prerequisites" : [ "ADR-046-provider-activation-nixos", "ADR046-activation-003" ], - "specId": "ADR-046-provider-activation-nixos", - "topologicalRank": 13, - "wave": "W6" + "specId" : "ADR-046-provider-activation-nixos", + "topologicalRank" : 13, + "wave" : "W6" }, { - "blockers": [], - "destinations": [ + "blockers" : [], + "destinations" : [ "packages/d2b/src/activation.rs" ], - "entryContracts": [ + "entryContracts" : [ "ADR-046-provider-activation-nixos" ], - "exitGate": "ADR046-W6 exit criteria (ADR-046-validation-and-delivery §4): every spec/work item in this wave Merged with clean destinations, all validators green, and the ten-role panel seal recorded", - "id": "ADR046-activation-005", - "kind": "work-item", - "owner": "ADR-046-cli-and-operations; activation CLI owner", - "parallelGroup": "wi:ADR-046-provider-activation-nixos", - "prerequisites": [ + "exitGate" : "ADR046-W6 exit criteria (ADR-046-validation-and-delivery §4): every spec/work item in this wave Merged with clean destinations, all validators green, and the ten-role panel seal recorded", + "id" : "ADR046-activation-005", + "kind" : "work-item", + "owner" : "ADR-046-cli-and-operations; activation CLI owner", + "parallelGroup" : "wi:ADR-046-provider-activation-nixos", + "prerequisites" : [ "ADR-046-provider-activation-nixos" ], - "specId": "ADR-046-provider-activation-nixos", - "topologicalRank": 11, - "wave": "W6" + "specId" : "ADR-046-provider-activation-nixos", + "topologicalRank" : 11, + "wave" : "W6" }, { - "blockers": [], - "destinations": [ + "blockers" : [], + "destinations" : [ "nixos-modules/providers/activation-nixos.nix" ], - "entryContracts": [ + "entryContracts" : [ "ADR-046-provider-activation-nixos" ], - "exitGate": "ADR046-W6 exit criteria (ADR-046-validation-and-delivery §4): every spec/work item in this wave Merged with clean destinations, all validators green, and the ten-role panel seal recorded", - "id": "ADR046-activation-006", - "kind": "work-item", - "owner": "ADR-046-nix-configuration; activation-nixos Nix owner", - "parallelGroup": "wi:ADR-046-provider-activation-nixos", - "prerequisites": [ + "exitGate" : "ADR046-W6 exit criteria (ADR-046-validation-and-delivery §4): every spec/work item in this wave Merged with clean destinations, all validators green, and the ten-role panel seal recorded", + "id" : "ADR046-activation-006", + "kind" : "work-item", + "owner" : "ADR-046-nix-configuration; activation-nixos Nix owner", + "parallelGroup" : "wi:ADR-046-provider-activation-nixos", + "prerequisites" : [ "ADR-046-provider-activation-nixos" ], - "specId": "ADR-046-provider-activation-nixos", - "topologicalRank": 11, - "wave": "W6" + "specId" : "ADR-046-provider-activation-nixos", + "topologicalRank" : 11, + "wave" : "W6" }, { - "blockers": [], - "destinations": [ + "blockers" : [], + "destinations" : [ "packages/d2b/src/lib.rs" ], - "entryContracts": [ + "entryContracts" : [ "ADR-046-provider-activation-nixos" ], - "exitGate": "ADR046-W6 exit criteria (ADR-046-validation-and-delivery §4): every spec/work item in this wave Merged with clean destinations, all validators green, and the ten-role panel seal recorded", - "id": "ADR046-activation-007", - "kind": "work-item", - "owner": "ADR046-activation-005; d2b CLI dispatcher owner", - "parallelGroup": "wi:ADR-046-provider-activation-nixos", - "prerequisites": [ + "exitGate" : "ADR046-W6 exit criteria (ADR-046-validation-and-delivery §4): every spec/work item in this wave Merged with clean destinations, all validators green, and the ten-role panel seal recorded", + "id" : "ADR046-activation-007", + "kind" : "work-item", + "owner" : "ADR046-activation-005; d2b CLI dispatcher owner", + "parallelGroup" : "wi:ADR-046-provider-activation-nixos", + "prerequisites" : [ "ADR-046-provider-activation-nixos", "ADR046-activation-005" ], - "specId": "ADR-046-provider-activation-nixos", - "topologicalRank": 12, - "wave": "W6" + "specId" : "ADR-046-provider-activation-nixos", + "topologicalRank" : 12, + "wave" : "W6" }, { - "blockers": [], - "destinations": [ + "blockers" : [], + "destinations" : [ "`packages/d2b-contracts/proto/d2b-resource-v3.proto`, `packages/d2b-resource-api/src/service.rs`, `client.rs`" ], - "entryContracts": [ + "entryContracts" : [ "ADR-046-resource-api-and-authorization" ], - "exitGate": "ADR046-W0 exit criteria (ADR-046-validation-and-delivery §4): every spec/work item in this wave Merged with clean destinations, all validators green, and the ten-role panel seal recorded", - "id": "ADR046-api-001", - "kind": "work-item", - "owner": "W0; resource API integrator", - "parallelGroup": "wi:ADR-046-resource-api-and-authorization", - "prerequisites": [ + "exitGate" : "ADR046-W0 exit criteria (ADR-046-validation-and-delivery §4): every spec/work item in this wave Merged with clean destinations, all validators green, and the ten-role panel seal recorded", + "id" : "ADR046-api-001", + "kind" : "work-item", + "owner" : "W0; resource API integrator", + "parallelGroup" : "wi:ADR-046-resource-api-and-authorization", + "prerequisites" : [ "ADR-046-resource-api-and-authorization" ], - "specId": "ADR-046-resource-api-and-authorization", - "topologicalRank": 5, - "wave": "W0" + "specId" : "ADR-046-resource-api-and-authorization", + "topologicalRank" : 5, + "wave" : "W0" }, { - "blockers": [], - "destinations": [ + "blockers" : [], + "destinations" : [ "`packages/d2b-resource-api/src/authz.rs`, `packages/d2b-core-controller/src/rbac.rs`" ], - "entryContracts": [ + "entryContracts" : [ "ADR-046-resource-api-and-authorization" ], - "exitGate": "ADR046-W0 exit criteria (ADR-046-validation-and-delivery §4): every spec/work item in this wave Merged with clean destinations, all validators green, and the ten-role panel seal recorded", - "id": "ADR046-api-002", - "kind": "work-item", - "owner": "ADR046-api-001; authorization integrator", - "parallelGroup": "wi:ADR-046-resource-api-and-authorization", - "prerequisites": [ + "exitGate" : "ADR046-W0 exit criteria (ADR-046-validation-and-delivery §4): every spec/work item in this wave Merged with clean destinations, all validators green, and the ten-role panel seal recorded", + "id" : "ADR046-api-002", + "kind" : "work-item", + "owner" : "ADR046-api-001; authorization integrator", + "parallelGroup" : "wi:ADR-046-resource-api-and-authorization", + "prerequisites" : [ "ADR-046-resource-api-and-authorization", "ADR046-api-001" ], - "specId": "ADR-046-resource-api-and-authorization", - "topologicalRank": 6, - "wave": "W0" + "specId" : "ADR-046-resource-api-and-authorization", + "topologicalRank" : 6, + "wave" : "W0" }, { - "blockers": [], - "destinations": [ + "blockers" : [], + "destinations" : [ "`packages/d2b-provider-audio-pipewire/src/audio_policy.rs`; re-exported from crate root" ], - "entryContracts": [ + "entryContracts" : [ "ADR-046-provider-audio-pipewire" ], - "exitGate": "ADR046-W6 exit criteria (ADR-046-validation-and-delivery §4): every spec/work item in this wave Merged with clean destinations, all validators green, and the ten-role panel seal recorded", - "id": "ADR046-audio-001", - "kind": "work-item", - "owner": "No prerequisites; `d2b-provider-audio-pipewire` crate", - "parallelGroup": "wi:ADR-046-provider-audio-pipewire", - "prerequisites": [ + "exitGate" : "ADR046-W6 exit criteria (ADR-046-validation-and-delivery §4): every spec/work item in this wave Merged with clean destinations, all validators green, and the ten-role panel seal recorded", + "id" : "ADR046-audio-001", + "kind" : "work-item", + "owner" : "No prerequisites; `d2b-provider-audio-pipewire` crate", + "parallelGroup" : "wi:ADR-046-provider-audio-pipewire", + "prerequisites" : [ "ADR-046-provider-audio-pipewire" ], - "specId": "ADR-046-provider-audio-pipewire", - "topologicalRank": 11, - "wave": "W6" + "specId" : "ADR-046-provider-audio-pipewire", + "topologicalRank" : 11, + "wave" : "W6" }, { - "blockers": [], - "destinations": [ + "blockers" : [], + "destinations" : [ "`packages/d2b-provider-audio-pipewire/src/argv.rs` (component template renderer)" ], - "entryContracts": [ + "entryContracts" : [ "ADR-046-provider-audio-pipewire" ], - "exitGate": "ADR046-W6 exit criteria (ADR-046-validation-and-delivery §4): every spec/work item in this wave Merged with clean destinations, all validators green, and the ten-role panel seal recorded", - "id": "ADR046-audio-002", - "kind": "work-item", - "owner": "Depends on `ADR046-audio-001`; Process Provider template schema", - "parallelGroup": "wi:ADR-046-provider-audio-pipewire", - "prerequisites": [ + "exitGate" : "ADR046-W6 exit criteria (ADR-046-validation-and-delivery §4): every spec/work item in this wave Merged with clean destinations, all validators green, and the ten-role panel seal recorded", + "id" : "ADR046-audio-002", + "kind" : "work-item", + "owner" : "Depends on `ADR046-audio-001`; Process Provider template schema", + "parallelGroup" : "wi:ADR-046-provider-audio-pipewire", + "prerequisites" : [ "ADR-046-provider-audio-pipewire", "ADR046-audio-001" ], - "specId": "ADR-046-provider-audio-pipewire", - "topologicalRank": 12, - "wave": "W6" + "specId" : "ADR-046-provider-audio-pipewire", + "topologicalRank" : 12, + "wave" : "W6" }, { - "blockers": [], - "destinations": [ + "blockers" : [], + "destinations" : [ "`packages/d2b-provider-audio-pipewire/src/mediator/enforcement.rs`" ], - "entryContracts": [ + "entryContracts" : [ "ADR-046-provider-audio-pipewire" ], - "exitGate": "ADR046-W6 exit criteria (ADR-046-validation-and-delivery §4): every spec/work item in this wave Merged with clean destinations, all validators green, and the ten-role panel seal recorded", - "id": "ADR046-audio-004", - "kind": "work-item", - "owner": "Depends on `ADR046-audio-001`; ComponentSession service contract; libpipewire 1.x API", - "parallelGroup": "wi:ADR-046-provider-audio-pipewire", - "prerequisites": [ + "exitGate" : "ADR046-W6 exit criteria (ADR-046-validation-and-delivery §4): every spec/work item in this wave Merged with clean destinations, all validators green, and the ten-role panel seal recorded", + "id" : "ADR046-audio-004", + "kind" : "work-item", + "owner" : "Depends on `ADR046-audio-001`; ComponentSession service contract; libpipewire 1.x API", + "parallelGroup" : "wi:ADR-046-provider-audio-pipewire", + "prerequisites" : [ "ADR-046-provider-audio-pipewire", "ADR046-audio-001" ], - "specId": "ADR-046-provider-audio-pipewire", - "topologicalRank": 12, - "wave": "W6" + "specId" : "ADR-046-provider-audio-pipewire", + "topologicalRank" : 12, + "wave" : "W6" }, { - "blockers": [], - "destinations": [ + "blockers" : [], + "destinations" : [ "`packages/d2b-provider-audio-pipewire/src/{resource_type,admission,provider_extension}.rs` (strict implementation extensions and binding only; common base lives under ADR046-provider-004)" ], - "entryContracts": [ + "entryContracts" : [ "ADR-046-provider-audio-pipewire" ], - "exitGate": "ADR046-W6 exit criteria (ADR-046-validation-and-delivery §4): every spec/work item in this wave Merged with clean destinations, all validators green, and the ten-role panel seal recorded", - "id": "ADR046-audio-005", - "kind": "work-item", - "owner": "ADR046-provider-004 common audio Service/Binding base; Core resource-api foundation; `d2b-provider-audio-pipewire` crate", - "parallelGroup": "wi:ADR-046-provider-audio-pipewire", - "prerequisites": [ + "exitGate" : "ADR046-W6 exit criteria (ADR-046-validation-and-delivery §4): every spec/work item in this wave Merged with clean destinations, all validators green, and the ten-role panel seal recorded", + "id" : "ADR046-audio-005", + "kind" : "work-item", + "owner" : "ADR046-provider-004 common audio Service/Binding base; Core resource-api foundation; `d2b-provider-audio-pipewire` crate", + "parallelGroup" : "wi:ADR-046-provider-audio-pipewire", + "prerequisites" : [ "ADR-046-provider-audio-pipewire", "ADR046-provider-004" ], - "specId": "ADR-046-provider-audio-pipewire", - "topologicalRank": 11, - "wave": "W6" + "specId" : "ADR-046-provider-audio-pipewire", + "topologicalRank" : 11, + "wave" : "W6" }, { - "blockers": [], - "destinations": [ + "blockers" : [], + "destinations" : [ "`packages/d2b-provider-audio-pipewire/src/controller/audio_service.rs`; `src/controller/audio_binding.rs`" ], - "entryContracts": [ + "entryContracts" : [ "ADR-046-provider-audio-pipewire" ], - "exitGate": "ADR046-W6 exit criteria (ADR-046-validation-and-delivery §4): every spec/work item in this wave Merged with clean destinations, all validators green, and the ten-role panel seal recorded", - "id": "ADR046-audio-006", - "kind": "work-item", - "owner": "Depends on `ADR046-audio-001` through `ADR046-audio-005`; core ResourceImport controller; system Process Provider; AudioMediator (`ADR046-audio-007`); GuestAudioAgent (`ADR046-audio-011`); no Provider state Volume under D087", - "parallelGroup": "wi:ADR-046-provider-audio-pipewire", - "prerequisites": [ + "exitGate" : "ADR046-W6 exit criteria (ADR-046-validation-and-delivery §4): every spec/work item in this wave Merged with clean destinations, all validators green, and the ten-role panel seal recorded", + "id" : "ADR046-audio-006", + "kind" : "work-item", + "owner" : "Depends on `ADR046-audio-001` through `ADR046-audio-005`; core ResourceImport controller; system Process Provider; AudioMediator (`ADR046-audio-007`); GuestAudioAgent (`ADR046-audio-011`); no Provider state Volume under D087", + "parallelGroup" : "wi:ADR-046-provider-audio-pipewire", + "prerequisites" : [ "ADR-046-provider-audio-pipewire", "ADR046-audio-001", "ADR046-audio-005", "ADR046-audio-007", "ADR046-audio-011" ], - "specId": "ADR-046-provider-audio-pipewire", - "topologicalRank": 14, - "wave": "W6" + "specId" : "ADR-046-provider-audio-pipewire", + "topologicalRank" : 14, + "wave" : "W6" }, { - "blockers": [], - "destinations": [ + "blockers" : [], + "destinations" : [ "`packages/d2b-provider-audio-pipewire/src/mediator/mod.rs`; `src/bin/audio_pipewire_mediator.rs`" ], - "entryContracts": [ + "entryContracts" : [ "ADR-046-provider-audio-pipewire" ], - "exitGate": "ADR046-W6 exit criteria (ADR-046-validation-and-delivery §4): every spec/work item in this wave Merged with clean destinations, all validators green, and the ten-role panel seal recorded", - "id": "ADR046-audio-007", - "kind": "work-item", - "owner": "Depends on `ADR046-audio-004`; ComponentSession service (ADR-046-componentsession-and-bus); libpipewire 1.x", - "parallelGroup": "wi:ADR-046-provider-audio-pipewire", - "prerequisites": [ + "exitGate" : "ADR046-W6 exit criteria (ADR-046-validation-and-delivery §4): every spec/work item in this wave Merged with clean destinations, all validators green, and the ten-role panel seal recorded", + "id" : "ADR046-audio-007", + "kind" : "work-item", + "owner" : "Depends on `ADR046-audio-004`; ComponentSession service (ADR-046-componentsession-and-bus); libpipewire 1.x", + "parallelGroup" : "wi:ADR-046-provider-audio-pipewire", + "prerequisites" : [ "ADR-046-provider-audio-pipewire", "ADR046-audio-004" ], - "specId": "ADR-046-provider-audio-pipewire", - "topologicalRank": 13, - "wave": "W6" + "specId" : "ADR-046-provider-audio-pipewire", + "topologicalRank" : 13, + "wave" : "W6" }, { - "blockers": [], - "destinations": [ + "blockers" : [], + "destinations" : [ "`nixos-modules/components/audio/v3-resource.nix`; `nixos-modules/components/audio/host-config.nix`; `nixos-modules/components/audio/guest-config.nix`" ], - "entryContracts": [ + "entryContracts" : [ "ADR-046-provider-audio-pipewire" ], - "exitGate": "ADR046-W6 exit criteria (ADR-046-validation-and-delivery §4): every spec/work item in this wave Merged with clean destinations, all validators green, and the ten-role panel seal recorded", - "id": "ADR046-audio-008", - "kind": "work-item", - "owner": "Depends on `ADR046-audio-005`; Nix resource compilation framework; `ADR-046-nix-configuration`", - "parallelGroup": "wi:ADR-046-provider-audio-pipewire", - "prerequisites": [ + "exitGate" : "ADR046-W6 exit criteria (ADR-046-validation-and-delivery §4): every spec/work item in this wave Merged with clean destinations, all validators green, and the ten-role panel seal recorded", + "id" : "ADR046-audio-008", + "kind" : "work-item", + "owner" : "Depends on `ADR046-audio-005`; Nix resource compilation framework; `ADR-046-nix-configuration`", + "parallelGroup" : "wi:ADR-046-provider-audio-pipewire", + "prerequisites" : [ "ADR-046-provider-audio-pipewire", "ADR046-audio-005" ], - "specId": "ADR-046-provider-audio-pipewire", - "topologicalRank": 12, - "wave": "W6" + "specId" : "ADR-046-provider-audio-pipewire", + "topologicalRank" : 12, + "wave" : "W6" }, { - "blockers": [], - "destinations": [ + "blockers" : [], + "destinations" : [ "`packages/d2b-provider-audio-pipewire/tests/minijail_contract.rs` (provider-local); retain cross-bundle source greps in `d2b-contract-tests`" ], - "entryContracts": [ + "entryContracts" : [ "ADR-046-provider-audio-pipewire" ], - "exitGate": "ADR046-W6 exit criteria (ADR-046-validation-and-delivery §4): every spec/work item in this wave Merged with clean destinations, all validators green, and the ten-role panel seal recorded", - "id": "ADR046-audio-009", - "kind": "work-item", - "owner": "Depends on `ADR046-audio-005`; `d2b-contract-tests` crate", - "parallelGroup": "wi:ADR-046-provider-audio-pipewire", - "prerequisites": [ + "exitGate" : "ADR046-W6 exit criteria (ADR-046-validation-and-delivery §4): every spec/work item in this wave Merged with clean destinations, all validators green, and the ten-role panel seal recorded", + "id" : "ADR046-audio-009", + "kind" : "work-item", + "owner" : "Depends on `ADR046-audio-005`; `d2b-contract-tests` crate", + "parallelGroup" : "wi:ADR-046-provider-audio-pipewire", + "prerequisites" : [ "ADR-046-provider-audio-pipewire", "ADR046-audio-005" ], - "specId": "ADR-046-provider-audio-pipewire", - "topologicalRank": 12, - "wave": "W6" + "specId" : "ADR-046-provider-audio-pipewire", + "topologicalRank" : 12, + "wave" : "W6" }, { - "blockers": [], - "destinations": [ + "blockers" : [], + "destinations" : [ "`packages/d2b-provider-audio-pipewire/src/telemetry.rs`" ], - "entryContracts": [ + "entryContracts" : [ "ADR-046-provider-audio-pipewire" ], - "exitGate": "ADR046-W6 exit criteria (ADR-046-validation-and-delivery §4): every spec/work item in this wave Merged with clean destinations, all validators green, and the ten-role panel seal recorded", - "id": "ADR046-audio-010", - "kind": "work-item", - "owner": "Depends on `ADR046-audio-006`, `ADR046-audio-007`, `ADR046-audio-011`; `d2b-telemetry` lightweight emitter", - "parallelGroup": "wi:ADR-046-provider-audio-pipewire", - "prerequisites": [ + "exitGate" : "ADR046-W6 exit criteria (ADR-046-validation-and-delivery §4): every spec/work item in this wave Merged with clean destinations, all validators green, and the ten-role panel seal recorded", + "id" : "ADR046-audio-010", + "kind" : "work-item", + "owner" : "Depends on `ADR046-audio-006`, `ADR046-audio-007`, `ADR046-audio-011`; `d2b-telemetry` lightweight emitter", + "parallelGroup" : "wi:ADR-046-provider-audio-pipewire", + "prerequisites" : [ "ADR-046-provider-audio-pipewire", "ADR046-audio-006", "ADR046-audio-007", "ADR046-audio-011" ], - "specId": "ADR-046-provider-audio-pipewire", - "topologicalRank": 15, - "wave": "W6" + "specId" : "ADR-046-provider-audio-pipewire", + "topologicalRank" : 15, + "wave" : "W6" }, { - "blockers": [], - "destinations": [ + "blockers" : [], + "destinations" : [ "`packages/d2b-provider-audio-pipewire/src/guest_agent/mod.rs`; `src/guest_agent/enforcement.rs`; `src/bin/audio_pipewire_guest_agent.rs`" ], - "entryContracts": [ + "entryContracts" : [ "ADR-046-provider-audio-pipewire" ], - "exitGate": "ADR046-W6 exit criteria (ADR-046-validation-and-delivery §4): every spec/work item in this wave Merged with clean destinations, all validators green, and the ten-role panel seal recorded", - "id": "ADR046-audio-011", - "kind": "work-item", - "owner": "Depends on `ADR046-audio-004`; ComponentSession service contract; libpipewire 1.x; system-systemd Process Provider for guest domain", - "parallelGroup": "wi:ADR-046-provider-audio-pipewire", - "prerequisites": [ + "exitGate" : "ADR046-W6 exit criteria (ADR-046-validation-and-delivery §4): every spec/work item in this wave Merged with clean destinations, all validators green, and the ten-role panel seal recorded", + "id" : "ADR046-audio-011", + "kind" : "work-item", + "owner" : "Depends on `ADR046-audio-004`; ComponentSession service contract; libpipewire 1.x; system-systemd Process Provider for guest domain", + "parallelGroup" : "wi:ADR-046-provider-audio-pipewire", + "prerequisites" : [ "ADR-046-provider-audio-pipewire", "ADR046-audio-004" ], - "specId": "ADR-046-provider-audio-pipewire", - "topologicalRank": 13, - "wave": "W6" + "specId" : "ADR-046-provider-audio-pipewire", + "topologicalRank" : 13, + "wave" : "W6" }, { - "blockers": [], - "destinations": [ + "blockers" : [], + "destinations" : [ "`packages/d2b-provider-audio-pipewire/src/share_adapter.rs`" ], - "entryContracts": [ + "entryContracts" : [ "ADR-046-provider-audio-pipewire" ], - "exitGate": "ADR046-W6 exit criteria (ADR-046-validation-and-delivery §4): every spec/work item in this wave Merged with clean destinations, all validators green, and the ten-role panel seal recorded", - "id": "ADR046-audio-012", - "kind": "work-item", - "owner": "ADR046-zone-control-019, ADR046-zone-control-020; audio Provider owner", - "parallelGroup": "wi:ADR-046-provider-audio-pipewire", - "prerequisites": [ + "exitGate" : "ADR046-W6 exit criteria (ADR-046-validation-and-delivery §4): every spec/work item in this wave Merged with clean destinations, all validators green, and the ten-role panel seal recorded", + "id" : "ADR046-audio-012", + "kind" : "work-item", + "owner" : "ADR046-zone-control-019, ADR046-zone-control-020; audio Provider owner", + "parallelGroup" : "wi:ADR-046-provider-audio-pipewire", + "prerequisites" : [ "ADR-046-provider-audio-pipewire", "ADR046-zone-control-019", "ADR046-zone-control-020" ], - "specId": "ADR-046-provider-audio-pipewire", - "topologicalRank": 11, - "wave": "W6" + "specId" : "ADR-046-provider-audio-pipewire", + "topologicalRank" : 13, + "wave" : "W6" }, { - "blockers": [], - "destinations": [ + "blockers" : [], + "destinations" : [ "`packages/d2b-provider-audio-pipewire/src/authority.rs` (speaker mixer + mic arbiter); `AuthorityDescriptor` on owner `AudioService`" ], - "entryContracts": [ + "entryContracts" : [ "ADR-046-provider-audio-pipewire" ], - "exitGate": "ADR046-W6 exit criteria (ADR-046-validation-and-delivery §4): every spec/work item in this wave Merged with clean destinations, all validators green, and the ten-role panel seal recorded", - "id": "ADR046-audio-013", - "kind": "work-item", - "owner": "Depends on `ADR046-audio-001`, `ADR046-audio-004`, `ADR046-zone-control-019`; audio Provider owner", - "parallelGroup": "wi:ADR-046-provider-audio-pipewire", - "prerequisites": [ + "exitGate" : "ADR046-W6 exit criteria (ADR-046-validation-and-delivery §4): every spec/work item in this wave Merged with clean destinations, all validators green, and the ten-role panel seal recorded", + "id" : "ADR046-audio-013", + "kind" : "work-item", + "owner" : "Depends on `ADR046-audio-001`, `ADR046-audio-004`, `ADR046-zone-control-019`; audio Provider owner", + "parallelGroup" : "wi:ADR-046-provider-audio-pipewire", + "prerequisites" : [ "ADR-046-provider-audio-pipewire", "ADR046-audio-001", "ADR046-audio-004", "ADR046-zone-control-019" ], - "specId": "ADR-046-provider-audio-pipewire", - "topologicalRank": 13, - "wave": "W6" + "specId" : "ADR-046-provider-audio-pipewire", + "topologicalRank" : 13, + "wave" : "W6" }, { - "blockers": [], - "destinations": [ + "blockers" : [], + "destinations" : [ "`packages/d2b-provider-audio-pipewire/src/streams.rs`" ], - "entryContracts": [ + "entryContracts" : [ "ADR-046-provider-audio-pipewire" ], - "exitGate": "ADR046-W6 exit criteria (ADR-046-validation-and-delivery §4): every spec/work item in this wave Merged with clean destinations, all validators green, and the ten-role panel seal recorded", - "id": "ADR046-audio-014", - "kind": "work-item", - "owner": "Depends on `ADR046-audio-013`, `ADR046-zone-control-019`; audio Provider owner", - "parallelGroup": "wi:ADR-046-provider-audio-pipewire", - "prerequisites": [ + "exitGate" : "ADR046-W6 exit criteria (ADR-046-validation-and-delivery §4): every spec/work item in this wave Merged with clean destinations, all validators green, and the ten-role panel seal recorded", + "id" : "ADR046-audio-014", + "kind" : "work-item", + "owner" : "Depends on `ADR046-audio-013`, `ADR046-zone-control-019`; audio Provider owner", + "parallelGroup" : "wi:ADR-046-provider-audio-pipewire", + "prerequisites" : [ "ADR-046-provider-audio-pipewire", "ADR046-audio-013", "ADR046-zone-control-019" ], - "specId": "ADR-046-provider-audio-pipewire", - "topologicalRank": 14, - "wave": "W6" - }, - { - "blockers": [], - "destinations": [ - "`packages/d2b-audit/src/{hash_chain.rs,segment.rs,rate_limit.rs,record_types.rs,sink.rs,export.rs}`" - ], - "entryContracts": [ - "ADR-046-telemetry-audit-and-support" - ], - "exitGate": "ADR046-W5 exit criteria (ADR-046-validation-and-delivery §4): every spec/work item in this wave Merged with clean destinations, all validators green, and the ten-role panel seal recorded", - "id": "ADR046-audit-001", - "kind": "work-item", - "owner": "W0/W1a; audit crate owner", - "parallelGroup": "wi:ADR-046-telemetry-audit-and-support", - "prerequisites": [ - "ADR-046-telemetry-audit-and-support" - ], - "specId": "ADR-046-telemetry-audit-and-support", - "topologicalRank": 10, - "wave": "W5" - }, - { - "blockers": [], - "destinations": [ - "`packages/d2b-resource-store-redb/src/audit.rs`, `packages/d2b-core-controller/src/authz_audit.rs`" - ], - "entryContracts": [ - "ADR-046-telemetry-audit-and-support" - ], - "exitGate": "ADR046-W5 exit criteria (ADR-046-validation-and-delivery §4): every spec/work item in this wave Merged with clean destinations, all validators green, and the ten-role panel seal recorded", - "id": "ADR046-audit-002", - "kind": "work-item", - "owner": "ADR046-audit-001 + ADR046-store-001; store/authz owner", - "parallelGroup": "wi:ADR-046-telemetry-audit-and-support", - "prerequisites": [ - "ADR-046-telemetry-audit-and-support", - "ADR046-audit-001", - "ADR046-store-001" - ], - "specId": "ADR-046-telemetry-audit-and-support", - "topologicalRank": 11, - "wave": "W5" - }, - { - "blockers": [], - "destinations": [ - "`packages/d2b-session/src/audit.rs`, `packages/d2b-bus/src/audit.rs`" - ], - "entryContracts": [ - "ADR-046-telemetry-audit-and-support" - ], - "exitGate": "ADR046-W5 exit criteria (ADR-046-validation-and-delivery §4): every spec/work item in this wave Merged with clean destinations, all validators green, and the ten-role panel seal recorded", - "id": "ADR046-audit-003", - "kind": "work-item", - "owner": "ADR046-audit-001 + ADR046-session-001 + ADR046-bus-001; session/bus owner", - "parallelGroup": "wi:ADR-046-telemetry-audit-and-support", - "prerequisites": [ - "ADR-046-telemetry-audit-and-support", - "ADR046-audit-001", - "ADR046-bus-001", - "ADR046-session-001" - ], - "specId": "ADR-046-telemetry-audit-and-support", - "topologicalRank": 11, - "wave": "W5" - }, - { - "blockers": [], - "destinations": [ - "`packages/d2b/src/zone_audit.rs` (new `d2b zone audit export` subcommand); `packages/d2b/tests/zone_audit_contract.rs`" - ], - "entryContracts": [ - "ADR-046-telemetry-audit-and-support" - ], - "exitGate": "ADR046-W5 exit criteria (ADR-046-validation-and-delivery §4): every spec/work item in this wave Merged with clean destinations, all validators green, and the ten-role panel seal recorded", - "id": "ADR046-audit-004", - "kind": "work-item", - "owner": "ADR046-audit-001; CLI owner", - "parallelGroup": "wi:ADR-046-telemetry-audit-and-support", - "prerequisites": [ - "ADR-046-telemetry-audit-and-support", - "ADR046-audit-001" - ], - "specId": "ADR-046-telemetry-audit-and-support", - "topologicalRank": 11, - "wave": "W5" + "specId" : "ADR-046-provider-audio-pipewire", + "topologicalRank" : 14, + "wave" : "W6" }, { - "blockers": [], - "destinations": [ + "blockers" : [], + "destinations" : [ "`src/{lib.rs,config.rs,schema.rs,error.rs,effect/mod.rs}`" ], - "entryContracts": [ + "entryContracts" : [ "ADR-046-provider-runtime-azure-virtual-machine" ], - "exitGate": "ADR046-W6 exit criteria (ADR-046-validation-and-delivery §4): every spec/work item in this wave Merged with clean destinations, all validators green, and the ten-role panel seal recorded", - "id": "ADR046-azure-vm-001", - "kind": "work-item", - "owner": "Provider contract owner", - "parallelGroup": "wi:ADR-046-provider-runtime-azure-virtual-machine", - "prerequisites": [ + "exitGate" : "ADR046-W6 exit criteria (ADR-046-validation-and-delivery §4): every spec/work item in this wave Merged with clean destinations, all validators green, and the ten-role panel seal recorded", + "id" : "ADR046-azure-vm-001", + "kind" : "work-item", + "owner" : "Provider contract owner", + "parallelGroup" : "wi:ADR-046-provider-runtime-azure-virtual-machine", + "prerequisites" : [ "ADR-046-provider-runtime-azure-virtual-machine" ], - "specId": "ADR-046-provider-runtime-azure-virtual-machine", - "topologicalRank": 11, - "wave": "W6" + "specId" : "ADR-046-provider-runtime-azure-virtual-machine", + "topologicalRank" : 11, + "wave" : "W6" }, { - "blockers": [], - "destinations": [ + "blockers" : [], + "destinations" : [ "`src/effect/{mod.rs,real.rs,fake.rs,rate_limit.rs}`" ], - "entryContracts": [ + "entryContracts" : [ "ADR-046-provider-runtime-azure-virtual-machine" ], - "exitGate": "ADR046-W6 exit criteria (ADR-046-validation-and-delivery §4): every spec/work item in this wave Merged with clean destinations, all validators green, and the ten-role panel seal recorded", - "id": "ADR046-azure-vm-002", - "kind": "work-item", - "owner": "ADR046-azure-vm-001", - "parallelGroup": "wi:ADR-046-provider-runtime-azure-virtual-machine", - "prerequisites": [ - "ADR-046-provider-runtime-azure-virtual-machine" + "exitGate" : "ADR046-W6 exit criteria (ADR-046-validation-and-delivery §4): every spec/work item in this wave Merged with clean destinations, all validators green, and the ten-role panel seal recorded", + "id" : "ADR046-azure-vm-002", + "kind" : "work-item", + "owner" : "ADR046-azure-vm-001", + "parallelGroup" : "wi:ADR-046-provider-runtime-azure-virtual-machine", + "prerequisites" : [ + "ADR-046-provider-runtime-azure-virtual-machine", + "ADR046-azure-vm-001" ], - "specId": "ADR-046-provider-runtime-azure-virtual-machine", - "topologicalRank": 11, - "wave": "W6" + "specId" : "ADR-046-provider-runtime-azure-virtual-machine", + "topologicalRank" : 12, + "wave" : "W6" }, { - "blockers": [], - "destinations": [ + "blockers" : [], + "destinations" : [ "`src/controller/{mod.rs,lifecycle.rs,idempotency.rs}`" ], - "entryContracts": [ + "entryContracts" : [ "ADR-046-provider-runtime-azure-virtual-machine" ], - "exitGate": "ADR046-W6 exit criteria (ADR-046-validation-and-delivery §4): every spec/work item in this wave Merged with clean destinations, all validators green, and the ten-role panel seal recorded", - "id": "ADR046-azure-vm-003", - "kind": "work-item", - "owner": "ADR046-azure-vm-001; ADR046-azure-vm-002; Guest ResourceType controller contract", - "parallelGroup": "wi:ADR-046-provider-runtime-azure-virtual-machine", - "prerequisites": [ - "ADR-046-provider-runtime-azure-virtual-machine" + "exitGate" : "ADR046-W6 exit criteria (ADR-046-validation-and-delivery §4): every spec/work item in this wave Merged with clean destinations, all validators green, and the ten-role panel seal recorded", + "id" : "ADR046-azure-vm-003", + "kind" : "work-item", + "owner" : "ADR046-azure-vm-001; ADR046-azure-vm-002; Guest ResourceType controller contract", + "parallelGroup" : "wi:ADR-046-provider-runtime-azure-virtual-machine", + "prerequisites" : [ + "ADR-046-provider-runtime-azure-virtual-machine", + "ADR046-azure-vm-001", + "ADR046-azure-vm-002" ], - "specId": "ADR-046-provider-runtime-azure-virtual-machine", - "topologicalRank": 11, - "wave": "W6" + "specId" : "ADR-046-provider-runtime-azure-virtual-machine", + "topologicalRank" : 13, + "wave" : "W6" }, { - "blockers": [], - "destinations": [ + "blockers" : [], + "destinations" : [ "`src/controller/bootstrap.rs`; `src/bootstrap_svc/{mod.rs,admission.rs,enrollment.rs}`" ], - "entryContracts": [ + "entryContracts" : [ "ADR-046-provider-runtime-azure-virtual-machine" ], - "exitGate": "ADR046-W6 exit criteria (ADR-046-validation-and-delivery §4): every spec/work item in this wave Merged with clean destinations, all validators green, and the ten-role panel seal recorded", - "id": "ADR046-azure-vm-004", - "kind": "work-item", - "owner": "ADR046-azure-vm-001; ComponentSession IKpsk2", - "parallelGroup": "wi:ADR-046-provider-runtime-azure-virtual-machine", - "prerequisites": [ - "ADR-046-provider-runtime-azure-virtual-machine" + "exitGate" : "ADR046-W6 exit criteria (ADR-046-validation-and-delivery §4): every spec/work item in this wave Merged with clean destinations, all validators green, and the ten-role panel seal recorded", + "id" : "ADR046-azure-vm-004", + "kind" : "work-item", + "owner" : "ADR046-azure-vm-001; ComponentSession IKpsk2", + "parallelGroup" : "wi:ADR-046-provider-runtime-azure-virtual-machine", + "prerequisites" : [ + "ADR-046-provider-runtime-azure-virtual-machine", + "ADR046-azure-vm-001" ], - "specId": "ADR-046-provider-runtime-azure-virtual-machine", - "topologicalRank": 11, - "wave": "W6" + "specId" : "ADR-046-provider-runtime-azure-virtual-machine", + "topologicalRank" : 12, + "wave" : "W6" }, { - "blockers": [], - "destinations": [ + "blockers" : [], + "destinations" : [ "`src/credential.rs`" ], - "entryContracts": [ + "entryContracts" : [ "ADR-046-provider-runtime-azure-virtual-machine" ], - "exitGate": "ADR046-W6 exit criteria (ADR-046-validation-and-delivery §4): every spec/work item in this wave Merged with clean destinations, all validators green, and the ten-role panel seal recorded", - "id": "ADR046-azure-vm-005", - "kind": "work-item", - "owner": "ADR046-azure-vm-003; Credential ResourceType; D055/D056", - "parallelGroup": "wi:ADR-046-provider-runtime-azure-virtual-machine", - "prerequisites": [ - "ADR-046-provider-runtime-azure-virtual-machine" + "exitGate" : "ADR046-W6 exit criteria (ADR-046-validation-and-delivery §4): every spec/work item in this wave Merged with clean destinations, all validators green, and the ten-role panel seal recorded", + "id" : "ADR046-azure-vm-005", + "kind" : "work-item", + "owner" : "ADR046-azure-vm-003; Credential ResourceType; D055/D056", + "parallelGroup" : "wi:ADR-046-provider-runtime-azure-virtual-machine", + "prerequisites" : [ + "ADR-046-provider-runtime-azure-virtual-machine", + "ADR046-azure-vm-003" ], - "specId": "ADR-046-provider-runtime-azure-virtual-machine", - "topologicalRank": 11, - "wave": "W6" + "specId" : "ADR-046-provider-runtime-azure-virtual-machine", + "topologicalRank" : 14, + "wave" : "W6" }, { - "blockers": [], - "destinations": [ + "blockers" : [], + "destinations" : [ "`src/controller/idempotency.rs`" ], - "entryContracts": [ + "entryContracts" : [ "ADR-046-provider-runtime-azure-virtual-machine" ], - "exitGate": "ADR046-W6 exit criteria (ADR-046-validation-and-delivery §4): every spec/work item in this wave Merged with clean destinations, all validators green, and the ten-role panel seal recorded", - "id": "ADR046-azure-vm-006", - "kind": "work-item", - "owner": "ADR046-azure-vm-003", - "parallelGroup": "wi:ADR-046-provider-runtime-azure-virtual-machine", - "prerequisites": [ - "ADR-046-provider-runtime-azure-virtual-machine" + "exitGate" : "ADR046-W6 exit criteria (ADR-046-validation-and-delivery §4): every spec/work item in this wave Merged with clean destinations, all validators green, and the ten-role panel seal recorded", + "id" : "ADR046-azure-vm-006", + "kind" : "work-item", + "owner" : "ADR046-azure-vm-003", + "parallelGroup" : "wi:ADR-046-provider-runtime-azure-virtual-machine", + "prerequisites" : [ + "ADR-046-provider-runtime-azure-virtual-machine", + "ADR046-azure-vm-003" ], - "specId": "ADR-046-provider-runtime-azure-virtual-machine", - "topologicalRank": 11, - "wave": "W6" + "specId" : "ADR-046-provider-runtime-azure-virtual-machine", + "topologicalRank" : 14, + "wave" : "W6" }, { - "blockers": [], - "destinations": [ + "blockers" : [], + "destinations" : [ "`nixos-modules/` (Provider/Guest resource emitters); crate Nix build" ], - "entryContracts": [ + "entryContracts" : [ "ADR-046-provider-runtime-azure-virtual-machine" ], - "exitGate": "ADR046-W6 exit criteria (ADR-046-validation-and-delivery §4): every spec/work item in this wave Merged with clean destinations, all validators green, and the ten-role panel seal recorded", - "id": "ADR046-azure-vm-007", - "kind": "work-item", - "owner": "ADR046-azure-vm-001; ADR-046-provider-state; ADR-046-nix-configuration", - "parallelGroup": "wi:ADR-046-provider-runtime-azure-virtual-machine", - "prerequisites": [ - "ADR-046-provider-runtime-azure-virtual-machine" + "exitGate" : "ADR046-W6 exit criteria (ADR-046-validation-and-delivery §4): every spec/work item in this wave Merged with clean destinations, all validators green, and the ten-role panel seal recorded", + "id" : "ADR046-azure-vm-007", + "kind" : "work-item", + "owner" : "ADR046-azure-vm-001; ADR-046-provider-state; ADR-046-nix-configuration", + "parallelGroup" : "wi:ADR-046-provider-runtime-azure-virtual-machine", + "prerequisites" : [ + "ADR-046-provider-runtime-azure-virtual-machine", + "ADR046-azure-vm-001" ], - "specId": "ADR-046-provider-runtime-azure-virtual-machine", - "topologicalRank": 11, - "wave": "W6" + "specId" : "ADR-046-provider-runtime-azure-virtual-machine", + "topologicalRank" : 12, + "wave" : "W6" }, { - "blockers": [], - "destinations": [ + "blockers" : [], + "destinations" : [ "`src/{telemetry.rs,audit.rs}`" ], - "entryContracts": [ + "entryContracts" : [ "ADR-046-provider-runtime-azure-virtual-machine" ], - "exitGate": "ADR046-W6 exit criteria (ADR-046-validation-and-delivery §4): every spec/work item in this wave Merged with clean destinations, all validators green, and the ten-role panel seal recorded", - "id": "ADR046-azure-vm-008", - "kind": "work-item", - "owner": "ADR046-azure-vm-003; ADR-046-telemetry-audit-and-support", - "parallelGroup": "wi:ADR-046-provider-runtime-azure-virtual-machine", - "prerequisites": [ - "ADR-046-provider-runtime-azure-virtual-machine" + "exitGate" : "ADR046-W6 exit criteria (ADR-046-validation-and-delivery §4): every spec/work item in this wave Merged with clean destinations, all validators green, and the ten-role panel seal recorded", + "id" : "ADR046-azure-vm-008", + "kind" : "work-item", + "owner" : "ADR046-azure-vm-003; ADR-046-telemetry-audit-and-support", + "parallelGroup" : "wi:ADR-046-provider-runtime-azure-virtual-machine", + "prerequisites" : [ + "ADR-046-provider-runtime-azure-virtual-machine", + "ADR046-azure-vm-003" ], - "specId": "ADR-046-provider-runtime-azure-virtual-machine", - "topologicalRank": 11, - "wave": "W6" + "specId" : "ADR-046-provider-runtime-azure-virtual-machine", + "topologicalRank" : 14, + "wave" : "W6" }, { - "blockers": [], - "destinations": [ + "blockers" : [], + "destinations" : [ "`tests/`; `integration/`" ], - "entryContracts": [ - "ADR-046-provider-runtime-azure-virtual-machine" - ], - "exitGate": "ADR046-W6 exit criteria (ADR-046-validation-and-delivery §4): every spec/work item in this wave Merged with clean destinations, all validators green, and the ten-role panel seal recorded", - "id": "ADR046-azure-vm-009", - "kind": "work-item", - "owner": "All ADR046-azure-vm-*", - "parallelGroup": "wi:ADR-046-provider-runtime-azure-virtual-machine", - "prerequisites": [ + "entryContracts" : [ "ADR-046-provider-runtime-azure-virtual-machine" ], - "specId": "ADR-046-provider-runtime-azure-virtual-machine", - "topologicalRank": 11, - "wave": "W6" - }, - { - "blockers": [], - "destinations": [ - "`packages/d2b-bus/src/{router,registry,authorization,streams,operations}.rs`" - ], - "entryContracts": [ - "ADR-046-componentsession-and-bus" - ], - "exitGate": "ADR046-W1 exit criteria (ADR-046-validation-and-delivery §4): every spec/work item in this wave Merged with clean destinations, all validators green, and the ten-role panel seal recorded", - "id": "ADR046-bus-001", - "kind": "work-item", - "owner": "Sessions + resource API; bus owner", - "parallelGroup": "wi:ADR-046-componentsession-and-bus", - "prerequisites": [ - "ADR-046-componentsession-and-bus" - ], - "specId": "ADR-046-componentsession-and-bus", - "topologicalRank": 6, - "wave": "W1" - }, - { - "blockers": [], - "destinations": [ - "`packages/d2b-client/src/` (adapt in place)." - ], - "entryContracts": [ - "ADR-046-nix-configuration" - ], - "exitGate": "ADR046-W5 exit criteria (ADR-046-validation-and-delivery §4): every spec/work item in this wave Merged with clean destinations, all validators green, and the ten-role panel seal recorded", - "id": "ADR046-bus-009", - "kind": "work-item", - "owner": "ADR046-nix-029; ADR046-nix-027; TargetInput v3 shape per ADR-046-componentsession-and-bus owning spec", - "parallelGroup": "wi:ADR-046-nix-configuration", - "prerequisites": [ - "ADR-046-nix-configuration", - "ADR046-nix-027", - "ADR046-nix-029" - ], - "specId": "ADR-046-nix-configuration", - "topologicalRank": 13, - "wave": "W5" - }, - { - "blockers": [], - "destinations": [ - "`packages/d2b-bus/src/routing/zone_service.rs`." - ], - "entryContracts": [ - "ADR-046-nix-configuration" - ], - "exitGate": "ADR046-W5 exit criteria (ADR-046-validation-and-delivery §4): every spec/work item in this wave Merged with clean destinations, all validators green, and the ten-role panel seal recorded", - "id": "ADR046-bus-010", - "kind": "work-item", - "owner": "ADR046-nix-023; ADR046-nix-027; naming and service name per ADR-046-componentsession-and-bus owning spec", - "parallelGroup": "wi:ADR-046-nix-configuration", - "prerequisites": [ - "ADR-046-nix-configuration", - "ADR046-nix-023", - "ADR046-nix-027" - ], - "specId": "ADR-046-nix-configuration", - "topologicalRank": 12, - "wave": "W5" - }, - { - "blockers": [], - "destinations": [ - "`packages/d2bd/src/provider_registry.rs` (adapt in place)." - ], - "entryContracts": [ - "ADR-046-nix-configuration" - ], - "exitGate": "ADR046-W5 exit criteria (ADR-046-validation-and-delivery §4): every spec/work item in this wave Merged with clean destinations, all validators green, and the ten-role panel seal recorded", - "id": "ADR046-bus-011", - "kind": "work-item", - "owner": "ADR046-nix-029; ADR046-nix-028; ADR046-nix-027; PROVIDER_BUNDLE_VERSION bump required on any bundle artifact format change", - "parallelGroup": "wi:ADR-046-nix-configuration", - "prerequisites": [ - "ADR-046-nix-configuration", - "ADR046-nix-027", - "ADR046-nix-028", - "ADR046-nix-029" - ], - "specId": "ADR-046-nix-configuration", - "topologicalRank": 13, - "wave": "W5" - }, - { - "blockers": [], - "destinations": [ - "`packages/d2bd/src/provider_effects.rs` (adapt in place)." - ], - "entryContracts": [ - "ADR-046-nix-configuration" - ], - "exitGate": "ADR046-W5 exit criteria (ADR-046-validation-and-delivery §4): every spec/work item in this wave Merged with clean destinations, all validators green, and the ten-role panel seal recorded", - "id": "ADR046-bus-012", - "kind": "work-item", - "owner": "ADR046-bus-011; ADR046-nix-029; GuestLifecycleRequest ResourceRef addressing per ADR-046-componentsession-and-bus owning spec", - "parallelGroup": "wi:ADR-046-nix-configuration", - "prerequisites": [ - "ADR-046-nix-configuration", - "ADR046-bus-011", - "ADR046-nix-029" + "exitGate" : "ADR046-W6 exit criteria (ADR-046-validation-and-delivery §4): every spec/work item in this wave Merged with clean destinations, all validators green, and the ten-role panel seal recorded", + "id" : "ADR046-azure-vm-009", + "kind" : "work-item", + "owner" : "All ADR046-azure-vm-*", + "parallelGroup" : "wi:ADR-046-provider-runtime-azure-virtual-machine", + "prerequisites" : [ + "ADR-046-provider-runtime-azure-virtual-machine" ], - "specId": "ADR-046-nix-configuration", - "topologicalRank": 14, - "wave": "W5" + "specId" : "ADR-046-provider-runtime-azure-virtual-machine", + "topologicalRank" : 11, + "wave" : "W6" }, { - "blockers": [], - "destinations": [ + "blockers" : [], + "destinations" : [ "`packages/d2b-provider-runtime-cloud-hypervisor/src/controller.rs`" ], - "entryContracts": [ + "entryContracts" : [ "ADR-046-provider-runtime-cloud-hypervisor" ], - "exitGate": "ADR046-W6 exit criteria (ADR-046-validation-and-delivery §4): every spec/work item in this wave Merged with clean destinations, all validators green, and the ten-role panel seal recorded", - "id": "ADR046-ch-001", - "kind": "work-item", - "owner": "Provider toolkit / system-minijail; W1 spike owner", - "parallelGroup": "wi:ADR-046-provider-runtime-cloud-hypervisor", - "prerequisites": [ + "exitGate" : "ADR046-W6 exit criteria (ADR-046-validation-and-delivery §4): every spec/work item in this wave Merged with clean destinations, all validators green, and the ten-role panel seal recorded", + "id" : "ADR046-ch-001", + "kind" : "work-item", + "owner" : "Provider toolkit / system-minijail; W1 spike owner", + "parallelGroup" : "wi:ADR-046-provider-runtime-cloud-hypervisor", + "prerequisites" : [ "ADR-046-provider-runtime-cloud-hypervisor" ], - "specId": "ADR-046-provider-runtime-cloud-hypervisor", - "topologicalRank": 11, - "wave": "W6" + "specId" : "ADR-046-provider-runtime-cloud-hypervisor", + "topologicalRank" : 11, + "wave" : "W6" }, { - "blockers": [], - "destinations": [ + "blockers" : [], + "destinations" : [ "`packages/d2b-provider-runtime-cloud-hypervisor/src/bootstrap_graph.rs`" ], - "entryContracts": [ + "entryContracts" : [ "ADR-046-provider-runtime-cloud-hypervisor" ], - "exitGate": "ADR046-W6 exit criteria (ADR-046-validation-and-delivery §4): every spec/work item in this wave Merged with clean destinations, all validators green, and the ten-role panel seal recorded", - "id": "ADR046-ch-002", - "kind": "work-item", - "owner": "ADR046-ch-001; Volume and Device foundation", - "parallelGroup": "wi:ADR-046-provider-runtime-cloud-hypervisor", - "prerequisites": [ + "exitGate" : "ADR046-W6 exit criteria (ADR-046-validation-and-delivery §4): every spec/work item in this wave Merged with clean destinations, all validators green, and the ten-role panel seal recorded", + "id" : "ADR046-ch-002", + "kind" : "work-item", + "owner" : "ADR046-ch-001; Volume and Device foundation", + "parallelGroup" : "wi:ADR-046-provider-runtime-cloud-hypervisor", + "prerequisites" : [ "ADR-046-provider-runtime-cloud-hypervisor", "ADR046-ch-001" ], - "specId": "ADR-046-provider-runtime-cloud-hypervisor", - "topologicalRank": 12, - "wave": "W6" + "specId" : "ADR-046-provider-runtime-cloud-hypervisor", + "topologicalRank" : 12, + "wave" : "W6" }, { - "blockers": [], - "destinations": [ + "blockers" : [], + "destinations" : [ "`packages/d2b-provider-runtime-cloud-hypervisor/src/vmm_argv.rs`; `tests/vmm_argv_golden_test.rs`" ], - "entryContracts": [ + "entryContracts" : [ "ADR-046-provider-runtime-cloud-hypervisor" ], - "exitGate": "ADR046-W6 exit criteria (ADR-046-validation-and-delivery §4): every spec/work item in this wave Merged with clean destinations, all validators green, and the ten-role panel seal recorded", - "id": "ADR046-ch-003", - "kind": "work-item", - "owner": "ADR046-ch-002; artifact catalog foundation", - "parallelGroup": "wi:ADR-046-provider-runtime-cloud-hypervisor", - "prerequisites": [ + "exitGate" : "ADR046-W6 exit criteria (ADR-046-validation-and-delivery §4): every spec/work item in this wave Merged with clean destinations, all validators green, and the ten-role panel seal recorded", + "id" : "ADR046-ch-003", + "kind" : "work-item", + "owner" : "ADR046-ch-002; artifact catalog foundation", + "parallelGroup" : "wi:ADR-046-provider-runtime-cloud-hypervisor", + "prerequisites" : [ "ADR-046-provider-runtime-cloud-hypervisor", "ADR046-ch-002" ], - "specId": "ADR-046-provider-runtime-cloud-hypervisor", - "topologicalRank": 13, - "wave": "W6" + "specId" : "ADR-046-provider-runtime-cloud-hypervisor", + "topologicalRank" : 13, + "wave" : "W6" }, { - "blockers": [], - "destinations": [ + "blockers" : [], + "destinations" : [ "`packages/d2b-provider-runtime-cloud-hypervisor/nix/` (Nix emitter); `nixos-modules/` option extension for `runtime-cloud-hypervisor` Guest schema" ], - "entryContracts": [ + "entryContracts" : [ "ADR-046-provider-runtime-cloud-hypervisor" ], - "exitGate": "ADR046-W6 exit criteria (ADR-046-validation-and-delivery §4): every spec/work item in this wave Merged with clean destinations, all validators green, and the ten-role panel seal recorded", - "id": "ADR046-ch-004", - "kind": "work-item", - "owner": "ADR046-ch-002; nix-configuration foundation (`ADR046-identities-002`)", - "parallelGroup": "wi:ADR-046-provider-runtime-cloud-hypervisor", - "prerequisites": [ + "exitGate" : "ADR046-W6 exit criteria (ADR-046-validation-and-delivery §4): every spec/work item in this wave Merged with clean destinations, all validators green, and the ten-role panel seal recorded", + "id" : "ADR046-ch-004", + "kind" : "work-item", + "owner" : "ADR046-ch-002; nix-configuration foundation (`ADR046-identities-002`)", + "parallelGroup" : "wi:ADR-046-provider-runtime-cloud-hypervisor", + "prerequisites" : [ "ADR-046-provider-runtime-cloud-hypervisor", "ADR046-ch-002", "ADR046-identities-002" ], - "specId": "ADR-046-provider-runtime-cloud-hypervisor", - "topologicalRank": 13, - "wave": "W6" + "specId" : "ADR-046-provider-runtime-cloud-hypervisor", + "topologicalRank" : 13, + "wave" : "W6" }, { - "blockers": [], - "destinations": [ + "blockers" : [], + "destinations" : [ "`packages/d2b-provider-runtime-cloud-hypervisor/src/health.rs`; `src/adoption.rs`" ], - "entryContracts": [ + "entryContracts" : [ "ADR-046-provider-runtime-cloud-hypervisor" ], - "exitGate": "ADR046-W6 exit criteria (ADR-046-validation-and-delivery §4): every spec/work item in this wave Merged with clean destinations, all validators green, and the ten-role panel seal recorded", - "id": "ADR046-ch-005", - "kind": "work-item", - "owner": "ADR046-ch-001; ComponentSession/d2b-bus (`ADR046-session-001`)", - "parallelGroup": "wi:ADR-046-provider-runtime-cloud-hypervisor", - "prerequisites": [ + "exitGate" : "ADR046-W6 exit criteria (ADR-046-validation-and-delivery §4): every spec/work item in this wave Merged with clean destinations, all validators green, and the ten-role panel seal recorded", + "id" : "ADR046-ch-005", + "kind" : "work-item", + "owner" : "ADR046-ch-001; ComponentSession/d2b-bus (`ADR046-session-001`)", + "parallelGroup" : "wi:ADR-046-provider-runtime-cloud-hypervisor", + "prerequisites" : [ "ADR-046-provider-runtime-cloud-hypervisor", "ADR046-ch-001", "ADR046-session-001" ], - "specId": "ADR-046-provider-runtime-cloud-hypervisor", - "topologicalRank": 12, - "wave": "W6" + "specId" : "ADR-046-provider-runtime-cloud-hypervisor", + "topologicalRank" : 12, + "wave" : "W6" }, { - "blockers": [], - "destinations": [ + "blockers" : [], + "destinations" : [ "`packages/d2b-provider-runtime-cloud-hypervisor/src/metrics.rs`; `src/audit.rs`" ], - "entryContracts": [ + "entryContracts" : [ "ADR-046-provider-runtime-cloud-hypervisor" ], - "exitGate": "ADR046-W6 exit criteria (ADR-046-validation-and-delivery §4): every spec/work item in this wave Merged with clean destinations, all validators green, and the ten-role panel seal recorded", - "id": "ADR046-ch-006", - "kind": "work-item", - "owner": "ADR046-ch-001; telemetry foundation (`ADR046-telem-001`)", - "parallelGroup": "wi:ADR-046-provider-runtime-cloud-hypervisor", - "prerequisites": [ + "exitGate" : "ADR046-W6 exit criteria (ADR-046-validation-and-delivery §4): every spec/work item in this wave Merged with clean destinations, all validators green, and the ten-role panel seal recorded", + "id" : "ADR046-ch-006", + "kind" : "work-item", + "owner" : "ADR046-ch-001; telemetry foundation (`ADR046-telem-001`)", + "parallelGroup" : "wi:ADR-046-provider-runtime-cloud-hypervisor", + "prerequisites" : [ "ADR-046-provider-runtime-cloud-hypervisor", "ADR046-ch-001", "ADR046-telem-001" ], - "specId": "ADR-046-provider-runtime-cloud-hypervisor", - "topologicalRank": 12, - "wave": "W6" + "specId" : "ADR-046-provider-runtime-cloud-hypervisor", + "topologicalRank" : 12, + "wave" : "W6" }, { - "blockers": [], - "destinations": [ + "blockers" : [], + "destinations" : [ "`packages/d2b-provider-runtime-cloud-hypervisor/src/state.rs`; `packages/d2b-provider-runtime-cloud-hypervisor/tests/state_status_test.rs`" ], - "entryContracts": [ + "entryContracts" : [ "ADR-046-provider-runtime-cloud-hypervisor" ], - "exitGate": "ADR046-W6 exit criteria (ADR-046-validation-and-delivery §4): every spec/work item in this wave Merged with clean destinations, all validators green, and the ten-role panel seal recorded", - "id": "ADR046-ch-007", - "kind": "work-item", - "owner": "ADR046-ch-001; `ADR046-pstate-001` (common status types)", - "parallelGroup": "wi:ADR-046-provider-runtime-cloud-hypervisor", - "prerequisites": [ + "exitGate" : "ADR046-W6 exit criteria (ADR-046-validation-and-delivery §4): every spec/work item in this wave Merged with clean destinations, all validators green, and the ten-role panel seal recorded", + "id" : "ADR046-ch-007", + "kind" : "work-item", + "owner" : "ADR046-ch-001; `ADR046-pstate-001` (common status types)", + "parallelGroup" : "wi:ADR-046-provider-runtime-cloud-hypervisor", + "prerequisites" : [ "ADR-046-provider-runtime-cloud-hypervisor", "ADR046-ch-001", "ADR046-pstate-001" ], - "specId": "ADR-046-provider-runtime-cloud-hypervisor", - "topologicalRank": 12, - "wave": "W6" + "specId" : "ADR-046-provider-runtime-cloud-hypervisor", + "topologicalRank" : 12, + "wave" : "W6" }, { - "blockers": [], - "destinations": [ + "blockers" : [], + "destinations" : [ "`packages/d2b/src/lib.rs`, `packages/d2b/src/context.rs`, `packages/d2b/src/dispatch.rs`" ], - "entryContracts": [ + "entryContracts" : [ "ADR-046-cli-and-operations" ], - "exitGate": "ADR046-W5 exit criteria (ADR-046-validation-and-delivery §4): every spec/work item in this wave Merged with clean destinations, all validators green, and the ten-role panel seal recorded", - "id": "ADR046-cli-001", - "kind": "work-item", - "owner": "ADR046-identities-001, ADR046-api-001; CLI crate owner", - "parallelGroup": "wi:ADR-046-cli-and-operations", - "prerequisites": [ + "exitGate" : "ADR046-W5 exit criteria (ADR-046-validation-and-delivery §4): every spec/work item in this wave Merged with clean destinations, all validators green, and the ten-role panel seal recorded", + "id" : "ADR046-cli-001", + "kind" : "work-item", + "owner" : "ADR046-identities-001, ADR046-api-001; CLI crate owner", + "parallelGroup" : "wi:ADR-046-cli-and-operations", + "prerequisites" : [ "ADR-046-cli-and-operations", "ADR046-api-001", "ADR046-identities-001" ], - "specId": "ADR-046-cli-and-operations", - "topologicalRank": 10, - "wave": "W5" + "specId" : "ADR-046-cli-and-operations", + "topologicalRank" : 10, + "wave" : "W5" }, { - "blockers": [], - "destinations": [ + "blockers" : [], + "destinations" : [ "`packages/d2b/src/guest.rs` (`d2b guest start/stop/restart/list/status`); unsafe-local workloads go to `packages/d2b/src/host.rs` (`d2b host list/status/get`), NOT guest.rs" ], - "entryContracts": [ + "entryContracts" : [ "ADR-046-cli-and-operations" ], - "exitGate": "ADR046-W5 exit criteria (ADR-046-validation-and-delivery §4): every spec/work item in this wave Merged with clean destinations, all validators green, and the ten-role panel seal recorded", - "id": "ADR046-cli-002", - "kind": "work-item", - "owner": "ADR046-cli-001, ADR046-api-001; CLI crate owner", - "parallelGroup": "wi:ADR-046-cli-and-operations", - "prerequisites": [ + "exitGate" : "ADR046-W5 exit criteria (ADR-046-validation-and-delivery §4): every spec/work item in this wave Merged with clean destinations, all validators green, and the ten-role panel seal recorded", + "id" : "ADR046-cli-002", + "kind" : "work-item", + "owner" : "ADR046-cli-001, ADR046-api-001; CLI crate owner", + "parallelGroup" : "wi:ADR-046-cli-and-operations", + "prerequisites" : [ "ADR-046-cli-and-operations", "ADR046-api-001", "ADR046-cli-001" ], - "specId": "ADR-046-cli-and-operations", - "topologicalRank": 11, - "wave": "W5" + "specId" : "ADR-046-cli-and-operations", + "topologicalRank" : 11, + "wave" : "W5" }, { - "blockers": [], - "destinations": [ + "blockers" : [], + "destinations" : [ "`packages/d2b/src/exec.rs` (`d2b exec run/attach/wait/status/list/logs/kill`)" ], - "entryContracts": [ + "entryContracts" : [ "ADR-046-cli-and-operations" ], - "exitGate": "ADR046-W5 exit criteria (ADR-046-validation-and-delivery §4): every spec/work item in this wave Merged with clean destinations, all validators green, and the ten-role panel seal recorded", - "id": "ADR046-cli-003", - "kind": "work-item", - "owner": "ADR046-cli-001, ADR046-api-001; CLI crate owner", - "parallelGroup": "wi:ADR-046-cli-and-operations", - "prerequisites": [ + "exitGate" : "ADR046-W5 exit criteria (ADR-046-validation-and-delivery §4): every spec/work item in this wave Merged with clean destinations, all validators green, and the ten-role panel seal recorded", + "id" : "ADR046-cli-003", + "kind" : "work-item", + "owner" : "ADR046-cli-001, ADR046-api-001; CLI crate owner", + "parallelGroup" : "wi:ADR-046-cli-and-operations", + "prerequisites" : [ "ADR-046-cli-and-operations", "ADR046-api-001", "ADR046-cli-001" ], - "specId": "ADR-046-cli-and-operations", - "topologicalRank": 11, - "wave": "W5" + "specId" : "ADR-046-cli-and-operations", + "topologicalRank" : 11, + "wave" : "W5" }, { - "blockers": [], - "destinations": [ + "blockers" : [], + "destinations" : [ "`packages/d2b/src/shell.rs` (`d2b shell open/attach/list/detach/kill/status`)" ], - "entryContracts": [ + "entryContracts" : [ "ADR-046-cli-and-operations" ], - "exitGate": "ADR046-W5 exit criteria (ADR-046-validation-and-delivery §4): every spec/work item in this wave Merged with clean destinations, all validators green, and the ten-role panel seal recorded", - "id": "ADR046-cli-004", - "kind": "work-item", - "owner": "ADR046-cli-001, `shell-terminal` Provider dossier; CLI crate owner", - "parallelGroup": "wi:ADR-046-cli-and-operations", - "prerequisites": [ + "exitGate" : "ADR046-W5 exit criteria (ADR-046-validation-and-delivery §4): every spec/work item in this wave Merged with clean destinations, all validators green, and the ten-role panel seal recorded", + "id" : "ADR046-cli-004", + "kind" : "work-item", + "owner" : "ADR046-cli-001, `shell-terminal` Provider dossier; CLI crate owner", + "parallelGroup" : "wi:ADR-046-cli-and-operations", + "prerequisites" : [ "ADR-046-cli-and-operations", "ADR046-cli-001" ], - "specId": "ADR-046-cli-and-operations", - "topologicalRank": 11, - "wave": "W5" + "specId" : "ADR-046-cli-and-operations", + "topologicalRank" : 11, + "wave" : "W5" }, { - "blockers": [], - "destinations": [ + "blockers" : [], + "destinations" : [ "`packages/d2b/src/provider.rs` (`d2b provider list/get/status/inspect`; dynamic projection loading)" ], - "entryContracts": [ + "entryContracts" : [ "ADR-046-cli-and-operations" ], - "exitGate": "ADR046-W5 exit criteria (ADR-046-validation-and-delivery §4): every spec/work item in this wave Merged with clean destinations, all validators green, and the ten-role panel seal recorded", - "id": "ADR046-cli-005", - "kind": "work-item", - "owner": "ADR046-cli-001, Provider model spec; CLI crate owner", - "parallelGroup": "wi:ADR-046-cli-and-operations", - "prerequisites": [ + "exitGate" : "ADR046-W5 exit criteria (ADR-046-validation-and-delivery §4): every spec/work item in this wave Merged with clean destinations, all validators green, and the ten-role panel seal recorded", + "id" : "ADR046-cli-005", + "kind" : "work-item", + "owner" : "ADR046-cli-001, Provider model spec; CLI crate owner", + "parallelGroup" : "wi:ADR-046-cli-and-operations", + "prerequisites" : [ "ADR-046-cli-and-operations", "ADR046-cli-001" ], - "specId": "ADR-046-cli-and-operations", - "topologicalRank": 11, - "wave": "W5" + "specId" : "ADR-046-cli-and-operations", + "topologicalRank" : 11, + "wave" : "W5" }, { - "blockers": [], - "destinations": [ + "blockers" : [], + "destinations" : [ "`packages/d2b/src/complete.rs` (`d2b complete bash/zsh/fish`)" ], - "entryContracts": [ + "entryContracts" : [ "ADR-046-cli-and-operations" ], - "exitGate": "ADR046-W5 exit criteria (ADR-046-validation-and-delivery §4): every spec/work item in this wave Merged with clean destinations, all validators green, and the ten-role panel seal recorded", - "id": "ADR046-cli-006", - "kind": "work-item", - "owner": "ADR046-cli-001; CLI crate owner", - "parallelGroup": "wi:ADR-046-cli-and-operations", - "prerequisites": [ + "exitGate" : "ADR046-W5 exit criteria (ADR-046-validation-and-delivery §4): every spec/work item in this wave Merged with clean destinations, all validators green, and the ten-role panel seal recorded", + "id" : "ADR046-cli-006", + "kind" : "work-item", + "owner" : "ADR046-cli-001; CLI crate owner", + "parallelGroup" : "wi:ADR-046-cli-and-operations", + "prerequisites" : [ "ADR-046-cli-and-operations", "ADR046-cli-001" ], - "specId": "ADR-046-cli-and-operations", - "topologicalRank": 11, - "wave": "W5" + "specId" : "ADR-046-cli-and-operations", + "topologicalRank" : 11, + "wave" : "W5" }, { - "blockers": [], - "destinations": [ + "blockers" : [], + "destinations" : [ "`packages/d2b/src/activation.rs` (`d2b activation build/generations/switch/boot/test/rollback/gc/migrate/keys/trust/rotate-known-host/config`)" ], - "entryContracts": [ + "entryContracts" : [ "ADR-046-cli-and-operations" ], - "exitGate": "ADR046-W5 exit criteria (ADR-046-validation-and-delivery §4): every spec/work item in this wave Merged with clean destinations, all validators green, and the ten-role panel seal recorded", - "id": "ADR046-cli-007", - "kind": "work-item", - "owner": "ADR046-cli-001; CLI/activation Provider owner", - "parallelGroup": "wi:ADR-046-cli-and-operations", - "prerequisites": [ + "exitGate" : "ADR046-W5 exit criteria (ADR-046-validation-and-delivery §4): every spec/work item in this wave Merged with clean destinations, all validators green, and the ten-role panel seal recorded", + "id" : "ADR046-cli-007", + "kind" : "work-item", + "owner" : "ADR046-cli-001; CLI/activation Provider owner", + "parallelGroup" : "wi:ADR-046-cli-and-operations", + "prerequisites" : [ "ADR-046-cli-and-operations", "ADR046-cli-001" ], - "specId": "ADR-046-cli-and-operations", - "topologicalRank": 11, - "wave": "W5" + "specId" : "ADR-046-cli-and-operations", + "topologicalRank" : 11, + "wave" : "W5" }, { - "blockers": [], - "destinations": [ + "blockers" : [], + "destinations" : [ "`packages/d2b/src/host.rs` (all `d2b host` subcommands)" ], - "entryContracts": [ + "entryContracts" : [ "ADR-046-cli-and-operations" ], - "exitGate": "ADR046-W5 exit criteria (ADR-046-validation-and-delivery §4): every spec/work item in this wave Merged with clean destinations, all validators green, and the ten-role panel seal recorded", - "id": "ADR046-cli-008", - "kind": "work-item", - "owner": "ADR046-cli-001; CLI crate owner", - "parallelGroup": "wi:ADR-046-cli-and-operations", - "prerequisites": [ + "exitGate" : "ADR046-W5 exit criteria (ADR-046-validation-and-delivery §4): every spec/work item in this wave Merged with clean destinations, all validators green, and the ten-role panel seal recorded", + "id" : "ADR046-cli-008", + "kind" : "work-item", + "owner" : "ADR046-cli-001; CLI crate owner", + "parallelGroup" : "wi:ADR-046-cli-and-operations", + "prerequisites" : [ "ADR-046-cli-and-operations", "ADR046-cli-001" ], - "specId": "ADR-046-cli-and-operations", - "topologicalRank": 11, - "wave": "W5" + "specId" : "ADR-046-cli-and-operations", + "topologicalRank" : 11, + "wave" : "W5" }, { - "blockers": [], - "destinations": [ + "blockers" : [], + "destinations" : [ "`packages/d2b/src/zone.rs` (`d2b zone get/list/status`)" ], - "entryContracts": [ + "entryContracts" : [ "ADR-046-cli-and-operations" ], - "exitGate": "ADR046-W5 exit criteria (ADR-046-validation-and-delivery §4): every spec/work item in this wave Merged with clean destinations, all validators green, and the ten-role panel seal recorded", - "id": "ADR046-cli-009", - "kind": "work-item", - "owner": "ADR046-cli-001; CLI crate owner", - "parallelGroup": "wi:ADR-046-cli-and-operations", - "prerequisites": [ + "exitGate" : "ADR046-W5 exit criteria (ADR-046-validation-and-delivery §4): every spec/work item in this wave Merged with clean destinations, all validators green, and the ten-role panel seal recorded", + "id" : "ADR046-cli-009", + "kind" : "work-item", + "owner" : "ADR046-cli-001; CLI crate owner", + "parallelGroup" : "wi:ADR-046-cli-and-operations", + "prerequisites" : [ "ADR-046-cli-and-operations", "ADR046-cli-001" ], - "specId": "ADR-046-cli-and-operations", - "topologicalRank": 11, - "wave": "W5" + "specId" : "ADR-046-cli-and-operations", + "topologicalRank" : 11, + "wave" : "W5" }, { - "blockers": [], - "destinations": [ + "blockers" : [], + "destinations" : [ "`packages/d2b/src/resource.rs` (standard `d2b get/list/watch/create/update-spec/delete/status` top-level verbs)" ], - "entryContracts": [ + "entryContracts" : [ "ADR-046-cli-and-operations" ], - "exitGate": "ADR046-W5 exit criteria (ADR-046-validation-and-delivery §4): every spec/work item in this wave Merged with clean destinations, all validators green, and the ten-role panel seal recorded", - "id": "ADR046-cli-010", - "kind": "work-item", - "owner": "ADR046-cli-001; CLI crate owner", - "parallelGroup": "wi:ADR-046-cli-and-operations", - "prerequisites": [ + "exitGate" : "ADR046-W5 exit criteria (ADR-046-validation-and-delivery §4): every spec/work item in this wave Merged with clean destinations, all validators green, and the ten-role panel seal recorded", + "id" : "ADR046-cli-010", + "kind" : "work-item", + "owner" : "ADR046-cli-001; CLI crate owner", + "parallelGroup" : "wi:ADR-046-cli-and-operations", + "prerequisites" : [ "ADR-046-cli-and-operations", "ADR046-cli-001" ], - "specId": "ADR-046-cli-and-operations", - "topologicalRank": 11, - "wave": "W5" + "specId" : "ADR-046-cli-and-operations", + "topologicalRank" : 11, + "wave" : "W5" }, { - "blockers": [], - "destinations": [ + "blockers" : [], + "destinations" : [ "Nix: `nixos-modules/options-zones.nix` (unified `d2b.zones..resources` attrset; per-type `spec` sub-options generated from ResourceTypeSchema/Provider schema), `nixos-modules/bundle-emit.nix` (canonical JSON emit + SHA256 pin), `nixos-modules/assertions.nix` (updated); core controller: `packages/d2b-core-controller/src/configuration.rs`, `packages/d2b-core-controller/src/cleanup.rs`; Contracts: `packages/d2b-contracts/src/zone_bundle.rs` (new)" ], - "entryContracts": [ + "entryContracts" : [ "ADR-046-cli-and-operations" ], - "exitGate": "ADR046-W5 exit criteria (ADR-046-validation-and-delivery §4): every spec/work item in this wave Merged with clean destinations, all validators green, and the ten-role panel seal recorded", - "id": "ADR046-cli-011", - "kind": "work-item", - "owner": "ADR046-identities-002, ADR046-cli-001, ADR046-cli-002, ADR046-cli-007; Nix module owner + Zone runtime owner", - "parallelGroup": "wi:ADR-046-cli-and-operations", - "prerequisites": [ + "exitGate" : "ADR046-W5 exit criteria (ADR-046-validation-and-delivery §4): every spec/work item in this wave Merged with clean destinations, all validators green, and the ten-role panel seal recorded", + "id" : "ADR046-cli-011", + "kind" : "work-item", + "owner" : "ADR046-identities-002, ADR046-cli-001, ADR046-cli-002, ADR046-cli-007; Nix module owner + Zone runtime owner", + "parallelGroup" : "wi:ADR-046-cli-and-operations", + "prerequisites" : [ "ADR-046-cli-and-operations", "ADR046-cli-001", "ADR046-cli-002", "ADR046-cli-007", "ADR046-identities-002" ], - "specId": "ADR-046-cli-and-operations", - "topologicalRank": 12, - "wave": "W5" + "specId" : "ADR-046-cli-and-operations", + "topologicalRank" : 12, + "wave" : "W5" }, { - "blockers": [], - "destinations": [ + "blockers" : [], + "destinations" : [ "`packages/d2b/src/endpoint.rs` (`d2b endpoint get/list/watch/status/resolve`)" ], - "entryContracts": [ + "entryContracts" : [ "ADR-046-cli-and-operations" ], - "exitGate": "ADR046-W5 exit criteria (ADR-046-validation-and-delivery §4): every spec/work item in this wave Merged with clean destinations, all validators green, and the ten-role panel seal recorded", - "id": "ADR046-cli-012", - "kind": "work-item", - "owner": "ADR046-cli-001, ADR046-cli-010; CLI crate owner", - "parallelGroup": "wi:ADR-046-cli-and-operations", - "prerequisites": [ + "exitGate" : "ADR046-W5 exit criteria (ADR-046-validation-and-delivery §4): every spec/work item in this wave Merged with clean destinations, all validators green, and the ten-role panel seal recorded", + "id" : "ADR046-cli-012", + "kind" : "work-item", + "owner" : "ADR046-cli-001, ADR046-cli-010; CLI crate owner", + "parallelGroup" : "wi:ADR-046-cli-and-operations", + "prerequisites" : [ "ADR-046-cli-and-operations", "ADR046-cli-001", "ADR046-cli-010" ], - "specId": "ADR-046-cli-and-operations", - "topologicalRank": 12, - "wave": "W5" + "specId" : "ADR-046-cli-and-operations", + "topologicalRank" : 12, + "wave" : "W5" }, { - "blockers": [], - "destinations": [ + "blockers" : [], + "destinations" : [ "`packages/d2b/src/share.rs` (`d2b export …` and `d2b import …` nouns)" ], - "entryContracts": [ + "entryContracts" : [ "ADR-046-cli-and-operations" ], - "exitGate": "ADR046-W5 exit criteria (ADR-046-validation-and-delivery §4): every spec/work item in this wave Merged with clean destinations, all validators green, and the ten-role panel seal recorded", - "id": "ADR046-cli-013", - "kind": "work-item", - "owner": "ADR046-cli-001, ADR046-cli-010, ADR046-zone-control-019; CLI crate owner", - "parallelGroup": "wi:ADR-046-cli-and-operations", - "prerequisites": [ + "exitGate" : "ADR046-W5 exit criteria (ADR-046-validation-and-delivery §4): every spec/work item in this wave Merged with clean destinations, all validators green, and the ten-role panel seal recorded", + "id" : "ADR046-cli-013", + "kind" : "work-item", + "owner" : "ADR046-cli-001, ADR046-cli-010, ADR046-zone-control-019; CLI crate owner", + "parallelGroup" : "wi:ADR-046-cli-and-operations", + "prerequisites" : [ "ADR-046-cli-and-operations", "ADR046-cli-001", - "ADR046-cli-010" - ], - "specId": "ADR-046-cli-and-operations", - "topologicalRank": 12, - "wave": "W5" - }, - { - "blockers": [], - "destinations": [ - "`packages/d2b-client/src/` (updated for v3 Zone API, replacing ADR45 daemon verbs with Zone resource operations)" - ], - "entryContracts": [ - "ADR-046-resources-zone-control" - ], - "exitGate": "ADR046-W5 exit criteria (ADR-046-validation-and-delivery §4): every spec/work item in this wave Merged with clean destinations, all validators green, and the ten-role panel seal recorded", - "id": "ADR046-client-001", - "kind": "work-item", - "owner": "ADR046-zone-control-011, ADR046-zone-control-012, ADR046-zone-control-013, ADR046-zone-control-018", - "parallelGroup": "wi:ADR-046-resources-zone-control", - "prerequisites": [ - "ADR-046-resources-zone-control" + "ADR046-cli-010", + "ADR046-zone-control-019" ], - "specId": "ADR-046-resources-zone-control", - "topologicalRank": 10, - "wave": "W5" + "specId" : "ADR-046-cli-and-operations", + "topologicalRank" : 12, + "wave" : "W5" }, { - "blockers": [], - "destinations": [ + "blockers" : [], + "destinations" : [ "packages/d2b-provider-clipboard-wayland/ with src, tests, integration, README.md, and binaries clipboard-controller, clipd-host, picker-session" ], - "entryContracts": [ + "entryContracts" : [ "ADR-046-provider-clipboard-wayland" ], - "exitGate": "ADR046-W6 exit criteria (ADR-046-validation-and-delivery §4): every spec/work item in this wave Merged with clean destinations, all validators green, and the ten-role panel seal recorded", - "id": "ADR046-clipboard-001", - "kind": "work-item", - "owner": "ADR-046-provider-model-and-packaging; Provider/clipboard-wayland crate owner", - "parallelGroup": "wi:ADR-046-provider-clipboard-wayland", - "prerequisites": [ + "exitGate" : "ADR046-W6 exit criteria (ADR-046-validation-and-delivery §4): every spec/work item in this wave Merged with clean destinations, all validators green, and the ten-role panel seal recorded", + "id" : "ADR046-clipboard-001", + "kind" : "work-item", + "owner" : "ADR-046-provider-model-and-packaging; Provider/clipboard-wayland crate owner", + "parallelGroup" : "wi:ADR-046-provider-clipboard-wayland", + "prerequisites" : [ "ADR-046-provider-clipboard-wayland" ], - "specId": "ADR-046-provider-clipboard-wayland", - "topologicalRank": 11, - "wave": "W6" + "specId" : "ADR-046-provider-clipboard-wayland", + "topologicalRank" : 11, + "wave" : "W6" }, { - "blockers": [], - "destinations": [ + "blockers" : [], + "destinations" : [ "packages/d2b-provider-clipboard-wayland/src/clipd_host/ service binary modules such as service, display_client, bridge, picker_coord, policy, fd, audit, history" ], - "entryContracts": [ + "entryContracts" : [ "ADR-046-provider-clipboard-wayland" ], - "exitGate": "ADR046-W6 exit criteria (ADR-046-validation-and-delivery §4): every spec/work item in this wave Merged with clean destinations, all validators green, and the ten-role panel seal recorded", - "id": "ADR046-clipboard-002", - "kind": "work-item", - "owner": "ADR046-clipboard-001; clipd-host service owner", - "parallelGroup": "wi:ADR-046-provider-clipboard-wayland", - "prerequisites": [ + "exitGate" : "ADR046-W6 exit criteria (ADR-046-validation-and-delivery §4): every spec/work item in this wave Merged with clean destinations, all validators green, and the ten-role panel seal recorded", + "id" : "ADR046-clipboard-002", + "kind" : "work-item", + "owner" : "ADR046-clipboard-001; clipd-host service owner", + "parallelGroup" : "wi:ADR-046-provider-clipboard-wayland", + "prerequisites" : [ "ADR-046-provider-clipboard-wayland", "ADR046-clipboard-001" ], - "specId": "ADR-046-provider-clipboard-wayland", - "topologicalRank": 12, - "wave": "W6" + "specId" : "ADR-046-provider-clipboard-wayland", + "topologicalRank" : 12, + "wave" : "W6" }, { - "blockers": [], - "destinations": [ + "blockers" : [], + "destinations" : [ "packages/d2b-provider-clipboard-wayland/src/controller/ and clipboard-controller binary" ], - "entryContracts": [ + "entryContracts" : [ "ADR-046-provider-clipboard-wayland" ], - "exitGate": "ADR046-W6 exit criteria (ADR-046-validation-and-delivery §4): every spec/work item in this wave Merged with clean destinations, all validators green, and the ten-role panel seal recorded", - "id": "ADR046-clipboard-003", - "kind": "work-item", - "owner": "ADR046-clipboard-001; clipboard-controller owner", - "parallelGroup": "wi:ADR-046-provider-clipboard-wayland", - "prerequisites": [ + "exitGate" : "ADR046-W6 exit criteria (ADR-046-validation-and-delivery §4): every spec/work item in this wave Merged with clean destinations, all validators green, and the ten-role panel seal recorded", + "id" : "ADR046-clipboard-003", + "kind" : "work-item", + "owner" : "ADR046-clipboard-001; clipboard-controller owner", + "parallelGroup" : "wi:ADR-046-provider-clipboard-wayland", + "prerequisites" : [ "ADR-046-provider-clipboard-wayland", "ADR046-clipboard-001" ], - "specId": "ADR-046-provider-clipboard-wayland", - "topologicalRank": 12, - "wave": "W6" + "specId" : "ADR-046-provider-clipboard-wayland", + "topologicalRank" : 12, + "wave" : "W6" }, { - "blockers": [], - "destinations": [ + "blockers" : [], + "destinations" : [ "packages/d2b-provider-clipboard-wayland/src/picker_session/ and picker-session binary" ], - "entryContracts": [ + "entryContracts" : [ "ADR-046-provider-clipboard-wayland" ], - "exitGate": "ADR046-W6 exit criteria (ADR-046-validation-and-delivery §4): every spec/work item in this wave Merged with clean destinations, all validators green, and the ten-role panel seal recorded", - "id": "ADR046-clipboard-004", - "kind": "work-item", - "owner": "ADR046-clipboard-001 and ADR046-clipboard-003; picker worker owner", - "parallelGroup": "wi:ADR-046-provider-clipboard-wayland", - "prerequisites": [ + "exitGate" : "ADR046-W6 exit criteria (ADR-046-validation-and-delivery §4): every spec/work item in this wave Merged with clean destinations, all validators green, and the ten-role panel seal recorded", + "id" : "ADR046-clipboard-004", + "kind" : "work-item", + "owner" : "ADR046-clipboard-001 and ADR046-clipboard-003; picker worker owner", + "parallelGroup" : "wi:ADR-046-provider-clipboard-wayland", + "prerequisites" : [ "ADR-046-provider-clipboard-wayland", "ADR046-clipboard-001", "ADR046-clipboard-003" ], - "specId": "ADR-046-provider-clipboard-wayland", - "topologicalRank": 13, - "wave": "W6" + "specId" : "ADR-046-provider-clipboard-wayland", + "topologicalRank" : 13, + "wave" : "W6" }, { - "blockers": [], - "destinations": [ + "blockers" : [], + "destinations" : [ "packages/d2b-provider-clipboard-wayland service descriptors and generated Rust async ttrpc bindings, plus any shared contracts crate selected by ADR-046-componentsession-and-bus" ], - "entryContracts": [ + "entryContracts" : [ "ADR-046-provider-clipboard-wayland" ], - "exitGate": "ADR046-W6 exit criteria (ADR-046-validation-and-delivery §4): every spec/work item in this wave Merged with clean destinations, all validators green, and the ten-role panel seal recorded", - "id": "ADR046-clipboard-005", - "kind": "work-item", - "owner": "ADR046-clipboard-001; ADR-046-componentsession-and-bus; clipboard service contract owner", - "parallelGroup": "wi:ADR-046-provider-clipboard-wayland", - "prerequisites": [ + "exitGate" : "ADR046-W6 exit criteria (ADR-046-validation-and-delivery §4): every spec/work item in this wave Merged with clean destinations, all validators green, and the ten-role panel seal recorded", + "id" : "ADR046-clipboard-005", + "kind" : "work-item", + "owner" : "ADR046-clipboard-001; ADR-046-componentsession-and-bus; clipboard service contract owner", + "parallelGroup" : "wi:ADR-046-provider-clipboard-wayland", + "prerequisites" : [ "ADR-046-provider-clipboard-wayland", "ADR046-clipboard-001" ], - "specId": "ADR-046-provider-clipboard-wayland", - "topologicalRank": 12, - "wave": "W6" + "specId" : "ADR-046-provider-clipboard-wayland", + "topologicalRank" : 12, + "wave" : "W6" }, { - "blockers": [], - "destinations": [ + "blockers" : [], + "destinations" : [ "nixos-modules/providers/clipboard-wayland.nix and d2b.artifacts.clipboard-wayland catalog entry" ], - "entryContracts": [ + "entryContracts" : [ "ADR-046-provider-clipboard-wayland" ], - "exitGate": "ADR046-W6 exit criteria (ADR-046-validation-and-delivery §4): every spec/work item in this wave Merged with clean destinations, all validators green, and the ten-role panel seal recorded", - "id": "ADR046-clipboard-006", - "kind": "work-item", - "owner": "ADR-046-nix-configuration; Provider/clipboard-wayland Nix owner", - "parallelGroup": "wi:ADR-046-provider-clipboard-wayland", - "prerequisites": [ + "exitGate" : "ADR046-W6 exit criteria (ADR-046-validation-and-delivery §4): every spec/work item in this wave Merged with clean destinations, all validators green, and the ten-role panel seal recorded", + "id" : "ADR046-clipboard-006", + "kind" : "work-item", + "owner" : "ADR-046-nix-configuration; Provider/clipboard-wayland Nix owner", + "parallelGroup" : "wi:ADR-046-provider-clipboard-wayland", + "prerequisites" : [ "ADR-046-provider-clipboard-wayland" ], - "specId": "ADR-046-provider-clipboard-wayland", - "topologicalRank": 11, - "wave": "W6" + "specId" : "ADR-046-provider-clipboard-wayland", + "topologicalRank" : 11, + "wave" : "W6" }, { - "blockers": [], - "destinations": [ + "blockers" : [], + "destinations" : [ "packages/d2b-provider-clipboard-wayland/src/controller/rbac.rs or equivalent controller reconcile module" ], - "entryContracts": [ + "entryContracts" : [ "ADR-046-provider-clipboard-wayland" ], - "exitGate": "ADR046-W6 exit criteria (ADR-046-validation-and-delivery §4): every spec/work item in this wave Merged with clean destinations, all validators green, and the ten-role panel seal recorded", - "id": "ADR046-clipboard-007", - "kind": "work-item", - "owner": "ADR046-clipboard-003; clipboard RBAC owner", - "parallelGroup": "wi:ADR-046-provider-clipboard-wayland", - "prerequisites": [ + "exitGate" : "ADR046-W6 exit criteria (ADR-046-validation-and-delivery §4): every spec/work item in this wave Merged with clean destinations, all validators green, and the ten-role panel seal recorded", + "id" : "ADR046-clipboard-007", + "kind" : "work-item", + "owner" : "ADR046-clipboard-003; clipboard RBAC owner", + "parallelGroup" : "wi:ADR-046-provider-clipboard-wayland", + "prerequisites" : [ "ADR-046-provider-clipboard-wayland", "ADR046-clipboard-003" ], - "specId": "ADR-046-provider-clipboard-wayland", - "topologicalRank": 13, - "wave": "W6" + "specId" : "ADR-046-provider-clipboard-wayland", + "topologicalRank" : 13, + "wave" : "W6" }, { - "blockers": [], - "destinations": [ + "blockers" : [], + "destinations" : [ "packages/d2b-provider-clipboard-wayland/src/service/audit.rs and packages/d2b-provider-clipboard-wayland/src/service/metrics.rs" ], - "entryContracts": [ + "entryContracts" : [ "ADR-046-provider-clipboard-wayland" ], - "exitGate": "ADR046-W6 exit criteria (ADR-046-validation-and-delivery §4): every spec/work item in this wave Merged with clean destinations, all validators green, and the ten-role panel seal recorded", - "id": "ADR046-clipboard-008", - "kind": "work-item", - "owner": "ADR046-clipboard-002; ADR-046-telemetry-audit-and-support; clipboard observability owner", - "parallelGroup": "wi:ADR-046-provider-clipboard-wayland", - "prerequisites": [ + "exitGate" : "ADR046-W6 exit criteria (ADR-046-validation-and-delivery §4): every spec/work item in this wave Merged with clean destinations, all validators green, and the ten-role panel seal recorded", + "id" : "ADR046-clipboard-008", + "kind" : "work-item", + "owner" : "ADR046-clipboard-002; ADR-046-telemetry-audit-and-support; clipboard observability owner", + "parallelGroup" : "wi:ADR-046-provider-clipboard-wayland", + "prerequisites" : [ "ADR-046-provider-clipboard-wayland", "ADR046-clipboard-002" ], - "specId": "ADR-046-provider-clipboard-wayland", - "topologicalRank": 13, - "wave": "W6" + "specId" : "ADR-046-provider-clipboard-wayland", + "topologicalRank" : 13, + "wave" : "W6" }, { - "blockers": [], - "destinations": [ + "blockers" : [], + "destinations" : [ "packages/d2b-provider-clipboard-wayland/tests/" ], - "entryContracts": [ + "entryContracts" : [ "ADR-046-provider-clipboard-wayland" ], - "exitGate": "ADR046-W6 exit criteria (ADR-046-validation-and-delivery §4): every spec/work item in this wave Merged with clean destinations, all validators green, and the ten-role panel seal recorded", - "id": "ADR046-clipboard-009", - "kind": "work-item", - "owner": "ADR046-clipboard-001 through ADR046-clipboard-008; clipboard test owner", - "parallelGroup": "wi:ADR-046-provider-clipboard-wayland", - "prerequisites": [ + "exitGate" : "ADR046-W6 exit criteria (ADR-046-validation-and-delivery §4): every spec/work item in this wave Merged with clean destinations, all validators green, and the ten-role panel seal recorded", + "id" : "ADR046-clipboard-009", + "kind" : "work-item", + "owner" : "ADR046-clipboard-001 through ADR046-clipboard-008; clipboard test owner", + "parallelGroup" : "wi:ADR-046-provider-clipboard-wayland", + "prerequisites" : [ "ADR-046-provider-clipboard-wayland", "ADR046-clipboard-001", + "ADR046-clipboard-002", + "ADR046-clipboard-003", + "ADR046-clipboard-004", + "ADR046-clipboard-005", + "ADR046-clipboard-006", + "ADR046-clipboard-007", "ADR046-clipboard-008" ], - "specId": "ADR-046-provider-clipboard-wayland", - "topologicalRank": 14, - "wave": "W6" + "specId" : "ADR-046-provider-clipboard-wayland", + "topologicalRank" : 14, + "wave" : "W6" }, { - "blockers": [], - "destinations": [ + "blockers" : [], + "destinations" : [ "packages/d2b-provider-clipboard-wayland/integration/" ], - "entryContracts": [ + "entryContracts" : [ "ADR-046-provider-clipboard-wayland" ], - "exitGate": "ADR046-W6 exit criteria (ADR-046-validation-and-delivery §4): every spec/work item in this wave Merged with clean destinations, all validators green, and the ten-role panel seal recorded", - "id": "ADR046-clipboard-010", - "kind": "work-item", - "owner": "ADR046-clipboard-009; display-wayland fake and clipboard integration owner", - "parallelGroup": "wi:ADR-046-provider-clipboard-wayland", - "prerequisites": [ + "exitGate" : "ADR046-W6 exit criteria (ADR-046-validation-and-delivery §4): every spec/work item in this wave Merged with clean destinations, all validators green, and the ten-role panel seal recorded", + "id" : "ADR046-clipboard-010", + "kind" : "work-item", + "owner" : "ADR046-clipboard-009; display-wayland fake and clipboard integration owner", + "parallelGroup" : "wi:ADR-046-provider-clipboard-wayland", + "prerequisites" : [ "ADR-046-provider-clipboard-wayland", "ADR046-clipboard-009" ], - "specId": "ADR-046-provider-clipboard-wayland", - "topologicalRank": 15, - "wave": "W6" + "specId" : "ADR-046-provider-clipboard-wayland", + "topologicalRank" : 15, + "wave" : "W6" }, { - "blockers": [], - "destinations": [ + "blockers" : [], + "destinations" : [ "packages/d2b-contract-tests/tests/policy_clipboard.rs" ], - "entryContracts": [ + "entryContracts" : [ "ADR-046-provider-clipboard-wayland" ], - "exitGate": "ADR046-W6 exit criteria (ADR-046-validation-and-delivery §4): every spec/work item in this wave Merged with clean destinations, all validators green, and the ten-role panel seal recorded", - "id": "ADR046-clipboard-011", - "kind": "work-item", - "owner": "ADR046-clipboard-005; packages/d2b-contract-tests owner", - "parallelGroup": "wi:ADR-046-provider-clipboard-wayland", - "prerequisites": [ + "exitGate" : "ADR046-W6 exit criteria (ADR-046-validation-and-delivery §4): every spec/work item in this wave Merged with clean destinations, all validators green, and the ten-role panel seal recorded", + "id" : "ADR046-clipboard-011", + "kind" : "work-item", + "owner" : "ADR046-clipboard-005; packages/d2b-contract-tests owner", + "parallelGroup" : "wi:ADR-046-provider-clipboard-wayland", + "prerequisites" : [ "ADR-046-provider-clipboard-wayland", "ADR046-clipboard-005" ], - "specId": "ADR-046-provider-clipboard-wayland", - "topologicalRank": 13, - "wave": "W6" + "specId" : "ADR-046-provider-clipboard-wayland", + "topologicalRank" : 13, + "wave" : "W6" }, { - "blockers": [], - "destinations": [ + "blockers" : [], + "destinations" : [ "nixos-modules/default.nix, docs/how-to/ migration guide, tests/static.sh example iteration expectations, CHANGELOG.md" ], - "entryContracts": [ + "entryContracts" : [ "ADR-046-provider-clipboard-wayland" ], - "exitGate": "ADR046-W6 exit criteria (ADR-046-validation-and-delivery §4): every spec/work item in this wave Merged with clean destinations, all validators green, and the ten-role panel seal recorded", - "id": "ADR046-clipboard-012", - "kind": "work-item", - "owner": "ADR046-clipboard-006; Nix/module removal owner", - "parallelGroup": "wi:ADR-046-provider-clipboard-wayland", - "prerequisites": [ + "exitGate" : "ADR046-W6 exit criteria (ADR-046-validation-and-delivery §4): every spec/work item in this wave Merged with clean destinations, all validators green, and the ten-role panel seal recorded", + "id" : "ADR046-clipboard-012", + "kind" : "work-item", + "owner" : "ADR046-clipboard-006; Nix/module removal owner", + "parallelGroup" : "wi:ADR-046-provider-clipboard-wayland", + "prerequisites" : [ "ADR-046-provider-clipboard-wayland", "ADR046-clipboard-006" ], - "specId": "ADR-046-provider-clipboard-wayland", - "topologicalRank": 12, - "wave": "W6" + "specId" : "ADR-046-provider-clipboard-wayland", + "topologicalRank" : 12, + "wave" : "W6" }, { - "blockers": [], - "destinations": [ + "blockers" : [], + "destinations" : [ "`packages/d2b-core-controller/src/{main,configuration,api_catalog,authz,providers,controllers,ownership,watches,cleanup,zone_links,budgets,store}.rs`" ], - "entryContracts": [ + "entryContracts" : [ "ADR-046-core-controllers" ], - "exitGate": "ADR046-W4 exit criteria (ADR-046-validation-and-delivery §4): every spec/work item in this wave Merged with clean destinations, all validators green, and the ten-role panel seal recorded", - "id": "ADR046-core-001", - "kind": "work-item", - "owner": "W0/W1a; core-controller owner", - "parallelGroup": "wi:ADR-046-core-controllers", - "prerequisites": [ + "exitGate" : "ADR046-W4 exit criteria (ADR-046-validation-and-delivery §4): every spec/work item in this wave Merged with clean destinations, all validators green, and the ten-role panel seal recorded", + "id" : "ADR046-core-001", + "kind" : "work-item", + "owner" : "W0/W1a; core-controller owner", + "parallelGroup" : "wi:ADR-046-core-controllers", + "prerequisites" : [ "ADR-046-core-controllers" ], - "specId": "ADR-046-core-controllers", - "topologicalRank": 9, - "wave": "W4" + "specId" : "ADR-046-core-controllers", + "topologicalRank" : 9, + "wave" : "W4" }, { - "blockers": [], - "destinations": [ + "blockers" : [], + "destinations" : [ "`packages/d2b-provider-system-core/src/{host,user}.rs` linked into fixed core controller" ], - "entryContracts": [ + "entryContracts" : [ "ADR-046-core-controllers" ], - "exitGate": "ADR046-W4 exit criteria (ADR-046-validation-and-delivery §4): every spec/work item in this wave Merged with clean destinations, all validators green, and the ten-role panel seal recorded", - "id": "ADR046-core-002", - "kind": "work-item", - "owner": "ADR046-core-001; system-core Provider owner", - "parallelGroup": "wi:ADR-046-core-controllers", - "prerequisites": [ + "exitGate" : "ADR046-W4 exit criteria (ADR-046-validation-and-delivery §4): every spec/work item in this wave Merged with clean destinations, all validators green, and the ten-role panel seal recorded", + "id" : "ADR046-core-002", + "kind" : "work-item", + "owner" : "ADR046-core-001; system-core Provider owner", + "parallelGroup" : "wi:ADR-046-core-controllers", + "prerequisites" : [ "ADR-046-core-controllers", "ADR046-core-001" ], - "specId": "ADR-046-core-controllers", - "topologicalRank": 10, - "wave": "W4" + "specId" : "ADR-046-core-controllers", + "topologicalRank" : 10, + "wave" : "W4" }, { - "blockers": [], - "destinations": [ + "blockers" : [], + "destinations" : [ "`packages/d2b-provider-credential-entra/src/{lib.rs,controller.rs,service.rs,controller_main.rs,agent_main.rs,audit.rs,telemetry.rs}` and corresponding tests/integration docs" ], - "entryContracts": [ + "entryContracts" : [ "ADR-046-provider-credential-entra" ], - "exitGate": "ADR046-W6 exit criteria (ADR-046-validation-and-delivery §4): every spec/work item in this wave Merged with clean destinations, all validators green, and the ten-role panel seal recorded", - "id": "ADR046-cred-entra-001", - "kind": "work-item", - "owner": "`ADR046-credential-001` (Credential base fields and status), `ADR046-credential-002` (`d2b.credential.v3` service), D092 Endpoint, D093 Entrablau identity Guest decision, credential-entra owner", - "parallelGroup": "wi:ADR-046-provider-credential-entra", - "prerequisites": [ + "exitGate" : "ADR046-W6 exit criteria (ADR-046-validation-and-delivery §4): every spec/work item in this wave Merged with clean destinations, all validators green, and the ten-role panel seal recorded", + "id" : "ADR046-cred-entra-001", + "kind" : "work-item", + "owner" : "`ADR046-credential-001` (Credential base fields and status), `ADR046-credential-002` (`d2b.credential.v3` service), D092 Endpoint, D093 Entrablau identity Guest decision, credential-entra owner", + "parallelGroup" : "wi:ADR-046-provider-credential-entra", + "prerequisites" : [ "ADR-046-provider-credential-entra", "ADR046-credential-001", "ADR046-credential-002" ], - "specId": "ADR-046-provider-credential-entra", - "topologicalRank": 11, - "wave": "W6" + "specId" : "ADR-046-provider-credential-entra", + "topologicalRank" : 11, + "wave" : "W6" }, { - "blockers": [], - "destinations": [ + "blockers" : [], + "destinations" : [ "`packages/d2b-provider-credential-managed-identity/src/{lib.rs, controller.rs, agent.rs, service.rs, audit.rs, telemetry.rs}`; `packages/d2b-provider-credential-managed-identity/{controller/main.rs, agent/main.rs}`; `packages/d2b-provider-credential-managed-identity/tests/{lifecycle.rs, conformance.rs, faults.rs, canary.rs, delivery.rs, placement.rs, topology.rs}`; `packages/d2b-provider-credential-managed-identity/integration/{container-service.sh, host-guest-placement.nix, aca-credential-ref.sh, cleanup-rollback.sh}`; `packages/d2b-provider-credential-managed-identity/README.md`" ], - "entryContracts": [ + "entryContracts" : [ "ADR-046-provider-credential-managed-identity" ], - "exitGate": "ADR046-W6 exit criteria (ADR-046-validation-and-delivery §4): every spec/work item in this wave Merged with clean destinations, all validators green, and the ten-role panel seal recorded", - "id": "ADR046-cred-mi-001", - "kind": "work-item", - "owner": "`ADR046-credential-001` (contracts), `ADR046-credential-002` (service); `ADR046-reconcile-001`; `ADR046-mi-topology-001`; `credential-managed-identity` crate owner", - "parallelGroup": "wi:ADR-046-provider-credential-managed-identity", - "prerequisites": [ + "exitGate" : "ADR046-W6 exit criteria (ADR-046-validation-and-delivery §4): every spec/work item in this wave Merged with clean destinations, all validators green, and the ten-role panel seal recorded", + "id" : "ADR046-cred-mi-001", + "kind" : "work-item", + "owner" : "`ADR046-credential-001` (contracts), `ADR046-credential-002` (service); `ADR046-reconcile-001`; `ADR046-cred-mi-005`; `credential-managed-identity` crate owner", + "parallelGroup" : "wi:ADR-046-provider-credential-managed-identity", + "prerequisites" : [ "ADR-046-provider-credential-managed-identity", + "ADR046-cred-mi-005", "ADR046-credential-001", "ADR046-credential-002", "ADR046-reconcile-001" ], - "specId": "ADR-046-provider-credential-managed-identity", - "topologicalRank": 11, - "wave": "W6" + "specId" : "ADR-046-provider-credential-managed-identity", + "topologicalRank" : 12, + "wave" : "W6" }, { - "blockers": [], - "destinations": [ + "blockers" : [], + "destinations" : [ "packages/d2b-provider-credential-managed-identity/src/controller.rs; packages/d2b-contracts/src/v3/credential_controller.rs" ], - "entryContracts": [ + "entryContracts" : [ "ADR-046-provider-credential-managed-identity" ], - "exitGate": "ADR046-W6 exit criteria (ADR-046-validation-and-delivery §4): every spec/work item in this wave Merged with clean destinations, all validators green, and the ten-role panel seal recorded", - "id": "ADR046-cred-mi-002", - "kind": "work-item", - "owner": "ADR046-credential-001, ADR046-credential-002; ADR046-reconcile-001, ADR046-reconcile-002; ADR046-mi-topology-001; owner: Credential controller toolkit and managed-identity controller", - "parallelGroup": "wi:ADR-046-provider-credential-managed-identity", - "prerequisites": [ + "exitGate" : "ADR046-W6 exit criteria (ADR-046-validation-and-delivery §4): every spec/work item in this wave Merged with clean destinations, all validators green, and the ten-role panel seal recorded", + "id" : "ADR046-cred-mi-002", + "kind" : "work-item", + "owner" : "ADR046-credential-001, ADR046-credential-002; ADR046-reconcile-001, ADR046-reconcile-002; ADR046-cred-mi-005; owner: Credential controller toolkit and managed-identity controller", + "parallelGroup" : "wi:ADR-046-provider-credential-managed-identity", + "prerequisites" : [ "ADR-046-provider-credential-managed-identity", + "ADR046-cred-mi-005", "ADR046-credential-001", "ADR046-credential-002", "ADR046-reconcile-001", "ADR046-reconcile-002" ], - "specId": "ADR-046-provider-credential-managed-identity", - "topologicalRank": 11, - "wave": "W6" + "specId" : "ADR-046-provider-credential-managed-identity", + "topologicalRank" : 12, + "wave" : "W6" }, { - "blockers": [], - "destinations": [ + "blockers" : [], + "destinations" : [ "nixos-modules/options-resources.nix; nixos-modules/activation-nixos-cleanup.nix; integration/aca-credential-ref.sh" ], - "entryContracts": [ + "entryContracts" : [ "ADR-046-provider-credential-managed-identity" ], - "exitGate": "ADR046-W6 exit criteria (ADR-046-validation-and-delivery §4): every spec/work item in this wave Merged with clean destinations, all validators green, and the ten-role panel seal recorded", - "id": "ADR046-cred-mi-003", - "kind": "work-item", - "owner": "Depends on ADR046-credential-001, ADR046-credential-002, and ADR046-cred-mi-001; owner: Nix resource compiler and activation cleanup", - "parallelGroup": "wi:ADR-046-provider-credential-managed-identity", - "prerequisites": [ + "exitGate" : "ADR046-W6 exit criteria (ADR-046-validation-and-delivery §4): every spec/work item in this wave Merged with clean destinations, all validators green, and the ten-role panel seal recorded", + "id" : "ADR046-cred-mi-003", + "kind" : "work-item", + "owner" : "Depends on ADR046-credential-001, ADR046-credential-002, and ADR046-cred-mi-001; owner: Nix resource compiler and activation cleanup", + "parallelGroup" : "wi:ADR-046-provider-credential-managed-identity", + "prerequisites" : [ "ADR-046-provider-credential-managed-identity", + "ADR046-cred-mi-001", "ADR046-credential-001", "ADR046-credential-002" ], - "specId": "ADR-046-provider-credential-managed-identity", - "topologicalRank": 11, - "wave": "W6" + "specId" : "ADR-046-provider-credential-managed-identity", + "topologicalRank" : 13, + "wave" : "W6" }, { - "blockers": [], - "destinations": [ + "blockers" : [], + "destinations" : [ "packages/d2b-provider-credential-managed-identity/src/{audit.rs,telemetry.rs}; packages/d2b-contract-tests/tests/credential_audit.rs" ], - "entryContracts": [ + "entryContracts" : [ "ADR-046-provider-credential-managed-identity" ], - "exitGate": "ADR046-W6 exit criteria (ADR-046-validation-and-delivery §4): every spec/work item in this wave Merged with clean destinations, all validators green, and the ten-role panel seal recorded", - "id": "ADR046-cred-mi-004", - "kind": "work-item", - "owner": "Depends on ADR046-cred-mi-001 and ADR046-mi-topology-001; owner: credential-managed-identity audit/telemetry implementation", - "parallelGroup": "wi:ADR-046-provider-credential-managed-identity", - "prerequisites": [ + "exitGate" : "ADR046-W6 exit criteria (ADR-046-validation-and-delivery §4): every spec/work item in this wave Merged with clean destinations, all validators green, and the ten-role panel seal recorded", + "id" : "ADR046-cred-mi-004", + "kind" : "work-item", + "owner" : "Depends on ADR046-cred-mi-001 and ADR046-cred-mi-005; owner: credential-managed-identity audit/telemetry implementation", + "parallelGroup" : "wi:ADR-046-provider-credential-managed-identity", + "prerequisites" : [ + "ADR-046-provider-credential-managed-identity", + "ADR046-cred-mi-001", + "ADR046-cred-mi-005" + ], + "specId" : "ADR-046-provider-credential-managed-identity", + "topologicalRank" : 13, + "wave" : "W6" + }, + { + "blockers" : [], + "destinations" : [ + "packages/d2b-provider-credential-managed-identity/src/{controller.rs,agent.rs}; packages/d2b-provider-credential-managed-identity/{controller/main.rs,agent/main.rs}; packages/d2b-provider-credential-managed-identity/tests/topology.rs" + ], + "entryContracts" : [ "ADR-046-provider-credential-managed-identity" ], - "specId": "ADR-046-provider-credential-managed-identity", - "topologicalRank": 11, - "wave": "W6" + "exitGate" : "ADR046-W6 exit criteria (ADR-046-validation-and-delivery §4): every spec/work item in this wave Merged with clean destinations, all validators green, and the ten-role panel seal recorded", + "id" : "ADR046-cred-mi-005", + "kind" : "work-item", + "owner" : "ADR046-credential-001 and ADR046-credential-002; owner: credential-managed-identity controller/agent topology. This topology contract is consumed by the later managed-identity controller work.", + "parallelGroup" : "wi:ADR-046-provider-credential-managed-identity", + "prerequisites" : [ + "ADR-046-provider-credential-managed-identity", + "ADR046-credential-001", + "ADR046-credential-002" + ], + "specId" : "ADR-046-provider-credential-managed-identity", + "topologicalRank" : 11, + "wave" : "W6" }, { - "blockers": [], - "destinations": [ + "blockers" : [], + "destinations" : [ "packages/d2b-contracts/src/v3/credential.rs" ], - "entryContracts": [ + "entryContracts" : [ "ADR-046-provider-credential-secret-service" ], - "exitGate": "ADR046-W6 exit criteria (ADR-046-validation-and-delivery §4): every spec/work item in this wave Merged with clean destinations, all validators green, and the ten-role panel seal recorded", - "id": "ADR046-cred-ss-001", - "kind": "work-item", - "owner": "Dependency for ADR046-cred-ss-003; owner: `packages/d2b-contracts` Credential ResourceType contract", - "parallelGroup": "wi:ADR-046-provider-credential-secret-service", - "prerequisites": [ + "exitGate" : "ADR046-W6 exit criteria (ADR-046-validation-and-delivery §4): every spec/work item in this wave Merged with clean destinations, all validators green, and the ten-role panel seal recorded", + "id" : "ADR046-cred-ss-001", + "kind" : "work-item", + "owner" : "Dependency for ADR046-cred-ss-003; owner: `packages/d2b-contracts` Credential ResourceType contract", + "parallelGroup" : "wi:ADR-046-provider-credential-secret-service", + "prerequisites" : [ "ADR-046-provider-credential-secret-service" ], - "specId": "ADR-046-provider-credential-secret-service", - "topologicalRank": 11, - "wave": "W6" + "specId" : "ADR-046-provider-credential-secret-service", + "topologicalRank" : 11, + "wave" : "W6" }, { - "blockers": [], - "destinations": [ + "blockers" : [], + "destinations" : [ "packages/d2b-contracts/proto/v3/credential.proto; packages/d2b-credential-service/" ], - "entryContracts": [ + "entryContracts" : [ "ADR-046-provider-credential-secret-service" ], - "exitGate": "ADR046-W6 exit criteria (ADR-046-validation-and-delivery §4): every spec/work item in this wave Merged with clean destinations, all validators green, and the ten-role panel seal recorded", - "id": "ADR046-cred-ss-002", - "kind": "work-item", - "owner": "Dependency for ADR046-cred-ss-003; owner: credential service contract/codegen", - "parallelGroup": "wi:ADR-046-provider-credential-secret-service", - "prerequisites": [ + "exitGate" : "ADR046-W6 exit criteria (ADR-046-validation-and-delivery §4): every spec/work item in this wave Merged with clean destinations, all validators green, and the ten-role panel seal recorded", + "id" : "ADR046-cred-ss-002", + "kind" : "work-item", + "owner" : "Dependency for ADR046-cred-ss-003; owner: credential service contract/codegen", + "parallelGroup" : "wi:ADR-046-provider-credential-secret-service", + "prerequisites" : [ "ADR-046-provider-credential-secret-service" ], - "specId": "ADR-046-provider-credential-secret-service", - "topologicalRank": 11, - "wave": "W6" + "specId" : "ADR-046-provider-credential-secret-service", + "topologicalRank" : 11, + "wave" : "W6" }, { - "blockers": [], - "destinations": [ + "blockers" : [], + "destinations" : [ "`packages/d2b-provider-credential-secret-service/src/{lib.rs, controller.rs, service.rs, main.rs}`; `packages/d2b-provider-credential-secret-service/tests/{lifecycle.rs, conformance.rs, faults.rs, canary.rs, delivery.rs, placement.rs}`; `packages/d2b-provider-credential-secret-service/integration/{container-service.sh, host-placement.nix, guest-placement.nix, cleanup-rollback.sh}`; `packages/d2b-provider-credential-secret-service/README.md`" ], - "entryContracts": [ + "entryContracts" : [ "ADR-046-provider-credential-secret-service" ], - "exitGate": "ADR046-W6 exit criteria (ADR-046-validation-and-delivery §4): every spec/work item in this wave Merged with clean destinations, all validators green, and the ten-role panel seal recorded", - "id": "ADR046-cred-ss-003", - "kind": "work-item", - "owner": "`ADR046-cred-ss-001` (contract types); `ADR046-cred-ss-002` (service proto); `ADR046-reconcile-001`; credential-secret-service owner", - "parallelGroup": "wi:ADR-046-provider-credential-secret-service", - "prerequisites": [ + "exitGate" : "ADR046-W6 exit criteria (ADR-046-validation-and-delivery §4): every spec/work item in this wave Merged with clean destinations, all validators green, and the ten-role panel seal recorded", + "id" : "ADR046-cred-ss-003", + "kind" : "work-item", + "owner" : "`ADR046-cred-ss-001` (contract types); `ADR046-cred-ss-002` (service proto); `ADR046-reconcile-001`; credential-secret-service owner", + "parallelGroup" : "wi:ADR-046-provider-credential-secret-service", + "prerequisites" : [ "ADR-046-provider-credential-secret-service", + "ADR046-cred-ss-001", + "ADR046-cred-ss-002", "ADR046-reconcile-001" ], - "specId": "ADR-046-provider-credential-secret-service", - "topologicalRank": 11, - "wave": "W6" + "specId" : "ADR-046-provider-credential-secret-service", + "topologicalRank" : 12, + "wave" : "W6" }, { - "blockers": [], - "destinations": [ + "blockers" : [], + "destinations" : [ "packages/d2b-provider-credential-/src/controller.rs" ], - "entryContracts": [ + "entryContracts" : [ "ADR-046-provider-credential-secret-service" ], - "exitGate": "ADR046-W6 exit criteria (ADR-046-validation-and-delivery §4): every spec/work item in this wave Merged with clean destinations, all validators green, and the ten-role panel seal recorded", - "id": "ADR046-cred-ss-004", - "kind": "work-item", - "owner": "Dependency for ADR046-cred-ss-003; owner: common Credential controller/reconciliation toolkit", - "parallelGroup": "wi:ADR-046-provider-credential-secret-service", - "prerequisites": [ + "exitGate" : "ADR046-W6 exit criteria (ADR-046-validation-and-delivery §4): every spec/work item in this wave Merged with clean destinations, all validators green, and the ten-role panel seal recorded", + "id" : "ADR046-cred-ss-004", + "kind" : "work-item", + "owner" : "Dependency for ADR046-cred-ss-003; owner: common Credential controller/reconciliation toolkit", + "parallelGroup" : "wi:ADR-046-provider-credential-secret-service", + "prerequisites" : [ "ADR-046-provider-credential-secret-service" ], - "specId": "ADR-046-provider-credential-secret-service", - "topologicalRank": 11, - "wave": "W6" + "specId" : "ADR-046-provider-credential-secret-service", + "topologicalRank" : 11, + "wave" : "W6" }, { - "blockers": [], - "destinations": [ + "blockers" : [], + "destinations" : [ "nixos-modules/options-resources.nix; nixos-modules/activation-nixos-cleanup.nix" ], - "entryContracts": [ + "entryContracts" : [ "ADR-046-provider-credential-secret-service" ], - "exitGate": "ADR046-W6 exit criteria (ADR-046-validation-and-delivery §4): every spec/work item in this wave Merged with clean destinations, all validators green, and the ten-role panel seal recorded", - "id": "ADR046-cred-ss-005", - "kind": "work-item", - "owner": "Dependency for ADR046-cred-ss-003; owner: Nix resource compiler and activation cleanup", - "parallelGroup": "wi:ADR-046-provider-credential-secret-service", - "prerequisites": [ + "exitGate" : "ADR046-W6 exit criteria (ADR-046-validation-and-delivery §4): every spec/work item in this wave Merged with clean destinations, all validators green, and the ten-role panel seal recorded", + "id" : "ADR046-cred-ss-005", + "kind" : "work-item", + "owner" : "Dependency for ADR046-cred-ss-003; owner: Nix resource compiler and activation cleanup", + "parallelGroup" : "wi:ADR-046-provider-credential-secret-service", + "prerequisites" : [ "ADR-046-provider-credential-secret-service" ], - "specId": "ADR-046-provider-credential-secret-service", - "topologicalRank": 11, - "wave": "W6" + "specId" : "ADR-046-provider-credential-secret-service", + "topologicalRank" : 11, + "wave" : "W6" }, { - "blockers": [], - "destinations": [ + "blockers" : [], + "destinations" : [ "packages/d2b-provider-credential-secret-service/src/{audit.rs,telemetry.rs}" ], - "entryContracts": [ + "entryContracts" : [ "ADR-046-provider-credential-secret-service" ], - "exitGate": "ADR046-W6 exit criteria (ADR-046-validation-and-delivery §4): every spec/work item in this wave Merged with clean destinations, all validators green, and the ten-role panel seal recorded", - "id": "ADR046-cred-ss-006", - "kind": "work-item", - "owner": "Dependency for ADR046-cred-ss-003; owner: credential-secret-service audit and telemetry implementation", - "parallelGroup": "wi:ADR-046-provider-credential-secret-service", - "prerequisites": [ + "exitGate" : "ADR046-W6 exit criteria (ADR-046-validation-and-delivery §4): every spec/work item in this wave Merged with clean destinations, all validators green, and the ten-role panel seal recorded", + "id" : "ADR046-cred-ss-006", + "kind" : "work-item", + "owner" : "Dependency for ADR046-cred-ss-003; owner: credential-secret-service audit and telemetry implementation", + "parallelGroup" : "wi:ADR-046-provider-credential-secret-service", + "prerequisites" : [ "ADR-046-provider-credential-secret-service" ], - "specId": "ADR-046-provider-credential-secret-service", - "topologicalRank": 11, - "wave": "W6" + "specId" : "ADR-046-provider-credential-secret-service", + "topologicalRank" : 11, + "wave" : "W6" }, { - "blockers": [], - "destinations": [ + "blockers" : [], + "destinations" : [ "`packages/d2b-contracts/src/v3/credential.rs`" ], - "entryContracts": [ + "entryContracts" : [ "ADR-046-resources-credential" ], - "exitGate": "ADR046-W4 exit criteria (ADR-046-validation-and-delivery §4): every spec/work item in this wave Merged with clean destinations, all validators green, and the ten-role panel seal recorded", - "id": "ADR046-credential-001", - "kind": "work-item", - "owner": "`ADR046-object-001` (resource envelope); `ADR046-identities-001` (types); W0 shared contract root; `d2b-contracts`", - "parallelGroup": "wi:ADR-046-resources-credential", - "prerequisites": [ + "exitGate" : "ADR046-W4 exit criteria (ADR-046-validation-and-delivery §4): every spec/work item in this wave Merged with clean destinations, all validators green, and the ten-role panel seal recorded", + "id" : "ADR046-credential-001", + "kind" : "work-item", + "owner" : "`ADR046-object-001` (resource envelope); `ADR046-identities-001` (types); W0 shared contract root; `d2b-contracts`", + "parallelGroup" : "wi:ADR-046-resources-credential", + "prerequisites" : [ "ADR-046-resources-credential", "ADR046-identities-001", "ADR046-object-001" ], - "specId": "ADR-046-resources-credential", - "topologicalRank": 9, - "wave": "W4" + "specId" : "ADR-046-resources-credential", + "topologicalRank" : 9, + "wave" : "W4" }, { - "blockers": [], - "destinations": [ + "blockers" : [], + "destinations" : [ "`packages/d2b-contracts/proto/v3/credential.proto`; `packages/d2b-credential-service/src/{service.rs, client.rs, server.rs}`" ], - "entryContracts": [ + "entryContracts" : [ "ADR-046-resources-credential" ], - "exitGate": "ADR046-W4 exit criteria (ADR-046-validation-and-delivery §4): every spec/work item in this wave Merged with clean destinations, all validators green, and the ten-role panel seal recorded", - "id": "ADR046-credential-002", - "kind": "work-item", - "owner": "`ADR046-credential-001`; `ADR046-api-001` (resource API); `ADR046-bus-001` (d2b-bus); Credential service owner", - "parallelGroup": "wi:ADR-046-resources-credential", - "prerequisites": [ + "exitGate" : "ADR046-W4 exit criteria (ADR-046-validation-and-delivery §4): every spec/work item in this wave Merged with clean destinations, all validators green, and the ten-role panel seal recorded", + "id" : "ADR046-credential-002", + "kind" : "work-item", + "owner" : "`ADR046-credential-001`; `ADR046-api-001` (resource API); `ADR046-session-003` (d2b-bus); Credential service owner", + "parallelGroup" : "wi:ADR-046-resources-credential", + "prerequisites" : [ "ADR-046-resources-credential", "ADR046-api-001", - "ADR046-bus-001", - "ADR046-credential-001" + "ADR046-credential-001", + "ADR046-session-003" ], - "specId": "ADR-046-resources-credential", - "topologicalRank": 10, - "wave": "W4" + "specId" : "ADR-046-resources-credential", + "topologicalRank" : 10, + "wave" : "W4" }, { - "blockers": [], - "destinations": [ + "blockers" : [], + "destinations" : [ "`packages/d2b-provider-credential-secret-service/src/{lib.rs, controller.rs, service.rs, main.rs}` (implementation + binary); `packages/d2b-provider-credential-secret-service/tests/{lifecycle.rs, conformance.rs, faults.rs, canary.rs, delivery.rs, placement.rs}` (hermetic Cargo integration); `packages/d2b-provider-credential-secret-service/integration/{container-service.sh, host-placement.nix, cleanup-rollback.sh}` (orchestration fixtures); `packages/d2b-provider-credential-secret-service/README.md` (all §Provider README required sections)" ], - "entryContracts": [ + "entryContracts" : [ "ADR-046-resources-credential" ], - "exitGate": "ADR046-W4 exit criteria (ADR-046-validation-and-delivery §4): every spec/work item in this wave Merged with clean destinations, all validators green, and the ten-role panel seal recorded", - "id": "ADR046-credential-003", - "kind": "work-item", - "owner": "`ADR046-credential-001`, `ADR046-credential-002`; `ADR046-reconcile-001`; credential-secret-service owner", - "parallelGroup": "wi:ADR-046-resources-credential", - "prerequisites": [ + "exitGate" : "ADR046-W4 exit criteria (ADR-046-validation-and-delivery §4): every spec/work item in this wave Merged with clean destinations, all validators green, and the ten-role panel seal recorded", + "id" : "ADR046-credential-003", + "kind" : "work-item", + "owner" : "`ADR046-credential-001`, `ADR046-credential-002`; `ADR046-reconcile-001`; credential-secret-service owner", + "parallelGroup" : "wi:ADR-046-resources-credential", + "prerequisites" : [ "ADR-046-resources-credential", "ADR046-credential-001", "ADR046-credential-002", "ADR046-reconcile-001" ], - "specId": "ADR-046-resources-credential", - "topologicalRank": 11, - "wave": "W4" + "specId" : "ADR-046-resources-credential", + "topologicalRank" : 11, + "wave" : "W4" }, { - "blockers": [], - "destinations": [ + "blockers" : [], + "destinations" : [ "`packages/d2b-provider-credential-entra/src/{lib.rs, controller.rs, service.rs, main.rs}`; `packages/d2b-provider-credential-entra/tests/{lifecycle.rs, conformance.rs, faults.rs, canary.rs, delivery.rs, placement.rs}`; `packages/d2b-provider-credential-entra/integration/{container-service.sh, guest-placement.nix, cleanup-rollback.sh}`; `packages/d2b-provider-credential-entra/README.md` (all §Provider README required sections)" ], - "entryContracts": [ + "entryContracts" : [ "ADR-046-resources-credential" ], - "exitGate": "ADR046-W4 exit criteria (ADR-046-validation-and-delivery §4): every spec/work item in this wave Merged with clean destinations, all validators green, and the ten-role panel seal recorded", - "id": "ADR046-credential-004", - "kind": "work-item", - "owner": "`ADR046-credential-001`, `ADR046-credential-002`; `ADR046-reconcile-001`; credential-entra owner", - "parallelGroup": "wi:ADR-046-resources-credential", - "prerequisites": [ + "exitGate" : "ADR046-W4 exit criteria (ADR-046-validation-and-delivery §4): every spec/work item in this wave Merged with clean destinations, all validators green, and the ten-role panel seal recorded", + "id" : "ADR046-credential-004", + "kind" : "work-item", + "owner" : "`ADR046-credential-001`, `ADR046-credential-002`; `ADR046-reconcile-001`; credential-entra owner", + "parallelGroup" : "wi:ADR-046-resources-credential", + "prerequisites" : [ "ADR-046-resources-credential", "ADR046-credential-001", "ADR046-credential-002", "ADR046-reconcile-001" ], - "specId": "ADR-046-resources-credential", - "topologicalRank": 11, - "wave": "W4" + "specId" : "ADR-046-resources-credential", + "topologicalRank" : 11, + "wave" : "W4" }, { - "blockers": [], - "destinations": [ + "blockers" : [], + "destinations" : [ "`packages/d2b-provider-credential-managed-identity/src/{lib.rs, controller.rs, service.rs, main.rs}`; `packages/d2b-provider-credential-managed-identity/tests/{lifecycle.rs, conformance.rs, faults.rs, canary.rs, delivery.rs, placement.rs}`; `packages/d2b-provider-credential-managed-identity/integration/{container-service.sh, host-guest-placement.nix, aca-credential-ref.sh, cleanup-rollback.sh}`; `packages/d2b-provider-credential-managed-identity/README.md` (all §Provider README required sections)" ], - "entryContracts": [ + "entryContracts" : [ "ADR-046-resources-credential" ], - "exitGate": "ADR046-W4 exit criteria (ADR-046-validation-and-delivery §4): every spec/work item in this wave Merged with clean destinations, all validators green, and the ten-role panel seal recorded", - "id": "ADR046-credential-005", - "kind": "work-item", - "owner": "`ADR046-credential-001`, `ADR046-credential-002`; `ADR046-reconcile-001`; credential-managed-identity owner", - "parallelGroup": "wi:ADR-046-resources-credential", - "prerequisites": [ + "exitGate" : "ADR046-W4 exit criteria (ADR-046-validation-and-delivery §4): every spec/work item in this wave Merged with clean destinations, all validators green, and the ten-role panel seal recorded", + "id" : "ADR046-credential-005", + "kind" : "work-item", + "owner" : "`ADR046-credential-001`, `ADR046-credential-002`; `ADR046-reconcile-001`; credential-managed-identity owner", + "parallelGroup" : "wi:ADR-046-resources-credential", + "prerequisites" : [ "ADR-046-resources-credential", "ADR046-credential-001", "ADR046-credential-002", "ADR046-reconcile-001" ], - "specId": "ADR-046-resources-credential", - "topologicalRank": 11, - "wave": "W4" + "specId" : "ADR-046-resources-credential", + "topologicalRank" : 11, + "wave" : "W4" }, { - "blockers": [], - "destinations": [ + "blockers" : [], + "destinations" : [ "`packages/d2b-provider-credential-/src/controller.rs`; `packages/d2b-contracts/src/v3/credential_controller.rs`" ], - "entryContracts": [ + "entryContracts" : [ "ADR-046-resources-credential" ], - "exitGate": "ADR046-W4 exit criteria (ADR-046-validation-and-delivery §4): every spec/work item in this wave Merged with clean destinations, all validators green, and the ten-role panel seal recorded", - "id": "ADR046-credential-006", - "kind": "work-item", - "owner": "`ADR046-credential-001`, `ADR046-credential-002`; `ADR046-reconcile-001`, `ADR046-reconcile-002`; Credential controller owner", - "parallelGroup": "wi:ADR-046-resources-credential", - "prerequisites": [ + "exitGate" : "ADR046-W4 exit criteria (ADR-046-validation-and-delivery §4): every spec/work item in this wave Merged with clean destinations, all validators green, and the ten-role panel seal recorded", + "id" : "ADR046-credential-006", + "kind" : "work-item", + "owner" : "`ADR046-credential-001`, `ADR046-credential-002`; `ADR046-reconcile-001`, `ADR046-reconcile-002`; Credential controller owner", + "parallelGroup" : "wi:ADR-046-resources-credential", + "prerequisites" : [ "ADR-046-resources-credential", "ADR046-credential-001", "ADR046-credential-002", "ADR046-reconcile-001", "ADR046-reconcile-002" ], - "specId": "ADR-046-resources-credential", - "topologicalRank": 11, - "wave": "W4" + "specId" : "ADR-046-resources-credential", + "topologicalRank" : 11, + "wave" : "W4" }, { - "blockers": [], - "destinations": [ + "blockers" : [], + "destinations" : [ "`nixos-modules/options-resources.nix` (generic schema-derived resource options; not type-specific), `nixos-modules/activation-nixos-cleanup.nix`" ], - "entryContracts": [ + "entryContracts" : [ "ADR-046-resources-credential" ], - "exitGate": "ADR046-W4 exit criteria (ADR-046-validation-and-delivery §4): every spec/work item in this wave Merged with clean destinations, all validators green, and the ten-role panel seal recorded", - "id": "ADR046-credential-007", - "kind": "work-item", - "owner": "`ADR046-credential-001`; `ADR046-identities-002` (Nix resource compiler); `ADR046-api-001` (resource API, for create/update/delete); `ADR046-reconcile-001` (activation-nixos controller); Nix integrator", - "parallelGroup": "wi:ADR-046-resources-credential", - "prerequisites": [ + "exitGate" : "ADR046-W4 exit criteria (ADR-046-validation-and-delivery §4): every spec/work item in this wave Merged with clean destinations, all validators green, and the ten-role panel seal recorded", + "id" : "ADR046-credential-007", + "kind" : "work-item", + "owner" : "`ADR046-credential-001`; `ADR046-identities-002` (Nix resource compiler); `ADR046-api-001` (resource API, for create/update/delete); `ADR046-reconcile-001` (activation-nixos controller); Nix integrator", + "parallelGroup" : "wi:ADR-046-resources-credential", + "prerequisites" : [ "ADR-046-resources-credential", "ADR046-api-001", "ADR046-credential-001", "ADR046-identities-002", "ADR046-reconcile-001" ], - "specId": "ADR-046-resources-credential", - "topologicalRank": 10, - "wave": "W4" + "specId" : "ADR-046-resources-credential", + "topologicalRank" : 10, + "wave" : "W4" }, { - "blockers": [], - "destinations": [ + "blockers" : [], + "destinations" : [ "`packages/d2b-provider-credential-/src/audit.rs`, `telemetry.rs`; `packages/d2b-contract-tests/tests/credential_audit.rs`" ], - "entryContracts": [ + "entryContracts" : [ "ADR-046-resources-credential" ], - "exitGate": "ADR046-W4 exit criteria (ADR-046-validation-and-delivery §4): every spec/work item in this wave Merged with clean destinations, all validators green, and the ten-role panel seal recorded", - "id": "ADR046-credential-008", - "kind": "work-item", - "owner": "`ADR046-credential-001`, `ADR046-credential-006`; audit/OTEL integrator", - "parallelGroup": "wi:ADR-046-resources-credential", - "prerequisites": [ + "exitGate" : "ADR046-W4 exit criteria (ADR-046-validation-and-delivery §4): every spec/work item in this wave Merged with clean destinations, all validators green, and the ten-role panel seal recorded", + "id" : "ADR046-credential-008", + "kind" : "work-item", + "owner" : "`ADR046-credential-001`, `ADR046-credential-006`; audit/OTEL integrator", + "parallelGroup" : "wi:ADR-046-resources-credential", + "prerequisites" : [ "ADR-046-resources-credential", "ADR046-credential-001", "ADR046-credential-006" ], - "specId": "ADR-046-resources-credential", - "topologicalRank": 12, - "wave": "W4" + "specId" : "ADR-046-resources-credential", + "topologicalRank" : 12, + "wave" : "W4" }, { - "blockers": [], - "destinations": [ + "blockers" : [], + "destinations" : [ "`docs/specs/ADR-046-decision-register.md`" ], - "entryContracts": [ + "entryContracts" : [ "ADR-046-decision-register" ], - "exitGate": "ADR046-W0 exit criteria (ADR-046-validation-and-delivery §4): every spec/work item in this wave Merged with clean destinations, all validators green, and the ten-role panel seal recorded", - "id": "ADR046-decisions-001", - "kind": "work-item", - "owner": "ADR 0046 integrator", - "parallelGroup": "wi:ADR-046-decision-register", - "prerequisites": [ + "exitGate" : "ADR046-W0 exit criteria (ADR-046-validation-and-delivery §4): every spec/work item in this wave Merged with clean destinations, all validators green, and the ten-role panel seal recorded", + "id" : "ADR046-decisions-001", + "kind" : "work-item", + "owner" : "ADR 0046 integrator", + "parallelGroup" : "wi:ADR-046-decision-register", + "prerequisites" : [ "ADR-046-decision-register" ], - "specId": "ADR-046-decision-register", - "topologicalRank": 1, - "wave": "W0" + "specId" : "ADR-046-decision-register", + "topologicalRank" : 1, + "wave" : "W0" }, { - "blockers": [], - "destinations": [ + "blockers" : [], + "destinations" : [ "`packages/xtask/src/heavy_gate.rs`; `Makefile` targets `heavy-check`, `heavy-test-integration`, `heavy-test-host-integration`, `heavy-test-hardware`, `heavy-cargo-test`, `heavy-flake-check`" ], - "entryContracts": [ + "entryContracts" : [ "ADR-046-validation-and-delivery" ], - "exitGate": "ADR046-W7 exit criteria (ADR-046-validation-and-delivery §4): every spec/work item in this wave Merged with clean destinations, all validators green, and the ten-role panel seal recorded", - "id": "ADR046-delivery-001", - "kind": "work-item", - "owner": "`ADR046-W0`; delivery-tooling integrator", - "parallelGroup": "wi:ADR-046-validation-and-delivery", - "prerequisites": [ + "exitGate" : "ADR046-W7 exit criteria (ADR-046-validation-and-delivery §4): every spec/work item in this wave Merged with clean destinations, all validators green, and the ten-role panel seal recorded", + "id" : "ADR046-delivery-001", + "kind" : "work-item", + "owner" : "`ADR046-W0`; delivery-tooling integrator", + "parallelGroup" : "wi:ADR-046-validation-and-delivery", + "prerequisites" : [ "ADR-046-validation-and-delivery" ], - "specId": "ADR-046-validation-and-delivery", - "topologicalRank": 13, - "wave": "W7" + "specId" : "ADR-046-validation-and-delivery", + "topologicalRank" : 13, + "wave" : "W7" }, { - "blockers": [], - "destinations": [ + "blockers" : [], + "destinations" : [ "`packages/xtask/src/delivery/snapshot.rs`" ], - "entryContracts": [ + "entryContracts" : [ "ADR-046-validation-and-delivery" ], - "exitGate": "ADR046-W7 exit criteria (ADR-046-validation-and-delivery §4): every spec/work item in this wave Merged with clean destinations, all validators green, and the ten-role panel seal recorded", - "id": "ADR046-delivery-002", - "kind": "work-item", - "owner": "`ADR046-W0`; delivery-tooling integrator", - "parallelGroup": "wi:ADR-046-validation-and-delivery", - "prerequisites": [ + "exitGate" : "ADR046-W7 exit criteria (ADR-046-validation-and-delivery §4): every spec/work item in this wave Merged with clean destinations, all validators green, and the ten-role panel seal recorded", + "id" : "ADR046-delivery-002", + "kind" : "work-item", + "owner" : "`ADR046-W0`; delivery-tooling integrator", + "parallelGroup" : "wi:ADR-046-validation-and-delivery", + "prerequisites" : [ "ADR-046-validation-and-delivery" ], - "specId": "ADR-046-validation-and-delivery", - "topologicalRank": 13, - "wave": "W7" + "specId" : "ADR-046-validation-and-delivery", + "topologicalRank" : 13, + "wave" : "W7" }, { - "blockers": [], - "destinations": [ + "blockers" : [], + "destinations" : [ "`packages/xtask/src/delivery/validate_import.rs`; external candidate-ID-addressed evidence directory (never under Git)" ], - "entryContracts": [ + "entryContracts" : [ "ADR-046-validation-and-delivery" ], - "exitGate": "ADR046-W7 exit criteria (ADR-046-validation-and-delivery §4): every spec/work item in this wave Merged with clean destinations, all validators green, and the ten-role panel seal recorded", - "id": "ADR046-delivery-003", - "kind": "work-item", - "owner": "`ADR046-delivery-002`; delivery-tooling integrator", - "parallelGroup": "wi:ADR-046-validation-and-delivery", - "prerequisites": [ + "exitGate" : "ADR046-W7 exit criteria (ADR-046-validation-and-delivery §4): every spec/work item in this wave Merged with clean destinations, all validators green, and the ten-role panel seal recorded", + "id" : "ADR046-delivery-003", + "kind" : "work-item", + "owner" : "`ADR046-delivery-002`; delivery-tooling integrator", + "parallelGroup" : "wi:ADR-046-validation-and-delivery", + "prerequisites" : [ "ADR-046-validation-and-delivery", "ADR046-delivery-002" ], - "specId": "ADR-046-validation-and-delivery", - "topologicalRank": 14, - "wave": "W7" + "specId" : "ADR-046-validation-and-delivery", + "topologicalRank" : 14, + "wave" : "W7" }, { - "blockers": [], - "destinations": [ + "blockers" : [], + "destinations" : [ "`packages/xtask/src/gen_spec_set.rs`; `docs/specs/ADR-046-spec-set.json`, `docs/specs/ADR-046-work-items.json`" ], - "entryContracts": [ + "entryContracts" : [ "ADR-046-validation-and-delivery" ], - "exitGate": "ADR046-W7 exit criteria (ADR-046-validation-and-delivery §4): every spec/work item in this wave Merged with clean destinations, all validators green, and the ten-role panel seal recorded", - "id": "ADR046-delivery-004", - "kind": "work-item", - "owner": "`ADR046-delivery-002`; spec-set integrator", - "parallelGroup": "wi:ADR-046-validation-and-delivery", - "prerequisites": [ + "exitGate" : "ADR046-W7 exit criteria (ADR-046-validation-and-delivery §4): every spec/work item in this wave Merged with clean destinations, all validators green, and the ten-role panel seal recorded", + "id" : "ADR046-delivery-004", + "kind" : "work-item", + "owner" : "`ADR046-delivery-002`; spec-set integrator", + "parallelGroup" : "wi:ADR-046-validation-and-delivery", + "prerequisites" : [ "ADR-046-validation-and-delivery", "ADR046-delivery-002" ], - "specId": "ADR-046-validation-and-delivery", - "topologicalRank": 14, - "wave": "W7" + "specId" : "ADR-046-validation-and-delivery", + "topologicalRank" : 14, + "wave" : "W7" }, { - "blockers": [], - "destinations": [ + "blockers" : [], + "destinations" : [ "`packages/xtask/src/delivery/panel.rs`" ], - "entryContracts": [ + "entryContracts" : [ "ADR-046-validation-and-delivery" ], - "exitGate": "ADR046-W7 exit criteria (ADR-046-validation-and-delivery §4): every spec/work item in this wave Merged with clean destinations, all validators green, and the ten-role panel seal recorded", - "id": "ADR046-delivery-005", - "kind": "work-item", - "owner": "`ADR046-delivery-002`, `ADR046-delivery-003`; panel-tooling integrator", - "parallelGroup": "wi:ADR-046-validation-and-delivery", - "prerequisites": [ + "exitGate" : "ADR046-W7 exit criteria (ADR-046-validation-and-delivery §4): every spec/work item in this wave Merged with clean destinations, all validators green, and the ten-role panel seal recorded", + "id" : "ADR046-delivery-005", + "kind" : "work-item", + "owner" : "`ADR046-delivery-002`, `ADR046-delivery-003`; panel-tooling integrator", + "parallelGroup" : "wi:ADR-046-validation-and-delivery", + "prerequisites" : [ "ADR-046-validation-and-delivery", "ADR046-delivery-002", "ADR046-delivery-003" ], - "specId": "ADR-046-validation-and-delivery", - "topologicalRank": 15, - "wave": "W7" + "specId" : "ADR-046-validation-and-delivery", + "topologicalRank" : 15, + "wave" : "W7" }, { - "blockers": [], - "destinations": [ + "blockers" : [], + "destinations" : [ "`packages/xtask/src/delivery/{seal,eligibility,history_proof}.rs`" ], - "entryContracts": [ + "entryContracts" : [ "ADR-046-validation-and-delivery" ], - "exitGate": "ADR046-W7 exit criteria (ADR-046-validation-and-delivery §4): every spec/work item in this wave Merged with clean destinations, all validators green, and the ten-role panel seal recorded", - "id": "ADR046-delivery-006", - "kind": "work-item", - "owner": "`ADR046-delivery-002`, `ADR046-delivery-004`, `ADR046-delivery-005`; delivery-tooling integrator", - "parallelGroup": "wi:ADR-046-validation-and-delivery", - "prerequisites": [ + "exitGate" : "ADR046-W7 exit criteria (ADR-046-validation-and-delivery §4): every spec/work item in this wave Merged with clean destinations, all validators green, and the ten-role panel seal recorded", + "id" : "ADR046-delivery-006", + "kind" : "work-item", + "owner" : "`ADR046-delivery-002`, `ADR046-delivery-004`, `ADR046-delivery-005`; delivery-tooling integrator", + "parallelGroup" : "wi:ADR-046-validation-and-delivery", + "prerequisites" : [ "ADR-046-validation-and-delivery", "ADR046-delivery-002", "ADR046-delivery-004", "ADR046-delivery-005" ], - "specId": "ADR-046-validation-and-delivery", - "topologicalRank": 16, - "wave": "W7" + "specId" : "ADR-046-validation-and-delivery", + "topologicalRank" : 16, + "wave" : "W7" }, { - "blockers": [], - "destinations": [ + "blockers" : [], + "destinations" : [ "`packages/xtask/src/test_runtime_ledger.rs`; a `make`-invokable timing gate reusing `make test-rust`/Layer-1 shard targets" ], - "entryContracts": [ + "entryContracts" : [ "ADR-046-validation-and-delivery" ], - "exitGate": "ADR046-W7 exit criteria (ADR-046-validation-and-delivery §4): every spec/work item in this wave Merged with clean destinations, all validators green, and the ten-role panel seal recorded", - "id": "ADR046-delivery-007", - "kind": "work-item", - "owner": "`ADR046-W0`; delivery-tooling integrator", - "parallelGroup": "wi:ADR-046-validation-and-delivery", - "prerequisites": [ + "exitGate" : "ADR046-W7 exit criteria (ADR-046-validation-and-delivery §4): every spec/work item in this wave Merged with clean destinations, all validators green, and the ten-role panel seal recorded", + "id" : "ADR046-delivery-007", + "kind" : "work-item", + "owner" : "`ADR046-W0`; delivery-tooling integrator", + "parallelGroup" : "wi:ADR-046-validation-and-delivery", + "prerequisites" : [ "ADR-046-validation-and-delivery" ], - "specId": "ADR-046-validation-and-delivery", - "topologicalRank": 13, - "wave": "W7" + "specId" : "ADR-046-validation-and-delivery", + "topologicalRank" : 13, + "wave" : "W7" }, { - "blockers": [], - "destinations": [ + "blockers" : [], + "destinations" : [ "`docs/specs/ADR-046-implementation-graph.json`, `docs/specs/ADR-046-implementation-graph.md` (generated by `ADR046-streamline-001`'s `xtask implementation-graph`); the artifact contract, generation, validation, and ready-wave query are owned by §3.5 of this spec" ], - "entryContracts": [ + "entryContracts" : [ "ADR-046-validation-and-delivery" ], - "exitGate": "ADR046-W7 exit criteria (ADR-046-validation-and-delivery §4): every spec/work item in this wave Merged with clean destinations, all validators green, and the ten-role panel seal recorded", - "id": "ADR046-delivery-008", - "kind": "work-item", - "owner": "`ADR046-streamline-001`, `ADR046-W0`; delivery-tooling integrator", - "parallelGroup": "wi:ADR-046-validation-and-delivery", - "prerequisites": [ + "exitGate" : "ADR046-W7 exit criteria (ADR-046-validation-and-delivery §4): every spec/work item in this wave Merged with clean destinations, all validators green, and the ten-role panel seal recorded", + "id" : "ADR046-delivery-008", + "kind" : "work-item", + "owner" : "`ADR046-streamline-001`, `ADR046-W0`; delivery-tooling integrator", + "parallelGroup" : "wi:ADR-046-validation-and-delivery", + "prerequisites" : [ "ADR-046-validation-and-delivery", "ADR046-streamline-001" ], - "specId": "ADR-046-validation-and-delivery", - "topologicalRank": 13, - "wave": "W7" + "specId" : "ADR-046-validation-and-delivery", + "topologicalRank" : 13, + "wave" : "W7" + }, + { + "blockers" : [], + "destinations" : [ + "`packages/xtask/src/gen_spec_set.rs`; `packages/d2b-contract-tests/tests/policy_adr046_work_items.rs`; generated spec-set, work-item, and implementation-graph drift checks" + ], + "entryContracts" : [ + "ADR-046-validation-and-delivery" + ], + "exitGate" : "ADR046-W7 exit criteria (ADR-046-validation-and-delivery §4): every spec/work item in this wave Merged with clean destinations, all validators green, and the ten-role panel seal recorded", + "id" : "ADR046-delivery-009", + "kind" : "work-item", + "owner" : "`ADR046-delivery-004`, `ADR046-delivery-008`; spec-set policy-test owner", + "parallelGroup" : "wi:ADR-046-validation-and-delivery", + "prerequisites" : [ + "ADR-046-validation-and-delivery", + "ADR046-delivery-004", + "ADR046-delivery-008" + ], + "specId" : "ADR-046-validation-and-delivery", + "topologicalRank" : 15, + "wave" : "W7" }, { - "blockers": [], - "destinations": [ + "blockers" : [], + "destinations" : [ "`packages/d2b-contracts/src/v3/device.rs`" ], - "entryContracts": [ + "entryContracts" : [ "ADR-046-resources-device" ], - "exitGate": "ADR046-W5 exit criteria (ADR-046-validation-and-delivery §4): every spec/work item in this wave Merged with clean destinations, all validators green, and the ten-role panel seal recorded", - "id": "ADR046-device-001", - "kind": "work-item", - "owner": "W0 shared contract root; `d2b-contracts`", - "parallelGroup": "wi:ADR-046-resources-device", - "prerequisites": [ + "exitGate" : "ADR046-W5 exit criteria (ADR-046-validation-and-delivery §4): every spec/work item in this wave Merged with clean destinations, all validators green, and the ten-role panel seal recorded", + "id" : "ADR046-device-001", + "kind" : "work-item", + "owner" : "W0 shared contract root; `d2b-contracts`", + "parallelGroup" : "wi:ADR-046-resources-device", + "prerequisites" : [ "ADR-046-resources-device" ], - "specId": "ADR-046-resources-device", - "topologicalRank": 10, - "wave": "W5" + "specId" : "ADR-046-resources-device", + "topologicalRank" : 10, + "wave" : "W5" }, { - "blockers": [], - "destinations": [ + "blockers" : [], + "destinations" : [ "`packages/d2b-provider-device-tpm/src/` (controller, swtpm runner, state-dir logic); `packages/d2b-provider-device-tpm/tests/` (hermetic Cargo integration); `packages/d2b-provider-device-tpm/integration/` (container/Host scenarios); `packages/d2b-provider-device-tpm/README.md`" ], - "entryContracts": [ + "entryContracts" : [ "ADR-046-resources-device" ], - "exitGate": "ADR046-W5 exit criteria (ADR-046-validation-and-delivery §4): every spec/work item in this wave Merged with clean destinations, all validators green, and the ten-role panel seal recorded", - "id": "ADR046-device-002", - "kind": "work-item", - "owner": "ADR046-device-001; device-tpm provider owner", - "parallelGroup": "wi:ADR-046-resources-device", - "prerequisites": [ + "exitGate" : "ADR046-W5 exit criteria (ADR-046-validation-and-delivery §4): every spec/work item in this wave Merged with clean destinations, all validators green, and the ten-role panel seal recorded", + "id" : "ADR046-device-002", + "kind" : "work-item", + "owner" : "ADR046-device-001; device-tpm provider owner", + "parallelGroup" : "wi:ADR-046-resources-device", + "prerequisites" : [ "ADR-046-resources-device", "ADR046-device-001" ], - "specId": "ADR-046-resources-device", - "topologicalRank": 11, - "wave": "W5" + "specId" : "ADR-046-resources-device", + "topologicalRank" : 11, + "wave" : "W5" }, { - "blockers": [], - "destinations": [ + "blockers" : [], + "destinations" : [ "`packages/d2b-provider-device-usbip/src/` (controller, daemon Process, bind/unbind EphemeralProcess, firewall); `packages/d2b-provider-device-usbip/tests/` (hermetic Cargo integration); `packages/d2b-provider-device-usbip/integration/` (container/Host scenarios); `packages/d2b-provider-device-usbip/README.md`" ], - "entryContracts": [ + "entryContracts" : [ "ADR-046-resources-device" ], - "exitGate": "ADR046-W5 exit criteria (ADR-046-validation-and-delivery §4): every spec/work item in this wave Merged with clean destinations, all validators green, and the ten-role panel seal recorded", - "id": "ADR046-device-003", - "kind": "work-item", - "owner": "ADR046-device-001; device-usbip provider owner", - "parallelGroup": "wi:ADR-046-resources-device", - "prerequisites": [ + "exitGate" : "ADR046-W5 exit criteria (ADR-046-validation-and-delivery §4): every spec/work item in this wave Merged with clean destinations, all validators green, and the ten-role panel seal recorded", + "id" : "ADR046-device-003", + "kind" : "work-item", + "owner" : "ADR046-device-001; device-usbip provider owner", + "parallelGroup" : "wi:ADR-046-resources-device", + "prerequisites" : [ "ADR-046-resources-device", "ADR046-device-001" ], - "specId": "ADR-046-resources-device", - "topologicalRank": 11, - "wave": "W5" + "specId" : "ADR-046-resources-device", + "topologicalRank" : 11, + "wave" : "W5" }, { - "blockers": [], - "destinations": [ + "blockers" : [], + "destinations" : [ "`packages/d2b-provider-device-security-key/src/` (controller, relay Process, guest frontend Process, lease/session ring); `packages/d2b-provider-device-security-key/tests/` (hermetic Cargo integration); `packages/d2b-provider-device-security-key/integration/` (container/Host/Guest scenarios); `packages/d2b-provider-device-security-key/README.md`" ], - "entryContracts": [ + "entryContracts" : [ "ADR-046-resources-device" ], - "exitGate": "ADR046-W5 exit criteria (ADR-046-validation-and-delivery §4): every spec/work item in this wave Merged with clean destinations, all validators green, and the ten-role panel seal recorded", - "id": "ADR046-device-004", - "kind": "work-item", - "owner": "ADR046-device-001; device-security-key provider owner", - "parallelGroup": "wi:ADR-046-resources-device", - "prerequisites": [ + "exitGate" : "ADR046-W5 exit criteria (ADR-046-validation-and-delivery §4): every spec/work item in this wave Merged with clean destinations, all validators green, and the ten-role panel seal recorded", + "id" : "ADR046-device-004", + "kind" : "work-item", + "owner" : "ADR046-device-001; device-security-key provider owner", + "parallelGroup" : "wi:ADR-046-resources-device", + "prerequisites" : [ "ADR-046-resources-device", "ADR046-device-001" ], - "specId": "ADR-046-resources-device", - "topologicalRank": 11, - "wave": "W5" + "specId" : "ADR-046-resources-device", + "topologicalRank" : 11, + "wave" : "W5" }, { - "blockers": [], - "destinations": [ + "blockers" : [], + "destinations" : [ "`packages/d2b-provider-device-gpu/src/` (controller, GPU/render-node/video worker Processes, broker token set); `packages/d2b-provider-device-gpu/tests/` (hermetic Cargo integration); `packages/d2b-provider-device-gpu/integration/` (container/Host/Guest scenarios); `packages/d2b-provider-device-gpu/README.md`" ], - "entryContracts": [ + "entryContracts" : [ "ADR-046-resources-device" ], - "exitGate": "ADR046-W5 exit criteria (ADR-046-validation-and-delivery §4): every spec/work item in this wave Merged with clean destinations, all validators green, and the ten-role panel seal recorded", - "id": "ADR046-device-005", - "kind": "work-item", - "owner": "ADR046-device-001; device-gpu provider owner", - "parallelGroup": "wi:ADR-046-resources-device", - "prerequisites": [ + "exitGate" : "ADR046-W5 exit criteria (ADR-046-validation-and-delivery §4): every spec/work item in this wave Merged with clean destinations, all validators green, and the ten-role panel seal recorded", + "id" : "ADR046-device-005", + "kind" : "work-item", + "owner" : "ADR046-device-001; device-gpu provider owner", + "parallelGroup" : "wi:ADR-046-resources-device", + "prerequisites" : [ "ADR-046-resources-device", "ADR046-device-001" ], - "specId": "ADR-046-resources-device", - "topologicalRank": 11, - "wave": "W5" + "specId" : "ADR-046-resources-device", + "topologicalRank" : 11, + "wave" : "W5" }, { - "blockers": [], - "destinations": [ + "blockers" : [], + "destinations" : [ "`nixos-modules/resources-device.nix`; `nixos-modules/bundle-artifacts.nix` (bundle emission for resource store); `nixos-modules/assertions.nix` (six eval-time device assertions)" ], - "entryContracts": [ + "entryContracts" : [ "ADR-046-resources-device" ], - "exitGate": "ADR046-W5 exit criteria (ADR-046-validation-and-delivery §4): every spec/work item in this wave Merged with clean destinations, all validators green, and the ten-role panel seal recorded", - "id": "ADR046-device-006", - "kind": "work-item", - "owner": "ADR046-device-001 through ADR046-device-005; Nix integrator", - "parallelGroup": "wi:ADR-046-resources-device", - "prerequisites": [ + "exitGate" : "ADR046-W5 exit criteria (ADR-046-validation-and-delivery §4): every spec/work item in this wave Merged with clean destinations, all validators green, and the ten-role panel seal recorded", + "id" : "ADR046-device-006", + "kind" : "work-item", + "owner" : "ADR046-device-001 through ADR046-device-005; Nix integrator", + "parallelGroup" : "wi:ADR-046-resources-device", + "prerequisites" : [ "ADR-046-resources-device", "ADR046-device-001", + "ADR046-device-002", + "ADR046-device-003", + "ADR046-device-004", "ADR046-device-005" ], - "specId": "ADR-046-resources-device", - "topologicalRank": 12, - "wave": "W5" + "specId" : "ADR-046-resources-device", + "topologicalRank" : 12, + "wave" : "W5" }, { - "blockers": [], - "destinations": [ + "blockers" : [], + "destinations" : [ "`packages/d2b-core-controller/src/configuration.rs`; `packages/d2b-contract-tests/tests/device_gen_cleanup.rs`" ], - "entryContracts": [ + "entryContracts" : [ "ADR-046-resources-device" ], - "exitGate": "ADR046-W5 exit criteria (ADR-046-validation-and-delivery §4): every spec/work item in this wave Merged with clean destinations, all validators green, and the ten-role panel seal recorded", - "id": "ADR046-device-007", - "kind": "work-item", - "owner": "ADR046-device-006; Zone runtime implementer", - "parallelGroup": "wi:core-config-hub", - "prerequisites": [ + "exitGate" : "ADR046-W5 exit criteria (ADR-046-validation-and-delivery §4): every spec/work item in this wave Merged with clean destinations, all validators green, and the ten-role panel seal recorded", + "id" : "ADR046-device-007", + "kind" : "work-item", + "owner" : "ADR046-device-006; Zone runtime implementer", + "parallelGroup" : "wi:core-config-hub:w5", + "prerequisites" : [ "ADR-046-resources-device", - "ADR046-core-001", "ADR046-device-006" ], - "specId": "ADR-046-resources-device", - "topologicalRank": 13, - "wave": "W5" + "specId" : "ADR-046-resources-device", + "topologicalRank" : 13, + "wave" : "W5" }, { - "blockers": [], - "destinations": [ + "blockers" : [], + "destinations" : [ "`packages/xtask/src/main.rs` (`check-provider-layout` subcommand); `packages/d2b-contract-tests/tests/workspace_policy.rs` (provider-layout policy assertions)" ], - "entryContracts": [ + "entryContracts" : [ "ADR-046-resources-device" ], - "exitGate": "ADR046-W5 exit criteria (ADR-046-validation-and-delivery §4): every spec/work item in this wave Merged with clean destinations, all validators green, and the ten-role panel seal recorded", - "id": "ADR046-device-008", - "kind": "work-item", - "owner": "ADR046-device-002 through ADR046-device-005; workspace/tooling maintainer", - "parallelGroup": "wi:ADR-046-resources-device", - "prerequisites": [ + "exitGate" : "ADR046-W5 exit criteria (ADR-046-validation-and-delivery §4): every spec/work item in this wave Merged with clean destinations, all validators green, and the ten-role panel seal recorded", + "id" : "ADR046-device-008", + "kind" : "work-item", + "owner" : "ADR046-device-002 through ADR046-device-005; workspace/tooling maintainer", + "parallelGroup" : "wi:ADR-046-resources-device", + "prerequisites" : [ "ADR-046-resources-device", "ADR046-device-002", + "ADR046-device-003", + "ADR046-device-004", "ADR046-device-005" ], - "specId": "ADR-046-resources-device", - "topologicalRank": 12, - "wave": "W5" + "specId" : "ADR-046-resources-device", + "topologicalRank" : 12, + "wave" : "W5" }, { - "blockers": [], - "destinations": [ + "blockers" : [], + "destinations" : [ "packages/d2b-provider-device-tpm/{src/,tests/,integration/README.md,README.md}; Cargo workspace membership" ], - "entryContracts": [ + "entryContracts" : [ "ADR-046-provider-device-tpm" ], - "exitGate": "ADR046-W6 exit criteria (ADR-046-validation-and-delivery §4): every spec/work item in this wave Merged with clean destinations, all validators green, and the ten-role panel seal recorded", - "id": "ADR046-device-tpm-001", - "kind": "work-item", - "owner": "P0; unblocked; owner: `d2b-provider-device-tpm` crate", - "parallelGroup": "wi:ADR-046-provider-device-tpm", - "prerequisites": [ + "exitGate" : "ADR046-W6 exit criteria (ADR-046-validation-and-delivery §4): every spec/work item in this wave Merged with clean destinations, all validators green, and the ten-role panel seal recorded", + "id" : "ADR046-device-tpm-001", + "kind" : "work-item", + "owner" : "P0; unblocked; owner: `d2b-provider-device-tpm` crate", + "parallelGroup" : "wi:ADR-046-provider-device-tpm", + "prerequisites" : [ "ADR-046-provider-device-tpm" ], - "specId": "ADR-046-provider-device-tpm", - "topologicalRank": 11, - "wave": "W6" + "specId" : "ADR-046-provider-device-tpm", + "topologicalRank" : 11, + "wave" : "W6" }, { - "blockers": [], - "destinations": [ + "blockers" : [], + "destinations" : [ "packages/d2b-provider-device-tpm/src/{effect_port.rs,effect_impl.rs}; packages/d2b-provider-device-tpm/tests/effect_fake.rs" ], - "entryContracts": [ + "entryContracts" : [ "ADR-046-provider-device-tpm" ], - "exitGate": "ADR046-W6 exit criteria (ADR-046-validation-and-delivery §4): every spec/work item in this wave Merged with clean destinations, all validators green, and the ten-role panel seal recorded", - "id": "ADR046-device-tpm-002", - "kind": "work-item", - "owner": "P0; blocked by ADR046-device-tpm-001; owner: device-tpm effect boundary", - "parallelGroup": "wi:ADR-046-provider-device-tpm", - "prerequisites": [ - "ADR-046-provider-device-tpm" + "exitGate" : "ADR046-W6 exit criteria (ADR-046-validation-and-delivery §4): every spec/work item in this wave Merged with clean destinations, all validators green, and the ten-role panel seal recorded", + "id" : "ADR046-device-tpm-002", + "kind" : "work-item", + "owner" : "P0; blocked by ADR046-device-tpm-001; owner: device-tpm effect boundary", + "parallelGroup" : "wi:ADR-046-provider-device-tpm", + "prerequisites" : [ + "ADR-046-provider-device-tpm", + "ADR046-device-tpm-001" ], - "specId": "ADR-046-provider-device-tpm", - "topologicalRank": 11, - "wave": "W6" + "specId" : "ADR-046-provider-device-tpm", + "topologicalRank" : 12, + "wave" : "W6" }, { - "blockers": [], - "destinations": [ + "blockers" : [], + "destinations" : [ "packages/d2b-provider-device-tpm/src/controller.rs; packages/d2b-provider-device-tpm/tests/controller_fsm.rs" ], - "entryContracts": [ + "entryContracts" : [ "ADR-046-provider-device-tpm" ], - "exitGate": "ADR046-W6 exit criteria (ADR-046-validation-and-delivery §4): every spec/work item in this wave Merged with clean destinations, all validators green, and the ten-role panel seal recorded", - "id": "ADR046-device-tpm-003", - "kind": "work-item", - "owner": "P0; blocked by ADR046-device-tpm-002; owner: device-tpm controller FSM", - "parallelGroup": "wi:ADR-046-provider-device-tpm", - "prerequisites": [ - "ADR-046-provider-device-tpm" + "exitGate" : "ADR046-W6 exit criteria (ADR-046-validation-and-delivery §4): every spec/work item in this wave Merged with clean destinations, all validators green, and the ten-role panel seal recorded", + "id" : "ADR046-device-tpm-003", + "kind" : "work-item", + "owner" : "P0; blocked by ADR046-device-tpm-002; owner: device-tpm controller FSM", + "parallelGroup" : "wi:ADR-046-provider-device-tpm", + "prerequisites" : [ + "ADR-046-provider-device-tpm", + "ADR046-device-tpm-002" ], - "specId": "ADR-046-provider-device-tpm", - "topologicalRank": 11, - "wave": "W6" + "specId" : "ADR-046-provider-device-tpm", + "topologicalRank" : 13, + "wave" : "W6" }, { - "blockers": [], - "destinations": [ + "blockers" : [], + "destinations" : [ "packages/d2b-provider-device-tpm/src/resources.rs; packages/d2b-provider-device-tpm/tests/volume_create.rs" ], - "entryContracts": [ + "entryContracts" : [ "ADR-046-provider-device-tpm" ], - "exitGate": "ADR046-W6 exit criteria (ADR-046-validation-and-delivery §4): every spec/work item in this wave Merged with clean destinations, all validators green, and the ten-role panel seal recorded", - "id": "ADR046-device-tpm-004", - "kind": "work-item", - "owner": "P0; blocked by ADR046-device-tpm-001; owner: device-tpm resource builders", - "parallelGroup": "wi:ADR-046-provider-device-tpm", - "prerequisites": [ - "ADR-046-provider-device-tpm" + "exitGate" : "ADR046-W6 exit criteria (ADR-046-validation-and-delivery §4): every spec/work item in this wave Merged with clean destinations, all validators green, and the ten-role panel seal recorded", + "id" : "ADR046-device-tpm-004", + "kind" : "work-item", + "owner" : "P0; blocked by ADR046-device-tpm-001; owner: device-tpm resource builders", + "parallelGroup" : "wi:ADR-046-provider-device-tpm", + "prerequisites" : [ + "ADR-046-provider-device-tpm", + "ADR046-device-tpm-001" ], - "specId": "ADR-046-provider-device-tpm", - "topologicalRank": 11, - "wave": "W6" + "specId" : "ADR-046-provider-device-tpm", + "topologicalRank" : 12, + "wave" : "W6" }, { - "blockers": [], - "destinations": [ + "blockers" : [], + "destinations" : [ "packages/d2b-provider-device-tpm/src/resources.rs; Process spec tests under packages/d2b-provider-device-tpm/tests/" ], - "entryContracts": [ + "entryContracts" : [ "ADR-046-provider-device-tpm" ], - "exitGate": "ADR046-W6 exit criteria (ADR-046-validation-and-delivery §4): every spec/work item in this wave Merged with clean destinations, all validators green, and the ten-role panel seal recorded", - "id": "ADR046-device-tpm-005", - "kind": "work-item", - "owner": "P0; blocked by ADR046-device-tpm-004; owner: device-tpm Process resource builder", - "parallelGroup": "wi:ADR-046-provider-device-tpm", - "prerequisites": [ - "ADR-046-provider-device-tpm" + "exitGate" : "ADR046-W6 exit criteria (ADR-046-validation-and-delivery §4): every spec/work item in this wave Merged with clean destinations, all validators green, and the ten-role panel seal recorded", + "id" : "ADR046-device-tpm-005", + "kind" : "work-item", + "owner" : "P0; blocked by ADR046-device-tpm-004; owner: device-tpm Process resource builder", + "parallelGroup" : "wi:ADR-046-provider-device-tpm", + "prerequisites" : [ + "ADR-046-provider-device-tpm", + "ADR046-device-tpm-004" ], - "specId": "ADR-046-provider-device-tpm", - "topologicalRank": 11, - "wave": "W6" + "specId" : "ADR-046-provider-device-tpm", + "topologicalRank" : 13, + "wave" : "W6" }, { - "blockers": [], - "destinations": [ + "blockers" : [], + "destinations" : [ "packages/d2b-provider-device-tpm/src/resources.rs; packages/d2b-provider-device-tpm/tests/flush_mandatory.rs" ], - "entryContracts": [ + "entryContracts" : [ "ADR-046-provider-device-tpm" ], - "exitGate": "ADR046-W6 exit criteria (ADR-046-validation-and-delivery §4): every spec/work item in this wave Merged with clean destinations, all validators green, and the ten-role panel seal recorded", - "id": "ADR046-device-tpm-006", - "kind": "work-item", - "owner": "P0; blocked by ADR046-device-tpm-003; owner: device-tpm EphemeralProcess resource builder", - "parallelGroup": "wi:ADR-046-provider-device-tpm", - "prerequisites": [ - "ADR-046-provider-device-tpm" + "exitGate" : "ADR046-W6 exit criteria (ADR-046-validation-and-delivery §4): every spec/work item in this wave Merged with clean destinations, all validators green, and the ten-role panel seal recorded", + "id" : "ADR046-device-tpm-006", + "kind" : "work-item", + "owner" : "P0; blocked by ADR046-device-tpm-003; owner: device-tpm EphemeralProcess resource builder", + "parallelGroup" : "wi:ADR-046-provider-device-tpm", + "prerequisites" : [ + "ADR-046-provider-device-tpm", + "ADR046-device-tpm-003" ], - "specId": "ADR-046-provider-device-tpm", - "topologicalRank": 11, - "wave": "W6" + "specId" : "ADR-046-provider-device-tpm", + "topologicalRank" : 14, + "wave" : "W6" }, { - "blockers": [], - "destinations": [ + "blockers" : [], + "destinations" : [ "packages/d2b-provider-device-tpm/src/status.rs; packages/d2b-provider-device-tpm/tests/{endpoint_ref.rs,redaction.rs}" ], - "entryContracts": [ + "entryContracts" : [ "ADR-046-provider-device-tpm" ], - "exitGate": "ADR046-W6 exit criteria (ADR-046-validation-and-delivery §4): every spec/work item in this wave Merged with clean destinations, all validators green, and the ten-role panel seal recorded", - "id": "ADR046-device-tpm-007", - "kind": "work-item", - "owner": "P1; blocked by ADR046-device-tpm-003; owner: device-tpm status builder", - "parallelGroup": "wi:ADR-046-provider-device-tpm", - "prerequisites": [ - "ADR-046-provider-device-tpm" + "exitGate" : "ADR046-W6 exit criteria (ADR-046-validation-and-delivery §4): every spec/work item in this wave Merged with clean destinations, all validators green, and the ten-role panel seal recorded", + "id" : "ADR046-device-tpm-007", + "kind" : "work-item", + "owner" : "P1; blocked by ADR046-device-tpm-003; owner: device-tpm status builder", + "parallelGroup" : "wi:ADR-046-provider-device-tpm", + "prerequisites" : [ + "ADR-046-provider-device-tpm", + "ADR046-device-tpm-003" ], - "specId": "ADR-046-provider-device-tpm", - "topologicalRank": 11, - "wave": "W6" + "specId" : "ADR-046-provider-device-tpm", + "topologicalRank" : 14, + "wave" : "W6" }, { - "blockers": [], - "destinations": [ + "blockers" : [], + "destinations" : [ "packages/d2b-provider-device-tpm/src/{effect_port.rs,status.rs}; packages/d2b-provider-device-tpm/integration/guest_endpoint.rs" ], - "entryContracts": [ + "entryContracts" : [ "ADR-046-provider-device-tpm" ], - "exitGate": "ADR046-W6 exit criteria (ADR-046-validation-and-delivery §4): every spec/work item in this wave Merged with clean destinations, all validators green, and the ten-role panel seal recorded", - "id": "ADR046-device-tpm-008", - "kind": "work-item", - "owner": "P1; blocked by ADR046-device-tpm-007; owner: device-tpm endpoint handoff integration", - "parallelGroup": "wi:ADR-046-provider-device-tpm", - "prerequisites": [ - "ADR-046-provider-device-tpm" + "exitGate" : "ADR046-W6 exit criteria (ADR-046-validation-and-delivery §4): every spec/work item in this wave Merged with clean destinations, all validators green, and the ten-role panel seal recorded", + "id" : "ADR046-device-tpm-008", + "kind" : "work-item", + "owner" : "P1; blocked by ADR046-device-tpm-007; owner: device-tpm endpoint handoff integration", + "parallelGroup" : "wi:ADR-046-provider-device-tpm", + "prerequisites" : [ + "ADR-046-provider-device-tpm", + "ADR046-device-tpm-007" ], - "specId": "ADR-046-provider-device-tpm", - "topologicalRank": 11, - "wave": "W6" + "specId" : "ADR-046-provider-device-tpm", + "topologicalRank" : 15, + "wave" : "W6" }, { - "blockers": [], - "destinations": [ + "blockers" : [], + "destinations" : [ "packages/d2b-provider-device-tpm/tests/marker_fail_closed.rs; packages/d2b-provider-device-tpm/integration/marker_tamper.rs" ], - "entryContracts": [ + "entryContracts" : [ "ADR-046-provider-device-tpm" ], - "exitGate": "ADR046-W6 exit criteria (ADR-046-validation-and-delivery §4): every spec/work item in this wave Merged with clean destinations, all validators green, and the ten-role panel seal recorded", - "id": "ADR046-device-tpm-009", - "kind": "work-item", - "owner": "P0; blocked by ADR046-device-tpm-004; owner: device-tpm marker/fail-closed tests", - "parallelGroup": "wi:ADR-046-provider-device-tpm", - "prerequisites": [ - "ADR-046-provider-device-tpm" + "exitGate" : "ADR046-W6 exit criteria (ADR-046-validation-and-delivery §4): every spec/work item in this wave Merged with clean destinations, all validators green, and the ten-role panel seal recorded", + "id" : "ADR046-device-tpm-009", + "kind" : "work-item", + "owner" : "P0; blocked by ADR046-device-tpm-004; owner: device-tpm marker/fail-closed tests", + "parallelGroup" : "wi:ADR-046-provider-device-tpm", + "prerequisites" : [ + "ADR-046-provider-device-tpm", + "ADR046-device-tpm-004" ], - "specId": "ADR-046-provider-device-tpm", - "topologicalRank": 11, - "wave": "W6" + "specId" : "ADR-046-provider-device-tpm", + "topologicalRank" : 13, + "wave" : "W6" }, { - "blockers": [], - "destinations": [ + "blockers" : [], + "destinations" : [ "packages/d2b-provider-device-tpm/src/resources.rs; packages/d2b-provider-device-tpm/tests/controller_process.rs" ], - "entryContracts": [ + "entryContracts" : [ "ADR-046-provider-device-tpm" ], - "exitGate": "ADR046-W6 exit criteria (ADR-046-validation-and-delivery §4): every spec/work item in this wave Merged with clean destinations, all validators green, and the ten-role panel seal recorded", - "id": "ADR046-device-tpm-010", - "kind": "work-item", - "owner": "P1; blocked by ADR046-device-tpm-001; owner: device-tpm controller Process descriptor", - "parallelGroup": "wi:ADR-046-provider-device-tpm", - "prerequisites": [ - "ADR-046-provider-device-tpm" + "exitGate" : "ADR046-W6 exit criteria (ADR-046-validation-and-delivery §4): every spec/work item in this wave Merged with clean destinations, all validators green, and the ten-role panel seal recorded", + "id" : "ADR046-device-tpm-010", + "kind" : "work-item", + "owner" : "P1; blocked by ADR046-device-tpm-001; owner: device-tpm controller Process descriptor", + "parallelGroup" : "wi:ADR-046-provider-device-tpm", + "prerequisites" : [ + "ADR-046-provider-device-tpm", + "ADR046-device-tpm-001" ], - "specId": "ADR-046-provider-device-tpm", - "topologicalRank": 11, - "wave": "W6" + "specId" : "ADR-046-provider-device-tpm", + "topologicalRank" : 12, + "wave" : "W6" }, { - "blockers": [], - "destinations": [ + "blockers" : [], + "destinations" : [ "nixos-modules/options-resources.nix and Nix eval/golden tests for §17.1 Device JSON" ], - "entryContracts": [ + "entryContracts" : [ "ADR-046-provider-device-tpm" ], - "exitGate": "ADR046-W6 exit criteria (ADR-046-validation-and-delivery §4): every spec/work item in this wave Merged with clean destinations, all validators green, and the ten-role panel seal recorded", - "id": "ADR046-device-tpm-011", - "kind": "work-item", - "owner": "P1; blocked by ADR046-device-tpm-001; owner: Nix Resource compiler for Device declarations", - "parallelGroup": "wi:ADR-046-provider-device-tpm", - "prerequisites": [ - "ADR-046-provider-device-tpm" + "exitGate" : "ADR046-W6 exit criteria (ADR-046-validation-and-delivery §4): every spec/work item in this wave Merged with clean destinations, all validators green, and the ten-role panel seal recorded", + "id" : "ADR046-device-tpm-011", + "kind" : "work-item", + "owner" : "P1; blocked by ADR046-device-tpm-001; owner: Nix Resource compiler for Device declarations", + "parallelGroup" : "wi:ADR-046-provider-device-tpm", + "prerequisites" : [ + "ADR-046-provider-device-tpm", + "ADR046-device-tpm-001" ], - "specId": "ADR-046-provider-device-tpm", - "topologicalRank": 11, - "wave": "W6" + "specId" : "ADR-046-provider-device-tpm", + "topologicalRank" : 12, + "wave" : "W6" }, { - "blockers": [], - "destinations": [ + "blockers" : [], + "destinations" : [ "packages/d2b-provider-device-tpm/src/controller.rs; packages/d2b-provider-device-tpm/tests/finalizer.rs" ], - "entryContracts": [ + "entryContracts" : [ "ADR-046-provider-device-tpm" ], - "exitGate": "ADR046-W6 exit criteria (ADR-046-validation-and-delivery §4): every spec/work item in this wave Merged with clean destinations, all validators green, and the ten-role panel seal recorded", - "id": "ADR046-device-tpm-012", - "kind": "work-item", - "owner": "P0; blocked by ADR046-device-tpm-003; owner: device-tpm finalizer lifecycle", - "parallelGroup": "wi:ADR-046-provider-device-tpm", - "prerequisites": [ - "ADR-046-provider-device-tpm" + "exitGate" : "ADR046-W6 exit criteria (ADR-046-validation-and-delivery §4): every spec/work item in this wave Merged with clean destinations, all validators green, and the ten-role panel seal recorded", + "id" : "ADR046-device-tpm-012", + "kind" : "work-item", + "owner" : "P0; blocked by ADR046-device-tpm-003; owner: device-tpm finalizer lifecycle", + "parallelGroup" : "wi:ADR-046-provider-device-tpm", + "prerequisites" : [ + "ADR-046-provider-device-tpm", + "ADR046-device-tpm-003" ], - "specId": "ADR-046-provider-device-tpm", - "topologicalRank": 11, - "wave": "W6" + "specId" : "ADR-046-provider-device-tpm", + "topologicalRank" : 14, + "wave" : "W6" }, { - "blockers": [], - "destinations": [ + "blockers" : [], + "destinations" : [ "packages/d2bd/src/*; packages/d2b-core/src/processes.rs; packages/d2b-provider-device-tpm/src/; packages/d2b-host/src/swtpm_argv.rs" ], - "entryContracts": [ + "entryContracts" : [ "ADR-046-provider-device-tpm" ], - "exitGate": "ADR046-W6 exit criteria (ADR-046-validation-and-delivery §4): every spec/work item in this wave Merged with clean destinations, all validators green, and the ten-role panel seal recorded", - "id": "ADR046-device-tpm-013", - "kind": "work-item", - "owner": "P0; blocked by ADR046-device-tpm-002; owner: device-tpm migration/removal cleanup", - "parallelGroup": "wi:ADR-046-provider-device-tpm", - "prerequisites": [ - "ADR-046-provider-device-tpm" + "exitGate" : "ADR046-W6 exit criteria (ADR-046-validation-and-delivery §4): every spec/work item in this wave Merged with clean destinations, all validators green, and the ten-role panel seal recorded", + "id" : "ADR046-device-tpm-013", + "kind" : "work-item", + "owner" : "P0; blocked by ADR046-device-tpm-002; owner: device-tpm migration/removal cleanup", + "parallelGroup" : "wi:ADR-046-provider-device-tpm", + "prerequisites" : [ + "ADR-046-provider-device-tpm", + "ADR046-device-tpm-002" ], - "specId": "ADR-046-provider-device-tpm", - "topologicalRank": 11, - "wave": "W6" + "specId" : "ADR-046-provider-device-tpm", + "topologicalRank" : 13, + "wave" : "W6" }, { - "blockers": [], - "destinations": [ + "blockers" : [], + "destinations" : [ "`packages/d2b-provider-display-wayland/src/`" ], - "entryContracts": [ + "entryContracts" : [ "ADR-046-provider-display-wayland" ], - "exitGate": "ADR046-W6 exit criteria (ADR-046-validation-and-delivery §4): every spec/work item in this wave Merged with clean destinations, all validators green, and the ten-role panel seal recorded", - "id": "ADR046-display-001", - "kind": "work-item", - "owner": "`ADR046-provider-001`, `ADR046-process-001`; display Provider owner", - "parallelGroup": "wi:ADR-046-provider-display-wayland", - "prerequisites": [ + "exitGate" : "ADR046-W6 exit criteria (ADR-046-validation-and-delivery §4): every spec/work item in this wave Merged with clean destinations, all validators green, and the ten-role panel seal recorded", + "id" : "ADR046-display-001", + "kind" : "work-item", + "owner" : "`ADR046-provider-001`, `ADR046-process-001`; display Provider owner", + "parallelGroup" : "wi:ADR-046-provider-display-wayland", + "prerequisites" : [ "ADR-046-provider-display-wayland", "ADR046-process-001", "ADR046-provider-001" ], - "specId": "ADR-046-provider-display-wayland", - "topologicalRank": 11, - "wave": "W6" + "specId" : "ADR-046-provider-display-wayland", + "topologicalRank" : 11, + "wave" : "W6" }, { - "blockers": [], - "destinations": [ + "blockers" : [], + "destinations" : [ "Zone bundle emitter for `WaylandSession` / `WaylandPolicy` ResourceSpecs under `d2b.zones..resources.*`; `WaylandSession` color resolution in Nix bundle emitter" ], - "entryContracts": [ + "entryContracts" : [ "ADR-046-provider-display-wayland" ], - "exitGate": "ADR046-W6 exit criteria (ADR-046-validation-and-delivery §4): every spec/work item in this wave Merged with clean destinations, all validators green, and the ten-role panel seal recorded", - "id": "ADR046-display-002", - "kind": "work-item", - "owner": "`ADR046-display-001`; Nix integrator", - "parallelGroup": "wi:ADR-046-provider-display-wayland", - "prerequisites": [ + "exitGate" : "ADR046-W6 exit criteria (ADR-046-validation-and-delivery §4): every spec/work item in this wave Merged with clean destinations, all validators green, and the ten-role panel seal recorded", + "id" : "ADR046-display-002", + "kind" : "work-item", + "owner" : "`ADR046-display-001`; Nix integrator", + "parallelGroup" : "wi:ADR-046-provider-display-wayland", + "prerequisites" : [ "ADR-046-provider-display-wayland", "ADR046-display-001" ], - "specId": "ADR-046-provider-display-wayland", - "topologicalRank": 12, - "wave": "W6" + "specId" : "ADR-046-provider-display-wayland", + "topologicalRank" : 12, + "wave" : "W6" }, { - "blockers": [], - "destinations": [ + "blockers" : [], + "destinations" : [ "`packages/d2b-provider-display-wayland/src/audit.rs`, `packages/d2b-provider-display-wayland/src/metrics.rs`" ], - "entryContracts": [ + "entryContracts" : [ "ADR-046-provider-display-wayland" ], - "exitGate": "ADR046-W6 exit criteria (ADR-046-validation-and-delivery §4): every spec/work item in this wave Merged with clean destinations, all validators green, and the ten-role panel seal recorded", - "id": "ADR046-display-003", - "kind": "work-item", - "owner": "`ADR046-display-001`; telemetry/audit owner", - "parallelGroup": "wi:ADR-046-provider-display-wayland", - "prerequisites": [ + "exitGate" : "ADR046-W6 exit criteria (ADR-046-validation-and-delivery §4): every spec/work item in this wave Merged with clean destinations, all validators green, and the ten-role panel seal recorded", + "id" : "ADR046-display-003", + "kind" : "work-item", + "owner" : "`ADR046-display-001`; telemetry/audit owner", + "parallelGroup" : "wi:ADR-046-provider-display-wayland", + "prerequisites" : [ "ADR-046-provider-display-wayland", "ADR046-display-001" ], - "specId": "ADR-046-provider-display-wayland", - "topologicalRank": 12, - "wave": "W6" + "specId" : "ADR-046-provider-display-wayland", + "topologicalRank" : 12, + "wave" : "W6" }, { - "blockers": [], - "destinations": [ + "blockers" : [], + "destinations" : [ "`packages/d2b-provider-display-wayland/integration/`" ], - "entryContracts": [ + "entryContracts" : [ "ADR-046-provider-display-wayland" ], - "exitGate": "ADR046-W6 exit criteria (ADR-046-validation-and-delivery §4): every spec/work item in this wave Merged with clean destinations, all validators green, and the ten-role panel seal recorded", - "id": "ADR046-display-004", - "kind": "work-item", - "owner": "`ADR046-display-001`; integration test owner", - "parallelGroup": "wi:ADR-046-provider-display-wayland", - "prerequisites": [ + "exitGate" : "ADR046-W6 exit criteria (ADR-046-validation-and-delivery §4): every spec/work item in this wave Merged with clean destinations, all validators green, and the ten-role panel seal recorded", + "id" : "ADR046-display-004", + "kind" : "work-item", + "owner" : "`ADR046-display-001`; integration test owner", + "parallelGroup" : "wi:ADR-046-provider-display-wayland", + "prerequisites" : [ "ADR-046-provider-display-wayland", "ADR046-display-001" ], - "specId": "ADR-046-provider-display-wayland", - "topologicalRank": 12, - "wave": "W6" - }, - { - "blockers": [], - "destinations": [ - "`packages/d2b/src/zone_doctor.rs`, `packages/d2b/tests/zone_doctor_contract.rs`" - ], - "entryContracts": [ - "ADR-046-telemetry-audit-and-support" - ], - "exitGate": "ADR046-W5 exit criteria (ADR-046-validation-and-delivery §4): every spec/work item in this wave Merged with clean destinations, all validators green, and the ten-role panel seal recorded", - "id": "ADR046-doctor-001", - "kind": "work-item", - "owner": "ADR046-core-001 + ADR046-audit-001; CLI/doctor owner", - "parallelGroup": "wi:ADR-046-telemetry-audit-and-support", - "prerequisites": [ - "ADR-046-telemetry-audit-and-support", - "ADR046-audit-001", - "ADR046-core-001" - ], - "specId": "ADR-046-telemetry-audit-and-support", - "topologicalRank": 11, - "wave": "W5" - }, - { - "blockers": [], - "destinations": [ - "`packages/d2b/src/zone_support_bundle.rs`, `packages/d2b/tests/zone_support_bundle_contract.rs`" - ], - "entryContracts": [ - "ADR-046-telemetry-audit-and-support" - ], - "exitGate": "ADR046-W5 exit criteria (ADR-046-validation-and-delivery §4): every spec/work item in this wave Merged with clean destinations, all validators green, and the ten-role panel seal recorded", - "id": "ADR046-doctor-002", - "kind": "work-item", - "owner": "ADR046-doctor-001; CLI/doctor owner", - "parallelGroup": "wi:ADR-046-telemetry-audit-and-support", - "prerequisites": [ - "ADR-046-telemetry-audit-and-support", - "ADR046-doctor-001" - ], - "specId": "ADR-046-telemetry-audit-and-support", - "topologicalRank": 12, - "wave": "W5" + "specId" : "ADR-046-provider-display-wayland", + "topologicalRank" : 12, + "wave" : "W6" }, { - "blockers": [], - "destinations": [ + "blockers" : [], + "destinations" : [ "`packages/d2b-contracts/src/v3/host.rs`, `packages/d2b-contracts/src/v3/guest.rs`, `packages/d2b-contracts/src/v3/execution_policy.rs`, `packages/d2b-contracts/src/v3/process.rs`, `packages/d2b-contracts/src/v3/ephemeral_process.rs`, `packages/d2b-contracts/src/v3/user.rs`" ], - "entryContracts": [ + "entryContracts" : [ "ADR-046-resources-host-guest-process-user" ], - "exitGate": "ADR046-W5 exit criteria (ADR-046-validation-and-delivery §4): every spec/work item in this wave Merged with clean destinations, all validators green, and the ten-role panel seal recorded", - "id": "ADR046-exec-001", - "kind": "work-item", - "owner": "W0 shared contract root; `d2b-contracts`", - "parallelGroup": "wi:ADR-046-resources-host-guest-process-user", - "prerequisites": [ + "exitGate" : "ADR046-W5 exit criteria (ADR-046-validation-and-delivery §4): every spec/work item in this wave Merged with clean destinations, all validators green, and the ten-role panel seal recorded", + "id" : "ADR046-exec-001", + "kind" : "work-item", + "owner" : "W0 shared contract root; `d2b-contracts`", + "parallelGroup" : "wi:ADR-046-resources-host-guest-process-user", + "prerequisites" : [ "ADR-046-resources-host-guest-process-user" ], - "specId": "ADR-046-resources-host-guest-process-user", - "topologicalRank": 10, - "wave": "W5" + "specId" : "ADR-046-resources-host-guest-process-user", + "topologicalRank" : 10, + "wave" : "W5" }, { - "blockers": [], - "destinations": [ - "`packages/d2b-contracts/src/v3/process_provider.rs`: LaunchTicket, ProcessIdentityDigest, AdoptionCandidate, PidfdEvidence, WaitReapOwner, ProcessOutcome, ExitClass" + "blockers" : [], + "destinations" : [ + "`packages/d2b-contracts/src/v3/process_provider.rs`: LaunchTicket, ProcessIdentityDigest, AdoptionCandidate, PidfdEvidence, WaitReapOwner, BrokerTerminalResult, ProcessOutcome, ExitClass" ], - "entryContracts": [ + "entryContracts" : [ "ADR-046-resources-host-guest-process-user" ], - "exitGate": "ADR046-W5 exit criteria (ADR-046-validation-and-delivery §4): every spec/work item in this wave Merged with clean destinations, all validators green, and the ten-role panel seal recorded", - "id": "ADR046-exec-002", - "kind": "work-item", - "owner": "ADR046-exec-001; `d2b-contracts`", - "parallelGroup": "wi:ADR-046-resources-host-guest-process-user", - "prerequisites": [ + "exitGate" : "ADR046-W5 exit criteria (ADR-046-validation-and-delivery §4): every spec/work item in this wave Merged with clean destinations, all validators green, and the ten-role panel seal recorded", + "id" : "ADR046-exec-002", + "kind" : "work-item", + "owner" : "ADR046-exec-001; `d2b-contracts`", + "parallelGroup" : "wi:ADR-046-resources-host-guest-process-user", + "prerequisites" : [ "ADR-046-resources-host-guest-process-user", "ADR046-exec-001" ], - "specId": "ADR-046-resources-host-guest-process-user", - "topologicalRank": 11, - "wave": "W5" + "specId" : "ADR-046-resources-host-guest-process-user", + "topologicalRank" : 11, + "wave" : "W5" }, { - "blockers": [], - "destinations": [ + "blockers" : [], + "destinations" : [ "`packages/d2b-provider-system-core/src/host.rs`: HostReconciler; status/conditions/capability probe implementation; `packages/d2b-provider-system-core/tests/`: hermetic reconcile/conformance/fault tests; `packages/d2b-provider-system-core/integration/`: Host probe and lifecycle integration scenarios; `packages/d2b-provider-system-core/README.md`: Provider identity, `spec.provider.settings` schema, ResourceTypes, placement, RBAC, security posture, telemetry labels, build/test commands (provider crate standard layout — see §Provider crate standard layout)" ], - "entryContracts": [ + "entryContracts" : [ "ADR-046-resources-host-guest-process-user" ], - "exitGate": "ADR046-W5 exit criteria (ADR-046-validation-and-delivery §4): every spec/work item in this wave Merged with clean destinations, all validators green, and the ten-role panel seal recorded", - "id": "ADR046-exec-003", - "kind": "work-item", - "owner": "ADR046-exec-001; system-core Provider owner", - "parallelGroup": "wi:ADR-046-resources-host-guest-process-user", - "prerequisites": [ + "exitGate" : "ADR046-W5 exit criteria (ADR-046-validation-and-delivery §4): every spec/work item in this wave Merged with clean destinations, all validators green, and the ten-role panel seal recorded", + "id" : "ADR046-exec-003", + "kind" : "work-item", + "owner" : "ADR046-exec-001; system-core Provider owner", + "parallelGroup" : "wi:ADR-046-resources-host-guest-process-user", + "prerequisites" : [ "ADR-046-resources-host-guest-process-user", "ADR046-exec-001" ], - "specId": "ADR-046-resources-host-guest-process-user", - "topologicalRank": 11, - "wave": "W5" + "specId" : "ADR-046-resources-host-guest-process-user", + "topologicalRank" : 11, + "wave" : "W5" }, { - "blockers": [], - "destinations": [ + "blockers" : [], + "destinations" : [ "`packages/d2b-provider-system-core/src/user.rs`: UserReconciler; NSS lookup implementation" ], - "entryContracts": [ + "entryContracts" : [ "ADR-046-resources-host-guest-process-user" ], - "exitGate": "ADR046-W5 exit criteria (ADR-046-validation-and-delivery §4): every spec/work item in this wave Merged with clean destinations, all validators green, and the ten-role panel seal recorded", - "id": "ADR046-exec-004", - "kind": "work-item", - "owner": "ADR046-exec-001; system-core Provider owner", - "parallelGroup": "wi:ADR-046-resources-host-guest-process-user", - "prerequisites": [ + "exitGate" : "ADR046-W5 exit criteria (ADR-046-validation-and-delivery §4): every spec/work item in this wave Merged with clean destinations, all validators green, and the ten-role panel seal recorded", + "id" : "ADR046-exec-004", + "kind" : "work-item", + "owner" : "ADR046-exec-001; system-core Provider owner", + "parallelGroup" : "wi:ADR-046-resources-host-guest-process-user", + "prerequisites" : [ "ADR-046-resources-host-guest-process-user", "ADR046-exec-001" ], - "specId": "ADR-046-resources-host-guest-process-user", - "topologicalRank": 11, - "wave": "W5" + "specId" : "ADR-046-resources-host-guest-process-user", + "topologicalRank" : 11, + "wave" : "W5" }, { - "blockers": [], - "destinations": [ + "blockers" : [], + "destinations" : [ "`packages/d2b-provider-system-core/src/host.rs` (continued); bootstrap startup sequence" ], - "entryContracts": [ + "entryContracts" : [ "ADR-046-resources-host-guest-process-user" ], - "exitGate": "ADR046-W5 exit criteria (ADR-046-validation-and-delivery §4): every spec/work item in this wave Merged with clean destinations, all validators green, and the ten-role panel seal recorded", - "id": "ADR046-exec-005", - "kind": "work-item", - "owner": "ADR046-exec-001 + ADR046-exec-004; system-core Provider owner", - "parallelGroup": "wi:ADR-046-resources-host-guest-process-user", - "prerequisites": [ + "exitGate" : "ADR046-W5 exit criteria (ADR-046-validation-and-delivery §4): every spec/work item in this wave Merged with clean destinations, all validators green, and the ten-role panel seal recorded", + "id" : "ADR046-exec-005", + "kind" : "work-item", + "owner" : "ADR046-exec-001 + ADR046-exec-004; system-core Provider owner", + "parallelGroup" : "wi:ADR-046-resources-host-guest-process-user", + "prerequisites" : [ "ADR-046-resources-host-guest-process-user", "ADR046-exec-001", "ADR046-exec-004" ], - "specId": "ADR-046-resources-host-guest-process-user", - "topologicalRank": 12, - "wave": "W5" + "specId" : "ADR-046-resources-host-guest-process-user", + "topologicalRank" : 12, + "wave" : "W5" }, { - "blockers": [], - "destinations": [ + "blockers" : [], + "destinations" : [ "`packages/d2b-provider-system-systemd/src/`: launch.rs, adoption.rs, pidfd.rs, wait.rs, user_supervisor.rs; `packages/d2b-provider-system-systemd/tests/`: hermetic lifecycle/conformance/fault tests; `packages/d2b-provider-system-systemd/integration/`: transient-unit and user-supervisor integration scenarios; `packages/d2b-provider-system-systemd/README.md`: Provider identity, `spec.provider.settings` schema, ResourceTypes, placement, RBAC, security posture, telemetry labels, build/test commands (provider crate standard layout — see §Provider crate standard layout)" ], - "entryContracts": [ + "entryContracts" : [ "ADR-046-resources-host-guest-process-user" ], - "exitGate": "ADR046-W5 exit criteria (ADR-046-validation-and-delivery §4): every spec/work item in this wave Merged with clean destinations, all validators green, and the ten-role panel seal recorded", - "id": "ADR046-exec-006", - "kind": "work-item", - "owner": "ADR046-exec-001 + ADR046-exec-002; system-systemd Process Provider owner", - "parallelGroup": "wi:ADR-046-resources-host-guest-process-user", - "prerequisites": [ + "exitGate" : "ADR046-W5 exit criteria (ADR-046-validation-and-delivery §4): every spec/work item in this wave Merged with clean destinations, all validators green, and the ten-role panel seal recorded", + "id" : "ADR046-exec-006", + "kind" : "work-item", + "owner" : "ADR046-exec-001 + ADR046-exec-002; system-systemd Process Provider owner", + "parallelGroup" : "wi:ADR-046-resources-host-guest-process-user", + "prerequisites" : [ "ADR-046-resources-host-guest-process-user", "ADR046-exec-001", "ADR046-exec-002" ], - "specId": "ADR-046-resources-host-guest-process-user", - "topologicalRank": 12, - "wave": "W5" + "specId" : "ADR-046-resources-host-guest-process-user", + "topologicalRank" : 12, + "wave" : "W5" }, { - "blockers": [], - "destinations": [ + "blockers" : [], + "destinations" : [ "`packages/d2b-provider-system-minijail/src/`: sandbox_compiler.rs, launch.rs, adoption.rs, pidfd.rs, wait.rs, user_ns.rs; `packages/d2b-provider-system-minijail/tests/`: hermetic sandbox-compilation/lifecycle/conformance/fault tests; `packages/d2b-provider-system-minijail/integration/`: clone3/user-namespace and broker-spawn integration scenarios; `packages/d2b-provider-system-minijail/README.md`: Provider identity, `spec.provider.settings` schema, ResourceTypes, placement, RBAC, security posture (capabilities, namespaces, seccomp), telemetry labels, build/test commands (provider crate standard layout — see §Provider crate standard layout)" ], - "entryContracts": [ + "entryContracts" : [ "ADR-046-resources-host-guest-process-user" ], - "exitGate": "ADR046-W5 exit criteria (ADR-046-validation-and-delivery §4): every spec/work item in this wave Merged with clean destinations, all validators green, and the ten-role panel seal recorded", - "id": "ADR046-exec-007", - "kind": "work-item", - "owner": "ADR046-exec-001 + ADR046-exec-002; system-minijail Process Provider owner", - "parallelGroup": "wi:ADR-046-resources-host-guest-process-user", - "prerequisites": [ + "exitGate" : "ADR046-W5 exit criteria (ADR-046-validation-and-delivery §4): every spec/work item in this wave Merged with clean destinations, all validators green, and the ten-role panel seal recorded", + "id" : "ADR046-exec-007", + "kind" : "work-item", + "owner" : "ADR046-exec-001 + ADR046-exec-002; system-minijail Process Provider owner", + "parallelGroup" : "wi:ADR-046-resources-host-guest-process-user", + "prerequisites" : [ "ADR-046-resources-host-guest-process-user", "ADR046-exec-001", "ADR046-exec-002" ], - "specId": "ADR-046-resources-host-guest-process-user", - "topologicalRank": 12, - "wave": "W5" + "specId" : "ADR-046-resources-host-guest-process-user", + "topologicalRank" : 12, + "wave" : "W5" }, { - "blockers": [], - "destinations": [ + "blockers" : [], + "destinations" : [ "`packages/d2b-process-conformance/src/`: shared conformance test matrix run against both system-systemd and system-minijail providers" ], - "entryContracts": [ + "entryContracts" : [ "ADR-046-resources-host-guest-process-user" ], - "exitGate": "ADR046-W5 exit criteria (ADR-046-validation-and-delivery §4): every spec/work item in this wave Merged with clean destinations, all validators green, and the ten-role panel seal recorded", - "id": "ADR046-exec-008", - "kind": "work-item", - "owner": "ADR046-exec-006 + ADR046-exec-007; conformance test owner", - "parallelGroup": "wi:ADR-046-resources-host-guest-process-user", - "prerequisites": [ + "exitGate" : "ADR046-W5 exit criteria (ADR-046-validation-and-delivery §4): every spec/work item in this wave Merged with clean destinations, all validators green, and the ten-role panel seal recorded", + "id" : "ADR046-exec-008", + "kind" : "work-item", + "owner" : "ADR046-exec-006 + ADR046-exec-007; conformance test owner", + "parallelGroup" : "wi:ADR-046-resources-host-guest-process-user", + "prerequisites" : [ "ADR-046-resources-host-guest-process-user", "ADR046-exec-006", "ADR046-exec-007" ], - "specId": "ADR-046-resources-host-guest-process-user", - "topologicalRank": 13, - "wave": "W5" + "specId" : "ADR-046-resources-host-guest-process-user", + "topologicalRank" : 13, + "wave" : "W5" }, { - "blockers": [], - "destinations": [ + "blockers" : [], + "destinations" : [ "`packages/d2b-provider-system-core/src/host.rs` (user-only no-isolation Host); `nixos-modules/options-zones.nix` (Nix unsafe-local Host declaration)" ], - "entryContracts": [ + "entryContracts" : [ "ADR-046-resources-host-guest-process-user" ], - "exitGate": "ADR046-W5 exit criteria (ADR-046-validation-and-delivery §4): every spec/work item in this wave Merged with clean destinations, all validators green, and the ten-role panel seal recorded", - "id": "ADR046-exec-009", - "kind": "work-item", - "owner": "ADR046-exec-001; unsafe-local migration owner", - "parallelGroup": "wi:ADR-046-resources-host-guest-process-user", - "prerequisites": [ + "exitGate" : "ADR046-W5 exit criteria (ADR-046-validation-and-delivery §4): every spec/work item in this wave Merged with clean destinations, all validators green, and the ten-role panel seal recorded", + "id" : "ADR046-exec-009", + "kind" : "work-item", + "owner" : "ADR046-exec-001; unsafe-local migration owner", + "parallelGroup" : "wi:ADR-046-resources-host-guest-process-user", + "prerequisites" : [ "ADR-046-resources-host-guest-process-user", "ADR046-exec-001" ], - "specId": "ADR-046-resources-host-guest-process-user", - "topologicalRank": 11, - "wave": "W5" + "specId" : "ADR-046-resources-host-guest-process-user", + "topologicalRank" : 11, + "wave" : "W5" }, { - "blockers": [], - "destinations": [ + "blockers" : [], + "destinations" : [ "`packages/d2b-provider-system-systemd/src/guest_exec.rs` (guest-domain EphemeralProcess launch via systemd-run inside guest); `packages/d2b-session/` (ComponentSession replacing ad-hoc guest ttrpc); runtime Provider guest bootstrap Process" ], - "entryContracts": [ + "entryContracts" : [ "ADR-046-resources-host-guest-process-user" ], - "exitGate": "ADR046-W5 exit criteria (ADR-046-validation-and-delivery §4): every spec/work item in this wave Merged with clean destinations, all validators green, and the ten-role panel seal recorded", - "id": "ADR046-exec-010", - "kind": "work-item", - "owner": "ADR046-exec-001 + ADR046-exec-007; guestd migration owner", - "parallelGroup": "wi:ADR-046-resources-host-guest-process-user", - "prerequisites": [ + "exitGate" : "ADR046-W5 exit criteria (ADR-046-validation-and-delivery §4): every spec/work item in this wave Merged with clean destinations, all validators green, and the ten-role panel seal recorded", + "id" : "ADR046-exec-010", + "kind" : "work-item", + "owner" : "ADR046-exec-001 + ADR046-exec-007; guestd migration owner", + "parallelGroup" : "wi:ADR-046-resources-host-guest-process-user", + "prerequisites" : [ "ADR-046-resources-host-guest-process-user", "ADR046-exec-001", "ADR046-exec-007" ], - "specId": "ADR-046-resources-host-guest-process-user", - "topologicalRank": 13, - "wave": "W5" + "specId" : "ADR-046-resources-host-guest-process-user", + "topologicalRank" : 13, + "wave" : "W5" }, { - "blockers": [], - "destinations": [ + "blockers" : [], + "destinations" : [ "guest-domain process attachment becomes a ComponentSession named stream to the EphemeralProcess running in the guest; `UserExecSession` trait reimplemented as a typed ResourceClient+ComponentSession attachment" ], - "entryContracts": [ + "entryContracts" : [ "ADR-046-resources-host-guest-process-user" ], - "exitGate": "ADR046-W5 exit criteria (ADR-046-validation-and-delivery §4): every spec/work item in this wave Merged with clean destinations, all validators green, and the ten-role panel seal recorded", - "id": "ADR046-exec-011", - "kind": "work-item", - "owner": "ADR046-exec-004 + ADR046-exec-010; userd migration owner", - "parallelGroup": "wi:ADR-046-resources-host-guest-process-user", - "prerequisites": [ + "exitGate" : "ADR046-W5 exit criteria (ADR-046-validation-and-delivery §4): every spec/work item in this wave Merged with clean destinations, all validators green, and the ten-role panel seal recorded", + "id" : "ADR046-exec-011", + "kind" : "work-item", + "owner" : "ADR046-exec-004 + ADR046-exec-010; userd migration owner", + "parallelGroup" : "wi:ADR-046-resources-host-guest-process-user", + "prerequisites" : [ "ADR-046-resources-host-guest-process-user", "ADR046-exec-004", "ADR046-exec-010" ], - "specId": "ADR-046-resources-host-guest-process-user", - "topologicalRank": 14, - "wave": "W5" + "specId" : "ADR-046-resources-host-guest-process-user", + "topologicalRank" : 14, + "wave" : "W5" }, { - "blockers": [], - "destinations": [ + "blockers" : [], + "destinations" : [ "`nixos-modules/options-zones.nix`: `d2b.zones..resources` option as `types.attrsOf (types.submodule resourceModule)` where each resource module has `type` (required enum), optional `metadata` submodule (`ownerRef`, `labels`, `annotations`), and `spec` (type-dependent, auto-generated submodule); `nixos-modules/zone-bundle.nix`: zone resource bundle emitter (see ADR046-exec-014); `nixos-modules/resource-schemas/`: generated per-type Nix option modules imported by `options-zones.nix`" ], - "entryContracts": [ + "entryContracts" : [ "ADR-046-resources-host-guest-process-user" ], - "exitGate": "ADR046-W5 exit criteria (ADR-046-validation-and-delivery §4): every spec/work item in this wave Merged with clean destinations, all validators green, and the ten-role panel seal recorded", - "id": "ADR046-exec-012", - "kind": "work-item", - "owner": "ADR046-exec-001; Nix resource compiler owner", - "parallelGroup": "wi:ADR-046-resources-host-guest-process-user", - "prerequisites": [ + "exitGate" : "ADR046-W5 exit criteria (ADR-046-validation-and-delivery §4): every spec/work item in this wave Merged with clean destinations, all validators green, and the ten-role panel seal recorded", + "id" : "ADR046-exec-012", + "kind" : "work-item", + "owner" : "ADR046-exec-001; Nix resource compiler owner", + "parallelGroup" : "wi:ADR-046-resources-host-guest-process-user", + "prerequisites" : [ "ADR-046-resources-host-guest-process-user", "ADR046-exec-001" ], - "specId": "ADR-046-resources-host-guest-process-user", - "topologicalRank": 11, - "wave": "W5" + "specId" : "ADR-046-resources-host-guest-process-user", + "topologicalRank" : 11, + "wave" : "W5" }, { - "blockers": [], - "destinations": [ + "blockers" : [], + "destinations" : [ "`packages/d2b-core-controller/src/cleanup.rs`: EphemeralProcess TTL cleanup controller handler" ], - "entryContracts": [ + "entryContracts" : [ "ADR-046-resources-host-guest-process-user" ], - "exitGate": "ADR046-W5 exit criteria (ADR-046-validation-and-delivery §4): every spec/work item in this wave Merged with clean destinations, all validators green, and the ten-role panel seal recorded", - "id": "ADR046-exec-013", - "kind": "work-item", - "owner": "ADR046-exec-008; EphemeralProcess cleanup controller owner", - "parallelGroup": "wi:core-config-hub", - "prerequisites": [ + "exitGate" : "ADR046-W5 exit criteria (ADR-046-validation-and-delivery §4): every spec/work item in this wave Merged with clean destinations, all validators green, and the ten-role panel seal recorded", + "id" : "ADR046-exec-013", + "kind" : "work-item", + "owner" : "ADR046-exec-008; EphemeralProcess cleanup controller owner", + "parallelGroup" : "wi:core-config-hub:w5", + "prerequisites" : [ "ADR-046-resources-host-guest-process-user", - "ADR046-core-001", "ADR046-exec-008" ], - "specId": "ADR-046-resources-host-guest-process-user", - "topologicalRank": 14, - "wave": "W5" + "specId" : "ADR-046-resources-host-guest-process-user", + "topologicalRank" : 14, + "wave" : "W5" }, { - "blockers": [], - "destinations": [ + "blockers" : [], + "destinations" : [ "`nixos-modules/zone-bundle.nix`: Zone resource bundle emitter; `nixos-modules/resource-schemas/`: generated per-type Nix option submodules; `packages/d2b-contracts/src/v3/resource_bundle.rs`: `ResourceBundle`, `ResourceEnvelope`, `BundleManifest`, `BundleIntegrityPin` Rust types; `packages/xtask/src/gen_resource_schemas.rs`: `xtask gen-resource-schemas` (generates schema JSON and Nix option modules)" ], - "entryContracts": [ + "entryContracts" : [ "ADR-046-resources-host-guest-process-user" ], - "exitGate": "ADR046-W5 exit criteria (ADR-046-validation-and-delivery §4): every spec/work item in this wave Merged with clean destinations, all validators green, and the ten-role panel seal recorded", - "id": "ADR046-exec-014", - "kind": "work-item", - "owner": "ADR046-exec-001 + ADR046-exec-012; Nix resource compiler owner", - "parallelGroup": "wi:ADR-046-resources-host-guest-process-user", - "prerequisites": [ + "exitGate" : "ADR046-W5 exit criteria (ADR-046-validation-and-delivery §4): every spec/work item in this wave Merged with clean destinations, all validators green, and the ten-role panel seal recorded", + "id" : "ADR046-exec-014", + "kind" : "work-item", + "owner" : "ADR046-exec-001 + ADR046-exec-012; Nix resource compiler owner", + "parallelGroup" : "wi:ADR-046-resources-host-guest-process-user", + "prerequisites" : [ "ADR-046-resources-host-guest-process-user", "ADR046-exec-001", "ADR046-exec-012" ], - "specId": "ADR-046-resources-host-guest-process-user", - "topologicalRank": 12, - "wave": "W5" + "specId" : "ADR-046-resources-host-guest-process-user", + "topologicalRank" : 12, + "wave" : "W5" }, { - "blockers": [], - "destinations": [ + "blockers" : [], + "destinations" : [ "`packages/d2b-core-controller/src/configuration.rs`: `ZoneConfigController`, `GenerationState`, `PendingCleanup`, `BundleActivation`, `ActivationResult`, `ActivationError`, `GenerationDiff`, `DiffEntry`, `DiffKind`, `CleanupRecord`, `CleanupPhase`, `CleanupOutcome`, `RetentionPolicy`, `RetentionState`; `packages/d2b-core-controller/src/audit.rs`: audit event emission per the \"Audit events\" section" ], - "entryContracts": [ + "entryContracts" : [ "ADR-046-resources-host-guest-process-user" ], - "exitGate": "ADR046-W5 exit criteria (ADR-046-validation-and-delivery §4): every spec/work item in this wave Merged with clean destinations, all validators green, and the ten-role panel seal recorded", - "id": "ADR046-exec-015", - "kind": "work-item", - "owner": "ADR046-exec-014 + ADR046-exec-001 + ADR046-exec-022; configuration generation controller owner", - "parallelGroup": "wi:core-config-hub", - "prerequisites": [ + "exitGate" : "ADR046-W5 exit criteria (ADR-046-validation-and-delivery §4): every spec/work item in this wave Merged with clean destinations, all validators green, and the ten-role panel seal recorded", + "id" : "ADR046-exec-015", + "kind" : "work-item", + "owner" : "ADR046-exec-014 + ADR046-exec-001 + ADR046-exec-022; configuration generation controller owner", + "parallelGroup" : "wi:core-config-hub:w5", + "prerequisites" : [ "ADR-046-resources-host-guest-process-user", - "ADR046-core-001", "ADR046-exec-001", "ADR046-exec-014", "ADR046-exec-022" ], - "specId": "ADR-046-resources-host-guest-process-user", - "topologicalRank": 15, - "wave": "W5" + "specId" : "ADR-046-resources-host-guest-process-user", + "topologicalRank" : 15, + "wave" : "W5" }, { - "blockers": [], - "destinations": [ + "blockers" : [], + "destinations" : [ "`packages/d2b-bus-session/src/`: all above modules verbatim; `packages/d2b-bus-session/tests/`: all above tests verbatim" ], - "entryContracts": [ + "entryContracts" : [ "ADR-046-resources-host-guest-process-user" ], - "exitGate": "ADR046-W5 exit criteria (ADR-046-validation-and-delivery §4): every spec/work item in this wave Merged with clean destinations, all validators green, and the ten-role panel seal recorded", - "id": "ADR046-exec-016", - "kind": "work-item", - "owner": "ADR046-exec-001; `d2b-session` v3 crate owner", - "parallelGroup": "wi:ADR-046-resources-host-guest-process-user", - "prerequisites": [ + "exitGate" : "ADR046-W5 exit criteria (ADR-046-validation-and-delivery §4): every spec/work item in this wave Merged with clean destinations, all validators green, and the ten-role panel seal recorded", + "id" : "ADR046-exec-016", + "kind" : "work-item", + "owner" : "ADR046-exec-001; `d2b-session` v3 crate owner", + "parallelGroup" : "wi:ADR-046-resources-host-guest-process-user", + "prerequisites" : [ "ADR-046-resources-host-guest-process-user", "ADR046-exec-001" ], - "specId": "ADR-046-resources-host-guest-process-user", - "topologicalRank": 11, - "wave": "W5" + "specId" : "ADR-046-resources-host-guest-process-user", + "topologicalRank" : 11, + "wave" : "W5" }, { - "blockers": [], - "destinations": [ + "blockers" : [], + "destinations" : [ "`packages/d2b-bus-session-unix/src/`: all above modules verbatim; `packages/d2b-bus-session-unix/tests/`: all above tests verbatim" ], - "entryContracts": [ + "entryContracts" : [ "ADR-046-resources-host-guest-process-user" ], - "exitGate": "ADR046-W5 exit criteria (ADR-046-validation-and-delivery §4): every spec/work item in this wave Merged with clean destinations, all validators green, and the ten-role panel seal recorded", - "id": "ADR046-exec-017", - "kind": "work-item", - "owner": "ADR046-exec-016; `d2b-bus-session-unix` crate owner", - "parallelGroup": "wi:ADR-046-resources-host-guest-process-user", - "prerequisites": [ + "exitGate" : "ADR046-W5 exit criteria (ADR-046-validation-and-delivery §4): every spec/work item in this wave Merged with clean destinations, all validators green, and the ten-role panel seal recorded", + "id" : "ADR046-exec-017", + "kind" : "work-item", + "owner" : "ADR046-exec-016; `d2b-bus-session-unix` crate owner", + "parallelGroup" : "wi:ADR-046-resources-host-guest-process-user", + "prerequisites" : [ "ADR-046-resources-host-guest-process-user", "ADR046-exec-016" ], - "specId": "ADR-046-resources-host-guest-process-user", - "topologicalRank": 12, - "wave": "W5" + "specId" : "ADR-046-resources-host-guest-process-user", + "topologicalRank" : 12, + "wave" : "W5" }, { - "blockers": [], - "destinations": [ + "blockers" : [], + "destinations" : [ "`packages/d2b-bus-wire/src/session.rs`: v3 bus protocol constants and wire types; all numeric constants copied verbatim; `PREFACE_MAGIC` retained; `EndpointPolicy` and `EndpointPolicyIdentity` adapted to use v3 `ZoneId`/`ProviderId` instead of ADR45 `RealmId` in the policy identity fingerprint" ], - "entryContracts": [ + "entryContracts" : [ "ADR-046-resources-host-guest-process-user" ], - "exitGate": "ADR046-W5 exit criteria (ADR-046-validation-and-delivery §4): every spec/work item in this wave Merged with clean destinations, all validators green, and the ten-role panel seal recorded", - "id": "ADR046-exec-018", - "kind": "work-item", - "owner": "ADR046-exec-016; `d2b-bus-wire` contract owner", - "parallelGroup": "wi:ADR-046-resources-host-guest-process-user", - "prerequisites": [ + "exitGate" : "ADR046-W5 exit criteria (ADR-046-validation-and-delivery §4): every spec/work item in this wave Merged with clean destinations, all validators green, and the ten-role panel seal recorded", + "id" : "ADR046-exec-018", + "kind" : "work-item", + "owner" : "ADR046-exec-016; `d2b-bus-wire` contract owner", + "parallelGroup" : "wi:ADR-046-resources-host-guest-process-user", + "prerequisites" : [ "ADR-046-resources-host-guest-process-user", "ADR046-exec-016" ], - "specId": "ADR-046-resources-host-guest-process-user", - "topologicalRank": 12, - "wave": "W5" + "specId" : "ADR-046-resources-host-guest-process-user", + "topologicalRank" : 12, + "wave" : "W5" }, { - "blockers": [], - "destinations": [ + "blockers" : [], + "destinations" : [ "`packages/d2b-provider-runtime/src/`: `registry.rs`, `rpc.rs`, `instance.rs`, `context.rs`, `error.rs`; provider trait objects moved to `d2b-bus-wire` or `d2b-provider-contracts`" ], - "entryContracts": [ + "entryContracts" : [ "ADR-046-resources-host-guest-process-user" ], - "exitGate": "ADR046-W5 exit criteria (ADR-046-validation-and-delivery §4): every spec/work item in this wave Merged with clean destinations, all validators green, and the ten-role panel seal recorded", - "id": "ADR046-exec-019", - "kind": "work-item", - "owner": "ADR046-exec-016 + ADR046-exec-018; `d2b-provider-runtime` crate owner", - "parallelGroup": "wi:ADR-046-resources-host-guest-process-user", - "prerequisites": [ + "exitGate" : "ADR046-W5 exit criteria (ADR-046-validation-and-delivery §4): every spec/work item in this wave Merged with clean destinations, all validators green, and the ten-role panel seal recorded", + "id" : "ADR046-exec-019", + "kind" : "work-item", + "owner" : "ADR046-exec-016 + ADR046-exec-018; `d2b-provider-runtime` crate owner", + "parallelGroup" : "wi:ADR-046-resources-host-guest-process-user", + "prerequisites" : [ "ADR-046-resources-host-guest-process-user", "ADR046-exec-016", "ADR046-exec-018" ], - "specId": "ADR-046-resources-host-guest-process-user", - "topologicalRank": 13, - "wave": "W5" + "specId" : "ADR-046-resources-host-guest-process-user", + "topologicalRank" : 13, + "wave" : "W5" }, { - "blockers": [], - "destinations": [ + "blockers" : [], + "destinations" : [ "`packages/d2b-provider-toolkit/src/`: retain all modules verbatim; adapt `ProviderAgentAdapter` to use v3 `ProviderRegistry` (ADR046-exec-019) and v3 bus wire types; adapt `GeneratedProviderServiceServer` to use v3 generated service stubs (ADR046-exec-021); `packages/d2b-provider-agent/src/`: adapted from `gateway-runtime/src/provider_agent.rs`" ], - "entryContracts": [ + "entryContracts" : [ "ADR-046-resources-host-guest-process-user" ], - "exitGate": "ADR046-W5 exit criteria (ADR-046-validation-and-delivery §4): every spec/work item in this wave Merged with clean destinations, all validators green, and the ten-role panel seal recorded", - "id": "ADR046-exec-020", - "kind": "work-item", - "owner": "ADR046-exec-019; `d2b-provider-toolkit` owner", - "parallelGroup": "wi:ADR-046-resources-host-guest-process-user", - "prerequisites": [ + "exitGate" : "ADR046-W5 exit criteria (ADR-046-validation-and-delivery §4): every spec/work item in this wave Merged with clean destinations, all validators green, and the ten-role panel seal recorded", + "id" : "ADR046-exec-020", + "kind" : "work-item", + "owner" : "ADR046-exec-019; `d2b-provider-toolkit` owner", + "parallelGroup" : "wi:ADR-046-resources-host-guest-process-user", + "prerequisites" : [ "ADR-046-resources-host-guest-process-user", "ADR046-exec-019" ], - "specId": "ADR-046-resources-host-guest-process-user", - "topologicalRank": 14, - "wave": "W5" + "specId" : "ADR-046-resources-host-guest-process-user", + "topologicalRank" : 14, + "wave" : "W5" }, { - "blockers": [], - "destinations": [ + "blockers" : [], + "destinations" : [ "`packages/d2b-bus-contracts/src/generated_v3_services/`: v3 generated ttrpc stubs for Zone service methods (Resource CRUD, Watch, ComponentSession service verbs); `packages/d2b-zone-service/src/`: Zone runtime service handler adapted from `DaemonServiceV2` pattern; `packages/d2b-zone-service/src/admission.rs`, `handler.rs`, `routing.rs`" ], - "entryContracts": [ + "entryContracts" : [ "ADR-046-resources-host-guest-process-user" ], - "exitGate": "ADR046-W5 exit criteria (ADR-046-validation-and-delivery §4): every spec/work item in this wave Merged with clean destinations, all validators green, and the ten-role panel seal recorded", - "id": "ADR046-exec-021", - "kind": "work-item", - "owner": "ADR046-exec-016 + ADR046-exec-018; v3 Zone service contract owner", - "parallelGroup": "wi:ADR-046-resources-host-guest-process-user", - "prerequisites": [ + "exitGate" : "ADR046-W5 exit criteria (ADR-046-validation-and-delivery §4): every spec/work item in this wave Merged with clean destinations, all validators green, and the ten-role panel seal recorded", + "id" : "ADR046-exec-021", + "kind" : "work-item", + "owner" : "ADR046-exec-016 + ADR046-exec-018; v3 Zone service contract owner", + "parallelGroup" : "wi:ADR-046-resources-host-guest-process-user", + "prerequisites" : [ "ADR-046-resources-host-guest-process-user", "ADR046-exec-016", "ADR046-exec-018" ], - "specId": "ADR-046-resources-host-guest-process-user", - "topologicalRank": 13, - "wave": "W5" + "specId" : "ADR-046-resources-host-guest-process-user", + "topologicalRank" : 13, + "wave" : "W5" }, { - "blockers": [], - "destinations": [ + "blockers" : [], + "destinations" : [ "`packages/d2b-bus-client/src/`: all above modules; `DaemonClient` → `ZoneClient` (v3 Resource CRUD/Watch verbs); `GuestClient` → `ProcessAttachClient`; `HostSocketConnector` → `ZoneSocketConnector`; `LocalDaemonSession` → `LocalZoneSession`" ], - "entryContracts": [ + "entryContracts" : [ "ADR-046-resources-host-guest-process-user" ], - "exitGate": "ADR046-W5 exit criteria (ADR-046-validation-and-delivery §4): every spec/work item in this wave Merged with clean destinations, all validators green, and the ten-role panel seal recorded", - "id": "ADR046-exec-022", - "kind": "work-item", - "owner": "ADR046-exec-016 + ADR046-exec-021; `d2b-bus-client` crate owner", - "parallelGroup": "wi:ADR-046-resources-host-guest-process-user", - "prerequisites": [ + "exitGate" : "ADR046-W5 exit criteria (ADR-046-validation-and-delivery §4): every spec/work item in this wave Merged with clean destinations, all validators green, and the ten-role panel seal recorded", + "id" : "ADR046-exec-022", + "kind" : "work-item", + "owner" : "ADR046-exec-016 + ADR046-exec-021; `d2b-bus-client` crate owner", + "parallelGroup" : "wi:ADR-046-resources-host-guest-process-user", + "prerequisites" : [ "ADR-046-resources-host-guest-process-user", "ADR046-exec-016", "ADR046-exec-021" ], - "specId": "ADR-046-resources-host-guest-process-user", - "topologicalRank": 14, - "wave": "W5" + "specId" : "ADR-046-resources-host-guest-process-user", + "topologicalRank" : 14, + "wave" : "W5" }, { - "blockers": [], - "destinations": [ + "blockers" : [], + "destinations" : [ "`packages/d2b-zone-router/src/`: `router.rs` (v3 `ZoneOperationRouter` — idempotency semantics copied verbatim; dedup key namespace adapted from `(realm, principal, node, kind, key)` to `(zone, resource-type, resource-name, verb, idempotency-key)`); `service.rs` (v3 `ZoneServiceLimits`, `ZoneServiceServer`, `ZoneAuditEvent`); `resolver.rs` (v3 `ZoneTargetResolver`, `ZoneEntrypointTable`)" ], - "entryContracts": [ + "entryContracts" : [ "ADR-046-resources-host-guest-process-user" ], - "exitGate": "ADR046-W5 exit criteria (ADR-046-validation-and-delivery §4): every spec/work item in this wave Merged with clean destinations, all validators green, and the ten-role panel seal recorded", - "id": "ADR046-exec-023", - "kind": "work-item", - "owner": "ADR046-exec-021 + ADR046-exec-019; Zone bus routing owner", - "parallelGroup": "wi:ADR-046-resources-host-guest-process-user", - "prerequisites": [ + "exitGate" : "ADR046-W5 exit criteria (ADR-046-validation-and-delivery §4): every spec/work item in this wave Merged with clean destinations, all validators green, and the ten-role panel seal recorded", + "id" : "ADR046-exec-023", + "kind" : "work-item", + "owner" : "ADR046-exec-021 + ADR046-exec-019; Zone bus routing owner", + "parallelGroup" : "wi:ADR-046-resources-host-guest-process-user", + "prerequisites" : [ "ADR-046-resources-host-guest-process-user", "ADR046-exec-019", "ADR046-exec-021" ], - "specId": "ADR-046-resources-host-guest-process-user", - "topologicalRank": 14, - "wave": "W5" + "specId" : "ADR-046-resources-host-guest-process-user", + "topologicalRank" : 14, + "wave" : "W5" + }, + { + "blockers" : [], + "destinations" : [ + "`packages/d2b-core-controller/src/user_session_authority.rs` (or a core/user-agent per-session agent Process under `Provider/system-systemd`); `AuthorityDescriptor` on the session authority" + ], + "entryContracts" : [ + "ADR-046-resources-host-guest-process-user" + ], + "exitGate" : "ADR046-W5 exit criteria (ADR-046-validation-and-delivery §4): every spec/work item in this wave Merged with clean destinations, all validators green, and the ten-role panel seal recorded", + "id" : "ADR046-exec-024", + "kind" : "work-item", + "owner" : "ADR046-zone-control-019 (authority index); `Provider/system-systemd` (user manager) + core/user-agent owner", + "parallelGroup" : "wi:ADR-046-resources-host-guest-process-user", + "prerequisites" : [ + "ADR-046-resources-host-guest-process-user", + "ADR046-zone-control-019" + ], + "specId" : "ADR-046-resources-host-guest-process-user", + "topologicalRank" : 12, + "wave" : "W5" }, { - "blockers": [], - "destinations": [ + "blockers" : [], + "destinations" : [ "`proofs/redb-resource-store-spike/`" ], - "entryContracts": [ + "entryContracts" : [ "ADR-046-feasibility-and-spikes" ], - "exitGate": "ADR046-W7 exit criteria (ADR-046-validation-and-delivery §4): every spec/work item in this wave Merged with clean destinations, all validators green, and the ten-role panel seal recorded", - "id": "ADR046-feasibility-001", - "kind": "work-item", - "owner": "W0 shared contract root; store/reconciliation integrator", - "parallelGroup": "wi:ADR-046-feasibility-and-spikes", - "prerequisites": [ + "exitGate" : "ADR046-W7 exit criteria (ADR-046-validation-and-delivery §4): every spec/work item in this wave Merged with clean destinations, all validators green, and the ten-role panel seal recorded", + "id" : "ADR046-feasibility-001", + "kind" : "work-item", + "owner" : "W0 shared contract root; store/reconciliation integrator", + "parallelGroup" : "wi:ADR-046-feasibility-and-spikes", + "prerequisites" : [ "ADR-046-feasibility-and-spikes" ], - "specId": "ADR-046-feasibility-and-spikes", - "topologicalRank": 11, - "wave": "W7" + "specId" : "ADR-046-feasibility-and-spikes", + "topologicalRank" : 11, + "wave" : "W7" }, { - "blockers": [], - "destinations": [ + "blockers" : [], + "destinations" : [ "`proofs/process-fastlaunch-spike/`" ], - "entryContracts": [ + "entryContracts" : [ "ADR-046-feasibility-and-spikes" ], - "exitGate": "ADR046-W7 exit criteria (ADR-046-validation-and-delivery §4): every spec/work item in this wave Merged with clean destinations, all validators green, and the ten-role panel seal recorded", - "id": "ADR046-feasibility-002", - "kind": "work-item", - "owner": "`ADR046-feasibility-001`; reconciliation/process integrator", - "parallelGroup": "wi:ADR-046-feasibility-and-spikes", - "prerequisites": [ + "exitGate" : "ADR046-W7 exit criteria (ADR-046-validation-and-delivery §4): every spec/work item in this wave Merged with clean destinations, all validators green, and the ten-role panel seal recorded", + "id" : "ADR046-feasibility-002", + "kind" : "work-item", + "owner" : "`ADR046-feasibility-001`; reconciliation/process integrator", + "parallelGroup" : "wi:ADR-046-feasibility-and-spikes", + "prerequisites" : [ "ADR-046-feasibility-and-spikes", "ADR046-feasibility-001" ], - "specId": "ADR-046-feasibility-and-spikes", - "topologicalRank": 12, - "wave": "W7" + "specId" : "ADR-046-feasibility-and-spikes", + "topologicalRank" : 12, + "wave" : "W7" }, { - "blockers": [], - "destinations": [ + "blockers" : [], + "destinations" : [ "`proofs/effectport-async-spike/`" ], - "entryContracts": [ + "entryContracts" : [ "ADR-046-feasibility-and-spikes" ], - "exitGate": "ADR046-W7 exit criteria (ADR-046-validation-and-delivery §4): every spec/work item in this wave Merged with clean destinations, all validators green, and the ten-role panel seal recorded", - "id": "ADR046-feasibility-003", - "kind": "work-item", - "owner": "Independent of `-001`/`-002`; EffectPort/ProviderSupervisor integrator", - "parallelGroup": "wi:ADR-046-feasibility-and-spikes", - "prerequisites": [ + "exitGate" : "ADR046-W7 exit criteria (ADR-046-validation-and-delivery §4): every spec/work item in this wave Merged with clean destinations, all validators green, and the ten-role panel seal recorded", + "id" : "ADR046-feasibility-003", + "kind" : "work-item", + "owner" : "Independent of `-001`/`-002`; EffectPort/ProviderSupervisor integrator", + "parallelGroup" : "wi:ADR-046-feasibility-and-spikes", + "prerequisites" : [ "ADR-046-feasibility-and-spikes" ], - "specId": "ADR-046-feasibility-and-spikes", - "topologicalRank": 11, - "wave": "W7" + "specId" : "ADR-046-feasibility-and-spikes", + "topologicalRank" : 11, + "wave" : "W7" }, { - "blockers": [], - "destinations": [ + "blockers" : [], + "destinations" : [ "`proofs/provider-packaging-spike/`" ], - "entryContracts": [ + "entryContracts" : [ "ADR-046-feasibility-and-spikes" ], - "exitGate": "ADR046-W7 exit criteria (ADR-046-validation-and-delivery §4): every spec/work item in this wave Merged with clean destinations, all validators green, and the ten-role panel seal recorded", - "id": "ADR046-feasibility-004", - "kind": "work-item", - "owner": "Independent of `-001`/`-002`/`-003`; Provider packaging/toolkit integrator", - "parallelGroup": "wi:ADR-046-feasibility-and-spikes", - "prerequisites": [ + "exitGate" : "ADR046-W7 exit criteria (ADR-046-validation-and-delivery §4): every spec/work item in this wave Merged with clean destinations, all validators green, and the ten-role panel seal recorded", + "id" : "ADR046-feasibility-004", + "kind" : "work-item", + "owner" : "Independent of `-001`/`-002`/`-003`; Provider packaging/toolkit integrator", + "parallelGroup" : "wi:ADR-046-feasibility-and-spikes", + "prerequisites" : [ "ADR-046-feasibility-and-spikes" ], - "specId": "ADR-046-feasibility-and-spikes", - "topologicalRank": 11, - "wave": "W7" + "specId" : "ADR-046-feasibility-and-spikes", + "topologicalRank" : 11, + "wave" : "W7" }, { - "blockers": [], - "destinations": [ + "blockers" : [], + "destinations" : [ "`proofs/bus-routing-noise-spike/`, `proofs/transport-opaque-streams-spike/`, `proofs/credential-kk-e2e-spike/`" ], - "entryContracts": [ + "entryContracts" : [ "ADR-046-feasibility-and-spikes" ], - "exitGate": "ADR046-W7 exit criteria (ADR-046-validation-and-delivery §4): every spec/work item in this wave Merged with clean destinations, all validators green, and the ten-role panel seal recorded", - "id": "ADR046-feasibility-005", - "kind": "work-item", - "owner": "Independent of `-001` through `-004`; bus/session/transport/credential integrator", - "parallelGroup": "wi:ADR-046-feasibility-and-spikes", - "prerequisites": [ + "exitGate" : "ADR046-W7 exit criteria (ADR-046-validation-and-delivery §4): every spec/work item in this wave Merged with clean destinations, all validators green, and the ten-role panel seal recorded", + "id" : "ADR046-feasibility-005", + "kind" : "work-item", + "owner" : "Independent of `-001` through `-004`; bus/session/transport/credential integrator", + "parallelGroup" : "wi:ADR-046-feasibility-and-spikes", + "prerequisites" : [ "ADR-046-feasibility-and-spikes" ], - "specId": "ADR-046-feasibility-and-spikes", - "topologicalRank": 11, - "wave": "W7" + "specId" : "ADR-046-feasibility-and-spikes", + "topologicalRank" : 11, + "wave" : "W7" }, { - "blockers": [], - "destinations": [ + "blockers" : [], + "destinations" : [ "`proofs/provider-state-export-spike/`, `proofs/volume-policy-spike/`" ], - "entryContracts": [ + "entryContracts" : [ "ADR-046-feasibility-and-spikes" ], - "exitGate": "ADR046-W7 exit criteria (ADR-046-validation-and-delivery §4): every spec/work item in this wave Merged with clean destinations, all validators green, and the ten-role panel seal recorded", - "id": "ADR046-feasibility-006", - "kind": "work-item", - "owner": "Independent of `-001` through `-005`; Provider-state/Volume integrator", - "parallelGroup": "wi:ADR-046-feasibility-and-spikes", - "prerequisites": [ + "exitGate" : "ADR046-W7 exit criteria (ADR-046-validation-and-delivery §4): every spec/work item in this wave Merged with clean destinations, all validators green, and the ten-role panel seal recorded", + "id" : "ADR046-feasibility-006", + "kind" : "work-item", + "owner" : "Independent of `-001` through `-005`; Provider-state/Volume integrator", + "parallelGroup" : "wi:ADR-046-feasibility-and-spikes", + "prerequisites" : [ "ADR-046-feasibility-and-spikes" ], - "specId": "ADR-046-feasibility-and-spikes", - "topologicalRank": 11, - "wave": "W7" + "specId" : "ADR-046-feasibility-and-spikes", + "topologicalRank" : 11, + "wave" : "W7" }, { - "blockers": [], - "destinations": [ + "blockers" : [], + "destinations" : [ "`proofs/process-provider-conformance-spike/`" ], - "entryContracts": [ + "entryContracts" : [ "ADR-046-feasibility-and-spikes" ], - "exitGate": "ADR046-W7 exit criteria (ADR-046-validation-and-delivery §4): every spec/work item in this wave Merged with clean destinations, all validators green, and the ten-role panel seal recorded", - "id": "ADR046-feasibility-007", - "kind": "work-item", - "owner": "Independent of `-001` through `-006`; Process Provider integrator", - "parallelGroup": "wi:ADR-046-feasibility-and-spikes", - "prerequisites": [ + "exitGate" : "ADR046-W7 exit criteria (ADR-046-validation-and-delivery §4): every spec/work item in this wave Merged with clean destinations, all validators green, and the ten-role panel seal recorded", + "id" : "ADR046-feasibility-007", + "kind" : "work-item", + "owner" : "Independent of `-001` through `-006`; Process Provider integrator", + "parallelGroup" : "wi:ADR-046-feasibility-and-spikes", + "prerequisites" : [ "ADR-046-feasibility-and-spikes" ], - "specId": "ADR-046-feasibility-and-spikes", - "topologicalRank": 11, - "wave": "W7" + "specId" : "ADR-046-feasibility-and-spikes", + "topologicalRank" : 11, + "wave" : "W7" }, { - "blockers": [], - "destinations": [ + "blockers" : [], + "destinations" : [ "`proofs/nix-authoring-spike/`" ], - "entryContracts": [ + "entryContracts" : [ "ADR-046-feasibility-and-spikes" ], - "exitGate": "ADR046-W7 exit criteria (ADR-046-validation-and-delivery §4): every spec/work item in this wave Merged with clean destinations, all validators green, and the ten-role panel seal recorded", - "id": "ADR046-feasibility-008", - "kind": "work-item", - "owner": "Independent of `-001` through `-007`; Nix/xtask integrator", - "parallelGroup": "wi:ADR-046-feasibility-and-spikes", - "prerequisites": [ + "exitGate" : "ADR046-W7 exit criteria (ADR-046-validation-and-delivery §4): every spec/work item in this wave Merged with clean destinations, all validators green, and the ten-role panel seal recorded", + "id" : "ADR046-feasibility-008", + "kind" : "work-item", + "owner" : "Independent of `-001` through `-007`; Nix/xtask integrator", + "parallelGroup" : "wi:ADR-046-feasibility-and-spikes", + "prerequisites" : [ "ADR-046-feasibility-and-spikes" ], - "specId": "ADR-046-feasibility-and-spikes", - "topologicalRank": 11, - "wave": "W7" + "specId" : "ADR-046-feasibility-and-spikes", + "topologicalRank" : 11, + "wave" : "W7" }, { - "blockers": [], - "destinations": [ + "blockers" : [], + "destinations" : [ "`proofs/cli-discovery-spike/`, `proofs/clean-cutover-spike/`" ], - "entryContracts": [ + "entryContracts" : [ "ADR-046-feasibility-and-spikes" ], - "exitGate": "ADR046-W7 exit criteria (ADR-046-validation-and-delivery §4): every spec/work item in this wave Merged with clean destinations, all validators green, and the ten-role panel seal recorded", - "id": "ADR046-feasibility-009", - "kind": "work-item", - "owner": "Independent of `-001` through `-008`; CLI integrator", - "parallelGroup": "wi:ADR-046-feasibility-and-spikes", - "prerequisites": [ + "exitGate" : "ADR046-W7 exit criteria (ADR-046-validation-and-delivery §4): every spec/work item in this wave Merged with clean destinations, all validators green, and the ten-role panel seal recorded", + "id" : "ADR046-feasibility-009", + "kind" : "work-item", + "owner" : "Independent of `-001` through `-008`; CLI integrator", + "parallelGroup" : "wi:ADR-046-feasibility-and-spikes", + "prerequisites" : [ "ADR-046-feasibility-and-spikes" ], - "specId": "ADR-046-feasibility-and-spikes", - "topologicalRank": 11, - "wave": "W7" + "specId" : "ADR-046-feasibility-and-spikes", + "topologicalRank" : 11, + "wave" : "W7" }, { - "blockers": [], - "destinations": [ + "blockers" : [], + "destinations" : [ "`proofs/e2e-composition-spike/`" ], - "entryContracts": [ + "entryContracts" : [ "ADR-046-feasibility-and-spikes" ], - "exitGate": "ADR046-W7 exit criteria (ADR-046-validation-and-delivery §4): every spec/work item in this wave Merged with clean destinations, all validators green, and the ten-role panel seal recorded", - "id": "ADR046-feasibility-010", - "kind": "work-item", - "owner": "`ADR046-feasibility-001` through `ADR046-feasibility-009` (integrates their fakes; must run last)", - "parallelGroup": "wi:ADR-046-feasibility-and-spikes", - "prerequisites": [ + "exitGate" : "ADR046-W7 exit criteria (ADR-046-validation-and-delivery §4): every spec/work item in this wave Merged with clean destinations, all validators green, and the ten-role panel seal recorded", + "id" : "ADR046-feasibility-010", + "kind" : "work-item", + "owner" : "`ADR046-feasibility-001` through `ADR046-feasibility-009` (integrates their fakes; must run last)", + "parallelGroup" : "wi:ADR-046-feasibility-and-spikes", + "prerequisites" : [ "ADR-046-feasibility-and-spikes", "ADR046-feasibility-001", "ADR046-feasibility-009" ], - "specId": "ADR-046-feasibility-and-spikes", - "topologicalRank": 12, - "wave": "W7" + "specId" : "ADR-046-feasibility-and-spikes", + "topologicalRank" : 12, + "wave" : "W7" }, { - "blockers": [], - "destinations": [ + "blockers" : [], + "destinations" : [ "`proofs/test-runtime-budget-spike/`; the committed baseline ledger consumed by `ADR046-delivery-007`" ], - "entryContracts": [ + "entryContracts" : [ "ADR-046-feasibility-and-spikes" ], - "exitGate": "ADR046-W7 exit criteria (ADR-046-validation-and-delivery §4): every spec/work item in this wave Merged with clean destinations, all validators green, and the ten-role panel seal recorded", - "id": "ADR046-feasibility-011", - "kind": "work-item", - "owner": "`ADR046-delivery-007`; delivery/test-tooling integrator", - "parallelGroup": "wi:ADR-046-feasibility-and-spikes", - "prerequisites": [ + "exitGate" : "ADR046-W7 exit criteria (ADR-046-validation-and-delivery §4): every spec/work item in this wave Merged with clean destinations, all validators green, and the ten-role panel seal recorded", + "id" : "ADR046-feasibility-011", + "kind" : "work-item", + "owner" : "`ADR046-delivery-007`; delivery/test-tooling integrator", + "parallelGroup" : "wi:ADR-046-feasibility-and-spikes", + "prerequisites" : [ "ADR-046-feasibility-and-spikes", "ADR046-delivery-007" ], - "specId": "ADR-046-feasibility-and-spikes", - "topologicalRank": 14, - "wave": "W7" + "specId" : "ADR-046-feasibility-and-spikes", + "topologicalRank" : 14, + "wave" : "W7" }, { - "blockers": [], - "destinations": [ - "`packages/d2b-provider-system-core/src/{host_reconciler.rs,host_status.rs,host_process_audit.rs}`; adapted `nixos-modules/unsafe-local-workloads-json.nix`; `packages/d2b-provider-system-core/tests/host_posture_contract.rs`" + "blockers" : [], + "destinations" : [ + "`packages/d2b-provider-device-gpu/` with `src/`, `tests/`, `integration/`, `README.md`; add to workspace `Cargo.toml` members list (alphanumerically sorted)" ], - "entryContracts": [ - "ADR-046-telemetry-audit-and-support" + "entryContracts" : [ + "ADR-046-provider-device-gpu" ], - "exitGate": "ADR046-W5 exit criteria (ADR-046-validation-and-delivery §4): every spec/work item in this wave Merged with clean destinations, all validators green, and the ten-role panel seal recorded", - "id": "ADR046-host-posture-001", - "kind": "work-item", - "owner": "ADR046-audit-001 + ADR046-core-001; `Provider/system-core` owner", - "parallelGroup": "wi:ADR-046-telemetry-audit-and-support", - "prerequisites": [ - "ADR-046-telemetry-audit-and-support", - "ADR046-audit-001", - "ADR046-core-001" + "exitGate" : "ADR046-W6 exit criteria (ADR-046-validation-and-delivery §4): every spec/work item in this wave Merged with clean destinations, all validators green, and the ten-role panel seal recorded", + "id" : "ADR046-gpu-001", + "kind" : "work-item", + "owner" : "`ADR046-resources-device` accepted; `ADR046-provider-model-and-packaging` accepted; workspace root must add crate", + "parallelGroup" : "wi:ADR-046-provider-device-gpu", + "prerequisites" : [ + "ADR-046-provider-device-gpu" + ], + "specId" : "ADR-046-provider-device-gpu", + "topologicalRank" : 11, + "wave" : "W6" + }, + { + "blockers" : [], + "destinations" : [ + "`packages/d2b-provider-device-gpu/src/controller.rs`" + ], + "entryContracts" : [ + "ADR-046-provider-device-gpu" + ], + "exitGate" : "ADR046-W6 exit criteria (ADR-046-validation-and-delivery §4): every spec/work item in this wave Merged with clean destinations, all validators green, and the ten-role panel seal recorded", + "id" : "ADR046-gpu-002", + "kind" : "work-item", + "owner" : "ADR046-gpu-001; `ADR-046-resource-reconciliation` implementation present; Provider toolkit `ResourceClient` available", + "parallelGroup" : "wi:ADR-046-provider-device-gpu", + "prerequisites" : [ + "ADR-046-provider-device-gpu", + "ADR046-gpu-001" + ], + "specId" : "ADR-046-provider-device-gpu", + "topologicalRank" : 12, + "wave" : "W6" + }, + { + "blockers" : [], + "destinations" : [ + "`packages/d2b-provider-device-gpu/src/probe.rs`" + ], + "entryContracts" : [ + "ADR-046-provider-device-gpu" + ], + "exitGate" : "ADR046-W6 exit criteria (ADR-046-validation-and-delivery §4): every spec/work item in this wave Merged with clean destinations, all validators green, and the ten-role panel seal recorded", + "id" : "ADR046-gpu-003", + "kind" : "work-item", + "owner" : "ADR046-gpu-002", + "parallelGroup" : "wi:ADR-046-provider-device-gpu", + "prerequisites" : [ + "ADR-046-provider-device-gpu", + "ADR046-gpu-002" + ], + "specId" : "ADR-046-provider-device-gpu", + "topologicalRank" : 13, + "wave" : "W6" + }, + { + "blockers" : [], + "destinations" : [ + "`packages/d2b-provider-device-gpu/src/arbitration.rs`" + ], + "entryContracts" : [ + "ADR-046-provider-device-gpu" + ], + "exitGate" : "ADR046-W6 exit criteria (ADR-046-validation-and-delivery §4): every spec/work item in this wave Merged with clean destinations, all validators green, and the ten-role panel seal recorded", + "id" : "ADR046-gpu-004", + "kind" : "work-item", + "owner" : "ADR046-gpu-002", + "parallelGroup" : "wi:ADR-046-provider-device-gpu", + "prerequisites" : [ + "ADR-046-provider-device-gpu", + "ADR046-gpu-002" + ], + "specId" : "ADR-046-provider-device-gpu", + "topologicalRank" : 13, + "wave" : "W6" + }, + { + "blockers" : [], + "destinations" : [ + "`packages/d2b-provider-device-gpu/src/worker_gpu.rs`" + ], + "entryContracts" : [ + "ADR-046-provider-device-gpu" + ], + "exitGate" : "ADR046-W6 exit criteria (ADR-046-validation-and-delivery §4): every spec/work item in this wave Merged with clean destinations, all validators green, and the ten-role panel seal recorded", + "id" : "ADR046-gpu-005", + "kind" : "work-item", + "owner" : "ADR046-gpu-002; `ADR046-components-processes-and-sandbox` (Provider/system-minijail present and able to handle Process resources with `gpu-worker`/`render-node-worker` templates)", + "parallelGroup" : "wi:ADR-046-provider-device-gpu", + "prerequisites" : [ + "ADR-046-provider-device-gpu", + "ADR046-gpu-002" + ], + "specId" : "ADR-046-provider-device-gpu", + "topologicalRank" : 13, + "wave" : "W6" + }, + { + "blockers" : [], + "destinations" : [ + "`packages/d2b-provider-device-gpu/src/worker_video.rs`, `tests/wire_constant_snapshot.rs`" + ], + "entryContracts" : [ + "ADR-046-provider-device-gpu" + ], + "exitGate" : "ADR046-W6 exit criteria (ADR-046-validation-and-delivery §4): every spec/work item in this wave Merged with clean destinations, all validators green, and the ten-role panel seal recorded", + "id" : "ADR046-gpu-006", + "kind" : "work-item", + "owner" : "ADR046-gpu-005 (video depends on GPU Process being Ready)", + "parallelGroup" : "wi:ADR-046-provider-device-gpu", + "prerequisites" : [ + "ADR-046-provider-device-gpu", + "ADR046-gpu-005" + ], + "specId" : "ADR-046-provider-device-gpu", + "topologicalRank" : 14, + "wave" : "W6" + }, + { + "blockers" : [], + "destinations" : [ + "`nixos-modules/assertions.nix` (new GPU Device eval assertions); `tests/unit/nix/cases/device-gpu-eval.nix` (new Nix eval case); committed settings schema `docs/reference/schemas/v3/providers/device-gpu.settings.json`" + ], + "entryContracts" : [ + "ADR-046-provider-device-gpu" + ], + "exitGate" : "ADR046-W6 exit criteria (ADR-046-validation-and-delivery §4): every spec/work item in this wave Merged with clean destinations, all validators green, and the ten-role panel seal recorded", + "id" : "ADR046-gpu-007", + "kind" : "work-item", + "owner" : "ADR046-gpu-001; ADR 0046 Zone Nix emitter wired; `ADR-046-nix-configuration` Nix emitter present", + "parallelGroup" : "wi:ADR-046-provider-device-gpu", + "prerequisites" : [ + "ADR-046-provider-device-gpu", + "ADR046-gpu-001" + ], + "specId" : "ADR-046-provider-device-gpu", + "topologicalRank" : 12, + "wave" : "W6" + }, + { + "blockers" : [], + "destinations" : [ + "`packages/d2b-provider-device-gpu/` component descriptor; controller/status tests" + ], + "entryContracts" : [ + "ADR-046-provider-device-gpu" + ], + "exitGate" : "ADR046-W6 exit criteria (ADR-046-validation-and-delivery §4): every spec/work item in this wave Merged with clean destinations, all validators green, and the ten-role panel seal recorded", + "id" : "ADR046-gpu-008", + "kind" : "work-item", + "owner" : "ADR046-gpu-001; D087 status-first state model present in the foundational ADR-046 specs", + "parallelGroup" : "wi:ADR-046-provider-device-gpu", + "prerequisites" : [ + "ADR-046-provider-device-gpu", + "ADR046-gpu-001" + ], + "specId" : "ADR-046-provider-device-gpu", + "topologicalRank" : 12, + "wave" : "W6" + }, + { + "blockers" : [], + "destinations" : [ + "`packages/d2b-provider-device-gpu/README.md`" + ], + "entryContracts" : [ + "ADR-046-provider-device-gpu" + ], + "exitGate" : "ADR046-W6 exit criteria (ADR-046-validation-and-delivery §4): every spec/work item in this wave Merged with clean destinations, all validators green, and the ten-role panel seal recorded", + "id" : "ADR046-gpu-009", + "kind" : "work-item", + "owner" : "ADR046-gpu-001", + "parallelGroup" : "wi:ADR-046-provider-device-gpu", + "prerequisites" : [ + "ADR-046-provider-device-gpu", + "ADR046-gpu-001" ], - "specId": "ADR-046-telemetry-audit-and-support", - "topologicalRank": 11, - "wave": "W5" + "specId" : "ADR-046-provider-device-gpu", + "topologicalRank" : 12, + "wave" : "W6" }, { - "blockers": [], - "destinations": [ + "blockers" : [], + "destinations" : [ "`packages/d2b-contracts/src/v3/identity.rs`, `packages/d2b-contracts/src/v3/resource_ref.rs`" ], - "entryContracts": [ + "entryContracts" : [ "ADR-046-terminology-and-identities" ], - "exitGate": "ADR046-W0 exit criteria (ADR-046-validation-and-delivery §4): every spec/work item in this wave Merged with clean destinations, all validators green, and the ten-role panel seal recorded", - "id": "ADR046-identities-001", - "kind": "work-item", - "owner": "W0 shared contract root; `d2b-contracts`", - "parallelGroup": "wi:ADR-046-terminology-and-identities", - "prerequisites": [ + "exitGate" : "ADR046-W0 exit criteria (ADR-046-validation-and-delivery §4): every spec/work item in this wave Merged with clean destinations, all validators green, and the ten-role panel seal recorded", + "id" : "ADR046-identities-001", + "kind" : "work-item", + "owner" : "W0 shared contract root; `d2b-contracts`", + "parallelGroup" : "wi:ADR-046-terminology-and-identities", + "prerequisites" : [ "ADR-046-terminology-and-identities" ], - "specId": "ADR-046-terminology-and-identities", - "topologicalRank": 2, - "wave": "W0" + "specId" : "ADR-046-terminology-and-identities", + "topologicalRank" : 2, + "wave" : "W0" }, { - "blockers": [], - "destinations": [ + "blockers" : [], + "destinations" : [ "`nixos-modules/options-zones.nix`, `nixos-modules/resources.nix`, `nixos-modules/index.nix`" ], - "entryContracts": [ + "entryContracts" : [ "ADR-046-terminology-and-identities" ], - "exitGate": "ADR046-W0 exit criteria (ADR-046-validation-and-delivery §4): every spec/work item in this wave Merged with clean destinations, all validators green, and the ten-role panel seal recorded", - "id": "ADR046-identities-002", - "kind": "work-item", - "owner": "ADR046-identities-001; Nix integrator", - "parallelGroup": "wi:ADR-046-terminology-and-identities", - "prerequisites": [ + "exitGate" : "ADR046-W0 exit criteria (ADR-046-validation-and-delivery §4): every spec/work item in this wave Merged with clean destinations, all validators green, and the ten-role panel seal recorded", + "id" : "ADR046-identities-002", + "kind" : "work-item", + "owner" : "ADR046-identities-001; Nix integrator", + "parallelGroup" : "wi:ADR-046-terminology-and-identities", + "prerequisites" : [ "ADR-046-terminology-and-identities", "ADR046-identities-001" ], - "specId": "ADR-046-terminology-and-identities", - "topologicalRank": 3, - "wave": "W0" - }, - { - "blockers": [], - "destinations": [ - "packages/d2b-provider-credential-managed-identity/src/{controller.rs,agent.rs}; packages/d2b-provider-credential-managed-identity/{controller/main.rs,agent/main.rs}; packages/d2b-provider-credential-managed-identity/tests/topology.rs" - ], - "entryContracts": [ - "ADR-046-provider-credential-managed-identity" - ], - "exitGate": "ADR046-W6 exit criteria (ADR-046-validation-and-delivery §4): every spec/work item in this wave Merged with clean destinations, all validators green, and the ten-role panel seal recorded", - "id": "ADR046-mi-topology-001", - "kind": "work-item", - "owner": "ADR046-credential-001 and ADR046-credential-002; ADR046-cred-mi-001; ADR046-cred-mi-002; owner: credential-managed-identity controller/agent topology", - "parallelGroup": "wi:ADR-046-provider-credential-managed-identity", - "prerequisites": [ - "ADR-046-provider-credential-managed-identity", - "ADR046-credential-001", - "ADR046-credential-002" - ], - "specId": "ADR-046-provider-credential-managed-identity", - "topologicalRank": 11, - "wave": "W6" + "specId" : "ADR-046-terminology-and-identities", + "topologicalRank" : 3, + "wave" : "W0" }, { - "blockers": [], - "destinations": [ + "blockers" : [], + "destinations" : [ "`packages/d2b-provider-system-minijail/src/sandbox_compiler.rs`" ], - "entryContracts": [ + "entryContracts" : [ "ADR-046-provider-system-minijail" ], - "exitGate": "ADR046-W6 exit criteria (ADR-046-validation-and-delivery §4): every spec/work item in this wave Merged with clean destinations, all validators green, and the ten-role panel seal recorded", - "id": "ADR046-minijail-001", - "kind": "work-item", - "owner": "`ADR046-process-001` (common spec/status types); `ADR046-provider-001` (toolkit/contracts); system-minijail Provider owner", - "parallelGroup": "wi:ADR-046-provider-system-minijail", - "prerequisites": [ + "exitGate" : "ADR046-W6 exit criteria (ADR-046-validation-and-delivery §4): every spec/work item in this wave Merged with clean destinations, all validators green, and the ten-role panel seal recorded", + "id" : "ADR046-minijail-001", + "kind" : "work-item", + "owner" : "`ADR046-process-001` (common spec/status types); `ADR046-provider-001` (toolkit/contracts); system-minijail Provider owner", + "parallelGroup" : "wi:ADR-046-provider-system-minijail", + "prerequisites" : [ "ADR-046-provider-system-minijail", "ADR046-process-001", "ADR046-provider-001" ], - "specId": "ADR-046-provider-system-minijail", - "topologicalRank": 11, - "wave": "W6" + "specId" : "ADR-046-provider-system-minijail", + "topologicalRank" : 11, + "wave" : "W6" }, { - "blockers": [], - "destinations": [ + "blockers" : [], + "destinations" : [ "`packages/d2b-provider-system-minijail/src/launch.rs`" ], - "entryContracts": [ + "entryContracts" : [ "ADR-046-provider-system-minijail" ], - "exitGate": "ADR046-W6 exit criteria (ADR-046-validation-and-delivery §4): every spec/work item in this wave Merged with clean destinations, all validators green, and the ten-role panel seal recorded", - "id": "ADR046-minijail-002", - "kind": "work-item", - "owner": "ADR046-minijail-001; common `LaunchTicket` contract", - "parallelGroup": "wi:ADR-046-provider-system-minijail", - "prerequisites": [ + "exitGate" : "ADR046-W6 exit criteria (ADR-046-validation-and-delivery §4): every spec/work item in this wave Merged with clean destinations, all validators green, and the ten-role panel seal recorded", + "id" : "ADR046-minijail-002", + "kind" : "work-item", + "owner" : "ADR046-minijail-001; common `LaunchTicket` contract", + "parallelGroup" : "wi:ADR-046-provider-system-minijail", + "prerequisites" : [ "ADR-046-provider-system-minijail", "ADR046-minijail-001" ], - "specId": "ADR-046-provider-system-minijail", - "topologicalRank": 12, - "wave": "W6" + "specId" : "ADR-046-provider-system-minijail", + "topologicalRank" : 12, + "wave" : "W6" }, { - "blockers": [], - "destinations": [ + "blockers" : [], + "destinations" : [ "Broker-side: `d2b-priv-broker` retains `SpawnRunner` op, invoked by the `MinijailProcessEffectPort` implementation owned by core/ProviderSupervisor; Provider-side: `packages/d2b-provider-system-minijail/src/launch.rs` calls `MinijailProcessEffectPort` with opaque Process/LaunchTicket/profile IDs; `user_ns.rs` implements the user namespace pre-establishment protocol" ], - "entryContracts": [ + "entryContracts" : [ "ADR-046-provider-system-minijail" ], - "exitGate": "ADR046-W6 exit criteria (ADR-046-validation-and-delivery §4): every spec/work item in this wave Merged with clean destinations, all validators green, and the ten-role panel seal recorded", - "id": "ADR046-minijail-003", - "kind": "work-item", - "owner": "ADR046-minijail-001; broker integration owner", - "parallelGroup": "wi:ADR-046-provider-system-minijail", - "prerequisites": [ + "exitGate" : "ADR046-W6 exit criteria (ADR-046-validation-and-delivery §4): every spec/work item in this wave Merged with clean destinations, all validators green, and the ten-role panel seal recorded", + "id" : "ADR046-minijail-003", + "kind" : "work-item", + "owner" : "ADR046-minijail-001; broker integration owner", + "parallelGroup" : "wi:ADR-046-provider-system-minijail", + "prerequisites" : [ "ADR-046-provider-system-minijail", "ADR046-minijail-001" ], - "specId": "ADR-046-provider-system-minijail", - "topologicalRank": 12, - "wave": "W6" + "specId" : "ADR-046-provider-system-minijail", + "topologicalRank" : 12, + "wave" : "W6" }, { - "blockers": [], - "destinations": [ - "`packages/d2b-provider-system-minijail/src/pidfd.rs`; `packages/d2b-provider-system-minijail/src/wait.rs`" + "blockers" : [], + "destinations" : [ + "Broker-side parent wait/reap and typed terminal relay in `packages/d2b-priv-broker/src/`; non-parent observation/status consumption in `packages/d2b-provider-system-minijail/src/{pidfd,wait}.rs`" ], - "entryContracts": [ + "entryContracts" : [ "ADR-046-provider-system-minijail" ], - "exitGate": "ADR046-W6 exit criteria (ADR-046-validation-and-delivery §4): every spec/work item in this wave Merged with clean destinations, all validators green, and the ten-role panel seal recorded", - "id": "ADR046-minijail-004", - "kind": "work-item", - "owner": "ADR046-minijail-003; wait/pidfd owner", - "parallelGroup": "wi:ADR-046-provider-system-minijail", - "prerequisites": [ + "exitGate" : "ADR046-W6 exit criteria (ADR-046-validation-and-delivery §4): every spec/work item in this wave Merged with clean destinations, all validators green, and the ten-role panel seal recorded", + "id" : "ADR046-minijail-004", + "kind" : "work-item", + "owner" : "ADR046-minijail-003; wait/pidfd owner", + "parallelGroup" : "wi:ADR-046-provider-system-minijail", + "prerequisites" : [ "ADR-046-provider-system-minijail", "ADR046-minijail-003" ], - "specId": "ADR-046-provider-system-minijail", - "topologicalRank": 13, - "wave": "W6" + "specId" : "ADR-046-provider-system-minijail", + "topologicalRank" : 13, + "wave" : "W6" }, { - "blockers": [], - "destinations": [ + "blockers" : [], + "destinations" : [ "`packages/d2b-provider-system-minijail/src/` — controller binary entry point; reconcile loop; adoption; quarantine; bootstrap authz; health/status; restart; finalize" ], - "entryContracts": [ + "entryContracts" : [ "ADR-046-provider-system-minijail" ], - "exitGate": "ADR046-W6 exit criteria (ADR-046-validation-and-delivery §4): every spec/work item in this wave Merged with clean destinations, all validators green, and the ten-role panel seal recorded", - "id": "ADR046-minijail-005", - "kind": "work-item", - "owner": "All of ADR046-minijail-001 through ADR046-minijail-004; ComponentSession/d2b-bus (ADR046-session-001, ADR046-bus-001); bootstrap authz", - "parallelGroup": "wi:ADR-046-provider-system-minijail", - "prerequisites": [ + "exitGate" : "ADR046-W6 exit criteria (ADR-046-validation-and-delivery §4): every spec/work item in this wave Merged with clean destinations, all validators green, and the ten-role panel seal recorded", + "id" : "ADR046-minijail-005", + "kind" : "work-item", + "owner" : "All of ADR046-minijail-001 through ADR046-minijail-004; ComponentSession/d2b-bus (ADR046-session-001, ADR046-session-003); bootstrap authz", + "parallelGroup" : "wi:ADR-046-provider-system-minijail", + "prerequisites" : [ "ADR-046-provider-system-minijail", - "ADR046-bus-001", "ADR046-minijail-001", + "ADR046-minijail-002", + "ADR046-minijail-003", "ADR046-minijail-004", - "ADR046-session-001" + "ADR046-session-001", + "ADR046-session-003" ], - "specId": "ADR-046-provider-system-minijail", - "topologicalRank": 14, - "wave": "W6" + "specId" : "ADR-046-provider-system-minijail", + "topologicalRank" : 14, + "wave" : "W6" }, { - "blockers": [], - "destinations": [ + "blockers" : [], + "destinations" : [ "`nixos-modules/` — v3 Nix `Process`/`EphemeralProcess` resource authoring; Provider catalog entry; `docs/reference/schemas/v3/Process.json`; `docs/reference/schemas/v3/EphemeralProcess.json`; `make test-drift` schema drift gate" ], - "entryContracts": [ + "entryContracts" : [ "ADR-046-provider-system-minijail" ], - "exitGate": "ADR046-W6 exit criteria (ADR-046-validation-and-delivery §4): every spec/work item in this wave Merged with clean destinations, all validators green, and the ten-role panel seal recorded", - "id": "ADR046-minijail-006", - "kind": "work-item", - "owner": "ADR046-minijail-005; Nix integrator; test infrastructure owner", - "parallelGroup": "wi:ADR-046-provider-system-minijail", - "prerequisites": [ + "exitGate" : "ADR046-W6 exit criteria (ADR-046-validation-and-delivery §4): every spec/work item in this wave Merged with clean destinations, all validators green, and the ten-role panel seal recorded", + "id" : "ADR046-minijail-006", + "kind" : "work-item", + "owner" : "ADR046-minijail-005; Nix integrator; test infrastructure owner", + "parallelGroup" : "wi:ADR-046-provider-system-minijail", + "prerequisites" : [ "ADR-046-provider-system-minijail", "ADR046-minijail-005" ], - "specId": "ADR-046-provider-system-minijail", - "topologicalRank": 15, - "wave": "W6" + "specId" : "ADR-046-provider-system-minijail", + "topologicalRank" : 15, + "wave" : "W6" }, { - "blockers": [], - "destinations": [ + "blockers" : [], + "destinations" : [ "`packages/d2b-contracts/src/v3/network.rs`: NetworkSpec, NetworkStatus, AttachmentSpec, AttachmentStatus, ExternalAttachmentSpec, ExternalAttachmentStatus, PortForwardSpec, NetworkConditionType; `packages/d2b-contracts/src/v3/ifname.rs`: IfName newtype, derivation, collision detection (extracted from `d2b-host/src/ifname.rs`). Also defines `User/net-local-controller` as a proper Resource with explicit lifecycle: `Provider/network-local`'s Nix package/module provisions the reserved `net-local-controller` OS account with a private fixed UID/GID in Host prerequisites and in the generic net-VM nixos-system artifact (same account, same UID/GID inside the Guest); the network-local controller creates and owns the User Resource (`spec.osUsername: net-local-controller`, `ownerRef: Provider/network-local`, `managedBy: controller`); `Provider/system-core` verifies the account via NSS lookup and reconciles the User Resource to Ready — it does not provision the OS account. No numeric UID/GID enters any ResourceSpec field, authz check, or audit record; `User.status` MAY carry diagnostic `uid`/`gid` values discovered by NSS lookup, but those are informational only and are never authorization inputs. The network-local controller waits for `User/net-local-controller` to reach `Ready` before creating any config Volume (reconcile precondition, not a bootstrap side effect)." ], - "entryContracts": [ + "entryContracts" : [ "ADR-046-resources-network" ], - "exitGate": "ADR046-W4 exit criteria (ADR-046-validation-and-delivery §4): every spec/work item in this wave Merged with clean destinations, all validators green, and the ten-role panel seal recorded", - "id": "ADR046-network-001", - "kind": "work-item", - "owner": "W0 shared contract root; `d2b-contracts`", - "parallelGroup": "wi:ADR-046-resources-network", - "prerequisites": [ + "exitGate" : "ADR046-W4 exit criteria (ADR-046-validation-and-delivery §4): every spec/work item in this wave Merged with clean destinations, all validators green, and the ten-role panel seal recorded", + "id" : "ADR046-network-001", + "kind" : "work-item", + "owner" : "W0 shared contract root; `d2b-contracts`", + "parallelGroup" : "wi:ADR-046-resources-network", + "prerequisites" : [ "ADR-046-resources-network" ], - "specId": "ADR-046-resources-network", - "topologicalRank": 9, - "wave": "W4" + "specId" : "ADR-046-resources-network", + "topologicalRank" : 9, + "wave" : "W4" }, { - "blockers": [], - "destinations": [ + "blockers" : [], + "destinations" : [ "`packages/d2b-provider-network-local/src/ifname.rs`, `bridge_port.rs`, `nftables.rs`, `routes.rs`, `netlink.rs`" ], - "entryContracts": [ + "entryContracts" : [ "ADR-046-resources-network" ], - "exitGate": "ADR046-W4 exit criteria (ADR-046-validation-and-delivery §4): every spec/work item in this wave Merged with clean destinations, all validators green, and the ten-role panel seal recorded", - "id": "ADR046-network-002", - "kind": "work-item", - "owner": "ADR046-network-001; d2b-host network modules owner", - "parallelGroup": "wi:ADR-046-resources-network", - "prerequisites": [ + "exitGate" : "ADR046-W4 exit criteria (ADR-046-validation-and-delivery §4): every spec/work item in this wave Merged with clean destinations, all validators green, and the ten-role panel seal recorded", + "id" : "ADR046-network-002", + "kind" : "work-item", + "owner" : "ADR046-network-001; d2b-host network modules owner", + "parallelGroup" : "wi:ADR-046-resources-network", + "prerequisites" : [ "ADR-046-resources-network", "ADR046-network-001" ], - "specId": "ADR-046-resources-network", - "topologicalRank": 10, - "wave": "W4" + "specId" : "ADR-046-resources-network", + "topologicalRank" : 10, + "wave" : "W4" }, { - "blockers": [], - "destinations": [ + "blockers" : [], + "destinations" : [ "`packages/d2b-provider-network-local/` — artifact catalog integration for net-VM nixos-system artifact resolution; `packages/d2b-provider-network-local/nix/` — default net-VM NixOS module (parameterized successor to net.nix), built and registered as a nixos-system artifact in `d2b.artifacts`" ], - "entryContracts": [ + "entryContracts" : [ "ADR-046-resources-network" ], - "exitGate": "ADR046-W4 exit criteria (ADR-046-validation-and-delivery §4): every spec/work item in this wave Merged with clean destinations, all validators green, and the ten-role panel seal recorded", - "id": "ADR046-network-003", - "kind": "work-item", - "owner": "ADR046-network-001, ADR046-network-002; Provider/runtime-cloud-hypervisor dossier owner", - "parallelGroup": "wi:ADR-046-resources-network", - "prerequisites": [ + "exitGate" : "ADR046-W4 exit criteria (ADR-046-validation-and-delivery §4): every spec/work item in this wave Merged with clean destinations, all validators green, and the ten-role panel seal recorded", + "id" : "ADR046-network-003", + "kind" : "work-item", + "owner" : "ADR046-network-001, ADR046-network-002; Provider/runtime-cloud-hypervisor dossier owner", + "parallelGroup" : "wi:ADR-046-resources-network", + "prerequisites" : [ "ADR-046-resources-network", "ADR046-network-001", "ADR046-network-002" ], - "specId": "ADR-046-resources-network", - "topologicalRank": 11, - "wave": "W4" + "specId" : "ADR-046-resources-network", + "topologicalRank" : 11, + "wave" : "W4" }, { - "blockers": [], - "destinations": [ + "blockers" : [], + "destinations" : [ "`nixos-modules/resources-network.nix`: Nix resource object emitter for Network ResourceType; `nixos-modules/index.nix`: network resource compilation section" ], - "entryContracts": [ + "entryContracts" : [ "ADR-046-resources-network" ], - "exitGate": "ADR046-W4 exit criteria (ADR-046-validation-and-delivery §4): every spec/work item in this wave Merged with clean destinations, all validators green, and the ten-role panel seal recorded", - "id": "ADR046-network-004", - "kind": "work-item", - "owner": "ADR046-network-001, ADR046-network-002, ADR046-network-003; Nix integrator", - "parallelGroup": "wi:ADR-046-resources-network", - "prerequisites": [ + "exitGate" : "ADR046-W4 exit criteria (ADR-046-validation-and-delivery §4): every spec/work item in this wave Merged with clean destinations, all validators green, and the ten-role panel seal recorded", + "id" : "ADR046-network-004", + "kind" : "work-item", + "owner" : "ADR046-network-001, ADR046-network-002, ADR046-network-003; Nix integrator", + "parallelGroup" : "wi:ADR-046-resources-network", + "prerequisites" : [ "ADR-046-resources-network", "ADR046-network-001", "ADR046-network-002", "ADR046-network-003" ], - "specId": "ADR-046-resources-network", - "topologicalRank": 12, - "wave": "W4" + "specId" : "ADR-046-resources-network", + "topologicalRank" : 12, + "wave" : "W4" }, { - "blockers": [], - "destinations": [ + "blockers" : [], + "destinations" : [ "`packages/d2b-provider-network-local/src/controller.rs`: async NetworkReconciler; `packages/d2b-provider-network-local/src/plan.rs`: ReconcilePlan computation; `packages/d2b-provider-network-local/src/observe.rs`: drift-detection observe loop. Full crate layout required (see [Package and crate boundary](#package-and-crate-boundary)): `src/` (controller/plan/observe + colocated unit tests), `tests/` (hermetic conformance and state-machine tests), `integration/` (provider-system reconcile fixtures), `README.md` (Network ResourceType, controller binary, placement, RBAC, security invariants, build/test/integration commands)." ], - "entryContracts": [ - "ADR-046-resources-network" - ], - "exitGate": "ADR046-W4 exit criteria (ADR-046-validation-and-delivery §4): every spec/work item in this wave Merged with clean destinations, all validators green, and the ten-role panel seal recorded", - "id": "ADR046-network-005", - "kind": "work-item", - "owner": "ADR046-network-001–004; network-local controller owner; D-NETWORK-001, D-NETWORK-002, and D-NETWORK-003 resolved", - "parallelGroup": "wi:ADR-046-resources-network", - "prerequisites": [ - "ADR-046-resources-network", - "ADR046-network-001" - ], - "specId": "ADR-046-resources-network", - "topologicalRank": 10, - "wave": "W4" - }, - { - "blockers": [], - "destinations": [ - "`tests/unit/nix/cases/net-vm-network.nix` (adapted to v3 resource API); updated golden pins; `tests/host-integration/bridge-isolation.nix` (adapted); `packages/d2b-priv-broker/tests/bridge_lifecycle.rs` (new hermetic broker tests). Provider crate test directories: `packages/d2b-provider-network-local/tests/` — hermetic Cargo integration tests (conformance suite, controller state machine, CIDR validation vectors, IfName determinism, invariant tests INV-NET-001–007, reconcile/observe/finalize with deterministic clock, fault injection); `packages/d2b-provider-network-local/integration/` — container/Host/Guest lifecycle fixtures invoked by `make test-integration` (bridge isolation, east-west double opt-in, nftables drift detection, macvtap lifecycle). Both directories required by package policy." - ], - "entryContracts": [ - "ADR-046-resources-network" - ], - "exitGate": "ADR046-W4 exit criteria (ADR-046-validation-and-delivery §4): every spec/work item in this wave Merged with clean destinations, all validators green, and the ten-role panel seal recorded", - "id": "ADR046-network-006", - "kind": "work-item", - "owner": "ADR046-network-001, ADR046-network-005; test owner", - "parallelGroup": "wi:ADR-046-resources-network", - "prerequisites": [ - "ADR-046-resources-network", - "ADR046-network-001", - "ADR046-network-005" - ], - "specId": "ADR-046-resources-network", - "topologicalRank": 11, - "wave": "W4" - }, - { - "blockers": [], - "destinations": [ - "Network controller exposes `status.network.hostUplinkIp` and `status.network.uplinkBridge.ifName` as read-only dependency inputs for `Provider/device-usbip`. `packages/d2b-provider-device-usbip/src/controller.rs` reads these through a ResourceClient `networkRef` dependency watch and issues the closed `UsbipBindFirewallRule` broker operation for the TCP/3240 carve-out rule. Network spec is not mutated by device-usbip. Full crate layout required for `packages/d2b-provider-device-usbip/` (see [Package and crate boundary](#package-and-crate-boundary)): `src/` (controller and usbip runner + unit tests), `tests/` (hermetic conformance, dependency-watch state machine, UsbipBindFirewallRule round-trip), `integration/` (Host/Guest USBIP attach/detach lifecycle fixtures), `README.md` (Provider identity, UsbipDevice ResourceType, USBIP daemon Process, Network read-only dependency contract, RBAC, security invariants, standalone-repo path)." - ], - "entryContracts": [ - "ADR-046-resources-network" - ], - "exitGate": "ADR046-W4 exit criteria (ADR-046-validation-and-delivery §4): every spec/work item in this wave Merged with clean destinations, all validators green, and the ten-role panel seal recorded", - "id": "ADR046-network-007", - "kind": "work-item", - "owner": "ADR046-network-005; device-usbip Provider dossier; D-NETWORK-002 resolved", - "parallelGroup": "wi:ADR-046-resources-network", - "prerequisites": [ - "ADR-046-resources-network", - "ADR046-network-005" - ], - "specId": "ADR-046-resources-network", - "topologicalRank": 11, - "wave": "W4" - }, - { - "blockers": [], - "destinations": [ - "`packages/d2b-core-controller/src/configuration.rs`: bundle application, diff, generation-transition logic (including per-item name-conflict handling), prior-bundle retention under `/var/lib/d2b/zones//configuration/prior/`; `packages/d2b-core-controller/src/cleanup.rs`: removal scheduling and `PendingCleanup` condition tracking; `packages/d2b-contracts/src/generation_bundle.rs`: `ZoneBundle`/`BundleResource`/`BundleMetadata` **input** DTOs — MUST NOT include `managedBy` or `configurationGeneration` (both are persisted resource metadata set by core at activation, not bundle input fields); `ManagedBy` closed enum `{ Configuration, Controller, Api }` and `configurationGeneration: u64` live in `packages/d2b-core-controller/src/resource_store.rs` as persisted resource metadata fields; `nixos-modules/resources-network.nix` (emits bundle with `managedBy`/`configurationGeneration` absent; core sets both at activation per ADR046-network-004); `d2b.zones..retainedGenerations` Nix/compiler-level Zone option (outside `Zone.spec`; default `3`, range `1..16`); `tests/unit/nix/cases/generation-cleanup-absent-network.nix`; `packages/d2b-contracts/tests/generation_bundle.rs`; `tests/host-integration/nix-generation-cleanup.nix`" - ], - "entryContracts": [ - "ADR-046-resources-network" - ], - "exitGate": "ADR046-W4 exit criteria (ADR-046-validation-and-delivery §4): every spec/work item in this wave Merged with clean destinations, all validators green, and the ten-role panel seal recorded", - "id": "ADR046-network-008", - "kind": "work-item", - "owner": "ADR046-network-004, ADR046-network-005; Zone runtime integrator", - "parallelGroup": "wi:core-config-hub", - "prerequisites": [ - "ADR-046-resources-network", - "ADR046-core-001", - "ADR046-network-004", - "ADR046-network-005" - ], - "specId": "ADR-046-resources-network", - "topologicalRank": 13, - "wave": "W4" - }, - { - "blockers": [], - "destinations": [ - "`packages/d2b-provider-network-local/src/process_specs.rs`." - ], - "entryContracts": [ - "ADR-046-provider-network-local" - ], - "exitGate": "ADR046-W6 exit criteria (ADR-046-validation-and-delivery §4): every spec/work item in this wave Merged with clean destinations, all validators green, and the ten-role panel seal recorded", - "id": "ADR046-network-009", - "kind": "work-item", - "owner": "Provider; Process resource builders owned by `d2b-provider-network-local`.", - "parallelGroup": "wi:ADR-046-provider-network-local", - "prerequisites": [ - "ADR-046-provider-network-local" - ], - "specId": "ADR-046-provider-network-local", - "topologicalRank": 11, - "wave": "W6" - }, - { - "blockers": [], - "destinations": [ - "`net-vm-base` nixos-system artifact and artifact catalog entry `d2b.artifacts.net-vm-base`." - ], - "entryContracts": [ - "ADR-046-provider-network-local" - ], - "exitGate": "ADR046-W6 exit criteria (ADR-046-validation-and-delivery §4): every spec/work item in this wave Merged with clean destinations, all validators green, and the ten-role panel seal recorded", - "id": "ADR046-network-010", - "kind": "work-item", - "owner": "net-vm artifact; owns generic `net-vm-base` nixos-system artifact and shared net-local-controller UID/GID reservation.", - "parallelGroup": "wi:ADR-046-provider-network-local", - "prerequisites": [ - "ADR-046-provider-network-local" - ], - "specId": "ADR-046-provider-network-local", - "topologicalRank": 11, - "wave": "W6" - }, - { - "blockers": [], - "destinations": [ - "Nix module resource emission for `Provider/network-local`, `User/net-local-controller`, host OS account, `provider-network-local`, and `net-vm-base` artifacts." - ], - "entryContracts": [ - "ADR-046-provider-network-local" - ], - "exitGate": "ADR046-W6 exit criteria (ADR-046-validation-and-delivery §4): every spec/work item in this wave Merged with clean destinations, all validators green, and the ten-role panel seal recorded", - "id": "ADR046-network-011", - "kind": "work-item", - "owner": "Nix; owns resource declaration, User declaration, host account provisioning, and artifact catalog wiring.", - "parallelGroup": "wi:ADR-046-provider-network-local", - "prerequisites": [ - "ADR-046-provider-network-local" - ], - "specId": "ADR-046-provider-network-local", - "topologicalRank": 11, - "wave": "W6" - }, - { - "blockers": [], - "destinations": [ - "Nix flake/resource schema checks for declared Networks and provider `validate.rs` parity." - ], - "entryContracts": [ - "ADR-046-provider-network-local" - ], - "exitGate": "ADR046-W6 exit criteria (ADR-046-validation-and-delivery §4): every spec/work item in this wave Merged with clean destinations, all validators green, and the ten-role panel seal recorded", - "id": "ADR046-network-012", - "kind": "work-item", - "owner": "Nix; depends on Network resource schema and CIDR validation rules.", - "parallelGroup": "wi:ADR-046-provider-network-local", - "prerequisites": [ - "ADR-046-provider-network-local" - ], - "specId": "ADR-046-provider-network-local", - "topologicalRank": 11, - "wave": "W6" - }, - { - "blockers": [], - "destinations": [ - "`packages/d2b-provider-network-local/tests/schema_roundtrip.rs`, `tests/ifname_derive.rs`, and `tests/cidr_overlap.rs`." - ], - "entryContracts": [ - "ADR-046-provider-network-local" - ], - "exitGate": "ADR046-W6 exit criteria (ADR-046-validation-and-delivery §4): every spec/work item in this wave Merged with clean destinations, all validators green, and the ten-role panel seal recorded", - "id": "ADR046-network-013", - "kind": "work-item", - "owner": "Tests; owned by `d2b-provider-network-local` hermetic test suite.", - "parallelGroup": "wi:ADR-046-provider-network-local", - "prerequisites": [ - "ADR-046-provider-network-local" - ], - "specId": "ADR-046-provider-network-local", - "topologicalRank": 11, - "wave": "W6" - }, - { - "blockers": [], - "destinations": [ - "`packages/d2b-provider-network-local/tests/controller_state.rs`." - ], - "entryContracts": [ - "ADR-046-provider-network-local" - ], - "exitGate": "ADR046-W6 exit criteria (ADR-046-validation-and-delivery §4): every spec/work item in this wave Merged with clean destinations, all validators green, and the ten-role panel seal recorded", - "id": "ADR046-network-014", - "kind": "work-item", - "owner": "Tests; depends on ADR046-nl-006 controller and fake `NetworkEffectPort` from `d2b-contracts`.", - "parallelGroup": "wi:ADR-046-provider-network-local", - "prerequisites": [ - "ADR-046-provider-network-local", - "ADR046-nl-006" - ], - "specId": "ADR-046-provider-network-local", - "topologicalRank": 13, - "wave": "W6" - }, - { - "blockers": [], - "destinations": [ - "`packages/d2b-provider-network-local/integration/host_fabric.rs`, `guest_lifecycle.rs`, `agent_reload.rs`, and `delete_sequence.rs`." - ], - "entryContracts": [ - "ADR-046-provider-network-local" + "entryContracts" : [ + "ADR-046-resources-network" ], - "exitGate": "ADR046-W6 exit criteria (ADR-046-validation-and-delivery §4): every spec/work item in this wave Merged with clean destinations, all validators green, and the ten-role panel seal recorded", - "id": "ADR046-network-015", - "kind": "work-item", - "owner": "Tests; integration coverage for the complete Network lifecycle.", - "parallelGroup": "wi:ADR-046-provider-network-local", - "prerequisites": [ - "ADR-046-provider-network-local" + "exitGate" : "ADR046-W4 exit criteria (ADR-046-validation-and-delivery §4): every spec/work item in this wave Merged with clean destinations, all validators green, and the ten-role panel seal recorded", + "id" : "ADR046-network-005", + "kind" : "work-item", + "owner" : "ADR046-network-001–004; network-local controller owner; D-NETWORK-001, D-NETWORK-002, and D-NETWORK-003 resolved", + "parallelGroup" : "wi:ADR-046-resources-network", + "prerequisites" : [ + "ADR-046-resources-network", + "ADR046-network-001" ], - "specId": "ADR-046-provider-network-local", - "topologicalRank": 11, - "wave": "W6" + "specId" : "ADR-046-resources-network", + "topologicalRank" : 10, + "wave" : "W4" }, { - "blockers": [], - "destinations": [ - "Process templates for agent and dnsmasq plus sandbox/eval tests." + "blockers" : [], + "destinations" : [ + "`tests/unit/nix/cases/net-vm-network.nix` (adapted to v3 resource API); updated golden pins; `tests/host-integration/bridge-isolation.nix` (adapted); `packages/d2b-priv-broker/tests/bridge_lifecycle.rs` (new hermetic broker tests). Provider crate test directories: `packages/d2b-provider-network-local/tests/` — hermetic Cargo integration tests (conformance suite, controller state machine, CIDR validation vectors, IfName determinism, invariant tests INV-NET-001–007, reconcile/observe/finalize with deterministic clock, fault injection); `packages/d2b-provider-network-local/integration/` — container/Host/Guest lifecycle fixtures invoked by `make test-integration` (bridge isolation, east-west double opt-in, nftables drift detection, macvtap lifecycle). Both directories required by package policy." ], - "entryContracts": [ - "ADR-046-provider-network-local" + "entryContracts" : [ + "ADR-046-resources-network" ], - "exitGate": "ADR046-W6 exit criteria (ADR-046-validation-and-delivery §4): every spec/work item in this wave Merged with clean destinations, all validators green, and the ten-role panel seal recorded", - "id": "ADR046-network-016", - "kind": "work-item", - "owner": "Security; depends on Process sandbox schema and `Provider/system-minijail` guest namespace inheritance.", - "parallelGroup": "wi:ADR-046-provider-network-local", - "prerequisites": [ - "ADR-046-provider-network-local" + "exitGate" : "ADR046-W4 exit criteria (ADR-046-validation-and-delivery §4): every spec/work item in this wave Merged with clean destinations, all validators green, and the ten-role panel seal recorded", + "id" : "ADR046-network-006", + "kind" : "work-item", + "owner" : "ADR046-network-001, ADR046-network-005; test owner", + "parallelGroup" : "wi:ADR-046-resources-network", + "prerequisites" : [ + "ADR-046-resources-network", + "ADR046-network-001", + "ADR046-network-005" ], - "specId": "ADR-046-provider-network-local", - "topologicalRank": 11, - "wave": "W6" + "specId" : "ADR-046-resources-network", + "topologicalRank" : 11, + "wave" : "W4" }, { - "blockers": [], - "destinations": [ - "`packages/d2b-provider-network-local/README.md`." + "blockers" : [], + "destinations" : [ + "`Provider/device-usbip` owns one relay Process/Endpoint authority per Network plus the typed EffectPort adapter for `UsbipBindFirewallRule`. The controller watches only the `networkRef` resource's identity/readiness/generation; Core privately resolves Network UID to relay attachment and firewall intent. Network spec/status is not mutated with USBIP fields. Full crate layout required for `packages/d2b-provider-device-usbip/` (see [Package and crate boundary](#package-and-crate-boundary)): `src/` (controller and usbip runner + unit tests), `tests/` (hermetic conformance, dependency-watch state machine, UsbipBindFirewallRule round-trip), `integration/` (Host/Guest USBIP attach/detach lifecycle fixtures), `README.md` (Provider identity, provider-neutral USB Service/Binding types, USBIP Processes/Endpoints, Network least-privilege dependency contract, RBAC, security invariants, standalone-repo path)." ], - "entryContracts": [ - "ADR-046-provider-network-local" + "entryContracts" : [ + "ADR-046-resources-network" ], - "exitGate": "ADR046-W6 exit criteria (ADR-046-validation-and-delivery §4): every spec/work item in this wave Merged with clean destinations, all validators green, and the ten-role panel seal recorded", - "id": "ADR046-network-017", - "kind": "work-item", - "owner": "Docs; owned by `d2b-provider-network-local` package documentation.", - "parallelGroup": "wi:ADR-046-provider-network-local", - "prerequisites": [ - "ADR-046-provider-network-local" + "exitGate" : "ADR046-W4 exit criteria (ADR-046-validation-and-delivery §4): every spec/work item in this wave Merged with clean destinations, all validators green, and the ten-role panel seal recorded", + "id" : "ADR046-network-007", + "kind" : "work-item", + "owner" : "ADR046-network-005; device-usbip Provider dossier; D-NETWORK-002 resolved", + "parallelGroup" : "wi:ADR-046-resources-network", + "prerequisites" : [ + "ADR-046-resources-network", + "ADR046-network-005" ], - "specId": "ADR-046-provider-network-local", - "topologicalRank": 11, - "wave": "W6" + "specId" : "ADR-046-resources-network", + "topologicalRank" : 11, + "wave" : "W4" }, { - "blockers": [], - "destinations": [ - "Integration/boundary tests for `Provider/network-local` and `Provider/device-usbip`; no new network-local broker op destination." + "blockers" : [], + "destinations" : [ + "`packages/d2b-core-controller/src/configuration.rs`: bundle application, diff, generation-transition logic (including per-item name-conflict handling), prior-bundle retention under `/var/lib/d2b/zones//configuration/prior/`; `packages/d2b-core-controller/src/cleanup.rs`: removal scheduling and `PendingCleanup` condition tracking; `packages/d2b-contracts/src/generation_bundle.rs`: `ZoneBundle`/`BundleResource`/`BundleMetadata` **input** DTOs — MUST NOT include `managedBy` or `configurationGeneration` (both are persisted resource metadata set by core at activation, not bundle input fields); `ManagedBy` closed enum `{ Configuration, Controller, Api }` and `configurationGeneration: u64` live in `packages/d2b-core-controller/src/resource_store.rs` as persisted resource metadata fields; `nixos-modules/resources-network.nix` (emits bundle with `managedBy`/`configurationGeneration` absent; core sets both at activation per ADR046-network-004); `d2b.zones..retainedGenerations` Nix/compiler-level Zone option (outside `Zone.spec`; default `3`, range `1..16`); `tests/unit/nix/cases/generation-cleanup-absent-network.nix`; `packages/d2b-contracts/tests/generation_bundle.rs`; `tests/host-integration/nix-generation-cleanup.nix`" ], - "entryContracts": [ - "ADR-046-provider-network-local" + "entryContracts" : [ + "ADR-046-resources-network" ], - "exitGate": "ADR046-W6 exit criteria (ADR-046-validation-and-delivery §4): every spec/work item in this wave Merged with clean destinations, all validators green, and the ten-role panel seal recorded", - "id": "ADR046-network-018", - "kind": "work-item", - "owner": "Broker plus device provider boundary; `UsbipBindFirewallRule` remains owned by `Provider/device-usbip`.", - "parallelGroup": "wi:ADR-046-provider-network-local", - "prerequisites": [ - "ADR-046-provider-network-local" + "exitGate" : "ADR046-W4 exit criteria (ADR-046-validation-and-delivery §4): every spec/work item in this wave Merged with clean destinations, all validators green, and the ten-role panel seal recorded", + "id" : "ADR046-network-008", + "kind" : "work-item", + "owner" : "ADR046-network-004, ADR046-network-005; Zone runtime integrator", + "parallelGroup" : "wi:core-config-hub:w4", + "prerequisites" : [ + "ADR-046-resources-network", + "ADR046-network-004", + "ADR046-network-005" ], - "specId": "ADR-046-provider-network-local", - "topologicalRank": 11, - "wave": "W6" + "specId" : "ADR-046-resources-network", + "topologicalRank" : 13, + "wave" : "W4" }, { - "blockers": [], - "destinations": [ - "Provider descriptor, controller-main deployment, `tests/state_schema_roundtrip.rs`, and eval case `provider-state-volume-eval.nix`." + "blockers" : [], + "destinations" : [ + "`packages/d2b-contracts/src/v3/network.rs` external-attachment sharing schema/status; `packages/d2b-core-controller/src/authority.rs` Core-derived physical-NIC identity and Host-global claim; Provider/network-local descriptor/reconcile/finalizer" ], - "entryContracts": [ - "ADR-046-provider-network-local" + "entryContracts" : [ + "ADR-046-resources-network" ], - "exitGate": "ADR046-W6 exit criteria (ADR-046-validation-and-delivery §4): every spec/work item in this wave Merged with clean destinations, all validators green, and the ten-role panel seal recorded", - "id": "ADR046-network-019", - "kind": "work-item", - "owner": "Provider; depends on D087 ProviderStateSet and status-first storage rules.", - "parallelGroup": "wi:ADR-046-provider-network-local", - "prerequisites": [ - "ADR-046-provider-network-local" + "exitGate" : "ADR046-W4 exit criteria (ADR-046-validation-and-delivery §4): every spec/work item in this wave Merged with clean destinations, all validators green, and the ten-role panel seal recorded", + "id" : "ADR046-network-009", + "kind" : "work-item", + "owner" : "D097 Host-global authority index; ADR046-network-001, ADR046-network-005; Provider/network-local and Core authority owners", + "parallelGroup" : "wi:ADR-046-resources-network", + "prerequisites" : [ + "ADR-046-resources-network", + "ADR046-network-001", + "ADR046-network-005" ], - "specId": "ADR-046-provider-network-local", - "topologicalRank": 11, - "wave": "W6" + "specId" : "ADR-046-resources-network", + "topologicalRank" : 11, + "wave" : "W4" }, { - "blockers": [], - "destinations": [ - "`nixos-modules/options-zones.nix` (Zone-level options: `label`, `retainedGenerations`, `trustedPublishers` — compiler settings, not Zone spec fields); `nixos-modules/options-zones-resources.nix` (unified `resources` attrset)" + "blockers" : [], + "destinations" : [ + "`nixos-modules/options-zones.nix` (Zone-level options: `label`, `parentZone`, `retainedGenerations`, `trustedPublishers` — compiler settings, not Zone spec fields); `nixos-modules/options-zones-resources.nix` (unified `resources` attrset)" ], - "entryContracts": [ + "entryContracts" : [ "ADR-046-nix-configuration" ], - "exitGate": "ADR046-W5 exit criteria (ADR-046-validation-and-delivery §4): every spec/work item in this wave Merged with clean destinations, all validators green, and the ten-role panel seal recorded", - "id": "ADR046-nix-001", - "kind": "work-item", - "owner": "W0; `d2b-contracts` identities (ADR046-identities-001, ADR046-identities-002)", - "parallelGroup": "wi:ADR-046-nix-configuration", - "prerequisites": [ + "exitGate" : "ADR046-W5 exit criteria (ADR-046-validation-and-delivery §4): every spec/work item in this wave Merged with clean destinations, all validators green, and the ten-role panel seal recorded", + "id" : "ADR046-nix-001", + "kind" : "work-item", + "owner" : "W0; `d2b-contracts` identities (ADR046-identities-001, ADR046-identities-002)", + "parallelGroup" : "wi:ADR-046-nix-configuration", + "prerequisites" : [ "ADR-046-nix-configuration", "ADR046-identities-001", "ADR046-identities-002" ], - "specId": "ADR-046-nix-configuration", - "topologicalRank": 10, - "wave": "W5" + "specId" : "ADR-046-nix-configuration", + "topologicalRank" : 10, + "wave" : "W5" }, { - "blockers": [], - "destinations": [ + "blockers" : [], + "destinations" : [ "`Network` resource fields in `nixos-modules/options-zones-resources.nix`; `Guest` resource fields" ], - "entryContracts": [ + "entryContracts" : [ "ADR-046-nix-configuration" ], - "exitGate": "ADR046-W5 exit criteria (ADR-046-validation-and-delivery §4): every spec/work item in this wave Merged with clean destinations, all validators green, and the ten-role panel seal recorded", - "id": "ADR046-nix-002", - "kind": "work-item", - "owner": "ADR046-nix-001; env/VM migration", - "parallelGroup": "wi:ADR-046-nix-configuration", - "prerequisites": [ + "exitGate" : "ADR046-W5 exit criteria (ADR-046-validation-and-delivery §4): every spec/work item in this wave Merged with clean destinations, all validators green, and the ten-role panel seal recorded", + "id" : "ADR046-nix-002", + "kind" : "work-item", + "owner" : "ADR046-nix-001; env/VM migration", + "parallelGroup" : "wi:ADR-046-nix-configuration", + "prerequisites" : [ "ADR-046-nix-configuration", "ADR046-nix-001" ], - "specId": "ADR-046-nix-configuration", - "topologicalRank": 11, - "wave": "W5" + "specId" : "ADR-046-nix-configuration", + "topologicalRank" : 11, + "wave" : "W5" }, { - "blockers": [], - "destinations": [ + "blockers" : [], + "destinations" : [ "`nixos-modules/options-site.nix` (retained); per-Zone options in `options-zones.nix`" ], - "entryContracts": [ + "entryContracts" : [ "ADR-046-nix-configuration" ], - "exitGate": "ADR046-W5 exit criteria (ADR-046-validation-and-delivery §4): every spec/work item in this wave Merged with clean destinations, all validators green, and the ten-role panel seal recorded", - "id": "ADR046-nix-003", - "kind": "work-item", - "owner": "ADR046-nix-001; site options", - "parallelGroup": "wi:ADR-046-nix-configuration", - "prerequisites": [ + "exitGate" : "ADR046-W5 exit criteria (ADR-046-validation-and-delivery §4): every spec/work item in this wave Merged with clean destinations, all validators green, and the ten-role panel seal recorded", + "id" : "ADR046-nix-003", + "kind" : "work-item", + "owner" : "ADR046-nix-001; site options", + "parallelGroup" : "wi:ADR-046-nix-configuration", + "prerequisites" : [ "ADR-046-nix-configuration", "ADR046-nix-001" ], - "specId": "ADR-046-nix-configuration", - "topologicalRank": 11, - "wave": "W5" + "specId" : "ADR-046-nix-configuration", + "topologicalRank" : 11, + "wave" : "W5" }, { - "blockers": [], - "destinations": [ + "blockers" : [], + "destinations" : [ "`nixos-modules/index.nix` (rewritten); emits `/etc/d2b/index.json`" ], - "entryContracts": [ + "entryContracts" : [ "ADR-046-nix-configuration" ], - "exitGate": "ADR046-W5 exit criteria (ADR-046-validation-and-delivery §4): every spec/work item in this wave Merged with clean destinations, all validators green, and the ten-role panel seal recorded", - "id": "ADR046-nix-004", - "kind": "work-item", - "owner": "ADR046-nix-001, ADR046-nix-002", - "parallelGroup": "wi:ADR-046-nix-configuration", - "prerequisites": [ + "exitGate" : "ADR046-W5 exit criteria (ADR-046-validation-and-delivery §4): every spec/work item in this wave Merged with clean destinations, all validators green, and the ten-role panel seal recorded", + "id" : "ADR046-nix-004", + "kind" : "work-item", + "owner" : "ADR046-nix-001, ADR046-nix-002", + "parallelGroup" : "wi:ADR-046-nix-configuration", + "prerequisites" : [ "ADR-046-nix-configuration", "ADR046-nix-001", "ADR046-nix-002" ], - "specId": "ADR-046-nix-configuration", - "topologicalRank": 12, - "wave": "W5" + "specId" : "ADR-046-nix-configuration", + "topologicalRank" : 12, + "wave" : "W5" }, { - "blockers": [], - "destinations": [ + "blockers" : [], + "destinations" : [ "`nixos-modules/bundle-zones.nix` (per-Zone bundle derivation); common helpers retained in `bundle-artifacts.nix`" ], - "entryContracts": [ + "entryContracts" : [ "ADR-046-nix-configuration" ], - "exitGate": "ADR046-W5 exit criteria (ADR-046-validation-and-delivery §4): every spec/work item in this wave Merged with clean destinations, all validators green, and the ten-role panel seal recorded", - "id": "ADR046-nix-005", - "kind": "work-item", - "owner": "ADR046-nix-004", - "parallelGroup": "wi:ADR-046-nix-configuration", - "prerequisites": [ + "exitGate" : "ADR046-W5 exit criteria (ADR-046-validation-and-delivery §4): every spec/work item in this wave Merged with clean destinations, all validators green, and the ten-role panel seal recorded", + "id" : "ADR046-nix-005", + "kind" : "work-item", + "owner" : "ADR046-nix-004", + "parallelGroup" : "wi:ADR-046-nix-configuration", + "prerequisites" : [ "ADR-046-nix-configuration", "ADR046-nix-004" ], - "specId": "ADR-046-nix-configuration", - "topologicalRank": 13, - "wave": "W5" + "specId" : "ADR-046-nix-configuration", + "topologicalRank" : 13, + "wave" : "W5" }, { - "blockers": [], - "destinations": [ + "blockers" : [], + "destinations" : [ "`nixos-modules/resources-zones-processes.nix`; emits `zones//processes.json`" ], - "entryContracts": [ + "entryContracts" : [ "ADR-046-nix-configuration" ], - "exitGate": "ADR046-W5 exit criteria (ADR-046-validation-and-delivery §4): every spec/work item in this wave Merged with clean destinations, all validators green, and the ten-role panel seal recorded", - "id": "ADR046-nix-006", - "kind": "work-item", - "owner": "ADR046-nix-005; Process Provider work items (ADR046-primitives-002)", - "parallelGroup": "wi:ADR-046-nix-configuration", - "prerequisites": [ + "exitGate" : "ADR046-W5 exit criteria (ADR-046-validation-and-delivery §4): every spec/work item in this wave Merged with clean destinations, all validators green, and the ten-role panel seal recorded", + "id" : "ADR046-nix-006", + "kind" : "work-item", + "owner" : "ADR046-nix-005; Process Provider work items (ADR046-primitives-002)", + "parallelGroup" : "wi:ADR-046-nix-configuration", + "prerequisites" : [ "ADR-046-nix-configuration", "ADR046-nix-005", "ADR046-primitives-002" ], - "specId": "ADR-046-nix-configuration", - "topologicalRank": 14, - "wave": "W5" + "specId" : "ADR-046-nix-configuration", + "topologicalRank" : 14, + "wave" : "W5" }, { - "blockers": [], - "destinations": [ + "blockers" : [], + "destinations" : [ "`nixos-modules/resources-zones-volumes.nix`; emits `zones//volumes.json`; OFD lock rows move to `d2b-contracts` internals" ], - "entryContracts": [ + "entryContracts" : [ "ADR-046-nix-configuration" ], - "exitGate": "ADR046-W5 exit criteria (ADR-046-validation-and-delivery §4): every spec/work item in this wave Merged with clean destinations, all validators green, and the ten-role panel seal recorded", - "id": "ADR046-nix-007", - "kind": "work-item", - "owner": "ADR046-nix-005; Volume Provider work items (ADR046-primitives-003)", - "parallelGroup": "wi:ADR-046-nix-configuration", - "prerequisites": [ + "exitGate" : "ADR046-W5 exit criteria (ADR-046-validation-and-delivery §4): every spec/work item in this wave Merged with clean destinations, all validators green, and the ten-role panel seal recorded", + "id" : "ADR046-nix-007", + "kind" : "work-item", + "owner" : "ADR046-nix-005; Volume Provider work items (ADR046-primitives-003)", + "parallelGroup" : "wi:ADR-046-nix-configuration", + "prerequisites" : [ "ADR-046-nix-configuration", "ADR046-nix-005", "ADR046-primitives-003" ], - "specId": "ADR-046-nix-configuration", - "topologicalRank": 14, - "wave": "W5" + "specId" : "ADR-046-nix-configuration", + "topologicalRank" : 14, + "wave" : "W5" }, { - "blockers": [], - "destinations": [ - "Zone self-resource in `zones//zone.json`; allocator concept retired from Nix; socket paths in Zone resource spec; `realm-controllers.json` RETAINED during migration (live d2bd reads it)" + "blockers" : [], + "destinations" : [ + "Compiler-only `parentZone` map in `nixos-modules/options-zones.nix`; local-root allocator bootstrap compiler/sealer; runtime-created Zone self-resource with `spec = {}`; child-local ZoneLink resource; `realm-controllers.json` RETAINED during migration (live d2bd reads it)" ], - "entryContracts": [ + "entryContracts" : [ "ADR-046-nix-configuration" ], - "exitGate": "ADR046-W5 exit criteria (ADR-046-validation-and-delivery §4): every spec/work item in this wave Merged with clean destinations, all validators green, and the ten-role panel seal recorded", - "id": "ADR046-nix-008", - "kind": "work-item", - "owner": "ADR046-nix-004; Zone/Network migration", - "parallelGroup": "wi:ADR-046-nix-configuration", - "prerequisites": [ + "exitGate" : "ADR046-W5 exit criteria (ADR-046-validation-and-delivery §4): every spec/work item in this wave Merged with clean destinations, all validators green, and the ten-role panel seal recorded", + "id" : "ADR046-nix-008", + "kind" : "work-item", + "owner" : "ADR046-nix-004; Zone/Network migration", + "parallelGroup" : "wi:ADR-046-nix-configuration", + "prerequisites" : [ "ADR-046-nix-configuration", "ADR046-nix-004" ], - "specId": "ADR-046-nix-configuration", - "topologicalRank": 13, - "wave": "W5" + "specId" : "ADR-046-nix-configuration", + "topologicalRank" : 13, + "wave" : "W5" }, { - "blockers": [], - "destinations": [ + "blockers" : [], + "destinations" : [ "Provider/display-wayland and Provider/shell-terminal Process configs in `zones//processes.json`; `Provider/credential-entra` Credential resource; `realm-identity.json` RETAINED during migration" ], - "entryContracts": [ + "entryContracts" : [ "ADR-046-nix-configuration" ], - "exitGate": "ADR046-W5 exit criteria (ADR-046-validation-and-delivery §4): every spec/work item in this wave Merged with clean destinations, all validators green, and the ten-role panel seal recorded", - "id": "ADR046-nix-009", - "kind": "work-item", - "owner": "ADR046-nix-006; display/credential Provider work items", - "parallelGroup": "wi:ADR-046-nix-configuration", - "prerequisites": [ + "exitGate" : "ADR046-W5 exit criteria (ADR-046-validation-and-delivery §4): every spec/work item in this wave Merged with clean destinations, all validators green, and the ten-role panel seal recorded", + "id" : "ADR046-nix-009", + "kind" : "work-item", + "owner" : "ADR046-nix-006; display/credential Provider work items", + "parallelGroup" : "wi:ADR-046-nix-configuration", + "prerequisites" : [ "ADR-046-nix-configuration", "ADR046-nix-006" ], - "specId": "ADR-046-nix-configuration", - "topologicalRank": 15, - "wave": "W5" + "specId" : "ADR-046-nix-configuration", + "topologicalRank" : 15, + "wave" : "W5" }, { - "blockers": [], - "destinations": [ + "blockers" : [], + "destinations" : [ "User-only `Host` resource in `zones//hosts.json` (`spec.isolationPosture: \"none\"`, `defaultDomain: user`, `allowedDomains: [user]`, `defaultUserRef: User/`); child `Process` resources in `zones//processes.json` using normal Process Providers; shell session supervisor → `Process` under `Provider/shell-terminal`; never a `Guest`; not a v3 Provider" ], - "entryContracts": [ + "entryContracts" : [ "ADR-046-nix-configuration" ], - "exitGate": "ADR046-W5 exit criteria (ADR-046-validation-and-delivery §4): every spec/work item in this wave Merged with clean destinations, all validators green, and the ten-role panel seal recorded", - "id": "ADR046-nix-010", - "kind": "work-item", - "owner": "ADR046-nix-001; unsafe-local migration", - "parallelGroup": "wi:ADR-046-nix-configuration", - "prerequisites": [ + "exitGate" : "ADR046-W5 exit criteria (ADR-046-validation-and-delivery §4): every spec/work item in this wave Merged with clean destinations, all validators green, and the ten-role panel seal recorded", + "id" : "ADR046-nix-010", + "kind" : "work-item", + "owner" : "ADR046-nix-001; unsafe-local migration", + "parallelGroup" : "wi:ADR-046-nix-configuration", + "prerequisites" : [ "ADR-046-nix-configuration", "ADR046-nix-001" ], - "specId": "ADR-046-nix-configuration", - "topologicalRank": 11, - "wave": "W5" + "specId" : "ADR-046-nix-configuration", + "topologicalRank" : 11, + "wave" : "W5" }, { - "blockers": [], - "destinations": [ + "blockers" : [], + "destinations" : [ "`nixos-modules/privileges-json.nix` (retained); `/etc/d2b/privileges.json` (retained, site-wide)" ], - "entryContracts": [ + "entryContracts" : [ "ADR-046-nix-configuration" ], - "exitGate": "ADR046-W5 exit criteria (ADR-046-validation-and-delivery §4): every spec/work item in this wave Merged with clean destinations, all validators green, and the ten-role panel seal recorded", - "id": "ADR046-nix-011", - "kind": "work-item", - "owner": "Broker privileges owner", - "parallelGroup": "wi:ADR-046-nix-configuration", - "prerequisites": [ + "exitGate" : "ADR046-W5 exit criteria (ADR-046-validation-and-delivery §4): every spec/work item in this wave Merged with clean destinations, all validators green, and the ten-role panel seal recorded", + "id" : "ADR046-nix-011", + "kind" : "work-item", + "owner" : "Broker privileges owner", + "parallelGroup" : "wi:ADR-046-nix-configuration", + "prerequisites" : [ "ADR-046-nix-configuration" ], - "specId": "ADR-046-nix-configuration", - "topologicalRank": 10, - "wave": "W5" + "specId" : "ADR-046-nix-configuration", + "topologicalRank" : 10, + "wave" : "W5" }, { - "blockers": [], - "destinations": [ + "blockers" : [], + "destinations" : [ "`nixos-modules/closures-json.nix` (rewritten, keyed by artifact ID from `d2b.artifacts` with `type = \"nixos-system\"`); `nixos-modules/minijail-profiles.nix` (retained, adapted to reference Zone Guests)" ], - "entryContracts": [ + "entryContracts" : [ "ADR-046-nix-configuration" ], - "exitGate": "ADR046-W5 exit criteria (ADR-046-validation-and-delivery §4): every spec/work item in this wave Merged with clean destinations, all validators green, and the ten-role panel seal recorded", - "id": "ADR046-nix-012", - "kind": "work-item", - "owner": "ADR046-nix-005; ADR046-nix-022 (artifact catalog emitter); Provider/volume-virtiofs", - "parallelGroup": "wi:ADR-046-nix-configuration", - "prerequisites": [ + "exitGate" : "ADR046-W5 exit criteria (ADR-046-validation-and-delivery §4): every spec/work item in this wave Merged with clean destinations, all validators green, and the ten-role panel seal recorded", + "id" : "ADR046-nix-012", + "kind" : "work-item", + "owner" : "ADR046-nix-005; ADR046-nix-022 (artifact catalog emitter); Provider/volume-virtiofs", + "parallelGroup" : "wi:ADR-046-nix-configuration", + "prerequisites" : [ "ADR-046-nix-configuration", "ADR046-nix-005", "ADR046-nix-022" ], - "specId": "ADR-046-nix-configuration", - "topologicalRank": 15, - "wave": "W5" + "specId" : "ADR-046-nix-configuration", + "topologicalRank" : 15, + "wave" : "W5" }, { - "blockers": [], - "destinations": [ + "blockers" : [], + "destinations" : [ "Per-Zone `zones//bundle.json` (`schemaVersion`); Host resource in `zones//hosts.json`" ], - "entryContracts": [ + "entryContracts" : [ "ADR-046-nix-configuration" ], - "exitGate": "ADR046-W5 exit criteria (ADR-046-validation-and-delivery §4): every spec/work item in this wave Merged with clean destinations, all validators green, and the ten-role panel seal recorded", - "id": "ADR046-nix-013", - "kind": "work-item", - "owner": "ADR046-nix-005; manifest contract", - "parallelGroup": "wi:ADR-046-nix-configuration", - "prerequisites": [ + "exitGate" : "ADR046-W5 exit criteria (ADR-046-validation-and-delivery §4): every spec/work item in this wave Merged with clean destinations, all validators green, and the ten-role panel seal recorded", + "id" : "ADR046-nix-013", + "kind" : "work-item", + "owner" : "ADR046-nix-005; manifest contract", + "parallelGroup" : "wi:ADR-046-nix-configuration", + "prerequisites" : [ "ADR-046-nix-configuration", "ADR046-nix-005" ], - "specId": "ADR-046-nix-configuration", - "topologicalRank": 14, - "wave": "W5" + "specId" : "ADR-046-nix-configuration", + "topologicalRank" : 14, + "wave" : "W5" }, { - "blockers": [], - "destinations": [ + "blockers" : [], + "destinations" : [ "`nixos-modules/assertions.nix`" ], - "entryContracts": [ + "entryContracts" : [ "ADR-046-nix-configuration" ], - "exitGate": "ADR046-W5 exit criteria (ADR-046-validation-and-delivery §4): every spec/work item in this wave Merged with clean destinations, all validators green, and the ten-role panel seal recorded", - "id": "ADR046-nix-014", - "kind": "work-item", - "owner": "ADR046-nix-001, ADR046-nix-002", - "parallelGroup": "wi:ADR-046-nix-configuration", - "prerequisites": [ + "exitGate" : "ADR046-W5 exit criteria (ADR-046-validation-and-delivery §4): every spec/work item in this wave Merged with clean destinations, all validators green, and the ten-role panel seal recorded", + "id" : "ADR046-nix-014", + "kind" : "work-item", + "owner" : "ADR046-nix-001, ADR046-nix-002", + "parallelGroup" : "wi:ADR-046-nix-configuration", + "prerequisites" : [ "ADR-046-nix-configuration", "ADR046-nix-001", "ADR046-nix-002" ], - "specId": "ADR-046-nix-configuration", - "topologicalRank": 12, - "wave": "W5" + "specId" : "ADR-046-nix-configuration", + "topologicalRank" : 12, + "wave" : "W5" }, { - "blockers": [], - "destinations": [ + "blockers" : [], + "destinations" : [ "Same files; updated to use Zone bundle activation path and Zone resource state dirs" ], - "entryContracts": [ + "entryContracts" : [ "ADR-046-nix-configuration" ], - "exitGate": "ADR046-W5 exit criteria (ADR-046-validation-and-delivery §4): every spec/work item in this wave Merged with clean destinations, all validators green, and the ten-role panel seal recorded", - "id": "ADR046-nix-015", - "kind": "work-item", - "owner": "ADR046-nix-001; host activation", - "parallelGroup": "wi:ADR-046-nix-configuration", - "prerequisites": [ + "exitGate" : "ADR046-W5 exit criteria (ADR-046-validation-and-delivery §4): every spec/work item in this wave Merged with clean destinations, all validators green, and the ten-role panel seal recorded", + "id" : "ADR046-nix-015", + "kind" : "work-item", + "owner" : "ADR046-nix-001; host activation", + "parallelGroup" : "wi:ADR-046-nix-configuration", + "prerequisites" : [ "ADR-046-nix-configuration", "ADR046-nix-001" ], - "specId": "ADR-046-nix-configuration", - "topologicalRank": 11, - "wave": "W5" + "specId" : "ADR-046-nix-configuration", + "topologicalRank" : 11, + "wave" : "W5" }, { - "blockers": [], - "destinations": [ + "blockers" : [], + "destinations" : [ "Network reconciliation by `Provider/network-local` Process resources" ], - "entryContracts": [ + "entryContracts" : [ "ADR-046-nix-configuration" ], - "exitGate": "ADR046-W5 exit criteria (ADR-046-validation-and-delivery §4): every spec/work item in this wave Merged with clean destinations, all validators green, and the ten-role panel seal recorded", - "id": "ADR046-nix-016", - "kind": "work-item", - "owner": "ADR046-nix-002; Provider/network-local dossier", - "parallelGroup": "wi:ADR-046-nix-configuration", - "prerequisites": [ + "exitGate" : "ADR046-W5 exit criteria (ADR-046-validation-and-delivery §4): every spec/work item in this wave Merged with clean destinations, all validators green, and the ten-role panel seal recorded", + "id" : "ADR046-nix-016", + "kind" : "work-item", + "owner" : "ADR046-nix-002; Provider/network-local dossier", + "parallelGroup" : "wi:ADR-046-nix-configuration", + "prerequisites" : [ "ADR-046-nix-configuration", "ADR046-nix-002" ], - "specId": "ADR-046-nix-configuration", - "topologicalRank": 12, - "wave": "W5" + "specId" : "ADR-046-nix-configuration", + "topologicalRank" : 12, + "wave" : "W5" }, { - "blockers": [], - "destinations": [ + "blockers" : [], + "destinations" : [ "Per-VM store reconciliation by `Provider/volume-virtiofs` EphemeralProcess/Process resources" ], - "entryContracts": [ + "entryContracts" : [ "ADR-046-nix-configuration" ], - "exitGate": "ADR046-W5 exit criteria (ADR-046-validation-and-delivery §4): every spec/work item in this wave Merged with clean destinations, all validators green, and the ten-role panel seal recorded", - "id": "ADR046-nix-017", - "kind": "work-item", - "owner": "ADR046-nix-012; Provider/volume-virtiofs", - "parallelGroup": "wi:ADR-046-nix-configuration", - "prerequisites": [ + "exitGate" : "ADR046-W5 exit criteria (ADR-046-validation-and-delivery §4): every spec/work item in this wave Merged with clean destinations, all validators green, and the ten-role panel seal recorded", + "id" : "ADR046-nix-017", + "kind" : "work-item", + "owner" : "ADR046-nix-012; Provider/volume-virtiofs", + "parallelGroup" : "wi:ADR-046-nix-configuration", + "prerequisites" : [ "ADR-046-nix-configuration", "ADR046-nix-012" ], - "specId": "ADR-046-nix-configuration", - "topologicalRank": 16, - "wave": "W5" + "specId" : "ADR-046-nix-configuration", + "topologicalRank" : 16, + "wave" : "W5" }, { - "blockers": [], - "destinations": [ + "blockers" : [], + "destinations" : [ "`Provider/device-tpm`, `Provider/device-usbip`, `Provider/device-gpu`, `Provider/audio-pipewire` resource install declarations in `options-zones-resources.nix`" ], - "entryContracts": [ + "entryContracts" : [ "ADR-046-nix-configuration" ], - "exitGate": "ADR046-W5 exit criteria (ADR-046-validation-and-delivery §4): every spec/work item in this wave Merged with clean destinations, all validators green, and the ten-role panel seal recorded", - "id": "ADR046-nix-018", - "kind": "work-item", - "owner": "ADR046-nix-002; Provider dossiers for graphics, tpm, usbip, audio", - "parallelGroup": "wi:ADR-046-nix-configuration", - "prerequisites": [ + "exitGate" : "ADR046-W5 exit criteria (ADR-046-validation-and-delivery §4): every spec/work item in this wave Merged with clean destinations, all validators green, and the ten-role panel seal recorded", + "id" : "ADR046-nix-018", + "kind" : "work-item", + "owner" : "ADR046-nix-002; Provider dossiers for graphics, tpm, usbip, audio", + "parallelGroup" : "wi:ADR-046-nix-configuration", + "prerequisites" : [ "ADR-046-nix-configuration", "ADR046-nix-002" ], - "specId": "ADR-046-nix-configuration", - "topologicalRank": 12, - "wave": "W5" + "specId" : "ADR-046-nix-configuration", + "topologicalRank" : 12, + "wave" : "W5" }, { - "blockers": [], - "destinations": [ + "blockers" : [], + "destinations" : [ "`docs/reference/schemas/v3/.json` for each ResourceType; `nixos-modules/resource-schema-validation.nix` (validates emitted spec against committed JSON Schema at build time); `nixos-modules/provider-settings-validation.nix` (validates `spec.provider.settings` where declared in schema, and Provider `config`, against Provider-embedded schema at build time); `nixos-modules/assertions.nix` (Credential ref enforcement, secret-pattern rejection)" ], - "entryContracts": [ + "entryContracts" : [ "ADR-046-nix-configuration" ], - "exitGate": "ADR046-W5 exit criteria (ADR-046-validation-and-delivery §4): every spec/work item in this wave Merged with clean destinations, all validators green, and the ten-role panel seal recorded", - "id": "ADR046-nix-019", - "kind": "work-item", - "owner": "ADR046-nix-005; ADR046-nix-001; `d2b-contracts` schema generation (ADR046-nix-027)", - "parallelGroup": "wi:ADR-046-nix-configuration", - "prerequisites": [ + "exitGate" : "ADR046-W5 exit criteria (ADR-046-validation-and-delivery §4): every spec/work item in this wave Merged with clean destinations, all validators green, and the ten-role panel seal recorded", + "id" : "ADR046-nix-019", + "kind" : "work-item", + "owner" : "ADR046-nix-005; ADR046-nix-001; `d2b-contracts` schema generation (ADR046-nix-027)", + "parallelGroup" : "wi:ADR-046-nix-configuration", + "prerequisites" : [ "ADR-046-nix-configuration", "ADR046-nix-001", "ADR046-nix-005", "ADR046-nix-027" ], - "specId": "ADR-046-nix-configuration", - "topologicalRank": 14, - "wave": "W5" + "specId" : "ADR-046-nix-configuration", + "topologicalRank" : 14, + "wave" : "W5" }, { - "blockers": [], - "destinations": [ + "blockers" : [], + "destinations" : [ "Configuration-publication controller handler in `packages/d2bd/src/config_publication.rs`; `ConfigurationOwnedClassifier`; `AbsentResourceReaper`; `Zone` status conditions in `d2b-contracts/src/v3/zone_status.rs`; cleanup audit emitter in `d2b-state/src/audit_segments.rs`" ], - "entryContracts": [ + "entryContracts" : [ "ADR-046-nix-configuration" ], - "exitGate": "ADR046-W5 exit criteria (ADR-046-validation-and-delivery §4): every spec/work item in this wave Merged with clean destinations, all validators green, and the ten-role panel seal recorded", - "id": "ADR046-nix-020", - "kind": "work-item", - "owner": "ADR046-nix-005; ADR046-nix-001; configuration-publication controller (ADR-046-core-controllers)", - "parallelGroup": "wi:ADR-046-nix-configuration", - "prerequisites": [ + "exitGate" : "ADR046-W5 exit criteria (ADR-046-validation-and-delivery §4): every spec/work item in this wave Merged with clean destinations, all validators green, and the ten-role panel seal recorded", + "id" : "ADR046-nix-020", + "kind" : "work-item", + "owner" : "ADR046-nix-005; ADR046-nix-001; configuration-publication controller (ADR-046-core-controllers)", + "parallelGroup" : "wi:ADR-046-nix-configuration", + "prerequisites" : [ "ADR-046-nix-configuration", "ADR046-nix-001", "ADR046-nix-005" ], - "specId": "ADR-046-nix-configuration", - "topologicalRank": 14, - "wave": "W5" + "specId" : "ADR-046-nix-configuration", + "topologicalRank" : 14, + "wave" : "W5" }, { - "blockers": [], - "destinations": [ + "blockers" : [], + "destinations" : [ "`packages/d2b-contract-tests/tests/provider-crate-layout.rs`; workspace scan in `packages/xtask/src/main.rs` (extend `check-workspace` or add `check-provider-layout` subcommand)" ], - "entryContracts": [ + "entryContracts" : [ "ADR-046-nix-configuration" ], - "exitGate": "ADR046-W5 exit criteria (ADR-046-validation-and-delivery §4): every spec/work item in this wave Merged with clean destinations, all validators green, and the ten-role panel seal recorded", - "id": "ADR046-nix-021", - "kind": "work-item", - "owner": "ADR046-nix-001; `d2b-contracts` workspace policy tests", - "parallelGroup": "wi:ADR-046-nix-configuration", - "prerequisites": [ + "exitGate" : "ADR046-W5 exit criteria (ADR-046-validation-and-delivery §4): every spec/work item in this wave Merged with clean destinations, all validators green, and the ten-role panel seal recorded", + "id" : "ADR046-nix-021", + "kind" : "work-item", + "owner" : "ADR046-nix-001; `d2b-contracts` workspace policy tests", + "parallelGroup" : "wi:ADR-046-nix-configuration", + "prerequisites" : [ "ADR-046-nix-configuration", "ADR046-nix-001" ], - "specId": "ADR-046-nix-configuration", - "topologicalRank": 11, - "wave": "W5" + "specId" : "ADR-046-nix-configuration", + "topologicalRank" : 11, + "wave" : "W5" }, { - "blockers": [], - "destinations": [ + "blockers" : [], + "destinations" : [ "`nixos-modules/artifact-catalog.nix` (new emitter); `nixos-modules/options-artifacts.nix` (new option: `d2b.artifacts. = { package; type; }`); `/etc/d2b/artifact-catalog.json` (output artifact, `root:d2bd` 0640); `nixos-modules/bundle-zones.nix` (extend to include artifact catalog digest in `bundle.json`); `nixos-modules/options-zones-resources.nix` (replace `closureRef` / `nixosSystem` helpers with `systemArtifactId` validation)" ], - "entryContracts": [ + "entryContracts" : [ "ADR-046-nix-configuration" ], - "exitGate": "ADR046-W5 exit criteria (ADR-046-validation-and-delivery §4): every spec/work item in this wave Merged with clean destinations, all validators green, and the ten-role panel seal recorded", - "id": "ADR046-nix-022", - "kind": "work-item", - "owner": "ADR046-nix-005 (bundle derivation); `d2b-contracts` schema generation (ADR046-nix-027)", - "parallelGroup": "wi:ADR-046-nix-configuration", - "prerequisites": [ + "exitGate" : "ADR046-W5 exit criteria (ADR-046-validation-and-delivery §4): every spec/work item in this wave Merged with clean destinations, all validators green, and the ten-role panel seal recorded", + "id" : "ADR046-nix-022", + "kind" : "work-item", + "owner" : "ADR046-nix-005 (bundle derivation); `d2b-contracts` schema generation (ADR046-nix-027)", + "parallelGroup" : "wi:ADR-046-nix-configuration", + "prerequisites" : [ "ADR-046-nix-configuration", "ADR046-nix-005", "ADR046-nix-027" ], - "specId": "ADR-046-nix-configuration", - "topologicalRank": 14, - "wave": "W5" + "specId" : "ADR-046-nix-configuration", + "topologicalRank" : 14, + "wave" : "W5" }, { - "blockers": [], - "destinations": [ + "blockers" : [], + "destinations" : [ "`packages/d2b-bus/src/session/` (new crate `d2b-bus`); `ComponentSessionDriver` becomes the central abstraction for all Zone bus sessions." ], - "entryContracts": [ + "entryContracts" : [ "ADR-046-nix-configuration" ], - "exitGate": "ADR046-W5 exit criteria (ADR-046-validation-and-delivery §4): every spec/work item in this wave Merged with clean destinations, all validators green, and the ten-role panel seal recorded", - "id": "ADR046-nix-023", - "kind": "work-item", - "owner": "ADR-046-componentsession-and-bus spec; ADR046-nix-027 (wire contract) must land first", - "parallelGroup": "wi:ADR-046-nix-configuration", - "prerequisites": [ + "exitGate" : "ADR046-W5 exit criteria (ADR-046-validation-and-delivery §4): every spec/work item in this wave Merged with clean destinations, all validators green, and the ten-role panel seal recorded", + "id" : "ADR046-nix-023", + "kind" : "work-item", + "owner" : "ADR-046-componentsession-and-bus spec; ADR046-nix-027 (wire contract) must land first", + "parallelGroup" : "wi:ADR-046-nix-configuration", + "prerequisites" : [ "ADR-046-nix-configuration", "ADR046-nix-027" ], - "specId": "ADR-046-nix-configuration", - "topologicalRank": 11, - "wave": "W5" + "specId" : "ADR-046-nix-configuration", + "topologicalRank" : 11, + "wave" : "W5" }, { - "blockers": [], - "destinations": [ + "blockers" : [], + "destinations" : [ "`packages/d2b-bus/src/session/` (same crate as ADR046-nix-023)." ], - "entryContracts": [ + "entryContracts" : [ "ADR-046-nix-configuration" ], - "exitGate": "ADR046-W5 exit criteria (ADR-046-validation-and-delivery §4): every spec/work item in this wave Merged with clean destinations, all validators green, and the ten-role panel seal recorded", - "id": "ADR046-nix-024", - "kind": "work-item", - "owner": "ADR046-nix-023; ADR046-nix-027", - "parallelGroup": "wi:ADR-046-nix-configuration", - "prerequisites": [ + "exitGate" : "ADR046-W5 exit criteria (ADR-046-validation-and-delivery §4): every spec/work item in this wave Merged with clean destinations, all validators green, and the ten-role panel seal recorded", + "id" : "ADR046-nix-024", + "kind" : "work-item", + "owner" : "ADR046-nix-023; ADR046-nix-027", + "parallelGroup" : "wi:ADR-046-nix-configuration", + "prerequisites" : [ "ADR-046-nix-configuration", "ADR046-nix-023", "ADR046-nix-027" ], - "specId": "ADR-046-nix-configuration", - "topologicalRank": 12, - "wave": "W5" + "specId" : "ADR-046-nix-configuration", + "topologicalRank" : 12, + "wave" : "W5" }, { - "blockers": [], - "destinations": [ + "blockers" : [], + "destinations" : [ "`packages/d2b-bus/src/session/`." ], - "entryContracts": [ + "entryContracts" : [ "ADR-046-nix-configuration" ], - "exitGate": "ADR046-W5 exit criteria (ADR-046-validation-and-delivery §4): every spec/work item in this wave Merged with clean destinations, all validators green, and the ten-role panel seal recorded", - "id": "ADR046-nix-025", - "kind": "work-item", - "owner": "ADR046-nix-023; ADR046-nix-027", - "parallelGroup": "wi:ADR-046-nix-configuration", - "prerequisites": [ + "exitGate" : "ADR046-W5 exit criteria (ADR-046-validation-and-delivery §4): every spec/work item in this wave Merged with clean destinations, all validators green, and the ten-role panel seal recorded", + "id" : "ADR046-nix-025", + "kind" : "work-item", + "owner" : "ADR046-nix-023; ADR046-nix-027", + "parallelGroup" : "wi:ADR-046-nix-configuration", + "prerequisites" : [ "ADR-046-nix-configuration", "ADR046-nix-023", "ADR046-nix-027" ], - "specId": "ADR-046-nix-configuration", - "topologicalRank": 12, - "wave": "W5" + "specId" : "ADR-046-nix-configuration", + "topologicalRank" : 12, + "wave" : "W5" }, { - "blockers": [], - "destinations": [ + "blockers" : [], + "destinations" : [ "`packages/d2b-bus/src/transport/unix/`." ], - "entryContracts": [ + "entryContracts" : [ "ADR-046-nix-configuration" ], - "exitGate": "ADR046-W5 exit criteria (ADR-046-validation-and-delivery §4): every spec/work item in this wave Merged with clean destinations, all validators green, and the ten-role panel seal recorded", - "id": "ADR046-nix-026", - "kind": "work-item", - "owner": "ADR046-nix-023", - "parallelGroup": "wi:ADR-046-nix-configuration", - "prerequisites": [ + "exitGate" : "ADR046-W5 exit criteria (ADR-046-validation-and-delivery §4): every spec/work item in this wave Merged with clean destinations, all validators green, and the ten-role panel seal recorded", + "id" : "ADR046-nix-026", + "kind" : "work-item", + "owner" : "ADR046-nix-023", + "parallelGroup" : "wi:ADR-046-nix-configuration", + "prerequisites" : [ "ADR-046-nix-configuration", "ADR046-nix-023" ], - "specId": "ADR-046-nix-configuration", - "topologicalRank": 12, - "wave": "W5" + "specId" : "ADR-046-nix-configuration", + "topologicalRank" : 12, + "wave" : "W5" }, { - "blockers": [], - "destinations": [ + "blockers" : [], + "destinations" : [ "`packages/d2b-contracts/src/v3/component_session.rs`." ], - "entryContracts": [ + "entryContracts" : [ "ADR-046-nix-configuration" ], - "exitGate": "ADR046-W5 exit criteria (ADR-046-validation-and-delivery §4): every spec/work item in this wave Merged with clean destinations, all validators green, and the ten-role panel seal recorded", - "id": "ADR046-nix-027", - "kind": "work-item", - "owner": "ADR-046-componentsession-and-bus; naming and wire enumeration decisions per ADR-046-componentsession-and-bus owning spec before final adoption", - "parallelGroup": "wi:ADR-046-nix-configuration", - "prerequisites": [ + "exitGate" : "ADR046-W5 exit criteria (ADR-046-validation-and-delivery §4): every spec/work item in this wave Merged with clean destinations, all validators green, and the ten-role panel seal recorded", + "id" : "ADR046-nix-027", + "kind" : "work-item", + "owner" : "ADR-046-componentsession-and-bus; naming and wire enumeration decisions per ADR-046-componentsession-and-bus owning spec before final adoption", + "parallelGroup" : "wi:ADR-046-nix-configuration", + "prerequisites" : [ "ADR-046-nix-configuration" ], - "specId": "ADR-046-nix-configuration", - "topologicalRank": 10, - "wave": "W5" + "specId" : "ADR-046-nix-configuration", + "topologicalRank" : 10, + "wave" : "W5" }, { - "blockers": [], - "destinations": [ + "blockers" : [], + "destinations" : [ "`packages/d2b-contracts/src/v3/services/`." ], - "entryContracts": [ + "entryContracts" : [ "ADR-046-nix-configuration" ], - "exitGate": "ADR046-W5 exit criteria (ADR-046-validation-and-delivery §4): every spec/work item in this wave Merged with clean destinations, all validators green, and the ten-role panel seal recorded", - "id": "ADR046-nix-028", - "kind": "work-item", - "owner": "ADR046-nix-027; Zone service naming per ADR-046-componentsession-and-bus owning spec", - "parallelGroup": "wi:ADR-046-nix-configuration", - "prerequisites": [ + "exitGate" : "ADR046-W5 exit criteria (ADR-046-validation-and-delivery §4): every spec/work item in this wave Merged with clean destinations, all validators green, and the ten-role panel seal recorded", + "id" : "ADR046-nix-028", + "kind" : "work-item", + "owner" : "ADR046-nix-027; Zone service naming per ADR-046-componentsession-and-bus owning spec", + "parallelGroup" : "wi:ADR-046-nix-configuration", + "prerequisites" : [ "ADR-046-nix-configuration", "ADR046-nix-027" ], - "specId": "ADR-046-nix-configuration", - "topologicalRank": 11, - "wave": "W5" + "specId" : "ADR-046-nix-configuration", + "topologicalRank" : 11, + "wave" : "W5" }, { - "blockers": [], - "destinations": [ + "blockers" : [], + "destinations" : [ "`packages/d2b-provider/src/` (adapt in place)." ], - "entryContracts": [ + "entryContracts" : [ "ADR-046-nix-configuration" ], - "exitGate": "ADR046-W5 exit criteria (ADR-046-validation-and-delivery §4): every spec/work item in this wave Merged with clean destinations, all validators green, and the ten-role panel seal recorded", - "id": "ADR046-nix-029", - "kind": "work-item", - "owner": "ADR046-nix-027; ADR046-nix-028; EndpointRole naming per ADR-046-componentsession-and-bus owning spec", - "parallelGroup": "wi:ADR-046-nix-configuration", - "prerequisites": [ + "exitGate" : "ADR046-W5 exit criteria (ADR-046-validation-and-delivery §4): every spec/work item in this wave Merged with clean destinations, all validators green, and the ten-role panel seal recorded", + "id" : "ADR046-nix-029", + "kind" : "work-item", + "owner" : "ADR046-nix-027; ADR046-nix-028; EndpointRole naming per ADR-046-componentsession-and-bus owning spec", + "parallelGroup" : "wi:ADR-046-nix-configuration", + "prerequisites" : [ "ADR-046-nix-configuration", "ADR046-nix-027", "ADR046-nix-028" ], - "specId": "ADR-046-nix-configuration", - "topologicalRank": 12, - "wave": "W5" + "specId" : "ADR-046-nix-configuration", + "topologicalRank" : 12, + "wave" : "W5" }, { - "blockers": [], - "destinations": [ + "blockers" : [], + "destinations" : [ "`packages/d2b-provider-toolkit/src/` (adapt in place)." ], - "entryContracts": [ + "entryContracts" : [ "ADR-046-nix-configuration" ], - "exitGate": "ADR046-W5 exit criteria (ADR-046-validation-and-delivery §4): every spec/work item in this wave Merged with clean destinations, all validators green, and the ten-role panel seal recorded", - "id": "ADR046-nix-030", - "kind": "work-item", - "owner": "ADR046-nix-029; ADR046-nix-028; EndpointRole and ServicePackage naming per ADR-046-componentsession-and-bus owning spec", - "parallelGroup": "wi:ADR-046-nix-configuration", - "prerequisites": [ + "exitGate" : "ADR046-W5 exit criteria (ADR-046-validation-and-delivery §4): every spec/work item in this wave Merged with clean destinations, all validators green, and the ten-role panel seal recorded", + "id" : "ADR046-nix-030", + "kind" : "work-item", + "owner" : "ADR046-nix-029; ADR046-nix-028; EndpointRole and ServicePackage naming per ADR-046-componentsession-and-bus owning spec", + "parallelGroup" : "wi:ADR-046-nix-configuration", + "prerequisites" : [ "ADR-046-nix-configuration", "ADR046-nix-028", "ADR046-nix-029" ], - "specId": "ADR-046-nix-configuration", - "topologicalRank": 13, - "wave": "W5" + "specId" : "ADR-046-nix-configuration", + "topologicalRank" : 13, + "wave" : "W5" }, { - "blockers": [], - "destinations": [ + "blockers" : [], + "destinations" : [ "`nixos-modules/resources-sharing.nix`; `nixos-modules/assertions.nix`; `tests/unit/nix/cases/resource-sharing.nix`" ], - "entryContracts": [ + "entryContracts" : [ "ADR-046-nix-configuration" ], - "exitGate": "ADR046-W5 exit criteria (ADR-046-validation-and-delivery §4): every spec/work item in this wave Merged with clean destinations, all validators green, and the ten-role panel seal recorded", - "id": "ADR046-nix-031", - "kind": "work-item", - "owner": "ADR046-provider-004, ADR046-zone-control-019, ADR046-zone-control-020, ADR046-zone-control-024; Nix resource compiler owner", - "parallelGroup": "wi:ADR-046-nix-configuration", - "prerequisites": [ + "exitGate" : "ADR046-W5 exit criteria (ADR-046-validation-and-delivery §4): every spec/work item in this wave Merged with clean destinations, all validators green, and the ten-role panel seal recorded", + "id" : "ADR046-nix-031", + "kind" : "work-item", + "owner" : "ADR046-provider-004, ADR046-zone-control-019, ADR046-zone-control-020, ADR046-zone-control-024; Nix resource compiler owner", + "parallelGroup" : "wi:ADR-046-nix-configuration", + "prerequisites" : [ "ADR-046-nix-configuration", "ADR046-provider-004", "ADR046-zone-control-019", "ADR046-zone-control-020", "ADR046-zone-control-024" ], - "specId": "ADR-046-nix-configuration", - "topologicalRank": 11, - "wave": "W5" + "specId" : "ADR-046-nix-configuration", + "topologicalRank" : 14, + "wave" : "W5" + }, + { + "blockers" : [], + "destinations" : [ + "`packages/d2b-client/src/` (adapt in place)." + ], + "entryContracts" : [ + "ADR-046-nix-configuration" + ], + "exitGate" : "ADR046-W5 exit criteria (ADR-046-validation-and-delivery §4): every spec/work item in this wave Merged with clean destinations, all validators green, and the ten-role panel seal recorded", + "id" : "ADR046-nix-032", + "kind" : "work-item", + "owner" : "ADR046-nix-029; ADR046-nix-027; TargetInput v3 shape per ADR-046-componentsession-and-bus owning spec", + "parallelGroup" : "wi:ADR-046-nix-configuration", + "prerequisites" : [ + "ADR-046-nix-configuration", + "ADR046-nix-027", + "ADR046-nix-029" + ], + "specId" : "ADR-046-nix-configuration", + "topologicalRank" : 13, + "wave" : "W5" + }, + { + "blockers" : [], + "destinations" : [ + "`packages/d2b-bus/src/routing/zone_service.rs`." + ], + "entryContracts" : [ + "ADR-046-nix-configuration" + ], + "exitGate" : "ADR046-W5 exit criteria (ADR-046-validation-and-delivery §4): every spec/work item in this wave Merged with clean destinations, all validators green, and the ten-role panel seal recorded", + "id" : "ADR046-nix-033", + "kind" : "work-item", + "owner" : "ADR046-nix-023; ADR046-nix-027; naming and service name per ADR-046-componentsession-and-bus owning spec", + "parallelGroup" : "wi:ADR-046-nix-configuration", + "prerequisites" : [ + "ADR-046-nix-configuration", + "ADR046-nix-023", + "ADR046-nix-027" + ], + "specId" : "ADR-046-nix-configuration", + "topologicalRank" : 12, + "wave" : "W5" + }, + { + "blockers" : [], + "destinations" : [ + "`packages/d2bd/src/provider_registry.rs` (adapt in place)." + ], + "entryContracts" : [ + "ADR-046-nix-configuration" + ], + "exitGate" : "ADR046-W5 exit criteria (ADR-046-validation-and-delivery §4): every spec/work item in this wave Merged with clean destinations, all validators green, and the ten-role panel seal recorded", + "id" : "ADR046-nix-034", + "kind" : "work-item", + "owner" : "ADR046-nix-029; ADR046-nix-028; ADR046-nix-027; PROVIDER_BUNDLE_VERSION bump required on any bundle artifact format change", + "parallelGroup" : "wi:ADR-046-nix-configuration", + "prerequisites" : [ + "ADR-046-nix-configuration", + "ADR046-nix-027", + "ADR046-nix-028", + "ADR046-nix-029" + ], + "specId" : "ADR-046-nix-configuration", + "topologicalRank" : 13, + "wave" : "W5" + }, + { + "blockers" : [], + "destinations" : [ + "`packages/d2bd/src/provider_effects.rs` (adapt in place)." + ], + "entryContracts" : [ + "ADR-046-nix-configuration" + ], + "exitGate" : "ADR046-W5 exit criteria (ADR-046-validation-and-delivery §4): every spec/work item in this wave Merged with clean destinations, all validators green, and the ten-role panel seal recorded", + "id" : "ADR046-nix-035", + "kind" : "work-item", + "owner" : "ADR046-nix-034; ADR046-nix-029; GuestLifecycleRequest ResourceRef addressing per ADR-046-componentsession-and-bus owning spec", + "parallelGroup" : "wi:ADR-046-nix-configuration", + "prerequisites" : [ + "ADR-046-nix-configuration", + "ADR046-nix-029", + "ADR046-nix-034" + ], + "specId" : "ADR-046-nix-configuration", + "topologicalRank" : 14, + "wave" : "W5" }, { - "blockers": [], - "destinations": [ + "blockers" : [], + "destinations" : [ "`d2b-contracts` trait plus `d2b-core` core adapter; maps to broker wire operations and audit emission." ], - "entryContracts": [ + "entryContracts" : [ "ADR-046-provider-network-local" ], - "exitGate": "ADR046-W6 exit criteria (ADR-046-validation-and-delivery §4): every spec/work item in this wave Merged with clean destinations, all validators green, and the ten-role panel seal recorded", - "id": "ADR046-nl-001", - "kind": "work-item", - "owner": "Core; owns `NetworkEffectPort` contract/versioning in `d2b-contracts` and adapter implementation in `d2b-core`.", - "parallelGroup": "wi:ADR-046-provider-network-local", - "prerequisites": [ + "exitGate" : "ADR046-W6 exit criteria (ADR-046-validation-and-delivery §4): every spec/work item in this wave Merged with clean destinations, all validators green, and the ten-role panel seal recorded", + "id" : "ADR046-nl-001", + "kind" : "work-item", + "owner" : "Core; owns `NetworkEffectPort` contract/versioning in `d2b-contracts` and adapter implementation in `d2b-core`.", + "parallelGroup" : "wi:ADR-046-provider-network-local", + "prerequisites" : [ "ADR-046-provider-network-local" ], - "specId": "ADR-046-provider-network-local", - "topologicalRank": 11, - "wave": "W6" + "specId" : "ADR-046-provider-network-local", + "topologicalRank" : 11, + "wave" : "W6" }, { - "blockers": [], - "destinations": [ + "blockers" : [], + "destinations" : [ "Broker wire contract and broker/core adapter operation table for `CreateBridge`, `DeleteBridge`, `ReadNftablesDigest`, `ReadSysctlState`, and `ReadBridgePortFlags`." ], - "entryContracts": [ + "entryContracts" : [ "ADR-046-provider-network-local" ], - "exitGate": "ADR046-W6 exit criteria (ADR-046-validation-and-delivery §4): every spec/work item in this wave Merged with clean destinations, all validators green, and the ten-role panel seal recorded", - "id": "ADR046-nl-002", - "kind": "work-item", - "owner": "Core; broker/core contract work consumed by ADR046-nl-001.", - "parallelGroup": "wi:ADR-046-provider-network-local", - "prerequisites": [ + "exitGate" : "ADR046-W6 exit criteria (ADR-046-validation-and-delivery §4): every spec/work item in this wave Merged with clean destinations, all validators green, and the ten-role panel seal recorded", + "id" : "ADR046-nl-002", + "kind" : "work-item", + "owner" : "Core; broker/core contract work consumed by ADR046-nl-001.", + "parallelGroup" : "wi:ADR-046-provider-network-local", + "prerequisites" : [ "ADR-046-provider-network-local", "ADR046-nl-001" ], - "specId": "ADR-046-provider-network-local", - "topologicalRank": 12, - "wave": "W6" + "specId" : "ADR-046-provider-network-local", + "topologicalRank" : 12, + "wave" : "W6" }, { - "blockers": [], - "destinations": [ + "blockers" : [], + "destinations" : [ "`d2b-contracts` opaque byte-array newtypes; core-held HMAC key and provider-facing redacted handle types." ], - "entryContracts": [ + "entryContracts" : [ "ADR-046-provider-network-local" ], - "exitGate": "ADR046-W6 exit criteria (ADR-046-validation-and-delivery §4): every spec/work item in this wave Merged with clean destinations, all validators green, and the ten-role panel seal recorded", - "id": "ADR046-nl-003", - "kind": "work-item", - "owner": "Core; handle DTOs are owned by `d2b-contracts` and consumed by `d2b-core` plus `Provider/network-local`.", - "parallelGroup": "wi:ADR-046-provider-network-local", - "prerequisites": [ + "exitGate" : "ADR046-W6 exit criteria (ADR-046-validation-and-delivery §4): every spec/work item in this wave Merged with clean destinations, all validators green, and the ten-role panel seal recorded", + "id" : "ADR046-nl-003", + "kind" : "work-item", + "owner" : "Core; handle DTOs are owned by `d2b-contracts` and consumed by `d2b-core` plus `Provider/network-local`.", + "parallelGroup" : "wi:ADR-046-provider-network-local", + "prerequisites" : [ "ADR-046-provider-network-local" ], - "specId": "ADR-046-provider-network-local", - "topologicalRank": 11, - "wave": "W6" + "specId" : "ADR-046-provider-network-local", + "topologicalRank" : 11, + "wave" : "W6" }, { - "blockers": [], - "destinations": [ + "blockers" : [], + "destinations" : [ "Core LaunchTicket builder and dependency resolver that walks `Guest.ownerRef: Network/` to resolved tap FDs." ], - "entryContracts": [ + "entryContracts" : [ "ADR-046-provider-network-local" ], - "exitGate": "ADR046-W6 exit criteria (ADR-046-validation-and-delivery §4): every spec/work item in this wave Merged with clean destinations, all validators green, and the ten-role panel seal recorded", - "id": "ADR046-nl-004", - "kind": "work-item", - "owner": "Core; depends on ADR046-nl-003 handles and runtime-cloud-hypervisor LaunchTicket consumption.", - "parallelGroup": "wi:ADR-046-provider-network-local", - "prerequisites": [ + "exitGate" : "ADR046-W6 exit criteria (ADR-046-validation-and-delivery §4): every spec/work item in this wave Merged with clean destinations, all validators green, and the ten-role panel seal recorded", + "id" : "ADR046-nl-004", + "kind" : "work-item", + "owner" : "Core; depends on ADR046-nl-003 handles and runtime-cloud-hypervisor LaunchTicket consumption.", + "parallelGroup" : "wi:ADR-046-provider-network-local", + "prerequisites" : [ "ADR-046-provider-network-local", "ADR046-nl-003" ], - "specId": "ADR-046-provider-network-local", - "topologicalRank": 12, - "wave": "W6" + "specId" : "ADR-046-provider-network-local", + "topologicalRank" : 12, + "wave" : "W6" }, { - "blockers": [], - "destinations": [ + "blockers" : [], + "destinations" : [ "Core adapter imports `d2b-host` modules; `packages/d2b-provider-network-local/src/ifname.rs` re-exports `d2b_host::ifname::derive_ifname` only." ], - "entryContracts": [ + "entryContracts" : [ "ADR-046-provider-network-local" ], - "exitGate": "ADR046-W6 exit criteria (ADR-046-validation-and-delivery §4): every spec/work item in this wave Merged with clean destinations, all validators green, and the ten-role panel seal recorded", - "id": "ADR046-nl-005", - "kind": "work-item", - "owner": "Provider plus Core; provider validates through `d2b_host::ifname::derive_ifname`, while core adapter consumes host networking modules.", - "parallelGroup": "wi:ADR-046-provider-network-local", - "prerequisites": [ + "exitGate" : "ADR046-W6 exit criteria (ADR-046-validation-and-delivery §4): every spec/work item in this wave Merged with clean destinations, all validators green, and the ten-role panel seal recorded", + "id" : "ADR046-nl-005", + "kind" : "work-item", + "owner" : "Provider plus Core; provider validates through `d2b_host::ifname::derive_ifname`, while core adapter consumes host networking modules.", + "parallelGroup" : "wi:ADR-046-provider-network-local", + "prerequisites" : [ "ADR-046-provider-network-local" ], - "specId": "ADR-046-provider-network-local", - "topologicalRank": 11, - "wave": "W6" + "specId" : "ADR-046-provider-network-local", + "topologicalRank" : 11, + "wave" : "W6" }, { - "blockers": [], - "destinations": [ + "blockers" : [], + "destinations" : [ "`packages/d2b-provider-network-local/src/controller.rs`." ], - "entryContracts": [ + "entryContracts" : [ "ADR-046-provider-network-local" ], - "exitGate": "ADR046-W6 exit criteria (ADR-046-validation-and-delivery §4): every spec/work item in this wave Merged with clean destinations, all validators green, and the ten-role panel seal recorded", - "id": "ADR046-nl-006", - "kind": "work-item", - "owner": "Provider; depends on ADR046-nl-001 through ADR046-nl-005 and owns the Network reconcile/observe/finalize handlers.", - "parallelGroup": "wi:ADR-046-provider-network-local", - "prerequisites": [ + "exitGate" : "ADR046-W6 exit criteria (ADR-046-validation-and-delivery §4): every spec/work item in this wave Merged with clean destinations, all validators green, and the ten-role panel seal recorded", + "id" : "ADR046-nl-006", + "kind" : "work-item", + "owner" : "Provider; depends on ADR046-nl-001 through ADR046-nl-005 and owns the Network reconcile/observe/finalize handlers.", + "parallelGroup" : "wi:ADR-046-provider-network-local", + "prerequisites" : [ "ADR-046-provider-network-local", "ADR046-nl-001", + "ADR046-nl-002", + "ADR046-nl-003", + "ADR046-nl-004", "ADR046-nl-005" ], - "specId": "ADR-046-provider-network-local", - "topologicalRank": 12, - "wave": "W6" + "specId" : "ADR-046-provider-network-local", + "topologicalRank" : 13, + "wave" : "W6" }, { - "blockers": [], - "destinations": [ + "blockers" : [], + "destinations" : [ "`packages/d2b-provider-network-local/src/process_specs.rs` agent template plus agent service implementation in the net-VM artifact." ], - "entryContracts": [ + "entryContracts" : [ "ADR-046-provider-network-local" ], - "exitGate": "ADR046-W6 exit criteria (ADR-046-validation-and-delivery §4): every spec/work item in this wave Merged with clean destinations, all validators green, and the ten-role panel seal recorded", - "id": "ADR046-nl-007", - "kind": "work-item", - "owner": "Provider; owns net-agent ComponentSession service and depends on ComponentSession/bus and net-VM Process lifecycle.", - "parallelGroup": "wi:ADR-046-provider-network-local", - "prerequisites": [ + "exitGate" : "ADR046-W6 exit criteria (ADR-046-validation-and-delivery §4): every spec/work item in this wave Merged with clean destinations, all validators green, and the ten-role panel seal recorded", + "id" : "ADR046-nl-007", + "kind" : "work-item", + "owner" : "Provider; owns net-agent ComponentSession service and depends on ComponentSession/bus and net-VM Process lifecycle.", + "parallelGroup" : "wi:ADR-046-provider-network-local", + "prerequisites" : [ "ADR-046-provider-network-local" ], - "specId": "ADR-046-provider-network-local", - "topologicalRank": 11, - "wave": "W6" + "specId" : "ADR-046-provider-network-local", + "topologicalRank" : 11, + "wave" : "W6" }, { - "blockers": [], - "destinations": [ + "blockers" : [], + "destinations" : [ "`packages/d2b-provider-network-local/src/config_volume.rs`." ], - "entryContracts": [ + "entryContracts" : [ "ADR-046-provider-network-local" ], - "exitGate": "ADR046-W6 exit criteria (ADR-046-validation-and-delivery §4): every spec/work item in this wave Merged with clean destinations, all validators green, and the ten-role panel seal recorded", - "id": "ADR046-nl-008", - "kind": "work-item", - "owner": "Provider; config rendering owned by `Provider/network-local`, storage reconciliation owned by `Provider/volume-local`.", - "parallelGroup": "wi:ADR-046-provider-network-local", - "prerequisites": [ + "exitGate" : "ADR046-W6 exit criteria (ADR-046-validation-and-delivery §4): every spec/work item in this wave Merged with clean destinations, all validators green, and the ten-role panel seal recorded", + "id" : "ADR046-nl-008", + "kind" : "work-item", + "owner" : "Provider; config rendering owned by `Provider/network-local`, storage reconciliation owned by `Provider/volume-local`.", + "parallelGroup" : "wi:ADR-046-provider-network-local", + "prerequisites" : [ "ADR-046-provider-network-local" ], - "specId": "ADR-046-provider-network-local", - "topologicalRank": 11, - "wave": "W6" + "specId" : "ADR-046-provider-network-local", + "topologicalRank" : 11, + "wave" : "W6" }, { - "blockers": [], - "destinations": [ - "`packages/d2b-provider-notification-desktop/src/{types,redact,action_nonce}.rs`" + "blockers" : [], + "destinations" : [ + "`packages/d2b-provider-network-local/src/process_specs.rs`." ], - "entryContracts": [ - "ADR-046-provider-notification-desktop" + "entryContracts" : [ + "ADR-046-provider-network-local" ], - "exitGate": "ADR046-W6 exit criteria (ADR-046-validation-and-delivery §4): every spec/work item in this wave Merged with clean destinations, all validators green, and the ten-role panel seal recorded", - "id": "ADR046-notify-001", - "kind": "work-item", - "owner": "W0 shared contract root; session/bus owner", - "parallelGroup": "wi:ADR-046-provider-notification-desktop", - "prerequisites": [ - "ADR-046-provider-notification-desktop" + "exitGate" : "ADR046-W6 exit criteria (ADR-046-validation-and-delivery §4): every spec/work item in this wave Merged with clean destinations, all validators green, and the ten-role panel seal recorded", + "id" : "ADR046-nl-009", + "kind" : "work-item", + "owner" : "Provider; Process resource builders owned by `d2b-provider-network-local`.", + "parallelGroup" : "wi:ADR-046-provider-network-local", + "prerequisites" : [ + "ADR-046-provider-network-local" ], - "specId": "ADR-046-provider-notification-desktop", - "topologicalRank": 11, - "wave": "W6" + "specId" : "ADR-046-provider-network-local", + "topologicalRank" : 11, + "wave" : "W6" }, { - "blockers": [], - "destinations": [ - "`packages/d2b-provider-notification-desktop/src/stream_admission.rs`" + "blockers" : [], + "destinations" : [ + "`net-vm-base` nixos-system artifact and artifact catalog entry `d2b.artifacts.net-vm-base`." ], - "entryContracts": [ - "ADR-046-provider-notification-desktop" + "entryContracts" : [ + "ADR-046-provider-network-local" ], - "exitGate": "ADR046-W6 exit criteria (ADR-046-validation-and-delivery §4): every spec/work item in this wave Merged with clean destinations, all validators green, and the ten-role panel seal recorded", - "id": "ADR046-notify-002", - "kind": "work-item", - "owner": "ADR046-session-001, ADR046-bus-001; session/bus wiring", - "parallelGroup": "wi:ADR-046-provider-notification-desktop", - "prerequisites": [ - "ADR-046-provider-notification-desktop", - "ADR046-bus-001", - "ADR046-session-001" + "exitGate" : "ADR046-W6 exit criteria (ADR-046-validation-and-delivery §4): every spec/work item in this wave Merged with clean destinations, all validators green, and the ten-role panel seal recorded", + "id" : "ADR046-nl-010", + "kind" : "work-item", + "owner" : "net-vm artifact; owns generic `net-vm-base` nixos-system artifact and shared net-local-controller UID/GID reservation.", + "parallelGroup" : "wi:ADR-046-provider-network-local", + "prerequisites" : [ + "ADR-046-provider-network-local" ], - "specId": "ADR-046-provider-notification-desktop", - "topologicalRank": 11, - "wave": "W6" + "specId" : "ADR-046-provider-network-local", + "topologicalRank" : 11, + "wave" : "W6" }, { - "blockers": [], - "destinations": [ - "`packages/d2b-provider-notification-desktop/src/controller.rs`" + "blockers" : [], + "destinations" : [ + "Nix module resource emission for `Provider/network-local`, `User/net-local-controller`, host OS account, `provider-network-local`, and `net-vm-base` artifacts." ], - "entryContracts": [ - "ADR-046-provider-notification-desktop" + "entryContracts" : [ + "ADR-046-provider-network-local" ], - "exitGate": "ADR046-W6 exit criteria (ADR-046-validation-and-delivery §4): every spec/work item in this wave Merged with clean destinations, all validators green, and the ten-role panel seal recorded", - "id": "ADR046-notify-003", - "kind": "work-item", - "owner": "ADR046-notify-001, ADR046-reconcile-001; controller owner", - "parallelGroup": "wi:ADR-046-provider-notification-desktop", - "prerequisites": [ - "ADR-046-provider-notification-desktop", - "ADR046-notify-001", - "ADR046-reconcile-001" + "exitGate" : "ADR046-W6 exit criteria (ADR-046-validation-and-delivery §4): every spec/work item in this wave Merged with clean destinations, all validators green, and the ten-role panel seal recorded", + "id" : "ADR046-nl-011", + "kind" : "work-item", + "owner" : "Nix; owns resource declaration, User declaration, host account provisioning, and artifact catalog wiring.", + "parallelGroup" : "wi:ADR-046-provider-network-local", + "prerequisites" : [ + "ADR-046-provider-network-local" ], - "specId": "ADR-046-provider-notification-desktop", - "topologicalRank": 12, - "wave": "W6" + "specId" : "ADR-046-provider-network-local", + "topologicalRank" : 11, + "wave" : "W6" }, { - "blockers": [], - "destinations": [ - "`packages/d2b-provider-notification-desktop/src/host_sink.rs`" + "blockers" : [], + "destinations" : [ + "Nix flake/resource schema checks for declared Networks and provider `validate.rs` parity." ], - "entryContracts": [ - "ADR-046-provider-notification-desktop" + "entryContracts" : [ + "ADR-046-provider-network-local" ], - "exitGate": "ADR046-W6 exit criteria (ADR-046-validation-and-delivery §4): every spec/work item in this wave Merged with clean destinations, all validators green, and the ten-role panel seal recorded", - "id": "ADR046-notify-004", - "kind": "work-item", - "owner": "ADR046-notify-002, ADR046-notify-003; host-sink owner", - "parallelGroup": "wi:ADR-046-provider-notification-desktop", - "prerequisites": [ - "ADR-046-provider-notification-desktop", - "ADR046-notify-002", - "ADR046-notify-003" + "exitGate" : "ADR046-W6 exit criteria (ADR-046-validation-and-delivery §4): every spec/work item in this wave Merged with clean destinations, all validators green, and the ten-role panel seal recorded", + "id" : "ADR046-nl-012", + "kind" : "work-item", + "owner" : "Nix; depends on Network resource schema and CIDR validation rules.", + "parallelGroup" : "wi:ADR-046-provider-network-local", + "prerequisites" : [ + "ADR-046-provider-network-local" ], - "specId": "ADR-046-provider-notification-desktop", - "topologicalRank": 13, - "wave": "W6" + "specId" : "ADR-046-provider-network-local", + "topologicalRank" : 11, + "wave" : "W6" }, { - "blockers": [], - "destinations": [ - "`packages/d2b-provider-notification-desktop/src/guest_source.rs`" + "blockers" : [], + "destinations" : [ + "`packages/d2b-provider-network-local/tests/schema_roundtrip.rs`, `tests/ifname_derive.rs`, and `tests/cidr_overlap.rs`." ], - "entryContracts": [ - "ADR-046-provider-notification-desktop" + "entryContracts" : [ + "ADR-046-provider-network-local" ], - "exitGate": "ADR046-W6 exit criteria (ADR-046-validation-and-delivery §4): every spec/work item in this wave Merged with clean destinations, all validators green, and the ten-role panel seal recorded", - "id": "ADR046-notify-005", - "kind": "work-item", - "owner": "ADR046-notify-002; guest-source owner", - "parallelGroup": "wi:ADR-046-provider-notification-desktop", - "prerequisites": [ - "ADR-046-provider-notification-desktop", - "ADR046-notify-002" + "exitGate" : "ADR046-W6 exit criteria (ADR-046-validation-and-delivery §4): every spec/work item in this wave Merged with clean destinations, all validators green, and the ten-role panel seal recorded", + "id" : "ADR046-nl-013", + "kind" : "work-item", + "owner" : "Tests; owned by `d2b-provider-network-local` hermetic test suite.", + "parallelGroup" : "wi:ADR-046-provider-network-local", + "prerequisites" : [ + "ADR-046-provider-network-local" ], - "specId": "ADR-046-provider-notification-desktop", - "topologicalRank": 12, - "wave": "W6" + "specId" : "ADR-046-provider-network-local", + "topologicalRank" : 11, + "wave" : "W6" }, { - "blockers": [], - "destinations": [ - "Nix: Zone resource authoring in `nixos-modules/`; metrics: `packages/d2b-provider-notification-desktop/src/`" + "blockers" : [], + "destinations" : [ + "`packages/d2b-provider-network-local/tests/controller_state.rs`." ], - "entryContracts": [ - "ADR-046-provider-notification-desktop" + "entryContracts" : [ + "ADR-046-provider-network-local" ], - "exitGate": "ADR046-W6 exit criteria (ADR-046-validation-and-delivery §4): every spec/work item in this wave Merged with clean destinations, all validators green, and the ten-role panel seal recorded", - "id": "ADR046-notify-006", - "kind": "work-item", - "owner": "ADR046-notify-001; Nix/telemetry owner", - "parallelGroup": "wi:ADR-046-provider-notification-desktop", - "prerequisites": [ - "ADR-046-provider-notification-desktop", - "ADR046-notify-001" + "exitGate" : "ADR046-W6 exit criteria (ADR-046-validation-and-delivery §4): every spec/work item in this wave Merged with clean destinations, all validators green, and the ten-role panel seal recorded", + "id" : "ADR046-nl-014", + "kind" : "work-item", + "owner" : "Tests; depends on ADR046-nl-006 controller and fake `NetworkEffectPort` from `d2b-contracts`.", + "parallelGroup" : "wi:ADR-046-provider-network-local", + "prerequisites" : [ + "ADR-046-provider-network-local", + "ADR046-nl-006" ], - "specId": "ADR-046-provider-notification-desktop", - "topologicalRank": 12, - "wave": "W6" + "specId" : "ADR-046-provider-network-local", + "topologicalRank" : 14, + "wave" : "W6" }, { - "blockers": [], - "destinations": [ - "`packages/d2b-contracts/src/v3/resource.rs`, `resource_status.rs`, `resource_schema.rs`" + "blockers" : [], + "destinations" : [ + "`packages/d2b-provider-network-local/integration/host_fabric.rs`, `guest_lifecycle.rs`, `agent_reload.rs`, and `delete_sequence.rs`." ], - "entryContracts": [ - "ADR-046-resource-object-model" + "entryContracts" : [ + "ADR-046-provider-network-local" ], - "exitGate": "ADR046-W0 exit criteria (ADR-046-validation-and-delivery §4): every spec/work item in this wave Merged with clean destinations, all validators green, and the ten-role panel seal recorded", - "id": "ADR046-object-001", - "kind": "work-item", - "owner": "W0 shared contract root; `d2b-contracts`", - "parallelGroup": "wi:ADR-046-resource-object-model", - "prerequisites": [ - "ADR-046-resource-object-model" + "exitGate" : "ADR046-W6 exit criteria (ADR-046-validation-and-delivery §4): every spec/work item in this wave Merged with clean destinations, all validators green, and the ten-role panel seal recorded", + "id" : "ADR046-nl-015", + "kind" : "work-item", + "owner" : "Tests; integration coverage for the complete Network lifecycle.", + "parallelGroup" : "wi:ADR-046-provider-network-local", + "prerequisites" : [ + "ADR-046-provider-network-local" ], - "specId": "ADR-046-resource-object-model", - "topologicalRank": 3, - "wave": "W0" + "specId" : "ADR-046-provider-network-local", + "topologicalRank" : 11, + "wave" : "W6" }, { - "blockers": [], - "destinations": [ - "`packages/d2b-resource-store-redb/src/ownership.rs`, `packages/d2b-controller-toolkit/src/owner_hints.rs`" + "blockers" : [], + "destinations" : [ + "Process templates for agent and dnsmasq plus sandbox/eval tests." ], - "entryContracts": [ - "ADR-046-resource-object-model" + "entryContracts" : [ + "ADR-046-provider-network-local" ], - "exitGate": "ADR046-W0 exit criteria (ADR-046-validation-and-delivery §4): every spec/work item in this wave Merged with clean destinations, all validators green, and the ten-role panel seal recorded", - "id": "ADR046-object-002", - "kind": "work-item", - "owner": "ADR046-object-001; native resource store", - "parallelGroup": "wi:ADR-046-resource-object-model", - "prerequisites": [ - "ADR-046-resource-object-model", - "ADR046-object-001" + "exitGate" : "ADR046-W6 exit criteria (ADR-046-validation-and-delivery §4): every spec/work item in this wave Merged with clean destinations, all validators green, and the ten-role panel seal recorded", + "id" : "ADR046-nl-016", + "kind" : "work-item", + "owner" : "Security; depends on Process sandbox schema and `Provider/system-minijail` guest namespace inheritance.", + "parallelGroup" : "wi:ADR-046-provider-network-local", + "prerequisites" : [ + "ADR-046-provider-network-local" ], - "specId": "ADR-046-resource-object-model", - "topologicalRank": 4, - "wave": "W0" + "specId" : "ADR-046-provider-network-local", + "topologicalRank" : 11, + "wave" : "W6" }, { - "blockers": [], - "destinations": [ - "`packages/d2b-provider-observability-otel/src/{forwarder_bin,controller,binding}.rs`" + "blockers" : [], + "destinations" : [ + "`packages/d2b-provider-network-local/README.md`." ], - "entryContracts": [ - "ADR-046-provider-observability-otel" + "entryContracts" : [ + "ADR-046-provider-network-local" ], - "exitGate": "ADR046-W6 exit criteria (ADR-046-validation-and-delivery §4): every spec/work item in this wave Merged with clean destinations, all validators green, and the ten-role panel seal recorded", - "id": "ADR046-otel-001", - "kind": "work-item", - "owner": "ADR046-telem-001 (BoundedEmitter crate); W0/W2; telemetry/observability owner", - "parallelGroup": "wi:ADR-046-provider-observability-otel", - "prerequisites": [ - "ADR-046-provider-observability-otel", - "ADR046-telem-001" + "exitGate" : "ADR046-W6 exit criteria (ADR-046-validation-and-delivery §4): every spec/work item in this wave Merged with clean destinations, all validators green, and the ten-role panel seal recorded", + "id" : "ADR046-nl-017", + "kind" : "work-item", + "owner" : "Docs; owned by `d2b-provider-network-local` package documentation.", + "parallelGroup" : "wi:ADR-046-provider-network-local", + "prerequisites" : [ + "ADR-046-provider-network-local" ], - "specId": "ADR-046-provider-observability-otel", - "topologicalRank": 11, - "wave": "W6" + "specId" : "ADR-046-provider-network-local", + "topologicalRank" : 11, + "wave" : "W6" }, { - "blockers": [], - "destinations": [ - "`packages/d2b-provider-observability-otel/src/{collector_bin,emitter_socket,exporter,controller,service,binding}.rs`; updated Nix observability modules" + "blockers" : [], + "destinations" : [ + "Device-usbip EffectPort/adapter owns USBIP rules, drift, and strict provider status; network-local host/net-VM renderers and status cover only Network-owned policy." ], - "entryContracts": [ - "ADR-046-provider-observability-otel" + "entryContracts" : [ + "ADR-046-provider-network-local" ], - "exitGate": "ADR046-W6 exit criteria (ADR-046-validation-and-delivery §4): every spec/work item in this wave Merged with clean destinations, all validators green, and the ten-role panel seal recorded", - "id": "ADR046-otel-002", - "kind": "work-item", - "owner": "ADR046-otel-001 + ADR046-telem-001 + ADR046-provider-001 (Provider toolkit) + ADR046-provider-004 (common telemetry Service/Binding base) + resource/Endpoint/Volume contracts; W2; observability owner", - "parallelGroup": "wi:ADR-046-provider-observability-otel", - "prerequisites": [ - "ADR-046-provider-observability-otel", - "ADR046-otel-001", - "ADR046-provider-001", - "ADR046-provider-004", - "ADR046-telem-001" + "exitGate" : "ADR046-W6 exit criteria (ADR-046-validation-and-delivery §4): every spec/work item in this wave Merged with clean destinations, all validators green, and the ten-role panel seal recorded", + "id" : "ADR046-nl-018", + "kind" : "work-item", + "owner" : "Broker plus device provider boundary; `UsbipBindFirewallRule` remains owned by `Provider/device-usbip`.", + "parallelGroup" : "wi:ADR-046-provider-network-local", + "prerequisites" : [ + "ADR-046-provider-network-local" ], - "specId": "ADR-046-provider-observability-otel", - "topologicalRank": 12, - "wave": "W6" + "specId" : "ADR-046-provider-network-local", + "topologicalRank" : 11, + "wave" : "W6" }, { - "blockers": [], - "destinations": [ - "`packages/d2b-provider-observability-otel/src/nix/journald.nix`; `packages/d2b-provider-observability-otel/src/journald.rs`" + "blockers" : [], + "destinations" : [ + "Provider descriptor, controller-main deployment, `tests/state_schema_roundtrip.rs`, and eval case `provider-state-volume-eval.nix`." ], - "entryContracts": [ - "ADR-046-provider-observability-otel" + "entryContracts" : [ + "ADR-046-provider-network-local" ], - "exitGate": "ADR046-W6 exit criteria (ADR-046-validation-and-delivery §4): every spec/work item in this wave Merged with clean destinations, all validators green, and the ten-role panel seal recorded", - "id": "ADR046-otel-003", - "kind": "work-item", - "owner": "ADR046-otel-002 + ADR046-telem-001 + ADR046-volume-001; Nix/observability owner", - "parallelGroup": "wi:ADR-046-provider-observability-otel", - "prerequisites": [ - "ADR-046-provider-observability-otel", - "ADR046-otel-002", - "ADR046-telem-001", - "ADR046-volume-001" + "exitGate" : "ADR046-W6 exit criteria (ADR-046-validation-and-delivery §4): every spec/work item in this wave Merged with clean destinations, all validators green, and the ten-role panel seal recorded", + "id" : "ADR046-nl-019", + "kind" : "work-item", + "owner" : "Provider; depends on D087 ProviderStateSet and status-first storage rules.", + "parallelGroup" : "wi:ADR-046-provider-network-local", + "prerequisites" : [ + "ADR-046-provider-network-local" ], - "specId": "ADR-046-provider-observability-otel", - "topologicalRank": 13, - "wave": "W6" + "specId" : "ADR-046-provider-network-local", + "topologicalRank" : 11, + "wave" : "W6" }, { - "blockers": [], - "destinations": [ - "`packages/d2b-contract-tests/tests/policy_observability.rs` (updated); `packages/d2b-contract-tests/tests/policy_telemetry_redaction.rs` (new, per ADR046-telem-008); `packages/d2b-provider-observability-otel/tests/no_vm_label_in_metrics.rs`" + "blockers" : [], + "destinations" : [ + "Network schema/Provider descriptor, Core authority index, Network reconcile/update/finalizer, runtime LaunchTicket resolver, and authority tests" ], - "entryContracts": [ - "ADR-046-provider-observability-otel" + "entryContracts" : [ + "ADR-046-provider-network-local" ], - "exitGate": "ADR046-W6 exit criteria (ADR-046-validation-and-delivery §4): every spec/work item in this wave Merged with clean destinations, all validators green, and the ten-role panel seal recorded", - "id": "ADR046-otel-004", - "kind": "work-item", - "owner": "ADR046-otel-002; policy/contract-tests owner", - "parallelGroup": "wi:ADR-046-provider-observability-otel", - "prerequisites": [ - "ADR-046-provider-observability-otel", - "ADR046-otel-002" + "exitGate" : "ADR046-W6 exit criteria (ADR-046-validation-and-delivery §4): every spec/work item in this wave Merged with clean destinations, all validators green, and the ten-role panel seal recorded", + "id" : "ADR046-nl-020", + "kind" : "work-item", + "owner" : "D097 Core authority index, Network contracts, runtime-cloud-hypervisor private attachment path", + "parallelGroup" : "wi:ADR-046-provider-network-local", + "prerequisites" : [ + "ADR-046-provider-network-local" ], - "specId": "ADR-046-provider-observability-otel", - "topologicalRank": 13, - "wave": "W6" + "specId" : "ADR-046-provider-network-local", + "topologicalRank" : 11, + "wave" : "W6" }, { - "blockers": [], - "destinations": [ - "`packages/d2b-provider-observability-otel/src/share_adapter.rs`" + "blockers" : [], + "destinations" : [ + "`packages/d2b-provider-notification-desktop/src/{types,redact,action_nonce}.rs`" ], - "entryContracts": [ - "ADR-046-provider-observability-otel" + "entryContracts" : [ + "ADR-046-provider-notification-desktop" ], - "exitGate": "ADR046-W6 exit criteria (ADR-046-validation-and-delivery §4): every spec/work item in this wave Merged with clean destinations, all validators green, and the ten-role panel seal recorded", - "id": "ADR046-otel-005", - "kind": "work-item", - "owner": "ADR046-zone-control-019, ADR046-zone-control-020; observability Provider owner", - "parallelGroup": "wi:ADR-046-provider-observability-otel", - "prerequisites": [ - "ADR-046-provider-observability-otel", - "ADR046-zone-control-019", - "ADR046-zone-control-020" + "exitGate" : "ADR046-W6 exit criteria (ADR-046-validation-and-delivery §4): every spec/work item in this wave Merged with clean destinations, all validators green, and the ten-role panel seal recorded", + "id" : "ADR046-notify-001", + "kind" : "work-item", + "owner" : "W0 shared contract root; session/bus owner", + "parallelGroup" : "wi:ADR-046-provider-notification-desktop", + "prerequisites" : [ + "ADR-046-provider-notification-desktop" ], - "specId": "ADR-046-provider-observability-otel", - "topologicalRank": 11, - "wave": "W6" + "specId" : "ADR-046-provider-notification-desktop", + "topologicalRank" : 11, + "wave" : "W6" }, { - "blockers": [], - "destinations": [ - "`packages/d2b-provider-observability-otel/src/{authority,service,binding,projection}.rs`; `AuthorityDescriptor` on the `sys-obs` `TelemetryService`" + "blockers" : [], + "destinations" : [ + "`packages/d2b-provider-notification-desktop/src/stream_admission.rs`" ], - "entryContracts": [ - "ADR-046-provider-observability-otel" + "entryContracts" : [ + "ADR-046-provider-notification-desktop" ], - "exitGate": "ADR046-W6 exit criteria (ADR-046-validation-and-delivery §4): every spec/work item in this wave Merged with clean destinations, all validators green, and the ten-role panel seal recorded", - "id": "ADR046-otel-006", - "kind": "work-item", - "owner": "ADR046-otel-005, ADR046-zone-control-019, ADR046-zone-control-020; observability Provider owner", - "parallelGroup": "wi:ADR-046-provider-observability-otel", - "prerequisites": [ - "ADR-046-provider-observability-otel", - "ADR046-otel-005", - "ADR046-zone-control-019", - "ADR046-zone-control-020" + "exitGate" : "ADR046-W6 exit criteria (ADR-046-validation-and-delivery §4): every spec/work item in this wave Merged with clean destinations, all validators green, and the ten-role panel seal recorded", + "id" : "ADR046-notify-002", + "kind" : "work-item", + "owner" : "ADR046-session-001, ADR046-session-003; session/bus wiring", + "parallelGroup" : "wi:ADR-046-provider-notification-desktop", + "prerequisites" : [ + "ADR-046-provider-notification-desktop", + "ADR046-session-001", + "ADR046-session-003" ], - "specId": "ADR-046-provider-observability-otel", - "topologicalRank": 12, - "wave": "W6" + "specId" : "ADR-046-provider-notification-desktop", + "topologicalRank" : 11, + "wave" : "W6" }, { - "blockers": [], - "destinations": [ - "`packages/d2b-contract-tests/tests/policy_provider_crate_layout.rs` (new file; gated under D2B_FIXTURES in existing `tests/tools/rust-workspace-checks.sh`)" + "blockers" : [], + "destinations" : [ + "`packages/d2b-provider-notification-desktop/src/controller.rs`" ], - "entryContracts": [ - "ADR-046-resources-zone-control" + "entryContracts" : [ + "ADR-046-provider-notification-desktop" ], - "exitGate": "ADR046-W5 exit criteria (ADR-046-validation-and-delivery §4): every spec/work item in this wave Merged with clean destinations, all validators green, and the ten-role panel seal recorded", - "id": "ADR046-pkg-001", - "kind": "work-item", - "owner": "ADR046-zone-control-003; workspace policy owner", - "parallelGroup": "wi:ADR-046-resources-zone-control", - "prerequisites": [ - "ADR-046-resources-zone-control" + "exitGate" : "ADR046-W6 exit criteria (ADR-046-validation-and-delivery §4): every spec/work item in this wave Merged with clean destinations, all validators green, and the ten-role panel seal recorded", + "id" : "ADR046-notify-003", + "kind" : "work-item", + "owner" : "ADR046-notify-001, ADR046-reconcile-001; controller owner", + "parallelGroup" : "wi:ADR-046-provider-notification-desktop", + "prerequisites" : [ + "ADR-046-provider-notification-desktop", + "ADR046-notify-001", + "ADR046-reconcile-001" ], - "specId": "ADR-046-resources-zone-control", - "topologicalRank": 10, - "wave": "W5" + "specId" : "ADR-046-provider-notification-desktop", + "topologicalRank" : 12, + "wave" : "W6" }, { - "blockers": [], - "destinations": [ - "`packages/d2b-contracts/src/v3/host.rs`, `guest.rs`, `execution_policy.rs`, `process.rs`, `volume.rs`, `user.rs`, `network.rs`, `device.rs`, `credential.rs`" + "blockers" : [], + "destinations" : [ + "`packages/d2b-provider-notification-desktop/src/host_sink.rs`" ], - "entryContracts": [ - "ADR-046-primitive-resource-composition" + "entryContracts" : [ + "ADR-046-provider-notification-desktop" ], - "exitGate": "ADR046-W2 exit criteria (ADR-046-validation-and-delivery §4): every spec/work item in this wave Merged with clean destinations, all validators green, and the ten-role panel seal recorded", - "id": "ADR046-primitives-001", - "kind": "work-item", - "owner": "W0; resource contracts", - "parallelGroup": "wi:ADR-046-primitive-resource-composition", - "prerequisites": [ - "ADR-046-primitive-resource-composition" + "exitGate" : "ADR046-W6 exit criteria (ADR-046-validation-and-delivery §4): every spec/work item in this wave Merged with clean destinations, all validators green, and the ten-role panel seal recorded", + "id" : "ADR046-notify-004", + "kind" : "work-item", + "owner" : "ADR046-notify-002, ADR046-notify-003; host-sink owner", + "parallelGroup" : "wi:ADR-046-provider-notification-desktop", + "prerequisites" : [ + "ADR-046-provider-notification-desktop", + "ADR046-notify-002", + "ADR046-notify-003" ], - "specId": "ADR-046-primitive-resource-composition", - "topologicalRank": 7, - "wave": "W2" + "specId" : "ADR-046-provider-notification-desktop", + "topologicalRank" : 13, + "wave" : "W6" }, { - "blockers": [], - "destinations": [ - "`packages/d2b-provider-system-systemd/`, `packages/d2b-provider-system-minijail/`, shared neutral process conformance library" + "blockers" : [], + "destinations" : [ + "`packages/d2b-provider-notification-desktop/src/guest_source.rs`" ], - "entryContracts": [ - "ADR-046-primitive-resource-composition" + "entryContracts" : [ + "ADR-046-provider-notification-desktop" ], - "exitGate": "ADR046-W2 exit criteria (ADR-046-validation-and-delivery §4): every spec/work item in this wave Merged with clean destinations, all validators green, and the ten-role panel seal recorded", - "id": "ADR046-primitives-002", - "kind": "work-item", - "owner": "Process contracts; system Provider slices", - "parallelGroup": "wi:ADR-046-primitive-resource-composition", - "prerequisites": [ - "ADR-046-primitive-resource-composition" + "exitGate" : "ADR046-W6 exit criteria (ADR-046-validation-and-delivery §4): every spec/work item in this wave Merged with clean destinations, all validators green, and the ten-role panel seal recorded", + "id" : "ADR046-notify-005", + "kind" : "work-item", + "owner" : "ADR046-notify-002; guest-source owner", + "parallelGroup" : "wi:ADR-046-provider-notification-desktop", + "prerequisites" : [ + "ADR-046-provider-notification-desktop", + "ADR046-notify-002" ], - "specId": "ADR-046-primitive-resource-composition", - "topologicalRank": 7, - "wave": "W2" + "specId" : "ADR-046-provider-notification-desktop", + "topologicalRank" : 12, + "wave" : "W6" }, { - "blockers": [], - "destinations": [ - "`packages/d2b-provider-volume-*/`; `nixos-modules/resources-volume.nix`" + "blockers" : [], + "destinations" : [ + "Nix: Zone resource authoring in `nixos-modules/`; metrics: `packages/d2b-provider-notification-desktop/src/`" ], - "entryContracts": [ - "ADR-046-primitive-resource-composition" + "entryContracts" : [ + "ADR-046-provider-notification-desktop" ], - "exitGate": "ADR046-W2 exit criteria (ADR-046-validation-and-delivery §4): every spec/work item in this wave Merged with clean destinations, all validators green, and the ten-role panel seal recorded", - "id": "ADR046-primitives-003", - "kind": "work-item", - "owner": "Volume contract; Volume Provider slices", - "parallelGroup": "wi:ADR-046-primitive-resource-composition", - "prerequisites": [ - "ADR-046-primitive-resource-composition" + "exitGate" : "ADR046-W6 exit criteria (ADR-046-validation-and-delivery §4): every spec/work item in this wave Merged with clean destinations, all validators green, and the ten-role panel seal recorded", + "id" : "ADR046-notify-006", + "kind" : "work-item", + "owner" : "ADR046-notify-001; Nix/telemetry owner", + "parallelGroup" : "wi:ADR-046-provider-notification-desktop", + "prerequisites" : [ + "ADR-046-provider-notification-desktop", + "ADR046-notify-001" ], - "specId": "ADR-046-primitive-resource-composition", - "topologicalRank": 7, - "wave": "W2" + "specId" : "ADR-046-provider-notification-desktop", + "topologicalRank" : 12, + "wave" : "W6" }, { - "blockers": [], - "destinations": [ - "`packages/d2b-process/src/`, `packages/d2b-provider-supervisor/src/`" + "blockers" : [], + "destinations" : [ + "`packages/d2b-contracts/src/v3/resource.rs`, `resource_status.rs`, `resource_schema.rs`" ], - "entryContracts": [ - "ADR-046-components-processes-and-sandbox" + "entryContracts" : [ + "ADR-046-resource-object-model" ], - "exitGate": "ADR046-W4 exit criteria (ADR-046-validation-and-delivery §4): every spec/work item in this wave Merged with clean destinations, all validators green, and the ten-role panel seal recorded", - "id": "ADR046-process-001", - "kind": "work-item", - "owner": "W0/W2; Process contracts/supervisor", - "parallelGroup": "wi:ADR-046-components-processes-and-sandbox", - "prerequisites": [ - "ADR-046-components-processes-and-sandbox" + "exitGate" : "ADR046-W0 exit criteria (ADR-046-validation-and-delivery §4): every spec/work item in this wave Merged with clean destinations, all validators green, and the ten-role panel seal recorded", + "id" : "ADR046-object-001", + "kind" : "work-item", + "owner" : "W0 shared contract root; `d2b-contracts`", + "parallelGroup" : "wi:ADR-046-resource-object-model", + "prerequisites" : [ + "ADR-046-resource-object-model" ], - "specId": "ADR-046-components-processes-and-sandbox", - "topologicalRank": 9, - "wave": "W4" + "specId" : "ADR-046-resource-object-model", + "topologicalRank" : 3, + "wave" : "W0" }, { - "blockers": [], - "destinations": [ - "`packages/d2b-provider-system-systemd/`, `packages/d2b-provider-system-minijail/`" + "blockers" : [], + "destinations" : [ + "`packages/d2b-resource-store-redb/src/ownership.rs`, `packages/d2b-controller-toolkit/src/owner_hints.rs`" ], - "entryContracts": [ - "ADR-046-components-processes-and-sandbox" + "entryContracts" : [ + "ADR-046-resource-object-model" ], - "exitGate": "ADR046-W4 exit criteria (ADR-046-validation-and-delivery §4): every spec/work item in this wave Merged with clean destinations, all validators green, and the ten-role panel seal recorded", - "id": "ADR046-process-002", - "kind": "work-item", - "owner": "ADR046-process-001; systemd/minijail Provider owners", - "parallelGroup": "wi:ADR-046-components-processes-and-sandbox", - "prerequisites": [ - "ADR-046-components-processes-and-sandbox", - "ADR046-process-001" + "exitGate" : "ADR046-W0 exit criteria (ADR-046-validation-and-delivery §4): every spec/work item in this wave Merged with clean destinations, all validators green, and the ten-role panel seal recorded", + "id" : "ADR046-object-002", + "kind" : "work-item", + "owner" : "ADR046-object-001; native resource store", + "parallelGroup" : "wi:ADR-046-resource-object-model", + "prerequisites" : [ + "ADR-046-resource-object-model", + "ADR046-object-001" ], - "specId": "ADR-046-components-processes-and-sandbox", - "topologicalRank": 10, - "wave": "W4" + "specId" : "ADR-046-resource-object-model", + "topologicalRank" : 4, + "wave" : "W0" }, { - "blockers": [], - "destinations": [ - "`packages/d2b-contracts/src/v3/provider.rs`, `packages/d2b-provider/src/lib.rs`, `packages/d2b-provider-toolkit/`" + "blockers" : [], + "destinations" : [ + "`packages/d2b-provider-observability-otel/src/{forwarder_bin,controller,binding}.rs`" ], - "entryContracts": [ - "ADR-046-provider-model-and-packaging" + "entryContracts" : [ + "ADR-046-provider-observability-otel" ], - "exitGate": "ADR046-W3 exit criteria (ADR-046-validation-and-delivery §4): every spec/work item in this wave Merged with clean destinations, all validators green, and the ten-role panel seal recorded", - "id": "ADR046-provider-001", - "kind": "work-item", - "owner": "W0; Provider contract/catalog owner", - "parallelGroup": "wi:ADR-046-provider-model-and-packaging", - "prerequisites": [ - "ADR-046-provider-model-and-packaging" + "exitGate" : "ADR046-W6 exit criteria (ADR-046-validation-and-delivery §4): every spec/work item in this wave Merged with clean destinations, all validators green, and the ten-role panel seal recorded", + "id" : "ADR046-otel-001", + "kind" : "work-item", + "owner" : "ADR046-telem-001 (BoundedEmitter crate); W0/W2; telemetry/observability owner", + "parallelGroup" : "wi:ADR-046-provider-observability-otel", + "prerequisites" : [ + "ADR-046-provider-observability-otel", + "ADR046-telem-001" ], - "specId": "ADR-046-provider-model-and-packaging", - "topologicalRank": 8, - "wave": "W3" + "specId" : "ADR-046-provider-observability-otel", + "topologicalRank" : 11, + "wave" : "W6" }, { - "blockers": [], - "destinations": [ - "one `packages/d2b-provider--/` per Provider with mandatory src/, tests/, integration/, README.md; generic Nix Provider package/catalog emitter" + "blockers" : [], + "destinations" : [ + "`packages/d2b-provider-observability-otel/src/{collector_bin,emitter_socket,exporter,controller,service,binding}.rs`; updated Nix observability modules" ], - "entryContracts": [ - "ADR-046-provider-model-and-packaging" + "entryContracts" : [ + "ADR-046-provider-observability-otel" ], - "exitGate": "ADR046-W3 exit criteria (ADR-046-validation-and-delivery §4): every spec/work item in this wave Merged with clean destinations, all validators green, and the ten-role panel seal recorded", - "id": "ADR046-provider-002", - "kind": "work-item", - "owner": "Provider contract; package/Nix integrator", - "parallelGroup": "wi:ADR-046-provider-model-and-packaging", - "prerequisites": [ - "ADR-046-provider-model-and-packaging" + "exitGate" : "ADR046-W6 exit criteria (ADR-046-validation-and-delivery §4): every spec/work item in this wave Merged with clean destinations, all validators green, and the ten-role panel seal recorded", + "id" : "ADR046-otel-002", + "kind" : "work-item", + "owner" : "ADR046-otel-001 + ADR046-telem-001 + ADR046-provider-001 (Provider toolkit) + ADR046-provider-004 (common telemetry Service/Binding base) + resource/Endpoint/Volume contracts; W2; observability owner", + "parallelGroup" : "wi:ADR-046-provider-observability-otel", + "prerequisites" : [ + "ADR-046-provider-observability-otel", + "ADR046-otel-001", + "ADR046-provider-001", + "ADR046-provider-004", + "ADR046-telem-001" ], - "specId": "ADR-046-provider-model-and-packaging", - "topologicalRank": 8, - "wave": "W3" + "specId" : "ADR-046-provider-observability-otel", + "topologicalRank" : 12, + "wave" : "W6" }, { - "blockers": [], - "destinations": [ - "`packages/d2b-provider-system-core/`, `d2b-provider-system-systemd/`, `d2b-provider-system-minijail/`" + "blockers" : [], + "destinations" : [ + "`packages/d2b-provider-observability-otel/src/nix/journald.nix`; `packages/d2b-provider-observability-otel/src/journald.rs`" ], - "entryContracts": [ - "ADR-046-provider-model-and-packaging" + "entryContracts" : [ + "ADR-046-provider-observability-otel" ], - "exitGate": "ADR046-W3 exit criteria (ADR-046-validation-and-delivery §4): every spec/work item in this wave Merged with clean destinations, all validators green, and the ten-role panel seal recorded", - "id": "ADR046-provider-003", - "kind": "work-item", - "owner": "Process contracts; system Provider owners", - "parallelGroup": "wi:ADR-046-provider-model-and-packaging", - "prerequisites": [ - "ADR-046-provider-model-and-packaging" + "exitGate" : "ADR046-W6 exit criteria (ADR-046-validation-and-delivery §4): every spec/work item in this wave Merged with clean destinations, all validators green, and the ten-role panel seal recorded", + "id" : "ADR046-otel-003", + "kind" : "work-item", + "owner" : "ADR046-otel-002 + ADR046-telem-001 + ADR046-volume-001; Nix/observability owner", + "parallelGroup" : "wi:ADR-046-provider-observability-otel", + "prerequisites" : [ + "ADR-046-provider-observability-otel", + "ADR046-otel-002", + "ADR046-telem-001", + "ADR046-volume-001" + ], + "specId" : "ADR-046-provider-observability-otel", + "topologicalRank" : 13, + "wave" : "W6" + }, + { + "blockers" : [], + "destinations" : [ + "`packages/d2b-contract-tests/tests/policy_observability.rs` (updated); `packages/d2b-contract-tests/tests/policy_telemetry_redaction.rs` (new, per ADR046-telem-008); `packages/d2b-provider-observability-otel/tests/no_vm_label_in_metrics.rs`" + ], + "entryContracts" : [ + "ADR-046-provider-observability-otel" + ], + "exitGate" : "ADR046-W6 exit criteria (ADR-046-validation-and-delivery §4): every spec/work item in this wave Merged with clean destinations, all validators green, and the ten-role panel seal recorded", + "id" : "ADR046-otel-004", + "kind" : "work-item", + "owner" : "ADR046-otel-002; policy/contract-tests owner", + "parallelGroup" : "wi:ADR-046-provider-observability-otel", + "prerequisites" : [ + "ADR-046-provider-observability-otel", + "ADR046-otel-002" ], - "specId": "ADR-046-provider-model-and-packaging", - "topologicalRank": 8, - "wave": "W3" + "specId" : "ADR-046-provider-observability-otel", + "topologicalRank" : 13, + "wave" : "W6" }, { - "blockers": [], - "destinations": [ - "`packages/d2b-contracts/src/v3/semantic_services/{mod,audio,security_key,telemetry,usb}.rs`; generated schema artifacts for the eight exact qualified ResourceTypes" + "blockers" : [], + "destinations" : [ + "`packages/d2b-provider-observability-otel/src/share_adapter.rs`" ], - "entryContracts": [ - "ADR-046-provider-model-and-packaging" + "entryContracts" : [ + "ADR-046-provider-observability-otel" ], - "exitGate": "ADR046-W3 exit criteria (ADR-046-validation-and-delivery §4): every spec/work item in this wave Merged with clean destinations, all validators green, and the ten-role panel seal recorded", - "id": "ADR046-provider-004", - "kind": "work-item", - "owner": "ADR046-provider-001; shared semantic Service/Binding contract owner", - "parallelGroup": "wi:ADR-046-provider-model-and-packaging", - "prerequisites": [ - "ADR-046-provider-model-and-packaging", - "ADR046-provider-001" + "exitGate" : "ADR046-W6 exit criteria (ADR-046-validation-and-delivery §4): every spec/work item in this wave Merged with clean destinations, all validators green, and the ten-role panel seal recorded", + "id" : "ADR046-otel-005", + "kind" : "work-item", + "owner" : "ADR046-zone-control-019, ADR046-zone-control-020; observability Provider owner", + "parallelGroup" : "wi:ADR-046-provider-observability-otel", + "prerequisites" : [ + "ADR-046-provider-observability-otel", + "ADR046-zone-control-019", + "ADR046-zone-control-020" ], - "specId": "ADR-046-provider-model-and-packaging", - "topologicalRank": 9, - "wave": "W3" + "specId" : "ADR-046-provider-observability-otel", + "topologicalRank" : 13, + "wave" : "W6" }, { - "blockers": [], - "destinations": [ - "`packages/d2b-provider/src/agent.rs` (v3 provider agent dispatch); `packages/d2b-contracts/src/v3/provider_registry.rs` (v3 provider registry wire contract)" + "blockers" : [], + "destinations" : [ + "`packages/d2b-provider-observability-otel/src/{authority,service,binding,projection}.rs`; `AuthorityDescriptor` on the `sys-obs` `TelemetryService`" ], - "entryContracts": [ - "ADR-046-resources-zone-control" + "entryContracts" : [ + "ADR-046-provider-observability-otel" ], - "exitGate": "ADR046-W5 exit criteria (ADR-046-validation-and-delivery §4): every spec/work item in this wave Merged with clean destinations, all validators green, and the ten-role panel seal recorded", - "id": "ADR046-provider-agent-001", - "kind": "work-item", - "owner": "ADR046-zone-control-011, ADR046-zone-control-017, ADR046-zone-control-018", - "parallelGroup": "wi:ADR-046-resources-zone-control", - "prerequisites": [ - "ADR-046-resources-zone-control" + "exitGate" : "ADR046-W6 exit criteria (ADR-046-validation-and-delivery §4): every spec/work item in this wave Merged with clean destinations, all validators green, and the ten-role panel seal recorded", + "id" : "ADR046-otel-006", + "kind" : "work-item", + "owner" : "ADR046-otel-005, ADR046-zone-control-019, ADR046-zone-control-020; observability Provider owner", + "parallelGroup" : "wi:ADR-046-provider-observability-otel", + "prerequisites" : [ + "ADR-046-provider-observability-otel", + "ADR046-otel-005", + "ADR046-zone-control-019", + "ADR046-zone-control-020" ], - "specId": "ADR-046-resources-zone-control", - "topologicalRank": 10, - "wave": "W5" + "specId" : "ADR-046-provider-observability-otel", + "topologicalRank" : 14, + "wave" : "W6" }, { - "blockers": [], - "destinations": [ - "`packages/d2b-provider-device-gpu/` with `src/`, `tests/`, `integration/`, `README.md`; add to workspace `Cargo.toml` members list (alphanumerically sorted)" + "blockers" : [], + "destinations" : [ + "`packages/d2b-contracts/src/v3/host.rs`, `guest.rs`, `execution_policy.rs`, `process.rs`, `volume.rs`, `user.rs`, `network.rs`, `device.rs`, `credential.rs`" ], - "entryContracts": [ - "ADR-046-provider-device-gpu" + "entryContracts" : [ + "ADR-046-primitive-resource-composition" ], - "exitGate": "ADR046-W6 exit criteria (ADR-046-validation-and-delivery §4): every spec/work item in this wave Merged with clean destinations, all validators green, and the ten-role panel seal recorded", - "id": "ADR046-provider-device-gpu-01", - "kind": "work-item", - "owner": "`ADR046-resources-device` accepted; `ADR046-provider-model-and-packaging` accepted; workspace root must add crate", - "parallelGroup": "wi:ADR-046-provider-device-gpu", - "prerequisites": [ - "ADR-046-provider-device-gpu" + "exitGate" : "ADR046-W2 exit criteria (ADR-046-validation-and-delivery §4): every spec/work item in this wave Merged with clean destinations, all validators green, and the ten-role panel seal recorded", + "id" : "ADR046-primitives-001", + "kind" : "work-item", + "owner" : "W0; resource contracts", + "parallelGroup" : "wi:ADR-046-primitive-resource-composition", + "prerequisites" : [ + "ADR-046-primitive-resource-composition" ], - "specId": "ADR-046-provider-device-gpu", - "topologicalRank": 11, - "wave": "W6" + "specId" : "ADR-046-primitive-resource-composition", + "topologicalRank" : 7, + "wave" : "W2" }, { - "blockers": [], - "destinations": [ - "`packages/d2b-provider-device-gpu/src/controller.rs`" + "blockers" : [], + "destinations" : [ + "`packages/d2b-provider-system-systemd/`, `packages/d2b-provider-system-minijail/`, shared neutral process conformance library" ], - "entryContracts": [ - "ADR-046-provider-device-gpu" + "entryContracts" : [ + "ADR-046-primitive-resource-composition" ], - "exitGate": "ADR046-W6 exit criteria (ADR-046-validation-and-delivery §4): every spec/work item in this wave Merged with clean destinations, all validators green, and the ten-role panel seal recorded", - "id": "ADR046-provider-device-gpu-02", - "kind": "work-item", - "owner": "ADR046-provider-device-gpu-01; `ADR-046-resource-reconciliation` implementation present; Provider toolkit `ResourceClient` available", - "parallelGroup": "wi:ADR-046-provider-device-gpu", - "prerequisites": [ - "ADR-046-provider-device-gpu" + "exitGate" : "ADR046-W2 exit criteria (ADR-046-validation-and-delivery §4): every spec/work item in this wave Merged with clean destinations, all validators green, and the ten-role panel seal recorded", + "id" : "ADR046-primitives-002", + "kind" : "work-item", + "owner" : "Process contracts; system Provider slices", + "parallelGroup" : "wi:ADR-046-primitive-resource-composition", + "prerequisites" : [ + "ADR-046-primitive-resource-composition" ], - "specId": "ADR-046-provider-device-gpu", - "topologicalRank": 11, - "wave": "W6" + "specId" : "ADR-046-primitive-resource-composition", + "topologicalRank" : 7, + "wave" : "W2" }, { - "blockers": [], - "destinations": [ - "`packages/d2b-provider-device-gpu/src/probe.rs`" + "blockers" : [], + "destinations" : [ + "`packages/d2b-provider-volume-*/`; `nixos-modules/resources-volume.nix`" ], - "entryContracts": [ - "ADR-046-provider-device-gpu" + "entryContracts" : [ + "ADR-046-primitive-resource-composition" ], - "exitGate": "ADR046-W6 exit criteria (ADR-046-validation-and-delivery §4): every spec/work item in this wave Merged with clean destinations, all validators green, and the ten-role panel seal recorded", - "id": "ADR046-provider-device-gpu-03", - "kind": "work-item", - "owner": "ADR046-provider-device-gpu-02", - "parallelGroup": "wi:ADR-046-provider-device-gpu", - "prerequisites": [ - "ADR-046-provider-device-gpu" + "exitGate" : "ADR046-W2 exit criteria (ADR-046-validation-and-delivery §4): every spec/work item in this wave Merged with clean destinations, all validators green, and the ten-role panel seal recorded", + "id" : "ADR046-primitives-003", + "kind" : "work-item", + "owner" : "Volume contract; Volume Provider slices", + "parallelGroup" : "wi:ADR-046-primitive-resource-composition", + "prerequisites" : [ + "ADR-046-primitive-resource-composition" ], - "specId": "ADR-046-provider-device-gpu", - "topologicalRank": 11, - "wave": "W6" + "specId" : "ADR-046-primitive-resource-composition", + "topologicalRank" : 7, + "wave" : "W2" }, { - "blockers": [], - "destinations": [ - "`packages/d2b-provider-device-gpu/src/arbitration.rs`" + "blockers" : [], + "destinations" : [ + "`packages/d2b-process/src/`, `packages/d2b-provider-supervisor/src/`" ], - "entryContracts": [ - "ADR-046-provider-device-gpu" + "entryContracts" : [ + "ADR-046-components-processes-and-sandbox" ], - "exitGate": "ADR046-W6 exit criteria (ADR-046-validation-and-delivery §4): every spec/work item in this wave Merged with clean destinations, all validators green, and the ten-role panel seal recorded", - "id": "ADR046-provider-device-gpu-04", - "kind": "work-item", - "owner": "ADR046-provider-device-gpu-02", - "parallelGroup": "wi:ADR-046-provider-device-gpu", - "prerequisites": [ - "ADR-046-provider-device-gpu" + "exitGate" : "ADR046-W4 exit criteria (ADR-046-validation-and-delivery §4): every spec/work item in this wave Merged with clean destinations, all validators green, and the ten-role panel seal recorded", + "id" : "ADR046-process-001", + "kind" : "work-item", + "owner" : "W0/W2; Process contracts/supervisor", + "parallelGroup" : "wi:ADR-046-components-processes-and-sandbox", + "prerequisites" : [ + "ADR-046-components-processes-and-sandbox" ], - "specId": "ADR-046-provider-device-gpu", - "topologicalRank": 11, - "wave": "W6" + "specId" : "ADR-046-components-processes-and-sandbox", + "topologicalRank" : 9, + "wave" : "W4" }, { - "blockers": [], - "destinations": [ - "`packages/d2b-provider-device-gpu/src/worker_gpu.rs`" + "blockers" : [], + "destinations" : [ + "`packages/d2b-provider-system-systemd/`, `packages/d2b-provider-system-minijail/`" ], - "entryContracts": [ - "ADR-046-provider-device-gpu" + "entryContracts" : [ + "ADR-046-components-processes-and-sandbox" ], - "exitGate": "ADR046-W6 exit criteria (ADR-046-validation-and-delivery §4): every spec/work item in this wave Merged with clean destinations, all validators green, and the ten-role panel seal recorded", - "id": "ADR046-provider-device-gpu-05", - "kind": "work-item", - "owner": "ADR046-provider-device-gpu-02; `ADR046-components-processes-and-sandbox` (Provider/system-minijail present and able to handle Process resources with `gpu-worker`/`render-node-worker` templates)", - "parallelGroup": "wi:ADR-046-provider-device-gpu", - "prerequisites": [ - "ADR-046-provider-device-gpu" + "exitGate" : "ADR046-W4 exit criteria (ADR-046-validation-and-delivery §4): every spec/work item in this wave Merged with clean destinations, all validators green, and the ten-role panel seal recorded", + "id" : "ADR046-process-002", + "kind" : "work-item", + "owner" : "ADR046-process-001; systemd/minijail Provider owners", + "parallelGroup" : "wi:ADR-046-components-processes-and-sandbox", + "prerequisites" : [ + "ADR-046-components-processes-and-sandbox", + "ADR046-process-001" ], - "specId": "ADR-046-provider-device-gpu", - "topologicalRank": 11, - "wave": "W6" + "specId" : "ADR-046-components-processes-and-sandbox", + "topologicalRank" : 10, + "wave" : "W4" }, { - "blockers": [], - "destinations": [ - "`packages/d2b-provider-device-gpu/src/worker_video.rs`, `tests/wire_constant_snapshot.rs`" + "blockers" : [], + "destinations" : [ + "`packages/d2b-contracts/src/v3/provider.rs`, `packages/d2b-provider/src/lib.rs`, `packages/d2b-provider-toolkit/`" ], - "entryContracts": [ - "ADR-046-provider-device-gpu" + "entryContracts" : [ + "ADR-046-provider-model-and-packaging" ], - "exitGate": "ADR046-W6 exit criteria (ADR-046-validation-and-delivery §4): every spec/work item in this wave Merged with clean destinations, all validators green, and the ten-role panel seal recorded", - "id": "ADR046-provider-device-gpu-06", - "kind": "work-item", - "owner": "ADR046-provider-device-gpu-05 (video depends on GPU Process being Ready)", - "parallelGroup": "wi:ADR-046-provider-device-gpu", - "prerequisites": [ - "ADR-046-provider-device-gpu" + "exitGate" : "ADR046-W3 exit criteria (ADR-046-validation-and-delivery §4): every spec/work item in this wave Merged with clean destinations, all validators green, and the ten-role panel seal recorded", + "id" : "ADR046-provider-001", + "kind" : "work-item", + "owner" : "W0; Provider contract/catalog owner", + "parallelGroup" : "wi:ADR-046-provider-model-and-packaging", + "prerequisites" : [ + "ADR-046-provider-model-and-packaging" ], - "specId": "ADR-046-provider-device-gpu", - "topologicalRank": 11, - "wave": "W6" + "specId" : "ADR-046-provider-model-and-packaging", + "topologicalRank" : 8, + "wave" : "W3" }, { - "blockers": [], - "destinations": [ - "`nixos-modules/assertions.nix` (new GPU Device eval assertions); `tests/unit/nix/cases/device-gpu-eval.nix` (new Nix eval case); committed settings schema `docs/reference/schemas/v3/providers/device-gpu.settings.json`" + "blockers" : [], + "destinations" : [ + "one `packages/d2b-provider--/` per Provider with mandatory src/, tests/, integration/, README.md; generic Nix Provider package/catalog emitter" ], - "entryContracts": [ - "ADR-046-provider-device-gpu" + "entryContracts" : [ + "ADR-046-provider-model-and-packaging" ], - "exitGate": "ADR046-W6 exit criteria (ADR-046-validation-and-delivery §4): every spec/work item in this wave Merged with clean destinations, all validators green, and the ten-role panel seal recorded", - "id": "ADR046-provider-device-gpu-07", - "kind": "work-item", - "owner": "ADR046-provider-device-gpu-01; ADR 0046 Zone Nix emitter wired; `ADR-046-nix-configuration` Nix emitter present", - "parallelGroup": "wi:ADR-046-provider-device-gpu", - "prerequisites": [ - "ADR-046-provider-device-gpu" + "exitGate" : "ADR046-W3 exit criteria (ADR-046-validation-and-delivery §4): every spec/work item in this wave Merged with clean destinations, all validators green, and the ten-role panel seal recorded", + "id" : "ADR046-provider-002", + "kind" : "work-item", + "owner" : "Provider contract; package/Nix integrator", + "parallelGroup" : "wi:ADR-046-provider-model-and-packaging", + "prerequisites" : [ + "ADR-046-provider-model-and-packaging" ], - "specId": "ADR-046-provider-device-gpu", - "topologicalRank": 11, - "wave": "W6" + "specId" : "ADR-046-provider-model-and-packaging", + "topologicalRank" : 8, + "wave" : "W3" }, { - "blockers": [], - "destinations": [ - "`packages/d2b-provider-device-gpu/` component descriptor; controller/status tests" + "blockers" : [], + "destinations" : [ + "`packages/d2b-provider-system-core/`, `d2b-provider-system-systemd/`, `d2b-provider-system-minijail/`" ], - "entryContracts": [ - "ADR-046-provider-device-gpu" + "entryContracts" : [ + "ADR-046-provider-model-and-packaging" ], - "exitGate": "ADR046-W6 exit criteria (ADR-046-validation-and-delivery §4): every spec/work item in this wave Merged with clean destinations, all validators green, and the ten-role panel seal recorded", - "id": "ADR046-provider-device-gpu-08", - "kind": "work-item", - "owner": "ADR046-provider-device-gpu-01; D087 status-first state model present in the foundational ADR-046 specs", - "parallelGroup": "wi:ADR-046-provider-device-gpu", - "prerequisites": [ - "ADR-046-provider-device-gpu" + "exitGate" : "ADR046-W3 exit criteria (ADR-046-validation-and-delivery §4): every spec/work item in this wave Merged with clean destinations, all validators green, and the ten-role panel seal recorded", + "id" : "ADR046-provider-003", + "kind" : "work-item", + "owner" : "Process contracts; system Provider owners", + "parallelGroup" : "wi:ADR-046-provider-model-and-packaging", + "prerequisites" : [ + "ADR-046-provider-model-and-packaging" ], - "specId": "ADR-046-provider-device-gpu", - "topologicalRank": 11, - "wave": "W6" + "specId" : "ADR-046-provider-model-and-packaging", + "topologicalRank" : 8, + "wave" : "W3" }, { - "blockers": [], - "destinations": [ - "`packages/d2b-provider-device-gpu/README.md`" + "blockers" : [], + "destinations" : [ + "`packages/d2b-contracts/src/v3/semantic_services/{mod,audio,security_key,telemetry,usb}.rs`; generated schema artifacts for the eight exact qualified ResourceTypes" ], - "entryContracts": [ - "ADR-046-provider-device-gpu" + "entryContracts" : [ + "ADR-046-provider-model-and-packaging" ], - "exitGate": "ADR046-W6 exit criteria (ADR-046-validation-and-delivery §4): every spec/work item in this wave Merged with clean destinations, all validators green, and the ten-role panel seal recorded", - "id": "ADR046-provider-device-gpu-09", - "kind": "work-item", - "owner": "ADR046-provider-device-gpu-01", - "parallelGroup": "wi:ADR-046-provider-device-gpu", - "prerequisites": [ - "ADR-046-provider-device-gpu" + "exitGate" : "ADR046-W3 exit criteria (ADR-046-validation-and-delivery §4): every spec/work item in this wave Merged with clean destinations, all validators green, and the ten-role panel seal recorded", + "id" : "ADR046-provider-004", + "kind" : "work-item", + "owner" : "ADR046-provider-001; shared semantic Service/Binding contract owner", + "parallelGroup" : "wi:ADR-046-provider-model-and-packaging", + "prerequisites" : [ + "ADR-046-provider-model-and-packaging", + "ADR046-provider-001" ], - "specId": "ADR-046-provider-device-gpu", - "topologicalRank": 11, - "wave": "W6" + "specId" : "ADR-046-provider-model-and-packaging", + "topologicalRank" : 9, + "wave" : "W3" }, { - "blockers": [], - "destinations": [ + "blockers" : [], + "destinations" : [ "`packages/d2b-contracts/src/v3/volume_state.rs`" ], - "entryContracts": [ + "entryContracts" : [ "ADR-046-provider-state" ], - "exitGate": "ADR046-W4 exit criteria (ADR-046-validation-and-delivery §4): every spec/work item in this wave Merged with clean destinations, all validators green, and the ten-role panel seal recorded", - "id": "ADR046-pstate-001", - "kind": "work-item", - "owner": "ADR046-primitives-001; v3 contracts owner", - "parallelGroup": "wi:ADR-046-provider-state", - "prerequisites": [ + "exitGate" : "ADR046-W4 exit criteria (ADR-046-validation-and-delivery §4): every spec/work item in this wave Merged with clean destinations, all validators green, and the ten-role panel seal recorded", + "id" : "ADR046-pstate-001", + "kind" : "work-item", + "owner" : "ADR046-primitives-001; v3 contracts owner", + "parallelGroup" : "wi:ADR-046-provider-state", + "prerequisites" : [ "ADR-046-provider-state", "ADR046-primitives-001" ], - "specId": "ADR-046-provider-state", - "topologicalRank": 9, - "wave": "W4" + "specId" : "ADR-046-provider-state", + "topologicalRank" : 9, + "wave" : "W4" }, { - "blockers": [], - "destinations": [ + "blockers" : [], + "destinations" : [ "`packages/d2b-contracts/src/v3/provider.rs` (component descriptor `stateNamespaces` field)" ], - "entryContracts": [ + "entryContracts" : [ "ADR-046-provider-state" ], - "exitGate": "ADR046-W4 exit criteria (ADR-046-validation-and-delivery §4): every spec/work item in this wave Merged with clean destinations, all validators green, and the ten-role panel seal recorded", - "id": "ADR046-pstate-002", - "kind": "work-item", - "owner": "ADR046-pstate-001; Provider contracts owner", - "parallelGroup": "wi:ADR-046-provider-state", - "prerequisites": [ + "exitGate" : "ADR046-W4 exit criteria (ADR-046-validation-and-delivery §4): every spec/work item in this wave Merged with clean destinations, all validators green, and the ten-role panel seal recorded", + "id" : "ADR046-pstate-002", + "kind" : "work-item", + "owner" : "ADR046-pstate-001; Provider contracts owner", + "parallelGroup" : "wi:ADR-046-provider-state", + "prerequisites" : [ "ADR-046-provider-state", "ADR046-pstate-001" ], - "specId": "ADR-046-provider-state", - "topologicalRank": 10, - "wave": "W4" + "specId" : "ADR-046-provider-state", + "topologicalRank" : 10, + "wave" : "W4" }, { - "blockers": [], - "destinations": [ + "blockers" : [], + "destinations" : [ "`packages/d2b-provider-volume-local/` (new crate, full scaffold required): `src/{atomic.rs, path.rs, lock.rs, marker.rs, effect_port.rs}`; `tests/volume_local.rs` (marker missing/replaced/mismatch, domain-isolation rejection, quota enforcement); `integration/volume_local.rs` (real Host filesystem provision, broker-maintained marker check, domain-isolation rejection cross-process); `README.md`" ], - "entryContracts": [ + "entryContracts" : [ "ADR-046-provider-state" ], - "exitGate": "ADR046-W4 exit criteria (ADR-046-validation-and-delivery §4): every spec/work item in this wave Merged with clean destinations, all validators green, and the ten-role panel seal recorded", - "id": "ADR046-pstate-003", - "kind": "work-item", - "owner": "ADR046-pstate-001; volume-local Provider owner", - "parallelGroup": "wi:ADR-046-provider-state", - "prerequisites": [ + "exitGate" : "ADR046-W4 exit criteria (ADR-046-validation-and-delivery §4): every spec/work item in this wave Merged with clean destinations, all validators green, and the ten-role panel seal recorded", + "id" : "ADR046-pstate-003", + "kind" : "work-item", + "owner" : "ADR046-pstate-001; volume-local Provider owner", + "parallelGroup" : "wi:ADR-046-provider-state", + "prerequisites" : [ "ADR-046-provider-state", "ADR046-pstate-001" ], - "specId": "ADR-046-provider-state", - "topologicalRank": 10, - "wave": "W4" + "specId" : "ADR-046-provider-state", + "topologicalRank" : 10, + "wave" : "W4" }, { - "blockers": [], - "destinations": [ + "blockers" : [], + "destinations" : [ "`packages/d2b-provider-volume-local/src/migration.rs`; `packages/d2b-provider-volume-local/tests/migration_unit.rs` (hermetic staging Volume and prepare/commit/rollback); `packages/d2b-provider-volume-local/integration/migration.rs` (real Host crash-injection at each migration step, N-Volume cross-component coordination); `packages/d2b-controller-toolkit/src/state_migration.rs`" ], - "entryContracts": [ + "entryContracts" : [ "ADR-046-provider-state" ], - "exitGate": "ADR046-W4 exit criteria (ADR-046-validation-and-delivery §4): every spec/work item in this wave Merged with clean destinations, all validators green, and the ten-role panel seal recorded", - "id": "ADR046-pstate-004", - "kind": "work-item", - "owner": "ADR046-reconcile-001, ADR046-pstate-003; volume-local Provider and controller-toolkit owners", - "parallelGroup": "wi:ADR-046-provider-state", - "prerequisites": [ + "exitGate" : "ADR046-W4 exit criteria (ADR-046-validation-and-delivery §4): every spec/work item in this wave Merged with clean destinations, all validators green, and the ten-role panel seal recorded", + "id" : "ADR046-pstate-004", + "kind" : "work-item", + "owner" : "ADR046-reconcile-001, ADR046-pstate-003; volume-local Provider and controller-toolkit owners", + "parallelGroup" : "wi:ADR-046-provider-state", + "prerequisites" : [ "ADR-046-provider-state", "ADR046-pstate-003", "ADR046-reconcile-001" ], - "specId": "ADR-046-provider-state", - "topologicalRank": 11, - "wave": "W4" + "specId" : "ADR-046-provider-state", + "topologicalRank" : 11, + "wave" : "W4" }, { - "blockers": [], - "destinations": [ + "blockers" : [], + "destinations" : [ "`packages/d2b-provider-volume-local/src/sealing.rs`; `packages/d2b-provider-volume-local/tests/sealing_unit.rs` (hermetic seal/rotation state machine); `packages/d2b-provider-volume-local/integration/sealing.rs` (real Credential lease flow, interrupted key rotation with live Credential Provider)" ], - "entryContracts": [ + "entryContracts" : [ "ADR-046-provider-state" ], - "exitGate": "ADR046-W4 exit criteria (ADR-046-validation-and-delivery §4): every spec/work item in this wave Merged with clean destinations, all validators green, and the ten-role panel seal recorded", - "id": "ADR046-pstate-005", - "kind": "work-item", - "owner": "ADR046-pstate-003; volume-local Provider and Credential Provider owners", - "parallelGroup": "wi:ADR-046-provider-state", - "prerequisites": [ + "exitGate" : "ADR046-W4 exit criteria (ADR-046-validation-and-delivery §4): every spec/work item in this wave Merged with clean destinations, all validators green, and the ten-role panel seal recorded", + "id" : "ADR046-pstate-005", + "kind" : "work-item", + "owner" : "ADR046-pstate-003; volume-local Provider and Credential Provider owners", + "parallelGroup" : "wi:ADR-046-provider-state", + "prerequisites" : [ "ADR-046-provider-state", "ADR046-pstate-003" ], - "specId": "ADR-046-provider-state", - "topologicalRank": 11, - "wave": "W4" + "specId" : "ADR-046-provider-state", + "topologicalRank" : 11, + "wave" : "W4" }, { - "blockers": [], - "destinations": [ + "blockers" : [], + "destinations" : [ "`packages/d2b-provider-volume-local/src/snapshot.rs`; `packages/d2b-provider-volume-local/tests/snapshot_unit.rs` (hermetic `snapshotPolicy` enforcement, retention count and TTL logic); `packages/d2b-provider-volume-local/integration/snapshot.rs` (real Host filesystem snapshot creation, retention expiry, pre-migration auto-snapshot with interrupted migration)" ], - "entryContracts": [ + "entryContracts" : [ "ADR-046-provider-state" ], - "exitGate": "ADR046-W4 exit criteria (ADR-046-validation-and-delivery §4): every spec/work item in this wave Merged with clean destinations, all validators green, and the ten-role panel seal recorded", - "id": "ADR046-pstate-006", - "kind": "work-item", - "owner": "ADR046-pstate-003, ADR046-pstate-004; volume-local Provider and snapshot toolkit owners", - "parallelGroup": "wi:ADR-046-provider-state", - "prerequisites": [ + "exitGate" : "ADR046-W4 exit criteria (ADR-046-validation-and-delivery §4): every spec/work item in this wave Merged with clean destinations, all validators green, and the ten-role panel seal recorded", + "id" : "ADR046-pstate-006", + "kind" : "work-item", + "owner" : "ADR046-pstate-003, ADR046-pstate-004; volume-local Provider and snapshot toolkit owners", + "parallelGroup" : "wi:ADR-046-provider-state", + "prerequisites" : [ "ADR-046-provider-state", "ADR046-pstate-003", "ADR046-pstate-004" ], - "specId": "ADR-046-provider-state", - "topologicalRank": 12, - "wave": "W4" + "specId" : "ADR-046-provider-state", + "topologicalRank" : 12, + "wave" : "W4" }, { - "blockers": [], - "destinations": [ + "blockers" : [], + "destinations" : [ "`packages/d2b-provider-volume-local/src/relocation.rs`; `packages/d2b-provider-volume-local/tests/relocation_unit.rs` (hermetic finalizer set/clear, commit/failure state machine); `packages/d2b-provider-volume-local/integration/relocation.rs` (real Host-to-Host anchored file copy, crash at copy midpoint, virtiofs source re-point after successful relocation)" ], - "entryContracts": [ + "entryContracts" : [ "ADR-046-provider-state" ], - "exitGate": "ADR046-W4 exit criteria (ADR-046-validation-and-delivery §4): every spec/work item in this wave Merged with clean destinations, all validators green, and the ten-role panel seal recorded", - "id": "ADR046-pstate-007", - "kind": "work-item", - "owner": "ADR046-pstate-003; volume-local Provider owner", - "parallelGroup": "wi:ADR-046-provider-state", - "prerequisites": [ + "exitGate" : "ADR046-W4 exit criteria (ADR-046-validation-and-delivery §4): every spec/work item in this wave Merged with clean destinations, all validators green, and the ten-role panel seal recorded", + "id" : "ADR046-pstate-007", + "kind" : "work-item", + "owner" : "ADR046-pstate-003; volume-local Provider owner", + "parallelGroup" : "wi:ADR-046-provider-state", + "prerequisites" : [ "ADR-046-provider-state", "ADR046-pstate-003" ], - "specId": "ADR-046-provider-state", - "topologicalRank": 11, - "wave": "W4" + "specId" : "ADR-046-provider-state", + "topologicalRank" : 11, + "wave" : "W4" }, { - "blockers": [], - "destinations": [ + "blockers" : [], + "destinations" : [ "`packages/d2b-provider-volume-local/src/audit.rs`; `packages/d2b-provider-volume-local/src/otel.rs`; `packages/d2b-provider-volume-local/tests/audit_unit.rs` (hermetic audit golden records, OTEL cardinality label tests)" ], - "entryContracts": [ + "entryContracts" : [ "ADR-046-provider-state" ], - "exitGate": "ADR046-W4 exit criteria (ADR-046-validation-and-delivery §4): every spec/work item in this wave Merged with clean destinations, all validators green, and the ten-role panel seal recorded", - "id": "ADR046-pstate-008", - "kind": "work-item", - "owner": "ADR046-pstate-001; Zone audit stream, OTEL provider owners", - "parallelGroup": "wi:ADR-046-provider-state", - "prerequisites": [ + "exitGate" : "ADR046-W4 exit criteria (ADR-046-validation-and-delivery §4): every spec/work item in this wave Merged with clean destinations, all validators green, and the ten-role panel seal recorded", + "id" : "ADR046-pstate-008", + "kind" : "work-item", + "owner" : "ADR046-pstate-001; Zone audit stream, OTEL provider owners", + "parallelGroup" : "wi:ADR-046-provider-state", + "prerequisites" : [ "ADR-046-provider-state", "ADR046-pstate-001" ], - "specId": "ADR-046-provider-state", - "topologicalRank": 10, - "wave": "W4" + "specId" : "ADR-046-provider-state", + "topologicalRank" : 10, + "wave" : "W4" }, { - "blockers": [], - "destinations": [ + "blockers" : [], + "destinations" : [ "`packages/d2b-provider-volume-local/tests/state.rs` (ported hermetic atomic/lock/quarantine/lease tests); `packages/d2b-provider-volume-local/tests/migration.rs` (ported migration fault-injection, cross-component N-Volume coordination); `packages/d2b-provider-volume-local/integration/provider_state.rs` (end-to-end: live daemon, real Host Volume mount, cross-process worker subview, full audit stream); `packages/d2b-provider-volume-local/integration/audit.rs` (live Zone audit stream emission and OTEL export)" ], - "entryContracts": [ + "entryContracts" : [ "ADR-046-provider-state" ], - "exitGate": "ADR046-W4 exit criteria (ADR-046-validation-and-delivery §4): every spec/work item in this wave Merged with clean destinations, all validators green, and the ten-role panel seal recorded", - "id": "ADR046-pstate-009", - "kind": "work-item", - "owner": "ADR046-pstate-001 through ADR046-pstate-008; integration test owner", - "parallelGroup": "wi:ADR-046-provider-state", - "prerequisites": [ + "exitGate" : "ADR046-W4 exit criteria (ADR-046-validation-and-delivery §4): every spec/work item in this wave Merged with clean destinations, all validators green, and the ten-role panel seal recorded", + "id" : "ADR046-pstate-009", + "kind" : "work-item", + "owner" : "ADR046-pstate-001 through ADR046-pstate-008; integration test owner", + "parallelGroup" : "wi:ADR-046-provider-state", + "prerequisites" : [ "ADR-046-provider-state", "ADR046-pstate-001", + "ADR046-pstate-002", + "ADR046-pstate-003", + "ADR046-pstate-004", + "ADR046-pstate-005", + "ADR046-pstate-006", + "ADR046-pstate-007", "ADR046-pstate-008" ], - "specId": "ADR-046-provider-state", - "topologicalRank": 11, - "wave": "W4" + "specId" : "ADR-046-provider-state", + "topologicalRank" : 13, + "wave" : "W4" }, { - "blockers": [], - "destinations": [ + "blockers" : [], + "destinations" : [ "`nixos-modules/zone-resources.nix` (per-Zone bundle emitter NixOS module); `packages/d2b-core/src/v3/zone_bundle.rs` (shared bundle DTOs: `ZoneResourceBundle`, `BundleResource`, `contentId` computation); `packages/d2b-core-controller/src/configuration.rs` (diff/apply loop, name-conflict detection, `pending-cleanup` Zone status, `maxFinalizerDurationSeconds` stall detection — NOT in `d2b-provider-volume-local`); `packages/d2b-core-controller/tests/configuration.rs` (hermetic bundle diff, absent-resource Delete dispatch, name-conflict `Degraded/name-conflict` item, integrity-failure abort); `packages/d2b-core-controller/integration/configuration.rs` (container-based generation activation with running Providers, absent-resource cleanup, rollback, finalizer-timeout stall detection)" ], - "entryContracts": [ + "entryContracts" : [ "ADR-046-provider-state" ], - "exitGate": "ADR046-W4 exit criteria (ADR-046-validation-and-delivery §4): every spec/work item in this wave Merged with clean destinations, all validators green, and the ten-role panel seal recorded", - "id": "ADR046-pstate-010", - "kind": "work-item", - "owner": "ADR046-pstate-001; Zone resource bundle owner, NixOS module owner, `d2b-core-controller` owner (ADR-046-core-controllers)", - "parallelGroup": "wi:ADR-046-provider-state", - "prerequisites": [ + "exitGate" : "ADR046-W4 exit criteria (ADR-046-validation-and-delivery §4): every spec/work item in this wave Merged with clean destinations, all validators green, and the ten-role panel seal recorded", + "id" : "ADR046-pstate-010", + "kind" : "work-item", + "owner" : "ADR046-pstate-001; Zone resource bundle owner, NixOS module owner, `d2b-core-controller` owner (ADR-046-core-controllers)", + "parallelGroup" : "wi:ADR-046-provider-state", + "prerequisites" : [ "ADR-046-provider-state", "ADR046-pstate-001" ], - "specId": "ADR-046-provider-state", - "topologicalRank": 10, - "wave": "W4" + "specId" : "ADR-046-provider-state", + "topologicalRank" : 10, + "wave" : "W4" }, { - "blockers": [], - "destinations": [ + "blockers" : [], + "destinations" : [ "`packages/xtask/src/provider_crate_policy.rs`; `tests/unit/gates/provider-crate-layout-check.sh`" ], - "entryContracts": [ + "entryContracts" : [ "ADR-046-provider-state" ], - "exitGate": "ADR046-W4 exit criteria (ADR-046-validation-and-delivery §4): every spec/work item in this wave Merged with clean destinations, all validators green, and the ten-role panel seal recorded", - "id": "ADR046-pstate-011", - "kind": "work-item", - "owner": "ADR046-pstate-003; workspace policy and xtask owners", - "parallelGroup": "wi:ADR-046-provider-state", - "prerequisites": [ + "exitGate" : "ADR046-W4 exit criteria (ADR-046-validation-and-delivery §4): every spec/work item in this wave Merged with clean destinations, all validators green, and the ten-role panel seal recorded", + "id" : "ADR046-pstate-011", + "kind" : "work-item", + "owner" : "ADR046-pstate-003; workspace policy and xtask owners", + "parallelGroup" : "wi:ADR-046-provider-state", + "prerequisites" : [ "ADR-046-provider-state", "ADR046-pstate-003" ], - "specId": "ADR-046-provider-state", - "topologicalRank": 11, - "wave": "W4" + "specId" : "ADR-046-provider-state", + "topologicalRank" : 11, + "wave" : "W4" }, { - "blockers": [], - "destinations": [ + "blockers" : [], + "destinations" : [ "`packages/d2b-core-controller/src/optional_state_admission.rs` (storage-need admission: reject a declared namespace whose payload is derivable from spec/status/core ledger/external observation with `component-state-not-justified`; only declared namespaces produce a Volume; stateless components produce none); `packages/d2b-core-controller/tests/optional_state_admission.rs` (hermetic: stateless component → no Volume; declared `storageNeed` variants accepted; unjustified namespace rejected; status-first restart revalidation — controller re-derives observed state from status/core ledger/external observation after restart and never treats a status field as authority); `packages/d2b-provider-volume-local/tests/status_bounds.rs` (hermetic: total canonical serialized status cap and provider-specific detail cap enforced; oversize status write → typed rejection; status carries no secret/path/argv/PID/unit/stream/ring content)" ], - "entryContracts": [ + "entryContracts" : [ "ADR-046-provider-state" ], - "exitGate": "ADR046-W4 exit criteria (ADR-046-validation-and-delivery §4): every spec/work item in this wave Merged with clean destinations, all validators green, and the ten-role panel seal recorded", - "id": "ADR046-pstate-012", - "kind": "work-item", - "owner": "ADR046-pstate-001, ADR046-pstate-002; Zone runtime owner (`d2b-core-controller`), volume-local Provider owner", - "parallelGroup": "wi:ADR-046-provider-state", - "prerequisites": [ + "exitGate" : "ADR046-W4 exit criteria (ADR-046-validation-and-delivery §4): every spec/work item in this wave Merged with clean destinations, all validators green, and the ten-role panel seal recorded", + "id" : "ADR046-pstate-012", + "kind" : "work-item", + "owner" : "ADR046-pstate-001, ADR046-pstate-002; Zone runtime owner (`d2b-core-controller`), volume-local Provider owner", + "parallelGroup" : "wi:ADR-046-provider-state", + "prerequisites" : [ "ADR-046-provider-state", "ADR046-pstate-001", "ADR046-pstate-002" ], - "specId": "ADR-046-provider-state", - "topologicalRank": 11, - "wave": "W4" + "specId" : "ADR-046-provider-state", + "topologicalRank" : 11, + "wave" : "W4" }, { - "blockers": [], - "destinations": [ - "`packages/d2b-controller-toolkit/src/lib.rs`, `runner.rs`, `queue.rs`, `context.rs`, `result.rs`" + "blockers" : [], + "destinations" : [ + "packages/d2b-provider-runtime-qemu-media/{src/lib.rs,tests/provider_layout.rs,integration/mod.rs,README.md}" ], - "entryContracts": [ - "ADR-046-resource-reconciliation" + "entryContracts" : [ + "ADR-046-provider-runtime-qemu-media" ], - "exitGate": "ADR046-W1 exit criteria (ADR-046-validation-and-delivery §4): every spec/work item in this wave Merged with clean destinations, all validators green, and the ten-role panel seal recorded", - "id": "ADR046-reconcile-001", - "kind": "work-item", - "owner": "W0/W1a; controller toolkit owner", - "parallelGroup": "wi:ADR-046-resource-reconciliation", - "prerequisites": [ - "ADR-046-resource-reconciliation" + "exitGate" : "ADR046-W6 exit criteria (ADR-046-validation-and-delivery §4): every spec/work item in this wave Merged with clean destinations, all validators green, and the ten-role panel seal recorded", + "id" : "ADR046-qemu-media-001", + "kind" : "work-item", + "owner" : "P0; blocks all other runtime-qemu-media work items; owner: `runtime-qemu-media` Provider crate", + "parallelGroup" : "wi:ADR-046-provider-runtime-qemu-media", + "prerequisites" : [ + "ADR-046-provider-runtime-qemu-media" ], - "specId": "ADR-046-resource-reconciliation", - "topologicalRank": 6, - "wave": "W1" + "specId" : "ADR-046-provider-runtime-qemu-media", + "topologicalRank" : 11, + "wave" : "W6" }, { - "blockers": [], - "destinations": [ - "`packages/d2b-core-controller/src/hints.rs`, `dependencies.rs`, `owner_reconcile.rs`" + "blockers" : [], + "destinations" : [ + "packages/d2b-provider-runtime-qemu-media/src/types/guest.rs" ], - "entryContracts": [ - "ADR-046-resource-reconciliation" + "entryContracts" : [ + "ADR-046-provider-runtime-qemu-media" ], - "exitGate": "ADR046-W1 exit criteria (ADR-046-validation-and-delivery §4): every spec/work item in this wave Merged with clean destinations, all validators green, and the ten-role panel seal recorded", - "id": "ADR046-reconcile-002", - "kind": "work-item", - "owner": "Store/API + ADR046-reconcile-001; core controller", - "parallelGroup": "wi:ADR-046-resource-reconciliation", - "prerequisites": [ - "ADR-046-resource-reconciliation", - "ADR046-reconcile-001" + "exitGate" : "ADR046-W6 exit criteria (ADR-046-validation-and-delivery §4): every spec/work item in this wave Merged with clean destinations, all validators green, and the ten-role panel seal recorded", + "id" : "ADR046-qemu-media-002", + "kind" : "work-item", + "owner" : "P0; depends on ADR046-qemu-media-001; owner: runtime-qemu-media type/schema implementation", + "parallelGroup" : "wi:ADR-046-provider-runtime-qemu-media", + "prerequisites" : [ + "ADR-046-provider-runtime-qemu-media", + "ADR046-qemu-media-001" ], - "specId": "ADR-046-resource-reconciliation", - "topologicalRank": 7, - "wave": "W1" + "specId" : "ADR-046-provider-runtime-qemu-media", + "topologicalRank" : 12, + "wave" : "W6" }, { - "blockers": [], - "destinations": [ - "`packages/d2b-controller-toolkit/benches/reaction.rs`, Process Provider integration tests" + "blockers" : [], + "destinations" : [ + "packages/d2b-provider-runtime-qemu-media/src/config.rs" ], - "entryContracts": [ - "ADR-046-resource-reconciliation" + "entryContracts" : [ + "ADR-046-provider-runtime-qemu-media" ], - "exitGate": "ADR046-W1 exit criteria (ADR-046-validation-and-delivery §4): every spec/work item in this wave Merged with clean destinations, all validators green, and the ten-role panel seal recorded", - "id": "ADR046-reconcile-003", - "kind": "work-item", - "owner": "Process Providers + benchmark owner", - "parallelGroup": "wi:ADR-046-resource-reconciliation", - "prerequisites": [ - "ADR-046-resource-reconciliation" + "exitGate" : "ADR046-W6 exit criteria (ADR-046-validation-and-delivery §4): every spec/work item in this wave Merged with clean destinations, all validators green, and the ten-role panel seal recorded", + "id" : "ADR046-qemu-media-003", + "kind" : "work-item", + "owner" : "P0; depends on ADR046-qemu-media-001; owner: runtime-qemu-media Provider config/schema implementation", + "parallelGroup" : "wi:ADR-046-provider-runtime-qemu-media", + "prerequisites" : [ + "ADR-046-provider-runtime-qemu-media", + "ADR046-qemu-media-001" ], - "specId": "ADR-046-resource-reconciliation", - "topologicalRank": 6, - "wave": "W1" + "specId" : "ADR-046-provider-runtime-qemu-media", + "topologicalRank" : 12, + "wave" : "W6" }, { - "blockers": [], - "destinations": [ - "`packages/d2b-cutover/src/{inventory,snapshot,checkpoint}.rs`" + "blockers" : [], + "destinations" : [ + "packages/d2b-provider-runtime-qemu-media/src/{descriptor.rs,state.rs}; no Volume management code for Provider state" ], - "entryContracts": [ - "ADR-046-reset-and-cutover" + "entryContracts" : [ + "ADR-046-provider-runtime-qemu-media" ], - "exitGate": "ADR046-W7 exit criteria (ADR-046-validation-and-delivery §4): every spec/work item in this wave Merged with clean destinations, all validators green, and the ten-role panel seal recorded", - "id": "ADR046-reset-001", - "kind": "work-item", - "owner": "W0 shared contract root; storage/broker integrator", - "parallelGroup": "wi:ADR-046-reset-and-cutover", - "prerequisites": [ - "ADR-046-reset-and-cutover" + "exitGate" : "ADR046-W6 exit criteria (ADR-046-validation-and-delivery §4): every spec/work item in this wave Merged with clean destinations, all validators green, and the ten-role panel seal recorded", + "id" : "ADR046-qemu-media-004", + "kind" : "work-item", + "owner" : "P0; depends on ADR046-qemu-media-001 and ADR046-qemu-media-003; owner: runtime-qemu-media controller descriptor/state implementation", + "parallelGroup" : "wi:ADR-046-provider-runtime-qemu-media", + "prerequisites" : [ + "ADR-046-provider-runtime-qemu-media", + "ADR046-qemu-media-001", + "ADR046-qemu-media-003" ], - "specId": "ADR-046-reset-and-cutover", - "topologicalRank": 11, - "wave": "W7" + "specId" : "ADR-046-provider-runtime-qemu-media", + "topologicalRank" : 13, + "wave" : "W6" }, { - "blockers": [], - "destinations": [ - "`packages/d2b-cutover/src/{bundle_validate,trust_preflight}.rs`" + "blockers" : [], + "destinations" : [ + "packages/d2b-provider-runtime-qemu-media/src/controller/volume.rs" ], - "entryContracts": [ - "ADR-046-reset-and-cutover" + "entryContracts" : [ + "ADR-046-provider-runtime-qemu-media" ], - "exitGate": "ADR046-W7 exit criteria (ADR-046-validation-and-delivery §4): every spec/work item in this wave Merged with clean destinations, all validators green, and the ten-role panel seal recorded", - "id": "ADR046-reset-002", - "kind": "work-item", - "owner": "ADR046-reset-001; `d2b-resource-store-redb` owner; `d2b-provider` catalog owner", - "parallelGroup": "wi:ADR-046-reset-and-cutover", - "prerequisites": [ - "ADR-046-reset-and-cutover", - "ADR046-reset-001" + "exitGate" : "ADR046-W6 exit criteria (ADR-046-validation-and-delivery §4): every spec/work item in this wave Merged with clean destinations, all validators green, and the ten-role panel seal recorded", + "id" : "ADR046-qemu-media-005", + "kind" : "work-item", + "owner" : "P1; depends on ADR046-qemu-media-001 and ADR046-qemu-media-003; owner: runtime-qemu-media controller Volume reconciliation", + "parallelGroup" : "wi:ADR-046-provider-runtime-qemu-media", + "prerequisites" : [ + "ADR-046-provider-runtime-qemu-media", + "ADR046-qemu-media-001", + "ADR046-qemu-media-003" ], - "specId": "ADR-046-reset-and-cutover", - "topologicalRank": 12, - "wave": "W7" + "specId" : "ADR-046-provider-runtime-qemu-media", + "topologicalRank" : 13, + "wave" : "W6" }, { - "blockers": [], - "destinations": [ - "`packages/d2b-cutover/src/{consent,drain,disposition}.rs`" + "blockers" : [], + "destinations" : [ + "packages/d2b-provider-runtime-qemu-media/src/controller/media_watch.rs" ], - "entryContracts": [ - "ADR-046-reset-and-cutover" + "entryContracts" : [ + "ADR-046-provider-runtime-qemu-media" ], - "exitGate": "ADR046-W7 exit criteria (ADR-046-validation-and-delivery §4): every spec/work item in this wave Merged with clean destinations, all validators green, and the ten-role panel seal recorded", - "id": "ADR046-reset-003", - "kind": "work-item", - "owner": "ADR046-reset-001, ADR046-reset-002; Process/Guest lifecycle owner", - "parallelGroup": "wi:ADR-046-reset-and-cutover", - "prerequisites": [ - "ADR-046-reset-and-cutover", - "ADR046-reset-001", - "ADR046-reset-002" + "exitGate" : "ADR046-W6 exit criteria (ADR-046-validation-and-delivery §4): every spec/work item in this wave Merged with clean destinations, all validators green, and the ten-role panel seal recorded", + "id" : "ADR046-qemu-media-006", + "kind" : "work-item", + "owner" : "P1; depends on ADR046-qemu-media-002 and ADR046-qemu-media-005; owner: runtime-qemu-media media dependency controller", + "parallelGroup" : "wi:ADR-046-provider-runtime-qemu-media", + "prerequisites" : [ + "ADR-046-provider-runtime-qemu-media", + "ADR046-qemu-media-002", + "ADR046-qemu-media-005" ], - "specId": "ADR-046-reset-and-cutover", - "topologicalRank": 13, - "wave": "W7" + "specId" : "ADR-046-provider-runtime-qemu-media", + "topologicalRank" : 14, + "wave" : "W6" }, { - "blockers": [], - "destinations": [ - "`packages/d2b-cutover/src/adopt.rs`, thin wrapper invoking `ADR-046-provider-state`'s migration `EphemeralProcess` prepare/stage/commit/rollback machinery with cutover-specific source paths" + "blockers" : [], + "destinations" : [ + "packages/d2b-provider-runtime-qemu-media/src/controller/device_watch.rs" ], - "entryContracts": [ - "ADR-046-reset-and-cutover" + "entryContracts" : [ + "ADR-046-provider-runtime-qemu-media" ], - "exitGate": "ADR046-W7 exit criteria (ADR-046-validation-and-delivery §4): every spec/work item in this wave Merged with clean destinations, all validators green, and the ten-role panel seal recorded", - "id": "ADR046-reset-004", - "kind": "work-item", - "owner": "ADR046-reset-003; `ADR-046-provider-state` owner; `device-tpm`/`volume-local` Provider owners", - "parallelGroup": "wi:ADR-046-reset-and-cutover", - "prerequisites": [ - "ADR-046-reset-and-cutover", - "ADR046-reset-003" + "exitGate" : "ADR046-W6 exit criteria (ADR-046-validation-and-delivery §4): every spec/work item in this wave Merged with clean destinations, all validators green, and the ten-role panel seal recorded", + "id" : "ADR046-qemu-media-007", + "kind" : "work-item", + "owner" : "P1; depends on ADR046-qemu-media-002; owner: runtime-qemu-media Device dependency controller", + "parallelGroup" : "wi:ADR-046-provider-runtime-qemu-media", + "prerequisites" : [ + "ADR-046-provider-runtime-qemu-media", + "ADR046-qemu-media-002" ], - "specId": "ADR-046-reset-and-cutover", - "topologicalRank": 14, - "wave": "W7" + "specId" : "ADR-046-provider-runtime-qemu-media", + "topologicalRank" : 13, + "wave" : "W6" }, { - "blockers": [], - "destinations": [ - "`packages/d2b-cutover/src/{store_bootstrap,provider_sequence}.rs`" + "blockers" : [], + "destinations" : [ + "packages/d2b-provider-runtime-qemu-media/src/controller/display.rs" ], - "entryContracts": [ - "ADR-046-reset-and-cutover" + "entryContracts" : [ + "ADR-046-provider-runtime-qemu-media" ], - "exitGate": "ADR046-W7 exit criteria (ADR-046-validation-and-delivery §4): every spec/work item in this wave Merged with clean destinations, all validators green, and the ten-role panel seal recorded", - "id": "ADR046-reset-005", - "kind": "work-item", - "owner": "ADR046-reset-004; `d2b-resource-store-redb` owner (`ADR046-store-003`); core-controller owner (`ADR046-core-001`)", - "parallelGroup": "wi:ADR-046-reset-and-cutover", - "prerequisites": [ - "ADR-046-reset-and-cutover", - "ADR046-core-001", - "ADR046-reset-004", - "ADR046-store-003" + "exitGate" : "ADR046-W6 exit criteria (ADR-046-validation-and-delivery §4): every spec/work item in this wave Merged with clean destinations, all validators green, and the ten-role panel seal recorded", + "id" : "ADR046-qemu-media-008", + "kind" : "work-item", + "owner" : "P1; depends on ADR046-qemu-media-002 and the `display-wayland` Provider dossier; owner: runtime-qemu-media display integration", + "parallelGroup" : "wi:ADR-046-provider-runtime-qemu-media", + "prerequisites" : [ + "ADR-046-provider-runtime-qemu-media", + "ADR046-qemu-media-002" ], - "specId": "ADR-046-reset-and-cutover", - "topologicalRank": 15, - "wave": "W7" + "specId" : "ADR-046-provider-runtime-qemu-media", + "topologicalRank" : 13, + "wave" : "W6" }, { - "blockers": [], - "destinations": [ - "`packages/d2b-cutover/src/{zonelink_cutover,guest_activation}.rs`" + "blockers" : [], + "destinations" : [ + "packages/d2b-provider-runtime-qemu-media/src/controller/process_builder.rs" ], - "entryContracts": [ - "ADR-046-reset-and-cutover" + "entryContracts" : [ + "ADR-046-provider-runtime-qemu-media" ], - "exitGate": "ADR046-W7 exit criteria (ADR-046-validation-and-delivery §4): every spec/work item in this wave Merged with clean destinations, all validators green, and the ten-role panel seal recorded", - "id": "ADR046-reset-006", - "kind": "work-item", - "owner": "ADR046-reset-005; `ADR-046-zone-routing` owner; `ADR-046-resources-zone-control` owner", - "parallelGroup": "wi:ADR-046-reset-and-cutover", - "prerequisites": [ - "ADR-046-reset-and-cutover", - "ADR046-reset-005" + "exitGate" : "ADR046-W6 exit criteria (ADR-046-validation-and-delivery §4): every spec/work item in this wave Merged with clean destinations, all validators green, and the ten-role panel seal recorded", + "id" : "ADR046-qemu-media-009", + "kind" : "work-item", + "owner" : "P1; depends on ADR046-qemu-media-002, ADR046-qemu-media-005, ADR046-qemu-media-006, ADR046-qemu-media-007, ADR046-qemu-media-008, and ADR046-qemu-media-012; owner: runtime-qemu-media Process launch builder", + "parallelGroup" : "wi:ADR-046-provider-runtime-qemu-media", + "prerequisites" : [ + "ADR-046-provider-runtime-qemu-media", + "ADR046-qemu-media-002", + "ADR046-qemu-media-005", + "ADR046-qemu-media-006", + "ADR046-qemu-media-007", + "ADR046-qemu-media-008", + "ADR046-qemu-media-012" ], - "specId": "ADR-046-reset-and-cutover", - "topologicalRank": 16, - "wave": "W7" + "specId" : "ADR-046-provider-runtime-qemu-media", + "topologicalRank" : 15, + "wave" : "W6" }, { - "blockers": [], - "destinations": [ - "`packages/d2b-cutover/src/{verify,doctor,degraded}.rs`" + "blockers" : [], + "destinations" : [ + "packages/d2b-provider-runtime-qemu-media/src/qmp/" ], - "entryContracts": [ - "ADR-046-reset-and-cutover" + "entryContracts" : [ + "ADR-046-provider-runtime-qemu-media" ], - "exitGate": "ADR046-W7 exit criteria (ADR-046-validation-and-delivery §4): every spec/work item in this wave Merged with clean destinations, all validators green, and the ten-role panel seal recorded", - "id": "ADR046-reset-007", - "kind": "work-item", - "owner": "ADR046-reset-006; telemetry-audit-and-support owner (`d2b-audit`)", - "parallelGroup": "wi:ADR-046-reset-and-cutover", - "prerequisites": [ - "ADR-046-reset-and-cutover", - "ADR046-reset-006" + "exitGate" : "ADR046-W6 exit criteria (ADR-046-validation-and-delivery §4): every spec/work item in this wave Merged with clean destinations, all validators green, and the ten-role panel seal recorded", + "id" : "ADR046-qemu-media-010", + "kind" : "work-item", + "owner" : "P1; depends on ADR046-qemu-media-009; owner: runtime-qemu-media QMP client implementation", + "parallelGroup" : "wi:ADR-046-provider-runtime-qemu-media", + "prerequisites" : [ + "ADR-046-provider-runtime-qemu-media", + "ADR046-qemu-media-009" ], - "specId": "ADR-046-reset-and-cutover", - "topologicalRank": 17, - "wave": "W7" + "specId" : "ADR-046-provider-runtime-qemu-media", + "topologicalRank" : 16, + "wave" : "W6" }, { - "blockers": [], - "destinations": [ - "`packages/d2b-cutover/src/finalize.rs`" + "blockers" : [], + "destinations" : [ + "packages/d2b-provider-runtime-qemu-media/src/controller/hotplug.rs" ], - "entryContracts": [ - "ADR-046-reset-and-cutover" + "entryContracts" : [ + "ADR-046-provider-runtime-qemu-media" ], - "exitGate": "ADR046-W7 exit criteria (ADR-046-validation-and-delivery §4): every spec/work item in this wave Merged with clean destinations, all validators green, and the ten-role panel seal recorded", - "id": "ADR046-reset-008", - "kind": "work-item", - "owner": "ADR046-reset-007; owner of each retiring artifact's ADR 0046 successor work item", - "parallelGroup": "wi:ADR-046-reset-and-cutover", - "prerequisites": [ - "ADR-046-reset-and-cutover", - "ADR046-reset-007" + "exitGate" : "ADR046-W6 exit criteria (ADR-046-validation-and-delivery §4): every spec/work item in this wave Merged with clean destinations, all validators green, and the ten-role panel seal recorded", + "id" : "ADR046-qemu-media-011", + "kind" : "work-item", + "owner" : "P2; depends on ADR046-qemu-media-006 and ADR046-qemu-media-010; owner: runtime-qemu-media hotplug controller", + "parallelGroup" : "wi:ADR-046-provider-runtime-qemu-media", + "prerequisites" : [ + "ADR-046-provider-runtime-qemu-media", + "ADR046-qemu-media-006", + "ADR046-qemu-media-010" ], - "specId": "ADR-046-reset-and-cutover", - "topologicalRank": 18, - "wave": "W7" + "specId" : "ADR-046-provider-runtime-qemu-media", + "topologicalRank" : 17, + "wave" : "W6" }, { - "blockers": [], - "destinations": [ - "`packages/d2b-cutover/src/{journal,rollback,hold}.rs`" + "blockers" : [], + "destinations" : [ + "packages/d2b-provider-runtime-qemu-media/src/controller/network.rs" ], - "entryContracts": [ - "ADR-046-reset-and-cutover" + "entryContracts" : [ + "ADR-046-provider-runtime-qemu-media" ], - "exitGate": "ADR046-W7 exit criteria (ADR-046-validation-and-delivery §4): every spec/work item in this wave Merged with clean destinations, all validators green, and the ten-role panel seal recorded", - "id": "ADR046-reset-009", - "kind": "work-item", - "owner": "ADR046-reset-003 through ADR046-reset-006; `ADR-046-provider-state` incident-hold owner", - "parallelGroup": "wi:ADR-046-reset-and-cutover", - "prerequisites": [ - "ADR-046-reset-and-cutover", - "ADR046-reset-003", - "ADR046-reset-006" + "exitGate" : "ADR046-W6 exit criteria (ADR-046-validation-and-delivery §4): every spec/work item in this wave Merged with clean destinations, all validators green, and the ten-role panel seal recorded", + "id" : "ADR046-qemu-media-012", + "kind" : "work-item", + "owner" : "P1; depends on ADR046-qemu-media-002 and Provider config `networkProviderRef`; owner: runtime-qemu-media network dependency integration", + "parallelGroup" : "wi:ADR-046-provider-runtime-qemu-media", + "prerequisites" : [ + "ADR-046-provider-runtime-qemu-media", + "ADR046-qemu-media-002" ], - "specId": "ADR-046-reset-and-cutover", - "topologicalRank": 17, - "wave": "W7" + "specId" : "ADR-046-provider-runtime-qemu-media", + "topologicalRank" : 13, + "wave" : "W6" }, { - "blockers": [], - "destinations": [ - "`packages/d2b-cutover/src/reset_scope.rs`; `d2b host reset` CLI command" + "blockers" : [], + "destinations" : [ + "packages/d2b-provider-runtime-qemu-media/src/controller/reconcile.rs" + ], + "entryContracts" : [ + "ADR-046-provider-runtime-qemu-media" + ], + "exitGate" : "ADR046-W6 exit criteria (ADR-046-validation-and-delivery §4): every spec/work item in this wave Merged with clean destinations, all validators green, and the ten-role panel seal recorded", + "id" : "ADR046-qemu-media-013", + "kind" : "work-item", + "owner" : "P1; depends on ADR046-qemu-media-005 through ADR046-qemu-media-012; owner: runtime-qemu-media controller", + "parallelGroup" : "wi:ADR-046-provider-runtime-qemu-media", + "prerequisites" : [ + "ADR-046-provider-runtime-qemu-media", + "ADR046-qemu-media-005", + "ADR046-qemu-media-006", + "ADR046-qemu-media-007", + "ADR046-qemu-media-008", + "ADR046-qemu-media-009", + "ADR046-qemu-media-010", + "ADR046-qemu-media-011", + "ADR046-qemu-media-012" + ], + "specId" : "ADR-046-provider-runtime-qemu-media", + "topologicalRank" : 18, + "wave" : "W6" + }, + { + "blockers" : [], + "destinations" : [ + "packages/d2b-provider-runtime-qemu-media/src/controller/status.rs" + ], + "entryContracts" : [ + "ADR-046-provider-runtime-qemu-media" + ], + "exitGate" : "ADR046-W6 exit criteria (ADR-046-validation-and-delivery §4): every spec/work item in this wave Merged with clean destinations, all validators green, and the ten-role panel seal recorded", + "id" : "ADR046-qemu-media-014", + "kind" : "work-item", + "owner" : "P1; depends on ADR046-qemu-media-013; owner: runtime-qemu-media status/error implementation", + "parallelGroup" : "wi:ADR-046-provider-runtime-qemu-media", + "prerequisites" : [ + "ADR-046-provider-runtime-qemu-media", + "ADR046-qemu-media-013" + ], + "specId" : "ADR-046-provider-runtime-qemu-media", + "topologicalRank" : 19, + "wave" : "W6" + }, + { + "blockers" : [], + "destinations" : [ + "packages/d2b-provider-runtime-qemu-media/src/audit.rs" + ], + "entryContracts" : [ + "ADR-046-provider-runtime-qemu-media" + ], + "exitGate" : "ADR046-W6 exit criteria (ADR-046-validation-and-delivery §4): every spec/work item in this wave Merged with clean destinations, all validators green, and the ten-role panel seal recorded", + "id" : "ADR046-qemu-media-015", + "kind" : "work-item", + "owner" : "P2; depends on ADR046-qemu-media-013 and ADR046-qemu-media-014; owner: runtime-qemu-media audit integration", + "parallelGroup" : "wi:ADR-046-provider-runtime-qemu-media", + "prerequisites" : [ + "ADR-046-provider-runtime-qemu-media", + "ADR046-qemu-media-013", + "ADR046-qemu-media-014" + ], + "specId" : "ADR-046-provider-runtime-qemu-media", + "topologicalRank" : 20, + "wave" : "W6" + }, + { + "blockers" : [], + "destinations" : [ + "packages/d2b-provider-runtime-qemu-media/src/telemetry.rs" + ], + "entryContracts" : [ + "ADR-046-provider-runtime-qemu-media" + ], + "exitGate" : "ADR046-W6 exit criteria (ADR-046-validation-and-delivery §4): every spec/work item in this wave Merged with clean destinations, all validators green, and the ten-role panel seal recorded", + "id" : "ADR046-qemu-media-016", + "kind" : "work-item", + "owner" : "P2; depends on ADR046-qemu-media-013 and ADR046-qemu-media-014; owner: runtime-qemu-media telemetry integration", + "parallelGroup" : "wi:ADR-046-provider-runtime-qemu-media", + "prerequisites" : [ + "ADR-046-provider-runtime-qemu-media", + "ADR046-qemu-media-013", + "ADR046-qemu-media-014" + ], + "specId" : "ADR-046-provider-runtime-qemu-media", + "topologicalRank" : 20, + "wave" : "W6" + }, + { + "blockers" : [], + "destinations" : [ + "nixos-modules/options-guest-qemu-media.nix; nixos-modules/assertions.nix" + ], + "entryContracts" : [ + "ADR-046-provider-runtime-qemu-media" + ], + "exitGate" : "ADR046-W6 exit criteria (ADR-046-validation-and-delivery §4): every spec/work item in this wave Merged with clean destinations, all validators green, and the ten-role panel seal recorded", + "id" : "ADR046-qemu-media-017", + "kind" : "work-item", + "owner" : "P1; depends on ADR046-qemu-media-002 and ADR046-qemu-media-003; owner: Nix resource compiler and runtime-qemu-media options", + "parallelGroup" : "wi:ADR-046-provider-runtime-qemu-media", + "prerequisites" : [ + "ADR-046-provider-runtime-qemu-media", + "ADR046-qemu-media-002", + "ADR046-qemu-media-003" + ], + "specId" : "ADR-046-provider-runtime-qemu-media", + "topologicalRank" : 13, + "wave" : "W6" + }, + { + "blockers" : [], + "destinations" : [ + "packages/d2b-provider-runtime-qemu-media/tests/conformance_guest.rs" + ], + "entryContracts" : [ + "ADR-046-provider-runtime-qemu-media" + ], + "exitGate" : "ADR046-W6 exit criteria (ADR-046-validation-and-delivery §4): every spec/work item in this wave Merged with clean destinations, all validators green, and the ten-role panel seal recorded", + "id" : "ADR046-qemu-media-018", + "kind" : "work-item", + "owner" : "P2; depends on ADR046-qemu-media-013 through ADR046-qemu-media-016; owner: runtime-qemu-media conformance tests", + "parallelGroup" : "wi:ADR-046-provider-runtime-qemu-media", + "prerequisites" : [ + "ADR-046-provider-runtime-qemu-media", + "ADR046-qemu-media-013", + "ADR046-qemu-media-014", + "ADR046-qemu-media-015", + "ADR046-qemu-media-016" + ], + "specId" : "ADR-046-provider-runtime-qemu-media", + "topologicalRank" : 21, + "wave" : "W6" + }, + { + "blockers" : [], + "destinations" : [ + "packages/d2b-provider-runtime-qemu-media/integration/" + ], + "entryContracts" : [ + "ADR-046-provider-runtime-qemu-media" + ], + "exitGate" : "ADR046-W6 exit criteria (ADR-046-validation-and-delivery §4): every spec/work item in this wave Merged with clean destinations, all validators green, and the ten-role panel seal recorded", + "id" : "ADR046-qemu-media-019", + "kind" : "work-item", + "owner" : "P2; depends on ADR046-qemu-media-005 through ADR046-qemu-media-018; owner: runtime-qemu-media integration fixtures", + "parallelGroup" : "wi:ADR-046-provider-runtime-qemu-media", + "prerequisites" : [ + "ADR-046-provider-runtime-qemu-media", + "ADR046-qemu-media-005", + "ADR046-qemu-media-006", + "ADR046-qemu-media-007", + "ADR046-qemu-media-008", + "ADR046-qemu-media-009", + "ADR046-qemu-media-010", + "ADR046-qemu-media-011", + "ADR046-qemu-media-012", + "ADR046-qemu-media-013", + "ADR046-qemu-media-014", + "ADR046-qemu-media-015", + "ADR046-qemu-media-016", + "ADR046-qemu-media-017", + "ADR046-qemu-media-018" + ], + "specId" : "ADR-046-provider-runtime-qemu-media", + "topologicalRank" : 22, + "wave" : "W6" + }, + { + "blockers" : [], + "destinations" : [ + "`packages/d2b-controller-toolkit/src/lib.rs`, `runner.rs`, `queue.rs`, `context.rs`, `result.rs`" + ], + "entryContracts" : [ + "ADR-046-resource-reconciliation" + ], + "exitGate" : "ADR046-W1 exit criteria (ADR-046-validation-and-delivery §4): every spec/work item in this wave Merged with clean destinations, all validators green, and the ten-role panel seal recorded", + "id" : "ADR046-reconcile-001", + "kind" : "work-item", + "owner" : "W0/W1a; controller toolkit owner", + "parallelGroup" : "wi:ADR-046-resource-reconciliation", + "prerequisites" : [ + "ADR-046-resource-reconciliation" + ], + "specId" : "ADR-046-resource-reconciliation", + "topologicalRank" : 6, + "wave" : "W1" + }, + { + "blockers" : [], + "destinations" : [ + "`packages/d2b-core-controller/src/hints.rs`, `dependencies.rs`, `owner_reconcile.rs`" + ], + "entryContracts" : [ + "ADR-046-resource-reconciliation" ], - "entryContracts": [ + "exitGate" : "ADR046-W1 exit criteria (ADR-046-validation-and-delivery §4): every spec/work item in this wave Merged with clean destinations, all validators green, and the ten-role panel seal recorded", + "id" : "ADR046-reconcile-002", + "kind" : "work-item", + "owner" : "Store/API + ADR046-reconcile-001; core controller", + "parallelGroup" : "wi:ADR-046-resource-reconciliation", + "prerequisites" : [ + "ADR-046-resource-reconciliation", + "ADR046-reconcile-001" + ], + "specId" : "ADR-046-resource-reconciliation", + "topologicalRank" : 7, + "wave" : "W1" + }, + { + "blockers" : [], + "destinations" : [ + "`packages/d2b-controller-toolkit/benches/reaction.rs`, Process Provider integration tests" + ], + "entryContracts" : [ + "ADR-046-resource-reconciliation" + ], + "exitGate" : "ADR046-W1 exit criteria (ADR-046-validation-and-delivery §4): every spec/work item in this wave Merged with clean destinations, all validators green, and the ten-role panel seal recorded", + "id" : "ADR046-reconcile-003", + "kind" : "work-item", + "owner" : "Process Providers + benchmark owner", + "parallelGroup" : "wi:ADR-046-resource-reconciliation", + "prerequisites" : [ + "ADR-046-resource-reconciliation" + ], + "specId" : "ADR-046-resource-reconciliation", + "topologicalRank" : 6, + "wave" : "W1" + }, + { + "blockers" : [], + "destinations" : [ + "`packages/d2b-cutover/src/{inventory,snapshot,checkpoint}.rs`" + ], + "entryContracts" : [ "ADR-046-reset-and-cutover" ], - "exitGate": "ADR046-W7 exit criteria (ADR-046-validation-and-delivery §4): every spec/work item in this wave Merged with clean destinations, all validators green, and the ten-role panel seal recorded", - "id": "ADR046-reset-010", - "kind": "work-item", - "owner": "ADR046-reset-005 through ADR046-reset-007; `ADR-046-resources-zone-control` owner (§2.6/§9.4)", - "parallelGroup": "wi:ADR-046-reset-and-cutover", - "prerequisites": [ - "ADR-046-reset-and-cutover", - "ADR046-reset-005", - "ADR046-reset-007" + "exitGate" : "ADR046-W7 exit criteria (ADR-046-validation-and-delivery §4): every spec/work item in this wave Merged with clean destinations, all validators green, and the ten-role panel seal recorded", + "id" : "ADR046-reset-001", + "kind" : "work-item", + "owner" : "W0 shared contract root; storage/broker integrator", + "parallelGroup" : "wi:ADR-046-reset-and-cutover", + "prerequisites" : [ + "ADR-046-reset-and-cutover" ], - "specId": "ADR-046-reset-and-cutover", - "topologicalRank": 18, - "wave": "W7" + "specId" : "ADR-046-reset-and-cutover", + "topologicalRank" : 11, + "wave" : "W7" }, { - "blockers": [], - "destinations": [ - "`tests/integration/live/cutover-real-host.sh`, `tests/integration/live/cutover-real-host-cloud-guest.sh`, `tests/host-integration/hardware/cutover-real-tpm.sh`, `tests/host-integration/hardware/cutover-real-usbip-security-key.sh`" + "blockers" : [], + "destinations" : [ + "`packages/d2b-cutover/src/{bundle_validate,trust_preflight}.rs`" ], - "entryContracts": [ + "entryContracts" : [ "ADR-046-reset-and-cutover" ], - "exitGate": "ADR046-W7 exit criteria (ADR-046-validation-and-delivery §4): every spec/work item in this wave Merged with clean destinations, all validators green, and the ten-role panel seal recorded", - "id": "ADR046-reset-011", - "kind": "work-item", - "owner": "ADR046-reset-001 through ADR046-reset-010, fully landed", - "parallelGroup": "wi:ADR-046-reset-and-cutover", - "prerequisites": [ + "exitGate" : "ADR046-W7 exit criteria (ADR-046-validation-and-delivery §4): every spec/work item in this wave Merged with clean destinations, all validators green, and the ten-role panel seal recorded", + "id" : "ADR046-reset-002", + "kind" : "work-item", + "owner" : "ADR046-reset-001; `d2b-resource-store-redb` owner; `d2b-provider` catalog owner", + "parallelGroup" : "wi:ADR-046-reset-and-cutover", + "prerequisites" : [ "ADR-046-reset-and-cutover", - "ADR046-reset-001", - "ADR046-reset-010" + "ADR046-reset-001" ], - "specId": "ADR-046-reset-and-cutover", - "topologicalRank": 19, - "wave": "W7" + "specId" : "ADR-046-reset-and-cutover", + "topologicalRank" : 12, + "wave" : "W7" }, { - "blockers": [], - "destinations": [ - "`packages/d2b-session/` copied verbatim; v3 contract extension renames endpoint purpose values while preserving backward wire compatibility during transition." + "blockers" : [], + "destinations" : [ + "`packages/d2b-cutover/src/{consent,drain,disposition}.rs`" ], - "entryContracts": [ - "ADR-046-telemetry-audit-and-support" + "entryContracts" : [ + "ADR-046-reset-and-cutover" ], - "exitGate": "ADR046-W5 exit criteria (ADR-046-validation-and-delivery §4): every spec/work item in this wave Merged with clean destinations, all validators green, and the ten-role panel seal recorded", - "id": "ADR046-reuse-001", - "kind": "work-item", - "owner": "ADR046-telem-001 (d2b-telemetry must exist first for MetricsSink injection); session owner", - "parallelGroup": "wi:ADR-046-telemetry-audit-and-support", - "prerequisites": [ - "ADR-046-telemetry-audit-and-support", - "ADR046-telem-001" + "exitGate" : "ADR046-W7 exit criteria (ADR-046-validation-and-delivery §4): every spec/work item in this wave Merged with clean destinations, all validators green, and the ten-role panel seal recorded", + "id" : "ADR046-reset-003", + "kind" : "work-item", + "owner" : "ADR046-reset-001, ADR046-reset-002; Process/Guest lifecycle owner", + "parallelGroup" : "wi:ADR-046-reset-and-cutover", + "prerequisites" : [ + "ADR-046-reset-and-cutover", + "ADR046-reset-001", + "ADR046-reset-002" ], - "specId": "ADR-046-telemetry-audit-and-support", - "topologicalRank": 11, - "wave": "W5" + "specId" : "ADR-046-reset-and-cutover", + "topologicalRank" : 13, + "wave" : "W7" }, { - "blockers": [], - "destinations": [ - "`packages/d2b-session-unix/` copied verbatim." + "blockers" : [], + "destinations" : [ + "`packages/d2b-cutover/src/adopt.rs`, thin wrapper invoking `ADR-046-provider-state`'s migration `EphemeralProcess` prepare/stage/commit/rollback machinery with cutover-specific source paths" ], - "entryContracts": [ - "ADR-046-telemetry-audit-and-support" + "entryContracts" : [ + "ADR-046-reset-and-cutover" ], - "exitGate": "ADR046-W5 exit criteria (ADR-046-validation-and-delivery §4): every spec/work item in this wave Merged with clean destinations, all validators green, and the ten-role panel seal recorded", - "id": "ADR046-reuse-002", - "kind": "work-item", - "owner": "ADR046-reuse-001; Unix transport owner", - "parallelGroup": "wi:ADR-046-telemetry-audit-and-support", - "prerequisites": [ - "ADR-046-telemetry-audit-and-support", - "ADR046-reuse-001" + "exitGate" : "ADR046-W7 exit criteria (ADR-046-validation-and-delivery §4): every spec/work item in this wave Merged with clean destinations, all validators green, and the ten-role panel seal recorded", + "id" : "ADR046-reset-004", + "kind" : "work-item", + "owner" : "ADR046-reset-003; `ADR-046-provider-state` owner; `device-tpm`/`volume-local` Provider owners", + "parallelGroup" : "wi:ADR-046-reset-and-cutover", + "prerequisites" : [ + "ADR-046-reset-and-cutover", + "ADR046-reset-003" ], - "specId": "ADR-046-telemetry-audit-and-support", - "topologicalRank": 12, - "wave": "W5" + "specId" : "ADR-046-reset-and-cutover", + "topologicalRank" : 14, + "wave" : "W7" }, { - "blockers": [], - "destinations": [ - "`packages/d2b-client/` copied; DaemonClient/GuestClient adapted to v3 service packages; `MetadataInput::with_trace` drives `TraceContext` propagation." + "blockers" : [], + "destinations" : [ + "`packages/d2b-cutover/src/{store_bootstrap,provider_sequence}.rs`" ], - "entryContracts": [ - "ADR-046-telemetry-audit-and-support" + "entryContracts" : [ + "ADR-046-reset-and-cutover" ], - "exitGate": "ADR046-W5 exit criteria (ADR-046-validation-and-delivery §4): every spec/work item in this wave Merged with clean destinations, all validators green, and the ten-role panel seal recorded", - "id": "ADR046-reuse-003", - "kind": "work-item", - "owner": "ADR046-reuse-001; client owner", - "parallelGroup": "wi:ADR-046-telemetry-audit-and-support", - "prerequisites": [ - "ADR-046-telemetry-audit-and-support", - "ADR046-reuse-001" + "exitGate" : "ADR046-W7 exit criteria (ADR-046-validation-and-delivery §4): every spec/work item in this wave Merged with clean destinations, all validators green, and the ten-role panel seal recorded", + "id" : "ADR046-reset-005", + "kind" : "work-item", + "owner" : "ADR046-reset-004; `d2b-resource-store-redb` owner (`ADR046-store-003`); core-controller owner (`ADR046-core-001`)", + "parallelGroup" : "wi:ADR-046-reset-and-cutover", + "prerequisites" : [ + "ADR-046-reset-and-cutover", + "ADR046-core-001", + "ADR046-reset-004", + "ADR046-store-003" ], - "specId": "ADR-046-telemetry-audit-and-support", - "topologicalRank": 12, - "wave": "W5" + "specId" : "ADR-046-reset-and-cutover", + "topologicalRank" : 15, + "wave" : "W7" }, { - "blockers": [], - "destinations": [ - "`packages/d2b-provider/` and `packages/d2b-provider-toolkit/` copied with v3 session admission and bus routing adaptations." + "blockers" : [], + "destinations" : [ + "`packages/d2b-cutover/src/{zonelink_cutover,guest_activation}.rs`" ], - "entryContracts": [ - "ADR-046-telemetry-audit-and-support" + "entryContracts" : [ + "ADR-046-reset-and-cutover" ], - "exitGate": "ADR046-W5 exit criteria (ADR-046-validation-and-delivery §4): every spec/work item in this wave Merged with clean destinations, all validators green, and the ten-role panel seal recorded", - "id": "ADR046-reuse-004", - "kind": "work-item", - "owner": "ADR046-reuse-001 + ADR046-reuse-003; Provider owner", - "parallelGroup": "wi:ADR-046-telemetry-audit-and-support", - "prerequisites": [ - "ADR-046-telemetry-audit-and-support", - "ADR046-reuse-001", - "ADR046-reuse-003" + "exitGate" : "ADR046-W7 exit criteria (ADR-046-validation-and-delivery §4): every spec/work item in this wave Merged with clean destinations, all validators green, and the ten-role panel seal recorded", + "id" : "ADR046-reset-006", + "kind" : "work-item", + "owner" : "ADR046-reset-005; `ADR-046-zone-routing` owner; `ADR-046-resources-zone-control` owner", + "parallelGroup" : "wi:ADR-046-reset-and-cutover", + "prerequisites" : [ + "ADR-046-reset-and-cutover", + "ADR046-reset-005" ], - "specId": "ADR-046-telemetry-audit-and-support", - "topologicalRank": 13, - "wave": "W5" + "specId" : "ADR-046-reset-and-cutover", + "topologicalRank" : 16, + "wave" : "W7" }, { - "blockers": [], - "destinations": [ - "`packages/d2b-provider-observability-otel/src/agent.rs` adapted; ComponentSessionDriver mock becomes shared Provider session fixture." + "blockers" : [], + "destinations" : [ + "`packages/d2b-cutover/src/{verify,doctor,degraded}.rs`" ], - "entryContracts": [ - "ADR-046-telemetry-audit-and-support" + "entryContracts" : [ + "ADR-046-reset-and-cutover" ], - "exitGate": "ADR046-W5 exit criteria (ADR-046-validation-and-delivery §4): every spec/work item in this wave Merged with clean destinations, all validators green, and the ten-role panel seal recorded", - "id": "ADR046-reuse-005", - "kind": "work-item", - "owner": "ADR046-reuse-004 + ADR046-audit-003; Provider agent / observability-otel owner", - "parallelGroup": "wi:ADR-046-telemetry-audit-and-support", - "prerequisites": [ - "ADR-046-telemetry-audit-and-support", - "ADR046-audit-003", - "ADR046-reuse-004" + "exitGate" : "ADR046-W7 exit criteria (ADR-046-validation-and-delivery §4): every spec/work item in this wave Merged with clean destinations, all validators green, and the ten-role panel seal recorded", + "id" : "ADR046-reset-007", + "kind" : "work-item", + "owner" : "ADR046-reset-006; telemetry-audit-and-support owner (`d2b-audit`)", + "parallelGroup" : "wi:ADR-046-reset-and-cutover", + "prerequisites" : [ + "ADR-046-reset-and-cutover", + "ADR046-reset-006" ], - "specId": "ADR-046-telemetry-audit-and-support", - "topologicalRank": 14, - "wave": "W5" + "specId" : "ADR-046-reset-and-cutover", + "topologicalRank" : 17, + "wave" : "W7" }, { - "blockers": [], - "destinations": [ - "`packages/d2b-bus/src/routing.rs` adapted from `service_v2.rs`; RemoteNodeErrorKind stable code pattern becomes v3 BusErrorKind." + "blockers" : [], + "destinations" : [ + "`packages/d2b-cutover/src/finalize.rs`" ], - "entryContracts": [ - "ADR-046-telemetry-audit-and-support" + "entryContracts" : [ + "ADR-046-reset-and-cutover" ], - "exitGate": "ADR046-W5 exit criteria (ADR-046-validation-and-delivery §4): every spec/work item in this wave Merged with clean destinations, all validators green, and the ten-role panel seal recorded", - "id": "ADR046-reuse-006", - "kind": "work-item", - "owner": "ADR046-reuse-001 + ADR046-bus-001; bus routing owner", - "parallelGroup": "wi:ADR-046-telemetry-audit-and-support", - "prerequisites": [ - "ADR-046-telemetry-audit-and-support", - "ADR046-bus-001", - "ADR046-reuse-001" + "exitGate" : "ADR046-W7 exit criteria (ADR-046-validation-and-delivery §4): every spec/work item in this wave Merged with clean destinations, all validators green, and the ten-role panel seal recorded", + "id" : "ADR046-reset-008", + "kind" : "work-item", + "owner" : "ADR046-reset-007; owner of each retiring artifact's ADR 0046 successor work item", + "parallelGroup" : "wi:ADR-046-reset-and-cutover", + "prerequisites" : [ + "ADR-046-reset-and-cutover", + "ADR046-reset-007" ], - "specId": "ADR-046-telemetry-audit-and-support", - "topologicalRank": 12, - "wave": "W5" + "specId" : "ADR-046-reset-and-cutover", + "topologicalRank" : 18, + "wave" : "W7" }, { - "blockers": [], - "destinations": [ - "`packages/d2b-bus/src/service_router.rs` and `packages/d2b-core-controller/src/provider_effects.rs`." + "blockers" : [], + "destinations" : [ + "`packages/d2b-cutover/src/{journal,rollback,hold}.rs`" ], - "entryContracts": [ - "ADR-046-telemetry-audit-and-support" + "entryContracts" : [ + "ADR-046-reset-and-cutover" ], - "exitGate": "ADR046-W5 exit criteria (ADR-046-validation-and-delivery §4): every spec/work item in this wave Merged with clean destinations, all validators green, and the ten-role panel seal recorded", - "id": "ADR046-reuse-007", - "kind": "work-item", - "owner": "ADR046-reuse-004 + ADR046-reuse-006; core-controller routing owner", - "parallelGroup": "wi:ADR-046-telemetry-audit-and-support", - "prerequisites": [ - "ADR-046-telemetry-audit-and-support", - "ADR046-reuse-004", - "ADR046-reuse-006" + "exitGate" : "ADR046-W7 exit criteria (ADR-046-validation-and-delivery §4): every spec/work item in this wave Merged with clean destinations, all validators green, and the ten-role panel seal recorded", + "id" : "ADR046-reset-009", + "kind" : "work-item", + "owner" : "ADR046-reset-003 through ADR046-reset-006; `ADR-046-provider-state` incident-hold owner", + "parallelGroup" : "wi:ADR-046-reset-and-cutover", + "prerequisites" : [ + "ADR-046-reset-and-cutover", + "ADR046-reset-003", + "ADR046-reset-004", + "ADR046-reset-005", + "ADR046-reset-006" ], - "specId": "ADR-046-telemetry-audit-and-support", - "topologicalRank": 14, - "wave": "W5" + "specId" : "ADR-046-reset-and-cutover", + "topologicalRank" : 17, + "wave" : "W7" }, { - "blockers": [], - "destinations": [ - "`packages/d2b-contract-tests/tests/component_session_v2_vectors.rs` and `tests/noise_vectors.rs` copied verbatim." + "blockers" : [], + "destinations" : [ + "`packages/d2b-cutover/src/reset_scope.rs`; `d2b host reset` CLI command" ], - "entryContracts": [ - "ADR-046-telemetry-audit-and-support" + "entryContracts" : [ + "ADR-046-reset-and-cutover" ], - "exitGate": "ADR046-W5 exit criteria (ADR-046-validation-and-delivery §4): every spec/work item in this wave Merged with clean destinations, all validators green, and the ten-role panel seal recorded", - "id": "ADR046-reuse-008", - "kind": "work-item", - "owner": "ADR046-reuse-001; contract-tests owner", - "parallelGroup": "wi:ADR-046-telemetry-audit-and-support", - "prerequisites": [ - "ADR-046-telemetry-audit-and-support", - "ADR046-reuse-001" + "exitGate" : "ADR046-W7 exit criteria (ADR-046-validation-and-delivery §4): every spec/work item in this wave Merged with clean destinations, all validators green, and the ten-role panel seal recorded", + "id" : "ADR046-reset-010", + "kind" : "work-item", + "owner" : "ADR046-reset-005 through ADR046-reset-007; `ADR-046-resources-zone-control` owner (§2.6/§9.4)", + "parallelGroup" : "wi:ADR-046-reset-and-cutover", + "prerequisites" : [ + "ADR-046-reset-and-cutover", + "ADR046-reset-005", + "ADR046-reset-006", + "ADR046-reset-007" ], - "specId": "ADR-046-telemetry-audit-and-support", - "topologicalRank": 12, - "wave": "W5" + "specId" : "ADR-046-reset-and-cutover", + "topologicalRank" : 18, + "wave" : "W7" }, { - "blockers": [], - "destinations": [ - "`packages/d2b-telemetry/src/session_metrics_sink.rs`." + "blockers" : [], + "destinations" : [ + "`tests/integration/live/cutover-real-host.sh`, `tests/integration/live/cutover-real-host-cloud-guest.sh`, `tests/host-integration/hardware/cutover-real-tpm.sh`, `tests/host-integration/hardware/cutover-real-usbip-security-key.sh`" ], - "entryContracts": [ - "ADR-046-telemetry-audit-and-support" + "entryContracts" : [ + "ADR-046-reset-and-cutover" ], - "exitGate": "ADR046-W5 exit criteria (ADR-046-validation-and-delivery §4): every spec/work item in this wave Merged with clean destinations, all validators green, and the ten-role panel seal recorded", - "id": "ADR046-reuse-009", - "kind": "work-item", - "owner": "ADR046-reuse-001 + ADR046-telem-001 + ADR046-telem-003; telemetry/session owner", - "parallelGroup": "wi:ADR-046-telemetry-audit-and-support", - "prerequisites": [ - "ADR-046-telemetry-audit-and-support", - "ADR046-reuse-001", - "ADR046-telem-001", - "ADR046-telem-003" + "exitGate" : "ADR046-W7 exit criteria (ADR-046-validation-and-delivery §4): every spec/work item in this wave Merged with clean destinations, all validators green, and the ten-role panel seal recorded", + "id" : "ADR046-reset-011", + "kind" : "work-item", + "owner" : "ADR046-reset-001 through ADR046-reset-010, fully landed", + "parallelGroup" : "wi:ADR-046-reset-and-cutover", + "prerequisites" : [ + "ADR-046-reset-and-cutover", + "ADR046-reset-001", + "ADR046-reset-002", + "ADR046-reset-003", + "ADR046-reset-004", + "ADR046-reset-005", + "ADR046-reset-006", + "ADR046-reset-007", + "ADR046-reset-008", + "ADR046-reset-009", + "ADR046-reset-010" ], - "specId": "ADR-046-telemetry-audit-and-support", - "topologicalRank": 12, - "wave": "W5" + "specId" : "ADR-046-reset-and-cutover", + "topologicalRank" : 19, + "wave" : "W7" }, { - "blockers": [], - "destinations": [ + "blockers" : [], + "destinations" : [ "`packages/d2b-contracts/src/v3/zone_routing.rs`" ], - "entryContracts": [ + "entryContracts" : [ "ADR-046-zone-routing" ], - "exitGate": "ADR046-W2 exit criteria (ADR-046-validation-and-delivery §4): every spec/work item in this wave Merged with clean destinations, all validators green, and the ten-role panel seal recorded", - "id": "ADR046-routing-001", - "kind": "work-item", - "owner": "W0/W1a; zone routing contract owner", - "parallelGroup": "wi:ADR-046-zone-routing", - "prerequisites": [ + "exitGate" : "ADR046-W2 exit criteria (ADR-046-validation-and-delivery §4): every spec/work item in this wave Merged with clean destinations, all validators green, and the ten-role panel seal recorded", + "id" : "ADR046-routing-001", + "kind" : "work-item", + "owner" : "W0/W1a; zone routing contract owner", + "parallelGroup" : "wi:ADR-046-zone-routing", + "prerequisites" : [ "ADR-046-zone-routing" ], - "specId": "ADR-046-zone-routing", - "topologicalRank": 7, - "wave": "W2" + "specId" : "ADR-046-zone-routing", + "topologicalRank" : 7, + "wave" : "W2" }, { - "blockers": [], - "destinations": [ + "blockers" : [], + "destinations" : [ "`packages/d2b-zone-routing/src/engine.rs`" ], - "entryContracts": [ + "entryContracts" : [ "ADR-046-zone-routing" ], - "exitGate": "ADR046-W2 exit criteria (ADR-046-validation-and-delivery §4): every spec/work item in this wave Merged with clean destinations, all validators green, and the ten-role panel seal recorded", - "id": "ADR046-routing-002", - "kind": "work-item", - "owner": "ADR046-routing-001; zone route engine owner", - "parallelGroup": "wi:ADR-046-zone-routing", - "prerequisites": [ + "exitGate" : "ADR046-W2 exit criteria (ADR-046-validation-and-delivery §4): every spec/work item in this wave Merged with clean destinations, all validators green, and the ten-role panel seal recorded", + "id" : "ADR046-routing-002", + "kind" : "work-item", + "owner" : "ADR046-routing-001; zone route engine owner", + "parallelGroup" : "wi:ADR-046-zone-routing", + "prerequisites" : [ "ADR-046-zone-routing", "ADR046-routing-001" ], - "specId": "ADR-046-zone-routing", - "topologicalRank": 8, - "wave": "W2" + "specId" : "ADR-046-zone-routing", + "topologicalRank" : 8, + "wave" : "W2" }, { - "blockers": [], - "destinations": [ + "blockers" : [], + "destinations" : [ "`packages/d2b-contracts/src/v3/zone_link.rs` (ZoneLink spec/status types, intent types, namespace allocation); `packages/d2b-zone-routing/src/resolver.rs` (ZoneEntrypointResolver)" ], - "entryContracts": [ + "entryContracts" : [ "ADR-046-zone-routing" ], - "exitGate": "ADR046-W2 exit criteria (ADR-046-validation-and-delivery §4): every spec/work item in this wave Merged with clean destinations, all validators green, and the ten-role panel seal recorded", - "id": "ADR046-routing-003", - "kind": "work-item", - "owner": "ADR046-routing-001, ADR046-routing-002; ZoneLink resource owner", - "parallelGroup": "wi:ADR-046-zone-routing", - "prerequisites": [ + "exitGate" : "ADR046-W2 exit criteria (ADR-046-validation-and-delivery §4): every spec/work item in this wave Merged with clean destinations, all validators green, and the ten-role panel seal recorded", + "id" : "ADR046-routing-003", + "kind" : "work-item", + "owner" : "ADR046-routing-001, ADR046-routing-002; ZoneLink resource owner", + "parallelGroup" : "wi:ADR-046-zone-routing", + "prerequisites" : [ "ADR-046-zone-routing", "ADR046-routing-001", "ADR046-routing-002" ], - "specId": "ADR-046-zone-routing", - "topologicalRank": 9, - "wave": "W2" + "specId" : "ADR-046-zone-routing", + "topologicalRank" : 9, + "wave" : "W2" }, { - "blockers": [], - "destinations": [ + "blockers" : [], + "destinations" : [ "`packages/d2b-core-controller/src/zone_links.rs`" ], - "entryContracts": [ + "entryContracts" : [ "ADR-046-zone-routing" ], - "exitGate": "ADR046-W2 exit criteria (ADR-046-validation-and-delivery §4): every spec/work item in this wave Merged with clean destinations, all validators green, and the ten-role panel seal recorded", - "id": "ADR046-routing-004", - "kind": "work-item", - "owner": "ADR046-routing-003; core-controller ZoneLink handler owner", - "parallelGroup": "wi:ADR-046-zone-routing", - "prerequisites": [ + "exitGate" : "ADR046-W2 exit criteria (ADR-046-validation-and-delivery §4): every spec/work item in this wave Merged with clean destinations, all validators green, and the ten-role panel seal recorded", + "id" : "ADR046-routing-004", + "kind" : "work-item", + "owner" : "ADR046-routing-003; core-controller ZoneLink handler owner", + "parallelGroup" : "wi:ADR-046-zone-routing", + "prerequisites" : [ "ADR-046-zone-routing", "ADR046-routing-003" ], - "specId": "ADR-046-zone-routing", - "topologicalRank": 10, - "wave": "W2" + "specId" : "ADR-046-zone-routing", + "topologicalRank" : 10, + "wave" : "W2" }, { - "blockers": [], - "destinations": [ + "blockers" : [], + "destinations" : [ "`packages/d2b-bus/src/zone_route.rs` (cross-Zone bus routing), `packages/d2b-bus/src/relay.rs` (per-hop relay handler)" ], - "entryContracts": [ + "entryContracts" : [ "ADR-046-zone-routing" ], - "exitGate": "ADR046-W2 exit criteria (ADR-046-validation-and-delivery §4): every spec/work item in this wave Merged with clean destinations, all validators green, and the ten-role panel seal recorded", - "id": "ADR046-routing-005", - "kind": "work-item", - "owner": "ADR046-routing-002, ADR046-routing-007 (from ComponentSession spec); d2b-bus owner", - "parallelGroup": "wi:ADR-046-zone-routing", - "prerequisites": [ + "exitGate" : "ADR046-W2 exit criteria (ADR-046-validation-and-delivery §4): every spec/work item in this wave Merged with clean destinations, all validators green, and the ten-role panel seal recorded", + "id" : "ADR046-routing-005", + "kind" : "work-item", + "owner" : "ADR046-routing-002, ADR046-routing-007 (from ComponentSession spec); d2b-bus owner", + "parallelGroup" : "wi:ADR-046-zone-routing", + "prerequisites" : [ "ADR-046-zone-routing", "ADR046-routing-002", "ADR046-routing-007" ], - "specId": "ADR-046-zone-routing", - "topologicalRank": 9, - "wave": "W2" + "specId" : "ADR-046-zone-routing", + "topologicalRank" : 9, + "wave" : "W2" }, { - "blockers": [], - "destinations": [ + "blockers" : [], + "destinations" : [ "`packages/d2b-zone-routing/tests/route_engine_vectors.rs`, `packages/d2b-zone-routing/benches/route_decision.rs`" ], - "entryContracts": [ + "entryContracts" : [ "ADR-046-zone-routing" ], - "exitGate": "ADR046-W2 exit criteria (ADR-046-validation-and-delivery §4): every spec/work item in this wave Merged with clean destinations, all validators green, and the ten-role panel seal recorded", - "id": "ADR046-routing-006", - "kind": "work-item", - "owner": "ADR046-routing-002; benchmark owner", - "parallelGroup": "wi:ADR-046-zone-routing", - "prerequisites": [ + "exitGate" : "ADR046-W2 exit criteria (ADR-046-validation-and-delivery §4): every spec/work item in this wave Merged with clean destinations, all validators green, and the ten-role panel seal recorded", + "id" : "ADR046-routing-006", + "kind" : "work-item", + "owner" : "ADR046-routing-002; benchmark owner", + "parallelGroup" : "wi:ADR-046-zone-routing", + "prerequisites" : [ "ADR-046-zone-routing", "ADR046-routing-002" ], - "specId": "ADR-046-zone-routing", - "topologicalRank": 9, - "wave": "W2" + "specId" : "ADR-046-zone-routing", + "topologicalRank" : 9, + "wave" : "W2" }, { - "blockers": [], - "destinations": [ + "blockers" : [], + "destinations" : [ "`packages/d2b-bus/src/session/`" ], - "entryContracts": [ + "entryContracts" : [ "ADR-046-zone-routing" ], - "exitGate": "ADR046-W2 exit criteria (ADR-046-validation-and-delivery §4): every spec/work item in this wave Merged with clean destinations, all validators green, and the ten-role panel seal recorded", - "id": "ADR046-routing-007", - "kind": "work-item", - "owner": "W0; d2b-bus owner", - "parallelGroup": "wi:ADR-046-zone-routing", - "prerequisites": [ + "exitGate" : "ADR046-W2 exit criteria (ADR-046-validation-and-delivery §4): every spec/work item in this wave Merged with clean destinations, all validators green, and the ten-role panel seal recorded", + "id" : "ADR046-routing-007", + "kind" : "work-item", + "owner" : "W0; d2b-bus owner", + "parallelGroup" : "wi:ADR-046-zone-routing", + "prerequisites" : [ "ADR-046-zone-routing" ], - "specId": "ADR-046-zone-routing", - "topologicalRank": 7, - "wave": "W2" + "specId" : "ADR-046-zone-routing", + "topologicalRank" : 7, + "wave" : "W2" }, { - "blockers": [], - "destinations": [ + "blockers" : [], + "destinations" : [ "`packages/d2b-bus/src/transport/unix.rs`, `packages/d2b-bus/src/transport/credit.rs`" ], - "entryContracts": [ + "entryContracts" : [ "ADR-046-zone-routing" ], - "exitGate": "ADR046-W2 exit criteria (ADR-046-validation-and-delivery §4): every spec/work item in this wave Merged with clean destinations, all validators green, and the ten-role panel seal recorded", - "id": "ADR046-routing-008", - "kind": "work-item", - "owner": "ADR046-routing-007; transport-provider owner", - "parallelGroup": "wi:ADR-046-zone-routing", - "prerequisites": [ + "exitGate" : "ADR046-W2 exit criteria (ADR-046-validation-and-delivery §4): every spec/work item in this wave Merged with clean destinations, all validators green, and the ten-role panel seal recorded", + "id" : "ADR046-routing-008", + "kind" : "work-item", + "owner" : "ADR046-routing-007; transport-provider owner", + "parallelGroup" : "wi:ADR-046-zone-routing", + "prerequisites" : [ "ADR-046-zone-routing", "ADR046-routing-007" ], - "specId": "ADR-046-zone-routing", - "topologicalRank": 8, - "wave": "W2" + "specId" : "ADR-046-zone-routing", + "topologicalRank" : 8, + "wave" : "W2" }, { - "blockers": [], - "destinations": [ + "blockers" : [], + "destinations" : [ "`packages/d2b-contracts/src/v3/zone_session.rs`" ], - "entryContracts": [ + "entryContracts" : [ "ADR-046-zone-routing" ], - "exitGate": "ADR046-W2 exit criteria (ADR-046-validation-and-delivery §4): every spec/work item in this wave Merged with clean destinations, all validators green, and the ten-role panel seal recorded", - "id": "ADR046-routing-009", - "kind": "work-item", - "owner": "ADR046-routing-007; contracts owner", - "parallelGroup": "wi:ADR-046-zone-routing", - "prerequisites": [ + "exitGate" : "ADR046-W2 exit criteria (ADR-046-validation-and-delivery §4): every spec/work item in this wave Merged with clean destinations, all validators green, and the ten-role panel seal recorded", + "id" : "ADR046-routing-009", + "kind" : "work-item", + "owner" : "ADR046-routing-007; contracts owner", + "parallelGroup" : "wi:ADR-046-zone-routing", + "prerequisites" : [ "ADR-046-zone-routing", "ADR046-routing-007" ], - "specId": "ADR-046-zone-routing", - "topologicalRank": 8, - "wave": "W2" + "specId" : "ADR-046-zone-routing", + "topologicalRank" : 8, + "wave" : "W2" }, { - "blockers": [], - "destinations": [ + "blockers" : [], + "destinations" : [ "`packages/d2b-resource-client/`" ], - "entryContracts": [ + "entryContracts" : [ "ADR-046-zone-routing" ], - "exitGate": "ADR046-W2 exit criteria (ADR-046-validation-and-delivery §4): every spec/work item in this wave Merged with clean destinations, all validators green, and the ten-role panel seal recorded", - "id": "ADR046-routing-010", - "kind": "work-item", - "owner": "ADR046-routing-007, ADR046-routing-009; resource-client owner", - "parallelGroup": "wi:ADR-046-zone-routing", - "prerequisites": [ + "exitGate" : "ADR046-W2 exit criteria (ADR-046-validation-and-delivery §4): every spec/work item in this wave Merged with clean destinations, all validators green, and the ten-role panel seal recorded", + "id" : "ADR046-routing-010", + "kind" : "work-item", + "owner" : "ADR046-routing-007, ADR046-routing-009; resource-client owner", + "parallelGroup" : "wi:ADR-046-zone-routing", + "prerequisites" : [ "ADR-046-zone-routing", "ADR046-routing-007", "ADR046-routing-009" ], - "specId": "ADR-046-zone-routing", - "topologicalRank": 9, - "wave": "W2" + "specId" : "ADR-046-zone-routing", + "topologicalRank" : 9, + "wave" : "W2" }, { - "blockers": [], - "destinations": [ + "blockers" : [], + "destinations" : [ "`nixos-modules/options-zones.nix` (new structural base), `nixos-modules/generated/options-zones-.nix` (generated per ResourceType by `xtask gen-zone-nix-options`), `nixos-modules/assertions.nix` (new Zone assertions)" ], - "entryContracts": [ + "entryContracts" : [ "ADR-046-zone-routing" ], - "exitGate": "ADR046-W2 exit criteria (ADR-046-validation-and-delivery §4): every spec/work item in this wave Merged with clean destinations, all validators green, and the ten-role panel seal recorded", - "id": "ADR046-routing-011", - "kind": "work-item", - "owner": "ADR046-routing-001; Nix module owner", - "parallelGroup": "wi:ADR-046-zone-routing", - "prerequisites": [ + "exitGate" : "ADR046-W2 exit criteria (ADR-046-validation-and-delivery §4): every spec/work item in this wave Merged with clean destinations, all validators green, and the ten-role panel seal recorded", + "id" : "ADR046-routing-011", + "kind" : "work-item", + "owner" : "ADR046-routing-001; Nix module owner", + "parallelGroup" : "wi:ADR-046-zone-routing", + "prerequisites" : [ "ADR-046-zone-routing", "ADR046-routing-001" ], - "specId": "ADR-046-zone-routing", - "topologicalRank": 8, - "wave": "W2" + "specId" : "ADR-046-zone-routing", + "topologicalRank" : 8, + "wave" : "W2" }, { - "blockers": [], - "destinations": [ - "`nixos-modules/zone-resources-json.nix` (new), `nixos-modules/bundle-artifacts.nix` (new row for per-Zone `resource-bundle.json`), `packages/xtask/src/main.rs` (`gen-zone-schemas` subcommand emitting `docs/reference/schemas/v3/.schema.json` for Zone and ZoneLink; `gen-zone-nix-options` subcommand emitting `nixos-modules/generated/options-zones-.nix`)" + "blockers" : [], + "destinations" : [ + "`nixos-modules/zone-resources-json.nix` (new), private local-root allocator bootstrap compiler/sealer input (not a ResourceSpec or public bundle), `nixos-modules/bundle-artifacts.nix` (new row for per-Zone `resource-bundle.json`), `packages/xtask/src/main.rs` (`gen-zone-schemas` subcommand emitting `docs/reference/schemas/v3/.schema.json` for Zone and ZoneLink; `gen-zone-nix-options` subcommand emitting `nixos-modules/generated/options-zones-.nix`)" ], - "entryContracts": [ + "entryContracts" : [ "ADR-046-zone-routing" ], - "exitGate": "ADR046-W2 exit criteria (ADR-046-validation-and-delivery §4): every spec/work item in this wave Merged with clean destinations, all validators green, and the ten-role panel seal recorded", - "id": "ADR046-routing-012", - "kind": "work-item", - "owner": "ADR046-routing-011, ADR046-routing-001; bundle emitter owner", - "parallelGroup": "wi:ADR-046-zone-routing", - "prerequisites": [ + "exitGate" : "ADR046-W2 exit criteria (ADR-046-validation-and-delivery §4): every spec/work item in this wave Merged with clean destinations, all validators green, and the ten-role panel seal recorded", + "id" : "ADR046-routing-012", + "kind" : "work-item", + "owner" : "ADR046-routing-011, ADR046-routing-001; bundle emitter owner", + "parallelGroup" : "wi:ADR-046-zone-routing", + "prerequisites" : [ "ADR-046-zone-routing", "ADR046-routing-001", "ADR046-routing-011" ], - "specId": "ADR-046-zone-routing", - "topologicalRank": 9, - "wave": "W2" + "specId" : "ADR-046-zone-routing", + "topologicalRank" : 9, + "wave" : "W2" }, { - "blockers": [], - "destinations": [ + "blockers" : [], + "destinations" : [ "`packages/d2b-core-controller/src/configuration.rs` (defined by ADR-046-core-controllers); shared bundle DTOs may live in `packages/d2b-core/`" ], - "entryContracts": [ + "entryContracts" : [ "ADR-046-zone-routing" ], - "exitGate": "ADR046-W2 exit criteria (ADR-046-validation-and-delivery §4): every spec/work item in this wave Merged with clean destinations, all validators green, and the ten-role panel seal recorded", - "id": "ADR046-routing-013", - "kind": "work-item", - "owner": "ADR046-routing-012, ADR046-routing-003; `d2b-core-controller` owner (ADR-046-core-controllers)", - "parallelGroup": "wi:ADR-046-zone-routing", - "prerequisites": [ + "exitGate" : "ADR046-W2 exit criteria (ADR-046-validation-and-delivery §4): every spec/work item in this wave Merged with clean destinations, all validators green, and the ten-role panel seal recorded", + "id" : "ADR046-routing-013", + "kind" : "work-item", + "owner" : "ADR046-routing-012, ADR046-routing-003; `d2b-core-controller` owner (ADR-046-core-controllers)", + "parallelGroup" : "wi:ADR-046-zone-routing", + "prerequisites" : [ "ADR-046-zone-routing", "ADR046-routing-003", "ADR046-routing-012" ], - "specId": "ADR-046-zone-routing", - "topologicalRank": 10, - "wave": "W2" + "specId" : "ADR-046-zone-routing", + "topologicalRank" : 10, + "wave" : "W2" }, { - "blockers": [], - "destinations": [ + "blockers" : [], + "destinations" : [ "`packages/d2b-provider/src/` (adapted in place)" ], - "entryContracts": [ + "entryContracts" : [ "ADR-046-zone-routing" ], - "exitGate": "ADR046-W2 exit criteria (ADR-046-validation-and-delivery §4): every spec/work item in this wave Merged with clean destinations, all validators green, and the ten-role panel seal recorded", - "id": "ADR046-routing-014", - "kind": "work-item", - "owner": "ADR046-routing-007; Provider resource owner", - "parallelGroup": "wi:ADR-046-zone-routing", - "prerequisites": [ + "exitGate" : "ADR046-W2 exit criteria (ADR-046-validation-and-delivery §4): every spec/work item in this wave Merged with clean destinations, all validators green, and the ten-role panel seal recorded", + "id" : "ADR046-routing-014", + "kind" : "work-item", + "owner" : "ADR046-routing-007; Provider resource owner", + "parallelGroup" : "wi:ADR-046-zone-routing", + "prerequisites" : [ "ADR-046-zone-routing", "ADR046-routing-007" ], - "specId": "ADR-046-zone-routing", - "topologicalRank": 8, - "wave": "W2" + "specId" : "ADR-046-zone-routing", + "topologicalRank" : 8, + "wave" : "W2" }, { - "blockers": [], - "destinations": [ + "blockers" : [], + "destinations" : [ "`packages/d2b-provider-toolkit/src/` (adapted in place)" ], - "entryContracts": [ + "entryContracts" : [ "ADR-046-zone-routing" ], - "exitGate": "ADR046-W2 exit criteria (ADR-046-validation-and-delivery §4): every spec/work item in this wave Merged with clean destinations, all validators green, and the ten-role panel seal recorded", - "id": "ADR046-routing-015", - "kind": "work-item", - "owner": "ADR046-routing-014; Provider agent process owner", - "parallelGroup": "wi:ADR-046-zone-routing", - "prerequisites": [ + "exitGate" : "ADR046-W2 exit criteria (ADR-046-validation-and-delivery §4): every spec/work item in this wave Merged with clean destinations, all validators green, and the ten-role panel seal recorded", + "id" : "ADR046-routing-015", + "kind" : "work-item", + "owner" : "ADR046-routing-014; Provider agent process owner", + "parallelGroup" : "wi:ADR-046-zone-routing", + "prerequisites" : [ "ADR-046-zone-routing", "ADR046-routing-014" ], - "specId": "ADR-046-zone-routing", - "topologicalRank": 9, - "wave": "W2" + "specId" : "ADR-046-zone-routing", + "topologicalRank" : 9, + "wave" : "W2" }, { - "blockers": [], - "destinations": [ + "blockers" : [], + "destinations" : [ "`packages/d2b-zone-routing/src/service.rs`" ], - "entryContracts": [ + "entryContracts" : [ "ADR-046-zone-routing" ], - "exitGate": "ADR046-W2 exit criteria (ADR-046-validation-and-delivery §4): every spec/work item in this wave Merged with clean destinations, all validators green, and the ten-role panel seal recorded", - "id": "ADR046-routing-016", - "kind": "work-item", - "owner": "ADR046-routing-007, ADR046-routing-001; Zone service owner", - "parallelGroup": "wi:ADR-046-zone-routing", - "prerequisites": [ + "exitGate" : "ADR046-W2 exit criteria (ADR-046-validation-and-delivery §4): every spec/work item in this wave Merged with clean destinations, all validators green, and the ten-role panel seal recorded", + "id" : "ADR046-routing-016", + "kind" : "work-item", + "owner" : "ADR046-routing-007, ADR046-routing-001; Zone service owner", + "parallelGroup" : "wi:ADR-046-zone-routing", + "prerequisites" : [ "ADR-046-zone-routing", "ADR046-routing-001", "ADR046-routing-007" ], - "specId": "ADR-046-zone-routing", - "topologicalRank": 8, - "wave": "W2" + "specId" : "ADR-046-zone-routing", + "topologicalRank" : 8, + "wave" : "W2" }, { - "blockers": [], - "destinations": [ + "blockers" : [], + "destinations" : [ "`packages/d2b-contract-tests/tests/policy_telemetry_redaction.rs`" ], - "entryContracts": [ + "entryContracts" : [ "ADR-046-security-and-threat-model" ], - "exitGate": "ADR046-W7 exit criteria (ADR-046-validation-and-delivery §4): every spec/work item in this wave Merged with clean destinations, all validators green, and the ten-role panel seal recorded", - "id": "ADR046-security-001", - "kind": "work-item", - "owner": "All ResourceType/Provider specs; owned by the security/telemetry integrator alongside `ADR046-telem-008`", - "parallelGroup": "wi:ADR-046-security-and-threat-model", - "prerequisites": [ + "exitGate" : "ADR046-W7 exit criteria (ADR-046-validation-and-delivery §4): every spec/work item in this wave Merged with clean destinations, all validators green, and the ten-role panel seal recorded", + "id" : "ADR046-security-001", + "kind" : "work-item", + "owner" : "All ResourceType/Provider specs; owned by the security/telemetry integrator alongside `ADR046-telem-008`", + "parallelGroup" : "wi:ADR-046-security-and-threat-model", + "prerequisites" : [ "ADR-046-security-and-threat-model", "ADR046-telem-008" ], - "specId": "ADR-046-security-and-threat-model", - "topologicalRank": 12, - "wave": "W7" + "specId" : "ADR-046-security-and-threat-model", + "topologicalRank" : 12, + "wave" : "W7" }, { - "blockers": [], - "destinations": [ + "blockers" : [], + "destinations" : [ "`packages/d2b-session/tests/noise_conformance.rs`, `packages/d2b-session/fuzz/fuzz_targets/{handshake_offer,record_frame}.rs`" ], - "entryContracts": [ + "entryContracts" : [ "ADR-046-security-and-threat-model" ], - "exitGate": "ADR046-W7 exit criteria (ADR-046-validation-and-delivery §4): every spec/work item in this wave Merged with clean destinations, all validators green, and the ten-role panel seal recorded", - "id": "ADR046-security-002", - "kind": "work-item", - "owner": "`ADR046-session-001`/`ADR046-bus-001` (ComponentSession/d2b-bus implementation)", - "parallelGroup": "wi:ADR-046-security-and-threat-model", - "prerequisites": [ + "exitGate" : "ADR046-W7 exit criteria (ADR-046-validation-and-delivery §4): every spec/work item in this wave Merged with clean destinations, all validators green, and the ten-role panel seal recorded", + "id" : "ADR046-security-002", + "kind" : "work-item", + "owner" : "`ADR046-session-001`/`ADR046-session-003` (ComponentSession/d2b-bus implementation)", + "parallelGroup" : "wi:ADR-046-security-and-threat-model", + "prerequisites" : [ "ADR-046-security-and-threat-model", - "ADR046-bus-001", - "ADR046-session-001" + "ADR046-session-001", + "ADR046-session-003" ], - "specId": "ADR-046-security-and-threat-model", - "topologicalRank": 12, - "wave": "W7" + "specId" : "ADR-046-security-and-threat-model", + "topologicalRank" : 12, + "wave" : "W7" }, { - "blockers": [], - "destinations": [ + "blockers" : [], + "destinations" : [ "`packages/d2b-resource-store/tests/rbac_property.rs`" ], - "entryContracts": [ + "entryContracts" : [ "ADR-046-security-and-threat-model" ], - "exitGate": "ADR046-W7 exit criteria (ADR-046-validation-and-delivery §4): every spec/work item in this wave Merged with clean destinations, all validators green, and the ten-role panel seal recorded", - "id": "ADR046-security-003", - "kind": "work-item", - "owner": "`ADR046-zone-control-004`/`ADR046-zone-control-005` (Role/RoleBinding implementation)", - "parallelGroup": "wi:ADR-046-security-and-threat-model", - "prerequisites": [ - "ADR-046-security-and-threat-model" + "exitGate" : "ADR046-W7 exit criteria (ADR-046-validation-and-delivery §4): every spec/work item in this wave Merged with clean destinations, all validators green, and the ten-role panel seal recorded", + "id" : "ADR046-security-003", + "kind" : "work-item", + "owner" : "`ADR046-zone-control-004`/`ADR046-zone-control-005` (Role/RoleBinding implementation)", + "parallelGroup" : "wi:ADR-046-security-and-threat-model", + "prerequisites" : [ + "ADR-046-security-and-threat-model", + "ADR046-zone-control-004", + "ADR046-zone-control-005" ], - "specId": "ADR-046-security-and-threat-model", - "topologicalRank": 12, - "wave": "W7" + "specId" : "ADR-046-security-and-threat-model", + "topologicalRank" : 13, + "wave" : "W7" }, { - "blockers": [], - "destinations": [ + "blockers" : [], + "destinations" : [ "`packages/d2b-bus/fuzz/fuzz_targets/zonelink_frame.rs`, `packages/d2b-bus/tests/zonelink_structural_rejection.rs`" ], - "entryContracts": [ + "entryContracts" : [ "ADR-046-security-and-threat-model" ], - "exitGate": "ADR046-W7 exit criteria (ADR-046-validation-and-delivery §4): every spec/work item in this wave Merged with clean destinations, all validators green, and the ten-role panel seal recorded", - "id": "ADR046-security-004", - "kind": "work-item", - "owner": "`ADR046-routing-005`/`ADR046-bus-001` (ZoneLink/d2b-bus relay implementation)", - "parallelGroup": "wi:ADR-046-security-and-threat-model", - "prerequisites": [ + "exitGate" : "ADR046-W7 exit criteria (ADR-046-validation-and-delivery §4): every spec/work item in this wave Merged with clean destinations, all validators green, and the ten-role panel seal recorded", + "id" : "ADR046-security-004", + "kind" : "work-item", + "owner" : "`ADR046-routing-005`/`ADR046-session-003` (ZoneLink/d2b-bus relay implementation)", + "parallelGroup" : "wi:ADR-046-security-and-threat-model", + "prerequisites" : [ "ADR-046-security-and-threat-model", - "ADR046-bus-001", - "ADR046-routing-005" + "ADR046-routing-005", + "ADR046-session-003" ], - "specId": "ADR-046-security-and-threat-model", - "topologicalRank": 12, - "wave": "W7" + "specId" : "ADR-046-security-and-threat-model", + "topologicalRank" : 12, + "wave" : "W7" }, { - "blockers": [], - "destinations": [ + "blockers" : [], + "destinations" : [ "`packages/xtask/src/effectport_boundary_check.rs`, wired into `make test-policy`" ], - "entryContracts": [ + "entryContracts" : [ "ADR-046-security-and-threat-model" ], - "exitGate": "ADR046-W7 exit criteria (ADR-046-validation-and-delivery §4): every spec/work item in this wave Merged with clean destinations, all validators green, and the ten-role panel seal recorded", - "id": "ADR046-security-005", - "kind": "work-item", - "owner": "Every Provider dossier's own crate; owned by the Provider packaging integrator (`ADR-046-provider-model-and-packaging`)", - "parallelGroup": "wi:ADR-046-security-and-threat-model", - "prerequisites": [ + "exitGate" : "ADR046-W7 exit criteria (ADR-046-validation-and-delivery §4): every spec/work item in this wave Merged with clean destinations, all validators green, and the ten-role panel seal recorded", + "id" : "ADR046-security-005", + "kind" : "work-item", + "owner" : "Every Provider dossier's own crate; owned by the Provider packaging integrator (`ADR-046-provider-model-and-packaging`)", + "parallelGroup" : "wi:ADR-046-security-and-threat-model", + "prerequisites" : [ "ADR-046-security-and-threat-model" ], - "specId": "ADR-046-security-and-threat-model", - "topologicalRank": 12, - "wave": "W7" + "specId" : "ADR-046-security-and-threat-model", + "topologicalRank" : 12, + "wave" : "W7" }, { - "blockers": [], - "destinations": [ + "blockers" : [], + "destinations" : [ "`packages/d2b-provider-system-minijail/tests/launchticket_toctou.rs`" ], - "entryContracts": [ + "entryContracts" : [ "ADR-046-security-and-threat-model" ], - "exitGate": "ADR046-W7 exit criteria (ADR-046-validation-and-delivery §4): every spec/work item in this wave Merged with clean destinations, all validators green, and the ten-role panel seal recorded", - "id": "ADR046-security-006", - "kind": "work-item", - "owner": "`ADR046-minijail-002`/`ADR046-minijail-003` (LaunchTicket/EffectPort implementation)", - "parallelGroup": "wi:ADR-046-security-and-threat-model", - "prerequisites": [ + "exitGate" : "ADR046-W7 exit criteria (ADR-046-validation-and-delivery §4): every spec/work item in this wave Merged with clean destinations, all validators green, and the ten-role panel seal recorded", + "id" : "ADR046-security-006", + "kind" : "work-item", + "owner" : "`ADR046-minijail-002`/`ADR046-minijail-003` (LaunchTicket/EffectPort implementation)", + "parallelGroup" : "wi:ADR-046-security-and-threat-model", + "prerequisites" : [ "ADR-046-security-and-threat-model", "ADR046-minijail-002", "ADR046-minijail-003" ], - "specId": "ADR-046-security-and-threat-model", - "topologicalRank": 13, - "wave": "W7" + "specId" : "ADR-046-security-and-threat-model", + "topologicalRank" : 13, + "wave" : "W7" }, { - "blockers": [], - "destinations": [ + "blockers" : [], + "destinations" : [ "`packages/d2b-contract-tests/tests/quarantine_not_kill_matrix.rs`" ], - "entryContracts": [ + "entryContracts" : [ "ADR-046-security-and-threat-model" ], - "exitGate": "ADR046-W7 exit criteria (ADR-046-validation-and-delivery §4): every spec/work item in this wave Merged with clean destinations, all validators green, and the ten-role panel seal recorded", - "id": "ADR046-security-007", - "kind": "work-item", - "owner": "`ADR046-minijail-005`, `ADR046-systemd-002`, `ADR046-aca-001`, `ADR046-volume-001` (every quarantine-on-ambiguity implementation)", - "parallelGroup": "wi:ADR-046-security-and-threat-model", - "prerequisites": [ + "exitGate" : "ADR046-W7 exit criteria (ADR-046-validation-and-delivery §4): every spec/work item in this wave Merged with clean destinations, all validators green, and the ten-role panel seal recorded", + "id" : "ADR046-security-007", + "kind" : "work-item", + "owner" : "`ADR046-minijail-005`, `ADR046-systemd-002`, `ADR046-aca-001`, `ADR046-volume-001` (every quarantine-on-ambiguity implementation)", + "parallelGroup" : "wi:ADR-046-security-and-threat-model", + "prerequisites" : [ "ADR-046-security-and-threat-model", "ADR046-aca-001", "ADR046-minijail-005", "ADR046-systemd-002", "ADR046-volume-001" ], - "specId": "ADR-046-security-and-threat-model", - "topologicalRank": 15, - "wave": "W7" + "specId" : "ADR-046-security-and-threat-model", + "topologicalRank" : 15, + "wave" : "W7" }, { - "blockers": [], - "destinations": [ + "blockers" : [], + "destinations" : [ "`packages/d2b-provider-system-core/tests/no_isolation_propagation.rs`" ], - "entryContracts": [ + "entryContracts" : [ "ADR-046-security-and-threat-model" ], - "exitGate": "ADR046-W7 exit criteria (ADR-046-validation-and-delivery §4): every spec/work item in this wave Merged with clean destinations, all validators green, and the ten-role panel seal recorded", - "id": "ADR046-security-008", - "kind": "work-item", - "owner": "`ADR046-exec-002` (Host `isolationPosture` propagation)", - "parallelGroup": "wi:ADR-046-security-and-threat-model", - "prerequisites": [ + "exitGate" : "ADR046-W7 exit criteria (ADR-046-validation-and-delivery §4): every spec/work item in this wave Merged with clean destinations, all validators green, and the ten-role panel seal recorded", + "id" : "ADR046-security-008", + "kind" : "work-item", + "owner" : "`ADR046-exec-002` (Host `isolationPosture` propagation)", + "parallelGroup" : "wi:ADR-046-security-and-threat-model", + "prerequisites" : [ "ADR-046-security-and-threat-model", "ADR046-exec-002" ], - "specId": "ADR-046-security-and-threat-model", - "topologicalRank": 12, - "wave": "W7" + "specId" : "ADR-046-security-and-threat-model", + "topologicalRank" : 12, + "wave" : "W7" }, { - "blockers": [], - "destinations": [ + "blockers" : [], + "destinations" : [ "`packages/d2b-provider-volume-local/tests/marker_tamper_fault_injection.rs`" ], - "entryContracts": [ + "entryContracts" : [ "ADR-046-security-and-threat-model" ], - "exitGate": "ADR046-W7 exit criteria (ADR-046-validation-and-delivery §4): every spec/work item in this wave Merged with clean destinations, all validators green, and the ten-role panel seal recorded", - "id": "ADR046-security-009", - "kind": "work-item", - "owner": "`ADR046-pstate-003` (Volume identity marker)", - "parallelGroup": "wi:ADR-046-security-and-threat-model", - "prerequisites": [ + "exitGate" : "ADR046-W7 exit criteria (ADR-046-validation-and-delivery §4): every spec/work item in this wave Merged with clean destinations, all validators green, and the ten-role panel seal recorded", + "id" : "ADR046-security-009", + "kind" : "work-item", + "owner" : "`ADR046-pstate-003` (Volume identity marker)", + "parallelGroup" : "wi:ADR-046-security-and-threat-model", + "prerequisites" : [ "ADR-046-security-and-threat-model", "ADR046-pstate-003" ], - "specId": "ADR-046-security-and-threat-model", - "topologicalRank": 12, - "wave": "W7" + "specId" : "ADR-046-security-and-threat-model", + "topologicalRank" : 12, + "wave" : "W7" }, { - "blockers": [], - "destinations": [ + "blockers" : [], + "destinations" : [ "`packages/d2b-contract-tests/tests/zero_secret_invariant.rs`" ], - "entryContracts": [ + "entryContracts" : [ "ADR-046-security-and-threat-model" ], - "exitGate": "ADR046-W7 exit criteria (ADR-046-validation-and-delivery §4): every spec/work item in this wave Merged with clean destinations, all validators green, and the ten-role panel seal recorded", - "id": "ADR046-security-010", - "kind": "work-item", - "owner": "`ADR046-credential-001` through `ADR046-credential-007`", - "parallelGroup": "wi:ADR-046-security-and-threat-model", - "prerequisites": [ + "exitGate" : "ADR046-W7 exit criteria (ADR-046-validation-and-delivery §4): every spec/work item in this wave Merged with clean destinations, all validators green, and the ten-role panel seal recorded", + "id" : "ADR046-security-010", + "kind" : "work-item", + "owner" : "`ADR046-credential-001` through `ADR046-credential-007`", + "parallelGroup" : "wi:ADR-046-security-and-threat-model", + "prerequisites" : [ "ADR-046-security-and-threat-model", "ADR046-credential-001", "ADR046-credential-007" ], - "specId": "ADR-046-security-and-threat-model", - "topologicalRank": 12, - "wave": "W7" + "specId" : "ADR-046-security-and-threat-model", + "topologicalRank" : 12, + "wave" : "W7" }, { - "blockers": [], - "destinations": [ + "blockers" : [], + "destinations" : [ "`packages/d2b-provider-{clipboard-wayland,shell-terminal,device-security-key,notification-desktop}/tests/stream_redaction.rs` (one per Provider, same shared test helper crate)" ], - "entryContracts": [ + "entryContracts" : [ "ADR-046-security-and-threat-model" ], - "exitGate": "ADR046-W7 exit criteria (ADR-046-validation-and-delivery §4): every spec/work item in this wave Merged with clean destinations, all validators green, and the ten-role panel seal recorded", - "id": "ADR046-security-011", - "kind": "work-item", - "owner": "`ADR046-notify-004`, clipboard/terminal/security-key Provider work items", - "parallelGroup": "wi:ADR-046-security-and-threat-model", - "prerequisites": [ + "exitGate" : "ADR046-W7 exit criteria (ADR-046-validation-and-delivery §4): every spec/work item in this wave Merged with clean destinations, all validators green, and the ten-role panel seal recorded", + "id" : "ADR046-security-011", + "kind" : "work-item", + "owner" : "`ADR046-notify-004`, clipboard/terminal/security-key Provider work items", + "parallelGroup" : "wi:ADR-046-security-and-threat-model", + "prerequisites" : [ "ADR-046-security-and-threat-model", "ADR046-notify-004" ], - "specId": "ADR-046-security-and-threat-model", - "topologicalRank": 14, - "wave": "W7" + "specId" : "ADR-046-security-and-threat-model", + "topologicalRank" : 14, + "wave" : "W7" }, { - "blockers": [], - "destinations": [ + "blockers" : [], + "destinations" : [ "`packages/d2b-audit/tests/privileged_fail_closed.rs`" ], - "entryContracts": [ + "entryContracts" : [ "ADR-046-security-and-threat-model" ], - "exitGate": "ADR046-W7 exit criteria (ADR-046-validation-and-delivery §4): every spec/work item in this wave Merged with clean destinations, all validators green, and the ten-role panel seal recorded", - "id": "ADR046-security-012", - "kind": "work-item", - "owner": "`ADR046-audit-002` (privileged audit durability)", - "parallelGroup": "wi:ADR-046-security-and-threat-model", - "prerequisites": [ + "exitGate" : "ADR046-W7 exit criteria (ADR-046-validation-and-delivery §4): every spec/work item in this wave Merged with clean destinations, all validators green, and the ten-role panel seal recorded", + "id" : "ADR046-security-012", + "kind" : "work-item", + "owner" : "`ADR046-telem-013` (privileged audit durability)", + "parallelGroup" : "wi:ADR-046-security-and-threat-model", + "prerequisites" : [ "ADR-046-security-and-threat-model", - "ADR046-audit-002" + "ADR046-telem-013" ], - "specId": "ADR-046-security-and-threat-model", - "topologicalRank": 12, - "wave": "W7" + "specId" : "ADR-046-security-and-threat-model", + "topologicalRank" : 12, + "wave" : "W7" }, { - "blockers": [], - "destinations": [ + "blockers" : [], + "destinations" : [ "`packages/d2b-bus/tests/dos_ceiling_fault_injection.rs`" ], - "entryContracts": [ + "entryContracts" : [ "ADR-046-security-and-threat-model" ], - "exitGate": "ADR046-W7 exit criteria (ADR-046-validation-and-delivery §4): every spec/work item in this wave Merged with clean destinations, all validators green, and the ten-role panel seal recorded", - "id": "ADR046-security-013", - "kind": "work-item", - "owner": "`ADR046-zone-control-009`/`ADR046-zone-control-010` (Quota/EmergencyPolicy), `ADR046-session-001` (session limits)", - "parallelGroup": "wi:ADR-046-security-and-threat-model", - "prerequisites": [ + "exitGate" : "ADR046-W7 exit criteria (ADR-046-validation-and-delivery §4): every spec/work item in this wave Merged with clean destinations, all validators green, and the ten-role panel seal recorded", + "id" : "ADR046-security-013", + "kind" : "work-item", + "owner" : "`ADR046-zone-control-009`/`ADR046-zone-control-010` (Quota/EmergencyPolicy), `ADR046-session-001` (session limits)", + "parallelGroup" : "wi:ADR-046-security-and-threat-model", + "prerequisites" : [ "ADR-046-security-and-threat-model", - "ADR046-session-001" + "ADR046-session-001", + "ADR046-zone-control-009", + "ADR046-zone-control-010" ], - "specId": "ADR-046-security-and-threat-model", - "topologicalRank": 12, - "wave": "W7" + "specId" : "ADR-046-security-and-threat-model", + "topologicalRank" : 12, + "wave" : "W7" }, { - "blockers": [], - "destinations": [ + "blockers" : [], + "destinations" : [ "`packages/d2b/src/commands/{doctor,support_bundle}.rs`" ], - "entryContracts": [ + "entryContracts" : [ "ADR-046-security-and-threat-model" ], - "exitGate": "ADR046-W7 exit criteria (ADR-046-validation-and-delivery §4): every spec/work item in this wave Merged with clean destinations, all validators green, and the ten-role panel seal recorded", - "id": "ADR046-security-014", - "kind": "work-item", - "owner": "`ADR046-doctor-001`/`ADR046-doctor-002` (doctor/support-bundle commands)", - "parallelGroup": "wi:ADR-046-security-and-threat-model", - "prerequisites": [ + "exitGate" : "ADR046-W7 exit criteria (ADR-046-validation-and-delivery §4): every spec/work item in this wave Merged with clean destinations, all validators green, and the ten-role panel seal recorded", + "id" : "ADR046-security-014", + "kind" : "work-item", + "owner" : "`ADR046-telem-016`/`ADR046-telem-017` (doctor/support-bundle commands)", + "parallelGroup" : "wi:ADR-046-security-and-threat-model", + "prerequisites" : [ "ADR-046-security-and-threat-model", - "ADR046-doctor-001", - "ADR046-doctor-002" + "ADR046-telem-016", + "ADR046-telem-017" ], - "specId": "ADR-046-security-and-threat-model", - "topologicalRank": 13, - "wave": "W7" + "specId" : "ADR-046-security-and-threat-model", + "topologicalRank" : 13, + "wave" : "W7" }, { - "blockers": [], - "destinations": [ + "blockers" : [], + "destinations" : [ "`packages/d2b-core-controller/src/reset.rs`, `packages/d2b-core-controller/tests/reset_atomicity.rs`" ], - "entryContracts": [ + "entryContracts" : [ "ADR-046-security-and-threat-model" ], - "exitGate": "ADR046-W7 exit criteria (ADR-046-validation-and-delivery §4): every spec/work item in this wave Merged with clean destinations, all validators green, and the ten-role panel seal recorded", - "id": "ADR046-security-015", - "kind": "work-item", - "owner": "`ADR046-audit-001` (`StateReset` record), Volume/Credential lifecycle work items", - "parallelGroup": "wi:ADR-046-security-and-threat-model", - "prerequisites": [ + "exitGate" : "ADR046-W7 exit criteria (ADR-046-validation-and-delivery §4): every spec/work item in this wave Merged with clean destinations, all validators green, and the ten-role panel seal recorded", + "id" : "ADR046-security-015", + "kind" : "work-item", + "owner" : "`ADR046-telem-012` (`StateReset` record), Volume/Credential lifecycle work items", + "parallelGroup" : "wi:ADR-046-security-and-threat-model", + "prerequisites" : [ "ADR-046-security-and-threat-model", - "ADR046-audit-001" + "ADR046-telem-012" ], - "specId": "ADR-046-security-and-threat-model", - "topologicalRank": 12, - "wave": "W7" + "specId" : "ADR-046-security-and-threat-model", + "topologicalRank" : 12, + "wave" : "W7" }, { - "blockers": [], - "destinations": [ + "blockers" : [], + "destinations" : [ "`tests/unit/gates/security-matrix-coverage.sh`" ], - "entryContracts": [ + "entryContracts" : [ "ADR-046-security-and-threat-model" ], - "exitGate": "ADR046-W7 exit criteria (ADR-046-validation-and-delivery §4): every spec/work item in this wave Merged with clean destinations, all validators green, and the ten-role panel seal recorded", - "id": "ADR046-security-016", - "kind": "work-item", - "owner": "Documentation/CI integrator; depends on every Provider dossier's own `Security` section existing", - "parallelGroup": "wi:ADR-046-security-and-threat-model", - "prerequisites": [ + "exitGate" : "ADR046-W7 exit criteria (ADR-046-validation-and-delivery §4): every spec/work item in this wave Merged with clean destinations, all validators green, and the ten-role panel seal recorded", + "id" : "ADR046-security-016", + "kind" : "work-item", + "owner" : "Documentation/CI integrator; depends on every Provider dossier's own `Security` section existing", + "parallelGroup" : "wi:ADR-046-security-and-threat-model", + "prerequisites" : [ "ADR-046-security-and-threat-model" ], - "specId": "ADR-046-security-and-threat-model", - "topologicalRank": 12, - "wave": "W7" + "specId" : "ADR-046-security-and-threat-model", + "topologicalRank" : 12, + "wave" : "W7" }, { - "blockers": [], - "destinations": [ + "blockers" : [], + "destinations" : [ "`tests/integration/containers/malicious-child-zone.rs`" ], - "entryContracts": [ + "entryContracts" : [ "ADR-046-security-and-threat-model" ], - "exitGate": "ADR046-W7 exit criteria (ADR-046-validation-and-delivery §4): every spec/work item in this wave Merged with clean destinations, all validators green, and the ten-role panel seal recorded", - "id": "ADR046-security-017", - "kind": "work-item", - "owner": "`ADR046-routing-004`, gateway-custody Provider work items (`ADR046-aca-*`, `ADR046-azure-vm-*`, `ADR046-transport-relay-*`)", - "parallelGroup": "wi:ADR-046-security-and-threat-model", - "prerequisites": [ + "exitGate" : "ADR046-W7 exit criteria (ADR-046-validation-and-delivery §4): every spec/work item in this wave Merged with clean destinations, all validators green, and the ten-role panel seal recorded", + "id" : "ADR046-security-017", + "kind" : "work-item", + "owner" : "`ADR046-routing-004`, gateway-custody Provider work items (`ADR046-aca-*`, `ADR046-azure-vm-*`, `ADR046-transport-relay-*`)", + "parallelGroup" : "wi:ADR-046-security-and-threat-model", + "prerequisites" : [ "ADR-046-security-and-threat-model", "ADR046-routing-004" ], - "specId": "ADR-046-security-and-threat-model", - "topologicalRank": 12, - "wave": "W7" + "specId" : "ADR-046-security-and-threat-model", + "topologicalRank" : 12, + "wave" : "W7" }, { - "blockers": [], - "destinations": [ + "blockers" : [], + "destinations" : [ "`docs/reference/security-manual-validation-checklist.md` (new reference doc, out of scope for this spec's own file but named here as the required destination for the future implementation PR)" ], - "entryContracts": [ + "entryContracts" : [ "ADR-046-security-and-threat-model" ], - "exitGate": "ADR046-W7 exit criteria (ADR-046-validation-and-delivery §4): every spec/work item in this wave Merged with clean destinations, all validators green, and the ten-role panel seal recorded", - "id": "ADR046-security-018", - "kind": "work-item", - "owner": "Manual validation owner; depends on `ADR046-azure-vm-*`, `ADR046-transport-relay-*`, `ADR046-aca-*`, device Provider work items reaching a testable state", - "parallelGroup": "wi:ADR-046-security-and-threat-model", - "prerequisites": [ + "exitGate" : "ADR046-W7 exit criteria (ADR-046-validation-and-delivery §4): every spec/work item in this wave Merged with clean destinations, all validators green, and the ten-role panel seal recorded", + "id" : "ADR046-security-018", + "kind" : "work-item", + "owner" : "Manual validation owner; depends on `ADR046-azure-vm-*`, `ADR046-transport-relay-*`, `ADR046-aca-*`, device Provider work items reaching a testable state", + "parallelGroup" : "wi:ADR-046-security-and-threat-model", + "prerequisites" : [ "ADR-046-security-and-threat-model" ], - "specId": "ADR-046-security-and-threat-model", - "topologicalRank": 12, - "wave": "W7" + "specId" : "ADR-046-security-and-threat-model", + "topologicalRank" : 12, + "wave" : "W7" }, { - "blockers": [], - "destinations": [ - "`packages/d2b-contracts/src/v3/component_session.rs`, `packages/d2b-session/`" + "blockers" : [], + "destinations" : [ + "`packages/d2b-contract-tests/tests/minijail_process_ownership.rs`; `tests/host-integration/minijail-cgroup-kill.nix`" ], - "entryContracts": [ - "ADR-046-componentsession-and-bus" + "entryContracts" : [ + "ADR-046-security-and-threat-model" ], - "exitGate": "ADR046-W1 exit criteria (ADR-046-validation-and-delivery §4): every spec/work item in this wave Merged with clean destinations, all validators green, and the ten-role panel seal recorded", - "id": "ADR046-session-001", - "kind": "work-item", - "owner": "W0 shared contract root", - "parallelGroup": "wi:ADR-046-componentsession-and-bus", - "prerequisites": [ - "ADR-046-componentsession-and-bus" + "exitGate" : "ADR046-W7 exit criteria (ADR-046-validation-and-delivery §4): every spec/work item in this wave Merged with clean destinations, all validators green, and the ten-role panel seal recorded", + "id" : "ADR046-security-019", + "kind" : "work-item", + "owner" : "`ADR046-exec-003`/`ADR046-exec-007` and `ADR046-minijail-003` through `ADR046-minijail-005`; system-minijail/broker integration owner", + "parallelGroup" : "wi:ADR-046-security-and-threat-model", + "prerequisites" : [ + "ADR-046-security-and-threat-model", + "ADR046-exec-003", + "ADR046-exec-007", + "ADR046-minijail-003", + "ADR046-minijail-005" ], - "specId": "ADR-046-componentsession-and-bus", - "topologicalRank": 6, - "wave": "W1" + "specId" : "ADR-046-security-and-threat-model", + "topologicalRank" : 15, + "wave" : "W7" }, { - "blockers": [], - "destinations": [ - "`packages/d2b-session-unix/`, future enrolled transport adapter crates" + "blockers" : [], + "destinations" : [ + "Move to `packages/d2b-provider-device-security-key/src/session.rs` and `cid.rs`; adapt to Provider Process model (remove daemon Mutex wrapping, add async relay protocol)" ], - "entryContracts": [ - "ADR-046-componentsession-and-bus" + "entryContracts" : [ + "ADR-046-provider-device-security-key" ], - "exitGate": "ADR046-W1 exit criteria (ADR-046-validation-and-delivery §4): every spec/work item in this wave Merged with clean destinations, all validators green, and the ten-role panel seal recorded", - "id": "ADR046-session-002", - "kind": "work-item", - "owner": "ADR046-session-001; transport owner", - "parallelGroup": "wi:ADR-046-componentsession-and-bus", - "prerequisites": [ - "ADR-046-componentsession-and-bus", - "ADR046-session-001" + "exitGate" : "ADR046-W6 exit criteria (ADR-046-validation-and-delivery §4): every spec/work item in this wave Merged with clean destinations, all validators green, and the ten-role panel seal recorded", + "id" : "ADR046-security-key-001", + "kind" : "work-item", + "owner" : "ADR-046 provider-device-security-key session/relay owner; depends on ADR046-security-key-008 and the ComponentSession/Process contracts.", + "parallelGroup" : "wi:ADR-046-provider-device-security-key", + "prerequisites" : [ + "ADR-046-provider-device-security-key", + "ADR046-security-key-008" ], - "specId": "ADR-046-componentsession-and-bus", - "topologicalRank": 7, - "wave": "W1" + "specId" : "ADR-046-provider-device-security-key", + "topologicalRank" : 12, + "wave" : "W6" }, { - "blockers": [], - "destinations": [ - "`packages/d2b-provider-shell-terminal/src/resources/{pool,session}.rs`" + "blockers" : [], + "destinations" : [ + "Move to `packages/d2b-provider-device-security-key/src/relay.rs`; replace daemon-internal Unix socket proxy with ComponentSession over the owned Service Endpoint" ], - "entryContracts": [ - "ADR-046-provider-shell-terminal" + "entryContracts" : [ + "ADR-046-provider-device-security-key" ], - "exitGate": "ADR046-W6 exit criteria (ADR-046-validation-and-delivery §4): every spec/work item in this wave Merged with clean destinations, all validators green, and the ten-role panel seal recorded", - "id": "ADR046-sterm-001", - "kind": "work-item", - "owner": "Resource schemas area; owned by `d2b-provider-shell-terminal` resource modules.", - "parallelGroup": "wi:ADR-046-provider-shell-terminal", - "prerequisites": [ - "ADR-046-provider-shell-terminal" + "exitGate" : "ADR046-W6 exit criteria (ADR-046-validation-and-delivery §4): every spec/work item in this wave Merged with clean destinations, all validators green, and the ten-role panel seal recorded", + "id" : "ADR046-security-key-002", + "kind" : "work-item", + "owner" : "ADR-046 provider-device-security-key relay extraction owner; depends on ADR046-security-key-008, ADR046-security-key-001, and the frozen ComponentSession/Endpoint contracts.", + "parallelGroup" : "wi:ADR-046-provider-device-security-key", + "prerequisites" : [ + "ADR-046-provider-device-security-key", + "ADR046-security-key-001", + "ADR046-security-key-008" ], - "specId": "ADR-046-provider-shell-terminal", - "topologicalRank": 11, - "wave": "W6" + "specId" : "ADR-046-provider-device-security-key", + "topologicalRank" : 13, + "wave" : "W6" }, { - "blockers": [], - "destinations": [ - "`packages/d2b-provider-shell-terminal/src/bin/d2b-shell-terminal-controller.rs`" + "blockers" : [], + "destinations" : [ + "Adopt `main.rs` and `uhid.rs` as the v3 Process binary entry point; replace `framing.rs`/`vsock.rs` with ComponentSession client from `d2b-session-unix/src/vsock.rs`; wire as Process service in Provider crate" ], - "entryContracts": [ - "ADR-046-provider-shell-terminal" + "entryContracts" : [ + "ADR-046-provider-device-security-key" ], - "exitGate": "ADR046-W6 exit criteria (ADR-046-validation-and-delivery §4): every spec/work item in this wave Merged with clean destinations, all validators green, and the ten-role panel seal recorded", - "id": "ADR046-sterm-002", - "kind": "work-item", - "owner": "Controller binary area; owned by `d2b-provider-shell-terminal` controller and core Operation ledger integration.", - "parallelGroup": "wi:ADR-046-provider-shell-terminal", - "prerequisites": [ - "ADR-046-provider-shell-terminal" + "exitGate" : "ADR046-W6 exit criteria (ADR-046-validation-and-delivery §4): every spec/work item in this wave Merged with clean destinations, all validators green, and the ten-role panel seal recorded", + "id" : "ADR046-security-key-003", + "kind" : "work-item", + "owner" : "ADR-046 provider-device-security-key frontend extraction owner; depends on ADR046-security-key-008 and frozen Process/ComponentSession contracts.", + "parallelGroup" : "wi:ADR-046-provider-device-security-key", + "prerequisites" : [ + "ADR-046-provider-device-security-key", + "ADR046-security-key-008" ], - "specId": "ADR-046-provider-shell-terminal", - "topologicalRank": 11, - "wave": "W6" + "specId" : "ADR-046-provider-device-security-key", + "topologicalRank" : 12, + "wave" : "W6" }, { - "blockers": [], - "destinations": [ - "`packages/d2b-provider-shell-terminal/src/bin/d2b-shell-session-supervisor.rs`" + "blockers" : [], + "destinations" : [ + "Preserve revalidation logic; update `SecurityKeyOpenDevice` to use bundle device table `device_token` as sole open target (no iterative sysfs scan); add zone-field handling; remove sysfs fallback. **Core's LaunchTicket calls this internally; the Provider does not call it.**" ], - "entryContracts": [ - "ADR-046-provider-shell-terminal" + "entryContracts" : [ + "ADR-046-provider-device-security-key" ], - "exitGate": "ADR046-W6 exit criteria (ADR-046-validation-and-delivery §4): every spec/work item in this wave Merged with clean destinations, all validators green, and the ten-role panel seal recorded", - "id": "ADR046-sterm-003", - "kind": "work-item", - "owner": "Supervisor binary area; owned by `d2b-provider-shell-terminal` session supervisor runtime.", - "parallelGroup": "wi:ADR-046-provider-shell-terminal", - "prerequisites": [ - "ADR-046-provider-shell-terminal" + "exitGate" : "ADR046-W6 exit criteria (ADR-046-validation-and-delivery §4): every spec/work item in this wave Merged with clean destinations, all validators green, and the ten-role panel seal recorded", + "id" : "ADR046-security-key-004", + "kind" : "work-item", + "owner" : "Core LaunchTicket/privileged broker reuse owner; depends on ADR-046-resources-device and ADR046-security-key-013 probe/device-token population.", + "parallelGroup" : "wi:ADR-046-provider-device-security-key", + "prerequisites" : [ + "ADR-046-provider-device-security-key", + "ADR046-security-key-013" ], - "specId": "ADR-046-provider-shell-terminal", - "topologicalRank": 11, - "wave": "W6" + "specId" : "ADR-046-provider-device-security-key", + "topologicalRank" : 14, + "wave" : "W6" }, { - "blockers": [], - "destinations": [ - "`packages/d2b-provider-shell-terminal/src/process_templates.rs`" + "blockers" : [], + "destinations" : [ + "Adapt to v3 Zone/ResourceRef identifiers; preserve serde shapes for zero downstream breakage where possible; remove `SecurityKeyApplyUdevRulesRequest` (ADR046-security-key-035)" ], - "entryContracts": [ - "ADR-046-provider-shell-terminal" + "entryContracts" : [ + "ADR-046-provider-device-security-key" ], - "exitGate": "ADR046-W6 exit criteria (ADR-046-validation-and-delivery §4): every spec/work item in this wave Merged with clean destinations, all validators green, and the ten-role panel seal recorded", - "id": "ADR046-sterm-004", - "kind": "work-item", - "owner": "Process templates area; owned by Nix compiler plus shell-terminal controller.", - "parallelGroup": "wi:ADR-046-provider-shell-terminal", - "prerequisites": [ - "ADR-046-provider-shell-terminal" + "exitGate" : "ADR046-W6 exit criteria (ADR-046-validation-and-delivery §4): every spec/work item in this wave Merged with clean destinations, all validators green, and the ten-role panel seal recorded", + "id" : "ADR046-security-key-005", + "kind" : "work-item", + "owner" : "`d2b-contracts` security-key ceremony/effect DTO owner; depends on ADR046-provider-004, ADR-046-resource-object-model, ADR-046-resources-device, and ADR046-security-key-008.", + "parallelGroup" : "wi:ADR-046-provider-device-security-key", + "prerequisites" : [ + "ADR-046-provider-device-security-key", + "ADR046-provider-004", + "ADR046-security-key-008" ], - "specId": "ADR-046-provider-shell-terminal", - "topologicalRank": 11, - "wave": "W6" + "specId" : "ADR-046-provider-device-security-key", + "topologicalRank" : 12, + "wave" : "W6" }, { - "blockers": [], - "destinations": [ - "`packages/d2b-provider-shell-terminal/src/service/open_session.rs`" + "blockers" : [], + "destinations" : [ + "Move to `packages/d2b-provider-device-security-key/tests/`; update imports and v3 type names" ], - "entryContracts": [ - "ADR-046-provider-shell-terminal" + "entryContracts" : [ + "ADR-046-provider-device-security-key" ], - "exitGate": "ADR046-W6 exit criteria (ADR-046-validation-and-delivery §4): every spec/work item in this wave Merged with clean destinations, all validators green, and the ten-role panel seal recorded", - "id": "ADR046-sterm-005", - "kind": "work-item", - "owner": "OpenSession lifecycle area; owned by controller service implementation.", - "parallelGroup": "wi:ADR-046-provider-shell-terminal", - "prerequisites": [ - "ADR-046-provider-shell-terminal" + "exitGate" : "ADR046-W6 exit criteria (ADR-046-validation-and-delivery §4): every spec/work item in this wave Merged with clean destinations, all validators green, and the ten-role panel seal recorded", + "id" : "ADR046-security-key-006", + "kind" : "work-item", + "owner" : "Provider crate test owner; depends on ADR046-security-key-005 v3 DTOs and ADR046-security-key-008 provider crate layout.", + "parallelGroup" : "wi:ADR-046-provider-device-security-key", + "prerequisites" : [ + "ADR-046-provider-device-security-key", + "ADR046-security-key-005", + "ADR046-security-key-008" ], - "specId": "ADR-046-provider-shell-terminal", - "topologicalRank": 11, - "wave": "W6" + "specId" : "ADR-046-provider-device-security-key", + "topologicalRank" : 13, + "wave" : "W6" }, { - "blockers": [], - "destinations": [ - "`packages/d2b-provider-shell-terminal/src/session/{pty,ring}.rs`" + "blockers" : [], + "destinations" : [ + "Move to `packages/d2b-provider-device-security-key/tests/`; update for v3 Process resource minijail profile; retain zero-capabilities assertion" ], - "entryContracts": [ - "ADR-046-provider-shell-terminal" + "entryContracts" : [ + "ADR-046-provider-device-security-key" ], - "exitGate": "ADR046-W6 exit criteria (ADR-046-validation-and-delivery §4): every spec/work item in this wave Merged with clean destinations, all validators green, and the ten-role panel seal recorded", - "id": "ADR046-sterm-006", - "kind": "work-item", - "owner": "PTY and ring area; owned by per-session supervisor runtime.", - "parallelGroup": "wi:ADR-046-provider-shell-terminal", - "prerequisites": [ - "ADR-046-provider-shell-terminal" + "exitGate" : "ADR046-W6 exit criteria (ADR-046-validation-and-delivery §4): every spec/work item in this wave Merged with clean destinations, all validators green, and the ten-role panel seal recorded", + "id" : "ADR046-security-key-007", + "kind" : "work-item", + "owner" : "Provider crate test/minijail adaptation owner; depends on ADR046-security-key-008 and the frozen Process sandbox contract.", + "parallelGroup" : "wi:ADR-046-provider-device-security-key", + "prerequisites" : [ + "ADR-046-provider-device-security-key", + "ADR046-security-key-008" ], - "specId": "ADR-046-provider-shell-terminal", - "topologicalRank": 11, - "wave": "W6" + "specId" : "ADR-046-provider-device-security-key", + "topologicalRank" : 12, + "wave" : "W6" }, { - "blockers": [], - "destinations": [ - "`packages/d2b-provider-shell-terminal/src/session/adopt.rs`" + "blockers" : [], + "destinations" : [ + "New crate `packages/d2b-provider-device-security-key/` with `src/`, `tests/`, `integration/`, `README.md` (workspace policy requires all four)" ], - "entryContracts": [ - "ADR-046-provider-shell-terminal" + "entryContracts" : [ + "ADR-046-provider-device-security-key" ], - "exitGate": "ADR046-W6 exit criteria (ADR-046-validation-and-delivery §4): every spec/work item in this wave Merged with clean destinations, all validators green, and the ten-role panel seal recorded", - "id": "ADR046-sterm-007", - "kind": "work-item", - "owner": "Adoption and routing area; owned by controller session adoption/routing module.", - "parallelGroup": "wi:ADR-046-provider-shell-terminal", - "prerequisites": [ - "ADR-046-provider-shell-terminal" + "exitGate" : "ADR046-W6 exit criteria (ADR-046-validation-and-delivery §4): every spec/work item in this wave Merged with clean destinations, all validators green, and the ten-role panel seal recorded", + "id" : "ADR046-security-key-008", + "kind" : "work-item", + "owner" : "ADR-046 provider-device-security-key crate owner; depends on provider-model/package workspace policy.", + "parallelGroup" : "wi:ADR-046-provider-device-security-key", + "prerequisites" : [ + "ADR-046-provider-device-security-key" ], - "specId": "ADR-046-provider-shell-terminal", - "topologicalRank": 11, - "wave": "W6" + "specId" : "ADR-046-provider-device-security-key", + "topologicalRank" : 11, + "wave" : "W6" }, { - "blockers": [], - "destinations": [ - "`packages/d2b-provider-shell-terminal/src/host_rules.rs`" + "blockers" : [], + "destinations" : [ + "`packages/d2b-provider-device-security-key/src/controller.rs`" ], - "entryContracts": [ - "ADR-046-provider-shell-terminal" + "entryContracts" : [ + "ADR-046-provider-device-security-key" ], - "exitGate": "ADR046-W6 exit criteria (ADR-046-validation-and-delivery §4): every spec/work item in this wave Merged with clean destinations, all validators green, and the ten-role panel seal recorded", - "id": "ADR046-sterm-008", - "kind": "work-item", - "owner": "Host rules area; owned by shell-terminal Host policy module.", - "parallelGroup": "wi:ADR-046-provider-shell-terminal", - "prerequisites": [ - "ADR-046-provider-shell-terminal" + "exitGate" : "ADR046-W6 exit criteria (ADR-046-validation-and-delivery §4): every spec/work item in this wave Merged with clean destinations, all validators green, and the ten-role panel seal recorded", + "id" : "ADR046-security-key-009", + "kind" : "work-item", + "owner" : "Provider controller owner; depends on ADR046-security-key-013 probe, ADR046-security-key-016 templates, ADR046-security-key-025 effect port, ADR046-security-key-026 Service/Binding contracts, ADR046-security-key-027 status contract, and ADR-046-resource-reconciliation.", + "parallelGroup" : "wi:ADR-046-provider-device-security-key", + "prerequisites" : [ + "ADR-046-provider-device-security-key", + "ADR046-security-key-013", + "ADR046-security-key-016", + "ADR046-security-key-025", + "ADR046-security-key-026", + "ADR046-security-key-027" ], - "specId": "ADR-046-provider-shell-terminal", - "topologicalRank": 11, - "wave": "W6" + "specId" : "ADR-046-provider-device-security-key", + "topologicalRank" : 15, + "wave" : "W6" }, { - "blockers": [], - "destinations": [ - "`packages/d2b-provider-shell-terminal/src/guest_rules.rs`" + "blockers" : [], + "destinations" : [ + "`packages/d2b-provider-device-security-key/src/relay.rs`" ], - "entryContracts": [ - "ADR-046-provider-shell-terminal" + "entryContracts" : [ + "ADR-046-provider-device-security-key" ], - "exitGate": "ADR046-W6 exit criteria (ADR-046-validation-and-delivery §4): every spec/work item in this wave Merged with clean destinations, all validators green, and the ten-role panel seal recorded", - "id": "ADR046-sterm-009", - "kind": "work-item", - "owner": "Guest rules area; owned by shell-terminal Guest policy module.", - "parallelGroup": "wi:ADR-046-provider-shell-terminal", - "prerequisites": [ - "ADR-046-provider-shell-terminal" + "exitGate" : "ADR046-W6 exit criteria (ADR-046-validation-and-delivery §4): every spec/work item in this wave Merged with clean destinations, all validators green, and the ten-role panel seal recorded", + "id" : "ADR046-security-key-010", + "kind" : "work-item", + "owner" : "Authority Service relay owner; depends on ADR046-security-key-001, ADR046-security-key-002, ADR046-security-key-011, ADR046-security-key-012, ADR046-security-key-014, ADR046-security-key-016, and ADR046-security-key-018.", + "parallelGroup" : "wi:ADR-046-provider-device-security-key", + "prerequisites" : [ + "ADR-046-provider-device-security-key", + "ADR046-security-key-001", + "ADR046-security-key-002", + "ADR046-security-key-011", + "ADR046-security-key-012", + "ADR046-security-key-014", + "ADR046-security-key-016", + "ADR046-security-key-018" ], - "specId": "ADR-046-provider-shell-terminal", - "topologicalRank": 11, - "wave": "W6" + "specId" : "ADR-046-provider-device-security-key", + "topologicalRank" : 16, + "wave" : "W6" }, { - "blockers": [], - "destinations": [ - "`packages/d2b-provider-shell-terminal/src/authz.rs`" + "blockers" : [], + "destinations" : [ + "`packages/d2b-provider-device-security-key/src/session.rs`" ], - "entryContracts": [ - "ADR-046-provider-shell-terminal" + "entryContracts" : [ + "ADR-046-provider-device-security-key" ], - "exitGate": "ADR046-W6 exit criteria (ADR-046-validation-and-delivery §4): every spec/work item in this wave Merged with clean destinations, all validators green, and the ten-role panel seal recorded", - "id": "ADR046-sterm-010", - "kind": "work-item", - "owner": "RBAC and relay denial area; owned by shell-terminal authorization module.", - "parallelGroup": "wi:ADR-046-provider-shell-terminal", - "prerequisites": [ - "ADR-046-provider-shell-terminal" + "exitGate" : "ADR046-W6 exit criteria (ADR-046-validation-and-delivery §4): every spec/work item in this wave Merged with clean destinations, all validators green, and the ten-role panel seal recorded", + "id" : "ADR046-security-key-011", + "kind" : "work-item", + "owner" : "Relay ceremony-session foundation owner; depends on ADR046-security-key-001 and ADR046-security-key-008.", + "parallelGroup" : "wi:ADR-046-provider-device-security-key", + "prerequisites" : [ + "ADR-046-provider-device-security-key", + "ADR046-security-key-001", + "ADR046-security-key-008" ], - "specId": "ADR-046-provider-shell-terminal", - "topologicalRank": 11, - "wave": "W6" + "specId" : "ADR-046-provider-device-security-key", + "topologicalRank" : 13, + "wave" : "W6" }, { - "blockers": [], - "destinations": [ - "`packages/d2b-provider-shell-terminal/src/{audit,telemetry}.rs`" + "blockers" : [], + "destinations" : [ + "`packages/d2b-provider-device-security-key/src/cid.rs`" ], - "entryContracts": [ - "ADR-046-provider-shell-terminal" + "entryContracts" : [ + "ADR-046-provider-device-security-key" ], - "exitGate": "ADR046-W6 exit criteria (ADR-046-validation-and-delivery §4): every spec/work item in this wave Merged with clean destinations, all validators green, and the ten-role panel seal recorded", - "id": "ADR046-sterm-011", - "kind": "work-item", - "owner": "Audit and telemetry area; owned by shell-terminal audit/telemetry modules.", - "parallelGroup": "wi:ADR-046-provider-shell-terminal", - "prerequisites": [ - "ADR-046-provider-shell-terminal" + "exitGate" : "ADR046-W6 exit criteria (ADR-046-validation-and-delivery §4): every spec/work item in this wave Merged with clean destinations, all validators green, and the ten-role panel seal recorded", + "id" : "ADR046-security-key-012", + "kind" : "work-item", + "owner" : "Relay CID-translation foundation owner; depends on ADR046-security-key-001 and ADR046-security-key-008.", + "parallelGroup" : "wi:ADR-046-provider-device-security-key", + "prerequisites" : [ + "ADR-046-provider-device-security-key", + "ADR046-security-key-001", + "ADR046-security-key-008" ], - "specId": "ADR-046-provider-shell-terminal", - "topologicalRank": 11, - "wave": "W6" + "specId" : "ADR-046-provider-device-security-key", + "topologicalRank" : 13, + "wave" : "W6" }, { - "blockers": [], - "destinations": [ - "`packages/d2b-provider-shell-terminal/src/migration.rs`" + "blockers" : [], + "destinations" : [ + "`packages/d2b-provider-device-security-key/src/probe.rs`; Provider activation/Core private bundle device table population for label → `device_token`" ], - "entryContracts": [ - "ADR-046-provider-shell-terminal" + "entryContracts" : [ + "ADR-046-provider-device-security-key" ], - "exitGate": "ADR046-W6 exit criteria (ADR-046-validation-and-delivery §4): every spec/work item in this wave Merged with clean destinations, all validators green, and the ten-role panel seal recorded", - "id": "ADR046-sterm-012", - "kind": "work-item", - "owner": "Baseline removal area; owned by migration/removal implementation for shell-terminal.", - "parallelGroup": "wi:ADR-046-provider-shell-terminal", - "prerequisites": [ - "ADR-046-provider-shell-terminal" + "exitGate" : "ADR046-W6 exit criteria (ADR-046-validation-and-delivery §4): every spec/work item in this wave Merged with clean destinations, all validators green, and the ten-role panel seal recorded", + "id" : "ADR046-security-key-013", + "kind" : "work-item", + "owner" : "Probe/effect-port and activation owner; depends on ADR046-security-key-025 effect port and Core private bundle device table support.", + "parallelGroup" : "wi:ADR-046-provider-device-security-key", + "prerequisites" : [ + "ADR-046-provider-device-security-key", + "ADR046-security-key-025" ], - "specId": "ADR-046-provider-shell-terminal", - "topologicalRank": 11, - "wave": "W6" + "specId" : "ADR-046-provider-device-security-key", + "topologicalRank" : 13, + "wave" : "W6" }, { - "blockers": [], - "destinations": [ - "`packages/d2b-provider-shell-terminal/src/service/{controller,supervisor}.rs`" + "blockers" : [], + "destinations" : [ + "`packages/d2b-provider-device-security-key/src/descriptor.rs`" ], - "entryContracts": [ - "ADR-046-provider-shell-terminal" + "entryContracts" : [ + "ADR-046-provider-device-security-key" ], - "exitGate": "ADR046-W6 exit criteria (ADR-046-validation-and-delivery §4): every spec/work item in this wave Merged with clean destinations, all validators green, and the ten-role panel seal recorded", - "id": "ADR046-sterm-013", - "kind": "work-item", - "owner": "Supervisor service area; owned by shell-terminal controller/supervisor service modules and ComponentSession contracts.", - "parallelGroup": "wi:ADR-046-provider-shell-terminal", - "prerequisites": [ - "ADR-046-provider-shell-terminal" + "exitGate" : "ADR046-W6 exit criteria (ADR-046-validation-and-delivery §4): every spec/work item in this wave Merged with clean destinations, all validators green, and the ten-role panel seal recorded", + "id" : "ADR046-security-key-014", + "kind" : "work-item", + "owner" : "ComponentSession/security descriptor contract owner; depends on ADR046-security-key-008, ADR046-security-key-005, and ADR-046-componentsession-and-bus.", + "parallelGroup" : "wi:ADR-046-provider-device-security-key", + "prerequisites" : [ + "ADR-046-provider-device-security-key", + "ADR046-security-key-005", + "ADR046-security-key-008" ], - "specId": "ADR-046-provider-shell-terminal", - "topologicalRank": 11, - "wave": "W6" + "specId" : "ADR-046-provider-device-security-key", + "topologicalRank" : 13, + "wave" : "W6" }, { - "blockers": [], - "destinations": [ - "`packages/d2b-resource-store/src/lib.rs`, `packages/d2b-resource-store-redb/src/lib.rs`, `schema.rs`, `keys.rs`, `transaction.rs`" + "blockers" : [], + "destinations" : [ + "`nixos-modules/minijail-profiles.nix` entries for relay and controller; provider descriptor sandbox templates for relay/controller/frontend" ], - "entryContracts": [ - "ADR-046-resource-store-redb" + "entryContracts" : [ + "ADR-046-provider-device-security-key" ], - "exitGate": "ADR046-W0 exit criteria (ADR-046-validation-and-delivery §4): every spec/work item in this wave Merged with clean destinations, all validators green, and the ten-role panel seal recorded", - "id": "ADR046-store-001", - "kind": "work-item", - "owner": "W0; store integrator", - "parallelGroup": "wi:ADR-046-resource-store-redb", - "prerequisites": [ - "ADR-046-resource-store-redb" + "exitGate" : "ADR046-W6 exit criteria (ADR-046-validation-and-delivery §4): every spec/work item in this wave Merged with clean destinations, all validators green, and the ten-role panel seal recorded", + "id" : "ADR046-security-key-015", + "kind" : "work-item", + "owner" : "Sandbox/minijail foundation owner; depends on ADR046-security-key-008, ADR046-security-key-007, and ADR-046-components-processes-and-sandbox.", + "parallelGroup" : "wi:ADR-046-provider-device-security-key", + "prerequisites" : [ + "ADR-046-provider-device-security-key", + "ADR046-security-key-007", + "ADR046-security-key-008" ], - "specId": "ADR-046-resource-store-redb", - "topologicalRank": 4, - "wave": "W0" + "specId" : "ADR-046-provider-device-security-key", + "topologicalRank" : 13, + "wave" : "W6" }, { - "blockers": [], - "destinations": [ - "`packages/d2b-resource-store-redb/src/revision_log.rs`, `packages/d2b-resource-api/src/watch.rs`" + "blockers" : [], + "destinations" : [ + "Provider descriptor Process templates and owned CTAPHID `Endpoint` template for `Provider/device-security-key`" ], - "entryContracts": [ - "ADR-046-resource-store-redb" + "entryContracts" : [ + "ADR-046-provider-device-security-key" ], - "exitGate": "ADR046-W0 exit criteria (ADR-046-validation-and-delivery §4): every spec/work item in this wave Merged with clean destinations, all validators green, and the ten-role panel seal recorded", - "id": "ADR046-store-002", - "kind": "work-item", - "owner": "ADR046-store-001; watch/reconciliation integrator", - "parallelGroup": "wi:ADR-046-resource-store-redb", - "prerequisites": [ - "ADR-046-resource-store-redb", - "ADR046-store-001" + "exitGate" : "ADR046-W6 exit criteria (ADR-046-validation-and-delivery §4): every spec/work item in this wave Merged with clean destinations, all validators green, and the ten-role panel seal recorded", + "id" : "ADR046-security-key-016", + "kind" : "work-item", + "owner" : "Provider process/Endpoint-template owner; depends on ADR046-security-key-008, ADR046-security-key-015, and ADR046-security-key-026 Service/Binding ownership contracts.", + "parallelGroup" : "wi:ADR-046-provider-device-security-key", + "prerequisites" : [ + "ADR-046-provider-device-security-key", + "ADR046-security-key-008", + "ADR046-security-key-015", + "ADR046-security-key-026" ], - "specId": "ADR-046-resource-store-redb", - "topologicalRank": 5, - "wave": "W0" + "specId" : "ADR-046-provider-device-security-key", + "topologicalRank" : 14, + "wave" : "W6" }, { - "blockers": [], - "destinations": [ - "`packages/d2b-resource-store-redb/src/backup.rs`, `migration.rs`; generated v3 storage row" + "blockers" : [], + "destinations" : [ + "Signed Provider descriptor JSON for `Provider/device-security-key` in the provider package" ], - "entryContracts": [ - "ADR-046-resource-store-redb" + "entryContracts" : [ + "ADR-046-provider-device-security-key" ], - "exitGate": "ADR046-W0 exit criteria (ADR-046-validation-and-delivery §4): every spec/work item in this wave Merged with clean destinations, all validators green, and the ten-role panel seal recorded", - "id": "ADR046-store-003", - "kind": "work-item", - "owner": "ADR046-store-001; storage/broker integrator", - "parallelGroup": "wi:ADR-046-resource-store-redb", - "prerequisites": [ - "ADR-046-resource-store-redb", - "ADR046-store-001" + "exitGate" : "ADR046-W6 exit criteria (ADR-046-validation-and-delivery §4): every spec/work item in this wave Merged with clean destinations, all validators green, and the ten-role panel seal recorded", + "id" : "ADR046-security-key-017", + "kind" : "work-item", + "owner" : "Provider package descriptor owner; depends on ADR046-security-key-008, ADR046-security-key-005, ADR046-security-key-014, ADR046-security-key-016, ADR046-security-key-026, ADR046-security-key-027, and ADR-046-provider-model-and-packaging.", + "parallelGroup" : "wi:ADR-046-provider-device-security-key", + "prerequisites" : [ + "ADR-046-provider-device-security-key", + "ADR046-security-key-005", + "ADR046-security-key-008", + "ADR046-security-key-014", + "ADR046-security-key-016", + "ADR046-security-key-026", + "ADR046-security-key-027" ], - "specId": "ADR-046-resource-store-redb", - "topologicalRank": 5, - "wave": "W0" + "specId" : "ADR-046-provider-device-security-key", + "topologicalRank" : 15, + "wave" : "W6" }, { - "blockers": [], - "destinations": [ - "`docs/specs/ADR-046-spec-set.json`, `docs/specs/ADR-046-work-items.json`, `docs/specs/ADR-046-implementation-graph.json`, `docs/specs/ADR-046-implementation-graph.md` (generated, committed non-member artifacts; named in `docs/specs/README.md`)" + "blockers" : [], + "destinations" : [ + "v3 `SecurityKeyOpenDevice` broker op and Core LaunchTicket DeviceGrant resolution path" ], - "entryContracts": [ - "ADR-046-streamline" + "entryContracts" : [ + "ADR-046-provider-device-security-key" ], - "exitGate": "ADR046-W7 exit criteria (ADR-046-validation-and-delivery §4): every spec/work item in this wave Merged with clean destinations, all validators green, and the ten-role panel seal recorded", - "id": "ADR046-streamline-001", - "kind": "work-item", - "owner": "`packages/xtask` owner; no dependency on any other streamline item", - "parallelGroup": "wi:ADR-046-streamline", - "prerequisites": [ - "ADR-046-streamline" + "exitGate" : "ADR046-W6 exit criteria (ADR-046-validation-and-delivery §4): every spec/work item in this wave Merged with clean destinations, all validators green, and the ten-role panel seal recorded", + "id" : "ADR046-security-key-018", + "kind" : "work-item", + "owner" : "Core LaunchTicket/broker owner; depends on ADR046-security-key-004, ADR046-security-key-005, ADR046-security-key-013, and ADR-046-resources-device.", + "parallelGroup" : "wi:ADR-046-provider-device-security-key", + "prerequisites" : [ + "ADR-046-provider-device-security-key", + "ADR046-security-key-004", + "ADR046-security-key-005", + "ADR046-security-key-013" ], - "specId": "ADR-046-streamline", - "topologicalRank": 12, - "wave": "W7" + "specId" : "ADR-046-provider-device-security-key", + "topologicalRank" : 15, + "wave" : "W6" }, { - "blockers": [], - "destinations": [ - "`docs/specs/schemas/*.schema.json` (Tier A: hand-authored-once canonical source checked into the tree, matching the frozen D010/D032/D075/D076/D080 decisions exactly); `packages/d2b-core/src/resource_schema/*.rs` (Tier B: the eventual Rust source of truth once ResourceType implementation exists, at which point the Tier A JSON becomes generated from Rust instead of hand-authored)" + "blockers" : [], + "destinations" : [ + "`nixos-modules/` resource compiler/eval assertions for physical Device, authority Service, ResourceExport/Import, and consumer Binding" ], - "entryContracts": [ - "ADR-046-streamline" + "entryContracts" : [ + "ADR-046-provider-device-security-key" ], - "exitGate": "ADR046-W7 exit criteria (ADR-046-validation-and-delivery §4): every spec/work item in this wave Merged with clean destinations, all validators green, and the ten-role panel seal recorded", - "id": "ADR046-streamline-002", - "kind": "work-item", - "owner": "ADR046-streamline-001; `d2b-core` owner (Tier B only)", - "parallelGroup": "wi:ADR-046-streamline", - "prerequisites": [ - "ADR-046-streamline", - "ADR046-streamline-001" + "exitGate" : "ADR046-W6 exit criteria (ADR-046-validation-and-delivery §4): every spec/work item in this wave Merged with clean destinations, all validators green, and the ten-role panel seal recorded", + "id" : "ADR046-security-key-019", + "kind" : "work-item", + "owner" : "Nix resource compiler owner; depends on ADR046-security-key-017, ADR046-security-key-026, ADR046-zone-control-024, and ADR-046-nix-configuration.", + "parallelGroup" : "wi:ADR-046-provider-device-security-key", + "prerequisites" : [ + "ADR-046-provider-device-security-key", + "ADR046-security-key-017", + "ADR046-security-key-026", + "ADR046-zone-control-024" ], - "specId": "ADR-046-streamline", - "topologicalRank": 13, - "wave": "W7" + "specId" : "ADR-046-provider-device-security-key", + "topologicalRank" : 16, + "wave" : "W6" }, { - "blockers": [], - "destinations": [ - "`packages/xtask/src/bin/spec_schema_check.rs`; wired as a Layer-1 policy lint at `packages/d2b-contract-tests/tests/policy_spec_schema.rs`" + "blockers" : [], + "destinations" : [ + "`nixos-modules/components/security-key-guest.nix` migration gate `d2b.securityKey._legacySystemdUnit`" ], - "entryContracts": [ - "ADR-046-streamline" + "entryContracts" : [ + "ADR-046-provider-device-security-key" ], - "exitGate": "ADR046-W7 exit criteria (ADR-046-validation-and-delivery §4): every spec/work item in this wave Merged with clean destinations, all validators green, and the ten-role panel seal recorded", - "id": "ADR046-streamline-003", - "kind": "work-item", - "owner": "ADR046-streamline-001, ADR046-streamline-002", - "parallelGroup": "wi:ADR-046-streamline", - "prerequisites": [ - "ADR-046-streamline", - "ADR046-streamline-001", - "ADR046-streamline-002" + "exitGate" : "ADR046-W6 exit criteria (ADR-046-validation-and-delivery §4): every spec/work item in this wave Merged with clean destinations, all validators green, and the ten-role panel seal recorded", + "id" : "ADR046-security-key-020", + "kind" : "work-item", + "owner" : "Guest Nix migration owner; depends on ADR046-security-key-003, ADR046-security-key-016, and ADR046-security-key-026 Binding-owned frontend contract.", + "parallelGroup" : "wi:ADR-046-provider-device-security-key", + "prerequisites" : [ + "ADR-046-provider-device-security-key", + "ADR046-security-key-003", + "ADR046-security-key-016", + "ADR046-security-key-026" ], - "specId": "ADR-046-streamline", - "topologicalRank": 14, - "wave": "W7" + "specId" : "ADR-046-provider-device-security-key", + "topologicalRank" : 15, + "wave" : "W6" }, { - "blockers": [], - "destinations": [ - "`docs/specs/providers/TEMPLATE.md` (committed, non-normative reference); `packages/xtask/src/bin/new_provider_dossier.rs` (`cargo run -p xtask -- new-provider-dossier --name `)" + "blockers" : [], + "destinations" : [ + "Core `device-grant` audit and Provider controller Service/Binding ceremony lifecycle audit" ], - "entryContracts": [ - "ADR-046-streamline" + "entryContracts" : [ + "ADR-046-provider-device-security-key" ], - "exitGate": "ADR046-W7 exit criteria (ADR-046-validation-and-delivery §4): every spec/work item in this wave Merged with clean destinations, all validators green, and the ten-role panel seal recorded", - "id": "ADR046-streamline-004", - "kind": "work-item", - "owner": "ADR046-streamline-001, ADR046-streamline-002", - "parallelGroup": "wi:ADR-046-streamline", - "prerequisites": [ - "ADR-046-streamline", - "ADR046-streamline-001", - "ADR046-streamline-002" + "exitGate" : "ADR046-W6 exit criteria (ADR-046-validation-and-delivery §4): every spec/work item in this wave Merged with clean destinations, all validators green, and the ten-role panel seal recorded", + "id" : "ADR046-security-key-021", + "kind" : "work-item", + "owner" : "Audit owner for Core device-grant and Service/Binding lifecycle; depends on ADR046-security-key-009, ADR046-security-key-018, and ADR-046-telemetry-audit-and-support.", + "parallelGroup" : "wi:ADR-046-provider-device-security-key", + "prerequisites" : [ + "ADR-046-provider-device-security-key", + "ADR046-security-key-009", + "ADR046-security-key-018" ], - "specId": "ADR-046-streamline", - "topologicalRank": 14, - "wave": "W7" + "specId" : "ADR-046-provider-device-security-key", + "topologicalRank" : 16, + "wave" : "W6" }, { - "blockers": [], - "destinations": [ - "`packages/d2b-contract-tests/tests/policy_spec_vocabulary.rs`, `policy_spec_ownership.rs`, `policy_spec_finalizer_phase.rs`, `policy_spec_source_policy.rs`" + "blockers" : [], + "destinations" : [ + "Provider/controller bounded telemetry emitter and observability-otel handoff for security-key metrics" ], - "entryContracts": [ - "ADR-046-streamline" + "entryContracts" : [ + "ADR-046-provider-device-security-key" ], - "exitGate": "ADR046-W7 exit criteria (ADR-046-validation-and-delivery §4): every spec/work item in this wave Merged with clean destinations, all validators green, and the ten-role panel seal recorded", - "id": "ADR046-streamline-005", - "kind": "work-item", - "owner": "ADR046-streamline-001", - "parallelGroup": "wi:ADR-046-streamline", - "prerequisites": [ - "ADR-046-streamline", - "ADR046-streamline-001" + "exitGate" : "ADR046-W6 exit criteria (ADR-046-validation-and-delivery §4): every spec/work item in this wave Merged with clean destinations, all validators green, and the ten-role panel seal recorded", + "id" : "ADR046-security-key-022", + "kind" : "work-item", + "owner" : "Observability owner; depends on ADR046-security-key-010 relay, ADR046-security-key-009 controller, and ADR-046-telemetry-audit-and-support.", + "parallelGroup" : "wi:ADR-046-provider-device-security-key", + "prerequisites" : [ + "ADR-046-provider-device-security-key", + "ADR046-security-key-009", + "ADR046-security-key-010" ], - "specId": "ADR-046-streamline", - "topologicalRank": 13, - "wave": "W7" + "specId" : "ADR-046-provider-device-security-key", + "topologicalRank" : 17, + "wave" : "W6" }, { - "blockers": [], - "destinations": [ - "`packages/d2b-resource-store-redb/tests/provider_state_graph.rs` (or the eventual crate implementing Zone resource storage)" + "blockers" : [], + "destinations" : [ + "`packages/d2b-provider-device-security-key/README.md`" ], - "entryContracts": [ - "ADR-046-streamline" + "entryContracts" : [ + "ADR-046-provider-device-security-key" ], - "exitGate": "ADR046-W7 exit criteria (ADR-046-validation-and-delivery §4): every spec/work item in this wave Merged with clean destinations, all validators green, and the ten-role panel seal recorded", - "id": "ADR046-streamline-006", - "kind": "work-item", - "owner": "The Zone resource-store implementation work item (not yet filed; blocked on W0-W10 implementation request per D024); ADR046-streamline-001 for the doc-level invariant source", - "parallelGroup": "wi:ADR-046-streamline", - "prerequisites": [ - "ADR-046-streamline", - "ADR046-streamline-001" + "exitGate" : "ADR046-W6 exit criteria (ADR-046-validation-and-delivery §4): every spec/work item in this wave Merged with clean destinations, all validators green, and the ten-role panel seal recorded", + "id" : "ADR046-security-key-023", + "kind" : "work-item", + "owner" : "Provider documentation owner; depends on ADR046-security-key-008 through ADR046-security-key-022 and ADR046-security-key-024 through ADR046-security-key-029 for complete behavior.", + "parallelGroup" : "wi:ADR-046-provider-device-security-key", + "prerequisites" : [ + "ADR-046-provider-device-security-key", + "ADR046-security-key-008", + "ADR046-security-key-009", + "ADR046-security-key-010", + "ADR046-security-key-011", + "ADR046-security-key-012", + "ADR046-security-key-013", + "ADR046-security-key-014", + "ADR046-security-key-015", + "ADR046-security-key-016", + "ADR046-security-key-017", + "ADR046-security-key-018", + "ADR046-security-key-019", + "ADR046-security-key-020", + "ADR046-security-key-021", + "ADR046-security-key-022", + "ADR046-security-key-024", + "ADR046-security-key-025", + "ADR046-security-key-026", + "ADR046-security-key-027", + "ADR046-security-key-028", + "ADR046-security-key-029" + ], + "specId" : "ADR-046-provider-device-security-key", + "topologicalRank" : 19, + "wave" : "W6" + }, + { + "blockers" : [], + "destinations" : [ + "Authority/projection Service Endpoint and Binding private Endpoint resolution, including transport-vsock and ZoneLink encrypted streams" + ], + "entryContracts" : [ + "ADR-046-provider-device-security-key" ], - "specId": "ADR-046-streamline", - "topologicalRank": 13, - "wave": "W7" + "exitGate" : "ADR046-W6 exit criteria (ADR-046-validation-and-delivery §4): every spec/work item in this wave Merged with clean destinations, all validators green, and the ten-role panel seal recorded", + "id" : "ADR046-security-key-024", + "kind" : "work-item", + "owner" : "Endpoint/ComponentSession integration owner; depends on ADR046-security-key-003, ADR046-security-key-010, ADR046-security-key-014, ADR046-security-key-016, ADR046-security-key-026, and ADR-046-componentsession-and-bus.", + "parallelGroup" : "wi:ADR-046-provider-device-security-key", + "prerequisites" : [ + "ADR-046-provider-device-security-key", + "ADR046-security-key-003", + "ADR046-security-key-010", + "ADR046-security-key-014", + "ADR046-security-key-016", + "ADR046-security-key-026" + ], + "specId" : "ADR-046-provider-device-security-key", + "topologicalRank" : 17, + "wave" : "W6" }, { - "blockers": [], - "destinations": [ - "`packages/d2b-contract-tests/tests/policy_effectport_boundary.rs`, `policy_worker_bus_boundary.rs`" + "blockers" : [], + "destinations" : [ + "`d2b-contracts` neutral `SecurityKeyEffectPort` trait/types; `packages/d2b-provider-device-security-key/src/effect_port.rs` re-export; Core adapter implementation in `d2b-provider` or `d2b-provider-toolkit`" ], - "entryContracts": [ - "ADR-046-streamline" + "entryContracts" : [ + "ADR-046-provider-device-security-key" ], - "exitGate": "ADR046-W7 exit criteria (ADR-046-validation-and-delivery §4): every spec/work item in this wave Merged with clean destinations, all validators green, and the ten-role panel seal recorded", - "id": "ADR046-streamline-007", - "kind": "work-item", - "owner": "The Provider-toolkit/EffectPort implementation work item (not yet filed; blocked on the W0-W10 implementation request); ADR046-streamline-001 for the doc-level invariant source", - "parallelGroup": "wi:ADR-046-streamline", - "prerequisites": [ - "ADR-046-streamline", - "ADR046-streamline-001" + "exitGate" : "ADR046-W6 exit criteria (ADR-046-validation-and-delivery §4): every spec/work item in this wave Merged with clean destinations, all validators green, and the ten-role panel seal recorded", + "id" : "ADR046-security-key-025", + "kind" : "work-item", + "owner" : "`d2b-contracts` neutral effect-port foundation owner; depends on ADR046-security-key-008 and ADR-046-resources-device.", + "parallelGroup" : "wi:ADR-046-provider-device-security-key", + "prerequisites" : [ + "ADR-046-provider-device-security-key", + "ADR046-security-key-008" ], - "specId": "ADR-046-streamline", - "topologicalRank": 13, - "wave": "W7" + "specId" : "ADR-046-provider-device-security-key", + "topologicalRank" : 12, + "wave" : "W6" }, { - "blockers": [], - "destinations": [ - "`packages/d2b-contract-tests/tests/policy_work_items.rs`" + "blockers" : [], + "destinations" : [ + "`packages/d2b-provider-device-security-key/src/{resource_type,provider_extension,admission}.rs`; controller contracts; system-core Guest UHID authority-subresource DeviceGrant (common base lives under ADR046-provider-004)" ], - "entryContracts": [ - "ADR-046-streamline" + "entryContracts" : [ + "ADR-046-provider-device-security-key" ], - "exitGate": "ADR046-W7 exit criteria (ADR-046-validation-and-delivery §4): every spec/work item in this wave Merged with clean destinations, all validators green, and the ten-role panel seal recorded", - "id": "ADR046-streamline-008", - "kind": "work-item", - "owner": "ADR046-streamline-001", - "parallelGroup": "wi:ADR-046-streamline", - "prerequisites": [ - "ADR-046-streamline", - "ADR046-streamline-001" + "exitGate" : "ADR046-W6 exit criteria (ADR-046-validation-and-delivery §4): every spec/work item in this wave Merged with clean destinations, all validators green, and the ten-role panel seal recorded", + "id" : "ADR046-security-key-026", + "kind" : "work-item", + "owner" : "Device-security-key Service/Binding implementation owner; depends on ADR046-provider-004, ADR046-security-key-008, ADR046-security-key-005, resource object/Device/D096/D097 contracts.", + "parallelGroup" : "wi:ADR-046-provider-device-security-key", + "prerequisites" : [ + "ADR-046-provider-device-security-key", + "ADR046-provider-004", + "ADR046-security-key-005", + "ADR046-security-key-008" + ], + "specId" : "ADR-046-provider-device-security-key", + "topologicalRank" : 13, + "wave" : "W6" + }, + { + "blockers" : [], + "destinations" : [ + "Provider descriptor state declaration, controller/status logic, Process templates, and Nix principal provisioning for `Provider/device-security-key`" + ], + "entryContracts" : [ + "ADR-046-provider-device-security-key" + ], + "exitGate" : "ADR046-W6 exit criteria (ADR-046-validation-and-delivery §4): every spec/work item in this wave Merged with clean destinations, all validators green, and the ten-role panel seal recorded", + "id" : "ADR046-security-key-027", + "kind" : "work-item", + "owner" : "Provider state/status contract owner; depends on ADR046-security-key-008, ADR046-security-key-026, and ADR-046-provider-state.", + "parallelGroup" : "wi:ADR-046-provider-device-security-key", + "prerequisites" : [ + "ADR-046-provider-device-security-key", + "ADR046-security-key-008", + "ADR046-security-key-026" ], - "specId": "ADR-046-streamline", - "topologicalRank": 13, - "wave": "W7" + "specId" : "ADR-046-provider-device-security-key", + "topologicalRank" : 14, + "wave" : "W6" }, { - "blockers": [], - "destinations": [ - "`docs/specs/ADR-046-provider-catalog.md` (generated, committed)" + "blockers" : [], + "destinations" : [ + "`packages/d2b-provider-device-security-key/src/share_adapter.rs`" ], - "entryContracts": [ - "ADR-046-streamline" + "entryContracts" : [ + "ADR-046-provider-device-security-key" ], - "exitGate": "ADR046-W7 exit criteria (ADR-046-validation-and-delivery §4): every spec/work item in this wave Merged with clean destinations, all validators green, and the ten-role panel seal recorded", - "id": "ADR046-streamline-009", - "kind": "work-item", - "owner": "ADR046-streamline-001", - "parallelGroup": "wi:ADR-046-streamline", - "prerequisites": [ - "ADR-046-streamline", - "ADR046-streamline-001" + "exitGate" : "ADR046-W6 exit criteria (ADR-046-validation-and-delivery §4): every spec/work item in this wave Merged with clean destinations, all validators green, and the ten-role panel seal recorded", + "id" : "ADR046-security-key-028", + "kind" : "work-item", + "owner" : "Cross-Zone adapter owner; depends on ADR046-security-key-024, ADR046-security-key-026, ADR046-security-key-029, ADR046-zone-control-019, and ADR046-zone-control-020.", + "parallelGroup" : "wi:ADR-046-provider-device-security-key", + "prerequisites" : [ + "ADR-046-provider-device-security-key", + "ADR046-security-key-024", + "ADR046-security-key-026", + "ADR046-security-key-029", + "ADR046-zone-control-019", + "ADR046-zone-control-020" ], - "specId": "ADR-046-streamline", - "topologicalRank": 13, - "wave": "W7" + "specId" : "ADR-046-provider-device-security-key", + "topologicalRank" : 18, + "wave" : "W6" }, { - "blockers": [], - "destinations": [ - "`tests/tools/reconcile-stale-base.sh` (reporting only) plus a documented `git town sync`/`git town` restack procedure this report feeds into, since this repository does not yet use Git Town and F1/F11 show plain rebase/cherry-pick was insufficient to prevent duplicate reconciliation" + "blockers" : [], + "destinations" : [ + "`packages/d2b-provider-device-security-key/src/{authority,relay,streams}.rs`; D097 `AuthorityDescriptor` on authority SecurityKeyService" ], - "entryContracts": [ - "ADR-046-streamline" + "entryContracts" : [ + "ADR-046-provider-device-security-key" ], - "exitGate": "ADR046-W7 exit criteria (ADR-046-validation-and-delivery §4): every spec/work item in this wave Merged with clean destinations, all validators green, and the ten-role panel seal recorded", - "id": "ADR046-streamline-010", - "kind": "work-item", - "owner": "ADR046-streamline-001", - "parallelGroup": "wi:ADR-046-streamline", - "prerequisites": [ - "ADR-046-streamline", - "ADR046-streamline-001" + "exitGate" : "ADR046-W6 exit criteria (ADR-046-validation-and-delivery §4): every spec/work item in this wave Merged with clean destinations, all validators green, and the ten-role panel seal recorded", + "id" : "ADR046-security-key-029", + "kind" : "work-item", + "owner" : "D097 authority foundation owner; depends on ADR046-security-key-001, ADR046-security-key-002, ADR046-security-key-003, ADR046-security-key-004, ADR046-security-key-018, ADR046-security-key-026, ADR046-zone-control-024, and the D097 authority contract.", + "parallelGroup" : "wi:ADR-046-provider-device-security-key", + "prerequisites" : [ + "ADR-046-provider-device-security-key", + "ADR046-security-key-001", + "ADR046-security-key-002", + "ADR046-security-key-003", + "ADR046-security-key-004", + "ADR046-security-key-018", + "ADR046-security-key-026", + "ADR046-zone-control-024" ], - "specId": "ADR-046-streamline", - "topologicalRank": 13, - "wave": "W7" + "specId" : "ADR-046-provider-device-security-key", + "topologicalRank" : 16, + "wave" : "W6" }, { - "blockers": [], - "destinations": [ - "`packages/xtask/src/bin/handoff_manifest.rs` (schema/validator only); manifest instances are per-round artifacts referenced from PR bodies, not committed to the tree (consistent with this repository's \"Screenshot and visual artifact hygiene\"-style external-evidence posture)" + "blockers" : [], + "destinations" : [ + "Removed from daemon; successor behavior lives in `packages/d2b-provider-device-security-key/src/relay.rs`, `session.rs`, and `cid.rs`" ], - "entryContracts": [ - "ADR-046-streamline" + "entryContracts" : [ + "ADR-046-provider-device-security-key" ], - "exitGate": "ADR046-W7 exit criteria (ADR-046-validation-and-delivery §4): every spec/work item in this wave Merged with clean destinations, all validators green, and the ten-role panel seal recorded", - "id": "ADR046-streamline-011", - "kind": "work-item", - "owner": "No prerequisite; `packages/xtask` schema/validator owner", - "parallelGroup": "wi:ADR-046-streamline", - "prerequisites": [ - "ADR-046-streamline" + "exitGate" : "ADR046-W6 exit criteria (ADR-046-validation-and-delivery §4): every spec/work item in this wave Merged with clean destinations, all validators green, and the ten-role panel seal recorded", + "id" : "ADR046-security-key-030", + "kind" : "work-item", + "owner" : "Provider-device-security-key removal owner; depends on ADR046-security-key-001, ADR046-security-key-002, ADR046-security-key-010, ADR046-security-key-011, and ADR046-security-key-012 successor relay/session coverage.", + "parallelGroup" : "wi:ADR-046-provider-device-security-key", + "prerequisites" : [ + "ADR-046-provider-device-security-key", + "ADR046-security-key-001", + "ADR046-security-key-002", + "ADR046-security-key-010", + "ADR046-security-key-011", + "ADR046-security-key-012" ], - "specId": "ADR-046-streamline", - "topologicalRank": 12, - "wave": "W7" + "specId" : "ADR-046-provider-device-security-key", + "topologicalRank" : 17, + "wave" : "W6" }, { - "blockers": [], - "destinations": [ - "`tests/tools/import-task-db-consistency.sh`" + "blockers" : [], + "destinations" : [ + "Removed from daemon startup; successor launch path is ProviderDeployment/controller-created relay Process plus Endpoint/ComponentSession transport" ], - "entryContracts": [ - "ADR-046-streamline" + "entryContracts" : [ + "ADR-046-provider-device-security-key" ], - "exitGate": "ADR046-W7 exit criteria (ADR-046-validation-and-delivery §4): every spec/work item in this wave Merged with clean destinations, all validators green, and the ten-role panel seal recorded", - "id": "ADR046-streamline-012", - "kind": "work-item", - "owner": "ADR046-streamline-011 (shares the manifest schema for commit-SHA binding)", - "parallelGroup": "wi:ADR-046-streamline", - "prerequisites": [ - "ADR-046-streamline", - "ADR046-streamline-011" + "exitGate" : "ADR046-W6 exit criteria (ADR-046-validation-and-delivery §4): every spec/work item in this wave Merged with clean destinations, all validators green, and the ten-role panel seal recorded", + "id" : "ADR046-security-key-031", + "kind" : "work-item", + "owner" : "d2bd integration removal owner; depends on ADR046-security-key-030.", + "parallelGroup" : "wi:ADR-046-provider-device-security-key", + "prerequisites" : [ + "ADR-046-provider-device-security-key", + "ADR046-security-key-030" ], - "specId": "ADR-046-streamline", - "topologicalRank": 13, - "wave": "W7" + "specId" : "ADR-046-provider-device-security-key", + "topologicalRank" : 18, + "wave" : "W6" }, { - "blockers": [], - "destinations": [ - "`tests/tools/anti-serialization-report.sh`" + "blockers" : [], + "destinations" : [ + "Removed from guest Nix module; successor is Binding-owned `Process/binding--sk-frontend`" ], - "entryContracts": [ - "ADR-046-streamline" + "entryContracts" : [ + "ADR-046-provider-device-security-key" ], - "exitGate": "ADR046-W7 exit criteria (ADR-046-validation-and-delivery §4): every spec/work item in this wave Merged with clean destinations, all validators green, and the ten-role panel seal recorded", - "id": "ADR046-streamline-013", - "kind": "work-item", - "owner": "ADR046-streamline-001, ADR046-streamline-010", - "parallelGroup": "wi:ADR-046-streamline", - "prerequisites": [ - "ADR-046-streamline", - "ADR046-streamline-001", - "ADR046-streamline-010" + "exitGate" : "ADR046-W6 exit criteria (ADR-046-validation-and-delivery §4): every spec/work item in this wave Merged with clean destinations, all validators green, and the ten-role panel seal recorded", + "id" : "ADR046-security-key-032", + "kind" : "work-item", + "owner" : "Guest Nix module removal owner; depends on ADR046-security-key-020, ADR046-security-key-003, and ADR046-security-key-026 Binding frontend/UHID contract.", + "parallelGroup" : "wi:ADR-046-provider-device-security-key", + "prerequisites" : [ + "ADR-046-provider-device-security-key", + "ADR046-security-key-003", + "ADR046-security-key-020", + "ADR046-security-key-026" ], - "specId": "ADR-046-streamline", - "topologicalRank": 14, - "wave": "W7" + "specId" : "ADR-046-provider-device-security-key", + "topologicalRank" : 16, + "wave" : "W6" }, { - "blockers": [], - "destinations": [ - "`tests/tools/run-layer.sh` extension (this repository already has `tests/tools/run-layer.sh` and `layer1-jobs.py` bounded-parallelism precedent) plus fake `EffectPort`/`ResourceClient` stub crates under `packages/d2b-provider-toolkit-fakes/`" + "blockers" : [], + "destinations" : [ + "Removed from `packages/d2b-contract-tests/tests/`; successor tests live in `packages/d2b-provider-device-security-key/tests/`" ], - "entryContracts": [ - "ADR-046-streamline" + "entryContracts" : [ + "ADR-046-provider-device-security-key" ], - "exitGate": "ADR046-W7 exit criteria (ADR-046-validation-and-delivery §4): every spec/work item in this wave Merged with clean destinations, all validators green, and the ten-role panel seal recorded", - "id": "ADR046-streamline-014", - "kind": "work-item", - "owner": "The Provider-toolkit implementation work item (blocked on the W0-W10 implementation request)", - "parallelGroup": "wi:ADR-046-streamline", - "prerequisites": [ - "ADR-046-streamline" + "exitGate" : "ADR046-W6 exit criteria (ADR-046-validation-and-delivery §4): every spec/work item in this wave Merged with clean destinations, all validators green, and the ten-role panel seal recorded", + "id" : "ADR046-security-key-033", + "kind" : "work-item", + "owner" : "Test-suite migration/removal owner; depends on ADR046-security-key-006 and ADR046-security-key-007 provider-crate successor tests.", + "parallelGroup" : "wi:ADR-046-provider-device-security-key", + "prerequisites" : [ + "ADR-046-provider-device-security-key", + "ADR046-security-key-006", + "ADR046-security-key-007" ], - "specId": "ADR-046-streamline", - "topologicalRank": 12, - "wave": "W7" + "specId" : "ADR-046-provider-device-security-key", + "topologicalRank" : 14, + "wave" : "W6" }, { - "blockers": [], - "destinations": [ - "Shared `packages/xtask` regeneration-conflict-detection helper consumed by every `gen-*`/`spec-registry` subcommand" + "blockers" : [], + "destinations" : [ + "Removed from `d2b-core/src/processes.rs`; successor frontend is a v3 Process resource owned by `Provider/device-security-key`" ], - "entryContracts": [ - "ADR-046-streamline" + "entryContracts" : [ + "ADR-046-provider-device-security-key" ], - "exitGate": "ADR046-W7 exit criteria (ADR-046-validation-and-delivery §4): every spec/work item in this wave Merged with clean destinations, all validators green, and the ten-role panel seal recorded", - "id": "ADR046-streamline-015", - "kind": "work-item", - "owner": "ADR046-streamline-001, ADR046-streamline-009, ADR046-streamline-002 (Tier A part)", - "parallelGroup": "wi:ADR-046-streamline", - "prerequisites": [ - "ADR-046-streamline", - "ADR046-streamline-001", - "ADR046-streamline-002", - "ADR046-streamline-009" + "exitGate" : "ADR046-W6 exit criteria (ADR-046-validation-and-delivery §4): every spec/work item in this wave Merged with clean destinations, all validators green, and the ten-role panel seal recorded", + "id" : "ADR046-security-key-034", + "kind" : "work-item", + "owner" : "Core ProcessRole removal owner; depends on ADR046-security-key-016 Process resources, ADR046-security-key-015 sandbox templates, and system-minijail/system-systemd conformance.", + "parallelGroup" : "wi:ADR-046-provider-device-security-key", + "prerequisites" : [ + "ADR-046-provider-device-security-key", + "ADR046-security-key-015", + "ADR046-security-key-016" ], - "specId": "ADR-046-streamline", - "topologicalRank": 14, - "wave": "W7" + "specId" : "ADR-046-provider-device-security-key", + "topologicalRank" : 15, + "wave" : "W6" }, { - "blockers": [], - "destinations": [ - "`packages/d2b-contract-tests/tests/policy_no_leaked_decision_prefix.rs`; invoked as a required precondition script `tests/tools/pre-panel-gate.sh`" + "blockers" : [], + "destinations" : [ + "Removed from contracts and broker; successor access is static guest Nix `uhid` module plus Core pre-opened `/dev/uhid` DeviceGrant for the frontend Process" ], - "entryContracts": [ - "ADR-046-streamline" + "entryContracts" : [ + "ADR-046-provider-device-security-key" ], - "exitGate": "ADR046-W7 exit criteria (ADR-046-validation-and-delivery §4): every spec/work item in this wave Merged with clean destinations, all validators green, and the ten-role panel seal recorded", - "id": "ADR046-streamline-016", - "kind": "work-item", - "owner": "ADR046-streamline-001, ADR046-streamline-008", - "parallelGroup": "wi:ADR-046-streamline", - "prerequisites": [ - "ADR-046-streamline", - "ADR046-streamline-001", - "ADR046-streamline-008" + "exitGate" : "ADR046-W6 exit criteria (ADR-046-validation-and-delivery §4): every spec/work item in this wave Merged with clean destinations, all validators green, and the ten-role panel seal recorded", + "id" : "ADR046-security-key-035", + "kind" : "work-item", + "owner" : "Broker/contracts/Nix removal owner; depends on ADR046-security-key-005, ADR046-security-key-018, ADR046-security-key-020, and ADR046-security-key-026 Guest-substrate UHID replacement.", + "parallelGroup" : "wi:ADR-046-provider-device-security-key", + "prerequisites" : [ + "ADR-046-provider-device-security-key", + "ADR046-security-key-005", + "ADR046-security-key-018", + "ADR046-security-key-020", + "ADR046-security-key-026" ], - "specId": "ADR-046-streamline", - "topologicalRank": 14, - "wave": "W7" + "specId" : "ADR-046-provider-device-security-key", + "topologicalRank" : 16, + "wave" : "W6" }, { - "blockers": [], - "destinations": [ - "`docs/specs/ADR-046-streamline-evidence-commands.md` (a follow-up artifact outside this task's file scope; tracked here as a required future addition, not authored by this spec)" + "blockers" : [], + "destinations" : [ + "`packages/d2b-contracts/src/v3/component_session.rs`, `packages/d2b-session/`" ], - "entryContracts": [ - "ADR-046-streamline" + "entryContracts" : [ + "ADR-046-componentsession-and-bus" ], - "exitGate": "ADR046-W7 exit criteria (ADR-046-validation-and-delivery §4): every spec/work item in this wave Merged with clean destinations, all validators green, and the ten-role panel seal recorded", - "id": "ADR046-streamline-017", - "kind": "work-item", - "owner": "No prerequisite; `docs/specs` evidence-command documentation owner", - "parallelGroup": "wi:ADR-046-streamline", - "prerequisites": [ - "ADR-046-streamline" + "exitGate" : "ADR046-W1 exit criteria (ADR-046-validation-and-delivery §4): every spec/work item in this wave Merged with clean destinations, all validators green, and the ten-role panel seal recorded", + "id" : "ADR046-session-001", + "kind" : "work-item", + "owner" : "W0 shared contract root", + "parallelGroup" : "wi:ADR-046-componentsession-and-bus", + "prerequisites" : [ + "ADR-046-componentsession-and-bus" ], - "specId": "ADR-046-streamline", - "topologicalRank": 12, - "wave": "W7" + "specId" : "ADR-046-componentsession-and-bus", + "topologicalRank" : 6, + "wave" : "W1" }, { - "blockers": [], - "destinations": [ - "`tests/tools/worktree-disk-report.sh`" + "blockers" : [], + "destinations" : [ + "`packages/d2b-session-unix/`, future enrolled transport adapter crates" ], - "entryContracts": [ - "ADR-046-streamline" + "entryContracts" : [ + "ADR-046-componentsession-and-bus" ], - "exitGate": "ADR046-W7 exit criteria (ADR-046-validation-and-delivery §4): every spec/work item in this wave Merged with clean destinations, all validators green, and the ten-role panel seal recorded", - "id": "ADR046-streamline-018", - "kind": "work-item", - "owner": "ADR046-streamline-010 (shares branch-staleness detection)", - "parallelGroup": "wi:ADR-046-streamline", - "prerequisites": [ - "ADR-046-streamline", - "ADR046-streamline-010" + "exitGate" : "ADR046-W1 exit criteria (ADR-046-validation-and-delivery §4): every spec/work item in this wave Merged with clean destinations, all validators green, and the ten-role panel seal recorded", + "id" : "ADR046-session-002", + "kind" : "work-item", + "owner" : "ADR046-session-001; transport owner", + "parallelGroup" : "wi:ADR-046-componentsession-and-bus", + "prerequisites" : [ + "ADR-046-componentsession-and-bus", + "ADR046-session-001" ], - "specId": "ADR-046-streamline", - "topologicalRank": 14, - "wave": "W7" + "specId" : "ADR-046-componentsession-and-bus", + "topologicalRank" : 7, + "wave" : "W1" }, { - "blockers": [], - "destinations": [ - "`packages/xtask/src/bin/terminology_check.rs` (`cargo run -p xtask -- terminology-check`)" + "blockers" : [], + "destinations" : [ + "`packages/d2b-bus/src/{router,registry,authorization,streams,operations}.rs`" ], - "entryContracts": [ - "ADR-046-streamline" + "entryContracts" : [ + "ADR-046-componentsession-and-bus" ], - "exitGate": "ADR046-W7 exit criteria (ADR-046-validation-and-delivery §4): every spec/work item in this wave Merged with clean destinations, all validators green, and the ten-role panel seal recorded", - "id": "ADR046-streamline-019", - "kind": "work-item", - "owner": "ADR046-streamline-001", - "parallelGroup": "wi:ADR-046-streamline", - "prerequisites": [ - "ADR-046-streamline", - "ADR046-streamline-001" + "exitGate" : "ADR046-W1 exit criteria (ADR-046-validation-and-delivery §4): every spec/work item in this wave Merged with clean destinations, all validators green, and the ten-role panel seal recorded", + "id" : "ADR046-session-003", + "kind" : "work-item", + "owner" : "Sessions + resource API; bus owner", + "parallelGroup" : "wi:ADR-046-componentsession-and-bus", + "prerequisites" : [ + "ADR-046-componentsession-and-bus" ], - "specId": "ADR-046-streamline", - "topologicalRank": 13, - "wave": "W7" + "specId" : "ADR-046-componentsession-and-bus", + "topologicalRank" : 6, + "wave" : "W1" }, { - "blockers": [], - "destinations": [ - "`packages/d2b-contract-tests/tests/policy_test_placement.rs`" + "blockers" : [], + "destinations" : [ + "`packages/d2b-provider-shell-terminal/src/resources/{pool,session}.rs`" ], - "entryContracts": [ - "ADR-046-streamline" + "entryContracts" : [ + "ADR-046-provider-shell-terminal" ], - "exitGate": "ADR046-W7 exit criteria (ADR-046-validation-and-delivery §4): every spec/work item in this wave Merged with clean destinations, all validators green, and the ten-role panel seal recorded", - "id": "ADR046-streamline-020", - "kind": "work-item", - "owner": "ADR046-streamline-001", - "parallelGroup": "wi:ADR-046-streamline", - "prerequisites": [ - "ADR-046-streamline", - "ADR046-streamline-001" + "exitGate" : "ADR046-W6 exit criteria (ADR-046-validation-and-delivery §4): every spec/work item in this wave Merged with clean destinations, all validators green, and the ten-role panel seal recorded", + "id" : "ADR046-sterm-001", + "kind" : "work-item", + "owner" : "Resource schemas area; owned by `d2b-provider-shell-terminal` resource modules.", + "parallelGroup" : "wi:ADR-046-provider-shell-terminal", + "prerequisites" : [ + "ADR-046-provider-shell-terminal" ], - "specId": "ADR-046-streamline", - "topologicalRank": 13, - "wave": "W7" + "specId" : "ADR-046-provider-shell-terminal", + "topologicalRank" : 11, + "wave" : "W6" }, { - "blockers": [], - "destinations": [ - "`packages/d2b-contract-tests/tests/policy_test_determinism.rs`" + "blockers" : [], + "destinations" : [ + "`packages/d2b-provider-shell-terminal/src/bin/d2b-shell-terminal-controller.rs`" ], - "entryContracts": [ - "ADR-046-streamline" + "entryContracts" : [ + "ADR-046-provider-shell-terminal" ], - "exitGate": "ADR046-W7 exit criteria (ADR-046-validation-and-delivery §4): every spec/work item in this wave Merged with clean destinations, all validators green, and the ten-role panel seal recorded", - "id": "ADR046-streamline-021", - "kind": "work-item", - "owner": "ADR046-streamline-020", - "parallelGroup": "wi:ADR-046-streamline", - "prerequisites": [ - "ADR-046-streamline", - "ADR046-streamline-020" + "exitGate" : "ADR046-W6 exit criteria (ADR-046-validation-and-delivery §4): every spec/work item in this wave Merged with clean destinations, all validators green, and the ten-role panel seal recorded", + "id" : "ADR046-sterm-002", + "kind" : "work-item", + "owner" : "Controller binary area; owned by `d2b-provider-shell-terminal` controller and core Operation ledger integration.", + "parallelGroup" : "wi:ADR-046-provider-shell-terminal", + "prerequisites" : [ + "ADR-046-provider-shell-terminal" ], - "specId": "ADR-046-streamline", - "topologicalRank": 14, - "wave": "W7" + "specId" : "ADR-046-provider-shell-terminal", + "topologicalRank" : 11, + "wave" : "W6" }, { - "blockers": [], - "destinations": [ - "`packages/xtask/src/test_runtime_ledger.rs` (shared with `ADR046-delivery-007`)" + "blockers" : [], + "destinations" : [ + "`packages/d2b-provider-shell-terminal/src/bin/d2b-shell-session-supervisor.rs`" ], - "entryContracts": [ - "ADR-046-streamline" + "entryContracts" : [ + "ADR-046-provider-shell-terminal" ], - "exitGate": "ADR046-W7 exit criteria (ADR-046-validation-and-delivery §4): every spec/work item in this wave Merged with clean destinations, all validators green, and the ten-role panel seal recorded", - "id": "ADR046-streamline-022", - "kind": "work-item", - "owner": "ADR046-delivery-007", - "parallelGroup": "wi:ADR-046-streamline", - "prerequisites": [ - "ADR-046-streamline", - "ADR046-delivery-007" + "exitGate" : "ADR046-W6 exit criteria (ADR-046-validation-and-delivery §4): every spec/work item in this wave Merged with clean destinations, all validators green, and the ten-role panel seal recorded", + "id" : "ADR046-sterm-003", + "kind" : "work-item", + "owner" : "Supervisor binary area; owned by `d2b-provider-shell-terminal` session supervisor runtime.", + "parallelGroup" : "wi:ADR-046-provider-shell-terminal", + "prerequisites" : [ + "ADR-046-provider-shell-terminal" ], - "specId": "ADR-046-streamline", - "topologicalRank": 14, - "wave": "W7" + "specId" : "ADR-046-provider-shell-terminal", + "topologicalRank" : 11, + "wave" : "W6" }, { - "blockers": [], - "destinations": [ - "`packages/xtask/src/bin/legacy_test_retirement.rs` (`cargo run -p xtask -- legacy-test-retirement`)" + "blockers" : [], + "destinations" : [ + "`packages/d2b-provider-shell-terminal/src/process_templates.rs`" ], - "entryContracts": [ - "ADR-046-streamline" + "entryContracts" : [ + "ADR-046-provider-shell-terminal" ], - "exitGate": "ADR046-W7 exit criteria (ADR-046-validation-and-delivery §4): every spec/work item in this wave Merged with clean destinations, all validators green, and the ten-role panel seal recorded", - "id": "ADR046-streamline-023", - "kind": "work-item", - "owner": "ADR046-streamline-008, ADR046-streamline-022", - "parallelGroup": "wi:ADR-046-streamline", - "prerequisites": [ - "ADR-046-streamline", - "ADR046-streamline-008", - "ADR046-streamline-022" + "exitGate" : "ADR046-W6 exit criteria (ADR-046-validation-and-delivery §4): every spec/work item in this wave Merged with clean destinations, all validators green, and the ten-role panel seal recorded", + "id" : "ADR046-sterm-004", + "kind" : "work-item", + "owner" : "Process templates area; owned by Nix compiler plus shell-terminal controller.", + "parallelGroup" : "wi:ADR-046-provider-shell-terminal", + "prerequisites" : [ + "ADR-046-provider-shell-terminal" ], - "specId": "ADR-046-streamline", - "topologicalRank": 15, - "wave": "W7" + "specId" : "ADR-046-provider-shell-terminal", + "topologicalRank" : 11, + "wave" : "W6" }, { - "blockers": [], - "destinations": [ - "`packages/xtask/src/bin/implementation_graph.rs` (`cargo run -p xtask -- implementation-graph`); folds the spec-set and test-runtime emitters into the one `xtask` dispatch" + "blockers" : [], + "destinations" : [ + "`packages/d2b-provider-shell-terminal/src/service/open_session.rs`" ], - "entryContracts": [ - "ADR-046-streamline" + "entryContracts" : [ + "ADR-046-provider-shell-terminal" ], - "exitGate": "ADR046-W7 exit criteria (ADR-046-validation-and-delivery §4): every spec/work item in this wave Merged with clean destinations, all validators green, and the ten-role panel seal recorded", - "id": "ADR046-streamline-024", - "kind": "work-item", - "owner": "ADR046-streamline-001", - "parallelGroup": "wi:ADR-046-streamline", - "prerequisites": [ - "ADR-046-streamline", - "ADR046-streamline-001" + "exitGate" : "ADR046-W6 exit criteria (ADR-046-validation-and-delivery §4): every spec/work item in this wave Merged with clean destinations, all validators green, and the ten-role panel seal recorded", + "id" : "ADR046-sterm-005", + "kind" : "work-item", + "owner" : "OpenSession lifecycle area; owned by controller service implementation.", + "parallelGroup" : "wi:ADR-046-provider-shell-terminal", + "prerequisites" : [ + "ADR-046-provider-shell-terminal" ], - "specId": "ADR-046-streamline", - "topologicalRank": 13, - "wave": "W7" + "specId" : "ADR-046-provider-shell-terminal", + "topologicalRank" : 11, + "wave" : "W6" }, { - "blockers": [], - "destinations": [ - "`packages/d2b-provider-system-systemd/src/controller.rs` (async reconcile loop), `src/launch.rs` (launch algorithm via effect port), `src/effect_port.rs` (`SystemdProcessEffectPort` trait + test double), `src/adoption.rs` (adoption algorithm), `src/sandbox.rs` (SandboxSpec → unit property compiler)" + "blockers" : [], + "destinations" : [ + "`packages/d2b-provider-shell-terminal/src/session/{pty,ring}.rs`" ], - "entryContracts": [ - "ADR-046-provider-system-systemd" + "entryContracts" : [ + "ADR-046-provider-shell-terminal" ], - "exitGate": "ADR046-W6 exit criteria (ADR-046-validation-and-delivery §4): every spec/work item in this wave Merged with clean destinations, all validators green, and the ten-role panel seal recorded", - "id": "ADR046-systemd-001", - "kind": "work-item", - "owner": "`ADR046-process-002`; Process contracts/supervisor owner; effect port interface owner", - "parallelGroup": "wi:ADR-046-provider-system-systemd", - "prerequisites": [ - "ADR-046-provider-system-systemd", - "ADR046-process-002" + "exitGate" : "ADR046-W6 exit criteria (ADR-046-validation-and-delivery §4): every spec/work item in this wave Merged with clean destinations, all validators green, and the ten-role panel seal recorded", + "id" : "ADR046-sterm-006", + "kind" : "work-item", + "owner" : "PTY and ring area; owned by per-session supervisor runtime.", + "parallelGroup" : "wi:ADR-046-provider-shell-terminal", + "prerequisites" : [ + "ADR-046-provider-shell-terminal" ], - "specId": "ADR-046-provider-system-systemd", - "topologicalRank": 11, - "wave": "W6" + "specId" : "ADR-046-provider-shell-terminal", + "topologicalRank" : 11, + "wave" : "W6" }, { - "blockers": [], - "destinations": [ - "`nixos-modules/` (Provider ResourceSpec emission for `system-systemd`); `packages/d2b-provider-system-systemd/` package derivation and catalog entry" + "blockers" : [], + "destinations" : [ + "`packages/d2b-provider-shell-terminal/src/session/adopt.rs`" ], - "entryContracts": [ - "ADR-046-provider-system-systemd" + "entryContracts" : [ + "ADR-046-provider-shell-terminal" ], - "exitGate": "ADR046-W6 exit criteria (ADR-046-validation-and-delivery §4): every spec/work item in this wave Merged with clean destinations, all validators green, and the ten-role panel seal recorded", - "id": "ADR046-systemd-002", - "kind": "work-item", - "owner": "`ADR046-systemd-001`; Nix/package integrator", - "parallelGroup": "wi:ADR-046-provider-system-systemd", - "prerequisites": [ - "ADR-046-provider-system-systemd", - "ADR046-systemd-001" + "exitGate" : "ADR046-W6 exit criteria (ADR-046-validation-and-delivery §4): every spec/work item in this wave Merged with clean destinations, all validators green, and the ten-role panel seal recorded", + "id" : "ADR046-sterm-007", + "kind" : "work-item", + "owner" : "Adoption and routing area; owned by controller session adoption/routing module.", + "parallelGroup" : "wi:ADR-046-provider-shell-terminal", + "prerequisites" : [ + "ADR-046-provider-shell-terminal" ], - "specId": "ADR-046-provider-system-systemd", - "topologicalRank": 12, - "wave": "W6" + "specId" : "ADR-046-provider-shell-terminal", + "topologicalRank" : 11, + "wave" : "W6" }, { - "blockers": [], - "destinations": [ - "`packages/d2b-provider-system-systemd/tests/conformance.rs`, `tests/fault.rs`, `tests/ephemeral.rs`, `tests/sandbox_compile.rs`; `integration/host_scenario.rs`, `integration/guest_scenario.rs`" + "blockers" : [], + "destinations" : [ + "`packages/d2b-provider-shell-terminal/src/host_rules.rs`" ], - "entryContracts": [ - "ADR-046-provider-system-systemd" + "entryContracts" : [ + "ADR-046-provider-shell-terminal" ], - "exitGate": "ADR046-W6 exit criteria (ADR-046-validation-and-delivery §4): every spec/work item in this wave Merged with clean destinations, all validators green, and the ten-role panel seal recorded", - "id": "ADR046-systemd-003", - "kind": "work-item", - "owner": "`ADR046-systemd-001`; conformance kit / test infrastructure", - "parallelGroup": "wi:ADR-046-provider-system-systemd", - "prerequisites": [ - "ADR-046-provider-system-systemd", - "ADR046-systemd-001" + "exitGate" : "ADR046-W6 exit criteria (ADR-046-validation-and-delivery §4): every spec/work item in this wave Merged with clean destinations, all validators green, and the ten-role panel seal recorded", + "id" : "ADR046-sterm-008", + "kind" : "work-item", + "owner" : "Host rules area; owned by shell-terminal Host policy module.", + "parallelGroup" : "wi:ADR-046-provider-shell-terminal", + "prerequisites" : [ + "ADR-046-provider-shell-terminal" ], - "specId": "ADR-046-provider-system-systemd", - "topologicalRank": 12, - "wave": "W6" + "specId" : "ADR-046-provider-shell-terminal", + "topologicalRank" : 11, + "wave" : "W6" }, { - "blockers": [], - "destinations": [ - "`packages/d2b-telemetry/src/{trace_context.rs,audit_hash.rs,emitter.rs,meter_registry.rs,redaction_guard.rs}`" + "blockers" : [], + "destinations" : [ + "`packages/d2b-provider-shell-terminal/src/guest_rules.rs`" ], - "entryContracts": [ - "ADR-046-telemetry-audit-and-support" + "entryContracts" : [ + "ADR-046-provider-shell-terminal" ], - "exitGate": "ADR046-W5 exit criteria (ADR-046-validation-and-delivery §4): every spec/work item in this wave Merged with clean destinations, all validators green, and the ten-role panel seal recorded", - "id": "ADR046-telem-001", - "kind": "work-item", - "owner": "W0/W1a; telemetry crate owner", - "parallelGroup": "wi:ADR-046-telemetry-audit-and-support", - "prerequisites": [ - "ADR-046-telemetry-audit-and-support" + "exitGate" : "ADR046-W6 exit criteria (ADR-046-validation-and-delivery §4): every spec/work item in this wave Merged with clean destinations, all validators green, and the ten-role panel seal recorded", + "id" : "ADR046-sterm-009", + "kind" : "work-item", + "owner" : "Guest rules area; owned by shell-terminal Guest policy module.", + "parallelGroup" : "wi:ADR-046-provider-shell-terminal", + "prerequisites" : [ + "ADR-046-provider-shell-terminal" ], - "specId": "ADR-046-telemetry-audit-and-support", - "topologicalRank": 10, - "wave": "W5" + "specId" : "ADR-046-provider-shell-terminal", + "topologicalRank" : 11, + "wave" : "W6" }, { - "blockers": [], - "destinations": [ - "`packages/d2b-resource-store-redb/src/metrics.rs`, `packages/d2b-resource-store-redb/src/tracing.rs`" + "blockers" : [], + "destinations" : [ + "`packages/d2b-provider-shell-terminal/src/authz.rs`" ], - "entryContracts": [ - "ADR-046-telemetry-audit-and-support" + "entryContracts" : [ + "ADR-046-provider-shell-terminal" ], - "exitGate": "ADR046-W5 exit criteria (ADR-046-validation-and-delivery §4): every spec/work item in this wave Merged with clean destinations, all validators green, and the ten-role panel seal recorded", - "id": "ADR046-telem-002", - "kind": "work-item", - "owner": "ADR046-telem-001 + ADR046-store-001; store owner", - "parallelGroup": "wi:ADR-046-telemetry-audit-and-support", - "prerequisites": [ - "ADR-046-telemetry-audit-and-support", - "ADR046-store-001", - "ADR046-telem-001" + "exitGate" : "ADR046-W6 exit criteria (ADR-046-validation-and-delivery §4): every spec/work item in this wave Merged with clean destinations, all validators green, and the ten-role panel seal recorded", + "id" : "ADR046-sterm-010", + "kind" : "work-item", + "owner" : "RBAC and relay denial area; owned by shell-terminal authorization module.", + "parallelGroup" : "wi:ADR-046-provider-shell-terminal", + "prerequisites" : [ + "ADR-046-provider-shell-terminal" ], - "specId": "ADR-046-telemetry-audit-and-support", - "topologicalRank": 11, - "wave": "W5" + "specId" : "ADR-046-provider-shell-terminal", + "topologicalRank" : 11, + "wave" : "W6" }, { - "blockers": [], - "destinations": [ - "`packages/d2b-resource-api/src/metrics.rs`, `packages/d2b-session/src/metrics.rs`, `packages/d2b-bus/src/metrics.rs`" + "blockers" : [], + "destinations" : [ + "`packages/d2b-provider-shell-terminal/src/{audit,telemetry}.rs`" ], - "entryContracts": [ - "ADR-046-telemetry-audit-and-support" + "entryContracts" : [ + "ADR-046-provider-shell-terminal" ], - "exitGate": "ADR046-W5 exit criteria (ADR-046-validation-and-delivery §4): every spec/work item in this wave Merged with clean destinations, all validators green, and the ten-role panel seal recorded", - "id": "ADR046-telem-003", - "kind": "work-item", - "owner": "ADR046-telem-001 + ADR046-session-001 + ADR046-bus-001; session/bus owner", - "parallelGroup": "wi:ADR-046-telemetry-audit-and-support", - "prerequisites": [ - "ADR-046-telemetry-audit-and-support", - "ADR046-bus-001", - "ADR046-session-001", - "ADR046-telem-001" + "exitGate" : "ADR046-W6 exit criteria (ADR-046-validation-and-delivery §4): every spec/work item in this wave Merged with clean destinations, all validators green, and the ten-role panel seal recorded", + "id" : "ADR046-sterm-011", + "kind" : "work-item", + "owner" : "Audit and telemetry area; owned by shell-terminal audit/telemetry modules.", + "parallelGroup" : "wi:ADR-046-provider-shell-terminal", + "prerequisites" : [ + "ADR-046-provider-shell-terminal" ], - "specId": "ADR-046-telemetry-audit-and-support", - "topologicalRank": 11, - "wave": "W5" + "specId" : "ADR-046-provider-shell-terminal", + "topologicalRank" : 11, + "wave" : "W6" }, { - "blockers": [], - "destinations": [ - "`packages/d2b-core-controller/src/metrics.rs`, `packages/d2b-core-controller/src/tracing.rs`" + "blockers" : [], + "destinations" : [ + "`packages/d2b-provider-shell-terminal/src/migration.rs`" ], - "entryContracts": [ - "ADR-046-telemetry-audit-and-support" + "entryContracts" : [ + "ADR-046-provider-shell-terminal" ], - "exitGate": "ADR046-W5 exit criteria (ADR-046-validation-and-delivery §4): every spec/work item in this wave Merged with clean destinations, all validators green, and the ten-role panel seal recorded", - "id": "ADR046-telem-004", - "kind": "work-item", - "owner": "ADR046-telem-001 + ADR046-core-001; core-controller owner", - "parallelGroup": "wi:ADR-046-telemetry-audit-and-support", - "prerequisites": [ - "ADR-046-telemetry-audit-and-support", - "ADR046-core-001", - "ADR046-telem-001" + "exitGate" : "ADR046-W6 exit criteria (ADR-046-validation-and-delivery §4): every spec/work item in this wave Merged with clean destinations, all validators green, and the ten-role panel seal recorded", + "id" : "ADR046-sterm-012", + "kind" : "work-item", + "owner" : "Baseline removal area; owned by migration/removal implementation for shell-terminal.", + "parallelGroup" : "wi:ADR-046-provider-shell-terminal", + "prerequisites" : [ + "ADR-046-provider-shell-terminal" ], - "specId": "ADR-046-telemetry-audit-and-support", - "topologicalRank": 11, - "wave": "W5" + "specId" : "ADR-046-provider-shell-terminal", + "topologicalRank" : 11, + "wave" : "W6" }, { - "blockers": [], - "destinations": [ - "`packages/d2b-provider-supervisor/src/metrics.rs`, `packages/d2b-provider-supervisor/src/tracing.rs`" + "blockers" : [], + "destinations" : [ + "`packages/d2b-provider-shell-terminal/src/service/{controller,supervisor}.rs`" ], - "entryContracts": [ - "ADR-046-telemetry-audit-and-support" + "entryContracts" : [ + "ADR-046-provider-shell-terminal" ], - "exitGate": "ADR046-W5 exit criteria (ADR-046-validation-and-delivery §4): every spec/work item in this wave Merged with clean destinations, all validators green, and the ten-role panel seal recorded", - "id": "ADR046-telem-005", - "kind": "work-item", - "owner": "ADR046-telem-001 + ADR046-process-001; Process Provider owner", - "parallelGroup": "wi:ADR-046-telemetry-audit-and-support", - "prerequisites": [ - "ADR-046-telemetry-audit-and-support", - "ADR046-process-001", - "ADR046-telem-001" + "exitGate" : "ADR046-W6 exit criteria (ADR-046-validation-and-delivery §4): every spec/work item in this wave Merged with clean destinations, all validators green, and the ten-role panel seal recorded", + "id" : "ADR046-sterm-013", + "kind" : "work-item", + "owner" : "Supervisor service area; owned by shell-terminal controller/supervisor service modules and ComponentSession contracts.", + "parallelGroup" : "wi:ADR-046-provider-shell-terminal", + "prerequisites" : [ + "ADR-046-provider-shell-terminal" ], - "specId": "ADR-046-telemetry-audit-and-support", - "topologicalRank": 11, - "wave": "W5" + "specId" : "ADR-046-provider-shell-terminal", + "topologicalRank" : 11, + "wave" : "W6" }, { - "blockers": [], - "destinations": [ - "`packages/d2b-provider-observability-otel/src/`, `nixos-modules/components/observability/` (adapted files)" + "blockers" : [], + "destinations" : [ + "`packages/d2b-resource-store/src/lib.rs`, `packages/d2b-resource-store-redb/src/lib.rs`, `schema.rs`, `keys.rs`, `transaction.rs`" ], - "entryContracts": [ - "ADR-046-telemetry-audit-and-support" + "entryContracts" : [ + "ADR-046-resource-store-redb" ], - "exitGate": "ADR046-W5 exit criteria (ADR-046-validation-and-delivery §4): every spec/work item in this wave Merged with clean destinations, all validators green, and the ten-role panel seal recorded", - "id": "ADR046-telem-006", - "kind": "work-item", - "owner": "ADR046-process-001 + ADR046-provider-001; `observability-otel` Provider owner", - "parallelGroup": "wi:ADR-046-telemetry-audit-and-support", - "prerequisites": [ - "ADR-046-telemetry-audit-and-support", - "ADR046-process-001", - "ADR046-provider-001" + "exitGate" : "ADR046-W0 exit criteria (ADR-046-validation-and-delivery §4): every spec/work item in this wave Merged with clean destinations, all validators green, and the ten-role panel seal recorded", + "id" : "ADR046-store-001", + "kind" : "work-item", + "owner" : "W0; store integrator", + "parallelGroup" : "wi:ADR-046-resource-store-redb", + "prerequisites" : [ + "ADR-046-resource-store-redb" ], - "specId": "ADR-046-telemetry-audit-and-support", - "topologicalRank": 10, - "wave": "W5" + "specId" : "ADR-046-resource-store-redb", + "topologicalRank" : 4, + "wave" : "W0" }, { - "blockers": [], - "destinations": [ - "`packages/d2b-provider-observability-otel/src/nix/journald.nix` (new Nix fragment)" + "blockers" : [], + "destinations" : [ + "`packages/d2b-resource-store-redb/src/revision_log.rs`, `packages/d2b-resource-api/src/watch.rs`" ], - "entryContracts": [ - "ADR-046-telemetry-audit-and-support" + "entryContracts" : [ + "ADR-046-resource-store-redb" ], - "exitGate": "ADR046-W5 exit criteria (ADR-046-validation-and-delivery §4): every spec/work item in this wave Merged with clean destinations, all validators green, and the ten-role panel seal recorded", - "id": "ADR046-telem-007", - "kind": "work-item", - "owner": "ADR046-telem-006; Nix/observability owner", - "parallelGroup": "wi:ADR-046-telemetry-audit-and-support", - "prerequisites": [ - "ADR-046-telemetry-audit-and-support", - "ADR046-telem-006" + "exitGate" : "ADR046-W0 exit criteria (ADR-046-validation-and-delivery §4): every spec/work item in this wave Merged with clean destinations, all validators green, and the ten-role panel seal recorded", + "id" : "ADR046-store-002", + "kind" : "work-item", + "owner" : "ADR046-store-001; watch/reconciliation integrator", + "parallelGroup" : "wi:ADR-046-resource-store-redb", + "prerequisites" : [ + "ADR-046-resource-store-redb", + "ADR046-store-001" ], - "specId": "ADR-046-telemetry-audit-and-support", - "topologicalRank": 11, - "wave": "W5" + "specId" : "ADR-046-resource-store-redb", + "topologicalRank" : 5, + "wave" : "W0" }, { - "blockers": [], - "destinations": [ - "`packages/d2b-contract-tests/tests/policy_telemetry_redaction.rs` (new); updated `policy_observability.rs`; updated `policy_metrics.rs`" + "blockers" : [], + "destinations" : [ + "`packages/d2b-resource-store-redb/src/backup.rs`, `migration.rs`; generated v3 storage row" ], - "entryContracts": [ - "ADR-046-telemetry-audit-and-support" + "entryContracts" : [ + "ADR-046-resource-store-redb" ], - "exitGate": "ADR046-W5 exit criteria (ADR-046-validation-and-delivery §4): every spec/work item in this wave Merged with clean destinations, all validators green, and the ten-role panel seal recorded", - "id": "ADR046-telem-008", - "kind": "work-item", - "owner": "ADR046-telem-001; policy/contract-tests owner", - "parallelGroup": "wi:ADR-046-telemetry-audit-and-support", - "prerequisites": [ - "ADR-046-telemetry-audit-and-support", - "ADR046-telem-001" + "exitGate" : "ADR046-W0 exit criteria (ADR-046-validation-and-delivery §4): every spec/work item in this wave Merged with clean destinations, all validators green, and the ten-role panel seal recorded", + "id" : "ADR046-store-003", + "kind" : "work-item", + "owner" : "ADR046-store-001; storage/broker integrator", + "parallelGroup" : "wi:ADR-046-resource-store-redb", + "prerequisites" : [ + "ADR-046-resource-store-redb", + "ADR046-store-001" ], - "specId": "ADR-046-telemetry-audit-and-support", - "topologicalRank": 11, - "wave": "W5" + "specId" : "ADR-046-resource-store-redb", + "topologicalRank" : 5, + "wave" : "W0" }, { - "blockers": [], - "destinations": [ - "`nixos-modules/resources.nix` (uniform `d2b.zones..resources` schema-aware option; `spec.*` option types generated from `ResourceTypeSchema` for each `type`); `nixos-modules/resources-bundle.nix` (ADR-only: sorted integrity-pinned bundle derivation)" + "blockers" : [], + "destinations" : [ + "`docs/specs/ADR-046-spec-set.json`, `docs/specs/ADR-046-work-items.json`, `docs/specs/ADR-046-implementation-graph.json`, `docs/specs/ADR-046-implementation-graph.md` (generated, committed non-member artifacts; named in `docs/specs/README.md`)" ], - "entryContracts": [ - "ADR-046-telemetry-audit-and-support" + "entryContracts" : [ + "ADR-046-streamline" ], - "exitGate": "ADR046-W5 exit criteria (ADR-046-validation-and-delivery §4): every spec/work item in this wave Merged with clean destinations, all validators green, and the ten-role panel seal recorded", - "id": "ADR046-telem-009", - "kind": "work-item", - "owner": "W0/W1a; Nix integrator (adapts `nixos-modules/options-observability.nix` and `options-realms.nix`)", - "parallelGroup": "wi:ADR-046-telemetry-audit-and-support", - "prerequisites": [ - "ADR-046-telemetry-audit-and-support" + "exitGate" : "ADR046-W7 exit criteria (ADR-046-validation-and-delivery §4): every spec/work item in this wave Merged with clean destinations, all validators green, and the ten-role panel seal recorded", + "id" : "ADR046-streamline-001", + "kind" : "work-item", + "owner" : "`packages/xtask` owner; no dependency on any other streamline item", + "parallelGroup" : "wi:ADR-046-streamline", + "prerequisites" : [ + "ADR-046-streamline" ], - "specId": "ADR-046-telemetry-audit-and-support", - "topologicalRank": 10, - "wave": "W5" + "specId" : "ADR-046-streamline", + "topologicalRank" : 12, + "wave" : "W7" }, { - "blockers": [], - "destinations": [ - "`nixos-modules/resources-bundle.nix` (build-time validation step 4 in the `resources-bundle` derivation); `packages/d2b-core-controller/src/configuration.rs` (runtime activation checks)" + "blockers" : [], + "destinations" : [ + "`docs/specs/schemas/*.schema.json` (Tier A: hand-authored-once canonical source checked into the tree, matching the frozen D010/D032/D075/D076/D080 decisions exactly); `packages/d2b-core/src/resource_schema/*.rs` (Tier B: the eventual Rust source of truth once ResourceType implementation exists, at which point the Tier A JSON becomes generated from Rust instead of hand-authored)" ], - "entryContracts": [ - "ADR-046-telemetry-audit-and-support" + "entryContracts" : [ + "ADR-046-streamline" ], - "exitGate": "ADR046-W5 exit criteria (ADR-046-validation-and-delivery §4): every spec/work item in this wave Merged with clean destinations, all validators green, and the ten-role panel seal recorded", - "id": "ADR046-telem-010", - "kind": "work-item", - "owner": "ADR046-telem-009 + ADR046-telem-006 + ADR046-store-001; schema/validation owner", - "parallelGroup": "wi:ADR-046-telemetry-audit-and-support", - "prerequisites": [ - "ADR-046-telemetry-audit-and-support", - "ADR046-store-001", - "ADR046-telem-006", - "ADR046-telem-009" + "exitGate" : "ADR046-W7 exit criteria (ADR-046-validation-and-delivery §4): every spec/work item in this wave Merged with clean destinations, all validators green, and the ten-role panel seal recorded", + "id" : "ADR046-streamline-002", + "kind" : "work-item", + "owner" : "ADR046-streamline-001; `d2b-core` owner (Tier B only)", + "parallelGroup" : "wi:ADR-046-streamline", + "prerequisites" : [ + "ADR-046-streamline", + "ADR046-streamline-001" ], - "specId": "ADR-046-telemetry-audit-and-support", - "topologicalRank": 11, - "wave": "W5" + "specId" : "ADR-046-streamline", + "topologicalRank" : 13, + "wave" : "W7" }, { - "blockers": [], - "destinations": [ - "`packages/d2b-core-controller/src/{configuration.rs, ownership.rs}`" + "blockers" : [], + "destinations" : [ + "`packages/xtask/src/bin/spec_schema_check.rs`; wired as a Layer-1 policy lint at `packages/d2b-contract-tests/tests/policy_spec_schema.rs`" ], - "entryContracts": [ - "ADR-046-telemetry-audit-and-support" + "entryContracts" : [ + "ADR-046-streamline" ], - "exitGate": "ADR046-W5 exit criteria (ADR-046-validation-and-delivery §4): every spec/work item in this wave Merged with clean destinations, all validators green, and the ten-role panel seal recorded", - "id": "ADR046-telem-011", - "kind": "work-item", - "owner": "ADR046-telem-009 + ADR046-telem-010 + ADR046-audit-001 + ADR046-store-001; core-controller owner", - "parallelGroup": "wi:core-config-hub", - "prerequisites": [ - "ADR-046-telemetry-audit-and-support", - "ADR046-audit-001", - "ADR046-core-001", - "ADR046-store-001", - "ADR046-telem-009", - "ADR046-telem-010" + "exitGate" : "ADR046-W7 exit criteria (ADR-046-validation-and-delivery §4): every spec/work item in this wave Merged with clean destinations, all validators green, and the ten-role panel seal recorded", + "id" : "ADR046-streamline-003", + "kind" : "work-item", + "owner" : "ADR046-streamline-001, ADR046-streamline-002", + "parallelGroup" : "wi:ADR-046-streamline", + "prerequisites" : [ + "ADR-046-streamline", + "ADR046-streamline-001", + "ADR046-streamline-002" ], - "specId": "ADR-046-telemetry-audit-and-support", - "topologicalRank": 12, - "wave": "W5" + "specId" : "ADR-046-streamline", + "topologicalRank" : 14, + "wave" : "W7" }, { - "blockers": [], - "destinations": [ - "`packages/d2b-provider-transport-azure-relay/src/relay_transport.rs`" + "blockers" : [], + "destinations" : [ + "`docs/specs/providers/TEMPLATE.md` (committed, non-normative reference); `packages/xtask/src/bin/new_provider_dossier.rs` (`cargo run -p xtask -- new-provider-dossier --name `)" ], - "entryContracts": [ - "ADR-046-provider-transport-azure-relay" + "entryContracts" : [ + "ADR-046-streamline" ], - "exitGate": "ADR046-W6 exit criteria (ADR-046-validation-and-delivery §4): every spec/work item in this wave Merged with clean destinations, all validators green, and the ten-role panel seal recorded", - "id": "ADR046-transport-relay-001", - "kind": "work-item", - "owner": "W0 shared contract root; ComponentSession transport adapter owner", - "parallelGroup": "wi:ADR-046-provider-transport-azure-relay", - "prerequisites": [ - "ADR-046-provider-transport-azure-relay" + "exitGate" : "ADR046-W7 exit criteria (ADR-046-validation-and-delivery §4): every spec/work item in this wave Merged with clean destinations, all validators green, and the ten-role panel seal recorded", + "id" : "ADR046-streamline-004", + "kind" : "work-item", + "owner" : "ADR046-streamline-001, ADR046-streamline-002", + "parallelGroup" : "wi:ADR-046-streamline", + "prerequisites" : [ + "ADR-046-streamline", + "ADR046-streamline-001", + "ADR046-streamline-002" ], - "specId": "ADR-046-provider-transport-azure-relay", - "topologicalRank": 11, - "wave": "W6" + "specId" : "ADR-046-streamline", + "topologicalRank" : 14, + "wave" : "W7" }, { - "blockers": [], - "destinations": [ - "`packages/d2b-provider-transport-azure-relay/src/credential_client.rs`" + "blockers" : [], + "destinations" : [ + "`packages/d2b-contract-tests/tests/policy_spec_vocabulary.rs`, `policy_spec_ownership.rs`, `policy_spec_finalizer_phase.rs`, `policy_spec_source_policy.rs`" ], - "entryContracts": [ - "ADR-046-provider-transport-azure-relay" + "entryContracts" : [ + "ADR-046-streamline" ], - "exitGate": "ADR046-W6 exit criteria (ADR-046-validation-and-delivery §4): every spec/work item in this wave Merged with clean destinations, all validators green, and the ten-role panel seal recorded", - "id": "ADR046-transport-relay-002", - "kind": "work-item", - "owner": "ADR046-transport-relay-001; Credential KK session; ComponentSession/d2b-bus owner", - "parallelGroup": "wi:ADR-046-provider-transport-azure-relay", - "prerequisites": [ - "ADR-046-provider-transport-azure-relay" + "exitGate" : "ADR046-W7 exit criteria (ADR-046-validation-and-delivery §4): every spec/work item in this wave Merged with clean destinations, all validators green, and the ten-role panel seal recorded", + "id" : "ADR046-streamline-005", + "kind" : "work-item", + "owner" : "ADR046-streamline-001", + "parallelGroup" : "wi:ADR-046-streamline", + "prerequisites" : [ + "ADR-046-streamline", + "ADR046-streamline-001" ], - "specId": "ADR-046-provider-transport-azure-relay", - "topologicalRank": 11, - "wave": "W6" + "specId" : "ADR-046-streamline", + "topologicalRank" : 13, + "wave" : "W7" }, { - "blockers": [], - "destinations": [ - "`packages/d2b-provider-transport-azure-relay/src/reconnect.rs`" + "blockers" : [], + "destinations" : [ + "`packages/d2b-resource-store-redb/tests/provider_state_graph.rs` (or the eventual crate implementing Zone resource storage)" ], - "entryContracts": [ - "ADR-046-provider-transport-azure-relay" + "entryContracts" : [ + "ADR-046-streamline" ], - "exitGate": "ADR046-W6 exit criteria (ADR-046-validation-and-delivery §4): every spec/work item in this wave Merged with clean destinations, all validators green, and the ten-role panel seal recorded", - "id": "ADR046-transport-relay-003", - "kind": "work-item", - "owner": "ADR046-transport-relay-001; reconnect contract; ZoneLink handler", - "parallelGroup": "wi:ADR-046-provider-transport-azure-relay", - "prerequisites": [ - "ADR-046-provider-transport-azure-relay" + "exitGate" : "ADR046-W7 exit criteria (ADR-046-validation-and-delivery §4): every spec/work item in this wave Merged with clean destinations, all validators green, and the ten-role panel seal recorded", + "id" : "ADR046-streamline-006", + "kind" : "work-item", + "owner" : "The Zone resource-store implementation work item (not yet filed; blocked on W0-W10 implementation request per D024); ADR046-streamline-001 for the doc-level invariant source", + "parallelGroup" : "wi:ADR-046-streamline", + "prerequisites" : [ + "ADR-046-streamline", + "ADR046-streamline-001" ], - "specId": "ADR-046-provider-transport-azure-relay", - "topologicalRank": 11, - "wave": "W6" + "specId" : "ADR-046-streamline", + "topologicalRank" : 13, + "wave" : "W7" }, { - "blockers": [], - "destinations": [ - "`packages/d2b-provider-transport-azure-relay/src/transport_settings.rs`; `docs/reference/schemas/v3/providers/transport-azure-relay.transport-settings.json`" + "blockers" : [], + "destinations" : [ + "`packages/d2b-contract-tests/tests/policy_effectport_boundary.rs`, `policy_worker_bus_boundary.rs`" ], - "entryContracts": [ - "ADR-046-provider-transport-azure-relay" + "entryContracts" : [ + "ADR-046-streamline" ], - "exitGate": "ADR046-W6 exit criteria (ADR-046-validation-and-delivery §4): every spec/work item in this wave Merged with clean destinations, all validators green, and the ten-role panel seal recorded", - "id": "ADR046-transport-relay-004", - "kind": "work-item", - "owner": "ADR046-transport-relay-001; transport settings schema; Nix configuration owner", - "parallelGroup": "wi:ADR-046-provider-transport-azure-relay", - "prerequisites": [ - "ADR-046-provider-transport-azure-relay" + "exitGate" : "ADR046-W7 exit criteria (ADR-046-validation-and-delivery §4): every spec/work item in this wave Merged with clean destinations, all validators green, and the ten-role panel seal recorded", + "id" : "ADR046-streamline-007", + "kind" : "work-item", + "owner" : "The Provider-toolkit/EffectPort implementation work item (not yet filed; blocked on the W0-W10 implementation request); ADR046-streamline-001 for the doc-level invariant source", + "parallelGroup" : "wi:ADR-046-streamline", + "prerequisites" : [ + "ADR-046-streamline", + "ADR046-streamline-001" ], - "specId": "ADR-046-provider-transport-azure-relay", - "topologicalRank": 11, - "wave": "W6" + "specId" : "ADR-046-streamline", + "topologicalRank" : 13, + "wave" : "W7" }, { - "blockers": [], - "destinations": [ - "`packages/d2b-provider-transport-azure-relay/src/backpressure.rs`" + "blockers" : [], + "destinations" : [ + "`packages/d2b-contract-tests/tests/policy_work_items.rs`" ], - "entryContracts": [ - "ADR-046-provider-transport-azure-relay" + "entryContracts" : [ + "ADR-046-streamline" ], - "exitGate": "ADR046-W6 exit criteria (ADR-046-validation-and-delivery §4): every spec/work item in this wave Merged with clean destinations, all validators green, and the ten-role panel seal recorded", - "id": "ADR046-transport-relay-005", - "kind": "work-item", - "owner": "ADR046-transport-relay-001; backpressure/credit contract", - "parallelGroup": "wi:ADR-046-provider-transport-azure-relay", - "prerequisites": [ - "ADR-046-provider-transport-azure-relay" + "exitGate" : "ADR046-W7 exit criteria (ADR-046-validation-and-delivery §4): every spec/work item in this wave Merged with clean destinations, all validators green, and the ten-role panel seal recorded", + "id" : "ADR046-streamline-008", + "kind" : "work-item", + "owner" : "ADR046-streamline-001", + "parallelGroup" : "wi:ADR-046-streamline", + "prerequisites" : [ + "ADR-046-streamline", + "ADR046-streamline-001" ], - "specId": "ADR-046-provider-transport-azure-relay", - "topologicalRank": 11, - "wave": "W6" + "specId" : "ADR-046-streamline", + "topologicalRank" : 13, + "wave" : "W7" }, { - "blockers": [], - "destinations": [ - "`packages/d2b-provider-transport-azure-relay/src/{metrics.rs, audit.rs}`" + "blockers" : [], + "destinations" : [ + "`docs/specs/ADR-046-provider-catalog.md` (generated, committed)" ], - "entryContracts": [ - "ADR-046-provider-transport-azure-relay" + "entryContracts" : [ + "ADR-046-streamline" ], - "exitGate": "ADR046-W6 exit criteria (ADR-046-validation-and-delivery §4): every spec/work item in this wave Merged with clean destinations, all validators green, and the ten-role panel seal recorded", - "id": "ADR046-transport-relay-006", - "kind": "work-item", - "owner": "ADR046-transport-relay-001 through ADR046-transport-relay-005; telemetry/audit owner", - "parallelGroup": "wi:ADR-046-provider-transport-azure-relay", - "prerequisites": [ - "ADR-046-provider-transport-azure-relay" + "exitGate" : "ADR046-W7 exit criteria (ADR-046-validation-and-delivery §4): every spec/work item in this wave Merged with clean destinations, all validators green, and the ten-role panel seal recorded", + "id" : "ADR046-streamline-009", + "kind" : "work-item", + "owner" : "ADR046-streamline-001", + "parallelGroup" : "wi:ADR-046-streamline", + "prerequisites" : [ + "ADR-046-streamline", + "ADR046-streamline-001" ], - "specId": "ADR-046-provider-transport-azure-relay", - "topologicalRank": 11, - "wave": "W6" + "specId" : "ADR-046-streamline", + "topologicalRank" : 13, + "wave" : "W7" }, { - "blockers": [], - "destinations": [ - "`packages/d2b-provider-transport-azure-relay/src/tests/integration/README`" + "blockers" : [], + "destinations" : [ + "`tests/tools/reconcile-stale-base.sh` (reporting only) plus a documented `git town sync`/`git town` restack procedure this report feeds into, since this repository does not yet use Git Town and F1/F11 show plain rebase/cherry-pick was insufficient to prevent duplicate reconciliation" ], - "entryContracts": [ - "ADR-046-provider-transport-azure-relay" + "entryContracts" : [ + "ADR-046-streamline" ], - "exitGate": "ADR046-W6 exit criteria (ADR-046-validation-and-delivery §4): every spec/work item in this wave Merged with clean destinations, all validators green, and the ten-role panel seal recorded", - "id": "ADR046-transport-relay-007", - "kind": "work-item", - "owner": "Provider crate owner; integration test owner", - "parallelGroup": "wi:ADR-046-provider-transport-azure-relay", - "prerequisites": [ - "ADR-046-provider-transport-azure-relay" + "exitGate" : "ADR046-W7 exit criteria (ADR-046-validation-and-delivery §4): every spec/work item in this wave Merged with clean destinations, all validators green, and the ten-role panel seal recorded", + "id" : "ADR046-streamline-010", + "kind" : "work-item", + "owner" : "ADR046-streamline-001", + "parallelGroup" : "wi:ADR-046-streamline", + "prerequisites" : [ + "ADR-046-streamline", + "ADR046-streamline-001" ], - "specId": "ADR-046-provider-transport-azure-relay", - "topologicalRank": 11, - "wave": "W6" + "specId" : "ADR-046-streamline", + "topologicalRank" : 13, + "wave" : "W7" }, { - "blockers": [], - "destinations": [ - "`packages/d2b-provider-transport-unix/src/credit.rs` (imports `MAX_PACKET_ATTACHMENTS=32`, `RESERVED_CONTROL_FDS=64`, credit-class constants from v3 contract); `src/portal.rs` (imports `MAX_PACKET_ATTACHMENTS` for portal validation)" + "blockers" : [], + "destinations" : [ + "`packages/xtask/src/bin/handoff_manifest.rs` (schema/validator only); manifest instances are per-round artifacts referenced from PR bodies, not committed to the tree (consistent with this repository's \"Screenshot and visual artifact hygiene\"-style external-evidence posture)" ], - "entryContracts": [ - "ADR-046-provider-transport-unix" + "entryContracts" : [ + "ADR-046-streamline" ], - "exitGate": "ADR046-W6 exit criteria (ADR-046-validation-and-delivery §4): every spec/work item in this wave Merged with clean destinations, all validators green, and the ten-role panel seal recorded", - "id": "ADR046-transport-unix-001", - "kind": "work-item", - "owner": "ADR046-session-001 (v3 ComponentSession contracts); W0 shared contract root", - "parallelGroup": "wi:ADR-046-provider-transport-unix", - "prerequisites": [ - "ADR-046-provider-transport-unix", - "ADR046-session-001" + "exitGate" : "ADR046-W7 exit criteria (ADR-046-validation-and-delivery §4): every spec/work item in this wave Merged with clean destinations, all validators green, and the ten-role panel seal recorded", + "id" : "ADR046-streamline-011", + "kind" : "work-item", + "owner" : "No prerequisite; `packages/xtask` schema/validator owner", + "parallelGroup" : "wi:ADR-046-streamline", + "prerequisites" : [ + "ADR-046-streamline" ], - "specId": "ADR-046-provider-transport-unix", - "topologicalRank": 11, - "wave": "W6" + "specId" : "ADR-046-streamline", + "topologicalRank" : 12, + "wave" : "W7" }, { - "blockers": [], - "destinations": [ - "`packages/d2b-provider-transport-unix/src/{seqpacket,identity,socket}.rs`" + "blockers" : [], + "destinations" : [ + "`tests/tools/import-task-db-consistency.sh`" ], - "entryContracts": [ - "ADR-046-provider-transport-unix" + "entryContracts" : [ + "ADR-046-streamline" ], - "exitGate": "ADR046-W6 exit criteria (ADR-046-validation-and-delivery §4): every spec/work item in this wave Merged with clean destinations, all validators green, and the ten-role panel seal recorded", - "id": "ADR046-transport-unix-002", - "kind": "work-item", - "owner": "ADR046-transport-unix-001; d2b-bus transport layer (ADR046-bus-001)", - "parallelGroup": "wi:ADR-046-provider-transport-unix", - "prerequisites": [ - "ADR-046-provider-transport-unix", - "ADR046-bus-001" + "exitGate" : "ADR046-W7 exit criteria (ADR-046-validation-and-delivery §4): every spec/work item in this wave Merged with clean destinations, all validators green, and the ten-role panel seal recorded", + "id" : "ADR046-streamline-012", + "kind" : "work-item", + "owner" : "ADR046-streamline-011 (shares the manifest schema for commit-SHA binding)", + "parallelGroup" : "wi:ADR-046-streamline", + "prerequisites" : [ + "ADR-046-streamline", + "ADR046-streamline-011" ], - "specId": "ADR-046-provider-transport-unix", - "topologicalRank": 11, - "wave": "W6" + "specId" : "ADR-046-streamline", + "topologicalRank" : 13, + "wave" : "W7" }, { - "blockers": [], - "destinations": [ - "`packages/d2b-provider-transport-unix/src/{stream,socket}.rs`" + "blockers" : [], + "destinations" : [ + "`tests/tools/anti-serialization-report.sh`" ], - "entryContracts": [ - "ADR-046-provider-transport-unix" + "entryContracts" : [ + "ADR-046-streamline" ], - "exitGate": "ADR046-W6 exit criteria (ADR-046-validation-and-delivery §4): every spec/work item in this wave Merged with clean destinations, all validators green, and the ten-role panel seal recorded", - "id": "ADR046-transport-unix-003", - "kind": "work-item", - "owner": "ADR046-transport-unix-001", - "parallelGroup": "wi:ADR-046-provider-transport-unix", - "prerequisites": [ - "ADR-046-provider-transport-unix" + "exitGate" : "ADR046-W7 exit criteria (ADR-046-validation-and-delivery §4): every spec/work item in this wave Merged with clean destinations, all validators green, and the ten-role panel seal recorded", + "id" : "ADR046-streamline-013", + "kind" : "work-item", + "owner" : "ADR046-streamline-001, ADR046-streamline-010", + "parallelGroup" : "wi:ADR-046-streamline", + "prerequisites" : [ + "ADR-046-streamline", + "ADR046-streamline-001", + "ADR046-streamline-010" ], - "specId": "ADR-046-provider-transport-unix", - "topologicalRank": 11, - "wave": "W6" + "specId" : "ADR-046-streamline", + "topologicalRank" : 14, + "wave" : "W7" }, { - "blockers": [], - "destinations": [ - "`packages/d2b-provider-transport-unix/src/credit.rs`" + "blockers" : [], + "destinations" : [ + "`tests/tools/run-layer.sh` extension (this repository already has `tests/tools/run-layer.sh` and `layer1-jobs.py` bounded-parallelism precedent) plus fake `EffectPort`/`ResourceClient` stub crates under `packages/d2b-provider-toolkit-fakes/`" ], - "entryContracts": [ - "ADR-046-provider-transport-unix" + "entryContracts" : [ + "ADR-046-streamline" ], - "exitGate": "ADR046-W6 exit criteria (ADR-046-validation-and-delivery §4): every spec/work item in this wave Merged with clean destinations, all validators green, and the ten-role panel seal recorded", - "id": "ADR046-transport-unix-004", - "kind": "work-item", - "owner": "ADR046-transport-unix-001", - "parallelGroup": "wi:ADR-046-provider-transport-unix", - "prerequisites": [ - "ADR-046-provider-transport-unix" + "exitGate" : "ADR046-W7 exit criteria (ADR-046-validation-and-delivery §4): every spec/work item in this wave Merged with clean destinations, all validators green, and the ten-role panel seal recorded", + "id" : "ADR046-streamline-014", + "kind" : "work-item", + "owner" : "The Provider-toolkit implementation work item (blocked on the W0-W10 implementation request)", + "parallelGroup" : "wi:ADR-046-streamline", + "prerequisites" : [ + "ADR-046-streamline" ], - "specId": "ADR-046-provider-transport-unix", - "topologicalRank": 11, - "wave": "W6" + "specId" : "ADR-046-streamline", + "topologicalRank" : 12, + "wave" : "W7" }, { - "blockers": [], - "destinations": [ - "`packages/d2b-provider-transport-unix/src/descriptor.rs`" + "blockers" : [], + "destinations" : [ + "Shared `packages/xtask` regeneration-conflict-detection helper consumed by every `gen-*`/`spec-registry` subcommand" ], - "entryContracts": [ - "ADR-046-provider-transport-unix" + "entryContracts" : [ + "ADR-046-streamline" ], - "exitGate": "ADR046-W6 exit criteria (ADR-046-validation-and-delivery §4): every spec/work item in this wave Merged with clean destinations, all validators green, and the ten-role panel seal recorded", - "id": "ADR046-transport-unix-005", - "kind": "work-item", - "owner": "ADR046-transport-unix-002", - "parallelGroup": "wi:ADR-046-provider-transport-unix", - "prerequisites": [ - "ADR-046-provider-transport-unix" + "exitGate" : "ADR046-W7 exit criteria (ADR-046-validation-and-delivery §4): every spec/work item in this wave Merged with clean destinations, all validators green, and the ten-role panel seal recorded", + "id" : "ADR046-streamline-015", + "kind" : "work-item", + "owner" : "ADR046-streamline-001, ADR046-streamline-009, ADR046-streamline-002 (Tier A part)", + "parallelGroup" : "wi:ADR-046-streamline", + "prerequisites" : [ + "ADR-046-streamline", + "ADR046-streamline-001", + "ADR046-streamline-002", + "ADR046-streamline-009" ], - "specId": "ADR-046-provider-transport-unix", - "topologicalRank": 11, - "wave": "W6" + "specId" : "ADR-046-streamline", + "topologicalRank" : 14, + "wave" : "W7" }, { - "blockers": [], - "destinations": [ - "`packages/d2b-provider-transport-unix/src/admission.rs`" + "blockers" : [], + "destinations" : [ + "`packages/d2b-contract-tests/tests/policy_no_leaked_decision_prefix.rs`; invoked as a required precondition script `tests/tools/pre-panel-gate.sh`" ], - "entryContracts": [ - "ADR-046-provider-transport-unix" + "entryContracts" : [ + "ADR-046-streamline" ], - "exitGate": "ADR046-W6 exit criteria (ADR-046-validation-and-delivery §4): every spec/work item in this wave Merged with clean destinations, all validators green, and the ten-role panel seal recorded", - "id": "ADR046-transport-unix-006", - "kind": "work-item", - "owner": "ADR046-transport-unix-002", - "parallelGroup": "wi:ADR-046-provider-transport-unix", - "prerequisites": [ - "ADR-046-provider-transport-unix" + "exitGate" : "ADR046-W7 exit criteria (ADR-046-validation-and-delivery §4): every spec/work item in this wave Merged with clean destinations, all validators green, and the ten-role panel seal recorded", + "id" : "ADR046-streamline-016", + "kind" : "work-item", + "owner" : "ADR046-streamline-001, ADR046-streamline-008", + "parallelGroup" : "wi:ADR-046-streamline", + "prerequisites" : [ + "ADR-046-streamline", + "ADR046-streamline-001", + "ADR046-streamline-008" ], - "specId": "ADR-046-provider-transport-unix", - "topologicalRank": 11, - "wave": "W6" + "specId" : "ADR-046-streamline", + "topologicalRank" : 14, + "wave" : "W7" }, { - "blockers": [], - "destinations": [ - "`packages/d2b-provider-transport-unix/src/{portal,service}.rs`" + "blockers" : [], + "destinations" : [ + "`docs/specs/ADR-046-streamline-evidence-commands.md` (a follow-up artifact outside this task's file scope; tracked here as a required future addition, not authored by this spec)" ], - "entryContracts": [ - "ADR-046-provider-transport-unix" + "entryContracts" : [ + "ADR-046-streamline" ], - "exitGate": "ADR046-W6 exit criteria (ADR-046-validation-and-delivery §4): every spec/work item in this wave Merged with clean destinations, all validators green, and the ten-role panel seal recorded", - "id": "ADR046-transport-unix-007", - "kind": "work-item", - "owner": "ADR046-transport-unix-002 through 006; ADR046-bus-001 (d2b-bus ComponentSession method dispatch); ADR046-session-001 (named-stream protocol)", - "parallelGroup": "wi:ADR-046-provider-transport-unix", - "prerequisites": [ - "ADR-046-provider-transport-unix", - "ADR046-bus-001", - "ADR046-session-001" + "exitGate" : "ADR046-W7 exit criteria (ADR-046-validation-and-delivery §4): every spec/work item in this wave Merged with clean destinations, all validators green, and the ten-role panel seal recorded", + "id" : "ADR046-streamline-017", + "kind" : "work-item", + "owner" : "No prerequisite; `docs/specs` evidence-command documentation owner", + "parallelGroup" : "wi:ADR-046-streamline", + "prerequisites" : [ + "ADR-046-streamline" ], - "specId": "ADR-046-provider-transport-unix", - "topologicalRank": 11, - "wave": "W6" + "specId" : "ADR-046-streamline", + "topologicalRank" : 12, + "wave" : "W7" }, { - "blockers": [], - "destinations": [ - "`packages/d2b-provider-transport-unix/` crate Cargo.toml binary target `d2b-transport-unix-service`; Provider component descriptor JSON committed at `packages/d2b-provider-transport-unix/descriptor/unix-transport-service.json`; Nix package derivation at `packages/d2b-provider-transport-unix/`" + "blockers" : [], + "destinations" : [ + "`tests/tools/worktree-disk-report.sh`" ], - "entryContracts": [ - "ADR-046-provider-transport-unix" + "entryContracts" : [ + "ADR-046-streamline" ], - "exitGate": "ADR046-W6 exit criteria (ADR-046-validation-and-delivery §4): every spec/work item in this wave Merged with clean destinations, all validators green, and the ten-role panel seal recorded", - "id": "ADR046-transport-unix-008", - "kind": "work-item", - "owner": "ADR046-transport-unix-007; ADR046-provider-003 (system Provider framework); Provider/system-minijail (ADR046-provider-003)", - "parallelGroup": "wi:ADR-046-provider-transport-unix", - "prerequisites": [ - "ADR-046-provider-transport-unix", - "ADR046-provider-003" + "exitGate" : "ADR046-W7 exit criteria (ADR-046-validation-and-delivery §4): every spec/work item in this wave Merged with clean destinations, all validators green, and the ten-role panel seal recorded", + "id" : "ADR046-streamline-018", + "kind" : "work-item", + "owner" : "ADR046-streamline-010 (shares branch-staleness detection)", + "parallelGroup" : "wi:ADR-046-streamline", + "prerequisites" : [ + "ADR-046-streamline", + "ADR046-streamline-010" ], - "specId": "ADR-046-provider-transport-unix", - "topologicalRank": 11, - "wave": "W6" + "specId" : "ADR-046-streamline", + "topologicalRank" : 14, + "wave" : "W7" }, { - "blockers": [], - "destinations": [ - "`docs/reference/schemas/v3/providers/transport-unix.transport-binding.json`; `nixos-modules/assertions.nix` (assertion additions); generated `nixos-modules/generated/options-zones-ZoneLink.nix` provider-settings submodule" + "blockers" : [], + "destinations" : [ + "`packages/xtask/src/bin/terminology_check.rs` (`cargo run -p xtask -- terminology-check`)" ], - "entryContracts": [ - "ADR-046-provider-transport-unix" + "entryContracts" : [ + "ADR-046-streamline" ], - "exitGate": "ADR046-W6 exit criteria (ADR-046-validation-and-delivery §4): every spec/work item in this wave Merged with clean destinations, all validators green, and the ten-role panel seal recorded", - "id": "ADR046-transport-unix-009", - "kind": "work-item", - "owner": "ADR046-transport-unix-001; Nix/build integrator", - "parallelGroup": "wi:ADR-046-provider-transport-unix", - "prerequisites": [ - "ADR-046-provider-transport-unix" + "exitGate" : "ADR046-W7 exit criteria (ADR-046-validation-and-delivery §4): every spec/work item in this wave Merged with clean destinations, all validators green, and the ten-role panel seal recorded", + "id" : "ADR046-streamline-019", + "kind" : "work-item", + "owner" : "ADR046-streamline-001", + "parallelGroup" : "wi:ADR-046-streamline", + "prerequisites" : [ + "ADR-046-streamline", + "ADR046-streamline-001" ], - "specId": "ADR-046-provider-transport-unix", - "topologicalRank": 11, - "wave": "W6" + "specId" : "ADR-046-streamline", + "topologicalRank" : 13, + "wave" : "W7" }, { - "blockers": [], - "destinations": [ - "`packages/d2b-provider-transport-unix/src/{audit,metrics}.rs`" + "blockers" : [], + "destinations" : [ + "`packages/d2b-contract-tests/tests/policy_test_placement.rs`" ], - "entryContracts": [ - "ADR-046-provider-transport-unix" + "entryContracts" : [ + "ADR-046-streamline" ], - "exitGate": "ADR046-W6 exit criteria (ADR-046-validation-and-delivery §4): every spec/work item in this wave Merged with clean destinations, all validators green, and the ten-role panel seal recorded", - "id": "ADR046-transport-unix-010", - "kind": "work-item", - "owner": "ADR046-transport-unix-007; ADR-046-telemetry-audit-and-support", - "parallelGroup": "wi:ADR-046-provider-transport-unix", - "prerequisites": [ - "ADR-046-provider-transport-unix" + "exitGate" : "ADR046-W7 exit criteria (ADR-046-validation-and-delivery §4): every spec/work item in this wave Merged with clean destinations, all validators green, and the ten-role panel seal recorded", + "id" : "ADR046-streamline-020", + "kind" : "work-item", + "owner" : "ADR046-streamline-001", + "parallelGroup" : "wi:ADR-046-streamline", + "prerequisites" : [ + "ADR-046-streamline", + "ADR046-streamline-001" ], - "specId": "ADR-046-provider-transport-unix", - "topologicalRank": 11, - "wave": "W6" + "specId" : "ADR-046-streamline", + "topologicalRank" : 13, + "wave" : "W7" }, { - "blockers": [], - "destinations": [ - "`packages/d2b-provider-transport-unix/integration/` and `integration/README.md`" + "blockers" : [], + "destinations" : [ + "`packages/d2b-contract-tests/tests/policy_test_determinism.rs`" ], - "entryContracts": [ - "ADR-046-provider-transport-unix" + "entryContracts" : [ + "ADR-046-streamline" ], - "exitGate": "ADR046-W6 exit criteria (ADR-046-validation-and-delivery §4): every spec/work item in this wave Merged with clean destinations, all validators green, and the ten-role panel seal recorded", - "id": "ADR046-transport-unix-011", - "kind": "work-item", - "owner": "ADR046-transport-unix-007 through 010; test orchestration owner", - "parallelGroup": "wi:ADR-046-provider-transport-unix", - "prerequisites": [ - "ADR-046-provider-transport-unix" + "exitGate" : "ADR046-W7 exit criteria (ADR-046-validation-and-delivery §4): every spec/work item in this wave Merged with clean destinations, all validators green, and the ten-role panel seal recorded", + "id" : "ADR046-streamline-021", + "kind" : "work-item", + "owner" : "ADR046-streamline-020", + "parallelGroup" : "wi:ADR-046-streamline", + "prerequisites" : [ + "ADR-046-streamline", + "ADR046-streamline-020" ], - "specId": "ADR-046-provider-transport-unix", - "topologicalRank": 11, - "wave": "W6" + "specId" : "ADR-046-streamline", + "topologicalRank" : 14, + "wave" : "W7" }, { - "blockers": [], - "destinations": [ - "packages/d2b-contracts/src/usbip_effect_port.rs" + "blockers" : [], + "destinations" : [ + "`packages/xtask/src/test_runtime_ledger.rs` (shared with `ADR046-delivery-007`)" ], - "entryContracts": [ - "ADR-046-provider-device-usbip" + "entryContracts" : [ + "ADR-046-streamline" ], - "exitGate": "ADR046-W6 exit criteria (ADR-046-validation-and-delivery §4): every spec/work item in this wave Merged with clean destinations, all validators green, and the ten-role panel seal recorded", - "id": "ADR046-usbip-001", - "kind": "work-item", - "owner": "d2b-contracts crate shape stabilised by shared root contract; d2b-contracts owner", - "parallelGroup": "wi:ADR-046-provider-device-usbip", - "prerequisites": [ - "ADR-046-provider-device-usbip" + "exitGate" : "ADR046-W7 exit criteria (ADR-046-validation-and-delivery §4): every spec/work item in this wave Merged with clean destinations, all validators green, and the ten-role panel seal recorded", + "id" : "ADR046-streamline-022", + "kind" : "work-item", + "owner" : "ADR046-delivery-007", + "parallelGroup" : "wi:ADR-046-streamline", + "prerequisites" : [ + "ADR-046-streamline", + "ADR046-delivery-007" ], - "specId": "ADR-046-provider-device-usbip", - "topologicalRank": 11, - "wave": "W6" + "specId" : "ADR-046-streamline", + "topologicalRank" : 14, + "wave" : "W7" }, { - "blockers": [], - "destinations": [ - "packages/d2b-core/src/device_usbip_adapter.rs" + "blockers" : [], + "destinations" : [ + "`packages/xtask/src/bin/legacy_test_retirement.rs` (`cargo run -p xtask -- legacy-test-retirement`)" ], - "entryContracts": [ - "ADR-046-provider-device-usbip" + "entryContracts" : [ + "ADR-046-streamline" ], - "exitGate": "ADR046-W6 exit criteria (ADR-046-validation-and-delivery §4): every spec/work item in this wave Merged with clean destinations, all validators green, and the ten-role panel seal recorded", - "id": "ADR046-usbip-002", - "kind": "work-item", - "owner": "ADR046-usbip-001, ADR046-zone-control-024; UsbipBindFirewallRule broker op; d2b-host usbip argv support; framework core adapter owner", - "parallelGroup": "wi:ADR-046-provider-device-usbip", - "prerequisites": [ - "ADR-046-provider-device-usbip", - "ADR046-usbip-001", - "ADR046-zone-control-024" + "exitGate" : "ADR046-W7 exit criteria (ADR-046-validation-and-delivery §4): every spec/work item in this wave Merged with clean destinations, all validators green, and the ten-role panel seal recorded", + "id" : "ADR046-streamline-023", + "kind" : "work-item", + "owner" : "ADR046-streamline-008, ADR046-streamline-022", + "parallelGroup" : "wi:ADR-046-streamline", + "prerequisites" : [ + "ADR-046-streamline", + "ADR046-streamline-008", + "ADR046-streamline-022" ], - "specId": "ADR-046-provider-device-usbip", - "topologicalRank": 12, - "wave": "W6" + "specId" : "ADR-046-streamline", + "topologicalRank" : 15, + "wave" : "W7" }, { - "blockers": [], - "destinations": [ - "packages/d2b-provider-device-usbip/" + "blockers" : [], + "destinations" : [ + "`packages/xtask/src/bin/implementation_graph.rs` (`cargo run -p xtask -- implementation-graph`); folds the spec-set and test-runtime emitters into the one `xtask` dispatch" ], - "entryContracts": [ - "ADR-046-provider-device-usbip" + "entryContracts" : [ + "ADR-046-streamline" ], - "exitGate": "ADR046-W6 exit criteria (ADR-046-validation-and-delivery §4): every spec/work item in this wave Merged with clean destinations, all validators green, and the ten-role panel seal recorded", - "id": "ADR046-usbip-003", - "kind": "work-item", - "owner": "ADR046-usbip-001, ADR046-provider-004; Provider model crate structure; device-usbip provider owner", - "parallelGroup": "wi:ADR-046-provider-device-usbip", - "prerequisites": [ - "ADR-046-provider-device-usbip", - "ADR046-provider-004", - "ADR046-usbip-001" + "exitGate" : "ADR046-W7 exit criteria (ADR-046-validation-and-delivery §4): every spec/work item in this wave Merged with clean destinations, all validators green, and the ten-role panel seal recorded", + "id" : "ADR046-streamline-024", + "kind" : "work-item", + "owner" : "ADR046-streamline-001", + "parallelGroup" : "wi:ADR-046-streamline", + "prerequisites" : [ + "ADR-046-streamline", + "ADR046-streamline-001" ], - "specId": "ADR-046-provider-device-usbip", - "topologicalRank": 12, - "wave": "W6" + "specId" : "ADR-046-streamline", + "topologicalRank" : 13, + "wave" : "W7" }, { - "blockers": [], - "destinations": [ - "packages/d2b-provider-device-usbip/src/{controller,reconcile,export_import}.rs" + "blockers" : [], + "destinations" : [ + "`packages/d2b-provider-system-core/src/manifest.rs`, `packages/d2b-provider-system-core/src/audit.rs`, and `packages/d2b-provider-system-core/tests/provider_boundary.rs`" ], - "entryContracts": [ - "ADR-046-provider-device-usbip" + "entryContracts" : [ + "ADR-046-provider-system-core" ], - "exitGate": "ADR046-W6 exit criteria (ADR-046-validation-and-delivery §4): every spec/work item in this wave Merged with clean destinations, all validators green, and the ten-role panel seal recorded", - "id": "ADR046-usbip-004", - "kind": "work-item", - "owner": "ADR046-usbip-001, ADR046-usbip-002, ADR046-usbip-003, and ADR046-zone-control-024; device-usbip controller owner", - "parallelGroup": "wi:ADR-046-provider-device-usbip", - "prerequisites": [ - "ADR-046-provider-device-usbip", - "ADR046-usbip-001", - "ADR046-usbip-002", - "ADR046-usbip-003", - "ADR046-zone-control-024" + "exitGate" : "ADR046-W6 exit criteria (ADR-046-validation-and-delivery §4): every spec/work item in this wave Merged with clean destinations, all validators green, and the ten-role panel seal recorded", + "id" : "ADR046-system-core-001", + "kind" : "work-item", + "owner" : "`ADR046-provider-001`, `ADR046-exec-003`, `ADR046-exec-004`, `ADR046-exec-005`, `ADR046-pstate-012`, `ADR046-telem-001`, and `ADR046-telem-012`; `Provider/system-core` owner", + "parallelGroup" : "wi:ADR-046-provider-system-core", + "prerequisites" : [ + "ADR-046-provider-system-core", + "ADR046-exec-003", + "ADR046-exec-004", + "ADR046-exec-005", + "ADR046-provider-001", + "ADR046-pstate-012", + "ADR046-telem-001", + "ADR046-telem-012" ], - "specId": "ADR-046-provider-device-usbip", - "topologicalRank": 13, - "wave": "W6" + "specId" : "ADR-046-provider-system-core", + "topologicalRank" : 13, + "wave" : "W6" }, { - "blockers": [], - "destinations": [ - "packages/d2b-provider-device-usbip/src/reconcile.rs" + "blockers" : [], + "destinations" : [ + "`packages/d2b-provider-system-systemd/src/controller.rs` (async reconcile loop), `src/launch.rs` (launch algorithm via effect port), `src/effect_port.rs` (`SystemdProcessEffectPort` trait + test double), `src/adoption.rs` (adoption algorithm), `src/sandbox.rs` (SandboxSpec → unit property compiler)" ], - "entryContracts": [ - "ADR-046-provider-device-usbip" + "entryContracts" : [ + "ADR-046-provider-system-systemd" ], - "exitGate": "ADR046-W6 exit criteria (ADR-046-validation-and-delivery §4): every spec/work item in this wave Merged with clean destinations, all validators green, and the ten-role panel seal recorded", - "id": "ADR046-usbip-005", - "kind": "work-item", - "owner": "ADR046-usbip-003; Process ResourceType schema; device-usbip process lifecycle owner", - "parallelGroup": "wi:ADR-046-provider-device-usbip", - "prerequisites": [ - "ADR-046-provider-device-usbip", - "ADR046-usbip-003" + "exitGate" : "ADR046-W6 exit criteria (ADR-046-validation-and-delivery §4): every spec/work item in this wave Merged with clean destinations, all validators green, and the ten-role panel seal recorded", + "id" : "ADR046-systemd-001", + "kind" : "work-item", + "owner" : "`ADR046-process-002`; Process contracts/supervisor owner; effect port interface owner", + "parallelGroup" : "wi:ADR-046-provider-system-systemd", + "prerequisites" : [ + "ADR-046-provider-system-systemd", + "ADR046-process-002" ], - "specId": "ADR-046-provider-device-usbip", - "topologicalRank": 13, - "wave": "W6" + "specId" : "ADR-046-provider-system-systemd", + "topologicalRank" : 11, + "wave" : "W6" }, { - "blockers": [], - "destinations": [ - "packages/d2b-provider-device-usbip/src/status.rs" + "blockers" : [], + "destinations" : [ + "`nixos-modules/` (Provider ResourceSpec emission for `system-systemd`); `packages/d2b-provider-system-systemd/` package derivation and catalog entry" ], - "entryContracts": [ - "ADR-046-provider-device-usbip" + "entryContracts" : [ + "ADR-046-provider-system-systemd" ], - "exitGate": "ADR046-W6 exit criteria (ADR-046-validation-and-delivery §4): every spec/work item in this wave Merged with clean destinations, all validators green, and the ten-role panel seal recorded", - "id": "ADR046-usbip-006", - "kind": "work-item", - "owner": "ADR046-usbip-003; Device provider details plus `UsbService` and `UsbBinding` base/provider status schema owner", - "parallelGroup": "wi:ADR-046-provider-device-usbip", - "prerequisites": [ - "ADR-046-provider-device-usbip", - "ADR046-usbip-003" + "exitGate" : "ADR046-W6 exit criteria (ADR-046-validation-and-delivery §4): every spec/work item in this wave Merged with clean destinations, all validators green, and the ten-role panel seal recorded", + "id" : "ADR046-systemd-002", + "kind" : "work-item", + "owner" : "`ADR046-systemd-001`; Nix/package integrator", + "parallelGroup" : "wi:ADR-046-provider-system-systemd", + "prerequisites" : [ + "ADR-046-provider-system-systemd", + "ADR046-systemd-001" ], - "specId": "ADR-046-provider-device-usbip", - "topologicalRank": 13, - "wave": "W6" + "specId" : "ADR-046-provider-system-systemd", + "topologicalRank" : 12, + "wave" : "W6" }, { - "blockers": [], - "destinations": [ - "packages/d2b-provider-device-usbip/{src,tests,integration/README.md}; tests/host-integration/usbip-service.nix; tests/host-integration/hardware/usbip-service.sh" + "blockers" : [], + "destinations" : [ + "`packages/d2b-provider-system-systemd/tests/conformance.rs`, `tests/fault.rs`, `tests/ephemeral.rs`, `tests/sandbox_compile.rs`; `integration/host_scenario.rs`, `integration/guest_scenario.rs`" ], - "entryContracts": [ - "ADR-046-provider-device-usbip" + "entryContracts" : [ + "ADR-046-provider-system-systemd" ], - "exitGate": "ADR046-W6 exit criteria (ADR-046-validation-and-delivery §4): every spec/work item in this wave Merged with clean destinations, all validators green, and the ten-role panel seal recorded", - "id": "ADR046-usbip-007", - "kind": "work-item", - "owner": "ADR046-usbip-004 and ADR046-usbip-005; device-usbip integration owner", - "parallelGroup": "wi:ADR-046-provider-device-usbip", - "prerequisites": [ - "ADR-046-provider-device-usbip", - "ADR046-usbip-004", - "ADR046-usbip-005" + "exitGate" : "ADR046-W6 exit criteria (ADR-046-validation-and-delivery §4): every spec/work item in this wave Merged with clean destinations, all validators green, and the ten-role panel seal recorded", + "id" : "ADR046-systemd-003", + "kind" : "work-item", + "owner" : "`ADR046-systemd-001`; conformance kit / test infrastructure", + "parallelGroup" : "wi:ADR-046-provider-system-systemd", + "prerequisites" : [ + "ADR-046-provider-system-systemd", + "ADR046-systemd-001" ], - "specId": "ADR-046-provider-device-usbip", - "topologicalRank": 14, - "wave": "W6" + "specId" : "ADR-046-provider-system-systemd", + "topologicalRank" : 12, + "wave" : "W6" }, { - "blockers": [], - "destinations": [ - "nixos-modules/components/usbip.nix, nixos-modules/options-zones.nix, nixos-modules/assertions.nix" + "blockers" : [], + "destinations" : [ + "`packages/d2b-telemetry/src/{trace_context.rs,audit_hash.rs,emitter.rs,meter_registry.rs,redaction_guard.rs}`" ], - "entryContracts": [ - "ADR-046-provider-device-usbip" + "entryContracts" : [ + "ADR-046-telemetry-audit-and-support" ], - "exitGate": "ADR046-W6 exit criteria (ADR-046-validation-and-delivery §4): every spec/work item in this wave Merged with clean destinations, all validators green, and the ten-role panel seal recorded", - "id": "ADR046-usbip-008", - "kind": "work-item", - "owner": "ADR046-usbip-003, ADR046-zone-control-024; ADR-046-nix-configuration; Nix integrator", - "parallelGroup": "wi:ADR-046-provider-device-usbip", - "prerequisites": [ - "ADR-046-provider-device-usbip", - "ADR046-usbip-003", - "ADR046-zone-control-024" + "exitGate" : "ADR046-W5 exit criteria (ADR-046-validation-and-delivery §4): every spec/work item in this wave Merged with clean destinations, all validators green, and the ten-role panel seal recorded", + "id" : "ADR046-telem-001", + "kind" : "work-item", + "owner" : "W0/W1a; telemetry crate owner", + "parallelGroup" : "wi:ADR-046-telemetry-audit-and-support", + "prerequisites" : [ + "ADR-046-telemetry-audit-and-support" ], - "specId": "ADR-046-provider-device-usbip", - "topologicalRank": 13, - "wave": "W6" + "specId" : "ADR-046-telemetry-audit-and-support", + "topologicalRank" : 10, + "wave" : "W5" }, { - "blockers": [], - "destinations": [ - "packages/d2bd/src/, nixos-modules/network.nix, packages/d2b-core/src/processes.rs" + "blockers" : [], + "destinations" : [ + "`packages/d2b-resource-store-redb/src/metrics.rs`, `packages/d2b-resource-store-redb/src/tracing.rs`" ], - "entryContracts": [ - "ADR-046-provider-device-usbip" + "entryContracts" : [ + "ADR-046-telemetry-audit-and-support" ], - "exitGate": "ADR046-W6 exit criteria (ADR-046-validation-and-delivery §4): every spec/work item in this wave Merged with clean destinations, all validators green, and the ten-role panel seal recorded", - "id": "ADR046-usbip-009", - "kind": "work-item", - "owner": "ADR046-usbip-004 and ADR046-usbip-008; Provider fully wired and validated; daemon cleanup owner", - "parallelGroup": "wi:ADR-046-provider-device-usbip", - "prerequisites": [ - "ADR-046-provider-device-usbip", - "ADR046-usbip-004", - "ADR046-usbip-008" + "exitGate" : "ADR046-W5 exit criteria (ADR-046-validation-and-delivery §4): every spec/work item in this wave Merged with clean destinations, all validators green, and the ten-role panel seal recorded", + "id" : "ADR046-telem-002", + "kind" : "work-item", + "owner" : "ADR046-telem-001 + ADR046-store-001; store owner", + "parallelGroup" : "wi:ADR-046-telemetry-audit-and-support", + "prerequisites" : [ + "ADR-046-telemetry-audit-and-support", + "ADR046-store-001", + "ADR046-telem-001" ], - "specId": "ADR-046-provider-device-usbip", - "topologicalRank": 14, - "wave": "W6" + "specId" : "ADR-046-telemetry-audit-and-support", + "topologicalRank" : 11, + "wave" : "W5" }, { - "blockers": [], - "destinations": [ - "`packages/d2b-core-controller/src/user_session_authority.rs` (or a core/user-agent per-session agent Process under `Provider/system-systemd`); `AuthorityDescriptor` on the session authority" + "blockers" : [], + "destinations" : [ + "`packages/d2b-resource-api/src/metrics.rs`, `packages/d2b-session/src/metrics.rs`, `packages/d2b-bus/src/metrics.rs`" ], - "entryContracts": [ - "ADR-046-resources-host-guest-process-user" + "entryContracts" : [ + "ADR-046-telemetry-audit-and-support" ], - "exitGate": "ADR046-W5 exit criteria (ADR-046-validation-and-delivery §4): every spec/work item in this wave Merged with clean destinations, all validators green, and the ten-role panel seal recorded", - "id": "ADR046-user-session-001", - "kind": "work-item", - "owner": "ADR046-zone-control-019 (authority index); `Provider/system-systemd` (user manager) + core/user-agent owner", - "parallelGroup": "wi:ADR-046-resources-host-guest-process-user", - "prerequisites": [ - "ADR-046-resources-host-guest-process-user" + "exitGate" : "ADR046-W5 exit criteria (ADR-046-validation-and-delivery §4): every spec/work item in this wave Merged with clean destinations, all validators green, and the ten-role panel seal recorded", + "id" : "ADR046-telem-003", + "kind" : "work-item", + "owner" : "ADR046-telem-001 + ADR046-session-001 + ADR046-session-003; session/bus owner", + "parallelGroup" : "wi:ADR-046-telemetry-audit-and-support", + "prerequisites" : [ + "ADR-046-telemetry-audit-and-support", + "ADR046-session-001", + "ADR046-session-003", + "ADR046-telem-001" ], - "specId": "ADR-046-resources-host-guest-process-user", - "topologicalRank": 10, - "wave": "W5" + "specId" : "ADR-046-telemetry-audit-and-support", + "topologicalRank" : 11, + "wave" : "W5" }, { - "blockers": [], - "destinations": [ - "`d2b-contracts/src/v3/volume_layout.rs` (LayoutEntry, EntryType, all policy enums, AclGrant, Invariant, SensitivityClass); `d2b-contracts/src/v3/volume_spec.rs` (VolumeSpec, ViewSpec, Attachment, QuotaSpec, SourceKind, `SourcePolicyId` opaque newtype); `d2b-contracts/src/v3/effect_port.rs` (`VolumeEffectPort` trait, opaque ID newtypes `VolumeId`/`LayoutEntryId`/`UserId`/`ViewId` each with custom redacted Debug, and `VolumeMountToken` opaque handle with custom redacted Debug)" + "blockers" : [], + "destinations" : [ + "`packages/d2b-core-controller/src/metrics.rs`, `packages/d2b-core-controller/src/tracing.rs`" ], - "entryContracts": [ - "ADR-046-provider-volume-local" + "entryContracts" : [ + "ADR-046-telemetry-audit-and-support" ], - "exitGate": "ADR046-W6 exit criteria (ADR-046-validation-and-delivery §4): every spec/work item in this wave Merged with clean destinations, all validators green, and the ten-role panel seal recorded", - "id": "ADR046-vl-001", - "kind": "work-item", - "owner": "ADR046-primitives-001; v3 contracts owner", - "parallelGroup": "wi:ADR-046-provider-volume-local", - "prerequisites": [ - "ADR-046-provider-volume-local", - "ADR046-primitives-001" + "exitGate" : "ADR046-W5 exit criteria (ADR-046-validation-and-delivery §4): every spec/work item in this wave Merged with clean destinations, all validators green, and the ten-role panel seal recorded", + "id" : "ADR046-telem-004", + "kind" : "work-item", + "owner" : "ADR046-telem-001 + ADR046-core-001; core-controller owner", + "parallelGroup" : "wi:ADR-046-telemetry-audit-and-support", + "prerequisites" : [ + "ADR-046-telemetry-audit-and-support", + "ADR046-core-001", + "ADR046-telem-001" ], - "specId": "ADR-046-provider-volume-local", - "topologicalRank": 11, - "wave": "W6" + "specId" : "ADR-046-telemetry-audit-and-support", + "topologicalRank" : 11, + "wave" : "W5" }, { - "blockers": [], - "destinations": [ - "Full `packages/d2b-provider-volume-local/` scaffold per §Crate layout: `src/`, `tests/`, `integration/`, `README.md`; crate `Cargo.toml` depends only on `d2b-contracts`, `d2b-provider`, `d2b-provider-toolkit`" + "blockers" : [], + "destinations" : [ + "`packages/d2b-provider-supervisor/src/metrics.rs`, `packages/d2b-provider-supervisor/src/tracing.rs`" ], - "entryContracts": [ - "ADR-046-provider-volume-local" + "entryContracts" : [ + "ADR-046-telemetry-audit-and-support" ], - "exitGate": "ADR046-W6 exit criteria (ADR-046-validation-and-delivery §4): every spec/work item in this wave Merged with clean destinations, all validators green, and the ten-role panel seal recorded", - "id": "ADR046-vl-002", - "kind": "work-item", - "owner": "ADR046-vl-001; volume-local Provider owner", - "parallelGroup": "wi:ADR-046-provider-volume-local", - "prerequisites": [ - "ADR-046-provider-volume-local", - "ADR046-vl-001" + "exitGate" : "ADR046-W5 exit criteria (ADR-046-validation-and-delivery §4): every spec/work item in this wave Merged with clean destinations, all validators green, and the ten-role panel seal recorded", + "id" : "ADR046-telem-005", + "kind" : "work-item", + "owner" : "ADR046-telem-001 + ADR046-process-001; Process Provider owner", + "parallelGroup" : "wi:ADR-046-telemetry-audit-and-support", + "prerequisites" : [ + "ADR-046-telemetry-audit-and-support", + "ADR046-process-001", + "ADR046-telem-001" ], - "specId": "ADR-046-provider-volume-local", - "topologicalRank": 12, - "wave": "W6" + "specId" : "ADR-046-telemetry-audit-and-support", + "topologicalRank" : 11, + "wave" : "W5" }, { - "blockers": [], - "destinations": [ - "`src/controller.rs`, `src/layout.rs`, `src/acl.rs`, `src/source.rs`" + "blockers" : [], + "destinations" : [ + "`packages/d2b-provider-observability-otel/src/`, `nixos-modules/components/observability/` (adapted files)" ], - "entryContracts": [ - "ADR-046-provider-volume-local" + "entryContracts" : [ + "ADR-046-telemetry-audit-and-support" ], - "exitGate": "ADR046-W6 exit criteria (ADR-046-validation-and-delivery §4): every spec/work item in this wave Merged with clean destinations, all validators green, and the ten-role panel seal recorded", - "id": "ADR046-vl-003", - "kind": "work-item", - "owner": "ADR046-vl-002; ADR046-reconcile-001; d2b-bus/ComponentSession owner", - "parallelGroup": "wi:ADR-046-provider-volume-local", - "prerequisites": [ - "ADR-046-provider-volume-local", - "ADR046-reconcile-001", - "ADR046-vl-002" + "exitGate" : "ADR046-W5 exit criteria (ADR-046-validation-and-delivery §4): every spec/work item in this wave Merged with clean destinations, all validators green, and the ten-role panel seal recorded", + "id" : "ADR046-telem-006", + "kind" : "work-item", + "owner" : "ADR046-process-001 + ADR046-provider-001; `observability-otel` Provider owner", + "parallelGroup" : "wi:ADR-046-telemetry-audit-and-support", + "prerequisites" : [ + "ADR-046-telemetry-audit-and-support", + "ADR046-process-001", + "ADR046-provider-001" ], - "specId": "ADR-046-provider-volume-local", - "topologicalRank": 13, - "wave": "W6" + "specId" : "ADR-046-telemetry-audit-and-support", + "topologicalRank" : 10, + "wave" : "W5" }, { - "blockers": [], - "destinations": [ - "`src/store_view.rs`; `tests/store_view.rs`; `integration/store_view.rs`" + "blockers" : [], + "destinations" : [ + "`packages/d2b-provider-observability-otel/src/nix/journald.nix` (new Nix fragment)" ], - "entryContracts": [ - "ADR-046-provider-volume-local" + "entryContracts" : [ + "ADR-046-telemetry-audit-and-support" ], - "exitGate": "ADR046-W6 exit criteria (ADR-046-validation-and-delivery §4): every spec/work item in this wave Merged with clean destinations, all validators green, and the ten-role panel seal recorded", - "id": "ADR046-vl-004", - "kind": "work-item", - "owner": "ADR046-vl-003; runtime-cloud-hypervisor Provider owner", - "parallelGroup": "wi:ADR-046-provider-volume-local", - "prerequisites": [ - "ADR-046-provider-volume-local", - "ADR046-vl-003" + "exitGate" : "ADR046-W5 exit criteria (ADR-046-validation-and-delivery §4): every spec/work item in this wave Merged with clean destinations, all validators green, and the ten-role panel seal recorded", + "id" : "ADR046-telem-007", + "kind" : "work-item", + "owner" : "ADR046-telem-006; Nix/observability owner", + "parallelGroup" : "wi:ADR-046-telemetry-audit-and-support", + "prerequisites" : [ + "ADR-046-telemetry-audit-and-support", + "ADR046-telem-006" ], - "specId": "ADR-046-provider-volume-local", - "topologicalRank": 14, - "wave": "W6" + "specId" : "ADR-046-telemetry-audit-and-support", + "topologicalRank" : 11, + "wave" : "W5" }, { - "blockers": [], - "destinations": [ - "`src/swtpm_volume.rs`; `tests/swtpm_volume.rs`; `integration/swtpm_marker.rs`" + "blockers" : [], + "destinations" : [ + "`packages/d2b-contract-tests/tests/policy_telemetry_redaction.rs` (new); updated `policy_observability.rs`; updated `policy_metrics.rs`" ], - "entryContracts": [ - "ADR-046-provider-volume-local" + "entryContracts" : [ + "ADR-046-telemetry-audit-and-support" ], - "exitGate": "ADR046-W6 exit criteria (ADR-046-validation-and-delivery §4): every spec/work item in this wave Merged with clean destinations, all validators green, and the ten-role panel seal recorded", - "id": "ADR046-vl-005", - "kind": "work-item", - "owner": "ADR046-vl-002; device-tpm Provider owner", - "parallelGroup": "wi:ADR-046-provider-volume-local", - "prerequisites": [ - "ADR-046-provider-volume-local", - "ADR046-vl-002" + "exitGate" : "ADR046-W5 exit criteria (ADR-046-validation-and-delivery §4): every spec/work item in this wave Merged with clean destinations, all validators green, and the ten-role panel seal recorded", + "id" : "ADR046-telem-008", + "kind" : "work-item", + "owner" : "ADR046-telem-001; policy/contract-tests owner", + "parallelGroup" : "wi:ADR-046-telemetry-audit-and-support", + "prerequisites" : [ + "ADR-046-telemetry-audit-and-support", + "ADR046-telem-001" ], - "specId": "ADR-046-provider-volume-local", - "topologicalRank": 13, - "wave": "W6" + "specId" : "ADR-046-telemetry-audit-and-support", + "topologicalRank" : 11, + "wave" : "W5" }, { - "blockers": [], - "destinations": [ - "`src/source.rs` (block-image and tmpfs branches); `tests/source.rs`; `integration/block_image.rs`" + "blockers" : [], + "destinations" : [ + "`nixos-modules/resources.nix` (uniform `d2b.zones..resources` schema-aware option; `spec.*` option types generated from `ResourceTypeSchema` for each `type`); `nixos-modules/resources-bundle.nix` (ADR-only: sorted integrity-pinned bundle derivation)" ], - "entryContracts": [ - "ADR-046-provider-volume-local" + "entryContracts" : [ + "ADR-046-telemetry-audit-and-support" ], - "exitGate": "ADR046-W6 exit criteria (ADR-046-validation-and-delivery §4): every spec/work item in this wave Merged with clean destinations, all validators green, and the ten-role panel seal recorded", - "id": "ADR046-vl-006", - "kind": "work-item", - "owner": "ADR046-vl-003", - "parallelGroup": "wi:ADR-046-provider-volume-local", - "prerequisites": [ - "ADR-046-provider-volume-local", - "ADR046-vl-003" + "exitGate" : "ADR046-W5 exit criteria (ADR-046-validation-and-delivery §4): every spec/work item in this wave Merged with clean destinations, all validators green, and the ten-role panel seal recorded", + "id" : "ADR046-telem-009", + "kind" : "work-item", + "owner" : "W0/W1a; Nix integrator (adapts `nixos-modules/options-observability.nix` and `options-realms.nix`)", + "parallelGroup" : "wi:ADR-046-telemetry-audit-and-support", + "prerequisites" : [ + "ADR-046-telemetry-audit-and-support" ], - "specId": "ADR-046-provider-volume-local", - "topologicalRank": 14, - "wave": "W6" + "specId" : "ADR-046-telemetry-audit-and-support", + "topologicalRank" : 10, + "wave" : "W5" }, { - "blockers": [], - "destinations": [ - "`src/{migration,snapshot}.rs`; `tests/{migration_unit,snapshot_unit}.rs`; `integration/{migration,snapshot}.rs`" + "blockers" : [], + "destinations" : [ + "`nixos-modules/resources-bundle.nix` (build-time validation step 4 in the `resources-bundle` derivation); `packages/d2b-core-controller/src/configuration.rs` (runtime activation checks)" ], - "entryContracts": [ - "ADR-046-provider-volume-local" + "entryContracts" : [ + "ADR-046-telemetry-audit-and-support" ], - "exitGate": "ADR046-W6 exit criteria (ADR-046-validation-and-delivery §4): every spec/work item in this wave Merged with clean destinations, all validators green, and the ten-role panel seal recorded", - "id": "ADR046-vl-007", - "kind": "work-item", - "owner": "ADR046-vl-002; ADR046-vl-003; ADR046-pstate-004 through ADR046-pstate-006", - "parallelGroup": "wi:ADR-046-provider-volume-local", - "prerequisites": [ - "ADR-046-provider-volume-local", - "ADR046-pstate-004", - "ADR046-pstate-006", - "ADR046-vl-002", - "ADR046-vl-003" + "exitGate" : "ADR046-W5 exit criteria (ADR-046-validation-and-delivery §4): every spec/work item in this wave Merged with clean destinations, all validators green, and the ten-role panel seal recorded", + "id" : "ADR046-telem-010", + "kind" : "work-item", + "owner" : "ADR046-telem-009 + ADR046-telem-006 + ADR046-store-001; schema/validation owner", + "parallelGroup" : "wi:ADR-046-telemetry-audit-and-support", + "prerequisites" : [ + "ADR-046-telemetry-audit-and-support", + "ADR046-store-001", + "ADR046-telem-006", + "ADR046-telem-009" ], - "specId": "ADR-046-provider-volume-local", - "topologicalRank": 14, - "wave": "W6" + "specId" : "ADR-046-telemetry-audit-and-support", + "topologicalRank" : 11, + "wave" : "W5" }, { - "blockers": [], - "destinations": [ - "`src/relocation.rs`; `tests/relocation_unit.rs`; `integration/relocation.rs`" + "blockers" : [], + "destinations" : [ + "`packages/d2b-core-controller/src/{configuration.rs, ownership.rs}`" ], - "entryContracts": [ - "ADR-046-provider-volume-local" + "entryContracts" : [ + "ADR-046-telemetry-audit-and-support" ], - "exitGate": "ADR046-W6 exit criteria (ADR-046-validation-and-delivery §4): every spec/work item in this wave Merged with clean destinations, all validators green, and the ten-role panel seal recorded", - "id": "ADR046-vl-008", - "kind": "work-item", - "owner": "ADR046-vl-003; ADR046-vl-007", - "parallelGroup": "wi:ADR-046-provider-volume-local", - "prerequisites": [ - "ADR-046-provider-volume-local", - "ADR046-vl-003", - "ADR046-vl-007" + "exitGate" : "ADR046-W5 exit criteria (ADR-046-validation-and-delivery §4): every spec/work item in this wave Merged with clean destinations, all validators green, and the ten-role panel seal recorded", + "id" : "ADR046-telem-011", + "kind" : "work-item", + "owner" : "ADR046-telem-009 + ADR046-telem-010 + ADR046-telem-012 + ADR046-store-001; core-controller owner", + "parallelGroup" : "wi:core-config-hub:w5", + "prerequisites" : [ + "ADR-046-telemetry-audit-and-support", + "ADR046-store-001", + "ADR046-telem-009", + "ADR046-telem-010", + "ADR046-telem-012" ], - "specId": "ADR-046-provider-volume-local", - "topologicalRank": 15, - "wave": "W6" + "specId" : "ADR-046-telemetry-audit-and-support", + "topologicalRank" : 12, + "wave" : "W5" }, { - "blockers": [], - "destinations": [ - "`src/audit.rs`; `src/otel.rs`; `src/error.rs`; `tests/audit_unit.rs`; `integration/audit.rs`" + "blockers" : [], + "destinations" : [ + "`packages/d2b-audit/src/{hash_chain.rs,segment.rs,rate_limit.rs,record_types.rs,sink.rs,export.rs}`" ], - "entryContracts": [ - "ADR-046-provider-volume-local" + "entryContracts" : [ + "ADR-046-telemetry-audit-and-support" ], - "exitGate": "ADR046-W6 exit criteria (ADR-046-validation-and-delivery §4): every spec/work item in this wave Merged with clean destinations, all validators green, and the ten-role panel seal recorded", - "id": "ADR046-vl-009", - "kind": "work-item", - "owner": "ADR046-vl-001; Zone audit stream owner; `observability-otel` Provider owner", - "parallelGroup": "wi:ADR-046-provider-volume-local", - "prerequisites": [ - "ADR-046-provider-volume-local", - "ADR046-vl-001" + "exitGate" : "ADR046-W5 exit criteria (ADR-046-validation-and-delivery §4): every spec/work item in this wave Merged with clean destinations, all validators green, and the ten-role panel seal recorded", + "id" : "ADR046-telem-012", + "kind" : "work-item", + "owner" : "W0/W1a; audit crate owner", + "parallelGroup" : "wi:ADR-046-telemetry-audit-and-support", + "prerequisites" : [ + "ADR-046-telemetry-audit-and-support" ], - "specId": "ADR-046-provider-volume-local", - "topologicalRank": 12, - "wave": "W6" + "specId" : "ADR-046-telemetry-audit-and-support", + "topologicalRank" : 10, + "wave" : "W5" }, { - "blockers": [], - "destinations": [ - "`nixos-modules/zone-resources.nix` (per §ADR046-pstate-010); `root-config.schema.json` in the Provider package" + "blockers" : [], + "destinations" : [ + "`packages/d2b-resource-store-redb/src/audit.rs`, `packages/d2b-core-controller/src/authz_audit.rs`" ], - "entryContracts": [ - "ADR-046-provider-volume-local" + "entryContracts" : [ + "ADR-046-telemetry-audit-and-support" ], - "exitGate": "ADR046-W6 exit criteria (ADR-046-validation-and-delivery §4): every spec/work item in this wave Merged with clean destinations, all validators green, and the ten-role panel seal recorded", - "id": "ADR046-vl-010", - "kind": "work-item", - "owner": "ADR046-vl-001; ADR046-pstate-010; NixOS module owner", - "parallelGroup": "wi:ADR-046-provider-volume-local", - "prerequisites": [ - "ADR-046-provider-volume-local", - "ADR046-pstate-010", - "ADR046-vl-001" + "exitGate" : "ADR046-W5 exit criteria (ADR-046-validation-and-delivery §4): every spec/work item in this wave Merged with clean destinations, all validators green, and the ten-role panel seal recorded", + "id" : "ADR046-telem-013", + "kind" : "work-item", + "owner" : "ADR046-telem-012 + ADR046-store-001; store/authz owner", + "parallelGroup" : "wi:ADR-046-telemetry-audit-and-support", + "prerequisites" : [ + "ADR-046-telemetry-audit-and-support", + "ADR046-store-001", + "ADR046-telem-012" ], - "specId": "ADR-046-provider-volume-local", - "topologicalRank": 12, - "wave": "W6" + "specId" : "ADR-046-telemetry-audit-and-support", + "topologicalRank" : 11, + "wave" : "W5" }, { - "blockers": [], - "destinations": [ - "`packages/xtask/src/provider_crate_policy.rs`; `tests/unit/gates/provider-crate-layout-check.sh`" + "blockers" : [], + "destinations" : [ + "`packages/d2b-session/src/audit.rs`, `packages/d2b-bus/src/audit.rs`" ], - "entryContracts": [ - "ADR-046-provider-volume-local" + "entryContracts" : [ + "ADR-046-telemetry-audit-and-support" ], - "exitGate": "ADR046-W6 exit criteria (ADR-046-validation-and-delivery §4): every spec/work item in this wave Merged with clean destinations, all validators green, and the ten-role panel seal recorded", - "id": "ADR046-vl-011", - "kind": "work-item", - "owner": "ADR046-vl-002; ADR046-pstate-011; workspace policy owner", - "parallelGroup": "wi:ADR-046-provider-volume-local", - "prerequisites": [ - "ADR-046-provider-volume-local", - "ADR046-pstate-011", - "ADR046-vl-002" + "exitGate" : "ADR046-W5 exit criteria (ADR-046-validation-and-delivery §4): every spec/work item in this wave Merged with clean destinations, all validators green, and the ten-role panel seal recorded", + "id" : "ADR046-telem-014", + "kind" : "work-item", + "owner" : "ADR046-telem-012 + ADR046-session-001 + ADR046-session-003; session/bus owner", + "parallelGroup" : "wi:ADR-046-telemetry-audit-and-support", + "prerequisites" : [ + "ADR-046-telemetry-audit-and-support", + "ADR046-session-001", + "ADR046-session-003", + "ADR046-telem-012" ], - "specId": "ADR-046-provider-volume-local", - "topologicalRank": 13, - "wave": "W6" + "specId" : "ADR-046-telemetry-audit-and-support", + "topologicalRank" : 11, + "wave" : "W5" }, { - "blockers": [], - "destinations": [ - "`packages/d2b-host/src/volume_effect_adapter.rs` (or the equivalent host-runtime crate designated by the Zone broker owner); implements the `VolumeEffectPort` trait defined in `d2b-contracts`" + "blockers" : [], + "destinations" : [ + "`packages/d2b/src/zone_audit.rs` (new `d2b zone audit export` subcommand); `packages/d2b/tests/zone_audit_contract.rs`" ], - "entryContracts": [ - "ADR-046-provider-volume-local" + "entryContracts" : [ + "ADR-046-telemetry-audit-and-support" ], - "exitGate": "ADR046-W6 exit criteria (ADR-046-validation-and-delivery §4): every spec/work item in this wave Merged with clean destinations, all validators green, and the ten-role panel seal recorded", - "id": "ADR046-vl-012", - "kind": "work-item", - "owner": "ADR046-vl-001; ADR046-vl-002; Zone broker/core owner", - "parallelGroup": "wi:ADR-046-provider-volume-local", - "prerequisites": [ - "ADR-046-provider-volume-local", - "ADR046-vl-001", - "ADR046-vl-002" + "exitGate" : "ADR046-W5 exit criteria (ADR-046-validation-and-delivery §4): every spec/work item in this wave Merged with clean destinations, all validators green, and the ten-role panel seal recorded", + "id" : "ADR046-telem-015", + "kind" : "work-item", + "owner" : "ADR046-telem-012; CLI owner", + "parallelGroup" : "wi:ADR-046-telemetry-audit-and-support", + "prerequisites" : [ + "ADR-046-telemetry-audit-and-support", + "ADR046-telem-012" ], - "specId": "ADR-046-provider-volume-local", - "topologicalRank": 13, - "wave": "W6" + "specId" : "ADR-046-telemetry-audit-and-support", + "topologicalRank" : 11, + "wave" : "W5" }, { - "blockers": [], - "destinations": [ - "Zone core ProviderDeployment controller-start path (outside `d2b-provider-volume-local`)" + "blockers" : [], + "destinations" : [ + "`packages/d2b/src/zone_doctor.rs`, `packages/d2b/tests/zone_doctor_contract.rs`" ], - "entryContracts": [ - "ADR-046-provider-volume-local" + "entryContracts" : [ + "ADR-046-telemetry-audit-and-support" ], - "exitGate": "ADR046-W6 exit criteria (ADR-046-validation-and-delivery §4): every spec/work item in this wave Merged with clean destinations, all validators green, and the ten-role panel seal recorded", - "id": "ADR046-vl-013", - "kind": "work-item", - "owner": "ADR046-vl-001; ADR046-vl-012; Zone broker/core owner", - "parallelGroup": "wi:ADR-046-provider-volume-local", - "prerequisites": [ - "ADR-046-provider-volume-local", - "ADR046-vl-001", - "ADR046-vl-012" + "exitGate" : "ADR046-W5 exit criteria (ADR-046-validation-and-delivery §4): every spec/work item in this wave Merged with clean destinations, all validators green, and the ten-role panel seal recorded", + "id" : "ADR046-telem-016", + "kind" : "work-item", + "owner" : "ADR046-core-001 + ADR046-telem-012; CLI/doctor owner", + "parallelGroup" : "wi:ADR-046-telemetry-audit-and-support", + "prerequisites" : [ + "ADR-046-telemetry-audit-and-support", + "ADR046-core-001", + "ADR046-telem-012" ], - "specId": "ADR-046-provider-volume-local", - "topologicalRank": 14, - "wave": "W6" + "specId" : "ADR-046-telemetry-audit-and-support", + "topologicalRank" : 11, + "wave" : "W5" }, { - "blockers": [], - "destinations": [ - "`packages/d2b-contracts/src/v3/volume.rs`, `volume_layout.rs`, `volume_attachment.rs`" + "blockers" : [], + "destinations" : [ + "`packages/d2b/src/zone_support_bundle.rs`, `packages/d2b/tests/zone_support_bundle_contract.rs`" ], - "entryContracts": [ - "ADR-046-resources-volume" + "entryContracts" : [ + "ADR-046-telemetry-audit-and-support" ], - "exitGate": "ADR046-W5 exit criteria (ADR-046-validation-and-delivery §4): every spec/work item in this wave Merged with clean destinations, all validators green, and the ten-role panel seal recorded", - "id": "ADR046-volume-001", - "kind": "work-item", - "owner": "W0 shared contract root; `d2b-contracts`", - "parallelGroup": "wi:ADR-046-resources-volume", - "prerequisites": [ - "ADR-046-resources-volume" + "exitGate" : "ADR046-W5 exit criteria (ADR-046-validation-and-delivery §4): every spec/work item in this wave Merged with clean destinations, all validators green, and the ten-role panel seal recorded", + "id" : "ADR046-telem-017", + "kind" : "work-item", + "owner" : "ADR046-telem-016; CLI/doctor owner", + "parallelGroup" : "wi:ADR-046-telemetry-audit-and-support", + "prerequisites" : [ + "ADR-046-telemetry-audit-and-support", + "ADR046-telem-016" ], - "specId": "ADR-046-resources-volume", - "topologicalRank": 10, - "wave": "W5" + "specId" : "ADR-046-telemetry-audit-and-support", + "topologicalRank" : 12, + "wave" : "W5" }, { - "blockers": [], - "destinations": [ - "`packages/d2b-provider-volume-local/src/` (layout engine, store_view.rs, swtpm_volume.rs, broker op adapters); `packages/d2b-provider-volume-local/tests/` (hermetic layout/store-view/swtpm tests); `packages/d2b-provider-volume-local/integration/` (container Host-path and store-view FS fixtures); `packages/d2b-provider-volume-local/README.md`" + "blockers" : [], + "destinations" : [ + "`packages/d2b-provider-system-core/src/{host_reconciler.rs,host_status.rs,host_process_audit.rs}`; adapted `nixos-modules/unsafe-local-workloads-json.nix`; `packages/d2b-provider-system-core/tests/host_posture_contract.rs`" ], - "entryContracts": [ - "ADR-046-resources-volume" + "entryContracts" : [ + "ADR-046-telemetry-audit-and-support" ], - "exitGate": "ADR046-W5 exit criteria (ADR-046-validation-and-delivery §4): every spec/work item in this wave Merged with clean destinations, all validators green, and the ten-role panel seal recorded", - "id": "ADR046-volume-002", - "kind": "work-item", - "owner": "ADR046-volume-001; volume-local Provider owner", - "parallelGroup": "wi:ADR-046-resources-volume", - "prerequisites": [ - "ADR-046-resources-volume", - "ADR046-volume-001" + "exitGate" : "ADR046-W5 exit criteria (ADR-046-validation-and-delivery §4): every spec/work item in this wave Merged with clean destinations, all validators green, and the ten-role panel seal recorded", + "id" : "ADR046-telem-018", + "kind" : "work-item", + "owner" : "ADR046-telem-012 + ADR046-core-001; `Provider/system-core` owner", + "parallelGroup" : "wi:ADR-046-telemetry-audit-and-support", + "prerequisites" : [ + "ADR-046-telemetry-audit-and-support", + "ADR046-core-001", + "ADR046-telem-012" ], - "specId": "ADR-046-resources-volume", - "topologicalRank": 11, - "wave": "W5" + "specId" : "ADR-046-telemetry-audit-and-support", + "topologicalRank" : 11, + "wave" : "W5" }, { - "blockers": [], - "destinations": [ - "`packages/d2b-provider-volume-virtiofs/src/` (controller, virtiofsd_argv.rs); `packages/d2b-provider-volume-virtiofs/tests/` (hermetic argv/lifecycle/ADR-0021 tests); `packages/d2b-provider-volume-virtiofs/integration/` (virtiofsd launch and guest-mount fixtures); `packages/d2b-provider-volume-virtiofs/README.md`" + "blockers" : [], + "destinations" : [ + "`packages/d2b-session/` copied verbatim; v3 contract extension renames endpoint purpose values while preserving backward wire compatibility during transition." ], - "entryContracts": [ - "ADR-046-resources-volume" + "entryContracts" : [ + "ADR-046-telemetry-audit-and-support" ], - "exitGate": "ADR046-W5 exit criteria (ADR-046-validation-and-delivery §4): every spec/work item in this wave Merged with clean destinations, all validators green, and the ten-role panel seal recorded", - "id": "ADR046-volume-003", - "kind": "work-item", - "owner": "ADR046-volume-001; volume-virtiofs Provider owner", - "parallelGroup": "wi:ADR-046-resources-volume", - "prerequisites": [ - "ADR-046-resources-volume", - "ADR046-volume-001" + "exitGate" : "ADR046-W5 exit criteria (ADR-046-validation-and-delivery §4): every spec/work item in this wave Merged with clean destinations, all validators green, and the ten-role panel seal recorded", + "id" : "ADR046-telem-019", + "kind" : "work-item", + "owner" : "ADR046-telem-001 (d2b-telemetry must exist first for MetricsSink injection); session owner", + "parallelGroup" : "wi:ADR-046-telemetry-audit-and-support", + "prerequisites" : [ + "ADR-046-telemetry-audit-and-support", + "ADR046-telem-001" ], - "specId": "ADR-046-resources-volume", - "topologicalRank": 11, - "wave": "W5" + "specId" : "ADR-046-telemetry-audit-and-support", + "topologicalRank" : 11, + "wave" : "W5" }, { - "blockers": [], - "destinations": [ - "`nixos-modules/resources-volume.nix`, `nixos-modules/options-volumes.nix`" + "blockers" : [], + "destinations" : [ + "`packages/d2b-session-unix/` copied verbatim." ], - "entryContracts": [ - "ADR-046-resources-volume" + "entryContracts" : [ + "ADR-046-telemetry-audit-and-support" ], - "exitGate": "ADR046-W5 exit criteria (ADR-046-validation-and-delivery §4): every spec/work item in this wave Merged with clean destinations, all validators green, and the ten-role panel seal recorded", - "id": "ADR046-volume-004", - "kind": "work-item", - "owner": "ADR046-volume-001; Nix integrator", - "parallelGroup": "wi:ADR-046-resources-volume", - "prerequisites": [ - "ADR-046-resources-volume", - "ADR046-volume-001" + "exitGate" : "ADR046-W5 exit criteria (ADR-046-validation-and-delivery §4): every spec/work item in this wave Merged with clean destinations, all validators green, and the ten-role panel seal recorded", + "id" : "ADR046-telem-020", + "kind" : "work-item", + "owner" : "ADR046-telem-019; Unix transport owner", + "parallelGroup" : "wi:ADR-046-telemetry-audit-and-support", + "prerequisites" : [ + "ADR-046-telemetry-audit-and-support", + "ADR046-telem-019" ], - "specId": "ADR-046-resources-volume", - "topologicalRank": 11, - "wave": "W5" + "specId" : "ADR-046-telemetry-audit-and-support", + "topologicalRank" : 12, + "wave" : "W5" }, { - "blockers": [], - "destinations": [ - "`packages/d2b-provider-volume-local/src/` (block-image, quota, snapshots, tmpfs, ACL reconciliation); `packages/d2b-provider-volume-local/tests/` (hermetic quota/tmpfs/ACL/block-image/snapshot tests); `packages/d2b-provider-volume-local/integration/` (block-image virtio-blk, FS-without-quota fixture, tmpfs memory-budget); `packages/d2b-provider-volume-virtiofs/src/` (single-writer enforcement, shared-write, private socket path contract); `packages/d2b-provider-volume-virtiofs/tests/` (single-writer rejection, shared-write capability gate, socket-path invariant); `packages/d2b-provider-volume-virtiofs/integration/` (shared-write cross-Guest fixture)" + "blockers" : [], + "destinations" : [ + "`packages/d2b-client/` copied; DaemonClient/GuestClient adapted to v3 service packages; `MetadataInput::with_trace` drives `TraceContext` propagation." ], - "entryContracts": [ - "ADR-046-resources-volume" + "entryContracts" : [ + "ADR-046-telemetry-audit-and-support" ], - "exitGate": "ADR046-W5 exit criteria (ADR-046-validation-and-delivery §4): every spec/work item in this wave Merged with clean destinations, all validators green, and the ten-role panel seal recorded", - "id": "ADR046-volume-005", - "kind": "work-item", - "owner": "ADR046-volume-002, ADR046-volume-003; respective Provider owners", - "parallelGroup": "wi:ADR-046-resources-volume", - "prerequisites": [ - "ADR-046-resources-volume", - "ADR046-volume-002", - "ADR046-volume-003" + "exitGate" : "ADR046-W5 exit criteria (ADR-046-validation-and-delivery §4): every spec/work item in this wave Merged with clean destinations, all validators green, and the ten-role panel seal recorded", + "id" : "ADR046-telem-021", + "kind" : "work-item", + "owner" : "ADR046-telem-019; client owner", + "parallelGroup" : "wi:ADR-046-telemetry-audit-and-support", + "prerequisites" : [ + "ADR-046-telemetry-audit-and-support", + "ADR046-telem-019" ], - "specId": "ADR-046-resources-volume", - "topologicalRank": 12, - "wave": "W5" + "specId" : "ADR-046-telemetry-audit-and-support", + "topologicalRank" : 12, + "wave" : "W5" }, { - "blockers": [], - "destinations": [ - "`nixos-modules/resources-volume.nix` (Nix eval-time schema validation, canonical JSON emission, bundle digest); `packages/d2b-core-controller/src/configuration.rs` (config-publication handler cleanup logic); `packages/d2b-contracts/src/v3/zone_bundle.rs` (bundle index schema)" + "blockers" : [], + "destinations" : [ + "`packages/d2b-provider/` and `packages/d2b-provider-toolkit/` copied with v3 session admission and bus routing adaptations." ], - "entryContracts": [ - "ADR-046-resources-volume" + "entryContracts" : [ + "ADR-046-telemetry-audit-and-support" ], - "exitGate": "ADR046-W5 exit criteria (ADR-046-validation-and-delivery §4): every spec/work item in this wave Merged with clean destinations, all validators green, and the ten-role panel seal recorded", - "id": "ADR046-volume-006", - "kind": "work-item", - "owner": "ADR046-volume-001, ADR046-volume-004; Nix integrator and core-controller config-publication owner", - "parallelGroup": "wi:ADR-046-resources-volume", - "prerequisites": [ - "ADR-046-resources-volume", - "ADR046-volume-001", - "ADR046-volume-004" + "exitGate" : "ADR046-W5 exit criteria (ADR-046-validation-and-delivery §4): every spec/work item in this wave Merged with clean destinations, all validators green, and the ten-role panel seal recorded", + "id" : "ADR046-telem-022", + "kind" : "work-item", + "owner" : "ADR046-telem-019 + ADR046-telem-021; Provider owner", + "parallelGroup" : "wi:ADR-046-telemetry-audit-and-support", + "prerequisites" : [ + "ADR-046-telemetry-audit-and-support", + "ADR046-telem-019", + "ADR046-telem-021" ], - "specId": "ADR-046-resources-volume", - "topologicalRank": 12, - "wave": "W5" + "specId" : "ADR-046-telemetry-audit-and-support", + "topologicalRank" : 13, + "wave" : "W5" }, { - "blockers": [], - "destinations": [ - "`packages/d2b-provider-transport-vsock/src/effect_port.rs`; test fake in `tests/effect_port_mock.rs`; redaction checks in `tests/redaction.rs`." + "blockers" : [], + "destinations" : [ + "`packages/d2b-provider-observability-otel/src/agent.rs` adapted; ComponentSessionDriver mock becomes shared Provider session fixture." ], - "entryContracts": [ - "ADR-046-provider-transport-vsock" + "entryContracts" : [ + "ADR-046-telemetry-audit-and-support" ], - "exitGate": "ADR046-W6 exit criteria (ADR-046-validation-and-delivery §4): every spec/work item in this wave Merged with clean destinations, all validators green, and the ten-role panel seal recorded", - "id": "ADR046-vsock-001", - "kind": "work-item", - "owner": "Title: Implement `VsockEffectPort` trait and `OpaqueEndpointId`/`OpaqueBindingId` newtypes; Phase 1; Priority P0; Depends on ADR046-bus-001 (OwnedTransport in d2b-session); Owner crate `d2b-provider-transport-vsock`.", - "parallelGroup": "wi:ADR-046-provider-transport-vsock", - "prerequisites": [ - "ADR-046-provider-transport-vsock", - "ADR046-bus-001" + "exitGate" : "ADR046-W5 exit criteria (ADR-046-validation-and-delivery §4): every spec/work item in this wave Merged with clean destinations, all validators green, and the ten-role panel seal recorded", + "id" : "ADR046-telem-023", + "kind" : "work-item", + "owner" : "ADR046-telem-022 + ADR046-telem-014; Provider agent / observability-otel owner", + "parallelGroup" : "wi:ADR-046-telemetry-audit-and-support", + "prerequisites" : [ + "ADR-046-telemetry-audit-and-support", + "ADR046-telem-014", + "ADR046-telem-022" ], - "specId": "ADR-046-provider-transport-vsock", - "topologicalRank": 11, - "wave": "W6" + "specId" : "ADR-046-telemetry-audit-and-support", + "topologicalRank" : 14, + "wave" : "W5" }, { - "blockers": [], - "destinations": [ - "`packages/d2b-provider-transport-vsock/src/framing.rs` and `src/bridge.rs`; tests in `packages/d2b-provider-transport-vsock/tests/framing.rs`." + "blockers" : [], + "destinations" : [ + "`packages/d2b-bus/src/routing.rs` adapted from `service_v2.rs`; RemoteNodeErrorKind stable code pattern becomes v3 BusErrorKind." ], - "entryContracts": [ - "ADR-046-provider-transport-vsock" + "entryContracts" : [ + "ADR-046-telemetry-audit-and-support" ], - "exitGate": "ADR046-W6 exit criteria (ADR-046-validation-and-delivery §4): every spec/work item in this wave Merged with clean destinations, all validators green, and the ten-role panel seal recorded", - "id": "ADR046-vsock-002", - "kind": "work-item", - "owner": "Title: Implement framing utilities and bridge task in Provider crate; Phase 1; Priority P0; Depends on ADR046-vsock-001; Owner crate `d2b-provider-transport-vsock`.", - "parallelGroup": "wi:ADR-046-provider-transport-vsock", - "prerequisites": [ - "ADR-046-provider-transport-vsock", - "ADR046-vsock-001" + "exitGate" : "ADR046-W5 exit criteria (ADR-046-validation-and-delivery §4): every spec/work item in this wave Merged with clean destinations, all validators green, and the ten-role panel seal recorded", + "id" : "ADR046-telem-024", + "kind" : "work-item", + "owner" : "ADR046-telem-019 + ADR046-session-003; bus routing owner", + "parallelGroup" : "wi:ADR-046-telemetry-audit-and-support", + "prerequisites" : [ + "ADR-046-telemetry-audit-and-support", + "ADR046-session-003", + "ADR046-telem-019" ], - "specId": "ADR-046-provider-transport-vsock", - "topologicalRank": 12, - "wave": "W6" + "specId" : "ADR-046-telemetry-audit-and-support", + "topologicalRank" : 12, + "wave" : "W5" }, { - "blockers": [], - "destinations": [ - "`packages/d2b-provider-transport-vsock/src/service.rs`; tests `tests/open_close.rs`, `tests/observe.rs`, and conformance kit." + "blockers" : [], + "destinations" : [ + "`packages/d2b-bus/src/service_router.rs` and `packages/d2b-core-controller/src/provider_effects.rs`." ], - "entryContracts": [ - "ADR-046-provider-transport-vsock" + "entryContracts" : [ + "ADR-046-telemetry-audit-and-support" ], - "exitGate": "ADR046-W6 exit criteria (ADR-046-validation-and-delivery §4): every spec/work item in this wave Merged with clean destinations, all validators green, and the ten-role panel seal recorded", - "id": "ADR046-vsock-003", - "kind": "work-item", - "owner": "Title: Implement `VsockTransportService` (OpenTransport/CloseTransport/ObserveTransport); Phase 1; Priority P0; Depends on ADR046-vsock-002 and ADR046-bus-001; Owner crate `d2b-provider-transport-vsock`.", - "parallelGroup": "wi:ADR-046-provider-transport-vsock", - "prerequisites": [ - "ADR-046-provider-transport-vsock", - "ADR046-bus-001", - "ADR046-vsock-002" + "exitGate" : "ADR046-W5 exit criteria (ADR-046-validation-and-delivery §4): every spec/work item in this wave Merged with clean destinations, all validators green, and the ten-role panel seal recorded", + "id" : "ADR046-telem-025", + "kind" : "work-item", + "owner" : "ADR046-telem-022 + ADR046-telem-024; core-controller routing owner", + "parallelGroup" : "wi:ADR-046-telemetry-audit-and-support", + "prerequisites" : [ + "ADR-046-telemetry-audit-and-support", + "ADR046-telem-022", + "ADR046-telem-024" ], - "specId": "ADR-046-provider-transport-vsock", - "topologicalRank": 13, - "wave": "W6" + "specId" : "ADR-046-telemetry-audit-and-support", + "topologicalRank" : 14, + "wave" : "W5" }, { - "blockers": [], - "destinations": [ - "`d2b-core-controller` Zone runtime `LiveVsockEffectPort`; Provider receives it by dependency injection at startup." + "blockers" : [], + "destinations" : [ + "`packages/d2b-contract-tests/tests/component_session_v2_vectors.rs` and `tests/noise_vectors.rs` copied verbatim." ], - "entryContracts": [ - "ADR-046-provider-transport-vsock" + "entryContracts" : [ + "ADR-046-telemetry-audit-and-support" ], - "exitGate": "ADR046-W6 exit criteria (ADR-046-validation-and-delivery §4): every spec/work item in this wave Merged with clean destinations, all validators green, and the ten-role panel seal recorded", - "id": "ADR046-vsock-004", - "kind": "work-item", - "owner": "Title: Implement `LiveVsockEffectPort` in Zone runtime; Phase 2; Priority P0; Depends on ADR046-vsock-001 and the Zone allocator (`ADR-046-resources-zone-control`); Owner crate `d2b-core-controller`.", - "parallelGroup": "wi:ADR-046-provider-transport-vsock", - "prerequisites": [ - "ADR-046-provider-transport-vsock", - "ADR046-vsock-001" + "exitGate" : "ADR046-W5 exit criteria (ADR-046-validation-and-delivery §4): every spec/work item in this wave Merged with clean destinations, all validators green, and the ten-role panel seal recorded", + "id" : "ADR046-telem-026", + "kind" : "work-item", + "owner" : "ADR046-telem-019; contract-tests owner", + "parallelGroup" : "wi:ADR-046-telemetry-audit-and-support", + "prerequisites" : [ + "ADR-046-telemetry-audit-and-support", + "ADR046-telem-019" ], - "specId": "ADR-046-provider-transport-vsock", - "topologicalRank": 12, - "wave": "W6" + "specId" : "ADR-046-telemetry-audit-and-support", + "topologicalRank" : 12, + "wave" : "W5" }, { - "blockers": [], - "destinations": [ - "ProviderDeployment Volume creation/deletion path plus `packages/d2b-provider-transport-vsock/tests/state_volume.rs`." + "blockers" : [], + "destinations" : [ + "`packages/d2b-telemetry/src/session_metrics_sink.rs`." ], - "entryContracts": [ - "ADR-046-provider-transport-vsock" + "entryContracts" : [ + "ADR-046-telemetry-audit-and-support" ], - "exitGate": "ADR046-W6 exit criteria (ADR-046-validation-and-delivery §4): every spec/work item in this wave Merged with clean destinations, all validators green, and the ten-role panel seal recorded", - "id": "ADR046-vsock-005", - "kind": "work-item", - "owner": "Title: Core ProviderDeployment creates/deletes service component state Volume; Phase 1; Priority P0; Depends on the volume-local Provider (`ADR-046-provider-volume-local`); Owner crate `d2b-provider-transport-vsock`.", - "parallelGroup": "wi:ADR-046-provider-transport-vsock", - "prerequisites": [ - "ADR-046-provider-transport-vsock" + "exitGate" : "ADR046-W5 exit criteria (ADR-046-validation-and-delivery §4): every spec/work item in this wave Merged with clean destinations, all validators green, and the ten-role panel seal recorded", + "id" : "ADR046-telem-027", + "kind" : "work-item", + "owner" : "ADR046-telem-019 + ADR046-telem-001 + ADR046-telem-003; telemetry/session owner", + "parallelGroup" : "wi:ADR-046-telemetry-audit-and-support", + "prerequisites" : [ + "ADR-046-telemetry-audit-and-support", + "ADR046-telem-001", + "ADR046-telem-003", + "ADR046-telem-019" ], - "specId": "ADR-046-provider-transport-vsock", - "topologicalRank": 11, - "wave": "W6" + "specId" : "ADR-046-telemetry-audit-and-support", + "topologicalRank" : 12, + "wave" : "W5" }, { - "blockers": [], - "destinations": [ - "`packages/d2b-provider-transport-vsock/integration/host_guest.rs` and `integration/no_fd_transfer.rs`." + "blockers" : [], + "destinations" : [ + "`packages/d2b-provider-transport-azure-relay/src/relay_transport.rs`" ], - "entryContracts": [ - "ADR-046-provider-transport-vsock" + "entryContracts" : [ + "ADR-046-provider-transport-azure-relay" ], - "exitGate": "ADR046-W6 exit criteria (ADR-046-validation-and-delivery §4): every spec/work item in this wave Merged with clean destinations, all validators green, and the ten-role panel seal recorded", - "id": "ADR046-vsock-006", - "kind": "work-item", - "owner": "Title: Integration test: real vsock socketpair + full ZoneLink open/close; Phase 2; Priority P1; Depends on ADR046-vsock-003 and ADR046-vsock-004; Owner crate `d2b-provider-transport-vsock`.", - "parallelGroup": "wi:ADR-046-provider-transport-vsock", - "prerequisites": [ - "ADR-046-provider-transport-vsock", - "ADR046-vsock-003", - "ADR046-vsock-004" + "exitGate" : "ADR046-W6 exit criteria (ADR-046-validation-and-delivery §4): every spec/work item in this wave Merged with clean destinations, all validators green, and the ten-role panel seal recorded", + "id" : "ADR046-transport-relay-001", + "kind" : "work-item", + "owner" : "W0 shared contract root; ComponentSession transport adapter owner", + "parallelGroup" : "wi:ADR-046-provider-transport-azure-relay", + "prerequisites" : [ + "ADR-046-provider-transport-azure-relay" ], - "specId": "ADR-046-provider-transport-vsock", - "topologicalRank": 14, - "wave": "W6" + "specId" : "ADR-046-provider-transport-azure-relay", + "topologicalRank" : 11, + "wave" : "W6" }, { - "blockers": [], - "destinations": [ - "Remove legacy paths from `d2b-host` and `d2bd`; replacement lives in `observability-otel` Provider native vsock relay and Guest resource lifecycle/bootstrap." + "blockers" : [], + "destinations" : [ + "`packages/d2b-provider-transport-azure-relay/src/credential_client.rs`" ], - "entryContracts": [ - "ADR-046-provider-transport-vsock" + "entryContracts" : [ + "ADR-046-provider-transport-azure-relay" ], - "exitGate": "ADR046-W6 exit criteria (ADR-046-validation-and-delivery §4): every spec/work item in this wave Merged with clean destinations, all validators green, and the ten-role panel seal recorded", - "id": "ADR046-vsock-007", - "kind": "work-item", - "owner": "Title: Delete legacy socat OTLP relay and CONNECT-proxy guest-control vsock; Phase 3; Priority P2; Depends on the observability-otel Provider (`ADR-046-provider-observability-otel`) and the Guest resource lifecycle (`ADR-046-resources-host-guest-process-user`); Owner crates `d2b-host`, `d2bd`.", - "parallelGroup": "wi:ADR-046-provider-transport-vsock", - "prerequisites": [ - "ADR-046-provider-transport-vsock" + "exitGate" : "ADR046-W6 exit criteria (ADR-046-validation-and-delivery §4): every spec/work item in this wave Merged with clean destinations, all validators green, and the ten-role panel seal recorded", + "id" : "ADR046-transport-relay-002", + "kind" : "work-item", + "owner" : "ADR046-transport-relay-001; Credential KK session; ComponentSession/d2b-bus owner", + "parallelGroup" : "wi:ADR-046-provider-transport-azure-relay", + "prerequisites" : [ + "ADR-046-provider-transport-azure-relay", + "ADR046-transport-relay-001" ], - "specId": "ADR-046-provider-transport-vsock", - "topologicalRank": 11, - "wave": "W6" + "specId" : "ADR-046-provider-transport-azure-relay", + "topologicalRank" : 12, + "wave" : "W6" }, { - "blockers": [], - "destinations": [ - "`packages/d2b-provider-volume-virtiofs/src/virtiofsd_argv.rs`; `packages/d2b-provider-volume-virtiofs/tests/argv_golden.rs`" + "blockers" : [], + "destinations" : [ + "`packages/d2b-provider-transport-azure-relay/src/reconnect.rs`" ], - "entryContracts": [ - "ADR-046-provider-volume-virtiofs" + "entryContracts" : [ + "ADR-046-provider-transport-azure-relay" ], - "exitGate": "ADR046-W6 exit criteria (ADR-046-validation-and-delivery §4): every spec/work item in this wave Merged with clean destinations, all validators green, and the ten-role panel seal recorded", - "id": "ADR046-vvfs-001", - "kind": "work-item", - "owner": "ADR046-volume-001 (Volume contract types); ADR046-vvfs-export-001 (Export type); W1; volume-virtiofs Provider owner", - "parallelGroup": "wi:ADR-046-provider-volume-virtiofs", - "prerequisites": [ - "ADR-046-provider-volume-virtiofs", - "ADR046-volume-001" + "exitGate" : "ADR046-W6 exit criteria (ADR-046-validation-and-delivery §4): every spec/work item in this wave Merged with clean destinations, all validators green, and the ten-role panel seal recorded", + "id" : "ADR046-transport-relay-003", + "kind" : "work-item", + "owner" : "ADR046-transport-relay-001; reconnect contract; ZoneLink handler", + "parallelGroup" : "wi:ADR-046-provider-transport-azure-relay", + "prerequisites" : [ + "ADR-046-provider-transport-azure-relay", + "ADR046-transport-relay-001" ], - "specId": "ADR-046-provider-volume-virtiofs", - "topologicalRank": 11, - "wave": "W6" + "specId" : "ADR-046-provider-transport-azure-relay", + "topologicalRank" : 12, + "wave" : "W6" }, { - "blockers": [], - "destinations": [ - "`packages/d2b-provider-volume-virtiofs/src/user_ns.rs` (conformance kit); `packages/d2b-provider-volume-virtiofs/tests/adr021_invariant.rs`" + "blockers" : [], + "destinations" : [ + "`packages/d2b-provider-transport-azure-relay/src/transport_settings.rs`; `docs/reference/schemas/v3/providers/transport-azure-relay.transport-settings.json`" ], - "entryContracts": [ - "ADR-046-provider-volume-virtiofs" + "entryContracts" : [ + "ADR-046-provider-transport-azure-relay" ], - "exitGate": "ADR046-W6 exit criteria (ADR-046-validation-and-delivery §4): every spec/work item in this wave Merged with clean destinations, all validators green, and the ten-role panel seal recorded", - "id": "ADR046-vvfs-002", - "kind": "work-item", - "owner": "ADR046-vvfs-001; ADR046-volume-001; W1; broker/spawn owner", - "parallelGroup": "wi:ADR-046-provider-volume-virtiofs", - "prerequisites": [ - "ADR-046-provider-volume-virtiofs", - "ADR046-volume-001", - "ADR046-vvfs-001" + "exitGate" : "ADR046-W6 exit criteria (ADR-046-validation-and-delivery §4): every spec/work item in this wave Merged with clean destinations, all validators green, and the ten-role panel seal recorded", + "id" : "ADR046-transport-relay-004", + "kind" : "work-item", + "owner" : "ADR046-transport-relay-001; transport settings schema; Nix configuration owner", + "parallelGroup" : "wi:ADR-046-provider-transport-azure-relay", + "prerequisites" : [ + "ADR-046-provider-transport-azure-relay", + "ADR046-transport-relay-001" ], - "specId": "ADR-046-provider-volume-virtiofs", - "topologicalRank": 12, - "wave": "W6" + "specId" : "ADR-046-provider-transport-azure-relay", + "topologicalRank" : 12, + "wave" : "W6" }, { - "blockers": [], - "destinations": [ - "`packages/d2b-provider-volume-virtiofs/src/controller.rs`; `packages/d2b-provider-volume-virtiofs/src/export.rs`" + "blockers" : [], + "destinations" : [ + "`packages/d2b-provider-transport-azure-relay/src/backpressure.rs`" ], - "entryContracts": [ - "ADR-046-provider-volume-virtiofs" + "entryContracts" : [ + "ADR-046-provider-transport-azure-relay" ], - "exitGate": "ADR046-W6 exit criteria (ADR-046-validation-and-delivery §4): every spec/work item in this wave Merged with clean destinations, all validators green, and the ten-role panel seal recorded", - "id": "ADR046-vvfs-003", - "kind": "work-item", - "owner": "ADR046-vvfs-001, ADR046-vvfs-002, ADR046-vvfs-export-001; ADR046-volume-001; W2; volume-virtiofs controller owner", - "parallelGroup": "wi:ADR-046-provider-volume-virtiofs", - "prerequisites": [ - "ADR-046-provider-volume-virtiofs", - "ADR046-volume-001", - "ADR046-vvfs-001", - "ADR046-vvfs-002" + "exitGate" : "ADR046-W6 exit criteria (ADR-046-validation-and-delivery §4): every spec/work item in this wave Merged with clean destinations, all validators green, and the ten-role panel seal recorded", + "id" : "ADR046-transport-relay-005", + "kind" : "work-item", + "owner" : "ADR046-transport-relay-001; backpressure/credit contract", + "parallelGroup" : "wi:ADR-046-provider-transport-azure-relay", + "prerequisites" : [ + "ADR-046-provider-transport-azure-relay", + "ADR046-transport-relay-001" ], - "specId": "ADR-046-provider-volume-virtiofs", - "topologicalRank": 13, - "wave": "W6" + "specId" : "ADR-046-provider-transport-azure-relay", + "topologicalRank" : 12, + "wave" : "W6" }, { - "blockers": [], - "destinations": [ - "`packages/d2b-provider-volume-virtiofs/src/readiness.rs`; `packages/d2b-provider-volume-virtiofs/integration/guest_mount_readiness/`" + "blockers" : [], + "destinations" : [ + "`packages/d2b-provider-transport-azure-relay/src/{metrics.rs, audit.rs}`" ], - "entryContracts": [ - "ADR-046-provider-volume-virtiofs" + "entryContracts" : [ + "ADR-046-provider-transport-azure-relay" ], - "exitGate": "ADR046-W6 exit criteria (ADR-046-validation-and-delivery §4): every spec/work item in this wave Merged with clean destinations, all validators green, and the ten-role panel seal recorded", - "id": "ADR046-vvfs-004", - "kind": "work-item", - "owner": "ADR046-vvfs-003; guest-control integration owner; W2", - "parallelGroup": "wi:ADR-046-provider-volume-virtiofs", - "prerequisites": [ - "ADR-046-provider-volume-virtiofs", - "ADR046-vvfs-003" + "exitGate" : "ADR046-W6 exit criteria (ADR-046-validation-and-delivery §4): every spec/work item in this wave Merged with clean destinations, all validators green, and the ten-role panel seal recorded", + "id" : "ADR046-transport-relay-006", + "kind" : "work-item", + "owner" : "ADR046-transport-relay-001 through ADR046-transport-relay-005; telemetry/audit owner", + "parallelGroup" : "wi:ADR-046-provider-transport-azure-relay", + "prerequisites" : [ + "ADR-046-provider-transport-azure-relay", + "ADR046-transport-relay-001", + "ADR046-transport-relay-002", + "ADR046-transport-relay-003", + "ADR046-transport-relay-004", + "ADR046-transport-relay-005" ], - "specId": "ADR-046-provider-volume-virtiofs", - "topologicalRank": 14, - "wave": "W6" + "specId" : "ADR-046-provider-transport-azure-relay", + "topologicalRank" : 13, + "wave" : "W6" }, { - "blockers": [], - "destinations": [ - "`packages/d2b-provider-volume-virtiofs/src/controller.rs` (pre-launch prerequisite check); `packages/d2b-provider-volume-virtiofs/integration/store_view_readonly/`" + "blockers" : [], + "destinations" : [ + "`packages/d2b-provider-transport-azure-relay/src/tests/integration/README`" ], - "entryContracts": [ - "ADR-046-provider-volume-virtiofs" + "entryContracts" : [ + "ADR-046-provider-transport-azure-relay" ], - "exitGate": "ADR046-W6 exit criteria (ADR-046-validation-and-delivery §4): every spec/work item in this wave Merged with clean destinations, all validators green, and the ten-role panel seal recorded", - "id": "ADR046-vvfs-005", - "kind": "work-item", - "owner": "ADR046-vvfs-003, ADR046-vvfs-004; ADR046-volume-002 (store-view Volume); W3", - "parallelGroup": "wi:ADR-046-provider-volume-virtiofs", - "prerequisites": [ - "ADR-046-provider-volume-virtiofs", - "ADR046-volume-002", - "ADR046-vvfs-003", - "ADR046-vvfs-004" + "exitGate" : "ADR046-W6 exit criteria (ADR-046-validation-and-delivery §4): every spec/work item in this wave Merged with clean destinations, all validators green, and the ten-role panel seal recorded", + "id" : "ADR046-transport-relay-007", + "kind" : "work-item", + "owner" : "Provider crate owner; integration test owner", + "parallelGroup" : "wi:ADR-046-provider-transport-azure-relay", + "prerequisites" : [ + "ADR-046-provider-transport-azure-relay" ], - "specId": "ADR-046-provider-volume-virtiofs", - "topologicalRank": 15, - "wave": "W6" + "specId" : "ADR-046-provider-transport-azure-relay", + "topologicalRank" : 11, + "wave" : "W6" }, { - "blockers": [], - "destinations": [ - "`nixos-modules/resources-volume.nix` (store-view and user Volume attachment emission); `nixos-modules/options-volumes.nix` (optional user-facing volume/attachment options)" + "blockers" : [], + "destinations" : [ + "`packages/d2b-provider-transport-unix/src/credit.rs` (imports `MAX_PACKET_ATTACHMENTS=32`, `RESERVED_CONTROL_FDS=64`, credit-class constants from v3 contract); `src/portal.rs` (imports `MAX_PACKET_ATTACHMENTS` for portal validation)" ], - "entryContracts": [ - "ADR-046-provider-volume-virtiofs" + "entryContracts" : [ + "ADR-046-provider-transport-unix" ], - "exitGate": "ADR046-W6 exit criteria (ADR-046-validation-and-delivery §4): every spec/work item in this wave Merged with clean destinations, all validators green, and the ten-role panel seal recorded", - "id": "ADR046-vvfs-006", - "kind": "work-item", - "owner": "ADR046-vvfs-001, ADR046-volume-004; Nix integrator; W3", - "parallelGroup": "wi:ADR-046-provider-volume-virtiofs", - "prerequisites": [ - "ADR-046-provider-volume-virtiofs", - "ADR046-volume-004", - "ADR046-vvfs-001" + "exitGate" : "ADR046-W6 exit criteria (ADR-046-validation-and-delivery §4): every spec/work item in this wave Merged with clean destinations, all validators green, and the ten-role panel seal recorded", + "id" : "ADR046-transport-unix-001", + "kind" : "work-item", + "owner" : "ADR046-session-001 (v3 ComponentSession contracts); W0 shared contract root", + "parallelGroup" : "wi:ADR-046-provider-transport-unix", + "prerequisites" : [ + "ADR-046-provider-transport-unix", + "ADR046-session-001" ], - "specId": "ADR-046-provider-volume-virtiofs", - "topologicalRank": 12, - "wave": "W6" + "specId" : "ADR-046-provider-transport-unix", + "topologicalRank" : 11, + "wave" : "W6" }, { - "blockers": [], - "destinations": [ - "`packages/d2b-provider-volume-virtiofs/src/export.rs`; `packages/d2b-contracts/src/v3/virtiofs_export.rs`" + "blockers" : [], + "destinations" : [ + "`packages/d2b-provider-transport-unix/src/{seqpacket,identity,socket}.rs`" ], - "entryContracts": [ - "ADR-046-provider-volume-virtiofs" + "entryContracts" : [ + "ADR-046-provider-transport-unix" ], - "exitGate": "ADR046-W6 exit criteria (ADR-046-validation-and-delivery §4): every spec/work item in this wave Merged with clean destinations, all validators green, and the ten-role panel seal recorded", - "id": "ADR046-vvfs-export-001", - "kind": "work-item", - "owner": "ADR046-volume-001; W1; volume-virtiofs Provider owner", - "parallelGroup": "wi:ADR-046-provider-volume-virtiofs", - "prerequisites": [ - "ADR-046-provider-volume-virtiofs", - "ADR046-volume-001" + "exitGate" : "ADR046-W6 exit criteria (ADR-046-validation-and-delivery §4): every spec/work item in this wave Merged with clean destinations, all validators green, and the ten-role panel seal recorded", + "id" : "ADR046-transport-unix-002", + "kind" : "work-item", + "owner" : "ADR046-transport-unix-001; d2b-bus transport layer (ADR046-session-003)", + "parallelGroup" : "wi:ADR-046-provider-transport-unix", + "prerequisites" : [ + "ADR-046-provider-transport-unix", + "ADR046-session-003", + "ADR046-transport-unix-001" ], - "specId": "ADR-046-provider-volume-virtiofs", - "topologicalRank": 11, - "wave": "W6" + "specId" : "ADR-046-provider-transport-unix", + "topologicalRank" : 12, + "wave" : "W6" }, { - "blockers": [], - "destinations": [ - "`packages/d2b-contracts/src/v3/{services,state,identity,provider}.rs` (new v3 namespace); generated stubs regenerated from v3 proto files in `packages/d2b-contracts/proto/v3/`" + "blockers" : [], + "destinations" : [ + "`packages/d2b-provider-transport-unix/src/{stream,socket}.rs`" ], - "entryContracts": [ - "ADR-046-resources-zone-control" + "entryContracts" : [ + "ADR-046-provider-transport-unix" ], - "exitGate": "ADR046-W5 exit criteria (ADR-046-validation-and-delivery §4): every spec/work item in this wave Merged with clean destinations, all validators green, and the ten-role panel seal recorded", - "id": "ADR046-wire-001", - "kind": "work-item", - "owner": "ADR046-zone-control-013", - "parallelGroup": "wi:ADR-046-resources-zone-control", - "prerequisites": [ - "ADR-046-resources-zone-control" + "exitGate" : "ADR046-W6 exit criteria (ADR-046-validation-and-delivery §4): every spec/work item in this wave Merged with clean destinations, all validators green, and the ten-role panel seal recorded", + "id" : "ADR046-transport-unix-003", + "kind" : "work-item", + "owner" : "ADR046-transport-unix-001", + "parallelGroup" : "wi:ADR-046-provider-transport-unix", + "prerequisites" : [ + "ADR-046-provider-transport-unix", + "ADR046-transport-unix-001" ], - "specId": "ADR-046-resources-zone-control", - "topologicalRank": 10, - "wave": "W5" + "specId" : "ADR-046-provider-transport-unix", + "topologicalRank" : 12, + "wave" : "W6" }, { - "blockers": [], - "destinations": [ - "`packages/d2b-contracts/src/v3/zone.rs`; `packages/d2b-core-controller/src/zone.rs`" + "blockers" : [], + "destinations" : [ + "`packages/d2b-provider-transport-unix/src/credit.rs`" ], - "entryContracts": [ - "ADR-046-resources-zone-control" + "entryContracts" : [ + "ADR-046-provider-transport-unix" ], - "exitGate": "ADR046-W5 exit criteria (ADR-046-validation-and-delivery §4): every spec/work item in this wave Merged with clean destinations, all validators green, and the ten-role panel seal recorded", - "id": "ADR046-zone-control-001", - "kind": "work-item", - "owner": "ADR046-object-001 (resource envelope); ADR046-store-001 (redb store); ADR046-identities-001 (types)", - "parallelGroup": "wi:ADR-046-resources-zone-control", - "prerequisites": [ - "ADR-046-resources-zone-control", - "ADR046-identities-001", - "ADR046-object-001", - "ADR046-store-001" + "exitGate" : "ADR046-W6 exit criteria (ADR-046-validation-and-delivery §4): every spec/work item in this wave Merged with clean destinations, all validators green, and the ten-role panel seal recorded", + "id" : "ADR046-transport-unix-004", + "kind" : "work-item", + "owner" : "ADR046-transport-unix-001", + "parallelGroup" : "wi:ADR-046-provider-transport-unix", + "prerequisites" : [ + "ADR-046-provider-transport-unix", + "ADR046-transport-unix-001" ], - "specId": "ADR-046-resources-zone-control", - "topologicalRank": 10, - "wave": "W5" + "specId" : "ADR-046-provider-transport-unix", + "topologicalRank" : 12, + "wave" : "W6" }, { - "blockers": [], - "destinations": [ - "`packages/d2b-contracts/src/v3/zone_link.rs`; `packages/d2b-core-controller/src/zone_link.rs`" + "blockers" : [], + "destinations" : [ + "`packages/d2b-provider-transport-unix/src/descriptor.rs`" ], - "entryContracts": [ - "ADR-046-resources-zone-control" + "entryContracts" : [ + "ADR-046-provider-transport-unix" ], - "exitGate": "ADR046-W5 exit criteria (ADR-046-validation-and-delivery §4): every spec/work item in this wave Merged with clean destinations, all validators green, and the ten-role panel seal recorded", - "id": "ADR046-zone-control-002", - "kind": "work-item", - "owner": "ADR046-zone-control-001; ADR046-zone-control-018 (ComponentSession)", - "parallelGroup": "wi:ADR-046-resources-zone-control", - "prerequisites": [ - "ADR-046-resources-zone-control" + "exitGate" : "ADR046-W6 exit criteria (ADR-046-validation-and-delivery §4): every spec/work item in this wave Merged with clean destinations, all validators green, and the ten-role panel seal recorded", + "id" : "ADR046-transport-unix-005", + "kind" : "work-item", + "owner" : "ADR046-transport-unix-002", + "parallelGroup" : "wi:ADR-046-provider-transport-unix", + "prerequisites" : [ + "ADR-046-provider-transport-unix", + "ADR046-transport-unix-002" ], - "specId": "ADR-046-resources-zone-control", - "topologicalRank": 10, - "wave": "W5" + "specId" : "ADR-046-provider-transport-unix", + "topologicalRank" : 13, + "wave" : "W6" }, { - "blockers": [], - "destinations": [ - "`packages/d2b-contracts/src/v3/provider.rs`; `packages/d2b-core-controller/src/provider_lifecycle.rs`; `packages/d2b-core-controller/src/api_catalog.rs`" + "blockers" : [], + "destinations" : [ + "`packages/d2b-provider-transport-unix/src/admission.rs`" ], - "entryContracts": [ - "ADR-046-resources-zone-control" + "entryContracts" : [ + "ADR-046-provider-transport-unix" ], - "exitGate": "ADR046-W5 exit criteria (ADR-046-validation-and-delivery §4): every spec/work item in this wave Merged with clean destinations, all validators green, and the ten-role panel seal recorded", - "id": "ADR046-zone-control-003", - "kind": "work-item", - "owner": "ADR046-zone-control-001; ADR046-api-001; ADR046-zone-control-017", - "parallelGroup": "wi:ADR-046-resources-zone-control", - "prerequisites": [ - "ADR-046-resources-zone-control", - "ADR046-api-001" + "exitGate" : "ADR046-W6 exit criteria (ADR-046-validation-and-delivery §4): every spec/work item in this wave Merged with clean destinations, all validators green, and the ten-role panel seal recorded", + "id" : "ADR046-transport-unix-006", + "kind" : "work-item", + "owner" : "ADR046-transport-unix-002", + "parallelGroup" : "wi:ADR-046-provider-transport-unix", + "prerequisites" : [ + "ADR-046-provider-transport-unix", + "ADR046-transport-unix-002" ], - "specId": "ADR-046-resources-zone-control", - "topologicalRank": 10, - "wave": "W5" + "specId" : "ADR-046-provider-transport-unix", + "topologicalRank" : 13, + "wave" : "W6" }, { - "blockers": [], - "destinations": [ - "`packages/d2b-contracts/src/v3/role.rs`; `packages/d2b-core-controller/src/authz.rs`" + "blockers" : [], + "destinations" : [ + "`packages/d2b-provider-transport-unix/src/{portal,service}.rs`" ], - "entryContracts": [ - "ADR-046-resources-zone-control" + "entryContracts" : [ + "ADR-046-provider-transport-unix" ], - "exitGate": "ADR046-W5 exit criteria (ADR-046-validation-and-delivery §4): every spec/work item in this wave Merged with clean destinations, all validators green, and the ten-role panel seal recorded", - "id": "ADR046-zone-control-004", - "kind": "work-item", - "owner": "ADR046-zone-control-001; ADR046-api-002", - "parallelGroup": "wi:ADR-046-resources-zone-control", - "prerequisites": [ - "ADR-046-resources-zone-control", - "ADR046-api-002" + "exitGate" : "ADR046-W6 exit criteria (ADR-046-validation-and-delivery §4): every spec/work item in this wave Merged with clean destinations, all validators green, and the ten-role panel seal recorded", + "id" : "ADR046-transport-unix-007", + "kind" : "work-item", + "owner" : "ADR046-transport-unix-002 through 006; ADR046-session-003 (d2b-bus ComponentSession method dispatch); ADR046-session-001 (named-stream protocol)", + "parallelGroup" : "wi:ADR-046-provider-transport-unix", + "prerequisites" : [ + "ADR-046-provider-transport-unix", + "ADR046-session-001", + "ADR046-session-003", + "ADR046-transport-unix-002", + "ADR046-transport-unix-003", + "ADR046-transport-unix-004", + "ADR046-transport-unix-005", + "ADR046-transport-unix-006" ], - "specId": "ADR-046-resources-zone-control", - "topologicalRank": 10, - "wave": "W5" + "specId" : "ADR-046-provider-transport-unix", + "topologicalRank" : 14, + "wave" : "W6" }, { - "blockers": [], - "destinations": [ - "`packages/d2b-contracts/src/v3/role_binding.rs`; `packages/d2b-core-controller/src/authz.rs` (shared with Role handler)" + "blockers" : [], + "destinations" : [ + "`packages/d2b-provider-transport-unix/` crate Cargo.toml binary target `d2b-transport-unix-service`; Provider component descriptor JSON committed at `packages/d2b-provider-transport-unix/descriptor/unix-transport-service.json`; Nix package derivation at `packages/d2b-provider-transport-unix/`" ], - "entryContracts": [ - "ADR-046-resources-zone-control" + "entryContracts" : [ + "ADR-046-provider-transport-unix" ], - "exitGate": "ADR046-W5 exit criteria (ADR-046-validation-and-delivery §4): every spec/work item in this wave Merged with clean destinations, all validators green, and the ten-role panel seal recorded", - "id": "ADR046-zone-control-005", - "kind": "work-item", - "owner": "ADR046-zone-control-004", - "parallelGroup": "wi:ADR-046-resources-zone-control", - "prerequisites": [ - "ADR-046-resources-zone-control" + "exitGate" : "ADR046-W6 exit criteria (ADR-046-validation-and-delivery §4): every spec/work item in this wave Merged with clean destinations, all validators green, and the ten-role panel seal recorded", + "id" : "ADR046-transport-unix-008", + "kind" : "work-item", + "owner" : "ADR046-transport-unix-007; ADR046-provider-003 (system Provider framework); Provider/system-minijail (ADR046-provider-003)", + "parallelGroup" : "wi:ADR-046-provider-transport-unix", + "prerequisites" : [ + "ADR-046-provider-transport-unix", + "ADR046-provider-003", + "ADR046-transport-unix-007" ], - "specId": "ADR-046-resources-zone-control", - "topologicalRank": 10, - "wave": "W5" + "specId" : "ADR-046-provider-transport-unix", + "topologicalRank" : 15, + "wave" : "W6" }, { - "blockers": [], - "destinations": [ - "`packages/d2b-resource-api/src/bootstrap_authz.rs`; Zone runtime startup path" + "blockers" : [], + "destinations" : [ + "`docs/reference/schemas/v3/providers/transport-unix.transport-binding.json`; `nixos-modules/assertions.nix` (assertion additions); generated `nixos-modules/generated/options-zones-ZoneLink.nix` provider-settings submodule" ], - "entryContracts": [ - "ADR-046-resources-zone-control" + "entryContracts" : [ + "ADR-046-provider-transport-unix" ], - "exitGate": "ADR046-W5 exit criteria (ADR-046-validation-and-delivery §4): every spec/work item in this wave Merged with clean destinations, all validators green, and the ten-role panel seal recorded", - "id": "ADR046-zone-control-006", - "kind": "work-item", - "owner": "ADR046-zone-control-004, ADR046-zone-control-005; Zone runtime", - "parallelGroup": "wi:ADR-046-resources-zone-control", - "prerequisites": [ - "ADR-046-resources-zone-control" + "exitGate" : "ADR046-W6 exit criteria (ADR-046-validation-and-delivery §4): every spec/work item in this wave Merged with clean destinations, all validators green, and the ten-role panel seal recorded", + "id" : "ADR046-transport-unix-009", + "kind" : "work-item", + "owner" : "ADR046-transport-unix-001; Nix/build integrator", + "parallelGroup" : "wi:ADR-046-provider-transport-unix", + "prerequisites" : [ + "ADR-046-provider-transport-unix", + "ADR046-transport-unix-001" ], - "specId": "ADR-046-resources-zone-control", - "topologicalRank": 10, - "wave": "W5" + "specId" : "ADR-046-provider-transport-unix", + "topologicalRank" : 12, + "wave" : "W6" }, { - "blockers": [], - "destinations": [ - "`nixos-modules/options-zones.nix`; `nixos-modules/resources-zone-control.nix`; extended `nixos-modules/index.nix`" + "blockers" : [], + "destinations" : [ + "`packages/d2b-provider-transport-unix/src/{audit,metrics}.rs`" ], - "entryContracts": [ - "ADR-046-resources-zone-control" + "entryContracts" : [ + "ADR-046-provider-transport-unix" ], - "exitGate": "ADR046-W5 exit criteria (ADR-046-validation-and-delivery §4): every spec/work item in this wave Merged with clean destinations, all validators green, and the ten-role panel seal recorded", - "id": "ADR046-zone-control-007", - "kind": "work-item", - "owner": "ADR046-identities-002; ADR046-zone-control-001; ADR046-zone-control-002; ADR046-zone-control-003; ADR046-zone-control-004; ADR046-zone-control-005; ADR046-zone-control-009; ADR046-zone-control-010", - "parallelGroup": "wi:ADR-046-resources-zone-control", - "prerequisites": [ - "ADR-046-resources-zone-control", - "ADR046-identities-002" + "exitGate" : "ADR046-W6 exit criteria (ADR-046-validation-and-delivery §4): every spec/work item in this wave Merged with clean destinations, all validators green, and the ten-role panel seal recorded", + "id" : "ADR046-transport-unix-010", + "kind" : "work-item", + "owner" : "ADR046-transport-unix-007; ADR-046-telemetry-audit-and-support", + "parallelGroup" : "wi:ADR-046-provider-transport-unix", + "prerequisites" : [ + "ADR-046-provider-transport-unix", + "ADR046-transport-unix-007" ], - "specId": "ADR-046-resources-zone-control", - "topologicalRank": 10, - "wave": "W5" + "specId" : "ADR-046-provider-transport-unix", + "topologicalRank" : 15, + "wave" : "W6" }, { - "blockers": [], - "destinations": [ - "`packages/d2b-contracts/src/v3/host.rs` (Host resource schema, user-domain variant); `packages/d2b-core-controller/src/host_user.rs` (reconciler owned by Provider/system-core); Nix Host authoring via `d2b.zones..resources. = { type = \"Host\"; spec = { ... }; };` (validated per ResourceTypeSchema; no separate `options-zones-hosts.nix` submodule)" + "blockers" : [], + "destinations" : [ + "`packages/d2b-provider-transport-unix/integration/` and `integration/README.md`" ], - "entryContracts": [ - "ADR-046-resources-zone-control" + "entryContracts" : [ + "ADR-046-provider-transport-unix" ], - "exitGate": "ADR046-W5 exit criteria (ADR-046-validation-and-delivery §4): every spec/work item in this wave Merged with clean destinations, all validators green, and the ten-role panel seal recorded", - "id": "ADR046-zone-control-008", - "kind": "work-item", - "owner": "ADR046-zone-control-001 (Zone resource); ADR046-zone-control-003 (Provider/system-core installed)", - "parallelGroup": "wi:ADR-046-resources-zone-control", - "prerequisites": [ - "ADR-046-resources-zone-control" + "exitGate" : "ADR046-W6 exit criteria (ADR-046-validation-and-delivery §4): every spec/work item in this wave Merged with clean destinations, all validators green, and the ten-role panel seal recorded", + "id" : "ADR046-transport-unix-011", + "kind" : "work-item", + "owner" : "ADR046-transport-unix-007 through 010; test orchestration owner", + "parallelGroup" : "wi:ADR-046-provider-transport-unix", + "prerequisites" : [ + "ADR-046-provider-transport-unix", + "ADR046-transport-unix-007", + "ADR046-transport-unix-008", + "ADR046-transport-unix-009", + "ADR046-transport-unix-010" ], - "specId": "ADR-046-resources-zone-control", - "topologicalRank": 10, - "wave": "W5" + "specId" : "ADR-046-provider-transport-unix", + "topologicalRank" : 16, + "wave" : "W6" }, { - "blockers": [], - "destinations": [ - "`packages/d2b-contracts/src/v3/quota.rs`; `packages/d2b-core-controller/src/quota.rs`; `packages/d2b-resource-api/src/quota_gate.rs`" + "blockers" : [], + "destinations" : [ + "packages/d2b-contracts/src/usbip_effect_port.rs" ], - "entryContracts": [ - "ADR-046-resources-zone-control" + "entryContracts" : [ + "ADR-046-provider-device-usbip" ], - "exitGate": "ADR046-W5 exit criteria (ADR-046-validation-and-delivery §4): every spec/work item in this wave Merged with clean destinations, all validators green, and the ten-role panel seal recorded", - "id": "ADR046-zone-control-009", - "kind": "work-item", - "owner": "ADR046-zone-control-001; Zone store (ADR046-store-001); Quota handler owner", - "parallelGroup": "wi:ADR-046-resources-zone-control", - "prerequisites": [ - "ADR-046-resources-zone-control", - "ADR046-store-001" + "exitGate" : "ADR046-W6 exit criteria (ADR-046-validation-and-delivery §4): every spec/work item in this wave Merged with clean destinations, all validators green, and the ten-role panel seal recorded", + "id" : "ADR046-usbip-001", + "kind" : "work-item", + "owner" : "d2b-contracts crate shape stabilised by shared root contract; d2b-contracts owner", + "parallelGroup" : "wi:ADR-046-provider-device-usbip", + "prerequisites" : [ + "ADR-046-provider-device-usbip" ], - "specId": "ADR-046-resources-zone-control", - "topologicalRank": 10, - "wave": "W5" + "specId" : "ADR-046-provider-device-usbip", + "topologicalRank" : 11, + "wave" : "W6" }, { - "blockers": [], - "destinations": [ - "`packages/d2b-contracts/src/v3/emergency_policy.rs`; `packages/d2b-core-controller/src/emergency_policy.rs`; `packages/d2b-resource-api/src/emergency_gate.rs`" + "blockers" : [], + "destinations" : [ + "packages/d2b-core/src/device_usbip_adapter.rs" ], - "entryContracts": [ - "ADR-046-resources-zone-control" + "entryContracts" : [ + "ADR-046-provider-device-usbip" ], - "exitGate": "ADR046-W5 exit criteria (ADR-046-validation-and-delivery §4): every spec/work item in this wave Merged with clean destinations, all validators green, and the ten-role panel seal recorded", - "id": "ADR046-zone-control-010", - "kind": "work-item", - "owner": "ADR046-zone-control-001; Zone store (ADR046-store-001); EmergencyPolicy handler owner", - "parallelGroup": "wi:ADR-046-resources-zone-control", - "prerequisites": [ - "ADR-046-resources-zone-control", - "ADR046-store-001" + "exitGate" : "ADR046-W6 exit criteria (ADR-046-validation-and-delivery §4): every spec/work item in this wave Merged with clean destinations, all validators green, and the ten-role panel seal recorded", + "id" : "ADR046-usbip-002", + "kind" : "work-item", + "owner" : "ADR046-usbip-001, ADR046-zone-control-024; UsbipBindFirewallRule broker op; d2b-host usbip argv support; framework core adapter owner", + "parallelGroup" : "wi:ADR-046-provider-device-usbip", + "prerequisites" : [ + "ADR-046-provider-device-usbip", + "ADR046-usbip-001", + "ADR046-zone-control-024" ], - "specId": "ADR-046-resources-zone-control", - "topologicalRank": 10, - "wave": "W5" + "specId" : "ADR-046-provider-device-usbip", + "topologicalRank" : 14, + "wave" : "W6" }, { - "blockers": [], - "destinations": [ - "`packages/d2b-bus/src/{lifecycle,engine,driver,streams,transport,error}.rs` (new crate `d2b-bus`)" + "blockers" : [], + "destinations" : [ + "packages/d2b-provider-device-usbip/" ], - "entryContracts": [ - "ADR-046-resources-zone-control" + "entryContracts" : [ + "ADR-046-provider-device-usbip" ], - "exitGate": "ADR046-W5 exit criteria (ADR-046-validation-and-delivery §4): every spec/work item in this wave Merged with clean destinations, all validators green, and the ten-role panel seal recorded", - "id": "ADR046-zone-control-011", - "kind": "work-item", - "owner": "ADR046-identities-001; ADR046-store-001", - "parallelGroup": "wi:ADR-046-resources-zone-control", - "prerequisites": [ - "ADR-046-resources-zone-control", - "ADR046-identities-001", - "ADR046-store-001" + "exitGate" : "ADR046-W6 exit criteria (ADR-046-validation-and-delivery §4): every spec/work item in this wave Merged with clean destinations, all validators green, and the ten-role panel seal recorded", + "id" : "ADR046-usbip-003", + "kind" : "work-item", + "owner" : "ADR046-usbip-001, ADR046-provider-004; Provider model crate structure; device-usbip provider owner", + "parallelGroup" : "wi:ADR-046-provider-device-usbip", + "prerequisites" : [ + "ADR-046-provider-device-usbip", + "ADR046-provider-004", + "ADR046-usbip-001" ], - "specId": "ADR-046-resources-zone-control", - "topologicalRank": 10, - "wave": "W5" + "specId" : "ADR-046-provider-device-usbip", + "topologicalRank" : 12, + "wave" : "W6" }, { - "blockers": [], - "destinations": [ - "`packages/d2b-bus-unix/src/{adapter,socket,pidfd,credit,descriptor,error,systemd}.rs` (new crate `d2b-bus-unix`)" + "blockers" : [], + "destinations" : [ + "packages/d2b-provider-device-usbip/src/{controller,reconcile,export_import}.rs" ], - "entryContracts": [ - "ADR-046-resources-zone-control" + "entryContracts" : [ + "ADR-046-provider-device-usbip" ], - "exitGate": "ADR046-W5 exit criteria (ADR-046-validation-and-delivery §4): every spec/work item in this wave Merged with clean destinations, all validators green, and the ten-role panel seal recorded", - "id": "ADR046-zone-control-012", - "kind": "work-item", - "owner": "ADR046-zone-control-011", - "parallelGroup": "wi:ADR-046-resources-zone-control", - "prerequisites": [ - "ADR-046-resources-zone-control" + "exitGate" : "ADR046-W6 exit criteria (ADR-046-validation-and-delivery §4): every spec/work item in this wave Merged with clean destinations, all validators green, and the ten-role panel seal recorded", + "id" : "ADR046-usbip-004", + "kind" : "work-item", + "owner" : "ADR046-usbip-001, ADR046-usbip-002, ADR046-usbip-003, and ADR046-zone-control-024; device-usbip controller owner", + "parallelGroup" : "wi:ADR-046-provider-device-usbip", + "prerequisites" : [ + "ADR-046-provider-device-usbip", + "ADR046-usbip-001", + "ADR046-usbip-002", + "ADR046-usbip-003", + "ADR046-zone-control-024" ], - "specId": "ADR-046-resources-zone-control", - "topologicalRank": 10, - "wave": "W5" + "specId" : "ADR-046-provider-device-usbip", + "topologicalRank" : 15, + "wave" : "W6" }, { - "blockers": [], - "destinations": [ - "`packages/d2b-contracts/src/v3/component_session.rs` (new v3 namespace in existing contracts crate)" + "blockers" : [], + "destinations" : [ + "packages/d2b-provider-device-usbip/src/reconcile.rs" ], - "entryContracts": [ - "ADR-046-resources-zone-control" + "entryContracts" : [ + "ADR-046-provider-device-usbip" ], - "exitGate": "ADR046-W5 exit criteria (ADR-046-validation-and-delivery §4): every spec/work item in this wave Merged with clean destinations, all validators green, and the ten-role panel seal recorded", - "id": "ADR046-zone-control-013", - "kind": "work-item", - "owner": "ADR046-identities-001", - "parallelGroup": "wi:ADR-046-resources-zone-control", - "prerequisites": [ - "ADR-046-resources-zone-control", - "ADR046-identities-001" + "exitGate" : "ADR046-W6 exit criteria (ADR-046-validation-and-delivery §4): every spec/work item in this wave Merged with clean destinations, all validators green, and the ten-role panel seal recorded", + "id" : "ADR046-usbip-005", + "kind" : "work-item", + "owner" : "ADR046-usbip-003; Process ResourceType schema; device-usbip process lifecycle owner", + "parallelGroup" : "wi:ADR-046-provider-device-usbip", + "prerequisites" : [ + "ADR-046-provider-device-usbip", + "ADR046-usbip-003" ], - "specId": "ADR-046-resources-zone-control", - "topologicalRank": 10, - "wave": "W5" + "specId" : "ADR-046-provider-device-usbip", + "topologicalRank" : 13, + "wave" : "W6" }, { - "blockers": [], - "destinations": [ - "`nixos-modules/options-zones.nix`, `nixos-modules/resource-type-validators.nix`" + "blockers" : [], + "destinations" : [ + "packages/d2b-provider-device-usbip/src/status.rs" ], - "entryContracts": [ - "ADR-046-resources-zone-control" + "entryContracts" : [ + "ADR-046-provider-device-usbip" ], - "exitGate": "ADR046-W5 exit criteria (ADR-046-validation-and-delivery §4): every spec/work item in this wave Merged with clean destinations, all validators green, and the ten-role panel seal recorded", - "id": "ADR046-zone-control-014", - "kind": "work-item", - "owner": "ADR046-zone-control-001; Nix module owner", - "parallelGroup": "wi:ADR-046-resources-zone-control", - "prerequisites": [ - "ADR-046-resources-zone-control" + "exitGate" : "ADR046-W6 exit criteria (ADR-046-validation-and-delivery §4): every spec/work item in this wave Merged with clean destinations, all validators green, and the ten-role panel seal recorded", + "id" : "ADR046-usbip-006", + "kind" : "work-item", + "owner" : "ADR046-usbip-003; Device provider details plus `UsbService` and `UsbBinding` base/provider status schema owner", + "parallelGroup" : "wi:ADR-046-provider-device-usbip", + "prerequisites" : [ + "ADR-046-provider-device-usbip", + "ADR046-usbip-003" ], - "specId": "ADR-046-resources-zone-control", - "topologicalRank": 10, - "wave": "W5" + "specId" : "ADR-046-provider-device-usbip", + "topologicalRank" : 13, + "wave" : "W6" }, { - "blockers": [], - "destinations": [ - "`packages/d2b-resource-compiler/src/{main,bundle,schema,validator,digest,sort,secret_lint,generation}.rs`; exposed as `pkgs.d2b-resource-compiler`; called from `nixos-modules/resource-compiler.nix`" + "blockers" : [], + "destinations" : [ + "packages/d2b-provider-device-usbip/{src,tests,integration/README.md}; tests/host-integration/usbip-service.nix; tests/host-integration/hardware/usbip-service.sh" ], - "entryContracts": [ - "ADR-046-resources-zone-control" + "entryContracts" : [ + "ADR-046-provider-device-usbip" ], - "exitGate": "ADR046-W5 exit criteria (ADR-046-validation-and-delivery §4): every spec/work item in this wave Merged with clean destinations, all validators green, and the ten-role panel seal recorded", - "id": "ADR046-zone-control-015", - "kind": "work-item", - "owner": "ADR046-zone-control-014; resource compiler owner", - "parallelGroup": "wi:ADR-046-resources-zone-control", - "prerequisites": [ - "ADR-046-resources-zone-control" + "exitGate" : "ADR046-W6 exit criteria (ADR-046-validation-and-delivery §4): every spec/work item in this wave Merged with clean destinations, all validators green, and the ten-role panel seal recorded", + "id" : "ADR046-usbip-007", + "kind" : "work-item", + "owner" : "ADR046-usbip-004 and ADR046-usbip-005; device-usbip integration owner", + "parallelGroup" : "wi:ADR-046-provider-device-usbip", + "prerequisites" : [ + "ADR-046-provider-device-usbip", + "ADR046-usbip-004", + "ADR046-usbip-005" ], - "specId": "ADR-046-resources-zone-control", - "topologicalRank": 10, - "wave": "W5" + "specId" : "ADR-046-provider-device-usbip", + "topologicalRank" : 16, + "wave" : "W6" }, { - "blockers": [], - "destinations": [ - "`packages/d2b-core-controller/src/configuration.rs` (Phase 3 activation, diff, delete dispatch); `packages/d2b-core-controller/src/cleanup.rs` (pending tracking, status, stuck detection, rollback verb handler)" + "blockers" : [], + "destinations" : [ + "nixos-modules/components/usbip.nix, nixos-modules/options-zones.nix, nixos-modules/assertions.nix" ], - "entryContracts": [ - "ADR-046-resources-zone-control" + "entryContracts" : [ + "ADR-046-provider-device-usbip" ], - "exitGate": "ADR046-W5 exit criteria (ADR-046-validation-and-delivery §4): every spec/work item in this wave Merged with clean destinations, all validators green, and the ten-role panel seal recorded", - "id": "ADR046-zone-control-016", - "kind": "work-item", - "owner": "ADR046-zone-control-015; ADR046-zone-control-001; configuration publication handler owner", - "parallelGroup": "wi:core-config-hub", - "prerequisites": [ - "ADR-046-resources-zone-control", - "ADR046-core-001" + "exitGate" : "ADR046-W6 exit criteria (ADR-046-validation-and-delivery §4): every spec/work item in this wave Merged with clean destinations, all validators green, and the ten-role panel seal recorded", + "id" : "ADR046-usbip-008", + "kind" : "work-item", + "owner" : "ADR046-usbip-003, ADR046-zone-control-024; ADR-046-nix-configuration; Nix integrator", + "parallelGroup" : "wi:ADR-046-provider-device-usbip", + "prerequisites" : [ + "ADR-046-provider-device-usbip", + "ADR046-usbip-003", + "ADR046-zone-control-024" ], - "specId": "ADR-046-resources-zone-control", - "topologicalRank": 10, - "wave": "W5" + "specId" : "ADR-046-provider-device-usbip", + "topologicalRank" : 14, + "wave" : "W6" }, { - "blockers": [], - "destinations": [ - "`packages/d2b-provider/src/{registry,rpc}.rs` (new v3 Provider package); `packages/d2b-provider-toolkit/src/{adapter,conformance,fixture,registration,server,values,redaction}.rs` (new v3 toolkit)" + "blockers" : [], + "destinations" : [ + "packages/d2bd/src/, nixos-modules/network.nix, packages/d2b-core/src/processes.rs" ], - "entryContracts": [ - "ADR-046-resources-zone-control" + "entryContracts" : [ + "ADR-046-provider-device-usbip" ], - "exitGate": "ADR046-W5 exit criteria (ADR-046-validation-and-delivery §4): every spec/work item in this wave Merged with clean destinations, all validators green, and the ten-role panel seal recorded", - "id": "ADR046-zone-control-017", - "kind": "work-item", - "owner": "ADR046-zone-control-011, ADR046-zone-control-013, ADR046-zone-control-003", - "parallelGroup": "wi:ADR-046-resources-zone-control", - "prerequisites": [ - "ADR-046-resources-zone-control" + "exitGate" : "ADR046-W6 exit criteria (ADR-046-validation-and-delivery §4): every spec/work item in this wave Merged with clean destinations, all validators green, and the ten-role panel seal recorded", + "id" : "ADR046-usbip-009", + "kind" : "work-item", + "owner" : "ADR046-usbip-004 and ADR046-usbip-008; Provider fully wired and validated; daemon cleanup owner", + "parallelGroup" : "wi:ADR-046-provider-device-usbip", + "prerequisites" : [ + "ADR-046-provider-device-usbip", + "ADR046-usbip-004", + "ADR046-usbip-008" ], - "specId": "ADR-046-resources-zone-control", - "topologicalRank": 10, - "wave": "W5" + "specId" : "ADR-046-provider-device-usbip", + "topologicalRank" : 16, + "wave" : "W6" }, { - "blockers": [], - "destinations": [ - "`packages/d2b-core-controller/src/zone_link.rs` (ZoneLink handler); `packages/d2b-resource-api/src/admission.rs` (request admission)" + "blockers" : [], + "destinations" : [ + "`d2b-contracts/src/v3/volume_layout.rs` (LayoutEntry, EntryType, all policy enums, AclGrant, Invariant, SensitivityClass); `d2b-contracts/src/v3/volume_spec.rs` (VolumeSpec, ViewSpec, Attachment, QuotaSpec, SourceKind, `SourcePolicyId` opaque newtype); `d2b-contracts/src/v3/effect_port.rs` (`VolumeEffectPort` trait, opaque ID newtypes `VolumeId`/`LayoutEntryId`/`UserId`/`ViewId` each with custom redacted Debug, and `VolumeMountToken` opaque handle with custom redacted Debug)" ], - "entryContracts": [ - "ADR-046-resources-zone-control" + "entryContracts" : [ + "ADR-046-provider-volume-local" ], - "exitGate": "ADR046-W5 exit criteria (ADR-046-validation-and-delivery §4): every spec/work item in this wave Merged with clean destinations, all validators green, and the ten-role panel seal recorded", - "id": "ADR046-zone-control-018", - "kind": "work-item", - "owner": "ADR046-zone-control-011, ADR046-zone-control-012, ADR046-zone-control-013, ADR046-zone-control-002", - "parallelGroup": "wi:ADR-046-resources-zone-control", - "prerequisites": [ - "ADR-046-resources-zone-control" + "exitGate" : "ADR046-W6 exit criteria (ADR-046-validation-and-delivery §4): every spec/work item in this wave Merged with clean destinations, all validators green, and the ten-role panel seal recorded", + "id" : "ADR046-vl-001", + "kind" : "work-item", + "owner" : "ADR046-primitives-001; v3 contracts owner", + "parallelGroup" : "wi:ADR-046-provider-volume-local", + "prerequisites" : [ + "ADR-046-provider-volume-local", + "ADR046-primitives-001" ], - "specId": "ADR-046-resources-zone-control", - "topologicalRank": 10, - "wave": "W5" + "specId" : "ADR-046-provider-volume-local", + "topologicalRank" : 11, + "wave" : "W6" }, { - "blockers": [], - "destinations": [ - "`packages/d2b-contracts/src/v3/{resource_export,resource_import}.rs` (base schemas); `packages/d2b-core-controller/src/export_import.rs` (core ZoneLink export/import routing controller); shared adapter trait in `packages/d2b-provider/src/share_adapter.rs` (`ExportAdapter`/`ImportAdapter` signed-capability traits)" + "blockers" : [], + "destinations" : [ + "Full `packages/d2b-provider-volume-local/` scaffold per §Crate layout: `src/`, `tests/`, `integration/`, `README.md`; crate `Cargo.toml` depends only on `d2b-contracts`, `d2b-provider`, `d2b-provider-toolkit`" ], - "entryContracts": [ - "ADR-046-resources-zone-control" + "entryContracts" : [ + "ADR-046-provider-volume-local" ], - "exitGate": "ADR046-W5 exit criteria (ADR-046-validation-and-delivery §4): every spec/work item in this wave Merged with clean destinations, all validators green, and the ten-role panel seal recorded", - "id": "ADR046-zone-control-019", - "kind": "work-item", - "owner": "ADR046-zone-control-001, ADR046-provider-004; ADR046-zonelink owner; `d2b-core-controller` + `d2b-contracts` owners", - "parallelGroup": "wi:ADR-046-resources-zone-control", - "prerequisites": [ - "ADR-046-resources-zone-control" + "exitGate" : "ADR046-W6 exit criteria (ADR-046-validation-and-delivery §4): every spec/work item in this wave Merged with clean destinations, all validators green, and the ten-role panel seal recorded", + "id" : "ADR046-vl-002", + "kind" : "work-item", + "owner" : "ADR046-vl-001; volume-local Provider owner", + "parallelGroup" : "wi:ADR-046-provider-volume-local", + "prerequisites" : [ + "ADR-046-provider-volume-local", + "ADR046-vl-001" ], - "specId": "ADR-046-resources-zone-control", - "topologicalRank": 10, - "wave": "W5" + "specId" : "ADR-046-provider-volume-local", + "topologicalRank" : 12, + "wave" : "W6" }, { - "blockers": [], - "destinations": [ - "`packages/d2b-core-controller/src/export_import_projection.rs` (local qualified Service projection lifecycle owned by `ResourceImport`)" + "blockers" : [], + "destinations" : [ + "`src/controller.rs`, `src/layout.rs`, `src/acl.rs`, `src/source.rs`" ], - "entryContracts": [ - "ADR-046-resources-zone-control" + "entryContracts" : [ + "ADR-046-provider-volume-local" ], - "exitGate": "ADR046-W5 exit criteria (ADR-046-validation-and-delivery §4): every spec/work item in this wave Merged with clean destinations, all validators green, and the ten-role panel seal recorded", - "id": "ADR046-zone-control-020", - "kind": "work-item", - "owner": "ADR046-zone-control-019; `d2b-core-controller` owner", - "parallelGroup": "wi:ADR-046-resources-zone-control", - "prerequisites": [ - "ADR-046-resources-zone-control" + "exitGate" : "ADR046-W6 exit criteria (ADR-046-validation-and-delivery §4): every spec/work item in this wave Merged with clean destinations, all validators green, and the ten-role panel seal recorded", + "id" : "ADR046-vl-003", + "kind" : "work-item", + "owner" : "ADR046-vl-002; ADR046-reconcile-001; d2b-bus/ComponentSession owner", + "parallelGroup" : "wi:ADR-046-provider-volume-local", + "prerequisites" : [ + "ADR-046-provider-volume-local", + "ADR046-reconcile-001", + "ADR046-vl-002" ], - "specId": "ADR-046-resources-zone-control", - "topologicalRank": 10, - "wave": "W5" + "specId" : "ADR-046-provider-volume-local", + "topologicalRank" : 13, + "wave" : "W6" }, { - "blockers": [], - "destinations": [ - "`packages/d2b-core-controller/src/{coordinator,configuration,zonelink}.rs`" + "blockers" : [], + "destinations" : [ + "`src/store_view.rs`; `tests/store_view.rs`; `integration/store_view.rs`" ], - "entryContracts": [ - "ADR-046-resources-zone-control" + "entryContracts" : [ + "ADR-046-provider-volume-local" ], - "exitGate": "ADR046-W5 exit criteria (ADR-046-validation-and-delivery §4): every spec/work item in this wave Merged with clean destinations, all validators green, and the ten-role panel seal recorded", - "id": "ADR046-zone-control-021", - "kind": "work-item", - "owner": "ADR046-zone-control-001, ADR046-zone-control-016; `d2b-core-controller` owner", - "parallelGroup": "wi:core-config-hub", - "prerequisites": [ - "ADR-046-resources-zone-control", - "ADR046-core-001" + "exitGate" : "ADR046-W6 exit criteria (ADR-046-validation-and-delivery §4): every spec/work item in this wave Merged with clean destinations, all validators green, and the ten-role panel seal recorded", + "id" : "ADR046-vl-004", + "kind" : "work-item", + "owner" : "ADR046-vl-003; runtime-cloud-hypervisor Provider owner", + "parallelGroup" : "wi:ADR-046-provider-volume-local", + "prerequisites" : [ + "ADR-046-provider-volume-local", + "ADR046-vl-003" ], - "specId": "ADR-046-resources-zone-control", - "topologicalRank": 10, - "wave": "W5" + "specId" : "ADR-046-provider-volume-local", + "topologicalRank" : 14, + "wave" : "W6" }, { - "blockers": [], - "destinations": [ - "`packages/d2b-core-controller/src/authority.rs`; resource API admission hook" + "blockers" : [], + "destinations" : [ + "`src/swtpm_volume.rs`; `tests/swtpm_volume.rs`; `integration/swtpm_marker.rs`" ], - "entryContracts": [ - "ADR-046-resources-zone-control" + "entryContracts" : [ + "ADR-046-provider-volume-local" ], - "exitGate": "ADR046-W5 exit criteria (ADR-046-validation-and-delivery §4): every spec/work item in this wave Merged with clean destinations, all validators green, and the ten-role panel seal recorded", - "id": "ADR046-zone-control-022", - "kind": "work-item", - "owner": "ADR046-zone-control-019, ADR046-api-001; `d2b-core-controller` + resource API owners", - "parallelGroup": "wi:ADR-046-resources-zone-control", - "prerequisites": [ - "ADR-046-resources-zone-control", - "ADR046-api-001" + "exitGate" : "ADR046-W6 exit criteria (ADR-046-validation-and-delivery §4): every spec/work item in this wave Merged with clean destinations, all validators green, and the ten-role panel seal recorded", + "id" : "ADR046-vl-005", + "kind" : "work-item", + "owner" : "ADR046-vl-002; device-tpm Provider owner", + "parallelGroup" : "wi:ADR-046-provider-volume-local", + "prerequisites" : [ + "ADR-046-provider-volume-local", + "ADR046-vl-002" ], - "specId": "ADR-046-resources-zone-control", - "topologicalRank": 10, - "wave": "W5" + "specId" : "ADR-046-provider-volume-local", + "topologicalRank" : 13, + "wave" : "W6" }, { - "blockers": [], - "destinations": [ - "`packages/d2b-core-controller/src/{quota,emergency_policy}.rs`; `packages/d2b-contracts/src/v3/{quota,emergency_policy}.rs`" + "blockers" : [], + "destinations" : [ + "`src/source.rs` (block-image and tmpfs branches); `tests/source.rs`; `integration/block_image.rs`" ], - "entryContracts": [ - "ADR-046-resources-zone-control" + "entryContracts" : [ + "ADR-046-provider-volume-local" ], - "exitGate": "ADR046-W5 exit criteria (ADR-046-validation-and-delivery §4): every spec/work item in this wave Merged with clean destinations, all validators green, and the ten-role panel seal recorded", - "id": "ADR046-zone-control-023", - "kind": "work-item", - "owner": "ADR046-zone-control-019; `d2b-core-controller` + `d2b-contracts` owners", - "parallelGroup": "wi:ADR-046-resources-zone-control", - "prerequisites": [ - "ADR-046-resources-zone-control" + "exitGate" : "ADR046-W6 exit criteria (ADR-046-validation-and-delivery §4): every spec/work item in this wave Merged with clean destinations, all validators green, and the ten-role panel seal recorded", + "id" : "ADR046-vl-006", + "kind" : "work-item", + "owner" : "ADR046-vl-003", + "parallelGroup" : "wi:ADR-046-provider-volume-local", + "prerequisites" : [ + "ADR-046-provider-volume-local", + "ADR046-vl-003" ], - "specId": "ADR-046-resources-zone-control", - "topologicalRank": 10, - "wave": "W5" + "specId" : "ADR-046-provider-volume-local", + "topologicalRank" : 14, + "wave" : "W6" }, { - "blockers": [], - "destinations": [ - "`packages/d2b-core-controller/src/authority.rs` (Host-global index scope + hardware admission)" + "blockers" : [], + "destinations" : [ + "`src/{migration,snapshot}.rs`; `tests/{migration_unit,snapshot_unit}.rs`; `integration/{migration,snapshot}.rs`" ], - "entryContracts": [ - "ADR-046-resources-zone-control" + "entryContracts" : [ + "ADR-046-provider-volume-local" ], - "exitGate": "ADR046-W5 exit criteria (ADR-046-validation-and-delivery §4): every spec/work item in this wave Merged with clean destinations, all validators green, and the ten-role panel seal recorded", - "id": "ADR046-zone-control-024", - "kind": "work-item", - "owner": "ADR046-zone-control-019, ADR046-zone-control-022; `d2b-core-controller` owner", - "parallelGroup": "wi:ADR-046-resources-zone-control", - "prerequisites": [ - "ADR-046-resources-zone-control" + "exitGate" : "ADR046-W6 exit criteria (ADR-046-validation-and-delivery §4): every spec/work item in this wave Merged with clean destinations, all validators green, and the ten-role panel seal recorded", + "id" : "ADR046-vl-007", + "kind" : "work-item", + "owner" : "ADR046-vl-002; ADR046-vl-003; ADR046-pstate-004 through ADR046-pstate-006", + "parallelGroup" : "wi:ADR-046-provider-volume-local", + "prerequisites" : [ + "ADR-046-provider-volume-local", + "ADR046-pstate-004", + "ADR046-pstate-005", + "ADR046-pstate-006", + "ADR046-vl-002", + "ADR046-vl-003" ], - "specId": "ADR-046-resources-zone-control", - "topologicalRank": 10, - "wave": "W5" + "specId" : "ADR-046-provider-volume-local", + "topologicalRank" : 14, + "wave" : "W6" }, { - "blockers": [], - "destinations": [ - "New crate `packages/d2b-provider-device-security-key/` with `src/`, `tests/`, `integration/`, `README.md` (workspace policy requires all four)" + "blockers" : [], + "destinations" : [ + "`src/relocation.rs`; `tests/relocation_unit.rs`; `integration/relocation.rs`" ], - "entryContracts": [ - "ADR-046-provider-device-security-key" + "entryContracts" : [ + "ADR-046-provider-volume-local" ], - "exitGate": "ADR046-W6 exit criteria (ADR-046-validation-and-delivery §4): every spec/work item in this wave Merged with clean destinations, all validators green, and the ten-role panel seal recorded", - "id": "W-N01", - "kind": "work-item", - "owner": "ADR-046 provider-device-security-key crate owner; depends on provider-model/package workspace policy.", - "parallelGroup": "wi:ADR-046-provider-device-security-key", - "prerequisites": [ - "ADR-046-provider-device-security-key" + "exitGate" : "ADR046-W6 exit criteria (ADR-046-validation-and-delivery §4): every spec/work item in this wave Merged with clean destinations, all validators green, and the ten-role panel seal recorded", + "id" : "ADR046-vl-008", + "kind" : "work-item", + "owner" : "ADR046-vl-003; ADR046-vl-007", + "parallelGroup" : "wi:ADR-046-provider-volume-local", + "prerequisites" : [ + "ADR-046-provider-volume-local", + "ADR046-vl-003", + "ADR046-vl-007" ], - "specId": "ADR-046-provider-device-security-key", - "topologicalRank": 11, - "wave": "W6" + "specId" : "ADR-046-provider-volume-local", + "topologicalRank" : 15, + "wave" : "W6" }, { - "blockers": [], - "destinations": [ - "`packages/d2b-provider-device-security-key/src/controller.rs`" + "blockers" : [], + "destinations" : [ + "`src/audit.rs`; `src/otel.rs`; `src/error.rs`; `tests/audit_unit.rs`; `integration/audit.rs`" ], - "entryContracts": [ - "ADR-046-provider-device-security-key" + "entryContracts" : [ + "ADR-046-provider-volume-local" ], - "exitGate": "ADR046-W6 exit criteria (ADR-046-validation-and-delivery §4): every spec/work item in this wave Merged with clean destinations, all validators green, and the ten-role panel seal recorded", - "id": "W-N02", - "kind": "work-item", - "owner": "Provider controller owner; depends on W-N06 probe, W-N09 templates, W-N18 effect port, W-N19 Service/Binding contracts, W-N20 status contract, and ADR-046-resource-reconciliation.", - "parallelGroup": "wi:ADR-046-provider-device-security-key", - "prerequisites": [ - "ADR-046-provider-device-security-key", - "W-N06", - "W-N09", - "W-N18", - "W-N19", - "W-N20" + "exitGate" : "ADR046-W6 exit criteria (ADR-046-validation-and-delivery §4): every spec/work item in this wave Merged with clean destinations, all validators green, and the ten-role panel seal recorded", + "id" : "ADR046-vl-009", + "kind" : "work-item", + "owner" : "ADR046-vl-001; Zone audit stream owner; `observability-otel` Provider owner", + "parallelGroup" : "wi:ADR-046-provider-volume-local", + "prerequisites" : [ + "ADR-046-provider-volume-local", + "ADR046-vl-001" ], - "specId": "ADR-046-provider-device-security-key", - "topologicalRank": 15, - "wave": "W6" + "specId" : "ADR-046-provider-volume-local", + "topologicalRank" : 12, + "wave" : "W6" }, { - "blockers": [], - "destinations": [ - "`packages/d2b-provider-device-security-key/src/relay.rs`" + "blockers" : [], + "destinations" : [ + "`nixos-modules/zone-resources.nix` (per §ADR046-pstate-010); `root-config.schema.json` in the Provider package" ], - "entryContracts": [ - "ADR-046-provider-device-security-key" + "entryContracts" : [ + "ADR-046-provider-volume-local" ], - "exitGate": "ADR046-W6 exit criteria (ADR-046-validation-and-delivery §4): every spec/work item in this wave Merged with clean destinations, all validators green, and the ten-role panel seal recorded", - "id": "W-N03", - "kind": "work-item", - "owner": "Authority Service relay owner; depends on W-R01, W-R02, W-N04, W-N05, W-N07, W-N09, and W-N11.", - "parallelGroup": "wi:ADR-046-provider-device-security-key", - "prerequisites": [ - "ADR-046-provider-device-security-key", - "W-N04", - "W-N05", - "W-N07", - "W-N09", - "W-N11", - "W-R01", - "W-R02" + "exitGate" : "ADR046-W6 exit criteria (ADR-046-validation-and-delivery §4): every spec/work item in this wave Merged with clean destinations, all validators green, and the ten-role panel seal recorded", + "id" : "ADR046-vl-010", + "kind" : "work-item", + "owner" : "ADR046-vl-001; ADR046-pstate-010; NixOS module owner", + "parallelGroup" : "wi:ADR-046-provider-volume-local", + "prerequisites" : [ + "ADR-046-provider-volume-local", + "ADR046-pstate-010", + "ADR046-vl-001" ], - "specId": "ADR-046-provider-device-security-key", - "topologicalRank": 16, - "wave": "W6" + "specId" : "ADR-046-provider-volume-local", + "topologicalRank" : 12, + "wave" : "W6" }, { - "blockers": [], - "destinations": [ - "`packages/d2b-provider-device-security-key/src/session.rs`" + "blockers" : [], + "destinations" : [ + "`packages/xtask/src/provider_crate_policy.rs`; `tests/unit/gates/provider-crate-layout-check.sh`" ], - "entryContracts": [ - "ADR-046-provider-device-security-key" + "entryContracts" : [ + "ADR-046-provider-volume-local" ], - "exitGate": "ADR046-W6 exit criteria (ADR-046-validation-and-delivery §4): every spec/work item in this wave Merged with clean destinations, all validators green, and the ten-role panel seal recorded", - "id": "W-N04", - "kind": "work-item", - "owner": "Relay ceremony-session foundation owner; depends on W-R01 and W-N01.", - "parallelGroup": "wi:ADR-046-provider-device-security-key", - "prerequisites": [ - "ADR-046-provider-device-security-key", - "W-N01", - "W-R01" + "exitGate" : "ADR046-W6 exit criteria (ADR-046-validation-and-delivery §4): every spec/work item in this wave Merged with clean destinations, all validators green, and the ten-role panel seal recorded", + "id" : "ADR046-vl-011", + "kind" : "work-item", + "owner" : "ADR046-vl-002; ADR046-pstate-011; workspace policy owner", + "parallelGroup" : "wi:ADR-046-provider-volume-local", + "prerequisites" : [ + "ADR-046-provider-volume-local", + "ADR046-pstate-011", + "ADR046-vl-002" ], - "specId": "ADR-046-provider-device-security-key", - "topologicalRank": 13, - "wave": "W6" + "specId" : "ADR-046-provider-volume-local", + "topologicalRank" : 13, + "wave" : "W6" }, { - "blockers": [], - "destinations": [ - "`packages/d2b-provider-device-security-key/src/cid.rs`" + "blockers" : [], + "destinations" : [ + "`packages/d2b-host/src/volume_effect_adapter.rs` (or the equivalent host-runtime crate designated by the Zone broker owner); implements the `VolumeEffectPort` trait defined in `d2b-contracts`" ], - "entryContracts": [ - "ADR-046-provider-device-security-key" + "entryContracts" : [ + "ADR-046-provider-volume-local" ], - "exitGate": "ADR046-W6 exit criteria (ADR-046-validation-and-delivery §4): every spec/work item in this wave Merged with clean destinations, all validators green, and the ten-role panel seal recorded", - "id": "W-N05", - "kind": "work-item", - "owner": "Relay CID-translation foundation owner; depends on W-R01 and W-N01.", - "parallelGroup": "wi:ADR-046-provider-device-security-key", - "prerequisites": [ - "ADR-046-provider-device-security-key", - "W-N01", - "W-R01" + "exitGate" : "ADR046-W6 exit criteria (ADR-046-validation-and-delivery §4): every spec/work item in this wave Merged with clean destinations, all validators green, and the ten-role panel seal recorded", + "id" : "ADR046-vl-012", + "kind" : "work-item", + "owner" : "ADR046-vl-001; ADR046-vl-002; Zone broker/core owner", + "parallelGroup" : "wi:ADR-046-provider-volume-local", + "prerequisites" : [ + "ADR-046-provider-volume-local", + "ADR046-vl-001", + "ADR046-vl-002" ], - "specId": "ADR-046-provider-device-security-key", - "topologicalRank": 13, - "wave": "W6" + "specId" : "ADR-046-provider-volume-local", + "topologicalRank" : 13, + "wave" : "W6" }, { - "blockers": [], - "destinations": [ - "`packages/d2b-provider-device-security-key/src/probe.rs`; Provider activation/Core private bundle device table population for label → `device_token`" + "blockers" : [], + "destinations" : [ + "Zone core ProviderDeployment controller-start path (outside `d2b-provider-volume-local`)" ], - "entryContracts": [ - "ADR-046-provider-device-security-key" + "entryContracts" : [ + "ADR-046-provider-volume-local" ], - "exitGate": "ADR046-W6 exit criteria (ADR-046-validation-and-delivery §4): every spec/work item in this wave Merged with clean destinations, all validators green, and the ten-role panel seal recorded", - "id": "W-N06", - "kind": "work-item", - "owner": "Probe/effect-port and activation owner; depends on W-N18 effect port and Core private bundle device table support.", - "parallelGroup": "wi:ADR-046-provider-device-security-key", - "prerequisites": [ - "ADR-046-provider-device-security-key", - "W-N18" + "exitGate" : "ADR046-W6 exit criteria (ADR-046-validation-and-delivery §4): every spec/work item in this wave Merged with clean destinations, all validators green, and the ten-role panel seal recorded", + "id" : "ADR046-vl-013", + "kind" : "work-item", + "owner" : "ADR046-vl-001; ADR046-vl-012; Zone broker/core owner", + "parallelGroup" : "wi:ADR-046-provider-volume-local", + "prerequisites" : [ + "ADR-046-provider-volume-local", + "ADR046-vl-001", + "ADR046-vl-012" ], - "specId": "ADR-046-provider-device-security-key", - "topologicalRank": 13, - "wave": "W6" + "specId" : "ADR-046-provider-volume-local", + "topologicalRank" : 14, + "wave" : "W6" }, { - "blockers": [], - "destinations": [ - "`packages/d2b-provider-device-security-key/src/descriptor.rs`" + "blockers" : [], + "destinations" : [ + "`packages/d2b-contracts/src/v3/volume.rs`, `volume_layout.rs`, `volume_attachment.rs`" ], - "entryContracts": [ - "ADR-046-provider-device-security-key" + "entryContracts" : [ + "ADR-046-resources-volume" ], - "exitGate": "ADR046-W6 exit criteria (ADR-046-validation-and-delivery §4): every spec/work item in this wave Merged with clean destinations, all validators green, and the ten-role panel seal recorded", - "id": "W-N07", - "kind": "work-item", - "owner": "ComponentSession/security descriptor contract owner; depends on W-N01, W-R05, and ADR-046-componentsession-and-bus.", - "parallelGroup": "wi:ADR-046-provider-device-security-key", - "prerequisites": [ - "ADR-046-provider-device-security-key", - "W-N01", - "W-R05" + "exitGate" : "ADR046-W5 exit criteria (ADR-046-validation-and-delivery §4): every spec/work item in this wave Merged with clean destinations, all validators green, and the ten-role panel seal recorded", + "id" : "ADR046-volume-001", + "kind" : "work-item", + "owner" : "W0 shared contract root; `d2b-contracts`", + "parallelGroup" : "wi:ADR-046-resources-volume", + "prerequisites" : [ + "ADR-046-resources-volume" ], - "specId": "ADR-046-provider-device-security-key", - "topologicalRank": 13, - "wave": "W6" + "specId" : "ADR-046-resources-volume", + "topologicalRank" : 10, + "wave" : "W5" }, { - "blockers": [], - "destinations": [ - "`nixos-modules/minijail-profiles.nix` entries for relay and controller; provider descriptor sandbox templates for relay/controller/frontend" + "blockers" : [], + "destinations" : [ + "`packages/d2b-provider-volume-local/src/` (layout engine, store_view.rs, swtpm_volume.rs, broker op adapters); `packages/d2b-provider-volume-local/tests/` (hermetic layout/store-view/swtpm tests); `packages/d2b-provider-volume-local/integration/` (container Host-path and store-view FS fixtures); `packages/d2b-provider-volume-local/README.md`" ], - "entryContracts": [ - "ADR-046-provider-device-security-key" + "entryContracts" : [ + "ADR-046-resources-volume" ], - "exitGate": "ADR046-W6 exit criteria (ADR-046-validation-and-delivery §4): every spec/work item in this wave Merged with clean destinations, all validators green, and the ten-role panel seal recorded", - "id": "W-N08", - "kind": "work-item", - "owner": "Sandbox/minijail foundation owner; depends on W-N01, W-R07, and ADR-046-components-processes-and-sandbox.", - "parallelGroup": "wi:ADR-046-provider-device-security-key", - "prerequisites": [ - "ADR-046-provider-device-security-key", - "W-N01", - "W-R07" + "exitGate" : "ADR046-W5 exit criteria (ADR-046-validation-and-delivery §4): every spec/work item in this wave Merged with clean destinations, all validators green, and the ten-role panel seal recorded", + "id" : "ADR046-volume-002", + "kind" : "work-item", + "owner" : "ADR046-volume-001; volume-local Provider owner", + "parallelGroup" : "wi:ADR-046-resources-volume", + "prerequisites" : [ + "ADR-046-resources-volume", + "ADR046-volume-001" ], - "specId": "ADR-046-provider-device-security-key", - "topologicalRank": 13, - "wave": "W6" + "specId" : "ADR-046-resources-volume", + "topologicalRank" : 11, + "wave" : "W5" }, { - "blockers": [], - "destinations": [ - "Provider descriptor Process templates and owned CTAPHID `Endpoint` template for `Provider/device-security-key`" + "blockers" : [], + "destinations" : [ + "`packages/d2b-provider-volume-virtiofs/src/` (controller, virtiofsd_argv.rs); `packages/d2b-provider-volume-virtiofs/tests/` (hermetic argv/lifecycle/ADR-0021 tests); `packages/d2b-provider-volume-virtiofs/integration/` (virtiofsd launch and guest-mount fixtures); `packages/d2b-provider-volume-virtiofs/README.md`" ], - "entryContracts": [ - "ADR-046-provider-device-security-key" + "entryContracts" : [ + "ADR-046-resources-volume" ], - "exitGate": "ADR046-W6 exit criteria (ADR-046-validation-and-delivery §4): every spec/work item in this wave Merged with clean destinations, all validators green, and the ten-role panel seal recorded", - "id": "W-N09", - "kind": "work-item", - "owner": "Provider process/Endpoint-template owner; depends on W-N01, W-N08, and W-N19 Service/Binding ownership contracts.", - "parallelGroup": "wi:ADR-046-provider-device-security-key", - "prerequisites": [ - "ADR-046-provider-device-security-key", - "W-N01", - "W-N08", - "W-N19" + "exitGate" : "ADR046-W5 exit criteria (ADR-046-validation-and-delivery §4): every spec/work item in this wave Merged with clean destinations, all validators green, and the ten-role panel seal recorded", + "id" : "ADR046-volume-003", + "kind" : "work-item", + "owner" : "ADR046-volume-001; volume-virtiofs Provider owner", + "parallelGroup" : "wi:ADR-046-resources-volume", + "prerequisites" : [ + "ADR-046-resources-volume", + "ADR046-volume-001" ], - "specId": "ADR-046-provider-device-security-key", - "topologicalRank": 14, - "wave": "W6" + "specId" : "ADR-046-resources-volume", + "topologicalRank" : 11, + "wave" : "W5" }, { - "blockers": [], - "destinations": [ - "Signed Provider descriptor JSON for `Provider/device-security-key` in the provider package" + "blockers" : [], + "destinations" : [ + "`nixos-modules/resources-volume.nix`, `nixos-modules/options-volumes.nix`" ], - "entryContracts": [ - "ADR-046-provider-device-security-key" + "entryContracts" : [ + "ADR-046-resources-volume" ], - "exitGate": "ADR046-W6 exit criteria (ADR-046-validation-and-delivery §4): every spec/work item in this wave Merged with clean destinations, all validators green, and the ten-role panel seal recorded", - "id": "W-N10", - "kind": "work-item", - "owner": "Provider package descriptor owner; depends on W-N01, W-R05, W-N07, W-N09, W-N19, W-N20, and ADR-046-provider-model-and-packaging.", - "parallelGroup": "wi:ADR-046-provider-device-security-key", - "prerequisites": [ - "ADR-046-provider-device-security-key", - "W-N01", - "W-N07", - "W-N09", - "W-N19", - "W-N20", - "W-R05" + "exitGate" : "ADR046-W5 exit criteria (ADR-046-validation-and-delivery §4): every spec/work item in this wave Merged with clean destinations, all validators green, and the ten-role panel seal recorded", + "id" : "ADR046-volume-004", + "kind" : "work-item", + "owner" : "ADR046-volume-001; Nix integrator", + "parallelGroup" : "wi:ADR-046-resources-volume", + "prerequisites" : [ + "ADR-046-resources-volume", + "ADR046-volume-001" ], - "specId": "ADR-046-provider-device-security-key", - "topologicalRank": 15, - "wave": "W6" + "specId" : "ADR-046-resources-volume", + "topologicalRank" : 11, + "wave" : "W5" }, { - "blockers": [], - "destinations": [ - "v3 `SecurityKeyOpenDevice` broker op and Core LaunchTicket DeviceGrant resolution path" + "blockers" : [], + "destinations" : [ + "`packages/d2b-provider-volume-local/src/` (block-image, quota, snapshots, tmpfs, ACL reconciliation); `packages/d2b-provider-volume-local/tests/` (hermetic quota/tmpfs/ACL/block-image/snapshot tests); `packages/d2b-provider-volume-local/integration/` (block-image virtio-blk, FS-without-quota fixture, tmpfs memory-budget); `packages/d2b-provider-volume-virtiofs/src/` (single-writer enforcement, shared-write, private socket path contract); `packages/d2b-provider-volume-virtiofs/tests/` (single-writer rejection, shared-write capability gate, socket-path invariant); `packages/d2b-provider-volume-virtiofs/integration/` (shared-write cross-Guest fixture)" ], - "entryContracts": [ - "ADR-046-provider-device-security-key" + "entryContracts" : [ + "ADR-046-resources-volume" ], - "exitGate": "ADR046-W6 exit criteria (ADR-046-validation-and-delivery §4): every spec/work item in this wave Merged with clean destinations, all validators green, and the ten-role panel seal recorded", - "id": "W-N11", - "kind": "work-item", - "owner": "Core LaunchTicket/broker owner; depends on W-R04, W-R05, W-N06, and ADR-046-resources-device.", - "parallelGroup": "wi:ADR-046-provider-device-security-key", - "prerequisites": [ - "ADR-046-provider-device-security-key", - "W-N06", - "W-R04", - "W-R05" + "exitGate" : "ADR046-W5 exit criteria (ADR-046-validation-and-delivery §4): every spec/work item in this wave Merged with clean destinations, all validators green, and the ten-role panel seal recorded", + "id" : "ADR046-volume-005", + "kind" : "work-item", + "owner" : "ADR046-volume-002, ADR046-volume-003; respective Provider owners", + "parallelGroup" : "wi:ADR-046-resources-volume", + "prerequisites" : [ + "ADR-046-resources-volume", + "ADR046-volume-002", + "ADR046-volume-003" ], - "specId": "ADR-046-provider-device-security-key", - "topologicalRank": 15, - "wave": "W6" + "specId" : "ADR-046-resources-volume", + "topologicalRank" : 12, + "wave" : "W5" }, { - "blockers": [], - "destinations": [ - "`nixos-modules/` resource compiler/eval assertions for physical Device, authority Service, ResourceExport/Import, and consumer Binding" + "blockers" : [], + "destinations" : [ + "`nixos-modules/resources-volume.nix` (Nix eval-time schema validation, canonical JSON emission, bundle digest); `packages/d2b-core-controller/src/configuration.rs` (config-publication handler cleanup logic); `packages/d2b-contracts/src/v3/zone_bundle.rs` (bundle index schema)" ], - "entryContracts": [ - "ADR-046-provider-device-security-key" + "entryContracts" : [ + "ADR-046-resources-volume" ], - "exitGate": "ADR046-W6 exit criteria (ADR-046-validation-and-delivery §4): every spec/work item in this wave Merged with clean destinations, all validators green, and the ten-role panel seal recorded", - "id": "W-N12", - "kind": "work-item", - "owner": "Nix resource compiler owner; depends on W-N10, W-N19, ADR046-zone-control-024, and ADR-046-nix-configuration.", - "parallelGroup": "wi:ADR-046-provider-device-security-key", - "prerequisites": [ - "ADR-046-provider-device-security-key", - "ADR046-zone-control-024", - "W-N10", - "W-N19" + "exitGate" : "ADR046-W5 exit criteria (ADR-046-validation-and-delivery §4): every spec/work item in this wave Merged with clean destinations, all validators green, and the ten-role panel seal recorded", + "id" : "ADR046-volume-006", + "kind" : "work-item", + "owner" : "ADR046-volume-001, ADR046-volume-004; Nix integrator and core-controller config-publication owner", + "parallelGroup" : "wi:ADR-046-resources-volume", + "prerequisites" : [ + "ADR-046-resources-volume", + "ADR046-volume-001", + "ADR046-volume-004" ], - "specId": "ADR-046-provider-device-security-key", - "topologicalRank": 16, - "wave": "W6" + "specId" : "ADR-046-resources-volume", + "topologicalRank" : 12, + "wave" : "W5" }, { - "blockers": [], - "destinations": [ - "`nixos-modules/components/security-key-guest.nix` migration gate `d2b.securityKey._legacySystemdUnit`" + "blockers" : [], + "destinations" : [ + "`packages/d2b-provider-transport-vsock/src/effect_port.rs`; test fake in `tests/effect_port_mock.rs`; redaction checks in `tests/redaction.rs`." ], - "entryContracts": [ - "ADR-046-provider-device-security-key" + "entryContracts" : [ + "ADR-046-provider-transport-vsock" ], - "exitGate": "ADR046-W6 exit criteria (ADR-046-validation-and-delivery §4): every spec/work item in this wave Merged with clean destinations, all validators green, and the ten-role panel seal recorded", - "id": "W-N13", - "kind": "work-item", - "owner": "Guest Nix migration owner; depends on W-R03, W-N09, and W-N19 Binding-owned frontend contract.", - "parallelGroup": "wi:ADR-046-provider-device-security-key", - "prerequisites": [ - "ADR-046-provider-device-security-key", - "W-N09", - "W-N19", - "W-R03" + "exitGate" : "ADR046-W6 exit criteria (ADR-046-validation-and-delivery §4): every spec/work item in this wave Merged with clean destinations, all validators green, and the ten-role panel seal recorded", + "id" : "ADR046-vsock-001", + "kind" : "work-item", + "owner" : "Title: Implement `VsockEffectPort` trait and `OpaqueEndpointId`/`OpaqueBindingId` newtypes; Phase 1; Priority P0; Depends on ADR046-session-003 (OwnedTransport in d2b-session); Owner crate `d2b-provider-transport-vsock`.", + "parallelGroup" : "wi:ADR-046-provider-transport-vsock", + "prerequisites" : [ + "ADR-046-provider-transport-vsock", + "ADR046-session-003" ], - "specId": "ADR-046-provider-device-security-key", - "topologicalRank": 15, - "wave": "W6" + "specId" : "ADR-046-provider-transport-vsock", + "topologicalRank" : 11, + "wave" : "W6" }, { - "blockers": [], - "destinations": [ - "Core `device-grant` audit and Provider controller Service/Binding ceremony lifecycle audit" + "blockers" : [], + "destinations" : [ + "`packages/d2b-provider-transport-vsock/src/framing.rs` and `src/bridge.rs`; tests in `packages/d2b-provider-transport-vsock/tests/framing.rs`." ], - "entryContracts": [ - "ADR-046-provider-device-security-key" + "entryContracts" : [ + "ADR-046-provider-transport-vsock" ], - "exitGate": "ADR046-W6 exit criteria (ADR-046-validation-and-delivery §4): every spec/work item in this wave Merged with clean destinations, all validators green, and the ten-role panel seal recorded", - "id": "W-N14", - "kind": "work-item", - "owner": "Audit owner for Core device-grant and Service/Binding lifecycle; depends on W-N02, W-N11, and ADR-046-telemetry-audit-and-support.", - "parallelGroup": "wi:ADR-046-provider-device-security-key", - "prerequisites": [ - "ADR-046-provider-device-security-key", - "W-N02", - "W-N11" + "exitGate" : "ADR046-W6 exit criteria (ADR-046-validation-and-delivery §4): every spec/work item in this wave Merged with clean destinations, all validators green, and the ten-role panel seal recorded", + "id" : "ADR046-vsock-002", + "kind" : "work-item", + "owner" : "Title: Implement framing utilities and bridge task in Provider crate; Phase 1; Priority P0; Depends on ADR046-vsock-001; Owner crate `d2b-provider-transport-vsock`.", + "parallelGroup" : "wi:ADR-046-provider-transport-vsock", + "prerequisites" : [ + "ADR-046-provider-transport-vsock", + "ADR046-vsock-001" ], - "specId": "ADR-046-provider-device-security-key", - "topologicalRank": 16, - "wave": "W6" + "specId" : "ADR-046-provider-transport-vsock", + "topologicalRank" : 12, + "wave" : "W6" }, { - "blockers": [], - "destinations": [ - "Provider/controller bounded telemetry emitter and observability-otel handoff for security-key metrics" + "blockers" : [], + "destinations" : [ + "`packages/d2b-provider-transport-vsock/src/service.rs`; tests `tests/open_close.rs`, `tests/observe.rs`, and conformance kit." ], - "entryContracts": [ - "ADR-046-provider-device-security-key" + "entryContracts" : [ + "ADR-046-provider-transport-vsock" ], - "exitGate": "ADR046-W6 exit criteria (ADR-046-validation-and-delivery §4): every spec/work item in this wave Merged with clean destinations, all validators green, and the ten-role panel seal recorded", - "id": "W-N15", - "kind": "work-item", - "owner": "Observability owner; depends on W-N03 relay, W-N02 controller, and ADR-046-telemetry-audit-and-support.", - "parallelGroup": "wi:ADR-046-provider-device-security-key", - "prerequisites": [ - "ADR-046-provider-device-security-key", - "W-N02", - "W-N03" + "exitGate" : "ADR046-W6 exit criteria (ADR-046-validation-and-delivery §4): every spec/work item in this wave Merged with clean destinations, all validators green, and the ten-role panel seal recorded", + "id" : "ADR046-vsock-003", + "kind" : "work-item", + "owner" : "Title: Implement `VsockTransportService` (OpenTransport/CloseTransport/ObserveTransport); Phase 1; Priority P0; Depends on ADR046-vsock-002 and ADR046-session-003; Owner crate `d2b-provider-transport-vsock`.", + "parallelGroup" : "wi:ADR-046-provider-transport-vsock", + "prerequisites" : [ + "ADR-046-provider-transport-vsock", + "ADR046-session-003", + "ADR046-vsock-002" ], - "specId": "ADR-046-provider-device-security-key", - "topologicalRank": 17, - "wave": "W6" + "specId" : "ADR-046-provider-transport-vsock", + "topologicalRank" : 13, + "wave" : "W6" }, { - "blockers": [], - "destinations": [ - "`packages/d2b-provider-device-security-key/README.md`" + "blockers" : [], + "destinations" : [ + "`d2b-core-controller` Zone runtime `LiveVsockEffectPort`; Provider receives it by dependency injection at startup." ], - "entryContracts": [ - "ADR-046-provider-device-security-key" + "entryContracts" : [ + "ADR-046-provider-transport-vsock" ], - "exitGate": "ADR046-W6 exit criteria (ADR-046-validation-and-delivery §4): every spec/work item in this wave Merged with clean destinations, all validators green, and the ten-role panel seal recorded", - "id": "W-N16", - "kind": "work-item", - "owner": "Provider documentation owner; depends on W-N01 through W-N15 and W-N17 through W-N22 for complete behavior.", - "parallelGroup": "wi:ADR-046-provider-device-security-key", - "prerequisites": [ - "ADR-046-provider-device-security-key", - "W-N01", - "W-N15", - "W-N17", - "W-N22" + "exitGate" : "ADR046-W6 exit criteria (ADR-046-validation-and-delivery §4): every spec/work item in this wave Merged with clean destinations, all validators green, and the ten-role panel seal recorded", + "id" : "ADR046-vsock-004", + "kind" : "work-item", + "owner" : "Title: Implement `LiveVsockEffectPort` in Zone runtime; Phase 2; Priority P0; Depends on ADR046-vsock-001 and the Zone allocator (`ADR-046-resources-zone-control`); Owner crate `d2b-core-controller`.", + "parallelGroup" : "wi:ADR-046-provider-transport-vsock", + "prerequisites" : [ + "ADR-046-provider-transport-vsock", + "ADR046-vsock-001" ], - "specId": "ADR-046-provider-device-security-key", - "topologicalRank": 18, - "wave": "W6" + "specId" : "ADR-046-provider-transport-vsock", + "topologicalRank" : 12, + "wave" : "W6" }, { - "blockers": [], - "destinations": [ - "Authority/projection Service Endpoint and Binding private Endpoint resolution, including transport-vsock and ZoneLink encrypted streams" + "blockers" : [], + "destinations" : [ + "ProviderDeployment Volume creation/deletion path plus `packages/d2b-provider-transport-vsock/tests/state_volume.rs`." ], - "entryContracts": [ - "ADR-046-provider-device-security-key" + "entryContracts" : [ + "ADR-046-provider-transport-vsock" ], - "exitGate": "ADR046-W6 exit criteria (ADR-046-validation-and-delivery §4): every spec/work item in this wave Merged with clean destinations, all validators green, and the ten-role panel seal recorded", - "id": "W-N17", - "kind": "work-item", - "owner": "Endpoint/ComponentSession integration owner; depends on W-R03, W-N03, W-N07, W-N09, W-N19, and ADR-046-componentsession-and-bus.", - "parallelGroup": "wi:ADR-046-provider-device-security-key", - "prerequisites": [ - "ADR-046-provider-device-security-key", - "W-N03", - "W-N07", - "W-N09", - "W-N19", - "W-R03" + "exitGate" : "ADR046-W6 exit criteria (ADR-046-validation-and-delivery §4): every spec/work item in this wave Merged with clean destinations, all validators green, and the ten-role panel seal recorded", + "id" : "ADR046-vsock-005", + "kind" : "work-item", + "owner" : "Title: Core ProviderDeployment creates/deletes service component state Volume; Phase 1; Priority P0; Depends on the volume-local Provider (`ADR-046-provider-volume-local`); Owner crate `d2b-provider-transport-vsock`.", + "parallelGroup" : "wi:ADR-046-provider-transport-vsock", + "prerequisites" : [ + "ADR-046-provider-transport-vsock" ], - "specId": "ADR-046-provider-device-security-key", - "topologicalRank": 17, - "wave": "W6" + "specId" : "ADR-046-provider-transport-vsock", + "topologicalRank" : 11, + "wave" : "W6" }, { - "blockers": [], - "destinations": [ - "`d2b-contracts` neutral `SecurityKeyEffectPort` trait/types; `packages/d2b-provider-device-security-key/src/effect_port.rs` re-export; Core adapter implementation in `d2b-provider` or `d2b-provider-toolkit`" + "blockers" : [], + "destinations" : [ + "`packages/d2b-provider-transport-vsock/integration/host_guest.rs` and `integration/no_fd_transfer.rs`." ], - "entryContracts": [ - "ADR-046-provider-device-security-key" + "entryContracts" : [ + "ADR-046-provider-transport-vsock" ], - "exitGate": "ADR046-W6 exit criteria (ADR-046-validation-and-delivery §4): every spec/work item in this wave Merged with clean destinations, all validators green, and the ten-role panel seal recorded", - "id": "W-N18", - "kind": "work-item", - "owner": "`d2b-contracts` neutral effect-port foundation owner; depends on W-N01 and ADR-046-resources-device.", - "parallelGroup": "wi:ADR-046-provider-device-security-key", - "prerequisites": [ - "ADR-046-provider-device-security-key", - "W-N01" + "exitGate" : "ADR046-W6 exit criteria (ADR-046-validation-and-delivery §4): every spec/work item in this wave Merged with clean destinations, all validators green, and the ten-role panel seal recorded", + "id" : "ADR046-vsock-006", + "kind" : "work-item", + "owner" : "Title: Integration test: real vsock socketpair + full ZoneLink open/close; Phase 2; Priority P1; Depends on ADR046-vsock-003 and ADR046-vsock-004; Owner crate `d2b-provider-transport-vsock`.", + "parallelGroup" : "wi:ADR-046-provider-transport-vsock", + "prerequisites" : [ + "ADR-046-provider-transport-vsock", + "ADR046-vsock-003", + "ADR046-vsock-004" ], - "specId": "ADR-046-provider-device-security-key", - "topologicalRank": 12, - "wave": "W6" + "specId" : "ADR-046-provider-transport-vsock", + "topologicalRank" : 14, + "wave" : "W6" }, { - "blockers": [], - "destinations": [ - "`packages/d2b-provider-device-security-key/src/{resource_type,provider_extension,admission}.rs`; controller contracts; system-core Guest UHID authority-subresource DeviceGrant (common base lives under ADR046-provider-004)" + "blockers" : [], + "destinations" : [ + "Remove legacy paths from `d2b-host` and `d2bd`; replacement lives in `observability-otel` Provider native vsock relay and Guest resource lifecycle/bootstrap." ], - "entryContracts": [ - "ADR-046-provider-device-security-key" + "entryContracts" : [ + "ADR-046-provider-transport-vsock" ], - "exitGate": "ADR046-W6 exit criteria (ADR-046-validation-and-delivery §4): every spec/work item in this wave Merged with clean destinations, all validators green, and the ten-role panel seal recorded", - "id": "W-N19", - "kind": "work-item", - "owner": "Device-security-key Service/Binding implementation owner; depends on ADR046-provider-004, W-N01, W-R05, resource object/Device/D096/D097 contracts.", - "parallelGroup": "wi:ADR-046-provider-device-security-key", - "prerequisites": [ - "ADR-046-provider-device-security-key", - "ADR046-provider-004", - "W-N01", - "W-R05" + "exitGate" : "ADR046-W6 exit criteria (ADR-046-validation-and-delivery §4): every spec/work item in this wave Merged with clean destinations, all validators green, and the ten-role panel seal recorded", + "id" : "ADR046-vsock-007", + "kind" : "work-item", + "owner" : "Title: Delete legacy socat OTLP relay and CONNECT-proxy guest-control vsock; Phase 3; Priority P2; Depends on the observability-otel Provider (`ADR-046-provider-observability-otel`) and the Guest resource lifecycle (`ADR-046-resources-host-guest-process-user`); Owner crates `d2b-host`, `d2bd`.", + "parallelGroup" : "wi:ADR-046-provider-transport-vsock", + "prerequisites" : [ + "ADR-046-provider-transport-vsock" ], - "specId": "ADR-046-provider-device-security-key", - "topologicalRank": 13, - "wave": "W6" + "specId" : "ADR-046-provider-transport-vsock", + "topologicalRank" : 11, + "wave" : "W6" }, { - "blockers": [], - "destinations": [ - "Provider descriptor state declaration, controller/status logic, Process templates, and Nix principal provisioning for `Provider/device-security-key`" + "blockers" : [], + "destinations" : [ + "`packages/d2b-provider-volume-virtiofs/src/virtiofsd_argv.rs`; `packages/d2b-provider-volume-virtiofs/tests/argv_golden.rs`" ], - "entryContracts": [ - "ADR-046-provider-device-security-key" + "entryContracts" : [ + "ADR-046-provider-volume-virtiofs" ], - "exitGate": "ADR046-W6 exit criteria (ADR-046-validation-and-delivery §4): every spec/work item in this wave Merged with clean destinations, all validators green, and the ten-role panel seal recorded", - "id": "W-N20", - "kind": "work-item", - "owner": "Provider state/status contract owner; depends on W-N01, W-N19, and ADR-046-provider-state.", - "parallelGroup": "wi:ADR-046-provider-device-security-key", - "prerequisites": [ - "ADR-046-provider-device-security-key", - "W-N01", - "W-N19" + "exitGate" : "ADR046-W6 exit criteria (ADR-046-validation-and-delivery §4): every spec/work item in this wave Merged with clean destinations, all validators green, and the ten-role panel seal recorded", + "id" : "ADR046-vvfs-001", + "kind" : "work-item", + "owner" : "ADR046-volume-001 (Volume contract types); ADR046-vvfs-007 (Export type); W1; volume-virtiofs Provider owner", + "parallelGroup" : "wi:ADR-046-provider-volume-virtiofs", + "prerequisites" : [ + "ADR-046-provider-volume-virtiofs", + "ADR046-volume-001", + "ADR046-vvfs-007" ], - "specId": "ADR-046-provider-device-security-key", - "topologicalRank": 14, - "wave": "W6" + "specId" : "ADR-046-provider-volume-virtiofs", + "topologicalRank" : 12, + "wave" : "W6" }, { - "blockers": [], - "destinations": [ - "`packages/d2b-provider-device-security-key/src/share_adapter.rs`" + "blockers" : [], + "destinations" : [ + "`packages/d2b-provider-volume-virtiofs/src/user_ns.rs` (conformance kit); `packages/d2b-provider-volume-virtiofs/tests/adr021_invariant.rs`" ], - "entryContracts": [ - "ADR-046-provider-device-security-key" + "entryContracts" : [ + "ADR-046-provider-volume-virtiofs" ], - "exitGate": "ADR046-W6 exit criteria (ADR-046-validation-and-delivery §4): every spec/work item in this wave Merged with clean destinations, all validators green, and the ten-role panel seal recorded", - "id": "W-N21", - "kind": "work-item", - "owner": "Cross-Zone adapter owner; depends on W-N17, W-N19, W-N22, ADR046-zone-control-019, and ADR046-zone-control-020.", - "parallelGroup": "wi:ADR-046-provider-device-security-key", - "prerequisites": [ - "ADR-046-provider-device-security-key", - "ADR046-zone-control-019", - "ADR046-zone-control-020", - "W-N17", - "W-N19", - "W-N22" + "exitGate" : "ADR046-W6 exit criteria (ADR-046-validation-and-delivery §4): every spec/work item in this wave Merged with clean destinations, all validators green, and the ten-role panel seal recorded", + "id" : "ADR046-vvfs-002", + "kind" : "work-item", + "owner" : "ADR046-vvfs-001; ADR046-volume-001; W1; broker/spawn owner", + "parallelGroup" : "wi:ADR-046-provider-volume-virtiofs", + "prerequisites" : [ + "ADR-046-provider-volume-virtiofs", + "ADR046-volume-001", + "ADR046-vvfs-001" ], - "specId": "ADR-046-provider-device-security-key", - "topologicalRank": 18, - "wave": "W6" + "specId" : "ADR-046-provider-volume-virtiofs", + "topologicalRank" : 13, + "wave" : "W6" }, { - "blockers": [], - "destinations": [ - "`packages/d2b-provider-device-security-key/src/{authority,relay,streams}.rs`; D097 `AuthorityDescriptor` on authority SecurityKeyService" + "blockers" : [], + "destinations" : [ + "`packages/d2b-provider-volume-virtiofs/src/controller.rs`; `packages/d2b-provider-volume-virtiofs/src/export.rs`" ], - "entryContracts": [ - "ADR-046-provider-device-security-key" + "entryContracts" : [ + "ADR-046-provider-volume-virtiofs" ], - "exitGate": "ADR046-W6 exit criteria (ADR-046-validation-and-delivery §4): every spec/work item in this wave Merged with clean destinations, all validators green, and the ten-role panel seal recorded", - "id": "W-N22", - "kind": "work-item", - "owner": "D097 authority foundation owner; depends on W-R01, W-R02, W-R03, W-R04, W-N11, W-N19, ADR046-zone-control-024, and the D097 authority contract.", - "parallelGroup": "wi:ADR-046-provider-device-security-key", - "prerequisites": [ - "ADR-046-provider-device-security-key", - "ADR046-zone-control-024", - "W-N11", - "W-N19", - "W-R01", - "W-R02", - "W-R03", - "W-R04" + "exitGate" : "ADR046-W6 exit criteria (ADR-046-validation-and-delivery §4): every spec/work item in this wave Merged with clean destinations, all validators green, and the ten-role panel seal recorded", + "id" : "ADR046-vvfs-003", + "kind" : "work-item", + "owner" : "ADR046-vvfs-001, ADR046-vvfs-002, ADR046-vvfs-007; ADR046-volume-001; W2; volume-virtiofs controller owner", + "parallelGroup" : "wi:ADR-046-provider-volume-virtiofs", + "prerequisites" : [ + "ADR-046-provider-volume-virtiofs", + "ADR046-volume-001", + "ADR046-vvfs-001", + "ADR046-vvfs-002", + "ADR046-vvfs-007" ], - "specId": "ADR-046-provider-device-security-key", - "topologicalRank": 16, - "wave": "W6" + "specId" : "ADR-046-provider-volume-virtiofs", + "topologicalRank" : 14, + "wave" : "W6" }, { - "blockers": [], - "destinations": [ - "Move to `packages/d2b-provider-device-security-key/src/session.rs` and `cid.rs`; adapt to Provider Process model (remove daemon Mutex wrapping, add async relay protocol)" + "blockers" : [], + "destinations" : [ + "`packages/d2b-provider-volume-virtiofs/src/readiness.rs`; `packages/d2b-provider-volume-virtiofs/integration/guest_mount_readiness/`" ], - "entryContracts": [ - "ADR-046-provider-device-security-key" + "entryContracts" : [ + "ADR-046-provider-volume-virtiofs" ], - "exitGate": "ADR046-W6 exit criteria (ADR-046-validation-and-delivery §4): every spec/work item in this wave Merged with clean destinations, all validators green, and the ten-role panel seal recorded", - "id": "W-R01", - "kind": "work-item", - "owner": "ADR-046 provider-device-security-key session/relay owner; depends on W-N01 and the ComponentSession/Process contracts.", - "parallelGroup": "wi:ADR-046-provider-device-security-key", - "prerequisites": [ - "ADR-046-provider-device-security-key", - "W-N01" + "exitGate" : "ADR046-W6 exit criteria (ADR-046-validation-and-delivery §4): every spec/work item in this wave Merged with clean destinations, all validators green, and the ten-role panel seal recorded", + "id" : "ADR046-vvfs-004", + "kind" : "work-item", + "owner" : "ADR046-vvfs-003; guest-control integration owner; W2", + "parallelGroup" : "wi:ADR-046-provider-volume-virtiofs", + "prerequisites" : [ + "ADR-046-provider-volume-virtiofs", + "ADR046-vvfs-003" ], - "specId": "ADR-046-provider-device-security-key", - "topologicalRank": 12, - "wave": "W6" + "specId" : "ADR-046-provider-volume-virtiofs", + "topologicalRank" : 15, + "wave" : "W6" }, { - "blockers": [], - "destinations": [ - "Move to `packages/d2b-provider-device-security-key/src/relay.rs`; replace daemon-internal Unix socket proxy with ComponentSession over the owned Service Endpoint" + "blockers" : [], + "destinations" : [ + "`packages/d2b-provider-volume-virtiofs/src/controller.rs` (pre-launch prerequisite check); `packages/d2b-provider-volume-virtiofs/integration/store_view_readonly/`" ], - "entryContracts": [ - "ADR-046-provider-device-security-key" + "entryContracts" : [ + "ADR-046-provider-volume-virtiofs" ], - "exitGate": "ADR046-W6 exit criteria (ADR-046-validation-and-delivery §4): every spec/work item in this wave Merged with clean destinations, all validators green, and the ten-role panel seal recorded", - "id": "W-R02", - "kind": "work-item", - "owner": "ADR-046 provider-device-security-key relay extraction owner; depends on W-N01, W-R01, and the frozen ComponentSession/Endpoint contracts.", - "parallelGroup": "wi:ADR-046-provider-device-security-key", - "prerequisites": [ - "ADR-046-provider-device-security-key", - "W-N01", - "W-R01" + "exitGate" : "ADR046-W6 exit criteria (ADR-046-validation-and-delivery §4): every spec/work item in this wave Merged with clean destinations, all validators green, and the ten-role panel seal recorded", + "id" : "ADR046-vvfs-005", + "kind" : "work-item", + "owner" : "ADR046-vvfs-003, ADR046-vvfs-004; ADR046-volume-002 (store-view Volume); W3", + "parallelGroup" : "wi:ADR-046-provider-volume-virtiofs", + "prerequisites" : [ + "ADR-046-provider-volume-virtiofs", + "ADR046-volume-002", + "ADR046-vvfs-003", + "ADR046-vvfs-004" ], - "specId": "ADR-046-provider-device-security-key", - "topologicalRank": 13, - "wave": "W6" + "specId" : "ADR-046-provider-volume-virtiofs", + "topologicalRank" : 16, + "wave" : "W6" }, { - "blockers": [], - "destinations": [ - "Adopt `main.rs` and `uhid.rs` as the v3 Process binary entry point; replace `framing.rs`/`vsock.rs` with ComponentSession client from `d2b-session-unix/src/vsock.rs`; wire as Process service in Provider crate" + "blockers" : [], + "destinations" : [ + "`nixos-modules/resources-volume.nix` (store-view and user Volume attachment emission); `nixos-modules/options-volumes.nix` (optional user-facing volume/attachment options)" ], - "entryContracts": [ - "ADR-046-provider-device-security-key" + "entryContracts" : [ + "ADR-046-provider-volume-virtiofs" ], - "exitGate": "ADR046-W6 exit criteria (ADR-046-validation-and-delivery §4): every spec/work item in this wave Merged with clean destinations, all validators green, and the ten-role panel seal recorded", - "id": "W-R03", - "kind": "work-item", - "owner": "ADR-046 provider-device-security-key frontend extraction owner; depends on W-N01 and frozen Process/ComponentSession contracts.", - "parallelGroup": "wi:ADR-046-provider-device-security-key", - "prerequisites": [ - "ADR-046-provider-device-security-key", - "W-N01" + "exitGate" : "ADR046-W6 exit criteria (ADR-046-validation-and-delivery §4): every spec/work item in this wave Merged with clean destinations, all validators green, and the ten-role panel seal recorded", + "id" : "ADR046-vvfs-006", + "kind" : "work-item", + "owner" : "ADR046-vvfs-001, ADR046-volume-004; Nix integrator; W3", + "parallelGroup" : "wi:ADR-046-provider-volume-virtiofs", + "prerequisites" : [ + "ADR-046-provider-volume-virtiofs", + "ADR046-volume-004", + "ADR046-vvfs-001" ], - "specId": "ADR-046-provider-device-security-key", - "topologicalRank": 12, - "wave": "W6" + "specId" : "ADR-046-provider-volume-virtiofs", + "topologicalRank" : 13, + "wave" : "W6" }, { - "blockers": [], - "destinations": [ - "Preserve revalidation logic; update `SecurityKeyOpenDevice` to use bundle device table `device_token` as sole open target (no iterative sysfs scan); add zone-field handling; remove sysfs fallback. **Core's LaunchTicket calls this internally; the Provider does not call it.**" + "blockers" : [], + "destinations" : [ + "`packages/d2b-provider-volume-virtiofs/src/export.rs`; `packages/d2b-contracts/src/v3/virtiofs_export.rs`" ], - "entryContracts": [ - "ADR-046-provider-device-security-key" + "entryContracts" : [ + "ADR-046-provider-volume-virtiofs" ], - "exitGate": "ADR046-W6 exit criteria (ADR-046-validation-and-delivery §4): every spec/work item in this wave Merged with clean destinations, all validators green, and the ten-role panel seal recorded", - "id": "W-R04", - "kind": "work-item", - "owner": "Core LaunchTicket/privileged broker reuse owner; depends on ADR-046-resources-device and W-N06 probe/device-token population.", - "parallelGroup": "wi:ADR-046-provider-device-security-key", - "prerequisites": [ - "ADR-046-provider-device-security-key", - "W-N06" + "exitGate" : "ADR046-W6 exit criteria (ADR-046-validation-and-delivery §4): every spec/work item in this wave Merged with clean destinations, all validators green, and the ten-role panel seal recorded", + "id" : "ADR046-vvfs-007", + "kind" : "work-item", + "owner" : "ADR046-volume-001; W1; volume-virtiofs Provider owner", + "parallelGroup" : "wi:ADR-046-provider-volume-virtiofs", + "prerequisites" : [ + "ADR-046-provider-volume-virtiofs", + "ADR046-volume-001" ], - "specId": "ADR-046-provider-device-security-key", - "topologicalRank": 14, - "wave": "W6" + "specId" : "ADR-046-provider-volume-virtiofs", + "topologicalRank" : 11, + "wave" : "W6" }, { - "blockers": [], - "destinations": [ - "Adapt to v3 Zone/ResourceRef identifiers; preserve serde shapes for zero downstream breakage where possible; remove `SecurityKeyApplyUdevRulesRequest` (W-X06)" + "blockers" : [], + "destinations" : [ + "`packages/d2b-contracts/src/v3/zone.rs`; `packages/d2b-core-controller/src/zone.rs`" ], - "entryContracts": [ - "ADR-046-provider-device-security-key" + "entryContracts" : [ + "ADR-046-resources-zone-control" ], - "exitGate": "ADR046-W6 exit criteria (ADR-046-validation-and-delivery §4): every spec/work item in this wave Merged with clean destinations, all validators green, and the ten-role panel seal recorded", - "id": "W-R05", - "kind": "work-item", - "owner": "`d2b-contracts` security-key ceremony/effect DTO owner; depends on ADR046-provider-004, ADR-046-resource-object-model, ADR-046-resources-device, and W-N01.", - "parallelGroup": "wi:ADR-046-provider-device-security-key", - "prerequisites": [ - "ADR-046-provider-device-security-key", - "ADR046-provider-004", - "W-N01" + "exitGate" : "ADR046-W5 exit criteria (ADR-046-validation-and-delivery §4): every spec/work item in this wave Merged with clean destinations, all validators green, and the ten-role panel seal recorded", + "id" : "ADR046-zone-control-001", + "kind" : "work-item", + "owner" : "ADR046-object-001 (resource envelope); ADR046-store-001 (redb store); ADR046-identities-001 (types)", + "parallelGroup" : "wi:ADR-046-resources-zone-control", + "prerequisites" : [ + "ADR-046-resources-zone-control", + "ADR046-identities-001", + "ADR046-object-001", + "ADR046-store-001" ], - "specId": "ADR-046-provider-device-security-key", - "topologicalRank": 12, - "wave": "W6" + "specId" : "ADR-046-resources-zone-control", + "topologicalRank" : 10, + "wave" : "W5" }, { - "blockers": [], - "destinations": [ - "Move to `packages/d2b-provider-device-security-key/tests/`; update imports and v3 type names" + "blockers" : [], + "destinations" : [ + "`packages/d2b-contracts/src/v3/zone_link.rs`; `packages/d2b-core-controller/src/zone_link.rs`" ], - "entryContracts": [ - "ADR-046-provider-device-security-key" + "entryContracts" : [ + "ADR-046-resources-zone-control" ], - "exitGate": "ADR046-W6 exit criteria (ADR-046-validation-and-delivery §4): every spec/work item in this wave Merged with clean destinations, all validators green, and the ten-role panel seal recorded", - "id": "W-R06", - "kind": "work-item", - "owner": "Provider crate test owner; depends on W-R05 v3 DTOs and W-N01 provider crate layout.", - "parallelGroup": "wi:ADR-046-provider-device-security-key", - "prerequisites": [ - "ADR-046-provider-device-security-key", - "W-N01", - "W-R05" + "exitGate" : "ADR046-W5 exit criteria (ADR-046-validation-and-delivery §4): every spec/work item in this wave Merged with clean destinations, all validators green, and the ten-role panel seal recorded", + "id" : "ADR046-zone-control-002", + "kind" : "work-item", + "owner" : "ADR046-zone-control-001; ADR046-zone-control-018 (ComponentSession)", + "parallelGroup" : "wi:ADR-046-resources-zone-control", + "prerequisites" : [ + "ADR-046-resources-zone-control", + "ADR046-zone-control-001", + "ADR046-zone-control-018" ], - "specId": "ADR-046-provider-device-security-key", - "topologicalRank": 13, - "wave": "W6" + "specId" : "ADR-046-resources-zone-control", + "topologicalRank" : 13, + "wave" : "W5" }, { - "blockers": [], - "destinations": [ - "Move to `packages/d2b-provider-device-security-key/tests/`; update for v3 Process resource minijail profile; retain zero-capabilities assertion" + "blockers" : [], + "destinations" : [ + "`packages/d2b-contracts/src/v3/provider.rs`; `packages/d2b-core-controller/src/provider_lifecycle.rs`; `packages/d2b-core-controller/src/api_catalog.rs`" ], - "entryContracts": [ - "ADR-046-provider-device-security-key" + "entryContracts" : [ + "ADR-046-resources-zone-control" ], - "exitGate": "ADR046-W6 exit criteria (ADR-046-validation-and-delivery §4): every spec/work item in this wave Merged with clean destinations, all validators green, and the ten-role panel seal recorded", - "id": "W-R07", - "kind": "work-item", - "owner": "Provider crate test/minijail adaptation owner; depends on W-N01 and the frozen Process sandbox contract.", - "parallelGroup": "wi:ADR-046-provider-device-security-key", - "prerequisites": [ - "ADR-046-provider-device-security-key", - "W-N01" + "exitGate" : "ADR046-W5 exit criteria (ADR-046-validation-and-delivery §4): every spec/work item in this wave Merged with clean destinations, all validators green, and the ten-role panel seal recorded", + "id" : "ADR046-zone-control-003", + "kind" : "work-item", + "owner" : "ADR046-zone-control-001; ADR046-api-001; ADR046-zone-control-017", + "parallelGroup" : "wi:ADR-046-resources-zone-control", + "prerequisites" : [ + "ADR-046-resources-zone-control", + "ADR046-api-001", + "ADR046-zone-control-001", + "ADR046-zone-control-017" ], - "specId": "ADR-046-provider-device-security-key", - "topologicalRank": 12, - "wave": "W6" + "specId" : "ADR-046-resources-zone-control", + "topologicalRank" : 12, + "wave" : "W5" }, { - "blockers": [], - "destinations": [ - "Removed from daemon; successor behavior lives in `packages/d2b-provider-device-security-key/src/relay.rs`, `session.rs`, and `cid.rs`" + "blockers" : [], + "destinations" : [ + "`packages/d2b-contracts/src/v3/role.rs`; `packages/d2b-core-controller/src/authz.rs`" ], - "entryContracts": [ - "ADR-046-provider-device-security-key" + "entryContracts" : [ + "ADR-046-resources-zone-control" ], - "exitGate": "ADR046-W6 exit criteria (ADR-046-validation-and-delivery §4): every spec/work item in this wave Merged with clean destinations, all validators green, and the ten-role panel seal recorded", - "id": "W-X01", - "kind": "work-item", - "owner": "Provider-device-security-key removal owner; depends on W-R01, W-R02, W-N03, W-N04, and W-N05 successor relay/session coverage.", - "parallelGroup": "wi:ADR-046-provider-device-security-key", - "prerequisites": [ - "ADR-046-provider-device-security-key", - "W-N03", - "W-N04", - "W-N05", - "W-R01", - "W-R02" + "exitGate" : "ADR046-W5 exit criteria (ADR-046-validation-and-delivery §4): every spec/work item in this wave Merged with clean destinations, all validators green, and the ten-role panel seal recorded", + "id" : "ADR046-zone-control-004", + "kind" : "work-item", + "owner" : "ADR046-zone-control-001; ADR046-api-002", + "parallelGroup" : "wi:ADR-046-resources-zone-control", + "prerequisites" : [ + "ADR-046-resources-zone-control", + "ADR046-api-002", + "ADR046-zone-control-001" ], - "specId": "ADR-046-provider-device-security-key", - "topologicalRank": 17, - "wave": "W6" + "specId" : "ADR-046-resources-zone-control", + "topologicalRank" : 11, + "wave" : "W5" }, { - "blockers": [], - "destinations": [ - "Removed from daemon startup; successor launch path is ProviderDeployment/controller-created relay Process plus Endpoint/ComponentSession transport" + "blockers" : [], + "destinations" : [ + "`packages/d2b-contracts/src/v3/role_binding.rs`; `packages/d2b-core-controller/src/authz.rs` (shared with Role handler)" ], - "entryContracts": [ - "ADR-046-provider-device-security-key" + "entryContracts" : [ + "ADR-046-resources-zone-control" ], - "exitGate": "ADR046-W6 exit criteria (ADR-046-validation-and-delivery §4): every spec/work item in this wave Merged with clean destinations, all validators green, and the ten-role panel seal recorded", - "id": "W-X02", - "kind": "work-item", - "owner": "d2bd integration removal owner; depends on W-X01.", - "parallelGroup": "wi:ADR-046-provider-device-security-key", - "prerequisites": [ - "ADR-046-provider-device-security-key", - "W-X01" + "exitGate" : "ADR046-W5 exit criteria (ADR-046-validation-and-delivery §4): every spec/work item in this wave Merged with clean destinations, all validators green, and the ten-role panel seal recorded", + "id" : "ADR046-zone-control-005", + "kind" : "work-item", + "owner" : "ADR046-zone-control-004", + "parallelGroup" : "wi:ADR-046-resources-zone-control", + "prerequisites" : [ + "ADR-046-resources-zone-control", + "ADR046-zone-control-004" ], - "specId": "ADR-046-provider-device-security-key", - "topologicalRank": 18, - "wave": "W6" + "specId" : "ADR-046-resources-zone-control", + "topologicalRank" : 12, + "wave" : "W5" }, { - "blockers": [], - "destinations": [ - "Removed from guest Nix module; successor is Binding-owned `Process/binding--sk-frontend`" + "blockers" : [], + "destinations" : [ + "`packages/d2b-resource-api/src/bootstrap_authz.rs`; Zone runtime startup path" ], - "entryContracts": [ - "ADR-046-provider-device-security-key" + "entryContracts" : [ + "ADR-046-resources-zone-control" ], - "exitGate": "ADR046-W6 exit criteria (ADR-046-validation-and-delivery §4): every spec/work item in this wave Merged with clean destinations, all validators green, and the ten-role panel seal recorded", - "id": "W-X03", - "kind": "work-item", - "owner": "Guest Nix module removal owner; depends on W-N13, W-R03, and W-N19 Binding frontend/UHID contract.", - "parallelGroup": "wi:ADR-046-provider-device-security-key", - "prerequisites": [ - "ADR-046-provider-device-security-key", - "W-N13", - "W-N19", - "W-R03" + "exitGate" : "ADR046-W5 exit criteria (ADR-046-validation-and-delivery §4): every spec/work item in this wave Merged with clean destinations, all validators green, and the ten-role panel seal recorded", + "id" : "ADR046-zone-control-006", + "kind" : "work-item", + "owner" : "ADR046-zone-control-004, ADR046-zone-control-005; Zone runtime", + "parallelGroup" : "wi:ADR-046-resources-zone-control", + "prerequisites" : [ + "ADR-046-resources-zone-control", + "ADR046-zone-control-004", + "ADR046-zone-control-005" ], - "specId": "ADR-046-provider-device-security-key", - "topologicalRank": 16, - "wave": "W6" + "specId" : "ADR-046-resources-zone-control", + "topologicalRank" : 13, + "wave" : "W5" }, { - "blockers": [], - "destinations": [ - "Removed from `packages/d2b-contract-tests/tests/`; successor tests live in `packages/d2b-provider-device-security-key/tests/`" + "blockers" : [], + "destinations" : [ + "`nixos-modules/options-zones.nix`; `nixos-modules/resources-zone-control.nix`; extended `nixos-modules/index.nix`" ], - "entryContracts": [ - "ADR-046-provider-device-security-key" + "entryContracts" : [ + "ADR-046-resources-zone-control" ], - "exitGate": "ADR046-W6 exit criteria (ADR-046-validation-and-delivery §4): every spec/work item in this wave Merged with clean destinations, all validators green, and the ten-role panel seal recorded", - "id": "W-X04", - "kind": "work-item", - "owner": "Test-suite migration/removal owner; depends on W-R06 and W-R07 provider-crate successor tests.", - "parallelGroup": "wi:ADR-046-provider-device-security-key", - "prerequisites": [ - "ADR-046-provider-device-security-key", - "W-R06", - "W-R07" + "exitGate" : "ADR046-W5 exit criteria (ADR-046-validation-and-delivery §4): every spec/work item in this wave Merged with clean destinations, all validators green, and the ten-role panel seal recorded", + "id" : "ADR046-zone-control-007", + "kind" : "work-item", + "owner" : "ADR046-identities-002; ADR046-zone-control-001; ADR046-zone-control-002; ADR046-zone-control-003; ADR046-zone-control-004; ADR046-zone-control-005; ADR046-zone-control-009; ADR046-zone-control-010", + "parallelGroup" : "wi:ADR-046-resources-zone-control", + "prerequisites" : [ + "ADR-046-resources-zone-control", + "ADR046-identities-002", + "ADR046-zone-control-001", + "ADR046-zone-control-002", + "ADR046-zone-control-003", + "ADR046-zone-control-004", + "ADR046-zone-control-005", + "ADR046-zone-control-009", + "ADR046-zone-control-010" ], - "specId": "ADR-046-provider-device-security-key", - "topologicalRank": 14, - "wave": "W6" + "specId" : "ADR-046-resources-zone-control", + "topologicalRank" : 14, + "wave" : "W5" }, { - "blockers": [], - "destinations": [ - "Removed from `d2b-core/src/processes.rs`; successor frontend is a v3 Process resource owned by `Provider/device-security-key`" + "blockers" : [], + "destinations" : [ + "`packages/d2b-contracts/src/v3/host.rs` (Host resource schema, user-domain variant); `packages/d2b-core-controller/src/host_user.rs` (reconciler owned by Provider/system-core); Nix Host authoring via `d2b.zones..resources. = { type = \"Host\"; spec = { ... }; };` (validated per ResourceTypeSchema; no separate `options-zones-hosts.nix` submodule)" ], - "entryContracts": [ - "ADR-046-provider-device-security-key" + "entryContracts" : [ + "ADR-046-resources-zone-control" ], - "exitGate": "ADR046-W6 exit criteria (ADR-046-validation-and-delivery §4): every spec/work item in this wave Merged with clean destinations, all validators green, and the ten-role panel seal recorded", - "id": "W-X05", - "kind": "work-item", - "owner": "Core ProcessRole removal owner; depends on W-N09 Process resources, W-N08 sandbox templates, and system-minijail/system-systemd conformance.", - "parallelGroup": "wi:ADR-046-provider-device-security-key", - "prerequisites": [ - "ADR-046-provider-device-security-key", - "W-N08", - "W-N09" + "exitGate" : "ADR046-W5 exit criteria (ADR-046-validation-and-delivery §4): every spec/work item in this wave Merged with clean destinations, all validators green, and the ten-role panel seal recorded", + "id" : "ADR046-zone-control-008", + "kind" : "work-item", + "owner" : "ADR046-zone-control-001 (Zone resource); ADR046-zone-control-003 (Provider/system-core installed)", + "parallelGroup" : "wi:ADR-046-resources-zone-control", + "prerequisites" : [ + "ADR-046-resources-zone-control", + "ADR046-zone-control-001", + "ADR046-zone-control-003" ], - "specId": "ADR-046-provider-device-security-key", - "topologicalRank": 15, - "wave": "W6" + "specId" : "ADR-046-resources-zone-control", + "topologicalRank" : 13, + "wave" : "W5" }, { - "blockers": [], - "destinations": [ - "Removed from contracts and broker; successor access is static guest Nix `uhid` module plus Core pre-opened `/dev/uhid` DeviceGrant for the frontend Process" + "blockers" : [], + "destinations" : [ + "`packages/d2b-contracts/src/v3/quota.rs`; `packages/d2b-core-controller/src/quota.rs`; `packages/d2b-resource-api/src/quota_gate.rs`" ], - "entryContracts": [ - "ADR-046-provider-device-security-key" + "entryContracts" : [ + "ADR-046-resources-zone-control" ], - "exitGate": "ADR046-W6 exit criteria (ADR-046-validation-and-delivery §4): every spec/work item in this wave Merged with clean destinations, all validators green, and the ten-role panel seal recorded", - "id": "W-X06", - "kind": "work-item", - "owner": "Broker/contracts/Nix removal owner; depends on W-R05, W-N11, W-N13, and W-N19 Guest-substrate UHID replacement.", - "parallelGroup": "wi:ADR-046-provider-device-security-key", - "prerequisites": [ - "ADR-046-provider-device-security-key", - "W-N11", - "W-N13", - "W-N19", - "W-R05" + "exitGate" : "ADR046-W5 exit criteria (ADR-046-validation-and-delivery §4): every spec/work item in this wave Merged with clean destinations, all validators green, and the ten-role panel seal recorded", + "id" : "ADR046-zone-control-009", + "kind" : "work-item", + "owner" : "ADR046-zone-control-001; Zone store (ADR046-store-001); Quota handler owner", + "parallelGroup" : "wi:ADR-046-resources-zone-control", + "prerequisites" : [ + "ADR-046-resources-zone-control", + "ADR046-store-001", + "ADR046-zone-control-001" ], - "specId": "ADR-046-provider-device-security-key", - "topologicalRank": 16, - "wave": "W6" + "specId" : "ADR-046-resources-zone-control", + "topologicalRank" : 11, + "wave" : "W5" }, { - "blockers": [], - "destinations": [ - "packages/d2b-provider-runtime-qemu-media/{src/lib.rs,tests/provider_layout.rs,integration/mod.rs,README.md}" + "blockers" : [], + "destinations" : [ + "`packages/d2b-contracts/src/v3/emergency_policy.rs`; `packages/d2b-core-controller/src/emergency_policy.rs`; `packages/d2b-resource-api/src/emergency_gate.rs`" ], - "entryContracts": [ - "ADR-046-provider-runtime-qemu-media" + "entryContracts" : [ + "ADR-046-resources-zone-control" ], - "exitGate": "ADR046-W6 exit criteria (ADR-046-validation-and-delivery §4): every spec/work item in this wave Merged with clean destinations, all validators green, and the ten-role panel seal recorded", - "id": "WI-001", - "kind": "work-item", - "owner": "P0; blocks all other runtime-qemu-media work items; owner: `runtime-qemu-media` Provider crate", - "parallelGroup": "wi:ADR-046-provider-runtime-qemu-media", - "prerequisites": [ - "ADR-046-provider-runtime-qemu-media" + "exitGate" : "ADR046-W5 exit criteria (ADR-046-validation-and-delivery §4): every spec/work item in this wave Merged with clean destinations, all validators green, and the ten-role panel seal recorded", + "id" : "ADR046-zone-control-010", + "kind" : "work-item", + "owner" : "ADR046-zone-control-001; Zone store (ADR046-store-001); EmergencyPolicy handler owner", + "parallelGroup" : "wi:ADR-046-resources-zone-control", + "prerequisites" : [ + "ADR-046-resources-zone-control", + "ADR046-store-001", + "ADR046-zone-control-001" ], - "specId": "ADR-046-provider-runtime-qemu-media", - "topologicalRank": 11, - "wave": "W6" + "specId" : "ADR-046-resources-zone-control", + "topologicalRank" : 11, + "wave" : "W5" }, { - "blockers": [], - "destinations": [ - "packages/d2b-provider-runtime-qemu-media/src/types/guest.rs" + "blockers" : [], + "destinations" : [ + "`packages/d2b-bus/src/{lifecycle,engine,driver,streams,transport,error}.rs` (new crate `d2b-bus`)" ], - "entryContracts": [ - "ADR-046-provider-runtime-qemu-media" + "entryContracts" : [ + "ADR-046-resources-zone-control" ], - "exitGate": "ADR046-W6 exit criteria (ADR-046-validation-and-delivery §4): every spec/work item in this wave Merged with clean destinations, all validators green, and the ten-role panel seal recorded", - "id": "WI-002", - "kind": "work-item", - "owner": "P0; depends on WI-001; owner: runtime-qemu-media type/schema implementation", - "parallelGroup": "wi:ADR-046-provider-runtime-qemu-media", - "prerequisites": [ - "ADR-046-provider-runtime-qemu-media" + "exitGate" : "ADR046-W5 exit criteria (ADR-046-validation-and-delivery §4): every spec/work item in this wave Merged with clean destinations, all validators green, and the ten-role panel seal recorded", + "id" : "ADR046-zone-control-011", + "kind" : "work-item", + "owner" : "ADR046-identities-001; ADR046-store-001", + "parallelGroup" : "wi:ADR-046-resources-zone-control", + "prerequisites" : [ + "ADR-046-resources-zone-control", + "ADR046-identities-001", + "ADR046-store-001" ], - "specId": "ADR-046-provider-runtime-qemu-media", - "topologicalRank": 11, - "wave": "W6" + "specId" : "ADR-046-resources-zone-control", + "topologicalRank" : 10, + "wave" : "W5" }, { - "blockers": [], - "destinations": [ - "packages/d2b-provider-runtime-qemu-media/src/config.rs" + "blockers" : [], + "destinations" : [ + "`packages/d2b-bus-unix/src/{adapter,socket,pidfd,credit,descriptor,error,systemd}.rs` (new crate `d2b-bus-unix`)" ], - "entryContracts": [ - "ADR-046-provider-runtime-qemu-media" + "entryContracts" : [ + "ADR-046-resources-zone-control" ], - "exitGate": "ADR046-W6 exit criteria (ADR-046-validation-and-delivery §4): every spec/work item in this wave Merged with clean destinations, all validators green, and the ten-role panel seal recorded", - "id": "WI-003", - "kind": "work-item", - "owner": "P0; depends on WI-001; owner: runtime-qemu-media Provider config/schema implementation", - "parallelGroup": "wi:ADR-046-provider-runtime-qemu-media", - "prerequisites": [ - "ADR-046-provider-runtime-qemu-media" + "exitGate" : "ADR046-W5 exit criteria (ADR-046-validation-and-delivery §4): every spec/work item in this wave Merged with clean destinations, all validators green, and the ten-role panel seal recorded", + "id" : "ADR046-zone-control-012", + "kind" : "work-item", + "owner" : "ADR046-zone-control-011", + "parallelGroup" : "wi:ADR-046-resources-zone-control", + "prerequisites" : [ + "ADR-046-resources-zone-control", + "ADR046-zone-control-011" ], - "specId": "ADR-046-provider-runtime-qemu-media", - "topologicalRank": 11, - "wave": "W6" + "specId" : "ADR-046-resources-zone-control", + "topologicalRank" : 11, + "wave" : "W5" }, { - "blockers": [], - "destinations": [ - "packages/d2b-provider-runtime-qemu-media/src/{descriptor.rs,state.rs}; no Volume management code for Provider state" + "blockers" : [], + "destinations" : [ + "`packages/d2b-contracts/src/v3/component_session.rs` (new v3 namespace in existing contracts crate)" ], - "entryContracts": [ - "ADR-046-provider-runtime-qemu-media" + "entryContracts" : [ + "ADR-046-resources-zone-control" ], - "exitGate": "ADR046-W6 exit criteria (ADR-046-validation-and-delivery §4): every spec/work item in this wave Merged with clean destinations, all validators green, and the ten-role panel seal recorded", - "id": "WI-003B", - "kind": "work-item", - "owner": "P0; depends on WI-001 and WI-003; owner: runtime-qemu-media controller descriptor/state implementation", - "parallelGroup": "wi:ADR-046-provider-runtime-qemu-media", - "prerequisites": [ - "ADR-046-provider-runtime-qemu-media" + "exitGate" : "ADR046-W5 exit criteria (ADR-046-validation-and-delivery §4): every spec/work item in this wave Merged with clean destinations, all validators green, and the ten-role panel seal recorded", + "id" : "ADR046-zone-control-013", + "kind" : "work-item", + "owner" : "ADR046-identities-001", + "parallelGroup" : "wi:ADR-046-resources-zone-control", + "prerequisites" : [ + "ADR-046-resources-zone-control", + "ADR046-identities-001" ], - "specId": "ADR-046-provider-runtime-qemu-media", - "topologicalRank": 11, - "wave": "W6" + "specId" : "ADR-046-resources-zone-control", + "topologicalRank" : 10, + "wave" : "W5" }, { - "blockers": [], - "destinations": [ - "packages/d2b-provider-runtime-qemu-media/src/controller/volume.rs" + "blockers" : [], + "destinations" : [ + "`nixos-modules/options-zones.nix`, `nixos-modules/resource-type-validators.nix`" ], - "entryContracts": [ - "ADR-046-provider-runtime-qemu-media" + "entryContracts" : [ + "ADR-046-resources-zone-control" ], - "exitGate": "ADR046-W6 exit criteria (ADR-046-validation-and-delivery §4): every spec/work item in this wave Merged with clean destinations, all validators green, and the ten-role panel seal recorded", - "id": "WI-004", - "kind": "work-item", - "owner": "P1; depends on WI-001 and WI-003; owner: runtime-qemu-media controller Volume reconciliation", - "parallelGroup": "wi:ADR-046-provider-runtime-qemu-media", - "prerequisites": [ - "ADR-046-provider-runtime-qemu-media" + "exitGate" : "ADR046-W5 exit criteria (ADR-046-validation-and-delivery §4): every spec/work item in this wave Merged with clean destinations, all validators green, and the ten-role panel seal recorded", + "id" : "ADR046-zone-control-014", + "kind" : "work-item", + "owner" : "ADR046-zone-control-001; Nix module owner", + "parallelGroup" : "wi:ADR-046-resources-zone-control", + "prerequisites" : [ + "ADR-046-resources-zone-control", + "ADR046-zone-control-001" ], - "specId": "ADR-046-provider-runtime-qemu-media", - "topologicalRank": 11, - "wave": "W6" + "specId" : "ADR-046-resources-zone-control", + "topologicalRank" : 11, + "wave" : "W5" }, { - "blockers": [], - "destinations": [ - "packages/d2b-provider-runtime-qemu-media/src/controller/media_watch.rs" + "blockers" : [], + "destinations" : [ + "`packages/d2b-resource-compiler/src/{main,bundle,schema,validator,digest,sort,secret_lint,generation}.rs`; exposed as `pkgs.d2b-resource-compiler`; called from `nixos-modules/resource-compiler.nix`" ], - "entryContracts": [ - "ADR-046-provider-runtime-qemu-media" + "entryContracts" : [ + "ADR-046-resources-zone-control" ], - "exitGate": "ADR046-W6 exit criteria (ADR-046-validation-and-delivery §4): every spec/work item in this wave Merged with clean destinations, all validators green, and the ten-role panel seal recorded", - "id": "WI-005", - "kind": "work-item", - "owner": "P1; depends on WI-002 and WI-004; owner: runtime-qemu-media media dependency controller", - "parallelGroup": "wi:ADR-046-provider-runtime-qemu-media", - "prerequisites": [ - "ADR-046-provider-runtime-qemu-media" + "exitGate" : "ADR046-W5 exit criteria (ADR-046-validation-and-delivery §4): every spec/work item in this wave Merged with clean destinations, all validators green, and the ten-role panel seal recorded", + "id" : "ADR046-zone-control-015", + "kind" : "work-item", + "owner" : "ADR046-zone-control-014; resource compiler owner", + "parallelGroup" : "wi:ADR-046-resources-zone-control", + "prerequisites" : [ + "ADR-046-resources-zone-control", + "ADR046-zone-control-014" ], - "specId": "ADR-046-provider-runtime-qemu-media", - "topologicalRank": 11, - "wave": "W6" + "specId" : "ADR-046-resources-zone-control", + "topologicalRank" : 12, + "wave" : "W5" }, { - "blockers": [], - "destinations": [ - "packages/d2b-provider-runtime-qemu-media/src/controller/device_watch.rs" + "blockers" : [], + "destinations" : [ + "`packages/d2b-core-controller/src/configuration.rs` (Phase 3 activation, diff, delete dispatch); `packages/d2b-core-controller/src/cleanup.rs` (pending tracking, status, stuck detection, rollback verb handler)" ], - "entryContracts": [ - "ADR-046-provider-runtime-qemu-media" + "entryContracts" : [ + "ADR-046-resources-zone-control" ], - "exitGate": "ADR046-W6 exit criteria (ADR-046-validation-and-delivery §4): every spec/work item in this wave Merged with clean destinations, all validators green, and the ten-role panel seal recorded", - "id": "WI-006", - "kind": "work-item", - "owner": "P1; depends on WI-002; owner: runtime-qemu-media Device dependency controller", - "parallelGroup": "wi:ADR-046-provider-runtime-qemu-media", - "prerequisites": [ - "ADR-046-provider-runtime-qemu-media" + "exitGate" : "ADR046-W5 exit criteria (ADR-046-validation-and-delivery §4): every spec/work item in this wave Merged with clean destinations, all validators green, and the ten-role panel seal recorded", + "id" : "ADR046-zone-control-016", + "kind" : "work-item", + "owner" : "ADR046-zone-control-015; ADR046-zone-control-001; configuration publication handler owner", + "parallelGroup" : "wi:core-config-hub:w5", + "prerequisites" : [ + "ADR-046-resources-zone-control", + "ADR046-zone-control-001", + "ADR046-zone-control-015" ], - "specId": "ADR-046-provider-runtime-qemu-media", - "topologicalRank": 11, - "wave": "W6" + "specId" : "ADR-046-resources-zone-control", + "topologicalRank" : 13, + "wave" : "W5" }, { - "blockers": [], - "destinations": [ - "packages/d2b-provider-runtime-qemu-media/src/controller/display.rs" + "blockers" : [], + "destinations" : [ + "`packages/d2b-provider/src/{registry,rpc}.rs` (new v3 Provider package); `packages/d2b-provider-toolkit/src/{adapter,conformance,fixture,registration,server,values,redaction}.rs` (new v3 toolkit)" ], - "entryContracts": [ - "ADR-046-provider-runtime-qemu-media" + "entryContracts" : [ + "ADR-046-resources-zone-control" ], - "exitGate": "ADR046-W6 exit criteria (ADR-046-validation-and-delivery §4): every spec/work item in this wave Merged with clean destinations, all validators green, and the ten-role panel seal recorded", - "id": "WI-007", - "kind": "work-item", - "owner": "P1; depends on WI-002 and the `display-wayland` Provider dossier; owner: runtime-qemu-media display integration", - "parallelGroup": "wi:ADR-046-provider-runtime-qemu-media", - "prerequisites": [ - "ADR-046-provider-runtime-qemu-media" + "exitGate" : "ADR046-W5 exit criteria (ADR-046-validation-and-delivery §4): every spec/work item in this wave Merged with clean destinations, all validators green, and the ten-role panel seal recorded", + "id" : "ADR046-zone-control-017", + "kind" : "work-item", + "owner" : "ADR046-zone-control-011, ADR046-zone-control-013; Provider registry/toolkit foundation owner", + "parallelGroup" : "wi:ADR-046-resources-zone-control", + "prerequisites" : [ + "ADR-046-resources-zone-control", + "ADR046-zone-control-011", + "ADR046-zone-control-013" ], - "specId": "ADR-046-provider-runtime-qemu-media", - "topologicalRank": 11, - "wave": "W6" + "specId" : "ADR-046-resources-zone-control", + "topologicalRank" : 11, + "wave" : "W5" }, { - "blockers": [], - "destinations": [ - "packages/d2b-provider-runtime-qemu-media/src/controller/process_builder.rs" + "blockers" : [], + "destinations" : [ + "`packages/d2b-core-controller/src/zone_link.rs` (ZoneLink handler); `packages/d2b-resource-api/src/admission.rs` (request admission)" ], - "entryContracts": [ - "ADR-046-provider-runtime-qemu-media" + "entryContracts" : [ + "ADR-046-resources-zone-control" ], - "exitGate": "ADR046-W6 exit criteria (ADR-046-validation-and-delivery §4): every spec/work item in this wave Merged with clean destinations, all validators green, and the ten-role panel seal recorded", - "id": "WI-008", - "kind": "work-item", - "owner": "P1; depends on WI-002, WI-004, WI-005, WI-006, WI-007, and WI-011; owner: runtime-qemu-media Process launch builder", - "parallelGroup": "wi:ADR-046-provider-runtime-qemu-media", - "prerequisites": [ - "ADR-046-provider-runtime-qemu-media" + "exitGate" : "ADR046-W5 exit criteria (ADR-046-validation-and-delivery §4): every spec/work item in this wave Merged with clean destinations, all validators green, and the ten-role panel seal recorded", + "id" : "ADR046-zone-control-018", + "kind" : "work-item", + "owner" : "ADR046-zone-control-011, ADR046-zone-control-012, ADR046-zone-control-013; ZoneLink session/admission foundation owner", + "parallelGroup" : "wi:ADR-046-resources-zone-control", + "prerequisites" : [ + "ADR-046-resources-zone-control", + "ADR046-zone-control-011", + "ADR046-zone-control-012", + "ADR046-zone-control-013" ], - "specId": "ADR-046-provider-runtime-qemu-media", - "topologicalRank": 11, - "wave": "W6" + "specId" : "ADR-046-resources-zone-control", + "topologicalRank" : 12, + "wave" : "W5" }, { - "blockers": [], - "destinations": [ - "packages/d2b-provider-runtime-qemu-media/src/qmp/" + "blockers" : [], + "destinations" : [ + "`packages/d2b-contracts/src/v3/{resource_export,resource_import}.rs` (base schemas); `packages/d2b-core-controller/src/export_import.rs` (core ZoneLink export/import routing controller); shared adapter trait in `packages/d2b-provider/src/share_adapter.rs` (`ExportAdapter`/`ImportAdapter` signed-capability traits)" ], - "entryContracts": [ - "ADR-046-provider-runtime-qemu-media" + "entryContracts" : [ + "ADR-046-resources-zone-control" ], - "exitGate": "ADR046-W6 exit criteria (ADR-046-validation-and-delivery §4): every spec/work item in this wave Merged with clean destinations, all validators green, and the ten-role panel seal recorded", - "id": "WI-009", - "kind": "work-item", - "owner": "P1; depends on WI-008; owner: runtime-qemu-media QMP client implementation", - "parallelGroup": "wi:ADR-046-provider-runtime-qemu-media", - "prerequisites": [ - "ADR-046-provider-runtime-qemu-media" + "exitGate" : "ADR046-W5 exit criteria (ADR-046-validation-and-delivery §4): every spec/work item in this wave Merged with clean destinations, all validators green, and the ten-role panel seal recorded", + "id" : "ADR046-zone-control-019", + "kind" : "work-item", + "owner" : "ADR046-zone-control-001, ADR046-provider-004; ADR046-zonelink owner; `d2b-core-controller` + `d2b-contracts` owners", + "parallelGroup" : "wi:ADR-046-resources-zone-control", + "prerequisites" : [ + "ADR-046-resources-zone-control", + "ADR046-provider-004", + "ADR046-zone-control-001" ], - "specId": "ADR-046-provider-runtime-qemu-media", - "topologicalRank": 11, - "wave": "W6" + "specId" : "ADR-046-resources-zone-control", + "topologicalRank" : 11, + "wave" : "W5" }, { - "blockers": [], - "destinations": [ - "packages/d2b-provider-runtime-qemu-media/src/controller/hotplug.rs" + "blockers" : [], + "destinations" : [ + "`packages/d2b-core-controller/src/export_import_projection.rs` (local qualified Service projection lifecycle owned by `ResourceImport`)" ], - "entryContracts": [ - "ADR-046-provider-runtime-qemu-media" + "entryContracts" : [ + "ADR-046-resources-zone-control" ], - "exitGate": "ADR046-W6 exit criteria (ADR-046-validation-and-delivery §4): every spec/work item in this wave Merged with clean destinations, all validators green, and the ten-role panel seal recorded", - "id": "WI-010", - "kind": "work-item", - "owner": "P2; depends on WI-005 and WI-009; owner: runtime-qemu-media hotplug controller", - "parallelGroup": "wi:ADR-046-provider-runtime-qemu-media", - "prerequisites": [ - "ADR-046-provider-runtime-qemu-media" + "exitGate" : "ADR046-W5 exit criteria (ADR-046-validation-and-delivery §4): every spec/work item in this wave Merged with clean destinations, all validators green, and the ten-role panel seal recorded", + "id" : "ADR046-zone-control-020", + "kind" : "work-item", + "owner" : "ADR046-zone-control-019; `d2b-core-controller` owner", + "parallelGroup" : "wi:ADR-046-resources-zone-control", + "prerequisites" : [ + "ADR-046-resources-zone-control", + "ADR046-zone-control-019" ], - "specId": "ADR-046-provider-runtime-qemu-media", - "topologicalRank": 11, - "wave": "W6" + "specId" : "ADR-046-resources-zone-control", + "topologicalRank" : 12, + "wave" : "W5" }, { - "blockers": [], - "destinations": [ - "packages/d2b-provider-runtime-qemu-media/src/controller/network.rs" + "blockers" : [], + "destinations" : [ + "`packages/d2b-core-controller/src/{coordinator,configuration,zonelink}.rs`" ], - "entryContracts": [ - "ADR-046-provider-runtime-qemu-media" + "entryContracts" : [ + "ADR-046-resources-zone-control" ], - "exitGate": "ADR046-W6 exit criteria (ADR-046-validation-and-delivery §4): every spec/work item in this wave Merged with clean destinations, all validators green, and the ten-role panel seal recorded", - "id": "WI-011", - "kind": "work-item", - "owner": "P1; depends on WI-002 and Provider config `networkProviderRef`; owner: runtime-qemu-media network dependency integration", - "parallelGroup": "wi:ADR-046-provider-runtime-qemu-media", - "prerequisites": [ - "ADR-046-provider-runtime-qemu-media" + "exitGate" : "ADR046-W5 exit criteria (ADR-046-validation-and-delivery §4): every spec/work item in this wave Merged with clean destinations, all validators green, and the ten-role panel seal recorded", + "id" : "ADR046-zone-control-021", + "kind" : "work-item", + "owner" : "ADR046-zone-control-001, ADR046-zone-control-016; `d2b-core-controller` owner", + "parallelGroup" : "wi:core-config-hub:w5", + "prerequisites" : [ + "ADR-046-resources-zone-control", + "ADR046-zone-control-001", + "ADR046-zone-control-016" ], - "specId": "ADR-046-provider-runtime-qemu-media", - "topologicalRank": 11, - "wave": "W6" + "specId" : "ADR-046-resources-zone-control", + "topologicalRank" : 14, + "wave" : "W5" }, { - "blockers": [], - "destinations": [ - "packages/d2b-provider-runtime-qemu-media/src/controller/reconcile.rs" + "blockers" : [], + "destinations" : [ + "`packages/d2b-core-controller/src/authority.rs`; resource API admission hook" ], - "entryContracts": [ - "ADR-046-provider-runtime-qemu-media" + "entryContracts" : [ + "ADR-046-resources-zone-control" ], - "exitGate": "ADR046-W6 exit criteria (ADR-046-validation-and-delivery §4): every spec/work item in this wave Merged with clean destinations, all validators green, and the ten-role panel seal recorded", - "id": "WI-012", - "kind": "work-item", - "owner": "P1; depends on WI-004 through WI-011; owner: runtime-qemu-media controller", - "parallelGroup": "wi:ADR-046-provider-runtime-qemu-media", - "prerequisites": [ - "ADR-046-provider-runtime-qemu-media" + "exitGate" : "ADR046-W5 exit criteria (ADR-046-validation-and-delivery §4): every spec/work item in this wave Merged with clean destinations, all validators green, and the ten-role panel seal recorded", + "id" : "ADR046-zone-control-022", + "kind" : "work-item", + "owner" : "ADR046-zone-control-019, ADR046-api-001; `d2b-core-controller` + resource API owners", + "parallelGroup" : "wi:ADR-046-resources-zone-control", + "prerequisites" : [ + "ADR-046-resources-zone-control", + "ADR046-api-001", + "ADR046-zone-control-019" ], - "specId": "ADR-046-provider-runtime-qemu-media", - "topologicalRank": 11, - "wave": "W6" + "specId" : "ADR-046-resources-zone-control", + "topologicalRank" : 12, + "wave" : "W5" }, { - "blockers": [], - "destinations": [ - "packages/d2b-provider-runtime-qemu-media/src/controller/status.rs" + "blockers" : [], + "destinations" : [ + "`packages/d2b-core-controller/src/{quota,emergency_policy}.rs`; `packages/d2b-contracts/src/v3/{quota,emergency_policy}.rs`" ], - "entryContracts": [ - "ADR-046-provider-runtime-qemu-media" + "entryContracts" : [ + "ADR-046-resources-zone-control" ], - "exitGate": "ADR046-W6 exit criteria (ADR-046-validation-and-delivery §4): every spec/work item in this wave Merged with clean destinations, all validators green, and the ten-role panel seal recorded", - "id": "WI-013", - "kind": "work-item", - "owner": "P1; depends on WI-012; owner: runtime-qemu-media status/error implementation", - "parallelGroup": "wi:ADR-046-provider-runtime-qemu-media", - "prerequisites": [ - "ADR-046-provider-runtime-qemu-media" + "exitGate" : "ADR046-W5 exit criteria (ADR-046-validation-and-delivery §4): every spec/work item in this wave Merged with clean destinations, all validators green, and the ten-role panel seal recorded", + "id" : "ADR046-zone-control-023", + "kind" : "work-item", + "owner" : "ADR046-zone-control-019; `d2b-core-controller` + `d2b-contracts` owners", + "parallelGroup" : "wi:ADR-046-resources-zone-control", + "prerequisites" : [ + "ADR-046-resources-zone-control", + "ADR046-zone-control-019" ], - "specId": "ADR-046-provider-runtime-qemu-media", - "topologicalRank": 11, - "wave": "W6" + "specId" : "ADR-046-resources-zone-control", + "topologicalRank" : 12, + "wave" : "W5" }, { - "blockers": [], - "destinations": [ - "packages/d2b-provider-runtime-qemu-media/src/audit.rs" + "blockers" : [], + "destinations" : [ + "`packages/d2b-core-controller/src/authority.rs` (Host-global index scope + hardware admission)" ], - "entryContracts": [ - "ADR-046-provider-runtime-qemu-media" + "entryContracts" : [ + "ADR-046-resources-zone-control" ], - "exitGate": "ADR046-W6 exit criteria (ADR-046-validation-and-delivery §4): every spec/work item in this wave Merged with clean destinations, all validators green, and the ten-role panel seal recorded", - "id": "WI-014", - "kind": "work-item", - "owner": "P2; depends on WI-012 and WI-013; owner: runtime-qemu-media audit integration", - "parallelGroup": "wi:ADR-046-provider-runtime-qemu-media", - "prerequisites": [ - "ADR-046-provider-runtime-qemu-media" + "exitGate" : "ADR046-W5 exit criteria (ADR-046-validation-and-delivery §4): every spec/work item in this wave Merged with clean destinations, all validators green, and the ten-role panel seal recorded", + "id" : "ADR046-zone-control-024", + "kind" : "work-item", + "owner" : "ADR046-zone-control-019, ADR046-zone-control-022; `d2b-core-controller` owner", + "parallelGroup" : "wi:ADR-046-resources-zone-control", + "prerequisites" : [ + "ADR-046-resources-zone-control", + "ADR046-zone-control-019", + "ADR046-zone-control-022" ], - "specId": "ADR-046-provider-runtime-qemu-media", - "topologicalRank": 11, - "wave": "W6" + "specId" : "ADR-046-resources-zone-control", + "topologicalRank" : 13, + "wave" : "W5" }, { - "blockers": [], - "destinations": [ - "packages/d2b-provider-runtime-qemu-media/src/telemetry.rs" + "blockers" : [], + "destinations" : [ + "`packages/d2b-client/src/` (updated for v3 Zone API, replacing ADR45 daemon verbs with Zone resource operations)" ], - "entryContracts": [ - "ADR-046-provider-runtime-qemu-media" + "entryContracts" : [ + "ADR-046-resources-zone-control" ], - "exitGate": "ADR046-W6 exit criteria (ADR-046-validation-and-delivery §4): every spec/work item in this wave Merged with clean destinations, all validators green, and the ten-role panel seal recorded", - "id": "WI-015", - "kind": "work-item", - "owner": "P2; depends on WI-012 and WI-013; owner: runtime-qemu-media telemetry integration", - "parallelGroup": "wi:ADR-046-provider-runtime-qemu-media", - "prerequisites": [ - "ADR-046-provider-runtime-qemu-media" + "exitGate" : "ADR046-W5 exit criteria (ADR-046-validation-and-delivery §4): every spec/work item in this wave Merged with clean destinations, all validators green, and the ten-role panel seal recorded", + "id" : "ADR046-zone-control-025", + "kind" : "work-item", + "owner" : "ADR046-zone-control-011, ADR046-zone-control-012, ADR046-zone-control-013, ADR046-zone-control-018", + "parallelGroup" : "wi:ADR-046-resources-zone-control", + "prerequisites" : [ + "ADR-046-resources-zone-control", + "ADR046-zone-control-011", + "ADR046-zone-control-012", + "ADR046-zone-control-013", + "ADR046-zone-control-018" ], - "specId": "ADR-046-provider-runtime-qemu-media", - "topologicalRank": 11, - "wave": "W6" + "specId" : "ADR-046-resources-zone-control", + "topologicalRank" : 13, + "wave" : "W5" }, { - "blockers": [], - "destinations": [ - "nixos-modules/options-guest-qemu-media.nix; nixos-modules/assertions.nix" + "blockers" : [], + "destinations" : [ + "`packages/d2b-contract-tests/tests/policy_provider_crate_layout.rs` (new file; gated under D2B_FIXTURES in existing `tests/tools/rust-workspace-checks.sh`)" ], - "entryContracts": [ - "ADR-046-provider-runtime-qemu-media" + "entryContracts" : [ + "ADR-046-resources-zone-control" ], - "exitGate": "ADR046-W6 exit criteria (ADR-046-validation-and-delivery §4): every spec/work item in this wave Merged with clean destinations, all validators green, and the ten-role panel seal recorded", - "id": "WI-016", - "kind": "work-item", - "owner": "P1; depends on WI-002 and WI-003; owner: Nix resource compiler and runtime-qemu-media options", - "parallelGroup": "wi:ADR-046-provider-runtime-qemu-media", - "prerequisites": [ - "ADR-046-provider-runtime-qemu-media" + "exitGate" : "ADR046-W5 exit criteria (ADR-046-validation-and-delivery §4): every spec/work item in this wave Merged with clean destinations, all validators green, and the ten-role panel seal recorded", + "id" : "ADR046-zone-control-026", + "kind" : "work-item", + "owner" : "ADR046-zone-control-003; workspace policy owner", + "parallelGroup" : "wi:ADR-046-resources-zone-control", + "prerequisites" : [ + "ADR-046-resources-zone-control", + "ADR046-zone-control-003" ], - "specId": "ADR-046-provider-runtime-qemu-media", - "topologicalRank": 11, - "wave": "W6" + "specId" : "ADR-046-resources-zone-control", + "topologicalRank" : 13, + "wave" : "W5" }, { - "blockers": [], - "destinations": [ - "packages/d2b-provider-runtime-qemu-media/tests/conformance_guest.rs" + "blockers" : [], + "destinations" : [ + "`packages/d2b-provider/src/agent.rs` (v3 provider agent dispatch); `packages/d2b-contracts/src/v3/provider_registry.rs` (v3 provider registry wire contract)" ], - "entryContracts": [ - "ADR-046-provider-runtime-qemu-media" + "entryContracts" : [ + "ADR-046-resources-zone-control" ], - "exitGate": "ADR046-W6 exit criteria (ADR-046-validation-and-delivery §4): every spec/work item in this wave Merged with clean destinations, all validators green, and the ten-role panel seal recorded", - "id": "WI-017", - "kind": "work-item", - "owner": "P2; depends on WI-012 through WI-015; owner: runtime-qemu-media conformance tests", - "parallelGroup": "wi:ADR-046-provider-runtime-qemu-media", - "prerequisites": [ - "ADR-046-provider-runtime-qemu-media" + "exitGate" : "ADR046-W5 exit criteria (ADR-046-validation-and-delivery §4): every spec/work item in this wave Merged with clean destinations, all validators green, and the ten-role panel seal recorded", + "id" : "ADR046-zone-control-027", + "kind" : "work-item", + "owner" : "ADR046-zone-control-011, ADR046-zone-control-017, ADR046-zone-control-018", + "parallelGroup" : "wi:ADR-046-resources-zone-control", + "prerequisites" : [ + "ADR-046-resources-zone-control", + "ADR046-zone-control-011", + "ADR046-zone-control-017", + "ADR046-zone-control-018" ], - "specId": "ADR-046-provider-runtime-qemu-media", - "topologicalRank": 11, - "wave": "W6" + "specId" : "ADR-046-resources-zone-control", + "topologicalRank" : 13, + "wave" : "W5" }, { - "blockers": [], - "destinations": [ - "packages/d2b-provider-runtime-qemu-media/integration/" + "blockers" : [], + "destinations" : [ + "`packages/d2b-contracts/src/v3/{services,state,identity,provider}.rs` (new v3 namespace); generated stubs regenerated from v3 proto files in `packages/d2b-contracts/proto/v3/`" ], - "entryContracts": [ - "ADR-046-provider-runtime-qemu-media" + "entryContracts" : [ + "ADR-046-resources-zone-control" ], - "exitGate": "ADR046-W6 exit criteria (ADR-046-validation-and-delivery §4): every spec/work item in this wave Merged with clean destinations, all validators green, and the ten-role panel seal recorded", - "id": "WI-018", - "kind": "work-item", - "owner": "P2; depends on WI-004 through WI-017; owner: runtime-qemu-media integration fixtures", - "parallelGroup": "wi:ADR-046-provider-runtime-qemu-media", - "prerequisites": [ - "ADR-046-provider-runtime-qemu-media" + "exitGate" : "ADR046-W5 exit criteria (ADR-046-validation-and-delivery §4): every spec/work item in this wave Merged with clean destinations, all validators green, and the ten-role panel seal recorded", + "id" : "ADR046-zone-control-028", + "kind" : "work-item", + "owner" : "ADR046-zone-control-013", + "parallelGroup" : "wi:ADR-046-resources-zone-control", + "prerequisites" : [ + "ADR-046-resources-zone-control", + "ADR046-zone-control-013" ], - "specId": "ADR-046-provider-runtime-qemu-media", - "topologicalRank": 11, - "wave": "W6" + "specId" : "ADR-046-resources-zone-control", + "topologicalRank" : 11, + "wave" : "W5" } ], - "schemaVersion": 1, - "status": "Proposed", - "waves": [ + "schemaVersion" : 1, + "status" : "Proposed", + "waves" : [ { - "parallelGroups": [ + "parallelGroups" : [ "W0-foundation-chain", "W0-reference-docs", "wi:ADR-046-decision-register", @@ -21832,55 +23204,55 @@ "wi:ADR-046-resource-store-redb", "wi:ADR-046-terminology-and-identities" ], - "specCount": 6, - "wave": "W0", - "workItemCount": 10 + "specCount" : 6, + "wave" : "W0", + "workItemCount" : 10 }, { - "parallelGroups": [ + "parallelGroups" : [ "W1-reconcile-and-bus", "wi:ADR-046-componentsession-and-bus", "wi:ADR-046-resource-reconciliation" ], - "specCount": 2, - "wave": "W1", - "workItemCount": 6 + "specCount" : 2, + "wave" : "W1", + "workItemCount" : 6 }, { - "parallelGroups": [ + "parallelGroups" : [ "W2-composition-and-routing", "wi:ADR-046-primitive-resource-composition", "wi:ADR-046-zone-routing" ], - "specCount": 2, - "wave": "W2", - "workItemCount": 19 + "specCount" : 2, + "wave" : "W2", + "workItemCount" : 19 }, { - "parallelGroups": [ + "parallelGroups" : [ "W3-provider-contract", "wi:ADR-046-provider-model-and-packaging" ], - "specCount": 1, - "wave": "W3", - "workItemCount": 4 + "specCount" : 1, + "wave" : "W3", + "workItemCount" : 4 }, { - "parallelGroups": [ + "parallelGroups" : [ "W4-parallel-specs", "wi:ADR-046-components-processes-and-sandbox", "wi:ADR-046-core-controllers", "wi:ADR-046-provider-state", "wi:ADR-046-resources-credential", "wi:ADR-046-resources-network", - "wi:core-config-hub" + "wi:core-config-hub:w4" ], - "specCount": 5, - "wave": "W4", - "workItemCount": 32 + "specCount" : 5, + "wave" : "W4", + "workItemCount" : 33 }, { - "parallelGroups": [ + "parallelGroups" : [ "W5-parallel-specs", "wi:ADR-046-cli-and-operations", "wi:ADR-046-nix-configuration", @@ -21889,14 +23261,14 @@ "wi:ADR-046-resources-volume", "wi:ADR-046-resources-zone-control", "wi:ADR-046-telemetry-audit-and-support", - "wi:core-config-hub" + "wi:core-config-hub:w5" ], - "specCount": 7, - "wave": "W5", - "workItemCount": 141 + "specCount" : 7, + "wave" : "W5", + "workItemCount" : 141 }, { - "parallelGroups": [ + "parallelGroups" : [ "W6-credentials", "W6-interaction", "W6-storage-network-device", @@ -21921,6 +23293,7 @@ "wi:ADR-046-provider-runtime-cloud-hypervisor", "wi:ADR-046-provider-runtime-qemu-media", "wi:ADR-046-provider-shell-terminal", + "wi:ADR-046-provider-system-core", "wi:ADR-046-provider-system-minijail", "wi:ADR-046-provider-system-systemd", "wi:ADR-046-provider-transport-azure-relay", @@ -21929,12 +23302,12 @@ "wi:ADR-046-provider-volume-local", "wi:ADR-046-provider-volume-virtiofs" ], - "specCount": 27, - "wave": "W6", - "workItemCount": 254 + "specCount" : 27, + "wave" : "W6", + "workItemCount" : 256 }, { - "parallelGroups": [ + "parallelGroups" : [ "W7-closing", "wi:ADR-046-feasibility-and-spikes", "wi:ADR-046-reset-and-cutover", @@ -21942,9 +23315,9 @@ "wi:ADR-046-streamline", "wi:ADR-046-validation-and-delivery" ], - "specCount": 5, - "wave": "W7", - "workItemCount": 72 + "specCount" : 5, + "wave" : "W7", + "workItemCount" : 74 } ] } diff --git a/docs/specs/ADR-046-implementation-graph.md b/docs/specs/ADR-046-implementation-graph.md index 8236a5688..5907276c3 100644 --- a/docs/specs/ADR-046-implementation-graph.md +++ b/docs/specs/ADR-046-implementation-graph.md @@ -12,7 +12,7 @@ The graph maps every member spec and every work item exactly once to a dependency-ordered launch wave (`W0`–`W7`) and a file-disjoint parallel group. -It includes all resolved security-key work-item dependencies; no lexical +It includes every resolved security-key work-item dependency; no lexical tie-break or omitted dependency is used. ## Counts @@ -21,10 +21,10 @@ tie-break or omitted dependency is used. | --- | --- | | Waves | 8 | | Spec nodes | 55 | -| Work-item nodes | 538 | -| Total nodes | 593 | -| Edges | 1677 | -| Max topological rank | 19 | +| Work-item nodes | 543 | +| Total nodes | 598 | +| Edges | 1896 | +| Max topological rank | 22 | ## Waves (W0–W7) @@ -34,10 +34,10 @@ tie-break or omitted dependency is used. | W1 | componentsession-and-bus, resource-reconciliation | 2 | 6 | W1-reconcile-and-bus | | W2 | primitive-resource-composition, zone-routing | 2 | 19 | W2-composition-and-routing | | W3 | provider-model-and-packaging | 1 | 4 | W3-provider-contract | -| W4 | components-processes-and-sandbox, core-controllers, provider-state, resources-credential, resources-network | 5 | 32 | W4-parallel-specs | +| W4 | components-processes-and-sandbox, core-controllers, provider-state, resources-credential, resources-network | 5 | 33 | W4-parallel-specs | | W5 | cli-and-operations, nix-configuration, resources-device, resources-host-guest-process-user, resources-volume, resources-zone-control, telemetry-audit-and-support | 7 | 141 | W5-parallel-specs | -| W6 | provider-activation-nixos, provider-audio-pipewire, provider-clipboard-wayland, provider-credential-entra, provider-credential-managed-identity, provider-credential-secret-service, provider-device-gpu, provider-device-security-key, provider-device-tpm, provider-device-usbip, provider-display-wayland, provider-network-local, provider-notification-desktop, provider-observability-otel, provider-runtime-azure-container-apps, provider-runtime-azure-virtual-machine, provider-runtime-cloud-hypervisor, provider-runtime-qemu-media, provider-shell-terminal, provider-system-core, provider-system-minijail, provider-system-systemd, provider-transport-azure-relay, provider-transport-unix, provider-transport-vsock, provider-volume-local, provider-volume-virtiofs | 27 | 254 | W6-credentials, W6-interaction, W6-storage-network-device, W6-system-host-guest, W6-transport-observability-activation | -| W7 | feasibility-and-spikes, reset-and-cutover, security-and-threat-model, streamline, validation-and-delivery | 5 | 72 | W7-closing | +| W6 | provider-activation-nixos, provider-audio-pipewire, provider-clipboard-wayland, provider-credential-entra, provider-credential-managed-identity, provider-credential-secret-service, provider-device-gpu, provider-device-security-key, provider-device-tpm, provider-device-usbip, provider-display-wayland, provider-network-local, provider-notification-desktop, provider-observability-otel, provider-runtime-azure-container-apps, provider-runtime-azure-virtual-machine, provider-runtime-cloud-hypervisor, provider-runtime-qemu-media, provider-shell-terminal, provider-system-core, provider-system-minijail, provider-system-systemd, provider-transport-azure-relay, provider-transport-unix, provider-transport-vsock, provider-volume-local, provider-volume-virtiofs | 27 | 256 | W6-credentials, W6-interaction, W6-storage-network-device, W6-system-host-guest, W6-transport-observability-activation | +| W7 | feasibility-and-spikes, reset-and-cutover, security-and-threat-model, streamline, validation-and-delivery | 5 | 74 | W7-closing | ## Dependency DAG (waves and prep barriers) @@ -138,6 +138,13 @@ fully represented in the JSON. | Prerequisite | Consumer | Type | | --- | --- | --- | +| `ADR046-core-001` | `ADR046-device-007` | file-overlap-order | +| `ADR046-core-001` | `ADR046-exec-013` | file-overlap-order | +| `ADR046-core-001` | `ADR046-exec-015` | file-overlap-order | +| `ADR046-core-001` | `ADR046-network-008` | file-overlap-order | +| `ADR046-core-001` | `ADR046-telem-011` | file-overlap-order | +| `ADR046-core-001` | `ADR046-zone-control-016` | file-overlap-order | +| `ADR046-core-001` | `ADR046-zone-control-021` | file-overlap-order | | `ADR-046-resource-object-model` | `ADR-046-resource-api-and-authorization` | shared-contract | | `ADR-046-resource-store-redb` | `ADR-046-resource-api-and-authorization` | shared-contract | | `ADR-046-terminology-and-identities` | `ADR-046-resource-api-and-authorization` | shared-contract | @@ -151,17 +158,12 @@ fully represented in the JSON. | `ADR-046-reset-and-cutover` | `ADR-046-validation-and-delivery` | shared-contract | | `ADR-046-security-and-threat-model` | `ADR-046-validation-and-delivery` | shared-contract | | `ADR-046-streamline` | `ADR-046-validation-and-delivery` | shared-contract | -| `ADR046-core-001` | `ADR046-device-007` | file-overlap-order | -| `ADR046-core-001` | `ADR046-exec-013` | file-overlap-order | -| `ADR046-core-001` | `ADR046-exec-015` | file-overlap-order | -| `ADR046-core-001` | `ADR046-network-008` | file-overlap-order | -| `ADR046-core-001` | `ADR046-telem-011` | file-overlap-order | -| `ADR046-core-001` | `ADR046-zone-control-016` | file-overlap-order | -| `ADR046-core-001` | `ADR046-zone-control-021` | file-overlap-order | Only the listed `file-overlap-order` edges constrain shared files. Provider integration ordering that touches disjoint crate trees is not represented as -file overlap. +file overlap. The former `wi:core-config-hub` is split into +`wi:core-config-hub:w4` and `wi:core-config-hub:w5`; each parallel group is +single-wave while the exact seven ordering edges above remain unchanged. ## Parallel groups @@ -200,7 +202,7 @@ file overlap. | `wi:ADR-046-provider-device-usbip` | W6 | 9 | | `wi:ADR-046-provider-display-wayland` | W6 | 4 | | `wi:ADR-046-provider-model-and-packaging` | W3 | 4 | -| `wi:ADR-046-provider-network-local` | W6 | 19 | +| `wi:ADR-046-provider-network-local` | W6 | 20 | | `wi:ADR-046-provider-notification-desktop` | W6 | 6 | | `wi:ADR-046-provider-observability-otel` | W6 | 6 | | `wi:ADR-046-provider-runtime-azure-container-apps` | W6 | 7 | @@ -209,6 +211,7 @@ file overlap. | `wi:ADR-046-provider-runtime-qemu-media` | W6 | 19 | | `wi:ADR-046-provider-shell-terminal` | W6 | 13 | | `wi:ADR-046-provider-state` | W4 | 12 | +| `wi:ADR-046-provider-system-core` | W6 | 1 | | `wi:ADR-046-provider-system-minijail` | W6 | 6 | | `wi:ADR-046-provider-system-systemd` | W6 | 3 | | `wi:ADR-046-provider-transport-azure-relay` | W6 | 7 | @@ -224,16 +227,17 @@ file overlap. | `wi:ADR-046-resources-credential` | W4 | 8 | | `wi:ADR-046-resources-device` | W5 | 7 | | `wi:ADR-046-resources-host-guest-process-user` | W5 | 22 | -| `wi:ADR-046-resources-network` | W4 | 7 | +| `wi:ADR-046-resources-network` | W4 | 8 | | `wi:ADR-046-resources-volume` | W5 | 6 | | `wi:ADR-046-resources-zone-control` | W5 | 26 | -| `wi:ADR-046-security-and-threat-model` | W7 | 18 | +| `wi:ADR-046-security-and-threat-model` | W7 | 19 | | `wi:ADR-046-streamline` | W7 | 24 | | `wi:ADR-046-telemetry-audit-and-support` | W5 | 26 | | `wi:ADR-046-terminology-and-identities` | W0 | 2 | -| `wi:ADR-046-validation-and-delivery` | W7 | 8 | +| `wi:ADR-046-validation-and-delivery` | W7 | 9 | | `wi:ADR-046-zone-routing` | W2 | 16 | -| `wi:core-config-hub` | W5 | 7 | +| `wi:core-config-hub:w4` | W4 | 1 | +| `wi:core-config-hub:w5` | W5 | 6 | ## Critical path (longest dependency chain) @@ -245,27 +249,30 @@ file overlap. 6. `ADR-046-resource-reconciliation` 7. `ADR-046-primitive-resource-composition` 8. `ADR-046-provider-model-and-packaging` -9. `ADR-046-components-processes-and-sandbox` -10. `ADR-046-cli-and-operations` -11. `ADR-046-reset-and-cutover` -12. `ADR046-reset-001` -13. `ADR046-reset-002` -14. `ADR046-reset-003` -15. `ADR046-reset-004` -16. `ADR046-reset-005` -17. `ADR046-reset-006` -18. `ADR046-reset-007` -19. `ADR046-reset-010` -20. `ADR046-reset-011` +9. `ADR-046-core-controllers` +10. `ADR-046-nix-configuration` +11. `ADR-046-provider-runtime-qemu-media` +12. `ADR046-qemu-media-001` +13. `ADR046-qemu-media-003` +14. `ADR046-qemu-media-005` +15. `ADR046-qemu-media-006` +16. `ADR046-qemu-media-009` +17. `ADR046-qemu-media-010` +18. `ADR046-qemu-media-011` +19. `ADR046-qemu-media-013` +20. `ADR046-qemu-media-014` +21. `ADR046-qemu-media-015` +22. `ADR046-qemu-media-018` +23. `ADR046-qemu-media-019` ## Regeneration findings (D095–D098) -- Regenerated from 55 member specs and 538 current work items; no stale 536-item graph content remains. +- Regenerated from 55 member specs and 543 current work items; every declared heading is represented exactly once. - `ADR046-provider-004` owns the common D098 Service/Binding base DTOs and schemas; the four implementation Providers own only strict extensions and controllers. - `ADR046-zone-control-024` owns the shared Core-derived `physical-usb-backing` tuple; both the security-key and USB effect DAGs depend on it. -- Every security-key `W-N`/`W-R`/`W-X` dependency in `Dependency/owner` is encoded. The dependency subgraph is acyclic and uses no generator tie-break. +- Every `ADR046-security-key-*` dependency in `Dependency/owner` is encoded. The dependency subgraph is acyclic and uses no generator tie-break. - File-overlap barriers cover only the shared core configuration/cleanup files. Soft cross-Provider integration order remains file-disjoint and concurrent. -- No repository generator exists at this Proposed stage. `ADR046-streamline-001`/`024` own the future canonical `xtask` implementation. +- No repository generator exists at this Proposed stage. `ADR046-streamline-001`/`024` and `ADR046-delivery-004`/`009` own the future canonical implementation and policy gate. ## Ready-wave algorithm diff --git a/docs/specs/ADR-046-nix-configuration.md b/docs/specs/ADR-046-nix-configuration.md index fe804eaae..502b53903 100644 --- a/docs/specs/ADR-046-nix-configuration.md +++ b/docs/specs/ADR-046-nix-configuration.md @@ -2212,7 +2212,7 @@ marked compile-only. | `WorkloadProviderKind`, `IsolationPosture`, `WorkloadExecutionPosture` | `d2b-realm-core/src/workload.rs` | Live in launcher metadata | `LocalVm`/`QemuMedia`/`ProviderManaged` → `Guest.providerRef` per table; `UnsafeLocal` → user-only `Host` with `spec.isolationPosture: "none"` (never `Guest`; not a v3 Provider) | ADR046-nix-001 | | `Capability` enum, `CapabilitySet` | `d2b-realm-core/src/capability.rs` | Live in provider advertisement | Role verbs / Provider descriptor fields per Capability disposition table | ADR046-nix-001 | | `RuntimeProvider`, `WorkloadProvider`, `HostSubstrateProvider` traits | `d2b-realm-provider/src/provider.rs` | Live (ACA/local-vm implement these) | Provider component descriptors + `ADR-046-provider-.md` dossiers | ADR046-provider-001 | -| `OperationRouter`, `DurableExecTable`, `TargetResolver` | `d2b-realm-router/src/`; `d2bd/src/realm_stubs.rs` | COMPILE-ONLY at baseline (`dead_code`-allowed, not called) | `d2b-bus` routing; adapt `RealmSessionAuthority`/`CredentialCustody`/`RealmServiceLimits` from `main:packages/d2b-realm-router/src/service_v2.rs` | ADR046-bus-010 | +| `OperationRouter`, `DurableExecTable`, `TargetResolver` | `d2b-realm-router/src/`; `d2bd/src/realm_stubs.rs` | COMPILE-ONLY at baseline (`dead_code`-allowed, not called) | `d2b-bus` routing; adapt `RealmSessionAuthority`/`CredentialCustody`/`RealmServiceLimits` from `main:packages/d2b-realm-router/src/service_v2.rs` | ADR046-nix-033 | | `TransportProvider`, `loopback`, `LocalTcpTransport` | `d2b-realm-transport/src/lib.rs`, `local_tcp.rs` | COMPILE-ONLY / conformance tests only | `Provider/transport-unix` (Unix seqpacket); `Provider/transport-vsock`; transport primitives adapt `main:packages/d2b-session-unix/src/` | ADR046-nix-026 | ### Systemd unit mapping @@ -2305,10 +2305,10 @@ excluded or adapted before integration. | `d2b-session-unix` | `adapter.rs`, `socket.rs`, `descriptor.rs`, `credit.rs`, `pidfd.rs`, `systemd.rs`, `vsock.rs` | `packages/d2b-bus/src/transport/unix/` | ADR046-nix-026 | | `d2b-provider` | `registry.rs`, `rpc.rs`, `instance.rs`, `context.rs` | `packages/d2b-provider/src/` (adapt in place) | ADR046-nix-029 | | `d2b-provider-toolkit` | `adapter.rs`, `server.rs`, `conformance.rs`, `registration.rs` | `packages/d2b-provider-toolkit/src/` (adapt in place) | ADR046-nix-030 | -| `d2b-client` | `client.rs`, `session.rs`, `target.rs`, `service.rs`, `daemon_service.rs`, `guest_service.rs`, `host_socket.rs` | `packages/d2b-client/src/` (adapt in place) | ADR046-bus-009 | -| `d2b-realm-router` | `service_v2.rs` | `packages/d2b-bus/src/routing/zone_service.rs` | ADR046-bus-010 | -| `d2bd` | `provider_registry.rs` | `packages/d2bd/src/provider_registry.rs` (adapt in place) | ADR046-bus-011 | -| `d2bd` | `provider_effects.rs` | `packages/d2bd/src/provider_effects.rs` (adapt in place) | ADR046-bus-012 | +| `d2b-client` | `client.rs`, `session.rs`, `target.rs`, `service.rs`, `daemon_service.rs`, `guest_service.rs`, `host_socket.rs` | `packages/d2b-client/src/` (adapt in place) | ADR046-nix-032 | +| `d2b-realm-router` | `service_v2.rs` | `packages/d2b-bus/src/routing/zone_service.rs` | ADR046-nix-033 | +| `d2bd` | `provider_registry.rs` | `packages/d2bd/src/provider_registry.rs` (adapt in place) | ADR046-nix-034 | +| `d2bd` | `provider_effects.rs` | `packages/d2bd/src/provider_effects.rs` (adapt in place) | ADR046-nix-035 | **ComponentSession wire contract — cross-reference only:** The choices for `EndpointRole`, `ServicePackage`, `EndpointPurpose`, `PurposeClass`, and @@ -2326,7 +2326,7 @@ are stable and must not change regardless of string/identifier renames. Zone service target is addressed as `ResourceRef/Zone`; Guest lifecycle operations address resources as `ResourceRef` (`Guest/`) with a Zone context (`Zone/`). Bundle/schema version constants are generated by the owning -contract work item (ADR046-bus-011/ADR046-bus-012). Cross-reference: +contract work item (ADR046-nix-034/ADR046-nix-035). Cross-reference: `ADR-046-componentsession-and-bus` and the d2b-contracts v3 work item. ## Implementation work items @@ -2369,9 +2369,9 @@ contract work item (ADR046-bus-011/ADR046-bus-012). Cross-reference: | --- | --- | | Dependency/owner | ADR046-nix-001; site options | | Current source | `nixos-modules/options-daemon.nix`, `options-site.nix`, `options-host.nix` | -| Reuse action | retain and extend | +| Reuse action | adapt | | Destination | `nixos-modules/options-site.nix` (retained); per-Zone options in `options-zones.nix` | -| Detailed design | `d2b.zones..retainedGenerations` (default 3, range 1..16, compiler setting — not emitted in Zone spec); `d2b.site.stateDir` maps to Zone storage roots; `d2b.site.usePrebuiltHostTools` retained | +| Detailed design | `d2b.zones..retainedGenerations` (default 3, range 1..16, compiler setting — not emitted in Zone spec); `d2b.site.stateDir` maps to Zone storage roots; `d2b.site.usePrebuiltHostTools` retained Primary reuse disposition: `adapt`. Preserved source-plan detail: retain and extend. | | Integration | `options-site.nix` retains site defaults consumed by `options-zones.nix` and `bundle-zones.nix`; activation-helper and Zone storage roots read `stateDir`/`usePrebuiltHostTools` settings. | | Data migration | Operator site options stay in place; new per-Zone `retainedGenerations` defaults apply on first v3 activation; no v2 runtime state import. | | Validation | nix-unit case verifies `retainedGenerations` default/range, `site.stateDir` mapping, and `usePrebuiltHostTools` retention. | @@ -2385,9 +2385,9 @@ contract work item (ADR046-bus-011/ADR046-bus-012). Cross-reference: | --- | --- | | Dependency/owner | ADR046-nix-001, ADR046-nix-002 | | Current source | `nixos-modules/index.nix` — `cfg._index.*`: `enabledEnvs`, `enabledVms`, `netMeta` (derives bridge names/IPs from `lanSubnet`/`uplinkSubnet`), `declaredRealms`, `enabledRealms`, `workloadsInEnv`, `runtimeRows` | -| Reuse action | rewrite | +| Reuse action | adapt | | Destination | `nixos-modules/index.nix` (rewritten); emits `/etc/d2b/index.json` | -| Detailed design | Cross-Zone normalized index: zone/host/guest/network/closure entries; executionIndex; networkIndex; closureIndex; sorted output; `cfg._index` attribute tree retained as internal helper during migration | +| Detailed design | Cross-Zone normalized index: zone/host/guest/network/closure entries; executionIndex; networkIndex; closureIndex; sorted output; `cfg._index` attribute tree retained as internal helper during migration Primary reuse disposition: `adapt`. Preserved source-plan detail: rewrite. | | Integration | `options-zones-resources.nix` feeds rewritten `index.nix`; `bundle-zones.nix`, resource emitters, and activation-helper consume `/etc/d2b/index.json`. | | Data migration | Index is rebuilt from v3 Zone resources at activation; full d2b 3.0 reset; no v2 index state import. | | Validation | nix-unit golden vectors for index shape; drift gate: `xtask gen-index` round-trip | @@ -2401,9 +2401,9 @@ contract work item (ADR046-bus-011/ADR046-bus-012). Cross-reference: | --- | --- | | Dependency/owner | ADR046-nix-004 | | Current source | `nixos-modules/bundle-artifacts.nix` (`artifactModule` submodule, `installFileName`, `mode 0640` ownership); `nixos-modules/bundle.nix` (bundle derivation, SHA256SUMS, `d2b._bundle` integrity chain) | -| Reuse action | extend and rewrite | +| Reuse action | adapt | | Destination | `nixos-modules/bundle-zones.nix` (per-Zone bundle derivation); common helpers retained in `bundle-artifacts.nix` | -| Detailed design | Per-Zone `bundle.json` with `candidateId`/`contentId` binding; SHA256 digest chain; `generationIndex`; atomic activation pointer; `manifestVersion` → `schemaVersion` rename | +| Detailed design | Per-Zone `bundle.json` with `candidateId`/`contentId` binding; SHA256 digest chain; `generationIndex`; atomic activation pointer; `manifestVersion` → `schemaVersion` rename Primary reuse disposition: `adapt`. Preserved source-plan detail: extend and rewrite. | | Integration | `d2b-activation-helper` reads `bundle.json` per Zone; validates digest chain before staging | | Data migration | Per-Zone bundles are generated from v3 resource declarations; full d2b 3.0 reset; no v2 monolithic bundle state import. | | Validation | Artifact-shape contract tests in `packages/d2b-contract-tests/tests/`; determinism test (build twice, diff outputs) | @@ -2417,9 +2417,9 @@ contract work item (ADR046-bus-011/ADR046-bus-012). Cross-reference: | --- | --- | | Dependency/owner | ADR046-nix-005; Process Provider work items (ADR046-primitives-002) | | Current source | `nixos-modules/processes-json.nix` (`VmProcessDag`, `ProcessRole`, `binaryPath`, `argv`, `share.source == "/nix/store"` sentinel); `packages/d2b-core/src/processes.rs` (`ProcessRole` enum — all variants in disposition table above) | -| Reuse action | extract and adapt | +| Reuse action | adapt | | Destination | `nixos-modules/resources-zones-processes.nix`; emits `zones//processes.json` | -| Detailed design | Process/EphemeralProcess resource serialization per disposition table and the frozen `ExecutionSpec`: exact `providerRef`/`executionRef`/domain/user/processClass/template/config/credential/mount/sandbox/budget/`networkUsage`/`deviceUsage`/telemetry names; semantic owner from `metadata.ownerRef` with template fallback through the Process Provider; no `packageRef`, free-form `binaryPath`, `argv`, renamed network/device fields, or inline endpoints; stable produced endpoints are separate owned `Endpoint` resources with `producerRef`; Process uses canonical `restartPolicy` fields; EphemeralProcess uses `runtimeDeadline` and has no restart policy; VsockRelay → `Process` under `Provider/transport-vsock`; GuestSshReadiness retired at v3 cutover; Usbip long-lived backend/proxy → `Process`, Usbip per-busid attach/detach → `EphemeralProcess`, all owned by `Provider/device-usbip` | +| Detailed design | Process/EphemeralProcess resource serialization per disposition table and the frozen `ExecutionSpec`: exact `providerRef`/`executionRef`/domain/user/processClass/template/config/credential/mount/sandbox/budget/`networkUsage`/`deviceUsage`/telemetry names; semantic owner from `metadata.ownerRef` with template fallback through the Process Provider; no `packageRef`, free-form `binaryPath`, `argv`, renamed network/device fields, or inline endpoints; stable produced endpoints are separate owned `Endpoint` resources with `producerRef`; Process uses canonical `restartPolicy` fields; EphemeralProcess uses `runtimeDeadline` and has no restart policy; VsockRelay → `Process` under `Provider/transport-vsock`; GuestSshReadiness retired at v3 cutover; Usbip long-lived backend/proxy → `Process`, Usbip per-busid attach/detach → `EphemeralProcess`, all owned by `Provider/device-usbip` Primary reuse disposition: `adapt`. Preserved source-plan detail: extract and adapt. | | Integration | `processes.json` replaces `cfg._bundle.processesJson`; Process Providers read the new format | | Data migration | Processes are emitted from v3 Process/EphemeralProcess resources; full d2b 3.0 reset; no v2 process artifact import. | | Validation | Process and EphemeralProcess exact-schema vectors; reject `packageRef`, `network`, `devices`, `endpoints`, `restart`, and `runDeadline`; accept canonical `networkUsage`, `deviceUsage`, `restartPolicy`, `runtimeDeadline`, and separately owned Endpoint with `producerRef`; no-raw-path assertion; ProcessRole parity test (every variant has a test case) | @@ -2433,9 +2433,9 @@ contract work item (ADR046-bus-011/ADR046-bus-012). Cross-reference: | --- | --- | | Dependency/owner | ADR046-nix-005; Volume Provider work items (ADR046-primitives-003) | | Current source | `nixos-modules/storage-json.nix` (all `mkPath` calls, `PrincipalRef` `uid`/`gid`/`user` kinds, `StorageRoot`/`StoragePathSpec`/`repairPolicy`/`cleanupPolicy`); `packages/d2b-core/src/storage.rs` (`StorageJson`, `StoragePathSpec`); `nixos-modules/sync-json.nix` (`SyncJson` OFD lock rows); `packages/d2b-core/src/sync.rs` | -| Reuse action | extract storage policy → adapt; retire sync rows | +| Reuse action | adapt | | Destination | `nixos-modules/resources-zones-volumes.nix`; emits `zones//volumes.json`; OFD lock rows move to `d2b-contracts` internals | -| Detailed design | Volume layout/views/ACL/no-follow/repair preserving current policy; `PrincipalRef { kind: "uid" }` → `User/` typed ref only; OFD rows removed from Nix artifacts | +| Detailed design | Volume layout/views/ACL/no-follow/repair preserving current policy; `PrincipalRef { kind: "uid" }` → `User/` typed ref only; OFD rows removed from Nix artifacts Primary reuse disposition: `adapt`. Preserved source-plan detail: extract storage policy → adapt; retire sync rows. | | Integration | Volume resources feed `zones//volumes.json`; the Volume Provider consumes it for path/view/ACL reconciliation; `d2b-contracts` internals own OFD lock rows. | | Data migration | Volume resources are regenerated from v3 config; full d2b 3.0 reset; no v2 storage/sync state import. | | Validation | Volume schema vectors; ACL/no-follow/view policy tests | @@ -2449,9 +2449,9 @@ contract work item (ADR046-bus-011/ADR046-bus-012). Cross-reference: | --- | --- | | Dependency/owner | ADR046-nix-004; Zone/Network migration | | Current source | `nixos-modules/allocator-json.nix` (realm/env bridge assignments, `allocatorStateDir`, socket paths, `providerPlacement`); `nixos-modules/realm-controller-config-json.nix` (emits `realm-controllers.json`); `packages/d2b-core/src/realm_controller_config.rs` (`RealmControllersJson`, `RealmControllerMetadataSummary`; read live by `d2bd/src/realm_access_resolver.rs` from `/etc/d2b/realm-controllers.json`) | -| Reuse action | adapt and retire | +| Reuse action | adapt | | Destination | Compiler-only `parentZone` map in `nixos-modules/options-zones.nix`; local-root allocator bootstrap compiler/sealer; runtime-created Zone self-resource with `spec = {}`; child-local ZoneLink resource; `realm-controllers.json` RETAINED during migration (live d2bd reads it) | -| Detailed design | Resolve each non-root `parentZone` to one declared Zone; reject a root value, missing/unknown/self/conflicting/cyclic parent, and ancestry deeper than 16 names. Canonicalize sorted child→parent rows and feed them to the local-root allocator's private bootstrap sealer. Parent allocators derive/pre-bind privileged socket/listener state from that topology; no socket path or `parentZone` enters `Zone.spec` or a ResourceRef. Child-local ZoneLinks provide transport/local route state. `realm-controllers.json` remains published until `realm_access_resolver` is replaced. | +| Detailed design | Resolve each non-root `parentZone` to one declared Zone; reject a root value, missing/unknown/self/conflicting/cyclic parent, and ancestry deeper than 16 names. Canonicalize sorted child→parent rows and feed them to the local-root allocator's private bootstrap sealer. Parent allocators derive/pre-bind privileged socket/listener state from that topology; no socket path or `parentZone` enters `Zone.spec` or a ResourceRef. Child-local ZoneLinks provide transport/local route state. `realm-controllers.json` remains published until `realm_access_resolver` is replaced. Primary reuse disposition: `adapt`. Preserved source-plan detail: adapt and retire. | | Integration | Nix Zone options → validated topology compiler → local-root allocator bootstrap sealer; ZoneLink controller consumes only its sealed selected-parent binding. During migration, `realm-controllers.json` remains available to the legacy resolver until the Zone topology/ZoneLink path takes over. | | Data migration | Topology is regenerated from `d2b.zones..parentZone`; full d2b 3.0 reset; no allocator/realm-controller state import. | | Validation | Zone self-resource round-trip with exact empty spec; `parentZone` required/root-forbidden/existence/one-parent/self/cycle/16-name-depth vectors; topology seal excludes `parentZone` from Zone/resource bundles; bootstrap socket/listener regression tests | @@ -2497,9 +2497,9 @@ contract work item (ADR046-bus-011/ADR046-bus-012). Cross-reference: | --- | --- | | Dependency/owner | Broker privileges owner | | Current source | `nixos-modules/privileges-json.nix` | -| Reuse action | retain | +| Reuse action | copy-unchanged | | Destination | `nixos-modules/privileges-json.nix` (retained); `/etc/d2b/privileges.json` (retained, site-wide) | -| Detailed design | Broker op catalog is not Zone-scoped; no structural change required | +| Detailed design | Broker op catalog is not Zone-scoped; no structural change required Primary reuse disposition: `copy-unchanged`. Preserved source-plan detail: retain. | | Integration | `privileges-json.nix` remains a site-wide bundle artifact consumed by broker authorization and drift gates; Zone resource emission references it without Zone scoping. | | Data migration | Full d2b 3.0 reset; no v2 state/config import | | Validation | Existing `tests/unit/gates/drift-check.sh` | @@ -2543,9 +2543,9 @@ contract work item (ADR046-bus-011/ADR046-bus-012). Cross-reference: | --- | --- | | Dependency/owner | ADR046-nix-001, ADR046-nix-002 | | Current source | `nixos-modules/assertions.nix` (CIDR overlap, `sys-` prefix, VM-name regex, platform gate) | -| Reuse action | extend in place | +| Reuse action | adapt | | Destination | `nixos-modules/assertions.nix` | -| Detailed design | Migrate existing assertions to Zone/Resource terminology; add ref validation, owner cycles, CIDR overlap (Zones), provider resolution, RoleBinding verb set assertions | +| Detailed design | Migrate existing assertions to Zone/Resource terminology; add ref validation, owner cycles, CIDR overlap (Zones), provider resolution, RoleBinding verb set assertions Primary reuse disposition: `adapt`. Preserved source-plan detail: extend in place. | | Integration | `assertions.nix` validates Zone/Resource declarations before index, bundle, and schema validation derivations run; failing configs block activation artifacts. | | Data migration | None — docs/tooling only; no runtime state | | Validation | Each new assertion has a failing-config test vector | @@ -2559,9 +2559,9 @@ contract work item (ADR046-bus-011/ADR046-bus-012). Cross-reference: | --- | --- | | Dependency/owner | ADR046-nix-001; host activation | | Current source | `nixos-modules/host.nix`, `host-daemon.nix` (fixed local-root endpoint set), `host-activation.nix`, `host-users.nix` | -| Reuse action | retain and adapt | +| Reuse action | adapt | | Destination | Same files; updated to use Zone bundle activation path and Zone resource state dirs | -| Detailed design | `d2b-activation-helper` updated to validate/stage per-Zone bundles; `d2bd.service` updated to read Zone bundle; `d2b` group retained for `SO_PEERCRED` | +| Detailed design | `d2b-activation-helper` updated to validate/stage per-Zone bundles; `d2bd.service` updated to read Zone bundle; `d2b` group retained for `SO_PEERCRED` Primary reuse disposition: `adapt`. Preserved source-plan detail: retain and adapt. | | Integration | Host activation units call `d2b-activation-helper` against per-Zone bundles, then `d2bd.service` reads the staged Zone bundle; `SO_PEERCRED` authorization remains via the `d2b` group. | | Data migration | Host activation reads newly staged Zone bundles on first v3 switch; full d2b 3.0 reset; no v2 daemon runtime state import. | | Validation | Host-integration test with Zone bundle activation and daemon readiness | @@ -2574,9 +2574,9 @@ contract work item (ADR046-bus-011/ADR046-bus-012). Cross-reference: | --- | --- | | Dependency/owner | ADR046-nix-002; Provider/network-local dossier | | Current source | `nixos-modules/network.nix`, `nixos-modules/net.nix` (including `lib.mkForce` DHCP neutralizer and per-env MTU/MSS/east-west wiring) | -| Reuse action | retain until Provider successor | +| Reuse action | copy-unchanged | | Destination | Network reconciliation by `Provider/network-local` Process resources | -| Detailed design | Current bridge/NAT/DHCP/firewall Nix units retained; `Provider/network-local` controller emits equivalent configuration from Network resources; `lib.mkForce` neutralization preserved | +| Detailed design | Current bridge/NAT/DHCP/firewall Nix units retained; `Provider/network-local` controller emits equivalent configuration from Network resources; `lib.mkForce` neutralization preserved Primary reuse disposition: `copy-unchanged`. Preserved source-plan detail: retain until Provider successor. | | Integration | Network resources feed Provider/network-local parity implementation while `net.nix`/`network.nix` remain the fallback; network eval tests compare both paths. | | Data migration | Network resource specs are generated from v3 Nix declarations; full d2b 3.0 reset; no v2 bridge/NAT runtime state import. | | Validation | `tests/net-vm-network-eval.sh` passes against Network resource spec | @@ -2590,9 +2590,9 @@ contract work item (ADR046-bus-011/ADR046-bus-012). Cross-reference: | --- | --- | | Dependency/owner | ADR046-nix-012; Provider/volume-virtiofs | | Current source | `nixos-modules/store.nix` (`ProcessRole::Virtiofsd` and `share.source == "/nix/store"` sentinel; per-VM hardlink farm) | -| Reuse action | retain until Provider successor | +| Reuse action | copy-unchanged | | Destination | Per-VM store reconciliation by `Provider/volume-virtiofs` EphemeralProcess/Process resources | -| Detailed design | `store.nix` retained; `Provider/volume-virtiofs` controller creates equivalent EphemeralProcess; per-VM store path derived from Zone stateDir + Guest name via Provider, not raw path in spec | +| Detailed design | `store.nix` retained; `Provider/volume-virtiofs` controller creates equivalent EphemeralProcess; per-VM store path derived from Zone stateDir + Guest name via Provider, not raw path in spec Primary reuse disposition: `copy-unchanged`. Preserved source-plan detail: retain until Provider successor. | | Integration | Guest/Volume resources drive Provider/volume-virtiofs, which stages hardlink farm processes consumed by Guest process resources; `store.nix` remains until parity. | | Data migration | Full d2b 3.0 reset; no v2 store metadata import | | Validation | Store hardlink integrity; no direct `/nix/store` export | @@ -2605,9 +2605,9 @@ contract work item (ADR046-bus-011/ADR046-bus-012). Cross-reference: | --- | --- | | Dependency/owner | ADR046-nix-002; Provider dossiers for graphics, tpm, usbip, audio | | Current source | `nixos-modules/components/graphics.nix` (`ProcessRole::Gpu`, `GpuRenderNode`, `Video`); `components/tpm.nix` (`ProcessRole::Swtpm`, `SwtpmPreStartFlush`); `components/usbip.nix` (`ProcessRole::Usbip`); `components/audio/` (`ProcessRole::Audio`) | -| Reuse action | each component becomes a Provider install resource + Device/Guest spec field | +| Reuse action | replace | | Destination | `Provider/device-tpm`, `Provider/device-usbip`, `Provider/device-gpu`, `Provider/audio-pipewire` resource install declarations in `options-zones-resources.nix` | -| Detailed design | `d2b.vms..tpm.enable = true` → `d2b.zones..resources.vm-tpm = { type = "Device"; spec = { providerRef = "Provider/device-tpm"; ... }; }`; all component eval assertions migrated to `assertions.nix`; GuestSshReadiness retired at v3 cutover; Usbip long-lived backend/proxy → `Process`, per-busid attach/detach → `EphemeralProcess`, both owned by `Provider/device-usbip` | +| Detailed design | `d2b.vms..tpm.enable = true` → `d2b.zones..resources.vm-tpm = { type = "Device"; spec = { providerRef = "Provider/device-tpm"; ... }; }`; all component eval assertions migrated to `assertions.nix`; GuestSshReadiness retired at v3 cutover; Usbip long-lived backend/proxy → `Process`, per-busid attach/detach → `EphemeralProcess`, both owned by `Provider/device-usbip` Primary reuse disposition: `replace`. Preserved source-plan detail: each component becomes a Provider install resource + Device/Guest spec field. | | Integration | Device/Guest specs feed Provider/device-* and Provider/audio-pipewire install declarations; migrated assertions/tests consume Zone resource configs instead of component toggles. | | Data migration | Component toggles migrate to v3 Device/Guest resource declarations; full d2b 3.0 reset; no v2 component runtime state import. | | Validation | Existing component eval tests; `tests/usbip-gating-eval.sh`; `tests/video-contract-eval.sh` | @@ -2621,9 +2621,9 @@ contract work item (ADR046-bus-011/ADR046-bus-012). Cross-reference: | --- | --- | | Dependency/owner | ADR046-nix-005; ADR046-nix-001; `d2b-contracts` schema generation (ADR046-nix-027) | | Current source | `nixos-modules/bundle-artifacts.nix` (`artifactModule` submodule, mode/ownership); `nixos-modules/bundle.nix` (digest chain, SHA256SUMS); `packages/xtask/src/main.rs` (`gen-schemas`); no current per-ResourceType JSON Schema under `docs/reference/schemas/v3/` | -| Reuse action | extend xtask schema generation; new Nix eval/build validation hooks | +| Reuse action | adapt | | Destination | `docs/reference/schemas/v3/.json` for each ResourceType; `nixos-modules/resource-schema-validation.nix` (validates emitted spec against committed JSON Schema at build time); `nixos-modules/provider-settings-validation.nix` (validates `spec.provider.settings` where declared in schema, and Provider `config`, against Provider-embedded schema at build time); `nixos-modules/assertions.nix` (Credential ref enforcement, secret-pattern rejection) | -| Detailed design | `cargo xtask gen-schemas` emits one JSON Schema per ResourceType under `docs/reference/schemas/v3/`; Nix derivation reads these schemas from `pkgs.d2b-resource-schemas` and validates every emitted `spec` JSON before producing the Zone bundle; Provider-settings validation reads `settingsSchemaDigest` from `provider-catalog.json` and resolves the schema from the Provider package closure; Credential ref enforcement: eval assertion rejects any `spec` string field matching `-----BEGIN`, `eyJ`, or a hex string ≥ 32 bytes in a secret-typed field; `managedBy` in any input spec rejected at eval (core-set runtime field, never in Nix input); bundle integrity: `candidateId`/`contentId` computed over canonical sorted output | +| Detailed design | `cargo xtask gen-schemas` emits one JSON Schema per ResourceType under `docs/reference/schemas/v3/`; Nix derivation reads these schemas from `pkgs.d2b-resource-schemas` and validates every emitted `spec` JSON before producing the Zone bundle; Provider-settings validation reads `settingsSchemaDigest` from `provider-catalog.json` and resolves the schema from the Provider package closure; Credential ref enforcement: eval assertion rejects any `spec` string field matching `-----BEGIN`, `eyJ`, or a hex string ≥ 32 bytes in a secret-typed field; `managedBy` in any input spec rejected at eval (core-set runtime field, never in Nix input); bundle integrity: `candidateId`/`contentId` computed over canonical sorted output Primary reuse disposition: `adapt`. Preserved source-plan detail: extend xtask schema generation; new Nix eval/build validation hooks. | | Integration | Validation hooks wired into `bundle-zones.nix` derivation; `d2b-activation-helper` re-verifies digest chain at staging | | Data migration | None — docs/tooling only; no runtime state | | Validation | Schema round-trip: emit spec, validate against schema, verify byte-identical re-emit; `spec.provider.settings` rejection test (unknown field, out-of-bounds value, raw store path, secret bytes) where schema declares the field; Provider `config` rejection test (unknown field); Credential ref enforcement: PEM-in-spec rejected; secret-pattern-in-spec rejected; valid `Credential/` ref accepted; `managedBy` in spec input rejected at eval | @@ -2637,7 +2637,7 @@ contract work item (ADR046-bus-011/ADR046-bus-012). Cross-reference: | --- | --- | | Dependency/owner | ADR046-nix-005; ADR046-nix-001; configuration-publication controller (ADR-046-core-controllers) | | Current source | No current equivalent; current `bundle.nix` replaces all artifacts atomically with no per-resource cleanup tracking | -| Reuse action | new | +| Reuse action | create | | Destination | Configuration-publication controller handler in `packages/d2bd/src/config_publication.rs`; `ConfigurationOwnedClassifier`; `AbsentResourceReaper`; `Zone` status conditions in `d2b-contracts/src/v3/zone_status.rs`; cleanup audit emitter in `d2b-state/src/audit_segments.rs` | | Detailed design | `ConfigurationOwnedClassifier`: classify resources by core-set `managedBy` field only — `managedBy=configuration` resources are owned by config publication; `managedBy=controller` and `managedBy=api` resources are never touched. At activation, diff new-generation bundle name+type set against all resources with `managedBy=configuration` in the Zone store; resources absent from the new bundle are enqueued for Delete. Never infer ownership from `ownerRef`, labels, or absence from emitted files. `AbsentResourceReaper`: processes the Absent queue asynchronously; does not block pointer swap (step 4); sets `status.phase=Pending` + `PendingDeletion` condition (`reason: AbsentFromConfiguration`); waits for all finalizers to clear; commits a single store transaction that appends the `Deleted` revision and removes the resource row and its indexes; subsequent `Get` returns not-found; the `ResourceDelete` audit event is appended afterward with dedup/exactly-once recovery. Zone phase: `Pending` during pointer-swap-to-first-reconcile window; `Degraded` while any `managedBy=configuration` resource carries `PendingDeletion` or a ZoneLink lags; `Ready` when all reconciled. Generation pruning: prune when `generationIndex ≤ activeIndex - retainedGenerations` AND all enqueued resources from that generation have reached `Deleted`. Rollback: re-adopt `managedBy=configuration` resources in `Pending/PendingDeletion` back to the rollback target generation's owned set | | Integration | `d2b-activation-helper` sets `managedBy=configuration` + `configurationGeneration` on every resource it activates; controller reads these fields to determine owned set — never `ownerRef` or bundle membership alone | @@ -2653,7 +2653,7 @@ contract work item (ADR046-bus-011/ADR046-bus-012). Cross-reference: | --- | --- | | Dependency/owner | ADR046-nix-001; `d2b-contracts` workspace policy tests | | Current source | `packages/d2b-contract-tests/tests/` (existing workspace policy lints: `tests/workspace-member-sort.rs`, `tests/crate-naming.rs`); no current Provider crate layout gate exists | -| Reuse action | new | +| Reuse action | create | | Destination | `packages/d2b-contract-tests/tests/provider-crate-layout.rs`; workspace scan in `packages/xtask/src/main.rs` (extend `check-workspace` or add `check-provider-layout` subcommand) | | Detailed design | Parse the root `packages/Cargo.toml` workspace member list; for every member path matching `packages/d2b-provider-*-*`: assert (1) `src/` directory exists and contains at least one `.rs` file; (2) `tests/` directory exists and contains at least one `.rs` file; (3) `integration/` directory exists and contains at least one `.rs` or fixture file; (4) `README.md` exists and is ≥ 200 bytes. All four conditions required; any single failure fails the test with a structured message naming the crate and missing path. Test runs as `cargo test -p d2b-contract-tests provider_crate_layout`; wired into `make test-policy`. | | Integration | Wired into `make test-policy` (same gate family as existing workspace policy tests); no new `Makefile` target needed unless `test-policy` does not yet exist | @@ -2669,7 +2669,7 @@ contract work item (ADR046-bus-011/ADR046-bus-012). Cross-reference: | --- | --- | | Dependency/owner | ADR046-nix-005 (bundle derivation); `d2b-contracts` schema generation (ADR046-nix-027) | | Current source | No current equivalent for a separate artifact catalog. Current `nixos-modules/bundle.nix` embeds package derivation references inline. Current `nixos-modules/closures-json.nix` uses `pkgs.closureInfo` keyed by `d2b.vms.`. | -| Reuse action | new | +| Reuse action | create | | Destination | `nixos-modules/artifact-catalog.nix` (new emitter); `nixos-modules/options-artifacts.nix` (new option: `d2b.artifacts. = { package; type; }`); `/etc/d2b/artifact-catalog.json` (output artifact, `root:d2bd` 0640); `nixos-modules/bundle-zones.nix` (extend to include artifact catalog digest in `bundle.json`); `nixos-modules/options-zones-resources.nix` (replace `closureRef` / `nixosSystem` helpers with `systemArtifactId` validation) | | Detailed design | `d2b.artifacts.` attrset option: `id` matches `^[a-z][a-z0-9-]*$`; `type ∈ { "provider", "nixos-system", "nixos-module-set", "config-bundle" }`; no other fields. Emitter computes `pkgs.closureInfo` for each entry and writes `artifact-catalog.json` with sorted entries (by `artifactId`) containing `artifactId`, `type`, `storePath` (private, for activation-helper staging), `packageDigest`, `closureDigest`, `closureSize`. `storePath` is a private field of the root:d2bd 0640 file; it is never emitted in public ResourceSpecs, status fields, audit records, or OTEL telemetry. The `bundle.json` manifest includes the artifact catalog file entry and its SHA256 digest. `d2b-activation-helper` reads `storePath` from the catalog to resolve and stage each artifact; verifies catalog digest before staging. Build-time validation: `artifactId` / `systemArtifactId` / `source.systemArtifactId` fields in resource specs resolve against `d2b.artifacts`; type-mismatch fails with a structured error. `d2b.providerCatalog..package` option is removed; replaced by `d2b.providerCatalog..artifactId`. `Guest.spec.systemArtifactId` replaces the former `nixosSystem` Nix-only helper. `Volume.source.systemArtifactId` replaces `source.closureRef`. | | Integration | `nixos-modules/closures-json.nix` rewritten (ADR046-nix-012) to key by artifact ID; `nixos-modules/bundle-zones.nix` includes artifact catalog in integrity chain | @@ -2686,9 +2686,9 @@ contract work item (ADR046-bus-011/ADR046-bus-012). Cross-reference: | --- | --- | | Dependency/owner | ADR-046-componentsession-and-bus spec; ADR046-nix-027 (wire contract) must land first | | Current source | Main commit source row below: `packages/d2b-session/` at `a1cc0b2d`, including session engine, handshake, lifecycle, scheduler, record protection, fragmentation, transport, driver, server, and metrics modules. | -| Reuse action | Reuse from main commit `a1cc0b2d`; adapt ADR45 EndpointPurpose/ServicePackage names listed in `ADR45 exclusions` while preserving wire tag values. | +| Reuse action | adapt | | Destination | `packages/d2b-bus/src/session/` (new crate `d2b-bus`); `ComponentSessionDriver` becomes the central abstraction for all Zone bus sessions. | -| Detailed design | Complete session protocol: preface negotiation, Noise handshake profiles, record protection, replay cache, fair scheduler, fragmentation/reassembly, keepalive, ttrpc bridge, generation discovery, and `ComponentSessionDriver` application surface. | +| Detailed design | Complete session protocol: preface negotiation, Noise handshake profiles, record protection, replay cache, fair scheduler, fragmentation/reassembly, keepalive, ttrpc bridge, generation discovery, and `ComponentSessionDriver` application surface. Primary reuse disposition: `adapt`. Preserved source-plan detail: Reuse from main commit `a1cc0b2d`; adapt ADR45 EndpointPurpose/ServicePackage names listed in `ADR45 exclusions` while preserving wire tag values.. | | Integration | `d2b-bus` session runtime is used by Zone bus sessions for local-root controller to broker, controller to guest agent, and controller to provider agent; `serve_ttrpc_services` exposes service handlers. | | Data migration | Full d2b 3.0 reset; no v2 session runtime state/config import | | Validation | Port `component_session.rs` and `noise_vectors.rs`; run `make test-rust`; Noise KAT vectors must pass after copy. | @@ -2706,9 +2706,9 @@ contract work item (ADR046-bus-011/ADR046-bus-012). Cross-reference: | --- | --- | | Dependency/owner | ADR046-nix-023; ADR046-nix-027 | | Current source | Main commit source row below: `packages/d2b-session/src/attachment.rs` and `packages/d2b-session/src/streams.rs` at `a1cc0b2d`. | -| Reuse action | Reuse from main commit `a1cc0b2d`; keep attachment/stream semantics and descriptor validation, with no ADR45 realm adaptation required for this item. | +| Reuse action | adapt | | Destination | `packages/d2b-bus/src/session/` (same crate as ADR046-nix-023). | -| Detailed design | Attachment ownership validates descriptors only after authenticated decrypt; unconsumed payloads close on drop. NamedStreamMux preserves half-close, reset, per-stream limits, aggregate limits, and credit-based flow control. | +| Detailed design | Attachment ownership validates descriptors only after authenticated decrypt; unconsumed payloads close on drop. NamedStreamMux preserves half-close, reset, per-stream limits, aggregate limits, and credit-based flow control. Primary reuse disposition: `adapt`. Preserved source-plan detail: Reuse from main commit `a1cc0b2d`; keep attachment/stream semantics and descriptor validation, with no ADR45 realm adaptation required for this item.. | | Integration | `d2b-bus` session transport validates attachments after authenticated decrypt and exposes named streams to ttrpc/services; Unix/vsock transports provide payloads. | | Data migration | Full d2b 3.0 reset; no v2 attachment or stream state/config import | | Validation | Port `component_session.rs` attachment ownership, stream mux, and credit-accounting tests. | @@ -2725,9 +2725,9 @@ contract work item (ADR046-bus-011/ADR046-bus-012). Cross-reference: | --- | --- | | Dependency/owner | ADR046-nix-023; ADR046-nix-027 | | Current source | Main commit source row below: `packages/d2b-session/src/cancellation.rs`, `deadline.rs`, and `bootstrap.rs` at `a1cc0b2d`. | -| Reuse action | Reuse from main commit `a1cc0b2d`; adapt `CancelRequest`/`CancelResponse` module paths if v3 services move the common proto types. | +| Reuse action | adapt | | Destination | `packages/d2b-bus/src/session/`. | -| Detailed design | Per-generation `RequestRegistry`, request-wide cancellation, `DeadlineBudget::admit_metadata()` as the inbound metadata gate, and single-use `BootstrapAdmission::consume()` replay protection. | +| Detailed design | Per-generation `RequestRegistry`, request-wide cancellation, `DeadlineBudget::admit_metadata()` as the inbound metadata gate, and single-use `BootstrapAdmission::consume()` replay protection. Primary reuse disposition: `adapt`. Preserved source-plan detail: Reuse from main commit `a1cc0b2d`; adapt `CancelRequest`/`CancelResponse` module paths if v3 services move the common proto types.. | | Integration | `RequestRegistry`, `DeadlineBudget`, and `BootstrapAdmission` feed all `d2b-bus` inbound ttrpc calls and resource API cancellation paths. | | Data migration | Full d2b 3.0 reset; no v2 cancellation/deadline/bootstrap state import | | Validation | Port cancel-before-dispatch, cancel-after-dispatch, generation-mismatch cancel, and deadline admit tests; add v3 common-proto path adapter test. | @@ -2744,9 +2744,9 @@ contract work item (ADR046-bus-011/ADR046-bus-012). Cross-reference: | --- | --- | | Dependency/owner | ADR046-nix-023 | | Current source | Main commit source row below: `packages/d2b-session-unix/src/{adapter,credit,descriptor,pidfd,socket,systemd,vsock}.rs` at `a1cc0b2d`. | -| Reuse action | Reuse from main commit `a1cc0b2d`; retain host-socket/native-vsock feature gates and replace only ADR45 socket-name strings with Zone bootstrap config. | +| Reuse action | adapt | | Destination | `packages/d2b-bus/src/transport/unix/`. | -| Detailed design | Unix seqpacket/stream transports, pidfd identity verification, six-scope credit pool with host/process bounds, systemd-activated seqpacket listener support, and length-prefixed vsock framing. | +| Detailed design | Unix seqpacket/stream transports, pidfd identity verification, six-scope credit pool with host/process bounds, systemd-activated seqpacket listener support, and length-prefixed vsock framing. Primary reuse disposition: `adapt`. Preserved source-plan detail: Reuse from main commit `a1cc0b2d`; retain host-socket/native-vsock feature gates and replace only ADR45 socket-name strings with Zone bootstrap config.. | | Integration | `d2b-bus` listeners and Provider/guest sessions instantiate UnixSeqpacket/UnixStream/vsock transports; credit pool enforces attachment FD budgets before handing payloads to session runtime. | | Data migration | Full d2b 3.0 reset; no v2 Unix transport state/config import | | Validation | Port `unix_session.rs` seqpacket, stream, pidfd, FD attachment, and credit pool tests; add Zone bootstrap socket-name fixture. | @@ -2763,9 +2763,9 @@ contract work item (ADR046-bus-011/ADR046-bus-012). Cross-reference: | --- | --- | | Dependency/owner | ADR-046-componentsession-and-bus; naming and wire enumeration decisions per ADR-046-componentsession-and-bus owning spec before final adoption | | Current source | Main commit source row below: `packages/d2b-contracts/src/v2_component_session.rs` at `a1cc0b2d` plus committed session/noise tests. | -| Reuse action | Reuse wire constants/types from main commit `a1cc0b2d`; rename ADR45 realm-oriented variants only as directed by the bus owning spec and preserve numeric tags. | +| Reuse action | adapt | | Destination | `packages/d2b-contracts/src/v3/component_session.rs`. | -| Detailed design | Canonical wire values, bounded vectors, binary size constants, closed-enum tag codecs, and fail-closed validation for the ComponentSession protocol. | +| Detailed design | Canonical wire values, bounded vectors, binary size constants, closed-enum tag codecs, and fail-closed validation for the ComponentSession protocol. Primary reuse disposition: `adapt`. Preserved source-plan detail: Reuse wire constants/types from main commit `a1cc0b2d`; rename ADR45 realm-oriented variants only as directed by the bus owning spec and preserve numeric tags.. | | Integration | `d2b-contracts` v3 component_session module is consumed by `d2b-bus`, `d2b-session`, `d2b-session-unix`, `d2b-client`, and provider toolkit for shared wire constants/enums. | | Data migration | Full d2b 3.0 reset; no v2 ComponentSession contract state import | | Validation | Port component session and Noise vector tests; add wire-tag stability gates for renamed role/service/purpose variants. | @@ -2782,9 +2782,9 @@ contract work item (ADR046-bus-011/ADR046-bus-012). Cross-reference: | --- | --- | | Dependency/owner | ADR046-nix-027; Zone service naming per ADR-046-componentsession-and-bus owning spec | | Current source | Main commit source row below: `packages/d2b-contracts/src/generated_v2_services/`, `v2_guest_services.rs`, and `v2_component_session.rs` service inventory at `a1cc0b2d`. | -| Reuse action | Reuse generated ttrpc service tables from main commit `a1cc0b2d`; adapt ADR45 Realm/Workload fields and service names to v3 Zone/Resource naming per owning bus spec. | +| Reuse action | adapt | | Destination | `packages/d2b-contracts/src/v3/services/`. | -| Detailed design | Versioned ttrpc service modules preserve server dispatch tables, shared request/cancel/outcome/error types, and service fingerprint inventory. | +| Detailed design | Versioned ttrpc service modules preserve server dispatch tables, shared request/cancel/outcome/error types, and service fingerprint inventory. Primary reuse disposition: `adapt`. Preserved source-plan detail: Reuse generated ttrpc service tables from main commit `a1cc0b2d`; adapt ADR45 Realm/Workload fields and service names to v3 Zone/Resource naming per owning bus spec.. | | Integration | Generated v3 services feed `d2b-bus` server/client dispatch, provider-toolkit conformance, and `SERVICE_INVENTORY` fingerprint validation. | | Data migration | Full d2b 3.0 reset; no v2 generated service state import | | Validation | Port provider-toolkit conformance and service fingerprint assertions; add v3 service-name gate for Zone service and ResourceName field adaptation. | @@ -2801,9 +2801,9 @@ contract work item (ADR046-bus-011/ADR046-bus-012). Cross-reference: | --- | --- | | Dependency/owner | ADR046-nix-027; ADR046-nix-028; EndpointRole naming per ADR-046-componentsession-and-bus owning spec | | Current source | Main commit source row below: `packages/d2b-provider/src/{registry,rpc,instance,context}.rs` at `a1cc0b2d`. | -| Reuse action | Reuse ProviderRegistry/RPC runtime from main commit `a1cc0b2d`; adapt `SessionIdentity` to Zone naming and update EndpointRole/ServicePackage names per owning bus spec. | +| Reuse action | adapt | | Destination | `packages/d2b-provider/src/` (adapt in place). | -| Detailed design | ProviderRegistry snapshot admission/drain, bounded in-flight caps, typed RpcProviderProxy dispatch, and build-time RegistryLimits validation. | +| Detailed design | ProviderRegistry snapshot admission/drain, bounded in-flight caps, typed RpcProviderProxy dispatch, and build-time RegistryLimits validation. Primary reuse disposition: `adapt`. Preserved source-plan detail: Reuse ProviderRegistry/RPC runtime from main commit `a1cc0b2d`; adapt `SessionIdentity` to Zone naming and update EndpointRole/ServicePackage names per owning bus spec.. | | Integration | Zone controller publishes active ProviderRegistry snapshots; provider-toolkit RPC proxy admits sessions through `d2b-bus` and dispatches typed Provider operations. | | Data migration | Full d2b 3.0 reset; no v2 ProviderRegistry runtime state import | | Validation | Port `d2b-provider` `runtime.rs` tests; add v3 AdmissionOptions peer-role and SessionIdentity zone-name gates. | @@ -2820,9 +2820,9 @@ contract work item (ADR046-bus-011/ADR046-bus-012). Cross-reference: | --- | --- | | Dependency/owner | ADR046-nix-029; ADR046-nix-028; EndpointRole and ServicePackage naming per ADR-046-componentsession-and-bus owning spec | | Current source | Main commit source row below: `packages/d2b-provider-toolkit/src/{adapter,server,conformance,registration}.rs` at `a1cc0b2d`. | -| Reuse action | Reuse provider toolkit from main commit `a1cc0b2d`; adapt endpoint role/service variant names and v2_identity imports while preserving wire tags. | +| Reuse action | adapt | | Destination | `packages/d2b-provider-toolkit/src/` (adapt in place). | -| Detailed design | ProviderAgentAdapter validates ComponentSession identity, GeneratedProviderServiceServer dispatches all ProviderType method families with bounded stores/in-flight state, and conformance kit gates every Provider implementation. | +| Detailed design | ProviderAgentAdapter validates ComponentSession identity, GeneratedProviderServiceServer dispatches all ProviderType method families with bounded stores/in-flight state, and conformance kit gates every Provider implementation. Primary reuse disposition: `adapt`. Preserved source-plan detail: Reuse provider toolkit from main commit `a1cc0b2d`; adapt endpoint role/service variant names and v2_identity imports while preserving wire tags.. | | Integration | Provider processes embed `GeneratedProviderServiceServer`; `ProviderAgentAdapter` validates ComponentSession identity before invoking registry instances; conformance kit gates every Provider crate. | | Data migration | Full d2b 3.0 reset; no v2 provider-toolkit runtime state import | | Validation | Port `conformance.rs`; assert endpoint role/service wire tags remain 7/4 after any variant rename. | @@ -2839,7 +2839,7 @@ contract work item (ADR046-bus-011/ADR046-bus-012). Cross-reference: | --- | --- | | Dependency/owner | ADR046-provider-004, ADR046-zone-control-019, ADR046-zone-control-020, ADR046-zone-control-024; Nix resource compiler owner | | Current source | None — D096/D098 semantic Service/Binding authoring and projection factories are net-new in v3 | -| Reuse action | net-new | +| Reuse action | create | | Destination | `nixos-modules/resources-sharing.nix`; `nixos-modules/assertions.nix`; `tests/unit/nix/cases/resource-sharing.nix` | | Detailed design | Compile the eight exact D098 semantic Service/Binding types plus `ResourceExport`/`ResourceImport` without a second vocabulary. Nix authors authority Services and consumer Bindings, never projection Services; Binding requires same-Zone `serviceRef` and allowed target, while all observed realization remains absent from spec. Core receives a stable projection name/factory fingerprint and creates exactly one same-type Service with ResourceImport ownership, `providerRef`, semantic base/import fields, and no `spec.provider`. Reject Device/Endpoint/Binding export/projection, implementation-qualified/former `*State` aliases, implementation detail in base schemas, cross-Zone refs, USB export without every policy opt-in, and any USB/security-key physical resource that does not use the exact Core-derived `physical-usb-backing` tuple class. | | Integration | The per-Zone generation uses ADR046-provider-004 common schema fingerprints and ADR046-zone-control-019/020 factory metadata; the four initial Provider modules contribute only strict Provider extensions and implementation config. | @@ -2847,15 +2847,15 @@ contract work item (ADR046-bus-011/ADR046-bus-012). Cross-reference: | Validation | Fast Nix eval/build tests cover all four exact pairs, common base schema discovery, canonical minimal base without `spec.provider`, stable lowering across repeated evaluations, same-Zone refs/targets, strict Provider extension placement, Service-only export, exactly-one Core projection metadata with no `spec.provider`, no auto-Binding, status-field rejection in spec, USB/security-key resolution to one byte-identical Host-global `(Host, physical-usb-backing, opaqueKeyDigest)` collision before effects, Provider-private-class bypass rejection, and rejection of all forbidden names/aliases. | | Removal proof | Provider-local duplicate Nix base schemas and old direct Device/Endpoint/`*State` projection emitters are removed after the shared emitter and all four dossier migration tests pass. | -### ADR046-bus-009 +### ADR046-nix-032 | Field | Value | | --- | --- | | Dependency/owner | ADR046-nix-029; ADR046-nix-027; TargetInput v3 shape per ADR-046-componentsession-and-bus owning spec | | Current source | Main commit source row below: `packages/d2b-client/src/{client,session,target,service,daemon_service,guest_service,host_socket}.rs` at `a1cc0b2d`. | -| Reuse action | Reuse async client from main commit `a1cc0b2d`; adapt TargetInput, ServiceOwner, service packages, and local socket identity to Zone/ResourceRef semantics. | +| Reuse action | adapt | | Destination | `packages/d2b-client/src/` (adapt in place). | -| Detailed design | Transport-neutral typed async client, ComponentSessionConnector abstraction, precise SessionFailure classification, signed metadata envelopes, named-stream client abstraction, and host-socket connector. | +| Detailed design | Transport-neutral typed async client, ComponentSessionConnector abstraction, precise SessionFailure classification, signed metadata envelopes, named-stream client abstraction, and host-socket connector. Primary reuse disposition: `adapt`. Preserved source-plan detail: Reuse async client from main commit `a1cc0b2d`; adapt TargetInput, ServiceOwner, service packages, and local socket identity to Zone/ResourceRef semantics.. | | Integration | CLI/controllers construct TargetInput/MetadataInput; `d2b-client` resolves Zone/Resource routes via `d2b-bus` and reuses ComponentSessionConnector for calls/streams. | | Data migration | Full d2b 3.0 reset; no v2 client state/config import | | Validation | Port `d2b-client` tests; add ResourceRef TargetInput and Zone bootstrap socket identity gates. | @@ -2866,15 +2866,15 @@ contract work item (ADR046-bus-011/ADR046-bus-012). Cross-reference: | v3 destination | `packages/d2b-client/src/` (adapt in place); client becomes the primary CLI and controller access path for Zone-local and cross-Zone ComponentSession services | | ADR45 exclusions | `TargetInput::Workload { realm: RealmId, workload: WorkloadId }` → v3 shape addresses resources as `ResourceRef` (e.g., `Zone/`, `Guest/`) per ADR-046-componentsession-and-bus owning spec; `TargetInput::Realm(RealmId)` → `TargetInput::Zone(ZoneId)`; `ServiceOwner::Workload { realm, workload }` → `ServiceOwner::Resource { zone: ZoneId, resource: ResourceName }`. `HostSocketConnector::local_daemon_endpoint_identity()` returns identity pinned to current `d2bd.socket` path — v3 socket path comes from Zone bootstrap config and must not be hard-coded. `DaemonClient`/`DaemonMethod` verb set per resource-api/authz foundation spec | -### ADR046-bus-010 +### ADR046-nix-033 | Field | Value | | --- | --- | | Dependency/owner | ADR046-nix-023; ADR046-nix-027; naming and service name per ADR-046-componentsession-and-bus owning spec | | Current source | Main commit source row below: `packages/d2b-realm-router/src/service_v2.rs` and tests at `a1cc0b2d`. | -| Reuse action | Reuse routing service behavior from main commit `a1cc0b2d`; rename RealmSessionAuthority/service identifiers to Zone terms per owning bus spec while preserving custody behavior. | +| Reuse action | adapt | | Destination | `packages/d2b-bus/src/routing/zone_service.rs`. | -| Detailed design | ZoneSessionAuthority preserves no-host-credential custody, gateway/ZoneLink custody behavior, bounded concurrent dispatch, and graceful shutdown semantics. | +| Detailed design | ZoneSessionAuthority preserves no-host-credential custody, gateway/ZoneLink custody behavior, bounded concurrent dispatch, and graceful shutdown semantics. Primary reuse disposition: `adapt`. Preserved source-plan detail: Reuse routing service behavior from main commit `a1cc0b2d`; rename RealmSessionAuthority/service identifiers to Zone terms per owning bus spec while preserving custody behavior.. | | Integration | `d2b-bus` Zone service handles route admission and shortcut lifecycle; ZoneSessionAuthority feeds session/audit/metric labels with stable custody state. | | Data migration | Full d2b 3.0 reset; no v2 routing state import | | Validation | Port `realm_service_v2.rs` and topology harness tests with Zone naming; add service-name wire gate. | @@ -2885,15 +2885,15 @@ contract work item (ADR046-bus-011/ADR046-bus-012). Cross-reference: | v3 destination | `packages/d2b-bus/src/routing/zone_service.rs`; `RealmSessionAuthority` renames to `ZoneSessionAuthority`; `CredentialCustody` is behavior-stable and requires no rename; `REALM_SERVICE_NAME` updates per ADR-046-componentsession-and-bus owning spec | | ADR45 exclusions | `realm: RealmId` field in `RealmSessionAuthority` → `zone: ZoneId`; `REALM_SERVICE_NAME = "d2b.realm.v2.RealmService"` → v3 service name per ADR-046-componentsession-and-bus owning spec; `EndpointRole::LocalRootController`, `RealmController`, `RemotePeer` used in `new()` validation — may rename per owning spec; wire tags remain stable; `PurposeClass::Local`/`Enrolled`/`Bootstrap` — confirm per owning spec | -### ADR046-bus-011 +### ADR046-nix-034 | Field | Value | | --- | --- | | Dependency/owner | ADR046-nix-029; ADR046-nix-028; ADR046-nix-027; PROVIDER_BUNDLE_VERSION bump required on any bundle artifact format change | | Current source | Main commit source row below: `packages/d2bd/src/provider_registry.rs` at `a1cc0b2d`. | -| Reuse action | Reuse fail-closed provider composition from main commit `a1cc0b2d`; adapt v2 identity/bundle artifact types to v3 provider registry contracts and bump bundle versions. | +| Reuse action | adapt | | Destination | `packages/d2bd/src/provider_registry.rs` (adapt in place). | -| Detailed design | Named ProviderCompositionError variants, first-party factory instantiation, bundle resolver validation, version/schema constants, and monotone lifecycle operation IDs. | +| Detailed design | Named ProviderCompositionError variants, first-party factory instantiation, bundle resolver validation, version/schema constants, and monotone lifecycle operation IDs. Primary reuse disposition: `adapt`. Preserved source-plan detail: Reuse fail-closed provider composition from main commit `a1cc0b2d`; adapt v2 identity/bundle artifact types to v3 provider registry contracts and bump bundle versions.. | | Integration | `provider_registry.rs` composes the v3 provider catalog into active ProviderRegistry snapshots consumed by `d2b-bus`/provider runtime and bundle validation. | | Data migration | Full d2b 3.0 reset; provider bundle format bumps to v3 and no v2 provider registry state import. | | Validation | Port provider_registry composition tests; add PROVIDER_BUNDLE_VERSION/SCHEMA_VERSION v3 bump gate and ZoneId/ResourceName binding test. | @@ -2904,15 +2904,15 @@ contract work item (ADR046-bus-011/ADR046-bus-012). Cross-reference: | v3 destination | `packages/d2bd/src/provider_registry.rs` (adapt in place); `PROVIDER_BUNDLE_VERSION` bumps when bundle artifact format changes; `PROVIDER_BUNDLE_SCHEMA_VERSION` updates from `"v2"` to `"v3"`; `ProviderCompositionError` variants retained with v3-specific variants added | | ADR45 exclusions | Uses `d2b_contracts::v2_identity::{RealmId, WorkloadId, RealmPath as ProviderRealmPath}` in binding contexts → adapt to `ZoneId`/`ResourceName`; `d2b_contracts::provider_registry_v2` module types (`ProviderBindingV2ConsumerView`, `ProviderRegistryEntryV2`, `ProviderRegistryV2`) are ADR45 bundle artifact types → v3 replaces with `d2b_contracts::v3::provider_registry`; `PROVIDER_BUNDLE_VERSION = 13` is the ADR45 pinned version — a bump is required before v3 adoption; numeric value is determined when the v3 bundle format is finalized in this work item | -### ADR046-bus-012 +### ADR046-nix-035 | Field | Value | | --- | --- | -| Dependency/owner | ADR046-bus-011; ADR046-nix-029; GuestLifecycleRequest ResourceRef addressing per ADR-046-componentsession-and-bus owning spec | +| Dependency/owner | ADR046-nix-034; ADR046-nix-029; GuestLifecycleRequest ResourceRef addressing per ADR-046-componentsession-and-bus owning spec | | Current source | Main commit source row below: `packages/d2bd/src/provider_effects.rs` at `a1cc0b2d`. | -| Reuse action | Reuse descriptor-bound effect dispatch from main commit `a1cc0b2d`; adapt lifecycle request wire type from VM name to Zone-scoped Guest ResourceRef. | +| Reuse action | adapt | | Destination | `packages/d2bd/src/provider_effects.rs` (adapt in place). | -| Detailed design | DaemonEffectAdapters bind semantic effect ports; ProviderLifecycleDispatch bounds lifecycle mutations and routes broker start/stop through blocking adapters with idempotency-key deduplication. | +| Detailed design | DaemonEffectAdapters bind semantic effect ports; ProviderLifecycleDispatch bounds lifecycle mutations and routes broker start/stop through blocking adapters with idempotency-key deduplication. Primary reuse disposition: `adapt`. Preserved source-plan detail: Reuse descriptor-bound effect dispatch from main commit `a1cc0b2d`; adapt lifecycle request wire type from VM name to Zone-scoped Guest ResourceRef.. | | Integration | `ProviderLifecycleDispatch` receives v3 Guest lifecycle operations from `d2b-bus`/core controller and routes broker start/stop through descriptor-bound effect ports. | | Data migration | Full d2b 3.0 reset; no v2 lifecycle mutation state import | | Validation | Port `provider_effects.rs` lifecycle dispatch tests; add ResourceRef Guest lifecycle request and BrokerCallerRole compatibility gates. | diff --git a/docs/specs/ADR-046-primitive-resource-composition.md b/docs/specs/ADR-046-primitive-resource-composition.md index f7b393467..644a64b07 100644 --- a/docs/specs/ADR-046-primitive-resource-composition.md +++ b/docs/specs/ADR-046-primitive-resource-composition.md @@ -412,9 +412,9 @@ systemd unit/minijail state; they write Process status. | --- | --- | | Dependency/owner | W0; resource contracts | | Current source | `packages/d2b-core/src/processes.rs`, `minijail_profile.rs`, `storage.rs`; `d2b-contracts/src/broker_wire.rs` | -| Reuse action | extract and adapt | +| Reuse action | adapt | | Destination | `packages/d2b-contracts/src/v3/host.rs`, `guest.rs`, `execution_policy.rs`, `process.rs`, `volume.rs`, `user.rs`, `network.rs`, `device.rs`, `credential.rs` | -| Detailed design | Complete minimal ResourceType schemas and shared execution/Volume sub-schemas | +| Detailed design | Complete minimal ResourceType schemas and shared execution/Volume sub-schemas Primary reuse disposition: `adapt`. Preserved source-plan detail: extract and adapt. | | Integration | Provider dossiers/controller descriptors bind exact types | | Data migration | Full reset | | Validation | Schema vectors and folded-field/no-duplicate-type policy tests | @@ -426,9 +426,9 @@ systemd unit/minijail state; they write Process status. | --- | --- | | Dependency/owner | Process contracts; system Provider slices | | Current source | broker SpawnRunner/pidfd; d2bd supervisor; unsafe-local helper; guest exec runner | -| Reuse action | extract and adapt | +| Reuse action | adapt | | Destination | `packages/d2b-provider-system-systemd/`, `packages/d2b-provider-system-minijail/`, shared neutral process conformance library | -| Detailed design | Common Process/EphemeralProcess, provider-specific launch/pidfd/wait/adoption/status | +| Detailed design | Common Process/EphemeralProcess, provider-specific launch/pidfd/wait/adoption/status Primary reuse disposition: `adapt`. Preserved source-plan detail: extract and adapt. | | Integration | Process controller registration under Host/Guest; d2b-bus ResourceClient/status | | Data migration | Current ProcessRoles converted by exact disposition table | | Validation | Shared conformance plus Host/Guest/user integration | @@ -440,9 +440,9 @@ systemd unit/minijail state; they write Process status. | --- | --- | | Dependency/owner | Volume contract; Volume Provider slices | | Current source | `storage-json.nix`, `d2b-core/src/storage.rs`, store/TPM/runtime path owners, virtiofsd argv/ProcessRole | -| Reuse action | extract and adapt | +| Reuse action | adapt | | Destination | `packages/d2b-provider-volume-*/`; `nixos-modules/resources-volume.nix` | -| Detailed design | Fine-grained Volume layout/views, host-path policy, virtiofs attachments/status/owned Process | +| Detailed design | Fine-grained Volume layout/views, host-path policy, virtiofs attachments/status/owned Process Primary reuse disposition: `adapt`. Preserved source-plan detail: extract and adapt. | | Integration | Host/Guest/Process refs and Volume controller | | Data migration | Full reset; Provider-specific state export only where separately specified | | Validation | ACL/no-follow/marker, sharing/views, virtiofs host/guest mount tests | diff --git a/docs/specs/ADR-046-provider-model-and-packaging.md b/docs/specs/ADR-046-provider-model-and-packaging.md index c75a0099b..3fca6854f 100644 --- a/docs/specs/ADR-046-provider-model-and-packaging.md +++ b/docs/specs/ADR-046-provider-model-and-packaging.md @@ -637,9 +637,9 @@ cannot alias, rename, or vendor-qualify the base types. | --- | --- | | Dependency/owner | W0; Provider contract/catalog owner | | Current source | `packages/d2b-realm-provider/src/{provider,capabilities,error,credential,rate_limit,conformance}.rs` | -| Reuse action | extract and adapt | +| Reuse action | adapt | | Destination | `packages/d2b-contracts/src/v3/provider.rs`, `packages/d2b-provider/src/lib.rs`, `packages/d2b-provider-toolkit/` | -| Detailed design | Provider resource/manifest/components/dependencies/services/trust/compatibility/toolkit | +| Detailed design | Provider resource/manifest/components/dependencies/services/trust/compatibility/toolkit Primary reuse disposition: `adapt`. Preserved source-plan detail: extract and adapt. | | Integration | Zone config/catalog → Provider resource → Process components → bus/resource routes | | Data migration | Full reset | | Validation | Contract vectors, fake/malicious Provider, one-crate/one-identity policy | @@ -665,9 +665,9 @@ cannot alias, rename, or vendor-qualify the base types. | --- | --- | | Dependency/owner | Process contracts; system Provider owners | | Current source | `d2bd` DAG/broker spawn; unsafe-local helper; guestd/exec runner; `d2b-host` runtime provider | -| Reuse action | extract and adapt | +| Reuse action | adapt | | Destination | `packages/d2b-provider-system-core/`, `d2b-provider-system-systemd/`, `d2b-provider-system-minijail/` | -| Detailed design | Bootstrap system-core; common Process/EphemeralProcess providers and pidfd conformance | +| Detailed design | Bootstrap system-core; common Process/EphemeralProcess providers and pidfd conformance Primary reuse disposition: `adapt`. Preserved source-plan detail: extract and adapt. | | Integration | Host/Guest providerRef/domain/userRef, local supervisors, resource status | | Data migration | Current roles converted under reset | | Validation | Shared conformance and host/user/non-Host tests | @@ -679,7 +679,7 @@ cannot alias, rename, or vendor-qualify the base types. | --- | --- | | Dependency/owner | ADR046-provider-001; shared semantic Service/Binding contract owner | | Current source | None — D098 common semantic Service/Binding bases are net-new ADR 0046 contracts | -| Reuse action | net-new | +| Reuse action | create | | Destination | `packages/d2b-contracts/src/v3/semantic_services/{mod,audio,security_key,telemetry,usb}.rs`; generated schema artifacts for the eight exact qualified ResourceTypes | | Detailed design | Define one shared strict base spec/status DTO and schema contract for each frozen D098 Service/Binding pair, including exact semantic type/schema IDs, versions, fingerprints, minimal valid base fixtures without `spec.provider`, authority/projection Service union, same-Zone Binding `serviceRef`/target rules, D088 `status.resource` layering, status-only observations, and projection-factory type binding. A Core-generated projection permits only `providerRef`, semantic base/import fields, and ResourceImport ownership; it rejects `spec.provider`. Register no implementation-qualified or former `*State` alias. | | Integration | Provider manifests and ResourceApiBindings consume the common catalog fingerprint; ADR046-zone-control-019/020 use the same factory metadata to admit an owner Service and core-create one same-type projection Service; the four initial Provider dossiers supply only strict implementation extensions/controllers. | diff --git a/docs/specs/ADR-046-provider-state.md b/docs/specs/ADR-046-provider-state.md index f7a53a3b9..70d13d62a 100644 --- a/docs/specs/ADR-046-provider-state.md +++ b/docs/specs/ADR-046-provider-state.md @@ -1021,9 +1021,9 @@ Every `packages/d2b-provider--/` crate created by this or | Work item ID | `ADR046-pstate-001` | | Dependency/owner | ADR046-primitives-001; v3 contracts owner | | Current source | `packages/d2b-core/src/storage.rs` (`StoragePathSpec` with `scope: ContractId` currently keyed by `ProcessRole`/Workload; `SensitivityClass`; `StorageLifecycle`; `StorageRootClass`); `packages/d2b-state/src/atomic.rs` (main, 6faa5256; absent from v3 baseline) | -| Reuse action | extract and adapt | +| Reuse action | adapt | | Destination | `packages/d2b-contracts/src/v3/volume_state.rs` | -| Detailed design | `VolumeStateSchema` struct (`schemaId`, `schemaVersion`, `schemaDigest`, `migrationPolicy`); `PersistenceClass` and `SensitivityClass` enums; `VolumeStateStatus` extension (`stateSchemaPhase`, `installedSchemaVersion`, `markerStatus`, `sealingStatus`, `quotaUsage`, `lastMigrationAt`); `StateEnvelope` (replaces v2 `StateEnvelope`); canonical JSON serde and digest helpers | +| Detailed design | `VolumeStateSchema` struct (`schemaId`, `schemaVersion`, `schemaDigest`, `migrationPolicy`); `PersistenceClass` and `SensitivityClass` enums; `VolumeStateStatus` extension (`stateSchemaPhase`, `installedSchemaVersion`, `markerStatus`, `sealingStatus`, `quotaUsage`, `lastMigrationAt`); `StateEnvelope` (replaces v2 `StateEnvelope`); canonical JSON serde and digest helpers Primary reuse disposition: `adapt`. Preserved source-plan detail: extract and adapt. | | Integration | Volume spec and status structs embed these types; Provider descriptor component stateNamespace declaration uses the same types | | Data migration | Full v3 reset; no v2 state schema import | | Validation | Schema golden vectors; phase/reason round-trip; StateEnvelope digest tests | @@ -1051,10 +1051,10 @@ Every `packages/d2b-provider--/` crate created by this or | Work item ID | `ADR046-pstate-003` | | Dependency/owner | ADR046-pstate-001; volume-local Provider owner | | Current source | `packages/d2b-state/src/atomic.rs`, `path.rs`, `lock.rs` (main, 6faa5256); `packages/d2b-priv-broker/src/ops/swtpm_dir.rs` (marker algorithm, v3 baseline) | -| Reuse action | copy-unchanged (path.rs) / adapt (atomic.rs, lock.rs) / adapt (swtpm_dir.rs marker algorithm) | +| Reuse action | adapt | | Destination | `packages/d2b-provider-volume-local/` (new crate, full scaffold required): `src/{atomic.rs, path.rs, lock.rs, marker.rs, effect_port.rs}`; `tests/volume_local.rs` (marker missing/replaced/mismatch, domain-isolation rejection, quota enforcement); `integration/volume_local.rs` (real Host filesystem provision, broker-maintained marker check, domain-isolation rejection cross-process); `README.md` | | Crate layout | Creates full `d2b-provider-volume-local/` scaffold. `src/` owns filesystem primitives and effect port; `tests/` owns hermetic fault-injection and conformance tests (no live daemon); `integration/` owns real-Host provision, marker verification, and cross-process domain-isolation scenarios; `README.md` documents volume-local Provider identity, `spec.config.*`, Volume ResourceType ownership, broker placement requirements, OFD-lock security model, state and telemetry surfaces, and build/test/integration commands | -| Detailed design | Anchored Volume root provision, identity marker write/check, quota soft-check on write, domain-isolation validation, fd-relative layout creation/repair/cleanup, broker-maintained marker root protocol; layout `ownerRef`/`groupRef` must reference a Nix-preprovisioned User principal or bounded system pool — Volume admission rejects runtime-created principals; `VolumeEffectPort` returns opaque IDs and named view dirfds only — no raw host path returned by any EffectPort operation; volume-local must support `source.executionRef: Guest/` for `guest-local` placement (controller running inside the Guest): when executing in a Guest domain, volume-local may not create, read, or hold dirfds/paths for Volumes sourced in another domain; `host-backed-guest` placement creates a `virtiofs.d2bus.org.Export` child per attachment entry and validates `hostCustodyPermitted: true` in the signed descriptor | +| Detailed design | Anchored Volume root provision, identity marker write/check, quota soft-check on write, domain-isolation validation, fd-relative layout creation/repair/cleanup, broker-maintained marker root protocol; layout `ownerRef`/`groupRef` must reference a Nix-preprovisioned User principal or bounded system pool — Volume admission rejects runtime-created principals; `VolumeEffectPort` returns opaque IDs and named view dirfds only — no raw host path returned by any EffectPort operation; volume-local must support `source.executionRef: Guest/` for `guest-local` placement (controller running inside the Guest): when executing in a Guest domain, volume-local may not create, read, or hold dirfds/paths for Volumes sourced in another domain; `host-backed-guest` placement creates a `virtiofs.d2bus.org.Export` child per attachment entry and validates `hostCustodyPermitted: true` in the signed descriptor Primary reuse disposition: `adapt`. Preserved source-plan detail: copy-unchanged (path.rs) / adapt (atomic.rs, lock.rs) / adapt (swtpm_dir.rs marker algorithm). | | Integration | `d2b-priv-broker` calls `volume_local::marker::provision_marker` at broker-maintained Volume creation; `d2b-provider-volume-local` controller calls `marker::verify_marker` on every daemon restart via reconcile startup relist | | Data migration | New marker written for each Volume at v3 first-boot; TPM marker path adapted from current swtpm-markers root | | Validation | Marker missing/replaced/mismatch tests; domain-isolation rejection tests; quota enforcement tests; crash at every provision step | @@ -1179,10 +1179,10 @@ Every `packages/d2b-provider--/` crate created by this or | Work item ID | `ADR046-pstate-011` | | Dependency/owner | ADR046-pstate-003; workspace policy and xtask owners | | Current source | `packages/xtask/src/main.rs` (`gen-schemas` and workspace-policy checks, v3 baseline `fd5b0067`); `tests/unit/gates/drift-check.sh` (schema drift gate, same baseline); `packages/d2b-contract-tests/` (contract-test policy, frozen per AGENTS.md) | -| Reuse action | extend | +| Reuse action | adapt | | Destination | `packages/xtask/src/provider_crate_policy.rs`; `tests/unit/gates/provider-crate-layout-check.sh` | | Crate layout | Not applicable (this work item implements the policy gate itself) | -| Detailed design | `cargo xtask check-provider-crate-layout` command: walks every workspace member matching `d2b-provider-*`; for each, asserts presence of `src/`, `tests/`, `integration/`, and `README.md`; asserts `integration/` contains at least one `.rs` file and a `README.md`; fails closed with a typed `missing-provider-crate-path` error listing every absent path; wired into `make test-policy` via `tests/unit/gates/provider-crate-layout-check.sh`; output is machine-readable JSON (`{ "crate": "…", "missing": ["integration/"] }` per violation) | +| Detailed design | `cargo xtask check-provider-crate-layout` command: walks every workspace member matching `d2b-provider-*`; for each, asserts presence of `src/`, `tests/`, `integration/`, and `README.md`; asserts `integration/` contains at least one `.rs` file and a `README.md`; fails closed with a typed `missing-provider-crate-path` error listing every absent path; wired into `make test-policy` via `tests/unit/gates/provider-crate-layout-check.sh`; output is machine-readable JSON (`{ "crate": "…", "missing": ["integration/"] }` per violation) Primary reuse disposition: `adapt`. Preserved source-plan detail: extend. | | Integration | `make test-policy` runs `cargo xtask check-provider-crate-layout`; GitHub CI runs `make test-policy` on every PR; `make check` includes `test-policy` as a required Layer-1 shard; workspace policy tests in `packages/d2b-contract-tests/` are extended with a static manifest check that asserts `provider_crate_layout` policy version is current | | Data migration | Not applicable | | Validation | Policy gate detects missing `src/` → error; missing `tests/` → error; missing `integration/` → error; missing `README.md` → error; empty `integration/` (no `.rs` files) → error; all four paths present and non-empty → pass; existing non-provider `d2b-*` crates not flagged; gate is idempotent across re-runs | diff --git a/docs/specs/ADR-046-reset-and-cutover.md b/docs/specs/ADR-046-reset-and-cutover.md index 552e99f82..4408f2776 100644 --- a/docs/specs/ADR-046-reset-and-cutover.md +++ b/docs/specs/ADR-046-reset-and-cutover.md @@ -1487,7 +1487,7 @@ owns the destination. | `d2b-priv-broker.service`, `d2b-priv-broker.socket` | §7, `production-reachable` | Preserve until Phase 10 gate clears, then Destroy | Zone-local privileged broker (`ADR046-provider-003`) | `ADR046-provider-003` | | `/run/d2b/d2bd.sock`, `/run/d2b/broker.sock` | §7 | Destroy (Phase 3, boot-scoped) | `/run/d2b/z-/...` fresh sockets | `ADR046-core-controllers` "Process model" | | `/run/d2b/allocator.sock` | §7, config-ref/schema-only, engine not live | Destroy (Phase 3; never adopted — no live allocator process to quiesce) | No successor socket; provisioning integrates into fixed core controllers | `ADR046-core-001` | -| `d2b-realm-router` PeerSession/MuxSession/`WorkloadOp`/`RealmMethod` wire | multiple rows, `dead-reachable`/`production-reachable` | Preserve (compiled into binary; Destroy only when the binary itself is retired at Phase 10) | ComponentSession/d2b-bus/`ResourceOp` | `ADR046-session-001`, `ADR046-bus-001`, `ADR046-api-001` | +| `d2b-realm-router` PeerSession/MuxSession/`WorkloadOp`/`RealmMethod` wire | multiple rows, `dead-reachable`/`production-reachable` | Preserve (compiled into binary; Destroy only when the binary itself is retired at Phase 10) | ComponentSession/d2b-bus/`ResourceOp` | `ADR046-session-001`, `ADR046-session-003`, `ADR046-api-001` | | `d2b-unsafe-local-helper` binary, `DaemonToUnsafeLocalHelper` protocol | §7, `production-reachable` | Preserve until Process Provider supervisor ticket migration lands, then Destroy | User-only `Host` Process supervisor | `ADR046-primitives-003` | | `d2b-guest-shell-runner` | `production-reachable` | Preserve until user-only Host shell Process parity, then Destroy | `Process` child of user-only Host | `ADR046-primitives-003` | | `~/.local/state/d2b/unsafe-local-scopes.json` | §7 evidence, per-user scope ledger | Adopt into a declared user-only Host state Volume — this ledger is real private per-user content that passes the storage-need test, not derivable from status/core ledger — via a per-user migration `EphemeralProcess` | User-only Host declared component state Volume | `ADR046-primitives-003` | @@ -1669,9 +1669,9 @@ applies here exactly as everywhere else in the repository). | Dependency/owner | W0 shared contract root; storage/broker integrator | | Current source | `packages/d2bd/src/storage_lifecycle.rs` (`run_startup_contract_check`, bundle-versioned contract validation pattern); `packages/d2bd/src/ownership_preflight.rs` (`EntrySpec`, legacy-recovery-artifact optionality); `packages/d2b/src/lib.rs` `build_storage_migration_plan`/`storage_migration_checkpoint_id` | | Reuse source | None from main; this is a v3-only cross-cutting concern with no main-branch equivalent | -| Reuse action | extract and adapt | +| Reuse action | adapt | | Destination | `packages/d2b-cutover/src/{inventory,snapshot,checkpoint}.rs` | -| Detailed design | Implement the seven closed inventories of [Authoritative inventories](#authoritative-inventories); the `checkpoint_id` digest algorithm of [Preflight and immutable snapshot](#preflight-and-immutable-snapshot); the atomic snapshot-write sequence (temp file, fsync, rename, parent fsync, post-rename immutability) | +| Detailed design | Implement the seven closed inventories of [Authoritative inventories](#authoritative-inventories); the `checkpoint_id` digest algorithm of [Preflight and immutable snapshot](#preflight-and-immutable-snapshot); the atomic snapshot-write sequence (temp file, fsync, rename, parent fsync, post-rename immutability) Primary reuse disposition: `adapt`. Preserved source-plan detail: extract and adapt. | | Integration | `d2b host cutover preflight`/`plan` CLI commands consume this crate exclusively; no other crate re-implements inventory walking | | Data migration | New; no prior inventory/snapshot format exists | | Validation | `checkpoint_id` determinism property test; snapshot atomic-write crash-injection test; `cutover_preflight_refuses_dirty_flake_check` | @@ -1701,9 +1701,9 @@ applies here exactly as everywhere else in the repository). | Dependency/owner | ADR046-reset-001, ADR046-reset-002; Process/Guest lifecycle owner | | Current source | `packages/d2b/src/lib.rs` `require_explicit_mutation_flag`, `cmd_host_destroy` (dry-run/apply precondition pattern); [ADR 0040](../adr/0040-graceful-vm-shutdown.md) graceful shutdown path | | Reuse source | None from main | -| Reuse action | extract and adapt | +| Reuse action | adapt | | Destination | `packages/d2b-cutover/src/{consent,drain,disposition}.rs` | -| Detailed design | Exact-consent-phrase gate bound to `checkpoint_id`; Phase 3 drain algorithm (§ [Old daemon/unit/process drain](#old-daemonunitprocess-drain)); the [Disposition framework](#disposition-framework)'s Adopt/Preserve/Destroy executor, delegating every Adopt to ADR046-reset-004 | +| Detailed design | Exact-consent-phrase gate bound to `checkpoint_id`; Phase 3 drain algorithm (§ [Old daemon/unit/process drain](#old-daemonunitprocess-drain)); the [Disposition framework](#disposition-framework)'s Adopt/Preserve/Destroy executor, delegating every Adopt to ADR046-reset-004 Primary reuse disposition: `adapt`. Preserved source-plan detail: extract and adapt. | | Integration | `d2b host cutover apply` orchestrates drain then disposition execution then hands off to Phase 5 (ADR046-reset-005) | | Data migration | Destructive; this is where Phase 3/4 boundary-of-no-return-approach begins (rollback still open through end of Phase 4) | | Validation | `cutover_apply_requires_exact_consent_phrase`; `cutover_drain_refuses_on_live_process` | @@ -1733,7 +1733,7 @@ applies here exactly as everywhere else in the repository). | Dependency/owner | ADR046-reset-004; `d2b-resource-store-redb` owner (`ADR046-store-003`); core-controller owner (`ADR046-core-001`) | | Current source | None (bootstrap sequencing over Zone runtime startup, which is itself ADR-only) | | Reuse source | None from main | -| Reuse action | new | +| Reuse action | create | | Destination | `packages/d2b-cutover/src/{store_bootstrap,provider_sequence}.rs` | | Detailed design | Phase 5 store creation per [Resource-store initialization](#resource-store-initialization); Phase 6 topological Provider install per [Provider install/topological start](#provider-installtopological-start), including the fixed staged default order and cycle-rejection check | | Integration | Invoked immediately after ADR046-reset-003/004 complete; hands off to Phase 7 (ADR046-reset-006) | @@ -1765,9 +1765,9 @@ applies here exactly as everywhere else in the repository). | Dependency/owner | ADR046-reset-006; telemetry-audit-and-support owner (`d2b-audit`) | | Current source | ADR 0034 degraded-state ledger taxonomy and repair-never-trusts-ledger-paths invariant | | Reuse source | None from main | -| Reuse action | extract and adapt | +| Reuse action | adapt | | Destination | `packages/d2b-cutover/src/{verify,doctor,degraded}.rs` | -| Detailed design | The ten `verify` checks in [Post-cutover verification](#post-cutover-verification); the `cutover-quarantined` degraded class and `doctor` reporting in [Failure/quarantine/manual recovery](#failurequarantinemanual-recovery); audit chain closure/genesis-record cross-check (check 9) | +| Detailed design | The ten `verify` checks in [Post-cutover verification](#post-cutover-verification); the `cutover-quarantined` degraded class and `doctor` reporting in [Failure/quarantine/manual recovery](#failurequarantinemanual-recovery); audit chain closure/genesis-record cross-check (check 9) Primary reuse disposition: `adapt`. Preserved source-plan detail: extract and adapt. | | Integration | `d2b host cutover verify`/`doctor` CLI commands; consumed by the Phase 10 finalize gate table | | Data migration | None — full d2b 3.0 reset; no prior state to migrate | | Validation | Injected-digest-mismatch test for TPM/durable-Volume verify checks; audit-genesis-cross-check test; `cutover-full-rehearsal.nix` | @@ -1781,7 +1781,7 @@ applies here exactly as everywhere else in the repository). | Dependency/owner | ADR046-reset-007; owner of each retiring artifact's ADR 0046 successor work item | | Current source | `ADR-046-cli-and-operations` "Removal notes" (live-successor-before-deletion criterion) | | Reuse source | None from main | -| Reuse action | new | +| Reuse action | create | | Destination | `packages/d2b-cutover/src/finalize.rs` | | Detailed design | Per-candidate independent gate evaluation exactly as tabled in [Old artifact/unit/schema removal gates](#old-artifactunitschema-removal-gates); separate consent phrase from `apply`; never partial-destroys a candidate | | Integration | `d2b host cutover finalize` CLI command; reads gate status from ADR046-reset-007's verify results plus each named policy-lint/integration test's pass/fail recorded in CI | @@ -1829,7 +1829,7 @@ applies here exactly as everywhere else in the repository). | Dependency/owner | ADR046-reset-001 through ADR046-reset-010, fully landed | | Current source | `tests/integration/live/` conventions; `tests/host-integration/hardware/` conventions | | Reuse source | None from main | -| Reuse action | new | +| Reuse action | create | | Destination | `tests/integration/live/cutover-real-host.sh`, `tests/integration/live/cutover-real-host-cloud-guest.sh`, `tests/host-integration/hardware/cutover-real-tpm.sh`, `tests/host-integration/hardware/cutover-real-usbip-security-key.sh` | | Detailed design | Manual, `D2B_LIVE=1`/hardware-gated validation scripts described in [Tests](#tests) Type 11/12 rows; never run in CI; require operator sign-off and an independent out-of-band backup before execution | | Integration | Run manually by an operator against a real host/device before the reset-and-cutover implementation is declared production-ready | diff --git a/docs/specs/ADR-046-resource-api-and-authorization.md b/docs/specs/ADR-046-resource-api-and-authorization.md index 6e0834b9e..35db52669 100644 --- a/docs/specs/ADR-046-resource-api-and-authorization.md +++ b/docs/specs/ADR-046-resource-api-and-authorization.md @@ -522,9 +522,9 @@ process data, and terminal bytes. | --- | --- | | Dependency/owner | W0; resource API integrator | | Current source | `packages/d2b-contracts/src/public_wire.rs`, `broker_wire.rs`; `d2b-daemon-access/src/lib.rs`; `d2b-realm-router/src/lib.rs` | -| Reuse action | extract and adapt | +| Reuse action | adapt | | Destination | `packages/d2b-contracts/proto/d2b-resource-v3.proto`, `packages/d2b-resource-api/src/service.rs`, `client.rs` | -| Detailed design | Async methods, contexts, preconditions, limits, errors, status/finalizer separation, batch API | +| Detailed design | Async methods, contexts, preconditions, limits, errors, status/finalizer separation, batch API Primary reuse disposition: `adapt`. Preserved source-plan detail: extract and adapt. | | Integration | d2b-bus exact service → Zone auth → redb actor | | Data migration | None; v3 clean break | | Validation | Protocol vectors; malformed/oversize/conflict/status-owner tests | diff --git a/docs/specs/ADR-046-resource-object-model.md b/docs/specs/ADR-046-resource-object-model.md index c627dbf5a..52441912a 100644 --- a/docs/specs/ADR-046-resource-object-model.md +++ b/docs/specs/ADR-046-resource-object-model.md @@ -718,9 +718,9 @@ The following are not standalone ResourceTypes: | --- | --- | | Dependency/owner | W0 shared contract root; `d2b-contracts` | | Current source | `packages/d2b-realm-core/src/ids.rs`, `workload.rs`, `error.rs`; `packages/d2b-core/src/storage.rs`, `processes.rs` | -| Reuse action | extract and adapt | +| Reuse action | adapt | | Destination | `packages/d2b-contracts/src/v3/resource.rs`, `resource_status.rs`, `resource_schema.rs` | -| Detailed design | Implement strict ResourceEnvelope, metadata, the three-layer spec shape (universal envelope + ResourceType base `spec.*` incl. `spec.providerRef` + optional canonical `spec.provider` `{ schemaId, schemaVersion, settings }`), the three-layer status shape (universal base + `status.resource` + optional `status.provider` with `providerRef`/`schemaId`/`schemaVersion`/`observedProviderGeneration`/`details`), phase/condition/outcome, canonical JSON, per-layer bounds/redaction, ownerRef/UID fields | +| Detailed design | Implement strict ResourceEnvelope, metadata, the three-layer spec shape (universal envelope + ResourceType base `spec.*` incl. `spec.providerRef` + optional canonical `spec.provider` `{ schemaId, schemaVersion, settings }`), the three-layer status shape (universal base + `status.resource` + optional `status.provider` with `providerRef`/`schemaId`/`schemaVersion`/`observedProviderGeneration`/`details`), phase/condition/outcome, canonical JSON, per-layer bounds/redaction, ownerRef/UID fields Primary reuse disposition: `adapt`. Preserved source-plan detail: extract and adapt. | | Integration | Store/API/SDK/Nix/codegen consume one contract | | Data migration | Full d2b 3.0 reset; no v2 resource import | | Validation | Golden JSON/protobuf vectors; serde unknown-field; three-layer spec shape round-trip; canonical minimal base-spec acceptance; base-schema version/fingerprint conformance; `spec.provider` deny-unknown/version-mismatch/shadow rejection and providerRef-binding; three-layer status shape round-trip; base-only projection (universal + `status.resource`) ignores/omits `status.provider`; `status.provider` unknown-field/version-mismatch rejection; status redaction/size/time/phase tests; `status.update` currency object round-trip (state/reasons/disruption/preserveState/owned+dependency refs bounded); `spec.updatePolicy` base round-trip | diff --git a/docs/specs/ADR-046-resource-reconciliation.md b/docs/specs/ADR-046-resource-reconciliation.md index 673fe8312..cdb67748d 100644 --- a/docs/specs/ADR-046-resource-reconciliation.md +++ b/docs/specs/ADR-046-resource-reconciliation.md @@ -454,9 +454,9 @@ core Operation ledger, and independent external observation. | --- | --- | | Dependency/owner | W0/W1a; controller toolkit owner | | Current source | `packages/d2b-realm-router/src/lib.rs`, `mux_session.rs`, `session_lifecycle.rs`; `packages/d2bd/src/supervisor/dag.rs` | -| Reuse action | extract and adapt | +| Reuse action | adapt | | Destination | `packages/d2b-controller-toolkit/src/lib.rs`, `runner.rs`, `queue.rs`, `context.rs`, `result.rs` | -| Detailed design | Async ResourceReconciler, watch receiver, coalescing, per-resource serialization, parallel tasks, retry/checkpoint/finalize; expedited priority lane and `CommittedRevisionProof`-gated effects (D090); `assess_update`/`plan_upgrade`/`execute_upgrade` methods serialized in the same single-flight (D091) | +| Detailed design | Async ResourceReconciler, watch receiver, coalescing, per-resource serialization, parallel tasks, retry/checkpoint/finalize; expedited priority lane and `CommittedRevisionProof`-gated effects (D090); `assess_update`/`plan_upgrade`/`execute_upgrade` methods serialized in the same single-flight (D091) Primary reuse disposition: `adapt`. Preserved source-plan detail: extract and adapt. | | Integration | Provider controller binaries wrap handlers with toolkit | | Data migration | None — full d2b 3.0 reset; no prior state to migrate | | Validation | Golden state-machine vectors, deterministic clocks, conflict/restart/queue tests; D090: commit-fails/Abort → no effect, controller finishes-before-commit gated on proof, effects-gate, status-write-delayed (`statusPersistence: pending`), normal-queued no-op/rejoin, concurrent mutation, delete event-only projection, expedited timeout committed-but-pending, restart re-entry no duplicate; D091: current/non-disruptive/each-trigger assess, UpgradeRequired-not-in-place, dependency propagation/topological drain-recycle-restart, GPU blocking, state/TPM preservation, crash/re-entry resume, single-flight reconcile-vs-upgrade serialization | diff --git a/docs/specs/ADR-046-resource-store-redb.md b/docs/specs/ADR-046-resource-store-redb.md index e6881823c..934d37578 100644 --- a/docs/specs/ADR-046-resource-store-redb.md +++ b/docs/specs/ADR-046-resource-store-redb.md @@ -351,9 +351,9 @@ authorization, or audit cannot be weakened to pass. | --- | --- | | Dependency/owner | W0; store integrator | | Current source | `packages/d2b-core/src/storage.rs`, `sync.rs`; `packages/d2bd/src/supervisor/state.rs`, `daemon_audit.rs`; `d2b-realm-router/src/lib.rs` | -| Reuse action | extract and adapt | +| Reuse action | adapt | | Destination | `packages/d2b-resource-store/src/lib.rs`, `packages/d2b-resource-store-redb/src/lib.rs`, `schema.rs`, `keys.rs`, `transaction.rs` | -| Detailed design | redb tables/encodings, fd backend, store identity, fair actor, MVCC reads, atomic indexes/revisions/operations/conflicts | +| Detailed design | redb tables/encodings, fd backend, store identity, fair actor, MVCC reads, atomic indexes/revisions/operations/conflicts Primary reuse disposition: `adapt`. Preserved source-plan detail: extract and adapt. | | Integration | Zone runtime owns store; resource API is sole caller | | Data migration | Full reset; logical backup only for v3 stores | | Validation | Unit/property/fault tests and hard benchmark | diff --git a/docs/specs/ADR-046-resources-credential.md b/docs/specs/ADR-046-resources-credential.md index baf03ab17..69eb8b206 100644 --- a/docs/specs/ADR-046-resources-credential.md +++ b/docs/specs/ADR-046-resources-credential.md @@ -1885,9 +1885,9 @@ config formalizes this as an `OpaqueAzureRef` with the same charset restriction. | Dependency/owner | `ADR046-object-001` (resource envelope); `ADR046-identities-001` (types); W0 shared contract root; `d2b-contracts` | | Current source | `packages/d2b-realm-provider/src/credential.rs`: `OpaqueAzureRef`, parse/deserialize/charset validation, tests | | Reuse source | main `a1cc0b2d`: `d2b-provider-credential-secret-service/src/lib.rs` types: `SecretServiceLeaseRequest`, `SecretServiceLeaseRef`, `SecretServiceLeaseGrant`, `SecretServiceLeaseInspection`, `SecretServiceLeaseRenewal`, `SecretServiceLeaseRevocation`, `SecretServiceLeaseState`; parallel entra/managed-identity types; `CredentialLease`, `CredentialLeaseRequest`, `CredentialLeaseState` from `d2b-contracts/src/v2_provider.rs` | -| Reuse action | extract and adapt | +| Reuse action | adapt | | Destination | `packages/d2b-contracts/src/v3/credential.rs` | -| Detailed design | Define `CredentialSpec`, `CredentialStatus`, `CredentialLeaseHandle` (opaque bounded newtype), `CredentialRotationPolicy`, `CredentialRevocationPolicy`, `CredentialScope`, `OperationClass` enum, `CredentialLeaseState`, `PlacementBinding`, `CredentialConditionType`, and all serde/validation/redaction helpers; reuse `OpaqueAzureRef` from v3 baseline directly; enforce zero-secret-bytes charset validation on all string fields at construction | +| Detailed design | Define `CredentialSpec`, `CredentialStatus`, `CredentialLeaseHandle` (opaque bounded newtype), `CredentialRotationPolicy`, `CredentialRevocationPolicy`, `CredentialScope`, `OperationClass` enum, `CredentialLeaseState`, `PlacementBinding`, `CredentialConditionType`, and all serde/validation/redaction helpers; reuse `OpaqueAzureRef` from v3 baseline directly; enforce zero-secret-bytes charset validation on all string fields at construction Primary reuse disposition: `adapt`. Preserved source-plan detail: extract and adapt. | | Integration | Credential controller, Provider dossier schemas, Nix compiler, and resource store all consume one canonical contract | | Data migration | Full d2b 3.0 reset; no v2 credential import | | Validation | Schema golden vectors; charset/length tests; serde unknown-field rejection; `OpaqueAzureRef` round-trip and secret-shape rejection parity; `leaseHandle` and `sourceVersion` opaque newtype tests; status redaction tests | @@ -1898,7 +1898,7 @@ config formalizes this as an `OpaqueAzureRef` with the same charset restriction. | Field | Value | | --- | --- | | Work item ID | `ADR046-credential-002` | -| Dependency/owner | `ADR046-credential-001`; `ADR046-api-001` (resource API); `ADR046-bus-001` (d2b-bus); Credential service owner | +| Dependency/owner | `ADR046-credential-001`; `ADR046-api-001` (resource API); `ADR046-session-003` (d2b-bus); Credential service owner | | Current source | `packages/d2b-realm-provider/src/provider.rs:CredentialProvider` (status-only trait); `d2b-contracts/proto/v2/provider_credential.proto` (main: Health, Capabilities, Status, AcquireLease, RefreshLease, RevokeLease) | | Reuse source | main `a1cc0b2d`: `packages/d2b-contracts/proto/v2/provider_credential.proto` method names | | Reuse action | adapt | @@ -1917,9 +1917,9 @@ config formalizes this as an `OpaqueAzureRef` with the same charset restriction. | Dependency/owner | `ADR046-credential-001`, `ADR046-credential-002`; `ADR046-reconcile-001`; credential-secret-service owner | | Current source | `packages/d2b-realm-provider/src/provider.rs:CredentialProvider` (minimal v3 baseline) | | Reuse source | main `a1cc0b2d`: `packages/d2b-provider-credential-secret-service/src/lib.rs` (full implementation); `src/tests.rs` (full test suite including `FakeOo7Port`, lease lifecycle, locked state, canary enforcement, cardinality limits) | -| Reuse action | copy and adapt | +| Reuse action | adapt | | Destination | `packages/d2b-provider-credential-secret-service/src/{lib.rs, controller.rs, service.rs, main.rs}` (implementation + binary); `packages/d2b-provider-credential-secret-service/tests/{lifecycle.rs, conformance.rs, faults.rs, canary.rs, delivery.rs, placement.rs}` (hermetic Cargo integration); `packages/d2b-provider-credential-secret-service/integration/{container-service.sh, host-placement.nix, cleanup-rollback.sh}` (orchestration fixtures); `packages/d2b-provider-credential-secret-service/README.md` (all §Provider README required sections) | -| Detailed design | Adapt `SecretServiceCredentialProvider` and `SecretServiceCredentialProviderFactory` to use v3 `d2b.credential.v3` service interface; replace v2 `CredentialProvider` trait impl with v3 controller/service handler; adapt `Oo7SecretServicePort` trait (retain all methods unchanged); ensure `SecretServiceOwner::Userd` placement restriction rejects system-domain and guest-agent construction; validate `collectionAlias` against provider-internal charset (not `OpaqueAzureRef`; collection aliases may include spaces); integrate with Provider resource descriptor and controller toolkit; test `credential_canary` never appears in any service response | +| Detailed design | Adapt `SecretServiceCredentialProvider` and `SecretServiceCredentialProviderFactory` to use v3 `d2b.credential.v3` service interface; replace v2 `CredentialProvider` trait impl with v3 controller/service handler; adapt `Oo7SecretServicePort` trait (retain all methods unchanged); ensure `SecretServiceOwner::Userd` placement restriction rejects system-domain and guest-agent construction; validate `collectionAlias` against provider-internal charset (not `OpaqueAzureRef`; collection aliases may include spaces); integrate with Provider resource descriptor and controller toolkit; test `credential_canary` never appears in any service response Primary reuse disposition: `adapt`. Preserved source-plan detail: copy and adapt. | | Integration | Target: user-domain Process (ADR-only `Process` ResourceType) under Host (ADR-only `Host` ResourceType); d2b-bus (ADR-only) routes `d2b.credential.v3` calls to this process; Credential controller reconciles status. Current v3 has no user-credential host process: v3 `d2b-userd` (`packages/d2b-userd/src/lib.rs`) is a guest exec stub only (exits 78 in service mode; `UserAttachRequest`/`UserExecSession` guest wire primitives only; evidence class: `test-only-or-preview`); no credential or keyring functionality. This integration path is fully new (ADR-only) work. | | Data migration | Full reset; no migration from old `CredentialProvider` trait | | Validation | **`src/` unit** (`#[cfg(test)]` in `src/`): `Oo7SecretServicePort` trait API surface, `SecretServiceOwner` placement guard, `collectionAlias` charset, `lockPolicy` state transitions. **`tests/` Cargo integration** (`cargo test -p d2b-provider-credential-secret-service`): copied test suite from main with v3 type substitutions; add `lifecycle.rs` (acquire/refresh/revoke/inspect end-to-end with `FakeOo7Port`); `conformance.rs` (all 11 `check_provider_conformance` arms pass); `faults.rs` (locked state → `credential-provider-unavailable`, unavailable, cardinality limit); `canary.rs` (`credential_canary` and `object_path_canary` absent from every response, status field, and delivery record); `delivery.rs` (delivery-session binding contract, zeroizing buffer, replay-safe sequence); `placement.rs` (system-domain and guest-agent construction rejected). **`integration/` fixtures**: `container-service.sh` (container-backed Provider service start/stop/drain); `host-placement.nix` (user-domain Host/Process placement in runNixOSTest); `cleanup-rollback.sh` (Nix-generation removal triggers async Delete and Provider-revoke finalizer). | @@ -1933,9 +1933,9 @@ config formalizes this as an `OpaqueAzureRef` with the same charset restriction. | Dependency/owner | `ADR046-credential-001`, `ADR046-credential-002`; `ADR046-reconcile-001`; credential-entra owner | | Current source | `packages/d2b-realm-provider/src/credential.rs:AzureControlPlaneRef`, `OpaqueAzureRef` (v3 baseline, reachable) | | Reuse source | main `a1cc0b2d`: `packages/d2b-provider-credential-entra/src/lib.rs` (full implementation); `src/tests.rs` (full test suite including `FakeEntraClient`, `credential_canary`/`endpoint_canary`, interaction-required, colocated-consumer, generation-mismatch tests) | -| Reuse action | copy and adapt | +| Reuse action | adapt | | Destination | `packages/d2b-provider-credential-entra/src/{lib.rs, controller.rs, service.rs, main.rs}`; `packages/d2b-provider-credential-entra/tests/{lifecycle.rs, conformance.rs, faults.rs, canary.rs, delivery.rs, placement.rs}`; `packages/d2b-provider-credential-entra/integration/{container-service.sh, guest-placement.nix, cleanup-rollback.sh}`; `packages/d2b-provider-credential-entra/README.md` (all §Provider README required sections) | -| Detailed design | Adapt `EntraCredentialProvider` and `EntraCredentialProviderFactory` to v3 service; replace v2 `AgentPlacementBinding` with v3 `PlacementBinding` enum (user-agent, guest-agent only; reject host-system); validate `tenantId` config field using `OpaqueAzureRef::parse` from v3 baseline `d2b-realm-provider/src/credential.rs` (note: current v3 source field is named via `AzureControlPlaneRef`; target field name is `tenantId`); retain `EntraCredentialClient` trait unchanged; map `EntraClientError::InteractionRequired` to `credential-provider-unavailable` (not denied); enforce `EntraCredentialOwner::ExactConsumer` so only the declared `consumerRef` may acquire | +| Detailed design | Adapt `EntraCredentialProvider` and `EntraCredentialProviderFactory` to v3 service; replace v2 `AgentPlacementBinding` with v3 `PlacementBinding` enum (user-agent, guest-agent only; reject host-system); validate `tenantId` config field using `OpaqueAzureRef::parse` from v3 baseline `d2b-realm-provider/src/credential.rs` (note: current v3 source field is named via `AzureControlPlaneRef`; target field name is `tenantId`); retain `EntraCredentialClient` trait unchanged; map `EntraClientError::InteractionRequired` to `credential-provider-unavailable` (not denied); enforce `EntraCredentialOwner::ExactConsumer` so only the declared `consumerRef` may acquire Primary reuse disposition: `adapt`. Preserved source-plan detail: copy and adapt. | | Integration | User-domain or system-domain Process under Guest; d2b-bus routing; Credential controller | | Data migration | Full reset | | Validation | **`src/` unit**: `EntraCredentialClient` trait API, `OpaqueAzureRef::parse` on `tenantId`, `EntraCredentialOwner::ExactConsumer` guard, `EntraClientState` transitions. **`tests/` Cargo integration**: `lifecycle.rs` (acquire/refresh/revoke/inspect with `FakeEntraClient`); `conformance.rs` (all conformance arms); `faults.rs` (interaction-required → unavailable, generation-mismatch, colocated-consumer rejection); `canary.rs` (`credential_canary` and `endpoint_canary` absent from every response and delivery record); `delivery.rs` (delivery-session binding, zeroizing, replay-safe); `placement.rs` (host-system placement rejected). **`integration/` fixtures**: `container-service.sh`; `guest-placement.nix` (user-domain and system-domain Process on Guest in runNixOSTest); `cleanup-rollback.sh`. | @@ -1949,9 +1949,9 @@ config formalizes this as an `OpaqueAzureRef` with the same charset restriction. | Dependency/owner | `ADR046-credential-001`, `ADR046-credential-002`; `ADR046-reconcile-001`; credential-managed-identity owner | | Current source | `packages/d2b-realm-provider/src/credential.rs:ManagedIdentityRef` (v3 baseline, reachable); `d2bd/src/lib.rs:managed_identity_client_id` (reachable ACA config); `d2b-provider-aca/src/lib.rs:managed_identity_client_id` (reachable) | | Reuse source | main `a1cc0b2d`: `packages/d2b-provider-credential-managed-identity/src/lib.rs` (full implementation); `src/tests.rs` (full test suite) | -| Reuse action | copy and adapt | +| Reuse action | adapt | | Destination | `packages/d2b-provider-credential-managed-identity/src/{lib.rs, controller.rs, service.rs, main.rs}`; `packages/d2b-provider-credential-managed-identity/tests/{lifecycle.rs, conformance.rs, faults.rs, canary.rs, delivery.rs, placement.rs}`; `packages/d2b-provider-credential-managed-identity/integration/{container-service.sh, host-guest-placement.nix, aca-credential-ref.sh, cleanup-rollback.sh}`; `packages/d2b-provider-credential-managed-identity/README.md` (all §Provider README required sections) | -| Detailed design | Adapt `ManagedIdentityCredentialProvider` to v3 service; enforce `ManagedIdentityCredentialOwner::ExactSdkConsumer`; reject user-agent placement (IMDS is machine-local, not user-session); validate `clientId` config using `OpaqueAzureRef::parse` directly from v3 baseline (note: current v3 source field is named via `ManagedIdentityRef.client_id`; target field name is `clientId`); retain `ManagedIdentityCredentialClient` trait unchanged; map `ManagedIdentityClientState::Unavailable` to `credential-provider-unavailable`; `sign-challenge` operation class returns schema-invalid immediately | +| Detailed design | Adapt `ManagedIdentityCredentialProvider` to v3 service; enforce `ManagedIdentityCredentialOwner::ExactSdkConsumer`; reject user-agent placement (IMDS is machine-local, not user-session); validate `clientId` config using `OpaqueAzureRef::parse` directly from v3 baseline (note: current v3 source field is named via `ManagedIdentityRef.client_id`; target field name is `clientId`); retain `ManagedIdentityCredentialClient` trait unchanged; map `ManagedIdentityClientState::Unavailable` to `credential-provider-unavailable`; `sign-challenge` operation class returns schema-invalid immediately Primary reuse disposition: `adapt`. Preserved source-plan detail: copy and adapt. | | Integration | System-domain Process under Host or Guest; d2b-bus routing; Credential controller; ACA runtime-azure-container-apps Provider may hold a reference to this Credential resource | | Data migration | Full reset; `d2b-provider-aca` managed_identity_client_id config field migrated to a Credential resource reference in the v3 ACA Provider config | | Validation | **`src/` unit**: `ManagedIdentityCredentialClient` trait, `OpaqueAzureRef::parse` on `clientId`, `ManagedIdentityCredentialOwner::ExactSdkConsumer` guard, `imdsEndpointAlias` validation, `sign-challenge` schema-invalid fast path. **`tests/` Cargo integration**: `lifecycle.rs` (acquire/refresh/revoke/inspect with `FakeClient`); `conformance.rs`; `faults.rs` (unavailable state, colocated-consumer rejection); `canary.rs` (canary absent from all responses and delivery records); `delivery.rs`; `placement.rs` (user-agent placement rejected). **`integration/` fixtures**: `container-service.sh`; `host-guest-placement.nix` (system-domain Host and Guest placement in runNixOSTest); `aca-credential-ref.sh` (ACA Provider config uses `credentialRef`; raw `managed_identity_client_id` absent); `cleanup-rollback.sh`. | diff --git a/docs/specs/ADR-046-resources-device.md b/docs/specs/ADR-046-resources-device.md index 0ef8c6f7b..ca9507fce 100644 --- a/docs/specs/ADR-046-resources-device.md +++ b/docs/specs/ADR-046-resources-device.md @@ -2039,9 +2039,9 @@ error listing the missing paths. There is no opt-out mechanism. | --- | --- | | Dependency/owner | W0 shared contract root; `d2b-contracts` | | Current source | `packages/d2b-contracts/src/security_key.rs` (SecurityKeyStatusResponse, SecurityKeySession, SecurityKeyLeaseState, SecurityKeyVmSessionState DTOs; implemented-and-reachable), `usbip.rs`, `broker_wire.rs`; `packages/d2b-core/src/privileges_w3.rs` (W3BrokerOperation: SecurityKeyOpenDevice, SecurityKeyApplyUdevRules, UsbipBindFirewallRule — implemented-and-reachable); `packages/d2b-core/src/manifest_v04.rs` VmEntry device fields (tpm, usbip_yubikey, security_key, graphics — old Workload manifest, generated-or-eval-contract) | -| Reuse action | extract and adapt | +| Reuse action | adapt | | Destination | `packages/d2b-contracts/src/v3/device.rs` | -| Detailed design | Device ResourceType schema (spec/status/conditions/claims/inventory); closed-set error codes; Device RBAC verbs; broker operation effect-limit constants | +| Detailed design | Device ResourceType schema (spec/status/conditions/claims/inventory); closed-set error codes; Device RBAC verbs; broker operation effect-limit constants Primary reuse disposition: `adapt`. Preserved source-plan detail: extract and adapt. | | Integration | Provider dossiers, resource API/store, CLI status surfaces | | Data migration | Full reset; no v2 device object import | | Validation | Schema golden vectors; unknown-field denial; exclusive/shared conflict rejection; arbitration/maxClaims invariant | @@ -2053,9 +2053,9 @@ error listing the missing paths. There is no opt-out mechanism. | --- | --- | | Dependency/owner | ADR046-device-001; device-tpm provider owner | | Current source | `packages/d2b-host/src/swtpm_argv.rs`; `packages/d2b-priv-broker/src/ops/swtpm_dir.rs`; `nixos-modules/components/tpm.nix` | -| Reuse action | extract and adapt | +| Reuse action | adapt | | Destination | `packages/d2b-provider-device-tpm/src/` (controller, swtpm runner, state-dir logic); `packages/d2b-provider-device-tpm/tests/` (hermetic Cargo integration); `packages/d2b-provider-device-tpm/integration/` (container/Host scenarios); `packages/d2b-provider-device-tpm/README.md` | -| Detailed design | Device spec/status; flush EphemeralProcess → swtpm Process sequencing; state-dir hardening; tamper-marker; finalizer non-deletion of Volume; Nix emitter; all four required crate paths present (see "Provider crate layout") | +| Detailed design | Device spec/status; flush EphemeralProcess → swtpm Process sequencing; state-dir hardening; tamper-marker; finalizer non-deletion of Volume; Nix emitter; all four required crate paths present (see "Provider crate layout") Primary reuse disposition: `adapt`. Preserved source-plan detail: extract and adapt. | | Integration | Zone resource store; Process controller; Volume lifecycle | | Data migration | State dir and tamper markers preserved across reset | | Validation | `src/`: swtpm argv golden, state-dir, flush sequencing, finalizer no-delete; `tests/`: `controller_state_machine.rs`, `conformance.rs`, `fault_swtpm_missing.rs`; `integration/`: `provision_and_reboot/`, `tamper_marker_survives/`, `finalizer_no_delete/`; workspace policy check: `make test-policy` passes with all four paths present | @@ -2067,9 +2067,9 @@ error listing the missing paths. There is no opt-out mechanism. | --- | --- | | Dependency/owner | ADR046-device-001; device-usbip provider owner | | Current source | `packages/d2b-contracts/src/usbip.rs` (USBIP DTOs, SYSFS_BUS_ID_MAX, bus-ID validation — implemented-and-reachable); `packages/d2b-core/src/bundle_resolver.rs` USBIP intents; `packages/d2b-core/src/privileges.rs` authz rows; `packages/d2bd/src/usbip_state_machine.rs` (typed per-busid bring-up state machine, step order: modprobe→lock→withhold→firewall→backend→bind→proxy — implemented-and-reachable); `packages/d2bd/src/usbipd_perenv_autostart.rs` (per-env usbipd daemon autostart — implemented-and-reachable); `packages/d2bd/src/usbip_reconcile_state.rs` (restart-safe reconciler state model — implemented-but-unwired); old Workload Nix option: `nixos-modules/options-realms-workloads.nix` `d2b.vms..usbip.*` (generated-or-eval-contract); `nixos-modules/components/usbip.nix` | -| Reuse action | extract and adapt | +| Reuse action | adapt | | Destination | `packages/d2b-provider-device-usbip/src/` (controller, daemon Process, bind/unbind EphemeralProcess, firewall); `packages/d2b-provider-device-usbip/tests/` (hermetic Cargo integration); `packages/d2b-provider-device-usbip/integration/` (container/Host scenarios); `packages/d2b-provider-device-usbip/README.md` | -| Detailed design | Device spec/status; bus ID validation; firewall rule ownership-marker; bind/unbind EphemeralProcess; per-Device daemon Process (owned by device-usbip; Network supplies dependency/firewall interface); Nix emitter; all four required crate paths present (see "Provider crate layout") | +| Detailed design | Device spec/status; bus ID validation; firewall rule ownership-marker; bind/unbind EphemeralProcess; per-Device daemon Process (owned by device-usbip; Network supplies dependency/firewall interface); Nix emitter; all four required crate paths present (see "Provider crate layout") Primary reuse disposition: `adapt`. Preserved source-plan detail: extract and adapt. | | Integration | Zone resource store; broker `UsbipBindFirewallRule`; nftables marker | | Data migration | None; full reset | | Validation | `src/`: bus ID corpus, firewall marker format, EphemeralProcess creation; `tests/`: `arbitration_conflict.rs`, `conformance.rs`, `firewall_marker.rs`, `explicit_attach_split.rs`; `integration/`: `arbitration_conflict/`, `busid_bind_cycle/`, `network_firewall_coexistence/`; workspace policy check: `make test-policy` passes with all four paths present | @@ -2081,9 +2081,9 @@ error listing the missing paths. There is no opt-out mechanism. | --- | --- | | Dependency/owner | ADR046-device-001; device-security-key provider owner | | Current source | `packages/d2b-contracts/src/security_key.rs` (DTOs — implemented-and-reachable); `packages/d2b-core/src/privileges_w3.rs` (W3BrokerOperation — implemented-and-reachable); **KEY: relay is in d2bd** — `packages/d2bd/src/security_key.rs` (CTAPHID relay: CID translation, SO_PEERCRED, hidraw async fd, accept loop — implemented-and-reachable) and `packages/d2bd/src/lib.rs:start_sk_accept_loop` (ProcessRole::SecurityKeyFrontend dispatch — implemented-and-reachable); **guest binary**: `packages/d2b-sk-frontend/src/` (static UHID frontend — implemented-and-reachable); old Workload Nix option: `nixos-modules/options-realms-workloads.nix` `d2b.vms..security_key.*`; `nixos-modules/components/security-key-guest.nix` | -| Reuse action | extract and adapt | +| Reuse action | adapt | | Destination | `packages/d2b-provider-device-security-key/src/` (controller, relay Process, guest frontend Process, lease/session ring); `packages/d2b-provider-device-security-key/tests/` (hermetic Cargo integration); `packages/d2b-provider-device-security-key/integration/` (container/Host/Guest scenarios); `packages/d2b-provider-device-security-key/README.md` | -| Detailed design | Device spec/status; unprivileged relay Process (`device--sk-relay`); guest frontend Process (`device--sk-frontend`, `executionRef: Guest/`); ceremony/CID/lease/session ring (max 1 session per Device); broker hidraw-only access; mandatory Core-derived `(Host, physical-usb-backing, opaqueKeyDigest)` claim shared with every USB Provider before effects; Nix emitter; all four required crate paths present (see "Provider crate layout") | +| Detailed design | Device spec/status; unprivileged relay Process (`device--sk-relay`); guest frontend Process (`device--sk-frontend`, `executionRef: Guest/`); ceremony/CID/lease/session ring (max 1 session per Device); broker hidraw-only access; mandatory Core-derived `(Host, physical-usb-backing, opaqueKeyDigest)` claim shared with every USB Provider before effects; Nix emitter; all four required crate paths present (see "Provider crate layout") Primary reuse disposition: `adapt`. Preserved source-plan detail: extract and adapt. | | Integration | Zone resource store; broker `SecurityKeyOpenDevice`/`SecurityKeyApplyUdevRules`; Guest frontend module | | Data migration | None; full reset | | Validation | `src/`: lease transitions, session ring eviction, broker op path-free, CID round-trip; `tests/`: `lease_state_machine.rs`, `session_ring.rs`, `mutual_exclusion.rs` proves security-key and USB implementations resolve one token to a byte-identical physical backing key and the loser receives `physical-usb-backing-conflict` before any effect, `conformance.rs`, `guest_frontend_process.rs`; `integration/`: `lease_acquire_cancel/`, `session_ring_capacity/`, `guest_frontend_connect/`; workspace policy check: `make test-policy` passes with all four paths present | @@ -2095,9 +2095,9 @@ error listing the missing paths. There is no opt-out mechanism. | --- | --- | | Dependency/owner | ADR046-device-001; device-gpu provider owner | | Current source | `packages/d2b-host/src/gpu_argv.rs`, `video_argv.rs`; `packages/d2b-core/src/bundle_resolver.rs` Gpu/GpuRenderNode/Video; `nixos-modules/components/graphics.nix`, `video/guest.nix` | -| Reuse action | extract and adapt | +| Reuse action | adapt | | Destination | `packages/d2b-provider-device-gpu/src/` (controller, GPU/render-node/video worker Processes, broker token set); `packages/d2b-provider-device-gpu/tests/` (hermetic Cargo integration); `packages/d2b-provider-device-gpu/integration/` (container/Host/Guest scenarios); `packages/d2b-provider-device-gpu/README.md` | -| Detailed design | Combined Device spec/status; GPU worker Process (`device--gpu`, exclusive); render-node Process (`device--render-node`, exclusive default, shared when explicit); video-decoder Process (`device--video`); broker token set; wire-contract constants; shared render-node arbitration enforcement; Nix emitter; all four required crate paths present (see "Provider crate layout") | +| Detailed design | Combined Device spec/status; GPU worker Process (`device--gpu`, exclusive); render-node Process (`device--render-node`, exclusive default, shared when explicit); video-decoder Process (`device--video`); broker token set; wire-contract constants; shared render-node arbitration enforcement; Nix emitter; all four required crate paths present (see "Provider crate layout") Primary reuse disposition: `adapt`. Preserved source-plan detail: extract and adapt. | | Integration | Zone resource store; broker `SpawnRunner`/`OpenDevice`; Display Provider device consumption | | Data migration | None; full reset | | Validation | `src/`: process role selection, wire-constant snapshot, render-node vs full-GPU branching; `tests/`: `combined_reconcile.rs`, `render_node_enforcement.rs`, `wire_constant_snapshot.rs`, `conformance.rs`; `integration/`: `gpu_worker_start/`, `render_node_shared/`, `video_dependency/`; workspace policy check: `make test-policy` passes with all four paths present | @@ -2123,7 +2123,7 @@ error listing the missing paths. There is no opt-out mechanism. | --- | --- | | Dependency/owner | ADR046-device-006; Zone runtime implementer | | Current source | None (new work; no equivalent in v3 baseline or main a1cc0b2d) | -| Reuse action | new | +| Reuse action | create | | Destination | `packages/d2b-core-controller/src/configuration.rs`; `packages/d2b-contract-tests/tests/device_gen_cleanup.rs` | | Detailed design | Implement the cleanup contract described in "Zone generation and cleanup": (1) on new generation activation, diff `resources` against prior generation's `resources` by (type, name) — resources absent from new generation that have `managedBy=configuration` go into `pendingDeletion`; (2) Zone phase transitions to `Degraded/pending-cleanup` until all items in `pendingDeletion` reach terminal Delete; (3) items in `pendingDeletion` with `managedBy=controller` or `managedBy=api` are rejected with `cleanup-config-ownership-mismatch`; (4) `managedBy=controller` and `managedBy=api` resources are never touched by generation cleanup — `cleanup-controller-resource-protected` emitted if attempted; `managedBy=api` resources persist until explicit Delete; (5) prior generations retained by count: default 3, range 1..16, configured via `d2b.zones..retainedGenerations`; (6) each deletion is non-blocking; (7) finalizer-stuck timeout emits `cleanup-finalizer-stuck` and leaves Zone in `Degraded/pending-cleanup`; (8) all deletions emit `config-resource-deletion-requested` audit record with digested resource identity | | Integration | Consumes Zone resource bundle from ADR046-device-006 emitter; drives Device Provider finalizers via normal resource-Delete protocol; feeds Zone status conditions `GenerationCleanPending` and `GenerationCleanError` | @@ -2137,9 +2137,9 @@ error listing the missing paths. There is no opt-out mechanism. | --- | --- | | Dependency/owner | ADR046-device-002 through ADR046-device-005; workspace/tooling maintainer | | Current source | `packages/xtask/src/main.rs` (existing `gen-*` and `check-*` commands — implemented-and-reachable); `packages/d2b-contract-tests/tests/workspace_policy.rs` (existing workspace policy tests — implemented-and-reachable) | -| Reuse action | extend | +| Reuse action | adapt | | Destination | `packages/xtask/src/main.rs` (`check-provider-layout` subcommand); `packages/d2b-contract-tests/tests/workspace_policy.rs` (provider-layout policy assertions) | -| Detailed design | Add `cargo xtask check-provider-layout`: enumerate workspace members matching `d2b-provider-*`; for each, assert `src/`, `tests/`, `integration/`, and `README.md` all exist relative to the crate root; report all missing paths before failing; no opt-out flag. Add companion test in `workspace_policy.rs` that asserts the same invariant against the static crate list in `Cargo.toml`. Wire `cargo xtask check-provider-layout` into `make test-policy` alongside existing workspace naming and sort checks. The check must also run in CI as part of the policy gate. | +| Detailed design | Add `cargo xtask check-provider-layout`: enumerate workspace members matching `d2b-provider-*`; for each, assert `src/`, `tests/`, `integration/`, and `README.md` all exist relative to the crate root; report all missing paths before failing; no opt-out flag. Add companion test in `workspace_policy.rs` that asserts the same invariant against the static crate list in `Cargo.toml`. Wire `cargo xtask check-provider-layout` into `make test-policy` alongside existing workspace naming and sort checks. The check must also run in CI as part of the policy gate. Primary reuse disposition: `adapt`. Preserved source-plan detail: extend. | | Integration | `make test-policy`; `make check`; GitHub CI policy job | | Data migration | None — full d2b 3.0 reset; no prior state to migrate | | Validation | Policy gate passes once all four Device Provider crates exist with required paths; gate fails predictably when any path is removed from a Provider crate fixture; test fixture crate with one missing path must produce a named error identifying the exact missing path | diff --git a/docs/specs/ADR-046-resources-host-guest-process-user.md b/docs/specs/ADR-046-resources-host-guest-process-user.md index 8693ff5eb..1639dd26d 100644 --- a/docs/specs/ADR-046-resources-host-guest-process-user.md +++ b/docs/specs/ADR-046-resources-host-guest-process-user.md @@ -2347,9 +2347,9 @@ A work item whose `Destination` row introduces a new `d2b-provider-*` crate must | Dependency/owner | W0 shared contract root; `d2b-contracts` | | Current source | `packages/d2b-core/src/processes.rs`: `ProcessRole` (18 variants), `ProcessNode`, `RoleProfile`, `NamespaceSet`, `MountPolicy`, `CgroupPlacement`, `ReadinessPredicate`; `packages/d2b-core/src/minijail_profile.rs`: `MinijailProfile`, `UserNamespaceProfile`, `NamespaceSet`, `MountPolicy`, `BindMount`, `CgroupPlacement`; `packages/d2b-core/src/storage.rs`: `StoragePathSpec`, `AclGrant`, `CleanupPolicy`, `RepairPolicy`; `packages/d2b-realm-core/src/ids.rs`: `RealmId`, `WorkloadId` (→ GuestRef), `NodeId` (→ HostRef), `ProviderId` (→ Provider ResourceRef), `ExecutionId` (→ EphemeralProcess exec identity), `PrincipalId` (→ User ResourceRef), `AllocatorLeaseId`, `ControllerGenerationId`; `packages/d2b-realm-core/src/workload.rs`: `WorkloadProviderKind` (`LocalVm`→runtime-cloud-hypervisor Provider, `QemuMedia`→runtime-qemu-media Provider, `ProviderManaged`→ACA/relay Providers, `UnsafeLocal`→user-only Host `isolationPosture="none"`), `IsolationPosture` (`VirtualMachine`→Guest, `ProviderManaged`→Guest, `UnsafeLocal`→Host `isolationPosture="none"`), `WorkloadExecutionPosture`, `WorkloadSummary`, `WorkloadState`; `packages/d2b-realm-core/src/target.rs`: `RealmTarget`, `TargetName`, `RealmTargetParser` (current analog for `/` ResourceRef parsing); `packages/d2b-realm-core/src/realm.rs`: `RealmPath`, `RealmControllerPlacement`, `EntrypointMode` (current Zone hierarchy analog); `packages/d2b-core/src/workload_identity.rs`: `WorkloadIdentity`, `WorkloadTarget` (= `RealmTarget`), `WorkloadBackend`, `WorkloadRuntimeIntent` (identity/backend separation reuse model for Host/Guest ResourceType split) | | Reuse source | `packages/d2b-contracts/src/v3/` as destination; no equivalent main source for Host/Guest/Process ResourceType contracts | -| Reuse action | extract and adapt | +| Reuse action | adapt | | Destination | `packages/d2b-contracts/src/v3/host.rs`, `packages/d2b-contracts/src/v3/guest.rs`, `packages/d2b-contracts/src/v3/execution_policy.rs`, `packages/d2b-contracts/src/v3/process.rs`, `packages/d2b-contracts/src/v3/ephemeral_process.rs`, `packages/d2b-contracts/src/v3/user.rs` | -| Detailed design | Implement strict typed Rust structs for HostSpec, GuestSpec, ExecutionPolicy, ExecutionSpec, SandboxSpec, BudgetSpec, MountSpec, NetworkUsageSpec, DeviceUsageSpec, EndpointSpec, TelemetrySpec, ProcessSpec, EphemeralProcessSpec, UserSpec; strict serde deny_unknown_fields; bounds/redaction on all string fields; stable error types; `UserSpec.osUsername` validated as OS username (1..255 bytes, no NUL/control/path-separator), not ResourceName grammar | +| Detailed design | Implement strict typed Rust structs for HostSpec, GuestSpec, ExecutionPolicy, ExecutionSpec, SandboxSpec, BudgetSpec, MountSpec, NetworkUsageSpec, DeviceUsageSpec, EndpointSpec, TelemetrySpec, ProcessSpec, EphemeralProcessSpec, UserSpec; strict serde deny_unknown_fields; bounds/redaction on all string fields; stable error types; `UserSpec.osUsername` validated as OS username (1..255 bytes, no NUL/control/path-separator), not ResourceName grammar Primary reuse disposition: `adapt`. Preserved source-plan detail: extract and adapt. | | Integration | Provider dossiers, controller descriptors, Zone resource API, Nix resource compiler | | Data migration | Full reset; no v2 resource import | | Validation | Golden JSON vectors for each ResourceType; serde unknown-field rejection; bounds enforcement; `UserSpec.osUsername` OS-username validation (underscore allowed, NUL rejected) | @@ -2362,9 +2362,9 @@ A work item whose `Destination` row introduces a new `d2b-provider-*` crate must | Work item ID | `ADR046-exec-002` | | Dependency/owner | ADR046-exec-001; `d2b-contracts` | | Current source | `packages/d2b-core/src/processes.rs`: `ReadinessPredicate`, `VmProcessInvariants`; `packages/d2b-core/src/processes.rs`: `SpawnRunnerPlanOp`; `packages/d2b-priv-broker/src/ops/` SpawnRunner | -| Reuse action | extract and adapt | +| Reuse action | adapt | | Destination | `packages/d2b-contracts/src/v3/process_provider.rs`: LaunchTicket, ProcessIdentityDigest, AdoptionCandidate, PidfdEvidence, WaitReapOwner, BrokerTerminalResult, ProcessOutcome, ExitClass | -| Detailed design | LaunchTicket (Process/EphemeralProcess ref/UID/revision/generation, owner Provider/component/template, executionRef/domain/userRef, providerRef, compiled sandbox/budget/mount/device/network/endpoint digest, inherited FD table, operation/deadline/cancellation, expected identity/readiness); ProcessIdentityDigest (opaque bounded hex string); AdoptionCandidate (cgroup leaf path relative to controller root, start-time token, executable hash); BrokerTerminalResult binds process identity/operation to the clone3 parent's exact-once wait/reap status and cannot be constructed from pidfd readability; all types zeroizing where credential-adjacent | +| Detailed design | LaunchTicket (Process/EphemeralProcess ref/UID/revision/generation, owner Provider/component/template, executionRef/domain/userRef, providerRef, compiled sandbox/budget/mount/device/network/endpoint digest, inherited FD table, operation/deadline/cancellation, expected identity/readiness); ProcessIdentityDigest (opaque bounded hex string); AdoptionCandidate (cgroup leaf path relative to controller root, start-time token, executable hash); BrokerTerminalResult binds process identity/operation to the clone3 parent's exact-once wait/reap status and cannot be constructed from pidfd readability; all types zeroizing where credential-adjacent Primary reuse disposition: `adapt`. Preserved source-plan detail: extract and adapt. | | Integration | ProviderSupervisor adapter; system-systemd and system-minijail Process Providers | | Data migration | Full reset | | Validation | Golden LaunchTicket and BrokerTerminalResult vectors; field redaction test; digest-binding test; duplicate/mismatched/non-parent terminal relay rejection | @@ -2377,9 +2377,9 @@ A work item whose `Destination` row introduces a new `d2b-provider-*` crate must | Work item ID | `ADR046-exec-003` | | Dependency/owner | ADR046-exec-001; system-core Provider owner | | Current source | `packages/d2b-core/src/host_check.rs`: `HostCheckReport`, `HostCheckSummary`, `HostCheckFinding`, `HostCheckSeverity`; `packages/d2bd/src/pidfs_probe.rs`; `packages/d2bd/src/kernel_module_check.rs`; `packages/d2b-core/src/provider_capabilities.rs`; `packages/d2b-realm-core/src/ids.rs`: `HostResourceId` (current host-identity handle), `NodeId` (execution node identity); `packages/d2b-realm-core/src/node.rs`: `NodeKind::FullHost`, `NodeSummary` (host node's capability advertisement — direct reuse model for Host status `capabilities[]`) | -| Reuse action | extract and adapt | +| Reuse action | adapt | | Destination | `packages/d2b-provider-system-core/src/host.rs`: HostReconciler; status/conditions/capability probe implementation; `packages/d2b-provider-system-core/tests/`: hermetic reconcile/conformance/fault tests; `packages/d2b-provider-system-core/integration/`: Host probe and lifecycle integration scenarios; `packages/d2b-provider-system-core/README.md`: Provider identity, `spec.provider.settings` schema, ResourceTypes, placement, RBAC, security posture, telemetry labels, build/test commands (provider crate standard layout — see §Provider crate standard layout) | -| Detailed design | Async Host reconcile loop per this spec's Reconcile section; HostCapabilityClass probe set (kvm/pidfd/cgroup-v2/user-namespace/wayland/audio-pipewire/gpu-render/tpm2/usbip); bounded OS probes with timeout; mandatory system-minijail placement gate for Linux ≥5.14 plus writable delegated-leaf `cgroup.kill` independent of optional `kernelVersionMin`; `isolationPosture` validation and status; aggregate budget reservation tracking via List; status write with expected revision | +| Detailed design | Async Host reconcile loop per this spec's Reconcile section; HostCapabilityClass probe set (kvm/pidfd/cgroup-v2/user-namespace/wayland/audio-pipewire/gpu-render/tpm2/usbip); bounded OS probes with timeout; mandatory system-minijail placement gate for Linux ≥5.14 plus writable delegated-leaf `cgroup.kill` independent of optional `kernelVersionMin`; `isolationPosture` validation and status; aggregate budget reservation tracking via List; status write with expected revision Primary reuse disposition: `adapt`. Preserved source-plan detail: extract and adapt. | | Integration | Provider/system-core fixed bootstrap process; ResourceClient Get/List/UpdateStatus | | Data migration | New Host resources from Nix; no v2 state import | | Validation | Multiple Hosts per Zone; system-only and user-only Hosts; capability probe mocks; Linux <5.14 and missing/unwritable `cgroup.kill` reject system-minijail before spawn; Linux ≥5.14 positive probe; `isolationPosture="none"` rejection of system processes; budget overcommit rejection; `tests/` all pass under `cargo test`; `integration/` scenario passes in container fixture; `README.md` present and covers all required sections (provider crate standard layout acceptance) | @@ -2392,9 +2392,9 @@ A work item whose `Destination` row introduces a new `d2b-provider-*` crate must | Work item ID | `ADR046-exec-004` | | Dependency/owner | ADR046-exec-001; system-core Provider owner | | Current source | `packages/d2b-userd/src/lib.rs`: `UserdConfig`, `UserSessionIdentity`; `packages/d2bd/src/admission.rs` (`SO_PEERCRED` UID/GID lookup); `packages/d2b-realm-core/src/ids.rs`: `PrincipalId` (current host-local principal identity); `packages/d2b-realm-core/src/access.rs`: `HostLocalPeerCredentialSemantics`, `HostLocalPeerCredentialSource`, `RealmAccessClientContract` (current host-local credential/uid resolution model; direct reuse precedent for v3 User → Process userRef resolution) | -| Reuse action | extract and adapt | +| Reuse action | adapt | | Destination | `packages/d2b-provider-system-core/src/user.rs`: UserReconciler; NSS lookup implementation | -| Detailed design | Async User reconcile loop per this spec's Reconcile section; NSS `getpwnam(spec.osUsername)` with bounded timeout (default 5 s); `spec.osUsername` validated on admission (1..255 bytes, no NUL/control/path-separator); `metadata.name` used only as Zone-local resource identity and `User/` ref; uid/gid/home/shell/group discovery written to status; session manager availability check via fixed user supervisor; status write; phase Degraded on SessionManagerReady=False | +| Detailed design | Async User reconcile loop per this spec's Reconcile section; NSS `getpwnam(spec.osUsername)` with bounded timeout (default 5 s); `spec.osUsername` validated on admission (1..255 bytes, no NUL/control/path-separator); `metadata.name` used only as Zone-local resource identity and `User/` ref; uid/gid/home/shell/group discovery written to status; session manager availability check via fixed user supervisor; status write; phase Degraded on SessionManagerReady=False Primary reuse disposition: `adapt`. Preserved source-plan detail: extract and adapt. | | Integration | Provider/system-core fixed bootstrap process; ResourceClient Get/List/UpdateStatus; other controllers resolve User status via ResourceClient | | Data migration | New User resources from Nix; no v2 state import | | Validation | User found/not-found; group membership; sessionManagerAvailable; multiple Users; deletion blocked by Process userRef; `spec.osUsername` with underscore succeeds NSS lookup where ResourceName grammar would reject it; `metadata.name` and `spec.osUsername` differ (e.g. `alice-admin` / `alice_admin`); `spec.osUsername` containing NUL/control/path-separator rejected at admission | @@ -2422,9 +2422,9 @@ A work item whose `Destination` row introduces a new `d2b-provider-*` crate must | Work item ID | `ADR046-exec-006` | | Dependency/owner | ADR046-exec-001 + ADR046-exec-002; system-systemd Process Provider owner | | Current source | `packages/d2b-unsafe-local-helper/src/systemd.rs`; `packages/d2b-guestd/src/exec.rs`: `SystemdRunUnitManager`, `ManagedUnit`, `ExecPolicy`; `packages/d2b-guestd/src/exec_linux.rs`; `packages/d2bd/src/supervisor/` (transient unit management) | -| Reuse action | extract and adapt | +| Reuse action | adapt | | Destination | `packages/d2b-provider-system-systemd/src/`: launch.rs, adoption.rs, pidfd.rs, wait.rs, user_supervisor.rs; `packages/d2b-provider-system-systemd/tests/`: hermetic lifecycle/conformance/fault tests; `packages/d2b-provider-system-systemd/integration/`: transient-unit and user-supervisor integration scenarios; `packages/d2b-provider-system-systemd/README.md`: Provider identity, `spec.provider.settings` schema, ResourceTypes, placement, RBAC, security posture, telemetry labels, build/test commands (provider crate standard layout — see §Provider crate standard layout) | -| Detailed design | system-systemd Process/EphemeralProcess provider conformance per this spec's system-systemd conformance section; transient system unit (Type=exec); InvocationID+cgroup+MainPID+start-time binding before pidfd_open; systemd-owned wait/reap; user domain via fixed user supervisor; adoption re-verification; sandboxSpec compilation to systemd hardening directives; runtimeDeadline enforcement; drainTimeout SIGTERM/SIGKILL sequence | +| Detailed design | system-systemd Process/EphemeralProcess provider conformance per this spec's system-systemd conformance section; transient system unit (Type=exec); InvocationID+cgroup+MainPID+start-time binding before pidfd_open; systemd-owned wait/reap; user domain via fixed user supervisor; adoption re-verification; sandboxSpec compilation to systemd hardening directives; runtimeDeadline enforcement; drainTimeout SIGTERM/SIGKILL sequence Primary reuse disposition: `adapt`. Preserved source-plan detail: extract and adapt. | | Integration | Zone-installed Provider/system-systemd; ProviderSupervisor LaunchTicket interface; ResourceClient UpdateStatus | | Data migration | Current ProcessRole/systemd unit roles converted by ProcessRole disposition table after parity | | Validation | Shared process conformance test matrix (lifecycle/readiness/crash/drain/adoption/user-domain/sandboxSpec/pidfd); system-specific InvocationID binding; no-static-unit test; `tests/` all pass under `cargo test`; `integration/` scenario passes in container fixture; `README.md` present and covers all required sections (provider crate standard layout acceptance) | @@ -2437,9 +2437,9 @@ A work item whose `Destination` row introduces a new `d2b-provider-*` crate must | Work item ID | `ADR046-exec-007` | | Dependency/owner | ADR046-exec-001 + ADR046-exec-002; system-minijail Process Provider owner | | Current source | `packages/d2b-core/src/processes.rs`: `ProcessNode`, `RoleProfile`, `NamespaceSet`, `MountPolicy`, `CgroupPlacement`; `packages/d2b-core/src/minijail_profile.rs`: full; `packages/d2b-priv-broker/src/ops/spawn_runner.rs` (if present at baseline); `packages/d2bd/src/supervisor/` pidfd/wait; `packages/d2b-core/src/process_builder.rs` | -| Reuse action | extract and adapt | +| Reuse action | adapt | | Destination | `packages/d2b-provider-system-minijail/src/`: sandbox_compiler.rs, launch.rs, adoption.rs, pidfd.rs, wait.rs, user_ns.rs; `packages/d2b-provider-system-minijail/tests/`: hermetic sandbox-compilation/lifecycle/conformance/fault tests; `packages/d2b-provider-system-minijail/integration/`: clone3/user-namespace and broker-spawn integration scenarios; `packages/d2b-provider-system-minijail/README.md`: Provider identity, `spec.provider.settings` schema, ResourceTypes, placement, RBAC, security posture (capabilities, namespaces, seccomp), telemetry labels, build/test commands (provider crate standard layout — see §Provider crate standard layout) | -| Detailed design | system-minijail Process/EphemeralProcess provider conformance per this spec's system-minijail conformance section; SandboxSpec-to-minijail plan compilation; Linux ≥5.14/cgroup.kill placement gate; broker clone3(CLONE_PIDFD|CLONE_INTO_CGROUP) parent retains sole waitid(P_PIDFD)/reap/exit-status ownership and relays a typed terminal result; ProviderSupervisor polls a verified duplicate and retains exact-main pidfd_send_signal semantics but never waits/reaps; adoption verifies original broker parent; runtimeDeadline/drainTimeout use graceful main signal then anchored leaf cgroup.kill, broker wait/reap, and empty-leaf proof; no PID/PGID fallback; EphemeralProcess one-shot launch | +| Detailed design | system-minijail Process/EphemeralProcess provider conformance per this spec's system-minijail conformance section; SandboxSpec-to-minijail plan compilation; Linux ≥5.14/cgroup.kill placement gate; broker clone3(CLONE_PIDFD|CLONE_INTO_CGROUP) parent retains sole waitid(P_PIDFD)/reap/exit-status ownership and relays a typed terminal result; ProviderSupervisor polls a verified duplicate and retains exact-main pidfd_send_signal semantics but never waits/reaps; adoption verifies original broker parent; runtimeDeadline/drainTimeout use graceful main signal then anchored leaf cgroup.kill, broker wait/reap, and empty-leaf proof; no PID/PGID fallback; EphemeralProcess one-shot launch Primary reuse disposition: `adapt`. Preserved source-plan detail: extract and adapt. | | Integration | Zone-installed Provider/system-minijail fixed bootstrap process; ProviderSupervisor LaunchTicket; privileged broker effect adapter | | Data migration | Current RoleProfile/NamespaceSet/MountPolicy/CgroupPlacement adapted to SandboxSpec/BudgetSpec | | Validation | Shared process conformance test matrix; minijail-specific sandbox compilation tests; user namespace tests; clone3 parent-only wait/reap and broker-relay tests; poll-readability-not-status test; pidfd_send_signal duplicate-holder test; setsid descendant/recycled-PGID cgroup.kill teardown test; Linux 5.14/cgroup.kill platform-gate tests; adoption quarantine asserts no signal/cgroup.kill; `tests/` all pass under `cargo test`; `integration/` scenario passes; `README.md` present and covers all required sections (provider crate standard layout acceptance) | @@ -2482,9 +2482,9 @@ A work item whose `Destination` row introduces a new `d2b-provider-*` crate must | Work item ID | `ADR046-exec-010` | | Dependency/owner | ADR046-exec-001 + ADR046-exec-007; guestd migration owner | | Current source | `packages/d2b-guestd/src/exec.rs`: `ExecPolicy`, `ExecError`, `ExecState`, `ExitOutcome`, `ValidatedCommand`, `SpawnedProcess`, `RingChunk`, `ExecSnapshot`, `ExecCreateInput`; `packages/d2b-guestd/src/exec_linux.rs`; `packages/d2b-guestd/src/exec_pty.rs`; `packages/d2b-guestd/src/detached.rs`: `ManagedUnit`, `UnitError`, `UnitIdentity`; `packages/d2b-guestd/src/detached_registry.rs`; `packages/d2b-guestd/src/service.rs`; `packages/d2b-guestd/src/auth.rs`; `packages/d2b-guestd/src/login_session.rs` | -| Reuse action | extract and adapt | +| Reuse action | adapt | | Destination | `packages/d2b-provider-system-systemd/src/guest_exec.rs` (guest-domain EphemeralProcess launch via systemd-run inside guest); `packages/d2b-session/` (ComponentSession replacing ad-hoc guest ttrpc); runtime Provider guest bootstrap Process | -| Detailed design | Guest-side execution transitions to EphemeralProcess/Process resources under the owning Guest; guestd service becomes a fixed bootstrap Process owned by the runtime Provider controller; exec operations become EphemeralProcess creates via the Zone ResourceClient; detached sessions become EphemeralProcess with failedTtl=24h; shell sessions become shell-terminal Provider Processes; guestd auth.rs becomes ComponentSession via d2b-session (copy/adapt from ADR046-session-001 per ComponentSession spec) | +| Detailed design | Guest-side execution transitions to EphemeralProcess/Process resources under the owning Guest; guestd service becomes a fixed bootstrap Process owned by the runtime Provider controller; exec operations become EphemeralProcess creates via the Zone ResourceClient; detached sessions become EphemeralProcess with failedTtl=24h; shell sessions become shell-terminal Provider Processes; guestd auth.rs becomes ComponentSession via d2b-session (copy/adapt from ADR046-session-001 per ComponentSession spec) Primary reuse disposition: `adapt`. Preserved source-plan detail: extract and adapt. | | Integration | runtime-cloud-hypervisor Guest controller creates guest-bootstrap Process; Zone ResourceClient in guest-bootstrap creates EphemeralProcess/Process on behalf of guest workloads | | Data migration | Full reset; no guestd session state migration | | Validation | Guest EphemeralProcess exec lifecycle; detached exec TTL; guestd auth replaced by ComponentSession; no SSH fallback | @@ -2497,9 +2497,9 @@ A work item whose `Destination` row introduces a new `d2b-provider-*` crate must | Work item ID | `ADR046-exec-011` | | Dependency/owner | ADR046-exec-004 + ADR046-exec-010; userd migration owner | | Current source | `packages/d2b-userd/src/lib.rs`: `UserdConfig`, `UserSessionIdentity`, `UserAttachRequest`, `UserOutputCursor`, `UserExecSession` trait, `UserdError`, `UserdTransport`, `UnixSocketOnly`, `validate_attach_request`; `packages/d2b-userd/src/main.rs` | -| Reuse action | adapt (logic retained; protocol replaced) | +| Reuse action | adapt | | Destination | guest-domain process attachment becomes a ComponentSession named stream to the EphemeralProcess running in the guest; `UserExecSession` trait reimplemented as a typed ResourceClient+ComponentSession attachment | -| Detailed design | `UserdConfig.socket_name` replaced by Zone-local EphemeralProcess ResourceRef and ComponentSession attach verb; `UserSessionIdentity.uid`/`gid` become Process `userRef` resolved against User resource status; `UserAttachRequest.exec_id`/`tty`/`initial_size` become ComponentSession method parameters on the EphemeralProcess attachment service; `UserdError` becomes stable v3 error codes; `validate_attach_request` tty/size validation retained in the ComponentSession session schema | +| Detailed design | `UserdConfig.socket_name` replaced by Zone-local EphemeralProcess ResourceRef and ComponentSession attach verb; `UserSessionIdentity.uid`/`gid` become Process `userRef` resolved against User resource status; `UserAttachRequest.exec_id`/`tty`/`initial_size` become ComponentSession method parameters on the EphemeralProcess attachment service; `UserdError` becomes stable v3 error codes; `validate_attach_request` tty/size validation retained in the ComponentSession session schema Primary reuse disposition: `adapt`. Preserved source-plan detail: adapt (logic retained; protocol replaced). | | Integration | CLI/client uses ResourceClient to Get/Watch EphemeralProcess; attaches via d2b-bus ComponentSession attach verb | | Data migration | Full reset; no session state import | | Validation | Attach/detach; TTY size; stream close; error path parity with UserdError variants | @@ -2527,7 +2527,7 @@ A work item whose `Destination` row introduces a new `d2b-provider-*` crate must | Work item ID | `ADR046-exec-013` | | Dependency/owner | ADR046-exec-008; EphemeralProcess cleanup controller owner | | Current source | No direct current equivalent; current processes.json has no TTL/cleanup concept | -| Reuse action | new | +| Reuse action | create | | Destination | `packages/d2b-core-controller/src/cleanup.rs`: EphemeralProcess TTL cleanup controller handler | | Detailed design | Cleanup controller handler as specified in core-controllers spec; watches EphemeralProcess resources for terminal phase; computes cleanupEligibleAt from successfulTtl/failedTtl + completedAt; handles incidentHold; respects finalizers; issues normal Delete via ResourceClient; does not remove rows directly; bounded requeue-at for TTL expiry | | Integration | core-controller process; ResourceClient Watch(EphemeralProcess); ResourceClient Delete | @@ -2557,7 +2557,7 @@ A work item whose `Destination` row introduces a new `d2b-provider-*` crate must | Work item ID | `ADR046-exec-015` | | Dependency/owner | ADR046-exec-014 + ADR046-exec-001 + ADR046-exec-022; configuration generation controller owner | | Current source | No direct current equivalent; current realm/workload configuration is applied at NixOS activation time directly, not through a Resource API generation controller | -| Reuse action | new | +| Reuse action | create | | Destination | `packages/d2b-core-controller/src/configuration.rs`: `ZoneConfigController`, `GenerationState`, `PendingCleanup`, `BundleActivation`, `ActivationResult`, `ActivationError`, `GenerationDiff`, `DiffEntry`, `DiffKind`, `CleanupRecord`, `CleanupPhase`, `CleanupOutcome`, `RetentionPolicy`, `RetentionState`; `packages/d2b-core-controller/src/audit.rs`: audit event emission per the "Audit events" section | | Detailed design | The Zone configuration controller (`packages/d2b-core-controller/src/configuration.rs`) runs as a fixed process in the `d2b-core-controller` crate. It watches `/etc/d2b/zones//resource-bundle.json` (inotify or polling). On change: (1) read and verify `bundleSha256` integrity; fail closed on mismatch, emit `d2b.zone.config.activate.error` with `config-bundle-integrity-failed`, make no changes. (2) Compare candidate `bundleSha256` against the currently active bundle record; if identical, this is a no-op re-activation (return immediately). (3) Verify resource type schema fingerprints (from bundle private fields) against committed schemas at `packages/d2b-contracts/src/v3/schemas/.json`; any mismatch fails the entire bundle closed with `config-schema-mismatch`, emits error, makes no changes. (4) Verify Provider schema fingerprints against installed Provider schemas; any mismatch fails the entire bundle closed with `provider-schema-mismatch`, emits error, makes no changes. (5) Verify `catalogSha256` binding against `/etc/d2b/artifact-catalog.json`; mismatch fails bundle closed. (6) Fetch current `metadata.managedBy="configuration"` resources via ResourceClient List (one call per type). (7) Compute `GenerationDiff` (new/changed/unchanged/removed) by type+name key. (8) Submit Create, UpdateSpec, and Delete intents concurrently with bounded async concurrency (default max 32 in-flight); activation returns after all intents are durably queued by the resource store, without waiting for reconcile loops to complete. For unchanged specs: issue UpdateConfigGeneration to refresh `metadata.configurationGeneration` to the new generation number; no controller reconcile triggered. For new resources: core sets `metadata.managedBy="configuration"` and `metadata.configurationGeneration=` on Create; if a same-name resource already exists with `metadata.managedBy="controller"` or `metadata.managedBy="api"`, record a per-item `config-collision` error for that resource without seizing it, emit error, continue other intents. For changed specs: submit UpdateSpec with `expectedRevision`; retry on optimistic lock conflict. For removed resources: submit Delete; set `metadata.deletionRequestedAt`. (9) Set Zone `phase=Pending` while create/update intents are outstanding. (10) Return after durable queue commit; do not block on reconcile completion. (11) The cleanup controller watches ResourceClient Watch streams for `Deleted` revision events (not polling GET) for each pending-cleanup resource by type+name+expectedRevision. When all finalizers release, the resource store commits the `Deleted` revision event atomically with row and index removal in a single transaction; following this commit, the audit subsystem appends the deletion audit record using a dedup/exactly-once recovery key (audit is NOT part of the atomic store transaction). Zone transitions to `phase=Degraded` immediately whenever any pending-cleanup item is outstanding. When all pending-cleanup items receive `Deleted` Watch events, `pendingCleanup` empties and Zone transitions to `phase=Ready`. Cleanup-stuck threshold: 10 min default; configurable; stuck resources remain Degraded without blocking later activations. Prior generation retention: controller retains the N most recently activated, cleanup-complete bundle records (default N=3; range 1..16; no time-based TTL). | | Integration | Fixed process in `packages/d2b-core-controller`; ResourceClient (Create/UpdateSpec/UpdateConfigGeneration/UpdateStatus/Delete/List/Watch) per ADR046-exec-022; Zone resource status UpdateStatus (configGeneration, pendingCleanup, lastActivatedAt, lastActivationError); Zone `phase` transitions (Pending while outstanding intents; Degraded immediately when cleanup remains; Ready when create/update complete and cleanup empty); audit segment per ADR046-exec-014 audit events table | @@ -2565,17 +2565,17 @@ A work item whose `Destination` row introduces a new `d2b-provider-*` crate must | Validation | Runtime/integration tests 19–23 from the "Tests for Nix configuration and ResourceType-specific lifecycle" section in this spec; additionally: `GenerationDiff` hermetic unit tests (new/changed/unchanged/removed classification); `bundleSha256` integrity failure aborts and emits correct audit event; `catalogSha256` mismatch aborts bundle; UpdateSpec optimistic lock conflict retried correctly; Watch `Deleted` revision events consumed (not polling GET) to track cleanup completion; Zone `phase=Pending` while intents outstanding; Zone `phase=Degraded` immediately when any cleanup outstanding (no grace window); Zone `phase=Ready` when complete; activation returns after durable queue commit, not after reconcile; same-name `managedBy=controller` OR `managedBy=api` collision emits per-item `config-collision` error without seizing resource, other intents continue; unchanged spec refreshes `configurationGeneration` without triggering controller reconcile; final deletion: atomic tx commits `Deleted` revision event + row/index removal only; audit append follows committed revision via dedup/exactly-once recovery (NOT part of atomic tx); recovery retry produces no duplicate audit record; prior bundle record released after cleanup-complete and retention count exceeded; activation with zero diff and identical bundleSha256 is a no-op | | Removal proof | None — net-new controller; no prior owner to remove | -### ADR046-user-session-001 +### ADR046-exec-024 | Field | Value | | --- | --- | -| Work item ID | `ADR046-user-session-001` | +| Work item ID | `ADR046-exec-024` | | Dependency/owner | ADR046-zone-control-019 (authority index); `Provider/system-systemd` (user manager) + core/user-agent owner | | Current source | None — the fixed user-session authority is today ambient prose across the display/audio/clipboard/notification/secret-service dossiers; no named owner exists | | Reuse source | `Provider/system-systemd` user-manager scope; D077 EffectPort/LaunchTicket FD handoff | -| Reuse action | net-new (name and implement the shared user-session authority) | +| Reuse action | adapt | | Destination | `packages/d2b-core-controller/src/user_session_authority.rs` (or a core/user-agent per-session agent Process under `Provider/system-systemd`); `AuthorityDescriptor` on the session authority | -| Detailed design | Name and implement the **fixed user-session authority** (D097 desktop/session): `authorityScope: seat` bound to `(Host, User, login-session/seat)`, opaque `authorityKey` (never a raw socket path/XDG_RUNTIME_DIR/DISPLAY/seat name), `cardinality: exactly-one` per `(Host, User, login-session)`, `arbitration: exclusive`, owner = a core/user-agent per-user-session agent Process (NOT a new Provider), adoption by `ownerProof` (agent Process identity + login-session id), `exportability: forbidden`. It is the sole opener of the compositor/PipeWire/session-bus FDs and hands them to desktop Providers only via the EffectPort/LaunchTicket. Core's authority index rejects a duplicate session authority (or a duplicate same-user display portal, clipboard host, notification sink, audio mediator, systemd user manager, Secret Service, or seat-input claimant) with `duplicateConflict` before any FD open; multi-user/seat is admitted only up to the declared per-Host limit. Guest-stop invalidates every session authority/lease bound to that Guest across display/audio/notification/credential/shell in one dependency-aware cascade (D091), with no stale FD surviving. Host input (`wl_seat`/pointer constraints) is an `at-most-one`-per-seat authority under this session authority; pointer-constraint enforcement is a declared boundary until an interaction Provider implements it. | +| Detailed design | Name and implement the **fixed user-session authority** (D097 desktop/session): `authorityScope: seat` bound to `(Host, User, login-session/seat)`, opaque `authorityKey` (never a raw socket path/XDG_RUNTIME_DIR/DISPLAY/seat name), `cardinality: exactly-one` per `(Host, User, login-session)`, `arbitration: exclusive`, owner = a core/user-agent per-user-session agent Process (NOT a new Provider), adoption by `ownerProof` (agent Process identity + login-session id), `exportability: forbidden`. It is the sole opener of the compositor/PipeWire/session-bus FDs and hands them to desktop Providers only via the EffectPort/LaunchTicket. Core's authority index rejects a duplicate session authority (or a duplicate same-user display portal, clipboard host, notification sink, audio mediator, systemd user manager, Secret Service, or seat-input claimant) with `duplicateConflict` before any FD open; multi-user/seat is admitted only up to the declared per-Host limit. Guest-stop invalidates every session authority/lease bound to that Guest across display/audio/notification/credential/shell in one dependency-aware cascade (D091), with no stale FD surviving. Host input (`wl_seat`/pointer constraints) is an `at-most-one`-per-seat authority under this session authority; pointer-constraint enforcement is a declared boundary until an interaction Provider implements it. Primary reuse disposition: `adapt`. Preserved source-plan detail: net-new (name and implement the shared user-session authority). | | Integration | Core authority index (ADR046-zone-control-019); `Provider/system-systemd` user manager; display/audio/clipboard/notification/secret-service/shell Provider services bind to this single authority for their FDs; D091 Guest-stop cascade | | Data migration | None — full d2b 3.0 reset | | Validation | Single session authority per `(Host, User, session)`; duplicate same-user session authority / desktop service rejected with `duplicateConflict`; multi-seat declared-limit enforcement; Guest-stop invalidates all bound desktop/audio/notification/credential/shell authorities and leases (no stale compositor/PipeWire/session-bus FD); seat-input second claimant rejected; adoption by `ownerProof` and quarantine on ambiguity; hermetic with fakes | @@ -2603,9 +2603,9 @@ integration point. | Dependency/owner | ADR046-exec-001; `d2b-session` v3 crate owner | | Current source | No pre-ADR45 v3 baseline equivalent; ADR-only | | Main reuse source | main `a1cc0b2d` `packages/d2b-session/src/`: `driver.rs` (`ComponentSessionDriver` trait, `SessionDriverHandle`); `engine.rs` (`SessionEngine`, `SessionEvent`); `handshake.rs` (`NoiseHandshake`, `EstablishedHandshake`, `HandshakeRole`, `HandshakeCredentials`, `NegotiatedOffer`, all encode/decode/negotiate functions, generation-discovery functions, `GENERATION_DISCOVERY_REQUEST_LEN`, `GENERATION_DISCOVERY_RESPONSE_LEN`); `lifecycle.rs` (`SessionLifecycle`, `SessionPhase`, `KeepaliveAction`); `record.rs` (`ProtectedRecord`, `RecordProtector`); `fragmentation.rs` (`Fragment`, `Fragmenter`, `Reassembler`); `scheduler.rs` (`FairScheduler`, `OutboundFrame`, `QueueClass`); `streams.rs` (`NamedStreamMux`, `StreamEvent`, `StreamId`, `StreamPhase`); `cancellation.rs` (`Cancellation`, `RequestRegistry`); `deadline.rs` (`DeadlineBudget`); `bootstrap.rs` (`BootstrapPsk`, `AdmittedBootstrapPsk`, `BootstrapAdmission`, `Secret32`); `attachment.rs` (`AttachmentPayload` trait, `AttachmentValidationError`, `OwnedAttachment`); `transport.rs` (`OwnedTransport` trait, `TransportDescriptor`, `TransportError`, `TransportPacket`); `metrics.rs` (`MetricEvent`, `MetricsSink`, `NoopMetrics`); `server.rs` (`serve_ttrpc_services`, `SessionServerError`); `error.rs` (`Result`, `SessionError`); `tests/component_session.rs` (all 10+ tests: fixed negotiation, Noise profiles, record protection, fragmentation, deadline, cancellation, lifecycle/keepalive/reconnect, named stream credit/fairness, bootstrap, transport portability); `tests/noise_vectors.rs` | -| Reuse action | copy verbatim; rename crate from `d2b-session` to `d2b-bus-session` or retain name | +| Reuse action | adapt | | Destination | `packages/d2b-bus-session/src/`: all above modules verbatim; `packages/d2b-bus-session/tests/`: all above tests verbatim | -| Detailed design | The entire `d2b-session` portable ComponentSession runtime is transport-agnostic and contains no ADR 0045 realm-specific types. All Noise handshake parameters, record framing, fragmentation, named stream mux, fair scheduler, cancellation, deadlines, bootstrap PSK, and attachment bindings are directly reusable. `ComponentSessionDriver` trait and `serve_ttrpc_services` are the primary integration surface for every v3 bus service. The `sessions/lib.rs` re-export boundary (`pub use d2b_contracts::v2_component_session as contract`) must be updated to point at the v3 wire contract module. | +| Detailed design | The entire `d2b-session` portable ComponentSession runtime is transport-agnostic and contains no ADR 0045 realm-specific types. All Noise handshake parameters, record framing, fragmentation, named stream mux, fair scheduler, cancellation, deadlines, bootstrap PSK, and attachment bindings are directly reusable. `ComponentSessionDriver` trait and `serve_ttrpc_services` are the primary integration surface for every v3 bus service. The `sessions/lib.rs` re-export boundary (`pub use d2b_contracts::v2_component_session as contract`) must be updated to point at the v3 wire contract module. Primary reuse disposition: `adapt`. Preserved source-plan detail: copy verbatim; rename crate from `d2b-session` to `d2b-bus-session` or retain name. | | Integration | All v3 bus service implementations (`d2b-zone-service`, `d2b-provider-agent`, `d2b-bus-client`) depend on this crate; EphemeralProcess attach service; Process Provider launch ticket channel | | Data migration | Full d2b 3.0 reset; no v2 state/config import | | Validation | Copy tests verbatim; all tests must pass on v3 baseline without modification; re-run `tests/noise_vectors.rs` golden vectors; add one v3-specific test: endpoint policy identity uses v3 `ZoneId`/zone-name binding rather than ADR45 `RealmId` | @@ -2620,9 +2620,9 @@ integration point. | Dependency/owner | ADR046-exec-016; `d2b-bus-session-unix` crate owner | | Current source | `packages/d2b-session-unix/src/` at pre-ADR45 baseline `b5ddbed6` (partially equivalent: `d2b-session-unix` existed with `d2b-session-unix/src/adapter.rs` `UnixSeqpacketTransport`, `d2b-session-unix/src/descriptor.rs`, `d2b-session-unix/src/pidfd.rs`); main commit extends with credit pools, full pidfd identity verifier, and `host-socket` feature gate | | Main reuse source | main `a1cc0b2d` `packages/d2b-session-unix/src/`: `adapter.rs` (`UnixSeqpacketTransport`, `UnixStreamTransport`, `UnixAttachmentPayload`, `OwnedUnixAttachment`, `PeerIdentityPolicy`, `DescriptorPolicyResolver`, `PathnamePeerVerifier`); `credit.rs` (`CreditPool`, `CreditScope`, `CreditScopeSet`, `CreditBundle`, `CreditError`, `ProcessCreditLimit`); `descriptor.rs` (`ReceivedPacket`, `VerifiedPacket`, `AcceptedAttachment`, `DescriptorPolicy`, `FirstPacketCredentials`, `ObjectIdentity`, `PeerCredentials`); `pidfd.rs` (`PidfdEvidence`, `PidfdIdentityVerifier`, `PidfdInfoSource`, `ProcPidfdIdentityVerifier`, `ProcSelfFdInfoSource`, `DigestEvidenceCallback`, `parse_pidfd_fdinfo`); `socket.rs`, `systemd.rs`, `vsock.rs`; `error.rs` (`UnixSessionError`); `tests/unix_session.rs` (20+ tests: seqpacket/stream transport, attachment transfer, SO_PEERCRED credential probing, pidfd identity verification, credit pool, full end-to-end session engine) | -| Reuse action | copy verbatim; rename crate from `d2b-session-unix` to `d2b-bus-session-unix` or retain name | +| Reuse action | adapt | | Destination | `packages/d2b-bus-session-unix/src/`: all above modules verbatim; `packages/d2b-bus-session-unix/tests/`: all above tests verbatim | -| Detailed design | Provides the Linux-specific `OwnedTransport` implementation for Unix seqpacket and stream sockets. `CreditPool`/`ProcessCreditLimit` enforces per-scope FD attachment budget (ADR45 constants: `MAX_PROCESS_ATTACHMENT_CREDITS = 2048`, `MAX_HOST_ATTACHMENT_CREDITS = 8192`, `RESERVED_CONTROL_FDS = 64`). `PidfdIdentityVerifier` provides the `/proc//fdinfo/` parse path that Process controllers use to verify process identity before `pidfd_open(2)` — this is a direct dependency of system-systemd and system-minijail Process Providers (ADR046-exec-006, ADR046-exec-007). | +| Detailed design | Provides the Linux-specific `OwnedTransport` implementation for Unix seqpacket and stream sockets. `CreditPool`/`ProcessCreditLimit` enforces per-scope FD attachment budget (ADR45 constants: `MAX_PROCESS_ATTACHMENT_CREDITS = 2048`, `MAX_HOST_ATTACHMENT_CREDITS = 8192`, `RESERVED_CONTROL_FDS = 64`). `PidfdIdentityVerifier` provides the `/proc//fdinfo/` parse path that Process controllers use to verify process identity before `pidfd_open(2)` — this is a direct dependency of system-systemd and system-minijail Process Providers (ADR046-exec-006, ADR046-exec-007). Primary reuse disposition: `adapt`. Preserved source-plan detail: copy verbatim; rename crate from `d2b-session-unix` to `d2b-bus-session-unix` or retain name. | | Integration | v3 Zone runtime public socket listener; system-minijail/system-systemd pidfd identity verification; EphemeralProcess attach named stream | | Data migration | Full d2b 3.0 reset; no v2 state/config import | | Validation | Copy all 20+ tests verbatim; all must pass; add v3-specific test: `PathnamePeerVerifier` verifies against `d2b-zonert` daemon uid; credit pool per-Zone-runtime limits match v3 constants | @@ -2637,9 +2637,9 @@ integration point. | Dependency/owner | ADR046-exec-016; `d2b-bus-wire` contract owner | | Current source | No pre-ADR45 v3 baseline equivalent for v3 bus wire types; ADR-only | | Main reuse source | main `a1cc0b2d` `packages/d2b-contracts/src/v2_component_session.rs`: all protocol constants (`PREFACE_MAGIC`, `COMPONENT_SESSION_MAJOR=2`, `COMPONENT_SESSION_MINOR=0`, `MAX_HANDSHAKE_OFFER_BYTES`, `HANDSHAKE_OFFER_CANONICAL_LEN`, `MAX_PROTECTED_CIPHERTEXT_BYTES`, `NOISE_TAG_BYTES`, `RECORD_LENGTH_BYTES`, `MAX_LOGICAL_MESSAGE_BYTES=1MiB`, `MAX_ACTIVE_NAMED_STREAMS=128`, `MAX_PACKET_ATTACHMENTS=32`, `MAX_REQUEST_ATTACHMENTS=64`, `MAX_OPERATION_ATTACHMENTS=128`, `MAX_SESSION_ATTACHMENTS=256`, `MAX_PROCESS_ATTACHMENT_CREDITS=2048`, `MAX_HOST_ATTACHMENT_CREDITS=8192`, `RESERVED_CONTROL_FDS=64`, `MAX_NAMED_STREAM_QUEUE_BYTES=256KiB`, `MAX_AGGREGATE_NAMED_STREAM_QUEUE_BYTES=4MiB`, `MAX_TTRPC_CONTROL_QUEUE_BYTES=2MiB`, `MAX_SESSION_CONTROL_QUEUE_BYTES=64KiB`, `MAX_CLOCK_SKEW_MS=30000`, `MAX_REQUEST_LIFETIME_MS=900000`, `LOCAL_HANDSHAKE_DEADLINE_MS=5000`, `REMOTE_HANDSHAKE_DEADLINE_MS=15000`, `LOCAL_RECONNECT_DEADLINE_MS=5000`, `REMOTE_RECONNECT_DEADLINE_MS=30000`, `MAX_RECONNECT_ATTEMPTS=10`, `MAX_RECONNECT_WINDOW_MS=300000`, `MAX_KEEPALIVE_INTERVAL_MS=60000`, `MAX_KEEPALIVE_TIMEOUT_MS=30000`, `RECORD_HEADER_LEN=24`, `FRAGMENT_HEADER_LEN=24`, `GUEST_SESSION_CREDENTIAL_MAGIC`, `GUEST_SESSION_CREDENTIAL_SCHEMA_VERSION`); `AttachmentDescriptor`; `LimitProfile`; `EndpointRole`; `PurposeClass`; `Locality`; `ServicePackage`; `RequestId`; `SessionErrorCode`; `BoundedVec`; `EndpointPolicy`; `EndpointPolicyIdentity`; `CancelRequest`/`CancelAck`/`CancelResult` | -| Reuse action | copy and adapt | +| Reuse action | adapt | | Destination | `packages/d2b-bus-wire/src/session.rs`: v3 bus protocol constants and wire types; all numeric constants copied verbatim; `PREFACE_MAGIC` retained; `EndpointPolicy` and `EndpointPolicyIdentity` adapted to use v3 `ZoneId`/`ProviderId` instead of ADR45 `RealmId` in the policy identity fingerprint | -| Detailed design | All numeric constants (frame sizes, credit limits, deadline values, reconnect limits) are directly reusable without change — they are derived from protocol analysis, not from realm semantics. `LimitProfile::local_default()` is the source for `serve_ttrpc_services` capacity; retain the value. `EndpointPolicy` carries the Noise static key and schema fingerprint; the fingerprint computation does not embed realm names and is reusable. The `EndpointPolicyIdentity` type carries the zone runtime's static public key — update from ADR45 `RealmId` to v3 `ZoneId` string encoding. | +| Detailed design | All numeric constants (frame sizes, credit limits, deadline values, reconnect limits) are directly reusable without change — they are derived from protocol analysis, not from realm semantics. `LimitProfile::local_default()` is the source for `serve_ttrpc_services` capacity; retain the value. `EndpointPolicy` carries the Noise static key and schema fingerprint; the fingerprint computation does not embed realm names and is reusable. The `EndpointPolicyIdentity` type carries the zone runtime's static public key — update from ADR45 `RealmId` to v3 `ZoneId` string encoding. Primary reuse disposition: `adapt`. Preserved source-plan detail: copy and adapt. | | Integration | `d2b-bus-session` imports constants from here; all v3 bus service and client crates import protocol constants from `d2b-bus-wire` | | Data migration | Full d2b 3.0 reset; no existing constants module state to migrate | | Validation | Compile-time assertions on all copied numeric constants matching source values; `EndpointPolicyIdentity` golden-vector test with v3 zone name encoding; `LimitProfile::local_default()` round-trip test | @@ -2654,9 +2654,9 @@ integration point. | Dependency/owner | ADR046-exec-016 + ADR046-exec-018; `d2b-provider-runtime` crate owner | | Current source | `packages/d2b-realm-provider/src/provider.rs` at baseline `b5ddbed6` (`HostSubstrateProvider`, `RuntimeProvider`, `WorkloadProvider` traits — baseline, unwired); ADR-only for the runtime registry and RPC proxy | | Main reuse source | main `a1cc0b2d` `packages/d2b-provider/src/`: `registry.rs` (`ProviderRegistry`, `ProviderRegistryBuilder`, `ProviderRegistryManager`, `RegistryLimits`, `AdmittedProvider`, `InFlightPermit`, `AdmissionOptions`); `rpc.rs` (`SessionIdentity`, `ProviderClock`, `SystemProviderClock`, `RpcOperation`, `RpcPayload`, `RpcCall`, `RpcResponse`, `AuthenticatedProviderRpc` trait, `RpcProviderProxy`); `instance.rs` (`ProviderInstance`, `ProviderFactory`, `provider_capabilities_are_dispatchable`, `provider_inspection_method`, `provider_method_is_dispatchable`); `context.rs` (`OwnedOperationContext`, `CancellationToken`); `error.rs` (`FactoryError`, `ProviderRuntimeError`, `RegistryBuildError`, `RegistryShutdownReport`); `lib.rs` re-exports; re-exports from `d2b_contracts::v2_provider` (all Provider trait objects: `RuntimeProvider`, `StorageProvider`, `NetworkProvider`, `DeviceProvider`, `CredentialProvider`, `AudioProvider`, `DisplayProvider`, `InfrastructureProvider`, `ObservabilityProvider`, `SubstrateProvider`, `TransportProvider`) | -| Reuse action | copy and adapt | +| Reuse action | adapt | | Destination | `packages/d2b-provider-runtime/src/`: `registry.rs`, `rpc.rs`, `instance.rs`, `context.rs`, `error.rs`; provider trait objects moved to `d2b-bus-wire` or `d2b-provider-contracts` | -| Detailed design | `ProviderRegistry` manages in-flight permits, draining, and provider lifecycle. `RegistryLimits { total_in_flight, per_provider_in_flight }` is directly reusable. `RpcProviderProxy` wraps a `ComponentSessionDriver` and dispatches typed `RpcCall` to an `AuthenticatedProviderRpc` implementation — the proxy pattern is fully reusable for v3 Provider resource controllers. `SessionIdentity { peer_role, service, provider_id, provider_type, provider_generation }` maps directly to a v3 Provider session credential. `InFlightPermit` RAII guard is directly reusable. The provider trait object set (`RuntimeProvider`, `StorageProvider`, etc.) adapts to v3 Provider resource typed methods; the trait hierarchy is preserved but `ProviderMethod` enum variant names may be renamed to drop ADR45 workload terminology. `ProviderRegistry::MAX_PROVIDER_REGISTRY_ENTRIES` bound from v2_provider is retained. | +| Detailed design | `ProviderRegistry` manages in-flight permits, draining, and provider lifecycle. `RegistryLimits { total_in_flight, per_provider_in_flight }` is directly reusable. `RpcProviderProxy` wraps a `ComponentSessionDriver` and dispatches typed `RpcCall` to an `AuthenticatedProviderRpc` implementation — the proxy pattern is fully reusable for v3 Provider resource controllers. `SessionIdentity { peer_role, service, provider_id, provider_type, provider_generation }` maps directly to a v3 Provider session credential. `InFlightPermit` RAII guard is directly reusable. The provider trait object set (`RuntimeProvider`, `StorageProvider`, etc.) adapts to v3 Provider resource typed methods; the trait hierarchy is preserved but `ProviderMethod` enum variant names may be renamed to drop ADR45 workload terminology. `ProviderRegistry::MAX_PROVIDER_REGISTRY_ENTRIES` bound from v2_provider is retained. Primary reuse disposition: `adapt`. Preserved source-plan detail: copy and adapt. | | Integration | Zone runtime provider-agent ComponentSession; system-core Provider controller; every v3 Provider resource controller | | Data migration | Full d2b 3.0 reset; no v2 state/config import | | Validation | Copy `d2b-provider` tests; `RegistryLimits::validate` enforces non-zero and per≤total; `InFlightPermit` RAII release; drain/retire state machine; `RpcProviderProxy` round-trip over `FakeProvider` (from toolkit ADR046-exec-020) | @@ -2672,9 +2672,9 @@ integration point. | Current source | `packages/d2b-realm-provider/src/conformance.rs` at baseline `b5ddbed6` (`check_provider_conformance`); ADR-only for toolkit server and fixture | | Main reuse source | main `a1cc0b2d` `packages/d2b-provider-toolkit/src/`: `adapter.rs` (`ProviderAgentAdapter` — wraps `ComponentSessionDriver` + `ProviderRegistry` into a session-driven dispatch loop); `server.rs` (`GeneratedProviderServiceServer` — wires generated ttrpc service stubs to `ProviderRegistry` dispatch); `conformance.rs` (`ConformanceError`, `check_descriptor_conformance`, `check_provider_conformance` — five conformance checks: Descriptor, CapabilityPublication, FixtureMismatch, Provider failure, ObservabilityQueryResult); `fixture.rs` (`DeterministicClock`, `FakeProvider`, `Fixture`, `sample_lease_request` — hermetic test fixture with all Provider trait implementations returning deterministic results); `redaction.rs` (`Redacted`, `Secret` — zero-copy redaction wrappers for audit/log outputs); `registration.rs` (`register_exact_instances`, `ToolkitError` — exact-instance registry validation); `values.rs` (`ProviderValues`); `lib.rs` re-exports | | Main reuse source (provider-agent) | main `a1cc0b2d` `packages/d2b-gateway-runtime/src/provider_agent.rs`: `ProviderAgentProcess` (entry point for a provider-agent process: accepts a pre-registered `ProviderRegistry` + established `ComponentSessionDriver`, runs `MAX_DISPATCH_IN_FLIGHT=64` concurrent dispatch, bounded audit ring `DEFAULT_AUDIT_CAPACITY=1024`, shutdown within `SHUTDOWN_TIMEOUT=5s`); `run_registered`, `run`; `ProviderAgentError`, `ProviderAgentAuditEvent`, `ProviderAgentAuditOutcome` | -| Reuse action | copy and adapt | +| Reuse action | adapt | | Destination | `packages/d2b-provider-toolkit/src/`: retain all modules verbatim; adapt `ProviderAgentAdapter` to use v3 `ProviderRegistry` (ADR046-exec-019) and v3 bus wire types; adapt `GeneratedProviderServiceServer` to use v3 generated service stubs (ADR046-exec-021); `packages/d2b-provider-agent/src/`: adapted from `gateway-runtime/src/provider_agent.rs` | -| Detailed design | `ProviderAgentAdapter` is the core: it drives a `ComponentSessionDriver` receive loop, dispatches decoded ttrpc frames to `ProviderRegistry`, and forwards responses. `GeneratedProviderServiceServer` closes the loop by registering all generated service stubs with `serve_ttrpc_services`. `FakeProvider` implements every v2 Provider trait with deterministic outputs — adapt each trait method to v3 Provider resource semantics while retaining the fixture pattern. `Redacted` / `Secret` zero-copy wrappers are used in every audit log path; copy verbatim. Provider conformance check pattern is retained: descriptor validation, capability publication, fixture round-trip, observability query result. | +| Detailed design | `ProviderAgentAdapter` is the core: it drives a `ComponentSessionDriver` receive loop, dispatches decoded ttrpc frames to `ProviderRegistry`, and forwards responses. `GeneratedProviderServiceServer` closes the loop by registering all generated service stubs with `serve_ttrpc_services`. `FakeProvider` implements every v2 Provider trait with deterministic outputs — adapt each trait method to v3 Provider resource semantics while retaining the fixture pattern. `Redacted` / `Secret` zero-copy wrappers are used in every audit log path; copy verbatim. Provider conformance check pattern is retained: descriptor validation, capability publication, fixture round-trip, observability query result. Primary reuse disposition: `adapt`. Preserved source-plan detail: copy and adapt. | | Integration | Every v3 Provider resource controller uses `ProviderAgentAdapter` + `ProviderAgentProcess`; conformance tests gate Provider dossier acceptance; `FakeProvider` is used in all Provider controller hermetic tests | | Data migration | Full d2b 3.0 reset; no v2 state/config import | | Validation | Copy conformance tests verbatim; `check_descriptor_conformance` passes on a `FakeProvider` descriptor; `check_provider_conformance` covers all five `ConformanceError` variants; `ProviderAgentProcess` shutdown within deadline test; `MAX_DISPATCH_IN_FLIGHT` semaphore back-pressure test | @@ -2689,9 +2689,9 @@ integration point. | Dependency/owner | ADR046-exec-016 + ADR046-exec-018; v3 Zone service contract owner | | Current source | No pre-ADR45 v3 baseline equivalent; ADR-only | | Main reuse source | main `a1cc0b2d` `packages/d2b-contracts/src/generated_v2_services/`: all ttrpc stub files (`daemon.rs`, `daemon_ttrpc.rs`, `guest.rs`, `guest_ttrpc.rs`, `realm.rs`, `realm_ttrpc.rs`, `activation.rs`, `broker.rs`, `clipboard.rs`, `provider_storage.rs`, `provider_substrate.rs`, `provider_transport.rs`, `runtime_systemd_user.rs`, `security_key.rs`, `shell.rs`, `terminal.rs`, `tty.rs`, `user.rs`, `wayland.rs` and their `_ttrpc.rs` counterparts); `v2_services.rs` (`StrictWireMessage` trait, `MethodSpec`, `ServiceSpec`, `ServiceInventoryDocument`, `ServiceDocument`, `service_inventory_document`, `service_schema_fingerprint`, `public_daemon_schema_fingerprint`, `direct_guest_schema_fingerprint`, `ServerStreamLease`, `TerminalStreamValidator`, `TerminalFrameDirection`, `server_stream_name`/`parse_server_stream_name`, `admit_metadata`, `validate_terminal_open_response_for_request`, `validate_spawn_response_for_request`, `decode_spawn_response_for_request`, `validate_provider_response_for_method`); `v2_guest_services.rs`, `v2_guest_configured_launches.rs` (for guest exec message types); `d2bd/src/control_services/daemon.rs` (`DaemonServiceV2`, `DaemonOperationHandler` trait, `DaemonCallContext`, `DaemonMethod` enum, `DaemonPeerRole`, `DaemonAdapter`, `daemon_endpoint_policy`, `daemon_channel_binding`, `DaemonSeqpacketTransport`); `d2bd/src/control_services/provider.rs`, `guest.rs`, `realm.rs`, `allocator.rs`, `broker.rs` — service handler skeletons | -| Reuse action | adapt; v3 generates new protobuf definitions and new ttrpc stubs; reuse message shaping, method dispatch patterns, and service inventory pattern | +| Reuse action | adapt | | Destination | `packages/d2b-bus-contracts/src/generated_v3_services/`: v3 generated ttrpc stubs for Zone service methods (Resource CRUD, Watch, ComponentSession service verbs); `packages/d2b-zone-service/src/`: Zone runtime service handler adapted from `DaemonServiceV2` pattern; `packages/d2b-zone-service/src/admission.rs`, `handler.rs`, `routing.rs` | -| Detailed design | `StrictWireMessage` trait (decode_strict, encode_strict) is directly reusable — it enforces deny-unknown-fields decode and schema-pinned fingerprint validation. `ServiceInventoryDocument` / `service_schema_fingerprint` pattern provides the service schema publication mechanism that v3 Provider resources use to advertise their ComponentSession service interface. `DaemonServiceV2` / `DaemonOperationHandler` pattern becomes the v3 Zone service handler base: `DaemonCallContext` → v3 `ZoneCallContext` with `ZoneId`, principal `User/`, operation deadline; `DaemonMethod` enum → v3 `ZoneMethod` (ResourceGet, ResourceList, ResourceWatch, ResourceCreate, ResourceUpdateSpec, ResourceUpdateStatus, ResourceDelete, BusAttach); `daemon_endpoint_policy` → v3 zone endpoint policy with v3 `ZoneId`-bound static key and schema fingerprint. `server_stream_name`/`parse_server_stream_name` for Watch stream naming is reusable verbatim. `TerminalStreamValidator` / `ServerStreamLease` for terminal byte stream safety is reusable for EphemeralProcess attach. | +| Detailed design | `StrictWireMessage` trait (decode_strict, encode_strict) is directly reusable — it enforces deny-unknown-fields decode and schema-pinned fingerprint validation. `ServiceInventoryDocument` / `service_schema_fingerprint` pattern provides the service schema publication mechanism that v3 Provider resources use to advertise their ComponentSession service interface. `DaemonServiceV2` / `DaemonOperationHandler` pattern becomes the v3 Zone service handler base: `DaemonCallContext` → v3 `ZoneCallContext` with `ZoneId`, principal `User/`, operation deadline; `DaemonMethod` enum → v3 `ZoneMethod` (ResourceGet, ResourceList, ResourceWatch, ResourceCreate, ResourceUpdateSpec, ResourceUpdateStatus, ResourceDelete, BusAttach); `daemon_endpoint_policy` → v3 zone endpoint policy with v3 `ZoneId`-bound static key and schema fingerprint. `server_stream_name`/`parse_server_stream_name` for Watch stream naming is reusable verbatim. `TerminalStreamValidator` / `ServerStreamLease` for terminal byte stream safety is reusable for EphemeralProcess attach. Primary reuse disposition: `adapt`. Preserved source-plan detail: adapt; v3 generates new protobuf definitions and new ttrpc stubs; reuse message shaping, method dispatch patterns, and service inventory pattern. | | Integration | Zone runtime ttrpc service over public socket ComponentSession; Provider resource controller attaches via `RpcProviderProxy`; ResourceClient Watch uses server stream naming convention | | Data migration | Full d2b 3.0 reset; no v2 state/config import | | Validation | `StrictWireMessage` decode rejects unknown fields on all v3 message types; `service_schema_fingerprint` is stable across builds; `ZoneCallContext` deadline enforcement test; `DaemonSeqpacketTransport` → `ZoneSeqpacketTransport` end-to-end roundtrip; generated v3 stub compile check | @@ -2707,9 +2707,9 @@ integration point. | Current source | No pre-ADR45 v3 baseline equivalent; ADR-only | | Main reuse source | main `a1cc0b2d` `packages/d2b-client/src/`: `client.rs` (`Client` with `RetryPolicy`, `CallOptions`, `CancellationToken`, `MetadataInput`, `ConnectedClient`, `Response`, `WallClock`, `SystemClock`); `session.rs` (`ConnectedSession`, `SessionCall`, `SessionReply`, `NamedStream`, `SharedDriver`, `ComponentSessionConnector` trait, `SessionFailure`); `service.rs` (`ServiceHandle`, `MethodHandle`, `GeneratedClient`, `ServiceKind`); `daemon_service.rs` (`DaemonClient`, `DaemonLifecycleRequest`, `DaemonMethod`, `DaemonTerminal`, `daemon_call_options`); `guest_service.rs` (`GuestClient`, `GuestOperation`, `GuestCancelCall`, `GuestInspectCall`, `GuestRetainedLogCall`); `host_socket.rs` (`HostSocketConnector`, `local_daemon_endpoint_identity`); `target.rs` (`TargetResolver` trait, `RouteTable`, `ResolvedTarget`, `RouteRecord`, `TargetInput`, `TransportKind`, `TransportSelection`, `ServiceOwner`); `error.rs` (`ClientError`, `RemoteErrorKind`, `RetryClass`); `tests/client.rs` | | Main reuse source (daemon-access) | main `a1cc0b2d` `packages/d2b-daemon-access/src/component_session.rs`: `LocalDaemonSession` (one authenticated local daemon session wrapping `DaemonClient`; connection via `connect_component_session` using `HostSocketConnector` + `local_daemon_endpoint_identity` + peer uid verification against `d2bd` uid); `connect_seqpacket` (blocking connect helper) | -| Reuse action | copy and adapt | +| Reuse action | adapt | | Destination | `packages/d2b-bus-client/src/`: all above modules; `DaemonClient` → `ZoneClient` (v3 Resource CRUD/Watch verbs); `GuestClient` → `ProcessAttachClient`; `HostSocketConnector` → `ZoneSocketConnector`; `LocalDaemonSession` → `LocalZoneSession` | -| Detailed design | `Client` is the core typed async client with bounded retry, wall-clock injection, and cancellation. `ConnectedSession` wraps a `ComponentSessionDriver` and provides `call()`, `open_stream()`, and `close()`. `ComponentSessionConnector` trait decouples connection establishment from the client — v3 `ZoneSocketConnector` implements this for the local Zone runtime public socket. `TargetResolver`/`RouteTable` provides request routing to local vs remote Zone runtimes. `HostSocketConnector::local_daemon_endpoint_identity` provides the peer identity pinning that prevents MITM on the local socket — this is a security-critical invariant; copy verbatim, rename from `d2bd` to `d2b-zonert` uid. `DaemonClient` method table adapts to v3 Resource verbs: `ResourceGet`, `ResourceList`, `ResourceWatch` (streaming), `ResourceCreate`, `ResourceUpdateSpec`, `ResourceUpdateStatus`, `ResourceDelete`. `ServiceHandle`/`MethodHandle`/`GeneratedClient` provide the typed client stub generation pattern. `RetryPolicy`/`RetryClass`/`RemoteErrorKind` error classification is directly reusable. | +| Detailed design | `Client` is the core typed async client with bounded retry, wall-clock injection, and cancellation. `ConnectedSession` wraps a `ComponentSessionDriver` and provides `call()`, `open_stream()`, and `close()`. `ComponentSessionConnector` trait decouples connection establishment from the client — v3 `ZoneSocketConnector` implements this for the local Zone runtime public socket. `TargetResolver`/`RouteTable` provides request routing to local vs remote Zone runtimes. `HostSocketConnector::local_daemon_endpoint_identity` provides the peer identity pinning that prevents MITM on the local socket — this is a security-critical invariant; copy verbatim, rename from `d2bd` to `d2b-zonert` uid. `DaemonClient` method table adapts to v3 Resource verbs: `ResourceGet`, `ResourceList`, `ResourceWatch` (streaming), `ResourceCreate`, `ResourceUpdateSpec`, `ResourceUpdateStatus`, `ResourceDelete`. `ServiceHandle`/`MethodHandle`/`GeneratedClient` provide the typed client stub generation pattern. `RetryPolicy`/`RetryClass`/`RemoteErrorKind` error classification is directly reusable. Primary reuse disposition: `adapt`. Preserved source-plan detail: copy and adapt. | | Integration | CLI (`d2b` binary), external Zone API callers, Process/EphemeralProcess controller ResourceClient, all consumer-facing API paths | | Data migration | Full d2b 3.0 reset; no v2 state/config import | | Validation | Copy `tests/client.rs` verbatim; add v3-specific tests: `ResourceWatch` streaming teardown; `ZoneSocketConnector` peer-uid mismatch rejection; retry policy respects `RetryClass::Transient`/`Permanent`; `TargetInput`→`ResolvedTarget` for local-only v3 Zone; `local_daemon_endpoint_identity` returns correct v3 zone-rt uid | diff --git a/docs/specs/ADR-046-resources-network.md b/docs/specs/ADR-046-resources-network.md index 77e486404..120dc3f22 100644 --- a/docs/specs/ADR-046-resources-network.md +++ b/docs/specs/ADR-046-resources-network.md @@ -2376,9 +2376,9 @@ bridges at reconcile time). | Dependency/owner | W0 shared contract root; `d2b-contracts` | | Current source | `packages/d2b-core/src/host.rs` lines 290–520 (`NetEnv`, `IfName`, `ExternalNetworkPolicy`, `NftablesModel`, `BridgePortFlags`, `TapRole`, `Ipv6SysctlEntry`, `IfNameMapping` lines 242–256; **also** `VmRuntimeRow` lines 155–167 with `tap`/`bridge`/`net_vm`/`env` fields — attachment status precursors); `packages/d2b-core/src/processes.rs` lines 98–141 (`ProcessNetworkInterface`, `ProcessNetworkInterfaceType`, `ProcessMacvtapInterface` — current VMM runner network interface DTOs; these are per-Guest VMM fields, not Network-level fields, and migrate to Guest spec under `Provider/runtime-cloud-hypervisor`); `packages/d2b-contracts/src/broker_wire.rs` (authoritative broker op list; network-relevant: `ApplyNftables`, `ApplyNmUnmanaged`, `ApplyRoute`, `ApplySysctl`, `SetBridgePortFlags`, `UpdateHostsFile`, `SeedDnsmasqLease`, `CreatePersistentTap`, `CreateTapFd`); `nixos-modules/lib.nix` lines 396–460 (`subnetIp`, `subnetMask`, `mkMac`, `cidrOverlaps`) | | Reuse source | None from main; all from v3 baseline | -| Reuse action | extract and adapt | +| Reuse action | adapt | | Destination | `packages/d2b-contracts/src/v3/network.rs`: NetworkSpec, NetworkStatus, AttachmentSpec, AttachmentStatus, ExternalAttachmentSpec, ExternalAttachmentStatus, PortForwardSpec, NetworkConditionType; `packages/d2b-contracts/src/v3/ifname.rs`: IfName newtype, derivation, collision detection (extracted from `d2b-host/src/ifname.rs`). Also defines `User/net-local-controller` as a proper Resource with explicit lifecycle: `Provider/network-local`'s Nix package/module provisions the reserved `net-local-controller` OS account with a private fixed UID/GID in Host prerequisites and in the generic net-VM nixos-system artifact (same account, same UID/GID inside the Guest); the network-local controller creates and owns the User Resource (`spec.osUsername: net-local-controller`, `ownerRef: Provider/network-local`, `managedBy: controller`); `Provider/system-core` verifies the account via NSS lookup and reconciles the User Resource to Ready — it does not provision the OS account. No numeric UID/GID enters any ResourceSpec field, authz check, or audit record; `User.status` MAY carry diagnostic `uid`/`gid` values discovered by NSS lookup, but those are informational only and are never authorization inputs. The network-local controller waits for `User/net-local-controller` to reach `Ready` before creating any config Volume (reconcile precondition, not a bootstrap side effect). | -| Detailed design | Strict ResourceEnvelope with Network-specific spec/status. IfName newtype: IFNAMSIZ-1 validated, FNV-1a 64-bit derivation, base32 Crockford, 8-char suffix, bridge/tap role prefixes, detect_collisions over IfNameMapping slice. cidrOverlaps: pure Rust IPv4 arithmetic, same algorithm as lib.nix. NetworkSpec validators: /24 lanCidr with .0 base, /30 uplinkCidr, unique attachment indices 2–250, default hostBlocklist enforcement. | +| Detailed design | Strict ResourceEnvelope with Network-specific spec/status. IfName newtype: IFNAMSIZ-1 validated, FNV-1a 64-bit derivation, base32 Crockford, 8-char suffix, bridge/tap role prefixes, detect_collisions over IfNameMapping slice. cidrOverlaps: pure Rust IPv4 arithmetic, same algorithm as lib.nix. NetworkSpec validators: /24 lanCidr with .0 base, /30 uplinkCidr, unique attachment indices 2–250, default hostBlocklist enforcement. Primary reuse disposition: `adapt`. Preserved source-plan detail: extract and adapt. | | Integration | Provider dossiers, Nix resource compiler, resource store/API bind these canonical types | | Data migration | Full d2b 3.0 reset; no env→Network import | | Validation | Golden JSON/CBOR vectors; CIDR overlap property tests; IfName collision and derivation determinism tests; default hostBlocklist enforcement; attachment index uniqueness; `User/net-local-controller` User resource lifecycle/readiness test: controller creates User Resource with `spec.osUsername = "net-local-controller"` (`ownerRef: Provider/network-local`); controller waits for User resource to reach `Ready` before proceeding; controller aborts with `ConfigVolumeReady=False/user-not-ready` if User resource is not Ready; verifies no numeric UID/GID appears in the Resource spec, authz check, or audit record; verifies that any diagnostic `uid`/`gid` in `User.status` is never used as an authorization input | @@ -2391,9 +2391,9 @@ bridges at reconcile time). | Work item ID | `ADR046-network-002` | | Dependency/owner | ADR046-network-001; d2b-host network modules owner | | Current source | `packages/d2b-host/src/ifname.rs` (FNV-1a derivation, detect_collisions, DEFAULT_PREFIX, BRIDGE_TAG, TAP_TAG); `packages/d2b-host/src/bridge_port.rs` (BridgePortReadback, east-west policy, TapRole defaults); `packages/d2b-host/src/nftables.rs` (NftBatch, hash_inet_d2b_table, coexistence policy); `packages/d2b-host/src/routes.rs` (route/dnsmasq-bound/IPv6 preflight); `packages/d2b-host/src/netlink.rs` (IPv6 sysctl sequence) | -| Reuse action | extract (ifname, bridge_port, nftables) into shared network-local Provider library; adapt (routes, netlink) into controller observe loop | +| Reuse action | adapt | | Destination | `packages/d2b-provider-network-local/src/ifname.rs`, `bridge_port.rs`, `nftables.rs`, `routes.rs`, `netlink.rs` | -| Detailed design | Move IfName derivation to `d2b-contracts` (ADR046-network-001); keep bridge_port, nftables, routes, netlink in Provider crate. nftables: retain the Network-owned `inet d2b` chain layout, ownership markers, and coexistence matrix; emit no USBIP/TCP-3240 rule and compute drift over only the Network UID ownership projection. routes: adapt dnsmasq-bound check to use Network status instead of `HostJson.environments`. netlink: keep IPv6-off sequence; add defense-in-depth re-application path. | +| Detailed design | Move IfName derivation to `d2b-contracts` (ADR046-network-001); keep bridge_port, nftables, routes, netlink in Provider crate. nftables: retain the Network-owned `inet d2b` chain layout, ownership markers, and coexistence matrix; emit no USBIP/TCP-3240 rule and compute drift over only the Network UID ownership projection. routes: adapt dnsmasq-bound check to use Network status instead of `HostJson.environments`. netlink: keep IPv6-off sequence; add defense-in-depth re-application path. Primary reuse disposition: `adapt`. Preserved source-plan detail: extract (ifname, bridge_port, nftables) into shared network-local Provider library; adapt (routes, netlink) into controller observe loop. | | Integration | Controller observe loop uses nftables digest drift, bridge_port readback, and IPv6 sysctl to drive `FirewallReady`, `FabricReady` conditions | | Data migration | None (behavior preserved; host bridge names change from `br--*` to `d2b-b` after cutover) | | Validation | Existing `bridge_port::tests::readback_matches_defaults`, `ops::tap::tests::set_bridge_port_flags_readback_drift_fails_closed`, `netlink::tests::ipv6_off_sequence_runs_in_order`, nftables coexistence matrix tests; all pinned in `tests/golden/pinned/host-prepare-network.txt` and `tests/golden/pinned/net-canaries.txt` | @@ -2466,9 +2466,9 @@ bridges at reconcile time). | Work item ID | `ADR046-network-007` | | Dependency/owner | ADR046-network-005; device-usbip Provider dossier; D-NETWORK-002 resolved | | Current source | `nixos-modules/network.nix` lines 444–461 (USBIP host firewall); `packages/d2b-core/src/host.rs` lines 324–328 (usbip_backend_port, usbip_busid_locks in NetEnv); `packages/d2b-host/src/` usbip_argv.rs | -| Reuse action | extract and adapt | +| Reuse action | adapt | | Destination | `Provider/device-usbip` owns one relay Process/Endpoint authority per Network plus the typed EffectPort adapter for `UsbipBindFirewallRule`. The controller watches only the `networkRef` resource's identity/readiness/generation; Core privately resolves Network UID to relay attachment and firewall intent. Network spec/status is not mutated with USBIP fields. Full crate layout required for `packages/d2b-provider-device-usbip/` (see [Package and crate boundary](#package-and-crate-boundary)): `src/` (controller and usbip runner + unit tests), `tests/` (hermetic conformance, dependency-watch state machine, UsbipBindFirewallRule round-trip), `integration/` (Host/Guest USBIP attach/detach lifecycle fixtures), `README.md` (Provider identity, provider-neutral USB Service/Binding types, USBIP Processes/Endpoints, Network least-privilege dependency contract, RBAC, security invariants, standalone-repo path). | -| Detailed design | Device-usbip's typed EffectPort is the sole semantic owner of every USBIP TCP/3240 rule. Its Core adapter resolves the opaque per-Network/per-busid intent and issues `UsbipBindFirewallRule`; its strict provider status owns firewall digest/drift. Network-local emits no generic host or net-VM TCP/3240 allow and ignores device-usbip ownership markers in Network drift. The device Provider owns exactly one multiplexed relay Endpoint authority per Network and supplies Binding proxies only authorized connected streams through LaunchTickets. | +| Detailed design | Device-usbip's typed EffectPort is the sole semantic owner of every USBIP TCP/3240 rule. Its Core adapter resolves the opaque per-Network/per-busid intent and issues `UsbipBindFirewallRule`; its strict provider status owns firewall digest/drift. Network-local emits no generic host or net-VM TCP/3240 allow and ignores device-usbip ownership markers in Network drift. The device Provider owns exactly one multiplexed relay Endpoint authority per Network and supplies Binding proxies only authorized connected streams through LaunchTickets. Primary reuse disposition: `adapt`. Preserved source-plan detail: extract and adapt. | | Integration | device-usbip watches Network readiness → Core adapter resolves opaque Network attachment → `UsbipBindFirewallRule` + one relay Endpoint authority → Binding proxy LaunchTicket | | Data migration | Current network.nix USBIP carve-out replaced by UsbipBindFirewallRule broker op | | Validation | device-usbip conformance tests cover exact per-Network/per-busid scoping, one relay Endpoint authority, ownership-scoped drift/status, foreign-marker rejection, and release; network-local nftables tests assert no TCP/3240/USBIP rule on host or net VM and prove USBIP rule churn does not change Network `FirewallReady`; the pinned USBIP firewall golden moves to device-usbip ownership | @@ -2481,7 +2481,7 @@ bridges at reconcile time). | Work item ID | `ADR046-network-008` | | Dependency/owner | ADR046-network-004, ADR046-network-005; Zone runtime integrator | | Current source | No current v3 source: generation lifecycle and `managedBy`/`configurationGeneration` classification do not exist on the pre-ADR45 v3 baseline. The v3 baseline uses NixOS activation scripts that atomically replace all host JSON artifacts; there is no per-resource generation tracking, no async cleanup queue, and no `managedBy` field. | -| Reuse action | new | +| Reuse action | create | | Destination | `packages/d2b-core-controller/src/configuration.rs`: bundle application, diff, generation-transition logic (including per-item name-conflict handling), prior-bundle retention under `/var/lib/d2b/zones//configuration/prior/`; `packages/d2b-core-controller/src/cleanup.rs`: removal scheduling and `PendingCleanup` condition tracking; `packages/d2b-contracts/src/generation_bundle.rs`: `ZoneBundle`/`BundleResource`/`BundleMetadata` **input** DTOs — MUST NOT include `managedBy` or `configurationGeneration` (both are persisted resource metadata set by core at activation, not bundle input fields); `ManagedBy` closed enum `{ Configuration, Controller, Api }` and `configurationGeneration: u64` live in `packages/d2b-core-controller/src/resource_store.rs` as persisted resource metadata fields; `nixos-modules/resources-network.nix` (emits bundle with `managedBy`/`configurationGeneration` absent; core sets both at activation per ADR046-network-004); `d2b.zones..retainedGenerations` Nix/compiler-level Zone option (outside `Zone.spec`; default `3`, range `1..16`); `tests/unit/nix/cases/generation-cleanup-absent-network.nix`; `packages/d2b-contracts/tests/generation_bundle.rs`; `tests/host-integration/nix-generation-cleanup.nix` | | Detailed design | **Core generation tracking** (`packages/d2b-core-controller/src/configuration.rs`): core maintains a monotone `configurationGeneration` counter per Zone in its durable state. On each bundle application it compares the incoming `contentHash` against the prior applied hash. If different, it increments `configurationGeneration`, sets `managedBy = "configuration"` and the new counter value on each resource in the bundle, and performs the resource diff (create/update/delete scheduling). The `managedBy` and `configurationGeneration` fields are absent from the Nix-emitted bundle and are set exclusively by core at activation time. **`managedBy` field and per-item name-conflict handling**: `ManagedBy` is a closed enum (`Configuration`, `Controller`, `Api`) persisted in resource metadata at `packages/d2b-core-controller/src/resource_store.rs`. It is NOT a field in `ZoneBundle`/`BundleResource` input DTOs; core sets it at activation. Controllers set `ManagedBy::Controller` when creating owned children (net-VM Guest, config Volume, guest-agent Process, mDNS Processes); exact controller identity/UID/generation are tracked in separate internal metadata, not embedded in the `managedBy` value. API-created resources carry `ManagedBy::Api` and persist until explicit delete with no bundle-driven lifecycle. Core's generation-transition logic only schedules bundle-driven Delete for `ManagedBy::Configuration` resources. **Per-item name-conflict handling**: when a bundle item's `(zone, name)` already exists with `managedBy ≠ "configuration"`, core skips that item and records it with `phase = Degraded, reason: name-conflict`; a `ResourceConflictSkipped` audit record is emitted for that item. All non-conflicting items in the bundle proceed normally (Provider-state contract). The existing resource is left completely untouched. The operator deletes the conflicting resource via the resource API; the next bundle application applies the item. **Removal scheduling**: on generation N+1 activation, core performs a set difference: `prev_configuration_managed - new_configuration_managed` = resources to delete. For each, it sets `metadata.deletionRequestedAt` in the resource store and emits a `ResourceDeletionScheduled` audit record. Normal finalizer-path Delete proceeds asynchronously. **`PendingCleanup` condition**: the Zone self resource carries a `PendingCleanup = True` condition while any `managedBy = Configuration` resource has `deletionRequestedAt` set and has not yet been atomically removed. Aggregate Zone `phase = Degraded` applies. The condition transitions to `False` and Zone phase returns to `Ready` when all scheduled deletions complete. **Prior generation bundle retention** (`cleanup.rs`): count-based (`d2b.zones..retainedGenerations`, outside `Zone.spec`, default 3, range 1..16); no TTL. Core copies prior bundles to `/var/lib/d2b/zones//configuration/prior/.json`. A generation is eligible for pruning when all configuration-managed resources from it have either been atomically removed or are present unchanged in a newer generation, AND the count would be exceeded. **`BundleActivated` audit record**: emitted at each generation transition with `contentHash`, `configurationGeneration`, `resourceCount`, and `providerSchemaDigests` map (digests from `type=provider` artifacts via `Provider.spec.artifactId`); no spec contents, CIDRs, or resource names appear in the record. Provider schema digests in the bundle are re-verified against installed Provider artifact digests at application time; a mismatch aborts application with a `BundleRejected` audit record. | | Integration | ADR046-network-004 (emitter writes bundle format; core sets `managedBy`/`configurationGeneration` at activation) → ADR046-network-008 (runtime reads and applies). ADR046-network-005 (controller Delete path) is invoked by ADR046-network-008 removal scheduling for Network resources. Zone `PendingCleanup` condition and `Degraded` phase are read by CLI `d2b zone status`. | @@ -2496,9 +2496,9 @@ bridges at reconcile time). | Work item ID | `ADR046-network-009` | | Dependency/owner | D097 Host-global authority index; ADR046-network-001, ADR046-network-005; Provider/network-local and Core authority owners | | Current source | Existing macvtap spawn path resolves `parentInterface` but has no cross-Zone authority admission or compatible-sharing contract | -| Reuse action | adapt the existing private macvtap-FD spawn path; add authority admission before it | +| Reuse action | adapt | | Destination | `packages/d2b-contracts/src/v3/network.rs` external-attachment sharing schema/status; `packages/d2b-core-controller/src/authority.rs` Core-derived physical-NIC identity and Host-global claim; Provider/network-local descriptor/reconcile/finalizer | -| Detailed design | Resolve operator-declared `parentInterface` against trusted Host inventory and derive an opaque `external-physical-nic/v1` digest; index `(Host, external-physical-nic, opaqueKeyDigest)` before any macvtap/VMM effect. `passthru`, `private`, and `vepa` are exclusive. `bridge` defaults exclusive and is multiplexed only under explicitly authored compatible policy. Use typed `external-physical-nic-conflict`; expose only bounded authority availability/holder-count/queue/arbitration/update-currency and conditions; keep digest, interface identity, and owner proof private. Parent/mode/policy update drains and releases the old claim before replacement; deletion closes macvtap/VMM ownership before releasing the claim; restart adopts exact owner proof and quarantines ambiguity. | +| Detailed design | Resolve operator-declared `parentInterface` against trusted Host inventory and derive an opaque `external-physical-nic/v1` digest; index `(Host, external-physical-nic, opaqueKeyDigest)` before any macvtap/VMM effect. `passthru`, `private`, and `vepa` are exclusive. `bridge` defaults exclusive and is multiplexed only under explicitly authored compatible policy. Use typed `external-physical-nic-conflict`; expose only bounded authority availability/holder-count/queue/arbitration/update-currency and conditions; keep digest, interface identity, and owner proof private. Parent/mode/policy update drains and releases the old claim before replacement; deletion closes macvtap/VMM ownership before releasing the claim; restart adopts exact owner proof and quarantines ambiguity. Primary reuse disposition: `adapt`. Preserved source-plan detail: adapt the existing private macvtap-FD spawn path; add authority admission before it. | | Integration | Network validation and Core authority preflight gate runtime-cloud-hypervisor LaunchTicket/`SpawnRunner`; the finalizer and D091 update planner release in dependency order | | Data migration | Full d2b 3.0 reset; no authority ledger import | | Validation | Hermetic authority tests cover same-Zone and cross-Zone exclusive collisions, mixed-policy conflicts, non-bridge multiplex rejection, explicit compatible bridge multiplex admission, Core-derived key equality for two selectors resolving to one fake NIC, caller-supplied digest rejection, no-effect conflict, owner-proof adoption/ambiguity, disruptive update, and release-after-close ordering. Nix eval covers schema and declared cross-Zone conflicts; host integration covers create/update/delete with a fake macvtap parent and status/condition transitions without raw identity exposure. | diff --git a/docs/specs/ADR-046-resources-volume.md b/docs/specs/ADR-046-resources-volume.md index 8e1699b56..121977f4d 100644 --- a/docs/specs/ADR-046-resources-volume.md +++ b/docs/specs/ADR-046-resources-volume.md @@ -1630,9 +1630,9 @@ audit record. | --- | --- | | Dependency/owner | W0 shared contract root; `d2b-contracts` | | Current source | `packages/d2b-core/src/storage.rs`, `sync.rs`; `nixos-modules/storage-json.nix` | -| Reuse action | extract and adapt | +| Reuse action | adapt | | Destination | `packages/d2b-contracts/src/v3/volume.rs`, `volume_layout.rs`, `volume_attachment.rs` | -| Detailed design | Complete Volume ResourceSpec, LayoutEntry, all policy enums (values preserved from baseline), AclGrant, ViewSpec, AttachmentSpec, quota placeholder, strict serde unknown-field rejection, canonicalization, bounds | +| Detailed design | Complete Volume ResourceSpec, LayoutEntry, all policy enums (values preserved from baseline), AclGrant, ViewSpec, AttachmentSpec, quota placeholder, strict serde unknown-field rejection, canonicalization, bounds Primary reuse disposition: `adapt`. Preserved source-plan detail: extract and adapt. | | Integration | Provider dossiers/controller descriptors bind exact types; Nix resource compiler emits canonical JSON | | Data migration | Full d2b 3.0 reset; storage.json rows migrated per table above | | Validation | Golden JSON spec vectors; serde unknown-field; path anchor/depth/traversal validators; ACL grant bounds; policy enum coverage | @@ -1644,9 +1644,9 @@ audit record. | --- | --- | | Dependency/owner | ADR046-volume-001; volume-local Provider owner | | Current source | `nixos-modules/storage-json.nix`, `nixos-modules/store.nix`, `packages/d2b-priv-broker/src/ops/swtpm_dir.rs`, `packages/d2b-priv-broker/src/ops/storage_contract.rs` (`reconcile_storage_scope`, `validate_lock_spec`), `packages/d2b-priv-broker/src/ops/store_sync.rs` (`run_store_sync`, `run_store_sync_repair`, `cleanup_store_view`, `prune_gcroots`), `packages/d2b-priv-broker/src/ops/store_sync_audit.rs`, `packages/d2b-priv-broker/src/ops/store_sync_export.rs`, `packages/d2b-priv-broker/src/ops/store_view_posture.rs` (`posture_store_view_matrix_paths`, `plant_live_marker_with_matrix_posture`), `packages/d2b-priv-broker/src/ops/state_dir.rs` (`PrepareStateDir`, `PrepareRuntimeDir`), `packages/d2b-host/src/hardlink_farm.rs` (`build_store_view`, `GenerationMarker`, `gcroots_dir`, `state_dir`), `packages/d2b-core/src/storage_lifecycle.rs` (`StorageLifecycleReport`, `StorageLifecycleIssue`), `packages/d2b-contract-tests/tests/storage_sync_contracts.rs`, `packages/d2bd/src/ownership_preflight.rs` | -| Reuse action | extract and adapt | +| Reuse action | adapt | | Destination | `packages/d2b-provider-volume-local/src/` (layout engine, store_view.rs, swtpm_volume.rs, broker op adapters); `packages/d2b-provider-volume-local/tests/` (hermetic layout/store-view/swtpm tests); `packages/d2b-provider-volume-local/integration/` (container Host-path and store-view FS fixtures); `packages/d2b-provider-volume-local/README.md` | -| Detailed design | volume-local controller: layout engine (provision/repair/cleanup/adopt per policy), store-view mode (hardlink farm from `hardlink_farm.rs`, private-NS sync, zero-length marker, `gcroots/` and `state/` at store-view root, `sync.lock` OFD), swtpm volume hardening (provisionIfNeverProvisioned + marker + fail-closed repair as in `swtpm_dir.rs`), path-free broker audit ops, storage lifecycle report, opaque BundleOpId contract preserved from `storage_contract.rs` | +| Detailed design | volume-local controller: layout engine (provision/repair/cleanup/adopt per policy), store-view mode (hardlink farm from `hardlink_farm.rs`, private-NS sync, zero-length marker, `gcroots/` and `state/` at store-view root, `sync.lock` OFD), swtpm volume hardening (provisionIfNeverProvisioned + marker + fail-closed repair as in `swtpm_dir.rs`), path-free broker audit ops, storage lifecycle report, opaque BundleOpId contract preserved from `storage_contract.rs` Primary reuse disposition: `adapt`. Preserved source-plan detail: extract and adapt. | | Integration | volume-local controller registered under Host/system-core; resource status written via ResourceClient | | Data migration | Per-VM `storage.json` rows (scoped `"vm:"` → `ownerRef: Guest/`) replaced by Volume resources generated by Nix resource compiler; broker continues to own path operations | | Validation | `tests/unit/nix/cases/per-vm-state-ownership.nix` adapted to Volume LayoutEntry matrix; `tests/unit/smoke/smoke-eval-tpm.nix` migrated to TPM Volume invariant; `d2b-contract-tests/tests/storage_sync_contracts.rs` parity tests adapted; new: store-view same-filesystem, zero-length marker existence, sync.lock preserve-OFD, gcroots at store-view root (not meta/), state/ dir existence, swtpm fail-closed-on-missing-after-provision, anchored-path validators | @@ -1658,9 +1658,9 @@ audit record. | --- | --- | | Dependency/owner | ADR046-volume-001; volume-virtiofs Provider owner | | Current source | `packages/d2b-host/src/virtiofsd_argv.rs` (`VirtiofsdArgvInput`, `generate_virtiofsd_argv`), `nixos-modules/minijail-profiles.nix` (virtiofsdProfiles; principals `d2b--runner`, `d2b--gctlfs`), `nixos-modules/processes-json.nix` (virtiofsdRunner shape; `roStoreSharedDir` sentinel), `packages/d2b-core/src/processes.rs` (`ProcessRole::Virtiofsd`, `VmProcessDag`; the virtiofsd dag node is a `ProcessRole::Virtiofsd` entry in a WorkloadId-keyed `VmProcessDag`), `packages/d2b-priv-broker/src/ops/spawn_runner.rs` (`SpawnRunnerPlan` for virtiofsd; current `SpawnRunnerPlanInput` carries `adr_carve_out` for virtiofsd swtpm path), `packages/d2b-priv-broker/src/sys.rs` (clone3/user-NS pre-establishment), ADR 0021 | -| Reuse action | extract and adapt | +| Reuse action | adapt | | Destination | `packages/d2b-provider-volume-virtiofs/src/` (controller, virtiofsd_argv.rs); `packages/d2b-provider-volume-virtiofs/tests/` (hermetic argv/lifecycle/ADR-0021 tests); `packages/d2b-provider-volume-virtiofs/integration/` (virtiofsd launch and guest-mount fixtures); `packages/d2b-provider-volume-virtiofs/README.md` | -| Detailed design | volume-virtiofs controller: attachment lifecycle, owned virtiofsd Process create/update/delete, argv generation (reuse current 14 tests), ADR 0021 invariant (`capabilityClasses: []`, `startRoot: false`, `sandbox: chroot`, user-NS via `userNamespace.mappingClass: process-principal-root`), per-attachment export socket readiness check (`unix-socket-exists` readiness kind; current v2 socket path: `/run/d2b/vms//-virtiofs-.sock`; v3: stable hash-derived private path under Zone runtime directory, never exposed in spec/status/API), guest-mount status observation, finalizer drain | +| Detailed design | volume-virtiofs controller: attachment lifecycle, owned virtiofsd Process create/update/delete, argv generation (reuse current 14 tests), ADR 0021 invariant (`capabilityClasses: []`, `startRoot: false`, `sandbox: chroot`, user-NS via `userNamespace.mappingClass: process-principal-root`), per-attachment export socket readiness check (`unix-socket-exists` readiness kind; current v2 socket path: `/run/d2b/vms//-virtiofs-.sock`; v3: stable hash-derived private path under Zone runtime directory, never exposed in spec/status/API), guest-mount status observation, finalizer drain Primary reuse disposition: `adapt`. Preserved source-plan detail: extract and adapt. | | Integration | volume-virtiofs registered under Host; virtiofsd Process resource owned by Volume; guest-control health integration for mount readiness | | Data migration | Current `processes-json.nix` virtiofsd `VmProcessDag` nodes (keyed by `WorkloadId` = current VM name, role `ProcessRole::Virtiofsd`) replaced by virtiofsd Process resources owned by Volume | | Validation | Migrated `virtiofsd_argv` unit tests (14 tests); `tests/tools/gen-migration-ledger.sh` virtiofsd-argv-shape gate adapted; `minijail-validator-virtiofsd` gate adapted to Process sandbox spec; new: attachment lifecycle (create/ready/delete), ADR 0021 invariant rejection test, multi-attachment isolation, readOnly flag per access mode, store-view shared-dir = store-view/live (never /nix/store) | @@ -1686,7 +1686,7 @@ audit record. | --- | --- | | Dependency/owner | ADR046-volume-002, ADR046-volume-003; respective Provider owners | | Current source | N/A (no baseline evidence for block-image, quota enforcement, snapshots, or tmpfs Volume paths) | -| Reuse action | new | +| Reuse action | create | | Destination | `packages/d2b-provider-volume-local/src/` (block-image, quota, snapshots, tmpfs, ACL reconciliation); `packages/d2b-provider-volume-local/tests/` (hermetic quota/tmpfs/ACL/block-image/snapshot tests); `packages/d2b-provider-volume-local/integration/` (block-image virtio-blk, FS-without-quota fixture, tmpfs memory-budget); `packages/d2b-provider-volume-virtiofs/src/` (single-writer enforcement, shared-write, private socket path contract); `packages/d2b-provider-volume-virtiofs/tests/` (single-writer rejection, shared-write capability gate, socket-path invariant); `packages/d2b-provider-volume-virtiofs/integration/` (shared-write cross-Guest fixture) | | Detailed design | (1) **block-image SourceKind**: add `SourceKind::BlockImage` to volume-local; manage raw/qcow2 image file lifecycle; emit virtio-blk attachment spec consumed by Guest Provider; `quota.maxBytes` required; add store-overlay.img migration path for current `DiskInit` plan-op. (2) **Quota hard enforcement**: implement `enforcement: hard` capability check in volume-local at Volume creation time; query backing FS for quota/limits support; test with no-project-quota fixture; enforce `maxBytes`/`maxInodes` via xfs project quota or ext4 per-dir quota where available. (3) **Volume snapshots/migrations**: design and implement EphemeralProcess templates in volume-local catalog for snapshot (copy-on-write or rsync capture) and content migration (atomic rename + sync); no CLI-only path; all operations surface through resource API. (4) **Single-writer enforcement**: volume-virtiofs controller tracks active `read-write` attachment count; rejects a second `read-write` attachment while one is active (returns `ResourceConflict` error); `shared-write` mode accepted only if Provider declares `supportsSharedWrite: true`. (5) **tmpfs source**: implement tmpfs mount/unmount lifecycle in volume-local; `maxBytes` → `size=`, `maxInodes` → `nr_inodes=` mount options; charge memory against Host/Guest budget; cleanup unmounts on Volume deletion or restart. (6) **Bounds enforcement**: enforce max 1024 layout entries, 64 Views, 64 attachments at schema validation layer; add corresponding row to API request-size limit table in `ADR-046-resource-api-and-authorization`. (7) **File/symlink first-class lifecycle**: implement independent `createPolicy`/`repairPolicy`/`cleanupPolicy` for `file` and `symlink` entries; implement `target` field validation (relative, no `..`, must resolve within Volume root); `symlink` create writes the target link. (8) **ACL principal ResourceRef**: remove bare `{type,ref}` struct from AclGrant; implement `User/` ResourceRef resolution with User resource watch and re-reconcile on User revision change. (9) **Continuous ACL reconciliation**: implement `foreignChildPolicy: preserve|fail` in broker reconcile loop; re-apply `accessAcl`/`defaultAcl` to all existing entries and children on every repair cycle; emit `ForeignAclViolation` condition when `foreignChildPolicy: fail` and unexpected entries found. (10) **virtiofsd socket path contract**: implement stable hash-derived private socket path in volume-virtiofs (deterministic hash of Zone name + Volume name + attachment executionRef); assert path never appears in public status, spec, audit, or CLI output; validate with a dedicated security invariant test. | | Integration | Each sub-item produces a focused spec amendment; resolved decisions already reflected in spec revision 2 | @@ -1700,7 +1700,7 @@ audit record. | --- | --- | | Dependency/owner | ADR046-volume-001, ADR046-volume-004; Nix integrator and core-controller config-publication owner | | Current source | `nixos-modules/storage-json.nix` (current Nix eval-time validation via `lib.asserts`; schema checked by `d2b-contract-tests/tests/storage_sync_contracts.rs`); `packages/d2bd/src/` (current config activation / host-prepare dispatch) | -| Reuse action | new | +| Reuse action | create | | Destination | `nixos-modules/resources-volume.nix` (Nix eval-time schema validation, canonical JSON emission, bundle digest); `packages/d2b-core-controller/src/configuration.rs` (config-publication handler cleanup logic); `packages/d2b-contracts/src/v3/zone_bundle.rs` (bundle index schema) | | Detailed design | **Nix eval/build validation**: implement all 15 validation steps in §Nix eval/build validation as Nix assertions; abort build on any failure with structured error (Volume name + field path + error class); provider-specific settings schema (`root-config.schema.json`, `attachment.schema.json`) read from the private artifact catalog entry for each Provider's `artifactId` by the resource compiler; validate against `lib.evalModules`-compatible schema; emit canonical sorted JSON with all defaults materialized; compute SHA-256 per resource; emit Zone resource bundle with `generationId` and `bundleDigest`. **Config-publication handler cleanup**: on new bundle activation, diff resources with `metadata.managedBy = "configuration"` between new and prior bundle; issue async Delete for resources absent from new bundle; mark deleted resources with `ConfigurationRemoved` condition; track pending-cleanup set in Zone status; Zone status is `Degraded/PendingCleanup` while prior-generation deletions are in progress; activation is immediate but Zone readiness reflects cleanup completion. **Config-owned vs controller-created distinction**: `metadata.managedBy = "configuration"` is the authoritative marker set by core at activation; the bundle index carries only digests; controller-created resources have `metadata.managedBy = "controller"` and are never touched by the configuration cleanup pass. **Prior generation retention**: retain `priorGenerationCount` prior generations (default 3, range 1..16); no time-based TTL; when count is exceeded prune oldest generation from the store with a tamper-evident audit record. **Generation reactivation**: re-activate any retained prior bundle via `ActivateGeneration` operation; cancel in-flight Deletes for resources being reinstated; issue Deletes for resources added by the aborted new generation. | | Integration | `nixos-modules/default.nix` wires resources-volume.nix; `d2b-core-controller` config-publication handler consumes the bundle; all Volume controllers observe `ConfigurationRemoved` condition and respond to finalizer triggers | diff --git a/docs/specs/ADR-046-resources-zone-control.md b/docs/specs/ADR-046-resources-zone-control.md index fea190bf8..f9d7b2e50 100644 --- a/docs/specs/ADR-046-resources-zone-control.md +++ b/docs/specs/ADR-046-resources-zone-control.md @@ -918,7 +918,7 @@ paths or the workspace/package policy check fails the build: #### 4.8.2 Policy enforcement The workspace policy check (`packages/d2b-contract-tests/tests/policy_provider_crate_layout.rs`, -implemented in work item ADR046-pkg-001) walks every `packages/d2b-provider-*` +implemented in work item ADR046-zone-control-026) walks every `packages/d2b-provider-*` crate directory in the workspace and asserts all four paths exist. A missing `src/`, `tests/`, `integration/`, or `README.md` in any Provider crate is a **hard policy failure** that blocks `make test-policy` (and therefore @@ -4145,9 +4145,9 @@ None of the following exist in baseline: | Work item ID | `ADR046-zone-control-001` | | Dependency/owner | ADR046-object-001 (resource envelope); ADR046-store-001 (redb store); ADR046-identities-001 (types) | | Current source | `packages/d2b-realm-core/src/ids.rs` (`RealmId`, `LABEL_PATTERN = "^[a-z][a-z0-9-]*$"`, `is_label()`, `MAX_ID_LEN = 128`, `IdError` — `implemented-and-reachable`, baseline `b5ddbed6`); `packages/d2b-realm-core/src/realm.rs` (`RealmPath`, `MAX_REALM_LABELS = 16`, `RealmControllerPlacement`, `EntrypointMode` — `implemented-and-reachable`, baseline `b5ddbed6`); Zone resource schema: `ADR-only` | -| Reuse action | extract and adapt (`RealmId`→`ResourceName` validators, `RealmPath` depth bound→ZoneLink depth, `RealmControllerPlacement`→Provider component placement); new (Zone resource schema/store table/handler) | +| Reuse action | adapt | | Destination | `packages/d2b-contracts/src/v3/zone.rs`; `packages/d2b-core-controller/src/zone.rs` | -| Detailed design | Zone ResourceType schema with `spec = {}`; self-resource enforcement; store_meta binding checks; phase/conditions; cardinality-1 enforcement; `zone-drain` finalizer; canonical JSON schema. Nix Zone options include compiler-only `parentZone`: required for every non-root Zone, forbidden on `local-root`, and never emitted into `Zone.spec` | +| Detailed design | Zone ResourceType schema with `spec = {}`; self-resource enforcement; store_meta binding checks; phase/conditions; cardinality-1 enforcement; `zone-drain` finalizer; canonical JSON schema. Nix Zone options include compiler-only `parentZone`: required for every non-root Zone, forbidden on `local-root`, and never emitted into `Zone.spec` Primary reuse disposition: `adapt`. Preserved source-plan detail: extract and adapt (`RealmId`→`ResourceName` validators, `RealmPath` depth bound→ZoneLink depth, `RealmControllerPlacement`→Provider component placement); new (Zone resource schema/store table/handler). | | Integration | Zone runtime open/upgrade verifies `Zone/` self resource; configuration publication handler creates/reconciles Zone; resource API rejects cross-Zone refs | | Data migration | Destructive d2b 3.0 reset; no v2/Realm Zone resource import | | Validation | `zone-self-resource-enforced`, `zone-uid-mismatch-quarantine`, `zone-name-mismatch-rejected`, `zone-cardinality-one`, `zone-cross-zone-ref-rejected`, `zone-owner-rejected`, `zone-deletion-only-on-drain` | @@ -4161,9 +4161,9 @@ None of the following exist in baseline: | Dependency/owner | ADR046-zone-control-001; ADR046-zone-control-018 (ComponentSession) | | Current source | `packages/d2b-realm-router/src/session.rs` (`PeerSession` — `implemented-but-unwired`, baseline `b5ddbed6`; no live call sites in d2bd or d2b — only reachable through `realm_stubs.rs` dead code in d2bd); `packages/d2b-realm-router/src/secure_session.rs` (`SecurePeerSession`, Noise-based — `implemented-but-unwired`); `packages/d2b-realm-router/src/mux_session.rs` (`MuxSession` — `implemented-but-unwired`); `packages/d2b-realm-router/src/session_lifecycle.rs` (`SessionLifecycle`, `SessionPhase` — `implemented-but-unwired`); `packages/d2b-realm-core/src/route_engine.rs` (`RouteTreeEngine`, `admit_advertisement()`, `decide_route()`, `RoutePruneReport` — `implemented-but-unwired`; exported from `d2b_realm_core::lib.rs:122` but zero call sites in live daemon or CLI code, tests only); `packages/d2b-realm-core/src/identity_store.rs` (`RealmIdentityStore`, `EnrollmentRecord`, `ChildKeyPin` — `implemented-and-reachable`); `packages/d2b-realm-core/src/realm.rs` (`RealmPath`, `MAX_REALM_LABELS = 16` — `implemented-and-reachable`); `packages/d2b-realm-core/src/access.rs` (`RealmTransportBinding::{LocalUnixSocket,RemoteRealmTransport,ProviderRealmTransport}` — `implemented-and-reachable` in CLI routing path); ZoneLink resource schema and cursor tables: `ADR-only` | | Reuse source | main `a1cc0b2d`: `packages/d2b-session/src/lifecycle.rs`, `d2b-session-unix/src/adapter.rs` for reconnect/transport precedents | -| Reuse action | extract and adapt (`SecurePeerSession` Noise model → ComponentSession Noise KK; `SessionLifecycle`/`SessionPhase` → ZoneLink session reconnect loop and connection detail fields; `Connecting`/`Established` current evidence phases drive `status.connected` and `status.phase` transitions to `Pending`/`Ready`, not direct phase values; `RouteTreeEngine.decide_route()` → cursor tracking; `RealmIdentityStore` enrollment → ZoneLink child key-pin) | +| Reuse action | adapt | | Destination | `packages/d2b-contracts/src/v3/zone_link.rs`; `packages/d2b-core-controller/src/zone_link.rs` | -| Detailed design | Child-local ZoneLink schema with self-matching `spec.childZoneName` plus same-Zone `spec.transportProviderRef`, `spec.transportSettings`, and `spec.transportCredentials`; at most one uplink resource per non-root Zone and none in local root; child-store cursor/intent state; reconnect loop with exponential backoff; local intent queue (max 256 entries); local child UID change detection; drain finalizer; no reciprocal parent-store resource; compiler-only `parentZone` selects the one allocator owner, while that allocator owns privileged listener/placement/route allocation effects and exposes only a sealed bootstrap/allocation interface | +| Detailed design | Child-local ZoneLink schema with self-matching `spec.childZoneName` plus same-Zone `spec.transportProviderRef`, `spec.transportSettings`, and `spec.transportCredentials`; at most one uplink resource per non-root Zone and none in local root; child-store cursor/intent state; reconnect loop with exponential backoff; local intent queue (max 256 entries); local child UID change detection; drain finalizer; no reciprocal parent-store resource; compiler-only `parentZone` selects the one allocator owner, while that allocator owns privileged listener/placement/route allocation effects and exposes only a sealed bootstrap/allocation interface Primary reuse disposition: `adapt`. Preserved source-plan detail: extract and adapt (`SecurePeerSession` Noise model → ComponentSession Noise KK; `SessionLifecycle`/`SessionPhase` → ZoneLink session reconnect loop and connection detail fields; `Connecting`/`Established` current evidence phases drive `status.connected` and `status.phase` transitions to `Pending`/`Ready`, not direct phase values; `RouteTreeEngine.decide_route()` → cursor tracking; `RealmIdentityStore` enrollment → ZoneLink child key-pin). | | Integration | child core-controller zone_link handler; child redb `zone_link_cursors` table; Nix-compiled `parentZone` bootstrap topology; selected parent allocator and route engine through sealed allocation authority; d2b-bus transport resolver; ComponentSession lifecycle | | Data migration | Destructive reset; no v2 Realm peer migration | | Validation | `zonelink-reconnect-child-uid-change`, `zonelink-disconnect-unknown-phase`, `zonelink-intent-queue-limit`, `zonelink-disabled-no-reconnect`, `zonelink-child-auth-denied-failed`, `zonelink-drain-closes-session`, `zonelink-child-name-matches-store`, `zonelink-one-child-local-uplink`, `zonelink-parent-bootstrap-binding`, `zonelink-parent-has-no-reciprocal-row` | @@ -4177,9 +4177,9 @@ None of the following exist in baseline: | Dependency/owner | ADR046-zone-control-001; ADR046-api-001; ADR046-zone-control-017 | | Current source | `packages/d2b-realm-provider/src/provider.rs` (`HostSubstrateProvider`, `RuntimeProvider`, `WorkloadProvider`, `DurableExecutionProvider`, `InfrastructureProvider`, `NodeProvider` traits — `implemented-and-reachable`, baseline `b5ddbed6`); `packages/d2b-realm-provider/src/capabilities.rs` (`RuntimeCapabilitySet`, `WorkloadCapabilitySet`, `NodeCapabilitySet` — `implemented-and-reachable`); `packages/d2b-realm-provider/src/conformance.rs` (`workload_lists_and_advertises`, `display_fails_closed_when_unsupported` — `implemented-and-reachable`); `packages/d2b-realm-provider/src/error.rs` (`ProviderError`, `ErrorKind`, `RetryHint`, `ProviderDiagnostic` — `implemented-and-reachable`); `packages/d2b-realm-core/src/ids.rs` (`ProviderId`, label-shaped — `implemented-and-reachable`); `packages/d2b-core/src/processes.rs` (`ProcessRole::{CloudHypervisorRunner, Virtiofsd, Swtpm, GpuRenderNode, Audio, Video, QemuMediaRunner, VsockRelay, OtelHostBridge, Usbip}`, `VmProcessDag`, `ProcessNode` — `implemented-and-reachable`); Provider resource schema and API catalog: `ADR-only` | | Reuse source | main `a1cc0b2d`: any `d2b-provider-toolkit` registry/descriptor patterns named in ADR046-zone-control-017 sub-items | -| Reuse action | extract and adapt (`workload_lists_and_advertises`/`display_fails_closed_when_unsupported` conformance behavior; `RuntimeCapabilitySet`/`WorkloadCapabilitySet`/`NodeCapabilitySet` → Provider component `supportedCapabilities`; `ProviderError`/`RetryHint` → Provider lifecycle error schema; `ProviderId` → Provider `metadata.name` validator; `ProcessRole` variants → Provider component type identifiers) | +| Reuse action | adapt | | Destination | `packages/d2b-contracts/src/v3/provider.rs`; `packages/d2b-core-controller/src/provider_lifecycle.rs`; `packages/d2b-core-controller/src/api_catalog.rs` | -| Detailed design | Provider resource schema with all spec fields from §4.3, including resolved component bounds (max 8 controllers, 8 services, 32 worker templates, 16 ResourceTypes per controller); trust/conformance/config validation; component descriptor validation; dependency alias resolution; API binding with permission intersection; lifecycle policies; Nix Provider installation options; Provider crate layout enforcement per §4.8 (see ADR046-pkg-001) | +| Detailed design | Provider resource schema with all spec fields from §4.3, including resolved component bounds (max 8 controllers, 8 services, 32 worker templates, 16 ResourceTypes per controller); trust/conformance/config validation; component descriptor validation; dependency alias resolution; API binding with permission intersection; lifecycle policies; Nix Provider installation options; Provider crate layout enforcement per §4.8 (see ADR046-zone-control-026) Primary reuse disposition: `adapt`. Preserved source-plan detail: extract and adapt (`workload_lists_and_advertises`/`display_fails_closed_when_unsupported` conformance behavior; `RuntimeCapabilitySet`/`WorkloadCapabilitySet`/`NodeCapabilitySet` → Provider component `supportedCapabilities`; `ProviderError`/`RetryHint` → Provider lifecycle error schema; `ProviderId` → Provider `metadata.name` validator; `ProcessRole` variants → Provider component type identifiers). | | Integration | Zone config publication installs Provider resources; API catalog handler binds exported ResourceTypes; Provider/system-core and Provider/system-minijail are bootstrap exceptions with pre-created records | | Data migration | Full reset; Provider packages recompiled and re-registered per new schema | | Validation | All §15.3 Provider tests including the resolved bounds checks | @@ -4192,9 +4192,9 @@ None of the following exist in baseline: | Work item ID | `ADR046-zone-control-004` | | Dependency/owner | ADR046-zone-control-001; ADR046-api-002 | | Current source | `packages/d2bd/src/admission.rs` (`PeerRole::{Admin, Launcher, HostShutdown}`, `PeerIdentity`, `authorize_peer()`, `verb_requires_admin()`, `verb_allowed_for_host_shutdown()` — `implemented-and-reachable`, baseline `b5ddbed6`); `packages/d2b-daemon-access/src/lib.rs` (`LocalUnixAllowlistRole::{Admin, Launcher}`, `DaemonAccessPolicyRole::RealmAdmin`, `DaemonAccessDecision`, `MappedDaemonAccessPrincipal`, `map_local_unix_daemon_access()` — `implemented-and-reachable`); Role resource schema and RBAC index: `ADR-only` | -| Reuse action | extract and adapt (`verb_requires_admin()` verb table → Role verb enum; `DaemonAccessDecision` error types → Role admission errors; `MappedDaemonAccessPrincipal` → subject identity model) | +| Reuse action | adapt | | Destination | `packages/d2b-contracts/src/v3/role.rs`; `packages/d2b-core-controller/src/authz.rs` | -| Detailed design | Role resource schema with rule schema from §5.3 and resolved bounds (32 rules, 16 resourceTypes per rule, 64 resourceNames, 32 executionRefs); verb enum; explicit wildcard enforcement; index builder; phase/conditions; `role-binding-drain` finalizer; Nix Role options; audit/OTEL instrumentation | +| Detailed design | Role resource schema with rule schema from §5.3 and resolved bounds (32 rules, 16 resourceTypes per rule, 64 resourceNames, 32 executionRefs); verb enum; explicit wildcard enforcement; index builder; phase/conditions; `role-binding-drain` finalizer; Nix Role options; audit/OTEL instrumentation Primary reuse disposition: `adapt`. Preserved source-plan detail: extract and adapt (`verb_requires_admin()` verb table → Role verb enum; `DaemonAccessDecision` error types → Role admission errors; `MappedDaemonAccessPrincipal` → subject identity model). | | Integration | Authorization evaluator (ADR046-api-002) reads Role index entries; core `authz` handler owns reconcile loop | | Data migration | Initial Roles generated from Nix config; no v2 Role resource import | | Validation | All §15.4 Role tests including the resolved bounds checks | @@ -4207,9 +4207,9 @@ None of the following exist in baseline: | Work item ID | `ADR046-zone-control-005` | | Dependency/owner | ADR046-zone-control-004 | | Current source | `packages/d2b-daemon-access/src/lib.rs` (`DaemonAccessDecision::Authorized { role: DaemonAccessPolicyRole }`, `DaemonAccessAdmissionSource`, `map_remote_daemon_access()` — `implemented-and-reachable`, baseline `b5ddbed6`; these implement the current coarse binding decision per-connection); RoleBinding resource schema: `ADR-only` | -| Reuse action | new (RoleBinding resource schema/store table/handler); adapt (`DaemonAccessAdmissionSource` identity fields → subject selector; `map_remote_daemon_access()` logic → subject UID-binding behavior) | +| Reuse action | adapt | | Destination | `packages/d2b-contracts/src/v3/role_binding.rs`; `packages/d2b-core-controller/src/authz.rs` (shared with Role handler) | -| Detailed design | RoleBinding resource schema with no expiry field and max 128 subjects; subject resolution and UID binding; external principal selector; scope narrowing intersection; revocation; immediate deletion with cache invalidation; Nix RoleBinding options | +| Detailed design | RoleBinding resource schema with no expiry field and max 128 subjects; subject resolution and UID binding; external principal selector; scope narrowing intersection; revocation; immediate deletion with cache invalidation; Nix RoleBinding options Primary reuse disposition: `adapt`. Preserved source-plan detail: new (RoleBinding resource schema/store table/handler); adapt (`DaemonAccessAdmissionSource` identity fields → subject selector; `map_remote_daemon_access()` logic → subject UID-binding behavior). | | Integration | Subject resolution uses store owner index; authorization evaluator reads combined Role+narrowing entry; revocation/update/delete flow uses the normal resource lifecycle | | Data migration | Initial RoleBindings generated from Nix config | | Validation | All §15.5 RoleBinding tests, including the 128-subject admission bound and no-expiry lifecycle model | @@ -4222,9 +4222,9 @@ None of the following exist in baseline: | Work item ID | `ADR046-zone-control-006` | | Dependency/owner | ADR046-zone-control-004, ADR046-zone-control-005; Zone runtime | | Current source | `packages/d2bd/src/admission.rs` (`authorize_peer()`, `verb_requires_admin()`, `verb_allowed_for_host_shutdown()`, `PeerRole::{Admin, Launcher, HostShutdown}` — `implemented-and-reachable`, baseline `b5ddbed6`); `packages/d2b-daemon-access/src/lib.rs` (`LocalUnixAllowlistRole`, `DaemonAccessDecision::Authorized/Denied`, `DaemonAccessAdmissionSource`, `MappedDaemonAccessPrincipal`, `map_local_unix_daemon_access()` — `implemented-and-reachable`); `packages/d2bd/src/lib.rs` (`LoadedRealmControllersConfig`, `LoadedRealmIdentityConfig` startup state — `implemented-and-reachable`); compiled bootstrap constant policy: `ADR-only` | -| Reuse action | extract and adapt (`authorize_peer()`/`verb_requires_admin()` verb table → bootstrap constant policy verb table; `PeerRole` two-variant model → system-core/system-minijail bootstrap subjects; `map_local_unix_daemon_access()` SO_PEERCRED mapping → bootstrap subject derivation; `LoadedRealmControllersConfig` startup path → Zone runtime startup bootstrap init sequence) | +| Reuse action | adapt | | Destination | `packages/d2b-resource-api/src/bootstrap_authz.rs`; Zone runtime startup path | -| Detailed design | Compiled bootstrap authorization as described in §9; exact subjects (system-core, system-minijail); closed verb table; non-configurable enforcement; atomic supersession after stored RBAC publishes; out-of-band reset path | +| Detailed design | Compiled bootstrap authorization as described in §9; exact subjects (system-core, system-minijail); closed verb table; non-configurable enforcement; atomic supersession after stored RBAC publishes; out-of-band reset path Primary reuse disposition: `adapt`. Preserved source-plan detail: extract and adapt (`authorize_peer()`/`verb_requires_admin()` verb table → bootstrap constant policy verb table; `PeerRole` two-variant model → system-core/system-minijail bootstrap subjects; `map_local_unix_daemon_access()` SO_PEERCRED mapping → bootstrap subject derivation; `LoadedRealmControllersConfig` startup path → Zone runtime startup bootstrap init sequence). | | Integration | Resource API authorization layer checks bootstrap policy before stored RBAC; supersession is triggered by first `IndexBuilt=True` event from authorization handler | | Data migration | Bootstrap is always freshly compiled; no migration | | Validation | All §15.6 bootstrap tests | @@ -4237,9 +4237,9 @@ None of the following exist in baseline: | Work item ID | `ADR046-zone-control-007` | | Dependency/owner | ADR046-identities-002; ADR046-zone-control-001; ADR046-zone-control-002; ADR046-zone-control-003; ADR046-zone-control-004; ADR046-zone-control-005; ADR046-zone-control-009; ADR046-zone-control-010 | | Current source | `nixos-modules/options-realms.nix` (`providerKind = "^[a-z][a-z0-9-]*$"` label regex matching `LABEL_PATTERN` in `ids.rs`; `providerType` submodule with `enable`/`kind`/`placement`/`freeformType` fields; `d2b.realms..providers.*` attrset — `generated-or-eval-contract`, baseline `b5ddbed6`); `nixos-modules/options-realms-workloads.nix` (`d2b.realms..workloads.*` submodule shape — `generated-or-eval-contract`); `d2b.zones.*` Nix options: `ADR-only` | -| Reuse action | adapt (`providerType` submodule → Provider option submodule; label regex `^[a-z][a-z0-9-]*$` retained unchanged; `placement` option → Provider component placement; `d2b.realms.*` option namespace → `d2b.zones.*` option namespace) | +| Reuse action | adapt | | Destination | `nixos-modules/options-zones.nix`; `nixos-modules/resources-zone-control.nix`; extended `nixos-modules/index.nix` | -| Detailed design | `d2b.zones..*` Nix options for Zone/ZoneLink/Provider/Role/RoleBinding/Quota/EmergencyPolicy authoring; compiler-only scalar `parentZone` required on non-root Zones and forbidden on `local-root`; Nix eval-time validation of parent existence, one resolved parent, self/cycle rejection, 16-name ancestry depth, ResourceRefs, verb enums, digest format, subject types, child-local ZoneLink self-name/one-uplink/local transport-ref constraints, and the no-expiry RoleBinding model; canonical JSON serialization; generation-bound resource bundle output; the compiler seals the validated parent map into allocator bootstrap topology without emitting `parentZone` into `Zone.spec` or reciprocal resources | +| Detailed design | `d2b.zones..*` Nix options for Zone/ZoneLink/Provider/Role/RoleBinding/Quota/EmergencyPolicy authoring; compiler-only scalar `parentZone` required on non-root Zones and forbidden on `local-root`; Nix eval-time validation of parent existence, one resolved parent, self/cycle rejection, 16-name ancestry depth, ResourceRefs, verb enums, digest format, subject types, child-local ZoneLink self-name/one-uplink/local transport-ref constraints, and the no-expiry RoleBinding model; canonical JSON serialization; generation-bound resource bundle output; the compiler seals the validated parent map into allocator bootstrap topology without emitting `parentZone` into `Zone.spec` or reciprocal resources Primary reuse disposition: `adapt`. Preserved source-plan detail: adapt (`providerType` submodule → Provider option submodule; label regex `^[a-z][a-z0-9-]*$` retained unchanged; `placement` option → Provider component placement; `d2b.realms.*` option namespace → `d2b.zones.*` option namespace). | | Integration | Nix compiler → sealed allocator topology for `parentZone`; resource compiler → configuration publication handler → Zone store; bootstrap Provider records auto-generated | | Data migration | Full reset; Nix realm options (`d2b.realms.*`) remain until purge wave | | Validation | nix-unit vectors for each Zone control type schema; cross-field constraint tests; rendered JSON contract tests (`make test-drift`) | @@ -4252,9 +4252,9 @@ None of the following exist in baseline: | Work item ID | `ADR046-zone-control-008` | | Dependency/owner | ADR046-zone-control-001 (Zone resource); ADR046-zone-control-003 (Provider/system-core installed) | | Current source | `d2b-realm-core/src/workload.rs:13,27,83` (`WorkloadProviderKind::UnsafeLocal`, `IsolationPosture::UnsafeLocal`, `WorkloadExecutionPosture` with canonical unsafe-local tuple at lines 206–211: `isolation=unsafe-local`, `environment=systemd-user-manager-ambient`, `executionIdentity=authenticated-requester-uid`, `sessionPersistence=user-manager-lifetime` — `implemented-and-reachable`, baseline `b5ddbed6`); `d2b-core/src/unsafe_local_workloads.rs:16,47,106,150` (`UnsafeLocalWorkloadsJson`, `UnsafeLocalWorkload`, `UnsafeLocalShellPolicy`, `MAX_UNSAFE_LOCAL_SHELL_SESSIONS=64`, `MAX_UNSAFE_LOCAL_WORKLOADS=256` — `implemented-and-reachable`); `d2bd/src/unsafe_local_helper.rs` (`HelperRegistry`, `bind_helper_socket`, `dispatch_launch` — `implemented-and-reachable`, live in `d2bd/src/lib.rs:1346–1468`); `nixos-modules/options-realms.nix:346` (`policy.allowUnsafeLocal` — `generated-or-eval-contract`); `nixos-modules/options-realms-workloads.nix:221,233` (`kind = "unsafe-local"`, doc "no isolation boundary" — `generated-or-eval-contract`) | -| Reuse action | adapt (`WorkloadExecutionPosture` unsafe-local posture tuple → Host `status.observedPosture`; posture details in audit record body, not OTEL labels; `UnsafeLocalShellPolicy.{defaultName,maxSessions}` → Host `spec.shellPolicy`; `HelperRegistry.dispatch_launch` → Zone runtime Host process launch broker; `policy.allowUnsafeLocal` → dedicated Host admission gate); new (Host ResourceType user-domain schema with `defaultDomain`, `allowedDomains`, `defaultUserRef`, `shellPolicy`, cardinality bounds) | +| Reuse action | adapt | | Destination | `packages/d2b-contracts/src/v3/host.rs` (Host resource schema, user-domain variant); `packages/d2b-core-controller/src/host_user.rs` (reconciler owned by Provider/system-core); Nix Host authoring via `d2b.zones..resources. = { type = "Host"; spec = { ... }; };` (validated per ResourceTypeSchema; no separate `options-zones-hosts.nix` submodule) | -| Detailed design | Host ResourceType schema for user-domain variant: `spec.defaultDomain=user`, `spec.allowedDomains=[user]`, `spec.defaultUserRef=User/`, `spec.shellPolicy` (adapted from `UnsafeLocalShellPolicy`), `spec.launcherItems` (adapted from `UnsafeLocalLauncherItem`). No-isolation posture recorded in `status.observedPosture`. A dedicated Host admission gate blocks unsafe-local Host creation without opt-in. Mandatory no-isolation warning in Host `status.conditions[0].message` and CLI/UI output for all Host commands. No-isolation posture included in audit record body (`isolation=no-isolation`); it is never emitted as an OTEL metric label value or span attribute. Child processes use standard Process Providers — no Provider resource with name or kind `unsafe-local` is created. Cardinality bounds: max 256 user-domain Hosts per Zone, max 64 launcher items per Host, max 64 shell sessions per Host. | +| Detailed design | Host ResourceType schema for user-domain variant: `spec.defaultDomain=user`, `spec.allowedDomains=[user]`, `spec.defaultUserRef=User/`, `spec.shellPolicy` (adapted from `UnsafeLocalShellPolicy`), `spec.launcherItems` (adapted from `UnsafeLocalLauncherItem`). No-isolation posture recorded in `status.observedPosture`. A dedicated Host admission gate blocks unsafe-local Host creation without opt-in. Mandatory no-isolation warning in Host `status.conditions[0].message` and CLI/UI output for all Host commands. No-isolation posture included in audit record body (`isolation=no-isolation`); it is never emitted as an OTEL metric label value or span attribute. Child processes use standard Process Providers — no Provider resource with name or kind `unsafe-local` is created. Cardinality bounds: max 256 user-domain Hosts per Zone, max 64 launcher items per Host, max 64 shell sessions per Host. Primary reuse disposition: `adapt`. Preserved source-plan detail: adapt (`WorkloadExecutionPosture` unsafe-local posture tuple → Host `status.observedPosture`; posture details in audit record body, not OTEL labels; `UnsafeLocalShellPolicy.{defaultName,maxSessions}` → Host `spec.shellPolicy`; `HelperRegistry.dispatch_launch` → Zone runtime Host process launch broker; `policy.allowUnsafeLocal` → dedicated Host admission gate); new (Host ResourceType user-domain schema with `defaultDomain`, `allowedDomains`, `defaultUserRef`, `shellPolicy`, cardinality bounds). | | Integration | Provider/system-core bootstrap exception creates Host resources from Zone config publication; Host controller reconciles `defaultUserRef` via User resource lookup; `HelperRegistry` in Zone runtime becomes per-uid launch broker for Host Process launch; Host admission layer enforces the `allowUnsafeLocal` gate before Host creation | | Data migration | Destructive reset; `unsafe-local-workloads.json` bundle artifact and `HelperRegistry` replaced by Host resource store entries + Process Provider launch | | Validation | `host-user-domain-no-isolation-warning-required`, `host-user-only-disallows-system-domain`, `host-allowUnsafeLocal-gates-creation`, `host-defaultUserRef-user-type-required`, `host-shell-policy-max-sessions-bound`, `host-launcher-item-max-count-bound`, `host-audit-body-isolation-label-present`, `host-otel-no-posture-label`, `host-cli-no-isolation-warning-present` | @@ -4269,7 +4269,7 @@ None of the following exist in baseline: | Dependency/owner | ADR046-zone-control-001; Zone store (ADR046-store-001); Quota handler owner | | Current source | `packages/d2b-realm-core/src/ids.rs` (`LABEL_PATTERN`, `MAX_ID_LEN` — ResourceName validation for quotaRef); `packages/d2b-core/src/unsafe_local_workloads.rs:16–164` (`MAX_UNSAFE_LOCAL_WORKLOADS=256`, etc. — bound evidence for quota ceiling defaults); no current quota ResourceType exists (`ADR-only`) | | Reuse source | None from main; Quota is a new ResourceType with no main-branch analog | -| Reuse action | net-new | +| Reuse action | create | | Destination | `packages/d2b-contracts/src/v3/quota.rs`; `packages/d2b-core-controller/src/quota.rs`; `packages/d2b-resource-api/src/quota_gate.rs` | | Detailed design | Quota resource schema with all spec/status fields from §7; ceiling bounds enforcement at admission (hard policy: reject over-quota with `quota-exceeded`; soft policy: warn); usage index built from resource scan with `quotaRef` field; per-ResourceType ceiling in `perTypeCeilings`; `core.quota-drain` finalizer that blocks Quota deletion until all dependent resources with `spec.quotaRef` pointing to this Quota are reassigned or deleted by authorized owners/operators — the controller never issues spec-updates to clear `quotaRef` on other resources; `dependentCount` status field updated from dependency index; audit event `quota-check` per admission; Nix Quota options per §7.7 | | Integration | Resource API admission gate (`packages/d2b-resource-api/src/quota_gate.rs`) called for every `create` verb; Zone controller triggers quota reconcile on resource-created/deleted/quotaRef-changed events; quota handler registered in core-controller process | @@ -4285,7 +4285,7 @@ None of the following exist in baseline: | Dependency/owner | ADR046-zone-control-001; Zone store (ADR046-store-001); EmergencyPolicy handler owner | | Current source | `packages/d2bd/src/lib.rs` admission gate (`authorize_peer()` — `implemented-and-reachable`, baseline `b5ddbed6`); `packages/d2b-daemon-access/src/lib.rs` (`DaemonAccessDecision::Denied` — basis for admission rejection pattern); no EmergencyPolicy ResourceType exists (`ADR-only`) | | Reuse source | None from main; EmergencyPolicy is a new ResourceType | -| Reuse action | net-new | +| Reuse action | create | | Destination | `packages/d2b-contracts/src/v3/emergency_policy.rs`; `packages/d2b-core-controller/src/emergency_policy.rs`; `packages/d2b-resource-api/src/emergency_gate.rs` | | Detailed design | EmergencyPolicy resource schema from §8; union semantics: multiple `enabled=true` policies allowed simultaneously; effective scope = OR of all enabled policies' scope flags; effective `drainDeadlineSeconds` = minimum across enabled policies; scope flag evaluation: `stopNewAdmissions` signal to API admission gate, `disconnectZoneLinks` graceful signal to ZoneLink handler, `stopProviderProcesses` suppresses Process launch and sends stop signal to running Provider component Processes (does NOT set `deletionRequestedAt` on Process resources; reconciliation resumes on deactivation), `drainOngoingOperations` deadline drain; `core.emergency-drain` finalizer for enabled policies; audit events `emergency-policy-activated` / `emergency-policy-deactivated`; `reason` field stored in spec and included in audit record body (never in OTEL metric label values, structured log labels, or status fields); Nix EmergencyPolicy options per §8.7 | | Integration | API admission gate checks union of enabled EmergencyPolicies before every admitted request; ZoneLink handler subscribes to EmergencyPolicy watch triggers; Provider process lifecycle listens for effective `stopProviderProcesses` and resumes launch on deactivation | @@ -4306,10 +4306,10 @@ Evidence class for all: `main-reuse-source`. | --- | --- | | Work item ID | `ADR046-zone-control-011` | | Dependency/owner | ADR046-identities-001; ADR046-store-001 | -| Main source | main `a1cc0b2d`: `packages/d2b-session/src/lifecycle.rs` (`SessionPhase::{Established,Disconnected,Reconnecting,Closing,Closed}`, `KeepaliveAction::{None,SendPing,Close}`, `SessionLifecycle::new/phase/on_activity/poll_keepalive/receive_pong/disconnect/begin_reconnect/reconnect_established/close` — lines 9–195); `packages/d2b-session/src/engine.rs` (`SessionEngine`, `SessionEvent`, `establish_initiator/responder`, `reconnect_initiator/responder`, `call/complete_call/cancel_call`, `send_ttrpc/receive`, `open/send/grant_credit/close/reset_named_stream`, `send_attachments`, `drive_keepalive`, `fail_closed`, encode/decode helpers for keepalive/cancel/close/stream-control/attachment — lines 33–995); `packages/d2b-session/src/driver.rs` (`ComponentSessionDriver` trait 18 methods, `SessionDriverHandle`, `DriverQueues` 4 event queues + named-send FIFO, `DRIVER_COMMAND_CAPACITY=128`, `DRIVER_EVENT_CAPACITY=128`, backpressure on enqueue and dequeue, test `cancelled_immediate_receiver_restores_queued_event` — lines 20–718); `packages/d2b-session/src/streams.rs` (`StreamId`, `StreamPhase::{Open,HalfClosedLocal,HalfClosedRemote,Closed,Reset}`, `StreamEvent::{Data,RemoteClosed,Reset}`, `NamedStreamMux::open/reserve_send/grant_send_credit/refund_send_credit/receive_data/close_local/receive_close/reset/remove_terminal/active` — lines 7–237); `packages/d2b-session/src/transport.rs` (`OwnedTransport` trait, `TransportDescriptor`, `TransportPacket`, `TransportError::{Disconnected,Truncated,LimitExceeded,InvalidAttachment,Other}` — lines 9–106); `packages/d2b-session/src/error.rs` (`SessionError`, all `From<>` mappings covering `ContractError`, `BinaryError`, `SequenceError`, `FragmentSequenceError`, `HandshakeRejectReason`, `TransportError` — lines 10–143); supporting modules: `attachment.rs`, `bootstrap.rs`, `cancellation.rs`, `deadline.rs`, `fragmentation.rs`, `metrics.rs`, `record.rs`, `scheduler.rs`, `server.rs` | -| Reuse action | copy + adapt | +| Current source | main `a1cc0b2d`: `packages/d2b-session/src/lifecycle.rs` (`SessionPhase::{Established,Disconnected,Reconnecting,Closing,Closed}`, `KeepaliveAction::{None,SendPing,Close}`, `SessionLifecycle::new/phase/on_activity/poll_keepalive/receive_pong/disconnect/begin_reconnect/reconnect_established/close` — lines 9–195); `packages/d2b-session/src/engine.rs` (`SessionEngine`, `SessionEvent`, `establish_initiator/responder`, `reconnect_initiator/responder`, `call/complete_call/cancel_call`, `send_ttrpc/receive`, `open/send/grant_credit/close/reset_named_stream`, `send_attachments`, `drive_keepalive`, `fail_closed`, encode/decode helpers for keepalive/cancel/close/stream-control/attachment — lines 33–995); `packages/d2b-session/src/driver.rs` (`ComponentSessionDriver` trait 18 methods, `SessionDriverHandle`, `DriverQueues` 4 event queues + named-send FIFO, `DRIVER_COMMAND_CAPACITY=128`, `DRIVER_EVENT_CAPACITY=128`, backpressure on enqueue and dequeue, test `cancelled_immediate_receiver_restores_queued_event` — lines 20–718); `packages/d2b-session/src/streams.rs` (`StreamId`, `StreamPhase::{Open,HalfClosedLocal,HalfClosedRemote,Closed,Reset}`, `StreamEvent::{Data,RemoteClosed,Reset}`, `NamedStreamMux::open/reserve_send/grant_send_credit/refund_send_credit/receive_data/close_local/receive_close/reset/remove_terminal/active` — lines 7–237); `packages/d2b-session/src/transport.rs` (`OwnedTransport` trait, `TransportDescriptor`, `TransportPacket`, `TransportError::{Disconnected,Truncated,LimitExceeded,InvalidAttachment,Other}` — lines 9–106); `packages/d2b-session/src/error.rs` (`SessionError`, all `From<>` mappings covering `ContractError`, `BinaryError`, `SequenceError`, `FragmentSequenceError`, `HandshakeRejectReason`, `TransportError` — lines 10–143); supporting modules: `attachment.rs`, `bootstrap.rs`, `cancellation.rs`, `deadline.rs`, `fragmentation.rs`, `metrics.rs`, `record.rs`, `scheduler.rs`, `server.rs` | +| Reuse action | adapt | | Destination | `packages/d2b-bus/src/{lifecycle,engine,driver,streams,transport,error}.rs` (new crate `d2b-bus`) | -| Detailed design | **Selected**: full `SessionLifecycle` FSM including keepalive ping-nonce/timeout close (`poll_keepalive` lines 81–124), nonce-exhaustion close (`NonceExhausted` remediation `ReplaceGeneration`), reconnect attempt counting + window expiry (`begin_reconnect` lines 147–174), generation increment on reconnect; `ComponentSessionDriver` 18-method trait as the d2b-bus driver contract; `SessionEngine` full frame encode/decode, keepalive, named-stream credit/half-close/reset; `NamedStreamMux` credit model with send/receive credit, phase transitions, terminal removal; `DriverQueues` backpressure (`QueueBackpressure`) on both deliver and receive paths; all `SessionError`/`From<>` mapping chains. **Excluded ADR45 assumptions**: `establish_initiator_with_generation_discovery()` (lines 102–123): ADR45 initiator-probes-server for current generation; in v3 the generation lives in the Zone resource store — use `establish_initiator()` directly. OTEL labels in `metrics.rs` reference ADR45 realm/workload dimensions — replace with Zone/ZoneLink labels per §13.2. | +| Detailed design | **Selected**: full `SessionLifecycle` FSM including keepalive ping-nonce/timeout close (`poll_keepalive` lines 81–124), nonce-exhaustion close (`NonceExhausted` remediation `ReplaceGeneration`), reconnect attempt counting + window expiry (`begin_reconnect` lines 147–174), generation increment on reconnect; `ComponentSessionDriver` 18-method trait as the d2b-bus driver contract; `SessionEngine` full frame encode/decode, keepalive, named-stream credit/half-close/reset; `NamedStreamMux` credit model with send/receive credit, phase transitions, terminal removal; `DriverQueues` backpressure (`QueueBackpressure`) on both deliver and receive paths; all `SessionError`/`From<>` mapping chains. **Excluded ADR45 assumptions**: `establish_initiator_with_generation_discovery()` (lines 102–123): ADR45 initiator-probes-server for current generation; in v3 the generation lives in the Zone resource store — use `establish_initiator()` directly. OTEL labels in `metrics.rs` reference ADR45 realm/workload dimensions — replace with Zone/ZoneLink labels per §13.2. Primary reuse disposition: `adapt`. Preserved source-plan detail: copy + adapt. | | Integration | `d2b-bus` crate is the sole session-transport dependency for ZoneLink (ADR046-zone-control-018), Provider component Processes (ADR046-zone-control-017), and the Zone resource API service layer (ADR046-api-001); `ComponentSessionDriver` is the interface ZoneLink uses to send/receive ttrpc frames and named streams | | Data migration | Not applicable; new crate | | Validation | `session-lifecycle-reconnect-attempts-exhausted`, `session-lifecycle-keepalive-timeout-closes`, `session-lifecycle-nonce-exhausted-close-with-replace-generation-remediation`, `session-driver-cancelled-receiver-restores-event` (ported from test at line 707), `named-stream-credit-half-close-then-remote-close-transitions-closed`, `named-stream-reset-cancels-pending-send` | @@ -4321,10 +4321,10 @@ Evidence class for all: `main-reuse-source`. | --- | --- | | Work item ID | `ADR046-zone-control-012` | | Dependency/owner | ADR046-zone-control-011 | -| Main source | main `a1cc0b2d`: `packages/d2b-session-unix/src/adapter.rs` (`UnixSeqpacketTransport::new`, `UnixStreamTransport::new`, `PeerIdentityPolicy::{Pathname{verifier},InheritedSocketpair{expected_peer}}`, `OwnedUnixAttachment::file`, `UnixAttachmentPayload`, `consume_peer_credentials()` credential strip+verify, `map_validation_error`/`map_transport_error`, tests `inherited_first_packet_requires_credentials`/`inherited_first_packet_rejects_wrong_credentials` — lines 23–852); `packages/d2b-session-unix/src/socket.rs` (`AncillaryCapacity::from_policy`, `OutboundPacket::new`, `SeqpacketSocket`, `prearmed_seqpacket_pair()`, `SendBurst`/`PacketBurst`/`SentPacket`, `recv_one` with `DONTWAIT|CMSG_CLOEXEC`, `send_one` with `DONTWAIT|NOSIGNAL`, SCM_RIGHTS+SCM_CREDENTIALS, fairness-budget burst loops, tests `raw_control_scanner_rejects_unknown_and_partial_headers`/`raw_control_scanner_accepts_exact_rights_shape` — lines 35–698); `packages/d2b-session-unix/src/pidfd.rs` (`PidfdEvidence::new`, `ProcPidfdIdentityVerifier::verify` with double-read race guard, `ProcSelfFdInfoSource::read_pid_from_fdinfo`, `DigestEvidenceCallback` — lines 8–159); `packages/d2b-session-unix/src/credit.rs` (`CreditPool::new/reserve`, `CreditScope::{Packet,Request,Operation,Session,Process,Host}`, `CreditScopeSet::reserve/reserve_ingress`, `CreditBundle::acquire_dispatch`, `ProcessCreditLimit::derive` from `RLIMIT_NOFILE` — lines 13–286); `packages/d2b-session-unix/src/descriptor.rs` (`ReceivedPacket::verify/verify_first_packet_credentials`, `DescriptorPolicy`, `ObjectIdentity`, `PeerCredentials`, `FirstPacketCredentials`, sealed memfd `F_SEAL_WRITE|GROW|SHRINK|SEAL` — lines 21–617); `packages/d2b-session-unix/src/error.rs` (`UnixSessionError` 21 variants, Display strings — lines 4–92); `packages/d2b-session-unix/src/systemd.rs` (`ActivatedSeqpacketListener`, `SystemdActivationError`) | -| Reuse action | copy + adapt | +| Current source | main `a1cc0b2d`: `packages/d2b-session-unix/src/adapter.rs` (`UnixSeqpacketTransport::new`, `UnixStreamTransport::new`, `PeerIdentityPolicy::{Pathname{verifier},InheritedSocketpair{expected_peer}}`, `OwnedUnixAttachment::file`, `UnixAttachmentPayload`, `consume_peer_credentials()` credential strip+verify, `map_validation_error`/`map_transport_error`, tests `inherited_first_packet_requires_credentials`/`inherited_first_packet_rejects_wrong_credentials` — lines 23–852); `packages/d2b-session-unix/src/socket.rs` (`AncillaryCapacity::from_policy`, `OutboundPacket::new`, `SeqpacketSocket`, `prearmed_seqpacket_pair()`, `SendBurst`/`PacketBurst`/`SentPacket`, `recv_one` with `DONTWAIT|CMSG_CLOEXEC`, `send_one` with `DONTWAIT|NOSIGNAL`, SCM_RIGHTS+SCM_CREDENTIALS, fairness-budget burst loops, tests `raw_control_scanner_rejects_unknown_and_partial_headers`/`raw_control_scanner_accepts_exact_rights_shape` — lines 35–698); `packages/d2b-session-unix/src/pidfd.rs` (`PidfdEvidence::new`, `ProcPidfdIdentityVerifier::verify` with double-read race guard, `ProcSelfFdInfoSource::read_pid_from_fdinfo`, `DigestEvidenceCallback` — lines 8–159); `packages/d2b-session-unix/src/credit.rs` (`CreditPool::new/reserve`, `CreditScope::{Packet,Request,Operation,Session,Process,Host}`, `CreditScopeSet::reserve/reserve_ingress`, `CreditBundle::acquire_dispatch`, `ProcessCreditLimit::derive` from `RLIMIT_NOFILE` — lines 13–286); `packages/d2b-session-unix/src/descriptor.rs` (`ReceivedPacket::verify/verify_first_packet_credentials`, `DescriptorPolicy`, `ObjectIdentity`, `PeerCredentials`, `FirstPacketCredentials`, sealed memfd `F_SEAL_WRITE|GROW|SHRINK|SEAL` — lines 21–617); `packages/d2b-session-unix/src/error.rs` (`UnixSessionError` 21 variants, Display strings — lines 4–92); `packages/d2b-session-unix/src/systemd.rs` (`ActivatedSeqpacketListener`, `SystemdActivationError`) | +| Reuse action | adapt | | Destination | `packages/d2b-bus-unix/src/{adapter,socket,pidfd,credit,descriptor,error,systemd}.rs` (new crate `d2b-bus-unix`) | -| Detailed design | **Selected**: `UnixSeqpacketTransport` seqpacket adapter implementing `OwnedTransport`; `PeerIdentityPolicy` SO_PEERCRED verification (pathname + inherited socketpair); `prearmed_seqpacket_pair()` for bootstrap inherited-socketpair path; `CreditPool`/`CreditScopeSet` six-scope credit model with per-packet/request/operation/session/process/host reservation; `ProcessCreditLimit::derive()` from `RLIMIT_NOFILE` minus `RESERVED_CONTROL_FDS`; `ProcPidfdIdentityVerifier` double-read race guard on `/proc/self/fdinfo/` with executable + cgroup digest callbacks; sealed memfd four-seal enforcement; `AncillaryCapacity` derived from `AttachmentPolicy`; `UnixSessionError` 21-variant Display strings preserved verbatim for audit log compatibility; `consume_peer_credentials()` strips-and-verifies SCM_CREDENTIALS exactly once on first packet. **Excluded ADR45 assumptions**: `vsock.rs` (`FramedVsockTransport`, `NativeVsockListener`, `NativeVsockTransport`): ADR45 guest-control vsock transport; v3 ZoneLink uses Unix sockets for host-local; vsock for Guest connections is separate work. Specific socket paths (`PUBLIC_SOCKET_PATH`, `BROKER_SOCKET_PATH`) are replaced by Zone-resource-managed paths. | +| Detailed design | **Selected**: `UnixSeqpacketTransport` seqpacket adapter implementing `OwnedTransport`; `PeerIdentityPolicy` SO_PEERCRED verification (pathname + inherited socketpair); `prearmed_seqpacket_pair()` for bootstrap inherited-socketpair path; `CreditPool`/`CreditScopeSet` six-scope credit model with per-packet/request/operation/session/process/host reservation; `ProcessCreditLimit::derive()` from `RLIMIT_NOFILE` minus `RESERVED_CONTROL_FDS`; `ProcPidfdIdentityVerifier` double-read race guard on `/proc/self/fdinfo/` with executable + cgroup digest callbacks; sealed memfd four-seal enforcement; `AncillaryCapacity` derived from `AttachmentPolicy`; `UnixSessionError` 21-variant Display strings preserved verbatim for audit log compatibility; `consume_peer_credentials()` strips-and-verifies SCM_CREDENTIALS exactly once on first packet. **Excluded ADR45 assumptions**: `vsock.rs` (`FramedVsockTransport`, `NativeVsockListener`, `NativeVsockTransport`): ADR45 guest-control vsock transport; v3 ZoneLink uses Unix sockets for host-local; vsock for Guest connections is separate work. Specific socket paths (`PUBLIC_SOCKET_PATH`, `BROKER_SOCKET_PATH`) are replaced by Zone-resource-managed paths. Primary reuse disposition: `adapt`. Preserved source-plan detail: copy + adapt. | | Integration | `d2b-bus-unix` provides `OwnedTransport` impl consumed by `d2b-bus` `SessionEngine`; Zone runtime passes accepted seqpacket socket to `UnixSeqpacketTransport::new`; Process pidfd identity verifier integrates with Host/Process broker launch | | Data migration | Not applicable; new crate | | Validation | `seqpacket-inherited-missing-credentials-rejected` (ported from `inherited_first_packet_requires_credentials`), `seqpacket-inherited-wrong-credentials-rejected` (ported from `inherited_first_packet_rejects_wrong_credentials`), `pidfd-double-read-race-guard-detects-pid-reuse`, `sealed-memfd-partial-seal-rejected`, `credit-scope-six-levels-ordered`, `ancillary-capacity-from-disabled-policy-is-zero`, `raw-control-unknown-header-rejected` (ported from `raw_control_scanner_rejects_unknown_and_partial_headers`), `raw-control-valid-rights-parsed` (ported from `raw_control_scanner_accepts_exact_rights_shape`) | @@ -4336,10 +4336,10 @@ Evidence class for all: `main-reuse-source`. | --- | --- | | Work item ID | `ADR046-zone-control-013` | | Dependency/owner | ADR046-identities-001 | -| Main source | main `a1cc0b2d`: `packages/d2b-contracts/src/v2_component_session.rs` (all protocol constants: `PREFACE_LEN=16`, `PREFACE_MAGIC=b"D2BCS2\r\n"`, `COMPONENT_SESSION_MAJOR=2`, `COMPONENT_SESSION_MINOR=0`, `MAX_HANDSHAKE_OFFER_BYTES=16384`, `HANDSHAKE_OFFER_CANONICAL_LEN=148`, `ENDPOINT_POLICY_IDENTITY_CANONICAL_LEN=140`, `MAX_PROTECTED_CIPHERTEXT_BYTES=65535`, `NOISE_TAG_BYTES=16`, `RECORD_LENGTH_BYTES=2`, `MAX_PROTECTED_PLAINTEXT_BYTES=65519`, `MAX_LOGICAL_MESSAGE_BYTES=1_048_576`, `MAX_ACTIVE_NAMED_STREAMS=128`, `MAX_PACKET_ATTACHMENTS=32`, `MAX_REQUEST_ATTACHMENTS=64`, `MAX_OPERATION_ATTACHMENTS=128`, `MAX_SESSION_ATTACHMENTS=256`, `MAX_PROCESS_ATTACHMENT_CREDITS=2048`, `MAX_HOST_ATTACHMENT_CREDITS=8192`, named-stream/control queue limits, credential size/flag constants; closed enums: `EndpointPurpose` 19 tags, `PurposeClass` 3 tags, `EndpointRole` 19 tags, `ServicePackage` 15 tags, `NoiseProfile` 3 tags, `IdentityEvidenceRequirement` 3 tags, `Locality` 4 tags, `TransportClass` 7 tags, `AttachmentPolicyKind`; structs: `ComponentSessionPreface::parse/encode`, `HandshakeOffer` 11 fields, `HandshakeAccept`, `HandshakeReject`, `HandshakeRejectReason` 21 tags, `LimitProfile` 15 fields with `local_default()`, `AttachmentPolicy::validate/disabled`, `BoundedVec` — throughout); `packages/d2b-session/src/handshake.rs` (`HandshakeRole::{Initiator,Responder}`, `HandshakeCredentials::{Nn,Kk,IkPsk2Initiator,IkPsk2Responder}`, `NegotiatedOffer`, `NoiseHandshake::new/write_next/read_next/finish`, `EstablishedHandshake::transcript_hash/generation`, `encode_offer/negotiate_offer`, generation-discovery magics `b"D2BGD2Q\n"`/`b"D2BGD2A\n"`, `encode_generation_discovery_request/accept/response/decode_generation_discovery_response` — lines 39–433) | -| Reuse action | copy + adapt | +| Current source | main `a1cc0b2d`: `packages/d2b-contracts/src/v2_component_session.rs` (all protocol constants: `PREFACE_LEN=16`, `PREFACE_MAGIC=b"D2BCS2\r\n"`, `COMPONENT_SESSION_MAJOR=2`, `COMPONENT_SESSION_MINOR=0`, `MAX_HANDSHAKE_OFFER_BYTES=16384`, `HANDSHAKE_OFFER_CANONICAL_LEN=148`, `ENDPOINT_POLICY_IDENTITY_CANONICAL_LEN=140`, `MAX_PROTECTED_CIPHERTEXT_BYTES=65535`, `NOISE_TAG_BYTES=16`, `RECORD_LENGTH_BYTES=2`, `MAX_PROTECTED_PLAINTEXT_BYTES=65519`, `MAX_LOGICAL_MESSAGE_BYTES=1_048_576`, `MAX_ACTIVE_NAMED_STREAMS=128`, `MAX_PACKET_ATTACHMENTS=32`, `MAX_REQUEST_ATTACHMENTS=64`, `MAX_OPERATION_ATTACHMENTS=128`, `MAX_SESSION_ATTACHMENTS=256`, `MAX_PROCESS_ATTACHMENT_CREDITS=2048`, `MAX_HOST_ATTACHMENT_CREDITS=8192`, named-stream/control queue limits, credential size/flag constants; closed enums: `EndpointPurpose` 19 tags, `PurposeClass` 3 tags, `EndpointRole` 19 tags, `ServicePackage` 15 tags, `NoiseProfile` 3 tags, `IdentityEvidenceRequirement` 3 tags, `Locality` 4 tags, `TransportClass` 7 tags, `AttachmentPolicyKind`; structs: `ComponentSessionPreface::parse/encode`, `HandshakeOffer` 11 fields, `HandshakeAccept`, `HandshakeReject`, `HandshakeRejectReason` 21 tags, `LimitProfile` 15 fields with `local_default()`, `AttachmentPolicy::validate/disabled`, `BoundedVec` — throughout); `packages/d2b-session/src/handshake.rs` (`HandshakeRole::{Initiator,Responder}`, `HandshakeCredentials::{Nn,Kk,IkPsk2Initiator,IkPsk2Responder}`, `NegotiatedOffer`, `NoiseHandshake::new/write_next/read_next/finish`, `EstablishedHandshake::transcript_hash/generation`, `encode_offer/negotiate_offer`, generation-discovery magics `b"D2BGD2Q\n"`/`b"D2BGD2A\n"`, `encode_generation_discovery_request/accept/response/decode_generation_discovery_response` — lines 39–433) | +| Reuse action | adapt | | Destination | `packages/d2b-contracts/src/v3/component_session.rs` (new v3 namespace in existing contracts crate) | -| Detailed design | **Selected**: all numeric constants verbatim; all 21 `HandshakeRejectReason` tags; `ComponentSessionPreface::parse/encode` with exact magic/length layout; `HandshakeOffer` 11-field shape; `AttachmentPolicy::validate` transport constraints (packet-atomic requires seqpacket or inherited-socketpair); `LimitProfile` 15-field profile; `NoiseHandshake` for Nn/Kk/IkPsk2 profiles; `EstablishedHandshake::transcript_hash`; `BoundedVec`. **Excluded ADR45 assumptions**: `EndpointPurpose` 19 tags and `ServicePackage` 15 tags encode ADR45-specific service families — v3 will append new tags for Zone API endpoints without renumbering existing ones. `IdentityEvidenceRequirement` + `GuestSessionCredentialV1` / `GuestBootstrapPsk` / `GUEST_SESSION_CREDENTIAL_MAGIC`: ADR45 guest bootstrap credential formats, excluded until v3 Guest bootstrap work item. Generation-discovery protocol (`encode_generation_discovery_request` lines 138–149): ADR45 initiator probes server for current generation; v3 generation lives in Zone resource store — generation-discovery excluded from initial d2b-bus copy. | +| Detailed design | **Selected**: all numeric constants verbatim; all 21 `HandshakeRejectReason` tags; `ComponentSessionPreface::parse/encode` with exact magic/length layout; `HandshakeOffer` 11-field shape; `AttachmentPolicy::validate` transport constraints (packet-atomic requires seqpacket or inherited-socketpair); `LimitProfile` 15-field profile; `NoiseHandshake` for Nn/Kk/IkPsk2 profiles; `EstablishedHandshake::transcript_hash`; `BoundedVec`. **Excluded ADR45 assumptions**: `EndpointPurpose` 19 tags and `ServicePackage` 15 tags encode ADR45-specific service families — v3 will append new tags for Zone API endpoints without renumbering existing ones. `IdentityEvidenceRequirement` + `GuestSessionCredentialV1` / `GuestBootstrapPsk` / `GUEST_SESSION_CREDENTIAL_MAGIC`: ADR45 guest bootstrap credential formats, excluded until v3 Guest bootstrap work item. Generation-discovery protocol (`encode_generation_discovery_request` lines 138–149): ADR45 initiator probes server for current generation; v3 generation lives in Zone resource store — generation-discovery excluded from initial d2b-bus copy. Primary reuse disposition: `adapt`. Preserved source-plan detail: copy + adapt. | | Integration | All d2b-bus, d2b-bus-unix, Provider registry, and client packages import v3 protocol constants; v3 Zone API service layer uses `EndpointPurpose`/`ServicePackage` tags for Zone API endpoints | | Data migration | Additive: new v3 tags appended to existing closed enums; ADR45 v2 tag values remain valid during coexistence | | Validation | `preface-magic-exact-16-bytes`, `preface-offer-len-zero-rejected`, `preface-offer-len-over-max-rejected`, `preface-offer-len-canonical-accepted`, `handshake-21-reject-reasons-all-covered`, `attachment-policy-packet-atomic-requires-seqpacket-or-inherited`, `limit-profile-local-default-all-fields-positive` | @@ -4350,8 +4350,8 @@ Evidence class for all: `main-reuse-source`. | Field | Value | | --- | --- | | Work item ID | `ADR046-zone-control-018` | -| Dependency/owner | ADR046-zone-control-011, ADR046-zone-control-012, ADR046-zone-control-013, ADR046-zone-control-002 | -| Main source | main `a1cc0b2d`: `packages/d2b-session/src/lifecycle.rs` (`SessionPhase` 5-phase FSM, `begin_reconnect` window/attempt bounds — lines 9–195); `packages/d2b-realm-router/src/service_v2.rs` (`RealmSessionAuthority::local_controller/gateway_peer`, authority validation rejecting invalid combinations — lines 53–123; `RealmServiceServer::call` wire/request/generation/lifetime/attachment validation — lines 415–497; `RealmServiceLimits` 15 fields — lines 147–181; `RealmAuditEvent`/`RealmAuditOutcome` — lines 236–245); `packages/d2b-realm-router/src/session_lifecycle.rs` (`SessionPhase::{Allocating,TokenMinting,RelayConnecting,DisplayOpening,Running,Stopping,Stopped}`, `fail`/`stop`/`finish_stop`, tests `forward_sequence_reaches_running_then_refuses_to_advance`, `failure_mid_establishment_rolls_into_teardown_and_records_phase`, `stop_is_idempotent`, `finish_stop_without_stopping_is_a_no_op` — lines 31–220); `packages/d2b-client/src/session.rs` (`ComponentSessionConnector` trait — lines 79–86; `NamedStream` lifecycle/close/reset/send/receive — lines 426–576); `packages/d2b-daemon-access/src/component_session.rs` (`connect_component_session()` peer-UID verify + `HandshakeCredentials::Nn` + `TransportKind::LocalUnix` — lines 53–85) | +| Dependency/owner | ADR046-zone-control-011, ADR046-zone-control-012, ADR046-zone-control-013; ZoneLink session/admission foundation owner | +| Current source | main `a1cc0b2d`: `packages/d2b-session/src/lifecycle.rs` (`SessionPhase` 5-phase FSM, `begin_reconnect` window/attempt bounds — lines 9–195); `packages/d2b-realm-router/src/service_v2.rs` (`RealmSessionAuthority::local_controller/gateway_peer`, authority validation rejecting invalid combinations — lines 53–123; `RealmServiceServer::call` wire/request/generation/lifetime/attachment validation — lines 415–497; `RealmServiceLimits` 15 fields — lines 147–181; `RealmAuditEvent`/`RealmAuditOutcome` — lines 236–245); `packages/d2b-realm-router/src/session_lifecycle.rs` (`SessionPhase::{Allocating,TokenMinting,RelayConnecting,DisplayOpening,Running,Stopping,Stopped}`, `fail`/`stop`/`finish_stop`, tests `forward_sequence_reaches_running_then_refuses_to_advance`, `failure_mid_establishment_rolls_into_teardown_and_records_phase`, `stop_is_idempotent`, `finish_stop_without_stopping_is_a_no_op` — lines 31–220); `packages/d2b-client/src/session.rs` (`ComponentSessionConnector` trait — lines 79–86; `NamedStream` lifecycle/close/reset/send/receive — lines 426–576); `packages/d2b-daemon-access/src/component_session.rs` (`connect_component_session()` peer-UID verify + `HandshakeCredentials::Nn` + `TransportKind::LocalUnix` — lines 53–85) | | Reuse action | adapt | | Destination | `packages/d2b-core-controller/src/zone_link.rs` (ZoneLink handler); `packages/d2b-resource-api/src/admission.rs` (request admission) | | Detailed design | **Selected**: `SessionPhase` 5-phase FSM (from main `d2b-session`) drives ZoneLink session state; `Established` state → `status.connected=true` + ZoneLink `status.phase=Ready`; `Disconnected`/`Reconnecting` states → `status.connected=false` + ZoneLink `status.phase=Pending` (or `Degraded` if degraded capability); session-internal phases are not exposed as `ZoneLink.status.phase` values — only the common Resource phases (`Pending|Ready|Degraded|Failed|Unknown`) appear in `status.phase` (§3.5); `begin_reconnect` window/attempt logic → ZoneLink reconnect loop; `RealmSessionAuthority` local vs gateway pattern → ZoneLink authority types for host-local vs transport-bridge sessions; `RealmServiceServer::call` wire validation (generation, request lifetime, attachment) → Zone API request admission; `RealmServiceLimits` 15 fields → ZoneLink `spec.limits`; `connect_component_session()` Nn peer-UID path → Zone runtime bootstrap ComponentSession; `NamedStream` lifecycle → ZoneLink named-stream operations; session-lifecycle tests ported as ZoneLink phase regression tests. **Excluded ADR45 assumptions**: `RealmSessionAuthority::gateway_peer()` (lines 72–87): gateway custody and `Locality::Remote` + `CredentialCustody::GatewayGuest` are ADR45 realm-gateway patterns; v3 ZoneLink transport is bound by the resolved `spec.transportProviderRef`, `spec.transportSettings`, and `spec.transportCredentials` contract instead. Realm 7-phase `SessionLifecycle` (`Allocating→…→Running→Stopping→Stopped`) is the ADR45 realm-specific lifecycle; ZoneLink uses the 5-phase d2b-session model. `GuestBootstrapPsk`/`GuestSessionCredentialV1`: ADR45 guest bootstrap, excluded. `realm_stubs.rs` `ApiService`/`ApiFrontend` dead code excluded (§16.2). | @@ -4365,53 +4365,53 @@ Evidence class for all: `main-reuse-source`. | Field | Value | | --- | --- | | Work item ID | `ADR046-zone-control-017` | -| Dependency/owner | ADR046-zone-control-011, ADR046-zone-control-013, ADR046-zone-control-003 | -| Main source | main `a1cc0b2d`: `packages/d2b-provider/src/registry.rs` (`RegistryLimits`, `AdmissionOptions`, `ProviderRegistryBuilder::new/limits/register_factory/register_instance/register_constructed/finish`, `ProviderRegistry::lifecycle/snapshot/instance/admit/shutdown`, `InFlightPermit`, `AdmittedProvider`, `ProviderRegistryManager::new/current/publish` — lines 33–568; tests `shutdown_closes_final_permit_notify_race`, `finish_drain_closes_final_permit_notify_race` — lines 683–691); `packages/d2b-provider/src/rpc.rs` (`SessionIdentity`, `ProviderClock`/`SystemProviderClock`, `RpcCall`, `RpcPayload`, `RpcResponse`, `AuthenticatedProviderRpc`, `RpcProviderProxy::new/preflight/call*`, `session_identity_matches_placement` — lines 27–895; test `provider_and_user_agent_session_identities_are_placement_exact` — lines 923–959); `packages/d2b-provider-toolkit/src/adapter.rs` (`ProviderAgentAdapter::new/invoke_session/invoke` — lines 18–194); `packages/d2b-provider-toolkit/src/conformance.rs` (`ConformanceError`, `check_descriptor_conformance`, `check_provider_conformance` — lines 8–126); `packages/d2b-provider-toolkit/src/fixture.rs` (`DeterministicClock`, `Fixture::new/from_descriptor/operation/request/call_context/session_identity`, `FakeProvider`, `sample_lease_request` — lines 39–262); `packages/d2b-provider-toolkit/src/registration.rs` (`ToolkitError`, `register_exact_instances` — lines 12–107); `packages/d2b-provider-toolkit/src/server.rs` (`GeneratedProviderServiceServer::from_session_handle/new/shutdown/generated_services` — lines 59–176; test `rpc_statuses_retain_closed_actionable_reasons`); `packages/d2b-provider-toolkit/src/values.rs` (`ProviderValues::new/descriptor/health/plan/handle_from_request/handle_from_plan/observation` — lines 18–192); `packages/d2b-provider-toolkit/src/redaction.rs` (`Redacted`, `Secret` — lines 3–37) | -| Reuse action | copy + adapt | +| Dependency/owner | ADR046-zone-control-011, ADR046-zone-control-013; Provider registry/toolkit foundation owner | +| Current source | main `a1cc0b2d`: `packages/d2b-provider/src/registry.rs` (`RegistryLimits`, `AdmissionOptions`, `ProviderRegistryBuilder::new/limits/register_factory/register_instance/register_constructed/finish`, `ProviderRegistry::lifecycle/snapshot/instance/admit/shutdown`, `InFlightPermit`, `AdmittedProvider`, `ProviderRegistryManager::new/current/publish` — lines 33–568; tests `shutdown_closes_final_permit_notify_race`, `finish_drain_closes_final_permit_notify_race` — lines 683–691); `packages/d2b-provider/src/rpc.rs` (`SessionIdentity`, `ProviderClock`/`SystemProviderClock`, `RpcCall`, `RpcPayload`, `RpcResponse`, `AuthenticatedProviderRpc`, `RpcProviderProxy::new/preflight/call*`, `session_identity_matches_placement` — lines 27–895; test `provider_and_user_agent_session_identities_are_placement_exact` — lines 923–959); `packages/d2b-provider-toolkit/src/adapter.rs` (`ProviderAgentAdapter::new/invoke_session/invoke` — lines 18–194); `packages/d2b-provider-toolkit/src/conformance.rs` (`ConformanceError`, `check_descriptor_conformance`, `check_provider_conformance` — lines 8–126); `packages/d2b-provider-toolkit/src/fixture.rs` (`DeterministicClock`, `Fixture::new/from_descriptor/operation/request/call_context/session_identity`, `FakeProvider`, `sample_lease_request` — lines 39–262); `packages/d2b-provider-toolkit/src/registration.rs` (`ToolkitError`, `register_exact_instances` — lines 12–107); `packages/d2b-provider-toolkit/src/server.rs` (`GeneratedProviderServiceServer::from_session_handle/new/shutdown/generated_services` — lines 59–176; test `rpc_statuses_retain_closed_actionable_reasons`); `packages/d2b-provider-toolkit/src/values.rs` (`ProviderValues::new/descriptor/health/plan/handle_from_request/handle_from_plan/observation` — lines 18–192); `packages/d2b-provider-toolkit/src/redaction.rs` (`Redacted`, `Secret` — lines 3–37) | +| Reuse action | adapt | | Destination | `packages/d2b-provider/src/{registry,rpc}.rs` (new v3 Provider package); `packages/d2b-provider-toolkit/src/{adapter,conformance,fixture,registration,server,values,redaction}.rs` (new v3 toolkit) | -| Detailed design | **Selected**: `ProviderRegistry` lifecycle `Accepting→Draining→Retired` with drain-waiter notify-race safety (ported from `shutdown_closes_final_permit_notify_race` / `finish_drain_closes_final_permit_notify_race` tests); `InFlightPermit` + global + per-provider in-flight quota; `ProviderRegistryManager::publish` validates snapshot before swap; `RpcProviderProxy::preflight` cancellation-check, deadline-check, method/capability match, session-identity/placement exactness; `ProviderAgentAdapter` rejects attachments with missing descriptors or non-increasing indexes (lines 79–99); `GeneratedProviderServiceServer::new` single-service-per-agent requirement + shutdown via atomic accept-flag + idle notify + timeout; `check_provider_conformance` health/inspection/observability check sequence; `Fixture`/`FakeProvider`/`DeterministicClock` as conformance harness; `Redacted`/`Secret` retained unchanged. **Excluded ADR45 assumptions**: `TrustedFirstPartyInProcess` as the only accepted placement (in `session_identity_matches_placement` lines 577–598): v3 Provider resources support multiple placements per `RealmControllerPlacement` mapping (§16.2); in-process placement is retained but not exclusive. Generated ttrpc stubs in `d2b-contracts/src/generated_v2_services/` are v2-service-specific; v3 Provider processes compile their own stubs from v3 proto files. ACA workload adapter (`d2b-gateway-runtime/src/aca_workload.rs`) excluded. | +| Detailed design | **Selected**: `ProviderRegistry` lifecycle `Accepting→Draining→Retired` with drain-waiter notify-race safety (ported from `shutdown_closes_final_permit_notify_race` / `finish_drain_closes_final_permit_notify_race` tests); `InFlightPermit` + global + per-provider in-flight quota; `ProviderRegistryManager::publish` validates snapshot before swap; `RpcProviderProxy::preflight` cancellation-check, deadline-check, method/capability match, session-identity/placement exactness; `ProviderAgentAdapter` rejects attachments with missing descriptors or non-increasing indexes (lines 79–99); `GeneratedProviderServiceServer::new` single-service-per-agent requirement + shutdown via atomic accept-flag + idle notify + timeout; `check_provider_conformance` health/inspection/observability check sequence; `Fixture`/`FakeProvider`/`DeterministicClock` as conformance harness; `Redacted`/`Secret` retained unchanged. **Excluded ADR45 assumptions**: `TrustedFirstPartyInProcess` as the only accepted placement (in `session_identity_matches_placement` lines 577–598): v3 Provider resources support multiple placements per `RealmControllerPlacement` mapping (§16.2); in-process placement is retained but not exclusive. Generated ttrpc stubs in `d2b-contracts/src/generated_v2_services/` are v2-service-specific; v3 Provider processes compile their own stubs from v3 proto files. ACA workload adapter (`d2b-gateway-runtime/src/aca_workload.rs`) excluded. Primary reuse disposition: `adapt`. Preserved source-plan detail: copy + adapt. | | Integration | Provider/system-core and Provider/system-minijail use `ProviderRegistryBuilder::register_constructed` as bootstrap exceptions; other Providers register instances from Process-spawned agents via `ProviderAgentAdapter`; Zone runtime hosts one `ProviderRegistry` per installed Provider component; `ProviderRegistryManager::publish` swaps registry on Provider update | | Data migration | Provider descriptors re-registered on Zone store bootstrap; no v2 provider state migration | | Validation | `provider-registry-drain-waiter-race-safe` (ported from both notify-race tests), `provider-registry-publish-validates-snapshot-before-swap`, `provider-rpc-proxy-placement-exact` (ported from `provider_and_user_agent_session_identities_are_placement_exact`), `provider-agent-adapter-rejects-non-monotone-attachment-indexes`, `provider-server-shutdown-drains-in-flight-requests`, `provider-conformance-health-inspection-observability-sequence` | | Removal proof | `d2b-realm-provider` trait crate removed after v3 Provider resource + registry integration (§16.5) | -### ADR046-provider-agent-001 +### ADR046-zone-control-027 | Field | Value | | --- | --- | -| Work item ID | `ADR046-provider-agent-001` | +| Work item ID | `ADR046-zone-control-027` | | Dependency/owner | ADR046-zone-control-011, ADR046-zone-control-017, ADR046-zone-control-018 | -| Main source | main `a1cc0b2d`: `packages/d2b-gateway-runtime/src/provider_agent.rs` (`ProviderAgentError::{UnregisteredAdapter,RegistryNotAccepting,RegistrationRejected,InvalidAuditCapacity,SessionClosed,ProtocolViolation}`, `ProviderAgentAuditOutcome`, `ProviderAgentAuditEvent`, `ProviderAgentProcess::from_registry/from_registry_with/provider_type/service_names/audit_snapshot/serve`, `run_registered`, bounded in-memory audit ring, frame dispatch loop: semaphore in-flight limit, service/method routing, negative-timeout guard, `SessionClosed` termination, `ProtocolViolation` audit + terminate — lines 31–452; tests `standalone_entrypoint_fails_without_registration`, `audit_capacity_is_bounded` — lines 454–486); `packages/d2b-contracts/src/provider_registry_v2.rs` (`ProviderRegistryV2` wire contract, `ProviderRegistryEntryV2::validate` with provider-id derivation rule, schema fingerprint, scope-digest, generation exactness, `TrustedFirstPartyInProcess` placement requirement, `ProviderIntentId` label rules `max 128 bytes`, `MAX_PROVIDER_REGISTRY_ENTRIES`, `MAX_PROVIDER_MAPPING_IDS=64`, `ProviderBindingV2` non-exhaustive + `UnsupportedProviderBindingV2` fallback, `ProviderRegistryV2::validate` sort/unique/count checks — lines 23–566; tests `validates_closed_local_runtime_mapping`, `validates_closed_local_observability_mapping`, `serializes_declared_mapping_axes_as_closed_variants`, `rejects_duplicate_or_unbounded_mapping_ids`, `local_storage_binding_realm_must_match_descriptor_placement`, `rejects_generation_and_exact_identity_mismatches`, `contradictory_binding_realm_json_is_unrepresentable`, `unknown_binding_axis_remains_rejected_on_the_wire`, `identity_mismatch_messages_name_the_failed_contract`, `accepts_explicit_empty_registry` — lines 722–1044) | -| Reuse action | copy + adapt | +| Current source | main `a1cc0b2d`: `packages/d2b-gateway-runtime/src/provider_agent.rs` (`ProviderAgentError::{UnregisteredAdapter,RegistryNotAccepting,RegistrationRejected,InvalidAuditCapacity,SessionClosed,ProtocolViolation}`, `ProviderAgentAuditOutcome`, `ProviderAgentAuditEvent`, `ProviderAgentProcess::from_registry/from_registry_with/provider_type/service_names/audit_snapshot/serve`, `run_registered`, bounded in-memory audit ring, frame dispatch loop: semaphore in-flight limit, service/method routing, negative-timeout guard, `SessionClosed` termination, `ProtocolViolation` audit + terminate — lines 31–452; tests `standalone_entrypoint_fails_without_registration`, `audit_capacity_is_bounded` — lines 454–486); `packages/d2b-contracts/src/provider_registry_v2.rs` (`ProviderRegistryV2` wire contract, `ProviderRegistryEntryV2::validate` with provider-id derivation rule, schema fingerprint, scope-digest, generation exactness, `TrustedFirstPartyInProcess` placement requirement, `ProviderIntentId` label rules `max 128 bytes`, `MAX_PROVIDER_REGISTRY_ENTRIES`, `MAX_PROVIDER_MAPPING_IDS=64`, `ProviderBindingV2` non-exhaustive + `UnsupportedProviderBindingV2` fallback, `ProviderRegistryV2::validate` sort/unique/count checks — lines 23–566; tests `validates_closed_local_runtime_mapping`, `validates_closed_local_observability_mapping`, `serializes_declared_mapping_axes_as_closed_variants`, `rejects_duplicate_or_unbounded_mapping_ids`, `local_storage_binding_realm_must_match_descriptor_placement`, `rejects_generation_and_exact_identity_mismatches`, `contradictory_binding_realm_json_is_unrepresentable`, `unknown_binding_axis_remains_rejected_on_the_wire`, `identity_mismatch_messages_name_the_failed_contract`, `accepts_explicit_empty_registry` — lines 722–1044) | +| Reuse action | adapt | | Destination | `packages/d2b-provider/src/agent.rs` (v3 provider agent dispatch); `packages/d2b-contracts/src/v3/provider_registry.rs` (v3 provider registry wire contract) | -| Detailed design | **Selected**: `ProviderAgentProcess::serve` dispatch loop with semaphore in-flight limit; unsupported-service/method → ttrpc error; negative-timeout rejection; `SessionClosed` clean termination; `ProtocolViolation` audit-and-terminate path; bounded audit ring; `GeneratedProviderServiceServer` single-service-per-agent requirement; `ProviderRegistryV2` entry validation: provider-id derivation, schema fingerprint, scope digest, generation exactness; `MAX_PROVIDER_MAPPING_IDS=64` → Provider component mapping bound; `ProviderBindingV2` non-exhaustive + explicit `UnsupportedProviderBindingV2` fallback (never panics on unknown axis); `ProviderIntentId` `max 128 bytes` label rules → Provider component `spec.intentRef`; all 10 `provider_registry_v2.rs` tests ported. **Excluded ADR45 assumptions**: `aca_workload.rs` (`AcaGatewayWorkload`): ADR45 ACA external provider adapter, excluded entirely. `waypipe_display.rs` (`WaypipeDisplayProvider`): ADR45 display provider, excluded. `ProviderRegistryV2.registry_generation` / `configuration_fingerprint` bind to ADR45 bundle generation; v3 Provider resource version is tracked in redb store, not a JSON bundle. `run()` binary entrypoint uses the fixed `d2b-provider-agent` command; v3 provider processes use normal Zone runtime Process launch. `TrustedFirstPartyInProcess` is the only placement in v2; v3 Provider resources extend to `HostLocal`/`GatewayVm` etc. | +| Detailed design | **Selected**: `ProviderAgentProcess::serve` dispatch loop with semaphore in-flight limit; unsupported-service/method → ttrpc error; negative-timeout rejection; `SessionClosed` clean termination; `ProtocolViolation` audit-and-terminate path; bounded audit ring; `GeneratedProviderServiceServer` single-service-per-agent requirement; `ProviderRegistryV2` entry validation: provider-id derivation, schema fingerprint, scope digest, generation exactness; `MAX_PROVIDER_MAPPING_IDS=64` → Provider component mapping bound; `ProviderBindingV2` non-exhaustive + explicit `UnsupportedProviderBindingV2` fallback (never panics on unknown axis); `ProviderIntentId` `max 128 bytes` label rules → Provider component `spec.intentRef`; all 10 `provider_registry_v2.rs` tests ported. **Excluded ADR45 assumptions**: `aca_workload.rs` (`AcaGatewayWorkload`): ADR45 ACA external provider adapter, excluded entirely. `waypipe_display.rs` (`WaypipeDisplayProvider`): ADR45 display provider, excluded. `ProviderRegistryV2.registry_generation` / `configuration_fingerprint` bind to ADR45 bundle generation; v3 Provider resource version is tracked in redb store, not a JSON bundle. `run()` binary entrypoint uses the fixed `d2b-provider-agent` command; v3 provider processes use normal Zone runtime Process launch. `TrustedFirstPartyInProcess` is the only placement in v2; v3 Provider resources extend to `HostLocal`/`GatewayVm` etc. Primary reuse disposition: `adapt`. Preserved source-plan detail: copy + adapt. | | Integration | Zone runtime spawns each Provider component Process via normal Process launch; Process binary calls `ProviderAgentProcess::from_registry` then `serve()` on established ComponentSession; on `SessionClosed` the process exits and Zone runtime observes `status.phase` transition | | Data migration | Not applicable; new implementation | | Validation | `provider-agent-dispatch-unsupported-service-returns-ttrpc-error`, `provider-agent-negative-timeout-rejected`, `provider-agent-session-closed-terminates-serve-loop`, `provider-agent-audit-ring-capacity-bounded` (ported from `audit_capacity_is_bounded`), `provider-registry-entry-fingerprint-generation-exact` (ported from `rejects_generation_and_exact_identity_mismatches`), `provider-registry-unknown-axis-fallback-non-exhaustive` (ported from `unknown_binding_axis_remains_rejected_on_the_wire`), `provider-registry-duplicate-ids-rejected` (ported from `rejects_duplicate_or_unbounded_mapping_ids`) | | Removal proof | Not applicable; new implementation | -### ADR046-client-001 +### ADR046-zone-control-025 | Field | Value | | --- | --- | -| Work item ID | `ADR046-client-001` | +| Work item ID | `ADR046-zone-control-025` | | Dependency/owner | ADR046-zone-control-011, ADR046-zone-control-012, ADR046-zone-control-013, ADR046-zone-control-018 | -| Main source | main `a1cc0b2d`: `packages/d2b-client/src/client.rs` (`WallClock`, `MetadataInput`, `RetryPolicy` 1..8 attempts, `CallOptions`, `CancellationToken`, `Client::new/with_clock/connect`, `ConnectedClient` methods incl. `session_generation/session_limits/service/invoke/invoke_with_attachments/named_stream/open_server_stream`, `prepare_typed_request/prepare_operation_context`, `can_retry/retryable_failure/validate_outbound_attachments/validate_reply/service_package/map_remote_kind/map_retry` — lines 35–921); `packages/d2b-client/src/session.rs` (`ComponentSessionConnector` trait, `NamedStream` lifecycle, `StreamDispatcher`, `SharedDriver`, aggregate-queue-bound test — lines 24–626); `packages/d2b-client/src/target.rs` (`ServiceOwner`, `TargetInput`, `TransportKind`, `RouteRecord`, `ResolvedTarget`, `TargetResolver`, `RouteTable` — lines 7–228); `packages/d2b-client/src/service.rs` (`ServiceKind`, `GeneratedClient`, `MethodHandle`, `ServiceHandle::new/kind/generated/proxy/method/invoke` — lines 21–184); `packages/d2b-client/src/daemon_service.rs` (`DaemonClient::new/session_generation/connected/resolve/inspect/lifecycle/open_terminal`, `DaemonTerminal`, `daemon_call_options`, `ensure_daemon_outcome`, `map_ttrpc_error`, test `redacted_terminal_debug_payload` — lines 29–689); `packages/d2b-client/src/host_socket.rs` (`HostSocketConnector::new/from_seqpacket_fd`, `local_daemon_endpoint_identity`, `ComponentSessionConnector::connect` — lines 252–383); `packages/d2b-client/src/error.rs` (`RemoteErrorKind`, `RetryClass`, `ClientError` — lines 5–128) | -| Reuse action | copy + adapt | +| Current source | main `a1cc0b2d`: `packages/d2b-client/src/client.rs` (`WallClock`, `MetadataInput`, `RetryPolicy` 1..8 attempts, `CallOptions`, `CancellationToken`, `Client::new/with_clock/connect`, `ConnectedClient` methods incl. `session_generation/session_limits/service/invoke/invoke_with_attachments/named_stream/open_server_stream`, `prepare_typed_request/prepare_operation_context`, `can_retry/retryable_failure/validate_outbound_attachments/validate_reply/service_package/map_remote_kind/map_retry` — lines 35–921); `packages/d2b-client/src/session.rs` (`ComponentSessionConnector` trait, `NamedStream` lifecycle, `StreamDispatcher`, `SharedDriver`, aggregate-queue-bound test — lines 24–626); `packages/d2b-client/src/target.rs` (`ServiceOwner`, `TargetInput`, `TransportKind`, `RouteRecord`, `ResolvedTarget`, `TargetResolver`, `RouteTable` — lines 7–228); `packages/d2b-client/src/service.rs` (`ServiceKind`, `GeneratedClient`, `MethodHandle`, `ServiceHandle::new/kind/generated/proxy/method/invoke` — lines 21–184); `packages/d2b-client/src/daemon_service.rs` (`DaemonClient::new/session_generation/connected/resolve/inspect/lifecycle/open_terminal`, `DaemonTerminal`, `daemon_call_options`, `ensure_daemon_outcome`, `map_ttrpc_error`, test `redacted_terminal_debug_payload` — lines 29–689); `packages/d2b-client/src/host_socket.rs` (`HostSocketConnector::new/from_seqpacket_fd`, `local_daemon_endpoint_identity`, `ComponentSessionConnector::connect` — lines 252–383); `packages/d2b-client/src/error.rs` (`RemoteErrorKind`, `RetryClass`, `ClientError` — lines 5–128) | +| Reuse action | adapt | | Destination | `packages/d2b-client/src/` (updated for v3 Zone API, replacing ADR45 daemon verbs with Zone resource operations) | -| Detailed design | **Selected**: `Client::connect()` target-resolve → ComponentSession-open → `ConnectedClient` lifecycle; `RetryPolicy` 1..8 bound + `retryable_failure()` safe-only retry detection; `NamedStream::send/receive/close_local/reset` lifecycle; `ComponentSessionConnector` trait as connector abstraction; `HostSocketConnector::from_seqpacket_fd` + `local_daemon_endpoint_identity` for Zone runtime socket connector; `RouteTable` ambiguous-route rejection; `ServiceHandle`/`GeneratedClient`/`MethodHandle` typed service client pattern; `map_ttrpc_error`/`validate_reply`/`map_retry` error-handling chain; `ClientError`/`RemoteErrorKind`/`RetryClass` error taxonomy; `DaemonClient` call-options and outcome helpers (infrastructure only). **Excluded ADR45 assumptions**: `DaemonMethod` enum (lines 29–56 of daemon_service.rs): ADR45 daemon verbs (`vm_start`, `vm_stop`, `list_realms`, etc.) — replaced with Zone API verbs. `GuestClient`/`guest_service.rs`: ADR45 guest operations; excluded until v3 Guest transport work item. Hardcoded socket path `PUBLIC_SOCKET_PATH = "/run/d2b/public.sock"` in `host_socket.rs`: replaced by Zone-resource-managed path. `TransportKind::LocalUnix` restriction in daemon-access: v3 allows multiple transport kinds per ZoneLink binding. | +| Detailed design | **Selected**: `Client::connect()` target-resolve → ComponentSession-open → `ConnectedClient` lifecycle; `RetryPolicy` 1..8 bound + `retryable_failure()` safe-only retry detection; `NamedStream::send/receive/close_local/reset` lifecycle; `ComponentSessionConnector` trait as connector abstraction; `HostSocketConnector::from_seqpacket_fd` + `local_daemon_endpoint_identity` for Zone runtime socket connector; `RouteTable` ambiguous-route rejection; `ServiceHandle`/`GeneratedClient`/`MethodHandle` typed service client pattern; `map_ttrpc_error`/`validate_reply`/`map_retry` error-handling chain; `ClientError`/`RemoteErrorKind`/`RetryClass` error taxonomy; `DaemonClient` call-options and outcome helpers (infrastructure only). **Excluded ADR45 assumptions**: `DaemonMethod` enum (lines 29–56 of daemon_service.rs): ADR45 daemon verbs (`vm_start`, `vm_stop`, `list_realms`, etc.) — replaced with Zone API verbs. `GuestClient`/`guest_service.rs`: ADR45 guest operations; excluded until v3 Guest transport work item. Hardcoded socket path `PUBLIC_SOCKET_PATH = "/run/d2b/public.sock"` in `host_socket.rs`: replaced by Zone-resource-managed path. `TransportKind::LocalUnix` restriction in daemon-access: v3 allows multiple transport kinds per ZoneLink binding. Primary reuse disposition: `adapt`. Preserved source-plan detail: copy + adapt. | | Integration | `d2b` CLI uses `d2b-client` to connect to Zone runtime via `HostSocketConnector`; a child Zone runtime uses `d2b-client` for its allocator-bound uplink while the parent route engine uses the established session for child calls; Provider toolkit conformance tests use `Fixture`/`FakeProvider` with `d2b-client` service handles | | Data migration | Not applicable; updated in place | | Validation | `client-retry-policy-max-8-attempts-enforced`, `client-named-stream-close-local-then-remote-close-transitions-closed`, `client-route-table-ambiguous-route-rejected`, `client-host-socket-peer-uid-verified-on-connect`, `client-retryable-failure-only-safe-mutations` | | Removal proof | `DaemonMethod` v2 verb enum retired after all v2 daemon operations migrated to Zone API | -### ADR046-wire-001 +### ADR046-zone-control-028 | Field | Value | | --- | --- | -| Work item ID | `ADR046-wire-001` | +| Work item ID | `ADR046-zone-control-028` | | Dependency/owner | ADR046-zone-control-013 | -| Main source | main `a1cc0b2d`: `packages/d2b-contracts/src/v2_services.rs` (`MethodSpec{mutating,stream_kind,...}`, `ServiceSpec`, `SERVICE_INVENTORY` covering 20+ services and all provider services, `service_schema_fingerprint`, `public_daemon_schema_fingerprint`, `direct_guest_schema_fingerprint`, `StrictWireMessage`, `decode_strict`, `encode_strict`, `admit_metadata`, `TerminalStreamValidator`, `ServerStreamLease`, `RedactedTerminalFrame`, stream-name validators — lines 204–1004; tests `public_endpoint_fingerprint_binds_both_services_dependencies_and_order`, `public_endpoint_fingerprint_binds_daemon_and_guest_method_descriptors`, `direct_guest_fingerprint_binds_activation_and_remains_separate_from_public_endpoint` — lines 872–1030); `packages/d2b-contracts/src/v2_state.rs` (constants: `STATE_SCHEMA_VERSION=2`, `STATE_SCHEMA_GENERATION=1`, `MAX_JSON_DOCUMENT_BYTES=1_048_576`, `MAX_INVENTORY_ROWS=4096`, `MAX_LOCKS=1024`, `MAX_LOCK_DEPENDENCIES=32`, `MAX_DISCOVERY_OBSERVATIONS=4096`, `MAX_AUDIT_RECORD_BYTES=8192`, `MAX_AUDIT_RECORDS_PER_SEGMENT=16384`, `MAX_AUDIT_SEGMENT_BYTES=64*1024*1024`, `MAX_AUDIT_RETENTION_DAYS=14`, `MAX_LOCK_DEADLINE_MS=300_000`; types: `Digest`, `Generation`, `OwnershipEpoch`, `SafeJsonInteger`, `StorageCategory`, `StateEnvelope`, `CanonicalPayloadVerifier`, `AtomicWritePhase`, `RunnerEvidence`, audit types incl. `AuditRecord/Segment/Checkpoint/Gap`, `detect_audit_gap`, `AuditRetentionPolicy`, lock types: `LockClass`, `LockSpec`, `SyncInventory`, `LeaseRecord`); `packages/d2b-contracts/src/v2_identity.rs` (`IdentityError` 13 variants, canonical name rules `^[a-z][a-z0-9-]*$` max 63 bytes start-lowercase-letter, `RealmPath` label/separator rules, `ProviderType::ALL` 11 types + `as_str()` — lines 11–250); `packages/d2b-contracts/src/v2_provider.rs` (bounded opaque IDs `[a-z][a-z0-9-]{0,63}`: `ImplementationId`, `OperationId`, `IdempotencyKey`, `PlanId`, `HandleId`, `LeaseId`, `TransferId`, `PROVIDER_CONTRACT_FINGERPRINT`, `ProviderContractError` 34 variants, `Fingerprint` 64 lowercase-hex chars — lines 18–219); `packages/d2b-contracts/src/generated_v2_services/` (all 40+ generated ttrpc client/server stubs for v2 services) | +| Current source | main `a1cc0b2d`: `packages/d2b-contracts/src/v2_services.rs` (`MethodSpec{mutating,stream_kind,...}`, `ServiceSpec`, `SERVICE_INVENTORY` covering 20+ services and all provider services, `service_schema_fingerprint`, `public_daemon_schema_fingerprint`, `direct_guest_schema_fingerprint`, `StrictWireMessage`, `decode_strict`, `encode_strict`, `admit_metadata`, `TerminalStreamValidator`, `ServerStreamLease`, `RedactedTerminalFrame`, stream-name validators — lines 204–1004; tests `public_endpoint_fingerprint_binds_both_services_dependencies_and_order`, `public_endpoint_fingerprint_binds_daemon_and_guest_method_descriptors`, `direct_guest_fingerprint_binds_activation_and_remains_separate_from_public_endpoint` — lines 872–1030); `packages/d2b-contracts/src/v2_state.rs` (constants: `STATE_SCHEMA_VERSION=2`, `STATE_SCHEMA_GENERATION=1`, `MAX_JSON_DOCUMENT_BYTES=1_048_576`, `MAX_INVENTORY_ROWS=4096`, `MAX_LOCKS=1024`, `MAX_LOCK_DEPENDENCIES=32`, `MAX_DISCOVERY_OBSERVATIONS=4096`, `MAX_AUDIT_RECORD_BYTES=8192`, `MAX_AUDIT_RECORDS_PER_SEGMENT=16384`, `MAX_AUDIT_SEGMENT_BYTES=64*1024*1024`, `MAX_AUDIT_RETENTION_DAYS=14`, `MAX_LOCK_DEADLINE_MS=300_000`; types: `Digest`, `Generation`, `OwnershipEpoch`, `SafeJsonInteger`, `StorageCategory`, `StateEnvelope`, `CanonicalPayloadVerifier`, `AtomicWritePhase`, `RunnerEvidence`, audit types incl. `AuditRecord/Segment/Checkpoint/Gap`, `detect_audit_gap`, `AuditRetentionPolicy`, lock types: `LockClass`, `LockSpec`, `SyncInventory`, `LeaseRecord`); `packages/d2b-contracts/src/v2_identity.rs` (`IdentityError` 13 variants, canonical name rules `^[a-z][a-z0-9-]*$` max 63 bytes start-lowercase-letter, `RealmPath` label/separator rules, `ProviderType::ALL` 11 types + `as_str()` — lines 11–250); `packages/d2b-contracts/src/v2_provider.rs` (bounded opaque IDs `[a-z][a-z0-9-]{0,63}`: `ImplementationId`, `OperationId`, `IdempotencyKey`, `PlanId`, `HandleId`, `LeaseId`, `TransferId`, `PROVIDER_CONTRACT_FINGERPRINT`, `ProviderContractError` 34 variants, `Fingerprint` 64 lowercase-hex chars — lines 18–219); `packages/d2b-contracts/src/generated_v2_services/` (all 40+ generated ttrpc client/server stubs for v2 services) | | Reuse action | adapt | | Destination | `packages/d2b-contracts/src/v3/{services,state,identity,provider}.rs` (new v3 namespace); generated stubs regenerated from v3 proto files in `packages/d2b-contracts/proto/v3/` | | Detailed design | **Selected**: `MethodSpec`/`ServiceSpec`/fingerprinting infrastructure → v3 Zone API service schema fingerprinting; `StrictWireMessage`/`decode_strict`/`encode_strict`/`admit_metadata` → v3 wire decode/admit for all resource API requests; `CanonicalPayloadVerifier` payload-digest binding → v3 resource store integrity checks; audit chain types `AuditRecord`/`AuditSegment`/`AuditCheckpoint`/`detect_audit_gap` + `MAX_AUDIT_RETENTION_DAYS=14`/`MAX_AUDIT_RECORD_BYTES=8192`/`MAX_AUDIT_SEGMENT_BYTES=64MiB` → v3 Zone audit (§13.3); lock types `LockSpec`/`SyncInventory`/`LeaseRecord` + `MAX_LOCKS=1024`/`MAX_LOCK_DEADLINE_MS=300_000` → v3 resource store lock layer; bounded opaque ID pattern `[a-z][a-z0-9-]{0,63}` → v3 `OperationId`/`HandleId`/`PlanId`/`LeaseId`; `Fingerprint` 64-hex-char → v3 Provider `spec.configFingerprint`; `ProviderContractError` 34 variants → v3 Provider operation error taxonomy; canonical name constraint `^[a-z][a-z0-9-]*$` max 63 bytes → same as `ids.rs::is_label()` shared validator (§16.2). **Excluded ADR45 assumptions**: `RealmLabel`/`WorkloadName`/`RealmPath` identity types: ADR45 workload/realm address format; replaced by `metadata.name` + Zone `ResourceRef`. `ProviderType::ALL` fixed 11-type closed enum: v3 Provider type is an open string field in the Provider resource spec. `STATE_SCHEMA_VERSION=2`/`STATE_SCHEMA_GENERATION=1`: v3 store schema uses redb table versioning, not a JSON schema version field. v2 service fingerprint tests reference ADR45-specific proto files; v3 fingerprints use different proto inputs but the same `service_schema_fingerprint` seeding mechanism. Generated stubs in `generated_v2_services/` are v2-specific and excluded; v3 uses regenerated stubs from `proto/v3/`. | @@ -4430,7 +4430,7 @@ Evidence class for all: `main-reuse-source`. | Dependency/owner | ADR046-zone-control-001; Nix module owner | | Current source | `nixos-modules/options-realms.nix` (option schema/assertion conventions); `nixos-modules/bundle-artifacts.nix` (bundle emit pattern); `nixos-modules/provider-registry-v2-json.nix` (provider-registry JSON emit); `nixos-modules/assertions.nix:730` (assertion style for unsafe-local); `packages/d2b-realm-core/src/ids.rs` (`LABEL_PATTERN`, `MAX_ID_LEN`, `is_label()` — eval-time name validation target) | | Reuse source | None from main; eval-time validation is Nix-native | -| Reuse action | net-new | +| Reuse action | create | | Destination | `nixos-modules/options-zones.nix`, `nixos-modules/resource-type-validators.nix` | | Detailed design | Declare the unified `d2b.zones..resources.` option tree with two sub-options: `type` (string, required) and `spec` (attrset, required), plus the Zone-level compiler-only `parentZone` scalar (plain Zone name, no default). Require `parentZone` on every non-root Zone and forbid it on `local-root`; resolve it against declared Zone keys, reject self-parent/conflicting module definitions/cycles, and cap each ancestry path at 16 Zone names. Compile the validated map into sealed allocator bootstrap topology; never emit it into the resource bundle or `Zone.spec`. Declare the `d2b.artifacts.` catalog option tree with `package` (types.package, required) and `type` (closed string enum, required); `d2b.artifacts` is a global option, not zone-local. For each core ResourceType (Zone, ZoneLink, Provider, Role, RoleBinding, Credential), a `genSchemaOptions` helper reads the corresponding ResourceTypeSchema JSON and emits per-type `spec` option declarations with correct Nix types, defaults, and documentation comments. Provider `spec.artifactId` is a plain string referencing the `d2b.artifacts` catalog; the `package` derivation is NOT a `spec` field. Non-core (Provider-registered) ResourceTypes are deferred to Phase 2 schema validation; Phase 1 accepts any non-empty `spec` attrset for non-core types. Implement all Phase 1 eval-time assertions (§14.10 Phase 1 table) via Nix `assert` and `lib.mkAssert`; `credentialRef` helper function producing `{ "$credentialRef": "Credential/" }` literals; `resourceRef` and `closedEnum` helpers; bootstrap provider exclusion assertions; reject operator-authored `type = "Zone"` resources; child-local ZoneLink assertions require `childZoneName` to equal the enclosing Zone key, reject a second uplink resource even when disabled, and reject any local-root uplink. The runtime creates `Zone/`, `Provider/system-core`, and `Provider/system-minijail` directly with `managedBy=controller`; none is emitted or inferred from the configuration bundle. `allowUnsafeLocal` maps to a dedicated Host admission gate (current `options-realms.nix:346`). Provider manifest-derived fields (`spec.exports`, `spec.components`, `spec.dependencies`, `spec.permissionClaims`, `spec.upgradePolicy`, `spec.restartPolicy`) are declared as read-only/not-settable options; setting them is an eval assertion error | | Integration | Validated `parentZone` map feeds the allocator bootstrap sealer; unified `d2b.zones..resources.*` option tree is consumed by ADR046-zone-control-015 resource compiler; Zone controller (ADR046-zone-control-001) reads the resulting bundle; Provider package conventions come from ADR046-zone-control-003 | @@ -4446,7 +4446,7 @@ Evidence class for all: `main-reuse-source`. | Dependency/owner | ADR046-zone-control-014; resource compiler owner | | Current source | `nixos-modules/bundle-artifacts.nix` (artifact emit pattern); `nixos-modules/processes-json.nix` (canonical JSON serialization conventions); `packages/xtask/src/main.rs` `gen-schemas` subcommand (schema-from-derivation pattern); `packages/d2b-core/src/bundle.rs` (current bundle DTO shape for adaptation reference) | | Reuse source | None from main; the resource compiler is a new pure-Rust Nix derivation | -| Reuse action | net-new | +| Reuse action | create | | Destination | `packages/d2b-resource-compiler/src/{main,bundle,schema,validator,digest,sort,secret_lint,generation}.rs`; exposed as `pkgs.d2b-resource-compiler`; called from `nixos-modules/resource-compiler.nix` | | Detailed design | Implement all Phase 2 build-time checks (§14.10 Phase 2 table): dispatch on `type` to look up ResourceTypeSchema; validate each resource's `spec` canonical JSON against the committed schema (build validation compares canonical rendered JSON against ResourceTypeSchema for each core type); compile the `d2b.artifacts.*` catalog: for each entry, build/include the derivation, verify `type` is a recognized value, compute `digest` over the derivation output, extract and hash manifest and config schema files, validate signature chain and conformance attestation; detect duplicate artifact IDs; for each Provider resource, look up `spec.artifactId` in the compiled catalog (build failure if absent or wrong type), verify `configSchemaDigest` matches schema SHA-256, validate operator `spec.config` against loaded JSON Schema using a pure-Rust validator bundled in the derivation, verify `manifestDigest` and signature chain, load manifest-derived fields (`exports`, `components`, `dependencies`, `permissionClaims`, `upgradePolicy`, `restartPolicy`) into the bundle envelope; emit private integrity-pinned artifact catalog (ID → type/digest/closure metadata) as a separate private file (never merged into the public resource bundle); check `spec.rules[*].resourceTypes` against installed Provider catalogs in the bundle (Role); verify `spec.roleRef` names an existing Role in the bundle (RoleBinding); verify `spec.subjects[*]` names resolve in bundle (RoleBinding); check ResourceType short-name collision across all Zone Providers; RFC 8785 canonical JSON serialization; per-resource `digest` computation; `bundleDigest` computation over sorted `resources` array; inline-secret heuristic lint (`--strict-secrets` flag); `generation` counter persistence in Nix module state; emit `zone-resources.json` bundle with all fields per §14.9 | | Integration | Reads from `d2b.zones..resources.*` (ADR046-zone-control-014); emits bundle consumed by ADR046-zone-control-001 configuration publication handler; generation counter stored as Nix module derivation input hash (hermetic) or in a NixOS state file (impure) — exact mechanism is implementation decision | @@ -4462,23 +4462,23 @@ Evidence class for all: `main-reuse-source`. | Dependency/owner | ADR046-zone-control-015; ADR046-zone-control-001; configuration publication handler owner | | Current source | `packages/d2bd/src/lib.rs` lines 1408 and 16741 (`RealmControllersJson` load — live active generation load pattern); `nixos-modules/realm-controller-config-json.nix` (current config bundle emit to `/etc/d2b/`); `packages/d2b-realm-core/src/allocator_engine.rs` (generation/activation pattern); `packages/d2b-core/src/unsafe_local_workloads.rs:16–164` (`MAX_UNSAFE_LOCAL_WORKLOADS=256`, etc. — bounds reference for Credential/Host cleanup) | | Reuse source | main `a1cc0b2d`: `packages/d2b-session/src/lifecycle.rs` `begin_reconnect` exponential backoff logic (cleanup retry); `packages/d2b-state/` lock/lease types (ADR046-store-001 dependency for bundle file locking) | -| Reuse action | extract exponential backoff from `begin_reconnect` | +| Reuse action | adapt | | Destination | `packages/d2b-core-controller/src/configuration.rs` (Phase 3 activation, diff, delete dispatch); `packages/d2b-core-controller/src/cleanup.rs` (pending tracking, status, stuck detection, rollback verb handler) | -| Detailed design | Implement all Phase 3 runtime activation checks (§14.10 Phase 3 table); `bundleDigest` integrity verify; `zoneUid` consistency check; `generation` monotone check; per-resource `digest` re-verify; atomic generation advance in `store_meta`; diff computation (resources with `managedBy=configuration` absent from new bundle → async Delete; `managedBy=controller`/`managedBy=api` resources untouched); `managedBy` and `configurationGeneration` field maintenance on resources in redb store; `cleanupPendingCount` and `generationCleanupPending` maintenance; Zone.status.phase → Degraded while cleanup pending, reverts on completion; `GenerationCleanupPending`/`GenerationCleanupFailed` condition management; stuck-cleanup `GenerationCleanupFailed=True` at `cleanupStuckThreshold` (default 5 min) with exponential backoff retry; prior generation bundle retention and pruning up to configured `retainedPriorGenerationCount` (default 3, range 1..16); audit emission for all four cleanup audit kinds (§14.11); `zone.config-rollback` verb handler | +| Detailed design | Implement all Phase 3 runtime activation checks (§14.10 Phase 3 table); `bundleDigest` integrity verify; `zoneUid` consistency check; `generation` monotone check; per-resource `digest` re-verify; atomic generation advance in `store_meta`; diff computation (resources with `managedBy=configuration` absent from new bundle → async Delete; `managedBy=controller`/`managedBy=api` resources untouched); `managedBy` and `configurationGeneration` field maintenance on resources in redb store; `cleanupPendingCount` and `generationCleanupPending` maintenance; Zone.status.phase → Degraded while cleanup pending, reverts on completion; `GenerationCleanupPending`/`GenerationCleanupFailed` condition management; stuck-cleanup `GenerationCleanupFailed=True` at `cleanupStuckThreshold` (default 5 min) with exponential backoff retry; prior generation bundle retention and pruning up to configured `retainedPriorGenerationCount` (default 3, range 1..16); audit emission for all four cleanup audit kinds (§14.11); `zone.config-rollback` verb handler Primary reuse disposition: `adapt`. Preserved source-plan detail: extract exponential backoff from `begin_reconnect`. | | Integration | Zone store / redb (ADR046-store-001); core-controller watch/trigger bus (ADR046-zone-control-011); Zone status writer (ADR046-zone-control-001); audit emitter (§13.2); Credential revocation hook (triggered when `deletionRequestedAt` is set on a Credential and `core.credential-revoke` finalizer is present; revocation completes before finalizer clearance) | | Data migration | Full reset; no prior bundle activation state exists to carry forward | | Validation | All Phase 3 runtime and cleanup tests in §15.8 (`nix-runtime-bundledigest-integrity`, `nix-runtime-generation-monotone`, `nix-runtime-zoneuid-mismatch-rejected`, `nix-runtime-zonename-mismatch-rejected`, `nix-runtime-activation-nonblocking`, `nix-runtime-provider-config-invalid-continues`, all `cleanup-*` and `rollback-*` tests) | | Removal proof | `d2bd/src/lib.rs` config-load at lines 1408 and 16741 removed after Zone configuration publication handler reaches parity; `realm-controller-config-json.nix` and `realm-identity-config-json.nix` Nix bundle-emit removed after resource compiler reaches parity | -### ADR046-pkg-001 +### ADR046-zone-control-026 | Field | Value | | --- | --- | -| Work item ID | `ADR046-pkg-001` | +| Work item ID | `ADR046-zone-control-026` | | Dependency/owner | ADR046-zone-control-003; workspace policy owner | | Current source | `packages/d2b-contract-tests/tests/policy_contracts.rs` lines 5–6 (D2B_FIXTURES gate / workspace-checks integration pattern — `implemented-and-reachable`, baseline `b5ddbed6`); `packages/d2b-contract-tests/tests/static_invariants.rs` (hermetic policy test structure — `implemented-and-reachable`); `tests/tools/rust-workspace-checks.sh` (D2B_FIXTURES step shell harness — `implemented-and-reachable`); AGENTS.md "Naming conventions" section (`-` workspace sort rules — `implemented-and-reachable`); `packages/d2b-realm-core/src/ids.rs` `LABEL_PATTERN` / `MAX_ID_LEN` (name regex reused for crate name token validation — `implemented-and-reachable`) | | Reuse source | None from main; workspace policy tests are repo-specific | -| Reuse action | net-new | +| Reuse action | create | | Destination | `packages/d2b-contract-tests/tests/policy_provider_crate_layout.rs` (new file; gated under D2B_FIXTURES in existing `tests/tools/rust-workspace-checks.sh`) | | Detailed design | Implement `policy_provider_crate_layout.rs` with the following test functions: (1) `every_provider_crate_has_src` — walk `packages/d2b-provider-*/` directories in the workspace, assert each contains `src/`; failure names crate and missing path; (2) `every_provider_crate_has_tests` — assert `tests/` present; (3) `every_provider_crate_has_integration` — assert `integration/` present; (4) `every_provider_crate_has_readme` — assert `README.md` present; (5) `every_provider_readme_has_required_sections` — read `README.md`, check for all nine section headings from §4.8.3 (case-insensitive, after stripping `#` and whitespace); failure names the missing heading(s); (6) `every_integration_file_has_target_declaration` — for each `integration/*.rs` file, scan first 20 lines for exactly one `//! integration-target: (container|host-integration)` declaration; failure names the file and the violation (missing/multiple/invalid value); (7) `non_provider_crates_exempt` — verify the check does not run on non-`d2b-provider-*` crates. All checks are filesystem-only (no compilation). Workspace member list is discovered by parsing `packages/Cargo.toml` `[workspace].members`. Gate: add the new test file to `tests/tools/rust-workspace-checks.sh` D2B_FIXTURES list alongside existing policy tests | | Integration | `make test-policy` and `make check` both fail if any provider crate violates §4.8; consistent with existing `no-bash-ast-walker` and workspace-sort gates; ADR046-zone-control-003 references §4.8 for Provider package conventions | @@ -4494,9 +4494,9 @@ Evidence class for all: `main-reuse-source`. | Dependency/owner | ADR046-zone-control-001, ADR046-provider-004; ADR046-zonelink owner; `d2b-core-controller` + `d2b-contracts` owners | | Current source | None — net-new ADR 0046 cross-Zone sharing model (D096); no pre-ADR45 baseline equivalent | | Reuse source | ZoneLink reconcile/handler scaffolding (§3); `packages/d2b-session/src/streams.rs` `NamedStream` credit/backpressure (bounded encrypted stream carriage) | -| Reuse action | net-new (extend ZoneLink controller) | +| Reuse action | adapt | | Destination | `packages/d2b-contracts/src/v3/{resource_export,resource_import}.rs` (base schemas); `packages/d2b-core-controller/src/export_import.rs` (core ZoneLink export/import routing controller); shared adapter trait in `packages/d2b-provider/src/share_adapter.rs` (`ExportAdapter`/`ImportAdapter` signed-capability traits) | -| Detailed design | Implement the `ResourceExport` and `ResourceImport` standard ResourceTypes per §8A plus signed Provider `ProjectionFactory` metadata binding qualified Service type, qualified Binding type, allowed owner-Service backing refs, allowed Binding target refs, projection schema/fingerprint, and aggregate factory fingerprint. Admission accepts only an owner Service as `ResourceExport.resourceRef`; matches export/import/local-factory type and fingerprints; and creates exactly one same-qualified-type projection Service (`ownerRef: ResourceImport/`). It never projects Device/Endpoint/Binding and never creates Binding. Binding spec is desired consumer intent only; observations belong only in status. No cross-Zone Ref, FD, secret, path, locator, or resource grant crosses a Zone; payload bytes use bounded encrypted named streams and high-churn sessions/streams remain internal. Export removal/ZoneLink loss revokes leases and degrades the projection Service; reconnect revalidates generation and both fingerprints. D091 currency propagates Service → export → import → projection Service → authored Binding → children. | +| Detailed design | Implement the `ResourceExport` and `ResourceImport` standard ResourceTypes per §8A plus signed Provider `ProjectionFactory` metadata binding qualified Service type, qualified Binding type, allowed owner-Service backing refs, allowed Binding target refs, projection schema/fingerprint, and aggregate factory fingerprint. Admission accepts only an owner Service as `ResourceExport.resourceRef`; matches export/import/local-factory type and fingerprints; and creates exactly one same-qualified-type projection Service (`ownerRef: ResourceImport/`). It never projects Device/Endpoint/Binding and never creates Binding. Binding spec is desired consumer intent only; observations belong only in status. No cross-Zone Ref, FD, secret, path, locator, or resource grant crosses a Zone; payload bytes use bounded encrypted named streams and high-churn sessions/streams remain internal. Export removal/ZoneLink loss revokes leases and degrades the projection Service; reconnect revalidates generation and both fingerprints. D091 currency propagates Service → export → import → projection Service → authored Binding → children. Primary reuse disposition: `adapt`. Preserved source-plan detail: net-new (extend ZoneLink controller). | | Integration | Zone store/redb (ADR046-store-001); shared D098 semantic base catalog (ADR046-provider-004); ZoneLink reconcile (§3); ComponentSession bounded encrypted named streams; signed projection factories/adapters for audio-pipewire, device-security-key, observability-otel, and policy-gated device-usbip; CLI graph rendering | | Data migration | None — full d2b 3.0 reset; no prior cross-Zone sharing state | | Validation | §8A.7: fast hermetic factory absent/mismatch/tamper, Service-only export target, exactly-one same-type projection Service, no Device/Endpoint/Binding projection, no auto-Binding, intent-only spec/status-only observations, backing/target allowlists, finalizer/update propagation, Provider classification, canonical Nix stability including compiler-only `d2b.zones.work.parentZone = "local-root"`, child-local `ZoneLink/work-uplink` in `d2b.zones.work.resources`, local `zoneLinkRef` resolution, quotas/reconnect/revoke, and no FD/secret/path tests; slower real encrypted-stream integration for audio/security-key/observability/policy-gated USBIP | @@ -4510,7 +4510,7 @@ Evidence class for all: `main-reuse-source`. | Dependency/owner | ADR046-zone-control-019; `d2b-core-controller` owner | | Current source | None — net-new ADR 0046 cross-Zone sharing model (D096) | | Reuse source | None from main; projection ownership reuses the core owner/child reconcile machinery (§11) | -| Reuse action | net-new | +| Reuse action | create | | Destination | `packages/d2b-core-controller/src/export_import_projection.rs` (local qualified Service projection lifecycle owned by `ResourceImport`) | | Detailed design | Core creates exactly one same-qualified-type projection Service per `ResourceImport` and keeps it synchronized with the remote Service lease. Operators/Nix separately author same-Zone matching Binding resources with `serviceRef` plus an allowed Guest/User/Zone target; Binding specs hold desired intent only and Binding controllers write observations only to status while owning Process/Endpoint children. On revoke, mark the projection draining/revoked and let Binding controllers stop children. On delete, wait for Bindings to be deleted/retargeted (`BindingReferencesRemain`), release the lease, delete only the projection Service/provider-owned children, then clear the import finalizer. Never create/delete Binding or project Device/Endpoint. | | Integration | ADR046-zone-control-019 controller; owner/dependency reconcile (§11, ADR046-reconcile-*); local semantic Provider import adapter | @@ -4526,9 +4526,9 @@ Evidence class for all: `main-reuse-source`. | Dependency/owner | ADR046-zone-control-001, ADR046-zone-control-016; `d2b-core-controller` owner | | Current source | `packages/d2bd/src/` process-global statics `USBIP_BACKGROUND_RECONCILE_ACTIVE`, `FORCE_SHUTDOWN_GENERATIONS`, and `activation_locks()`; current per-VM configuration staging symbols; ZoneLink cursor persistence in `zone_link_cursors` | | Reuse source | ZoneLink handler/cursor scaffolding (§3); core coordinator patterns | -| Reuse action | `adapt` (move process-global state to per-Zone status/coordinator) | +| Reuse action | adapt | | Destination | `packages/d2b-core-controller/src/{coordinator,configuration,zonelink}.rs` | -| Detailed design | Per D097 core-audit findings (§8B.2): move the process-global `USBIP_BACKGROUND_RECONCILE_ACTIVE`, `FORCE_SHUTDOWN_GENERATIONS`, and `activation_locks()` state into **per-Zone** provider/resource status or a per-Zone coordinator keyed by the authority index (no process-global singletons that ignore Zone boundaries). Migrate the configuration publisher's per-VM staging symbols to **per-Zone** staging under the single configuration-publisher authority. Make ZoneLink cursor persistence and restart adoption an authority owned by the ZoneLink handler (`ownerProof`; ambiguity quarantines). All coordinated through the core authority index; no direct broker path, no process-global lock. | +| Detailed design | Per D097 core-audit findings (§8B.2): move the process-global `USBIP_BACKGROUND_RECONCILE_ACTIVE`, `FORCE_SHUTDOWN_GENERATIONS`, and `activation_locks()` state into **per-Zone** provider/resource status or a per-Zone coordinator keyed by the authority index (no process-global singletons that ignore Zone boundaries). Migrate the configuration publisher's per-VM staging symbols to **per-Zone** staging under the single configuration-publisher authority. Make ZoneLink cursor persistence and restart adoption an authority owned by the ZoneLink handler (`ownerProof`; ambiguity quarantines). All coordinated through the core authority index; no direct broker path, no process-global lock. Primary reuse disposition: `adapt`. Preserved source-plan detail: `adapt` (move process-global state to per-Zone status/coordinator). | | Integration | Core authority index (ADR046-zone-control-019); ZoneLink reconcile (§3); configuration publication handler (ADR046-zone-control-016) | | Data migration | Full d2b 3.0 reset; no process-global state persisted across the cutover | | Validation | Two Zones on one host do not share `USBIP_BACKGROUND_RECONCILE_ACTIVE`/`FORCE_SHUTDOWN_GENERATIONS`/activation-lock state; per-Zone configuration staging isolation; ZoneLink cursor adoption by `ownerProof` and quarantine on ambiguity; hermetic with fakes | @@ -4542,7 +4542,7 @@ Evidence class for all: `main-reuse-source`. | Dependency/owner | ADR046-zone-control-019, ADR046-api-001; `d2b-core-controller` + resource API owners | | Current source | None — net-new D097 admission (Provider cardinality) | | Reuse source | Core authority index (ADR046-zone-control-019); resource API admission (`ADR-046-resource-api-and-authorization`) | -| Reuse action | net-new | +| Reuse action | adapt | | Destination | `packages/d2b-core-controller/src/authority.rs`; resource API admission hook | | Detailed design | Admission enforces **Provider controller cardinality** via the core authority index: most Providers are `exactly-one` per Zone; the observability Provider is `at-most-one` (zero-or-one). A `Create`/activation that would install a second controller for an `exactly-one` Provider (or a second observability Provider) is rejected with `duplicateConflict` naming the incumbent owner digest before any effect; config activation goes `Degraded`. | | Integration | Core authority index; resource API admission; configuration activation | @@ -4558,9 +4558,9 @@ Evidence class for all: `main-reuse-source`. | Dependency/owner | ADR046-zone-control-019; `d2b-core-controller` + `d2b-contracts` owners | | Current source | None — `Quota` and `EmergencyPolicy` scope-uniqueness are specified (§7, §8) but not implemented/tested at baseline | | Reuse source | `Quota`/`EmergencyPolicy` schemas (§7, §8); core authority index | -| Reuse action | net-new (implementation + tests) | +| Reuse action | adapt | | Destination | `packages/d2b-core-controller/src/{quota,emergency_policy}.rs`; `packages/d2b-contracts/src/v3/{quota,emergency_policy}.rs` | -| Detailed design | Implement `Quota` and `EmergencyPolicy` scope-uniqueness as `exactly-one`-per-scope authorities in the core authority index: a second `Quota`/`EmergencyPolicy` claiming the same scope is a `duplicateConflict`. Add the scope-uniqueness admission, status, and the test matrix (per D094 fast hermetic tests). | +| Detailed design | Implement `Quota` and `EmergencyPolicy` scope-uniqueness as `exactly-one`-per-scope authorities in the core authority index: a second `Quota`/`EmergencyPolicy` claiming the same scope is a `duplicateConflict`. Add the scope-uniqueness admission, status, and the test matrix (per D094 fast hermetic tests). Primary reuse disposition: `adapt`. Preserved source-plan detail: net-new (implementation + tests). | | Integration | Core authority index (ADR046-zone-control-019); resource API admission | | Data migration | None — full d2b 3.0 reset | | Validation | Duplicate-scope `Quota`/`EmergencyPolicy` rejected with `duplicateConflict`; single-scope admitted; union/individual scope flags honored; fast hermetic tests | @@ -4574,9 +4574,9 @@ Evidence class for all: `main-reuse-source`. | Dependency/owner | ADR046-zone-control-019, ADR046-zone-control-022; `d2b-core-controller` owner | | Current source | None — net-new D097 hardware-audit contract; today physical/kernel backings are guarded per-Zone or per-process, not Host-global | | Reuse source | Core authority index (ADR046-zone-control-019) | -| Reuse action | net-new (Host-global index scope for host/physical-device authorities) | +| Reuse action | adapt | | Destination | `packages/d2b-core-controller/src/authority.rs` (Host-global index scope + hardware admission) | -| Detailed design | Extend the core authority index so `host`, `physical-device`, `seat`, and `external-service` authorities are keyed **Host-global** (`(Host, authorityClass, opaqueKeyDigest)`), admitting exactly one owner across all Zones on the host, while `zone`-scoped authorities stay Zone-local. Enforce the §8B.3 hardware rows: GPU full-device exclusive vs render-node shared; per-Guest swtpm and physical TPM exclusive (state never wiped); one Core-derived `physical-usb-backing/v1` identity digest claimed through the exact `(Host, physical-usb-backing, opaqueKeyDigest)` tuple by every USB or security-key implementation before effects, plus the separate host-global `usbip-host` module; macvtap/NIC `parentInterface` `passthru` globally exclusive across all Zones; host-shared `/dev/kvm` and `/dev/vhost-vsock` as `Provider/system-core` grants (no 28th Provider, no `kvm` busClass); globally-unique vsock CID; fixed listener ports as `Endpoint`s; host store + per-Guest store-view writer; Network TAP/bridge. A second Zone claiming the same physical backing receives `physical-usb-backing-conflict` before any open, bind, withhold, module, relay, or attachment effect; restart adopts by `ownerProof`; Guest-stop drains dependent leases. GPU-owned `udmabuf`/video and per-session `vhost-vsock` tokens stay authority subresources/DeviceGrants (not resources/Providers). | +| Detailed design | Extend the core authority index so `host`, `physical-device`, `seat`, and `external-service` authorities are keyed **Host-global** (`(Host, authorityClass, opaqueKeyDigest)`), admitting exactly one owner across all Zones on the host, while `zone`-scoped authorities stay Zone-local. Enforce the §8B.3 hardware rows: GPU full-device exclusive vs render-node shared; per-Guest swtpm and physical TPM exclusive (state never wiped); one Core-derived `physical-usb-backing/v1` identity digest claimed through the exact `(Host, physical-usb-backing, opaqueKeyDigest)` tuple by every USB or security-key implementation before effects, plus the separate host-global `usbip-host` module; macvtap/NIC `parentInterface` `passthru` globally exclusive across all Zones; host-shared `/dev/kvm` and `/dev/vhost-vsock` as `Provider/system-core` grants (no 28th Provider, no `kvm` busClass); globally-unique vsock CID; fixed listener ports as `Endpoint`s; host store + per-Guest store-view writer; Network TAP/bridge. A second Zone claiming the same physical backing receives `physical-usb-backing-conflict` before any open, bind, withhold, module, relay, or attachment effect; restart adopts by `ownerProof`; Guest-stop drains dependent leases. GPU-owned `udmabuf`/video and per-session `vhost-vsock` tokens stay authority subresources/DeviceGrants (not resources/Providers). Primary reuse disposition: `adapt`. Preserved source-plan detail: net-new (Host-global index scope for host/physical-device authorities). | | Integration | Core authority index (ADR046-zone-control-019); Provider cardinality admission (ADR046-zone-control-022); `Provider/system-core` KVM/vhost-vsock grant; `Provider/device-*` and `Network` authorities | | Data migration | None — full d2b 3.0 reset | | Validation | Two Zones on one host cannot both claim one GPU/TPM/USB/`/dev/kvm`/passthru NIC/vsock CID/fixed port — second is `duplicateConflict`; security-key and USB implementations resolving the same physical token submit byte-identical `physical-usb-backing` tuple keys and the loser receives `physical-usb-backing-conflict` before any effect; Provider-private authority classes/digests cannot bypass the collision; render-node shared admits bounded holders; per-Guest swtpm exclusive and marker never wiped; host-global adoption by `ownerProof`; hardware D096 exportability (GPU/KVM/TPM/store/macvtap non-exportable; semantic USB policy-gated); fast hermetic with fakes | diff --git a/docs/specs/ADR-046-security-and-threat-model.md b/docs/specs/ADR-046-security-and-threat-model.md index 8cabdb550..714c999a4 100644 --- a/docs/specs/ADR-046-security-and-threat-model.md +++ b/docs/specs/ADR-046-security-and-threat-model.md @@ -1548,12 +1548,12 @@ targeted. **`d2b zone doctor`.** Resource status reads only; no resource names, paths, `argv`, or PIDs in output; includes an audit hash-chain integrity check -(`ADR-046-telemetry-audit-and-support`, `ADR046-doctor-001`). +(`ADR-046-telemetry-audit-and-support`, `ADR046-telem-016`). **`d2b zone support-bundle`.** No spec bytes and no `metadata.name` in the bundle; metadata and status only. When a Provider is quarantined, the bundle reports `bundle_completeness: "partial"` rather than silently omitting the -gap or blocking entirely (`ADR046-doctor-002`). This is the concrete tool an +gap or blocking entirely (`ADR046-telem-017`). This is the concrete tool an operator or a coordinated-disclosure responder uses to gather evidence without themselves becoming a redaction bypass — it is bound by exactly the same audit/status redaction rules as every other read path in @@ -1562,7 +1562,7 @@ same audit/status redaction rules as every other read path in **`d2b zone audit export`.** Admin-only (`audit-export` verb); hash-chain breaks are reported inline in the export stream rather than silently truncating history; output carries no old field names (`realm`/`node`/ -`workload_id`) and no path/`argv` content (`ADR046-audit-004`). +`workload_id`) and no path/`argv` content (`ADR046-telem-015`). **Coordination with disclosure policy.** `SECURITY.md`'s GitHub Security Advisory channel, response-time targets (7-day acknowledgment, 30-day @@ -1914,9 +1914,9 @@ close. Each maps to the attacker class it is scoped against. | Dependency/owner | All ResourceType/Provider specs; owned by the security/telemetry integrator alongside `ADR046-telem-008` | | Current source | `packages/d2b-contract-tests/tests/policy_observability.rs` (existing v3 cardinality/label policy gate) | | Reuse source | None (new cross-cutting gate; no equivalent exists in main) | -| Reuse action | extract and adapt | +| Reuse action | adapt | | Destination | `packages/d2b-contract-tests/tests/policy_telemetry_redaction.rs` | -| Detailed design | One policy test enumerating every forbidden metric-label/audit-field value from §21 and the content-secrecy table in §20 (store paths, `no_isolation`, credential bytes, raw paths/argv/PID/cgroup, CTAP/clipboard/terminal/notification content) and asserting, by static scan of instrumentation call sites plus a redaction-guard runtime test, that no `ADR046-*` Provider crate emits any of them | +| Detailed design | One policy test enumerating every forbidden metric-label/audit-field value from §21 and the content-secrecy table in §20 (store paths, `no_isolation`, credential bytes, raw paths/argv/PID/cgroup, CTAP/clipboard/terminal/notification content) and asserting, by static scan of instrumentation call sites plus a redaction-guard runtime test, that no `ADR046-*` Provider crate emits any of them Primary reuse disposition: `adapt`. Preserved source-plan detail: extract and adapt. | | Integration | Runs as part of `make test-lint`/`make test-rust`; every Provider crate's own redaction test (e.g. `tests/stream_redaction.rs`) is a per-Provider instance of the same closed list | | Data migration | None — full d2b 3.0 reset; no prior state to migrate | | Validation | Hermetic (`cargo test -p d2b-contract-tests policy_telemetry_redaction`); fails the build if a new Provider crate is added without a corresponding redaction test file under its `tests/` | @@ -1927,12 +1927,12 @@ close. Each maps to the attacker class it is scoped against. | Field | Value | | --- | --- | | Work item ID | `ADR046-security-002` | -| Dependency/owner | `ADR046-session-001`/`ADR046-bus-001` (ComponentSession/d2b-bus implementation) | +| Dependency/owner | `ADR046-session-001`/`ADR046-session-003` (ComponentSession/d2b-bus implementation) | | Current source | main `a1cc0b2d`: `d2b-session/tests/noise_vectors.rs`, `d2b-session/tests/component_session.rs`, `d2b-session-unix/tests/unix_session.rs` | | Reuse source | Same main commit/paths | -| Reuse action | copy and adapt | +| Reuse action | adapt | | Destination | `packages/d2b-session/tests/noise_conformance.rs`, `packages/d2b-session/fuzz/fuzz_targets/{handshake_offer,record_frame}.rs` | -| Detailed design | Property/fuzz test suite over the three Noise profiles (§7): exact NN/KK/IKpsk2 vectors and rejection mutations (copied), plus new `cargo-fuzz` targets mutating the canonical handshake offer, preface, and encrypted record frame to assert no panic/UB and that every malformed input is a typed rejection (never a partial accept) | +| Detailed design | Property/fuzz test suite over the three Noise profiles (§7): exact NN/KK/IKpsk2 vectors and rejection mutations (copied), plus new `cargo-fuzz` targets mutating the canonical handshake offer, preface, and encrypted record frame to assert no panic/UB and that every malformed input is a typed rejection (never a partial accept) Primary reuse disposition: `adapt`. Preserved source-plan detail: copy and adapt. | | Integration | Wired into `make test-rust` (vectors) and a separate `make test-fuzz` target (new; time-boxed nightly run, not part of the PR-blocking gate) | | Data migration | None — full d2b 3.0 reset; no prior state to migrate | | Validation | Hermetic vector tests plus fuzz corpus with a minimum 4-hour nightly run and zero crashes/hangs as acceptance | @@ -1946,9 +1946,9 @@ close. Each maps to the attacker class it is scoped against. | Dependency/owner | `ADR046-zone-control-004`/`ADR046-zone-control-005` (Role/RoleBinding implementation) | | Current source | `packages/d2bd/src/admission.rs` (`verb_requires_admin()` baseline verb table) | | Reuse source | None beyond the verb-table adaptation already tracked by `ADR046-zone-control-004` | -| Reuse action | extract and adapt | +| Reuse action | adapt | | Destination | `packages/d2b-resource-store/tests/rbac_property.rs` | -| Detailed design | Property test asserting, for a randomly generated Role/RoleBinding/request corpus: (1) no request whose payload sets a subject/role field ever changes the resolved `AuthenticatedSubjectContext.subjectRef`; (2) no non-core Role with a wildcard grant is ever admitted; (3) `scopeNarrowing` never widens beyond the referenced Role; (4) RoleBinding deletion never leaves an observable intermediate state under concurrent readers | +| Detailed design | Property test asserting, for a randomly generated Role/RoleBinding/request corpus: (1) no request whose payload sets a subject/role field ever changes the resolved `AuthenticatedSubjectContext.subjectRef`; (2) no non-core Role with a wildcard grant is ever admitted; (3) `scopeNarrowing` never widens beyond the referenced Role; (4) RoleBinding deletion never leaves an observable intermediate state under concurrent readers Primary reuse disposition: `adapt`. Preserved source-plan detail: extract and adapt. | | Integration | Runs against the real redb-backed resource store test harness, not a mock | | Data migration | None — full d2b 3.0 reset; no prior state to migrate | | Validation | Hermetic property test (`proptest`/`quickcheck`-style, minimum 10,000 cases per property) | @@ -1959,12 +1959,12 @@ close. Each maps to the attacker class it is scoped against. | Field | Value | | --- | --- | | Work item ID | `ADR046-security-004` | -| Dependency/owner | `ADR046-routing-005`/`ADR046-bus-001` (ZoneLink/d2b-bus relay implementation) | +| Dependency/owner | `ADR046-routing-005`/`ADR046-session-003` (ZoneLink/d2b-bus relay implementation) | | Current source | None (v3 ZoneLink relay is `ADR-only`) | | Reuse source | None | -| Reuse action | extract and adapt (design copied from `ADR-046-zone-routing.md` structural-rejection sections) | +| Reuse action | adapt | | Destination | `packages/d2b-bus/fuzz/fuzz_targets/zonelink_frame.rs`, `packages/d2b-bus/tests/zonelink_structural_rejection.rs` | -| Detailed design | Fuzz + property suite asserting that no mutation of a ZoneLink-bound frame (attachment count, credential-shaped byte runs, path-shaped strings, PID-shaped integers) is ever forwarded — every such mutation is rejected at serialization with `attachment-not-permitted-over-zone-link` or the transport-specific equivalent, never silently dropped or partially forwarded | +| Detailed design | Fuzz + property suite asserting that no mutation of a ZoneLink-bound frame (attachment count, credential-shaped byte runs, path-shaped strings, PID-shaped integers) is ever forwarded — every such mutation is rejected at serialization with `attachment-not-permitted-over-zone-link` or the transport-specific equivalent, never silently dropped or partially forwarded Primary reuse disposition: `adapt`. Preserved source-plan detail: extract and adapt (design copied from `ADR-046-zone-routing.md` structural-rejection sections). | | Integration | `make test-fuzz`; a companion container test (`tests/integration/containers/zonelink-cross-zone.rs`) runs two real Zone runtime containers connected by a real ZoneLink and asserts the same property end to end over the wire, not just in the frame-serialization unit | | Data migration | None — full d2b 3.0 reset; no prior state to migrate | | Validation | Fuzz corpus (`cargo fuzz run zonelink_frame -- -runs=1000000`, zero crashes); container test passes in `make test-integration` | @@ -2010,9 +2010,9 @@ close. Each maps to the attacker class it is scoped against. | Dependency/owner | `ADR046-minijail-005`, `ADR046-systemd-002`, `ADR046-aca-001`, `ADR046-volume-001` (every quarantine-on-ambiguity implementation) | | Current source | `packages/d2b-priv-broker/src/ops/swtpm_dir.rs` (existing fail-closed marker/quarantine pattern, `implemented-and-reachable`) | | Reuse source | Same v3 path, generalized | -| Reuse action | extract and adapt | +| Reuse action | adapt | | Destination | `packages/d2b-contract-tests/tests/quarantine_not_kill_matrix.rs` | -| Detailed design | One parameterized fault-injection matrix test, run once per adoption-capable Provider (`system-minijail`, `system-systemd`, `runtime-cloud-hypervisor`, `runtime-azure-container-apps`, `volume-local`), that restarts the controller with a deliberately ambiguous adoption candidate (duplicate InvocationID, mismatched marker inode, stale ACA operation handle) and asserts: (a) the resource transitions to `Degraded`/`Quarantined`, never `Deleted` or silently re-adopted; (b) no signal is sent to the ambiguous candidate process; (c) a `runtime-security-violation`-class audit record is emitted | +| Detailed design | One parameterized fault-injection matrix test, run once per adoption-capable Provider (`system-minijail`, `system-systemd`, `runtime-cloud-hypervisor`, `runtime-azure-container-apps`, `volume-local`), that restarts the controller with a deliberately ambiguous adoption candidate (duplicate InvocationID, mismatched marker inode, stale ACA operation handle) and asserts: (a) the resource transitions to `Degraded`/`Quarantined`, never `Deleted` or silently re-adopted; (b) no signal is sent to the ambiguous candidate process; (c) a `runtime-security-violation`-class audit record is emitted Primary reuse disposition: `adapt`. Preserved source-plan detail: extract and adapt. | | Integration | `make test-rust` for the in-process cases; `make test-host-integration` for the real-pidfd/real-cgroup cases (`tests/host-integration/quarantine-not-kill.nix`) | | Data migration | None — full d2b 3.0 reset; no prior state to migrate | | Validation | Matrix covers all five Providers listed; acceptance is 100% pass across all five with no signal sent to the ambiguous candidate in any case | @@ -2058,9 +2058,9 @@ close. Each maps to the attacker class it is scoped against. | Dependency/owner | `ADR046-credential-001` through `ADR046-credential-007` | | Current source | None (zero-secret invariant has no existing automated gate) | | Reuse source | None | -| Reuse action | extract and adapt (design from `ADR-046-resources-credential.md` §1.1) | +| Reuse action | adapt | | Destination | `packages/d2b-contract-tests/tests/zero_secret_invariant.rs` | -| Detailed design | Static + dynamic gate: (1) static — every DTO type reachable from a `Credential`-adjacent module must implement a hand-written redacted `Debug` and must not derive `Debug`, enforced by a `#[forbid(clippy::derive_debug_ambient)]`-style custom lint or an `xtask` AST scan; (2) dynamic — a property test that generates random `Credential` delivery sessions and asserts the delivered token/`SignChallenge` byte sequence never appears, byte-for-byte, in any captured audit record, OTEL span, log line, or resource-store row taken during the same test run | +| Detailed design | Static + dynamic gate: (1) static — every DTO type reachable from a `Credential`-adjacent module must implement a hand-written redacted `Debug` and must not derive `Debug`, enforced by a `#[forbid(clippy::derive_debug_ambient)]`-style custom lint or an `xtask` AST scan; (2) dynamic — a property test that generates random `Credential` delivery sessions and asserts the delivered token/`SignChallenge` byte sequence never appears, byte-for-byte, in any captured audit record, OTEL span, log line, or resource-store row taken during the same test run Primary reuse disposition: `adapt`. Preserved source-plan detail: extract and adapt (design from `ADR-046-resources-credential.md` §1.1). | | Integration | `make test-lint` (static scan) and `make test-rust` (dynamic property test) | | Data migration | None — full d2b 3.0 reset; no prior state to migrate | | Validation | Hermetic; the dynamic test additionally runs as a canary-byte test (a unique random marker is embedded in the token and searched for across every observability surface) | @@ -2087,7 +2087,7 @@ close. Each maps to the attacker class it is scoped against. | Field | Value | | --- | --- | | Work item ID | `ADR046-security-012` | -| Dependency/owner | `ADR046-audit-002` (privileged audit durability) | +| Dependency/owner | `ADR046-telem-013` (privileged audit durability) | | Current source | `packages/d2bd/src/daemon_audit.rs` (existing audit-write path, adapted target) | | Reuse source | Same v3 path | | Reuse action | adapt | @@ -2106,9 +2106,9 @@ close. Each maps to the attacker class it is scoped against. | Dependency/owner | `ADR046-zone-control-009`/`ADR046-zone-control-010` (Quota/EmergencyPolicy), `ADR046-session-001` (session limits) | | Current source | None new; ceiling values are already enumerated in `ADR-046-zone-routing.md`/`ADR-046-cli-and-operations.md` | | Reuse source | main `a1cc0b2d`: `d2b-session` credit-accounting/priority-scheduling tests, copied for the checked-arithmetic/priority-ordering assertions | -| Reuse action | copy and adapt | +| Reuse action | adapt | | Destination | `packages/d2b-bus/tests/dos_ceiling_fault_injection.rs` | -| Detailed design | Fault-injection/load test suite: (1) attachment-credit exhaustion at each of the six scopes (Packet/Request/Operation/Session/Process/Host), asserting typed rejection never a panic; (2) reconnect-storm exceeding `MAX_RECONNECT_ATTEMPTS`/`MAX_RECONNECT_WINDOW_MS`, asserting the session fails closed rather than looping; (3) ZoneLink hop-count/route-advertisement replay flood, asserting `hop-limit-exceeded`/`zone-advertisement-replay` rather than unbounded forwarding; (4) a stalled data stream under load, asserting control/cancellation traffic is never starved (priority-scheduling property) | +| Detailed design | Fault-injection/load test suite: (1) attachment-credit exhaustion at each of the six scopes (Packet/Request/Operation/Session/Process/Host), asserting typed rejection never a panic; (2) reconnect-storm exceeding `MAX_RECONNECT_ATTEMPTS`/`MAX_RECONNECT_WINDOW_MS`, asserting the session fails closed rather than looping; (3) ZoneLink hop-count/route-advertisement replay flood, asserting `hop-limit-exceeded`/`zone-advertisement-replay` rather than unbounded forwarding; (4) a stalled data stream under load, asserting control/cancellation traffic is never starved (priority-scheduling property) Primary reuse disposition: `adapt`. Preserved source-plan detail: copy and adapt. | | Integration | `make test-rust`; item (4) additionally runs as a container load test (`tests/integration/containers/backpressure-priority.rs`) with a real slow consumer | | Data migration | None — full d2b 3.0 reset; no prior state to migrate | | Validation | Hermetic fault-injection suite; container load test; acceptance is zero panics/unbounded-growth across all four scenarios | @@ -2119,12 +2119,12 @@ close. Each maps to the attacker class it is scoped against. | Field | Value | | --- | --- | | Work item ID | `ADR046-security-014` | -| Dependency/owner | `ADR046-doctor-001`/`ADR046-doctor-002` (doctor/support-bundle commands) | +| Dependency/owner | `ADR046-telem-016`/`ADR046-telem-017` (doctor/support-bundle commands) | | Current source | None (both commands are `ADR-only`) | | Reuse source | None | -| Reuse action | extract and adapt (design from `ADR-046-telemetry-audit-and-support.md`) | +| Reuse action | adapt | | Destination | `packages/d2b/src/commands/{doctor,support_bundle}.rs` | -| Detailed design | `d2b zone doctor` performs read-only status/audit-hash-chain checks with the redaction rules from §21 enforced on every field it prints; `d2b zone support-bundle` assembles a bounded archive of metadata+status (never spec bytes or `metadata.name`) and sets `bundle_completeness: "partial"` when any Provider in scope is quarantined, rather than omitting the gap silently | +| Detailed design | `d2b zone doctor` performs read-only status/audit-hash-chain checks with the redaction rules from §21 enforced on every field it prints; `d2b zone support-bundle` assembles a bounded archive of metadata+status (never spec bytes or `metadata.name`) and sets `bundle_completeness: "partial"` when any Provider in scope is quarantined, rather than omitting the gap silently Primary reuse disposition: `adapt`. Preserved source-plan detail: extract and adapt (design from `ADR-046-telemetry-audit-and-support.md`). | | Integration | `make test-rust` (CLI integration tests); a container test (`tests/integration/containers/support-bundle-quarantined.rs`) runs a real Zone with one quarantined Provider and asserts the bundle correctly reports `partial` | | Data migration | None — full d2b 3.0 reset; no prior state to migrate | | Validation | Hermetic CLI test asserting no spec byte or `metadata.name` appears in a generated bundle; container test for the quarantined-Provider case | @@ -2135,12 +2135,12 @@ close. Each maps to the attacker class it is scoped against. | Field | Value | | --- | --- | | Work item ID | `ADR046-security-015` | -| Dependency/owner | `ADR046-audit-001` (`StateReset` record), Volume/Credential lifecycle work items | +| Dependency/owner | `ADR046-telem-012` (`StateReset` record), Volume/Credential lifecycle work items | | Current source | Historical main ADR 0045 factory-reset design (`a1cc0b2d^:docs/adr/0045-provider-and-transport-framework.md`, reset process overview and apply-command verification steps) — reused only as a design precedent for atomicity/fail-closed sequencing, not as v3 architecture (see [Reset boundary](#reset-boundary) for the explicitly excluded assumptions) | | Reuse source | Same historical commit, sequencing pattern only (no code reuse; historical implementation was bash/systemd-generation-based and does not exist in any Rust crate) | -| Reuse action | adapt (pattern only) | +| Reuse action | adapt | | Destination | `packages/d2b-core-controller/src/reset.rs`, `packages/d2b-core-controller/tests/reset_atomicity.rs` | -| Detailed design | Implements the `scope` (`zone`, `provider`, `host`, or `guest`) `StateReset` flow from §25: quiesce via `EmergencyPolicy`, revoke open Credential leases in scope, destroy Volumes in scope (key-shred first), commit the `StateReset` audit record durably, and only then report the reset complete. A crash-recovery path re-derives "was this reset already committed?" solely from the durable `StateReset` record, never from partial filesystem state | +| Detailed design | Implements the `scope` (`zone`, `provider`, `host`, or `guest`) `StateReset` flow from §25: quiesce via `EmergencyPolicy`, revoke open Credential leases in scope, destroy Volumes in scope (key-shred first), commit the `StateReset` audit record durably, and only then report the reset complete. A crash-recovery path re-derives "was this reset already committed?" solely from the durable `StateReset` record, never from partial filesystem state Primary reuse disposition: `adapt`. Preserved source-plan detail: adapt (pattern only). | | Integration | `make test-rust` (unit-level state machine); a host/KVM integration test (`tests/host-integration/reset-atomicity.nix`) kills the process mid-reset at each of the four phases (quiesce, credential revoke, Volume destroy, audit commit) and asserts recovery never double-destroys, never silently completes without the audit record, and never leaves an orphaned sealed-Volume-without-key state | | Data migration | None (v3-native; no v1/v2 reset-generation state to migrate) | | Validation | Hermetic state-machine test; host/KVM crash-injection test at all four phases; acceptance is zero non-atomic outcomes across all injected crash points | @@ -2154,9 +2154,9 @@ close. Each maps to the attacker class it is scoped against. | Dependency/owner | Documentation/CI integrator; depends on every Provider dossier's own `Security` section existing | | Current source | None (cross-reference check does not exist) | | Reuse source | None | -| Reuse action | adapt (pattern from `tests/unit/gates/drift-check.sh`) | +| Reuse action | adapt | | Destination | `tests/unit/gates/security-matrix-coverage.sh` | -| Detailed design | A drift-style gate that parses [Per-ResourceType threat matrix](#per-resourcetype-threat-matrix) and [Per-Provider-family threat matrix](#per-provider-family-threat-matrix), confirms every one of the 19 standard ResourceTypes and all 27 Provider dossiers under `docs/specs/providers/` has a row, and confirms every referenced dossier file actually contains a `## Security`-class section (by heading grep) — failing the gate if a new ResourceType/Provider is added without a corresponding row and dossier section | +| Detailed design | A drift-style gate that parses [Per-ResourceType threat matrix](#per-resourcetype-threat-matrix) and [Per-Provider-family threat matrix](#per-provider-family-threat-matrix), confirms every one of the 19 standard ResourceTypes and all 27 Provider dossiers under `docs/specs/providers/` has a row, and confirms every referenced dossier file actually contains a `## Security`-class section (by heading grep) — failing the gate if a new ResourceType/Provider is added without a corresponding row and dossier section Primary reuse disposition: `adapt`. Preserved source-plan detail: adapt (pattern from `tests/unit/gates/drift-check.sh`). | | Integration | `make test-drift` | | Data migration | None — full d2b 3.0 reset; no prior state to migrate | | Validation | Hermetic shell-script gate; a negative test adds a scratch Provider dossier missing a Security section and asserts the gate fails | @@ -2170,9 +2170,9 @@ close. Each maps to the attacker class it is scoped against. | Dependency/owner | `ADR046-routing-004`, gateway-custody Provider work items (`ADR046-aca-*`, `ADR046-azure-vm-*`, `ADR046-transport-relay-*`) | | Current source | None — net-new v3 work; no pre-ADR45 baseline equivalent | | Reuse source | None | -| Reuse action | extract and adapt | +| Reuse action | adapt | | Destination | `tests/integration/containers/malicious-child-zone.rs` | -| Detailed design | Container-based penetration test running a real parent Zone and a deliberately malicious child Zone container that attempts, over a real ZoneLink: FD smuggling, credential-shaped byte injection, cross-Zone `ownerRef` forgery, capability-ceiling widening claims, and route-advertisement replay. Every attempt must be rejected by the parent with the specific typed error named in §10, and none may reach the parent's resource store, Credential state, or Host substrate | +| Detailed design | Container-based penetration test running a real parent Zone and a deliberately malicious child Zone container that attempts, over a real ZoneLink: FD smuggling, credential-shaped byte injection, cross-Zone `ownerRef` forgery, capability-ceiling widening claims, and route-advertisement replay. Every attempt must be rejected by the parent with the specific typed error named in §10, and none may reach the parent's resource store, Credential state, or Host substrate Primary reuse disposition: `adapt`. Preserved source-plan detail: extract and adapt. | | Integration | `make test-integration` (requires podman, per `AGENTS.md` "Local Layer 1 + container integration") | | Data migration | None — full d2b 3.0 reset; no prior state to migrate | | Validation | Container integration test; acceptance is zero successful attacks across all five attempted vectors | @@ -2186,9 +2186,9 @@ close. Each maps to the attacker class it is scoped against. | Dependency/owner | Manual validation owner; depends on `ADR046-azure-vm-*`, `ADR046-transport-relay-*`, `ADR046-aca-*`, device Provider work items reaching a testable state | | Current source | None (manual cloud/hardware validation has no automated equivalent by design) | | Reuse source | None | -| Reuse action | adapt (checklist pattern from `SECURITY.md`'s existing portability-roadmap manual milestones and `tests/README.md`'s manual hardware tier) | +| Reuse action | adapt | | Destination | `docs/reference/security-manual-validation-checklist.md` (new reference doc, out of scope for this spec's own file but named here as the required destination for the future implementation PR) | -| Detailed design | A checklist covering the scenarios that cannot be hermetically or even container-tested: (1) real Azure Container Apps/Azure VM credential rotation and revocation under `AzureEffectPort`, confirming zeroization on a real managed-identity/Entra token; (2) real TPM 2.0 hardware NVRAM persistence/tamper-marker behavior across a real host reboot; (3) real USBIP/security-key hardware mutual-exclusion enforcement with a physical FIDO2 device; (4) real Azure Relay listener/sender credential acquisition and relay-identity-not-local-auth verification against a live relay namespace | +| Detailed design | A checklist covering the scenarios that cannot be hermetically or even container-tested: (1) real Azure Container Apps/Azure VM credential rotation and revocation under `AzureEffectPort`, confirming zeroization on a real managed-identity/Entra token; (2) real TPM 2.0 hardware NVRAM persistence/tamper-marker behavior across a real host reboot; (3) real USBIP/security-key hardware mutual-exclusion enforcement with a physical FIDO2 device; (4) real Azure Relay listener/sender credential acquisition and relay-identity-not-local-auth verification against a live relay namespace Primary reuse disposition: `adapt`. Preserved source-plan detail: adapt (checklist pattern from `SECURITY.md`'s existing portability-roadmap manual milestones and `tests/README.md`'s manual hardware tier). | | Integration | Run manually before each tagged release touching a cloud/hardware Provider, per the existing `tests/README.md` manual-tier convention | | Data migration | None — full d2b 3.0 reset; no prior state to migrate | | Validation | Checklist sign-off recorded in the release's validation evidence, not a CI gate (matches `D2b_LIVE=1` manual-tier precedent in `AGENTS.md`) | @@ -2202,9 +2202,9 @@ close. Each maps to the attacker class it is scoped against. | Dependency/owner | `ADR046-exec-003`/`ADR046-exec-007` and `ADR046-minijail-003` through `ADR046-minijail-005`; system-minijail/broker integration owner | | Current source | `packages/d2bd/src/supervisor/pidfd_table.rs` (`BrokerReapLog`) and `packages/d2b-priv-broker/src/sys.rs` (`clone3_spawn_runner`) | | Reuse source | Same v3 paths, adapted to the corrected ownership contract | -| Reuse action | extract and adapt | +| Reuse action | adapt | | Destination | `packages/d2b-contract-tests/tests/minijail_process_ownership.rs`; `tests/host-integration/minijail-cgroup-kill.nix` | -| Detailed design | Hermetic contract test proves only the broker that called `clone3` can produce the identity-bound `BrokerTerminalResult`; a non-parent poll-readable pidfd cannot be converted to status, while a verified duplicate holder can still request exact-main `pidfd_send_signal`. Host integration launches an owned descendant that calls `setsid(2)` plus an unrelated recycled-PGID decoy, performs graceful exact-main stop followed by anchored leaf `cgroup.kill`, and proves the owned leaf reaches `populated 0`, the broker reaps exactly once, the decoy survives, and rmdir/finalizer clearing wait for both proofs. Negative cases prove ambiguous adoption emits no signal/`cgroup.kill`, and Linux <5.14 or missing/unwritable `cgroup.kill` fails before spawn. | +| Detailed design | Hermetic contract test proves only the broker that called `clone3` can produce the identity-bound `BrokerTerminalResult`; a non-parent poll-readable pidfd cannot be converted to status, while a verified duplicate holder can still request exact-main `pidfd_send_signal`. Host integration launches an owned descendant that calls `setsid(2)` plus an unrelated recycled-PGID decoy, performs graceful exact-main stop followed by anchored leaf `cgroup.kill`, and proves the owned leaf reaches `populated 0`, the broker reaps exactly once, the decoy survives, and rmdir/finalizer clearing wait for both proofs. Negative cases prove ambiguous adoption emits no signal/`cgroup.kill`, and Linux <5.14 or missing/unwritable `cgroup.kill` fails before spawn. Primary reuse disposition: `adapt`. Preserved source-plan detail: extract and adapt. | | Integration | Hermetic contract test in `make test-rust`; real pidfd/cgroup scenario in `make test-host-integration` | | Data migration | None — full d2b 3.0 reset; no prior state to migrate | | Validation | Parent-only wait/reap, poll-readability-not-status, duplicate-holder signaling, setsid/PGID-reuse resistance, quarantine no-kill, exact-once reap, and Linux ≥5.14 platform-gate assertions all pass | diff --git a/docs/specs/ADR-046-spec-set.json b/docs/specs/ADR-046-spec-set.json index bc6b7bde9..95ff5543f 100644 --- a/docs/specs/ADR-046-spec-set.json +++ b/docs/specs/ADR-046-spec-set.json @@ -1,17 +1,10 @@ { - "artifactKind": "d2b-adr-spec-set", - "schemaVersion": 1, - "adr": "0046", - "status": "Proposed", - "parent": "docs/adr/0046-d2b-3-provider-control-plane.md", - "baseline": "b5ddbed67867d9244bf33390868101bd9b053e49", - "members": [ + "adr" : "0046", + "artifactKind" : "d2b-adr-spec-set", + "baseline" : "b5ddbed67867d9244bf33390868101bd9b053e49", + "members" : [ { - "specId": "ADR-046-cli-and-operations", - "path": "docs/specs/ADR-046-cli-and-operations.md", - "status": "Proposed", - "version": 1, - "dependsOn": [ + "dependsOn" : [ "ADR-046-components-processes-and-sandbox", "ADR-046-componentsession-and-bus", "ADR-046-provider-model-and-packaging", @@ -19,72 +12,78 @@ "ADR-046-resource-object-model", "ADR-046-terminology-and-identities" ], - "supersedes": "Current v3 `d2b` CLI contract (`packages/d2b/src/lib.rs` at baseline)", - "sha256": "323c64cdd788f5a60c349df5eeebfebe5468825f7e47ccecaf41227be40b4e9a" + "path" : "docs/specs/ADR-046-cli-and-operations.md", + "sha256" : "90ed48fa2062e1580eb5c063f4c6c6fbbdf2dc6bc9db422013a3a57efbbc0e41", + "specId" : "ADR-046-cli-and-operations", + "status" : "Proposed", + "supersedes" : "Current v3 `d2b` CLI contract (`packages/d2b/src/lib.rs` at baseline)", + "version" : 1, + "workItemPrefix" : "cli" }, { - "specId": "ADR-046-components-processes-and-sandbox", - "path": "docs/specs/ADR-046-components-processes-and-sandbox.md", - "status": "Proposed", - "version": 1, - "dependsOn": [ + "dependsOn" : [ "ADR-046-componentsession-and-bus", "ADR-046-primitive-resource-composition", "ADR-046-provider-model-and-packaging" ], - "supersedes": "Current ProcessRole/VmProcessDag as the public process model", - "sha256": "b95b2d9b7acc146c9a7480241264262fd69da74ead64dfc6cb4adf45e950ca27" + "path" : "docs/specs/ADR-046-components-processes-and-sandbox.md", + "sha256" : "520f1f773f70e546d5566fa21b7981148c39790befa6fca0518d14ebf8b9ba96", + "specId" : "ADR-046-components-processes-and-sandbox", + "status" : "Proposed", + "supersedes" : "Current ProcessRole/VmProcessDag as the public process model", + "version" : 1, + "workItemPrefix" : "process" }, { - "specId": "ADR-046-componentsession-and-bus", - "path": "docs/specs/ADR-046-componentsession-and-bus.md", - "status": "Proposed", - "version": 1, - "dependsOn": [ + "dependsOn" : [ "ADR-046-resource-api-and-authorization", "ADR-046-terminology-and-identities" ], - "supersedes": "Current v3 Realm PeerSession and ad hoc guest/user/public IPC", - "sha256": "db79e9b8faa198241650fe575653c654adc67f004a2a52b003d7511e7b9c608b" + "path" : "docs/specs/ADR-046-componentsession-and-bus.md", + "sha256" : "dedab5286a5c324b9c1d3765d9c1230e3ad684a65a6fa15e8c48cc4d2b5f2164", + "specId" : "ADR-046-componentsession-and-bus", + "status" : "Proposed", + "supersedes" : "Current v3 Realm PeerSession and ad hoc guest/user/public IPC", + "version" : 1, + "workItemPrefix" : "session" }, { - "specId": "ADR-046-core-controllers", - "path": "docs/specs/ADR-046-core-controllers.md", - "status": "Proposed", - "version": 1, - "dependsOn": [ + "dependsOn" : [ "ADR-046-provider-model-and-packaging", "ADR-046-resource-api-and-authorization", "ADR-046-resource-reconciliation", "ADR-046-resource-store-redb" ], - "supersedes": "Current daemon-global hard-coded orchestration ownership", - "sha256": "46b3b6f9b450c11c2d35aaadc42666091b5df753064ca4e1243c09a065672df3" + "path" : "docs/specs/ADR-046-core-controllers.md", + "sha256" : "66cd76546c32adf160515b194f8a55a2df6243950f59a86f18060c380e7be0b8", + "specId" : "ADR-046-core-controllers", + "status" : "Proposed", + "supersedes" : "Current daemon-global hard-coded orchestration ownership", + "version" : 1, + "workItemPrefix" : "core" }, { - "specId": "ADR-046-current-code-migration-map", - "path": "docs/specs/ADR-046-current-code-migration-map.md", - "status": "Proposed", - "version": 1, - "dependsOn": [], - "supersedes": null, - "sha256": "efcbbabba718ca595ea58785ff9ee229d34df61856fd9d334c1ec9b9fec4b84f" + "dependsOn" : [], + "path" : "docs/specs/ADR-046-current-code-migration-map.md", + "sha256" : "efcbbabba718ca595ea58785ff9ee229d34df61856fd9d334c1ec9b9fec4b84f", + "specId" : "ADR-046-current-code-migration-map", + "status" : "Proposed", + "supersedes" : null, + "version" : 1, + "workItemPrefix" : null }, { - "specId": "ADR-046-decision-register", - "path": "docs/specs/ADR-046-decision-register.md", - "status": "Proposed", - "version": 1, - "dependsOn": [], - "supersedes": null, - "sha256": "b93e80a44159c11c830fa22b90f9c08643fe3c07d5037636bd4a77ab49d21e0c" + "dependsOn" : [], + "path" : "docs/specs/ADR-046-decision-register.md", + "sha256" : "d8dbd3c22bca785cd347e2b09ed3ff5c0b66c9ca5e8d0de7af17bc6550d9f690", + "specId" : "ADR-046-decision-register", + "status" : "Proposed", + "supersedes" : null, + "version" : 1, + "workItemPrefix" : "decisions" }, { - "specId": "ADR-046-feasibility-and-spikes", - "path": "docs/specs/ADR-046-feasibility-and-spikes.md", - "status": "Proposed", - "version": 1, - "dependsOn": [ + "dependsOn" : [ "ADR-046-cli-and-operations", "ADR-046-components-processes-and-sandbox", "ADR-046-componentsession-and-bus", @@ -101,42 +100,45 @@ "ADR-046-terminology-and-identities", "ADR-046-zone-routing" ], - "supersedes": null, - "sha256": "4b1ff14348a4f01586ba05fbb1128be472d2386aa83f542d5fb60fe56e79fc1a" + "path" : "docs/specs/ADR-046-feasibility-and-spikes.md", + "sha256" : "4ecfb8e3e90ac932f019424e5003897c3809647e3ffd0c9c72c9faab476b147f", + "specId" : "ADR-046-feasibility-and-spikes", + "status" : "Proposed", + "supersedes" : null, + "version" : 1, + "workItemPrefix" : "feasibility" }, { - "specId": "ADR-046-nix-configuration", - "path": "docs/specs/ADR-046-nix-configuration.md", - "status": "Proposed", - "version": 2, - "dependsOn": [ + "dependsOn" : [ "ADR-046-core-controllers", "ADR-046-primitive-resource-composition", "ADR-046-provider-model-and-packaging", "ADR-046-resource-object-model", "ADR-046-terminology-and-identities" ], - "supersedes": "Current `nixos-modules/options-realms*.nix`, `options-envs.nix`, `options-vms.nix`, `index.nix`, `bundle*.nix`, `*-json.nix`, and generated `/etc/d2b/*.json`", - "sha256": "8f8bfd7138c22e66d291571fd8d4552107453223f88077ef3ac598b15457a97b" + "path" : "docs/specs/ADR-046-nix-configuration.md", + "sha256" : "47ff8244dec4c6579af3b2740122ccb18e374e844f37f41dd7bd353bbdd6ab43", + "specId" : "ADR-046-nix-configuration", + "status" : "Proposed", + "supersedes" : "Current `nixos-modules/options-realms*.nix`, `options-envs.nix`, `options-vms.nix`, `index.nix`, `bundle*.nix`, `*-json.nix`, and generated `/etc/d2b/*.json`", + "version" : 2, + "workItemPrefix" : "nix" }, { - "specId": "ADR-046-primitive-resource-composition", - "path": "docs/specs/ADR-046-primitive-resource-composition.md", - "status": "Proposed", - "version": 1, - "dependsOn": [ + "dependsOn" : [ "ADR-046-resource-object-model", "ADR-046-resource-reconciliation" ], - "supersedes": "Current implementation-shaped ProcessRole composition", - "sha256": "8c558e7154ed8402ca733981d4ca33b6f318c43a9ce8598a7687ab29a25534ba" + "path" : "docs/specs/ADR-046-primitive-resource-composition.md", + "sha256" : "26c01f5c43de58396c81314ba9581486f6e168c2037d197619dee45b00e78fd2", + "specId" : "ADR-046-primitive-resource-composition", + "status" : "Proposed", + "supersedes" : "Current implementation-shaped ProcessRole composition", + "version" : 1, + "workItemPrefix" : "primitives" }, { - "specId": "ADR-046-provider-activation-nixos", - "path": "docs/specs/providers/ADR-046-provider-activation-nixos.md", - "status": "Proposed", - "version": 3, - "dependsOn": [ + "dependsOn" : [ "ADR-046-cli-and-operations", "ADR-046-componentsession-and-bus", "ADR-046-core-controllers", @@ -149,15 +151,16 @@ "ADR-046-resources-zone-control", "ADR-046-telemetry-audit-and-support" ], - "supersedes": "Current `d2b switch`/`boot`/`test`/`rollback`/`build`/`generations`/`gc`/`migrate` top-level verbs in `packages/d2b/src/lib.rs` and hardlink-farm activation in `packages/d2b-host/src/hardlink_farm.rs`", - "sha256": "d9f270e9dd44f63616c3b100e1196860befcbb9716befe69651015bce0317e69" + "path" : "docs/specs/providers/ADR-046-provider-activation-nixos.md", + "sha256" : "cfbfec0bde69a02f769a4b889eed4b3942f5a67f4ad56bdd7d8702476a67e738", + "specId" : "ADR-046-provider-activation-nixos", + "status" : "Proposed", + "supersedes" : "Current `d2b switch`/`boot`/`test`/`rollback`/`build`/`generations`/`gc`/`migrate` top-level verbs in `packages/d2b/src/lib.rs` and hardlink-farm activation in `packages/d2b-host/src/hardlink_farm.rs`", + "version" : 3, + "workItemPrefix" : "activation" }, { - "specId": "ADR-046-provider-audio-pipewire", - "path": "docs/specs/providers/ADR-046-provider-audio-pipewire.md", - "status": "Proposed", - "version": 9, - "dependsOn": [ + "dependsOn" : [ "ADR-046-components-processes-and-sandbox", "ADR-046-componentsession-and-bus", "ADR-046-nix-configuration", @@ -171,15 +174,16 @@ "ADR-046-resources-volume", "ADR-046-telemetry-audit-and-support" ], - "supersedes": "`nixos-modules/components/audio/host.nix`, `nixos-modules/components/audio/guest.nix`, `packages/d2b-core/src/audio_policy.rs`, `packages/d2bd/src/audio_dispatch.rs`, `packages/d2bd/src/audio_host_controller.rs`, `packages/d2b-host/src/audio_argv.rs`", - "sha256": "8d7ffa922776486f4bbc99a3fec70f2d864afac4006abc12003a4941b0cb3779" + "path" : "docs/specs/providers/ADR-046-provider-audio-pipewire.md", + "sha256" : "150ac5317cce68d193fbdb5188c1b0dacd26d78abea2d49e19353bf657cf8012", + "specId" : "ADR-046-provider-audio-pipewire", + "status" : "Proposed", + "supersedes" : "`nixos-modules/components/audio/host.nix`, `nixos-modules/components/audio/guest.nix`, `packages/d2b-core/src/audio_policy.rs`, `packages/d2bd/src/audio_dispatch.rs`, `packages/d2bd/src/audio_host_controller.rs`, `packages/d2b-host/src/audio_argv.rs`", + "version" : 10, + "workItemPrefix" : "audio" }, { - "specId": "ADR-046-provider-clipboard-wayland", - "path": "docs/specs/providers/ADR-046-provider-clipboard-wayland.md", - "status": "Proposed", - "version": 2, - "dependsOn": [ + "dependsOn" : [ "ADR-046-componentsession-and-bus", "ADR-046-core-controllers", "ADR-046-nix-configuration", @@ -189,15 +193,16 @@ "ADR-046-resources-host-guest-process-user", "ADR-046-telemetry-audit-and-support" ], - "supersedes": "`nixos-modules/clipboard.nix` (v3 migration), ADR-046-provider-clipboard-wayland v1", - "sha256": "da513c687c420b349e320be8203143dfce4622adce5e758b444773802c9a75a9" + "path" : "docs/specs/providers/ADR-046-provider-clipboard-wayland.md", + "sha256" : "2f270d76ee8b53f2c6b81d92b057868c50789c0f81366a3877c173beaaeba6ae", + "specId" : "ADR-046-provider-clipboard-wayland", + "status" : "Proposed", + "supersedes" : "`nixos-modules/clipboard.nix` (v3 migration), ADR-046-provider-clipboard-wayland v1", + "version" : 2, + "workItemPrefix" : "clipboard" }, { - "specId": "ADR-046-provider-credential-entra", - "path": "docs/specs/providers/ADR-046-provider-credential-entra.md", - "status": "Proposed", - "version": 2, - "dependsOn": [ + "dependsOn" : [ "ADR-046-componentsession-and-bus", "ADR-046-nix-configuration", "ADR-046-provider-model-and-packaging", @@ -206,15 +211,16 @@ "ADR-046-resources-host-guest-process-user", "ADR-046-telemetry-audit-and-support" ], - "supersedes": "No Host login/token chains; no direct `EntraCredentialClient` production egress; no `DefaultAzureCredential`, environment, DBus, browser, or path discovery", - "sha256": "ebfe04f8e685f441c43edbef49269a021094abe12967ca1d99ba885ec41ed5e7" + "path" : "docs/specs/providers/ADR-046-provider-credential-entra.md", + "sha256" : "348169aa3f7804384902099887620838cb8089fbb3eba24d4fdfdfcf2425f739", + "specId" : "ADR-046-provider-credential-entra", + "status" : "Proposed", + "supersedes" : "No Host login/token chains; no direct `EntraCredentialClient` production egress; no `DefaultAzureCredential`, environment, DBus, browser, or path discovery", + "version" : 2, + "workItemPrefix" : "cred-entra" }, { - "specId": "ADR-046-provider-credential-managed-identity", - "path": "docs/specs/providers/ADR-046-provider-credential-managed-identity.md", - "status": "Proposed", - "version": 1, - "dependsOn": [ + "dependsOn" : [ "ADR-046-componentsession-and-bus", "ADR-046-nix-configuration", "ADR-046-provider-model-and-packaging", @@ -222,15 +228,16 @@ "ADR-046-resources-credential", "ADR-046-telemetry-audit-and-support" ], - "supersedes": "Current v3 `ManagedIdentityRef`, `managed_identity_client_id` ACA config field, `CredentialProvider` trait (status/enrollment-only) in `d2b-realm-provider/src/credential.rs` and `provider.rs`", - "sha256": "2b57d790cc21d583a091029abd60f8f85edc6c0c5b8e3ee333c852b45c2b9a06" + "path" : "docs/specs/providers/ADR-046-provider-credential-managed-identity.md", + "sha256" : "192348ac96f06c90fadd73be9d437c81c4e742cd04415809c3b70cf4cdc30940", + "specId" : "ADR-046-provider-credential-managed-identity", + "status" : "Proposed", + "supersedes" : "Current v3 `ManagedIdentityRef`, `managed_identity_client_id` ACA config field, `CredentialProvider` trait (status/enrollment-only) in `d2b-realm-provider/src/credential.rs` and `provider.rs`", + "version" : 1, + "workItemPrefix" : "cred-mi" }, { - "specId": "ADR-046-provider-credential-secret-service", - "path": "docs/specs/providers/ADR-046-provider-credential-secret-service.md", - "status": "Proposed", - "version": 1, - "dependsOn": [ + "dependsOn" : [ "ADR-046-components-processes-and-sandbox", "ADR-046-componentsession-and-bus", "ADR-046-nix-configuration", @@ -240,15 +247,16 @@ "ADR-046-resources-credential", "ADR-046-telemetry-audit-and-support" ], - "supersedes": "v2 `SecretServiceCredentialProvider` / `SecretServiceCredentialProviderFactory` in `d2b-realm-provider`; v2 `CredentialProvider` trait", - "sha256": "6151f350c63416d13ffbb0bcbcb5ac1ed70350a411645214f7fe99d5824a29b2" + "path" : "docs/specs/providers/ADR-046-provider-credential-secret-service.md", + "sha256" : "b4684b48faf8d2f359172c1c5910abf543f46b6bc5fd592df6034084a938fa76", + "specId" : "ADR-046-provider-credential-secret-service", + "status" : "Proposed", + "supersedes" : "v2 `SecretServiceCredentialProvider` / `SecretServiceCredentialProviderFactory` in `d2b-realm-provider`; v2 `CredentialProvider` trait", + "version" : 1, + "workItemPrefix" : "cred-ss" }, { - "specId": "ADR-046-provider-device-gpu", - "path": "docs/specs/providers/ADR-046-provider-device-gpu.md", - "status": "Proposed", - "version": 8, - "dependsOn": [ + "dependsOn" : [ "ADR-046-components-processes-and-sandbox", "ADR-046-primitive-resource-composition", "ADR-046-provider-model-and-packaging", @@ -258,15 +266,16 @@ "ADR-046-resources-device", "ADR-046-telemetry-audit-and-support" ], - "supersedes": "`ProcessRole::Gpu`, `ProcessRole::GpuRenderNode`, `ProcessRole::Video` in `packages/d2b-core/src/processes.rs`; Nix `nixos-modules/components/graphics.nix`; Nix `nixos-modules/components/video/guest.nix`; `d2b.vms..graphics.*` options", - "sha256": "5ab5360cffd3122a6572a05d545a206b3ec69c952db3e8595238d77fc4c9d2cd" + "path" : "docs/specs/providers/ADR-046-provider-device-gpu.md", + "sha256" : "b7bd5c4b1bf167f53256613250e2fe9ec1db6e0bd2b4b2170b7125612d0a461f", + "specId" : "ADR-046-provider-device-gpu", + "status" : "Proposed", + "supersedes" : "`ProcessRole::Gpu`, `ProcessRole::GpuRenderNode`, `ProcessRole::Video` in `packages/d2b-core/src/processes.rs`; Nix `nixos-modules/components/graphics.nix`; Nix `nixos-modules/components/video/guest.nix`; `d2b.vms..graphics.*` options", + "version" : 8, + "workItemPrefix" : "gpu" }, { - "specId": "ADR-046-provider-device-security-key", - "path": "docs/specs/providers/ADR-046-provider-device-security-key.md", - "status": "Proposed", - "version": 6, - "dependsOn": [ + "dependsOn" : [ "ADR-046-components-processes-and-sandbox", "ADR-046-componentsession-and-bus", "ADR-046-nix-configuration", @@ -279,15 +288,16 @@ "ADR-046-resources-device", "ADR-046-telemetry-audit-and-support" ], - "supersedes": "`ProcessRole::SecurityKeyFrontend` daemon-internal accept loop (`packages/d2bd/src/security_key.rs`), `nixos-modules/components/security-key-guest.nix` untracked guest `d2b-sk-frontend.service` unit", - "sha256": "9b360b4cc652280efb12056f3cdf24a123c9a7a21ba36fd944d1dd0a32396bc0" + "path" : "docs/specs/providers/ADR-046-provider-device-security-key.md", + "sha256" : "40dcc4733e13ef47905a36759f97e8ac9ec22e0b9c9c378443f8e551e3f792d1", + "specId" : "ADR-046-provider-device-security-key", + "status" : "Proposed", + "supersedes" : "`ProcessRole::SecurityKeyFrontend` daemon-internal accept loop (`packages/d2bd/src/security_key.rs`), `nixos-modules/components/security-key-guest.nix` untracked guest `d2b-sk-frontend.service` unit", + "version" : 6, + "workItemPrefix" : "security-key" }, { - "specId": "ADR-046-provider-device-tpm", - "path": "docs/specs/providers/ADR-046-provider-device-tpm.md", - "status": "Proposed", - "version": 3, - "dependsOn": [ + "dependsOn" : [ "ADR-046-primitive-resource-composition", "ADR-046-provider-model-and-packaging", "ADR-046-provider-state", @@ -297,15 +307,16 @@ "ADR-046-resources-host-guest-process-user", "ADR-046-resources-volume" ], - "supersedes": "Current `nixos-modules/components/tpm.nix` swtpm provisioning and `d2b-priv-broker` swtpm-dir hardening path", - "sha256": "e8cc3ab1cb4e75a08d4ab9660d623dfcdd88dd891304a42958f855c9a60b1caf" + "path" : "docs/specs/providers/ADR-046-provider-device-tpm.md", + "sha256" : "3d7b47892427a4b4932dd97740a0e270bc75c662c704d6a98cb7b75e7ea2883e", + "specId" : "ADR-046-provider-device-tpm", + "status" : "Proposed", + "supersedes" : "Current `nixos-modules/components/tpm.nix` swtpm provisioning and `d2b-priv-broker` swtpm-dir hardening path", + "version" : 3, + "workItemPrefix" : "device-tpm" }, { - "specId": "ADR-046-provider-device-usbip", - "path": "docs/specs/providers/ADR-046-provider-device-usbip.md", - "status": "Proposed", - "version": 6, - "dependsOn": [ + "dependsOn" : [ "ADR-046-components-processes-and-sandbox", "ADR-046-componentsession-and-bus", "ADR-046-nix-configuration", @@ -319,15 +330,16 @@ "ADR-046-telemetry-audit-and-support", "ADR-046-zone-routing" ], - "supersedes": "`nixos-modules/components/usbip.nix` (host-side), per-env usbipd systemd units in `nixos-modules/network.nix`, `ProcessRole::Usbip` / `RunnerRole::Usbip` in current v3 baseline", - "sha256": "e5cc4b32e16a07e54472e3ef10dda4e18c308ea9660fabf89cbbd1a6f53ccf34" + "path" : "docs/specs/providers/ADR-046-provider-device-usbip.md", + "sha256" : "a88ee0635abc5feb93a028bb428fceb52430e6d5a6323962f1e62769a1fa03a9", + "specId" : "ADR-046-provider-device-usbip", + "status" : "Proposed", + "supersedes" : "`nixos-modules/components/usbip.nix` (host-side), per-env usbipd systemd units in `nixos-modules/network.nix`, `ProcessRole::Usbip` / `RunnerRole::Usbip` in current v3 baseline", + "version" : 7, + "workItemPrefix" : "usbip" }, { - "specId": "ADR-046-provider-display-wayland", - "path": "docs/specs/providers/ADR-046-provider-display-wayland.md", - "status": "Proposed", - "version": 2, - "dependsOn": [ + "dependsOn" : [ "ADR-046-components-processes-and-sandbox", "ADR-046-componentsession-and-bus", "ADR-046-nix-configuration", @@ -339,29 +351,31 @@ "ADR-046-resources-volume", "ADR-046-telemetry-audit-and-support" ], - "supersedes": "`ProcessRole::WaylandProxy` in `packages/d2b-core/src/processes.rs`; `LocalCrossDomainWaylandProvider` in `packages/d2b-host-providers/src/lib.rs`; `generate_wayland_proxy_argv` in `packages/d2b-host/src/wayland_proxy_argv.rs`; `nixos-modules/components/graphics.nix` `graphics.waylandProxy.*` options; `nixos-modules/ui-colors.nix` VM border color resolution; current `pkgs/wl-cross-domain-proxy` guest binary", - "sha256": "e1b6e24a6d9a0b5bf666e62ea087ca9d03bb15662dd5c02480106dd6e05c4514" + "path" : "docs/specs/providers/ADR-046-provider-display-wayland.md", + "sha256" : "dd598b4c5f55fdb29f4a25831ab72814fa4082f23c48cda9ab551a8423a46521", + "specId" : "ADR-046-provider-display-wayland", + "status" : "Proposed", + "supersedes" : "`ProcessRole::WaylandProxy` in `packages/d2b-core/src/processes.rs`; `LocalCrossDomainWaylandProvider` in `packages/d2b-host-providers/src/lib.rs`; `generate_wayland_proxy_argv` in `packages/d2b-host/src/wayland_proxy_argv.rs`; `nixos-modules/components/graphics.nix` `graphics.waylandProxy.*` options; `nixos-modules/ui-colors.nix` VM border color resolution; current `pkgs/wl-cross-domain-proxy` guest binary", + "version" : 2, + "workItemPrefix" : "display" }, { - "specId": "ADR-046-provider-model-and-packaging", - "path": "docs/specs/ADR-046-provider-model-and-packaging.md", - "status": "Proposed", - "version": 1, - "dependsOn": [ + "dependsOn" : [ "ADR-046-primitive-resource-composition", "ADR-046-resource-api-and-authorization", "ADR-046-resource-object-model", "ADR-046-resource-reconciliation" ], - "supersedes": "Current direct Provider construction/composition", - "sha256": "f89ba42da92d4bd43326b5928ebccfd9080cf88e03bd645e61e4ae9c36ce35fe" + "path" : "docs/specs/ADR-046-provider-model-and-packaging.md", + "sha256" : "2d002c97ddb6237cd2ea0d5d8b07f10678d6a27ee61910bcba6dd47d5743f41b", + "specId" : "ADR-046-provider-model-and-packaging", + "status" : "Proposed", + "supersedes" : "Current direct Provider construction/composition", + "version" : 1, + "workItemPrefix" : "provider" }, { - "specId": "ADR-046-provider-network-local", - "path": "docs/specs/providers/ADR-046-provider-network-local.md", - "status": "Proposed", - "version": 2, - "dependsOn": [ + "dependsOn" : [ "ADR-046-componentsession-and-bus", "ADR-046-current-code-migration-map", "ADR-046-nix-configuration", @@ -372,15 +386,16 @@ "ADR-046-resources-volume", "ADR-046-telemetry-audit-and-support" ], - "supersedes": "`nixos-modules/network.nix`, `nixos-modules/net.nix`", - "sha256": "0b7a069eadee6e3baa8d32f4219f57da04055113dd48e875c922e28093df7f6a" + "path" : "docs/specs/providers/ADR-046-provider-network-local.md", + "sha256" : "22e12144bf2f6a52dccc2288080f1f07777569bcbcbd7be80cac6bdb1ed2581a", + "specId" : "ADR-046-provider-network-local", + "status" : "Proposed", + "supersedes" : "`nixos-modules/network.nix`, `nixos-modules/net.nix`", + "version" : 3, + "workItemPrefix" : "nl" }, { - "specId": "ADR-046-provider-notification-desktop", - "path": "docs/specs/providers/ADR-046-provider-notification-desktop.md", - "status": "Proposed", - "version": 2, - "dependsOn": [ + "dependsOn" : [ "ADR-046-componentsession-and-bus", "ADR-046-nix-configuration", "ADR-046-primitive-resource-composition", @@ -389,15 +404,16 @@ "ADR-046-resource-reconciliation", "ADR-046-telemetry-audit-and-support" ], - "supersedes": null, - "sha256": "3e68686ead60587a94f62b56420e889c43a9850beb5343cd65515cc2c006604e" + "path" : "docs/specs/providers/ADR-046-provider-notification-desktop.md", + "sha256" : "7388e079a6b30eb235b04686237eaf7611b0b8721628cd14275cf78b14f10b01", + "specId" : "ADR-046-provider-notification-desktop", + "status" : "Proposed", + "supersedes" : null, + "version" : 2, + "workItemPrefix" : "notify" }, { - "specId": "ADR-046-provider-observability-otel", - "path": "docs/specs/providers/ADR-046-provider-observability-otel.md", - "status": "Proposed", - "version": 3, - "dependsOn": [ + "dependsOn" : [ "ADR-046-components-processes-and-sandbox", "ADR-046-componentsession-and-bus", "ADR-046-nix-configuration", @@ -411,15 +427,16 @@ "ADR-046-telemetry-audit-and-support", "ADR-046-terminology-and-identities" ], - "supersedes": "`ProcessRole::OtelHostBridge` / `RunnerRole::OtelHostBridge`; socat-based vsock forwarder in `packages/d2b-host/src/otel_host_bridge_argv.rs`; `packages/d2bd/src/otel_host_bridge_readiness.rs`; hand-rolled per-VM `nixos-modules/components/observability/` pipeline (adapted to per-Zone)", - "sha256": "08922e72a325908b6435487e312d096684e3d44684ec4652c6c9a8bcbf9f3379" + "path" : "docs/specs/providers/ADR-046-provider-observability-otel.md", + "sha256" : "c096549371c1092d986bb49be22f94b2f6928213d90d1e0e734e52fb84b48535", + "specId" : "ADR-046-provider-observability-otel", + "status" : "Proposed", + "supersedes" : "`ProcessRole::OtelHostBridge` / `RunnerRole::OtelHostBridge`; socat-based vsock forwarder in `packages/d2b-host/src/otel_host_bridge_argv.rs`; `packages/d2bd/src/otel_host_bridge_readiness.rs`; hand-rolled per-VM `nixos-modules/components/observability/` pipeline (adapted to per-Zone)", + "version" : 3, + "workItemPrefix" : "otel" }, { - "specId": "ADR-046-provider-runtime-azure-container-apps", - "path": "docs/specs/providers/ADR-046-provider-runtime-azure-container-apps.md", - "status": "Proposed", - "version": 1, - "dependsOn": [ + "dependsOn" : [ "ADR-046-componentsession-and-bus", "ADR-046-nix-configuration", "ADR-046-provider-model-and-packaging", @@ -429,15 +446,16 @@ "ADR-046-telemetry-audit-and-support", "ADR-046-zone-routing" ], - "supersedes": "`packages/d2b-provider-aca/` (`AcaWorkloadProvider`, `GuestControlEndpointProvider`), `AcaRelayTransportConfig`, direct vsock guest-control path", - "sha256": "5b1fef935431bc645517cdad9ff3b50b736a060f270e4019ccb7e2ad347bee4a" + "path" : "docs/specs/providers/ADR-046-provider-runtime-azure-container-apps.md", + "sha256" : "774c3cdb40552a2c66fb1386eec72ba2a887e33ce53979b8ba8344922a3a81e2", + "specId" : "ADR-046-provider-runtime-azure-container-apps", + "status" : "Proposed", + "supersedes" : "`packages/d2b-provider-aca/` (`AcaWorkloadProvider`, `GuestControlEndpointProvider`), `AcaRelayTransportConfig`, direct vsock guest-control path", + "version" : 1, + "workItemPrefix" : "aca" }, { - "specId": "ADR-046-provider-runtime-azure-virtual-machine", - "path": "docs/specs/providers/ADR-046-provider-runtime-azure-virtual-machine.md", - "status": "Proposed", - "version": 3, - "dependsOn": [ + "dependsOn" : [ "ADR-046-components-processes-and-sandbox", "ADR-046-componentsession-and-bus", "ADR-046-nix-configuration", @@ -454,15 +472,16 @@ "ADR-046-terminology-and-identities", "ADR-046-zone-routing" ], - "supersedes": "Current `InfrastructureProvider` trait (`d2b-realm-provider/src/provider.rs`); `AzureVmForbidden` explicit rejection in `provider_registry.rs`; `AZURE_VM_IMPLEMENTATION_ID` constant; `WorkloadProviderKind::ProviderManaged` paths for Azure VM workloads", - "sha256": "d5041f399b4c40bd81fba635e53820f467064525919120ec0d14d222779c8a4f" + "path" : "docs/specs/providers/ADR-046-provider-runtime-azure-virtual-machine.md", + "sha256" : "d26b90118dcda9a9ca91f174d451ed320ff09362c2662045d0139e3df833ff64", + "specId" : "ADR-046-provider-runtime-azure-virtual-machine", + "status" : "Proposed", + "supersedes" : "Current `InfrastructureProvider` trait (`d2b-realm-provider/src/provider.rs`); `AzureVmForbidden` explicit rejection in `provider_registry.rs`; `AZURE_VM_IMPLEMENTATION_ID` constant; `WorkloadProviderKind::ProviderManaged` paths for Azure VM workloads", + "version" : 3, + "workItemPrefix" : "azure-vm" }, { - "specId": "ADR-046-provider-runtime-cloud-hypervisor", - "path": "docs/specs/providers/ADR-046-provider-runtime-cloud-hypervisor.md", - "status": "Proposed", - "version": 1, - "dependsOn": [ + "dependsOn" : [ "ADR-046-components-processes-and-sandbox", "ADR-046-componentsession-and-bus", "ADR-046-nix-configuration", @@ -477,15 +496,16 @@ "ADR-046-resources-volume", "ADR-046-telemetry-audit-and-support" ], - "supersedes": "`packages/d2b-host/src/runtime_provider.rs` `CloudHypervisorRuntimeProvider`; `packages/d2bd/src/` VM lifecycle paths; `d2b-host-providers` adapter; `ProcessRole::CloudHypervisor`, `ProcessRole::Swtpm`, `ProcessRole::NetVm`; systemd unit `d2b--vm.service`; `SwtpmDir` broker op", - "sha256": "ec6c6fa964d18d9bc8658120065e8e267500b1aff7c29a65ef5257f8a38057b5" + "path" : "docs/specs/providers/ADR-046-provider-runtime-cloud-hypervisor.md", + "sha256" : "49242d7654a4ffe54a2a0ddaab4062c5a8f746d0e8e7d0eaef98d22b676a0b8e", + "specId" : "ADR-046-provider-runtime-cloud-hypervisor", + "status" : "Proposed", + "supersedes" : "`packages/d2b-host/src/runtime_provider.rs` `CloudHypervisorRuntimeProvider`; `packages/d2bd/src/` VM lifecycle paths; `d2b-host-providers` adapter; `ProcessRole::CloudHypervisor`, `ProcessRole::Swtpm`, `ProcessRole::NetVm`; systemd unit `d2b--vm.service`; `SwtpmDir` broker op", + "version" : 1, + "workItemPrefix" : "ch" }, { - "specId": "ADR-046-provider-runtime-qemu-media", - "path": "docs/specs/providers/ADR-046-provider-runtime-qemu-media.md", - "status": "Proposed", - "version": 2, - "dependsOn": [ + "dependsOn" : [ "ADR-046-components-processes-and-sandbox", "ADR-046-componentsession-and-bus", "ADR-046-current-code-migration-map", @@ -498,15 +518,16 @@ "ADR-046-resources-volume", "ADR-046-telemetry-audit-and-support" ], - "supersedes": "`docs/adr/0036-qemu-media-runtime.md`", - "sha256": "41b390ce115da993272f73d73b0cc99432411d51845efba35c79f865a040d83d" + "path" : "docs/specs/providers/ADR-046-provider-runtime-qemu-media.md", + "sha256" : "a941d6bac5b9b8e7b10063164f54776928c104ade0753544ca184331ca78d1ce", + "specId" : "ADR-046-provider-runtime-qemu-media", + "status" : "Proposed", + "supersedes" : "`docs/adr/0036-qemu-media-runtime.md`", + "version" : 2, + "workItemPrefix" : "qemu-media" }, { - "specId": "ADR-046-provider-shell-terminal", - "path": "docs/specs/providers/ADR-046-provider-shell-terminal.md", - "status": "Proposed", - "version": 2, - "dependsOn": [ + "dependsOn" : [ "ADR-046-components-processes-and-sandbox", "ADR-046-componentsession-and-bus", "ADR-046-core-controllers", @@ -524,15 +545,16 @@ "ADR-046-terminology-and-identities", "ADR-046-zone-routing" ], - "supersedes": "v1 of this spec; `guestd/src/shell.rs` guest persistent-shell runtime (ADR 0038); `d2b-unsafe-local-helper` shell supervisor and wire protocol v2 (ADR 0044); `ShellOp`/`ShellOpResponse` seqpacket protocol (`d2b-contracts/src/public_wire.rs:1319,1394,1527`)", - "sha256": "0b3204fc217771078b3b2538a1628a682065f1629f268712d89ed366a7532622" + "path" : "docs/specs/providers/ADR-046-provider-shell-terminal.md", + "sha256" : "6aaa6f3653ce851d2d31a4f1dc00bcb069ad0ef26685e66e63b33b3605171205", + "specId" : "ADR-046-provider-shell-terminal", + "status" : "Proposed", + "supersedes" : "v1 of this spec; `guestd/src/shell.rs` guest persistent-shell runtime (ADR 0038); `d2b-unsafe-local-helper` shell supervisor and wire protocol v2 (ADR 0044); `ShellOp`/`ShellOpResponse` seqpacket protocol (`d2b-contracts/src/public_wire.rs:1319,1394,1527`)", + "version" : 2, + "workItemPrefix" : "sterm" }, { - "specId": "ADR-046-provider-state", - "path": "docs/specs/ADR-046-provider-state.md", - "status": "Proposed", - "version": 1, - "dependsOn": [ + "dependsOn" : [ "ADR-046-componentsession-and-bus", "ADR-046-primitive-resource-composition", "ADR-046-provider-model-and-packaging", @@ -540,15 +562,16 @@ "ADR-046-resource-store-redb", "ADR-046-terminology-and-identities" ], - "supersedes": "`d2b-core/src/storage.rs` generated contract, ADR 0034 storage/sync/adoption model for v3", - "sha256": "9359ab1f957f4ae1c01c2efeeb75b06840dc766ddf1e0558ecc9935fe1db6c27" + "path" : "docs/specs/ADR-046-provider-state.md", + "sha256" : "070529d4af6edcc1468a4f221509cac4d44497b55588ac253e029e6ccda5f390", + "specId" : "ADR-046-provider-state", + "status" : "Proposed", + "supersedes" : "`d2b-core/src/storage.rs` generated contract, ADR 0034 storage/sync/adoption model for v3", + "version" : 1, + "workItemPrefix" : "pstate" }, { - "specId": "ADR-046-provider-system-core", - "path": "docs/specs/providers/ADR-046-provider-system-core.md", - "status": "Proposed", - "version": 1, - "dependsOn": [ + "dependsOn" : [ "ADR-046-componentsession-and-bus", "ADR-046-core-controllers", "ADR-046-current-code-migration-map", @@ -558,15 +581,16 @@ "ADR-046-resources-zone-control", "ADR-046-telemetry-audit-and-support" ], - "supersedes": "Current `WorkloadProviderKind::UnsafeLocal`, host/User grouping helpers, `HostJson`/`VmRuntimeRow` host-management paths in `d2bd` and `d2b-realm-core`", - "sha256": "f84dbc089290a9bb0a942764d58fb114ff41451968b71e22c3a09e8c714fcd90" + "path" : "docs/specs/providers/ADR-046-provider-system-core.md", + "sha256" : "88b56dc8b5ce5535317ab311931b3498488fb304a4456d84fa33d30f557df80f", + "specId" : "ADR-046-provider-system-core", + "status" : "Proposed", + "supersedes" : "Current `WorkloadProviderKind::UnsafeLocal`, host/User grouping helpers, `HostJson`/`VmRuntimeRow` host-management paths in `d2bd` and `d2b-realm-core`", + "version" : 1, + "workItemPrefix" : "system-core" }, { - "specId": "ADR-046-provider-system-minijail", - "path": "docs/specs/providers/ADR-046-provider-system-minijail.md", - "status": "Proposed", - "version": 1, - "dependsOn": [ + "dependsOn" : [ "ADR-046-components-processes-and-sandbox", "ADR-046-componentsession-and-bus", "ADR-046-core-controllers", @@ -580,15 +604,16 @@ "ADR-046-telemetry-audit-and-support", "ADR-046-terminology-and-identities" ], - "supersedes": "Current `d2b-priv-broker` SpawnRunner, `d2b-core` minijail profile, and `d2bd` supervisor pidfd/wait paths for minijail-spawned processes", - "sha256": "ae05f8e582f221ab430c4ca5a9db62299f2ec7bc49773419988e229838aad310" + "path" : "docs/specs/providers/ADR-046-provider-system-minijail.md", + "sha256" : "ccc2116577ba8aedfd59f2810a7af0bd3814227b9872c731424170b90cddaee5", + "specId" : "ADR-046-provider-system-minijail", + "status" : "Proposed", + "supersedes" : "Current `d2b-priv-broker` SpawnRunner, `d2b-core` minijail profile, and `d2bd` supervisor pidfd/wait paths for minijail-spawned processes", + "version" : 1, + "workItemPrefix" : "minijail" }, { - "specId": "ADR-046-provider-system-systemd", - "path": "docs/specs/providers/ADR-046-provider-system-systemd.md", - "status": "Proposed", - "version": 1, - "dependsOn": [ + "dependsOn" : [ "ADR-046-components-processes-and-sandbox", "ADR-046-componentsession-and-bus", "ADR-046-current-code-migration-map", @@ -597,15 +622,16 @@ "ADR-046-resources-host-guest-process-user", "ADR-046-telemetry-audit-and-support" ], - "supersedes": "Current `d2b-unsafe-local-helper` systemd scope runtime; current `d2bd` `supervisor/` VM process management; broker `SpawnRunner` (systemd-owned roles)", - "sha256": "6a4580dbd32edb663294f98c905d6bbb54a0d84640cbbd2e62d25d84039a9c81" + "path" : "docs/specs/providers/ADR-046-provider-system-systemd.md", + "sha256" : "c6bddd9a2f6d1e21700764e9515789277e28680b938730315160d144a452d28d", + "specId" : "ADR-046-provider-system-systemd", + "status" : "Proposed", + "supersedes" : "Current `d2b-unsafe-local-helper` systemd scope runtime; current `d2bd` `supervisor/` VM process management; broker `SpawnRunner` (systemd-owned roles)", + "version" : 1, + "workItemPrefix" : "systemd" }, { - "specId": "ADR-046-provider-transport-azure-relay", - "path": "docs/specs/providers/ADR-046-provider-transport-azure-relay.md", - "status": "Proposed", - "version": 1, - "dependsOn": [ + "dependsOn" : [ "ADR-046-componentsession-and-bus", "ADR-046-nix-configuration", "ADR-046-provider-model-and-packaging", @@ -614,15 +640,16 @@ "ADR-046-terminology-and-identities", "ADR-046-zone-routing" ], - "supersedes": "`d2b-provider-relay` gateway-display relay path (`AcaWorkloadProvider` + `RelayProvider` traits in `d2b-realm-provider`); `d2b-gateway-runtime/src/bin/d2b-gateway-relay.rs`; `packages/d2b-provider-relay/src/lib.rs` as a first-party transport surface", - "sha256": "d2b601776a6967a722eb87076cadf1cd9806dfa6d2fe426be3125096faeec733" + "path" : "docs/specs/providers/ADR-046-provider-transport-azure-relay.md", + "sha256" : "495872099dcd76c3731734a65c4c3413bc2d241ca6cefeda9d1ecb436b6d4ced", + "specId" : "ADR-046-provider-transport-azure-relay", + "status" : "Proposed", + "supersedes" : "`d2b-provider-relay` gateway-display relay path (`AcaWorkloadProvider` + `RelayProvider` traits in `d2b-realm-provider`); `d2b-gateway-runtime/src/bin/d2b-gateway-relay.rs`; `packages/d2b-provider-relay/src/lib.rs` as a first-party transport surface", + "version" : 1, + "workItemPrefix" : "transport-relay" }, { - "specId": "ADR-046-provider-transport-unix", - "path": "docs/specs/providers/ADR-046-provider-transport-unix.md", - "status": "Proposed", - "version": 3, - "dependsOn": [ + "dependsOn" : [ "ADR-046-components-processes-and-sandbox", "ADR-046-componentsession-and-bus", "ADR-046-core-controllers", @@ -635,15 +662,16 @@ "ADR-046-terminology-and-identities", "ADR-046-zone-routing" ], - "supersedes": "v1 of this dossier (ADR-046-provider-transport-unix v1; incorrect ownership model); `InheritedSocketTransport` SD_LISTEN_FDS path (`packages/d2b-session-unix/src/systemd.rs`)", - "sha256": "41328ba22573599419ff4195c33e18ae4ad2cfc5d4b811306707c9d67d63785a" + "path" : "docs/specs/providers/ADR-046-provider-transport-unix.md", + "sha256" : "6c5200e6b424c002ece340839684c7dfa7141c09de0e58e4e7b2aeed4bd12725", + "specId" : "ADR-046-provider-transport-unix", + "status" : "Proposed", + "supersedes" : "v1 of this dossier (ADR-046-provider-transport-unix v1; incorrect ownership model); `InheritedSocketTransport` SD_LISTEN_FDS path (`packages/d2b-session-unix/src/systemd.rs`)", + "version" : 3, + "workItemPrefix" : "transport-unix" }, { - "specId": "ADR-046-provider-transport-vsock", - "path": "docs/specs/providers/ADR-046-provider-transport-vsock.md", - "status": "Proposed", - "version": 2, - "dependsOn": [ + "dependsOn" : [ "ADR-046-componentsession-and-bus", "ADR-046-provider-model-and-packaging", "ADR-046-provider-state", @@ -653,15 +681,16 @@ "ADR-046-terminology-and-identities", "ADR-046-zone-routing" ], - "supersedes": "`packages/d2bd/src/guest_control_vsock.rs` transport probe (→ allocator-issued endpoint via VsockEffectPort); `packages/d2b-host/src/vsock_relay_argv.rs` socat relay (→ native FramedVsockTransport in Provider service); `NativeVsock`/`CloudHypervisorVsock` in `d2b-session-unix/src/vsock.rs` (→ FramedVsockTransport implementing OwnedTransport adapted as service-Provider transport bridge)", - "sha256": "fdbbfb4d0e3a33c49ef8d10069431414772a3da924395dec32e807ff6f43fb2f" + "path" : "docs/specs/providers/ADR-046-provider-transport-vsock.md", + "sha256" : "5a73239f90903b689ec05a0ddbbbe24b7a78ccedbb6de377cc105f0afe43d202", + "specId" : "ADR-046-provider-transport-vsock", + "status" : "Proposed", + "supersedes" : "`packages/d2bd/src/guest_control_vsock.rs` transport probe (→ allocator-issued endpoint via VsockEffectPort); `packages/d2b-host/src/vsock_relay_argv.rs` socat relay (→ native FramedVsockTransport in Provider service); `NativeVsock`/`CloudHypervisorVsock` in `d2b-session-unix/src/vsock.rs` (→ FramedVsockTransport implementing OwnedTransport adapted as service-Provider transport bridge)", + "version" : 2, + "workItemPrefix" : "vsock" }, { - "specId": "ADR-046-provider-volume-local", - "path": "docs/specs/providers/ADR-046-provider-volume-local.md", - "status": "Proposed", - "version": 2, - "dependsOn": [ + "dependsOn" : [ "ADR-046-components-processes-and-sandbox", "ADR-046-componentsession-and-bus", "ADR-046-nix-configuration", @@ -671,15 +700,16 @@ "ADR-046-resources-volume", "ADR-046-telemetry-audit-and-support" ], - "supersedes": "`d2b-priv-broker/src/ops/{state_dir,store_sync,store_sync_audit,store_sync_export,store_view_farm,store_view_posture,swtpm_dir}.rs`; `d2b-core/src/{storage,sync,storage_lifecycle}.rs` StorageJson/SyncJson contract; `nixos-modules/store.nix` per-VM hardlink farm activation", - "sha256": "1becd5d13ad4118bdce758899e5a4bd3aa555d0700d5a09c7b43c07a69f11147" + "path" : "docs/specs/providers/ADR-046-provider-volume-local.md", + "sha256" : "584b0a86ce1bfc3ba896eb817c43ea707009815323b3a464d7aedc923a0a07ff", + "specId" : "ADR-046-provider-volume-local", + "status" : "Proposed", + "supersedes" : "`d2b-priv-broker/src/ops/{state_dir,store_sync,store_sync_audit,store_sync_export,store_view_farm,store_view_posture,swtpm_dir}.rs`; `d2b-core/src/{storage,sync,storage_lifecycle}.rs` StorageJson/SyncJson contract; `nixos-modules/store.nix` per-VM hardlink farm activation", + "version" : 2, + "workItemPrefix" : "vl" }, { - "specId": "ADR-046-provider-volume-virtiofs", - "path": "docs/specs/providers/ADR-046-provider-volume-virtiofs.md", - "status": "Proposed", - "version": 2, - "dependsOn": [ + "dependsOn" : [ "ADR-046-components-processes-and-sandbox", "ADR-046-componentsession-and-bus", "ADR-046-nix-configuration", @@ -691,15 +721,16 @@ "ADR-046-resources-volume", "ADR-046-telemetry-audit-and-support" ], - "supersedes": "`nixos-modules/processes-json.nix` virtiofsdRunner block; `nixos-modules/minijail-profiles.nix` virtiofsdProfiles; `packages/d2b-host/src/virtiofsd_argv.rs`; `ProcessRole::Virtiofsd` dag nodes in `packages/d2bd/src/supervisor/dag.rs`", - "sha256": "01ee78166de9bdf08c1d4ae5dba4c70ed5445ee4504c8c63615ae3de68dddbda" + "path" : "docs/specs/providers/ADR-046-provider-volume-virtiofs.md", + "sha256" : "41883d577282a85d1771286c603f7e029356e59dae13eaa58c2103975f04e73b", + "specId" : "ADR-046-provider-volume-virtiofs", + "status" : "Proposed", + "supersedes" : "`nixos-modules/processes-json.nix` virtiofsdRunner block; `nixos-modules/minijail-profiles.nix` virtiofsdProfiles; `packages/d2b-host/src/virtiofsd_argv.rs`; `ProcessRole::Virtiofsd` dag nodes in `packages/d2bd/src/supervisor/dag.rs`", + "version" : 2, + "workItemPrefix" : "vvfs" }, { - "specId": "ADR-046-reset-and-cutover", - "path": "docs/specs/ADR-046-reset-and-cutover.md", - "status": "Proposed", - "version": 1, - "dependsOn": [ + "dependsOn" : [ "ADR-046-cli-and-operations", "ADR-046-components-processes-and-sandbox", "ADR-046-componentsession-and-bus", @@ -724,65 +755,70 @@ "ADR-046-terminology-and-identities", "ADR-046-zone-routing" ], - "supersedes": "[ADR 0034](../adr/0034-storage-lifecycle-restart-and-synchronization.md) \"Migration decision\" section for the d2b 3.0 cutover; the current `d2b host migrate-storage` verb (retired per `ADR-046-current-code-migration-map` §6 and `ADR-046-cli-and-operations` \"v2 command surface removed at 3.0 clean break\": it served the one-time v1→v2 storage layout cutover only and has no v3 successor)", - "sha256": "58e49d8e7dcb5fc7c3e1231c2092aa2317fb63e432005907bd3d480cb14f4e8d" + "path" : "docs/specs/ADR-046-reset-and-cutover.md", + "sha256" : "8bacde7ecc5384f57d35d348fb95926cf6da0930133bda0a9d96446a84a8a11d", + "specId" : "ADR-046-reset-and-cutover", + "status" : "Proposed", + "supersedes" : "[ADR 0034](../adr/0034-storage-lifecycle-restart-and-synchronization.md) \"Migration decision\" section for the d2b 3.0 cutover; the current `d2b host migrate-storage` verb (retired per `ADR-046-current-code-migration-map` §6 and `ADR-046-cli-and-operations` \"v2 command surface removed at 3.0 clean break\": it served the one-time v1→v2 storage layout cutover only and has no v3 successor)", + "version" : 1, + "workItemPrefix" : "reset" }, { - "specId": "ADR-046-resource-api-and-authorization", - "path": "docs/specs/ADR-046-resource-api-and-authorization.md", - "status": "Proposed", - "version": 1, - "dependsOn": [ + "dependsOn" : [ "ADR-046-resource-object-model", "ADR-046-resource-store-redb", "ADR-046-terminology-and-identities" ], - "supersedes": null, - "sha256": "a8aa34a62116ac6139902b55c1574efe8b037eafbce00802507e0a43562e5a6d" + "path" : "docs/specs/ADR-046-resource-api-and-authorization.md", + "sha256" : "68351d54635ad4813ee5de78968091991b7a037eef58958c9eb8953841c57023", + "specId" : "ADR-046-resource-api-and-authorization", + "status" : "Proposed", + "supersedes" : null, + "version" : 1, + "workItemPrefix" : "api" }, { - "specId": "ADR-046-resource-object-model", - "path": "docs/specs/ADR-046-resource-object-model.md", - "status": "Proposed", - "version": 1, - "dependsOn": [ + "dependsOn" : [ "ADR-046-decision-register", "ADR-046-terminology-and-identities" ], - "supersedes": null, - "sha256": "927d89f6a6960615bafaf54e5be560d29ccbd72c201df3e1a767c73c4c97329b" + "path" : "docs/specs/ADR-046-resource-object-model.md", + "sha256" : "451c0582b8f113c4a0f54367af7cbdb4d23a476adc25694e17b7e657928a7f71", + "specId" : "ADR-046-resource-object-model", + "status" : "Proposed", + "supersedes" : null, + "version" : 1, + "workItemPrefix" : "object" }, { - "specId": "ADR-046-resource-reconciliation", - "path": "docs/specs/ADR-046-resource-reconciliation.md", - "status": "Proposed", - "version": 1, - "dependsOn": [ + "dependsOn" : [ "ADR-046-resource-api-and-authorization", "ADR-046-resource-object-model", "ADR-046-resource-store-redb" ], - "supersedes": null, - "sha256": "a806edffe487e4f24bebb77bffbff816ead6d99a83ab8be2a2745ea9cb8a8396" + "path" : "docs/specs/ADR-046-resource-reconciliation.md", + "sha256" : "77b5561822464f15e3f623090026908b4954f5b031f17e5d35dfa054639b5733", + "specId" : "ADR-046-resource-reconciliation", + "status" : "Proposed", + "supersedes" : null, + "version" : 1, + "workItemPrefix" : "reconcile" }, { - "specId": "ADR-046-resource-store-redb", - "path": "docs/specs/ADR-046-resource-store-redb.md", - "status": "Proposed", - "version": 1, - "dependsOn": [ + "dependsOn" : [ "ADR-046-resource-object-model", "ADR-046-terminology-and-identities" ], - "supersedes": null, - "sha256": "ca9ba716eacfe83da1a29aab0ab6181f87f9b46b8f3541ed372a7278ebea58ff" + "path" : "docs/specs/ADR-046-resource-store-redb.md", + "sha256" : "9e8d68afc50aafeb6717b498da2790a88e37d9de0715c9a8a187caed620d7380", + "specId" : "ADR-046-resource-store-redb", + "status" : "Proposed", + "supersedes" : null, + "version" : 1, + "workItemPrefix" : "store" }, { - "specId": "ADR-046-resources-credential", - "path": "docs/specs/ADR-046-resources-credential.md", - "status": "Proposed", - "version": 1, - "dependsOn": [ + "dependsOn" : [ "ADR-046-componentsession-and-bus", "ADR-046-primitive-resource-composition", "ADR-046-provider-model-and-packaging", @@ -791,15 +827,16 @@ "ADR-046-resource-reconciliation", "ADR-046-terminology-and-identities" ], - "supersedes": "Current v3 `CredentialProvider` trait, `CredentialStatus`, `CredentialPlane` model in `d2b-realm-provider/src/credential.rs` and `provider.rs`", - "sha256": "f33663d48d47681b22cd4bcd103ad991f234b0da244466de0ba02ab8fda2aaba" + "path" : "docs/specs/ADR-046-resources-credential.md", + "sha256" : "09dbcec4e1eccb67022b38103e649d1fe4ebb86707a6c92e67725b2cd5a54363", + "specId" : "ADR-046-resources-credential", + "status" : "Proposed", + "supersedes" : "Current v3 `CredentialProvider` trait, `CredentialStatus`, `CredentialPlane` model in `d2b-realm-provider/src/credential.rs` and `provider.rs`", + "version" : 1, + "workItemPrefix" : "credential" }, { - "specId": "ADR-046-resources-device", - "path": "docs/specs/ADR-046-resources-device.md", - "status": "Proposed", - "version": 1, - "dependsOn": [ + "dependsOn" : [ "ADR-046-components-processes-and-sandbox", "ADR-046-primitive-resource-composition", "ADR-046-provider-model-and-packaging", @@ -807,15 +844,16 @@ "ADR-046-resource-object-model", "ADR-046-resource-reconciliation" ], - "supersedes": "Current ProcessRole device sidecars (Swtpm, SwtpmPreStartFlush, Usbip, SecurityKeyFrontend, Gpu, GpuRenderNode, Video) and their Nix components", - "sha256": "d70c6f595b354ef4c45b2be50c6d43210a09f803140243d39b3ec20a954457a6" + "path" : "docs/specs/ADR-046-resources-device.md", + "sha256" : "96c14da583e220bf4c9e35de5069702eadec4b1477fbcf80840ef13fed3ea0ce", + "specId" : "ADR-046-resources-device", + "status" : "Proposed", + "supersedes" : "Current ProcessRole device sidecars (Swtpm, SwtpmPreStartFlush, Usbip, SecurityKeyFrontend, Gpu, GpuRenderNode, Video) and their Nix components", + "version" : 1, + "workItemPrefix" : "device" }, { - "specId": "ADR-046-resources-host-guest-process-user", - "path": "docs/specs/ADR-046-resources-host-guest-process-user.md", - "status": "Proposed", - "version": 1, - "dependsOn": [ + "dependsOn" : [ "ADR-046-components-processes-and-sandbox", "ADR-046-core-controllers", "ADR-046-decision-register", @@ -826,45 +864,48 @@ "ADR-046-resource-reconciliation", "ADR-046-terminology-and-identities" ], - "supersedes": "Current `ProcessRole`/`VmProcessDag` as the public process model; current Realm workload/unsafe-local/execution DTOs; `WorkloadProviderKind`/`IsolationPosture` posture enums", - "sha256": "acb558794203b40d60e540a4117b56ed28249f53c13b99f578fc4dd13f24e7af" + "path" : "docs/specs/ADR-046-resources-host-guest-process-user.md", + "sha256" : "b0be4f30c866dd27c06389c9ddc332c66ed1e5c72da99d6ce9a48fc26583feeb", + "specId" : "ADR-046-resources-host-guest-process-user", + "status" : "Proposed", + "supersedes" : "Current `ProcessRole`/`VmProcessDag` as the public process model; current Realm workload/unsafe-local/execution DTOs; `WorkloadProviderKind`/`IsolationPosture` posture enums", + "version" : 1, + "workItemPrefix" : "exec" }, { - "specId": "ADR-046-resources-network", - "path": "docs/specs/ADR-046-resources-network.md", - "status": "Proposed", - "version": 1, - "dependsOn": [ + "dependsOn" : [ "ADR-046-primitive-resource-composition", "ADR-046-provider-model-and-packaging", "ADR-046-resource-object-model", "ADR-046-resource-reconciliation", "ADR-046-terminology-and-identities" ], - "supersedes": "`d2b.envs.` and `d2b.realms..network` Nix surfaces (v3 reset)", - "sha256": "e5350e0c7ad68a189441f742529870b684d8edef66c6b6e9c5a9f675b17e4e44" + "path" : "docs/specs/ADR-046-resources-network.md", + "sha256" : "238d8d2e2c7ad7ad3fa6e36d949bda3334263ccdc10a19d623e8f213132eb4cc", + "specId" : "ADR-046-resources-network", + "status" : "Proposed", + "supersedes" : "`d2b.envs.` and `d2b.realms..network` Nix surfaces (v3 reset)", + "version" : 2, + "workItemPrefix" : "network" }, { - "specId": "ADR-046-resources-volume", - "path": "docs/specs/ADR-046-resources-volume.md", - "status": "Proposed", - "version": 1, - "dependsOn": [ + "dependsOn" : [ "ADR-046-components-processes-and-sandbox", "ADR-046-primitive-resource-composition", "ADR-046-provider-model-and-packaging", "ADR-046-resource-object-model", "ADR-046-resource-reconciliation" ], - "supersedes": "`storage.json` v2, `sync.json` v1, per-VM hardlink-farm and store-view emitters in `nixos-modules/storage-json.nix`, `nixos-modules/store.nix`", - "sha256": "eb5cd2af09297be530e9999453cb916c6aee0f68203d15ef4f3f9fa8e88bc3a0" + "path" : "docs/specs/ADR-046-resources-volume.md", + "sha256" : "57e2534fea5ed3a9a636b772e4f2f206f530dadb8a13e57f00d9304107a882e3", + "specId" : "ADR-046-resources-volume", + "status" : "Proposed", + "supersedes" : "`storage.json` v2, `sync.json` v1, per-VM hardlink-farm and store-view emitters in `nixos-modules/storage-json.nix`, `nixos-modules/store.nix`", + "version" : 1, + "workItemPrefix" : "volume" }, { - "specId": "ADR-046-resources-zone-control", - "path": "docs/specs/ADR-046-resources-zone-control.md", - "status": "Proposed", - "version": 1, - "dependsOn": [ + "dependsOn" : [ "ADR-046-core-controllers", "ADR-046-decision-register", "ADR-046-provider-model-and-packaging", @@ -874,15 +915,16 @@ "ADR-046-resource-store-redb", "ADR-046-terminology-and-identities" ], - "supersedes": null, - "sha256": "eaed7f6f2115f121fed50e705ee311f7750432b0a0fd5a37cb477c67f7354b25" + "path" : "docs/specs/ADR-046-resources-zone-control.md", + "sha256" : "73de2de2eba753bd17528d397ffbc7dad68a41fa9007148c2bfc67cab77da479", + "specId" : "ADR-046-resources-zone-control", + "status" : "Proposed", + "supersedes" : null, + "version" : 1, + "workItemPrefix" : "zone-control" }, { - "specId": "ADR-046-security-and-threat-model", - "path": "docs/specs/ADR-046-security-and-threat-model.md", - "status": "Proposed", - "version": 1, - "dependsOn": [ + "dependsOn" : [ "ADR-046-cli-and-operations", "ADR-046-components-processes-and-sandbox", "ADR-046-componentsession-and-bus", @@ -934,15 +976,16 @@ "ADR-046-terminology-and-identities", "ADR-046-zone-routing" ], - "supersedes": "None. This spec is a new cross-cutting synthesis. `SECURITY.md` and `docs/explanation/design.md` remain the disclosure policy and pre-v3 (v1/v2) threat model for the protected `v3` baseline and are not superseded; historical main-branch ADR 0045 (`a1cc0b2d^:docs/adr/0045-provider-and-transport-framework.md`, \"d2b 2.0 provider and transport framework\") and ADR 0032/0034 remain non-ancestry reuse sources whose useful invariants this spec adapts under D001/D041", - "sha256": "22aeaae05007224cec7da11545179a3e39b064d36c625a07efcfab979f143547" + "path" : "docs/specs/ADR-046-security-and-threat-model.md", + "sha256" : "fd302854317e87b86f9ba1d349fcbcd00f729e9ec512ada409978308507212ee", + "specId" : "ADR-046-security-and-threat-model", + "status" : "Proposed", + "supersedes" : null, + "version" : 1, + "workItemPrefix" : "security" }, { - "specId": "ADR-046-streamline", - "path": "docs/specs/ADR-046-streamline.md", - "status": "Proposed", - "version": 1, - "dependsOn": [ + "dependsOn" : [ "ADR-046-current-code-migration-map", "ADR-046-decision-register", "ADR-046-primitive-resource-composition", @@ -953,15 +996,16 @@ "ADR-046-terminology-and-identities", "ADR-046-zone-routing" ], - "supersedes": null, - "sha256": "6ca85b66481d5aeaa20429836442d319bba2325f486588ecf69bdbe90e463e9f" + "path" : "docs/specs/ADR-046-streamline.md", + "sha256" : "54d78c6479cc97618beda2503b03e1283f37c52432ccf0c0fb2802e8241a0cd6", + "specId" : "ADR-046-streamline", + "status" : "Proposed", + "supersedes" : null, + "version" : 1, + "workItemPrefix" : "streamline" }, { - "specId": "ADR-046-telemetry-audit-and-support", - "path": "docs/specs/ADR-046-telemetry-audit-and-support.md", - "status": "Proposed", - "version": 2, - "dependsOn": [ + "dependsOn" : [ "ADR-046-components-processes-and-sandbox", "ADR-046-componentsession-and-bus", "ADR-046-core-controllers", @@ -970,26 +1014,28 @@ "ADR-046-resource-store-redb", "ADR-046-terminology-and-identities" ], - "supersedes": "Current `d2bd` hand-rolled Prometheus registry; current daemon/broker/gateway JSONL audit paths", - "sha256": "46f1e4be6b65c454d566730a947ec1494b18c31e25e0f9343ed8c3952a2ded46" + "path" : "docs/specs/ADR-046-telemetry-audit-and-support.md", + "sha256" : "0a47bc9532152e443a36ab61619a1fd3f3310fd858cb125e58e8b646d3cd1317", + "specId" : "ADR-046-telemetry-audit-and-support", + "status" : "Proposed", + "supersedes" : "Current `d2bd` hand-rolled Prometheus registry; current daemon/broker/gateway JSONL audit paths", + "version" : 2, + "workItemPrefix" : "telem" }, { - "specId": "ADR-046-terminology-and-identities", - "path": "docs/specs/ADR-046-terminology-and-identities.md", - "status": "Proposed", - "version": 1, - "dependsOn": [ + "dependsOn" : [ "ADR-046-decision-register" ], - "supersedes": "Public Realm terminology selected by ADR 0043 for d2b v3", - "sha256": "7f846b9f934e6cf10afa8bee32d1946a6d30003078993b6ab3b0b2a2e18e402a" + "path" : "docs/specs/ADR-046-terminology-and-identities.md", + "sha256" : "9a0d16015424be77cc0803642806f7884f596dc0d3daea5f0ad13231943678d4", + "specId" : "ADR-046-terminology-and-identities", + "status" : "Proposed", + "supersedes" : "Public Realm terminology selected by ADR 0043 for d2b v3", + "version" : 1, + "workItemPrefix" : "identities" }, { - "specId": "ADR-046-validation-and-delivery", - "path": "docs/specs/ADR-046-validation-and-delivery.md", - "status": "Proposed", - "version": 1, - "dependsOn": [ + "dependsOn" : [ "ADR-046-cli-and-operations", "ADR-046-components-processes-and-sandbox", "ADR-046-componentsession-and-bus", @@ -999,35 +1045,8 @@ "ADR-046-feasibility-and-spikes", "ADR-046-nix-configuration", "ADR-046-primitive-resource-composition", - "ADR-046-provider-activation-nixos", - "ADR-046-provider-audio-pipewire", - "ADR-046-provider-clipboard-wayland", - "ADR-046-provider-credential-entra", - "ADR-046-provider-credential-managed-identity", - "ADR-046-provider-credential-secret-service", - "ADR-046-provider-device-gpu", - "ADR-046-provider-device-security-key", - "ADR-046-provider-device-tpm", - "ADR-046-provider-device-usbip", - "ADR-046-provider-display-wayland", "ADR-046-provider-model-and-packaging", - "ADR-046-provider-network-local", - "ADR-046-provider-notification-desktop", - "ADR-046-provider-observability-otel", - "ADR-046-provider-runtime-azure-container-apps", - "ADR-046-provider-runtime-azure-virtual-machine", - "ADR-046-provider-runtime-cloud-hypervisor", - "ADR-046-provider-runtime-qemu-media", - "ADR-046-provider-shell-terminal", "ADR-046-provider-state", - "ADR-046-provider-system-core", - "ADR-046-provider-system-minijail", - "ADR-046-provider-system-systemd", - "ADR-046-provider-transport-azure-relay", - "ADR-046-provider-transport-unix", - "ADR-046-provider-transport-vsock", - "ADR-046-provider-volume-local", - "ADR-046-provider-volume-virtiofs", "ADR-046-reset-and-cutover", "ADR-046-resource-api-and-authorization", "ADR-046-resource-object-model", @@ -1045,23 +1064,32 @@ "ADR-046-terminology-and-identities", "ADR-046-zone-routing" ], - "supersedes": "This repository's current `AGENTS.md` \"Panel review\" phase-gate as the *sole* review mechanism for ADR 0046 work (extended, not replaced, per §12); ad hoc per-agent validation ordering for ADR 0046 implementation", - "sha256": "ea7163e0f0c5fc0167daf4161263e0b7253f3fc17933b5ab84f784306f2e5877" + "path" : "docs/specs/ADR-046-validation-and-delivery.md", + "sha256" : "ee3ea5d1e335388053e8a29ae228cefb073b30e89c275b882d53abb74601bbfe", + "specId" : "ADR-046-validation-and-delivery", + "status" : "Proposed", + "supersedes" : "This repository's current `AGENTS.md` \"Panel review\" phase-gate as the *sole* review mechanism for ADR 0046 work (extended, not replaced, per §12); ad hoc per-agent validation ordering for ADR 0046 implementation", + "version" : 1, + "workItemPrefix" : "delivery" }, { - "specId": "ADR-046-zone-routing", - "path": "docs/specs/ADR-046-zone-routing.md", - "status": "Proposed", - "version": 1, - "dependsOn": [ + "dependsOn" : [ "ADR-046-componentsession-and-bus", "ADR-046-resource-api-and-authorization", "ADR-046-resource-object-model", "ADR-046-resource-reconciliation", "ADR-046-terminology-and-identities" ], - "supersedes": "`RealmEntrypointTable`/`EntrypointMode`/`RealmControllerPlacement` enum (→ ZoneLink transport + private Zone runtime bootstrap placement); `RouteTreeEngine`/`routing.rs` route contracts (adapted, not deleted); `RemoteNodeRegistry` (→ ZoneLink controller); `OperationRouter` (→ ZoneLinkIdempotencyKey in d2b-bus); `realm_access_resolver` module (→ ZoneEntrypointResolver); `realm-controllers.json`/`realm-identity.json` bundle artifacts (→ Nix-authored Zone/ZoneLink resources); `d2b.realms.*` Nix option namespace (→ `d2b.zones.*`); `WorkloadId` routing targets (→ typed Guest/Host resource refs); `CapabilitySet`-only routing authz (→ per-hop RBAC narrowing + capability ceiling)", - "sha256": "f7021d1dd8c4ef3634d29e45cc322e6d7d9ed2f5de7d28527ff6541ce83fc264" + "path" : "docs/specs/ADR-046-zone-routing.md", + "sha256" : "8059eb4ef018db992cca53e07b0b29afd70d132a8bb13531b439dc5755683ef4", + "specId" : "ADR-046-zone-routing", + "status" : "Proposed", + "supersedes" : "`RealmEntrypointTable`/`EntrypointMode`/`RealmControllerPlacement` enum (→ compiler-only `parentZone` topology + ZoneLink transport + private Zone runtime bootstrap placement); `RouteTreeEngine`/`routing.rs` route contracts (adapted, not deleted); `RemoteNodeRegistry` (→ ZoneLink controller); `OperationRouter` (→ ZoneLinkIdempotencyKey in d2b-bus); `realm_access_resolver` module (→ ZoneEntrypointResolver); `realm-controllers.json`/`realm-identity.json` bundle artifacts (→ Nix-authored Zone settings/ZoneLink resources); `d2b.realms.*` Nix option namespace (→ `d2b.zones.*`); `WorkloadId` routing targets (→ typed Guest/Host resource refs); `CapabilitySet`-only routing authz (→ per-hop RBAC narrowing + capability ceiling)", + "version" : 1, + "workItemPrefix" : "routing" } - ] + ], + "parent" : "docs/adr/0046-d2b-3-provider-control-plane.md", + "schemaVersion" : 2, + "status" : "Proposed" } diff --git a/docs/specs/ADR-046-streamline.md b/docs/specs/ADR-046-streamline.md index 194139c92..056cc9d92 100644 --- a/docs/specs/ADR-046-streamline.md +++ b/docs/specs/ADR-046-streamline.md @@ -502,7 +502,7 @@ v3 source to extract from. | Owner/dependencies | `packages/xtask` owner; no dependency on any other streamline item | | Dependency/owner | `packages/xtask` owner; no dependency on any other streamline item | | Current source | None — net-new ADR 0046 spec tooling; no pre-ADR45 baseline equivalent | -| Reuse action | net-new | +| Reuse action | create | | Implementation shape | New `cargo run -p xtask -- spec-registry` subcommand parsing every spec's metadata table + work-item tables with a Markdown-table parser, emitting `ADR-046-spec-set.json` and `ADR-046-work-items.json`; a companion `cargo run -p xtask -- implementation-graph` reads those two manifests plus the `ADR-046-validation-and-delivery.md` §3 wave topology and emits `ADR-046-implementation-graph.json` and its rendered `.md`; both wired into the existing `gen-*`/`run_task` dispatch pattern in `packages/xtask/src/main.rs` | | Detailed design | New `cargo run -p xtask -- spec-registry` subcommand parsing every spec's metadata table + work-item tables with a Markdown-table parser, emitting the two manifest JSON files; the `implementation-graph` generator then maps every member spec and every work item exactly once to a wave and file-disjoint parallel group, emits typed edges (`spec-depends-on`, `work-item-depends-on`, `implements-spec`, `shared-contract`, `file-overlap-order`), computes topological rank, and renders the Mermaid/table human view; output is deterministic with sorted keys and no timestamps or host paths | | Integration | `tests/unit/gates/` drift gate (`xtask spec-registry`/`xtask implementation-graph` + `git diff --exit-code`) added to the existing drift-gate set; the graph is consumed by ADR046-streamline-005/006/008/013 and the ready-wave query in `ADR-046-validation-and-delivery` instead of each re-parsing Markdown independently | @@ -524,9 +524,9 @@ v3 source to extract from. | Owner/dependencies | ADR046-streamline-001; `d2b-core` owner (Tier B only) | | Dependency/owner | ADR046-streamline-001; `d2b-core` owner (Tier B only) | | Current source | Decision-register D010/D032/D075/D076/D080 prose is the Tier A source; no existing generated ResourceType schema source until Tier B | -| Reuse action | net-new Tier A schema source; later replace source with generated output from real d2b-core ResourceType types without changing consumers | +| Reuse action | create | | Implementation shape | Tier A: author the schema JSON once per primitive directly from the already-frozen decision-register entries (D010, D032, D075, D076, D080); Tier B: `xtask gen-spec-schemas` derives the same JSON from real Rust `#[derive(JsonSchema)]`-equivalent types once they land, replacing the hand-authored Tier A source without changing its consumers | -| Detailed design | Tier A: author the schema JSON once per primitive directly from the already-frozen decision-register entries (D010, D032, D075, D076, D080); Tier B: `xtask gen-spec-schemas` derives the same JSON from real Rust `#[derive(JsonSchema)]`-equivalent types once they land, replacing the hand-authored Tier A source without changing its consumers | +| Detailed design | Tier A: author the schema JSON once per primitive directly from the already-frozen decision-register entries (D010, D032, D075, D076, D080); Tier B: `xtask gen-spec-schemas` derives the same JSON from real Rust `#[derive(JsonSchema)]`-equivalent types once they land, replacing the hand-authored Tier A source without changing its consumers Primary reuse disposition: `create`. Preserved source-plan detail: net-new Tier A schema source; later replace source with generated output from real d2b-core ResourceType types without changing consumers. | | Integration | Consumed by ADR046-streamline-003 as the validation target and by ADR046-streamline-004's scaffold as the snippet source | | Data migration | None — docs/tooling only; no runtime state | | Validation | `spec_schema_check::canonical_field_set` | @@ -546,9 +546,9 @@ v3 source to extract from. | Owner/dependencies | ADR046-streamline-001, ADR046-streamline-002 | | Dependency/owner | ADR046-streamline-001, ADR046-streamline-002 | | Current source | None — net-new spec Markdown/schema lint; no existing fenced ResourceSpec extractor | -| Reuse action | net-new; reuse only the established xtask dispatch pattern and Rust Markdown-parser dependency class | +| Reuse action | create | | Implementation shape | Reuse the CommonMark parser already available to the Rust toolchain (the same class of dependency `xtask`'s existing `gen-*` commands already use for structured generation) to walk fenced blocks; classify each block by its enclosing heading (`## Nix authoring and configuration cleanup` vs. any other heading) to apply the current-evidence exclusion without a manual per-block marker | -| Detailed design | Reuse the CommonMark parser already available to the Rust toolchain (the same class of dependency `xtask`'s existing `gen-*` commands already use for structured generation) to walk fenced blocks; classify each block by its enclosing heading (`## Nix authoring and configuration cleanup` vs. any other heading) to apply the current-evidence exclusion without a manual per-block marker | +| Detailed design | Reuse the CommonMark parser already available to the Rust toolchain (the same class of dependency `xtask`'s existing `gen-*` commands already use for structured generation) to walk fenced blocks; classify each block by its enclosing heading (`## Nix authoring and configuration cleanup` vs. any other heading) to apply the current-evidence exclusion without a manual per-block marker Primary reuse disposition: `create`. Preserved source-plan detail: net-new; reuse only the established xtask dispatch pattern and Rust Markdown-parser dependency class. | | Integration | Added to `tests/tools/tier0-first-pass.sh` as an additional fast pre-check (closes F17) and to the standing `packages/d2b-contract-tests` policy-lint suite | | Data migration | None — docs/tooling only; no runtime state | | Validation | `spec_schema_check::extract_fenced_blocks`, `spec_schema_check::canonical_field_set`, `policy_spec_schema` (Type 4/5) | @@ -568,9 +568,9 @@ v3 source to extract from. | Owner/dependencies | ADR046-streamline-001, ADR046-streamline-002 | | Dependency/owner | ADR046-streamline-001, ADR046-streamline-002 | | Current source | None — net-new provider-dossier scaffold; canonical snippets come from ADR046-streamline-002 | -| Reuse action | net-new scaffold; reuse canonical schema/snippet source from ADR046-streamline-002 | +| Reuse action | create | | Implementation shape | Scaffold generator emits the template pre-filled with the requesting Provider's name, D080 qualification-grammar examples, and the canonical state-Volume snippet; does not attempt to author Provider-specific semantic sections (those remain the dossier author's normative content) | -| Detailed design | Scaffold generator emits the template pre-filled with the requesting Provider's name, D080 qualification-grammar examples, and the canonical state-Volume snippet; does not attempt to author Provider-specific semantic sections (those remain the dossier author's normative content) | +| Detailed design | Scaffold generator emits the template pre-filled with the requesting Provider's name, D080 qualification-grammar examples, and the canonical state-Volume snippet; does not attempt to author Provider-specific semantic sections (those remain the dossier author's normative content) Primary reuse disposition: `create`. Preserved source-plan detail: net-new scaffold; reuse canonical schema/snippet source from ADR046-streamline-002. | | Integration | Referenced from `docs/specs/README.md` "Required metadata" section (a follow-up doc edit outside this task's scope, tracked as a required cross-reference) | | Data migration | None — docs/tooling only; no runtime state | | Validation | `xtask_new_provider_dossier_scaffold`; the emitted scaffold must independently pass `spec_schema_check`/`work_item_validator` on first generation | @@ -590,9 +590,9 @@ v3 source to extract from. | Owner/dependencies | ADR046-streamline-001 | | Dependency/owner | ADR046-streamline-001 | | Current source | Decision-register D080/D081/D082/D083/D084/D037 invariants and ADR046-streamline-001 registry output; no existing cross-spec lint | -| Reuse action | net-new policy lints over generated registry and raw Markdown | +| Reuse action | create | | Implementation shape | Four focused lint files (one invariant family per file, matching the existing `packages/d2b-contract-tests/tests/policy_*.rs` one-concern-per-file convention) each scanning the registry output plus raw Markdown text for the specific violation patterns named in F2/F4/F5/F7/F9 | -| Detailed design | Four focused lint files (one invariant family per file, matching the existing `packages/d2b-contract-tests/tests/policy_*.rs` one-concern-per-file convention) each scanning the registry output plus raw Markdown text for the specific violation patterns named in F2/F4/F5/F7/F9 | +| Detailed design | Four focused lint files (one invariant family per file, matching the existing `packages/d2b-contract-tests/tests/policy_*.rs` one-concern-per-file convention) each scanning the registry output plus raw Markdown text for the specific violation patterns named in F2/F4/F5/F7/F9 Primary reuse disposition: `create`. Preserved source-plan detail: net-new policy lints over generated registry and raw Markdown. | | Integration | Standing Layer-1 policy-lint suite; runs on every PR touching `docs/specs/**` | | Data migration | None — docs/tooling only; no runtime state | | Validation | `policy_spec_vocabulary`, `policy_spec_ownership`, `policy_spec_finalizer_phase`, `policy_spec_source_policy` (all Type 5) | @@ -612,9 +612,9 @@ v3 source to extract from. | Owner/dependencies | The Zone resource-store implementation work item (not yet filed; blocked on W0-W10 implementation request per D024); ADR046-streamline-001 for the doc-level invariant source | | Dependency/owner | The Zone resource-store implementation work item (not yet filed; blocked on W0-W10 implementation request per D024); ADR046-streamline-001 for the doc-level invariant source | | Current source | None — real Zone resource-store/controller-registration graph not implemented yet; doc-level invariant source is ADR046-streamline-005 | -| Reuse action | net-new future runtime graph checker | +| Reuse action | create | | Implementation shape | A graph-walk over the real controller-registration/resource-ownership index (not Markdown) asserting the D076/D086/D087 invariants; the doc-level half of this check (dossier text describing the invariant correctly) is covered now by ADR046-streamline-005's `policy_spec_ownership` | -| Detailed design | A graph-walk over the real controller-registration/resource-ownership index (not Markdown) asserting the D076/D086/D087 invariants; the doc-level half of this check (dossier text describing the invariant correctly) is covered now by ADR046-streamline-005's `policy_spec_ownership` | +| Detailed design | A graph-walk over the real controller-registration/resource-ownership index (not Markdown) asserting the D076/D086/D087 invariants; the doc-level half of this check (dossier text describing the invariant correctly) is covered now by ADR046-streamline-005's `policy_spec_ownership` Primary reuse disposition: `create`. Preserved source-plan detail: net-new future runtime graph checker. | | Integration | Runs as a Type 3 integration test against the real resource-store crate once it exists | | Data migration | None — docs/tooling only; no runtime state | | Validation | New test asserting: zero `ProviderStateSet` rows in the store; every state Volume corresponds to a declared component namespace; no empty identity-only Volume; no bootstrap state Volume or bootstrap-storage mechanism exists | @@ -634,9 +634,9 @@ v3 source to extract from. | Owner/dependencies | The Provider-toolkit/EffectPort implementation work item (not yet filed; blocked on the W0-W10 implementation request); ADR046-streamline-001 for the doc-level invariant source | | Dependency/owner | The Provider-toolkit/EffectPort implementation work item (not yet filed; blocked on the W0-W10 implementation request); ADR046-streamline-001 for the doc-level invariant source | | Current source | Existing policy-broker lint pattern in packages/d2b-contract-tests/tests/policy_broker_dispositions.rs and policy_broker_schema.rs; no Provider crate source exists yet | -| Reuse action | adapt existing policy-broker lint pattern to Provider/Worker boundary checks | +| Reuse action | adapt | | Implementation shape | Static source scan (import-graph/symbol-use analysis) over compiled Provider crates, mirroring the existing `policy_broker_dispositions.rs` pattern already proven against `packages/d2bd`/`packages/d2b-priv-broker` | -| Detailed design | Static source scan (import-graph/symbol-use analysis) over compiled Provider crates, mirroring the existing `policy_broker_dispositions.rs` pattern already proven against `packages/d2bd`/`packages/d2b-priv-broker` | +| Detailed design | Static source scan (import-graph/symbol-use analysis) over compiled Provider crates, mirroring the existing `policy_broker_dispositions.rs` pattern already proven against `packages/d2bd`/`packages/d2b-priv-broker` Primary reuse disposition: `adapt`. Preserved source-plan detail: adapt existing policy-broker lint pattern to Provider/Worker boundary checks. | | Integration | Standing Layer-1 policy lint once Provider crates exist | | Data migration | None — docs/tooling only; no runtime state | | Validation | New tests asserting zero direct-broker-import matches in any Provider crate; zero disallowed-capability matches in any Worker binary | @@ -656,9 +656,9 @@ v3 source to extract from. | Owner/dependencies | ADR046-streamline-001 | | Dependency/owner | ADR046-streamline-001 | | Current source | None — no existing work-item required-field or unique-ID validator | -| Reuse action | net-new validator consuming ADR046-streamline-001 output | +| Reuse action | create | | Implementation shape | Consumes `docs/specs/ADR-046-work-items.json` (generated by ADR046-streamline-001); flags duplicate IDs, missing/placeholder fields, and dangling cross-references | -| Detailed design | Consumes `docs/specs/ADR-046-work-items.json` (generated by ADR046-streamline-001); flags duplicate IDs, missing/placeholder fields, and dangling cross-references | +| Detailed design | Consumes `docs/specs/ADR-046-work-items.json` (generated by ADR046-streamline-001); flags duplicate IDs, missing/placeholder fields, and dangling cross-references Primary reuse disposition: `create`. Preserved source-plan detail: net-new validator consuming ADR046-streamline-001 output. | | Integration | Standing Layer-1 policy lint | | Data migration | None — docs/tooling only; no runtime state | | Validation | `work_item_validator::unique_ids`, `work_item_validator::required_fields` | @@ -678,9 +678,9 @@ v3 source to extract from. | Owner/dependencies | ADR046-streamline-001 | | Dependency/owner | ADR046-streamline-001 | | Current source | None — no existing generated Provider catalog/index | -| Reuse action | net-new generator consuming registry and decision-register rows | +| Reuse action | create | | Implementation shape | `cargo run -p xtask -- gen-provider-catalog` reads the registry and decision-register D043-D049 rows and renders the table; drift-gated like the existing `gen-schemas`/`gen-migration-ledger` pattern | -| Detailed design | `cargo run -p xtask -- gen-provider-catalog` reads the registry and decision-register D043-D049 rows and renders the table; drift-gated like the existing `gen-schemas`/`gen-migration-ledger` pattern | +| Detailed design | `cargo run -p xtask -- gen-provider-catalog` reads the registry and decision-register D043-D049 rows and renders the table; drift-gated like the existing `gen-schemas`/`gen-migration-ledger` pattern Primary reuse disposition: `create`. Preserved source-plan detail: net-new generator consuming registry and decision-register rows. | | Integration | `tests/unit/gates/` drift gate | | Data migration | None — docs/tooling only; no runtime state | | Validation | `provider_catalog::frozen_family_membership`, `provider_catalog_matches_frozen_families` | @@ -700,9 +700,9 @@ v3 source to extract from. | Owner/dependencies | ADR046-streamline-001 | | Dependency/owner | ADR046-streamline-001 | | Current source | None — no existing stale-base/current-parent reconcile helper | -| Reuse action | net-new read-only git report | +| Reuse action | adapt | | Implementation shape | `git merge-base ` plus `git diff --name-only` intersection reporting; emits a JSON report (branch, divergence SHA, commits-behind count, file-overlap list) consumed by ADR046-streamline-013's ready/launched/blocker report | -| Detailed design | `git merge-base ` plus `git diff --name-only` intersection reporting; emits a JSON report (branch, divergence SHA, commits-behind count, file-overlap list) consumed by ADR046-streamline-013's ready/launched/blocker report | +| Detailed design | `git merge-base ` plus `git diff --name-only` intersection reporting; emits a JSON report (branch, divergence SHA, commits-behind count, file-overlap list) consumed by ADR046-streamline-013's ready/launched/blocker report Primary reuse disposition: `adapt`. Preserved source-plan detail: net-new read-only git report. | | Integration | Referenced from `AGENTS.md` once graduated (see [Permanent methodology](#permanent-methodology-after-accepted)); used manually before opening or restacking any future dossier branch | | Data migration | None — docs/tooling only; no runtime state | | Validation | `reconcile_helper_reports_stale_base` | @@ -722,9 +722,9 @@ v3 source to extract from. | Owner/dependencies | None — no prerequisite work item; owned by this spec | | Dependency/owner | No prerequisite; `packages/xtask` schema/validator owner | | Current source | None — no existing agent handoff manifest schema or validator | -| Reuse action | net-new schema and validator | +| Reuse action | create | | Implementation shape | A small JSON schema (`assigned_files: [string]`, `commit_sha: string`, `test_result: {command, exit_code}`, `base_sha: string`) plus a validator rejecting incomplete records; no attribution field for any AI/tool/model, consistent with this repository's commit/PR-body attribution rule | -| Detailed design | A small JSON schema (`assigned_files: [string]`, `commit_sha: string`, `test_result: {command, exit_code}`, `base_sha: string`) plus a validator rejecting incomplete records; no attribution field for any AI/tool/model, consistent with this repository's commit/PR-body attribution rule | +| Detailed design | A small JSON schema (`assigned_files: [string]`, `commit_sha: string`, `test_result: {command, exit_code}`, `base_sha: string`) plus a validator rejecting incomplete records; no attribution field for any AI/tool/model, consistent with this repository's commit/PR-body attribution rule Primary reuse disposition: `create`. Preserved source-plan detail: net-new schema and validator. | | Integration | Referenced from `AGENTS.md` "Worktrees for parallel agents" once graduated | | Data migration | None — docs/tooling only; no runtime state | | Validation | `handoff_manifest_rejects_incomplete_record` | @@ -744,9 +744,9 @@ v3 source to extract from. | Owner/dependencies | ADR046-streamline-011 (shares the manifest schema for commit-SHA binding) | | Dependency/owner | ADR046-streamline-011 (shares the manifest schema for commit-SHA binding) | | Current source | None — no existing task DB to git consistency importer | -| Reuse action | net-new read-only importer/checker | +| Reuse action | create | | Implementation shape | Reads the session database's todo table, resolves each `done` row's expected file set against `git log --name-only` for the current branch, and reports mismatches; read-only, no database or git mutation | -| Detailed design | Reads the session database's todo table, resolves each `done` row's expected file set against `git log --name-only` for the current branch, and reports mismatches; read-only, no database or git mutation | +| Detailed design | Reads the session database's todo table, resolves each `done` row's expected file set against `git log --name-only` for the current branch, and reports mismatches; read-only, no database or git mutation Primary reuse disposition: `create`. Preserved source-plan detail: net-new read-only importer/checker. | | Integration | Run manually at the end of each authoring round and before any pre-panel gate | | Data migration | None — docs/tooling only; no runtime state | | Validation | New test seeding a synthetic task DB with one orphaned "done" row and confirming the importer flags it | @@ -766,9 +766,9 @@ v3 source to extract from. | Owner/dependencies | ADR046-streamline-001, ADR046-streamline-010 | | Dependency/owner | ADR046-streamline-001, ADR046-streamline-010 | | Current source | None — no existing anti-serialization ready/launched/blocker report | -| Reuse action | net-new report combining registry, branch, and staleness data | +| Reuse action | adapt | | Implementation shape | Cross-references the spec registry's dependency graph (ADR046-streamline-001) against the set of currently open `adr0046-*` branches and the reconcile helper's (ADR046-streamline-010) staleness report; emits a per-scope ready/launched/blocked classification | -| Detailed design | Cross-references the spec registry's dependency graph (ADR046-streamline-001) against the set of currently open `adr0046-*` branches and the reconcile helper's (ADR046-streamline-010) staleness report; emits a per-scope ready/launched/blocked classification | +| Detailed design | Cross-references the spec registry's dependency graph (ADR046-streamline-001) against the set of currently open `adr0046-*` branches and the reconcile helper's (ADR046-streamline-010) staleness report; emits a per-scope ready/launched/blocked classification Primary reuse disposition: `adapt`. Preserved source-plan detail: net-new report combining registry, branch, and staleness data. | | Integration | Run at the start of each authoring round and after any foundation-spec change | | Data migration | None — docs/tooling only; no runtime state | | Validation | New test seeding a synthetic registry + branch list with one ready-but-unlaunched scope and confirming it is reported | @@ -788,9 +788,9 @@ v3 source to extract from. | Owner/dependencies | The Provider-toolkit implementation work item (blocked on the W0-W10 implementation request) | | Dependency/owner | The Provider-toolkit implementation work item (blocked on the W0-W10 implementation request) | | Current source | Existing bounded-parallel test-runner precedent in tests/tools/run-layer.sh and layer1-jobs.py; Provider toolkit fake crates are net-new | -| Reuse action | adapt bounded-parallel shard pattern; add net-new fake EffectPort/ResourceClient crates | +| Reuse action | adapt | | Implementation shape | Extend the existing `layer1-jobs.py` bounded-parallel-shard pattern with a per-Provider-crate shard definition; fake dependency crates implement the same `EffectPort`/`ResourceClient` trait surface with in-memory stand-ins | -| Detailed design | Extend the existing `layer1-jobs.py` bounded-parallel-shard pattern with a per-Provider-crate shard definition; fake dependency crates implement the same `EffectPort`/`ResourceClient` trait surface with in-memory stand-ins | +| Detailed design | Extend the existing `layer1-jobs.py` bounded-parallel-shard pattern with a per-Provider-crate shard definition; fake dependency crates implement the same `EffectPort`/`ResourceClient` trait surface with in-memory stand-ins Primary reuse disposition: `adapt`. Preserved source-plan detail: adapt bounded-parallel shard pattern; add net-new fake EffectPort/ResourceClient crates. | | Integration | `make test-unit`/`make check` shard addition once Provider crates exist | | Data migration | None — docs/tooling only; no runtime state | | Validation | New integration test proving a single Provider crate's test run succeeds with zero other Provider crates built | @@ -810,9 +810,9 @@ v3 source to extract from. | Owner/dependencies | ADR046-streamline-001, ADR046-streamline-009, ADR046-streamline-002 (Tier A part) | | Dependency/owner | ADR046-streamline-001, ADR046-streamline-009, ADR046-streamline-002 (Tier A part) | | Current source | Existing packages/xtask gen-* and drift-gate pattern; no existing generated-artifact conflict detector | -| Reuse action | adapt existing generator/drift-gate pattern with net-new input-hash conflict detection | +| Reuse action | adapt | | Implementation shape | Each generator hashes its input set before writing; if a concurrent regeneration on a sibling branch (detected via the reconcile helper's file-overlap report) would produce a different hash for the same output path, the generator refuses to overwrite and reports the divergent input set | -| Detailed design | Each generator hashes its input set before writing; if a concurrent regeneration on a sibling branch (detected via the reconcile helper's file-overlap report) would produce a different hash for the same output path, the generator refuses to overwrite and reports the divergent input set | +| Detailed design | Each generator hashes its input set before writing; if a concurrent regeneration on a sibling branch (detected via the reconcile helper's file-overlap report) would produce a different hash for the same output path, the generator refuses to overwrite and reports the divergent input set Primary reuse disposition: `adapt`. Preserved source-plan detail: adapt existing generator/drift-gate pattern with net-new input-hash conflict detection. | | Integration | Wired into every drift gate this spec and ADR046-streamline-001/009 introduce | | Data migration | None — docs/tooling only; no runtime state | | Validation | New test simulating two divergent input sets producing conflicting output for the same generated file and confirming the conflict is reported, not silently resolved | @@ -832,9 +832,9 @@ v3 source to extract from. | Owner/dependencies | ADR046-streamline-001, ADR046-streamline-008 | | Dependency/owner | ADR046-streamline-001, ADR046-streamline-008 | | Current source | D014/docs/specs/README.md zero-open-decision requirement; no existing automated pre-panel gate | -| Reuse action | net-new policy lint and precondition script | +| Reuse action | create | | Implementation shape | Scans registry output plus raw Markdown for `decision-required`, `TBD`, `TODO`, and any decision-ID-shaped token (`[A-Z]+-\d+`) not matching the canonical `D\d+` register numbering | -| Detailed design | Scans registry output plus raw Markdown for `decision-required`, `TBD`, `TODO`, and any decision-ID-shaped token (`[A-Z]+-\d+`) not matching the canonical `D\d+` register numbering | +| Detailed design | Scans registry output plus raw Markdown for `decision-required`, `TBD`, `TODO`, and any decision-ID-shaped token (`[A-Z]+-\d+`) not matching the canonical `D\d+` register numbering Primary reuse disposition: `create`. Preserved source-plan detail: net-new policy lint and precondition script. | | Integration | Required precondition before requesting the ADR 0046 documentation set's panel round (D014) | | Data migration | None — docs/tooling only; no runtime state | | Validation | `policy_no_leaked_decision_prefix` | @@ -854,9 +854,9 @@ v3 source to extract from. | Owner/dependencies | None — no prerequisite work item; owned by this spec | | Dependency/owner | No prerequisite; `docs/specs` evidence-command documentation owner | | Current source | This spec's Observed friction evidence command prose; no reusable evidence-command artifact exists yet | -| Reuse action | extract/adapt the already-cited evidence commands into a net-new documentation artifact | +| Reuse action | adapt | | Implementation shape | A plain Markdown list of the exact commands used to produce each F1-F17 citation above (already reproduced verbatim in this spec's prose), organized by friction ID, so re-running them is copy-paste rather than re-derivation | -| Detailed design | A plain Markdown list of the exact commands used to produce each F1-F17 citation above (already reproduced verbatim in this spec's prose), organized by friction ID, so re-running them is copy-paste rather than re-derivation | +| Detailed design | A plain Markdown list of the exact commands used to produce each F1-F17 citation above (already reproduced verbatim in this spec's prose), organized by friction ID, so re-running them is copy-paste rather than re-derivation Primary reuse disposition: `adapt`. Preserved source-plan detail: extract/adapt the already-cited evidence commands into a net-new documentation artifact. | | Integration | Referenced from PR bodies as external evidence per this repository's "PR bodies contain... check-status summaries only... may link to external evidence" convention | | Data migration | None — docs/tooling only; no runtime state | | Validation | Manual reviewer re-run of at least one command per friction ID during panel, confirmed to reproduce the cited count/SHA | @@ -876,9 +876,9 @@ v3 source to extract from. | Owner/dependencies | ADR046-streamline-010 (shares branch-staleness detection) | | Dependency/owner | ADR046-streamline-010 (shares branch-staleness detection) | | Current source | Existing disk-hygiene operator guidance; no ADR 0046 worktree disk report exists yet | -| Reuse action | adapt existing disk-hygiene reporting concepts into a net-new non-mutating script | +| Reuse action | adapt | | Implementation shape | `git worktree list --porcelain` plus `du -sh` per worktree's `packages/target/`; output is a report only, never a `git worktree remove`/`rm -rf` invocation | -| Detailed design | `git worktree list --porcelain` plus `du -sh` per worktree's `packages/target/`; output is a report only, never a `git worktree remove`/`rm -rf` invocation | +| Detailed design | `git worktree list --porcelain` plus `du -sh` per worktree's `packages/target/`; output is a report only, never a `git worktree remove`/`rm -rf` invocation Primary reuse disposition: `adapt`. Preserved source-plan detail: adapt existing disk-hygiene reporting concepts into a net-new non-mutating script. | | Integration | Run manually before requesting garbage collection or worktree removal, consistent with this repository's existing "Disk hygiene contract"-equivalent operator guidance for code waves | | Data migration | None — docs/tooling only; no runtime state | | Validation | New test confirming the reporting script's exit code and output never include a mutating command string, and confirming it correctly flags a fixture worktree with a real (non-symlink) `packages/target/` | @@ -898,9 +898,9 @@ v3 source to extract from. | Owner/dependencies | ADR046-streamline-001 | | Dependency/owner | ADR046-streamline-001 | | Current source | ADR-046 terminology and current-code migration-map specs; no automated old-to-new terminology helper exists yet | -| Reuse action | net-new terminology checker over existing mapping specs | +| Reuse action | create | | Implementation shape | Parses every "Current-code fit" table's "Current anchor" cell for symbol/crate names, cross-references against the migration map's disposition rows, and flags any current-source citation absent from the map | -| Detailed design | Parses every "Current-code fit" table's "Current anchor" cell for symbol/crate names, cross-references against the migration map's disposition rows, and flags any current-source citation absent from the map | +| Detailed design | Parses every "Current-code fit" table's "Current anchor" cell for symbol/crate names, cross-references against the migration map's disposition rows, and flags any current-source citation absent from the map Primary reuse disposition: `create`. Preserved source-plan detail: net-new terminology checker over existing mapping specs. | | Integration | Standing Layer-1 policy lint once wired; also usable ad hoc when authoring a new dossier's evidence section | | Data migration | None — docs/tooling only; no runtime state | | Validation | New test seeding a fixture spec citing a current-source symbol absent from the migration map and confirming it is flagged | @@ -920,9 +920,9 @@ v3 source to extract from. | Owner/dependencies | ADR046-streamline-001 | | Dependency/owner | ADR046-streamline-001 | | Current source | D094 test-placement requirement; no existing hermetic test-placement policy lint | -| Reuse action | net-new policy lint | +| Reuse action | create | | Implementation shape | Scans hermetic-tier Rust sources for banned API surfaces (`std::process::Command`, socket/container/DBus/systemd helpers, `#[ignore]`) and for `integration/`-only markers appearing outside `integration/` | -| Detailed design | Scans hermetic-tier Rust sources for banned API surfaces (`std::process::Command`, socket/container/DBus/systemd helpers, `#[ignore]`) and for `integration/`-only markers appearing outside `integration/` | +| Detailed design | Scans hermetic-tier Rust sources for banned API surfaces (`std::process::Command`, socket/container/DBus/systemd helpers, `#[ignore]`) and for `integration/`-only markers appearing outside `integration/` Primary reuse disposition: `create`. Preserved source-plan detail: net-new policy lint. | | Integration | `make test-policy` row; no new top-level `tests/*.sh` gate | | Data migration | None — docs/tooling only; no runtime state | | Validation | Fixture crate with an intentional process-spawning hermetic test is rejected naming the file/line; a correct crate passes | @@ -942,9 +942,9 @@ v3 source to extract from. | Owner/dependencies | ADR046-streamline-020 | | Dependency/owner | ADR046-streamline-020 | | Current source | D094 deterministic-clock requirement; no existing wall-clock sleep/retry policy lint | -| Reuse action | net-new policy lint layered on ADR046-streamline-020 | +| Reuse action | create | | Implementation shape | Scans hermetic sources for banned time/sleep APIs and asserts the deterministic fake-clock/RNG fixtures from the toolkit are used; classified crypto/property exceptions are allow-listed by explicit name with a declared per-test budget | -| Detailed design | Scans hermetic sources for banned time/sleep APIs and asserts the deterministic fake-clock/RNG fixtures from the toolkit are used; classified crypto/property exceptions are allow-listed by explicit name with a declared per-test budget | +| Detailed design | Scans hermetic sources for banned time/sleep APIs and asserts the deterministic fake-clock/RNG fixtures from the toolkit are used; classified crypto/property exceptions are allow-listed by explicit name with a declared per-test budget Primary reuse disposition: `create`. Preserved source-plan detail: net-new policy lint layered on ADR046-streamline-020. | | Integration | `make test-policy` row | | Data migration | None — docs/tooling only; no runtime state | | Validation | Fixture hermetic test using `thread::sleep` is rejected; a classified crypto test on the allow-list passes | @@ -964,9 +964,9 @@ v3 source to extract from. | Owner/dependencies | ADR046-delivery-007 | | Dependency/owner | ADR046-delivery-007 | | Current source | ADR046-delivery-007 timing-ledger design; no existing execution-only test-runtime ledger in this spec set | -| Reuse action | share/adapt ADR046-delivery-007 timing-ledger implementation for this gate | +| Reuse action | adapt | | Implementation shape | Parses per-test JSON timings, aggregates per test/crate/shard, compares against pinned budgets and the previous ledger, and fails on regression beyond the threshold | -| Detailed design | Parses per-test JSON timings, aggregates per test/crate/shard, compares against pinned budgets and the previous ledger, and fails on regression beyond the threshold | +| Detailed design | Parses per-test JSON timings, aggregates per test/crate/shard, compares against pinned budgets and the previous ledger, and fails on regression beyond the threshold Primary reuse disposition: `adapt`. Preserved source-plan detail: share/adapt ADR046-delivery-007 timing-ledger implementation for this gate. | | Integration | Consumed by wave entry/exit (`ADR-046-validation-and-delivery` §4/§10.16); `make test-rust` and Layer-1 shards run concurrently | | Data migration | None — docs/tooling only; no runtime state | | Validation | Synthetic timing regression fails the gate; ledger output is deterministic and machine-readable | @@ -986,9 +986,9 @@ v3 source to extract from. | Owner/dependencies | ADR046-streamline-008, ADR046-streamline-022 | | Dependency/owner | ADR046-streamline-008, ADR046-streamline-022 | | Current source | Current-code migration-map disposition rows and existing gate manifests; no legacy-test retirement generator exists yet | -| Reuse action | net-new generator reading existing migration rows and gate manifests without mutating them | +| Reuse action | adapt | | Implementation shape | Reads the migration map's disposition rows, cross-references the live `tests/layer1-jobs.json`/gate manifests, and emits the delete set plus an absence assertion; never deletes automatically — it produces the gated checklist and the failing test that proves incomplete retirement | -| Detailed design | Reads the migration map's disposition rows, cross-references the live `tests/layer1-jobs.json`/gate manifests, and emits the delete set plus an absence assertion; never deletes automatically — it produces the gated checklist and the failing test that proves incomplete retirement | +| Detailed design | Reads the migration map's disposition rows, cross-references the live `tests/layer1-jobs.json`/gate manifests, and emits the delete set plus an absence assertion; never deletes automatically — it produces the gated checklist and the failing test that proves incomplete retirement Primary reuse disposition: `adapt`. Preserved source-plan detail: net-new generator reading existing migration rows and gate manifests without mutating them. | | Integration | `make test-policy`/`make test-drift` row; wired to every current-code migration work item's removal proof | | Data migration | None — docs/tooling only; no runtime state | | Validation | Fixture with a replaced behavior whose old selector still appears in `tests/layer1-jobs.json` fails; once removed, the absence assertion passes | @@ -1008,9 +1008,9 @@ v3 source to extract from. | Owner/dependencies | ADR046-streamline-001 | | Dependency/owner | ADR046-streamline-001 | | Current source | The graph is regenerated by hand today; `ADR-046-spec-set.json` and the test-runtime ledger (ADR046-delivery-007) each have their own emitter — duplicate-generator finding, retained here for reconciliation | -| Reuse action | net-new generator that consumes the two manifests and the wave topology; folds the duplicate spec-set/test-runtime emitters into one canonical path | +| Reuse action | create | | Implementation shape | Reads the two manifests plus §3 wave topology, maps every member spec and every work item exactly once to a wave/parallel-group, emits typed edges and topological rank, renders the Mermaid/table `.md`, and is wired into the existing `gen-*` drift gate; the spec-set and test-runtime emitters become subcommands of the same `xtask` binary so there is one canonical generator, not three | -| Detailed design | Deterministic (sorted keys, no timestamps/host paths); regenerated after any spec or work-item change and after the two manifests; a drift gate runs it and `git diff --exit-code`. Retains the duplicate-generator findings for the spec-set manifest and the test-runtime ledger and reconciles them to the single `xtask` path rather than leaving three independent emitters | +| Detailed design | Deterministic (sorted keys, no timestamps/host paths); regenerated after any spec or work-item change and after the two manifests; a drift gate runs it and `git diff --exit-code`. Retains the duplicate-generator findings for the spec-set manifest and the test-runtime ledger and reconciles them to the single `xtask` path rather than leaving three independent emitters Primary reuse disposition: `create`. Preserved source-plan detail: net-new generator that consumes the two manifests and the wave topology; folds the duplicate spec-set/test-runtime emitters into one canonical path. | | Integration | `tests/unit/gates/` drift gate alongside `xtask spec-registry`; consumed by the ready-wave query in `ADR-046-validation-and-delivery` §3.5.1 | | Data migration | None — docs/tooling only; no runtime state | | Validation | `implementation_graph::every_spec_and_work_item_mapped_once`; `implementation_graph::acyclic_and_wave_monotonic`; `implementation_graph::parallel_groups_are_file_disjoint`; `implementation_graph_regenerates_clean`; duplicate-generator reconciliation asserts one canonical emitter path for spec-set and test-runtime | diff --git a/docs/specs/ADR-046-telemetry-audit-and-support.md b/docs/specs/ADR-046-telemetry-audit-and-support.md index 8419bdf6f..2e7c2ca70 100644 --- a/docs/specs/ADR-046-telemetry-audit-and-support.md +++ b/docs/specs/ADR-046-telemetry-audit-and-support.md @@ -1843,9 +1843,9 @@ New `packages/d2b-core-controller/tests/config_cleanup.rs`: | Work item ID | `ADR046-telem-001` | | Dependency/owner | W0/W1a; telemetry crate owner | | Current source | `packages/d2b-realm-core/src/trace_context.rs` (`TraceContext`, `MAX_TRACE_FIELD_LEN`); `packages/d2b-realm-core/src/audit.rs` (`AuditHash`, `AuditHashError`, `AuditChainLink`, `AuditChainRecord`); `packages/d2b-realm-core/src/ids.rs` (`OperationId`, `CorrelationId`); `packages/d2b-realm-codec-protobuf/src/lib.rs` (`encode_trace_context`, `decode_trace_context`); `packages/d2b-contract-tests/tests/policy_observability.rs::startup_tracing_avoids_host_path_fields` | -| Reuse action | extract unchanged (`TraceContext`, `AuditHash`, `AuditChainLink`); adapt (`OperationId`/`CorrelationId` for v3 record contract); add bounded emitter | +| Reuse action | adapt | | Destination | `packages/d2b-telemetry/src/{trace_context.rs,audit_hash.rs,emitter.rs,meter_registry.rs,redaction_guard.rs}` | -| Detailed design | `d2b-telemetry` provides: (1) `TraceContext` / `AuditHash` / `AuditChainLink` extracted unchanged; (2) `BoundedEmitter`: `tracing`-subscriber layer that serializes span/metric events into compact frames and writes them over a private Unix datagram socket to the `observability-otel` Provider — no `opentelemetry_sdk` dependency; (3) `RedactionGuard` span wrapper that asserts the v3 resource attribute allowlist at span creation. No OTEL SDK in this crate. | +| Detailed design | `d2b-telemetry` provides: (1) `TraceContext` / `AuditHash` / `AuditChainLink` extracted unchanged; (2) `BoundedEmitter`: `tracing`-subscriber layer that serializes span/metric events into compact frames and writes them over a private Unix datagram socket to the `observability-otel` Provider — no `opentelemetry_sdk` dependency; (3) `RedactionGuard` span wrapper that asserts the v3 resource attribute allowlist at span creation. No OTEL SDK in this crate. Primary reuse disposition: `adapt`. Preserved source-plan detail: extract unchanged (`TraceContext`, `AuditHash`, `AuditChainLink`); adapt (`OperationId`/`CorrelationId` for v3 record contract); add bounded emitter. | | Integration | Every v3 core process initializes a `BoundedEmitter` pointing at `$ZONE_STATE/telemetry/emitter.sock`; v3 audit records use `AuditHash`/`AuditChainLink` from this crate | | Data migration | Full d2b 3.0 reset; no v2 state/config import | | Validation | Unit test for `RedactionGuard` attribute gate; unit test for `BoundedEmitter` ring-full drop and FIFO drain; `policy_telemetry_redaction.rs::startup_tracing_avoids_host_path_fields` port; assert `config_source = "realm-controllers"` absent; assert no `opentelemetry_sdk` dependency in `d2b-telemetry` Cargo.toml | @@ -1858,9 +1858,9 @@ New `packages/d2b-core-controller/tests/config_cleanup.rs`: | Work item ID | `ADR046-telem-002` | | Dependency/owner | ADR046-telem-001 + ADR046-store-001; store owner | | Current source | `packages/d2bd/src/metrics.rs` (`MetricDescriptor`, `MetricKind`, `VM_START_BUCKETS_SECONDS`, `BROKER_REQUEST_BUCKETS_SECONDS`, `ACTIVATION_PHASE_BUCKETS_SECONDS` — for bucket pattern reference only; the `vm` labels are not reused) | -| Reuse action | adapt bucket boundary constants (rename; remove `vm` labels); replace hand-rolled `Registry` with `d2b-telemetry` `BoundedEmitter` meter API | +| Reuse action | adapt | | Destination | `packages/d2b-resource-store-redb/src/metrics.rs`, `packages/d2b-resource-store-redb/src/tracing.rs` | -| Detailed design | Instrument the store actor, write/read/group-commit paths with the metric inventory from this spec via `d2b-telemetry` `BoundedEmitter`. Emit `d2b.store.*` spans. The p95 `d2b_store_write_duration_seconds` hard target (≤10 ms) feeds the benchmark fixture. No `vm` label; `resource_type` label only from closed catalog. No OTEL SDK in the store crate. | +| Detailed design | Instrument the store actor, write/read/group-commit paths with the metric inventory from this spec via `d2b-telemetry` `BoundedEmitter`. Emit `d2b.store.*` spans. The p95 `d2b_store_write_duration_seconds` hard target (≤10 ms) feeds the benchmark fixture. No `vm` label; `resource_type` label only from closed catalog. No OTEL SDK in the store crate. Primary reuse disposition: `adapt`. Preserved source-plan detail: adapt bucket boundary constants (rename; remove `vm` labels); replace hand-rolled `Registry` with `d2b-telemetry` `BoundedEmitter` meter API. | | Integration | Store actor calls `d2b-telemetry` meter/tracer via `BoundedEmitter`; spans linked to API request spans via `TraceContext` | | Data migration | Full d2b 3.0 reset; no v2 state/config import | | Validation | p95 write ≤10 ms benchmark fixture; metric inventory policy test asserting no `vm` label; assert old `d2b_daemon_vm_state` shape absent | @@ -1871,11 +1871,11 @@ New `packages/d2b-core-controller/tests/config_cleanup.rs`: | Field | Value | | --- | --- | | Work item ID | `ADR046-telem-003` | -| Dependency/owner | ADR046-telem-001 + ADR046-session-001 + ADR046-bus-001; session/bus owner | +| Dependency/owner | ADR046-telem-001 + ADR046-session-001 + ADR046-session-003; session/bus owner | | Current source | `packages/d2b-realm-core/src/ids.rs` (`OperationId`, `CorrelationId`); `packages/d2b-realm-codec-protobuf/src/lib.rs` (`encode_trace_context`, `decode_trace_context` for v3 codec adaptation); `packages/d2b-realm-router/src/mux_session.rs`, `route_engine.rs` (current implemented-but-unwired generic routing) | -| Reuse action | adapt `TraceContext` protobuf codec for v3 resource API framing; adapt routing metrics patterns | +| Reuse action | adapt | | Destination | `packages/d2b-resource-api/src/metrics.rs`, `packages/d2b-session/src/metrics.rs`, `packages/d2b-bus/src/metrics.rs` | -| Detailed design | Instrument resource API verb dispatch, watch delivery, bus route resolution, and session handshake/reconnect per the metric/span catalog in this spec. Propagate `TraceContext` from incoming bus request to store write transaction span as child context. | +| Detailed design | Instrument resource API verb dispatch, watch delivery, bus route resolution, and session handshake/reconnect per the metric/span catalog in this spec. Propagate `TraceContext` from incoming bus request to store write transaction span as child context. Primary reuse disposition: `adapt`. Preserved source-plan detail: adapt `TraceContext` protobuf codec for v3 resource API framing; adapt routing metrics patterns. | | Integration | ResourceClient → bus → API → store span chain via `TraceContext` | | Data migration | Full d2b 3.0 reset; no v2 state/config import | | Validation | API request metric inventory test; session profile/outcome label cardinality gate; bus direction label gate; assert no `realm` field in span attributes | @@ -1888,9 +1888,9 @@ New `packages/d2b-core-controller/tests/config_cleanup.rs`: | Work item ID | `ADR046-telem-004` | | Dependency/owner | ADR046-telem-001 + ADR046-core-001; core-controller owner | | Current source | `packages/d2bd/src/metrics.rs` (`BROKER_REQUEST_BUCKETS_SECONDS` for reference; `d2b_daemon_broker_request_duration_seconds` labels `["op"]` as a cardinality-safe example); `packages/d2b-realm-core/src/allocator_engine.rs` (field `trace: Option` at line 873 — existing trace context wiring pattern) | -| Reuse action | adapt bucket patterns; adapt trace-context-in-reconcile pattern from `allocator_engine.rs` | +| Reuse action | adapt | | Destination | `packages/d2b-core-controller/src/metrics.rs`, `packages/d2b-core-controller/src/tracing.rs` | -| Detailed design | Emit `d2b.controller.hint` span at the instant the post-commit dispatcher fires; emit `d2b.controller.reconcile` child span at handler entry. Interval = p95 ≤5 ms target. `handler` label from closed set; no resource name labels. | +| Detailed design | Emit `d2b.controller.hint` span at the instant the post-commit dispatcher fires; emit `d2b.controller.reconcile` child span at handler entry. Interval = p95 ≤5 ms target. `handler` label from closed set; no resource name labels. Primary reuse disposition: `adapt`. Preserved source-plan detail: adapt bucket patterns; adapt trace-context-in-reconcile pattern from `allocator_engine.rs`. | | Integration | Post-commit dispatcher creates hint span; handler creates child reconcile span via `TraceContext` | | Data migration | Full d2b 3.0 reset; no v2 state/config import | | Validation | `hint_to_handler_latency` benchmark with p95 ≤5 ms assertion; closed `handler` label set gate | @@ -1903,9 +1903,9 @@ New `packages/d2b-core-controller/tests/config_cleanup.rs`: | Work item ID | `ADR046-telem-005` | | Dependency/owner | ADR046-telem-001 + ADR046-process-001; Process Provider owner | | Current source | `packages/d2bd/src/metrics.rs` (`d2b_daemon_vm_start_duration_seconds` `VM_START_BUCKETS_SECONDS`; `d2b_daemon_vm_shutdown_duration_seconds` `VM_SHUTDOWN_BUCKETS_SECONDS`; `d2b_daemon_vm_shutdown_total` labels `["vm", "vmm", "outcome"]` — `vmm` = current `RunnerRole` → v3 `provider`); `packages/d2bd/src/supervisor/pidfd.rs` (pidfd adoption/launch call sites); `packages/d2b-contracts/src/broker_wire.rs::RunnerRole` (`CloudHypervisor`, `QemuMedia`, `OtelHostBridge` etc. → v3 `provider` label values) | -| Reuse action | adapt launch histogram bucket constants; rename `vm` label to no label (process identity in resource attributes); rename `vmm`/`RunnerRole` → `provider` closed enum | +| Reuse action | adapt | | Destination | `packages/d2b-provider-supervisor/src/metrics.rs`, `packages/d2b-provider-supervisor/src/tracing.rs` | -| Detailed design | `d2b_process_launch_duration_seconds`: start = instant Process controller receives commit-to-Ready hint; end = first OS spawn call (clone3 or systemd unit start). This implements p95 ≤20 ms. `provider` label replaces `vmm`/`RunnerRole` with the closed set `{minijail,systemd}`. No `vm` name label. A separate `d2b_process_ready_duration_seconds` histogram covers launch-attempt → readiness signal (not a hard target). | +| Detailed design | `d2b_process_launch_duration_seconds`: start = instant Process controller receives commit-to-Ready hint; end = first OS spawn call (clone3 or systemd unit start). This implements p95 ≤20 ms. `provider` label replaces `vmm`/`RunnerRole` with the closed set `{minijail,systemd}`. No `vm` name label. A separate `d2b_process_ready_duration_seconds` histogram covers launch-attempt → readiness signal (not a hard target). Primary reuse disposition: `adapt`. Preserved source-plan detail: adapt launch histogram bucket constants; rename `vm` label to no label (process identity in resource attributes); rename `vmm`/`RunnerRole` → `provider` closed enum. | | Integration | Process Provider controller start handler → supervisor ticket delivery → first spawn call | | Data migration | Full d2b 3.0 reset; no v2 state/config import | | Validation | `commit_to_launch_latency` benchmark with p95 ≤20 ms assertion; assert no `vm` label in process metrics; `vmm→provider` label rename gate | @@ -1918,99 +1918,99 @@ New `packages/d2b-core-controller/tests/config_cleanup.rs`: | Work item ID | `ADR046-telem-006` | | Dependency/owner | ADR046-process-001 + ADR046-provider-001; `observability-otel` Provider owner | | Current source | `nixos-modules/components/observability/host.nix` (`otelRuntimeDir = "/run/d2b/otel"`, `hostEgressSocket`, ACL `setfacl` pattern, `scrapeJournal`, `hostCfg.identityName`); `nixos-modules/components/observability/stack.nix` (SigNoz stack, `ingressSources`, per-source `vmName`, `receiverGrpcPort`/`receiverHttpPort`, loopback binding, `cfg.signoz.listenPort`); `nixos-modules/components/observability/guest.nix` (`vm.name`/`vm.env`/`vm.role` identity stamping, guest collector); `packages/d2b-host/src/otel_host_bridge_argv.rs` (`OtelHostBridgeArgvInputs`; vsock forwarding); `packages/d2bd/src/otel_host_bridge_readiness.rs` (readiness gate pattern: `OtelHostBridgeReadiness::{Ready,Pending,Failed}`); `packages/d2b-core/src/processes.rs::ProcessRole::OtelHostBridge`; `packages/d2b-contracts/src/broker_wire.rs::RunnerRole::OtelHostBridge`; `packages/d2b-contract-tests/tests/{policy_observability.rs,minijail_relay_otel.rs}` | -| Reuse action | adapt Nix pipeline shape (replace per-VM `vmName` with per-Zone naming); adapt `OtelHostBridgeArgvInputs` vsock forwarding to native OTLP/gRPC-over-vsock; adapt readiness gate pattern (`OtelHostBridgeReadiness::Ready` → Provider phase `Ready`); adapt `ingressSources` per-VM → per-Zone | +| Reuse action | adapt | | Destination | `packages/d2b-provider-observability-otel/src/`, `nixos-modules/components/observability/` (adapted files) | -| Detailed design | `Provider/observability-otel` is an **ordinary optional non-bootstrap Process** (not counted toward the ≤64 MiB mandatory core aggregate). It owns: (1) per-Zone datagram receiver socket at `$ZONE_STATE/telemetry/emitter.sock` (drains frames from core emitters) and OTLP/gRPC Unix socket at `$ZONE_STATE/telemetry/otlp.sock`; (2) the full OTEL SDK with OTLP exporter — only this process links `opentelemetry_sdk`; (3) OTel Collector pipeline per Zone and per Host; (4) vsock OTLP forwarding to obs Zone (replaces socat-based `OtelHostBridgeArgvInputs`); (5) SigNoz stack Nix adapted from `stack.nix` with per-Zone `ingressSources` replacing per-VM `vmName`; (6) journald scrape (optional, disabled by default); (7) self-metrics endpoint. Zone/controller startup does not wait for this Provider. If absent or unready, Zone health is `Degraded` (not `Failed`). Readiness: socket exists and first drain cycle completes successfully. `d2b.observability.host.identityName` option preserved; `vmName` in `ingressSources` populated from Zone name. | +| Detailed design | `Provider/observability-otel` is an **ordinary optional non-bootstrap Process** (not counted toward the ≤64 MiB mandatory core aggregate). It owns: (1) per-Zone datagram receiver socket at `$ZONE_STATE/telemetry/emitter.sock` (drains frames from core emitters) and OTLP/gRPC Unix socket at `$ZONE_STATE/telemetry/otlp.sock`; (2) the full OTEL SDK with OTLP exporter — only this process links `opentelemetry_sdk`; (3) OTel Collector pipeline per Zone and per Host; (4) vsock OTLP forwarding to obs Zone (replaces socat-based `OtelHostBridgeArgvInputs`); (5) SigNoz stack Nix adapted from `stack.nix` with per-Zone `ingressSources` replacing per-VM `vmName`; (6) journald scrape (optional, disabled by default); (7) self-metrics endpoint. Zone/controller startup does not wait for this Provider. If absent or unready, Zone health is `Degraded` (not `Failed`). Readiness: socket exists and first drain cycle completes successfully. `d2b.observability.host.identityName` option preserved; `vmName` in `ingressSources` populated from Zone name. Primary reuse disposition: `adapt`. Preserved source-plan detail: adapt Nix pipeline shape (replace per-VM `vmName` with per-Zone naming); adapt `OtelHostBridgeArgvInputs` vsock forwarding to native OTLP/gRPC-over-vsock; adapt readiness gate pattern (`OtelHostBridgeReadiness::Ready` → Provider phase `Ready`); adapt `ingressSources` per-VM → per-Zone. | | Integration | Core process `BoundedEmitter` → `emitter.sock` → observability-otel collector → `otlp.sock` → vsock → obs Zone SigNoz; Zone startup independent of Provider readiness | | Data migration | Existing SigNoz data not migrated; v3 starts fresh | | Validation | `emitter_socket_receive`, `emitter_ring_drains_on_socket_available`, `emitter_ring_drop_on_overflow`, `no_vm_label_in_metrics`, `zone_startup_proceeds_without_provider` tests; adapted `policy_observability.rs` tests (retain `loki_native_otel_resource_attributes` and SigNoz-only backend assertions); adapted `minijail_relay_otel.rs` shape test for Provider-managed runner | | Removal proof | `otel_host_bridge_argv.rs` socat runner and `otel_host_bridge_readiness.rs` retired after `observability-otel` Provider delivers native OTLP/vsock and passes conformance; `ProcessRole::OtelHostBridge` and `RunnerRole::OtelHostBridge` retired from `d2b-core/src/processes.rs` and `d2b-contracts/src/broker_wire.rs` after Provider migration | -### ADR046-audit-001 +### ADR046-telem-012 | Field | Value | | --- | --- | -| Work item ID | `ADR046-audit-001` | +| Work item ID | `ADR046-telem-012` | | Dependency/owner | W0/W1a; audit crate owner | | Current source | `packages/d2b-realm-core/src/audit.rs` (`AuditHash::parse`, `AuditChainLink::new`/`verify`, `AuditChainRecord{stream: AuditStreamKind, realm: RealmPath, node: NodeId}`, `AuditStreamKind::{Gateway,RemoteNode,Daemon}`, `AuditSinkHealth`, `AuditRetentionFloorStatus`); `packages/d2bd/src/daemon_audit.rs` (hash-chain append algorithm, `prev_hash`/`record_hash` SHA-256 pattern, daily segment files `daemon-events-YYYY-MM-DD.jsonl`, `DaemonEvent` additive contract); `packages/d2b-priv-broker/src/audit.rs` (`AuditWriteClass::{Privileged,Unprivileged}`, `AuditDropSummary`, `DEFAULT_AUDIT_WRITES_PER_SECOND = 4096`, O_APPEND CLOEXEC file open, `AuditDropWarningState`); `packages/d2b-gateway-runtime/src/audit_jsonl.rs` (`JsonlGatewayAudit`, `DEFAULT_GATEWAY_AUDIT_RETENTION_DAYS = 14`, `prune_old` rotation algorithm); `packages/d2b-priv-broker/src/ops/audit_op.rs` (`OpAuditRecord`, `SwtpmDirAudit`, `SwtpmDirResult`, `SwtpmMarkerResult`); `packages/d2b-realm-core/src/ids.rs` (`OperationId`, `CorrelationId`, `PrincipalId` — `PrincipalId` becomes `subject_digest`); `packages/d2b/tests/audit_contract.rs`; `packages/d2b-priv-broker/tests/broker_export_audit.rs` | -| Reuse action | extract unchanged: `AuditHash`, `AuditChainLink` from `d2b-realm-core/src/audit.rs`; copy hash-chain append algorithm from `daemon_audit.rs`; copy `AuditWriteClass`/rate-limit/rotation/prune from broker `audit.rs`; adapt `JsonlGatewayAudit` segment writer; adapt `OpAuditRecord` to `BrokerEffect` record class | +| Reuse action | adapt | | Destination | `packages/d2b-audit/src/{hash_chain.rs,segment.rs,rate_limit.rs,record_types.rs,sink.rs,export.rs}` | -| Detailed design | `d2b-audit` provides: typed record structs per class; canonical serialization with `zone` replacing `realm: RealmPath`; SHA-256 hash chain (extracted from `daemon_audit.rs`); segment writer (O_APPEND CLOEXEC, 64 MiB / UTC-midnight rotation); 30-day compaction (adapts `prune_old` from `JsonlGatewayAudit`); `AuditWriteClass::{Privileged,Standard,BestEffort}` (extends current `{Privileged,Unprivileged}`); rate-limit with privileged-never-dropped invariant; export iterator with inline hash-break reporting. `AuditStreamKind` re-versioned: `Daemon→Zone`, `Gateway→ZoneLink`, `RemoteNode→RemoteZone`. `AuditChainRecord` re-versioned: `{zone: String}` replaces `{realm: RealmPath, node: NodeId}`. | +| Detailed design | `d2b-audit` provides: typed record structs per class; canonical serialization with `zone` replacing `realm: RealmPath`; SHA-256 hash chain (extracted from `daemon_audit.rs`); segment writer (O_APPEND CLOEXEC, 64 MiB / UTC-midnight rotation); 30-day compaction (adapts `prune_old` from `JsonlGatewayAudit`); `AuditWriteClass::{Privileged,Standard,BestEffort}` (extends current `{Privileged,Unprivileged}`); rate-limit with privileged-never-dropped invariant; export iterator with inline hash-break reporting. `AuditStreamKind` re-versioned: `Daemon→Zone`, `Gateway→ZoneLink`, `RemoteNode→RemoteZone`. `AuditChainRecord` re-versioned: `{zone: String}` replaces `{realm: RealmPath, node: NodeId}`. Primary reuse disposition: `adapt`. Preserved source-plan detail: extract unchanged: `AuditHash`, `AuditChainLink` from `d2b-realm-core/src/audit.rs`; copy hash-chain append algorithm from `daemon_audit.rs`; copy `AuditWriteClass`/rate-limit/rotation/prune from broker `audit.rs`; adapt `JsonlGatewayAudit` segment writer; adapt `OpAuditRecord` to `BrokerEffect` record class. | | Integration | Zone runtime, core-controller, Process Providers, broker effect bridge → `d2b-audit` sink; `d2b zone audit export` → export iterator | | Data migration | v3 bootstrap; existing daemon/broker JSONL files not migrated | | Validation | `audit_record_hash_chain`, `audit_record_schema` (no `realm`/`node` fields), `audit_segment_rotation`, `audit_rate_limit_privileged_never_dropped`, `audit_unavailable_blocks_privileged` | | Removal proof | `daemon_audit.rs`, broker `audit.rs`, `JsonlGatewayAudit` retired per-component after `d2b-audit` sink achieves parity | -### ADR046-audit-002 +### ADR046-telem-013 | Field | Value | | --- | --- | -| Work item ID | `ADR046-audit-002` | -| Dependency/owner | ADR046-audit-001 + ADR046-store-001; store/authz owner | +| Work item ID | `ADR046-telem-013` | +| Dependency/owner | ADR046-telem-012 + ADR046-store-001; store/authz owner | | Current source | `packages/d2b-priv-broker/src/ops/audit_op.rs` (`OpAuditRecord` structural pattern — operation, peer_uid, decision, result fields); `packages/d2b-realm-core/src/audit.rs::AuditEnvelope{principal: PrincipalId, scope: AuthorizationScope, decision: AuthzDecision}` (principal → v3 `subject_digest`) | -| Reuse action | adapt `OpAuditRecord` structural pattern for `ResourceMutation` / `RBACChange` record classes; adapt `PrincipalId` → `subject_digest` derivation | +| Reuse action | adapt | | Destination | `packages/d2b-resource-store-redb/src/audit.rs`, `packages/d2b-core-controller/src/authz_audit.rs` | -| Detailed design | `ResourceMutation` records emitted by the store actor inside the write transaction before commit returns. The audit sink must durably fsync the audit record before returning the commit success (privileged durability class). `RBACChange` emitted by the authz handler in the same write transaction. `subject_digest` = SHA-256 of normalized canonical subject string from v3 `AuthenticatedSubjectContext` (ADR-046-componentsession-and-bus). | +| Detailed design | `ResourceMutation` records emitted by the store actor inside the write transaction before commit returns. The audit sink must durably fsync the audit record before returning the commit success (privileged durability class). `RBACChange` emitted by the authz handler in the same write transaction. `subject_digest` = SHA-256 of normalized canonical subject string from v3 `AuthenticatedSubjectContext` (ADR-046-componentsession-and-bus). Primary reuse disposition: `adapt`. Preserved source-plan detail: adapt `OpAuditRecord` structural pattern for `ResourceMutation` / `RBACChange` record classes; adapt `PrincipalId` → `subject_digest` derivation. | | Integration | Store write transaction → `d2b-audit` sink → fsync → commit result | | Data migration | Full d2b 3.0 reset; no v2 state/config import | | Validation | Integration test: 100 mutations → verify hash-chained audit records with `zone` field, no `realm` field | | Removal proof | None — net-new; no prior owner to remove | -### ADR046-audit-003 +### ADR046-telem-014 | Field | Value | | --- | --- | -| Work item ID | `ADR046-audit-003` | -| Dependency/owner | ADR046-audit-001 + ADR046-session-001 + ADR046-bus-001; session/bus owner | +| Work item ID | `ADR046-telem-014` | +| Dependency/owner | ADR046-telem-012 + ADR046-session-001 + ADR046-session-003; session/bus owner | | Current source | `packages/d2b-gateway/src/audit.rs` (`GatewayAuditEvent`, `GatewayAuditKind::{DisplaySessionOpenAdmitted,DisplaySessionOpenDenied,DisplaySessionRunning,DisplaySessionClosed}`, `GatewayAudit` trait, `NoopGatewayAudit`); `packages/d2b-realm-core/src/audit.rs::AuditEnvelope{realm, principal, scope, decision, trace}` (fields adapted to v3 `SessionConnect` record class) | -| Reuse action | adapt `GatewayAudit` trait pattern for `SessionConnect` and `RouteAdmission` record classes; `NoopGatewayAudit` pattern reused for test sinks | +| Reuse action | adapt | | Destination | `packages/d2b-session/src/audit.rs`, `packages/d2b-bus/src/audit.rs` | -| Detailed design | `SessionConnect` records emitted at handshake completion. `GatewayAuditKind::DisplaySessionOpenAdmitted/Denied` → `event="connect"`, `authz_decision="allowed/denied"`. `GatewayAuditKind::DisplaySessionRunning` → informational `ProcessEffect`. `GatewayAuditKind::DisplaySessionClosed` → `event="close"`. `transport_class=zone_link` covers what the current `AuditStreamKind::Gateway` stream recorded for gateway-backed realm sessions. `RouteAdmission` records emitted at bus route resolution for denied routes. | +| Detailed design | `SessionConnect` records emitted at handshake completion. `GatewayAuditKind::DisplaySessionOpenAdmitted/Denied` → `event="connect"`, `authz_decision="allowed/denied"`. `GatewayAuditKind::DisplaySessionRunning` → informational `ProcessEffect`. `GatewayAuditKind::DisplaySessionClosed` → `event="close"`. `transport_class=zone_link` covers what the current `AuditStreamKind::Gateway` stream recorded for gateway-backed realm sessions. `RouteAdmission` records emitted at bus route resolution for denied routes. Primary reuse disposition: `adapt`. Preserved source-plan detail: adapt `GatewayAudit` trait pattern for `SessionConnect` and `RouteAdmission` record classes; `NoopGatewayAudit` pattern reused for test sinks. | | Integration | Session engine and bus router → `d2b-audit` sink | | Data migration | Full d2b 3.0 reset; no v2 state/config import | | Validation | Session connect/close/auth-failure audit tests; `GatewayAuditKind` → `SessionConnect` mapping test | | Removal proof | `NoopGatewayAudit` and gateway JSONL sink retired after gateway is on v3 resource API | -### ADR046-audit-004 +### ADR046-telem-015 | Field | Value | | --- | --- | -| Work item ID | `ADR046-audit-004` | -| Dependency/owner | ADR046-audit-001; CLI owner | +| Work item ID | `ADR046-telem-015` | +| Dependency/owner | ADR046-telem-012; CLI owner | | Current source | `packages/d2b/tests/audit_contract.rs` (`d2b audit --strict` returns 78; `auditResponse` relay; `authz-audit-requires-admin` denial; daemon-down exit 1 without bash fallback); `packages/d2b-priv-broker/tests/broker_export_audit.rs` (`export_audit_requires_admin_and_exports_op_audit_records`: admin-only, path-free, NDJSON `ExportBrokerAuditOk` shape, `peer_uid` field, `ApplyNftables` operation name in records) | -| Reuse action | adapt audit CLI contract test (daemon-down/exit behavior); adapt broker export test to new `zone` field and v3 record schema | +| Reuse action | adapt | | Destination | `packages/d2b/src/zone_audit.rs` (new `d2b zone audit export` subcommand); `packages/d2b/tests/zone_audit_contract.rs` | -| Detailed design | `d2b zone audit export` opens segments read-only (shared flock), streams NDJSON to stdout, validates hash chain inline, reports breaks as inline error records, enforces `audit-export` verb via resource API (admin-only, same `SO_PEERCRED`/Role check as current `ExportBrokerAuditOk`). Assert no `realm`, `node`, `workload_id` fields in exported records. | +| Detailed design | `d2b zone audit export` opens segments read-only (shared flock), streams NDJSON to stdout, validates hash chain inline, reports breaks as inline error records, enforces `audit-export` verb via resource API (admin-only, same `SO_PEERCRED`/Role check as current `ExportBrokerAuditOk`). Assert no `realm`, `node`, `workload_id` fields in exported records. Primary reuse disposition: `adapt`. Preserved source-plan detail: adapt audit CLI contract test (daemon-down/exit behavior); adapt broker export test to new `zone` field and v3 record schema. | | Integration | `d2b` CLI → resource API `audit-export` verb → `d2b-audit` export iterator → stdout | | Data migration | Full d2b 3.0 reset; no v2 state/config import | | Validation | `export_audit.rs`: admin-only, hash break inline, no old field names (`realm`/`node`/`workload_id`), no path/argv in output, exit 0 on clean chain | | Removal proof | `d2b audit` legacy command retained until `d2b zone audit export` covers all record classes | -### ADR046-doctor-001 +### ADR046-telem-016 | Field | Value | | --- | --- | -| Work item ID | `ADR046-doctor-001` | -| Dependency/owner | ADR046-core-001 + ADR046-audit-001; CLI/doctor owner | +| Work item ID | `ADR046-telem-016` | +| Dependency/owner | ADR046-core-001 + ADR046-telem-012; CLI/doctor owner | | Current source | `packages/d2b/tests/host_doctor_contract.rs` (env-redirect sandbox: `D2B_BROKER_SOCKET`, `D2B_PUBLIC_SOCKET`, `D2B_DAEMON_STATE_DIR`, `D2B_METRICS_URL`, `D2B_MANIFEST_PATH`; `doctor::render_summary` JSON envelope fields: `command`, `mode`, `broker_ready`, per-check `status`+`data`, `summary`, `exitCode`); `packages/d2bd/src/audit_check.rs` (`defects` array audit-chain validation pattern); `packages/d2bd/src/lib.rs` (doctor read-only path: `host doctor --read-only` reads from `D2B_DAEMON_STATE_DIR`, pidfd_table file, kernel-module check file, metrics URL) | -| Reuse action | adapt env-redirect sandbox test harness; adapt `defects` array pattern for `audit-hash-chain-clean` check; adapt `broker_ready` → `zone_phase` field | +| Reuse action | adapt | | Destination | `packages/d2b/src/zone_doctor.rs`, `packages/d2b/tests/zone_doctor_contract.rs` | -| Detailed design | `d2b zone doctor [--zone ] [--json]` reads resource status from Zone API (read-only verb), OTEL self-metrics from `observability-otel` Provider endpoint (optional), and audit segment inventory from `d2b-audit` segment reader. Named check set from this spec. Exit 0 on all-ready; 1 on any warn/error. Env-redirect sandbox for all test fixtures. Current `MANIFEST_JSON` fixture pattern adapted: `"_observability": {"enabled": false}` test ensures OTEL probe short-circuits cleanly. | +| Detailed design | `d2b zone doctor [--zone ] [--json]` reads resource status from Zone API (read-only verb), OTEL self-metrics from `observability-otel` Provider endpoint (optional), and audit segment inventory from `d2b-audit` segment reader. Named check set from this spec. Exit 0 on all-ready; 1 on any warn/error. Env-redirect sandbox for all test fixtures. Current `MANIFEST_JSON` fixture pattern adapted: `"_observability": {"enabled": false}` test ensures OTEL probe short-circuits cleanly. Primary reuse disposition: `adapt`. Preserved source-plan detail: adapt env-redirect sandbox test harness; adapt `defects` array pattern for `audit-hash-chain-clean` check; adapt `broker_ready` → `zone_phase` field. | | Integration | `d2b` CLI → Zone resource API status reads + audit segment reader | | Data migration | Full d2b 3.0 reset; no v2 state/config import | | Validation | `zone_doctor_contract.rs`: all-ready/degraded/quarantine/otel-absent/audit-absent fixtures; no resource names/paths/argv/PIDs; `zone_phase` field present; no legacy `broker_ready` field | | Removal proof | `d2b host doctor` retained until `d2b zone doctor` covers all check parity | -### ADR046-doctor-002 +### ADR046-telem-017 | Field | Value | | --- | --- | -| Work item ID | `ADR046-doctor-002` | -| Dependency/owner | ADR046-doctor-001; CLI/doctor owner | +| Work item ID | `ADR046-telem-017` | +| Dependency/owner | ADR046-telem-016; CLI/doctor owner | | Current source | `packages/d2b/tests/host_doctor_contract.rs` (env-redirect sandbox scaffold — no current `support-bundle` equivalent exists) | -| Reuse action | reuse env-redirect sandbox scaffold | +| Reuse action | adapt | | Destination | `packages/d2b/src/zone_support_bundle.rs`, `packages/d2b/tests/zone_support_bundle_contract.rs` | -| Detailed design | `d2b zone support-bundle [--zone ]` requires `support-bundle` verb. Reads bounded resource status snapshots (32 per type, 512 total; metadata + status only; no spec bytes; no `metadata.name`), controller queue depths, schema catalog (names+versions only), audit segment inventory, OTEL collector metrics summary, bounded structured log ring (2000 entries). NDJSON output. On quarantine: `bundle_completeness: "partial"`, exit 1. | +| Detailed design | `d2b zone support-bundle [--zone ]` requires `support-bundle` verb. Reads bounded resource status snapshots (32 per type, 512 total; metadata + status only; no spec bytes; no `metadata.name`), controller queue depths, schema catalog (names+versions only), audit segment inventory, OTEL collector metrics summary, bounded structured log ring (2000 entries). NDJSON output. On quarantine: `bundle_completeness: "partial"`, exit 1. Primary reuse disposition: `adapt`. Preserved source-plan detail: reuse env-redirect sandbox scaffold. | | Integration | `d2b` CLI → Zone resource API list (status subresource only) + controller introspection + audit segment reader + OTEL self-metrics | | Data migration | Full d2b 3.0 reset; no v2 state/config import | | Validation | `zone_support_bundle_contract.rs`: complete/partial bundles; no spec/name/path/argv; field completeness | @@ -2023,9 +2023,9 @@ New `packages/d2b-core-controller/tests/config_cleanup.rs`: | Work item ID | `ADR046-telem-007` | | Dependency/owner | ADR046-telem-006; Nix/observability owner | | Current source | `nixos-modules/components/observability/host.nix` (`scrapeJournal = hostCfg.scrapeJournal`, `journaldStorageDir = "/var/lib/d2b-host-otel-collector/journald"`, cgroup-path filtering pattern for host units) | -| Reuse action | adapt journald receiver config for per-Zone cgroup filter (`z-/*`, `s-/*`) | +| Reuse action | adapt | | Destination | `packages/d2b-provider-observability-otel/src/nix/journald.nix` (new Nix fragment) | -| Detailed design | `d2b.zones..observability.journald.enable = false` (default). When enabled: journald receiver follows `z-/*` and `s-/*` cgroup filters. Collector applies redaction: drops `MESSAGE` credential/path patterns, `_CMDLINE`, `_EXE`, `INVOCATION_ID`. Current `scrapeJournal` host option is preserved unchanged. | +| Detailed design | `d2b.zones..observability.journald.enable = false` (default). When enabled: journald receiver follows `z-/*` and `s-/*` cgroup filters. Collector applies redaction: drops `MESSAGE` credential/path patterns, `_CMDLINE`, `_EXE`, `INVOCATION_ID`. Current `scrapeJournal` host option is preserved unchanged. Primary reuse disposition: `adapt`. Preserved source-plan detail: adapt journald receiver config for per-Zone cgroup filter (`z-/*`, `s-/*`). | | Integration | `observability-otel` Provider Nix config → OTel Collector journald receiver → redaction filter → obs Zone | | Data migration | Full d2b 3.0 reset; no v2 state/config import | | Validation | Nix eval test: filter expression set when enabled; test that `_CMDLINE` and `INVOCATION_ID` appear in drop list | @@ -2038,24 +2038,24 @@ New `packages/d2b-core-controller/tests/config_cleanup.rs`: | Work item ID | `ADR046-telem-008` | | Dependency/owner | ADR046-telem-001; policy/contract-tests owner | | Current source | `packages/d2b-contract-tests/tests/policy_observability.rs` (`loki_native_otel_resource_attributes` allowlist: `["deployment.environment","host.name","service.name","service.namespace","source","vm.env","vm.name","vm.role"]`; `tempo_stack_signoz_backend_and_collector` SigNoz-only assertion; `startup_tracing_avoids_host_path_fields` forbidden fields); `packages/d2b-contract-tests/tests/policy_metrics.rs` (`EXPECTED_METRICS` table parity with `docs/reference/daemon-metrics.md`) | -| Reuse action | adapt and extend; keep existing tests; add new policy gates | +| Reuse action | adapt | | Destination | `packages/d2b-contract-tests/tests/policy_telemetry_redaction.rs` (new); updated `policy_observability.rs`; updated `policy_metrics.rs` | -| Detailed design | (1) Extend `loki_native_otel_resource_attributes` allowlist to include `d2b.zone`, `d2b.provider`, `d2b.component`, `service.version`. (2) Add redaction lint: scan all v3 instrumentation call sites for `realm`, `workload_id`, `node_id`, `vm` (as label key), `path`, `socket`, `argv`, `pid`, `exe`. (3) Add metric label gate: assert no v3 `MetricDescriptor` carries a `vm` label. (4) Add bucket boundary gates for 5 ms and 20 ms. (5) Retain: `startup_tracing_avoids_host_path_fields`; SigNoz-only backend assertion; `tempo_guest_collector_shape`; `config_source = "realm-controllers"` absence gate. | +| Detailed design | (1) Extend `loki_native_otel_resource_attributes` allowlist to include `d2b.zone`, `d2b.provider`, `d2b.component`, `service.version`. (2) Add redaction lint: scan all v3 instrumentation call sites for `realm`, `workload_id`, `node_id`, `vm` (as label key), `path`, `socket`, `argv`, `pid`, `exe`. (3) Add metric label gate: assert no v3 `MetricDescriptor` carries a `vm` label. (4) Add bucket boundary gates for 5 ms and 20 ms. (5) Retain: `startup_tracing_avoids_host_path_fields`; SigNoz-only backend assertion; `tempo_guest_collector_shape`; `config_source = "realm-controllers"` absence gate. Primary reuse disposition: `adapt`. Preserved source-plan detail: adapt and extend; keep existing tests; add new policy gates. | | Integration | Contract-tests run in workspace check and `make test-drift` | | Data migration | Full d2b 3.0 reset; no v2 state/config import | | Validation | These tests are their own validation artifact | | Removal proof | None — net-new; no prior owner to remove | -### ADR046-host-posture-001 +### ADR046-telem-018 | Field | Value | | --- | --- | -| Work item ID | `ADR046-host-posture-001` | -| Dependency/owner | ADR046-audit-001 + ADR046-core-001; `Provider/system-core` owner | +| Work item ID | `ADR046-telem-018` | +| Dependency/owner | ADR046-telem-012 + ADR046-core-001; `Provider/system-core` owner | | Current source | `packages/d2b-core/src/unsafe_local_workloads.rs` (`UnsafeLocalWorkloadsJson`, `UnsafeLocalWorkload`, `UnsafeLocalLauncherItem`, `UNSAFE_LOCAL_WORKLOADS_SCHEMA_VERSION = "v2"`, `MAX_UNSAFE_LOCAL_WORKLOADS = 256`); `packages/d2b-contracts/src/unsafe_local_wire.rs` (`HelperHello.uid: u32`, `HelperLaunchRequest`, `HelperShellRequest`, `HelperScopeKind::{Exec,Shell}`, `DaemonToUnsafeLocalHelper`, `UnsafeLocalHelperToDaemon`); `packages/d2bd/src/unsafe_local_helper.rs` (`HelperRegistry::new(daemon_uid, allowed_uids)`, `dispatch_launch`, `bind_helper_socket`); `packages/d2b-unsafe-local-helper/src/{main,protocol,runtime,systemd}.rs` (`HelperClient`, `ScopeRuntime`, `run_scope_supervisor`, `SystemdUserScopeManager`); `nixos-modules/options-realms-workloads.nix` (lines 221, 233–235 `kind = "unsafe-local"` description; lines 264–275 null `stateDir`/`runDir`); `nixos-modules/unsafe-local-workloads-json.nix` (`runtimeKind = "unsafe-local"`, `providerId = "unsafe-local"`); `nixos-modules/unsafe-local-helper.nix` (service unit) | -| Reuse action | adapt `UnsafeLocalWorkload` private-bundle contract for the `Host` resource spec payload; adapt `HelperRegistry::allowed_uids` constraint as `defaultUserRef=User/` validation; adapt Nix `unsafe-local-workloads-json.nix` emitter for the new Host resource shape; gap-fill: add `ProcessEffect{no_isolation:true}` at `dispatch_launch` / stop call sites | +| Reuse action | adapt | | Destination | `packages/d2b-provider-system-core/src/{host_reconciler.rs,host_status.rs,host_process_audit.rs}`; adapted `nixos-modules/unsafe-local-workloads-json.nix`; `packages/d2b-provider-system-core/tests/host_posture_contract.rs` | -| Detailed design | `Provider/system-core` reconciler: (1) On user-only `Host` resource creation (`defaultDomain=user`, `allowedDomains=[user]`), set `status.isolationPosture = "none"` and `status.isolationPostureMessage = "..."` unconditionally; reject any operator-supplied value for these fields. Host resources with other execution policies do not receive `isolationPosture`. (2) On every user-only Host process launch: emit `ProcessEffect{event:"launch", provider:"system-core-user", domain:"user", no_isolation:true, ...}` audit record. (3) On every user-only Host process stop: emit `ProcessEffect{event:"stop", ...}`. (4) `d2b zone list`/`inspect` CLI renders `⚠ no isolation boundary (user domain)` annotation only for `Host` resources with `isolationPosture: "none"`; annotation is not suppressible. (5) `isolation-posture-declared` doctor check: passes when user-only `Host` resource status has `isolationPosture: "none"`; omitted when Zone has no user-only `Host` resources. (6) `no_isolation=true` is emitted in `ProcessEffect` records only; it does not appear in any OTEL span attribute, log field, or metric label. | +| Detailed design | `Provider/system-core` reconciler: (1) On user-only `Host` resource creation (`defaultDomain=user`, `allowedDomains=[user]`), set `status.isolationPosture = "none"` and `status.isolationPostureMessage = "..."` unconditionally; reject any operator-supplied value for these fields. Host resources with other execution policies do not receive `isolationPosture`. (2) On every user-only Host process launch: emit `ProcessEffect{event:"launch", provider:"system-core-user", domain:"user", no_isolation:true, ...}` audit record. (3) On every user-only Host process stop: emit `ProcessEffect{event:"stop", ...}`. (4) `d2b zone list`/`inspect` CLI renders `⚠ no isolation boundary (user domain)` annotation only for `Host` resources with `isolationPosture: "none"`; annotation is not suppressible. (5) `isolation-posture-declared` doctor check: passes when user-only `Host` resource status has `isolationPosture: "none"`; omitted when Zone has no user-only `Host` resources. (6) `no_isolation=true` is emitted in `ProcessEffect` records only; it does not appear in any OTEL span attribute, log field, or metric label. Primary reuse disposition: `adapt`. Preserved source-plan detail: adapt `UnsafeLocalWorkload` private-bundle contract for the `Host` resource spec payload; adapt `HelperRegistry::allowed_uids` constraint as `defaultUserRef=User/` validation; adapt Nix `unsafe-local-workloads-json.nix` emitter for the new Host resource shape; gap-fill: add `ProcessEffect{no_isolation:true}` at `dispatch_launch` / stop call sites. | | Integration | `Provider/system-core` reconciler → `d2b-audit` sink; `d2b zone doctor` → resource status check; `d2b zone list`/`inspect` → CLI output renderer | | Data migration | Full d2b 3.0 reset; no v2 state/config import | | Validation | `host_posture_contract.rs` tests from the Host posture tests section of this spec; `d2b-contract-tests/tests/policy_telemetry_redaction.rs` asserts `no_isolation` key absent from all span/metric/log surfaces | @@ -2071,16 +2071,16 @@ evidence. Each item below records: exact main commit file/symbol, selected behav exact v3 destination/integration, and the ADR45-specific assumptions that must be excluded or adapted. -### ADR046-reuse-001 — ComponentSession v2 runtime +### ADR046-telem-019 — ComponentSession v2 runtime | Field | Value | | --- | --- | -| Work item ID | `ADR046-reuse-001` | +| Work item ID | `ADR046-telem-019` | | Dependency/owner | ADR046-telem-001 (d2b-telemetry must exist first for MetricsSink injection); session owner | | Current source | Main commit source row below: `packages/d2b-session/` and `packages/d2b-contracts/src/v2_component_session.rs` at `a1cc0b2d`. | -| Reuse action | Copy/adopt the core ComponentSession runtime; adapt EndpointPurpose/ServicePackage/RealmSessionAuthority names listed in Excluded ADR45 assumptions and inject a `d2b-telemetry` MetricsSink. | +| Reuse action | adapt | | Destination | `packages/d2b-session/` copied verbatim; v3 contract extension renames endpoint purpose values while preserving backward wire compatibility during transition. | -| Detailed design | Noise handshake, record protection, scheduler, named streams, cancellation, attachments, deadlines, metrics injection, ttrpc serving, and generation-discovery behavior from the selected source. | +| Detailed design | Noise handshake, record protection, scheduler, named streams, cancellation, attachments, deadlines, metrics injection, ttrpc serving, and generation-discovery behavior from the selected source. Primary reuse disposition: `adapt`. Preserved source-plan detail: Copy/adopt the core ComponentSession runtime; adapt EndpointPurpose/ServicePackage/RealmSessionAuthority names listed in Excluded ADR45 assumptions and inject a `d2b-telemetry` MetricsSink.. | | Data migration | Full d2b 3.0 reset; no v2 session runtime state/config import | | Removal proof | ADR45 topology enum names listed in Excluded ADR45 assumptions are adapted; no prior v3 session runtime owner to remove. | | Main commit source | `packages/d2b-session/` at `a1cc0b2d`. Key files: `src/lib.rs` (full public surface), `src/handshake.rs` (`NoiseHandshake`, `HandshakeCredentials`, `HandshakeRole`, `NegotiatedOffer`, generation-discovery encode/decode, transcript-hash), `src/engine.rs` (`SessionEngine`, `SessionEvent`), `src/driver.rs` (`ComponentSessionDriver`, `SessionDriverHandle`), `src/cancellation.rs` (`Cancellation`, `RequestRegistry`, `CancelRequest`/`CancelAck`/`CancelResult`), `src/streams.rs` (`NamedStreamMux`, `StreamId`, `StreamPhase`, `StreamEvent`), `src/attachment.rs` (`OwnedAttachment`, `AttachmentPayload`, `AttachmentValidationError`), `src/metrics.rs` (`MetricEvent`, `MetricsSink`, `NoopMetrics`), `src/record.rs` (`ProtectedRecord`, `RecordProtector`), `src/scheduler.rs` (`FairScheduler`, `OutboundFrame`, `QueueClass`), `src/server.rs` (`serve_ttrpc_services`), `src/lifecycle.rs` (`SessionLifecycle`, `SessionPhase`, `KeepaliveAction`), `src/deadline.rs` (`DeadlineBudget`). Tests: `tests/component_session.rs` (noise vector tests, record-protection round-trip, schema-mismatch rejection, LimitProfile fixture), `tests/noise_vectors.rs` (committed KAT vectors). Contract source: `packages/d2b-contracts/src/v2_component_session.rs` (all `closed_enum!` tables, `LimitProfile`, `EndpointPolicy`, `EndpointPolicyIdentity`, `MetricLabels`, all 50+ constants including `MAX_ACTIVE_NAMED_STREAMS=128`, `MAX_SESSION_ATTACHMENTS=256`, `LOCAL_HANDSHAKE_DEADLINE_MS=5000`, `MAX_RECONNECT_ATTEMPTS=10`). | @@ -2090,16 +2090,16 @@ excluded or adapted. | Integration | `d2b-bus` route handler calls `serve_ttrpc_services`; `d2b-session-unix` provides `OwnedTransport` impl; `d2b-telemetry` `MetricsSink` impl feeds `d2b_session_*` metrics inventory from this spec. | | Validation | Adopt `tests/component_session.rs` and `tests/noise_vectors.rs` unchanged; extend with v3 `EndpointPurpose` enum gate test; add `d2b-contract-tests/tests/component_session_v2_vectors.rs` (existing at `a1cc0b2d`) as-is. | -### ADR046-reuse-002 — Unix transport substrate +### ADR046-telem-020 — Unix transport substrate | Field | Value | | --- | --- | -| Work item ID | `ADR046-reuse-002` | -| Dependency/owner | ADR046-reuse-001; Unix transport owner | +| Work item ID | `ADR046-telem-020` | +| Dependency/owner | ADR046-telem-019; Unix transport owner | | Current source | Main commit source row below: `packages/d2b-session-unix/` at `a1cc0b2d`. | -| Reuse action | Copy verbatim; retain `host-socket`/`native-vsock` feature flags; no ADR45 topology adaptation required. | +| Reuse action | adapt | | Destination | `packages/d2b-session-unix/` copied verbatim. | -| Detailed design | SO_PASSCRED verification, pidfd identity verification, multi-scope credit reservation, emergency headroom, seqpacket atomic CLOEXEC transfer, stream reassembly, vsock transport, and descriptor policy enforcement. | +| Detailed design | SO_PASSCRED verification, pidfd identity verification, multi-scope credit reservation, emergency headroom, seqpacket atomic CLOEXEC transfer, stream reassembly, vsock transport, and descriptor policy enforcement. Primary reuse disposition: `adapt`. Preserved source-plan detail: Copy verbatim; retain `host-socket`/`native-vsock` feature flags; no ADR45 topology adaptation required.. | | Data migration | Full d2b 3.0 reset; no v2 transport runtime state/config import | | Removal proof | None — net-new; no prior owner to remove | | Main commit source | `packages/d2b-session-unix/` at `a1cc0b2d`. Key files: `src/adapter.rs` (`UnixSeqpacketTransport`, `UnixStreamTransport`, `PeerIdentityPolicy`, `DescriptorPolicyResolver`, `OwnedUnixAttachment`, `UnixAttachmentPayload`, `PathnamePeerVerifier`), `src/credit.rs` (`CreditPool`, `CreditScope`, `CreditScopeSet`, `ProcessCreditLimit`, `CreditBundle`, `CreditError` — rollback-on-failure, emergency-headroom constants), `src/descriptor.rs` (`PeerCredentials`, `FirstPacketCredentials`, `ObjectIdentity`, `PidfdIdentityPolicy`, `DescriptorPolicy`, `AcceptedAttachment`, `ReceivedPacket`), `src/pidfd.rs` (`PidfdEvidence`, `PidfdIdentityVerifier`, `verify_pidfd`), `src/socket.rs` (`SeqpacketSocket`, `StreamSocket`), `src/vsock.rs` (vsock transport). Tests: `tests/unix_session.rs` (`ancillary_capacity_is_derived_from_closed_hard_bounds`, `process_limit_preserves_emergency_headroom`, `failed_multiscope_reservation_rolls_back_every_prior_scope`, `staged_credit_reservations_release_once_at_each_scope`, `inherited_passcred_is_verified_but_never_repaired`, `first_packet_has_exact_directional_credentials`, `seqpacket_transfer_is_atomic_cloexec_and_object_exact`, `duplicate_kernel_objects_are_rejected_and_cleaned_up`, `owned_transport_adapters_transfer_packets_and_owned_files_end_to_end`, `stream_transport_reassembles_partial_and_coalesced_records`, `stream_transport_distinguishes_clean_and_partial_eof`). | @@ -2109,16 +2109,16 @@ excluded or adapted. | Integration | `d2b-bus` Zone-local listeners use `UnixSeqpacketTransport`; Provider agent connections use vsock transport from this crate; `CreditPool`/`CreditScopeSet` enforce per-Zone attachment FD budgets. | | Validation | Adopt all `unix_session.rs` tests unchanged. | -### ADR046-reuse-003 — Async client and retry layer +### ADR046-telem-021 — Async client and retry layer | Field | Value | | --- | --- | -| Work item ID | `ADR046-reuse-003` | -| Dependency/owner | ADR046-reuse-001; client owner | +| Work item ID | `ADR046-telem-021` | +| Dependency/owner | ADR046-telem-019; client owner | | Current source | Main commit source row below: `packages/d2b-client/` at `a1cc0b2d`. | -| Reuse action | Copy client layer; adapt service package/method sets, local-root Zone identity, and TraceContext propagation for v3. | +| Reuse action | adapt | | Destination | `packages/d2b-client/` copied; DaemonClient/GuestClient adapted to v3 service packages; `MetadataInput::with_trace` drives `TraceContext` propagation. | -| Detailed design | MetadataInput trace/correlation/idempotency, retry policy, cancellation token, generic client over resolver/connector/clock, typed service proxies, stream terminal, and host-socket verifier. | +| Detailed design | MetadataInput trace/correlation/idempotency, retry policy, cancellation token, generic client over resolver/connector/clock, typed service proxies, stream terminal, and host-socket verifier. Primary reuse disposition: `adapt`. Preserved source-plan detail: Copy client layer; adapt service package/method sets, local-root Zone identity, and TraceContext propagation for v3.. | | Data migration | Full d2b 3.0 reset; no v2 client state/config import | | Removal proof | ADR45 Daemon/Guest wrappers and RealmPath identity assumptions are adapted to v3 service packages/Zone name; no prior v3 client owner to remove. | | Main commit source | `packages/d2b-client/` at `a1cc0b2d`. Key files: `src/client.rs` (`Client`, `ConnectedClient`, `CallOptions`, `RetryPolicy`, `CancellationToken`, `MetadataInput`, `Response`, `WallClock`, `SystemClock`), `src/service.rs` (`ServiceHandle`, `MethodHandle`, `GeneratedClient`, `ServiceKind`), `src/session.rs` (session reconnect and credential binding), `src/target.rs` (target/resolver abstraction), `src/daemon_service.rs` (`DaemonClient`, `DaemonMethod`, `DaemonLifecycleRequest`, `DaemonTerminal`, `daemon_call_options`), `src/guest_service.rs` (`GuestClient`, `GuestOperation`, `GuestInspectCall`, `GuestCancelCall`, `GuestRetainedLogCall`), `src/host_socket.rs` (`HostSocketConnector`, `local_daemon_endpoint_identity`), `src/error.rs` (`ClientError`, `RemoteErrorKind`, `RetryClass`). Tests: `client.rs` (`typed_routes_select_exact_transport_without_fallback`, `daemon_typed_list_preserves_projection_and_truncation`, `daemon_typed_errors_and_generation_mismatch_are_actionable`, `guest_exec_management_preserves_typed_state_and_cancel_correlation`, `guest_retained_log_open_binds_range_resource_and_selection`, `terminal_uses_server_stream_and_validates_bidirectional_lifecycle`, `daemon_guest_proxy_reuses_the_authenticated_session`, `absent_daemon_guest_proxy_fails_closed_without_reconnecting`). | @@ -2128,16 +2128,16 @@ excluded or adapted. | Integration | Every controller/service that makes outbound calls uses `Client`; `MetadataInput::with_trace` feeds `d2b_api_request_duration_seconds` trace-id into `d2b.bus.route` span. | | Validation | Adopt typed-route, proxy-reuse, and cancel tests unchanged. Add v3 service-package name gate test. | -### ADR046-reuse-004 — Provider registry, RPC proxy, and conformance toolkit +### ADR046-telem-022 — Provider registry, RPC proxy, and conformance toolkit | Field | Value | | --- | --- | -| Work item ID | `ADR046-reuse-004` | -| Dependency/owner | ADR046-reuse-001 + ADR046-reuse-003; Provider owner | +| Work item ID | `ADR046-telem-022` | +| Dependency/owner | ADR046-telem-019 + ADR046-telem-021; Provider owner | | Current source | Main commit source row below: `packages/d2b-provider/` and `packages/d2b-provider-toolkit/` at `a1cc0b2d`. | -| Reuse action | Copy provider runtime/toolkit; adapt SessionIdentity Zone name and route through `d2b-bus` instead of daemon embedding. | +| Reuse action | adapt | | Destination | `packages/d2b-provider/` and `packages/d2b-provider-toolkit/` copied with v3 session admission and bus routing adaptations. | -| Detailed design | ProviderRegistry factory registration, bounded admission, RPC proxy fail-closed behavior, generated provider service admission/dispatch, redaction wrappers, and conformance gates. | +| Detailed design | ProviderRegistry factory registration, bounded admission, RPC proxy fail-closed behavior, generated provider service admission/dispatch, redaction wrappers, and conformance gates. Primary reuse disposition: `adapt`. Preserved source-plan detail: Copy provider runtime/toolkit; adapt SessionIdentity Zone name and route through `d2b-bus` instead of daemon embedding.. | | Data migration | Full d2b 3.0 reset; no v2 ProviderRegistry runtime state import | | Removal proof | Daemon-embedded ADR45 ProviderRegistry wiring is excluded; v3 provider-process registry replaces it without importing state. | | Main commit source | `packages/d2b-provider/` at `a1cc0b2d`. Files: `src/registry.rs` (`ProviderRegistry`, `ProviderRegistryBuilder`, `RegistryLimits`, `AdmissionOptions`), `src/rpc.rs` (`RpcCall`, `RpcPayload`, `RpcResponse`, `RpcOperation`, `AuthenticatedProviderRpc`, `RpcProviderProxy`, `SessionIdentity`), `src/instance.rs` (`ProviderInstance`, `ProviderFactory`, `CancellationToken`), `src/context.rs` (`ProviderCallContext`, `OwnedOperationContext`), `src/error.rs` (`ProviderRuntimeError`, `RegistryBuildError`, `FactoryError`). Tests: `runtime.rs` (`closed_error_context_is_actionable_without_identity_leaks`, `health_uses_no_input_inspection_methods_for_every_axis`, `all_provider_traits_are_object_safe`). `packages/d2b-provider-toolkit/` at `a1cc0b2d`. Files: `src/server.rs` (`GeneratedProviderServiceServer`, session admission, ttrpc method dispatch), `src/adapter.rs` (`invoke_session` → `AuthenticatedProviderRpc` bridge), `src/conformance.rs` (`check_descriptor_conformance`, `check_provider_conformance`), `src/fixture.rs` (conformance fixture), `src/redaction.rs` (`Redacted`, `Secret`), `src/registration.rs`, `src/values.rs`. Tests: `tests/conformance.rs` (`every_axis_passes_identical_in_process_and_rpc_conformance`, `conformance_uses_the_exact_real_descriptor_placement_and_target`, `provider_values_preserve_all_descriptor_and_operation_bindings`, `exact_registration_supports_all_axes_and_shared_factories`, `adapter_rejects_authenticated_identity_mismatch`, `rpc_proxy_fails_closed_on_cancellation_and_method_mismatch`, `rpc_proxy_preserves_plan_handle_and_adoption_bindings`, `rpc_proxy_rejects_mismatched_observability_query_results`, `redaction_wrappers_do_not_expose_canaries`). | @@ -2147,16 +2147,16 @@ excluded or adapted. | Integration | Each v3 Provider process embeds `ProviderRegistry` + `GeneratedProviderServiceServer`; `check_provider_conformance` runs in Provider install-time conformance check (feeds `d2b_provider_reconcile_total{outcome="error"}` on failure). | | Validation | Adopt all `conformance.rs` and `runtime.rs` tests unchanged. Add v3 `SessionIdentity` zone-name gate. Add conformance-failure → `d2b_provider_reconcile_total` metric integration test. | -### ADR046-reuse-005 — Provider agent process and gateway-runtime audit bridge +### ADR046-telem-023 — Provider agent process and gateway-runtime audit bridge | Field | Value | | --- | --- | -| Work item ID | `ADR046-reuse-005` | -| Dependency/owner | ADR046-reuse-004 + ADR046-audit-003; Provider agent / observability-otel owner | +| Work item ID | `ADR046-telem-023` | +| Dependency/owner | ADR046-telem-022 + ADR046-telem-014; Provider agent / observability-otel owner | | Current source | Main commit source row below: `packages/d2b-gateway-runtime/src/provider_agent.rs` at `a1cc0b2d` plus `tests/provider_agent_v2.rs`. | -| Reuse action | Adapt provider-agent session wrapper and audit ring; replace gateway-runtime audit envelope with v3 `d2b-audit` records. | +| Reuse action | adapt | | Destination | `packages/d2b-provider-observability-otel/src/agent.rs` adapted; ComponentSessionDriver mock becomes shared Provider session fixture. | -| Detailed design | Session-bound ProviderAgentProcess construction, bounded audit snapshot, closed-set ProviderAgentError paths, and full ComponentSessionDriver mock for hermetic Provider tests. | +| Detailed design | Session-bound ProviderAgentProcess construction, bounded audit snapshot, closed-set ProviderAgentError paths, and full ComponentSessionDriver mock for hermetic Provider tests. Primary reuse disposition: `adapt`. Preserved source-plan detail: Adapt provider-agent session wrapper and audit ring; replace gateway-runtime audit envelope with v3 `d2b-audit` records.. | | Data migration | Full d2b 3.0 reset; no v2 provider-agent runtime state import | | Removal proof | ADR45 gateway-runtime AuditEnvelope/realm custody and launch path are excluded; v3 provider agent is supervised by Provider/system-core or the owning Provider supervisor. | | Main commit source | `packages/d2b-gateway-runtime/src/provider_agent.rs` at `a1cc0b2d`. Types: `ProviderAgentProcess` (`from_registry`, `from_registry_with`, `provider_type`, `service_names`, `audit_snapshot`), `ProviderAgentError` (closed-set: `SessionClosed`, `ProtocolViolation`, `InvalidAuditCapacity`, `RegistryNotAccepting`, `UnregisteredAdapter`, `RegistrationRejected`), `ProviderAgentAuditOutcome`, `ProviderAgentAuditEvent`. Tests: `tests/provider_agent_v2.rs` (full `ComponentSessionDriver` mock implementing all 20 async driver methods: `start_ttrpc`/`complete_ttrpc`/`cancel`/`send_ttrpc`/`receive_ttrpc`/`register_inbound_call`/`complete_inbound_call`/`remove_inbound_call`/`send_attachments`/`receive_attachments`/`open_named_stream`/`send_named_stream`/`receive_named_stream`/`grant_named_stream_credit`/`close_named_stream`/`reset_named_stream`/`drive_keepalive`/`receive_control`/`close`). | @@ -2166,16 +2166,16 @@ excluded or adapted. | Integration | `observability-otel` Provider embeds a `ProviderAgentProcess`; session connect/disconnect emits `SessionConnect` audit records via `d2b-audit`; `ProviderAgentAuditEvent` ring feeds `d2b_provider_reconcile_total` metric on session error. | | Validation | Adopt `provider_agent_v2.rs` mock harness unchanged as shared v3 Provider session fixture. Add v3 audit-bridge test: provider-agent session → `SessionConnect{transport_class="zone_link"}` record emitted. | -### ADR046-reuse-006 — Realm service v2 routing and remote-node routing state +### ADR046-telem-024 — Realm service v2 routing and remote-node routing state | Field | Value | | --- | --- | -| Work item ID | `ADR046-reuse-006` | -| Dependency/owner | ADR046-reuse-001 + ADR046-bus-001; bus routing owner | +| Work item ID | `ADR046-telem-024` | +| Dependency/owner | ADR046-telem-019 + ADR046-session-003; bus routing owner | | Current source | Main commit source row below: `packages/d2b-realm-router/` at `a1cc0b2d`. | -| Reuse action | Adapt realm service routing to Zone service routing; preserve credential-custody, bounded routing, and stable error-code behavior. | +| Reuse action | adapt | | Destination | `packages/d2b-bus/src/routing.rs` adapted from `service_v2.rs`; RemoteNodeErrorKind stable code pattern becomes v3 BusErrorKind. | -| Detailed design | Realm/Zone service ttrpc handler table, session authority constructors, host/gateway credential custody split, stable low-cardinality error codes, and binding/shortcut bounds. | +| Detailed design | Realm/Zone service ttrpc handler table, session authority constructors, host/gateway credential custody split, stable low-cardinality error codes, and binding/shortcut bounds. Primary reuse disposition: `adapt`. Preserved source-plan detail: Adapt realm service routing to Zone service routing; preserve credential-custody, bounded routing, and stable error-code behavior.. | | Data migration | Full d2b 3.0 reset; no v2 routing state import | | Removal proof | ADR45 RealmId and RealmService names are adapted to Zone/resource UID routing; no prior v3 bus routing owner to remove. | | Main commit source | `packages/d2b-realm-router/` at `a1cc0b2d`. Key files: `src/service_v2.rs` (`RealmServiceServer`, `RealmSessionAuthority`, `RealmAuditEvent`, `RealmServiceProcess`, `RealmServiceLimits`, `CredentialCustody`, `REALM_SERVICE_NAME = "d2b.realm.v2.RealmService"`, `DEFAULT_MAX_REALM_BINDINGS = 256`, `DEFAULT_MAX_SHORTCUTS = 256`, `DEFAULT_MAX_MUTATION_RECORDS = 1024`, `DEFAULT_AUDIT_CAPACITY = 1024`), `src/remote_node.rs` (`RemoteNodeAvailability`, `RemoteNodeErrorKind` with stable `code()` method, `RemoteNodeError`), `src/session_lifecycle.rs`, `src/target_resolver.rs`, `src/execution.rs`. Tests: `tests/realm_service_v2.rs` (`authority_keeps_remote_credentials_in_gateway_guests`, `authenticated_bootstrap_enrollment_route_and_shortcut_lifecycle`). | @@ -2185,16 +2185,16 @@ excluded or adapted. | Integration | `d2b-bus` route handler adapts `RealmServiceServer` dispatch table; `RemoteNodeErrorKind::code()` values feed `d2b_bus_route_total{outcome}` metric labels; `CredentialCustody::Host` maps to `purpose_class=local` in `d2b_session_connect_total`. | | Validation | Adopt `authority_keeps_remote_credentials_in_gateway_guests` test renamed to `authority_keeps_remote_credentials_in_zone_link_sessions`; adapt `RealmId` → Zone name; adopt `authenticated_bootstrap_enrollment_route_and_shortcut_lifecycle` renamed with zone terminology. | -### ADR046-reuse-007 — d2bd service routing, provider effects, and daemon session tests +### ADR046-telem-025 — d2bd service routing, provider effects, and daemon session tests | Field | Value | | --- | --- | -| Work item ID | `ADR046-reuse-007` | -| Dependency/owner | ADR046-reuse-004 + ADR046-reuse-006; core-controller routing owner | +| Work item ID | `ADR046-telem-025` | +| Dependency/owner | ADR046-telem-022 + ADR046-telem-024; core-controller routing owner | | Current source | Main commit source row below: `packages/d2bd/` routing/provider effects files and tests at `a1cc0b2d`. | -| Reuse action | Adapt route-gate and ProviderLifecycleDispatch patterns; exclude daemon-embedded effect composition and route through v3 bus/provider processes. | +| Reuse action | adapt | | Destination | `packages/d2b-bus/src/service_router.rs` and `packages/d2b-core-controller/src/provider_effects.rs`. | -| Detailed design | Closed-set service.package route gates, provider lifecycle effect-adapter composition, closed-set error mapping, fixed local policy invariants, and bootstrap/enrolled credential binding shape. | +| Detailed design | Closed-set service.package route gates, provider lifecycle effect-adapter composition, closed-set error mapping, fixed local policy invariants, and bootstrap/enrolled credential binding shape. Primary reuse disposition: `adapt`. Preserved source-plan detail: Adapt route-gate and ProviderLifecycleDispatch patterns; exclude daemon-embedded effect composition and route through v3 bus/provider processes.. | | Data migration | Full d2b 3.0 reset; no v2 daemon routing/provider-effects state import | | Removal proof | ADR45 daemon-embedded effect adapters and realm child supervisor assumptions are excluded; v3 bus/provider effects own the adapted behavior. | | Main commit source | `packages/d2bd/` at `a1cc0b2d`. Key files: `src/provider_registry.rs` (full `ProviderCompositionError` mapping, factory composition, `DaemonEffectAdapters` wiring), `src/provider_effects.rs` (`ProviderLifecycleDispatch`, `DaemonEffectAdapterError`, effect adapter structs per domain: `DeviceEffectAdapter`, `AudioEffectAdapter`, etc.), `src/control_services/provider.rs` (`owns`: `service.package == "d2b.provider.v2"` route gate), `src/control_services/daemon.rs` (daemon service route gate), `src/realm_child_supervisor.rs` (realm supervisor with pidfd adoption), `src/realm_stubs.rs`. Tests: `tests/daemon_service_v2.rs` (`every_generated_daemon_method_has_one_typed_adapter`, `local_daemon_policy_is_fixed_and_has_no_negotiation_or_fd_surface`, `public_daemon_handshake_rejects_daemon_or_guest_proxy_schema_mismatch`, `daemon_uses_shared_bootstrap_and_enrolled_guest_credential_bindings`, `shared_guest_session_credential_rejects_zero_authority`, `daemon_guest_paths_do_not_call_broker_signing_or_define_a_private_codec`), `tests/realm_child_supervisor_v2.rs`, `tests/realm_service_v2.rs`. | @@ -2204,16 +2204,16 @@ excluded or adapted. | Integration | Bus service router uses `service.package` closed-set matching from route-gate pattern; `ProviderLifecycleDispatch` feeds `d2b_provider_component_phase` metric. | | Validation | Port `local_daemon_policy_is_fixed_and_has_no_negotiation_or_fd_surface` invariant to v3 bus local policy test; port `every_generated_daemon_method_has_one_typed_adapter` to v3 bus method adapter completeness test. | -### ADR046-reuse-008 — ComponentSession v2 vector tests and contract conformance +### ADR046-telem-026 — ComponentSession v2 vector tests and contract conformance | Field | Value | | --- | --- | -| Work item ID | `ADR046-reuse-008` | -| Dependency/owner | ADR046-reuse-001; contract-tests owner | +| Work item ID | `ADR046-telem-026` | +| Dependency/owner | ADR046-telem-019; contract-tests owner | | Current source | Main commit source row below: ComponentSession vector/conformance tests at `a1cc0b2d`. | -| Reuse action | Copy the vector and conformance tests verbatim; add v3 constant gate for ComponentSession major/minor version. | +| Reuse action | adapt | | Destination | `packages/d2b-contract-tests/tests/component_session_v2_vectors.rs` and `tests/noise_vectors.rs` copied verbatim. | -| Detailed design | Pinned Noise KAT vectors, transcript/PSK mutation rejection, typed bootstrap admission fixture, and public-key corruption rejection remain the session wire security ground truth. | +| Detailed design | Pinned Noise KAT vectors, transcript/PSK mutation rejection, typed bootstrap admission fixture, and public-key corruption rejection remain the session wire security ground truth. Primary reuse disposition: `adapt`. Preserved source-plan detail: Copy the vector and conformance tests verbatim; add v3 constant gate for ComponentSession major/minor version.. | | Data migration | None — docs/tooling only; no runtime state | | Removal proof | None — net-new; no prior owner to remove | | Main commit source | `packages/d2b-contract-tests/tests/component_session_v2_vectors.rs` at `a1cc0b2d` (`committed_noise_vectors_verify_with_pinned_snow`, `declared_noise_public_key_corruption_is_rejected`, `bootstrap_fixture_mutations_execute_typed_admission_state`, `transcript_and_psk_mutations_are_rejected`). `packages/d2b-contracts/tests/component_session_v2.rs` at `a1cc0b2d`. `packages/d2b-session/tests/noise_vectors.rs` (pinned KAT vectors). | @@ -2223,16 +2223,16 @@ excluded or adapted. | Integration | These tests run in `make test-rust` / `cargo test -p d2b-contract-tests` and `cargo test -p d2b-session`. They are gating for any Noise library update. | | Validation | These tests are self-validating. Add one gate: assert `COMPONENT_SESSION_MAJOR = 2` and `COMPONENT_SESSION_MINOR = 0` constants are unchanged in v3 contract. | -### ADR046-reuse-009 — Session MetricsSink → d2b-telemetry bridge +### ADR046-telem-027 — Session MetricsSink → d2b-telemetry bridge | Field | Value | | --- | --- | -| Work item ID | `ADR046-reuse-009` | -| Dependency/owner | ADR046-reuse-001 + ADR046-telem-001 + ADR046-telem-003; telemetry/session owner | +| Work item ID | `ADR046-telem-027` | +| Dependency/owner | ADR046-telem-019 + ADR046-telem-001 + ADR046-telem-003; telemetry/session owner | | Current source | Main commit source row below: `packages/d2b-session/src/metrics.rs` and `MetricLabels` in `packages/d2b-contracts/src/v2_component_session.rs` at `a1cc0b2d`. | -| Reuse action | Adapt MetricsSink bridge to v3 OTEL instruments and rename ADR45 endpoint-purpose label values to v3 closed-set labels. | +| Reuse action | adapt | | Destination | `packages/d2b-telemetry/src/session_metrics_sink.rs`. | -| Detailed design | Map every MetricEvent variant to the `d2b_session_*` inventory; convert MetricLabels closed sets to allowed v3 label strings; keep NoopMetrics for hermetic tests. | +| Detailed design | Map every MetricEvent variant to the `d2b_session_*` inventory; convert MetricLabels closed sets to allowed v3 label strings; keep NoopMetrics for hermetic tests. Primary reuse disposition: `adapt`. Preserved source-plan detail: Adapt MetricsSink bridge to v3 OTEL instruments and rename ADR45 endpoint-purpose label values to v3 closed-set labels.. | | Data migration | Full d2b 3.0 reset; no v2 telemetry metric state import | | Removal proof | ADR45 EndpointPurpose metric label strings are replaced by v3 purpose names; no prior v3 MetricsSink bridge owner to remove. | | Main commit source | `packages/d2b-session/src/metrics.rs` at `a1cc0b2d` (`MetricEvent` enum: `ActiveSessions`, `Handshake`, `ConnectAttempt`, `ReconnectAttempt`, `Close`, `ControlCreditExhaustion`, `QueueDepth`, `QueueCapacity`, `SchedulingDelay`, `RejectedRecord`; `MetricsSink` trait; `NoopMetrics`). `packages/d2b-contracts/src/v2_component_session.rs::MetricLabels` (`transport: TransportClass`, `purpose: EndpointPurpose`, `channel_class: ChannelClass`, `noise: NoiseProfile`, `locality: Locality`). | @@ -2249,9 +2249,9 @@ excluded or adapted. | Work item ID | `ADR046-telem-009` | | Dependency/owner | W0/W1a; Nix integrator (adapts `nixos-modules/options-observability.nix` and `options-realms.nix`) | | Current source | `nixos-modules/options-observability.nix` (`d2b.observability.host.identityName`, `scrapeJournal`, `otlpIngest.enable`, `signoz.jwtSecretFile`, `signoz.rootPasswordFile`, `signoz.clickhousePasswordFile`, `retention.*`, `sampling.*` — all predecessor options); `nixos-modules/options-realms.nix` (submodule/option-type/assertion pattern); `nixos-modules/components/observability/{host,stack,guest}.nix` (Nix pipeline shape, ACL pattern, `identityName`/`vmName`); `nixos-modules/manifest.nix` (resource-bundle emission pattern); `packages/d2b-contract-tests/tests/policy_observability.rs::loki_native_otel_resource_attributes` (closed allowlist enforcement — adapt as bundle schema policy test) | -| Reuse action | Implement uniform `d2b.zones..resources. = { type; spec; }` option with schema-driven `spec.*` generated option types; adapt option submodule pattern from `options-realms.nix`; adapt pipeline shape from `{host,stack,guest}.nix`; emit canonical sorted ResourceSpec JSON from `resources-bundle.nix` | +| Reuse action | adapt | | Destination | `nixos-modules/resources.nix` (uniform `d2b.zones..resources` schema-aware option; `spec.*` option types generated from `ResourceTypeSchema` for each `type`); `nixos-modules/resources-bundle.nix` (ADR-only: sorted integrity-pinned bundle derivation) | -| Detailed design | (1) Implement `d2b.zones..resources = lib.mkOption { type = lib.types.attrsOf (schemaAwareResourceSubmodule); }` where the submodule, given `config.type`, loads the registered `ResourceTypeSchema` and generates `spec.*` option types from it. For `type = "Provider"`, `spec.config.*` options are generated from the signed Provider schema for the package identified by `spec.artifactId` (see ADR-046-provider-model-and-packaging). No second bespoke vocabulary; `spec` fields mirror the canonical JSON fields exactly. (2) `resources-bundle.nix` derivation: serialize each resource to canonical sorted JSON (keys alphabetically sorted at every level); sort resources by `(type, name)`; compute generation digest; emit `zone-resources-.json` as Nix store output. Publication handler sets `metadata.managedBy = "configuration"` and `metadata.configurationGeneration` on activation — these fields are NOT authored in Nix. (3) `status`, UID, generation, revision, and timestamps are absent from Nix authoring; core fills them. | +| Detailed design | (1) Implement `d2b.zones..resources = lib.mkOption { type = lib.types.attrsOf (schemaAwareResourceSubmodule); }` where the submodule, given `config.type`, loads the registered `ResourceTypeSchema` and generates `spec.*` option types from it. For `type = "Provider"`, `spec.config.*` options are generated from the signed Provider schema for the package identified by `spec.artifactId` (see ADR-046-provider-model-and-packaging). No second bespoke vocabulary; `spec` fields mirror the canonical JSON fields exactly. (2) `resources-bundle.nix` derivation: serialize each resource to canonical sorted JSON (keys alphabetically sorted at every level); sort resources by `(type, name)`; compute generation digest; emit `zone-resources-.json` as Nix store output. Publication handler sets `metadata.managedBy = "configuration"` and `metadata.configurationGeneration` on activation — these fields are NOT authored in Nix. (3) `status`, UID, generation, revision, and timestamps are absent from Nix authoring; core fills them. Primary reuse disposition: `adapt`. Preserved source-plan detail: Implement uniform `d2b.zones..resources. = { type; spec; }` option with schema-driven `spec.*` generated option types; adapt option submodule pattern from `options-realms.nix`; adapt pipeline shape from `{host,stack,guest}.nix`; emit canonical sorted ResourceSpec JSON from `resources-bundle.nix`. | | Integration | `d2b-core-controller` reads the Nix store path from the activated system closure; secrets never appear in the bundle | | Data migration | Current `d2b.observability.*` options are retained with compat warnings (same pattern as current `retention.*`/`sampling.*` compat options); the v3 `d2b.zones..resources.*` option is the authoritative surface | | Validation | `eval_rejects_unknown_type`, `eval_rejects_invalid_emitter_ring_size`, `eval_rejects_unknown_provider_settings`, `eval_rejects_inline_secret_in_settings`, `eval_rejects_unresolved_credential_ref`, `eval_rejects_duplicate_resource_name` nix-unit cases; `bundle_is_sorted_canonically`, `bundle_digest_is_deterministic`, `bundle_contains_no_secret_values`, `bundle_schema_validates_against_provider_schema` contract tests | @@ -2264,9 +2264,9 @@ excluded or adapted. | Work item ID | `ADR046-telem-010` | | Dependency/owner | ADR046-telem-009 + ADR046-telem-006 + ADR046-store-001; schema/validation owner | | Current source | `packages/d2b-contract-tests/tests/policy_observability.rs::startup_tracing_avoids_host_path_fields` (forbidden-field pattern enforcement — adapt as bundle forbidden-field gate); `packages/d2b-contract-tests/tests/policy_metrics.rs` (metric inventory policy test pattern); `packages/d2b-priv-broker/src/runtime.rs` (current runtime schema load/verify pattern); `packages/d2b-contracts/src/provider_registry_v2.rs::ProviderBindingV2` (non-exhaustive signed schema contract) | -| Reuse action | Adapt `startup_tracing_avoids_host_path_fields` forbidden-field pattern for bundle schema gate; adapt `ProviderBindingV2` non-exhaustive contract for Provider-specific settings schema fingerprint | +| Reuse action | adapt | | Destination | `nixos-modules/resources-bundle.nix` (build-time validation step 4 in the `resources-bundle` derivation); `packages/d2b-core-controller/src/configuration.rs` (runtime activation checks) | -| Detailed design | Build-time: (1) For each `Provider` resource, fetch the `resourceTypeSchema` output from the package; validate `settings` JSON against the JSON Schema; fail the build on schema mismatch or unknown fields. (2) Assert no resource spec field contains a bare secret/path/argv (forbidden-field pattern from `startup_tracing_avoids_host_path_fields`). Runtime: (3) Core-controller re-validates Provider package identity (per ADR-046-provider-model-and-packaging) against the installed package; resolves Credential refs; checks conflict/bounds; rejects with closed-enum `generation-rejected` reason on any failure; no partial activation. (4) Provider schema mismatch between the bundle's schema and the installed Provider's live schema → reject, emit `generation-rejected{reason="package-identity-mismatch"}`. | +| Detailed design | Build-time: (1) For each `Provider` resource, fetch the `resourceTypeSchema` output from the package; validate `settings` JSON against the JSON Schema; fail the build on schema mismatch or unknown fields. (2) Assert no resource spec field contains a bare secret/path/argv (forbidden-field pattern from `startup_tracing_avoids_host_path_fields`). Runtime: (3) Core-controller re-validates Provider package identity (per ADR-046-provider-model-and-packaging) against the installed package; resolves Credential refs; checks conflict/bounds; rejects with closed-enum `generation-rejected` reason on any failure; no partial activation. (4) Provider schema mismatch between the bundle's schema and the installed Provider's live schema → reject, emit `generation-rejected{reason="package-identity-mismatch"}`. Primary reuse disposition: `adapt`. Preserved source-plan detail: Adapt `startup_tracing_avoids_host_path_fields` forbidden-field pattern for bundle schema gate; adapt `ProviderBindingV2` non-exhaustive contract for Provider-specific settings schema fingerprint. | | Integration | Nix `resources-bundle.nix` derivation gate + core-controller `configuration.rs` activation path | | Data migration | Full d2b 3.0 reset; no v2 state/config import | | Validation | `bundle_schema_validates_against_provider_schema` bundle contract test; `generation_rejected_emits_audit_record` cleanup contract test with a `schema-validation-failed` reason; add a nix-unit case `eval_rejects_unknown_fields_against_signed_schema` that runs the bundle derivation with a schema mismatch and asserts build failure | @@ -2277,11 +2277,11 @@ excluded or adapted. | Field | Value | | --- | --- | | Work item ID | `ADR046-telem-011` | -| Dependency/owner | ADR046-telem-009 + ADR046-telem-010 + ADR046-audit-001 + ADR046-store-001; core-controller owner | +| Dependency/owner | ADR046-telem-009 + ADR046-telem-010 + ADR046-telem-012 + ADR046-store-001; core-controller owner | | Current source | `packages/d2bd/src/daemon_audit.rs` (hash-chain `ResourceMutation`-like append pattern — adapt for cleanup audit records); `packages/d2b-priv-broker/src/audit.rs` (`AuditWriteClass::{Standard,Unprivileged}` — cleanup audit records use `Standard` durability); `packages/d2b-realm-core/src/audit.rs::AuditChainLink::new` (hash-chain append for cleanup audit records); `nixos-modules/manifest.nix` (prior-generation retention pattern in the current bundle contract) | -| Reuse action | Adapt hash-chain append from `daemon_audit.rs` for `ResourceMutation{trigger="config-cleanup"}` records; adapt prior-generation retention window from `manifest.nix` pattern | +| Reuse action | adapt | | Destination | `packages/d2b-core-controller/src/{configuration.rs, ownership.rs}` | -| Detailed design | (1) On new generation activation, every stored `managedBy=configuration` resource absent from the new configured set receives `deletionRequestedAt` plus `deletion-pending`; controller/API-managed resources are untouched. (2) Activation returns after durable intent queueing and does not wait for cleanup. (3) The ownership handler drives child-before-parent finalizers. (4) When finalizers clear, one atomic store transaction writes the `Deleted` revision/change event and removes the row and indexes. After commit, the audit subsystem appends `ResourceMutation{event="deleted", trigger="config-cleanup"}` from that revision using a dedup/exactly-once recovery key; audit append is not part of the store transaction. (5) Stall detection sets `cleanup-stalled` without force-removing finalizers. (6) Prior generations use count retention, default 3 and range 1..16, with no TTL. (7) Core sets `managedBy`/`configurationGeneration` in persisted resources; input bundles omit both. | +| Detailed design | (1) On new generation activation, every stored `managedBy=configuration` resource absent from the new configured set receives `deletionRequestedAt` plus `deletion-pending`; controller/API-managed resources are untouched. (2) Activation returns after durable intent queueing and does not wait for cleanup. (3) The ownership handler drives child-before-parent finalizers. (4) When finalizers clear, one atomic store transaction writes the `Deleted` revision/change event and removes the row and indexes. After commit, the audit subsystem appends `ResourceMutation{event="deleted", trigger="config-cleanup"}` from that revision using a dedup/exactly-once recovery key; audit append is not part of the store transaction. (5) Stall detection sets `cleanup-stalled` without force-removing finalizers. (6) Prior generations use count retention, default 3 and range 1..16, with no TTL. (7) Core sets `managedBy`/`configurationGeneration` in persisted resources; input bundles omit both. Primary reuse disposition: `adapt`. Preserved source-plan detail: Adapt hash-chain append from `daemon_audit.rs` for `ResourceMutation{trigger="config-cleanup"}` records; adapt prior-generation retention window from `manifest.nix` pattern. | | Integration | `d2b-core-controller::configuration.rs` (generation activation); `d2b-core-controller::ownership.rs` (cleanup ordering and atomic final deletion); `d2b-audit` sink (cleanup audit records) | | Data migration | None — the `managedBy`/`configurationGeneration`/`deletionRequestedAt` fields are new; existing resources gain them on first v3 activation | | Validation | All tests in "Configuration-owned cleanup contract tests" subsection; additionally: `managedby_configuration_set_on_activated_resources`, `controller_created_resources_have_managedby_controller`, `absent_resource_receives_delete_on_new_generation`, `deletion_sets_deletionrequestedat_not_phase`, `final_deletion_is_atomic`, `cleanup_does_not_touch_controller_children`, `pending_cleanup_condition_set_on_zone`, `zone_is_degraded_not_failed_during_cleanup`, `pending_cleanup_cleared_after_deletion_completes`, `prior_generation_retained_count_based`, `rollback_schedules_delete_for_new_generation_resources`, `audit_segments_preserved_on_provider_delete`, `cleanup_stall_condition_set`, `generation_rejected_emits_audit_record` | diff --git a/docs/specs/ADR-046-terminology-and-identities.md b/docs/specs/ADR-046-terminology-and-identities.md index 558838df3..313426701 100644 --- a/docs/specs/ADR-046-terminology-and-identities.md +++ b/docs/specs/ADR-046-terminology-and-identities.md @@ -277,9 +277,9 @@ cgroup/scope, and provider-specific process identity. | --- | --- | | Dependency/owner | W0 shared contract root; `d2b-contracts` | | Current source | `packages/d2b-realm-core/src/ids.rs`, `realm.rs`, `target.rs`, `workload.rs` | -| Reuse action | extract and adapt | +| Reuse action | adapt | | Destination | `packages/d2b-contracts/src/v3/identity.rs`, `packages/d2b-contracts/src/v3/resource_ref.rs` | -| Detailed design | Add ZoneId, ResourceTypeName, ResourceName, ResourceUid, ResourceRef, generation/revision newtypes, exact parsing/serde/Debug/redaction, and golden vectors | +| Detailed design | Add ZoneId, ResourceTypeName, ResourceName, ResourceUid, ResourceRef, generation/revision newtypes, exact parsing/serde/Debug/redaction, and golden vectors Primary reuse disposition: `adapt`. Preserved source-plan detail: extract and adapt. | | Integration | Resource API/store/controllers/SDK/Nix import only these canonical types | | Data migration | Destructive d2b 3.0 reset; no RealmRef parser compatibility | | Validation | Rust property/vector tests; pure-Nix vector parity; malformed/collision/UID-recreate tests | diff --git a/docs/specs/ADR-046-validation-and-delivery.md b/docs/specs/ADR-046-validation-and-delivery.md index 164132e9a..d1308d191 100644 --- a/docs/specs/ADR-046-validation-and-delivery.md +++ b/docs/specs/ADR-046-validation-and-delivery.md @@ -40,15 +40,20 @@ No `ADR046-W*` implementation wave in §3 may open until **all** of the following are true, per the parent ADR's "Review and acceptance" contract and `docs/specs/README.md`: -1. Every file in the `docs/specs/ADR-046-*` manifest — the 24 top-level specs, - the 27 `docs/specs/providers/ADR-046-provider-*` dossiers, this spec, and - the four forthcoming closing specs (`ADR-046-security-hardening`, - `ADR-046-streamline`, `ADR-046-reset-and-cutover`, - `ADR-046-feasibility-proofs`) — is `Status: Accepted`. +1. Every file in the `docs/specs/ADR-046-*` manifest — the exact 28 top-level + specs and 27 `docs/specs/providers/ADR-046-provider-*` dossiers — is + `Status: Accepted`. The five closing specs are + `ADR-046-feasibility-and-spikes`, `ADR-046-reset-and-cutover`, + `ADR-046-security-and-threat-model`, `ADR-046-streamline`, and this spec. 2. `ADR-046-decision-register` has zero rows under "Open decisions." 3. `docs/specs/ADR-046-spec-set.json` and `docs/specs/ADR-046-work-items.json` - exist, validate against their generator, and enumerate every spec above - with matching content digests (§8). + exist and enumerate every spec above with matching content digests (§8). + Every declared Markdown work-item heading is in exact bijection with the + work-item manifest, its owner/path/prefix and mandatory fields match, and + its `reuseAction` is one closed scalar. The future generator and its + fail-closed policy tests are `ADR046-delivery-004` and + `ADR046-delivery-009`; no checked-in generator exists in this Proposed + documentation candidate. 4. The ADR/spec PR has both required human review gates from the parent ADR: approval before the immutable final panel snapshot, and approval after unanimous panel signoff (this is the spec-authoring panel, distinct from @@ -90,7 +95,7 @@ below is the latest-safe placement, not the earliest-possible one. | `ADR046-W4` | `ADR-046-components-processes-and-sandbox` ‖ `ADR-046-core-controllers` ‖ `ADR-046-resources-network` ‖ `ADR-046-resources-credential` ‖ `ADR-046-provider-state` (five parallel specs) | `packages/d2b-process/`, `d2b-provider-supervisor/` (process effect ports); `packages/d2b-core-controller/`; `packages/d2b-provider-network-local/` schema half; `packages/d2b-provider-credential-*/` schema half; Volume `stateSchema`/`persistenceClass`/`sensitivityClass` extension | | `ADR046-W5` | `ADR-046-resources-zone-control` ‖ `ADR-046-resources-host-guest-process-user` ‖ `ADR-046-resources-volume` ‖ `ADR-046-resources-device` ‖ `ADR-046-telemetry-audit-and-support` ‖ `ADR-046-cli-and-operations` ‖ `ADR-046-nix-configuration` (seven parallel specs) | `packages/d2b-provider-system-{core,systemd,minijail}/`; `packages/d2b-provider-volume-{local,virtiofs}/` schema half; `packages/d2b-provider-device-*/` schema half; `packages/d2b-telemetry/`, `d2b-audit/`; `packages/d2b/` CLI; `nixos-modules/resources-*.nix` | | `ADR046-W6` | All 27 `ADR-046-provider-*` dossiers, grouped into five file-disjoint provider families (§3.3) | One `packages/d2b-provider--/` per Provider (27 crates) | -| `ADR046-W7` | `ADR-046-streamline` (final; also requires `ADR-046-security-hardening`, `ADR-046-reset-and-cutover`, `ADR-046-feasibility-proofs` closed) | Cross-cutting friction fixes, reset/cutover mechanics, and the release gate (§15) | +| `ADR046-W7` | `ADR-046-feasibility-and-spikes` ‖ `ADR-046-reset-and-cutover` ‖ `ADR-046-security-and-threat-model` ‖ `ADR-046-streamline` ‖ `ADR-046-validation-and-delivery` | Cross-cutting friction fixes, reset/cutover mechanics, feasibility closure, security closure, and the release gate (§15) | Waves are numbered `ADR046-W0`…`ADR046-W7` — an ADR-046-scoped namespace, distinct from this repository's general per-plan `Wn` commit-tag convention @@ -421,7 +426,7 @@ parallelism (it does, until resolved). are generated indexes (per `docs/specs/README.md`) binding exact member files, versions, statuses, dependency edges, content digests, and implementation work items. They are generated once the initial member set - exists (already true — all 24 top-level specs and 27 dossiers exist at + exists (already true — all 28 top-level specs and 27 dossiers exist at this baseline) and regenerated as the last commit of every wave in §3.2. A wave's exit criteria (§4) include this regeneration; `make test-drift` (extended per work item `ADR046-delivery-004`, §17) fails if the committed @@ -506,7 +511,7 @@ manifest/fixture, per that file's closed-set rule. | Tier0 | Syntax + shellcheck for every new/changed shell/doc surface introduced by ADR 0046 tooling (§17) | `make check-tier0` | existing target, no change | | Layer-1 lint | `cargo fmt`/`clippy` for every new `d2b-*` crate in §3.2/§3.3 | `make test-lint` | existing target, extended by new crate membership | | Layer-1 rust | `cargo test --workspace` across every new crate, including the three broker feature passes where a new crate touches `d2b-priv-broker` (none does, per D077 — no Provider process imports the broker) | `make test-rust` | existing target | -| Layer-1 proofs | Any new `proofs/` crate for redb/session invariants (only if a wave's feasibility spike needs a separate proof crate; see `ADR-046-feasibility-proofs`) | `make test-proofs` | existing target | +| Layer-1 proofs | Any new `proofs/` crate for redb/session invariants (only if a wave's feasibility spike needs a separate proof crate; see `ADR-046-feasibility-and-spikes`) | `make test-proofs` | existing target | | Layer-1 flake | `eval-*` checks extended with Zone/resource examples once `ADR046-W5`'s `nix-configuration` lands; `examples/minimal` gains a `d2b.zones.dev.resources.*` block | `make test-flake` | existing target, new fixture | | Layer-1 drift | Schema/Nix-option/spec-set drift gates from §8 | `make test-drift` | existing target, extended rows | | Layer-1 policy | Workspace-policy, provider-crate-layout, and telemetry/audit-redaction policy lints (§10.4, §10.9) | `make test-policy` | existing target, extended rows | @@ -842,7 +847,7 @@ with the production encrypted named-stream implementation. | --- | --- | --- | | Hardware | Real GPU/YubiKey/hardware-TPM passthrough for `device-gpu`, `device-security-key`, `device-tpm` | `make test-hardware`, manual, on a host with the devices | | Live-host | Destructive/stateful checks against a real deployed Zone (store adoption, restart/power-loss, USBIP guestd lifecycle equivalents) | `D2B_LIVE=1 bash tests/integration/live/.sh`, manual, never CI | -| Cloud | `runtime-azure-container-apps`, `runtime-azure-virtual-machine`, `transport-azure-relay`, `credential-managed-identity`, `credential-entra` against real Azure resources | manual tier, gated by `ADR-046-feasibility-proofs`; never run in CI or as a required wave-exit lane — recorded as external evidence only | +| Cloud | `runtime-azure-container-apps`, `runtime-azure-virtual-machine`, `transport-azure-relay`, `credential-managed-identity`, `credential-entra` against real Azure resources | manual tier, gated by `ADR-046-feasibility-and-spikes`; never run in CI or as a required wave-exit lane — recorded as external evidence only | ### 10.12 Restart/power-loss @@ -1207,10 +1212,10 @@ executed as part of landing this spec. `ADR046-W7` ("streamline & cutover") does not close, and d2b 3.0 does not release, until all of: -1. `ADR-046-streamline`, `ADR-046-security-hardening`, - `ADR-046-reset-and-cutover`, and `ADR-046-feasibility-proofs` are - `Accepted` and their own work items' `Validation` evidence is imported - per §12.2. +1. `ADR-046-streamline`, `ADR-046-security-and-threat-model`, + `ADR-046-reset-and-cutover`, `ADR-046-feasibility-and-spikes`, and this + validation spec are `Accepted` and their own work items' `Validation` + evidence is imported per §12.2. 2. Every `DELETE`/`REPLACE` row in `ADR-046-current-code-migration-map` has satisfied its removal-proof test (§9) on the `ADR046-W7` candidate snapshot — this is the destructive-cutover gate; d2b 3.0 does not ship @@ -1249,7 +1254,7 @@ binaries. | Required delta | Candidate-snapshot immutability, ten-role panel bound to one fixed model/provider and run exactly once per wave (not per round), `xtask delivery` subcommands, `xtask heavy-gate` semaphore, attest/seal/eligibility/history-proof tooling, the exact `ADR046-W0`–`ADR046-W7` wave graph and its file-overlap/shared-prep contracts | | Reuse path | Copy/adapt the sibling-lineage `xtask delivery`/`xtask heavy-gate` implementations named in §11/§12; extend (never replace) the existing Layer-1/Layer-2 taxonomy and Makefile targets; extend the existing ten-role panel table unchanged | | Replacement/deletion | Nothing in this repository's current validation/delivery tooling is removed by this spec; `ADR046-delivery-00x` work items (§17) are additive tooling built alongside, not instead of, the existing `Makefile`/panel-review process, until `ADR046-W7` explicitly retires any tooling the migration map marks `DELETE`/`REPLACE` | -| Feasibility proof | The sibling-lineage candidate-snapshot/panel/seal process is already a proven, currently-running process (see the concurrently active sibling panel-writing agents observed during authoring of this spec); `ADR-046-feasibility-proofs` additionally proves the ADR-0046-specific redb/reconciliation/session/package/state numeric targets cited in §10.4 | +| Feasibility proof | The sibling-lineage candidate-snapshot/panel/seal contract supplies the reuse design named in §11/§12; `ADR-046-feasibility-and-spikes` owns the ADR-0046-specific redb/reconciliation/session/package/state numeric proofs cited in §10.4 | | Future owner | Work items in §17 | ## 17. Implementation work items @@ -1262,9 +1267,9 @@ binaries. | Dependency/owner | `ADR046-W0`; delivery-tooling integrator | | Current source | none in this repository; `Makefile` heavy-lane targets do not yet exist | | Reuse source | sibling-lineage `cargo xtask heavy-gate` implementation (per D001/D041 unrestricted-reuse policy) | -| Reuse action | copy-unchanged, then adapt paths/crate names to this repository's `packages/xtask` layout | +| Reuse action | adapt | | Destination | `packages/xtask/src/heavy_gate.rs`; `Makefile` targets `heavy-check`, `heavy-test-integration`, `heavy-test-host-integration`, `heavy-test-hardware`, `heavy-cargo-test`, `heavy-flake-check` | -| Detailed design | Two-slot per-UID OFD-locked semaphore, 250 ms nonblocking retry up to 30 minutes, fail-closed on unsupported locking, duplicated locked-FD handoff to child, wrapper-owned group-signal/reap, as specified in §11 | +| Detailed design | Two-slot per-UID OFD-locked semaphore, 250 ms nonblocking retry up to 30 minutes, fail-closed on unsupported locking, duplicated locked-FD handoff to child, wrapper-owned group-signal/reap, as specified in §11 Primary reuse disposition: `adapt`. Preserved source-plan detail: copy-unchanged, then adapt paths/crate names to this repository's `packages/xtask` layout. | | Integration | Every heavy lane in §10.4/§10.10/§10.11 routes through this one binary; no wave adds a second lock mechanism | | Data migration | None — net-new tooling | | Validation | Unit tests for slot acquisition/timeout/fail-closed paths; integration test spawning two concurrent heavy-gate invocations and asserting the second blocks until the first releases | @@ -1278,9 +1283,9 @@ binaries. | Dependency/owner | `ADR046-W0`; delivery-tooling integrator | | Current source | none in this repository | | Reuse source | sibling-lineage `cargo xtask delivery wave snapshot` implementation | -| Reuse action | copy-unchanged, then adapt | +| Reuse action | adapt | | Destination | `packages/xtask/src/delivery/snapshot.rs` | -| Detailed design | Binds base/head OIDs, dependency graph, repository set into `candidate_id`/`content_id`/`snapshot_sha256` per §12.1 | +| Detailed design | Binds base/head OIDs, dependency graph, repository set into `candidate_id`/`content_id`/`snapshot_sha256` per §12.1 Primary reuse disposition: `adapt`. Preserved source-plan detail: copy-unchanged, then adapt. | | Integration | Called by the integrator immediately after PR opening (§13.1), before any validator/panel lane starts | | Data migration | None — full d2b 3.0 reset; no prior state to migrate | | Validation | Unit tests asserting identical inputs produce identical digests and any single-byte content change produces a different `content_id` | @@ -1294,9 +1299,9 @@ binaries. | Dependency/owner | `ADR046-delivery-002`; delivery-tooling integrator | | Current source | none in this repository | | Reuse source | sibling-lineage `cargo xtask delivery wave validate-import` implementation | -| Reuse action | copy-unchanged, then adapt | +| Reuse action | adapt | | Destination | `packages/xtask/src/delivery/validate_import.rs`; external candidate-ID-addressed evidence directory (never under Git) | -| Detailed design | Imports CI/local/host validator command/result evidence, keyed by `candidate_id`, per §12.2 | +| Detailed design | Imports CI/local/host validator command/result evidence, keyed by `candidate_id`, per §12.2 Primary reuse disposition: `adapt`. Preserved source-plan detail: copy-unchanged, then adapt. | | Integration | Consumed by `wave seal` (§ADR046-delivery-005) as one of the seal's required inputs | | Data migration | None — full d2b 3.0 reset; no prior state to migrate | | Validation | Test asserting evidence for a stale `candidate_id` is rejected; test asserting raw command output never lands in a tracked file | @@ -1310,9 +1315,9 @@ binaries. | Dependency/owner | `ADR046-delivery-002`; spec-set integrator | | Current source | `docs/specs/README.md`'s described-but-not-yet-generated `ADR-046-spec-set.json`/`ADR-046-work-items.json` contract | | Reuse source | none required — this generator is specific to the `docs/specs/ADR-046-*` manifest shape | -| Reuse action | adapt (new generator, following the existing `xtask gen-schemas`/`gen-nix-options` pattern already used for other generated artifacts) | +| Reuse action | adapt | | Destination | `packages/xtask/src/gen_spec_set.rs`; `docs/specs/ADR-046-spec-set.json`, `docs/specs/ADR-046-work-items.json` | -| Detailed design | Enumerates every `docs/specs/ADR-046-*.md` and `docs/specs/providers/ADR-046-provider-*.md` file, its metadata table, content digest, and every `### ADR046--` work item, per §8 | +| Detailed design | Enumerates every `docs/specs/ADR-046-*.md` and `docs/specs/providers/ADR-046-provider-*.md` file, its metadata table, registered globally unique `workItemPrefix`, content digest, and every `### ADR046--` work item, per §8. Primary reuse disposition: `adapt`. Preserved source-plan detail: adapt (new generator, following the existing `xtask gen-schemas`/`gen-nix-options` pattern already used for other generated artifacts). | | Integration | `make test-drift` gains a row running this generator and `git diff --exit-code`; every wave's exit criteria (§4) require it committed as the wave's last commit | | Data migration | None — full d2b 3.0 reset; no prior state to migrate | | Validation | Golden-fixture test against a small synthetic spec directory; drift test against the real `docs/specs/` tree | @@ -1326,9 +1331,9 @@ binaries. | Dependency/owner | `ADR046-delivery-002`, `ADR046-delivery-003`; panel-tooling integrator | | Current source | none in this repository; this repository's existing `AGENTS.md` panel-review process is host-local script tooling (`/etc/nixos/scripts/panel-review.{md,sh}`), not a candidate-bound `xtask` subcommand | | Reuse source | sibling-lineage `cargo xtask delivery wave panel-request`/`panel-attest` implementation | -| Reuse action | copy-unchanged, then adapt to bind the fixed `gemini-3.1-pro-preview`/`github-copilot` model/provider pair and this repository's existing ten-role roster (§12.3) | +| Reuse action | adapt | | Destination | `packages/xtask/src/delivery/panel.rs` | -| Detailed design | `panel-request` writes the candidate-bound request naming the exact ten roles and required model; `panel-attest` validates a directory of exactly ten strict 13-field records, rejecting wrong model/candidate binding, duplicate provider/run provenance, or inconsistent `signoff`/`recommendations`, per §12.3 | +| Detailed design | `panel-request` writes the candidate-bound request naming the exact ten roles and required model; `panel-attest` validates a directory of exactly ten strict 13-field records, rejecting wrong model/candidate binding, duplicate provider/run provenance, or inconsistent `signoff`/`recommendations`, per §12.3 Primary reuse disposition: `adapt`. Preserved source-plan detail: copy-unchanged, then adapt to bind the fixed `gemini-3.1-pro-preview`/`github-copilot` model/provider pair and this repository's existing ten-role roster (§12.3). | | Integration | Every wave's exit criteria (§4) require ten unanimous attested records before `wave seal` | | Data migration | None — full d2b 3.0 reset; no prior state to migrate | | Validation | Unit tests for every rejection class (wrong model, missing role, duplicate run_id, `signoff:true` with non-empty `recommendations`); integration test with ten synthetic valid records passing | @@ -1342,9 +1347,9 @@ binaries. | Dependency/owner | `ADR046-delivery-002`, `ADR046-delivery-004`, `ADR046-delivery-005`; delivery-tooling integrator | | Current source | none in this repository | | Reuse source | sibling-lineage `cargo xtask delivery wave seal`, `merge-eligibility`, and history/byte-identity proof implementation | -| Reuse action | copy-unchanged, then adapt | +| Reuse action | adapt | | Destination | `packages/xtask/src/delivery/{seal,eligibility,history_proof}.rs` | -| Detailed design | `seal` requires all ten panel records unanimous and bound to the same candidate/content/snapshot digests plus every validator lane passing; `merge-eligibility` checks each stacked PR's current base/head against the sealed OIDs or a passing history-proof; `history_proof` verifies byte-identical integrated content/generated artifacts/dependency diff/repository set across a rebase, per §12.4/§12.6 | +| Detailed design | `seal` requires all ten panel records unanimous and bound to the same candidate/content/snapshot digests plus every validator lane passing; `merge-eligibility` checks each stacked PR's current base/head against the sealed OIDs or a passing history-proof; `history_proof` verifies byte-identical integrated content/generated artifacts/dependency diff/repository set across a rebase, per §12.4/§12.6 Primary reuse disposition: `adapt`. Preserved source-plan detail: copy-unchanged, then adapt. | | Integration | `make check` gains no new required step for ordinary contributors; this tooling is invoked only by the wave integrator per §4/§13 | | Data migration | None — full d2b 3.0 reset; no prior state to migrate | | Validation | Unit tests for seal rejection on any missing/mismatched record; integration test proving a history-only rebase with identical content passes `history_proof` and reuses panel evidence, while any content change fails it | @@ -1374,10 +1379,26 @@ binaries. | Dependency/owner | `ADR046-streamline-001`, `ADR046-W0`; delivery-tooling integrator | | Current source | none in this repository; launch order and parallelism were previously derived only from this spec's §3/§6 prose | | Reuse source | `ADR-046-spec-set.json`, `ADR-046-work-items.json`, and §3.1–§3.4/§3.5 of this spec; no new framework | -| Reuse action | net-new (D095 artifact contract) | +| Reuse action | adapt | | Destination | `docs/specs/ADR-046-implementation-graph.json`, `docs/specs/ADR-046-implementation-graph.md` (generated by `ADR046-streamline-001`'s `xtask implementation-graph`); the artifact contract, generation, validation, and ready-wave query are owned by §3.5 of this spec | -| Detailed design | Owns the D095 implementation-graph contract: `artifactKind`/`schemaVersion`/`adr`/`status`; one node per member spec and per work item mapped exactly once to a `W0`–`W7` wave and a file-disjoint `parallelGroup`, with `owner`/`destinations`/`entryContracts`/`prerequisites`/`blockers`/`exitGate`/`topologicalRank`; typed `spec-depends-on`/`shared-contract`/`work-item-depends-on`/`implements-spec`/`file-overlap-order` edges; the §3.5.1 ready-wave query; and the anti-serialization invariant that every ready file-disjoint group launches concurrently while a same-wave dependency is a prep barrier, not whole-wave serialization. The graph is a generated non-member artifact and does not change the 55-member `ADR-046-spec-set.json` count. | +| Detailed design | Owns the D095 implementation-graph contract: `artifactKind`/`schemaVersion`/`adr`/`status`; one node per member spec and per work item mapped exactly once to a `W0`–`W7` wave and a file-disjoint `parallelGroup`, with `owner`/`destinations`/`entryContracts`/`prerequisites`/`blockers`/`exitGate`/`topologicalRank`; typed `spec-depends-on`/`shared-contract`/`work-item-depends-on`/`implements-spec`/`file-overlap-order` edges; the §3.5.1 ready-wave query; and the anti-serialization invariant that every ready file-disjoint group launches concurrently while a same-wave dependency is a prep barrier, not whole-wave serialization. The graph is a generated non-member artifact and does not change the 55-member `ADR-046-spec-set.json` count. Primary reuse disposition: `adapt`. Preserved source-plan detail: net-new (D095 artifact contract). | | Integration | Consumed by §4 wave entry/exit and §6 anti-serialization checks and by `ADR046-streamline-013`; a `tests/unit/gates/` drift gate regenerates and `git diff --exit-code`s the graph after any spec/work-item edit | | Data migration | None — docs/tooling only; no runtime state | | Validation | Every 55 spec node and every work item present exactly once; all edge endpoints resolve; graph acyclic; waves monotonic (dependencies earlier or explicit same-wave prep barrier); parallel groups claim no ordering absent a dependency/file-overlap edge; deterministic JSON with no timestamps/host paths; every Mermaid node ID valid; the ready-wave query returns the expected concurrently-launchable groups on a seeded fixture | | Removal proof | Not applicable | + +### ADR046-delivery-009 + +| Field | Value | +| --- | --- | +| Work item ID | `ADR046-delivery-009` | +| Dependency/owner | `ADR046-delivery-004`, `ADR046-delivery-008`; spec-set policy-test owner | +| Current source | The fail-closed completeness, identity, and closed-action contract in `docs/specs/README.md`; the Proposed tree has no checked-in generator or policy test | +| Reuse source | `ADR046-delivery-004` generator shape and the existing `d2b-contract-tests` fixture-driven policy-test pattern | +| Reuse action | adapt | +| Destination | `packages/xtask/src/gen_spec_set.rs`; `packages/d2b-contract-tests/tests/policy_adr046_work_items.rs`; generated spec-set, work-item, and implementation-graph drift checks | +| Detailed design | Parse every normative member's declared work-item headings and tables. Require an exact Markdown/manifest bijection; exact `specId`, `specPath`, and registered prefix; globally unique prefixes and IDs; three-digit nonzero ordinals; every mandatory field exactly once and nonempty; one closed scalar `reuseAction`; and `reuseSource: null` for `create`. Reject dropped, extra, malformed, duplicate, ambiguous, or unconsumed items before writing any artifact. Validate all dependency endpoints, DAG acyclicity, wave monotonicity, and single-wave parallel groups before atomically publishing all generated files. | +| Integration | `make test-policy` runs negative fixtures; `make test-drift` regenerates all ADR 0046 artifacts and requires a clean diff; `ADR046-delivery-008` consumes only a manifest that passed this policy | +| Data migration | None — documentation/build-policy contract only | +| Validation | Fixtures fail for a dropped heading, extra manifest row, duplicate ID/prefix, wrong owner/path/prefix, two-digit/zero ordinal, missing/duplicate mandatory field, free-form/compound action, `create` with a reuse source, dangling dependency, cyclic DAG, backward-wave dependency, and cross-wave parallel group; the exact 55-spec real tree passes with every item once | +| Removal proof | Not applicable; the policy remains the permanent generated-artifact closure gate | diff --git a/docs/specs/ADR-046-work-items.json b/docs/specs/ADR-046-work-items.json index d36518874..8bf73cc07 100644 --- a/docs/specs/ADR-046-work-items.json +++ b/docs/specs/ADR-046-work-items.json @@ -1,8078 +1,8153 @@ { - "artifactKind": "d2b-adr-work-items", - "schemaVersion": 1, - "adr": "0046", - "status": "Proposed", - "items": [ - { - "workItemId": "ADR046-aca-001", - "specId": "ADR-046-provider-runtime-azure-container-apps", - "specPath": "docs/specs/providers/ADR-046-provider-runtime-azure-container-apps.md", - "dependencyOwner": "ADR046-provider-001; runtime-aca owner", - "currentSource": "`packages/d2b-provider-aca/src/lib.rs`: `AcaWorkloadProvider`, 2841 lines production-reachable; `packages/d2b-provider-runtime-azure-container-apps/src/provider.rs`: `AzureContainerAppsRuntimeProvider`, 2796 lines (test-only at v3 baseline)", - "reuseSource": null, - "reuseAction": "REPLACE (old) + ADAPT (main types/traits)", - "destination": "`packages/d2b-provider-runtime-azure-container-apps/src/controller.rs`", - "detailedDesign": "Async `Guest` reconcile loop: `describe` → `validateSpec` → `plan` → `reconcile` → `observe` → `finalize`. Adoption before first `RuntimeEnsure`. Operation ledger persisted to Volume inside gateway Guest. Credential lease acquire/revoke per call. Noise KK enrollment via ZoneLink (from gateway Guest to managed ACA sandbox). `providerPhase` and `guestIdentityDigest` in `status.provider.details`; no raw endpoint/path in any status field. **ProviderDeployment creates both static Processes; ACA controller never instantiates its own Processes and never writes Provider status directly. All Processes run inside the gateway Guest. No Host Process, no Host Credential, no Host Azure HTTP socket. Long-running cloud ops return `progressing`/`requeue-at` immediately; never block watch loop.**", - "integration": "Zone ResourceClient → ProviderDeployment → Process launch inside gateway Guest → d2b-bus → deployment service", - "dataMigration": "Full d2b 3.0 reset; no v2 provider state compatibility", - "validation": "Controller conformance suite; adoption/ambiguity tests; deadline/cancellation matrix; redaction coverage; **gateway Guest placement validation: assert no Process has `executionRef: Host/*`**; Process spec field schema tests (`spec.template`, canonical `sandbox`/`budget`/`networkUsage`/`endpoints`/`readiness`/`restartPolicy` fields, `mounts` with `required: true`, `providerRef: Provider/system-minijail`); ProviderDeployment creates both Processes (controller never self-spawns); no raw endpoint/path in Guest status", - "removalProof": "`packages/d2b-provider-aca/` removed only after conformance suite green" - }, - { - "workItemId": "ADR046-aca-002", - "specId": "ADR-046-provider-runtime-azure-container-apps", - "specPath": "docs/specs/providers/ADR-046-provider-runtime-azure-container-apps.md", - "dependencyOwner": "ADR046-aca-001; deployment service owner", - "currentSource": "`packages/d2b-gateway-runtime/src/provider_agent.rs` (main)", - "reuseSource": null, - "reuseAction": "COPY/ADAPT (partial); exclude `aca_workload.rs`", - "destination": "`packages/d2b-provider-runtime-azure-container-apps/src/deployment_service.rs`", - "detailedDesign": "`ProviderAgentProcess`-shaped binary; bounded dispatch (64 in-flight); bounded audit ring (1024 capacity); shutdown within 5 s; serves `d2b.aca.v3.deployment` service schema including `GuestHealth` (health probing folded in from former health worker). All ACA API calls go through the injected `AcaControl` port — no ambient network call, no SDK default chain. Long-running ops return `progressing`/`requeue-at` to the caller; no blocking on Azure API completion.", - "integration": "ProviderDeployment spawns service; d2b-bus routes GuestProvision/Start/Stop/Destroy/Adopt/Inspect/Health methods", - "dataMigration": "None — full d2b 3.0 reset; no prior state to migrate", - "validation": "Service dispatch matrix; RBAC refusal tests; redaction tests; shutdown deadline tests", - "removalProof": "Old `GuestControlEndpointProvider` dispatch removed per ADR046-aca-001" - }, - { - "workItemId": "ADR046-aca-003", - "specId": "ADR-046-provider-runtime-azure-container-apps", - "specPath": "docs/specs/providers/ADR-046-provider-runtime-azure-container-apps.md", - "dependencyOwner": "ADR046-aca-001; credential integration owner", - "currentSource": "`packages/d2b-provider-runtime-azure-container-apps/src/control.rs` (main): `AcaCredentialLeaseClient`, `AcaCredentialLease`, `AcaCredentialLeaseRequest`, `AcaCredentialPurpose`", - "reuseSource": null, - "reuseAction": "RETAIN+ADAPT", - "destination": "`packages/d2b-contracts/src/provider_effects/aca.rs` (shared `d2b-contracts` provider-effects module; no new crate; provider crate remains one package)", - "detailedDesign": "`AcaCredentialLeaseClient`, `AcaCredentialLease`, `AcaCredentialLeaseRequest`, and `AcaCredentialPurpose` live in the shared `d2b-contracts` provider-effects module. Adapt `CredentialLease` to v3 Credential resource opaque lease handle. `AcaCredentialPurpose` maps to `allowedOperations` check against `Credential.spec`. Lease expiry capped at call deadline. Cleanup job pattern retained verbatim.", - "integration": "Controller acquires lease per reconcile step via injected `AcaCredentialLeaseClient`; raw token delivered only via Noise KK E2E channel through `d2b.credential.v3.AcquireToken` method", - "dataMigration": "None — full d2b 3.0 reset; no prior state to migrate", - "validation": "Mock credential client tests; lease cleanup timeout tests; token non-exposure assertion", - "removalProof": "Old `CredentialProvider` trait deleted after `credential-managed-identity` Provider conformance" - }, - { - "workItemId": "ADR046-aca-004", - "specId": "ADR-046-provider-runtime-azure-container-apps", - "specPath": "docs/specs/providers/ADR-046-provider-runtime-azure-container-apps.md", - "dependencyOwner": "ADR046-session-001; ZoneLink transport owner", - "currentSource": "`packages/d2b-provider-relay/src/lib.rs`: `AzureRelayTransportProvider`; `packages/d2b-provider-aca/src/lib.rs`: `AcaRelayTransportConfig`", - "reuseSource": null, - "reuseAction": "REPLACE (both); ADAPT config fields to ZoneLink transport settings", - "destination": "ZoneLink resource `spec.transportSettings` (§15.4); `Provider/transport-azure-relay` dossier (separate)", - "detailedDesign": "`AcaRelayTransportConfig` fields mapped to `transportSettings.relayNamespace`, `.relayHybridConnection`, `.relayCredentialRef`. KK enrollment replaces ZoneLink transport.", - "integration": "ZoneLink controller resolves transport Provider; ACA controller establishes enrolled KK ComponentSession over relay transport after `GuestProvision`", - "dataMigration": "No relay session compatibility; re-enroll on first `RuntimeAdopt`", - "validation": "ZoneLink enrollment tests; relay unavailability tests; KK re-enrollment after sandbox restart", - "removalProof": "`packages/d2b-provider-relay/` removed after `transport-azure-relay` Provider conformance" - }, - { - "workItemId": "ADR046-aca-005", - "specId": "ADR-046-provider-runtime-azure-container-apps", - "specPath": "docs/specs/providers/ADR-046-provider-runtime-azure-container-apps.md", - "dependencyOwner": "ADR046-aca-001; state/migration owner", - "currentSource": "`packages/d2b-provider-runtime-azure-container-apps/src/types.rs` (main): `AcaRuntimeConfig`, `AcaSandboxProfile`, `AcaResourceBinding`, `AcaWorkloadQuery` — test-only at v3 baseline", - "reuseSource": null, - "reuseAction": "RETAIN+ADAPT", - "destination": "`packages/d2b-provider-runtime-azure-container-apps/src/types.rs`", - "detailedDesign": "Replace `RealmId`/`WorkloadId` with v3 `Zone`/`Guest` UID types. `AcaResourceBinding` keys the adoption query. The provider declares **no** Provider state Volume: bounded, non-secret sandbox binding/adoption metadata lives in `Guest.status` (latest bounded observed handle digests) and in-flight operation/requeue truth lives in the core Operation ledger (D087). Neither Process mounts a state Volume; there is no `sandbox-state`/`service-state` Volume, no `User/d2b-aca-controller`/`User/d2b-aca-deployment-service` state-layout principal, and no empty identity-only Volume. On restart the controller re-derives observed binding from `Guest.status`, the core Operation ledger, and an external `find_sandboxes` query, treating status as observation, never authority. Host never holds cloud binding, admission, PSK, or operation state.", - "integration": "No Provider state Volume is created before Processes start; the controller writes its bounded observed binding/adoption metadata to `Guest.status` on material change and reads in-flight operation state from the core Operation ledger adapter", - "dataMigration": "None — no state Volume at v3 `1.0`", - "validation": "Controller/service declare empty `stateNamespaces`; no `sandbox-state`/`service-state` Volume created; neither Process mounts a state Volume; `Guest.status` binding/adoption fields are bounded, non-secret, and carry no credential/endpoint/poll-URL bytes; restart re-derivation from status/core ledger/external `find_sandboxes` without a Volume; core Operation ledger adapter integration test", - "removalProof": "Old in-memory-only operation ledger removed after core Operation ledger adapter passes; `operation-ledger` stateNamespace absent from component descriptor" - }, - { - "workItemId": "ADR046-aca-006", - "specId": "ADR-046-provider-runtime-azure-container-apps", - "specPath": "docs/specs/providers/ADR-046-provider-runtime-azure-container-apps.md", - "dependencyOwner": "ADR046-aca-001; Nix/telemetry owner", - "currentSource": "`nixos-modules/options-realms-workloads.nix`: `kind = \"ProviderManaged\"` → ACA; `packages/d2b-provider-aca/src/lib.rs`: tracing fields", - "reuseSource": null, - "reuseAction": "REPLACE (Nix emitter) + ADAPT (metric/audit shapes)", - "destination": "`nixos-modules/` (generated Guest resource options); `packages/d2b-provider-runtime-azure-container-apps/src/{audit,metrics}.rs`", - "detailedDesign": "Eval-time assertions for ACA-specific invariants (§15.6). Closed OTEL label set (§13.4). Audit event schema (§13.3). Tracing target constant `d2b_provider_runtime_azure_container_apps::credential_lease_cleanup` retained.", - "integration": "Nix eval gate; `observability-otel` Provider OTEL pipeline", - "dataMigration": "None — full d2b 3.0 reset; no prior state to migrate", - "validation": "Label cardinality policy test; audit commit-before-complete test; Nix assertion eval tests", - "removalProof": "Old Nix `ProviderManaged` workload options retired after Guest resource Nix emitter parity" - }, - { - "workItemId": "ADR046-aca-007", - "specId": "ADR-046-provider-runtime-azure-container-apps", - "specPath": "docs/specs/providers/ADR-046-provider-runtime-azure-container-apps.md", - "dependencyOwner": "ADR046-aca-001; Nix/gateway wiring owner", - "currentSource": "n/a — new requirement (gateway Guest placement)", - "reuseSource": null, - "reuseAction": "NEW", - "destination": "`nixos-modules/` (gateway Guest declaration, Process template wiring, Credential scope assertion); eval-time validation module", - "detailedDesign": "Nix eval-time assertions for: (a) `gatewayExecutionRef` resolves to a `Guest` resource, not `Host/*`; (b) Credential `scope.executionRef` matches `gatewayExecutionRef`; (c) all Process templates emitted for this Provider have `executionRef` equal to `gatewayExecutionRef`. No `User` resource or `users.users.*` declarations required — component principals are framework-assigned and not OS accounts. Gateway Guest NixOS closure includes only the two ACA component binaries (§15.6). Assertion error messages name the offending resource and the required `gatewayExecutionRef`.", - "integration": "Nix eval gate; `d2b.zones.*.resources` validation pass; consumer flake usage example", - "dataMigration": "None — full d2b 3.0 reset; no prior state to migrate", - "validation": "Nix eval assertion tests (wrong `executionRef` → assertion fires; correct setup → passes); §15.7 assertion coverage tests", - "removalProof": "n/a — ongoing eval-time constraint" - }, - { - "workItemId": "ADR046-activation-001", - "specId": "ADR-046-provider-activation-nixos", - "specPath": "docs/specs/providers/ADR-046-provider-activation-nixos.md", - "dependencyOwner": "Provider/activation-nixos runner owner; reused helper owner in d2b-host", - "currentSource": "packages/d2b-host/src/bin/d2b-activation-helper.rs", - "reuseSource": null, - "reuseAction": "adapt in place", - "destination": "packages/d2b-host/src/bin/d2b-activation-helper.rs", - "detailedDesign": "Replace the helper CLI flag interface with structured JSON input and JSON output, accept bounded systemArtifactId and activationMode, resolve store path internally, emit bounded outcome code, write no resource metadata, and preserve the no-bash-fallback invariant.", - "integration": "activation-runner invokes the helper through the pre-opened activation portal and system-manager effect resources, then reports structured outcome to activation-nixos status.", - "dataMigration": "Full d2b 3.0 reset; no v2 activation-helper invocation compatibility", - "validation": "Unit tests for JSON protocol, bounded outcomes, no resource metadata writes, and no Command::new bash fallback.", - "removalProof": "Legacy flag-based helper invocation is removed from activation-nixos paths once runner JSON protocol tests pass." - }, - { - "workItemId": "ADR046-activation-002", - "specId": "ADR-046-provider-activation-nixos", - "specPath": "docs/specs/providers/ADR-046-provider-activation-nixos.md", - "dependencyOwner": "ADR-046-resource-object-model and ADR-046-core-controllers; d2b-contracts activation-nixos owner", - "currentSource": "None — net-new v3 ResourceType; no pre-ADR45 baseline equivalent", - "reuseSource": null, - "reuseAction": "net-new schema and DTOs", - "destination": "docs/reference/schemas/v3/activation-nixos.d2bus.org.NixosGeneration.json and packages/d2b-contracts/src/activation_nixos.rs", - "detailedDesign": "Define JSON schema and Rust DTOs for activation-nixos.d2bus.org.NixosGeneration, with systemArtifactId as the only artifact reference, no store path fields, activationDetail as a closed status.resource enum absent from spec, and phase constrained to the common framework enum values.", - "integration": "Resource API, resource store, Nix compiler, activation-nixos controller, and CLI projections consume the schema and DTOs.", - "dataMigration": "Full d2b 3.0 reset; no v2 generation resource import", - "validation": "Schema golden vectors, serde unknown-field rejection, phase enum tests, activationDetail-not-in-spec test, and no-store-path-in-spec-or-status test.", - "removalProof": "None — net-new; no prior owner to remove" - }, - { - "workItemId": "ADR046-activation-003", - "specId": "ADR-046-provider-activation-nixos", - "specPath": "docs/specs/providers/ADR-046-provider-activation-nixos.md", - "dependencyOwner": "ADR046-activation-002; activation-nixos controller owner", - "currentSource": "Current top-level activation behavior in packages/d2b/src/lib.rs and hardlink-farm/store ownership split described in this dossier", - "reuseSource": null, - "reuseAction": "replace top-level imperative activation flow with resource controller logic", - "destination": "packages/d2b-provider-activation-nixos/src/controller/", - "detailedDesign": "Implement the reconcile loop for activation-nixos.d2bus.org.NixosGeneration: validate executionRef, systemArtifactId, and priorGenerationRef; dispatch one activation-runner EphemeralProcess with canonical startRoot=true shape; observe runner status; mark superseded generations; prune by retainedGenerations through the finalizer protocol; never perform direct store-path operations, nix-collect-garbage, explicit VolumeGcRequest, raw argv composition, or store path writes to resources.", - "integration": "Controller watches NixosGeneration resources through Zone resource API, creates activation-runner EphemeralProcesses, releases ownership references for Provider/volume-local, and writes bounded status.", - "dataMigration": "Full d2b 3.0 reset; adopt mode records an existing active generation but does not import v2 controller state", - "validation": "Controller tests for retention, finalizer sequence, no TTL retention, no direct store ops, no store path in status, deleted event-only removal, and runner shape.", - "removalProof": "Direct hardlink-farm and garbage-collection calls from activation-nixos reachable paths are absent after controller and runner tests pass." - }, - { - "workItemId": "ADR046-activation-004", - "specId": "ADR-046-provider-activation-nixos", - "specPath": "docs/specs/providers/ADR-046-provider-activation-nixos.md", - "dependencyOwner": "ADR046-activation-003; activation-runner owner", - "currentSource": "packages/d2b-host/src/bin/d2b-activation-helper.rs for helper invocation; runner process is net-new", - "reuseSource": null, - "reuseAction": "net-new runner invoking adapted helper", - "destination": "packages/d2b-provider-activation-nixos/src/runner/", - "detailedDesign": "Implement target-local activation-runner worker that executes on NixosGeneration.spec.executionRef for Host and Guest targets using the same contract, reads private artifact-catalog.json through the integrity channel, resolves systemArtifactId to a store path in memory only, invokes d2b-activation-helper through structured JSON, executes target-local switch-to-configuration through typed helper dispatch with no raw exec or SSH, emits structured outcome JSON, and never outputs store paths.", - "integration": "Controller-created EphemeralProcess runs under Provider/system-minijail on the target execution context and returns outcome through the activation portal for status update.", - "dataMigration": "Full d2b 3.0 reset; no v2 runner state import", - "validation": "Runner tests for artifact lookup, JSON helper invocation, Host and Guest target parity, no raw argv, no SSH, no store path in output, and terminal nonzero handling.", - "removalProof": "No old SSH or raw command fallback path exists in activation-runner after tests assert typed helper dispatch only." - }, - { - "workItemId": "ADR046-activation-005", - "specId": "ADR-046-provider-activation-nixos", - "specPath": "docs/specs/providers/ADR-046-provider-activation-nixos.md", - "dependencyOwner": "ADR-046-cli-and-operations; activation CLI owner", - "currentSource": "packages/d2b/src/lib.rs top-level cmd_switch, cmd_boot, cmd_test, cmd_rollback, cmd_build, cmd_generations, cmd_gc, and cmd_migrate", - "reuseSource": null, - "reuseAction": "replace with grouped CLI projection", - "destination": "packages/d2b/src/activation.rs", - "detailedDesign": "Implement d2b activation build, switch, boot, test, rollback, adopt, generations, gc, and migrate subcommands, projecting ADR-046 CLI and Operations behavior and ensuring no store path, digest, or artifact catalog field appears in JSON or human output.", - "integration": "d2b CLI dispatcher calls resource API and activation-nixos controller by creating or listing NixosGeneration resources; legacy top-level verbs are removed by ADR046-activation-007 after integration tests pass.", - "dataMigration": "Full d2b 3.0 reset; CLI command surface changes with no runtime state import", - "validation": "CLI integration tests for subcommand parsing, authorization, resource creation/listing, rollback priorGenerationRef, gc ownership release, and output redaction.", - "removalProof": "Legacy top-level verb removal is gated on ADR046-activation-007 after the d2b activation integration matrix passes." - }, - { - "workItemId": "ADR046-activation-006", - "specId": "ADR-046-provider-activation-nixos", - "specPath": "docs/specs/providers/ADR-046-provider-activation-nixos.md", - "dependencyOwner": "ADR-046-nix-configuration; activation-nixos Nix owner", - "currentSource": "Current VM Nix configuration emits activation inputs implicitly; this item creates the explicit Provider and NixosGeneration resource emitter", - "reuseSource": null, - "reuseAction": "net-new resource emitter adapted from existing Nix activation inputs", - "destination": "nixos-modules/providers/activation-nixos.nix", - "detailedDesign": "Emit Provider spec and activation-nixos.d2bus.org.NixosGeneration resources per target, flow retainedGenerations only through Provider.spec.config.retainedGenerations, reference systems by systemArtifactId only, omit store paths from all emitted resources, and avoid dedicated state-layout User or ComponentPrincipal because ProviderStateSet is empty.", - "integration": "Nix compiler emits Provider and NixosGeneration resources plus private artifact catalog entries consumed by core configuration publication and the activation-nixos controller.", - "dataMigration": "Full d2b 3.0 reset; existing d2b.vms activation settings are reauthored as Zone resources rather than imported", - "validation": "Nix eval tests for Provider config, NixosGeneration shape, retainedGenerations source, no systemStorePath in bundle, no state Volume or state-layout principal, and artifact ID resolution.", - "removalProof": "Old implicit activation Nix paths are unused by activation-nixos once resource emitter parity tests pass." - }, - { - "workItemId": "ADR046-activation-007", - "specId": "ADR-046-provider-activation-nixos", - "specPath": "docs/specs/providers/ADR-046-provider-activation-nixos.md", - "dependencyOwner": "ADR046-activation-005; d2b CLI dispatcher owner", - "currentSource": "packages/d2b/src/lib.rs cmd_switch, cmd_boot, cmd_test, cmd_rollback, cmd_build, cmd_generations, cmd_gc, cmd_migrate and dispatcher registrations", - "reuseSource": null, - "reuseAction": "delete legacy top-level commands", - "destination": "packages/d2b/src/lib.rs", - "detailedDesign": "Remove the legacy top-level activation command functions and their dispatcher registrations after the grouped d2b activation namespace passes integration tests.", - "integration": "CLI dispatcher routes only d2b activation subcommands for activation operations; documentation and tests use the new namespace.", - "dataMigration": "Full d2b 3.0 reset; no command alias compatibility window", - "validation": "CLI integration matrix for d2b activation passes; grep or contract test confirms old cmd_* symbols and dispatcher registrations are absent.", - "removalProof": "cmd_switch, cmd_boot, cmd_test, cmd_rollback, cmd_build, cmd_generations, cmd_gc, cmd_migrate and their registrations are deleted from packages/d2b/src/lib.rs." - }, - { - "workItemId": "ADR046-api-001", - "specId": "ADR-046-resource-api-and-authorization", - "specPath": "docs/specs/ADR-046-resource-api-and-authorization.md", - "dependencyOwner": "W0; resource API integrator", - "currentSource": "`packages/d2b-contracts/src/public_wire.rs`, `broker_wire.rs`; `d2b-daemon-access/src/lib.rs`; `d2b-realm-router/src/lib.rs`", - "reuseSource": null, - "reuseAction": "extract and adapt", - "destination": "`packages/d2b-contracts/proto/d2b-resource-v3.proto`, `packages/d2b-resource-api/src/service.rs`, `client.rs`", - "detailedDesign": "Async methods, contexts, preconditions, limits, errors, status/finalizer separation, batch API", - "integration": "d2b-bus exact service → Zone auth → redb actor", - "dataMigration": "None; v3 clean break", - "validation": "Protocol vectors; malformed/oversize/conflict/status-owner tests", - "removalProof": "Old command/resource-equivalent paths removed only per integration wave" - }, - { - "workItemId": "ADR046-api-002", - "specId": "ADR-046-resource-api-and-authorization", - "specPath": "docs/specs/ADR-046-resource-api-and-authorization.md", - "dependencyOwner": "ADR046-api-001; authorization integrator", - "currentSource": "`d2bd` public admission; `d2b-daemon-access` policy evidence; `d2b-realm-core/src/access.rs`, `audit.rs`", - "reuseSource": null, - "reuseAction": "adapt", - "destination": "`packages/d2b-resource-api/src/authz.rs`, `packages/d2b-core-controller/src/rbac.rs`", - "detailedDesign": "Role/RoleBinding schemas/evaluator/cache/revision invalidation, ComponentSession subject mapping, parent Zone access", - "integration": "Every resource/runtime method invokes one native evaluator before structural checks", - "dataMigration": "Generate initial Roles/Bindings from Nix v3 config", - "validation": "Decision matrix/property tests; revocation/cache/outage/parent-child tests", - "removalProof": "Legacy auth remains until every v3 route is covered" - }, - { - "workItemId": "ADR046-audio-001", - "specId": "ADR-046-provider-audio-pipewire", - "specPath": "docs/specs/providers/ADR-046-provider-audio-pipewire.md", - "dependencyOwner": "No prerequisites; `d2b-provider-audio-pipewire` crate", - "currentSource": "`packages/d2b-core/src/audio_policy.rs` (all symbols); `packages/d2b-core/tests/audio_policy.rs`", - "reuseSource": "Same baseline paths", - "reuseAction": "`copy-unchanged` (no daemon imports; pure DTO library)", - "destination": "`packages/d2b-provider-audio-pipewire/src/audio_policy.rs`; re-exported from crate root", - "detailedDesign": "`LevelPercent`, `AudioGrant`, `AudioPolicyState`, `parse_audio_state`, `to_v2_bytes`, `AudioPolicyError` copy unchanged. `AudioPolicyState` is the canonical in-memory representation of `AudioBinding.spec.grants`. `parse_audio_state`/`to_v2_bytes` are used only once during first-activation migration from a prior v1/v2 on-disk file; there is no ongoing state file in v3.", - "integration": "First-activation migration uses `parse_audio_state` and writes grants only into a per-Guest AudioBinding that also names the explicitly configured same-Zone owner AudioService.", - "dataMigration": "Parse v1/v2 once; require exactly one configured owner Service; write grants plus `serviceRef`; fail closed on missing/ambiguous Service; remove prior file only after successful commit.", - "validation": "`tests/audio_policy.rs`: all existing tests from `d2b-core/tests/audio_policy.rs` plus AudioBinding spec serialization tests", - "removalProof": "`d2b-core/src/audio_policy.rs` deleted when no `d2bd` caller references it; confirmed by `cargo check --no-default-features`." - }, - { - "workItemId": "ADR046-audio-002", - "specId": "ADR-046-provider-audio-pipewire", - "specPath": "docs/specs/providers/ADR-046-provider-audio-pipewire.md", - "dependencyOwner": "Depends on `ADR046-audio-001`; Process Provider template schema", - "currentSource": "`packages/d2b-host/src/audio_argv.rs` (all symbols + tests); `tests/golden/runner-shape/audio-argv-minimal.txt`", - "reuseSource": "Same baseline paths", - "reuseAction": "`adapt` — argv builder retained; becomes a signed component-template projection, not a live Process spec field", - "destination": "`packages/d2b-provider-audio-pipewire/src/argv.rs` (component template renderer)", - "detailedDesign": "`generate_audio_argv` remains the canonical argv builder for the `vhost-user-sound-worker` component template. The resulting argv/env/executableRef are sealed into the LaunchTicket. The per-Guest binary copy path enforcement remains via the LaunchTicket verifier. The live Process resource spec contains no argv or executableRef. The `--socket` argument is removed; the vhost-user service identity is `Endpoint/corp-vm-audio-vhost-user`, while the backing locator is resolved into the LaunchTicket under authorization.", - "integration": "The component template for `vhost-user-sound-worker` embeds the output of `generate_audio_argv`; the Process Provider resolves arg0 from the artifact catalog.", - "dataMigration": "No runtime state migration; argv template output is regenerated from the v3 component template, and live Process specs never store argv.", - "validation": "`tests/argv.rs`: rejection matrix (Nix store path, symlink, cross-guest copy, empty name); no-socket-in-argv assertion; no-argv-in-process-spec assertion", - "removalProof": "`d2b-host/src/audio_argv.rs` deleted after `d2bd` has no callers; confirmed by `cargo check -p d2b-host`." - }, - { - "workItemId": "ADR046-audio-004", - "specId": "ADR-046-provider-audio-pipewire", - "specPath": "docs/specs/providers/ADR-046-provider-audio-pipewire.md", - "dependencyOwner": "Depends on `ADR046-audio-001`; ComponentSession service contract; libpipewire 1.x API", - "currentSource": "`packages/d2bd/src/audio_host_controller.rs` `PipeWireHostController` enforcement logic; `QemuAudioController`", - "reuseSource": "Same baseline paths", - "reuseAction": "`adapt` — enforcement logic becomes a libpipewire API implementation behind the `SetGrant`/`SetLevel` ComponentSession service", - "destination": "`packages/d2b-provider-audio-pipewire/src/mediator/enforcement.rs`", - "detailedDesign": "Owner AudioService only: `SetGrant(channel, value)` maps `\"off\"` to `pw_node_set_param(SPA_PARAM_Props, mute=true, target.object=-1)` on the worker's node and `\"on\"` to `mute=false`; `SetLevel` maps to a bounded volume. `captureAlias` resolves privately through the registry. A projection AudioService routes the operation to the remote owner over its import stream and is denied any local mediator/PipeWire open. `FakeAudioMediator` is the hermetic test double. No state file, wpctl, EphemeralProcess, or node ID in any external surface.", - "integration": "AudioBinding controller calls its resolved AudioService. Owner Service dispatches locally to AudioMediator; projection Service dispatches over the encrypted import route to the remote owner.", - "dataMigration": "No state migration; mediator applies current AudioBinding grants and levels from resource state during reconcile, replacing host-controller direct writes.", - "validation": "`tests/mediator.rs` and `tests/enforcement.rs`: owner-Service SetGrant/SetLevel round-trip; projection routing with fake streams; projection-PipeWire-open denial; no-node-id-in-bus-message; ProviderSessionUnavailable; captureAlias registry resolution", - "removalProof": "`d2bd/src/audio_host_controller.rs` retired after `d2bd` audio dispatch path is replaced." - }, - { - "workItemId": "ADR046-audio-005", - "specId": "ADR-046-provider-audio-pipewire", - "specPath": "docs/specs/providers/ADR-046-provider-audio-pipewire.md", - "dependencyOwner": "ADR046-provider-004 common audio Service/Binding base; Core resource-api foundation; `d2b-provider-audio-pipewire` crate", - "currentSource": "None (ADR-only); structured after `d2b-contracts/src/public_wire.rs` audio types", - "reuseSource": "`public_wire.rs` `AudioChannel`, `AudioEnforcementPosture`, `AudioErrorKind`, `AudioProviderKind`, `AudioSetApplied`; `AudioProviderKind` is removal evidence, not a base-status field", - "reuseAction": "`adapt` provider-neutral closed enums; remove `AudioProviderKind` from base status; `ADR-only` for schema/admission", - "destination": "`packages/d2b-provider-audio-pipewire/src/{resource_type,admission,provider_extension}.rs` (strict implementation extensions and binding only; common base lives under ADR046-provider-004)", - "detailedDesign": "Bind the shared D098 `audio.d2bus.org.AudioService` and `audio.d2bus.org.AudioBinding` base schema versions/fingerprints from ADR046-provider-004 and define only strict audio-pipewire Provider extensions/admission. AudioService validates immutable `serviceRole`, same-Zone local Endpoint refs, owner-only D097 AuthorityDescriptor, projection-only `ownerRef: ResourceImport/`, Core-only projection creation, and projection `spec.provider` rejection. AudioBinding validates Guest ownership, required immutable same-Zone `serviceRef`, grants/levels/users, and forbids authority/export/projection semantics. Semantic authority/import/attachment observations stay under `status.resource`; implementation observations stay under `status.provider`. PipeWire fields are rejected from base spec/status. Register no provider-qualified or AudioState identifier and no serde/schema alias.", - "integration": "`Provider/audio-pipewire` signs and publishes implementation support for both neutral qualified ResourceTypeSchemas and strict provider-envelope schemas. Core import controller may create/delete only projection AudioService; ordinary resource API admission handles owner Services and AudioBindings.", - "dataMigration": "Full d2b 3.0 reset; owner Services and per-Guest AudioBindings are authored as new v3 resources; projection Services are core-generated from ResourceImport.", - "validation": "`tests/resource_type.rs`: consume the ADR046-provider-004 common fixtures/fingerprints; canonical minimal base without `spec.provider`; neutral qualified-name registration; both schema/status round-trips; clean-break rejection of provider-qualified names, every AudioState spelling, and all aliases; fake alternate-provider base conformance; strict base/provider unknown-field matrices; projection `spec.provider` rejection; D088 `status.resource`/`status.provider` placement; PipeWire fields only in strict provider envelopes/config; Service role/AuthorityDescriptor/ownerRef/Endpoint-locality rules; Core-only projection admission; AudioBinding required same-Zone serviceRef and Guest owner; immutable refs; out-of-range levels/users; explicit tests that AudioBinding cannot be exported or projected", - "removalProof": "None — both ResourceTypes are net-new" - }, - { - "workItemId": "ADR046-audio-006", - "specId": "ADR-046-provider-audio-pipewire", - "specPath": "docs/specs/providers/ADR-046-provider-audio-pipewire.md", - "dependencyOwner": "Depends on `ADR046-audio-001` through `ADR046-audio-005`; core ResourceImport controller; system Process Provider; AudioMediator (`ADR046-audio-007`); GuestAudioAgent (`ADR046-audio-011`); no Provider state Volume under D087", - "currentSource": "`packages/d2bd/src/audio_dispatch.rs` lines 250–end (dispatch ordering reference)", - "reuseSource": "None directly; reconcile flow is new async controller", - "reuseAction": "`adapt` — dispatch logic is the reference for step ordering only", - "destination": "`packages/d2b-provider-audio-pipewire/src/controller/audio_service.rs`; `src/controller/audio_binding.rs`", - "detailedDesign": "One controller binary registers deterministic handlers for the two neutral ResourceTypes, constrained to immutable `spec.providerRef: Provider/audio-pipewire`. Service handler watches AudioService, its ResourceImport owner and local Endpoints; owner semantics claim/revalidate D097 and local mediator, projection semantics bind only encrypted import streams and deny PipeWire. It cannot create/delete projection Service. Binding handler watches AudioBinding, same-Zone serviceRef, Guest/User, owned Process, and private Endpoints; creates the vhost-user worker, GuestAudioAgents, and private Endpoints, then calls the resolved Service and guest agents. A Service event enqueues serviceRef-indexed Bindings. A resource selecting another conforming Provider is ignored and cannot be status/finalizer-mutated. Neither handler uses broker/pidfd/EphemeralProcess/Volume/User operations or direct filesystem access.", - "integration": "Registered with Zone core as a controller under `Provider/audio-pipewire`.", - "dataMigration": "v1/v2 audio policy file migration is handled by ADR046-audio-001 before reconcile; the controller keeps no Provider state Volume and imports no additional runtime state.", - "validation": "Fast hermetic `tests/audio_service_controller.rs`: neutral type/provider selection, foreign-provider ignore/deny, owner authority, projection ownerRef/import chain, core-only create/delete, projection no-PipeWire-open, revocation and D091 propagation. `tests/audio_binding_controller.rs`: neutral type/provider selection, required same-Zone serviceRef, owner/projection dispatch, child Process/private Endpoint state machine, grant changes, absence/failures/deletion. Conformance asserts no AudioBinding export/projection, no broker/pidfd/EphemeralProcess/Volume/User ops. ProviderDeployment integration remains fake-only and validates empty ProviderStateSet.", - "removalProof": "Supersedes `audio_dispatch.rs`; `d2bd` audio dispatch deleted after e2e parity test confirms" - }, - { - "workItemId": "ADR046-audio-007", - "specId": "ADR-046-provider-audio-pipewire", - "specPath": "docs/specs/providers/ADR-046-provider-audio-pipewire.md", - "dependencyOwner": "Depends on `ADR046-audio-004`; ComponentSession service (ADR-046-componentsession-and-bus); libpipewire 1.x", - "currentSource": "`packages/d2bd/src/audio_host_controller.rs` PipeWire session access patterns (reference only)", - "reuseSource": "None; new component", - "reuseAction": "`ADR-only`", - "destination": "`packages/d2b-provider-audio-pipewire/src/mediator/mod.rs`; `src/bin/audio_pipewire_mediator.rs`", - "detailedDesign": "Owner AudioService implementation only. Long-lived user-session Process maintains per-AudioBinding nodes under the single owner backing, receives the pre-opened local PipeWire portal FD, and exposes `SetGrant`/`SetLevel` through `Endpoint/audio-pipewire-authority`. Projection Services never start/call a local mediator and cannot receive its FD. No EphemeralProcess, wpctl, remote Ref, or node identity in external surfaces.", - "integration": "Second binary in the `d2b-provider-audio-pipewire` package. Registered as a user-session service under `Provider/audio-pipewire`.", - "dataMigration": "No persisted mediator state migration; the service rebuilds its PipeWire node map from the registry on start and consumes current AudioBinding through controller calls.", - "validation": "`tests/mediator.rs`: owner-Service FD handoff and calls; captureAlias; node-id sealing; session-unavailable; concurrent Guest isolation; teardown; projection Service cannot resolve mediator Endpoint or portal attachment", - "removalProof": "Supersedes `d2bd`'s `PipeWireHostController` direct session access; `d2bd` audio host controller deleted after e2e parity" - }, - { - "workItemId": "ADR046-audio-008", - "specId": "ADR-046-provider-audio-pipewire", - "specPath": "docs/specs/providers/ADR-046-provider-audio-pipewire.md", - "dependencyOwner": "Depends on `ADR046-audio-005`; Nix resource compilation framework; `ADR-046-nix-configuration`", - "currentSource": "`nixos-modules/components/audio/host.nix` and `guest.nix`", - "reuseSource": "Same", - "reuseAction": "`replace`", - "destination": "`nixos-modules/components/audio/v3-resource.nix`; `nixos-modules/components/audio/host-config.nix`; `nixos-modules/components/audio/guest-config.nix`", - "detailedDesign": "`v3-resource.nix` emits only `audio.d2bus.org.AudioService` and `audio.d2bus.org.AudioBinding`, selected by `Provider/audio-pipewire`, with required same-Zone serviceRef; ResourceExport/Import examples always name the neutral AudioService. Projection Services are never authored: core materializes them from ResourceImport. Eval rejects provider-qualified ResourceTypes, every AudioState spelling, aliases, PipeWire fields in neutral base spec/status, AudioBinding export/projection, Service role/ownerRef/authority mismatches, cross-Zone Endpoint/service refs, and duplicate owner authority. Existing captureAlias stays in Provider config; guestUsers/group injection, runtime-audio derivation, host stream rules, and guest stack remain.", - "integration": "`nixos-modules/default.nix` imports all three modules.", - "dataMigration": "Full d2b 3.0 reset; legacy Nix audio options emit/deprecate to one owner AudioService plus per-Guest AudioBindings that reference it; no projection Service is authored.", - "validation": "`tests/unit/nix/cases/audio-v3-resource.nix`: exact neutral type names; provider-qualified/AudioState/alias rejection; strict provider-field placement; owner Service and Binding round-trip; same-Zone serviceRef; export/import Service type; projection core-only ownerRef chain; AudioBinding export/projection rejection; authority uniqueness; Endpoint locality; plus existing grants/users/captureAlias/deprecation/no-wpctl/no-audioFrontend assertions", - "removalProof": "`host.nix` and `guest.nix` kept as compat shims until v3 module deployed on all Zones" - }, - { - "workItemId": "ADR046-audio-009", - "specId": "ADR-046-provider-audio-pipewire", - "specPath": "docs/specs/providers/ADR-046-provider-audio-pipewire.md", - "dependencyOwner": "Depends on `ADR046-audio-005`; `d2b-contract-tests` crate", - "currentSource": "`packages/d2b-contract-tests/tests/minijail_audio_usbip.rs` audio section", - "reuseSource": "Same", - "reuseAction": "`adapt`", - "destination": "`packages/d2b-provider-audio-pipewire/tests/minijail_contract.rs` (provider-local); retain cross-bundle source greps in `d2b-contract-tests`", - "detailedDesign": "Retain worker sandbox assertions and add role-sensitive attachment checks: an owner-Service worker may receive only the local AudioMediator attachment; a projection-Service worker receives only its same-Zone route Endpoint and can never receive a PipeWire FD. Binding-owned private Endpoint ownership/producerRef is explicit. All Service/Binding/Process/Endpoint serialized forms remain locator-free.", - "integration": "Provider-local contract tests run in `d2b-provider-audio-pipewire`; retained cross-bundle greps in `d2b-contract-tests` ensure bundle-wide invariants still hold.", - "dataMigration": "None — test migration only; no runtime state.", - "validation": "`cargo test -p d2b-provider-audio-pipewire -- minijail` must pass; existing cross-bundle tests must continue to pass", - "removalProof": "The superseded duplicate shell validator `tests/minijail-validator-audio.sh` is deleted only after the successor Rust gate (`minijail_audio_usbip.rs` cross-bundle + provider-local `minijail_contract.rs`) is green and its removal-proof check passes; the `seccomp_policy_ref == \"w1-audio\"` assertion migrates to `spec.sandbox.seccompClass == \"audio-pipewire-worker\"` before removal. Cross-bundle Rust tests are retained." - }, - { - "workItemId": "ADR046-audio-010", - "specId": "ADR-046-provider-audio-pipewire", - "specPath": "docs/specs/providers/ADR-046-provider-audio-pipewire.md", - "dependencyOwner": "Depends on `ADR046-audio-006`, `ADR046-audio-007`, `ADR046-audio-011`; `d2b-telemetry` lightweight emitter", - "currentSource": "`packages/d2bd/src/audio_dispatch.rs` audit call sites (redaction pattern reference)", - "reuseSource": "Same; adapt redaction pattern", - "reuseAction": "`adapt`", - "destination": "`packages/d2b-provider-audio-pipewire/src/telemetry.rs`", - "detailedDesign": "Emit closed-label Service and Binding metrics plus post-commit audit. Service events distinguish only `owner\\|projection` and closed outcomes; they omit authority keys, import/export keys, remote identity, stream/session ids, and endpoints. Enforcement metrics cover owner-local and projection-routed calls without exposing route identity. ProcessEffect audit remains Process Provider-owned.", - "integration": "Audio controller and mediator call telemetry/audit emitters after commit or enforcement; d2b-telemetry exporter and policy_observability consume the resulting records.", - "dataMigration": "No telemetry/audit data migration; v3 emits new closed-label OTEL/audit records after cutover and old audio_dispatch audit sites are removed.", - "validation": "`tests/audio_telemetry.rs`: Service/Binding event separation, redaction, post-commit ordering, label cardinality, forbidden authority/import/stream/path fields, no ProcessEffect duplication", - "removalProof": "`audio_dispatch.rs` audit call sites deleted after cutover" - }, - { - "workItemId": "ADR046-audio-011", - "specId": "ADR-046-provider-audio-pipewire", - "specPath": "docs/specs/providers/ADR-046-provider-audio-pipewire.md", - "dependencyOwner": "Depends on `ADR046-audio-004`; ComponentSession service contract; libpipewire 1.x; system-systemd Process Provider for guest domain", - "currentSource": "`packages/d2b-guestd/src/audio_set.rs` (guestd wpctl dispatch — reference only)", - "reuseSource": "`packages/d2bd/src/audio_host_controller.rs` libpipewire enforcement patterns (reference only)", - "reuseAction": "`ADR-only` (new component; supersedes guestd wpctl dispatch path)", - "destination": "`packages/d2b-provider-audio-pipewire/src/guest_agent/mod.rs`; `src/guest_agent/enforcement.rs`; `src/bin/audio_pipewire_guest_agent.rs`", - "detailedDesign": "Long-lived user-domain Process running in the Guest under the guest workload user's UID. One Process resource per entry in `AudioBinding.spec.guestUsers`; each named by opaque UID digest (`ag-`) and carrying label `audio-pipewire.d2bus.org/role: guest-audio-agent`. `userRef` is the corresponding `User/` Zone resource. Opens a PipeWire connection in the Guest's compositor session (same-UID, natural access). Exposes a typed `AudioSet` ComponentSession service through an owned `Endpoint/ag--audio-set` (vsock transport, Guest→Zone d2b-bus). `AudioSet(mic, speaker, speakerLevel, micGain)` applies changes via libpipewire API (`pw_node_set_param` with `SPA_PARAM_Props`, `pw_stream_set_control`) on the guest virtio-snd PipeWire node. No wpctl binary, no command path, no EphemeralProcess. Controller calls ALL active GuestAudioAgent instances in parallel for each grant change and aggregates failures. `FakeGuestAudioAgent` is a test double behind `#[cfg(test)]`.", - "integration": "Third binary in the `d2b-provider-audio-pipewire` package. Declared as GuestAudioAgent Process resources by the audio-binding-controller (one per guestUser; template: `guest-audio-agent`). System Process Provider (`Provider/system-systemd`) launches each inside the Guest under the respective guest workload user's UID.", - "dataMigration": "No guest runtime state migration; GuestAudioAgent reconnects to guest PipeWire and applies current AudioBinding grants and levels on reconcile, replacing guestd wpctl dispatch.", - "validation": "`tests/guest_agent.rs`: AudioSet service call → libpipewire apply; mute/route/level; session-unavailable path; reconnect state restore; no wpctl binary; no command path; N-agent creation (one per guestUser); parallel call and aggregated failure", - "removalProof": "`d2b-guestd` wpctl audio dispatch path deleted after all Guests have GuestAudioAgent deployed and e2e parity test passes" - }, - { - "workItemId": "ADR046-audio-012", - "specId": "ADR-046-provider-audio-pipewire", - "specPath": "docs/specs/providers/ADR-046-provider-audio-pipewire.md", - "dependencyOwner": "ADR046-zone-control-019, ADR046-zone-control-020; audio Provider owner", - "currentSource": "None — net-new ADR 0046 cross-Zone sharing (D096)", - "reuseSource": "audio authority/mediator service (this dossier); `packages/d2b-provider/src/share_adapter.rs` `ExportAdapter`/`ImportAdapter` traits", - "reuseAction": "net-new (implement the signed audio export/import adapter)", - "destination": "`packages/d2b-provider-audio-pipewire/src/share_adapter.rs`", - "detailedDesign": "Implement signed `Provider/audio-pipewire` adapters only for the provider-neutral exportedType/projectionType `audio.d2bus.org.AudioService` when `spec.providerRef` selects this Provider. Export adapter admits the owner Service and its local authority Endpoint. Core creates/deletes the projection AudioService with `ownerRef: ResourceImport/`, `providerRef`, and semantic base/import fields but no `spec.provider`; routing derives from the signed local descriptor and ResourceImport record. The semantic factory fingerprint binds factory metadata plus projection-protocol version, never Provider/adapter identity, which the signed descriptor authenticates separately. The import adapter reconciles its semantic route and never creates AudioBinding or opens PipeWire. Per-Guest AudioBindings are ordinary consumer resources with same-Zone serviceRef. No provider-qualified type alias, FD/path/socket/remote Ref crosses a Zone.", - "integration": "Core export/import controller (ADR046-zone-control-019); local projection lifecycle (ADR046-zone-control-020); ComponentSession bounded encrypted named streams", - "dataMigration": "None — full d2b 3.0 reset", - "validation": "Fast hermetic `tests/share_adapter.rs`: exact neutral AudioService type and Provider selection; reject provider-qualified aliases and AudioBinding export/projection; accept owner AudioService export; Core-only projection creation/deletion; exact ResourceImport -> projection AudioService ownerRef chain with no `spec.provider`; semantic factory fingerprint unchanged by Provider/adapter identity mutation while signed identity authentication remains exact; projection never opens PipeWire; reconnect/revocation/D091 propagation with fake streams. Only `integration/real_stream.rs` exercises a real encrypted named stream.", - "removalProof": "Not applicable (new surface)" - }, - { - "workItemId": "ADR046-audio-013", - "specId": "ADR-046-provider-audio-pipewire", - "specPath": "docs/specs/providers/ADR-046-provider-audio-pipewire.md", - "dependencyOwner": "Depends on `ADR046-audio-001`, `ADR046-audio-004`, `ADR046-zone-control-019`; audio Provider owner", - "currentSource": "`packages/d2bd/src/audio_host_controller.rs` (`HostAudioController` trait, `PipeWireHostController::{from_audio_node,find_audio_node}`, `QemuAudioController`, `FakeHostController`); `packages/d2b-core/src/audio_policy.rs` (`LevelPercent`, `AudioGrant`, `AudioPolicyState`)", - "reuseSource": "Same baseline controller/policy symbols", - "reuseAction": "`adapt` — the host controller becomes the single authority service; no daemon `Mutex`/state-file wrapping", - "destination": "`packages/d2b-provider-audio-pipewire/src/authority.rs` (speaker mixer + mic arbiter); `AuthorityDescriptor` on owner `AudioService`", - "detailedDesign": "Exactly one owner AudioService holds the real PipeWire connection and D097 AuthorityDescriptor. Projection Services and AudioBindings cannot carry it. Speaker mixing and mic exclusivity/approved multiplexing retain quota, consent, priority, and fair queue. Core rejects duplicate owner Services before open and adopts by ownerProof. No new ProcessRole, broker path, or state file.", - "integration": "Owner AudioService references `Endpoint/audio-pipewire-authority`; same-Zone AudioBindings and remote projection Services call it. Core authority index admits exactly one owner Service.", - "dataMigration": "None — full d2b 3.0 reset; grants are authoritative in `AudioBinding.spec` (no state file).", - "validation": "Fast hermetic `tests/authority.rs`: AuthorityDescriptor accepted only on owner AudioService; Binding/projection rejection; duplicate conflict; mix/quota; mic arbitration/fairness; ownerProof adoption; D091 drain/recycle with fake clock/FakeHostController", - "removalProof": "`audio_host_controller.rs` daemon-side controller deleted after the authority service reaches parity; confirmed by `cargo check`." - }, - { - "workItemId": "ADR046-audio-014", - "specId": "ADR-046-provider-audio-pipewire", - "specPath": "docs/specs/providers/ADR-046-provider-audio-pipewire.md", - "dependencyOwner": "Depends on `ADR046-audio-013`, `ADR046-zone-control-019`; audio Provider owner", - "currentSource": "`packages/d2b-realm-core/src/stream.rs` (`StreamKind::{AudioPlayback,AudioCapture}` → `Capability::{AudioPlayback,AudioCapture}`, `StreamAuthz`, `StreamChannel` split-direction); `packages/d2b-realm-core/src/mux.rs` (credit-based flow); `packages/d2bd/src/guest_control_bridge.rs` (`audio_set_authenticated`/`audio_status_authenticated`, `GuestAudioSetRequest`/`GuestAudioStatus`)", - "reuseSource": "Same baseline stream/mux/bridge symbols", - "reuseAction": "`adapt` — audio frames ride the existing ComponentSession named-stream credit machinery", - "destination": "`packages/d2b-provider-audio-pipewire/src/streams.rs`", - "detailedDesign": "Per-import audio frames flow only over bounded encrypted named streams: one stream with two `StreamChannel`s for playback/capture split direction and a single `StreamAuthz` (a consumer never opens two authz contexts to split direction), credit-based backpressure (a sender spends only receiver-granted credit), per-import session generation, cancel, and deadline. `StreamKind::AudioPlayback`/`AudioCapture` require `Capability::AudioPlayback`/`AudioCapture`. No PipeWire FD/socket crosses a Zone; intermediaries see ciphertext. Guest audio calls (`audio_set`/`audio_status`) are issued to all active guests concurrently and results aggregated. Volume/gain (`LevelPercent`) and node identity are redacted from audit/OTEL/logs.", - "integration": "Owner and projection AudioService adapters allocate per-import streams over ComponentSession; AudioBinding only consumes the same-Zone Service Ref; core routes encrypted records only.", - "dataMigration": "None — full d2b 3.0 reset", - "validation": "Fast hermetic `tests/streams.rs`: projection-Service ownerRef/import binding, split-direction single-authz stream, credits, generation isolation, cancel/deadline, concurrency, ciphertext-only intermediary, redaction. Only `integration/real_stream.rs` runs the slower real encrypted stream.", - "removalProof": "Not applicable (new surface)" - }, - { - "workItemId": "ADR046-audit-001", - "specId": "ADR-046-telemetry-audit-and-support", - "specPath": "docs/specs/ADR-046-telemetry-audit-and-support.md", - "dependencyOwner": "W0/W1a; audit crate owner", - "currentSource": "`packages/d2b-realm-core/src/audit.rs` (`AuditHash::parse`, `AuditChainLink::new`/`verify`, `AuditChainRecord{stream: AuditStreamKind, realm: RealmPath, node: NodeId}`, `AuditStreamKind::{Gateway,RemoteNode,Daemon}`, `AuditSinkHealth`, `AuditRetentionFloorStatus`); `packages/d2bd/src/daemon_audit.rs` (hash-chain append algorithm, `prev_hash`/`record_hash` SHA-256 pattern, daily segment files `daemon-events-YYYY-MM-DD.jsonl`, `DaemonEvent` additive contract); `packages/d2b-priv-broker/src/audit.rs` (`AuditWriteClass::{Privileged,Unprivileged}`, `AuditDropSummary`, `DEFAULT_AUDIT_WRITES_PER_SECOND = 4096`, O_APPEND CLOEXEC file open, `AuditDropWarningState`); `packages/d2b-gateway-runtime/src/audit_jsonl.rs` (`JsonlGatewayAudit`, `DEFAULT_GATEWAY_AUDIT_RETENTION_DAYS = 14`, `prune_old` rotation algorithm); `packages/d2b-priv-broker/src/ops/audit_op.rs` (`OpAuditRecord`, `SwtpmDirAudit`, `SwtpmDirResult`, `SwtpmMarkerResult`); `packages/d2b-realm-core/src/ids.rs` (`OperationId`, `CorrelationId`, `PrincipalId` — `PrincipalId` becomes `subject_digest`); `packages/d2b/tests/audit_contract.rs`; `packages/d2b-priv-broker/tests/broker_export_audit.rs`", - "reuseSource": null, - "reuseAction": "extract unchanged: `AuditHash`, `AuditChainLink` from `d2b-realm-core/src/audit.rs`; copy hash-chain append algorithm from `daemon_audit.rs`; copy `AuditWriteClass`/rate-limit/rotation/prune from broker `audit.rs`; adapt `JsonlGatewayAudit` segment writer; adapt `OpAuditRecord` to `BrokerEffect` record class", - "destination": "`packages/d2b-audit/src/{hash_chain.rs,segment.rs,rate_limit.rs,record_types.rs,sink.rs,export.rs}`", - "detailedDesign": "`d2b-audit` provides: typed record structs per class; canonical serialization with `zone` replacing `realm: RealmPath`; SHA-256 hash chain (extracted from `daemon_audit.rs`); segment writer (O_APPEND CLOEXEC, 64 MiB / UTC-midnight rotation); 30-day compaction (adapts `prune_old` from `JsonlGatewayAudit`); `AuditWriteClass::{Privileged,Standard,BestEffort}` (extends current `{Privileged,Unprivileged}`); rate-limit with privileged-never-dropped invariant; export iterator with inline hash-break reporting. `AuditStreamKind` re-versioned: `Daemon→Zone`, `Gateway→ZoneLink`, `RemoteNode→RemoteZone`. `AuditChainRecord` re-versioned: `{zone: String}` replaces `{realm: RealmPath, node: NodeId}`.", - "integration": "Zone runtime, core-controller, Process Providers, broker effect bridge → `d2b-audit` sink; `d2b zone audit export` → export iterator", - "dataMigration": "v3 bootstrap; existing daemon/broker JSONL files not migrated", - "validation": "`audit_record_hash_chain`, `audit_record_schema` (no `realm`/`node` fields), `audit_segment_rotation`, `audit_rate_limit_privileged_never_dropped`, `audit_unavailable_blocks_privileged`", - "removalProof": "`daemon_audit.rs`, broker `audit.rs`, `JsonlGatewayAudit` retired per-component after `d2b-audit` sink achieves parity" - }, - { - "workItemId": "ADR046-audit-002", - "specId": "ADR-046-telemetry-audit-and-support", - "specPath": "docs/specs/ADR-046-telemetry-audit-and-support.md", - "dependencyOwner": "ADR046-audit-001 + ADR046-store-001; store/authz owner", - "currentSource": "`packages/d2b-priv-broker/src/ops/audit_op.rs` (`OpAuditRecord` structural pattern — operation, peer_uid, decision, result fields); `packages/d2b-realm-core/src/audit.rs::AuditEnvelope{principal: PrincipalId, scope: AuthorizationScope, decision: AuthzDecision}` (principal → v3 `subject_digest`)", - "reuseSource": null, - "reuseAction": "adapt `OpAuditRecord` structural pattern for `ResourceMutation` / `RBACChange` record classes; adapt `PrincipalId` → `subject_digest` derivation", - "destination": "`packages/d2b-resource-store-redb/src/audit.rs`, `packages/d2b-core-controller/src/authz_audit.rs`", - "detailedDesign": "`ResourceMutation` records emitted by the store actor inside the write transaction before commit returns. The audit sink must durably fsync the audit record before returning the commit success (privileged durability class). `RBACChange` emitted by the authz handler in the same write transaction. `subject_digest` = SHA-256 of normalized canonical subject string from v3 `AuthenticatedSubjectContext` (ADR-046-componentsession-and-bus).", - "integration": "Store write transaction → `d2b-audit` sink → fsync → commit result", - "dataMigration": "Full d2b 3.0 reset; no v2 state/config import", - "validation": "Integration test: 100 mutations → verify hash-chained audit records with `zone` field, no `realm` field", - "removalProof": "None — net-new; no prior owner to remove" - }, - { - "workItemId": "ADR046-audit-003", - "specId": "ADR-046-telemetry-audit-and-support", - "specPath": "docs/specs/ADR-046-telemetry-audit-and-support.md", - "dependencyOwner": "ADR046-audit-001 + ADR046-session-001 + ADR046-bus-001; session/bus owner", - "currentSource": "`packages/d2b-gateway/src/audit.rs` (`GatewayAuditEvent`, `GatewayAuditKind::{DisplaySessionOpenAdmitted,DisplaySessionOpenDenied,DisplaySessionRunning,DisplaySessionClosed}`, `GatewayAudit` trait, `NoopGatewayAudit`); `packages/d2b-realm-core/src/audit.rs::AuditEnvelope{realm, principal, scope, decision, trace}` (fields adapted to v3 `SessionConnect` record class)", - "reuseSource": null, - "reuseAction": "adapt `GatewayAudit` trait pattern for `SessionConnect` and `RouteAdmission` record classes; `NoopGatewayAudit` pattern reused for test sinks", - "destination": "`packages/d2b-session/src/audit.rs`, `packages/d2b-bus/src/audit.rs`", - "detailedDesign": "`SessionConnect` records emitted at handshake completion. `GatewayAuditKind::DisplaySessionOpenAdmitted/Denied` → `event=\"connect\"`, `authz_decision=\"allowed/denied\"`. `GatewayAuditKind::DisplaySessionRunning` → informational `ProcessEffect`. `GatewayAuditKind::DisplaySessionClosed` → `event=\"close\"`. `transport_class=zone_link` covers what the current `AuditStreamKind::Gateway` stream recorded for gateway-backed realm sessions. `RouteAdmission` records emitted at bus route resolution for denied routes.", - "integration": "Session engine and bus router → `d2b-audit` sink", - "dataMigration": "Full d2b 3.0 reset; no v2 state/config import", - "validation": "Session connect/close/auth-failure audit tests; `GatewayAuditKind` → `SessionConnect` mapping test", - "removalProof": "`NoopGatewayAudit` and gateway JSONL sink retired after gateway is on v3 resource API" - }, - { - "workItemId": "ADR046-audit-004", - "specId": "ADR-046-telemetry-audit-and-support", - "specPath": "docs/specs/ADR-046-telemetry-audit-and-support.md", - "dependencyOwner": "ADR046-audit-001; CLI owner", - "currentSource": "`packages/d2b/tests/audit_contract.rs` (`d2b audit --strict` returns 78; `auditResponse` relay; `authz-audit-requires-admin` denial; daemon-down exit 1 without bash fallback); `packages/d2b-priv-broker/tests/broker_export_audit.rs` (`export_audit_requires_admin_and_exports_op_audit_records`: admin-only, path-free, NDJSON `ExportBrokerAuditOk` shape, `peer_uid` field, `ApplyNftables` operation name in records)", - "reuseSource": null, - "reuseAction": "adapt audit CLI contract test (daemon-down/exit behavior); adapt broker export test to new `zone` field and v3 record schema", - "destination": "`packages/d2b/src/zone_audit.rs` (new `d2b zone audit export` subcommand); `packages/d2b/tests/zone_audit_contract.rs`", - "detailedDesign": "`d2b zone audit export` opens segments read-only (shared flock), streams NDJSON to stdout, validates hash chain inline, reports breaks as inline error records, enforces `audit-export` verb via resource API (admin-only, same `SO_PEERCRED`/Role check as current `ExportBrokerAuditOk`). Assert no `realm`, `node`, `workload_id` fields in exported records.", - "integration": "`d2b` CLI → resource API `audit-export` verb → `d2b-audit` export iterator → stdout", - "dataMigration": "Full d2b 3.0 reset; no v2 state/config import", - "validation": "`export_audit.rs`: admin-only, hash break inline, no old field names (`realm`/`node`/`workload_id`), no path/argv in output, exit 0 on clean chain", - "removalProof": "`d2b audit` legacy command retained until `d2b zone audit export` covers all record classes" - }, - { - "workItemId": "ADR046-azure-vm-001", - "specId": "ADR-046-provider-runtime-azure-virtual-machine", - "specPath": "docs/specs/providers/ADR-046-provider-runtime-azure-virtual-machine.md", - "dependencyOwner": "Provider contract owner", - "currentSource": "`d2bd/src/provider_registry.rs`: `AzureVmForbidden`, `AZURE_VM_IMPLEMENTATION_ID`; `d2b-realm-provider/src/provider.rs`: `InfrastructureProvider` (dead-reachable)", - "reuseSource": null, - "reuseAction": "Extract and adapt; DELETE `InfrastructureProvider` after this Provider is operational", - "destination": "`src/{lib.rs,config.rs,schema.rs,error.rs,effect/mod.rs}`", - "detailedDesign": "Provider descriptor/manifest; `spec.config` schema; Guest spec.provider.settings schema; `AzureEffectPort` trait + `AzureOperationHandle`; `AzureVmError` enum; `SandboxSpec` with semantic classes; `BudgetSpec` with SI suffix memory fields; `restartPolicy` class/backoffBase/backoffMax; `networkUsage.allowEgress=false`; Endpoint ResourceType templates with name/transport/purpose", - "integration": "ProviderDeployment loads the descriptor/catalog and ResourceType schemas; Nix and Guest specs reference the provider settings; controller and EffectPort modules consume the shared config/error types.", - "dataMigration": "Full d2b 3.0 reset; no v2 state/config import. Existing registry sentinels are deleted only after the Provider resource model replaces them.", - "validation": "Provider catalog; descriptor fingerprint; schema/conformance tests", - "removalProof": "`InfrastructureProvider` deleted; `AzureVmForbidden` removed after Provider resource model replaces registry" - }, - { - "workItemId": "ADR046-azure-vm-002", - "specId": "ADR-046-provider-runtime-azure-virtual-machine", - "specPath": "docs/specs/providers/ADR-046-provider-runtime-azure-virtual-machine.md", - "dependencyOwner": "ADR046-azure-vm-001", - "currentSource": "`d2b-realm-provider/src/rate_limit.rs` (implemented-and-reachable)", - "reuseSource": null, - "reuseAction": "Copy and adapt", - "destination": "`src/effect/{mod.rs,real.rs,fake.rs,rate_limit.rs}`", - "detailedDesign": "`AzureEffectPort` async trait; opaque `AzureOperationHandle` (bounded bytes, no poll URL); real `azure_core`/`azure_mgmt_compute` impl; `FakeAzureEffectPort` for hermetic tests; ARM 429/503/409 handling", - "integration": "Azure VM controller lifecycle/idempotency code calls `AzureEffectPort`; the real implementation talks to ARM in production and `FakeAzureEffectPort` drives hermetic lifecycle tests.", - "dataMigration": "No persistent data migration; in-flight ARM operation handles are new v3 status/core-ledger records and are re-derived or adopted on reconcile when absent.", - "validation": "`tests/lifecycle_hermetic.rs`; all ARM paths via `FakeAzureEffectPort`; no ARM URL in test assertions", - "removalProof": "Old `InfrastructureProvider` ARM simulation deleted after parity" - }, - { - "workItemId": "ADR046-azure-vm-003", - "specId": "ADR-046-provider-runtime-azure-virtual-machine", - "specPath": "docs/specs/providers/ADR-046-provider-runtime-azure-virtual-machine.md", - "dependencyOwner": "ADR046-azure-vm-001; ADR046-azure-vm-002; Guest ResourceType controller contract", - "currentSource": "`d2b-realm-provider/src/conformance.rs`; main `a1cc0b2d`: `d2b-provider-toolkit/src/reconciler_loop.rs`", - "reuseSource": null, - "reuseAction": "Copy/adapt main toolkit; adapt conformance shape", - "destination": "`src/controller/{mod.rs,lifecycle.rs,idempotency.rs}`", - "detailedDesign": "Non-blocking reconcile: `start_*(...)` → persist `AzureOperationHandle` → `requeue-at`; `poll_lro` on subsequent ticks; controller as authorized `update-status` writer for Guest resources; finalizer held until ARM delete confirmed; top-level `phase`, `status.resource`, and Azure `status.provider.details.providerPhase` written atomically", - "integration": "Zone core dispatches Guest resource events to the Azure VM controller; ResourceClient updates status/finalizers; `AzureEffectPort` starts, polls, and deletes ARM LROs.", - "dataMigration": "Full d2b 3.0 reset; old WorkloadProvider lifecycle state is not imported. Existing ARM resources may be adopted by tag/idempotency checks during reconcile.", - "validation": "`tests/lifecycle_hermetic.rs`; `tests/conformance.rs`", - "removalProof": "Old `WorkloadProvider::provision`/`deprovision` paths retired" - }, - { - "workItemId": "ADR046-azure-vm-004", - "specId": "ADR-046-provider-runtime-azure-virtual-machine", - "specPath": "docs/specs/providers/ADR-046-provider-runtime-azure-virtual-machine.md", - "dependencyOwner": "ADR046-azure-vm-001; ComponentSession IKpsk2", - "currentSource": "`d2b-realm-provider/src/types.rs`: `ProviderGuestdBootstrapContract` (implemented-and-reachable); main `a1cc0b2d`: `d2b-session/src/bootstrap.rs`", - "reuseSource": null, - "reuseAction": "Copy/adapt main `BootstrapPsk`/`BootstrapAdmission`", - "destination": "`src/controller/bootstrap.rs`; `src/bootstrap_svc/{mod.rs,admission.rs,enrollment.rs}`", - "detailedDesign": "PSK generation; sealed PSK/admission/enrollment recovery material (ciphertext) in the controller's single guest-local sealed recovery Volume; `GrantBootstrapAdmission` typed bus call; IKpsk2 in bootstrap-svc; enrollment record; enrolled KK; the bootstrap-svc declares **no** state Volume (session state in process memory; obtains sealed PSK/admission from the controller only); the controller's sealed recovery Volume is an ordinary Volume resource created by core ProviderDeployment (before component Process start) from the controller's single `stateNamespaces` declaration with a Nix-preprovisioned `User/azure-vm-controller` layout principal; ARM operation/idempotency records live in the core Operation ledger and non-secret observed cloud phase lives in `Guest.status` (D087); controller does not own, create, or add Volume to exported ResourceTypes; it consumes its view dirfd only", - "integration": "Controller creates and seals recovery material in its state Volume, grants bootstrap admission over the bus, and bootstrap-svc performs IKpsk2 enrollment for Guest sessions.", - "dataMigration": "No v2 bootstrap state import; the new sealed recovery Volume is initialized on first v3 activation, and old vsock bootstrap material is retired at cutover.", - "validation": "`tests/bootstrap_hermetic.rs`; `tests/error_redaction.rs`", - "removalProof": "Old vsock bootstrap path removed at v3 cutover" - }, - { - "workItemId": "ADR046-azure-vm-005", - "specId": "ADR-046-provider-runtime-azure-virtual-machine", - "specPath": "docs/specs/providers/ADR-046-provider-runtime-azure-virtual-machine.md", - "dependencyOwner": "ADR046-azure-vm-003; Credential ResourceType; D055/D056", - "currentSource": "`d2b-realm-provider/src/credential.rs`: `AzureControlPlaneRef`, `OpaqueAzureRef`, `ManagedIdentityRef` (implemented-and-reachable)", - "reuseSource": null, - "reuseAction": "Retain `OpaqueAzureRef` directly; adapt credential acquisition to enrolled KK", - "destination": "`src/credential.rs`", - "detailedDesign": "ARM credential via enrolled KK `AcquireToken`; zeroizing token handling; no ambient credential fallback; `credential-managed-identity` guest-agent placement", - "integration": "Controller obtains ARM credentials through enrolled KK and the Credential ResourceType before EffectPort operations; the credential-managed-identity guest agent provides the token source.", - "dataMigration": "No ambient credential migration; v3 requires ResourceType Credential/ManagedIdentityRef plus enrolled KK, and the old direct IMDS fallback is removed.", - "validation": "`tests/credential_hermetic.rs`; `tests/error_redaction.rs`", - "removalProof": "Old direct IMDS calls from controller removed" - }, - { - "workItemId": "ADR046-azure-vm-006", - "specId": "ADR-046-provider-runtime-azure-virtual-machine", - "specPath": "docs/specs/providers/ADR-046-provider-runtime-azure-virtual-machine.md", - "dependencyOwner": "ADR046-azure-vm-003", - "currentSource": "`d2bd/src/provider_registry.rs`: `NEXT_LIFECYCLE_OPERATION_ID: AtomicU64` (production-reachable)", - "reuseSource": null, - "reuseAction": "Adapt to deterministic per-Guest keys", - "destination": "`src/controller/idempotency.rs`", - "detailedDesign": "Deterministic ARM request ID derivation; `AzureOperationHandle` opaque persistence (no poll URL in state); ARM 409 adoption; finalizer held through async deletion", - "integration": "Lifecycle controller stores deterministic request IDs and opaque handles in the core Operation ledger/status; restart recovery reads them before polling or adopting ARM operations.", - "dataMigration": "Old `AtomicU64` operation IDs are not imported; v3 operations use deterministic keys, while missing handles are re-derived or adopted from ARM.", - "validation": "`tests/idempotency.rs`; restart-recovery scenario", - "removalProof": "`AtomicU64` lifecycle op ID removed after all ARM callers migrate" - }, - { - "workItemId": "ADR046-azure-vm-007", - "specId": "ADR-046-provider-runtime-azure-virtual-machine", - "specPath": "docs/specs/providers/ADR-046-provider-runtime-azure-virtual-machine.md", - "dependencyOwner": "ADR046-azure-vm-001; ADR-046-provider-state; ADR-046-nix-configuration", - "currentSource": "`nixos-modules/options-realms-workloads.nix`: `WorkloadProviderKind::ProviderManaged`", - "reuseSource": null, - "reuseAction": "Adapt", - "destination": "`nixos-modules/` (Provider/Guest resource emitters); crate Nix build", - "detailedDesign": "Nix `spec.config` shape; `controllerExecutionRef`/`networkRef` eval-time assertions; no Volume refs for data disks; `systemArtifactId=null` enforcement; the single controller sealed recovery Volume is an ordinary Volume resource created by core ProviderDeployment (not in Zone bundle; not operator-authored); the bootstrap-svc declares no state Volume; guest-local placement — reconciled by the Guest-local volume-local instance and expressed by `source.executionRef` = config gateway Guest; host MUST NOT hold ARM binding, admission, PSK, or operation state; ARM operation/idempotency records live in the core Operation ledger and non-secret observed cloud phase in `Guest.status` (D087); no virtiofs or host-to-guest attachment; manifest freezes guest-local with no fallback; controller does not create, own, or list Volume in exported ResourceTypes; `Provider/volume-local` is the sole Volume reconciler; controller consumes required view dirfd only; **the recovery Volume is `kind: state`, `persistenceClass: persistent`, `storageNeed: secret`, sealed via `sealingCredentialRef`, with nonzero `quotaBytes`, `quota.maxBytes`, `quota.maxInodes`, and `source.settings.sourcePolicyId`; `persistenceClass: ephemeral` and zero quotas are rejected**; it survives component/Provider restart and participates in upgrade/destroy/reset; full canonical Volume spec including `stateSchema`, `source`, `layout` with a Nix-preprovisioned `User/` principal (not ComponentPrincipal), `views`, `identityMarker`, `snapshotPolicy: null`, `retentionPolicy: null`; `sensitivityClass: private` and `volume-domain-mismatch` isolation enforced; canonical `SandboxSpec` fields with `namespaceClasses`/`capabilityClasses`/`seccompClass`/`noNewPrivileges`/`startRoot`/`environmentClass`/`readOnlyRoot`; `BudgetSpec` with SI suffix; `restartPolicy` class/backoffBase/backoffMax; Endpoint ResourceType templates with name/transport/purpose", - "integration": "Nix emitters produce Provider, Guest, Volume, and Endpoint resource specs consumed by ProviderDeployment, `Provider/volume-local`, the Process Provider, and the Azure VM controller.", - "dataMigration": "Full d2b 3.0 reset; old `d2b.realms..workloads.` config is replaced by v3 resource authoring with no automatic v2 config import.", - "validation": "Nix eval tests; `make test-flake`; `make test-drift`", - "removalProof": "`d2b.realms..workloads.` removed at v3 cutover" - }, - { - "workItemId": "ADR046-azure-vm-008", - "specId": "ADR-046-provider-runtime-azure-virtual-machine", - "specPath": "docs/specs/providers/ADR-046-provider-runtime-azure-virtual-machine.md", - "dependencyOwner": "ADR046-azure-vm-003; ADR-046-telemetry-audit-and-support", - "currentSource": "`d2bd/src/metrics.rs` (production-reachable)", - "reuseSource": null, - "reuseAction": "Adapt audit shape; replace Prometheus with d2b-telemetry emitter", - "destination": "`src/{telemetry.rs,audit.rs}`", - "detailedDesign": "Closed metric labels; OTEL span attributes; audit durability classes; `azure-vm-deleted` appended post-commit; no ARM URI, ARM resource ID, or cloud endpoint in any telemetry surface", - "integration": "Controller/error paths call telemetry and audit emitters after status commits; d2b-telemetry consumes the metrics/spans and policy_observability enforces redaction.", - "dataMigration": "No metrics/audit data migration; new OTEL/audit surfaces start at v3 cutover and the old Prometheus registry is retired.", - "validation": "`tests/error_redaction.rs`; `d2b-contract-tests/tests/policy_observability.rs` updated", - "removalProof": "`d2bd/src/metrics.rs` hand-rolled registry removed after observability-otel Provider integration" - }, - { - "workItemId": "ADR046-azure-vm-009", - "specId": "ADR-046-provider-runtime-azure-virtual-machine", - "specPath": "docs/specs/providers/ADR-046-provider-runtime-azure-virtual-machine.md", - "dependencyOwner": "All ADR046-azure-vm-*", - "currentSource": "No existing Azure VM tests at baseline; fake/hermetic patterns from main `a1cc0b2d`", - "reuseSource": null, - "reuseAction": "Copy/adapt fake toolkit; write new tests", - "destination": "`tests/`; `integration/`", - "detailedDesign": "See §Test requirements", - "integration": "Provider crate tests, fake toolkit, and integration harness run under cargo/Layer-1 and validate all ADR046-azure-vm-* outputs together.", - "dataMigration": "None — test-only work; no runtime state. Old mock tests are removed only after parity.", - "validation": "All tests pass", - "removalProof": "Old `InfrastructureProvider` mock tests deleted after parity" - }, - { - "workItemId": "ADR046-bus-001", - "specId": "ADR-046-componentsession-and-bus", - "specPath": "docs/specs/ADR-046-componentsession-and-bus.md", - "dependencyOwner": "Sessions + resource API; bus owner", - "currentSource": "v3 `d2b-realm-router`, target resolver, CLI routing, operation router", - "reuseSource": "Any useful main d2b-client/provider/session routing symbols named by implementation sub-items", - "reuseAction": "extract/adapt", - "destination": "`packages/d2b-bus/src/{router,registry,authorization,streams,operations}.rs`", - "detailedDesign": "Exact service/resource routes, RBAC, pinned reverse route, cancellation, named stream bridge, no wildcard pub/sub", - "integration": "Every ResourceClient/controller/Provider/CLI service", - "dataMigration": "None — full d2b 3.0 reset; no prior state to migrate", - "validation": "Message isolation, route/auth revocation, fairness, reconnect, no direct-store path", - "removalProof": "Old direct dispatch branches removed only after route parity" - }, - { - "workItemId": "ADR046-bus-009", - "specId": "ADR-046-nix-configuration", - "specPath": "docs/specs/ADR-046-nix-configuration.md", - "dependencyOwner": "ADR046-nix-029; ADR046-nix-027; TargetInput v3 shape per ADR-046-componentsession-and-bus owning spec", - "currentSource": "Main commit source row below: `packages/d2b-client/src/{client,session,target,service,daemon_service,guest_service,host_socket}.rs` at `a1cc0b2d`.", - "reuseSource": null, - "reuseAction": "Reuse async client from main commit `a1cc0b2d`; adapt TargetInput, ServiceOwner, service packages, and local socket identity to Zone/ResourceRef semantics.", - "destination": "`packages/d2b-client/src/` (adapt in place).", - "detailedDesign": "Transport-neutral typed async client, ComponentSessionConnector abstraction, precise SessionFailure classification, signed metadata envelopes, named-stream client abstraction, and host-socket connector.", - "integration": "CLI/controllers construct TargetInput/MetadataInput; `d2b-client` resolves Zone/Resource routes via `d2b-bus` and reuses ComponentSessionConnector for calls/streams.", - "dataMigration": "Full d2b 3.0 reset; no v2 client state/config import", - "validation": "Port `d2b-client` tests; add ResourceRef TargetInput and Zone bootstrap socket identity gates.", - "removalProof": "ADR45 TargetInput Realm/Workload forms are replaced by Zone/ResourceRef forms; no prior v3 client owner to remove." - }, - { - "workItemId": "ADR046-bus-010", - "specId": "ADR-046-nix-configuration", - "specPath": "docs/specs/ADR-046-nix-configuration.md", - "dependencyOwner": "ADR046-nix-023; ADR046-nix-027; naming and service name per ADR-046-componentsession-and-bus owning spec", - "currentSource": "Main commit source row below: `packages/d2b-realm-router/src/service_v2.rs` and tests at `a1cc0b2d`.", - "reuseSource": null, - "reuseAction": "Reuse routing service behavior from main commit `a1cc0b2d`; rename RealmSessionAuthority/service identifiers to Zone terms per owning bus spec while preserving custody behavior.", - "destination": "`packages/d2b-bus/src/routing/zone_service.rs`.", - "detailedDesign": "ZoneSessionAuthority preserves no-host-credential custody, gateway/ZoneLink custody behavior, bounded concurrent dispatch, and graceful shutdown semantics.", - "integration": "`d2b-bus` Zone service handles route admission and shortcut lifecycle; ZoneSessionAuthority feeds session/audit/metric labels with stable custody state.", - "dataMigration": "Full d2b 3.0 reset; no v2 routing state import", - "validation": "Port `realm_service_v2.rs` and topology harness tests with Zone naming; add service-name wire gate.", - "removalProof": "ADR45 RealmService name and RealmId fields are adapted to Zone service; no prior v3 router owner to remove." - }, - { - "workItemId": "ADR046-bus-011", - "specId": "ADR-046-nix-configuration", - "specPath": "docs/specs/ADR-046-nix-configuration.md", - "dependencyOwner": "ADR046-nix-029; ADR046-nix-028; ADR046-nix-027; PROVIDER_BUNDLE_VERSION bump required on any bundle artifact format change", - "currentSource": "Main commit source row below: `packages/d2bd/src/provider_registry.rs` at `a1cc0b2d`.", - "reuseSource": null, - "reuseAction": "Reuse fail-closed provider composition from main commit `a1cc0b2d`; adapt v2 identity/bundle artifact types to v3 provider registry contracts and bump bundle versions.", - "destination": "`packages/d2bd/src/provider_registry.rs` (adapt in place).", - "detailedDesign": "Named ProviderCompositionError variants, first-party factory instantiation, bundle resolver validation, version/schema constants, and monotone lifecycle operation IDs.", - "integration": "`provider_registry.rs` composes the v3 provider catalog into active ProviderRegistry snapshots consumed by `d2b-bus`/provider runtime and bundle validation.", - "dataMigration": "Full d2b 3.0 reset; provider bundle format bumps to v3 and no v2 provider registry state import.", - "validation": "Port provider_registry composition tests; add PROVIDER_BUNDLE_VERSION/SCHEMA_VERSION v3 bump gate and ZoneId/ResourceName binding test.", - "removalProof": "ADR45 `provider_registry_v2` bundle artifact types are replaced by `d2b_contracts::v3::provider_registry` after v3 bundle schema lands." - }, - { - "workItemId": "ADR046-bus-012", - "specId": "ADR-046-nix-configuration", - "specPath": "docs/specs/ADR-046-nix-configuration.md", - "dependencyOwner": "ADR046-bus-011; ADR046-nix-029; GuestLifecycleRequest ResourceRef addressing per ADR-046-componentsession-and-bus owning spec", - "currentSource": "Main commit source row below: `packages/d2bd/src/provider_effects.rs` at `a1cc0b2d`.", - "reuseSource": null, - "reuseAction": "Reuse descriptor-bound effect dispatch from main commit `a1cc0b2d`; adapt lifecycle request wire type from VM name to Zone-scoped Guest ResourceRef.", - "destination": "`packages/d2bd/src/provider_effects.rs` (adapt in place).", - "detailedDesign": "DaemonEffectAdapters bind semantic effect ports; ProviderLifecycleDispatch bounds lifecycle mutations and routes broker start/stop through blocking adapters with idempotency-key deduplication.", - "integration": "`ProviderLifecycleDispatch` receives v3 Guest lifecycle operations from `d2b-bus`/core controller and routes broker start/stop through descriptor-bound effect ports.", - "dataMigration": "Full d2b 3.0 reset; no v2 lifecycle mutation state import", - "validation": "Port `provider_effects.rs` lifecycle dispatch tests; add ResourceRef Guest lifecycle request and BrokerCallerRole compatibility gates.", - "removalProof": "`VmLifecycleRequest`/`vm_name` path is replaced by a v3 ResourceRef Guest lifecycle op after broker/public wire contract versioning." - }, - { - "workItemId": "ADR046-ch-001", - "specId": "ADR-046-provider-runtime-cloud-hypervisor", - "specPath": "docs/specs/providers/ADR-046-provider-runtime-cloud-hypervisor.md", - "dependencyOwner": "Provider toolkit / system-minijail; W1 spike owner", - "currentSource": "`d2b-host/src/runtime_provider.rs`; `d2b-host/src/ch_argv.rs`; `d2bd/src/supervisor/dag.rs`", - "reuseSource": null, - "reuseAction": "Extract and adapt", - "destination": "`packages/d2b-provider-runtime-cloud-hypervisor/src/controller.rs`", - "detailedDesign": "End-to-end: single Guest reconcile → synchronous dependency-readiness check via ResourceClient → VMM Process creation → guest-control health check in observe handler → status write. Uses fake bus/store/supervisor stubs from toolkit. Proves fast-path latency gates (≤5 ms hint, ≤20 ms VMM Process creation when all deps ready). No EphemeralProcess resources at any step.", - "integration": "Zone ResourceClient + system-minijail Process Provider + fake broker effect", - "dataMigration": "None (spike)", - "validation": "Unit: reconcile state machine, fast-path latency, adoption/ambiguity, finalize ordering. Integration: end-to-end VMM boot with real KVM and guest-control session (requires `make test-host-integration`)", - "removalProof": "Not applicable (new crate)" - }, - { - "workItemId": "ADR046-ch-002", - "specId": "ADR-046-provider-runtime-cloud-hypervisor", - "specPath": "docs/specs/providers/ADR-046-provider-runtime-cloud-hypervisor.md", - "dependencyOwner": "ADR046-ch-001; Volume and Device foundation", - "currentSource": "`d2b-core/src/processes.rs`; `nixos-modules/processes-json.nix`; `d2b-priv-broker/src/ops/swtpm_dir.rs`; `d2b-host/src/swtpm_argv.rs`", - "reuseSource": null, - "reuseAction": "EXTRACT and REPLACE", - "destination": "`packages/d2b-provider-runtime-cloud-hypervisor/src/bootstrap_graph.rs`", - "detailedDesign": "Single owned VMM Process resource; synchronous ResourceClient dependency check (Device/kvm + all declared Devices, Networks, virtiofs Volumes); immediate Process creation when all deps ready; no EphemeralProcess resources; conditional net-VM Guest creation; per-dependency readiness tracking in reconcile loop", - "integration": "Depends on `Provider/volume-virtiofs`, `Provider/device-tpm`, `Provider/device-kvm`, `Provider/network-local` ResourceType readiness", - "dataMigration": "v3 reset; no v2 process graph migration", - "validation": "Golden VMM Process spec vectors; dependency-ordering tests; parallel Guest tests (8 concurrent); net-VM creation tests; Device/kvm explicit-ref enforcement", - "removalProof": "`ProcessRole::CloudHypervisor`, `ProcessRole::Swtpm`, `ProcessRole::NetVm` variant callers deleted; `nixos-modules/processes-json.nix` VMM emitter deleted after parity" - }, - { - "workItemId": "ADR046-ch-003", - "specId": "ADR-046-provider-runtime-cloud-hypervisor", - "specPath": "docs/specs/providers/ADR-046-provider-runtime-cloud-hypervisor.md", - "dependencyOwner": "ADR046-ch-002; artifact catalog foundation", - "currentSource": "`d2b-host/src/ch_argv.rs::ChArgvInput`, `generate_ch_argv`; `tests/golden/runner-shape/cloud-hypervisor-argv-*.txt`", - "reuseSource": null, - "reuseAction": "COPY/ADAPT", - "destination": "`packages/d2b-provider-runtime-cloud-hypervisor/src/vmm_argv.rs`; `tests/vmm_argv_golden_test.rs`", - "detailedDesign": "`VmmArgvInput` derived from validated `GuestSpec.spec.provider.settings`; kernel/initrd/rootfs paths resolved privately from artifact catalog at dispatch time; no path in spec/status; golden tests for headless/q35/microvm/gpu/video/macvtap variants", - "integration": "ProviderSupervisor LaunchTicket resolution", - "dataMigration": "None — full d2b 3.0 reset; no prior state to migrate", - "validation": "Golden argv vectors matching `cloud-hypervisor-argv-*.txt` shapes with v3 adaptations; redaction test (no store path in Debug output)", - "removalProof": "`d2b-host/src/ch_argv.rs::generate_ch_argv` callers removed; old golden test files adapted" - }, - { - "workItemId": "ADR046-ch-004", - "specId": "ADR-046-provider-runtime-cloud-hypervisor", - "specPath": "docs/specs/providers/ADR-046-provider-runtime-cloud-hypervisor.md", - "dependencyOwner": "ADR046-ch-002; nix-configuration foundation (`ADR046-identities-002`)", - "currentSource": "`nixos-modules/options-realms-workloads.nix`; `nixos-modules/options-vms.nix`; `nixos-modules/processes-json.nix`; `nixos-modules/store.nix`", - "reuseSource": null, - "reuseAction": "ADAPT and REPLACE", - "destination": "`packages/d2b-provider-runtime-cloud-hypervisor/nix/` (Nix emitter); `nixos-modules/` option extension for `runtime-cloud-hypervisor` Guest schema", - "detailedDesign": "`d2b.zones..resources.` with `type = \"Guest\"` and `spec.provider.settings` validated against signed Provider schema; `spec.systemArtifactId` top-level field; artifact catalog `type = \"nixos-system\"` enforced by rule 17; Guest-control `Endpoint` resource emitted without raw locator; `make test-drift` gate for schema/Nix drift", - "integration": "Zone resource bundle emission; private artifact catalog; `xtask gen-resource-nix-options` for auto-generated Nix option types", - "dataMigration": "`d2b.vms.` → `d2b.zones..resources.` documented in migration guide", - "validation": "nix-unit eval tests: rule CH-1 through CH-4 + rules 1–17; golden resource bundle JSON (no store path); type-mismatch eval errors; raw locator rejection; `spec.systemArtifactId` at top-level in JSON (not in `spec.provider.settings`)", - "removalProof": "`options-vms.nix`; `options-realms-workloads.nix` (LocalVm path); `nixos-modules/processes-json.nix` (VMM emitter); `nixos-modules/store.nix` removed after integration parity" - }, - { - "workItemId": "ADR046-ch-005", - "specId": "ADR-046-provider-runtime-cloud-hypervisor", - "specPath": "docs/specs/providers/ADR-046-provider-runtime-cloud-hypervisor.md", - "dependencyOwner": "ADR046-ch-001; ComponentSession/d2b-bus (`ADR046-session-001`)", - "currentSource": "`packages/d2bd/src/provider_shutdown.rs::GracefulVmShutdown`; `packages/d2b-host/src/runtime_provider.rs::RuntimeProvider::plan_guest_update`", - "reuseSource": null, - "reuseAction": "ADAPT", - "destination": "`packages/d2b-provider-runtime-cloud-hypervisor/src/health.rs`; `src/adoption.rs`", - "detailedDesign": "Authenticated KK ComponentSession health check over vsock; adoption verification (pid/cgroup/executable/generation) within `adoptionWindow`; ambiguity → Unknown/Degraded, never broad kill; graceful shutdown via guest-control session before SIGTERM", - "integration": "ComponentSession enrolled KK; guest bootstrap credential from `d2b-gctl` virtiofs share; `GuestReachable` condition write", - "dataMigration": "None — full d2b 3.0 reset; no prior state to migrate", - "validation": "Fake guest-control server test; health check timeout/failure/retry; adoption property test (ambiguity, gone, stale pid); graceful shutdown ordering", - "removalProof": "`ProcessRole::GuestControlHealth` observation path; `ProcessRole::GuestSshReadiness` deleted at cutover" - }, - { - "workItemId": "ADR046-ch-006", - "specId": "ADR-046-provider-runtime-cloud-hypervisor", - "specPath": "docs/specs/providers/ADR-046-provider-runtime-cloud-hypervisor.md", - "dependencyOwner": "ADR046-ch-001; telemetry foundation (`ADR046-telem-001`)", - "currentSource": "`packages/d2bd/src/metrics.rs` (`d2b_daemon_vm_*`); `packages/d2b-contract-tests/tests/policy_observability.rs`", - "reuseSource": null, - "reuseAction": "REPLACE", - "destination": "`packages/d2b-provider-runtime-cloud-hypervisor/src/metrics.rs`; `src/audit.rs`", - "detailedDesign": "`d2b_runtime_ch_*` metrics from §18.3; bounded durable audit records from §17.3; no `vm=` metric label; no path/argv/socket in any field; closed OTEL attribute allowlist extended per §18.4", - "integration": "Zone lightweight bounded emitter; `Provider/observability-otel` forwarding", - "dataMigration": "`d2b_daemon_vm_*` metrics retired; consumers must update dashboards", - "validation": "`policy_observability.rs` updated with v3 allowlist; cardinality tests; bounded message/field tests; audit record schema golden vectors", - "removalProof": "Hand-rolled Prometheus registry (`d2bd/src/metrics.rs` `d2b_daemon_vm_*` section) deleted after migration" - }, - { - "workItemId": "ADR046-ch-007", - "specId": "ADR-046-provider-runtime-cloud-hypervisor", - "specPath": "docs/specs/providers/ADR-046-provider-runtime-cloud-hypervisor.md", - "dependencyOwner": "ADR046-ch-001; `ADR046-pstate-001` (common status types)", - "currentSource": "`packages/d2b-core/src/storage.rs` (`StoragePathSpec`, `SensitivityClass`) — to be retired", - "reuseSource": null, - "reuseAction": "REPLACE (storage.rs)", - "destination": "`packages/d2b-provider-runtime-cloud-hypervisor/src/state.rs`; `packages/d2b-provider-runtime-cloud-hypervisor/tests/state_status_test.rs`", - "detailedDesign": "`state.rs` owns the controller's bounded non-secret operational-state projection into the owning resource's `status` subresource (reconcile stage, per-Guest launch/adoption observations, bounded counters, closed-enum error detail) — the controller declares no Provider state Volume and mounts no `/state`; on restart it re-derives observed state from the Zone resource store, the core Operation ledger, and external observation (running VMM/virtiofsd re-adopted from cgroup leaves + fresh pidfds), treating `status` as observation, never authority (D087); status writes occur only on material change and stay within the status bounds", - "integration": "Volume is provisioned and mounted by ProviderDeployment before controller Process launch (`required: true` mount); controller observes Volume status only through its ComponentSession, never through direct ResourceClient Volume verbs", - "dataMigration": "v3 reset; no v2 state storage migration", - "validation": "`state_volume_test.rs` (hermetic): StateEnvelope round-trip, view helper correctness, startup-check behavior when Volume phase is not current", - "removalProof": "`d2b-core/src/storage.rs` `StoragePathSpec` / `SensitivityClass` retired only after all Provider state consumers migrate to v3 Volume state helpers" - }, - { - "workItemId": "ADR046-cli-001", - "specId": "ADR-046-cli-and-operations", - "specPath": "docs/specs/ADR-046-cli-and-operations.md", - "dependencyOwner": "ADR046-identities-001, ADR046-api-001; CLI crate owner", - "currentSource": "`packages/d2b/src/lib.rs`: `Context::from_env` (reads `D2B_PUBLIC_SOCKET` env or `/run/d2b/public.sock` default; single flat socket path — no zone-qualified path, no `ZoneContext`), `NativeCli`, `NativeCommand`, `dispatch`, `CliFailure`, `report_failure`, `stdout_is_tty`; socket path model: old `Context { public_socket, broker_socket }` → target: `ZoneContext { zone_name, socket_path, session_client }`", - "reuseSource": "main `a1cc0b2d` — copy/adapt these exact symbols: (1) `packages/d2b-client/src/client.rs` `Client`, `ConnectedClient`, `CallOptions`, `CancellationToken`, `MetadataInput`, `RetryPolicy`, `Response` — copy unchanged as the async client foundation; (2) `packages/d2b-client/src/host_socket.rs` `HostSocketConnector::from_seqpacket_fd`, `local_daemon_endpoint_identity` — adapt: replace fixed `d2b.daemon.v2` service lookup with zone-scoped service identity; replace hardcoded `RealmPath::parse(\"local-root\")` with `Zone/`-derived target; (3) `packages/d2b-daemon-access/src/component_session.rs` `LocalUnixDaemonAccess::connect_component_session()` connect chain — adapt: replace `TargetInput::LocalRoot(realm)` with the v3 Zone target variant; adapt socket path discovery for per-Zone paths; (4) `packages/d2b-client/src/session.rs` `ComponentSessionConnector`, `ConnectedSession`, `NamedStream`, `SessionCall`, `SessionReply`, `SharedDriver` — copy unchanged; (5) `packages/d2b-client/src/target.rs` `RouteTable`, `RouteRecord`, `TargetInput`, `ResolvedTarget`, `TransportKind`, `TransportSelection` — copy unchanged, excluding `TargetInput::Realm/Workload/Provider` variants which carry ADR 0045 assumptions; (6) `packages/d2b-contracts/src/v2_component_session.rs` `LimitProfile::local_default()` constants: `MAX_REQUEST_LIFETIME_MS=900000`, `LOCAL_HANDSHAKE_DEADLINE_MS=5000`, `MAX_RECONNECT_ATTEMPTS=10`, `MAX_ACTIVE_NAMED_STREAMS=128`, `MAX_LOGICAL_MESSAGE_BYTES=1048576`, `named_stream_queue_bytes=262144`, `aggregate_named_stream_queue_bytes=4194304` — copy unchanged; these bound every CLI deadline and stream operation", - "reuseAction": "adapt", - "destination": "`packages/d2b/src/lib.rs`, `packages/d2b/src/context.rs`, `packages/d2b/src/dispatch.rs`", - "detailedDesign": "Introduce `ZoneContext` (zone name, socket path, ComponentSession client); implement `--zone`/`D2B_ZONE`/nearest-socket discovery using adapted `LocalUnixDaemonAccess::connect_component_session()` chain; introduce `ResourceRef` argument parser; introduce unified `--json`/`--human`/`--deadline` flag infrastructure bounded by `MAX_REQUEST_LIFETIME_MS=900s`; freeze `--json` schema version 1; stabilize exit code table. Excluded ADR 0045 assumptions: `TargetInput::Realm`, `TargetInput::Workload`, `TargetInput::Provider` variants; `RealmPath::parse(\"local-root\")` / `RealmId::derive` pattern; `RealmPath`-based service owner types.", - "integration": "All command functions receive `ZoneContext`; resource API client (`Client`) is injected for testing", - "dataMigration": "None; context discovery replaces env-var path lookups", - "validation": "Zone-unavailable/fallback tests; ResourceRef parse/reject vectors; exit-code round-trip tests; TTY detection tests; adapt `client.rs:typed_routes_select_exact_transport_without_fallback` (line 1053), `connector_discovers_and_authenticates_the_driver_generation` (line 1254), `daemon_transport_rejects_ancillary_data_and_oversized_packets` (line 1312)", - "removalProof": "Old `Context` struct removed only after all command functions use `ZoneContext`" - }, - { - "workItemId": "ADR046-cli-002", - "specId": "ADR-046-cli-and-operations", - "specPath": "docs/specs/ADR-046-cli-and-operations.md", - "dependencyOwner": "ADR046-cli-001, ADR046-api-001; CLI crate owner", - "currentSource": "`packages/d2b/src/lib.rs`: `cmd_vm_start`, `cmd_vm_stop`, `cmd_vm_restart`, `cmd_vm_status`, `cmd_vm_list`, `cmd_list`, `cmd_status`; wire types: `ListResponse { vms: Vec }`, `StatusResponse { entries: Vec }`, `VmLifecycleState` (old: Stopped/Starting/Booted/Running/Stopping/Restarting/Failed/Unknown) from `packages/d2b-contracts/src/public_wire.rs:2152,2158,2605`; `ListEntry.vm: String` = `WorkloadId`; `VmStatus.lifecycle.state: VmLifecycleState` → target Guest `phase` (Pending\\|Ready\\|Succeeded\\|Degraded\\|Failed\\|Deleted\\|Unknown; Starting/Stopping/Restarting → conditions/reasons); `WorkloadPublicSummary.execution_posture: WorkloadExecutionPosture` from `public_wire.rs:267` (carries `IsolationPosture`; unsafe-local entries have `IsolationPosture::UnsafeLocal`)", - "reuseSource": "main `a1cc0b2d` — copy/adapt: (1) `packages/d2b-client/src/daemon_service.rs` `DaemonClient::lifecycle()` (line 210), `DaemonClient::list_workloads()` (line 148), `DaemonClient::inspect()` (line 179); `DaemonMethod::Apply/Start/Stop/Restart/ListWorkloads` variants (lines 31-46) — adapt: replace `WorkloadLifecycleRequest`/`WorkloadName` with `Guest/` ResourceRef; replace `TargetInput::Workload`-scoped calls with zone-root resource API calls; (2) `packages/d2b-contracts/src/generated_v2_services/daemon.rs` `WorkloadLifecycleProjection`, `DeploymentProjection`, `RuntimeProjection` — adapt field mapping to Guest resource spec/status; (3) `packages/d2b/src/lib.rs` `cmd_launch` (`LaunchArgs`) — adapt: the typed ComponentSession target resolution pattern applies but realm/workload-model types (`RealmPath`, `WorkloadName`) are excluded; behavior selected: idempotent apply with dry-run/apply precondition", - "reuseAction": "adapt", - "destination": "`packages/d2b/src/guest.rs` (`d2b guest start/stop/restart/list/status`); unsafe-local workloads go to `packages/d2b/src/host.rs` (`d2b host list/status/get`), NOT guest.rs", - "detailedDesign": "Route Guest lifecycle (WorkloadProviderKind: LocalVm/QemuMedia/ProviderManaged) through `d2b.resource.v3` Get/UpdateSpec/Watch; map dry-run/apply to resource API precondition; `--no-wait-ready` exits on accepted; with-wait uses `d2b status --watch` loop. WorkloadProviderKind::UnsafeLocal entries MUST route to `d2b host` commands only; any code path that would return an unsafe-local entry from `d2b guest list` is a correctness violation. v2 commands (`d2b up/down/restart/list/status`, `d2b vm start/stop/restart/list/status`) are deleted at 3.0; `d2b migrate-check` explains replacements.", - "integration": "ZoneContext → resource API client → Guest resource; status watch uses Watch stream", - "dataMigration": "None — full d2b 3.0 reset; no prior state to migrate", - "validation": "Dry-run/apply/wait/no-wait-ready tests; zone-unavailable degraded path; JSON output schema tests; confirm v2 command paths are absent (compilation failure if any cmd_vm_start/stop alias re-introduced)", - "removalProof": "Old `cmd_vm_start/stop/restart` seqpacket paths removed after Guest resource API paths are live with full test coverage" - }, - { - "workItemId": "ADR046-cli-003", - "specId": "ADR-046-cli-and-operations", - "specPath": "docs/specs/ADR-046-cli-and-operations.md", - "dependencyOwner": "ADR046-cli-001, ADR046-api-001; CLI crate owner", - "currentSource": "`packages/d2b/src/exec_client.rs` (entire FSM); `packages/d2b/src/terminal_client.rs`; `packages/d2b/src/lib.rs`: `cmd_vm_exec`, `cmd_vm_exec_management`, `VmExecArgs`", - "reuseSource": "main `a1cc0b2d` — copy/adapt: (1) `packages/d2b-client/src/daemon_service.rs` `DaemonClient::open_terminal(method, resource_id, operation_id, selection, options, cancellation)` returning `DaemonTerminal` (line 248) — copy-then-adapt: replaces the existing seqpacket Exec call; `DaemonMethod::Exec` (line 40) maps to `d2b exec run`; (2) `packages/d2b-contracts/src/generated_v2_services/terminal.rs` `TerminalOpenRequest`, `TerminalOpenResponse`, `TerminalStreamFrame`, `TerminalSelection`, `TerminalKind` — copy unchanged as the named-stream terminal wire protocol; (3) `packages/d2b-client/src/session.rs` `NamedStream` (`send`, `receive`, `cancel`, `close`, `is_terminal`) — copy unchanged; provides async stdio routing and cancel on disconnect; (4) `packages/d2b-client/src/daemon_service.rs` `GuestClient::inspect_exec()`, `cancel_exec()`, `open_exec_retained_log()` — adapt: rename from `WorkloadName`/`GuestClient` to `EphemeralProcess/` resource API; (5) `packages/d2b-session/src/streams.rs` `NamedStreamMux` limits (`MAX_ACTIVE_NAMED_STREAMS=128`, `named_stream_queue_bytes=262144`) — copy unchanged; bounds the exec stream pipeline; (6) `packages/d2b-session/src/cancellation.rs` `Cancellation`, `RequestRegistry` — copy unchanged; provides generation-bound per-request cancel; tests to adapt/import: `client.rs:terminal_uses_server_stream_and_validates_bidirectional_lifecycle`, `terminal_rejects_response_generation_and_non_server_stream_ids`, `invalid_terminal_selection_is_rejected_before_open_rpc`, `guest_exec_management_preserves_typed_state_and_cancel_correlation`, `guest_retained_log_open_binds_range_resource_and_selection`, `named_stream_fragments_over_queue_credit_and_has_terminal_actions`, `named_stream_grants_only_consumed_data_and_releases_blocked_sender`; excluded ADR 0045 assumptions: `GuestClient` internal `TargetInput::Workload`-scoped vsock routing (guest-control proxy uses old `WorkloadName`/`RealmPath` — these are excluded; v3 routes through resource API only)", - "reuseAction": "copy-then-adapt", - "destination": "`packages/d2b/src/exec.rs` (`d2b exec run/attach/wait/status/list/logs/kill`)", - "detailedDesign": "Map EphemeralProcess resource lifecycle; `exec run` creates resource and returns ref; `exec attach` opens named stream via adapted `DaemonClient::open_terminal(DaemonMethod::Exec, ...)` → `DaemonTerminal`; retain full `exec_client.rs` FSM and TTY machinery from baseline; retain `--json` envelope fields `source`/`reason`/`guestExitCode`/`signal`/`transportExitCode`; retain reserved exit codes 42/69/70/75/76/77. v2 commands (`d2b vm exec *`) are deleted at 3.0; no dispatch wiring. Excluded ADR 0045: `GuestClient` vsock/guest-control proxy path; `TargetInput::Workload`; old `WorkloadName`-keyed exec management.", - "integration": "ZoneContext → EphemeralProcess Create → named stream attach via `DaemonClient::open_terminal`", - "dataMigration": "None — full d2b 3.0 reset; no prior state to migrate", - "validation": "Full `exec_client.rs` test suite migrated; adapted tests from main `client.rs:terminal_*` and `guest_exec_*`; TTY/raw-mode/RAII/signal tests; `--json` envelope/disambiguation tests; capacity/transport/auth/protocol exit-code tests; confirm v2 `cmd_vm_exec` path is absent", - "removalProof": "Old `cmd_vm_exec` seqpacket path removed after `d2b exec` paths have equivalent coverage" - }, - { - "workItemId": "ADR046-cli-004", - "specId": "ADR-046-cli-and-operations", - "specPath": "docs/specs/ADR-046-cli-and-operations.md", - "dependencyOwner": "ADR046-cli-001, `shell-terminal` Provider dossier; CLI crate owner", - "currentSource": "`packages/d2b/src/lib.rs`: `cmd_shell` (`ShellArgs.vm: String` = `WorkloadId` or `RealmTarget`; routes through `route_vm_target()` → `VmTargetRoute::Local\\|Gateway`; gateway `Attach` fails closed via `shell_gateway_attach_failure()` with error class `gateway-shell-attach-unavailable` at lib.rs:1697,1780), `cmd_shell_attach`, `run_shell_fsm`, `ShellOwnerTransport`; wire: `ShellOp`, `ShellOpResponse`, `ShellAttachArgs { vm: String }`, `ShellListEntry`, `ShellSessionState` from `packages/d2b-contracts/src/public_wire.rs:1319,1394,1452,1409`; `exec_client.rs` signal/TTY machinery", - "reuseSource": "main `a1cc0b2d` — copy/adapt: (1) `packages/d2b-client/src/daemon_service.rs` `DaemonClient::open_terminal(DaemonMethod::Shell, ...)` returning `DaemonTerminal` — copy-then-adapt; same `TerminalOpenRequest`/`TerminalOpenResponse`/`DaemonTerminal` flow as cli-003, applied to shell open/attach; (2) `packages/d2b-contracts/src/generated_v2_services/shell.rs` and `shell_ttrpc.rs` `ShellService` methods: `ShellCreate`, `ShellAttach`, `ShellDetach`, `ShellList`, `ShellInspect`, `ShellKill`, `ShellCancel` (service definition); `ShellCreateRequest`/`ShellAttachRequest`/`ShellListResponse`/`ShellInspectResponse` — copy-then-adapt: these are the target ShellSession resource CRUD wire types; adapt field names from `workload_id`/`shell_name` to `Guest/` ResourceRef; (3) `packages/d2b-client/src/session.rs` `NamedStream` — copy unchanged; used for shell I/O stream; (4) `packages/d2b-session/src/cancellation.rs` `Cancellation`, `RequestRegistry` — copy unchanged; (5) `packages/d2b-session/src/deadline.rs` `DeadlineBudget` — copy unchanged; shell sessions use per-operation deadline tracking; tests to adapt/import: `client.rs:shell_management_uses_typed_selection_result_and_terminal_outcome`, `named_stream_fragments_over_queue_credit_and_has_terminal_actions`, `named_stream_grants_only_consumed_data_and_releases_blocked_sender`, `concurrent_named_streams_route_events_without_cross_consumption`; excluded ADR 0045 assumptions: `VmTargetRoute::Gateway` shell routing and `realm_router` relay path; old `ShellOp`/`ShellOpResponse` seqpacket wire; unsafe-local helper shell protocol v2", - "reuseAction": "copy-then-adapt", - "destination": "`packages/d2b/src/shell.rs` (`d2b shell open/attach/list/detach/kill/status`)", - "detailedDesign": "Route ShellSession resource lifecycle through resource API using adapted `ShellService` generated types; `shell open` → `ShellCreate` → `DaemonClient::open_terminal(Shell)` → `DaemonTerminal`; retain FSM/TTY/signal/RAII behavior from `run_shell_fsm`; `--name` required for kill; SIGHUP detaches without kill. v2 commands (`d2b shell *`) are deleted at 3.0; no dispatch wiring. Excluded: gateway relay path (`VmTargetRoute::Gateway`); old `ShellOp`/`ShellOpResponse` seqpacket protocol; `TargetInput::Workload`-keyed realm routing.", - "integration": "ZoneContext → ShellSession Create via `DaemonClient::open_terminal` → `NamedStream` I/O", - "dataMigration": "None — full d2b 3.0 reset; no prior state to migrate", - "validation": "Shell list/detach/kill/attach unit tests (adapted from existing); adapted `client.rs:shell_management_*` and `named_stream_*` tests; TTY RAII/signal tests; confirm v2 `cmd_shell` path is absent", - "removalProof": "Old `cmd_shell` seqpacket path removed after new shell commands have equivalent coverage" - }, - { - "workItemId": "ADR046-cli-005", - "specId": "ADR-046-cli-and-operations", - "specPath": "docs/specs/ADR-046-cli-and-operations.md", - "dependencyOwner": "ADR046-cli-001, Provider model spec; CLI crate owner", - "currentSource": "`packages/d2b/src/lib.rs`: `cmd_audio`, `cmd_clipboard_arm`, `cmd_vm_display`", - "reuseSource": "main `a1cc0b2d` — copy/adapt: (1) `packages/d2b-provider/src/rpc.rs` `RpcProviderProxy` — adapt: the CLI uses the inverse side (client calling provider via `ConnectedClient::invoke`); `RpcCall`, `RpcPayload`, `RpcResponse`, `RpcOperation` define the typed call shape for dynamic provider commands; (2) `packages/d2b-contracts/src/generated_v2_services/provider_runtime.rs`, `provider_display.rs`, `provider_audio.rs`, `provider_infrastructure.rs` — adapt: the generated service method types show what CLI projection verbs can be mapped to typed service calls; use as shape reference for the first audio/clipboard/display migration; (3) `packages/d2b-provider-toolkit/src/conformance.rs` `check_provider_conformance`, `check_descriptor_conformance` — copy-then-adapt into CLI-side projection conformance validation (bounds: 64 KiB, 32 sub-verbs, 2s deadline, shell-escape, newline strip); (4) `packages/d2b-provider-toolkit/src/server.rs` `GeneratedProviderServiceServer::generated_services()` — server-side only; use as reference for what CLI InspectSchema receives; excluded ADR 0045 assumptions: `ProviderRegistry`/`ProviderAgentAdapter` are server-side and not used in CLI; `RpcProviderProxy` internal `AuthenticatedProviderRpc` pattern is server-side; tests to adapt: `conformance.rs:every_axis_passes_identical_in_process_and_rpc_conformance`, `generated_server_dispatches_closed_methods_over_authenticated_session`", - "reuseAction": "adapt", - "destination": "`packages/d2b/src/provider.rs` (`d2b provider list/get/status/inspect`; dynamic projection loading)", - "detailedDesign": "`d2b provider list/get/status/inspect`; InspectSchema call returns dynamic projection descriptor using `ConnectedClient::invoke` with generated provider service types; projection bounds enforcement (64 KiB, 32 sub-verbs, 2s deadline, shell-escape, newline strip); built-in name collision guard; audio/clipboard/display as first providers to migrate their projections", - "integration": "ZoneContext → Provider resource + InspectSchema via `ConnectedClient::invoke`", - "dataMigration": "None — full d2b 3.0 reset; no prior state to migrate", - "validation": "Projection size/name/collision/timeout bounds tests; audio/clipboard/display projection conformance tests; completion script safety tests; adapted `conformance.rs` tests", - "removalProof": "Built-in `cmd_audio`/`cmd_clipboard_arm`/`cmd_vm_display` removed only after Provider projection paths pass equivalence tests" - }, - { - "workItemId": "ADR046-cli-006", - "specId": "ADR-046-cli-and-operations", - "specPath": "docs/specs/ADR-046-cli-and-operations.md", - "dependencyOwner": "ADR046-cli-001; CLI crate owner", - "currentSource": "None (no completion exists in v3 baseline)", - "reuseSource": "Optional: clap_complete crate (version to be pinned); no main-branch source", - "reuseAction": "copy-unchanged (clap_complete)", - "destination": "`packages/d2b/src/complete.rs` (`d2b complete bash/zsh/fish`)", - "detailedDesign": "`d2b complete ` emits completion script; uses clap `CommandFactory::command()` plus dynamic projection fetch (2s per-Provider, 10s total); result bounded at 256 KiB; shell-escaped; newlines stripped", - "integration": "Standalone command; no Zone API required for static completion; Zone API used for dynamic Provider projection", - "dataMigration": "None — full d2b 3.0 reset; no prior state to migrate", - "validation": "Completion script tests (bash/zsh/fish syntax valid); projection injection safety tests; deadline/partial-Provider tests", - "removalProof": "Not applicable" - }, - { - "workItemId": "ADR046-cli-007", - "specId": "ADR-046-cli-and-operations", - "specPath": "docs/specs/ADR-046-cli-and-operations.md", - "dependencyOwner": "ADR046-cli-001; CLI/activation Provider owner", - "currentSource": "`packages/d2b/src/lib.rs`: `cmd_build`, `cmd_generations`, `cmd_switch`, `cmd_boot`, `cmd_test`, `cmd_rollback`, `cmd_gc`, `cmd_migrate`, `cmd_keys_*`, `cmd_keys_trust`, `cmd_keys_rotate_known_host`, `ConfigCommand` variants", - "reuseSource": "main `a1cc0b2d` — copy/adapt: (1) `packages/d2b-client/src/daemon_service.rs` `DaemonClient::lifecycle()` with `DaemonMethod::Apply`, `DaemonMethod::Start`, `DaemonMethod::Stop`, `DaemonMethod::Restart` — copy-then-adapt: the apply/lifecycle dispatch pattern maps cleanly to `d2b activation switch/boot/test/rollback`; retain idempotency token and dry-run/apply precondition from `DaemonMethod::Apply`; (2) `packages/d2b-contracts/src/generated_v2_services/activation.rs` activation service method types — adapt: map `ActivationBuildRequest`, `ActivationSwitchRequest`, `ActivationGenerationsRequest` to typed CLI args; excluded ADR 0045 assumptions: `DaemonMethod::ListRealms` / `DaemonMethod::ListWorkloads` are not used; old `WorkloadName`-keyed dispatch is excluded", - "reuseAction": "adapt", - "destination": "`packages/d2b/src/activation.rs` (`d2b activation build/generations/switch/boot/test/rollback/gc/migrate/keys/trust/rotate-known-host/config`)", - "detailedDesign": "Route through `activation-nixos` Provider service via `ConnectedClient::invoke` using adapted `DaemonMethod::Apply`/lifecycle dispatch pattern; retain dry-run/apply; retain guest-control transport for config sync (no SSH). v2 top-level activation commands (`d2b build/switch/boot/test/rollback/gc/migrate/keys/trust/rotate-known-host/config`) are deleted at 3.0; no dispatch wiring.", - "integration": "ZoneContext → activation-nixos Provider service → resource API via `ConnectedClient::invoke`", - "dataMigration": "None — full d2b 3.0 reset; no prior state to migrate", - "validation": "All existing switch/boot/test/rollback/keys tests adapted; config sync/diff/approve/reject tests; confirm v2 top-level activation paths are absent; adapted `client.rs:daemon_typed_list_preserves_projection_and_truncation` apply pattern", - "removalProof": "Old top-level activation verbs removed only after `d2b activation *` paths have equivalent coverage" - }, - { - "workItemId": "ADR046-cli-008", - "specId": "ADR-046-cli-and-operations", - "specPath": "docs/specs/ADR-046-cli-and-operations.md", - "dependencyOwner": "ADR046-cli-001; CLI crate owner", - "currentSource": "`packages/d2b/src/lib.rs`: `cmd_host_check`, `cmd_host_prepare`, `cmd_host_destroy`, `cmd_host_doctor`, `cmd_host_install`, `cmd_host_reconcile`, `cmd_host_validate`; `host_validate.rs`", - "reuseSource": "main `a1cc0b2d` — copy/adapt: (1) `packages/d2b-daemon-access/src/component_session.rs` `LocalUnixDaemonAccess::connect_component_session()` connect chain — adapt: the zone-local host commands use the same connect chain as cli-001; `d2b host prepare` and `d2b host doctor` both require a live `ZoneContext`; (2) `packages/d2b-client/src/client.rs` `ConnectedClient::invoke()` with `CallOptions` and `CancellationToken` — copy unchanged; used for Host resource Get/UpdateSpec/Status calls and broker-op dispatch; (3) `packages/d2b-contracts/src/generated_v2_services/broker.rs` broker operation request/response types — adapt: `BrokerHostPrepareRequest`, `BrokerHostDestroyRequest`, `BrokerHostDoctorRequest` (or equivalent) types define the CLI argument shape; retain broker-mediated ownership-marker semantics from baseline; excluded ADR 0045 assumptions: `TargetInput::Workload`-scoped broker routing is excluded; broker operation routing uses zone-root LocalRoot pattern only", - "reuseAction": "adapt", - "destination": "`packages/d2b/src/host.rs` (all `d2b host` subcommands)", - "detailedDesign": "Route `host prepare/destroy` through Zone resource API Host reconcile operation via `ConnectedClient::invoke`; retain broker-mediated mutation and ownership-marker semantics; `host doctor` prefers Zone resource API status, falls back to local state files; `host check` retains exit-code 3; `host validate` retains wave/evidence-dir/scripts-dir/signature", - "integration": "ZoneContext → Host resource; broker op path retained for emergency/shutdown-hook", - "dataMigration": "None — full d2b 3.0 reset; no prior state to migrate", - "validation": "All existing host-check/prepare/destroy/doctor/install/reconcile/validate tests; exit-code 3 regression; doctor Zone-fallback/local-state-fallback tests", - "removalProof": "Raw broker-socket paths removed only after Host resource API routes have equivalent coverage" - }, - { - "workItemId": "ADR046-cli-009", - "specId": "ADR-046-cli-and-operations", - "specPath": "docs/specs/ADR-046-cli-and-operations.md", - "dependencyOwner": "ADR046-cli-001; CLI crate owner", - "currentSource": "`packages/d2b/src/lib.rs`: `cmd_realm_list` (reads static `realm-entrypoints.json` via `realm_policy_rows_raw()`), `cmd_realm_inspect`, `cmd_realm_enter` (→ `realm_gateway_exec_args` → `cmd_vm_exec` with `-it bash -l`), `cmd_realm_run` (→ `cmd_vm_exec` with caller argv); wire output types: `RealmListOutputV1 { realms: Vec }`, `RealmInspectOutputV1 { realm: RealmPolicyOutputV1 }` from `packages/d2b-contracts/src/cli_output.rs:285,292,345`; `RealmPolicyOutputV1` fields: `realm` (= `RealmId`), `mode`, `gateway_vm`, `gateway_target`, `gateway_state`, `cross_realm_policy`, `credential_boundary`; `target_routing.rs`: `Route::Local { vm }`, `Route::Gateway { gateway, target }`, `resolve_access_route()`, `VmTargetRoute`; `d2b-realm-router::RealmEntrypointTable`", - "reuseSource": "main `a1cc0b2d` — reference only (no copy): `packages/d2b-realm-router/src/service_v2.rs` `RealmServiceServer`, `RealmServiceProcess`, `RealmMethod::Inspect`, `RealmMethod::ResolveRoute` — server-side multi-realm routing; this is the ADR 0045 multi-Zone topology and is **excluded** from v3 CLI as a direct reuse source; `packages/d2b-realm-router/src/remote_node.rs` `RemoteNodeRegistration`, `RemoteNodeEntry` — constellation remote routing; also excluded; note: `packages/d2b-client/src/daemon_service.rs` `DaemonClient::list_workloads()` and `DaemonMethod::ListRealms` are the closest live list-call patterns, but their zone/workload scoping uses `RealmPath`/`RealmId` types that are ADR 0045-specific; adapt `ConnectedClient::invoke()` with a v3 Zone List request type instead; no main symbols are copied unchanged for cli-009; the zone resource API type design is an ADR-only deliverable pending Zone resource spec", - "reuseAction": "adapt", - "destination": "`packages/d2b/src/zone.rs` (`d2b zone get/list/status`)", - "detailedDesign": "`d2b zone get []` fetches Zone self resource via `ConnectedClient::invoke`; `d2b zone list` lists ZoneLink resources. v2 commands (`d2b realm list/inspect/enter/run`) are deleted at 3.0; no dispatch wiring. Excluded ADR 0045: `RealmServiceServer`/`RealmServiceProcess` multi-realm service; `RemoteNodeRegistration` constellation routing; `TargetInput::Realm`; `RealmMethod::ResolveRoute`/`AuthorizeShortcut`/`RevokeShortcut`.", - "integration": "ZoneContext → Zone resource Get/List via `ConnectedClient::invoke`", - "dataMigration": "None — full d2b 3.0 reset; no prior state to migrate", - "validation": "Zone get/list tests; confirm v2 `cmd_realm_*` paths are absent", - "removalProof": "`cmd_realm_*` and `target_routing.rs` removed only after zone routes pass equivalence tests" - }, - { - "workItemId": "ADR046-cli-010", - "specId": "ADR-046-cli-and-operations", - "specPath": "docs/specs/ADR-046-cli-and-operations.md", - "dependencyOwner": "ADR046-cli-001; CLI crate owner", - "currentSource": "None — net-new v3 work; no pre-ADR45 baseline equivalent", - "reuseSource": "main `a1cc0b2d` — copy/adapt: (1) `packages/d2b-client/src/client.rs` `ConnectedClient::invoke()`, `ConnectedClient::invoke_with_attachments()`, `ConnectedClient::open_server_stream()` — copy unchanged; these are the three primitives for resource Get/List/Watch respectively; (2) `packages/d2b-client/src/session.rs` `NamedStream` (`send`, `receive`, `cancel`, `close`, `is_terminal`) — copy unchanged; Watch stream output arrives over a named stream; (3) `packages/d2b-session/src/deadline.rs` `DeadlineBudget` — copy unchanged; `--deadline` flag maps to `DeadlineBudget::admit_metadata` wall deadline; `MAX_REQUEST_LIFETIME_MS=900000` caps all Watch/List deadlines; (4) `packages/d2b-client/src/client.rs` `CancellationToken::cancel()` — copy unchanged; `SIGINT`/SIGTERM → `CancellationToken::cancel()` → propagated to `ConnectedClient::invoke` and `NamedStream`; (5) `packages/d2b-client/src/client.rs` `MetadataInput`, `RetryPolicy`, `CallOptions` — copy unchanged; `--idempotency-token` maps to `MetadataInput`; `RetryPolicy::mutating_once()` is the default for Create/UpdateSpec/Delete; tests to adapt/import: `client.rs:metadata_retries_and_cancellation_use_canonical_driver`, `mutating_retries_require_stable_idempotency`, `concurrent_named_streams_route_events_without_cross_consumption`, `named_stream_grants_only_consumed_data_and_releases_blocked_sender`; excluded ADR 0045 assumptions: `TargetInput::Workload/Realm/Provider` routing variants; `GuestClient` cross-realm proxy routing; old `DeploymentProjection`/`RuntimeProjection` ADR 0045-specific field types", - "reuseAction": "copy-then-adapt", - "destination": "`packages/d2b/src/resource.rs` (standard `d2b get/list/watch/create/update-spec/delete/status` top-level verbs)", - "detailedDesign": "Generic typed dispatch to resource API Get/List/Watch/Create/UpdateSpec/Delete using `ConnectedClient::invoke` (Get/List/Create/UpdateSpec/Delete) and `ConnectedClient::open_server_stream` + `NamedStream` (Watch); ResourceRef argument parsing and validation; page token pagination; `--phase`/`--label-selector` filters; `--deadline` bounded by `MAX_REQUEST_LIFETIME_MS=900s` via `DeadlineBudget`; Watch output streams resource events as JSON lines; JSON schema version field; `CancellationToken` wired to process signal handlers. Excluded: `GuestClient` vsock exec/shell routing; `TargetInput` realm/workload/provider variants.", - "integration": "ZoneContext → `ConnectedClient` → resource API", - "dataMigration": "None — full d2b 3.0 reset; no prior state to migrate", - "validation": "Get/list/watch/create/update-spec/delete tests per ResourceType; pagination/filter/watch-deadline tests; error-class/exit-code tests; adapted `client.rs:metadata_retries_*` and `mutating_retries_*` and `concurrent_named_streams_*` tests", - "removalProof": "Not applicable (new surface)" - }, - { - "workItemId": "ADR046-cli-011", - "specId": "ADR-046-cli-and-operations", - "specPath": "docs/specs/ADR-046-cli-and-operations.md", - "dependencyOwner": "ADR046-identities-002, ADR046-cli-001, ADR046-cli-002, ADR046-cli-007; Nix module owner + Zone runtime owner", - "currentSource": "Nix emitters: `nixos-modules/options-realms-workloads.nix` (current `d2b.envs..vms..*`), `nixos-modules/options-realms.nix` (`d2b.realms.*`), `nixos-modules/unsafe-local-workloads-json.nix` (unsafe-local source), `nixos-modules/bundle-artifacts.nix`, `nixos-modules/manifest.nix`, `nixos-modules/assertions.nix`; JSON output: `/etc/d2b/processes.json` (old bundle), `/etc/d2b/realm-entrypoints.json` (static realm index); Zone runtime apply path: `packages/d2bd/src/` (activation apply handler — pre-ADR 0046 path through `cmd_host_prepare`/broker; no live resource bundle apply); cleanup: no current resource-deletion-on-bundle-apply path at baseline", - "reuseSource": "None (new implementation; no main `a1cc0b2d` reuse — this is the Nix/Zone side, not the CLI client side)", - "reuseAction": "replace", - "destination": "Nix: `nixos-modules/options-zones.nix` (unified `d2b.zones..resources` attrset; per-type `spec` sub-options generated from ResourceTypeSchema/Provider schema), `nixos-modules/bundle-emit.nix` (canonical JSON emit + SHA256 pin), `nixos-modules/assertions.nix` (updated); core controller: `packages/d2b-core-controller/src/configuration.rs`, `packages/d2b-core-controller/src/cleanup.rs`; Contracts: `packages/d2b-contracts/src/zone_bundle.rs` (new)", - "detailedDesign": "**Nix shape:** `d2b.zones..resources` is `attrsOf (submodule { type; optional metadata { ownerRef; labels; annotations }; spec })`. `spec` sub-options per `type` are generated from ResourceTypeSchema and signed Provider schemas; field names remain identical. `metadata.name`/`metadata.zone`/`apiVersion` are derived; status and all core metadata are rejected in input. Vendor-qualified types are admitted only when their schema is installed. **Nix emit:** `bundle-emit.nix` emits `/etc/d2b/zones//resource-bundle.json` plus its integrity pin with canonical resource ordering and schema digests. **Core-controller apply:** `configuration.rs` verifies bundle/catalog integrity, applies Create/Update/no-op intents with bounded async concurrency, refreshes `configurationGeneration` for unchanged configuration-managed resources without waking their controller, handles controller/API name collisions per-item without seizing them, and asynchronously deletes only persisted `managedBy=configuration` resources absent from the new configured set. `cleanup.rs` consumes `Deleted` revision watches and maintains `PendingCleanup`; it never force-removes finalizers. **Prior generation retention:** `d2b.zones..retainedGenerations`, default 3 and range 1–16, is a compiler setting outside `Zone.spec`; no TTL. Rollback reapplies a retained bundle as a new higher generation.", - "integration": "Nix build → per-Zone `resource-bundle.json` + global private artifact catalog → `d2b activation switch` → `d2b-core-controller` configuration service → resource API Create/Update/Delete → owner controllers → finalizer cascade → cleanup watcher → Zone status update", - "dataMigration": "Full reset from current manifest/processes/realm-entrypoints JSON format; prior Nix-generated artifacts (`/etc/d2b/processes.json`, `/etc/d2b/realm-entrypoints.json`) deleted after Zone resource bundle activates", - "validation": "Runtime integration: all CLI-visible cleanup/status/rollback/gc/audit tests (§CLI-visible tests for activation and cleanup), including no force-finalizer path; Nix unit and build tests owned by ADR-046-nix-configuration spec", - "removalProof": "Old `nixos-modules/manifest.nix`, `nixos-modules/bundle-artifacts.nix` emitters removed only after `bundle-emit.nix` produces equivalent-or-superseding output and all downstream consumers of the old bundle format are migrated" - }, - { - "workItemId": "ADR046-cli-012", - "specId": "ADR-046-cli-and-operations", - "specPath": "docs/specs/ADR-046-cli-and-operations.md", - "dependencyOwner": "ADR046-cli-001, ADR046-cli-010; CLI crate owner", - "currentSource": "None — net-new v3 work; `Endpoint` is a new standard ResourceType added by D092; no pre-ADR45 baseline equivalent", - "reuseSource": "main `a1cc0b2d` — copy/adapt: (1) `packages/d2b-client/src/client.rs` `ConnectedClient::invoke()` and `ConnectedClient::open_server_stream()` — copy unchanged; used for Endpoint Get/List/Status and Watch; (2) `packages/d2b-client/src/session.rs` `NamedStream` — copy unchanged; Watch stream output; (3) the ADR046-cli-010 generic resource-verb dispatch — reuse for `d2b endpoint get/list/watch/status`; the `d2b endpoint resolve` projection verb is net-new. No raw transport/FD handle type is imported into the CLI; resolution to a private transport/FD remains inside Core/ProviderSupervisor via EffectPort/LaunchTicket", - "reuseAction": "adapt", - "destination": "`packages/d2b/src/endpoint.rs` (`d2b endpoint get/list/watch/status/resolve`)", - "detailedDesign": "Add `Endpoint` to the frozen standard ResourceType set (17 types; local ResourceRef parser accepts `Endpoint/` with no Zone round-trip). Implement `d2b endpoint get/list/watch/status` over the ADR046-cli-010 generic verbs and `d2b endpoint resolve ` as a provider-neutral resolution projection printing `producerRef`, `endpointClass`, transport class, readiness, and capability/locality observations. Endpoint output is the base envelope only (base spec + base status per D092); any `status.provider` projection is bounded, redacted, and deny-unknown. **No raw locator** (path/address/CID/port/fd/credential) appears in any CLI field or the `resolve` projection. `status.update` currency and `d2b upgrade Endpoint/` visibility follow the standard verbs and D091. `create`/`update-spec`/`delete` are rejected on the operator surface except for statically Nix/API-authored Endpoints whose schema permits it.", - "integration": "ZoneContext → `ConnectedClient` → resource API for `Endpoint`; consumers reference endpoints only through `Endpoint/` ResourceRefs", - "dataMigration": "None — full d2b 3.0 reset; no prior state to migrate", - "validation": "`Endpoint` accepted as a frozen standard type; `Endpoint/` parses locally without a Zone round-trip; `d2b endpoint get/list/watch/status/resolve` parse and route correctly; `get`/`resolve` output carries no raw path/address/CID/port/fd/credential; provider status projection is bounded/redacted; `d2b list Endpoint --updates` and `d2b upgrade Endpoint/` expose `status.update` currency and disruption; operator `create`/`update-spec`/`delete` rejected for controller-owned Endpoints", - "removalProof": "Not applicable (new surface)" - }, - { - "workItemId": "ADR046-cli-013", - "specId": "ADR-046-cli-and-operations", - "specPath": "docs/specs/ADR-046-cli-and-operations.md", - "dependencyOwner": "ADR046-cli-001, ADR046-cli-010, ADR046-zone-control-019; CLI crate owner", - "currentSource": "None — net-new v3 work; `ResourceExport`/`ResourceImport` are new standard ResourceTypes added by D096; no pre-ADR45 baseline equivalent", - "reuseSource": "ADR046-cli-010 generic resource-verb dispatch; `packages/d2b-client/src/client.rs` `ConnectedClient::invoke`/`open_server_stream` — copy unchanged", - "reuseAction": "adapt", - "destination": "`packages/d2b/src/share.rs` (`d2b export …` and `d2b import …` nouns)", - "detailedDesign": "Add `ResourceExport` and `ResourceImport` to the frozen 19-type standard set while leaving qualified semantic/provider-neutral Service/Binding types dynamically cataloged. Implement export/import generic verbs, `d2b import projection`, and `d2b import graph`. Export accepts only a factory-bound owner Service. Import status exposes the same-semantic-type projection Service and verified factory/schema fingerprints; local `providerRef` selects the implementation. Graph renders import → projection Service → authored Bindings → controller-owned Process/Endpoint children with deterministic ownership/dependency edges. Binding spec is desired intent only and observations are status-only. Reject Device/Endpoint/Binding export targets, cross-Zone refs, absent/mismatched factory/type/fingerprints, implementation detail in the base, forbidden Providers, USB policy denial, unauthorized Zones, and capability-ceiling violations. No backing/remote refs, internal sessions/streams, FD, secret, path, locator, or bytes in output. Delete waits for authored Bindings to be removed/retargeted and never cascades them.", - "integration": "ZoneContext → resource API; signed Provider factory catalog; owner/dependency indexes for graph rendering; consumers use authored Binding → local projection Service, never ResourceImport directly", - "dataMigration": "None — full d2b 3.0 reset; no prior state to migrate", - "validation": "Both standard types parse locally; qualified semantic Service/Binding resolve dynamically; verbs route; Service-only export and factory/type/fingerprint/policy/capability rejections; projection preserves semantic Service type across local implementation selection; canonical minimal base works without `spec.provider`; graph shape exact and bounded; Binding never auto-created/deleted; Binding spec intent-only/status observations; no implementation detail/backing/remote-ref/session/stream/FD/secret/path/locator/bytes leakage", - "removalProof": "Not applicable (new surface)" - }, - { - "workItemId": "ADR046-client-001", - "specId": "ADR-046-resources-zone-control", - "specPath": "docs/specs/ADR-046-resources-zone-control.md", - "dependencyOwner": "ADR046-zone-control-011, ADR046-zone-control-012, ADR046-zone-control-013, ADR046-zone-control-018", - "currentSource": "main `a1cc0b2d`: `packages/d2b-client/src/client.rs` (`WallClock`, `MetadataInput`, `RetryPolicy` 1..8 attempts, `CallOptions`, `CancellationToken`, `Client::new/with_clock/connect`, `ConnectedClient` methods incl. `session_generation/session_limits/service/invoke/invoke_with_attachments/named_stream/open_server_stream`, `prepare_typed_request/prepare_operation_context`, `can_retry/retryable_failure/validate_outbound_attachments/validate_reply/service_package/map_remote_kind/map_retry` — lines 35–921); `packages/d2b-client/src/session.rs` (`ComponentSessionConnector` trait, `NamedStream` lifecycle, `StreamDispatcher`, `SharedDriver`, aggregate-queue-bound test — lines 24–626); `packages/d2b-client/src/target.rs` (`ServiceOwner`, `TargetInput`, `TransportKind`, `RouteRecord`, `ResolvedTarget`, `TargetResolver`, `RouteTable` — lines 7–228); `packages/d2b-client/src/service.rs` (`ServiceKind`, `GeneratedClient`, `MethodHandle`, `ServiceHandle::new/kind/generated/proxy/method/invoke` — lines 21–184); `packages/d2b-client/src/daemon_service.rs` (`DaemonClient::new/session_generation/connected/resolve/inspect/lifecycle/open_terminal`, `DaemonTerminal`, `daemon_call_options`, `ensure_daemon_outcome`, `map_ttrpc_error`, test `redacted_terminal_debug_payload` — lines 29–689); `packages/d2b-client/src/host_socket.rs` (`HostSocketConnector::new/from_seqpacket_fd`, `local_daemon_endpoint_identity`, `ComponentSessionConnector::connect` — lines 252–383); `packages/d2b-client/src/error.rs` (`RemoteErrorKind`, `RetryClass`, `ClientError` — lines 5–128)", - "reuseSource": null, - "reuseAction": "copy + adapt", - "destination": "`packages/d2b-client/src/` (updated for v3 Zone API, replacing ADR45 daemon verbs with Zone resource operations)", - "detailedDesign": "**Selected**: `Client::connect()` target-resolve → ComponentSession-open → `ConnectedClient` lifecycle; `RetryPolicy` 1..8 bound + `retryable_failure()` safe-only retry detection; `NamedStream::send/receive/close_local/reset` lifecycle; `ComponentSessionConnector` trait as connector abstraction; `HostSocketConnector::from_seqpacket_fd` + `local_daemon_endpoint_identity` for Zone runtime socket connector; `RouteTable` ambiguous-route rejection; `ServiceHandle`/`GeneratedClient`/`MethodHandle` typed service client pattern; `map_ttrpc_error`/`validate_reply`/`map_retry` error-handling chain; `ClientError`/`RemoteErrorKind`/`RetryClass` error taxonomy; `DaemonClient` call-options and outcome helpers (infrastructure only). **Excluded ADR45 assumptions**: `DaemonMethod` enum (lines 29–56 of daemon_service.rs): ADR45 daemon verbs (`vm_start`, `vm_stop`, `list_realms`, etc.) — replaced with Zone API verbs. `GuestClient`/`guest_service.rs`: ADR45 guest operations; excluded until v3 Guest transport work item. Hardcoded socket path `PUBLIC_SOCKET_PATH = \"/run/d2b/public.sock\"` in `host_socket.rs`: replaced by Zone-resource-managed path. `TransportKind::LocalUnix` restriction in daemon-access: v3 allows multiple transport kinds per ZoneLink binding.", - "integration": "`d2b` CLI uses `d2b-client` to connect to Zone runtime via `HostSocketConnector`; Zone runtime uses `d2b-client` for internal child-Zone ZoneLink connections; Provider toolkit conformance tests use `Fixture`/`FakeProvider` with `d2b-client` service handles", - "dataMigration": "Not applicable; updated in place", - "validation": "`client-retry-policy-max-8-attempts-enforced`, `client-named-stream-close-local-then-remote-close-transitions-closed`, `client-route-table-ambiguous-route-rejected`, `client-host-socket-peer-uid-verified-on-connect`, `client-retryable-failure-only-safe-mutations`", - "removalProof": "`DaemonMethod` v2 verb enum retired after all v2 daemon operations migrated to Zone API" - }, - { - "workItemId": "ADR046-clipboard-001", - "specId": "ADR-046-provider-clipboard-wayland", - "specPath": "docs/specs/providers/ADR-046-provider-clipboard-wayland.md", - "dependencyOwner": "ADR-046-provider-model-and-packaging; Provider/clipboard-wayland crate owner", - "currentSource": "None — net-new v3 work; no pre-ADR45 baseline equivalent for the provider crate skeleton", - "reuseSource": null, - "reuseAction": "net-new", - "destination": "packages/d2b-provider-clipboard-wayland/ with src, tests, integration, README.md, and binaries clipboard-controller, clipd-host, picker-session", - "detailedDesign": "Create the provider crate skeleton, required source layout, three binaries, and README covering purpose, component map, local build instructions, test commands, and display-wayland fake dependency for integration tests.", - "integration": "Workspace package manifest and Provider artifact catalog consume the crate; provider packaging registers component templates for core ProviderDeployment.", - "dataMigration": "None — docs/tooling only; no runtime state", - "validation": "Workspace provider layout policy plus README content review and follow-on make test-rust -p d2b-provider-clipboard-wayland once implementation exists.", - "removalProof": "None — net-new; no prior owner to remove" - }, - { - "workItemId": "ADR046-clipboard-002", - "specId": "ADR-046-provider-clipboard-wayland", - "specPath": "docs/specs/providers/ADR-046-provider-clipboard-wayland.md", - "dependencyOwner": "ADR046-clipboard-001; clipd-host service owner", - "currentSource": "packages/d2b-clipd/ algorithms and types listed in the Reuse from baseline section", - "reuseSource": null, - "reuseAction": "port and adapt algorithms; replace direct compositor, picker subprocess, Unix bridge, SO_PEERCRED, bridge directory, and group ACL paths", - "destination": "packages/d2b-provider-clipboard-wayland/src/clipd_host/ service binary modules such as service, display_client, bridge, picker_coord, policy, fd, audit, history", - "detailedDesign": "Adapt d2b-clipd into clipd-host: call RequestPickerSession over d2b.clipboard.picker-coord.v3, consume d2b.display.host-clipboard.v3 instead of WAYLAND_DISPLAY or NIRI_SOCKET, serve d2b.clipboard.bridge.v3 on Endpoint/clipboard-bridge, remove filesystem bridge and peer group ACL logic, and preserve MIME allowlist, FD safety, fail-closed audit, loop suppression, and LRU history algorithms.", - "integration": "Core creates Process/clipd-host; display-wayland wayland-proxy consumes Endpoint/clipboard-bridge; clipd-host consumes display-wayland host-clipboard service and controller picker coordination service.", - "dataMigration": "Full d2b 3.0 reset; clipboard history is bounded process memory and no v2 clipboard runtime state is imported", - "validation": "make test-rust -p d2b-provider-clipboard-wayland plus unit coverage for MIME policy, FD safety, audit fail-closed queue, history bounds, lifecycle purge and suspension, no filesystem bridge, and no bytes in status or audit.", - "removalProof": "Baseline picker subprocess, direct compositor/Niri clients, Unix bridge socket server, bridge directories, SO_PEERCRED peer config, and per-Guest groups are absent from the provider crate and covered by invariant tests." - }, - { - "workItemId": "ADR046-clipboard-003", - "specId": "ADR-046-provider-clipboard-wayland", - "specPath": "docs/specs/providers/ADR-046-provider-clipboard-wayland.md", - "dependencyOwner": "ADR046-clipboard-001; clipboard-controller owner", - "currentSource": "None — net-new v3 controller; no pre-ADR45 baseline equivalent", - "reuseSource": null, - "reuseAction": "net-new controller using existing resource API and ComponentSession contracts", - "destination": "packages/d2b-provider-clipboard-wayland/src/controller/ and clipboard-controller binary", - "detailedDesign": "Implement Process/clipboard-controller as a system-domain system-minijail Process serving d2b.clipboard.picker-coord.v3, creating picker EphemeralProcesses from the signed picker-session template, observing picker status, relaying Guest lifecycle messages to clipd-host, creating clipboard RBAC Roles and RoleBindings, writing only bounded redacted operational observations through the optimistic status writer, and never owning or mounting Provider state Volumes.", - "integration": "Core ProviderDeployment creates the controller Process; controller uses Zone resource API for EphemeralProcess and RBAC resources and ComponentSession to clipd-host for picker and purge or suspend coordination.", - "dataMigration": "Full d2b 3.0 reset; no controller durable state import because ProviderStateSet is empty", - "validation": "Controller unit tests for picker request validation, EphemeralProcess spec shape, terminal status callback, GuestStopped and GuestLocked handling, RBAC resources, status bounds, and empty ProviderStateSet.", - "removalProof": "None — net-new; no prior controller owner to remove" - }, - { - "workItemId": "ADR046-clipboard-004", - "specId": "ADR-046-provider-clipboard-wayland", - "specPath": "docs/specs/providers/ADR-046-provider-clipboard-wayland.md", - "dependencyOwner": "ADR046-clipboard-001 and ADR046-clipboard-003; picker worker owner", - "currentSource": "packages/d2b-clipd/src/picker.rs subprocess flow is replacement context only; picker-session worker is net-new", - "reuseSource": null, - "reuseAction": "rewrite as EphemeralProcess worker", - "destination": "packages/d2b-provider-clipboard-wayland/src/picker_session/ and picker-session binary", - "detailedDesign": "Implement picker-session as a user-domain worker EphemeralProcess with minimal environment, metadata over inherited ComponentSession named stream, restricted WAYLAND_SOCKET FD from display-wayland presentation portal, GTK4 closure-contained runtime, one Select or Cancel response, no clipboard FDs, no clipboard bytes, no compositor credentials, no socket paths, and typed PickerStartFailed on install or start failure instead of bypass.", - "integration": "clipboard-controller creates picker EphemeralProcess per paste request; ProviderSupervisor pre-opens restricted Wayland FD; picker returns result to controller which notifies clipd-host.", - "dataMigration": "Full d2b 3.0 reset; picker state is per-operation EphemeralProcess status only", - "validation": "Contract test that picker cannot bind zwlr_data_control_manager_v1 plus unit tests for processClass worker, no FDs or payload in picker config, TTL defaults, response framing, and requirePickerForPaste false bypass semantics in clipd-host.", - "removalProof": "Old d2b-clipd subprocess picker path is absent once RequestPickerSession and picker EphemeralProcess tests pass." - }, - { - "workItemId": "ADR046-clipboard-005", - "specId": "ADR-046-provider-clipboard-wayland", - "specPath": "docs/specs/providers/ADR-046-provider-clipboard-wayland.md", - "dependencyOwner": "ADR046-clipboard-001; ADR-046-componentsession-and-bus; clipboard service contract owner", - "currentSource": "None — net-new clipboard-wayland ComponentSession service definitions; display-wayland client stubs are consumed from the display-wayland contract", - "reuseSource": null, - "reuseAction": "net-new generation of service stubs and named-stream types", - "destination": "packages/d2b-provider-clipboard-wayland service descriptors and generated Rust async ttrpc bindings, plus any shared contracts crate selected by ADR-046-componentsession-and-bus", - "detailedDesign": "Generate stubs for d2b.clipboard.bridge.v3, d2b.clipboard.picker-coord.v3, and d2b.clipboard.v3, consume display-wayland d2b.display.host-clipboard.v3 client stubs, reject service-name collisions, and declare attachment classes clipboard-transfer-fd, host-selection-transfer-fd, and host-selection-supply-fd in the signed descriptor for ComponentSession handshake validation.", - "integration": "Service registry, Zone ComponentSession enrollment, clipd-host, clipboard-controller, display-wayland wayland-proxy, and CLI/operator clients all consume the generated bindings.", - "dataMigration": "None — docs/tooling only; no runtime state", - "validation": "Contract tests for wire format, service-name collision rejection, attachment class matching, and descriptor handshake validation.", - "removalProof": "Shared filesystem bridge path and SO_PEERCRED contract tests are removed in ADR046-clipboard-011 after ComponentSession contracts pass." - }, - { - "workItemId": "ADR046-clipboard-006", - "specId": "ADR-046-provider-clipboard-wayland", - "specPath": "docs/specs/providers/ADR-046-provider-clipboard-wayland.md", - "dependencyOwner": "ADR-046-nix-configuration; Provider/clipboard-wayland Nix owner", - "currentSource": "nixos-modules/clipboard.nix and current d2b.clipboard.* options", - "reuseSource": null, - "reuseAction": "replace option surface with Provider resource Nix module", - "destination": "nixos-modules/providers/clipboard-wayland.nix and d2b.artifacts.clipboard-wayland catalog entry", - "detailedDesign": "Implement Nix module emitting d2b.zones..resources. Provider resources with spec.artifactId and spec.config, validate hostExecutionRef, hostUserRef, displayWaylandRef, and pickerArtifactId, forbid spec.componentPlacements, spec.settings, and spec.status, and remove nixos-modules/clipboard.nix in the same landing sequence as the new module.", - "integration": "Nix resource compiler emits Provider resource and artifact catalog data consumed by core configuration publication and ProviderDeployment.", - "dataMigration": "Full d2b 3.0 reset; operators translate old d2b.clipboard.* options using the dossier mapping table", - "validation": "Nix eval tests for resource shape, reference validation, null displayWaylandRef host-only mode, artifact catalog lookup, and absence of deprecated spec fields.", - "removalProof": "nixos-modules/clipboard.nix import is removed and examples/static checks no longer reference old d2b.clipboard.* options." - }, - { - "workItemId": "ADR046-clipboard-007", - "specId": "ADR-046-provider-clipboard-wayland", - "specPath": "docs/specs/providers/ADR-046-provider-clipboard-wayland.md", - "dependencyOwner": "ADR046-clipboard-003; clipboard RBAC owner", - "currentSource": "None — net-new Zone RBAC resources for clipboard-wayland; no pre-ADR45 baseline equivalent", - "reuseSource": null, - "reuseAction": "net-new", - "destination": "packages/d2b-provider-clipboard-wayland/src/controller/rbac.rs or equivalent controller reconcile module", - "detailedDesign": "Controller creates Role/clipboard-admin, Role/clipboard-viewer, Role/clipboard-bridge-peer, Role/clipboard-picker-worker and RoleBindings display-wayland-bridge, host-admin-clipboard, picker-session-worker, all Zone-scoped, owned by Process/clipboard-controller, selector-bound for Process/picker-*, and cleaned up when Provider is deleted.", - "integration": "Resource API stores RBAC resources; ComponentSession authorization checks consume Roles and RoleBindings for management, bridge, and picker worker services.", - "dataMigration": "Full d2b 3.0 reset; no v2 RBAC state import", - "validation": "Controller RBAC reconcile tests for create, idempotent update, provider deletion cleanup, selector scoping, bridge peer authorization, and denied unauthorized management calls.", - "removalProof": "None — net-new; no prior owner to remove" - }, - { - "workItemId": "ADR046-clipboard-008", - "specId": "ADR-046-provider-clipboard-wayland", - "specPath": "docs/specs/providers/ADR-046-provider-clipboard-wayland.md", - "dependencyOwner": "ADR046-clipboard-002; ADR-046-telemetry-audit-and-support; clipboard observability owner", - "currentSource": "packages/d2b-clipd/src/audit.rs and policy types from packages/d2b-clipd/src/policy.rs", - "reuseSource": null, - "reuseAction": "port and adapt audit plus metrics with zone names and redaction changes", - "destination": "packages/d2b-provider-clipboard-wayland/src/service/audit.rs and packages/d2b-provider-clipboard-wayland/src/service/metrics.rs", - "detailedDesign": "Implement ClipboardAuditEvent and fail-closed Zone audit queue by porting baseline audit code, renaming realm fields to source_zone_id and dest_zone_id, making ReasonCode a closed enum with unknown protobuf fields rejected, replacing exact byte counts with SizeBucket, emitting to d2b.audit.v3, and adding closed-label OTEL metrics and spans from the dossier tables.", - "integration": "clipd-host emits audit events to the Zone audit sink and OTEL metrics/spans to the observability Provider pipeline during clipboard operations.", - "dataMigration": "Full d2b 3.0 reset; audit stream is v3 Zone-local and no v2 audit records are imported", - "validation": "Audit tests for no bytes in events, closed ReasonCode deserialization, fail-closed queue rejection, SizeBucket discretization, allowed metric labels, and excluded span attributes.", - "removalProof": "Old audit shape with realm field names and exact byte counts is absent after ported tests assert the v3 ClipboardAuditEvent schema." - }, - { - "workItemId": "ADR046-clipboard-009", - "specId": "ADR-046-provider-clipboard-wayland", - "specPath": "docs/specs/providers/ADR-046-provider-clipboard-wayland.md", - "dependencyOwner": "ADR046-clipboard-001 through ADR046-clipboard-008; clipboard test owner", - "currentSource": "packages/d2b-clipd/ behavior and this dossier invariants; no single baseline test path is declared for every assertion", - "reuseSource": null, - "reuseAction": "extract semantic assertions into hermetic provider tests", - "destination": "packages/d2b-provider-clipboard-wayland/tests/", - "detailedDesign": "Create hermetic unit and Cargo integration tests covering closed MIME policy, secret-hint suppression, FD validation and bounds, LRU and TTL history, fail-closed audit, lifecycle purge and suspend, picker EphemeralProcess invariants, no filesystem bridge, core-created Processes, empty ProviderStateSet, no state mounts or state-layout principals, status-first observation, and no clipboard bytes in status, audit, metrics, Operations, or Volumes.", - "integration": "cargo test -p d2b-provider-clipboard-wayland --lib --tests consumes the provider crate and fake clocks/effect ports without live Wayland, systemd, broker, or Nix eval.", - "dataMigration": "None — docs/tooling only; no runtime state", - "validation": "All tests listed in the Required test coverage table must pass under make test-rust -p d2b-provider-clipboard-wayland.", - "removalProof": "Replaced current-code tests receive explicit keep/adapt/move/delete dispositions and old duplicate tests are deleted once successor coverage passes." - }, - { - "workItemId": "ADR046-clipboard-010", - "specId": "ADR-046-provider-clipboard-wayland", - "specPath": "docs/specs/providers/ADR-046-provider-clipboard-wayland.md", - "dependencyOwner": "ADR046-clipboard-009; display-wayland fake and clipboard integration owner", - "currentSource": "None — net-new v3 provider integration scenarios; no pre-ADR45 baseline equivalent", - "reuseSource": null, - "reuseAction": "net-new integration suite with fake display-wayland and fake wayland-proxy", - "destination": "packages/d2b-provider-clipboard-wayland/integration/", - "detailedDesign": "Implement e2e paste, host capture, bridge backpressure, rate limiting, echo suppression, dependency absent host-only mode, GuestDestroyed purge, audit fail-closed, picker start timeout, and cross-zone denied scenarios using fake d2b.display.host-clipboard.v3 server and fake wayland-proxy bridge client without requiring a live compositor.", - "integration": "Provider integration lane exercises clipd-host, clipboard-controller, generated ComponentSession services, fake display-wayland service, and fake bridge client end-to-end.", - "dataMigration": "None — docs/tooling only; no runtime state", - "validation": "All integration scenarios in the dossier table pass and assert no live Wayland compositor dependency.", - "removalProof": "None — net-new; no prior owner to remove" - }, - { - "workItemId": "ADR046-clipboard-011", - "specId": "ADR-046-provider-clipboard-wayland", - "specPath": "docs/specs/providers/ADR-046-provider-clipboard-wayland.md", - "dependencyOwner": "ADR046-clipboard-005; packages/d2b-contract-tests owner", - "currentSource": "packages/d2b-contract-tests/tests/policy_clipboard.rs", - "reuseSource": null, - "reuseAction": "adapt contract tests and delete obsolete filesystem bridge assumptions", - "destination": "packages/d2b-contract-tests/tests/policy_clipboard.rs", - "detailedDesign": "Add contract tests for d2b.clipboard.bridge.v3 and d2b.clipboard.picker-coord.v3 wire formats, ReasonCode numeric stability, and attachment class descriptor names while removing tests that assume shared filesystem bridge paths or SO_PEERCRED config.", - "integration": "Contract test suite consumes generated service descriptors and guards downstream ComponentSession consumers.", - "dataMigration": "None — docs/tooling only; no runtime state", - "validation": "packages/d2b-contract-tests policy_clipboard.rs passes with v3 wire format and attachment descriptor assertions.", - "removalProof": "Tests for shared filesystem bridge paths and SO_PEERCRED config are removed from policy_clipboard.rs after v3 ComponentSession contract coverage lands." - }, - { - "workItemId": "ADR046-clipboard-012", - "specId": "ADR-046-provider-clipboard-wayland", - "specPath": "docs/specs/providers/ADR-046-provider-clipboard-wayland.md", - "dependencyOwner": "ADR046-clipboard-006; Nix/module removal owner", - "currentSource": "nixos-modules/clipboard.nix and its import in nixos-modules/default.nix", - "reuseSource": null, - "reuseAction": "delete superseded module and update migration docs", - "destination": "nixos-modules/default.nix, docs/how-to/ migration guide, tests/static.sh example iteration expectations, CHANGELOG.md", - "detailedDesign": "After the new Provider Nix module validates, delete nixos-modules/clipboard.nix, remove its default.nix import, update migration guide with the option mapping table, ensure tests/static.sh examples no longer rely on removed paths, and add an Unreleased changelog entry.", - "integration": "Nix module aggregator, docs migration flow, static example iteration, and changelog all reflect Provider/clipboard-wayland as the only clipboard surface.", - "dataMigration": "Full d2b 3.0 reset; no v2 clipboard runtime state or option import", - "validation": "New module eval tests pass and tests/static.sh example iteration has no references to removed option paths.", - "removalProof": "nixos-modules/clipboard.nix is deleted, default.nix import removed, and grep/static checks show no old d2b.clipboard option references in examples." - }, - { - "workItemId": "ADR046-core-001", - "specId": "ADR-046-core-controllers", - "specPath": "docs/specs/ADR-046-core-controllers.md", - "dependencyOwner": "W0/W1a; core-controller owner", - "currentSource": "`packages/d2bd/src/lib.rs`, `provider_registry.rs` if present on source ref, supervisor state, operations; `d2b-realm-core/src/{allocator_engine,identity_store}.rs`", - "reuseSource": "Useful pure handler/toolkit code from main named in implementation sub-items", - "reuseAction": "extract and adapt", - "destination": "`packages/d2b-core-controller/src/{main,configuration,api_catalog,authz,providers,controllers,ownership,watches,cleanup,zone_links,budgets,store}.rs`", - "detailedDesign": "One fixed process, isolated handlers, async ResourceClient, health/startup/restart", - "integration": "Zone runtime local bus/session; Provider/system-core resource identity", - "dataMigration": "Full reset", - "validation": "Per-handler unit/property tests plus multi-process startup/restart", - "removalProof": "Current daemon branches removed after handler/Provider parity" - }, - { - "workItemId": "ADR046-core-002", - "specId": "ADR-046-core-controllers", - "specPath": "docs/specs/ADR-046-core-controllers.md", - "dependencyOwner": "ADR046-core-001; system-core Provider owner", - "currentSource": "Nix host/Realm options/index; host check/provider code; user/group lookup and unsafe-local eligibility", - "reuseSource": "Any main local-host/user-provider code selected by exact sub-items", - "reuseAction": "extract/adapt", - "destination": "`packages/d2b-provider-system-core/src/{host,user}.rs` linked into fixed core controller", - "detailedDesign": "Host and User schemas/reconcile/status/capabilities", - "integration": "Bootstrap Provider/system-core; other controllers target Host/Guest/User refs", - "dataMigration": "New v3 resources from Nix", - "validation": "Multiple Hosts, system/user restrictions, UID/session drift", - "removalProof": "Host grouping/user helper policy removed only after resource parity" - }, - { - "workItemId": "ADR046-cred-entra-001", - "specId": "ADR-046-provider-credential-entra", - "specPath": "docs/specs/providers/ADR-046-provider-credential-entra.md", - "dependencyOwner": "`ADR046-credential-001` (Credential base fields and status), `ADR046-credential-002` (`d2b.credential.v3` service), D092 Endpoint, D093 Entrablau identity Guest decision, credential-entra owner", - "currentSource": "`d2b-realm-provider/src/credential.rs:OpaqueAzureRef` remains a bounded opaque identifier reuse source only; no Host login/token implementation is retained", - "reuseSource": null, - "reuseAction": "Adapt tests/types where non-secret; replace production token acquisition with Entrablau Endpoint client", - "destination": "`packages/d2b-provider-credential-entra/src/{lib.rs,controller.rs,service.rs,controller_main.rs,agent_main.rs,audit.rs,telemetry.rs}` and corresponding tests/integration docs", - "detailedDesign": "Implement secret-free controller/helper; require Credential base `identityGuestRef` and `loginEndpointRef`; resolve dependency alias `entra-login-token`; validate same-Zone Guest placement and exact `consumerRef`; implement typed `EntraTokenLeaseClient` whose production implementation is the Entrablau Endpoint; implement `BeginLogin`/`ObserveLogin`/`CancelLogin` projection to Credential base interaction status; route access-token leases end-to-end from Entrablau service to exact consumer over Noise_KK; keep all refresh/login/TPM state inside the Entrablau Guest; declare no Provider state Volume; reject Host placement and all ambient fallback chains.", - "integration": "Consumer composes `inputs.entrablau.nixosModules.default` into the identity Guest; sibling package declares login/token Process and Endpoint; d2b Credential resource binds `identityGuestRef`, `loginEndpointRef`, and `consumerRef`", - "dataMigration": "Full v3 reset of d2b Credential metadata; Entrablau Guest state is preserved unless explicitly destroyed by the sibling-owned reset flow", - "validation": "See §13", - "removalProof": "Old abstract Host credential paths and any direct Entra effect client are deleted only after the Entrablau Endpoint-backed provider passes the test matrix" - }, - { - "workItemId": "ADR046-cred-mi-001", - "specId": "ADR-046-provider-credential-managed-identity", - "specPath": "docs/specs/providers/ADR-046-provider-credential-managed-identity.md", - "dependencyOwner": "`ADR046-credential-001` (contracts), `ADR046-credential-002` (service); `ADR046-reconcile-001`; `ADR046-mi-topology-001`; `credential-managed-identity` crate owner", - "currentSource": "`packages/d2b-realm-provider/src/credential.rs:ManagedIdentityRef` (reachable); `packages/d2b-provider-aca/src/lib.rs:managed_identity_client_id` line 112 (reachable ACA config); `packages/d2bd/src/lib.rs:managed_identity_client_id` lines 3960, 4173 (reachable)", - "reuseSource": "main `a1cc0b2d`: `packages/d2b-provider-credential-managed-identity/src/lib.rs` (full implementation); `src/tests.rs` (full test suite)", - "reuseAction": "copy and adapt", - "destination": "`packages/d2b-provider-credential-managed-identity/src/{lib.rs, controller.rs, agent.rs, service.rs, audit.rs, telemetry.rs}`; `packages/d2b-provider-credential-managed-identity/{controller/main.rs, agent/main.rs}`; `packages/d2b-provider-credential-managed-identity/tests/{lifecycle.rs, conformance.rs, faults.rs, canary.rs, delivery.rs, placement.rs, topology.rs}`; `packages/d2b-provider-credential-managed-identity/integration/{container-service.sh, host-guest-placement.nix, aca-credential-ref.sh, cleanup-rollback.sh}`; `packages/d2b-provider-credential-managed-identity/README.md`", - "detailedDesign": "(1) Adapt `ManagedIdentityCredentialProvider` to `d2b.credential.v3` service interface; split controller and agent roles (see `ADR046-mi-topology-001`). (2) Enforce `ManagedIdentityCredentialOwner::ExactSdkConsumer` in agent via `AuthenticatedSubjectContext` from ComponentSession, independently of scope fields. (3) Reject `user-agent` placement: `scope.domainFilter=user` returns `credential-placement-mismatch` before agent spawn. (4) Validate `clientId` using `OpaqueAzureRef::parse` from v3 baseline; artifact IDs match `^[a-z][a-z0-9-]*$`. (5) Validate `imdsEndpointAlias` against closed enum `{azure-imds, azure-imds-aca}`; project into LaunchTicket at spawn time (never into Process spec config or env); co-located runtime Provider constructs `ManagedIdentityCredentialClient` from LaunchTicket projection and supplies via effect port; resolved URL never in any output surface. (6) Agent Process declares `networkUsage.allowEgress=false`; uses canonical Process template shape (see `ADR046-mi-topology-001` design item 6). (7) Reject `sign-challenge` with `credential-schema-invalid` immediately. (8) Map `ManagedIdentityClientState::Unavailable` to `credential-provider-unavailable`; no `InteractionRequired` state. (9) Implement `ManagedIdentityLeaseHandle` as opaque bounded newtype with redacted `Debug`. (10) All token bytes held by injected client in agent; delivered only via agent-terminated `Noise_KK` delivery session. (11) Integrate with Provider resource descriptor and controller toolkit. (12) Confirm `credential_canary` never appears in any service response, status field, delivery record outer header, or audit record. (13) Apply D087 status-first state: declare no Provider state Volume, keep ProviderStateSet empty, and write only bounded non-secret lease observation to `Credential.status` plus the Operation ledger.", - "integration": "Agent `Process` resource under `Host` or `Guest` executionRef; controller `Process` resource at Zone system host; d2b-bus routes `d2b.credential.v3` token-delivery calls to agent; Credential controller reconciles status; ACA `Provider/runtime-azure-container-apps` holds `credentialRef` pointing to a `credential-managed-identity`-backed Credential resource", - "dataMigration": "Full v3 reset. `d2b-provider-aca:managed_identity_client_id` raw field migrated to a Credential resource reference in the v3 ACA Provider config; see removal precondition below.", - "validation": "See §Test matrix", - "removalProof": "`d2b-provider-aca:managed_identity_client_id` raw field removed only after the `credential-managed-identity` controller and agent are integrated and the ACA Provider config uses `credentialRef` exclusively; `ProviderWorkloadIdentity::ManagedIdentity` bootstrap path superseded only after the ACA Provider controller uses the Credential resource for token acquisition" - }, - { - "workItemId": "ADR046-cred-mi-002", - "specId": "ADR-046-provider-credential-managed-identity", - "specPath": "docs/specs/providers/ADR-046-provider-credential-managed-identity.md", - "dependencyOwner": "ADR046-credential-001, ADR046-credential-002; ADR046-reconcile-001, ADR046-reconcile-002; ADR046-mi-topology-001; owner: Credential controller toolkit and managed-identity controller", - "currentSource": "`packages/d2b-realm-provider/src/provider.rs:CredentialProvider` status/enrollment-only trait; main `a1cc0b2d` managed-identity controller/test behavior listed in §Source reuse", - "reuseSource": null, - "reuseAction": "adapt shared Credential controller lifecycle to managed-identity controller/agent spawn and teardown", - "destination": "packages/d2b-provider-credential-managed-identity/src/controller.rs; packages/d2b-contracts/src/v3/credential_controller.rs", - "detailedDesign": "Managed-identity-specific controller design: implement async reconcile and agent spawn/teardown from §Async reconcile; enforce system-only domain; spawn agent on Credential admission plus dependency-ready, not on `phase=Ready`; implement `observeInterval=30s` health-check RPC to the agent, which calls `InspectMetadata` on the injected client; controller never calls IMDS; derive idempotency key as `SHA-256(UID \\|\\| \":\" \\|\\| rotationGeneration.to_le_bytes() \\|\\| \":\" \\|\\| operation_class_byte)`; enforce `MAX_LOCAL_LEASES=256` in the resource store; implement Deleted-phase closure by clearing `provider-revoke` only after agent Process deletion and revocation confirmation while core/audit own Deleted revision and deletion record.", - "integration": "Shared Credential controller contract produces reconcile events; managed-identity controller consumes them, manages agent Process resources, and writes Credential/agent status; generated controller contracts are consumed by all Credential Providers.", - "dataMigration": "None — controller lifecycle code only; no runtime state import", - "validation": "Managed-identity reconcile/controller tests; shared Credential reconciliation tests; topology tests validating agent spawn/teardown and Deleted-phase cleanup", - "removalProof": "V2 `CredentialProvider` status/enrollment trait lifecycle is superseded after shared controller reconcile and managed-identity agent lifecycle pass parity" - }, - { - "workItemId": "ADR046-cred-mi-003", - "specId": "ADR-046-provider-credential-managed-identity", - "specPath": "docs/specs/providers/ADR-046-provider-credential-managed-identity.md", - "dependencyOwner": "Depends on ADR046-credential-001, ADR046-credential-002, and ADR046-cred-mi-001; owner: Nix resource compiler and activation cleanup", - "currentSource": "`packages/d2b-provider-aca/src/lib.rs:managed_identity_client_id` and `packages/d2bd/src/lib.rs:managed_identity_client_id` are the current raw ACA managed-identity config surfaces; v3 Provider/Credential resource authoring is net-new", - "reuseSource": null, - "reuseAction": "replace raw ACA managed identity client-id fields with Credential resource references and v3 Provider/Credential Nix emission", - "destination": "nixos-modules/options-resources.nix; nixos-modules/activation-nixos-cleanup.nix; integration/aca-credential-ref.sh", - "detailedDesign": "Shared Nix and cleanup: implement Nix eval-time assertions 1–12 from §Eval-time assertions, closed enum schema for `imdsEndpointAlias`, `clientId` validation via `OpaqueAzureRef` charset, generation cleanup contract, and artifact catalog validation for `credential-managed-identity-bin`. Integration fixture asserts that the migrated ACA Provider config carries `credentialRef: \"Credential/aca-relay-mi\"` and the raw `managed_identity_client_id` string field is absent from rendered Provider config JSON and ACA runtime bundle.", - "integration": "Nix compiler emits Provider/Credential/ACA resource config; ResourceAPI admission validates it; activation cleanup deletes old generation resources through finalizers; ACA Provider consumes `credentialRef` to obtain tokens from the managed-identity Credential Provider.", - "dataMigration": "Full d2b 3.0 reset; raw ACA `managed_identity_client_id` config is replaced by a newly authored Credential resource reference rather than imported in place", - "validation": "Nix eval assertion tests; artifact catalog validation tests; `integration/aca-credential-ref.sh`", - "removalProof": "`managed_identity_client_id` raw fields in ACA config and daemon plumbing are removed only after ACA Provider config uses `credentialRef` exclusively" - }, - { - "workItemId": "ADR046-cred-mi-004", - "specId": "ADR-046-provider-credential-managed-identity", - "specPath": "docs/specs/providers/ADR-046-provider-credential-managed-identity.md", - "dependencyOwner": "Depends on ADR046-cred-mi-001 and ADR046-mi-topology-001; owner: credential-managed-identity audit/telemetry implementation", - "currentSource": "`packages/d2b-realm-provider/src/error.rs:contains_sensitive_shape`; `packages/d2b-core/src/realm_workloads_launcher.rs:LauncherMetadataInvariants.no_secrets_or_credentials`; main `a1cc0b2d` managed-identity canary tests listed in §Source reuse", - "reuseSource": null, - "reuseAction": "adapt existing sensitive-shape guard and canary pattern to v3 audit, OTEL, and metric surfaces", - "destination": "packages/d2b-provider-credential-managed-identity/src/{audit.rs,telemetry.rs}; packages/d2b-contract-tests/tests/credential_audit.rs", - "detailedDesign": "Shared audit/OTEL: emit audit records for all methods and controller events per §Audit; emit OTEL spans and metrics per §OTEL and metrics; add `d2b_credential_imds_calls_total` counter with bounded `alias` label; enforce `contains_sensitive_shape` on all string fields in audit records and metric labels; add canary tests for `managed-identity-canary`, `credential_canary`, and `imds-endpoint-canary` in `canary.rs`.", - "integration": "Controller and agent service methods call audit/telemetry helpers; audit subsystem and OTEL exporters consume bounded redacted records; contract tests validate credential audit shape across providers.", - "dataMigration": "None — audit/telemetry only; no runtime state import", - "validation": "`packages/d2b-contract-tests/tests/credential_audit.rs`; managed-identity `canary.rs`; audit/OTEL unit tests for labels and sensitive-shape rejection", - "removalProof": "None — audit/telemetry helpers are additive; no prior owner to remove" - }, - { - "workItemId": "ADR046-cred-ss-001", - "specId": "ADR-046-provider-credential-secret-service", - "specPath": "docs/specs/providers/ADR-046-provider-credential-secret-service.md", - "dependencyOwner": "Dependency for ADR046-cred-ss-003; owner: `packages/d2b-contracts` Credential ResourceType contract", - "currentSource": "`packages/d2b-realm-provider/src/provider.rs:CredentialProvider`; `packages/d2b-realm-provider/src/credential.rs` opaque credential refs and `OpaqueAzureRef` helpers", - "reuseSource": null, - "reuseAction": "adapt current credential/status concepts into v3 ResourceType DTOs; reuse `OpaqueAzureRef` directly where applicable", - "destination": "packages/d2b-contracts/src/v3/credential.rs", - "detailedDesign": "Contract types: define `CredentialSpec`, `CredentialStatus`, `CredentialLeaseHandle`, `OperationClass`, `PlacementBinding`, `CredentialConditionType`, and serde/validation/redaction helpers. Reuse `OpaqueAzureRef` from the v3 baseline. Full detail remains in `ADR-046-resources-credential` §Implementation work items.", - "integration": "Nix compiler emits these DTOs; ResourceAPI stores them; credential-secret-service controller/service consumes them; CLI and conformance tests validate base Credential spec/status behavior.", - "dataMigration": "Full d2b 3.0 reset; no v2 CredentialProvider status/config import", - "validation": "Credential ResourceType schema/serde/redaction tests from `ADR-046-resources-credential`; credential-secret-service conformance consumes the shared types", - "removalProof": "Old `d2b-realm-provider:CredentialProvider` trait and `CredentialStatus` enum are removed only after all three v3 Credential Provider controllers reach full reconcile parity" - }, - { - "workItemId": "ADR046-cred-ss-002", - "specId": "ADR-046-provider-credential-secret-service", - "specPath": "docs/specs/providers/ADR-046-provider-credential-secret-service.md", - "dependencyOwner": "Dependency for ADR046-cred-ss-003; owner: credential service contract/codegen", - "currentSource": "None — net-new v3 `d2b.credential.v3` service; no pre-ADR45 baseline service proto equivalent", - "reuseSource": null, - "reuseAction": "net-new service contract replacing the v2 in-process `CredentialProvider` trait", - "destination": "packages/d2b-contracts/proto/v3/credential.proto; packages/d2b-credential-service/", - "detailedDesign": "Service proto: define the `d2b.credential.v3` protobuf/ttrpc service and generate typed client/server code. Full detail remains in `ADR-046-resources-credential` §Implementation work items.", - "integration": "d2b-bus routes Credential service calls to credential-secret-service Process instances; generated client/server types bind the controller/service implementation to ComponentSession delivery.", - "dataMigration": "Full d2b 3.0 reset; no v2 service state import", - "validation": "Generated-code compile tests and credential service contract tests from `ADR-046-resources-credential`; credential-secret-service lifecycle/delivery tests consume the generated service", - "removalProof": "V2 `CredentialProvider` trait calls are superseded by `d2b.credential.v3` only after all credential providers reach parity" - }, - { - "workItemId": "ADR046-cred-ss-003", - "specId": "ADR-046-provider-credential-secret-service", - "specPath": "docs/specs/providers/ADR-046-provider-credential-secret-service.md", - "dependencyOwner": "`ADR046-cred-ss-001` (contract types); `ADR046-cred-ss-002` (service proto); `ADR046-reconcile-001`; credential-secret-service owner", - "currentSource": "`packages/d2b-realm-provider/src/provider.rs:CredentialProvider` (minimal v3 baseline)", - "reuseSource": "main `a1cc0b2d`: `packages/d2b-provider-credential-secret-service/src/lib.rs` (full implementation); `src/tests.rs` (full test suite including `FakeOo7Port`, lease lifecycle, locked state, canary enforcement, cardinality limits)", - "reuseAction": "copy and adapt", - "destination": "`packages/d2b-provider-credential-secret-service/src/{lib.rs, controller.rs, service.rs, main.rs}`; `packages/d2b-provider-credential-secret-service/tests/{lifecycle.rs, conformance.rs, faults.rs, canary.rs, delivery.rs, placement.rs}`; `packages/d2b-provider-credential-secret-service/integration/{container-service.sh, host-placement.nix, guest-placement.nix, cleanup-rollback.sh}`; `packages/d2b-provider-credential-secret-service/README.md`", - "detailedDesign": "Adapt `SecretServiceCredentialProvider` and `SecretServiceCredentialProviderFactory` to v3 `d2b.credential.v3` service; replace v2 `CredentialProvider` trait with v3 controller/service handler; retain `Oo7SecretServicePort` trait methods unchanged; ensure `SecretServiceOwner::Userd` placement guard rejects system-domain and guest-agent construction; validate `collectionAlias` against provider-internal charset (not `OpaqueAzureRef`; collection aliases may include spaces); integrate with Provider resource descriptor and controller toolkit; test that `credential_canary` never appears in any service response; create a Process resource per `(Zone, User, executionRef)` triple with `template = \"secret-service-controller\"` (plain string), canonical `sandbox` fields (`namespaceClasses`, `capabilityClasses`, `seccompClass`, `noNewPrivileges`, `startRoot`, `environmentClass`, `readOnlyRoot`), `budget` with nested `cpu`/`memory`/`pids`/`fds` sub-fields, `networkUsage: null`, no inline endpoint fields, an owned credential-service `Endpoint` resource, and `readiness.class = \"provider-defined\"`; component descriptor declares the required authenticated `dbus-session` FD attachment carried privately by the LaunchTicket; D087 status-first state model: no Provider state Volume is declared, ProviderStateSet is optional/query-time and empty, no Volume mount or layout principal is required, and the storage-need test is not met; bounded non-secret lease/acquisition/retry observation lives in `Credential.status` plus the core Operation ledger; any opaque status handle is non-secret, non-authorizing, bounded, safe for authorized status readers, and independently revalidated; no token/object-path/lease bytes persist anywhere; finalize() emits revoke outcome audit but MUST NOT emit the resource-deleted closure audit (audit subsystem only); controller writes only scoped Credential/Process health (core aggregates Provider status)", - "integration": "Target: user-domain `Process` resource under `Host` or `Guest` (ADR-only ResourceType); d2b-bus routes `d2b.credential.v3` calls to this process; Credential controller reconciles status. Current v3 has no user-credential host process: v3 `d2b-userd` is a guest exec stub (exits 78 in service mode; no credential functionality; `test-only-or-preview`). This integration path is fully new (ADR-only) work.", - "dataMigration": "Full reset; no migration from old `CredentialProvider` trait", - "validation": "See §16", - "removalProof": "Old `d2b-realm-provider:CredentialProvider` trait removed only after all three v3 Credential Provider controllers reach full reconcile parity" - }, - { - "workItemId": "ADR046-cred-ss-004", - "specId": "ADR-046-provider-credential-secret-service", - "specPath": "docs/specs/providers/ADR-046-provider-credential-secret-service.md", - "dependencyOwner": "Dependency for ADR046-cred-ss-003; owner: common Credential controller/reconciliation toolkit", - "currentSource": "ADR-only controller pattern from `ADR-046-resource-reconciliation`; no concrete secret-service baseline controller to import", - "reuseSource": null, - "reuseAction": "net-new shared controller handler pattern specialized by each Credential Provider", - "destination": "packages/d2b-provider-credential-/src/controller.rs", - "detailedDesign": "Controller toolkit: implement the common Credential controller handler conforming to the `ADR-046-resource-reconciliation` async loop. Secret-service-specific controller code plugs into this handler while keeping provider bytes out of status/store/audit.", - "integration": "Resource watches and Operation ledger drive the controller loop; credential-secret-service handler uses the toolkit to reconcile Credential status, finalizers, Process health, and service lifecycle.", - "dataMigration": "None — controller toolkit code only; no runtime state migration", - "validation": "Shared reconciliation tests from `ADR-046-resources-credential`; credential-secret-service lifecycle/fault tests verify the handler integration", - "removalProof": "None — shared toolkit is additive; v2 trait removal is tracked by ADR046-cred-ss-003/001 parity" - }, - { - "workItemId": "ADR046-cred-ss-005", - "specId": "ADR-046-provider-credential-secret-service", - "specPath": "docs/specs/providers/ADR-046-provider-credential-secret-service.md", - "dependencyOwner": "Dependency for ADR046-cred-ss-003; owner: Nix resource compiler and activation cleanup", - "currentSource": "None — net-new v3 `d2b.zones..resources.` Credential/Provider authoring surface; no pre-ADR45 baseline equivalent", - "reuseSource": null, - "reuseAction": "net-new Nix resource emission and cleanup contract", - "destination": "nixos-modules/options-resources.nix; nixos-modules/activation-nixos-cleanup.nix", - "detailedDesign": "Nix compiler: implement `d2b.zones..resources.` authoring, eval-time assertions, canonical JSON emission, artifact catalog, bundle digest, and generation cleanup contract. Full detail remains in `ADR-046-resources-credential` §Implementation work items.", - "integration": "Nix emits Provider/Credential resource JSON and artifact catalog entries; ResourceAPI admission and credential-secret-service controller consume the rendered resources; activation cleanup issues async Delete/finalizer flow on generation removal.", - "dataMigration": "Full d2b 3.0 reset; no old credential config is imported into v3 resources", - "validation": "Nix eval/assertion/golden tests from `ADR-046-resources-credential`; credential-secret-service cleanup rollback integration fixture", - "removalProof": "None — new v3 Nix resource surface; old trait removal waits for controller parity" - }, - { - "workItemId": "ADR046-cred-ss-006", - "specId": "ADR-046-provider-credential-secret-service", - "specPath": "docs/specs/providers/ADR-046-provider-credential-secret-service.md", - "dependencyOwner": "Dependency for ADR046-cred-ss-003; owner: credential-secret-service audit and telemetry implementation", - "currentSource": "`packages/d2b-core/src/realm_workloads_launcher.rs:LauncherMetadataInvariants.no_secrets_or_credentials`; secret-service main reuse canary tests listed in §14", - "reuseSource": null, - "reuseAction": "adapt zero-secret invariant and canary test pattern to credential-secret-service audit/OTEL surfaces", - "destination": "packages/d2b-provider-credential-secret-service/src/{audit.rs,telemetry.rs}", - "detailedDesign": "Audit/OTEL: emit audit records and OTEL spans/metrics for all credential service methods and controller events, with canary enforcement and no token/object-path/lease bytes in status, delivery outer headers, audit, metrics, spans, or logs. Full detail remains in `ADR-046-resources-credential` §Implementation work items.", - "integration": "Controller and service methods call audit/telemetry helpers; audit subsystem and OTEL exporters consume bounded event/span/metric records; canary tests verify every public observable surface stays secret-free.", - "dataMigration": "None — audit/telemetry only; no runtime state migration", - "validation": "Credential audit/OTEL tests from `ADR-046-resources-credential`; `tests/canary.rs` and `tests/delivery.rs` for credential-secret-service", - "removalProof": "None — audit/telemetry helpers are new; no prior owner to remove" - }, - { - "workItemId": "ADR046-credential-001", - "specId": "ADR-046-resources-credential", - "specPath": "docs/specs/ADR-046-resources-credential.md", - "dependencyOwner": "`ADR046-object-001` (resource envelope); `ADR046-identities-001` (types); W0 shared contract root; `d2b-contracts`", - "currentSource": "`packages/d2b-realm-provider/src/credential.rs`: `OpaqueAzureRef`, parse/deserialize/charset validation, tests", - "reuseSource": "main `a1cc0b2d`: `d2b-provider-credential-secret-service/src/lib.rs` types: `SecretServiceLeaseRequest`, `SecretServiceLeaseRef`, `SecretServiceLeaseGrant`, `SecretServiceLeaseInspection`, `SecretServiceLeaseRenewal`, `SecretServiceLeaseRevocation`, `SecretServiceLeaseState`; parallel entra/managed-identity types; `CredentialLease`, `CredentialLeaseRequest`, `CredentialLeaseState` from `d2b-contracts/src/v2_provider.rs`", - "reuseAction": "extract and adapt", - "destination": "`packages/d2b-contracts/src/v3/credential.rs`", - "detailedDesign": "Define `CredentialSpec`, `CredentialStatus`, `CredentialLeaseHandle` (opaque bounded newtype), `CredentialRotationPolicy`, `CredentialRevocationPolicy`, `CredentialScope`, `OperationClass` enum, `CredentialLeaseState`, `PlacementBinding`, `CredentialConditionType`, and all serde/validation/redaction helpers; reuse `OpaqueAzureRef` from v3 baseline directly; enforce zero-secret-bytes charset validation on all string fields at construction", - "integration": "Credential controller, Provider dossier schemas, Nix compiler, and resource store all consume one canonical contract", - "dataMigration": "Full d2b 3.0 reset; no v2 credential import", - "validation": "Schema golden vectors; charset/length tests; serde unknown-field rejection; `OpaqueAzureRef` round-trip and secret-shape rejection parity; `leaseHandle` and `sourceVersion` opaque newtype tests; status redaction tests", - "removalProof": "Old `CredentialProvider` trait and `CredentialStatus` enum removed only after all v3 Credential Provider controllers consume this contract" - }, - { - "workItemId": "ADR046-credential-002", - "specId": "ADR-046-resources-credential", - "specPath": "docs/specs/ADR-046-resources-credential.md", - "dependencyOwner": "`ADR046-credential-001`; `ADR046-api-001` (resource API); `ADR046-bus-001` (d2b-bus); Credential service owner", - "currentSource": "`packages/d2b-realm-provider/src/provider.rs:CredentialProvider` (status-only trait); `d2b-contracts/proto/v2/provider_credential.proto` (main: Health, Capabilities, Status, AcquireLease, RefreshLease, RevokeLease)", - "reuseSource": "main `a1cc0b2d`: `packages/d2b-contracts/proto/v2/provider_credential.proto` method names", - "reuseAction": "adapt", - "destination": "`packages/d2b-contracts/proto/v3/credential.proto`; `packages/d2b-credential-service/src/{service.rs, client.rs, server.rs}`", - "detailedDesign": "Define `d2b.credential.v3` protobuf service with methods: `Status`, `AcquireToken`, `RefreshToken`, `RevokeToken`, `SignChallenge`, `InspectMetadata`; each request carries `credential_ref`, `operation_class`, `operation_id`, `idempotency_key`, `requested_expiry_unix_ms`, `deadline_unix_ms`; `Status`, `RevokeToken`, and `InspectMetadata` responses carry only non-secret metadata (leaseHandle digest, rotationGeneration, sourceVersion, expiresAtUnixMs, state, outcome code); `AcquireToken`, `RefreshToken`, and `SignChallenge` responses additionally include a `delivery_session_params` field carrying the binding contract fields required to establish the end-to-end credential-delivery ComponentSession (see §Credential-delivery endpoint contract); the token bytes themselves travel in the separate Noise-encrypted delivery session, never in the outer DTO; strict unknown-field rejection; bounded message sizes; all record wrappers for delivery sessions must be zeroizing types", - "integration": "d2b-bus routes `d2b.credential.v3` service to the exact credential provider Process identified by `Credential.spec.providerRef`; RBAC checks `use-credential` verb before dispatch; for `AcquireToken`/`RefreshToken`/`SignChallenge`, bus additionally authorizes the credential-delivery endpoint route and forwards opaque Noise-encrypted delivery records without terminating or buffering them; bus never stores or inspects delivery record plaintext", - "dataMigration": "None — full d2b 3.0 reset; no prior state to migrate", - "validation": "Protocol golden vectors for each method; malformed/oversize rejection; `leaseHandle` opacity tests (secret-canary must not appear in outer DTO or delivery routing metadata); locked/unavailable/denied/expired state tests; delivery session binding contract round-trip; zeroizing record type unit tests; delivery channel never materialized in non-delivery method tests", - "removalProof": "Old v2 `CredentialProviderService` proto removed only after all v3 callers migrate" - }, - { - "workItemId": "ADR046-credential-003", - "specId": "ADR-046-resources-credential", - "specPath": "docs/specs/ADR-046-resources-credential.md", - "dependencyOwner": "`ADR046-credential-001`, `ADR046-credential-002`; `ADR046-reconcile-001`; credential-secret-service owner", - "currentSource": "`packages/d2b-realm-provider/src/provider.rs:CredentialProvider` (minimal v3 baseline)", - "reuseSource": "main `a1cc0b2d`: `packages/d2b-provider-credential-secret-service/src/lib.rs` (full implementation); `src/tests.rs` (full test suite including `FakeOo7Port`, lease lifecycle, locked state, canary enforcement, cardinality limits)", - "reuseAction": "copy and adapt", - "destination": "`packages/d2b-provider-credential-secret-service/src/{lib.rs, controller.rs, service.rs, main.rs}` (implementation + binary); `packages/d2b-provider-credential-secret-service/tests/{lifecycle.rs, conformance.rs, faults.rs, canary.rs, delivery.rs, placement.rs}` (hermetic Cargo integration); `packages/d2b-provider-credential-secret-service/integration/{container-service.sh, host-placement.nix, cleanup-rollback.sh}` (orchestration fixtures); `packages/d2b-provider-credential-secret-service/README.md` (all §Provider README required sections)", - "detailedDesign": "Adapt `SecretServiceCredentialProvider` and `SecretServiceCredentialProviderFactory` to use v3 `d2b.credential.v3` service interface; replace v2 `CredentialProvider` trait impl with v3 controller/service handler; adapt `Oo7SecretServicePort` trait (retain all methods unchanged); ensure `SecretServiceOwner::Userd` placement restriction rejects system-domain and guest-agent construction; validate `collectionAlias` against provider-internal charset (not `OpaqueAzureRef`; collection aliases may include spaces); integrate with Provider resource descriptor and controller toolkit; test `credential_canary` never appears in any service response", - "integration": "Target: user-domain Process (ADR-only `Process` ResourceType) under Host (ADR-only `Host` ResourceType); d2b-bus (ADR-only) routes `d2b.credential.v3` calls to this process; Credential controller reconciles status. Current v3 has no user-credential host process: v3 `d2b-userd` (`packages/d2b-userd/src/lib.rs`) is a guest exec stub only (exits 78 in service mode; `UserAttachRequest`/`UserExecSession` guest wire primitives only; evidence class: `test-only-or-preview`); no credential or keyring functionality. This integration path is fully new (ADR-only) work.", - "dataMigration": "Full reset; no migration from old `CredentialProvider` trait", - "validation": "**`src/` unit** (`#[cfg(test)]` in `src/`): `Oo7SecretServicePort` trait API surface, `SecretServiceOwner` placement guard, `collectionAlias` charset, `lockPolicy` state transitions. **`tests/` Cargo integration** (`cargo test -p d2b-provider-credential-secret-service`): copied test suite from main with v3 type substitutions; add `lifecycle.rs` (acquire/refresh/revoke/inspect end-to-end with `FakeOo7Port`); `conformance.rs` (all 11 `check_provider_conformance` arms pass); `faults.rs` (locked state → `credential-provider-unavailable`, unavailable, cardinality limit); `canary.rs` (`credential_canary` and `object_path_canary` absent from every response, status field, and delivery record); `delivery.rs` (delivery-session binding contract, zeroizing buffer, replay-safe sequence); `placement.rs` (system-domain and guest-agent construction rejected). **`integration/` fixtures**: `container-service.sh` (container-backed Provider service start/stop/drain); `host-placement.nix` (user-domain Host/Process placement in runNixOSTest); `cleanup-rollback.sh` (Nix-generation removal triggers async Delete and Provider-revoke finalizer).", - "removalProof": "Old `d2b-realm-provider:CredentialProvider` trait removed only after this controller and the other two Credential controllers reach full reconcile parity" - }, - { - "workItemId": "ADR046-credential-004", - "specId": "ADR-046-resources-credential", - "specPath": "docs/specs/ADR-046-resources-credential.md", - "dependencyOwner": "`ADR046-credential-001`, `ADR046-credential-002`; `ADR046-reconcile-001`; credential-entra owner", - "currentSource": "`packages/d2b-realm-provider/src/credential.rs:AzureControlPlaneRef`, `OpaqueAzureRef` (v3 baseline, reachable)", - "reuseSource": "main `a1cc0b2d`: `packages/d2b-provider-credential-entra/src/lib.rs` (full implementation); `src/tests.rs` (full test suite including `FakeEntraClient`, `credential_canary`/`endpoint_canary`, interaction-required, colocated-consumer, generation-mismatch tests)", - "reuseAction": "copy and adapt", - "destination": "`packages/d2b-provider-credential-entra/src/{lib.rs, controller.rs, service.rs, main.rs}`; `packages/d2b-provider-credential-entra/tests/{lifecycle.rs, conformance.rs, faults.rs, canary.rs, delivery.rs, placement.rs}`; `packages/d2b-provider-credential-entra/integration/{container-service.sh, guest-placement.nix, cleanup-rollback.sh}`; `packages/d2b-provider-credential-entra/README.md` (all §Provider README required sections)", - "detailedDesign": "Adapt `EntraCredentialProvider` and `EntraCredentialProviderFactory` to v3 service; replace v2 `AgentPlacementBinding` with v3 `PlacementBinding` enum (user-agent, guest-agent only; reject host-system); validate `tenantId` config field using `OpaqueAzureRef::parse` from v3 baseline `d2b-realm-provider/src/credential.rs` (note: current v3 source field is named via `AzureControlPlaneRef`; target field name is `tenantId`); retain `EntraCredentialClient` trait unchanged; map `EntraClientError::InteractionRequired` to `credential-provider-unavailable` (not denied); enforce `EntraCredentialOwner::ExactConsumer` so only the declared `consumerRef` may acquire", - "integration": "User-domain or system-domain Process under Guest; d2b-bus routing; Credential controller", - "dataMigration": "Full reset", - "validation": "**`src/` unit**: `EntraCredentialClient` trait API, `OpaqueAzureRef::parse` on `tenantId`, `EntraCredentialOwner::ExactConsumer` guard, `EntraClientState` transitions. **`tests/` Cargo integration**: `lifecycle.rs` (acquire/refresh/revoke/inspect with `FakeEntraClient`); `conformance.rs` (all conformance arms); `faults.rs` (interaction-required → unavailable, generation-mismatch, colocated-consumer rejection); `canary.rs` (`credential_canary` and `endpoint_canary` absent from every response and delivery record); `delivery.rs` (delivery-session binding, zeroizing, replay-safe); `placement.rs` (host-system placement rejected). **`integration/` fixtures**: `container-service.sh`; `guest-placement.nix` (user-domain and system-domain Process on Guest in runNixOSTest); `cleanup-rollback.sh`.", - "removalProof": "Same as ADR046-credential-003" - }, - { - "workItemId": "ADR046-credential-005", - "specId": "ADR-046-resources-credential", - "specPath": "docs/specs/ADR-046-resources-credential.md", - "dependencyOwner": "`ADR046-credential-001`, `ADR046-credential-002`; `ADR046-reconcile-001`; credential-managed-identity owner", - "currentSource": "`packages/d2b-realm-provider/src/credential.rs:ManagedIdentityRef` (v3 baseline, reachable); `d2bd/src/lib.rs:managed_identity_client_id` (reachable ACA config); `d2b-provider-aca/src/lib.rs:managed_identity_client_id` (reachable)", - "reuseSource": "main `a1cc0b2d`: `packages/d2b-provider-credential-managed-identity/src/lib.rs` (full implementation); `src/tests.rs` (full test suite)", - "reuseAction": "copy and adapt", - "destination": "`packages/d2b-provider-credential-managed-identity/src/{lib.rs, controller.rs, service.rs, main.rs}`; `packages/d2b-provider-credential-managed-identity/tests/{lifecycle.rs, conformance.rs, faults.rs, canary.rs, delivery.rs, placement.rs}`; `packages/d2b-provider-credential-managed-identity/integration/{container-service.sh, host-guest-placement.nix, aca-credential-ref.sh, cleanup-rollback.sh}`; `packages/d2b-provider-credential-managed-identity/README.md` (all §Provider README required sections)", - "detailedDesign": "Adapt `ManagedIdentityCredentialProvider` to v3 service; enforce `ManagedIdentityCredentialOwner::ExactSdkConsumer`; reject user-agent placement (IMDS is machine-local, not user-session); validate `clientId` config using `OpaqueAzureRef::parse` directly from v3 baseline (note: current v3 source field is named via `ManagedIdentityRef.client_id`; target field name is `clientId`); retain `ManagedIdentityCredentialClient` trait unchanged; map `ManagedIdentityClientState::Unavailable` to `credential-provider-unavailable`; `sign-challenge` operation class returns schema-invalid immediately", - "integration": "System-domain Process under Host or Guest; d2b-bus routing; Credential controller; ACA runtime-azure-container-apps Provider may hold a reference to this Credential resource", - "dataMigration": "Full reset; `d2b-provider-aca` managed_identity_client_id config field migrated to a Credential resource reference in the v3 ACA Provider config", - "validation": "**`src/` unit**: `ManagedIdentityCredentialClient` trait, `OpaqueAzureRef::parse` on `clientId`, `ManagedIdentityCredentialOwner::ExactSdkConsumer` guard, `imdsEndpointAlias` validation, `sign-challenge` schema-invalid fast path. **`tests/` Cargo integration**: `lifecycle.rs` (acquire/refresh/revoke/inspect with `FakeClient`); `conformance.rs`; `faults.rs` (unavailable state, colocated-consumer rejection); `canary.rs` (canary absent from all responses and delivery records); `delivery.rs`; `placement.rs` (user-agent placement rejected). **`integration/` fixtures**: `container-service.sh`; `host-guest-placement.nix` (system-domain Host and Guest placement in runNixOSTest); `aca-credential-ref.sh` (ACA Provider config uses `credentialRef`; raw `managed_identity_client_id` absent); `cleanup-rollback.sh`.", - "removalProof": "`d2b-provider-aca:managed_identity_client_id` raw field removed only after `credential-managed-identity` Provider controller is integrated and the ACA Provider config uses `credentialRef`" - }, - { - "workItemId": "ADR046-credential-006", - "specId": "ADR-046-resources-credential", - "specPath": "docs/specs/ADR-046-resources-credential.md", - "dependencyOwner": "`ADR046-credential-001`, `ADR046-credential-002`; `ADR046-reconcile-001`, `ADR046-reconcile-002`; Credential controller owner", - "currentSource": "No direct v3 current source; controller is `ADR-only`", - "reuseSource": "main `a1cc0b2d`: `packages/d2b-provider-toolkit/src/conformance.rs` (provider conformance pattern); `packages/d2b-provider-toolkit/src/adapter.rs` (controller toolkit pattern)", - "reuseAction": "adapt", - "destination": "`packages/d2b-provider-credential-/src/controller.rs`; `packages/d2b-contracts/src/v3/credential_controller.rs`", - "detailedDesign": "Implement Credential controller handler conforming to `ADR-046-resource-reconciliation` async loop; implement `reconcile`, `observe`, `finalize`, `drain`, and `health` handlers; implement rotation state machine (proactive/on-expiry/on-demand policies); implement `provider-revoke` finalizer execution with `revocation.onOwnerDelete` policy; implement provider-generation-change detection and revocation; implement `CredentialReady`, `RotationDue`, `ProviderUnavailable`, `LeaseRevoked` condition logic; implement bounded idempotency key derivation (Credential UID + rotationGeneration + operation class, no secret material); implement `observeInterval=30s` health check calling `InspectMetadata`; bounded retry/backpressure with typed `credential-rotation-failed` outcome; enforce `MAX_LOCAL_LEASES=256` per controller provider instance", - "integration": "Provider controller Process → d2b-bus → `d2b.credential.v3` service in provider process → injected client/port; status updates through resource API; watch subscription on Credential, Provider, Host/Guest dependency types", - "dataMigration": "None; v3 reset", - "validation": "Controller state-machine golden vectors; rotation-policy matrix (proactive/on-demand/on-expiry × success/locked/unavailable/expired); finalizer execution tests; provider-generation-change revocation tests; idempotency key derivation tests; observe-interval drift detection test; canary tests confirm zero secret bytes in all controller-written status fields", - "removalProof": "Not applicable (new controller)" - }, - { - "workItemId": "ADR046-credential-007", - "specId": "ADR-046-resources-credential", - "specPath": "docs/specs/ADR-046-resources-credential.md", - "dependencyOwner": "`ADR046-credential-001`; `ADR046-identities-002` (Nix resource compiler); `ADR046-api-001` (resource API, for create/update/delete); `ADR046-reconcile-001` (activation-nixos controller); Nix integrator", - "currentSource": "`nixos-modules/assertions.nix` (secret-shape assertions, `implemented-and-reachable`). Note: `nixos-modules/options-realms.nix` line 318 contains `d2b.realms..relay.credentialRef` — this uses the current v3 **Realm** terminology (current symbol for Zone) and is a relay credential **state-directory path** reference for gateway relay provisioning, not a Zone Credential resource declaration. It is not a source for this work item. No current v3 source exists for Zone Credential resource Nix declarations (ADR-only).", - "reuseSource": null, - "reuseAction": "adapt", - "destination": "`nixos-modules/options-resources.nix` (generic schema-derived resource options; not type-specific), `nixos-modules/activation-nixos-cleanup.nix`", - "detailedDesign": "**(1) Schema-derived options and eval-time validation**: implement `d2b.zones..resources. = { type = \"...\"; spec = { ... }; }` as a generic attrset option; an optional `metadata` sub-attr may contain `ownerRef` and/or presentation `labels`/`annotations`. Nix option types, defaults, and inline docs for `spec` fields are generated from the committed `ResourceTypeSchema` JSON (`docs/reference/schemas/v3/credential.json`) and the signed Provider schema — no bespoke options module is maintained separately. `metadata.name` derives from the attr key; `metadata.zone` from the Zone attr key; `apiVersion` defaults to `resources.d2bus.org/v3`; `status`/`uid`/`generation`/`revision`/timestamps/`managedBy`/`configurationGeneration` are not authored. Core assigns `metadata.managedBy = \"configuration\"` and `metadata.configurationGeneration = ` to all configuration-managed resources at create/update time; these are never authored in Nix. Eval-time assertions (applied to all entries with `type = \"Credential\"`): `spec.providerRef` resolves a Provider in the same Zone whose `credentialDomains` includes `spec.scope.domainFilter` and whose `spec.artifactId` (a sibling of `spec.config` on the Provider resource, not inside `spec.config`) resolves an artifact catalog entry of `type = \"provider\"`; `spec.audience` charset (`^[A-Za-z0-9._:/@-]+$`, max 256); `spec.rotation.proactiveWindowMs < maxLeaseLifetimeMs / 2`; `spec.consumerRef`/`scope.executionRef`/`scope.userRef` resolve declared Zone resources; duplicate `(providerRef, executionRef, userRef, audience)` tuple rejected; `contains_sensitive_shape` on all string fields; Provider-specific placement constraints; `allowedOperations` ⊆ `providerRef.supportedOperations`. **(2) Canonical JSON and bundle emission**: render `spec` attr directly to `spec` object in canonical JSON (no field renames/re-nesting); `metadata` in output contains only derived `name`/`zone` and optionally Nix-authored `ownerRef`/`labels`/`annotations`; `apiVersion` is top-level, not inside `metadata`; `finalizers` is omitted from the Nix-rendered input (core manages finalizers, never accepts them from the bundle); no management labels are emitted by Nix; sort bundle by `(type, name)`; write to `/etc/d2b/zones//resource-bundle.json` with digest. **(2b) Artifact catalog emission**: derivation-valued inputs (`d2b.artifacts.`) are compiled separately into an integrity-pinned artifact catalog (`/etc/d2b/zones//artifact-catalog.json`) with its own digest header; each entry records `id`, `type`, `sha256`, and bounded closure metadata; store paths are private catalog implementation data absent from the resource bundle, status, audit, and logs; `activation-nixos` verifies both digests before any create/update; missing or wrong-type `artifactId` references fail the NixOS build. **(3) Build-time schema validation**: validate rendered JSON against `docs/reference/schemas/v3/credential.json` and Provider-specific schema; enforce `secretRef` fields use `Credential/` refs; enforce no store paths in any resource bundle or status output; drift gate (`make test-drift`) regenerates schemas with `cargo xtask gen-schemas` and asserts `git diff --exit-code`; Nix options module drift checked in the same gate. **(4) Generation transition and cleanup contract**: activation-nixos controller verifies SHA-256 digest of both resource bundle and artifact catalog, creates/updates desired-set resources, activates without blocking on cleanup, issues async Delete for absent configuration-managed resources (those with `metadata.managedBy = \"configuration\"`), sets Degraded/Cleanup=True on removed resources; retains up to `retainedConfigurationMax` (default 3, range 1..16) prior bundles; oldest prune when count exceeded; no time-based rollback window. **(5) Configuration-managed vs controller/API isolation**: `managedBy` and `ownerRef` are orthogonal; configuration-managed and API-created resources may each carry an optional same-Zone `ownerRef` and participate in owner cascade. Cleanup checks `metadata.managedBy = \"configuration\"` before issuing Delete; resources with `metadata.managedBy = \"controller\"` or `metadata.managedBy = \"api\"` are never deleted by this path; API-created resources persist until explicit Delete and are never generation-swept.", - "integration": "`activation-nixos` Provider creates/updates Credential resources from emitted envelopes; Credential controller `provider-revoke` finalizer handles cleanup Deletes; owner controller reconciles children of deleted configuration-managed Credentials", - "dataMigration": "None; v3 reset", - "validation": "**(eval/build)**: nix-unit golden JSON envelope for each example (spec shape, no management labels in Nix output, sort, digest); assertion-failure tests for secret-shaped audience, mismatched providerRef/domainFilter, proactiveWindow > half maxLifetime, duplicate binding tuple, unresolved refs; artifact catalog: assertion-failure for missing `artifactId`, wrong-type `artifactId`, duplicate catalog ID; bundle + artifact catalog digest round-trip; artifact catalog store-path absence from resource bundle and status; Provider-specific schema cross-check; `make test-drift` schema drift gate. **(runtime integration in `tests/host-integration/`)**: `credential-cleanup-basic` (removed resource reaches Deleted); `credential-cleanup-nonblocking` (activation Ready before cleanup finalizer finishes); `credential-cleanup-pending-status` (Cleanup=True on removed resource, PendingCleanup=True on Provider); `credential-cleanup-stalled` (Degraded stall detection and recovery); `credential-cleanup-controller-children-preserved` (ownerRef children cleaned by Credential controller); `credential-cleanup-no-dynamic-deletion` (controller-created Credential with `managedBy = \"controller\"` not deleted); `credential-retained-generation-count` (up to retainedConfigurationMax bundles retained; rollback re-creates from retained bundle; oldest pruned when count exceeded); `credential-bundle-digest-mismatch` (tampered bundle aborts activation).", - "removalProof": "Not applicable (new module)" - }, - { - "workItemId": "ADR046-credential-008", - "specId": "ADR-046-resources-credential", - "specPath": "docs/specs/ADR-046-resources-credential.md", - "dependencyOwner": "`ADR046-credential-001`, `ADR046-credential-006`; audit/OTEL integrator", - "currentSource": "`packages/d2b-core/src/privileges.rs:SecretAccess` (implemented-and-reachable); `d2b-realm-provider/src/error.rs:ProviderDiagnostic`/`contains_sensitive_shape` (implemented-and-reachable); `packages/d2b-contract-tests/tests/policy_observability.rs` (reachable audit policy tests)", - "reuseSource": null, - "reuseAction": "adapt", - "destination": "`packages/d2b-provider-credential-/src/audit.rs`, `telemetry.rs`; `packages/d2b-contract-tests/tests/credential_audit.rs`", - "detailedDesign": "Implement audit record emission for all credential service methods and controller events using the field set defined in §Audit; implement OTEL span/metric emission using the closed label set in §OTEL and metrics; implement `contains_sensitive_shape` check in all string fields of audit records and metric label values (adapted from `d2b-realm-provider/src/error.rs:contains_sensitive_shape`); add canary-enforcement tests that verify `\"secret-canary\"`, `\"entra-token-canary\"`, and `\"managed-identity-canary\"` values never appear in any audit record, metric label, span attribute, log line, or status field across all Provider test suites", - "integration": "Credential controller and service handlers emit audit records and telemetry through Zone audit/OTEL paths", - "dataMigration": "None — full d2b 3.0 reset; no prior state to migrate", - "validation": "Canary tests across all three Provider crates; audit record field-presence tests; metric label cardinality tests; span attribute absence tests for forbidden fields", - "removalProof": "Not applicable" - }, - { - "workItemId": "ADR046-decisions-001", - "specId": "ADR-046-decision-register", - "specPath": "docs/specs/ADR-046-decision-register.md", - "dependencyOwner": "ADR 0046 integrator", - "currentSource": "v3 evidence baseline plus parent/spec contradictions", - "reuseSource": null, - "reuseAction": "adapt", - "destination": "`docs/specs/ADR-046-decision-register.md`", - "detailedDesign": "Record every evidence-underdetermined choice before dependent spec work proceeds", - "integration": "Parent decision summary and all affected specs link the decision ID", - "dataMigration": "None — full d2b 3.0 reset; no prior state to migrate", - "validation": "Zero unresolved entries before pre-panel review; manifest/link consistency", - "removalProof": "Not applicable" - }, - { - "workItemId": "ADR046-delivery-001", - "specId": "ADR-046-validation-and-delivery", - "specPath": "docs/specs/ADR-046-validation-and-delivery.md", - "dependencyOwner": "`ADR046-W0`; delivery-tooling integrator", - "currentSource": "none in this repository; `Makefile` heavy-lane targets do not yet exist", - "reuseSource": "sibling-lineage `cargo xtask heavy-gate` implementation (per D001/D041 unrestricted-reuse policy)", - "reuseAction": "copy-unchanged, then adapt paths/crate names to this repository's `packages/xtask` layout", - "destination": "`packages/xtask/src/heavy_gate.rs`; `Makefile` targets `heavy-check`, `heavy-test-integration`, `heavy-test-host-integration`, `heavy-test-hardware`, `heavy-cargo-test`, `heavy-flake-check`", - "detailedDesign": "Two-slot per-UID OFD-locked semaphore, 250 ms nonblocking retry up to 30 minutes, fail-closed on unsupported locking, duplicated locked-FD handoff to child, wrapper-owned group-signal/reap, as specified in §11", - "integration": "Every heavy lane in §10.4/§10.10/§10.11 routes through this one binary; no wave adds a second lock mechanism", - "dataMigration": "None — net-new tooling", - "validation": "Unit tests for slot acquisition/timeout/fail-closed paths; integration test spawning two concurrent heavy-gate invocations and asserting the second blocks until the first releases", - "removalProof": "Not applicable (net-new; nothing to remove)" - }, - { - "workItemId": "ADR046-delivery-002", - "specId": "ADR-046-validation-and-delivery", - "specPath": "docs/specs/ADR-046-validation-and-delivery.md", - "dependencyOwner": "`ADR046-W0`; delivery-tooling integrator", - "currentSource": "none in this repository", - "reuseSource": "sibling-lineage `cargo xtask delivery wave snapshot` implementation", - "reuseAction": "copy-unchanged, then adapt", - "destination": "`packages/xtask/src/delivery/snapshot.rs`", - "detailedDesign": "Binds base/head OIDs, dependency graph, repository set into `candidate_id`/`content_id`/`snapshot_sha256` per §12.1", - "integration": "Called by the integrator immediately after PR opening (§13.1), before any validator/panel lane starts", - "dataMigration": "None — full d2b 3.0 reset; no prior state to migrate", - "validation": "Unit tests asserting identical inputs produce identical digests and any single-byte content change produces a different `content_id`", - "removalProof": "Not applicable" - }, - { - "workItemId": "ADR046-delivery-003", - "specId": "ADR-046-validation-and-delivery", - "specPath": "docs/specs/ADR-046-validation-and-delivery.md", - "dependencyOwner": "`ADR046-delivery-002`; delivery-tooling integrator", - "currentSource": "none in this repository", - "reuseSource": "sibling-lineage `cargo xtask delivery wave validate-import` implementation", - "reuseAction": "copy-unchanged, then adapt", - "destination": "`packages/xtask/src/delivery/validate_import.rs`; external candidate-ID-addressed evidence directory (never under Git)", - "detailedDesign": "Imports CI/local/host validator command/result evidence, keyed by `candidate_id`, per §12.2", - "integration": "Consumed by `wave seal` (§ADR046-delivery-005) as one of the seal's required inputs", - "dataMigration": "None — full d2b 3.0 reset; no prior state to migrate", - "validation": "Test asserting evidence for a stale `candidate_id` is rejected; test asserting raw command output never lands in a tracked file", - "removalProof": "Not applicable" - }, - { - "workItemId": "ADR046-delivery-004", - "specId": "ADR-046-validation-and-delivery", - "specPath": "docs/specs/ADR-046-validation-and-delivery.md", - "dependencyOwner": "`ADR046-delivery-002`; spec-set integrator", - "currentSource": "`docs/specs/README.md`'s described-but-not-yet-generated `ADR-046-spec-set.json`/`ADR-046-work-items.json` contract", - "reuseSource": "none required — this generator is specific to the `docs/specs/ADR-046-*` manifest shape", - "reuseAction": "adapt (new generator, following the existing `xtask gen-schemas`/`gen-nix-options` pattern already used for other generated artifacts)", - "destination": "`packages/xtask/src/gen_spec_set.rs`; `docs/specs/ADR-046-spec-set.json`, `docs/specs/ADR-046-work-items.json`", - "detailedDesign": "Enumerates every `docs/specs/ADR-046-*.md` and `docs/specs/providers/ADR-046-provider-*.md` file, its metadata table, content digest, and every `### ADR046--` work item, per §8", - "integration": "`make test-drift` gains a row running this generator and `git diff --exit-code`; every wave's exit criteria (§4) require it committed as the wave's last commit", - "dataMigration": "None — full d2b 3.0 reset; no prior state to migrate", - "validation": "Golden-fixture test against a small synthetic spec directory; drift test against the real `docs/specs/` tree", - "removalProof": "Not applicable" - }, - { - "workItemId": "ADR046-delivery-005", - "specId": "ADR-046-validation-and-delivery", - "specPath": "docs/specs/ADR-046-validation-and-delivery.md", - "dependencyOwner": "`ADR046-delivery-002`, `ADR046-delivery-003`; panel-tooling integrator", - "currentSource": "none in this repository; this repository's existing `AGENTS.md` panel-review process is host-local script tooling (`/etc/nixos/scripts/panel-review.{md,sh}`), not a candidate-bound `xtask` subcommand", - "reuseSource": "sibling-lineage `cargo xtask delivery wave panel-request`/`panel-attest` implementation", - "reuseAction": "copy-unchanged, then adapt to bind the fixed `gemini-3.1-pro-preview`/`github-copilot` model/provider pair and this repository's existing ten-role roster (§12.3)", - "destination": "`packages/xtask/src/delivery/panel.rs`", - "detailedDesign": "`panel-request` writes the candidate-bound request naming the exact ten roles and required model; `panel-attest` validates a directory of exactly ten strict 13-field records, rejecting wrong model/candidate binding, duplicate provider/run provenance, or inconsistent `signoff`/`recommendations`, per §12.3", - "integration": "Every wave's exit criteria (§4) require ten unanimous attested records before `wave seal`", - "dataMigration": "None — full d2b 3.0 reset; no prior state to migrate", - "validation": "Unit tests for every rejection class (wrong model, missing role, duplicate run_id, `signoff:true` with non-empty `recommendations`); integration test with ten synthetic valid records passing", - "removalProof": "Not applicable" - }, - { - "workItemId": "ADR046-delivery-006", - "specId": "ADR-046-validation-and-delivery", - "specPath": "docs/specs/ADR-046-validation-and-delivery.md", - "dependencyOwner": "`ADR046-delivery-002`, `ADR046-delivery-004`, `ADR046-delivery-005`; delivery-tooling integrator", - "currentSource": "none in this repository", - "reuseSource": "sibling-lineage `cargo xtask delivery wave seal`, `merge-eligibility`, and history/byte-identity proof implementation", - "reuseAction": "copy-unchanged, then adapt", - "destination": "`packages/xtask/src/delivery/{seal,eligibility,history_proof}.rs`", - "detailedDesign": "`seal` requires all ten panel records unanimous and bound to the same candidate/content/snapshot digests plus every validator lane passing; `merge-eligibility` checks each stacked PR's current base/head against the sealed OIDs or a passing history-proof; `history_proof` verifies byte-identical integrated content/generated artifacts/dependency diff/repository set across a rebase, per §12.4/§12.6", - "integration": "`make check` gains no new required step for ordinary contributors; this tooling is invoked only by the wave integrator per §4/§13", - "dataMigration": "None — full d2b 3.0 reset; no prior state to migrate", - "validation": "Unit tests for seal rejection on any missing/mismatched record; integration test proving a history-only rebase with identical content passes `history_proof` and reuses panel evidence, while any content change fails it", - "removalProof": "Not applicable" - }, - { - "workItemId": "ADR046-delivery-007", - "specId": "ADR-046-validation-and-delivery", - "specPath": "docs/specs/ADR-046-validation-and-delivery.md", - "dependencyOwner": "`ADR046-W0`; delivery-tooling integrator", - "currentSource": "none in this repository; this codebase's existing `tests/tools/` timing logs (`d2b-static-timing.$$/`) are ad hoc, not a candidate-bound ledger", - "reuseSource": "existing `xtask`/`libtest --format=json` timing output; no new test framework", - "reuseAction": "adapt", - "destination": "`packages/xtask/src/test_runtime_ledger.rs`; a `make`-invokable timing gate reusing `make test-rust`/Layer-1 shard targets", - "detailedDesign": "Measures execution-only time (after build, warm cache) per test/crate/shard against §10.16 budgets, records the reference runner/repetitions/p95, reports the top slow tests, applies a historical regression threshold, and emits a machine-readable CI artifact; the placement lint rejects a hermetic-tier test that sleeps, spawns a process, or touches network/containers/DBus/systemd/broker/Nix/KVM/hardware/live cloud, and the deterministic-clock/sleep lint rejects wall-clock sleep/retry in `src/`/`tests/`", - "integration": "Every wave's entry/exit criteria (§4) consume the ledger artifact; `make test-rust` and Layer-1 shards run concurrently; no new top-level `tests/*.sh` gate is added", - "dataMigration": "None — full d2b 3.0 reset; no prior state to migrate", - "validation": "Policy self-tests: an intentional slow/sleep/process/network hermetic test is rejected; a synthetic timing regression fails the gate; parallel isolation holds under shuffled/parallel execution; a retired legacy selector is absent from `tests/layer1-jobs.json`, closed gate manifests, and CI shards", - "removalProof": "Not applicable" - }, - { - "workItemId": "ADR046-delivery-008", - "specId": "ADR-046-validation-and-delivery", - "specPath": "docs/specs/ADR-046-validation-and-delivery.md", - "dependencyOwner": "`ADR046-streamline-001`, `ADR046-W0`; delivery-tooling integrator", - "currentSource": "none in this repository; launch order and parallelism were previously derived only from this spec's §3/§6 prose", - "reuseSource": "`ADR-046-spec-set.json`, `ADR-046-work-items.json`, and §3.1–§3.4/§3.5 of this spec; no new framework", - "reuseAction": "net-new (D095 artifact contract)", - "destination": "`docs/specs/ADR-046-implementation-graph.json`, `docs/specs/ADR-046-implementation-graph.md` (generated by `ADR046-streamline-001`'s `xtask implementation-graph`); the artifact contract, generation, validation, and ready-wave query are owned by §3.5 of this spec", - "detailedDesign": "Owns the D095 implementation-graph contract: `artifactKind`/`schemaVersion`/`adr`/`status`; one node per member spec and per work item mapped exactly once to a `W0`–`W7` wave and a file-disjoint `parallelGroup`, with `owner`/`destinations`/`entryContracts`/`prerequisites`/`blockers`/`exitGate`/`topologicalRank`; typed `spec-depends-on`/`shared-contract`/`work-item-depends-on`/`implements-spec`/`file-overlap-order` edges; the §3.5.1 ready-wave query; and the anti-serialization invariant that every ready file-disjoint group launches concurrently while a same-wave dependency is a prep barrier, not whole-wave serialization. The graph is a generated non-member artifact and does not change the 55-member `ADR-046-spec-set.json` count.", - "integration": "Consumed by §4 wave entry/exit and §6 anti-serialization checks and by `ADR046-streamline-013`; a `tests/unit/gates/` drift gate regenerates and `git diff --exit-code`s the graph after any spec/work-item edit", - "dataMigration": "None — docs/tooling only; no runtime state", - "validation": "Every 55 spec node and every work item present exactly once; all edge endpoints resolve; graph acyclic; waves monotonic (dependencies earlier or explicit same-wave prep barrier); parallel groups claim no ordering absent a dependency/file-overlap edge; deterministic JSON with no timestamps/host paths; every Mermaid node ID valid; the ready-wave query returns the expected concurrently-launchable groups on a seeded fixture", - "removalProof": "Not applicable" - }, - { - "workItemId": "ADR046-device-001", - "specId": "ADR-046-resources-device", - "specPath": "docs/specs/ADR-046-resources-device.md", - "dependencyOwner": "W0 shared contract root; `d2b-contracts`", - "currentSource": "`packages/d2b-contracts/src/security_key.rs` (SecurityKeyStatusResponse, SecurityKeySession, SecurityKeyLeaseState, SecurityKeyVmSessionState DTOs; implemented-and-reachable), `usbip.rs`, `broker_wire.rs`; `packages/d2b-core/src/privileges_w3.rs` (W3BrokerOperation: SecurityKeyOpenDevice, SecurityKeyApplyUdevRules, UsbipBindFirewallRule — implemented-and-reachable); `packages/d2b-core/src/manifest_v04.rs` VmEntry device fields (tpm, usbip_yubikey, security_key, graphics — old Workload manifest, generated-or-eval-contract)", - "reuseSource": null, - "reuseAction": "extract and adapt", - "destination": "`packages/d2b-contracts/src/v3/device.rs`", - "detailedDesign": "Device ResourceType schema (spec/status/conditions/claims/inventory); closed-set error codes; Device RBAC verbs; broker operation effect-limit constants", - "integration": "Provider dossiers, resource API/store, CLI status surfaces", - "dataMigration": "Full reset; no v2 device object import", - "validation": "Schema golden vectors; unknown-field denial; exclusive/shared conflict rejection; arbitration/maxClaims invariant", - "removalProof": "Old ProcessRole/DTO branches retained until Provider integrations are live" - }, - { - "workItemId": "ADR046-device-002", - "specId": "ADR-046-resources-device", - "specPath": "docs/specs/ADR-046-resources-device.md", - "dependencyOwner": "ADR046-device-001; device-tpm provider owner", - "currentSource": "`packages/d2b-host/src/swtpm_argv.rs`; `packages/d2b-priv-broker/src/ops/swtpm_dir.rs`; `nixos-modules/components/tpm.nix`", - "reuseSource": null, - "reuseAction": "extract and adapt", - "destination": "`packages/d2b-provider-device-tpm/src/` (controller, swtpm runner, state-dir logic); `packages/d2b-provider-device-tpm/tests/` (hermetic Cargo integration); `packages/d2b-provider-device-tpm/integration/` (container/Host scenarios); `packages/d2b-provider-device-tpm/README.md`", - "detailedDesign": "Device spec/status; flush EphemeralProcess → swtpm Process sequencing; state-dir hardening; tamper-marker; finalizer non-deletion of Volume; Nix emitter; all four required crate paths present (see \"Provider crate layout\")", - "integration": "Zone resource store; Process controller; Volume lifecycle", - "dataMigration": "State dir and tamper markers preserved across reset", - "validation": "`src/`: swtpm argv golden, state-dir, flush sequencing, finalizer no-delete; `tests/`: `controller_state_machine.rs`, `conformance.rs`, `fault_swtpm_missing.rs`; `integration/`: `provision_and_reboot/`, `tamper_marker_survives/`, `finalizer_no_delete/`; workspace policy check: `make test-policy` passes with all four paths present", - "removalProof": "ProcessRole::Swtpm and SwtpmPreStartFlush removed after parity" - }, - { - "workItemId": "ADR046-device-003", - "specId": "ADR-046-resources-device", - "specPath": "docs/specs/ADR-046-resources-device.md", - "dependencyOwner": "ADR046-device-001; device-usbip provider owner", - "currentSource": "`packages/d2b-contracts/src/usbip.rs` (USBIP DTOs, SYSFS_BUS_ID_MAX, bus-ID validation — implemented-and-reachable); `packages/d2b-core/src/bundle_resolver.rs` USBIP intents; `packages/d2b-core/src/privileges.rs` authz rows; `packages/d2bd/src/usbip_state_machine.rs` (typed per-busid bring-up state machine, step order: modprobe→lock→withhold→firewall→backend→bind→proxy — implemented-and-reachable); `packages/d2bd/src/usbipd_perenv_autostart.rs` (per-env usbipd daemon autostart — implemented-and-reachable); `packages/d2bd/src/usbip_reconcile_state.rs` (restart-safe reconciler state model — implemented-but-unwired); old Workload Nix option: `nixos-modules/options-realms-workloads.nix` `d2b.vms..usbip.*` (generated-or-eval-contract); `nixos-modules/components/usbip.nix`", - "reuseSource": null, - "reuseAction": "extract and adapt", - "destination": "`packages/d2b-provider-device-usbip/src/` (controller, daemon Process, bind/unbind EphemeralProcess, firewall); `packages/d2b-provider-device-usbip/tests/` (hermetic Cargo integration); `packages/d2b-provider-device-usbip/integration/` (container/Host scenarios); `packages/d2b-provider-device-usbip/README.md`", - "detailedDesign": "Device spec/status; bus ID validation; firewall rule ownership-marker; bind/unbind EphemeralProcess; per-Device daemon Process (owned by device-usbip; Network supplies dependency/firewall interface); Nix emitter; all four required crate paths present (see \"Provider crate layout\")", - "integration": "Zone resource store; broker `UsbipBindFirewallRule`; nftables marker", - "dataMigration": "None; full reset", - "validation": "`src/`: bus ID corpus, firewall marker format, EphemeralProcess creation; `tests/`: `arbitration_conflict.rs`, `conformance.rs`, `firewall_marker.rs`, `explicit_attach_split.rs`; `integration/`: `arbitration_conflict/`, `busid_bind_cycle/`, `network_firewall_coexistence/`; workspace policy check: `make test-policy` passes with all four paths present", - "removalProof": "ProcessRole::Usbip removed after parity" - }, - { - "workItemId": "ADR046-device-004", - "specId": "ADR-046-resources-device", - "specPath": "docs/specs/ADR-046-resources-device.md", - "dependencyOwner": "ADR046-device-001; device-security-key provider owner", - "currentSource": "`packages/d2b-contracts/src/security_key.rs` (DTOs — implemented-and-reachable); `packages/d2b-core/src/privileges_w3.rs` (W3BrokerOperation — implemented-and-reachable); **KEY: relay is in d2bd** — `packages/d2bd/src/security_key.rs` (CTAPHID relay: CID translation, SO_PEERCRED, hidraw async fd, accept loop — implemented-and-reachable) and `packages/d2bd/src/lib.rs:start_sk_accept_loop` (ProcessRole::SecurityKeyFrontend dispatch — implemented-and-reachable); **guest binary**: `packages/d2b-sk-frontend/src/` (static UHID frontend — implemented-and-reachable); old Workload Nix option: `nixos-modules/options-realms-workloads.nix` `d2b.vms..security_key.*`; `nixos-modules/components/security-key-guest.nix`", - "reuseSource": null, - "reuseAction": "extract and adapt", - "destination": "`packages/d2b-provider-device-security-key/src/` (controller, relay Process, guest frontend Process, lease/session ring); `packages/d2b-provider-device-security-key/tests/` (hermetic Cargo integration); `packages/d2b-provider-device-security-key/integration/` (container/Host/Guest scenarios); `packages/d2b-provider-device-security-key/README.md`", - "detailedDesign": "Device spec/status; unprivileged relay Process (`device--sk-relay`); guest frontend Process (`device--sk-frontend`, `executionRef: Guest/`); ceremony/CID/lease/session ring (max 1 session per Device); broker hidraw-only access; mandatory Core-derived `(Host, physical-usb-backing, opaqueKeyDigest)` claim shared with every USB Provider before effects; Nix emitter; all four required crate paths present (see \"Provider crate layout\")", - "integration": "Zone resource store; broker `SecurityKeyOpenDevice`/`SecurityKeyApplyUdevRules`; Guest frontend module", - "dataMigration": "None; full reset", - "validation": "`src/`: lease transitions, session ring eviction, broker op path-free, CID round-trip; `tests/`: `lease_state_machine.rs`, `session_ring.rs`, `mutual_exclusion.rs` proves security-key and USB implementations resolve one token to a byte-identical physical backing key and the loser receives `physical-usb-backing-conflict` before any effect, `conformance.rs`, `guest_frontend_process.rs`; `integration/`: `lease_acquire_cancel/`, `session_ring_capacity/`, `guest_frontend_connect/`; workspace policy check: `make test-policy` passes with all four paths present", - "removalProof": "ProcessRole::SecurityKeyFrontend removed after parity" - }, - { - "workItemId": "ADR046-device-005", - "specId": "ADR-046-resources-device", - "specPath": "docs/specs/ADR-046-resources-device.md", - "dependencyOwner": "ADR046-device-001; device-gpu provider owner", - "currentSource": "`packages/d2b-host/src/gpu_argv.rs`, `video_argv.rs`; `packages/d2b-core/src/bundle_resolver.rs` Gpu/GpuRenderNode/Video; `nixos-modules/components/graphics.nix`, `video/guest.nix`", - "reuseSource": null, - "reuseAction": "extract and adapt", - "destination": "`packages/d2b-provider-device-gpu/src/` (controller, GPU/render-node/video worker Processes, broker token set); `packages/d2b-provider-device-gpu/tests/` (hermetic Cargo integration); `packages/d2b-provider-device-gpu/integration/` (container/Host/Guest scenarios); `packages/d2b-provider-device-gpu/README.md`", - "detailedDesign": "Combined Device spec/status; GPU worker Process (`device--gpu`, exclusive); render-node Process (`device--render-node`, exclusive default, shared when explicit); video-decoder Process (`device--video`); broker token set; wire-contract constants; shared render-node arbitration enforcement; Nix emitter; all four required crate paths present (see \"Provider crate layout\")", - "integration": "Zone resource store; broker `SpawnRunner`/`OpenDevice`; Display Provider device consumption", - "dataMigration": "None; full reset", - "validation": "`src/`: process role selection, wire-constant snapshot, render-node vs full-GPU branching; `tests/`: `combined_reconcile.rs`, `render_node_enforcement.rs`, `wire_constant_snapshot.rs`, `conformance.rs`; `integration/`: `gpu_worker_start/`, `render_node_shared/`, `video_dependency/`; workspace policy check: `make test-policy` passes with all four paths present", - "removalProof": "ProcessRole::Gpu, GpuRenderNode, Video removed after parity" - }, - { - "workItemId": "ADR046-device-006", - "specId": "ADR-046-resources-device", - "specPath": "docs/specs/ADR-046-resources-device.md", - "dependencyOwner": "ADR046-device-001 through ADR046-device-005; Nix integrator", - "currentSource": "`nixos-modules/components/tpm.nix`, `usbip.nix`, `security-key-guest.nix`, `video/guest.nix`, `graphics.nix`; `nixos-modules/assertions.nix`; **old Workload Nix namespace**: `nixos-modules/options-realms-workloads.nix` (`d2b.vms..tpm.enable`, `d2b.vms..graphics.enable`, `d2b.vms..usbip.*` — generated-or-eval-contract; v3 replaces `d2b.vms.*` with `d2b.zones.*`)", - "reuseSource": null, - "reuseAction": "adapt", - "destination": "`nixos-modules/resources-device.nix`; `nixos-modules/bundle-artifacts.nix` (bundle emission for resource store); `nixos-modules/assertions.nix` (six eval-time device assertions)", - "detailedDesign": "Nix authoring shape `d2b.zones..resources. = { type = \"Device\"; metadata.ownerRef = ...; spec = { ...exact ResourceSpec fields... }; };` as specified in \"Nix configuration\" section; `metadata.name`/`metadata.zone`/`apiVersion` derived automatically; `status` and Core management fields (`managedBy`, `configurationGeneration`, uid, generation, revision, timestamps, finalizers) omitted from emitted JSON; `spec` field names/types/defaults identical to ResourceTypeSchema with no renaming; per-Provider `spec.provider.settings` validated against signed Provider schema; no `artifactId` or store-path fields in Device `spec.provider.settings` — binary paths are resolved from Provider package closures; Credential-ref enforcement; artifact catalog emitted as a separate private integrity-pinned map (ID→type/digest/closure) by its own emitter; six eval-time validation assertions; canonical sorted-key full resource-envelope JSON emission (`apiVersion`, `type`, `metadata`, `spec` only); Zone resource bundle with `contentDigest` as specified in \"Zone resource bundle/generation\" section; Core sets `metadata.managedBy=configuration` and `metadata.configurationGeneration` at activation", - "integration": "Resource store Nix emitter; artifact catalog emitted separately by Provider/system resource emitter (not by this emitter); device resource JSON output; Zone generation object including `priorGeneration`, `pendingDeletion`, `cleanupStatus` fields; cleanup contract logic belongs in the Core configuration handler (`packages/d2b-core-controller/src/configuration.rs`) consuming the generation from this emitter", - "dataMigration": "Consumers migrate from per-VM options to Zone Device declarations; data migration guide references \"Nix configuration\" section migration table", - "validation": "nix-unit: `device-tpm-eval.nix`, `device-usbip-eval.nix`, `device-security-key-eval.nix`, `device-gpu-eval.nix`, `device-schema-validation.nix`, `device-gen-cleanup-eval.nix`, `device-bundle-canonical.nix`, `device-inline-secret-rejected.nix`, `device-artifact-catalog.nix`; contract tests: `device_resource_schema.rs`, `device_bundle_canonical.rs`, `device_gen_cleanup.rs`", - "removalProof": "Nix option `d2b.vms..tpm.enable` etc. retained until v3 reset" - }, - { - "workItemId": "ADR046-device-007", - "specId": "ADR-046-resources-device", - "specPath": "docs/specs/ADR-046-resources-device.md", - "dependencyOwner": "ADR046-device-006; Zone runtime implementer", - "currentSource": "None (new work; no equivalent in v3 baseline or main a1cc0b2d)", - "reuseSource": null, - "reuseAction": "new", - "destination": "`packages/d2b-core-controller/src/configuration.rs`; `packages/d2b-contract-tests/tests/device_gen_cleanup.rs`", - "detailedDesign": "Implement the cleanup contract described in \"Zone generation and cleanup\": (1) on new generation activation, diff `resources` against prior generation's `resources` by (type, name) — resources absent from new generation that have `managedBy=configuration` go into `pendingDeletion`; (2) Zone phase transitions to `Degraded/pending-cleanup` until all items in `pendingDeletion` reach terminal Delete; (3) items in `pendingDeletion` with `managedBy=controller` or `managedBy=api` are rejected with `cleanup-config-ownership-mismatch`; (4) `managedBy=controller` and `managedBy=api` resources are never touched by generation cleanup — `cleanup-controller-resource-protected` emitted if attempted; `managedBy=api` resources persist until explicit Delete; (5) prior generations retained by count: default 3, range 1..16, configured via `d2b.zones..retainedGenerations`; (6) each deletion is non-blocking; (7) finalizer-stuck timeout emits `cleanup-finalizer-stuck` and leaves Zone in `Degraded/pending-cleanup`; (8) all deletions emit `config-resource-deletion-requested` audit record with digested resource identity", - "integration": "Consumes Zone resource bundle from ADR046-device-006 emitter; drives Device Provider finalizers via normal resource-Delete protocol; feeds Zone status conditions `GenerationCleanPending` and `GenerationCleanError`", - "dataMigration": "Full d2b 3.0 reset; no prior generation cleanup state or v2 Device resource import", - "validation": "Fast hermetic (D094): in-process Rust selectors under the owning crate `tests/` — `packages/d2b-core-controller/tests/gen_cleanup.rs`, `packages/d2b-core-controller/tests/gen_cleanup_controller_protected.rs`, `packages/d2b-core-controller/tests/gen_cleanup_audit.rs` — driven by a fake Zone store/clock with deterministic ordering and no wall-clock sleep; slower coverage reuses the existing integration directory scenarios `tests/integration/containers/device-gen-cleanup.sh`, `device-controller-resource-protected.sh`, and `device-gen-cleanup-audit.sh`; feasibility proof: generation cleanup with fake Zone runtime. No new top-level shell gate is added.", - "removalProof": "None — net-new; no prior owner to remove" - }, - { - "workItemId": "ADR046-device-008", - "specId": "ADR-046-resources-device", - "specPath": "docs/specs/ADR-046-resources-device.md", - "dependencyOwner": "ADR046-device-002 through ADR046-device-005; workspace/tooling maintainer", - "currentSource": "`packages/xtask/src/main.rs` (existing `gen-*` and `check-*` commands — implemented-and-reachable); `packages/d2b-contract-tests/tests/workspace_policy.rs` (existing workspace policy tests — implemented-and-reachable)", - "reuseSource": null, - "reuseAction": "extend", - "destination": "`packages/xtask/src/main.rs` (`check-provider-layout` subcommand); `packages/d2b-contract-tests/tests/workspace_policy.rs` (provider-layout policy assertions)", - "detailedDesign": "Add `cargo xtask check-provider-layout`: enumerate workspace members matching `d2b-provider-*`; for each, assert `src/`, `tests/`, `integration/`, and `README.md` all exist relative to the crate root; report all missing paths before failing; no opt-out flag. Add companion test in `workspace_policy.rs` that asserts the same invariant against the static crate list in `Cargo.toml`. Wire `cargo xtask check-provider-layout` into `make test-policy` alongside existing workspace naming and sort checks. The check must also run in CI as part of the policy gate.", - "integration": "`make test-policy`; `make check`; GitHub CI policy job", - "dataMigration": "None — full d2b 3.0 reset; no prior state to migrate", - "validation": "Policy gate passes once all four Device Provider crates exist with required paths; gate fails predictably when any path is removed from a Provider crate fixture; test fixture crate with one missing path must produce a named error identifying the exact missing path", - "removalProof": "N/A (new tooling; not removed)" - }, - { - "workItemId": "ADR046-device-tpm-001", - "specId": "ADR-046-provider-device-tpm", - "specPath": "docs/specs/providers/ADR-046-provider-device-tpm.md", - "dependencyOwner": "P0; unblocked; owner: `d2b-provider-device-tpm` crate", - "currentSource": "No existing provider crate; baseline TPM behavior is in `nixos-modules/components/tpm.nix`, `packages/d2b-host/src/swtpm_argv.rs`, and broker/daemon swtpm paths listed in §18", - "reuseSource": null, - "reuseAction": "net-new crate scaffold; later items adapt baseline TPM behavior into the new Provider boundary", - "destination": "packages/d2b-provider-device-tpm/{src/,tests/,integration/README.md,README.md}; Cargo workspace membership", - "detailedDesign": "Crate scaffold: create `packages/d2b-provider-device-tpm/` with `src/`, `tests/`, `integration/README.md`, and `README.md`; add it to the Cargo workspace; workspace policy test must pass.", - "integration": "Workspace policy and Cargo consume the new crate; all controller, effect-port, resource-builder, status, Nix, and integration work lands under this scaffold.", - "dataMigration": "None — scaffold only; TPM state migration is covered by later Volume/Nix work per §17.3", - "validation": "Workspace policy test for required crate paths and Cargo workspace membership", - "removalProof": "None — net-new crate scaffold; legacy TPM owners are removed only by later parity/removal items" - }, - { - "workItemId": "ADR046-device-tpm-002", - "specId": "ADR-046-provider-device-tpm", - "specPath": "docs/specs/providers/ADR-046-provider-device-tpm.md", - "dependencyOwner": "P0; blocked by ADR046-device-tpm-001; owner: device-tpm effect boundary", - "currentSource": "`PrepareSwtpmDir` in `packages/d2b-priv-broker/src/ops/swtpm_dir.rs` and `SpawnRunner { role: Swtpm }` in `packages/d2b-priv-broker/src/ops/spawn_runner.rs` remain privileged executors, but the controller must not import broker crates", - "reuseSource": null, - "reuseAction": "wrap privileged effects behind an injected async `TpmEffectPort`; keep broker operations only behind `volume-local` and `system-minijail`", - "destination": "packages/d2b-provider-device-tpm/src/{effect_port.rs,effect_impl.rs}; packages/d2b-provider-device-tpm/tests/effect_fake.rs", - "detailedDesign": "TpmEffectPort and FakeTpmEffectPort: implement the effect trait, typed TPM EndpointRef handoff, and fake test port. Prove non-test files contain no `use d2b_priv_broker::` and the controller sees only opaque resource IDs and EndpointRefs.", - "integration": "Device controller calls `TpmEffectPort`; ResourceClient-backed implementation talks to ResourceAPI/ComponentSession; `volume-local` and `system-minijail` translate resource operations into broker effects.", - "dataMigration": "Existing TPM state migration follows §17.3: the old `/var/lib/d2b/vms//swtpm/` directory moves to the controller-created Volume path with the provisioning marker preserved and re-keyed; this item must not silently recreate missing state.", - "validation": "`tests/effect_fake.rs`; static proof that non-test files do not import `d2b_priv_broker`", - "removalProof": "Direct broker references in controller/daemon TPM paths are superseded by the effect-port/resource-provider boundary; final deletion is ADR046-device-tpm-013" - }, - { - "workItemId": "ADR046-device-tpm-003", - "specId": "ADR-046-provider-device-tpm", - "specPath": "docs/specs/providers/ADR-046-provider-device-tpm.md", - "dependencyOwner": "P0; blocked by ADR046-device-tpm-002; owner: device-tpm controller FSM", - "currentSource": "Current direct daemon/broker swtpm lifecycle call sites in `packages/d2bd/src/*` are superseded; controller algorithm is specified in §11.1", - "reuseSource": null, - "reuseAction": "replace direct daemon lifecycle with Provider reconcile against `FakeTpmEffectPort` and resource status", - "destination": "packages/d2b-provider-device-tpm/src/controller.rs; packages/d2b-provider-device-tpm/tests/controller_fsm.rs", - "detailedDesign": "Controller reconcile state machine: implement the Device reconcile algorithm from §11.1 against `FakeTpmEffectPort`, covering happy path, Volume not-ready, marker fail-closed, flush failure, swtpm maxRestarts, and finalizer behavior where Process is deleted and Volume retained.", - "integration": "Resource watches drive the controller; controller creates/observes Volume, Process, EphemeralProcess, and Endpoint resources through `TpmEffectPort`; Device status/finalizers expose outcomes to the ResourceAPI.", - "dataMigration": "Existing TPM state migration follows §17.3: the old `/var/lib/d2b/vms//swtpm/` directory moves to the controller-created Volume path with the provisioning marker preserved and re-keyed; this item must not silently recreate missing state.", - "validation": "`tests/controller_fsm.rs` covering happy path, Volume not-ready, marker fail-closed, flush failed, swtpm maxRestarts, and finalizer behavior", - "removalProof": "Direct daemon swtpm lifecycle logic is removable after this Provider reconcile FSM reaches parity and ADR046-device-tpm-013 removes the old call sites" - }, - { - "workItemId": "ADR046-device-tpm-004", - "specId": "ADR-046-provider-device-tpm", - "specPath": "docs/specs/providers/ADR-046-provider-device-tpm.md", - "dependencyOwner": "P0; blocked by ADR046-device-tpm-001; owner: device-tpm resource builders", - "currentSource": "`nixos-modules/components/tpm.nix` declares TPM enablement today; §17.3 defines migration from the existing swtpm directory and marker", - "reuseSource": null, - "reuseAction": "replace VM-level TPM option/state path with controller-created Device-owned Volume spec", - "destination": "packages/d2b-provider-device-tpm/src/resources.rs; packages/d2b-provider-device-tpm/tests/volume_create.rs", - "detailedDesign": "Controller-created Volume spec: implement `build_tpm_state_volume_spec` with `cleanupPolicy: never`, `repairPolicy: fail-closed`, `adoptionPolicy: quarantine-on-ambiguity`, `sensitivity: secret-adjacent`, required invariants, `source.sourceId`, no `hostPath`, no top-level identityMarker/persistenceClass/quotaBytes/stateSchema, `ownerRef: Device/`, `managedBy: controller`, empty attachments, and `quota: null`.", - "integration": "Device controller creates the TPM data Volume; `volume-local` materializes/protects state and marker; swtpm Process mounts the Volume; Guest runtime receives only EndpointRefs.", - "dataMigration": "Existing TPM state migration follows §17.3: the old `/var/lib/d2b/vms//swtpm/` directory moves to the controller-created Volume path with the provisioning marker preserved and re-keyed; this item must not silently recreate missing state.", - "validation": "`tests/volume_create.rs` proving every canonical Volume field and forbidden field listed in this item", - "removalProof": "`nixos-modules/components/tpm.nix` state-path ownership is superseded by the Device-owned Volume once migration and Nix roundtrip are complete" - }, - { - "workItemId": "ADR046-device-tpm-005", - "specId": "ADR-046-provider-device-tpm", - "specPath": "docs/specs/providers/ADR-046-provider-device-tpm.md", - "dependencyOwner": "P0; blocked by ADR046-device-tpm-004; owner: device-tpm Process resource builder", - "currentSource": "`SwtpmArgvInput` in `packages/d2b-host/src/swtpm_argv.rs`; `ProcessRole::Swtpm` in `packages/d2b-core/src/processes.rs`; `minijail_swtpm_video.rs` contract tests", - "reuseSource": null, - "reuseAction": "extract swtpm argv/sandbox intent into canonical Process resources; remove caller-supplied binary path, UID, GID, and socket path fields", - "destination": "packages/d2b-provider-device-tpm/src/resources.rs; Process spec tests under packages/d2b-provider-device-tpm/tests/", - "detailedDesign": "Canonical swtpm Process spec: implement `build_swtpm_process_spec` with `readOnlyRoot: true`, `userNamespace.mappingClass: process-principal-root`, namespace classes `[pid, mount, user]`, empty capability classes, `seccompClass: w1-swtpm`, two Device-owned Endpoint resources (`tpm` and `ctrl`), `mounts[0].required: true`, and no socket path, binary path, UID integer, or GID integer in any spec field.", - "integration": "Controller emits the Process spec; `system-minijail` consumes it and invokes broker `SpawnRunner`; Endpoint resources publish TPM and control sockets for downstream consumers.", - "dataMigration": "Existing TPM state migration follows §17.3: the old `/var/lib/d2b/vms//swtpm/` directory moves to the controller-created Volume path with the provisioning marker preserved and re-keyed; this item must not silently recreate missing state.", - "validation": "Process spec golden tests proving all required and forbidden fields; preserved `minijail_swtpm_video.rs` contract tests", - "removalProof": "`ProcessRole::Swtpm` and swtpm argv builder call sites can be retired after the canonical Process resource covers runner launch" - }, - { - "workItemId": "ADR046-device-tpm-006", - "specId": "ADR-046-provider-device-tpm", - "specPath": "docs/specs/providers/ADR-046-provider-device-tpm.md", - "dependencyOwner": "P0; blocked by ADR046-device-tpm-003; owner: device-tpm EphemeralProcess resource builder", - "currentSource": "`SwtpmIoctlFlushInput` in `packages/d2b-host/src/swtpm_argv.rs`; `ProcessRole::SwtpmPreStartFlush` in `packages/d2b-core/src/processes.rs`", - "reuseSource": null, - "reuseAction": "extract flush intent into mandatory EphemeralProcess resource; remove configurable `startupClear` path and caller-supplied binary path fields", - "destination": "packages/d2b-provider-device-tpm/src/resources.rs; packages/d2b-provider-device-tpm/tests/flush_mandatory.rs", - "detailedDesign": "Mandatory flush EphemeralProcess spec: implement `build_flush_ephemeral_process_spec`; no `startupClear` field exists; flush is always created before swtpm Process start with no skip path; TTLs are `successfulTtl: \"1h\"` and `failedTtl: \"24h\"`; no userNamespace on flush Process; deadlines are `startDeadline: \"30s\"` and `runtimeDeadline: \"60s\"`.", - "integration": "Controller inserts the EphemeralProcess before every swtpm activation cycle; Process provider runs the flush against the control Endpoint fd before the long-lived swtpm Process becomes Ready.", - "dataMigration": "Existing TPM state migration follows §17.3: the old `/var/lib/d2b/vms//swtpm/` directory moves to the controller-created Volume path with the provisioning marker preserved and re-keyed; this item must not silently recreate missing state.", - "validation": "`tests/flush_mandatory.rs` plus contract proof that user-NS is long-lived only", - "removalProof": "`ProcessRole::SwtpmPreStartFlush` and any optional startup-clear wiring are retired after mandatory EphemeralProcess coverage is complete" - }, - { - "workItemId": "ADR046-device-tpm-007", - "specId": "ADR-046-provider-device-tpm", - "specPath": "docs/specs/providers/ADR-046-provider-device-tpm.md", - "dependencyOwner": "P1; blocked by ADR046-device-tpm-003; owner: device-tpm status builder", - "currentSource": "Existing status/path observations from direct daemon TPM paths are superseded; status fields are defined in §10", - "reuseSource": null, - "reuseAction": "net-new bounded Device status projection; do not reuse path/socket/UID/GID/PID observations", - "destination": "packages/d2b-provider-device-tpm/src/status.rs; packages/d2b-provider-device-tpm/tests/{endpoint_ref.rs,redaction.rs}", - "detailedDesign": "Device status builder: implement `build_device_status`; `tpmEndpointRef` is an `Endpoint/` ResourceRef with no opaque endpoint ID compatibility alias and never a filesystem path; `stateVolumeRef` and `swtpmProcessRef` are canonical ResourceRef strings; no path, socket name, UID, GID, PID, or pidfd appears in status; `markerStatus` is one of `verified`, `missing`, `replaced`, or `unknown`.", - "integration": "Controller writes Device status; Guest runtime Provider reads EndpointRef from Device status; CLI/support tooling reads bounded non-secret status.", - "dataMigration": "None — status is re-derived during v3 reconcile; TPM state migration remains the Volume/marker migration in §17.3", - "validation": "`tests/endpoint_ref.rs`; `tests/redaction.rs`; status builder tests for allowed `markerStatus` values", - "removalProof": "Legacy path/socket status compatibility aliases are absent; removal is proven by redaction/status tests" - }, - { - "workItemId": "ADR046-device-tpm-008", - "specId": "ADR-046-provider-device-tpm", - "specPath": "docs/specs/providers/ADR-046-provider-device-tpm.md", - "dependencyOwner": "P1; blocked by ADR046-device-tpm-007; owner: device-tpm endpoint handoff integration", - "currentSource": "Baseline guest wiring consumed socket paths from TPM sidecar state; v3 handoff is the Endpoint resource contract in §8.4 and §10.5", - "reuseSource": null, - "reuseAction": "replace path handoff with EndpointRef and Zone runtime endpoint resolver fd acquisition", - "destination": "packages/d2b-provider-device-tpm/src/{effect_port.rs,status.rs}; packages/d2b-provider-device-tpm/integration/guest_endpoint.rs", - "detailedDesign": "EndpointRef handoff: hermetic tests prove `tpmEndpointRef` is an EndpointRef and never a path; integration proves Guest runtime Provider reads `tpmEndpointRef` and obtains the socket fd from the Zone runtime endpoint resolver with no path string in Guest spec or LaunchTicket API surface.", - "integration": "Device status publishes EndpointRef; Guest runtime Provider resolves the Endpoint through the Zone endpoint resolver; LaunchTicket receives an fd, not a socket path.", - "dataMigration": "None — endpoint handoff has no state migration; TPM data migration remains §17.3", - "validation": "`tests/endpoint_ref.rs`; `integration/guest_endpoint.rs`", - "removalProof": "Socket-path handoff is superseded when all Guest runtime TPM attachment uses EndpointRef resolution" - }, - { - "workItemId": "ADR046-device-tpm-009", - "specId": "ADR-046-provider-device-tpm", - "specPath": "docs/specs/providers/ADR-046-provider-device-tpm.md", - "dependencyOwner": "P0; blocked by ADR046-device-tpm-004; owner: device-tpm marker/fail-closed tests", - "currentSource": "Existing provisioning marker behavior in `/var/lib/d2b/swtpm-markers/` is preserved and re-keyed by volume-local per §17.3", - "reuseSource": null, - "reuseAction": "preserve fail-closed marker semantics while moving ownership to Volume/Device resources", - "destination": "packages/d2b-provider-device-tpm/tests/marker_fail_closed.rs; packages/d2b-provider-device-tpm/integration/marker_tamper.rs", - "detailedDesign": "Marker fail-closed test: FakeTpmEffectPort returning `markerStatus: replaced` makes Device Failed, prevents a second `ensure_state_volume` call, and prevents swtpm Process creation. Integration physically replaces `swtpm/`; volume-local sets Volume Failed; Device fails with no auto-recovery.", - "integration": "volume-local observes marker state and reports Volume/marker status; controller maps that to Device failure and blocks Process creation; integration exercises the actual filesystem marker path through volume-local.", - "dataMigration": "Existing TPM state migration follows §17.3: the old `/var/lib/d2b/vms//swtpm/` directory moves to the controller-created Volume path with the provisioning marker preserved and re-keyed; this item must not silently recreate missing state.", - "validation": "`tests/marker_fail_closed.rs`; `integration/marker_tamper.rs`", - "removalProof": "Any silent marker recreation path is removed/proven absent by the fail-closed tests" - }, - { - "workItemId": "ADR046-device-tpm-010", - "specId": "ADR-046-provider-device-tpm", - "specPath": "docs/specs/providers/ADR-046-provider-device-tpm.md", - "dependencyOwner": "P1; blocked by ADR046-device-tpm-001; owner: device-tpm controller Process descriptor", - "currentSource": "None — net-new v3 work; no pre-ADR45 baseline equivalent", - "reuseSource": null, - "reuseAction": "net-new status-first controller Process spec; no Provider state Volume is reused or created", - "destination": "packages/d2b-provider-device-tpm/src/resources.rs; packages/d2b-provider-device-tpm/tests/controller_process.rs", - "detailedDesign": "Controller Process (status-first; no Provider state Volume): implement the controller Process spec from §4.1 with `processClass: controller`, `readOnlyRoot: true`, empty mounts, no controller-scratch namespace, no scratch mount, no `User/device-tpm-controller-system` state-layout principal, no permission to create Provider-owned Volumes, and restart re-derivation from resource store plus external marker/process observations while treating status as observation.", - "integration": "ProviderDeployment creates the controller Process from the descriptor; controller status and Operation ledger carry bounded non-secret observations; Device-owned TPM data Volume remains separate from ProviderStateSet.", - "dataMigration": "None — controller has no Provider state Volume to migrate; Device data migration remains §17.3", - "validation": "Controller Process spec tests proving the bullets in this item", - "removalProof": "None — this item prevents introduction of a Provider state Volume and has no prior state owner to remove" - }, - { - "workItemId": "ADR046-device-tpm-011", - "specId": "ADR-046-provider-device-tpm", - "specPath": "docs/specs/providers/ADR-046-provider-device-tpm.md", - "dependencyOwner": "P1; blocked by ADR046-device-tpm-001; owner: Nix Resource compiler for Device declarations", - "currentSource": "`nixos-modules/components/tpm.nix` current `d2b.vms..tpm.enable` option is replaced by the Device declaration in §17.1", - "reuseSource": null, - "reuseAction": "replace VM-level TPM enable option with v3 Device resource emission and assertions", - "destination": "nixos-modules/options-resources.nix and Nix eval/golden tests for §17.1 Device JSON", - "detailedDesign": "Nix roundtrip test: Device Nix spec from §17.1 round-trips through the Nix emitter to expected resource JSON; emitted bundle contains no Volume, Process, or EphemeralProcess resources because controller-managed resources are not in the Nix bundle.", - "integration": "Nix authoring emits only the Device and Provider resources; ResourceAPI admission hands the Device to the controller; controller creates managed resources at runtime.", - "dataMigration": "Existing TPM state migration follows §17.3: the old `/var/lib/d2b/vms//swtpm/` directory moves to the controller-created Volume path with the provisioning marker preserved and re-keyed; this item must not silently recreate missing state.", - "validation": "Nix roundtrip/golden test for §17.1 and emitted-bundle absence of controller-managed resources", - "removalProof": "`d2b.vms..tpm.enable` in `nixos-modules/components/tpm.nix` is superseded after Device Nix declaration roundtrip and migration are in place" - }, - { - "workItemId": "ADR046-device-tpm-012", - "specId": "ADR-046-provider-device-tpm", - "specPath": "docs/specs/providers/ADR-046-provider-device-tpm.md", - "dependencyOwner": "P0; blocked by ADR046-device-tpm-003; owner: device-tpm finalizer lifecycle", - "currentSource": "Current TPM state retention behavior is tied to the swtpm directory and marker; v3 retention is `cleanupPolicy: never` on the Device-owned Volume", - "reuseSource": null, - "reuseAction": "preserve TPM identity retention while moving deletion sequencing to Resource finalizers", - "destination": "packages/d2b-provider-device-tpm/src/controller.rs; packages/d2b-provider-device-tpm/tests/finalizer.rs", - "detailedDesign": "Finalizer: Volume retained on Device deletion. Hermetic tests cover Device deletion finalizer leading to swtpm Process deletion, TPM state Volume not deleted because `cleanupPolicy: never`, Volume persists, Core emits `phase=Deleted` for Device after finalizer clears, and audit carries no path/UID.", - "integration": "Resource deletion sets finalizer; controller deletes Process and retains Volume; core completes Deleted revision after finalizer clears; audit subsystem records redacted deletion outcome.", - "dataMigration": "Existing TPM state migration follows §17.3: the old `/var/lib/d2b/vms//swtpm/` directory moves to the controller-created Volume path with the provisioning marker preserved and re-keyed; this item must not silently recreate missing state.", - "validation": "`tests/finalizer.rs` proving Process deletion, retained Volume, core Deleted phase, and redacted audit", - "removalProof": "Any cleanup path that deletes TPM state on Device deletion is removed/proven absent by the finalizer test" - }, - { - "workItemId": "ADR046-device-tpm-013", - "specId": "ADR-046-provider-device-tpm", - "specPath": "docs/specs/providers/ADR-046-provider-device-tpm.md", - "dependencyOwner": "P0; blocked by ADR046-device-tpm-002; owner: device-tpm migration/removal cleanup", - "currentSource": "`packages/d2bd/src/*` direct broker/swtpm call sites; `packages/d2b-core/src/processes.rs` `ProcessRole::Swtpm` and `ProcessRole::SwtpmPreStartFlush`; `packages/d2b-host/src/swtpm_argv.rs` argv builders", - "reuseSource": null, - "reuseAction": "remove direct broker references from daemon and move argv builders into the Provider with binary path fields removed; retain broker ops only behind resource providers", - "destination": "packages/d2bd/src/*; packages/d2b-core/src/processes.rs; packages/d2b-provider-device-tpm/src/; packages/d2b-host/src/swtpm_argv.rs", - "detailedDesign": "Remove direct broker references: remove pre-ADR-0046 daemon swtpm broker call sites, retire `ProcessRole::Swtpm` and `ProcessRole::SwtpmPreStartFlush`, move argv builders from `d2b-host/src/swtpm_argv.rs` to `d2b-provider-device-tpm/src/` with binary path fields removed, while retaining `d2b-priv-broker/src/ops/swtpm_dir.rs` for `volume-local` and `spawn_runner.rs` for `system-minijail`.", - "integration": "After controller/effect-port parity, daemon no longer calls TPM broker ops; Resource providers invoke broker effects from Volume and Process reconciliation; contract tests ensure swtpm sandbox/readiness still hold.", - "dataMigration": "Existing TPM state migration follows §17.3: the old `/var/lib/d2b/vms//swtpm/` directory moves to the controller-created Volume path with the provisioning marker preserved and re-keyed; this item must not silently recreate missing state.", - "validation": "Static search/proof for no direct broker swtpm references in daemon/controller plus preserved swtpm contract tests", - "removalProof": "Direct daemon swtpm call sites removed; `ProcessRole::Swtpm` and `ProcessRole::SwtpmPreStartFlush` retired; `d2b-host/src/swtpm_argv.rs` no longer owns Provider argv builders" - }, - { - "workItemId": "ADR046-display-001", - "specId": "ADR-046-provider-display-wayland", - "specPath": "docs/specs/providers/ADR-046-provider-display-wayland.md", - "dependencyOwner": "`ADR046-provider-001`, `ADR046-process-001`; display Provider owner", - "currentSource": "`packages/d2b-wayland-proxy/`, `packages/d2b-host/src/wayland_proxy_argv.rs`, `packages/d2b-host-providers/src/lib.rs`, `packages/d2b-realm-provider/src/{conformance,mock}.rs`", - "reuseSource": null, - "reuseAction": "extract and adapt", - "destination": "`packages/d2b-provider-display-wayland/src/`", - "detailedDesign": "Create Provider crate layout (`src/`, `tests/`, `integration/`, `README.md`); extract `FilterPolicy`, `PolicyInput`, `DecorationManager`, `BridgeConfig`, `ProxyReadinessEvent`, `ProxyIdentity`, `ClipboardGlobalDisposition` from `d2b-wayland-proxy`; implement single `display-controller` using toolkit `ResourceClient`/`Reconciler` to manage both `WaylandSession` and `WaylandPolicy` resources; implement `display-user-portal` as a separately sandboxed user-domain service that receives pre-opened compositor connections from the fixed user session supervisor (never reads `WAYLAND_DISPLAY`), validates same-user via `SO_PEERCRED`, and issues bounded per-session compositor connection attachment grants to `ProviderSupervisor`; implement LaunchTicket composition with opaque attachment grant handles (compositor, GPU endpoint) so no fd transits through the controller; implement pool-slot acquisition using opaque hash-derived account names (`d2b-wlp-` for bundle sessions, `d2b-wlp-p` for pool) that fails closed with `NoPrincipalAvailable` when all pool slots are occupied; implement `wl-cross-domain-proxy` guest frontend binary at `src/bin/wl-cross-domain-proxy.rs` within the Provider package; implement provider-neutral `display_fails_closed_when_unsupported` conformance; assert D087 status-first state: ProviderStateSet is empty and bounded operational state is in resource status plus the core Operation ledger", - "integration": "Zone Provider resource/catalog → `WaylandSession` controller (in `display-controller`) → Process resources → supervisor ticket; framework enrollment creates no Provider state Volume for display components", - "dataMigration": "Full reset; no v2 session compatibility", - "validation": "conformance vectors, fake-bus tests, filter policy golden tests (migrate from `packages/d2b-wayland-proxy/`), redaction/audit contract tests, no-fallback test, `controller_unknown_interface_fails_closed`, `controller_finalizer_ambiguous_retained`, `user_portal_unavailable_blocks_pending`, `provider_state_set_empty_status_first`", - "removalProof": "`packages/d2b-host-providers/src/lib.rs` `LocalCrossDomainWaylandProvider` removed only after `display-controller` passes conformance; `packages/d2b-host/src/wayland_proxy_argv.rs` removed only after Process template sealing verified" - }, - { - "workItemId": "ADR046-display-002", - "specId": "ADR-046-provider-display-wayland", - "specPath": "docs/specs/providers/ADR-046-provider-display-wayland.md", - "dependencyOwner": "`ADR046-display-001`; Nix integrator", - "currentSource": "`nixos-modules/components/graphics.nix` `graphics.waylandProxy.*`; `nixos-modules/ui-colors.nix` VM border color resolution; `nixos-modules/options-vms.nix` `graphics.*` options", - "reuseSource": null, - "reuseAction": "adapt", - "destination": "Zone bundle emitter for `WaylandSession` / `WaylandPolicy` ResourceSpecs under `d2b.zones..resources.*`; `WaylandSession` color resolution in Nix bundle emitter", - "detailedDesign": "Emit `WaylandSession` and `WaylandPolicy` ResourceSpecs from Nix; derive colors from `d2b-niri-border` palette (§16.2); enforce `crossDomainTrusted = true` at eval time; emit v3 `display-wayland-provider` artifact catalog entry with `spec.config.principalPoolSize` (default 4, bounds 1..32) and `spec.config.runtimeVolumePolicyId`; provision opaque hash-derived OS accounts (`d2b-wlp-` for bundle-declared sessions, `d2b-wlp-p` for pool; `isSystemUser = true`, no raw UID/GID) for all sessions and pool slots; validate that account names are unique and do not expose guest/session identity; validate signed Provider config schema; enforce eval-time bound that bundle session count + pool size does not exceed provisioned principal count; configure `display-controller` and `display-user-portal` in the signed ProviderDeployment manifest; nix-unit tests for color derivation, opaque principal name derivation, spec shape, JSON round-trip, and principal provisioning uniqueness", - "integration": "Zone NixOS module system → bundle emitter → `/etc/d2b/zones//bundle/generation-N.json`", - "dataMigration": "Legacy `graphics.waylandProxy.*` Nix options accepted with deprecation warning during migration window; removed after parity", - "validation": "nix-unit spec-shape tests, eval-time guard tests (crossDomainTrusted=false rejected), color derivation golden tests, principal provisioning count bound test", - "removalProof": "`nixos-modules/components/graphics.nix` `graphics.waylandProxy.*` and `nixos-modules/ui-colors.nix` VM color resolution removed only after Zone bundle emitter parity verified by a full nix-unit pass" - }, - { - "workItemId": "ADR046-display-003", - "specId": "ADR-046-provider-display-wayland", - "specPath": "docs/specs/providers/ADR-046-provider-display-wayland.md", - "dependencyOwner": "`ADR046-display-001`; telemetry/audit owner", - "currentSource": "`packages/d2b-wayland-proxy/src/diag.rs` (rate-limited bounded diagnostics)", - "reuseSource": null, - "reuseAction": "extract and adapt", - "destination": "`packages/d2b-provider-display-wayland/src/audit.rs`, `packages/d2b-provider-display-wayland/src/metrics.rs`", - "detailedDesign": "Implement audit record types for all events in §14.1; implement OTEL metric counters/gauges in §14.2; adapt `DiagRateLimiter` to use closed label sets; validate that no socket path, user identity, window title, or app-id appears in any log/audit/metric surface", - "integration": "Providers emit via Zone telemetry emitter; audit records committed before operation completion", - "dataMigration": "None — full d2b 3.0 reset; no prior state to migrate", - "validation": "Redaction contract tests (`policy_observability.rs` pattern), audit record schema tests, label-cardinality tests", - "removalProof": "N/A (new code)" - }, - { - "workItemId": "ADR046-display-004", - "specId": "ADR-046-provider-display-wayland", - "specPath": "docs/specs/providers/ADR-046-provider-display-wayland.md", - "dependencyOwner": "`ADR046-display-001`; integration test owner", - "currentSource": "`tests/integration/` test orchestration structure", - "reuseSource": null, - "reuseAction": "new", - "destination": "`packages/d2b-provider-display-wayland/integration/`", - "detailedDesign": "Container/Host/Guest/cross-process integration fixtures for: (a) end-to-end WaylandSession create → proxy Process ready → guest frontend ready; (b) GPU endpoint unavailable → Pending; (c) proxy crash → Failed backoff; (d) policy policy warning production; (e) clipboard boundary denial; (f) crossDomainTrusted=false admission rejection. Follows `ADR-046-provider-model-and-packaging` integration/ convention.", - "integration": "Invoked by existing repository test orchestration (`make test-integration` / container lane)", - "dataMigration": "None — full d2b 3.0 reset; no prior state to migrate", - "validation": "All six scenarios above pass; no socket paths in test output", - "removalProof": "N/A (new code)" - }, - { - "workItemId": "ADR046-doctor-001", - "specId": "ADR-046-telemetry-audit-and-support", - "specPath": "docs/specs/ADR-046-telemetry-audit-and-support.md", - "dependencyOwner": "ADR046-core-001 + ADR046-audit-001; CLI/doctor owner", - "currentSource": "`packages/d2b/tests/host_doctor_contract.rs` (env-redirect sandbox: `D2B_BROKER_SOCKET`, `D2B_PUBLIC_SOCKET`, `D2B_DAEMON_STATE_DIR`, `D2B_METRICS_URL`, `D2B_MANIFEST_PATH`; `doctor::render_summary` JSON envelope fields: `command`, `mode`, `broker_ready`, per-check `status`+`data`, `summary`, `exitCode`); `packages/d2bd/src/audit_check.rs` (`defects` array audit-chain validation pattern); `packages/d2bd/src/lib.rs` (doctor read-only path: `host doctor --read-only` reads from `D2B_DAEMON_STATE_DIR`, pidfd_table file, kernel-module check file, metrics URL)", - "reuseSource": null, - "reuseAction": "adapt env-redirect sandbox test harness; adapt `defects` array pattern for `audit-hash-chain-clean` check; adapt `broker_ready` → `zone_phase` field", - "destination": "`packages/d2b/src/zone_doctor.rs`, `packages/d2b/tests/zone_doctor_contract.rs`", - "detailedDesign": "`d2b zone doctor [--zone ] [--json]` reads resource status from Zone API (read-only verb), OTEL self-metrics from `observability-otel` Provider endpoint (optional), and audit segment inventory from `d2b-audit` segment reader. Named check set from this spec. Exit 0 on all-ready; 1 on any warn/error. Env-redirect sandbox for all test fixtures. Current `MANIFEST_JSON` fixture pattern adapted: `\"_observability\": {\"enabled\": false}` test ensures OTEL probe short-circuits cleanly.", - "integration": "`d2b` CLI → Zone resource API status reads + audit segment reader", - "dataMigration": "Full d2b 3.0 reset; no v2 state/config import", - "validation": "`zone_doctor_contract.rs`: all-ready/degraded/quarantine/otel-absent/audit-absent fixtures; no resource names/paths/argv/PIDs; `zone_phase` field present; no legacy `broker_ready` field", - "removalProof": "`d2b host doctor` retained until `d2b zone doctor` covers all check parity" - }, - { - "workItemId": "ADR046-doctor-002", - "specId": "ADR-046-telemetry-audit-and-support", - "specPath": "docs/specs/ADR-046-telemetry-audit-and-support.md", - "dependencyOwner": "ADR046-doctor-001; CLI/doctor owner", - "currentSource": "`packages/d2b/tests/host_doctor_contract.rs` (env-redirect sandbox scaffold — no current `support-bundle` equivalent exists)", - "reuseSource": null, - "reuseAction": "reuse env-redirect sandbox scaffold", - "destination": "`packages/d2b/src/zone_support_bundle.rs`, `packages/d2b/tests/zone_support_bundle_contract.rs`", - "detailedDesign": "`d2b zone support-bundle [--zone ]` requires `support-bundle` verb. Reads bounded resource status snapshots (32 per type, 512 total; metadata + status only; no spec bytes; no `metadata.name`), controller queue depths, schema catalog (names+versions only), audit segment inventory, OTEL collector metrics summary, bounded structured log ring (2000 entries). NDJSON output. On quarantine: `bundle_completeness: \"partial\"`, exit 1.", - "integration": "`d2b` CLI → Zone resource API list (status subresource only) + controller introspection + audit segment reader + OTEL self-metrics", - "dataMigration": "Full d2b 3.0 reset; no v2 state/config import", - "validation": "`zone_support_bundle_contract.rs`: complete/partial bundles; no spec/name/path/argv; field completeness", - "removalProof": "None — net-new; no prior owner to remove" - }, - { - "workItemId": "ADR046-exec-001", - "specId": "ADR-046-resources-host-guest-process-user", - "specPath": "docs/specs/ADR-046-resources-host-guest-process-user.md", - "dependencyOwner": "W0 shared contract root; `d2b-contracts`", - "currentSource": "`packages/d2b-core/src/processes.rs`: `ProcessRole` (18 variants), `ProcessNode`, `RoleProfile`, `NamespaceSet`, `MountPolicy`, `CgroupPlacement`, `ReadinessPredicate`; `packages/d2b-core/src/minijail_profile.rs`: `MinijailProfile`, `UserNamespaceProfile`, `NamespaceSet`, `MountPolicy`, `BindMount`, `CgroupPlacement`; `packages/d2b-core/src/storage.rs`: `StoragePathSpec`, `AclGrant`, `CleanupPolicy`, `RepairPolicy`; `packages/d2b-realm-core/src/ids.rs`: `RealmId`, `WorkloadId` (→ GuestRef), `NodeId` (→ HostRef), `ProviderId` (→ Provider ResourceRef), `ExecutionId` (→ EphemeralProcess exec identity), `PrincipalId` (→ User ResourceRef), `AllocatorLeaseId`, `ControllerGenerationId`; `packages/d2b-realm-core/src/workload.rs`: `WorkloadProviderKind` (`LocalVm`→runtime-cloud-hypervisor Provider, `QemuMedia`→runtime-qemu-media Provider, `ProviderManaged`→ACA/relay Providers, `UnsafeLocal`→user-only Host `isolationPosture=\"none\"`), `IsolationPosture` (`VirtualMachine`→Guest, `ProviderManaged`→Guest, `UnsafeLocal`→Host `isolationPosture=\"none\"`), `WorkloadExecutionPosture`, `WorkloadSummary`, `WorkloadState`; `packages/d2b-realm-core/src/target.rs`: `RealmTarget`, `TargetName`, `RealmTargetParser` (current analog for `/` ResourceRef parsing); `packages/d2b-realm-core/src/realm.rs`: `RealmPath`, `RealmControllerPlacement`, `EntrypointMode` (current Zone hierarchy analog); `packages/d2b-core/src/workload_identity.rs`: `WorkloadIdentity`, `WorkloadTarget` (= `RealmTarget`), `WorkloadBackend`, `WorkloadRuntimeIntent` (identity/backend separation reuse model for Host/Guest ResourceType split)", - "reuseSource": "`packages/d2b-contracts/src/v3/` as destination; no equivalent main source for Host/Guest/Process ResourceType contracts", - "reuseAction": "extract and adapt", - "destination": "`packages/d2b-contracts/src/v3/host.rs`, `packages/d2b-contracts/src/v3/guest.rs`, `packages/d2b-contracts/src/v3/execution_policy.rs`, `packages/d2b-contracts/src/v3/process.rs`, `packages/d2b-contracts/src/v3/ephemeral_process.rs`, `packages/d2b-contracts/src/v3/user.rs`", - "detailedDesign": "Implement strict typed Rust structs for HostSpec, GuestSpec, ExecutionPolicy, ExecutionSpec, SandboxSpec, BudgetSpec, MountSpec, NetworkUsageSpec, DeviceUsageSpec, EndpointSpec, TelemetrySpec, ProcessSpec, EphemeralProcessSpec, UserSpec; strict serde deny_unknown_fields; bounds/redaction on all string fields; stable error types; `UserSpec.osUsername` validated as OS username (1..255 bytes, no NUL/control/path-separator), not ResourceName grammar", - "integration": "Provider dossiers, controller descriptors, Zone resource API, Nix resource compiler", - "dataMigration": "Full reset; no v2 resource import", - "validation": "Golden JSON vectors for each ResourceType; serde unknown-field rejection; bounds enforcement; `UserSpec.osUsername` OS-username validation (underscore allowed, NUL rejected)", - "removalProof": "Old DTO types removed only after owning Resource/Provider integrations are live" - }, - { - "workItemId": "ADR046-exec-002", - "specId": "ADR-046-resources-host-guest-process-user", - "specPath": "docs/specs/ADR-046-resources-host-guest-process-user.md", - "dependencyOwner": "ADR046-exec-001; `d2b-contracts`", - "currentSource": "`packages/d2b-core/src/processes.rs`: `ReadinessPredicate`, `VmProcessInvariants`; `packages/d2b-core/src/processes.rs`: `SpawnRunnerPlanOp`; `packages/d2b-priv-broker/src/ops/` SpawnRunner", - "reuseSource": null, - "reuseAction": "extract and adapt", - "destination": "`packages/d2b-contracts/src/v3/process_provider.rs`: LaunchTicket, ProcessIdentityDigest, AdoptionCandidate, PidfdEvidence, WaitReapOwner, ProcessOutcome, ExitClass", - "detailedDesign": "LaunchTicket (Process/EphemeralProcess ref/UID/revision/generation, owner Provider/component/template, executionRef/domain/userRef, providerRef, compiled sandbox/budget/mount/device/network/endpoint digest, inherited FD table, operation/deadline/cancellation, expected identity/readiness); ProcessIdentityDigest (opaque bounded hex string); AdoptionCandidate (cgroup leaf path relative to controller root, start-time token, executable hash); all types zeroizing where credential-adjacent", - "integration": "ProviderSupervisor adapter; system-systemd and system-minijail Process Providers", - "dataMigration": "Full reset", - "validation": "Golden LaunchTicket vectors; field redaction test; digest-binding test", - "removalProof": "None — net-new types; no prior owner to remove" - }, - { - "workItemId": "ADR046-exec-003", - "specId": "ADR-046-resources-host-guest-process-user", - "specPath": "docs/specs/ADR-046-resources-host-guest-process-user.md", - "dependencyOwner": "ADR046-exec-001; system-core Provider owner", - "currentSource": "`packages/d2b-core/src/host_check.rs`: `HostCheckReport`, `HostCheckSummary`, `HostCheckFinding`, `HostCheckSeverity`; `packages/d2bd/src/pidfs_probe.rs`; `packages/d2bd/src/kernel_module_check.rs`; `packages/d2b-core/src/provider_capabilities.rs`; `packages/d2b-realm-core/src/ids.rs`: `HostResourceId` (current host-identity handle), `NodeId` (execution node identity); `packages/d2b-realm-core/src/node.rs`: `NodeKind::FullHost`, `NodeSummary` (host node's capability advertisement — direct reuse model for Host status `capabilities[]`)", - "reuseSource": null, - "reuseAction": "extract and adapt", - "destination": "`packages/d2b-provider-system-core/src/host.rs`: HostReconciler; status/conditions/capability probe implementation; `packages/d2b-provider-system-core/tests/`: hermetic reconcile/conformance/fault tests; `packages/d2b-provider-system-core/integration/`: Host probe and lifecycle integration scenarios; `packages/d2b-provider-system-core/README.md`: Provider identity, `spec.provider.settings` schema, ResourceTypes, placement, RBAC, security posture, telemetry labels, build/test commands (provider crate standard layout — see §Provider crate standard layout)", - "detailedDesign": "Async Host reconcile loop per this spec's Reconcile section; HostCapabilityClass probe set (kvm/pidfd/cgroup-v2/user-namespace/wayland/audio-pipewire/gpu-render/tpm2/usbip); bounded OS probes with timeout; `isolationPosture` validation and status; aggregate budget reservation tracking via List; status write with expected revision", - "integration": "Provider/system-core fixed bootstrap process; ResourceClient Get/List/UpdateStatus", - "dataMigration": "New Host resources from Nix; no v2 state import", - "validation": "Multiple Hosts per Zone; system-only and user-only Hosts; capability probe mocks; `isolationPosture=\"none\"` rejection of system processes; budget overcommit rejection; `tests/` all pass under `cargo test`; `integration/` scenario passes in container fixture; `README.md` present and covers all required sections (provider crate standard layout acceptance)", - "removalProof": "Current host capability checks in `d2bd` removed after Host reconcile parity" - }, - { - "workItemId": "ADR046-exec-004", - "specId": "ADR-046-resources-host-guest-process-user", - "specPath": "docs/specs/ADR-046-resources-host-guest-process-user.md", - "dependencyOwner": "ADR046-exec-001; system-core Provider owner", - "currentSource": "`packages/d2b-userd/src/lib.rs`: `UserdConfig`, `UserSessionIdentity`; `packages/d2bd/src/admission.rs` (`SO_PEERCRED` UID/GID lookup); `packages/d2b-realm-core/src/ids.rs`: `PrincipalId` (current host-local principal identity); `packages/d2b-realm-core/src/access.rs`: `HostLocalPeerCredentialSemantics`, `HostLocalPeerCredentialSource`, `RealmAccessClientContract` (current host-local credential/uid resolution model; direct reuse precedent for v3 User → Process userRef resolution)", - "reuseSource": null, - "reuseAction": "extract and adapt", - "destination": "`packages/d2b-provider-system-core/src/user.rs`: UserReconciler; NSS lookup implementation", - "detailedDesign": "Async User reconcile loop per this spec's Reconcile section; NSS `getpwnam(spec.osUsername)` with bounded timeout (default 5 s); `spec.osUsername` validated on admission (1..255 bytes, no NUL/control/path-separator); `metadata.name` used only as Zone-local resource identity and `User/` ref; uid/gid/home/shell/group discovery written to status; session manager availability check via fixed user supervisor; status write; phase Degraded on SessionManagerReady=False", - "integration": "Provider/system-core fixed bootstrap process; ResourceClient Get/List/UpdateStatus; other controllers resolve User status via ResourceClient", - "dataMigration": "New User resources from Nix; no v2 state import", - "validation": "User found/not-found; group membership; sessionManagerAvailable; multiple Users; deletion blocked by Process userRef; `spec.osUsername` with underscore succeeds NSS lookup where ResourceName grammar would reject it; `metadata.name` and `spec.osUsername` differ (e.g. `alice-admin` / `alice_admin`); `spec.osUsername` containing NUL/control/path-separator rejected at admission", - "removalProof": "Current local uid/group lookup in `d2bd/src/admission.rs` removed after User resource parity" - }, - { - "workItemId": "ADR046-exec-005", - "specId": "ADR-046-resources-host-guest-process-user", - "specPath": "docs/specs/ADR-046-resources-host-guest-process-user.md", - "dependencyOwner": "ADR046-exec-001 + ADR046-exec-004; system-core Provider owner", - "currentSource": "`packages/d2bd/src/lib.rs` (daemon startup); `packages/d2b-core/src/host_check.rs`; `nixos-modules/host.nix` (host activation); `nixos-modules/options-host.nix` (host options)", - "reuseSource": null, - "reuseAction": "adapt", - "destination": "`packages/d2b-provider-system-core/src/host.rs` (continued); bootstrap startup sequence", - "detailedDesign": "ProviderSystem-core fixed bootstrap: start with embedded Zone runtime and compiled bootstrap authorization; run Host reconcile before any Process Provider launches; create initial User resources from Nix; publish initial Role/RoleBinding from Nix config; hand off to stored RBAC", - "integration": "Zone runtime startup sequence; system-minijail bootstrap process launch", - "dataMigration": "Full v3 reset", - "validation": "Bootstrap without prior Host; User-before-Process ordering; compilation bootstrap authorization closed-set tests", - "removalProof": "Current d2bd initialization sequence removed after bootstrap parity" - }, - { - "workItemId": "ADR046-exec-006", - "specId": "ADR-046-resources-host-guest-process-user", - "specPath": "docs/specs/ADR-046-resources-host-guest-process-user.md", - "dependencyOwner": "ADR046-exec-001 + ADR046-exec-002; system-systemd Process Provider owner", - "currentSource": "`packages/d2b-unsafe-local-helper/src/systemd.rs`; `packages/d2b-guestd/src/exec.rs`: `SystemdRunUnitManager`, `ManagedUnit`, `ExecPolicy`; `packages/d2b-guestd/src/exec_linux.rs`; `packages/d2bd/src/supervisor/` (transient unit management)", - "reuseSource": null, - "reuseAction": "extract and adapt", - "destination": "`packages/d2b-provider-system-systemd/src/`: launch.rs, adoption.rs, pidfd.rs, wait.rs, user_supervisor.rs; `packages/d2b-provider-system-systemd/tests/`: hermetic lifecycle/conformance/fault tests; `packages/d2b-provider-system-systemd/integration/`: transient-unit and user-supervisor integration scenarios; `packages/d2b-provider-system-systemd/README.md`: Provider identity, `spec.provider.settings` schema, ResourceTypes, placement, RBAC, security posture, telemetry labels, build/test commands (provider crate standard layout — see §Provider crate standard layout)", - "detailedDesign": "system-systemd Process/EphemeralProcess provider conformance per this spec's system-systemd conformance section; transient system unit (Type=exec); InvocationID+cgroup+MainPID+start-time binding before pidfd_open; systemd-owned wait/reap; user domain via fixed user supervisor; adoption re-verification; sandboxSpec compilation to systemd hardening directives; runtimeDeadline enforcement; drainTimeout SIGTERM/SIGKILL sequence", - "integration": "Zone-installed Provider/system-systemd; ProviderSupervisor LaunchTicket interface; ResourceClient UpdateStatus", - "dataMigration": "Current ProcessRole/systemd unit roles converted by ProcessRole disposition table after parity", - "validation": "Shared process conformance test matrix (lifecycle/readiness/crash/drain/adoption/user-domain/sandboxSpec/pidfd); system-specific InvocationID binding; no-static-unit test; `tests/` all pass under `cargo test`; `integration/` scenario passes in container fixture; `README.md` present and covers all required sections (provider crate standard layout acceptance)", - "removalProof": "ProcessRole roles using systemd (Audio, WaylandProxy, VsockRelay, etc.) removed per disposition table after system-systemd parity" - }, - { - "workItemId": "ADR046-exec-007", - "specId": "ADR-046-resources-host-guest-process-user", - "specPath": "docs/specs/ADR-046-resources-host-guest-process-user.md", - "dependencyOwner": "ADR046-exec-001 + ADR046-exec-002; system-minijail Process Provider owner", - "currentSource": "`packages/d2b-core/src/processes.rs`: `ProcessNode`, `RoleProfile`, `NamespaceSet`, `MountPolicy`, `CgroupPlacement`; `packages/d2b-core/src/minijail_profile.rs`: full; `packages/d2b-priv-broker/src/ops/spawn_runner.rs` (if present at baseline); `packages/d2bd/src/supervisor/` pidfd/wait; `packages/d2b-core/src/process_builder.rs`", - "reuseSource": null, - "reuseAction": "extract and adapt", - "destination": "`packages/d2b-provider-system-minijail/src/`: sandbox_compiler.rs, launch.rs, adoption.rs, pidfd.rs, wait.rs, user_ns.rs; `packages/d2b-provider-system-minijail/tests/`: hermetic sandbox-compilation/lifecycle/conformance/fault tests; `packages/d2b-provider-system-minijail/integration/`: clone3/user-namespace and broker-spawn integration scenarios; `packages/d2b-provider-system-minijail/README.md`: Provider identity, `spec.provider.settings` schema, ResourceTypes, placement, RBAC, security posture (capabilities, namespaces, seccomp), telemetry labels, build/test commands (provider crate standard layout — see §Provider crate standard layout)", - "detailedDesign": "system-minijail Process/EphemeralProcess provider conformance per this spec's system-minijail conformance section; SandboxSpec-to-minijail plan compilation; broker clone3(CLONE_PIDFD|CLONE_INTO_CGROUP); user namespace pre-establishment (ADR 0021); d2b-owned wait/reap; adoption via cgroup leaf + process identity verification; runtimeDeadline enforcement; drainTimeout; EphemeralProcess one-shot launch", - "integration": "Zone-installed Provider/system-minijail fixed bootstrap process; ProviderSupervisor LaunchTicket; privileged broker effect adapter", - "dataMigration": "Current RoleProfile/NamespaceSet/MountPolicy/CgroupPlacement adapted to SandboxSpec/BudgetSpec", - "validation": "Shared process conformance test matrix; minijail-specific sandbox compilation tests; user namespace tests; clone3 pidfd tests; adoption quarantine tests; `tests/` all pass under `cargo test`; `integration/` scenario passes; `README.md` present and covers all required sections (provider crate standard layout acceptance)", - "removalProof": "ProcessRole roles using minijail (Virtiofsd, Swtpm, SecurityKeyFrontend, etc.) removed per disposition table after system-minijail parity" - }, - { - "workItemId": "ADR046-exec-008", - "specId": "ADR-046-resources-host-guest-process-user", - "specPath": "docs/specs/ADR-046-resources-host-guest-process-user.md", - "dependencyOwner": "ADR046-exec-006 + ADR046-exec-007; conformance test owner", - "currentSource": "`packages/d2b-core/src/processes.rs` test coverage; `packages/d2bd/src/supervisor/` tests; minijail/seccomp test vectors", - "reuseSource": null, - "reuseAction": "adapt", - "destination": "`packages/d2b-process-conformance/src/`: shared conformance test matrix run against both system-systemd and system-minijail providers", - "detailedDesign": "Full process lifecycle tests: start/ready/crash/restart/maxRestarts/drain/stop/delete; EphemeralProcess start/succeed/fail/ttl/cleanup; adoption after controller restart (fresh/quarantine); sandboxSpec compilation contract; pidfd rules (never-serialized/never-exported/re-verified-after-restart); user domain (system-systemd only); desiredLifecycle=stopped; fast path latency gate (<=5ms/<=20ms p95); 1/10/100 concurrent Process start", - "integration": "system-systemd and system-minijail providers must both pass all shared tests", - "dataMigration": "Full d2b 3.0 reset; no v2 state/config import", - "validation": "Hard pass/fail per-test; latency gates enforced; no exception for partial conformance", - "removalProof": "None — permanent conformance tests; no prior owner to remove" - }, - { - "workItemId": "ADR046-exec-009", - "specId": "ADR-046-resources-host-guest-process-user", - "specPath": "docs/specs/ADR-046-resources-host-guest-process-user.md", - "dependencyOwner": "ADR046-exec-001; unsafe-local migration owner", - "currentSource": "`packages/d2b-unsafe-local-helper/src/lib.rs`: `UserdConfig`, protocol traits; `packages/d2b-unsafe-local-helper/src/runtime.rs` (`ScopeRuntime`, `SupervisorSpec`); `packages/d2b-unsafe-local-helper/src/systemd.rs`; `packages/d2bd/src/unsafe_local_helper.rs`; `packages/d2b-realm-core/src/workload.rs`: `WorkloadProviderKind::UnsafeLocal`, `IsolationPosture::UnsafeLocal` (current evidence that the no-isolation posture exists and is classified separately from VM isolation — the exact semantics this spec's `Host.spec.isolationPosture=\"none\"` preserves); `packages/d2b-core/src/workload_identity.rs`: `WorkloadBackend::UnsafeLocal`; `nixos-modules/options-realms-workloads.nix` (`d2b.realms..workloads..kind = \"unsafe-local\"`)", - "reuseSource": null, - "reuseAction": "adapt", - "destination": "`packages/d2b-provider-system-core/src/host.rs` (user-only no-isolation Host); `nixos-modules/options-zones.nix` (Nix unsafe-local Host declaration)", - "detailedDesign": "v3 unsafe-local migration: `kind = \"unsafe-local\"` in the current Nix Realm workload model becomes a Host resource with `providerRef: Provider/system-core`, `spec.isolationPosture: \"none\"`, `defaultDomain: user`, `allowedDomains: [user]`, `defaultUserRef: User/`. This is a Host ResourceType, not a Guest and not a v3 Provider. Child Process and EphemeralProcess resources on this Host use the normal Process Providers (Provider/system-systemd for user-domain transient user scope; Provider/system-minijail is also valid for callers that explicitly request namespace isolation within the user session). No special unsafe-local-specific Provider is introduced. The explicit no-isolation posture and its warnings are preserved: Host status reflects `isolationPosture=\"none\"` and this is surfaced in every operator CLI/UI view as an explicit \"no isolation boundary\" warning; `ProcessEffect` audit records (launch, stop, adopt, quarantine) for child Processes and EphemeralProcesses carry the stable `no_isolation=true` attribute; operator CLI/UI always shows the warning and may not suppress it. The `no_isolation=true` attribute belongs on ProcessEffect records only — it must NOT appear on OTEL metric labels, span attributes, log fields, or audit records for other event kinds. The legacy helper protocol (`d2b-unsafe-local-helper`) is not exposed as a v3 ComponentSession service.", - "integration": "Host resource reconcile; User resource; system-systemd user-domain Process launch", - "dataMigration": "Full reset; no unsafe-local session state migration", - "validation": "User-only no-isolation Host rejected for system processes; `isolationPosture=\"none\"` Host rejected for `allowedDomains` containing `system`; `allowedDomains=[\"user\"]`+`defaultDomain=user`+`defaultUserRef` set with `isolationPosture=null` rejected at eval time (bidirectional evasion test); posture warning visible in CLI/UI status; `no_isolation=true` attribute present on ProcessEffect launch/stop/adopt/quarantine audit records for child Processes/EphemeralProcesses; `no_isolation=true` absent from OTEL span attributes, metric labels, log fields, and non-ProcessEffect audit records; user-domain Process under user-only Host starts correctly with normal Process Provider", - "removalProof": "`d2b-unsafe-local-helper` helper binary and protocol removed after user-only Host + shell-terminal Provider parity; `options-realms-workloads.nix` unsafe-local kind removed in Nix reset" - }, - { - "workItemId": "ADR046-exec-010", - "specId": "ADR-046-resources-host-guest-process-user", - "specPath": "docs/specs/ADR-046-resources-host-guest-process-user.md", - "dependencyOwner": "ADR046-exec-001 + ADR046-exec-007; guestd migration owner", - "currentSource": "`packages/d2b-guestd/src/exec.rs`: `ExecPolicy`, `ExecError`, `ExecState`, `ExitOutcome`, `ValidatedCommand`, `SpawnedProcess`, `RingChunk`, `ExecSnapshot`, `ExecCreateInput`; `packages/d2b-guestd/src/exec_linux.rs`; `packages/d2b-guestd/src/exec_pty.rs`; `packages/d2b-guestd/src/detached.rs`: `ManagedUnit`, `UnitError`, `UnitIdentity`; `packages/d2b-guestd/src/detached_registry.rs`; `packages/d2b-guestd/src/service.rs`; `packages/d2b-guestd/src/auth.rs`; `packages/d2b-guestd/src/login_session.rs`", - "reuseSource": null, - "reuseAction": "extract and adapt", - "destination": "`packages/d2b-provider-system-systemd/src/guest_exec.rs` (guest-domain EphemeralProcess launch via systemd-run inside guest); `packages/d2b-session/` (ComponentSession replacing ad-hoc guest ttrpc); runtime Provider guest bootstrap Process", - "detailedDesign": "Guest-side execution transitions to EphemeralProcess/Process resources under the owning Guest; guestd service becomes a fixed bootstrap Process owned by the runtime Provider controller; exec operations become EphemeralProcess creates via the Zone ResourceClient; detached sessions become EphemeralProcess with failedTtl=24h; shell sessions become shell-terminal Provider Processes; guestd auth.rs becomes ComponentSession via d2b-session (copy/adapt from ADR046-session-001 per ComponentSession spec)", - "integration": "runtime-cloud-hypervisor Guest controller creates guest-bootstrap Process; Zone ResourceClient in guest-bootstrap creates EphemeralProcess/Process on behalf of guest workloads", - "dataMigration": "Full reset; no guestd session state migration", - "validation": "Guest EphemeralProcess exec lifecycle; detached exec TTL; guestd auth replaced by ComponentSession; no SSH fallback", - "removalProof": "`d2b-guestd` binary removed after all guest-side behaviors have Process/EphemeralProcess/ComponentSession successors and tests pass" - }, - { - "workItemId": "ADR046-exec-011", - "specId": "ADR-046-resources-host-guest-process-user", - "specPath": "docs/specs/ADR-046-resources-host-guest-process-user.md", - "dependencyOwner": "ADR046-exec-004 + ADR046-exec-010; userd migration owner", - "currentSource": "`packages/d2b-userd/src/lib.rs`: `UserdConfig`, `UserSessionIdentity`, `UserAttachRequest`, `UserOutputCursor`, `UserExecSession` trait, `UserdError`, `UserdTransport`, `UnixSocketOnly`, `validate_attach_request`; `packages/d2b-userd/src/main.rs`", - "reuseSource": null, - "reuseAction": "adapt (logic retained; protocol replaced)", - "destination": "guest-domain process attachment becomes a ComponentSession named stream to the EphemeralProcess running in the guest; `UserExecSession` trait reimplemented as a typed ResourceClient+ComponentSession attachment", - "detailedDesign": "`UserdConfig.socket_name` replaced by Zone-local EphemeralProcess ResourceRef and ComponentSession attach verb; `UserSessionIdentity.uid`/`gid` become Process `userRef` resolved against User resource status; `UserAttachRequest.exec_id`/`tty`/`initial_size` become ComponentSession method parameters on the EphemeralProcess attachment service; `UserdError` becomes stable v3 error codes; `validate_attach_request` tty/size validation retained in the ComponentSession session schema", - "integration": "CLI/client uses ResourceClient to Get/Watch EphemeralProcess; attaches via d2b-bus ComponentSession attach verb", - "dataMigration": "Full reset; no session state import", - "validation": "Attach/detach; TTY size; stream close; error path parity with UserdError variants", - "removalProof": "`d2b-userd` binary removed after ComponentSession attach parity on EphemeralProcess; all d2b exec CLI paths must use new attach mechanism" - }, - { - "workItemId": "ADR046-exec-012", - "specId": "ADR-046-resources-host-guest-process-user", - "specPath": "docs/specs/ADR-046-resources-host-guest-process-user.md", - "dependencyOwner": "ADR046-exec-001; Nix resource compiler owner", - "currentSource": "`nixos-modules/options-realms-workloads.nix`: `d2b.realms..workloads..kind` = `local-vm`/`qemu-media` → `d2b.zones..resources.` (flat, `type=\"Guest\"`); `unsafe-local` → `d2b.zones..resources.` (flat, `type=\"Host\"`, user-only; see unsafe-local anchor); `nixos-modules/options-realms.nix`: top-level `d2b.realms` option shape including `providerKind` regex `^[a-z][a-z0-9-]*$` and `realmPath` regex `^[a-z][a-z0-9-]*(\\\\.[a-z][a-z0-9-]*)*$` (Zone hierarchy path encoding); `nixos-modules/processes-json.nix`; `nixos-modules/options-host.nix`; `nixos-modules/host.nix`; `packages/d2b-realm-core/src/realm.rs`: `RealmPath`, `RealmControllerPlacement` (current Zone declaration structure analog)", - "reuseSource": null, - "reuseAction": "adapt", - "destination": "`nixos-modules/options-zones.nix`: `d2b.zones..resources` option as `types.attrsOf (types.submodule resourceModule)` where each resource module has `type` (required enum), optional `metadata` submodule (`ownerRef`, `labels`, `annotations`), and `spec` (type-dependent, auto-generated submodule); `nixos-modules/zone-bundle.nix`: zone resource bundle emitter (see ADR046-exec-014); `nixos-modules/resource-schemas/`: generated per-type Nix option modules imported by `options-zones.nix`", - "detailedDesign": "`d2b.zones..resources` is a flat attrset; each entry has `type`, optional `metadata` (`ownerRef`, `labels`, `annotations`), and `spec`. The attrset key is the resource name. `spec` submodule fields and their Nix types/defaults/docs are auto-generated from the committed `packages/d2b-contracts/src/v3/schemas/.json` via `xtask gen-resource-nix-options`; no second vocabulary and no renaming of `spec.*` fields. `spec.provider.settings` sub-fields are constrained to the specific Provider's `providerNixSettingsSchema` attribute if present. All 17 eval-time validation rules from the \"Eval-time validation rules\" section are enforced by `lib.assertMsg` on the flat resource attrset. The `spec` object in the emitted JSON is the direct 1:1 serialization of the `spec` submodule. `metadata.ownerRef`, `metadata.labels`, `metadata.annotations` are serialized into the `metadata` object of the ResourceEnvelope JSON. `metadata.managedBy` and `metadata.configurationGeneration` are NOT in the bundle; they are set by the activation controller at runtime. `metadata.name` is the attrset key; `metadata.zone` is the enclosing zone key. Eval errors carry stable rule codes (1–17). Status is never present in the Nix option. `Guest.spec.systemArtifactId` (top-level spec field, not in `spec.provider.settings`) is validated against `d2b.artifacts` by rule 17; the `spec` submodule never contains derivation values or store paths.", - "integration": "Zone Nix configuration → eval-time validation → Nix-to-ResourceEnvelope compilers → zone bundle emitter (ADR046-exec-014) → configuration publication controller (ADR046-exec-015)", - "dataMigration": "Full reset; Realm/Workload options removed in the Nix reset wave", - "validation": "nix-unit eval/build tests 1–18 from the \"Tests\" section; eval validation rule tests 1–17 with expected error messages and stable codes; `spec` fields in emitted JSON match `spec` submodule values exactly (1:1 invariant test); `type` field in JSON matches `type` option value; `metadata.name` = attrset key; `metadata.zone` = enclosing zone key; `Guest.spec.systemArtifactId` plain string in resource bundle JSON at top-level spec (no store path, not in `spec.provider.settings`); missing/wrong-type artifact ID raises rule 17 eval error", - "removalProof": "Realm/Workload Nix options removed only after Zone resource Nix option parity and successful eval tests" - }, - { - "workItemId": "ADR046-exec-013", - "specId": "ADR-046-resources-host-guest-process-user", - "specPath": "docs/specs/ADR-046-resources-host-guest-process-user.md", - "dependencyOwner": "ADR046-exec-008; EphemeralProcess cleanup controller owner", - "currentSource": "No direct current equivalent; current processes.json has no TTL/cleanup concept", - "reuseSource": null, - "reuseAction": "new", - "destination": "`packages/d2b-core-controller/src/cleanup.rs`: EphemeralProcess TTL cleanup controller handler", - "detailedDesign": "Cleanup controller handler as specified in core-controllers spec; watches EphemeralProcess resources for terminal phase; computes cleanupEligibleAt from successfulTtl/failedTtl + completedAt; handles incidentHold; respects finalizers; issues normal Delete via ResourceClient; does not remove rows directly; bounded requeue-at for TTL expiry", - "integration": "core-controller process; ResourceClient Watch(EphemeralProcess); ResourceClient Delete", - "dataMigration": "Full d2b 3.0 reset; no v2 state/config import", - "validation": "Succeeded TTL default 1h; Failed TTL default 24h; incidentHold blocking; finalizer blocking; Delete with expected revision; cleanup controller restart recovery", - "removalProof": "None — net-new controller; no prior owner to remove" - }, - { - "workItemId": "ADR046-exec-014", - "specId": "ADR-046-resources-host-guest-process-user", - "specPath": "docs/specs/ADR-046-resources-host-guest-process-user.md", - "dependencyOwner": "ADR046-exec-001 + ADR046-exec-012; Nix resource compiler owner", - "currentSource": "`nixos-modules/processes-json.nix` (current Nix-to-JSON serialization pattern); `nixos-modules/manifest.nix` (current bundle manifest emitter with `bundleVersion`, `manifestVersion`, integrity-tracking pattern); `packages/xtask/src/main.rs` (`gen-schemas`) (current schema-fingerprint generation method); `packages/d2b-core/src/bundle.rs`: `Bundle`, `BundleVersion`, `BundleManifest`, `BundleArtifact` (current bundle integrity model)", - "reuseSource": null, - "reuseAction": "adapt", - "destination": "`nixos-modules/zone-bundle.nix`: Zone resource bundle emitter; `nixos-modules/resource-schemas/`: generated per-type Nix option submodules; `packages/d2b-contracts/src/v3/resource_bundle.rs`: `ResourceBundle`, `ResourceEnvelope`, `BundleManifest`, `BundleIntegrityPin` Rust types; `packages/xtask/src/gen_resource_schemas.rs`: `xtask gen-resource-schemas` (generates schema JSON and Nix option modules)", - "detailedDesign": "`zone-bundle.nix` iterates the flat `d2b.zones..resources` attrset (all types share the same attrset; each entry has `type`, optional `metadata`, and `spec`). For each entry it serializes the `spec` submodule to canonical JSON 1:1 (field names unchanged; keys sorted at every level; order-significant arrays preserve declaration order; semantically unordered arrays sorted). `metadata.ownerRef`, `metadata.labels`, `metadata.annotations` are serialized from the author-supplied `metadata` submodule into the envelope's `metadata` object. `metadata.managedBy` and `metadata.configurationGeneration` are NOT in the bundle envelope; they are set by the activation controller at runtime. `metadata.name` is the attrset key; `metadata.zone` is the zone key. `xtask gen-resource-schemas` generates both the schema JSON files AND the per-type Nix option submodule files under `nixos-modules/resource-schemas/` from the Rust DTO definitions; both must be regenerated when any ResourceType spec or Rust struct changes (same drift-gate pattern as current `make test-drift` / `xtask gen-schemas`). The bundle sorts all envelopes by `type` then `metadata.name` alphabetically, computes `bundleSha256` from the canonical JSON of the `resources` array, and emits the bundle manifest as a NixOS store artifact. No secret values, credentials, or OS paths not already declared in `spec` or `metadata` fields may appear in any envelope JSON. The bundle file includes private integrity metadata (resource type schema fingerprints, per-Provider schema fingerprints) alongside the `resources` array; these are bundle-level fields, never per-envelope fields. Alongside the resource bundle, the emitter installs the global private artifact catalog at `/etc/d2b/artifact-catalog.json` (root:d2bd 0640) mapping each `d2b.artifacts.` to `{ \"sha256\", \"size\", \"storePath\", \"type\" }`; this catalog is never included in public resource bundle envelopes. The bundle outer wrapper includes a `catalogSha256` binding the catalog to the bundle for activation-time integrity verification.", - "integration": "Zone Nix configuration → ADR046-exec-012 compilers → ADR046-exec-014 bundle emitter → `/etc/d2b/zones//resource-bundle.json` symlink → ADR046-exec-015 activation controller", - "dataMigration": "Full d2b 3.0 reset; new artifact replaces current `processes.json` and `manifest.json` pattern for Zone resources without importing v2 state", - "validation": "nix-unit test: bundle sort order (Host < Guest < Process; within type, names alphabetical); nix-unit test: `bundleSha256` recomputed from resources array matches recorded value; nix-unit test: schema fingerprints appear in private bundle file fields, not in any individual ResourceEnvelope `metadata` or `spec` object; nix-unit test: `catalogSha256` changes when any artifact derivation changes; nix-unit test: `schemaFingerprint` changes when ResourceTypeSchema JSON changes; nix-unit test: `providerSchemaFingerprint` changes when Provider settings schema changes and is null when Provider declares no schema; nix-unit test: no inline secret value passes through to bundle JSON; nix-unit test: identical configuration produces byte-identical bundle JSON across two builds; nix-unit test: artifact catalog JSON contains `storePath` field for each entry; nix-unit test: no envelope in `resources` array contains `storePath`, `nixSystem`, `schemaFingerprint`, or `providerSchemaFingerprint`", - "removalProof": "`nixos-modules/processes-json.nix` and current `manifest.nix` bundle artifact for Host/Guest/Process retained in parallel until Zone resource bundle replaces all roles; then removed" - }, - { - "workItemId": "ADR046-exec-015", - "specId": "ADR-046-resources-host-guest-process-user", - "specPath": "docs/specs/ADR-046-resources-host-guest-process-user.md", - "dependencyOwner": "ADR046-exec-014 + ADR046-exec-001 + ADR046-exec-022; configuration generation controller owner", - "currentSource": "No direct current equivalent; current realm/workload configuration is applied at NixOS activation time directly, not through a Resource API generation controller", - "reuseSource": null, - "reuseAction": "new", - "destination": "`packages/d2b-core-controller/src/configuration.rs`: `ZoneConfigController`, `GenerationState`, `PendingCleanup`, `BundleActivation`, `ActivationResult`, `ActivationError`, `GenerationDiff`, `DiffEntry`, `DiffKind`, `CleanupRecord`, `CleanupPhase`, `CleanupOutcome`, `RetentionPolicy`, `RetentionState`; `packages/d2b-core-controller/src/audit.rs`: audit event emission per the \"Audit events\" section", - "detailedDesign": "The Zone configuration controller (`packages/d2b-core-controller/src/configuration.rs`) runs as a fixed process in the `d2b-core-controller` crate. It watches `/etc/d2b/zones//resource-bundle.json` (inotify or polling). On change: (1) read and verify `bundleSha256` integrity; fail closed on mismatch, emit `d2b.zone.config.activate.error` with `config-bundle-integrity-failed`, make no changes. (2) Compare candidate `bundleSha256` against the currently active bundle record; if identical, this is a no-op re-activation (return immediately). (3) Verify resource type schema fingerprints (from bundle private fields) against committed schemas at `packages/d2b-contracts/src/v3/schemas/.json`; any mismatch fails the entire bundle closed with `config-schema-mismatch`, emits error, makes no changes. (4) Verify Provider schema fingerprints against installed Provider schemas; any mismatch fails the entire bundle closed with `provider-schema-mismatch`, emits error, makes no changes. (5) Verify `catalogSha256` binding against `/etc/d2b/artifact-catalog.json`; mismatch fails bundle closed. (6) Fetch current `metadata.managedBy=\"configuration\"` resources via ResourceClient List (one call per type). (7) Compute `GenerationDiff` (new/changed/unchanged/removed) by type+name key. (8) Submit Create, UpdateSpec, and Delete intents concurrently with bounded async concurrency (default max 32 in-flight); activation returns after all intents are durably queued by the resource store, without waiting for reconcile loops to complete. For unchanged specs: issue UpdateConfigGeneration to refresh `metadata.configurationGeneration` to the new generation number; no controller reconcile triggered. For new resources: core sets `metadata.managedBy=\"configuration\"` and `metadata.configurationGeneration=` on Create; if a same-name resource already exists with `metadata.managedBy=\"controller\"` or `metadata.managedBy=\"api\"`, record a per-item `config-collision` error for that resource without seizing it, emit error, continue other intents. For changed specs: submit UpdateSpec with `expectedRevision`; retry on optimistic lock conflict. For removed resources: submit Delete; set `metadata.deletionRequestedAt`. (9) Set Zone `phase=Pending` while create/update intents are outstanding. (10) Return after durable queue commit; do not block on reconcile completion. (11) The cleanup controller watches ResourceClient Watch streams for `Deleted` revision events (not polling GET) for each pending-cleanup resource by type+name+expectedRevision. When all finalizers release, the resource store commits the `Deleted` revision event atomically with row and index removal in a single transaction; following this commit, the audit subsystem appends the deletion audit record using a dedup/exactly-once recovery key (audit is NOT part of the atomic store transaction). Zone transitions to `phase=Degraded` immediately whenever any pending-cleanup item is outstanding. When all pending-cleanup items receive `Deleted` Watch events, `pendingCleanup` empties and Zone transitions to `phase=Ready`. Cleanup-stuck threshold: 10 min default; configurable; stuck resources remain Degraded without blocking later activations. Prior generation retention: controller retains the N most recently activated, cleanup-complete bundle records (default N=3; range 1..16; no time-based TTL).", - "integration": "Fixed process in `packages/d2b-core-controller`; ResourceClient (Create/UpdateSpec/UpdateConfigGeneration/UpdateStatus/Delete/List/Watch) per ADR046-exec-022; Zone resource status UpdateStatus (configGeneration, pendingCleanup, lastActivatedAt, lastActivationError); Zone `phase` transitions (Pending while outstanding intents; Degraded immediately when cleanup remains; Ready when create/update complete and cleanup empty); audit segment per ADR046-exec-014 audit events table", - "dataMigration": "Full d2b 3.0 reset; no v2 state/config import", - "validation": "Runtime/integration tests 19–23 from the \"Tests for Nix configuration and ResourceType-specific lifecycle\" section in this spec; additionally: `GenerationDiff` hermetic unit tests (new/changed/unchanged/removed classification); `bundleSha256` integrity failure aborts and emits correct audit event; `catalogSha256` mismatch aborts bundle; UpdateSpec optimistic lock conflict retried correctly; Watch `Deleted` revision events consumed (not polling GET) to track cleanup completion; Zone `phase=Pending` while intents outstanding; Zone `phase=Degraded` immediately when any cleanup outstanding (no grace window); Zone `phase=Ready` when complete; activation returns after durable queue commit, not after reconcile; same-name `managedBy=controller` OR `managedBy=api` collision emits per-item `config-collision` error without seizing resource, other intents continue; unchanged spec refreshes `configurationGeneration` without triggering controller reconcile; final deletion: atomic tx commits `Deleted` revision event + row/index removal only; audit append follows committed revision via dedup/exactly-once recovery (NOT part of atomic tx); recovery retry produces no duplicate audit record; prior bundle record released after cleanup-complete and retention count exceeded; activation with zero diff and identical bundleSha256 is a no-op", - "removalProof": "None — net-new controller; no prior owner to remove" - }, - { - "workItemId": "ADR046-exec-016", - "specId": "ADR-046-resources-host-guest-process-user", - "specPath": "docs/specs/ADR-046-resources-host-guest-process-user.md", - "dependencyOwner": "ADR046-exec-001; `d2b-session` v3 crate owner", - "currentSource": "No pre-ADR45 v3 baseline equivalent; ADR-only", - "reuseSource": null, - "reuseAction": "copy verbatim; rename crate from `d2b-session` to `d2b-bus-session` or retain name", - "destination": "`packages/d2b-bus-session/src/`: all above modules verbatim; `packages/d2b-bus-session/tests/`: all above tests verbatim", - "detailedDesign": "The entire `d2b-session` portable ComponentSession runtime is transport-agnostic and contains no ADR 0045 realm-specific types. All Noise handshake parameters, record framing, fragmentation, named stream mux, fair scheduler, cancellation, deadlines, bootstrap PSK, and attachment bindings are directly reusable. `ComponentSessionDriver` trait and `serve_ttrpc_services` are the primary integration surface for every v3 bus service. The `sessions/lib.rs` re-export boundary (`pub use d2b_contracts::v2_component_session as contract`) must be updated to point at the v3 wire contract module.", - "integration": "All v3 bus service implementations (`d2b-zone-service`, `d2b-provider-agent`, `d2b-bus-client`) depend on this crate; EphemeralProcess attach service; Process Provider launch ticket channel", - "dataMigration": "Full d2b 3.0 reset; no v2 state/config import", - "validation": "Copy tests verbatim; all tests must pass on v3 baseline without modification; re-run `tests/noise_vectors.rs` golden vectors; add one v3-specific test: endpoint policy identity uses v3 `ZoneId`/zone-name binding rather than ADR45 `RealmId`", - "removalProof": "None — net-new; no prior owner to remove" - }, - { - "workItemId": "ADR046-exec-017", - "specId": "ADR-046-resources-host-guest-process-user", - "specPath": "docs/specs/ADR-046-resources-host-guest-process-user.md", - "dependencyOwner": "ADR046-exec-016; `d2b-bus-session-unix` crate owner", - "currentSource": "`packages/d2b-session-unix/src/` at pre-ADR45 baseline `b5ddbed6` (partially equivalent: `d2b-session-unix` existed with `d2b-session-unix/src/adapter.rs` `UnixSeqpacketTransport`, `d2b-session-unix/src/descriptor.rs`, `d2b-session-unix/src/pidfd.rs`); main commit extends with credit pools, full pidfd identity verifier, and `host-socket` feature gate", - "reuseSource": null, - "reuseAction": "copy verbatim; rename crate from `d2b-session-unix` to `d2b-bus-session-unix` or retain name", - "destination": "`packages/d2b-bus-session-unix/src/`: all above modules verbatim; `packages/d2b-bus-session-unix/tests/`: all above tests verbatim", - "detailedDesign": "Provides the Linux-specific `OwnedTransport` implementation for Unix seqpacket and stream sockets. `CreditPool`/`ProcessCreditLimit` enforces per-scope FD attachment budget (ADR45 constants: `MAX_PROCESS_ATTACHMENT_CREDITS = 2048`, `MAX_HOST_ATTACHMENT_CREDITS = 8192`, `RESERVED_CONTROL_FDS = 64`). `PidfdIdentityVerifier` provides the `/proc//fdinfo/` parse path that Process controllers use to verify process identity before `pidfd_open(2)` — this is a direct dependency of system-systemd and system-minijail Process Providers (ADR046-exec-006, ADR046-exec-007).", - "integration": "v3 Zone runtime public socket listener; system-minijail/system-systemd pidfd identity verification; EphemeralProcess attach named stream", - "dataMigration": "Full d2b 3.0 reset; no v2 state/config import", - "validation": "Copy all 20+ tests verbatim; all must pass; add v3-specific test: `PathnamePeerVerifier` verifies against `d2b-zonert` daemon uid; credit pool per-Zone-runtime limits match v3 constants", - "removalProof": "If the crate is renamed, the superseded `packages/d2b-session-unix/` owner is removed or reduced to a compatibility wrapper after `packages/d2b-bus-session-unix/` passes copied and v3-specific tests; if the name is retained, no prior owner is removed." - }, - { - "workItemId": "ADR046-exec-018", - "specId": "ADR-046-resources-host-guest-process-user", - "specPath": "docs/specs/ADR-046-resources-host-guest-process-user.md", - "dependencyOwner": "ADR046-exec-016; `d2b-bus-wire` contract owner", - "currentSource": "No pre-ADR45 v3 baseline equivalent for v3 bus wire types; ADR-only", - "reuseSource": null, - "reuseAction": "copy and adapt", - "destination": "`packages/d2b-bus-wire/src/session.rs`: v3 bus protocol constants and wire types; all numeric constants copied verbatim; `PREFACE_MAGIC` retained; `EndpointPolicy` and `EndpointPolicyIdentity` adapted to use v3 `ZoneId`/`ProviderId` instead of ADR45 `RealmId` in the policy identity fingerprint", - "detailedDesign": "All numeric constants (frame sizes, credit limits, deadline values, reconnect limits) are directly reusable without change — they are derived from protocol analysis, not from realm semantics. `LimitProfile::local_default()` is the source for `serve_ttrpc_services` capacity; retain the value. `EndpointPolicy` carries the Noise static key and schema fingerprint; the fingerprint computation does not embed realm names and is reusable. The `EndpointPolicyIdentity` type carries the zone runtime's static public key — update from ADR45 `RealmId` to v3 `ZoneId` string encoding.", - "integration": "`d2b-bus-session` imports constants from here; all v3 bus service and client crates import protocol constants from `d2b-bus-wire`", - "dataMigration": "Full d2b 3.0 reset; no existing constants module state to migrate", - "validation": "Compile-time assertions on all copied numeric constants matching source values; `EndpointPolicyIdentity` golden-vector test with v3 zone name encoding; `LimitProfile::local_default()` round-trip test", - "removalProof": "None — net-new; no prior owner to remove" - }, - { - "workItemId": "ADR046-exec-019", - "specId": "ADR-046-resources-host-guest-process-user", - "specPath": "docs/specs/ADR-046-resources-host-guest-process-user.md", - "dependencyOwner": "ADR046-exec-016 + ADR046-exec-018; `d2b-provider-runtime` crate owner", - "currentSource": "`packages/d2b-realm-provider/src/provider.rs` at baseline `b5ddbed6` (`HostSubstrateProvider`, `RuntimeProvider`, `WorkloadProvider` traits — baseline, unwired); ADR-only for the runtime registry and RPC proxy", - "reuseSource": null, - "reuseAction": "copy and adapt", - "destination": "`packages/d2b-provider-runtime/src/`: `registry.rs`, `rpc.rs`, `instance.rs`, `context.rs`, `error.rs`; provider trait objects moved to `d2b-bus-wire` or `d2b-provider-contracts`", - "detailedDesign": "`ProviderRegistry` manages in-flight permits, draining, and provider lifecycle. `RegistryLimits { total_in_flight, per_provider_in_flight }` is directly reusable. `RpcProviderProxy` wraps a `ComponentSessionDriver` and dispatches typed `RpcCall` to an `AuthenticatedProviderRpc` implementation — the proxy pattern is fully reusable for v3 Provider resource controllers. `SessionIdentity { peer_role, service, provider_id, provider_type, provider_generation }` maps directly to a v3 Provider session credential. `InFlightPermit` RAII guard is directly reusable. The provider trait object set (`RuntimeProvider`, `StorageProvider`, etc.) adapts to v3 Provider resource typed methods; the trait hierarchy is preserved but `ProviderMethod` enum variant names may be renamed to drop ADR45 workload terminology. `ProviderRegistry::MAX_PROVIDER_REGISTRY_ENTRIES` bound from v2_provider is retained.", - "integration": "Zone runtime provider-agent ComponentSession; system-core Provider controller; every v3 Provider resource controller", - "dataMigration": "Full d2b 3.0 reset; no v2 state/config import", - "validation": "Copy `d2b-provider` tests; `RegistryLimits::validate` enforces non-zero and per≤total; `InFlightPermit` RAII release; drain/retire state machine; `RpcProviderProxy` round-trip over `FakeProvider` (from toolkit ADR046-exec-020)", - "removalProof": "Supersedes the baseline `packages/d2b-realm-provider/src/provider.rs` runtime/provider trait owner after `packages/d2b-provider-runtime/` registry/RPC tests pass and no v3 registry path imports ADR45 workload terminology." - }, - { - "workItemId": "ADR046-exec-020", - "specId": "ADR-046-resources-host-guest-process-user", - "specPath": "docs/specs/ADR-046-resources-host-guest-process-user.md", - "dependencyOwner": "ADR046-exec-019; `d2b-provider-toolkit` owner", - "currentSource": "`packages/d2b-realm-provider/src/conformance.rs` at baseline `b5ddbed6` (`check_provider_conformance`); ADR-only for toolkit server and fixture", - "reuseSource": null, - "reuseAction": "copy and adapt", - "destination": "`packages/d2b-provider-toolkit/src/`: retain all modules verbatim; adapt `ProviderAgentAdapter` to use v3 `ProviderRegistry` (ADR046-exec-019) and v3 bus wire types; adapt `GeneratedProviderServiceServer` to use v3 generated service stubs (ADR046-exec-021); `packages/d2b-provider-agent/src/`: adapted from `gateway-runtime/src/provider_agent.rs`", - "detailedDesign": "`ProviderAgentAdapter` is the core: it drives a `ComponentSessionDriver` receive loop, dispatches decoded ttrpc frames to `ProviderRegistry`, and forwards responses. `GeneratedProviderServiceServer` closes the loop by registering all generated service stubs with `serve_ttrpc_services`. `FakeProvider` implements every v2 Provider trait with deterministic outputs — adapt each trait method to v3 Provider resource semantics while retaining the fixture pattern. `Redacted` / `Secret` zero-copy wrappers are used in every audit log path; copy verbatim. Provider conformance check pattern is retained: descriptor validation, capability publication, fixture round-trip, observability query result.", - "integration": "Every v3 Provider resource controller uses `ProviderAgentAdapter` + `ProviderAgentProcess`; conformance tests gate Provider dossier acceptance; `FakeProvider` is used in all Provider controller hermetic tests", - "dataMigration": "Full d2b 3.0 reset; no v2 state/config import", - "validation": "Copy conformance tests verbatim; `check_descriptor_conformance` passes on a `FakeProvider` descriptor; `check_provider_conformance` covers all five `ConformanceError` variants; `ProviderAgentProcess` shutdown within deadline test; `MAX_DISPATCH_IN_FLIGHT` semaphore back-pressure test", - "removalProof": "Supersedes baseline `packages/d2b-realm-provider/src/conformance.rs` ownership only after `packages/d2b-provider-toolkit/` conformance coverage passes; ADR45 provider-agent registration behavior is not retained in the v3 provider-agent path." - }, - { - "workItemId": "ADR046-exec-021", - "specId": "ADR-046-resources-host-guest-process-user", - "specPath": "docs/specs/ADR-046-resources-host-guest-process-user.md", - "dependencyOwner": "ADR046-exec-016 + ADR046-exec-018; v3 Zone service contract owner", - "currentSource": "No pre-ADR45 v3 baseline equivalent; ADR-only", - "reuseSource": null, - "reuseAction": "adapt; v3 generates new protobuf definitions and new ttrpc stubs; reuse message shaping, method dispatch patterns, and service inventory pattern", - "destination": "`packages/d2b-bus-contracts/src/generated_v3_services/`: v3 generated ttrpc stubs for Zone service methods (Resource CRUD, Watch, ComponentSession service verbs); `packages/d2b-zone-service/src/`: Zone runtime service handler adapted from `DaemonServiceV2` pattern; `packages/d2b-zone-service/src/admission.rs`, `handler.rs`, `routing.rs`", - "detailedDesign": "`StrictWireMessage` trait (decode_strict, encode_strict) is directly reusable — it enforces deny-unknown-fields decode and schema-pinned fingerprint validation. `ServiceInventoryDocument` / `service_schema_fingerprint` pattern provides the service schema publication mechanism that v3 Provider resources use to advertise their ComponentSession service interface. `DaemonServiceV2` / `DaemonOperationHandler` pattern becomes the v3 Zone service handler base: `DaemonCallContext` → v3 `ZoneCallContext` with `ZoneId`, principal `User/`, operation deadline; `DaemonMethod` enum → v3 `ZoneMethod` (ResourceGet, ResourceList, ResourceWatch, ResourceCreate, ResourceUpdateSpec, ResourceUpdateStatus, ResourceDelete, BusAttach); `daemon_endpoint_policy` → v3 zone endpoint policy with v3 `ZoneId`-bound static key and schema fingerprint. `server_stream_name`/`parse_server_stream_name` for Watch stream naming is reusable verbatim. `TerminalStreamValidator` / `ServerStreamLease` for terminal byte stream safety is reusable for EphemeralProcess attach.", - "integration": "Zone runtime ttrpc service over public socket ComponentSession; Provider resource controller attaches via `RpcProviderProxy`; ResourceClient Watch uses server stream naming convention", - "dataMigration": "Full d2b 3.0 reset; no v2 state/config import", - "validation": "`StrictWireMessage` decode rejects unknown fields on all v3 message types; `service_schema_fingerprint` is stable across builds; `ZoneCallContext` deadline enforcement test; `DaemonSeqpacketTransport` → `ZoneSeqpacketTransport` end-to-end roundtrip; generated v3 stub compile check", - "removalProof": "None — net-new; no prior owner to remove" - }, - { - "workItemId": "ADR046-exec-022", - "specId": "ADR-046-resources-host-guest-process-user", - "specPath": "docs/specs/ADR-046-resources-host-guest-process-user.md", - "dependencyOwner": "ADR046-exec-016 + ADR046-exec-021; `d2b-bus-client` crate owner", - "currentSource": "No pre-ADR45 v3 baseline equivalent; ADR-only", - "reuseSource": null, - "reuseAction": "copy and adapt", - "destination": "`packages/d2b-bus-client/src/`: all above modules; `DaemonClient` → `ZoneClient` (v3 Resource CRUD/Watch verbs); `GuestClient` → `ProcessAttachClient`; `HostSocketConnector` → `ZoneSocketConnector`; `LocalDaemonSession` → `LocalZoneSession`", - "detailedDesign": "`Client` is the core typed async client with bounded retry, wall-clock injection, and cancellation. `ConnectedSession` wraps a `ComponentSessionDriver` and provides `call()`, `open_stream()`, and `close()`. `ComponentSessionConnector` trait decouples connection establishment from the client — v3 `ZoneSocketConnector` implements this for the local Zone runtime public socket. `TargetResolver`/`RouteTable` provides request routing to local vs remote Zone runtimes. `HostSocketConnector::local_daemon_endpoint_identity` provides the peer identity pinning that prevents MITM on the local socket — this is a security-critical invariant; copy verbatim, rename from `d2bd` to `d2b-zonert` uid. `DaemonClient` method table adapts to v3 Resource verbs: `ResourceGet`, `ResourceList`, `ResourceWatch` (streaming), `ResourceCreate`, `ResourceUpdateSpec`, `ResourceUpdateStatus`, `ResourceDelete`. `ServiceHandle`/`MethodHandle`/`GeneratedClient` provide the typed client stub generation pattern. `RetryPolicy`/`RetryClass`/`RemoteErrorKind` error classification is directly reusable.", - "integration": "CLI (`d2b` binary), external Zone API callers, Process/EphemeralProcess controller ResourceClient, all consumer-facing API paths", - "dataMigration": "Full d2b 3.0 reset; no v2 state/config import", - "validation": "Copy `tests/client.rs` verbatim; add v3-specific tests: `ResourceWatch` streaming teardown; `ZoneSocketConnector` peer-uid mismatch rejection; retry policy respects `RetryClass::Transient`/`Permanent`; `TargetInput`→`ResolvedTarget` for local-only v3 Zone; `local_daemon_endpoint_identity` returns correct v3 zone-rt uid", - "removalProof": "None — net-new; no prior owner to remove" - }, - { - "workItemId": "ADR046-exec-023", - "specId": "ADR-046-resources-host-guest-process-user", - "specPath": "docs/specs/ADR-046-resources-host-guest-process-user.md", - "dependencyOwner": "ADR046-exec-021 + ADR046-exec-019; Zone bus routing owner", - "currentSource": "`packages/d2b-realm-router/src/` at baseline `b5ddbed6` (`OperationRouter`, `RouteDecision`, `OperationRoutePlan`, `ReconcilableLease` — unwired in daemon); ADR-only for Zone service routing with idempotency", - "reuseSource": null, - "reuseAction": "adapt", - "destination": "`packages/d2b-zone-router/src/`: `router.rs` (v3 `ZoneOperationRouter` — idempotency semantics copied verbatim; dedup key namespace adapted from `(realm, principal, node, kind, key)` to `(zone, resource-type, resource-name, verb, idempotency-key)`); `service.rs` (v3 `ZoneServiceLimits`, `ZoneServiceServer`, `ZoneAuditEvent`); `resolver.rs` (v3 `ZoneTargetResolver`, `ZoneEntrypointTable`)", - "detailedDesign": "The idempotency/dedup semantics from `OperationRouter` are security-critical and must be copied exactly: (1) dedup key is the full 5-tuple namespace — reusing a key under a different principal is a conflict, not a replay; (2) expired keys leave tombstones for a no-reuse horizon; (3) same-key/same-request returns the original `operation_id` and recorded result; (4) same-key/different-request returns conflict error fail-closed. These semantics apply to all v3 Resource mutation verbs (Create, UpdateSpec, UpdateStatus, Delete) that carry an idempotency key. `RealmServiceLimits` numeric bounds are copied verbatim: `MAX_DISPATCH_IN_FLIGHT=64` gates concurrent resource mutations per Zone session. `RealmSessionAuthority` principal-binding model (session principal MUST match request principal field, derived in trusted code from authenticated session) maps directly to v3 Zone RBAC: `ZoneCallContext` carries the authenticated `User/` principal from `SO_PEERCRED`; no caller-supplied principal field is accepted. `DurableExecTable`/`DEFAULT_MAX_EXECUTIONS` from `execution.rs` provides the EphemeralProcess in-flight table bound for the Zone router.", - "integration": "Zone runtime ttrpc service (ADR046-exec-021); Zone ResourceClient (ADR046-exec-022); every Resource mutation verb", - "dataMigration": "Full d2b 3.0 reset; no v2 state/config import", - "validation": "Idempotency replay returns original result; conflict returns error; expired tombstone fails closed; `MAX_DISPATCH_IN_FLIGHT` semaphore back-pressure; principal-binding enforcement (mismatched principal returns auth-denied); `DurableExecTable` capacity limit; v3 5-tuple dedup key golden vector test", - "removalProof": "Supersedes the baseline `packages/d2b-realm-router/src/` routing owner after `packages/d2b-zone-router/` passes idempotency, dispatch-limit, and principal-binding tests; ADR45 realm/workload route tables are not imported into v3." - }, - { - "workItemId": "ADR046-feasibility-001", - "specId": "ADR-046-feasibility-and-spikes", - "specPath": "docs/specs/ADR-046-feasibility-and-spikes.md", - "dependencyOwner": "W0 shared contract root; store/reconciliation integrator", - "currentSource": "`packages/d2b-core/src/{storage,sync}.rs` (atomic/idempotency reference shape, E4); no redb usage exists anywhere in this repository at any inspected commit", - "reuseSource": "None (redb is a new external dependency; no main or v3 code implements it)", - "reuseAction": "`adapt` (the atomic-write/idempotency discipline in `storage.rs`/`sync.rs` is adapted into the spike's write-transaction algorithm; redb itself is used unmodified)", - "destination": "`proofs/redb-resource-store-spike/`", - "detailedDesign": "Implements SPIKE-01 and SPIKE-02: the eight-table schema, fair write queue, blocking store-actor, watch registrar, and hint bus described in those two spike entries", - "integration": "None (standalone; no d2b-bus/ComponentSession/broker dependency)", - "dataMigration": "None (disposable fixture data only)", - "validation": "SPIKE-01 metrics (1)-(5) and SPIKE-02 metrics (1) across all 3 concurrency profiles, per those entries' exact pass/fail thresholds", - "removalProof": "Per SPIKE-01/SPIKE-02 Cleanup rows: deleted once `packages/d2b-resource-store-redb` and `packages/d2b-controller-toolkit/benches/reaction.rs` reproduce equal-or-stricter coverage" - }, - { - "workItemId": "ADR046-feasibility-002", - "specId": "ADR-046-feasibility-and-spikes", - "specPath": "docs/specs/ADR-046-feasibility-and-spikes.md", - "dependencyOwner": "`ADR046-feasibility-001`; reconciliation/process integrator", - "currentSource": "`packages/d2bd/src/supervisor/{dag,pidfd}.rs` (current DAG/pidfd reference shape, E4)", - "reuseSource": "None (the generic async controller loop is ADR-only per `ADR-046-resource-reconciliation`'s own current-code-fit row)", - "reuseAction": "`adapt` (current DAG ordering/readiness concepts are adapted into the spike's per-resource single-flight/parallel-semaphore loop)", - "destination": "`proofs/process-fastlaunch-spike/`", - "detailedDesign": "Implements SPIKE-03: the fake Process controller loop, fake `ProcessLaunchEffectPort`, and the 1/10/100-concurrency commit-to-launch-attempt and next-dispatch-independence benchmarks", - "integration": "Consumes `ADR046-feasibility-001`'s hint-bus shape as its watch-receiver input", - "dataMigration": "None — full d2b 3.0 reset; no prior state to migrate", - "validation": "SPIKE-03 metrics (1)-(3) and thresholds", - "removalProof": "Deleted once `packages/d2b-controller-toolkit/benches/reaction.rs` and the Process Provider integration tests named by `ADR046-reconcile-003` reproduce equal-or-stricter coverage" - }, - { - "workItemId": "ADR046-feasibility-003", - "specId": "ADR-046-feasibility-and-spikes", - "specPath": "docs/specs/ADR-046-feasibility-and-spikes.md", - "dependencyOwner": "Independent of `-001`/`-002`; EffectPort/ProviderSupervisor integrator", - "currentSource": "`packages/d2b-priv-broker/src/ops/spawn_runner.rs` and `tests/pidfd_real_spawner.rs` (current blocking-call reference shape, E4)", - "reuseSource": "None", - "reuseAction": "`adapt`", - "destination": "`proofs/effectport-async-spike/`", - "detailedDesign": "Implements SPIKE-04: the four fake EffectPort traits, the deliberately slow blocking-primitive backends, and the current-thread-runtime heartbeat-jitter detector", - "integration": "None (standalone)", - "dataMigration": "None — full d2b 3.0 reset; no prior state to migrate", - "validation": "SPIKE-04 heartbeat-jitter metric and threshold", - "removalProof": "Deleted once `packages/d2b-provider-supervisor` and the volume-domain effect adapter each carry an equal-or-stricter in-tree blocking-adapter regression test" - }, - { - "workItemId": "ADR046-feasibility-004", - "specId": "ADR-046-feasibility-and-spikes", - "specPath": "docs/specs/ADR-046-feasibility-and-spikes.md", - "dependencyOwner": "Independent of `-001`/`-002`/`-003`; Provider packaging/toolkit integrator", - "currentSource": "None in v3 at `b5ddbed6` (no generic Provider registry exists; per parent ADR context, this is explicitly listed as missing)", - "reuseSource": "None", - "reuseAction": "`adapt` (the crate-layout policy check reuses the same `src/`/`tests/`/`integration/`/`README.md` structure already enforced elsewhere in this repository's workspace policy tests)", - "destination": "`proofs/provider-packaging-spike/`", - "detailedDesign": "Implements SPIKE-05: the two-binary crate, hand-authored manifest, fake `ProviderDeployment`, and the `cargo metadata` dependency-edge check", - "integration": "None (standalone)", - "dataMigration": "None — full d2b 3.0 reset; no prior state to migrate", - "validation": "SPIKE-05 metrics (1)-(4) across 20 repeated randomized-order manifest loads", - "removalProof": "Deleted once the real Provider-toolkit crate (`ADR046-provider-001` destination) ships equal-or-stricter manifest-parsing/enumeration/workspace-policy coverage" - }, - { - "workItemId": "ADR046-feasibility-005", - "specId": "ADR-046-feasibility-and-spikes", - "specPath": "docs/specs/ADR-046-feasibility-and-spikes.md", - "dependencyOwner": "Independent of `-001` through `-004`; bus/session/transport/credential integrator", - "currentSource": "`packages/d2b-realm-router/src/{session,secure_session,mux_session}.rs` (current routing reference shape, E4); main `a1cc0b2d` `packages/d2b-session/**`, `packages/d2b-session-unix/**` (re-verified green in E3)", - "reuseSource": "main `a1cc0b2d`: `packages/d2b-session/src/{handshake,bootstrap,record,engine,scheduler,streams,lifecycle,transport}.rs`, `packages/d2b-session-unix/src/{adapter,vsock,pidfd,socket,systemd,credit,descriptor}.rs`, and the exact test files listed in E3's table", - "reuseAction": "`copy-unchanged` for the Noise/record/transport machinery (path-dependency on a pinned local checkout of `a1cc0b2d`); `adapt` for the fake router/relay/credential-delivery wrapper code that SPIKE-06/07/08 add on top", - "destination": "`proofs/bus-routing-noise-spike/`, `proofs/transport-opaque-streams-spike/`, `proofs/credential-kk-e2e-spike/`", - "detailedDesign": "Implements SPIKE-06 (exact-addressed routing + per-recipient Noise isolation), SPIKE-07 (Unix/vsock/relay-shaped opaque byte-stream conformance across 3 backends), and SPIKE-08 (Credential Provider → consumer Provider KK delivery with the 13-field binding contract)", - "integration": "SPIKE-07's Unix backend and SPIKE-08's session machinery both depend on the same pinned `a1cc0b2d` path-dependency established for SPIKE-06", - "dataMigration": "None — full d2b 3.0 reset; no prior state to migrate", - "validation": "SPIKE-06 metrics (1)-(3), SPIKE-07 metrics (1)-(4) against the exact numeric gates already committed in the transport-unix/vsock dossiers, SPIKE-08 metrics (1)-(6)", - "removalProof": "Deleted per each spike's own Cleanup row: `packages/d2b-bus/src/router.rs` for SPIKE-06; the three real transport Provider crates for SPIKE-07; the real Credential Provider crates for SPIKE-08" - }, - { - "workItemId": "ADR046-feasibility-006", - "specId": "ADR-046-feasibility-and-spikes", - "specPath": "docs/specs/ADR-046-feasibility-and-spikes.md", - "dependencyOwner": "Independent of `-001` through `-005`; Provider-state/Volume integrator", - "currentSource": "None in v3 at `b5ddbed6` (ProviderStateSet and the generalized Volume ResourceType are both ADR-only)", - "reuseSource": "None", - "reuseAction": "`adapt`", - "destination": "`proofs/provider-state-export-spike/`, `proofs/volume-policy-spike/`", - "detailedDesign": "Implements SPIKE-09 (optional declared state-Volume creation order, guest-local/host-backed-guest placement, virtiofs Export child ownership) and SPIKE-10 (Volume ACL/`sourcePolicyId`/quota/lifecycle-marker policy conformance)", - "integration": "None between the two spikes beyond sharing the same fake resource-store oracle shape", - "dataMigration": "None — full d2b 3.0 reset; no prior state to migrate", - "validation": "SPIKE-09 metrics (1)-(4); SPIKE-10 metrics (1)-(5), zero-tolerance on path leakage", - "removalProof": "Deleted per each spike's Cleanup row: the real `ADR-046-provider-state` work-item destination for SPIKE-09; `d2b-provider-volume-local`'s own `tests/`/`integration/` suite for SPIKE-10" - }, - { - "workItemId": "ADR046-feasibility-007", - "specId": "ADR-046-feasibility-and-spikes", - "specPath": "docs/specs/ADR-046-feasibility-and-spikes.md", - "dependencyOwner": "Independent of `-001` through `-006`; Process Provider integrator", - "currentSource": "`packages/d2b-priv-broker/src/ops/spawn_runner.rs` (real-spawn reference shape, E4); current unsafe-local helper runtime/systemd invocation shape", - "reuseSource": "None", - "reuseAction": "`adapt`", - "destination": "`proofs/process-provider-conformance-spike/`", - "detailedDesign": "Implements SPIKE-11: the shared `ProcessProviderHarness` trait, the minijail-shaped `clone3(CLONE_PIDFD)` launcher, and the systemd transient-user-scope launcher, plus the identity-drift/quarantine and clean-exit cases", - "integration": "None (standalone; requires a Linux host with `clone3`/`pidfd_open`, and optionally a running `systemd --user` instance behind the `systemd-user` feature)", - "dataMigration": "None — full d2b 3.0 reset; no prior state to migrate", - "validation": "SPIKE-11 metrics (1)-(4), zero-tolerance on false adoption", - "removalProof": "Deleted once `packages/d2b-provider-system-systemd` and `packages/d2b-provider-system-minijail` each carry this exact shared conformance suite in their own `tests/`" - }, - { - "workItemId": "ADR046-feasibility-008", - "specId": "ADR-046-feasibility-and-spikes", - "specPath": "docs/specs/ADR-046-feasibility-and-spikes.md", - "dependencyOwner": "Independent of `-001` through `-007`; Nix/xtask integrator", - "currentSource": "`nixos-modules/assertions.nix` pattern, `packages/xtask` `gen-schemas` pattern, `make test-drift` gate (existing generated-or-eval-contract precedent)", - "reuseSource": "None", - "reuseAction": "`adapt`", - "destination": "`proofs/nix-authoring-spike/`", - "detailedDesign": "Implements SPIKE-12: the minimal flake, the two synthetic ResourceTypes, the hand-written committed schemas, the standalone `gen-schemas`-shaped drift check, and the two-generation removed-resource cleanup simulation", - "integration": "None (standalone flake; no dependency on the main `flake.nix`)", - "dataMigration": "None — full d2b 3.0 reset; no prior state to migrate", - "validation": "SPIKE-12 metrics (1)-(5), byte-for-byte reproducibility across 3 hermetic builds", - "removalProof": "Deleted once the real `nixos-modules/resources.nix` and `packages/xtask` `gen-schemas` implementation reproduce these metrics as part of `make test-drift`/`make test-flake`" - }, - { - "workItemId": "ADR046-feasibility-009", - "specId": "ADR-046-feasibility-and-spikes", - "specPath": "docs/specs/ADR-046-feasibility-and-spikes.md", - "dependencyOwner": "Independent of `-001` through `-008`; CLI integrator", - "currentSource": "`packages/d2b/src/lib.rs` `cmd_audio`/`cmd_clipboard_arm` and the current command-table shape (current CLI reference shape)", - "reuseSource": "None", - "reuseAction": "`adapt`", - "destination": "`proofs/cli-discovery-spike/`, `proofs/clean-cutover-spike/`", - "detailedDesign": "Implements SPIKE-13 (dynamic Provider-projection discovery, bounds, latency isolation) and SPIKE-14 (zero v2 dispatch, fresh Zone bootstrap ignoring legacy state)", - "integration": "None between the two spikes beyond sharing the same fixture command-table shape", - "dataMigration": "None — full d2b 3.0 reset; no prior state to migrate", - "validation": "SPIKE-13 metrics (1)-(7); SPIKE-14 metrics (1)-(3), zero-tolerance on legacy-file access", - "removalProof": "Deleted per each spike's Cleanup row: the real `d2b` CLI crate's own discovery conformance test for SPIKE-13; the real CLI crate's workspace-policy/lint gate plus the real bootstrap sequence for SPIKE-14" - }, - { - "workItemId": "ADR046-feasibility-010", - "specId": "ADR-046-feasibility-and-spikes", - "specPath": "docs/specs/ADR-046-feasibility-and-spikes.md", - "dependencyOwner": "`ADR046-feasibility-001` through `ADR046-feasibility-009` (integrates their fakes; must run last)", - "currentSource": "None (this is a pure integration of the other nine work items' fixtures)", - "reuseSource": "None beyond what `-001` through `-009` already reuse", - "reuseAction": "`adapt`", - "destination": "`proofs/e2e-composition-spike/`", - "detailedDesign": "Implements SPIKE-15: the three representative compositions (local/cloud-hypervisor, cloud/azure, interaction/shell-terminal-or-wayland), wired from the fakes built by `-001` through `-009`, plus the combined 3-Zone aggregate RSS measurement", - "integration": "Depends on and imports the fake shapes from `proofs/redb-resource-store-spike/`, `proofs/process-fastlaunch-spike/`, `proofs/effectport-async-spike/`, `proofs/provider-packaging-spike/`, `proofs/bus-routing-noise-spike/`, `proofs/transport-opaque-streams-spike/`, `proofs/credential-kk-e2e-spike/`, `proofs/provider-state-export-spike/`, `proofs/volume-policy-spike/`, and `proofs/process-provider-conformance-spike/`", - "dataMigration": "None — full d2b 3.0 reset; no prior state to migrate", - "validation": "SPIKE-15 metrics (1)-(4) across all three compositions", - "removalProof": "Deleted once the real integration test suites named by the individual Provider dossiers (`integration/` per D059) collectively reproduce all three compositions against real, non-fake Zone/store/bus/broker code" - }, - { - "workItemId": "ADR046-feasibility-011", - "specId": "ADR-046-feasibility-and-spikes", - "specPath": "docs/specs/ADR-046-feasibility-and-spikes.md", - "dependencyOwner": "`ADR046-delivery-007`; delivery/test-tooling integrator", - "currentSource": "this codebase's ad hoc `tests/tools/` timing logs (`d2b-static-timing.$$/`), which are not a candidate-bound, reference-runner-recorded ledger", - "reuseSource": "existing `libtest --format=json` timing output and `xtask` (no new test framework)", - "reuseAction": "`adapt`", - "destination": "`proofs/test-runtime-budget-spike/`; the committed baseline ledger consumed by `ADR046-delivery-007`", - "detailedDesign": "Establishes the D094 measurement baseline: records the reference runner class, repetition count, and per-test/crate/shard p95 for a representative hermetic crate; proves the §10.16 budgets (individual normal test p95 ≤50 ms, per-crate `--lib --tests` ≤2 s, Layer-1 hermetic shard ≤60 s) are met on the reference runner and that an injected slow/sleeping test is detected as a regression", - "integration": "Output ledger shape is consumed by the runtime ledger/timing gate; establishes the historical threshold seed", - "dataMigration": "None — full d2b 3.0 reset; no prior state to migrate", - "validation": "The representative crate meets every budget; a synthetic slow/sleep/process/network test is flagged; cold compile time is recorded on a separate line and excluded from the execution budgets", - "removalProof": "Deleted once `ADR046-delivery-007`'s in-tree ledger/timing gate reproduces the baseline against the real crate set" - }, - { - "workItemId": "ADR046-host-posture-001", - "specId": "ADR-046-telemetry-audit-and-support", - "specPath": "docs/specs/ADR-046-telemetry-audit-and-support.md", - "dependencyOwner": "ADR046-audit-001 + ADR046-core-001; `Provider/system-core` owner", - "currentSource": "`packages/d2b-core/src/unsafe_local_workloads.rs` (`UnsafeLocalWorkloadsJson`, `UnsafeLocalWorkload`, `UnsafeLocalLauncherItem`, `UNSAFE_LOCAL_WORKLOADS_SCHEMA_VERSION = \"v2\"`, `MAX_UNSAFE_LOCAL_WORKLOADS = 256`); `packages/d2b-contracts/src/unsafe_local_wire.rs` (`HelperHello.uid: u32`, `HelperLaunchRequest`, `HelperShellRequest`, `HelperScopeKind::{Exec,Shell}`, `DaemonToUnsafeLocalHelper`, `UnsafeLocalHelperToDaemon`); `packages/d2bd/src/unsafe_local_helper.rs` (`HelperRegistry::new(daemon_uid, allowed_uids)`, `dispatch_launch`, `bind_helper_socket`); `packages/d2b-unsafe-local-helper/src/{main,protocol,runtime,systemd}.rs` (`HelperClient`, `ScopeRuntime`, `run_scope_supervisor`, `SystemdUserScopeManager`); `nixos-modules/options-realms-workloads.nix` (lines 221, 233–235 `kind = \"unsafe-local\"` description; lines 264–275 null `stateDir`/`runDir`); `nixos-modules/unsafe-local-workloads-json.nix` (`runtimeKind = \"unsafe-local\"`, `providerId = \"unsafe-local\"`); `nixos-modules/unsafe-local-helper.nix` (service unit)", - "reuseSource": null, - "reuseAction": "adapt `UnsafeLocalWorkload` private-bundle contract for the `Host` resource spec payload; adapt `HelperRegistry::allowed_uids` constraint as `defaultUserRef=User/` validation; adapt Nix `unsafe-local-workloads-json.nix` emitter for the new Host resource shape; gap-fill: add `ProcessEffect{no_isolation:true}` at `dispatch_launch` / stop call sites", - "destination": "`packages/d2b-provider-system-core/src/{host_reconciler.rs,host_status.rs,host_process_audit.rs}`; adapted `nixos-modules/unsafe-local-workloads-json.nix`; `packages/d2b-provider-system-core/tests/host_posture_contract.rs`", - "detailedDesign": "`Provider/system-core` reconciler: (1) On user-only `Host` resource creation (`defaultDomain=user`, `allowedDomains=[user]`), set `status.isolationPosture = \"none\"` and `status.isolationPostureMessage = \"...\"` unconditionally; reject any operator-supplied value for these fields. Host resources with other execution policies do not receive `isolationPosture`. (2) On every user-only Host process launch: emit `ProcessEffect{event:\"launch\", provider:\"system-core-user\", domain:\"user\", no_isolation:true, ...}` audit record. (3) On every user-only Host process stop: emit `ProcessEffect{event:\"stop\", ...}`. (4) `d2b zone list`/`inspect` CLI renders `⚠ no isolation boundary (user domain)` annotation only for `Host` resources with `isolationPosture: \"none\"`; annotation is not suppressible. (5) `isolation-posture-declared` doctor check: passes when user-only `Host` resource status has `isolationPosture: \"none\"`; omitted when Zone has no user-only `Host` resources. (6) `no_isolation=true` is emitted in `ProcessEffect` records only; it does not appear in any OTEL span attribute, log field, or metric label.", - "integration": "`Provider/system-core` reconciler → `d2b-audit` sink; `d2b zone doctor` → resource status check; `d2b zone list`/`inspect` → CLI output renderer", - "dataMigration": "Full d2b 3.0 reset; no v2 state/config import", - "validation": "`host_posture_contract.rs` tests from the Host posture tests section of this spec; `d2b-contract-tests/tests/policy_telemetry_redaction.rs` asserts `no_isolation` key absent from all span/metric/log surfaces", - "removalProof": "`d2b-unsafe-local-helper` binary and `DaemonToUnsafeLocalHelper`/`UnsafeLocalHelperToDaemon` wire types retired after `Provider/system-core` Process Provider supervisor ticket migration; `nixos-modules/unsafe-local-helper.nix` Nix unit retired after migration; `nixos-modules/unsafe-local-workloads-json.nix` adapted (not deleted) to emit Host resource spec format" - }, - { - "workItemId": "ADR046-identities-001", - "specId": "ADR-046-terminology-and-identities", - "specPath": "docs/specs/ADR-046-terminology-and-identities.md", - "dependencyOwner": "W0 shared contract root; `d2b-contracts`", - "currentSource": "`packages/d2b-realm-core/src/ids.rs`, `realm.rs`, `target.rs`, `workload.rs`", - "reuseSource": null, - "reuseAction": "extract and adapt", - "destination": "`packages/d2b-contracts/src/v3/identity.rs`, `packages/d2b-contracts/src/v3/resource_ref.rs`", - "detailedDesign": "Add ZoneId, ResourceTypeName, ResourceName, ResourceUid, ResourceRef, generation/revision newtypes, exact parsing/serde/Debug/redaction, and golden vectors", - "integration": "Resource API/store/controllers/SDK/Nix import only these canonical types", - "dataMigration": "Destructive d2b 3.0 reset; no RealmRef parser compatibility", - "validation": "Rust property/vector tests; pure-Nix vector parity; malformed/collision/UID-recreate tests", - "removalProof": "Old public Realm target types removed only after all v3 callers consume Zone/ResourceRef" - }, - { - "workItemId": "ADR046-identities-002", - "specId": "ADR-046-terminology-and-identities", - "specPath": "docs/specs/ADR-046-terminology-and-identities.md", - "dependencyOwner": "ADR046-identities-001; Nix integrator", - "currentSource": "`nixos-modules/options-realms.nix`, `options-realms-workloads.nix`, `index.nix`", - "reuseSource": null, - "reuseAction": "adapt", - "destination": "`nixos-modules/options-zones.nix`, `nixos-modules/resources.nix`, `nixos-modules/index.nix`", - "detailedDesign": "Validate Zone names, ResourceTypes/names/refs, shared Host/Guest ExecutionPolicy, and canonical sorted resource identities", - "integration": "Nix resource objects serialize exactly the Rust contract", - "dataMigration": "Full reset and new Zone declarations", - "validation": "nix-unit vectors and rendered contract tests", - "removalProof": "Realm-facing declarations removed only in the reset/purge wave" - }, - { - "workItemId": "ADR046-mi-topology-001", - "specId": "ADR-046-provider-credential-managed-identity", - "specPath": "docs/specs/providers/ADR-046-provider-credential-managed-identity.md", - "dependencyOwner": "ADR046-credential-001 and ADR046-credential-002; ADR046-cred-mi-001; ADR046-cred-mi-002; owner: credential-managed-identity controller/agent topology", - "currentSource": "`packages/d2b-realm-provider/src/provider.rs:CredentialProvider` status/enrollment-only trait; main `a1cc0b2d` managed-identity Provider implementation and tests listed in §Source reuse", - "reuseSource": null, - "reuseAction": "copy and adapt the main managed-identity Provider; replace v2 provider registry/session assumptions with v3 controller/agent Process topology and `d2b.credential.v3` service", - "destination": "packages/d2b-provider-credential-managed-identity/src/{controller.rs,agent.rs}; packages/d2b-provider-credential-managed-identity/{controller/main.rs,agent/main.rs}; packages/d2b-provider-credential-managed-identity/tests/topology.rs", - "detailedDesign": "Implement the controller/agent process split: separate `d2b-managed-identity-controller` binary with no IMDS client and no KK delivery, and `d2b-managed-identity-agent` binary with injected IMDS client via effect port and KK delivery. Controller manages Credential resources, spawns/monitors agent Processes, uses canonical Process templates, attaches LaunchTickets projecting `imdsEndpointAlias` and `credentialRef`, monitors agent Process health with bounded backoff, performs Deleted-phase cleanup without emitting Deleted closure audit, and applies D087 status-first state with no Provider state Volume. Agent validates `ExactSdkConsumer` via `AuthenticatedSubjectContext`, serves token-delivery methods, terminates Noise_KK delivery sessions, reports lease state, declares no direct IMDS egress, and keeps token bytes transient.", - "integration": "ProviderDeployment starts the controller Process; controller reconciles Credential resources and creates agent Process/Endpoint resources at the declared executionRef; d2b-bus routes `d2b.credential.v3` calls to the agent; co-located runtime Provider injects the IMDS client through the LaunchTicket/effect port; core aggregates Provider status and audit subsystem appends deletion records.", - "dataMigration": "Full d2b 3.0 reset; no v2 managed-identity process/session state import", - "validation": "`tests/topology.rs`; `integration/host-guest-placement.nix`; `make test-rust`; `make test-integration`; `make test-host-integration`", - "removalProof": "V2 single-process/trait topology is superseded once controller and agent Process split is integrated and all token delivery terminates in the agent" - }, - { - "workItemId": "ADR046-minijail-001", - "specId": "ADR-046-provider-system-minijail", - "specPath": "docs/specs/providers/ADR-046-provider-system-minijail.md", - "dependencyOwner": "`ADR046-process-001` (common spec/status types); `ADR046-provider-001` (toolkit/contracts); system-minijail Provider owner", - "currentSource": "`d2b-core/src/minijail_profile.rs`; `d2b-core/src/processes.rs` (NamespaceSet, MountPolicy, CgroupPlacement); `d2b-priv-broker/src/ops/spawn_runner.rs`", - "reuseSource": null, - "reuseAction": "EXTRACT/ADAPT", - "destination": "`packages/d2b-provider-system-minijail/src/sandbox_compiler.rs`", - "detailedDesign": "Accept `SandboxSpec` from common contracts; compile NamespaceClass/CapabilityClass/SeccompClass/UserNamespaceSpec/mount/environment/rlimit/umask into a versioned `CompiledSandboxPlan`; compute `sandboxRevisionDigest`; all rejection conditions from §12.1; no raw bitmask/BPF/argv/path in any output type; golden round-trip test vectors", - "integration": "LaunchTicket builder (ADR046-minijail-002); effect port integration (ADR046-minijail-003)", - "dataMigration": "Full reset; current `MinijailProfile` not import-compatible with v3 SandboxSpec", - "validation": "`tests/sandbox_compilation.rs`; `tests/schema.rs`; golden vectors", - "removalProof": "Current `MinijailProfile`/`NamespaceSet` types in `d2b-core` removed after all callers migrate to SandboxSpec" - }, - { - "workItemId": "ADR046-minijail-002", - "specId": "ADR-046-provider-system-minijail", - "specPath": "docs/specs/providers/ADR-046-provider-system-minijail.md", - "dependencyOwner": "ADR046-minijail-001; common `LaunchTicket` contract", - "currentSource": "`d2b-core/src/process_builder.rs`; `d2bd/src/supervisor/*.rs` (ticket generation)", - "reuseSource": null, - "reuseAction": "ADAPT", - "destination": "`packages/d2b-provider-system-minijail/src/launch.rs`", - "detailedDesign": "LaunchTicket construction with compiled sandbox/budget/mount digests; ticket verification on ProviderSupervisor receipt; `d2b.supervisor.v3/IssueLaunchTicket` service call; expired/revoked/malformed ticket rejection", - "integration": "`ProviderSupervisor` local adapter; minijail controller (ADR046-minijail-005)", - "dataMigration": "None — full d2b 3.0 reset; no prior state to migrate", - "validation": "`tests/lifecycle.rs`; `tests/fault_injection.rs`; `tests/fast_path.rs`", - "removalProof": "Current `process_builder.rs` removed after parity" - }, - { - "workItemId": "ADR046-minijail-003", - "specId": "ADR-046-provider-system-minijail", - "specPath": "docs/specs/providers/ADR-046-provider-system-minijail.md", - "dependencyOwner": "ADR046-minijail-001; broker integration owner", - "currentSource": "`d2b-priv-broker/src/ops/spawn_runner.rs`; `d2b-priv-broker/src/sys.rs` (`clone3_spawn_runner`, user namespace setup)", - "reuseSource": null, - "reuseAction": "ADAPT", - "destination": "Broker-side: `d2b-priv-broker` retains `SpawnRunner` op, invoked by the `MinijailProcessEffectPort` implementation owned by core/ProviderSupervisor; Provider-side: `packages/d2b-provider-system-minijail/src/launch.rs` calls `MinijailProcessEffectPort` with opaque Process/LaunchTicket/profile IDs; `user_ns.rs` implements the user namespace pre-establishment protocol", - "detailedDesign": "`clone3(CLONE_PIDFD | CLONE_INTO_CGROUP)` with pre-declared cgroup leaf FD; user namespace pre-establishment sequence (§7.7) when `userNamespace` set; host UID 0 rejection; parent name-to-inode re-validation; zero-host-capability invariant (ADR 0021); `MinijailProcessEffectPort` privately maps opaque IDs to SpawnRunner/OpenDevice/clone3/uid-map/FD effects; Provider crate imports no broker service/client/DTO", - "integration": "ADR046-minijail-002 (LaunchTicket); real cgroup/broker fixture in `integration/clone3_pidfd/` and `integration/user_namespace/`", - "dataMigration": "None — full d2b 3.0 reset; no prior state to migrate", - "validation": "`tests/fault_injection.rs`; `integration/clone3_pidfd/`; `integration/user_namespace/`", - "removalProof": "Old broker `SpawnRunner` direct-caller paths in `d2bd` removed after system-minijail Provider integration" - }, - { - "workItemId": "ADR046-minijail-004", - "specId": "ADR-046-provider-system-minijail", - "specPath": "docs/specs/providers/ADR-046-provider-system-minijail.md", - "dependencyOwner": "ADR046-minijail-003; wait/pidfd owner", - "currentSource": "`d2bd/src/supervisor/pidfd_table.rs` (PidfdTable, WaitTermination, BrokerReapLog)", - "reuseSource": null, - "reuseAction": "EXTRACT/ADAPT", - "destination": "`packages/d2b-provider-system-minijail/src/pidfd.rs`; `packages/d2b-provider-system-minijail/src/wait.rs`", - "detailedDesign": "Async `waitid(P_PIDFD)` via `AsyncFd` fd readability; no blocking `waitid` on watch-loop task; `pidfd_open(2)` dispatched through bounded blocking adapter with explicit timeout; pidfd never serialized; pidfd close/reopen after controller restart; exit class classification (clean-exit/crash/signal/timeout/unknown); SIGTERM/SIGKILL via pidfd_send_signal; drainTimeout enforcement", - "integration": "Controller restart → adoption (ADR046-minijail-005); finalize (§8.6)", - "dataMigration": "None — full d2b 3.0 reset; no prior state to migrate", - "validation": "`tests/lifecycle.rs`; `tests/redaction.rs` (PID never in log/status/audit); `tests/blocking_adapter.rs` (pidfd_open via adapter; timeout → error)", - "removalProof": "Old `PidfdTable` in `d2bd` supervisor removed after Provider integration" - }, - { - "workItemId": "ADR046-minijail-005", - "specId": "ADR-046-provider-system-minijail", - "specPath": "docs/specs/providers/ADR-046-provider-system-minijail.md", - "dependencyOwner": "All of ADR046-minijail-001 through ADR046-minijail-004; ComponentSession/d2b-bus (ADR046-session-001, ADR046-bus-001); bootstrap authz", - "currentSource": "`d2bd/src/supervisor/*.rs` (DagExecutor, NodeOutcome); `d2bd/src/supervisor/pidfd_table.rs`; `d2b-realm-core/src/allocator_engine.rs` (adoption/identity concepts)", - "reuseSource": null, - "reuseAction": "ADAPT", - "destination": "`packages/d2b-provider-system-minijail/src/` — controller binary entry point; reconcile loop; adoption; quarantine; bootstrap authz; health/status; restart; finalize", - "detailedDesign": "Full Process/EphemeralProcess reconcile algorithm (§8); fast path ≤5/≤20 ms gates; spawn via `MinijailProcessEffectPort` (opaque IDs; no broker DTO imported); adoption algorithm (§8.5) with `/proc` reads and cgroup enumeration via bounded blocking adapters; quarantine on ambiguity; quarantine reuse blocked until externally established process-absence proof or full Zone reset; no signal to quarantined/ambiguous identity; restart/backoff; finalize (§8.6); EphemeralProcess continuation recovery (§9); bootstrap authz scope (§3); post-bootstrap RBAC; metric label closed-set enforcement (no `zone` label); controller writes status only on Process/EphemeralProcess resources; Provider resource status aggregated by core; the controller declares no Provider state Volume and mounts none — its bounded non-secret operational state lives in `status`/the core Operation ledger (§5.1, D087) and running units are re-adopted from cgroup leaves + fresh pidfds on restart", - "integration": "Zone runtime startup (bootstrap); all v3 ResourceClient/bus/session paths", - "dataMigration": "Full reset; current DAG/role snapshot import not required", - "validation": "`tests/lifecycle.rs`; `tests/ephemeral_lifecycle.rs`; `tests/conformance.rs`; `tests/adoption_quarantine.rs`; `tests/bootstrap_authz.rs`; `tests/fast_path.rs`; `tests/blocking_adapter.rs`; `integration/adoption_restart/`; `integration/quarantine_scenario/`; `integration/latency_gate/`; shared conformance suite in `d2b-process-conformance`", - "removalProof": "Current `d2bd` DAG executor and direct spawn paths removed only after all ProcessRoles in the role-disposition table (ADR-046-components-processes-and-sandbox, §Representative baseline mapping) reach parity under system-minijail or system-systemd" - }, - { - "workItemId": "ADR046-minijail-006", - "specId": "ADR-046-provider-system-minijail", - "specPath": "docs/specs/providers/ADR-046-provider-system-minijail.md", - "dependencyOwner": "ADR046-minijail-005; Nix integrator; test infrastructure owner", - "currentSource": "`nixos-modules/processes-json.nix`; `nixos-modules/minijail-profiles.nix`; `packages/d2b-contract-tests/tests/policy_observability.rs`", - "reuseSource": null, - "reuseAction": "ADAPT", - "destination": "`nixos-modules/` — v3 Nix `Process`/`EphemeralProcess` resource authoring; Provider catalog entry; `docs/reference/schemas/v3/Process.json`; `docs/reference/schemas/v3/EphemeralProcess.json`; `make test-drift` schema drift gate", - "detailedDesign": "Nix module accepts `d2b.zones..resources.` with `type = \"Process\"` or `\"EphemeralProcess\"`; eval-time validation rules (§16.4); build-time JSON validation (§16.5); artifact catalog integration; cleanup contract tests (§16.5)", - "integration": "`d2b.artifacts` catalog; Zone bundle emission; `make test-drift`", - "dataMigration": "Current `nixos-modules/processes-json.nix` and minijail profile Nix removed at cutover", - "validation": "`nix-unit` eval cases for every validation rule; schema drift gate; `tests/schema.rs`", - "removalProof": "`processes-json.nix`, `minijail-profiles.nix`, and `programs-json.nix` removed after v3 Nix parity" - }, - { - "workItemId": "ADR046-network-001", - "specId": "ADR-046-resources-network", - "specPath": "docs/specs/ADR-046-resources-network.md", - "dependencyOwner": "W0 shared contract root; `d2b-contracts`", - "currentSource": "`packages/d2b-core/src/host.rs` lines 290–520 (`NetEnv`, `IfName`, `ExternalNetworkPolicy`, `NftablesModel`, `BridgePortFlags`, `TapRole`, `Ipv6SysctlEntry`, `IfNameMapping` lines 242–256; **also** `VmRuntimeRow` lines 155–167 with `tap`/`bridge`/`net_vm`/`env` fields — attachment status precursors); `packages/d2b-core/src/processes.rs` lines 98–141 (`ProcessNetworkInterface`, `ProcessNetworkInterfaceType`, `ProcessMacvtapInterface` — current VMM runner network interface DTOs; these are per-Guest VMM fields, not Network-level fields, and migrate to Guest spec under `Provider/runtime-cloud-hypervisor`); `packages/d2b-contracts/src/broker_wire.rs` (authoritative broker op list; network-relevant: `ApplyNftables`, `ApplyNmUnmanaged`, `ApplyRoute`, `ApplySysctl`, `SetBridgePortFlags`, `UpdateHostsFile`, `SeedDnsmasqLease`, `CreatePersistentTap`, `CreateTapFd`); `nixos-modules/lib.nix` lines 396–460 (`subnetIp`, `subnetMask`, `mkMac`, `cidrOverlaps`)", - "reuseSource": "None from main; all from v3 baseline", - "reuseAction": "extract and adapt", - "destination": "`packages/d2b-contracts/src/v3/network.rs`: NetworkSpec, NetworkStatus, AttachmentSpec, AttachmentStatus, ExternalAttachmentSpec, ExternalAttachmentStatus, PortForwardSpec, NetworkConditionType; `packages/d2b-contracts/src/v3/ifname.rs`: IfName newtype, derivation, collision detection (extracted from `d2b-host/src/ifname.rs`). Also defines `User/net-local-controller` as a proper Resource with explicit lifecycle: `Provider/network-local`'s Nix package/module provisions the reserved `net-local-controller` OS account with a private fixed UID/GID in Host prerequisites and in the generic net-VM nixos-system artifact (same account, same UID/GID inside the Guest); the network-local controller creates and owns the User Resource (`spec.osUsername: net-local-controller`, `ownerRef: Provider/network-local`, `managedBy: controller`); `Provider/system-core` verifies the account via NSS lookup and reconciles the User Resource to Ready — it does not provision the OS account. No numeric UID/GID enters any ResourceSpec field, authz check, or audit record; `User.status` MAY carry diagnostic `uid`/`gid` values discovered by NSS lookup, but those are informational only and are never authorization inputs. The network-local controller waits for `User/net-local-controller` to reach `Ready` before creating any config Volume (reconcile precondition, not a bootstrap side effect).", - "detailedDesign": "Strict ResourceEnvelope with Network-specific spec/status. IfName newtype: IFNAMSIZ-1 validated, FNV-1a 64-bit derivation, base32 Crockford, 8-char suffix, bridge/tap role prefixes, detect_collisions over IfNameMapping slice. cidrOverlaps: pure Rust IPv4 arithmetic, same algorithm as lib.nix. NetworkSpec validators: /24 lanCidr with .0 base, /30 uplinkCidr, unique attachment indices 2–250, default hostBlocklist enforcement.", - "integration": "Provider dossiers, Nix resource compiler, resource store/API bind these canonical types", - "dataMigration": "Full d2b 3.0 reset; no env→Network import", - "validation": "Golden JSON/CBOR vectors; CIDR overlap property tests; IfName collision and derivation determinism tests; default hostBlocklist enforcement; attachment index uniqueness; `User/net-local-controller` User resource lifecycle/readiness test: controller creates User Resource with `spec.osUsername = \"net-local-controller\"` (`ownerRef: Provider/network-local`); controller waits for User resource to reach `Ready` before proceeding; controller aborts with `ConfigVolumeReady=False/user-not-ready` if User resource is not Ready; verifies no numeric UID/GID appears in the Resource spec, authz check, or audit record; verifies that any diagnostic `uid`/`gid` in `User.status` is never used as an authorization input", - "removalProof": "Old `d2b_core::host::NetEnv` and related types removed only after v3 resource API consumers use `d2b_contracts::v3::network` types" - }, - { - "workItemId": "ADR046-network-002", - "specId": "ADR-046-resources-network", - "specPath": "docs/specs/ADR-046-resources-network.md", - "dependencyOwner": "ADR046-network-001; d2b-host network modules owner", - "currentSource": "`packages/d2b-host/src/ifname.rs` (FNV-1a derivation, detect_collisions, DEFAULT_PREFIX, BRIDGE_TAG, TAP_TAG); `packages/d2b-host/src/bridge_port.rs` (BridgePortReadback, east-west policy, TapRole defaults); `packages/d2b-host/src/nftables.rs` (NftBatch, hash_inet_d2b_table, coexistence policy, USBIP carve-out skeleton); `packages/d2b-host/src/routes.rs` (route/dnsmasq-bound/IPv6 preflight); `packages/d2b-host/src/netlink.rs` (IPv6 sysctl sequence)", - "reuseSource": null, - "reuseAction": "extract (ifname, bridge_port, nftables) into shared network-local Provider library; adapt (routes, netlink) into controller observe loop", - "destination": "`packages/d2b-provider-network-local/src/ifname.rs`, `bridge_port.rs`, `nftables.rs`, `routes.rs`, `netlink.rs`", - "detailedDesign": "Move IfName derivation to `d2b-contracts` (ADR046-network-001); keep bridge_port, nftables, routes, netlink in Provider crate. nftables: retain full `inet d2b` chain layout, ownership markers, coexistence matrix, USBIP carve-out skeleton as typed inputs from Network spec. routes: adapt dnsmasq-bound check to use Network status instead of `HostJson.environments`. netlink: keep IPv6-off sequence; add defense-in-depth re-application path.", - "integration": "Controller observe loop uses nftables digest drift, bridge_port readback, and IPv6 sysctl to drive `FirewallReady`, `FabricReady` conditions", - "dataMigration": "None (behavior preserved; host bridge names change from `br--*` to `d2b-b` after cutover)", - "validation": "Existing `bridge_port::tests::readback_matches_defaults`, `ops::tap::tests::set_bridge_port_flags_readback_drift_fails_closed`, `netlink::tests::ipv6_off_sequence_runs_in_order`, nftables coexistence matrix tests; all pinned in `tests/golden/pinned/host-prepare-network.txt` and `tests/golden/pinned/net-canaries.txt`", - "removalProof": "`packages/d2b-host/src/{ifname,bridge_port,nftables,routes,netlink}.rs` removed only after Provider conformance tests pass" - }, - { - "workItemId": "ADR046-network-003", - "specId": "ADR-046-resources-network", - "specPath": "docs/specs/ADR-046-resources-network.md", - "dependencyOwner": "ADR046-network-001, ADR046-network-002; Provider/runtime-cloud-hypervisor dossier owner", - "currentSource": "`nixos-modules/net.nix` (full file, 450 lines); `nixos-modules/net-mdns.nix`; `nixos-modules/lib.nix` subnetIp/mkMac/cidrOverlaps", - "reuseSource": null, - "reuseAction": "adapt", - "destination": "`packages/d2b-provider-network-local/` — artifact catalog integration for net-VM nixos-system artifact resolution; `packages/d2b-provider-network-local/nix/` — default net-VM NixOS module (parameterized successor to net.nix), built and registered as a nixos-system artifact in `d2b.artifacts`", - "detailedDesign": "`Network.spec.netVmSystemArtifactId` is REQUIRED. It must reference a declared `d2b.artifacts` entry with `type = \"nixos-system\"`; verified at Nix build time (Stage 2 check, hard build error if absent or wrong type). No implicit default exists; Provider artifacts cannot silently provide a separately typed system artifact. The controller sets `Guest.spec.systemArtifactId` to the artifact ID value at reconcile time (the value is already validated by the build; the controller fails closed if absent at runtime). The net-VM nixos-system artifact is **generic** (INV-NET-008): it contains the guest-agent binary and runtime, kernel, base NixOS services, systemd-networkd NIC bootstrap, and the `net-local-controller` **OS account** provisioned by `Provider/network-local`'s Nix module (same private fixed UID/GID as on the Host, so that virtiofs view ACLs on config Volume layout entries are enforced consistently inside the Guest; `Provider/system-core` performs NSS lookup reconciliation, not OS account provisioning; no numeric UID/GID appears in any ResourceSpec field, authz check, or audit record; `User.status` MAY carry diagnostic `uid`/`gid` from NSS lookup but those are informational only and never authorization inputs). It does NOT encode per-Network desired data; per-Network config (dnsmasq, nftables, routing, attachments) is delivered via the controller-owned config Volume and applied by the guest-agent Process. The artifact preserves compile-time-fixed content: `lib.mkForce` on 10-eth-dhcp (INV-NET-001); two systemd-networkd interface units matched by MAC; IPv6 suppression sysctls on NIC interfaces; ip6 filter table drop-all policy. **mDNS reflector and local dnsmasq DNS bridge are separate owned Process resources** (D-NETWORK-001); they are not inline services in the artifact.", - "integration": "Network controller resolves artifact ID → sets `Guest.spec.systemArtifactId`. Controller separately creates `Volume/net--config` with per-Network config and `Process/net--agent` (guest-agent). `Provider/runtime-cloud-hypervisor` reads `systemArtifactId` to produce the net-VM bundle and mounts the Volume view into the Guest.", - "dataMigration": "Destructive v3 reset; existing net VMs are re-created under new IfNames", - "validation": "nix-unit: `tests/unit/nix/cases/net-vm-network.nix` (adapted to v3 resource API); INV-NET-001 assertion in new nix-unit case; no mDNS inline service appears in the generated artifact; no per-Network dnsmasq or nftables data in artifact (INV-NET-008); integration test: mDNS Process resources are created when `spec.mdns.enable = true`; Stage 2 build test: absent `netVmSystemArtifactId` fails with required-field build error; wrong artifact type fails with `artifact-type-mismatch` error; `packages/d2b-provider-network-local/tests/net_vm_artifact_is_generic.rs` — two Networks with different CIDRs produce same `systemArtifactId` and different config Volume content", - "removalProof": "`nixos-modules/net.nix` and `nixos-modules/net-mdns.nix` removed only after net-VM artifact parity tests pass" - }, - { - "workItemId": "ADR046-network-004", - "specId": "ADR-046-resources-network", - "specPath": "docs/specs/ADR-046-resources-network.md", - "dependencyOwner": "ADR046-network-001, ADR046-network-002, ADR046-network-003; Nix integrator", - "currentSource": "`nixos-modules/network.nix` (full file, 500+ lines; bridge/netdev/sysctl/NM-unmanaged/route sections); `nixos-modules/host-json.nix` (emits `host.json` `environments[].nftables`, `environments[].ifNameMappings`, `environments[].usbipBusidLocks` — superseded by Network status API); `nixos-modules/processes-json.nix` (emits `processes.json` `ProcessNetworkInterface`/`ProcessMacvtapInterface` per runner — superseded by Guest spec network fields); `nixos-modules/index.nix` envMeta/netMeta sections; `nixos-modules/options-envs.nix`; `nixos-modules/options-realms-network.nix`; `nixos-modules/options-vms.nix` (`d2b.vms..env` line 944, `d2b.vms..index` line 962 — current attachment semantics source; maps to `Network.spec.attachments[].executionRef` + `index`); `nixos-modules/options-site.nix` (`d2b.site.allowUnsafeEastWest` line 48 — maps to per-Network `Network.spec.isolation.allowEastWest`; Zone.spec is empty in v3; `d2b.hostLanCidrs` line 382 — maps to Host resource network inventory at runtime; not a Zone.spec field)", - "reuseSource": null, - "reuseAction": "adapt", - "destination": "`nixos-modules/resources-network.nix`: Nix resource object emitter for Network ResourceType; `nixos-modules/index.nix`: network resource compilation section", - "detailedDesign": "The emitter replaces `d2b.envs.` with `d2b.zones..resources. = { type = \"Network\"; spec = { ... }; }` (attr key = resource name; `type` explicit field; `spec` fields identical to the canonical ResourceSpec JSON — no bespoke Nix vocabulary). It validates CIDR shape, attachment index uniqueness, external attachment constraints, and CIDR overlap (reusing `cidrOverlaps` from `lib.nix`). **Bridges are NOT emitted as `systemd.network.netdevs` entries** (D-NETWORK-003 resolved; bridges are created dynamically by the broker `CreateBridge` op at reconcile time). The Nix emitter provisions only bootstrap/static prerequisites that do not require runtime bridge IfNames: `networking.networkmanager.unmanaged` pattern for the `d2b-*` prefix (covers all dynamically-created bridges and taps regardless of specific IfNames; emitted to `00-d2b-unmanaged.conf`); schema validation and controller binary deployment artifacts. Current `d2b.vms..env` + `d2b.vms..index` attachment semantics (`options-vms.nix` lines 944/962) become `Network.spec.attachments[].executionRef` + `index`. Current `d2b.site.allowUnsafeEastWest` (`options-site.nix` line 48) moves to the per-Network `isolation.allowEastWest` field; Zone.spec is empty in v3. Current `d2b.hostLanCidrs` (`options-site.nix` line 382) becomes the Host resource's network inventory, queried at runtime; at Nix build time the eval may validate CIDRs against declared host configuration input. The emitter does not emit `boot.kernel.sysctl` entries per bridge IfName (bridges do not exist at activation time; IPv6 suppression is applied by `CreateBridge` and `ApplySysctl` per INV-NET-002). **Nix option types** for `spec.*` fields are generated from `Network.schema.json`; they are not hand-written. **Bundle generation**: the emitter collects all declared `Network` resource objects, sorts them lexicographically by `(type, name)`, serializes each as canonical JSON **omitting `managedBy` and `configurationGeneration`** (core sets these at activation), and assembles the Zone resource bundle at `$out/bundle.json` (see [Nix configuration contract — Stage 3](#stage-3--build-output-zone-resource-bundle)). The emitter records a `providerSchemaDigest` entry for `Provider/network-local` in the bundle resolved from the artifact catalog. The bundle's `contentHash` is a SHA-256 of the sorted canonical resource array; the derivation is fixed-output so that identical configuration always produces the same store path. The `managedBy` field is NOT set by the emitter; core sets `managedBy = \"configuration\"` and assigns the `configurationGeneration` counter when applying the bundle. The core controller retains prior bundle copies under `/var/lib/d2b/zones//configuration/prior/` per [Generation lifecycle and cleanup contract](#generation-lifecycle-and-cleanup-contract).", - "integration": "Nix resource objects serialize exactly the Rust NetworkSpec contract (ADR046-network-001). The provider install declares the schema digest. Zone runtime generation-transition logic (ADR046-network-008) reads the bundle at activation.", - "dataMigration": "Full v3 reset; `d2b.envs.*` declarations must be rewritten as Network resources", - "validation": "nix-unit CIDR overlap, assertion eval, and bridge-sysctl cases; `make test-flake` with updated examples; `make test-drift` for schema/emitter parity; `packages/d2b-contracts/tests/generation_bundle.rs` for bundle format and `contentHash` stability; nix-unit `tests/unit/nix/cases/generation-cleanup-absent-network.nix` for removed-resource scheduling (added by ADR046-network-008)", - "removalProof": "`nixos-modules/network.nix`, `nixos-modules/options-envs.nix`, and `nixos-modules/options-realms-network.nix` removed only after `resources-network.nix` and controller reach parity; `d2b.envs` consumer migration guide updated" - }, - { - "workItemId": "ADR046-network-005", - "specId": "ADR-046-resources-network", - "specPath": "docs/specs/ADR-046-resources-network.md", - "dependencyOwner": "ADR046-network-001–004; network-local controller owner; D-NETWORK-001, D-NETWORK-002, and D-NETWORK-003 resolved", - "currentSource": "`nixos-modules/network.nix` (tap/sysctl sections); `packages/d2b-host/src/{bridge_port,nftables,netlink,routes}.rs`; broker ops in `packages/d2b-contracts/src/broker_wire.rs`: **real runtime ops** `ApplyNftables`, `ApplyNmUnmanaged`, `ApplyRoute`, `ApplySysctl`, `SetBridgePortFlags`, `UpdateHostsFile`, `SeedDnsmasqLease` (all `implemented-and-reachable`); **new ops to author**: `CreateBridge`, `DeleteBridge` (do not exist in v3 baseline; must be added to `broker_wire.rs` and implemented as `RealBrokerRequest` handlers in `packages/d2b-priv-broker/src/runtime.rs`); **NOT current ops**: `CreateMacvtap` does not exist — macvtap is created inside broker's `SpawnRunner` dispatch (`packages/d2b-priv-broker/src/runtime.rs` line 5097 `live_create_macvtap_fd`)", - "reuseSource": null, - "reuseAction": "adapt", - "destination": "`packages/d2b-provider-network-local/src/controller.rs`: async NetworkReconciler; `packages/d2b-provider-network-local/src/plan.rs`: ReconcilePlan computation; `packages/d2b-provider-network-local/src/observe.rs`: drift-detection observe loop. Full crate layout required (see [Package and crate boundary](#package-and-crate-boundary)): `src/` (controller/plan/observe + colocated unit tests), `tests/` (hermetic conformance and state-machine tests), `integration/` (provider-system reconcile fixtures), `README.md` (Network ResourceType, controller binary, placement, RBAC, security invariants, build/test/integration commands).", - "detailedDesign": "Implements full async reconcile interface from `ADR-046-resource-reconciliation`. `plan()` computes desired vs. actual bridge-presence, sysctl, host-side nftables, hosts-file, NM-unmanaged, config Volume content, Guest, guest-agent Process, and mDNS-Process states. `reconcile()` dispatches in order: `CreateBridge` for each bridge not present (broker applies IPv6 sysctls atomically; `CreateBridge` failure sets `FabricReady=False/bridge-create-error` and aborts) → `ApplySysctl` (defense-in-depth IPv6) → `ApplyNftables` (host-side `inet d2b` table) → `ApplyNmUnmanaged` → `ApplyRoute` → `UpdateHostsFile` → `SeedDnsmasqLease` for new reservations → **Volume upsert** (two-phase): Phase 1 — create `Volume/net--config` with `kind: ephemeral`, `source.executionRef: Host/`, `source.settings.kind: tmpfs`, `quota: {maxBytes: 4194304, maxInodes: 128, enforcement: hard}` (tmpfs quota charged to Host memory budget), `layout` entries (root directory with `type: directory` and four config files each with `type: file`, `ownerRef: User/net-local-controller`, `groupRef: User/net-local-controller`, `mode: \"0640\"`, `accessAcl: []`, `defaultAcl: []`, `noFollow: true`, conservative create/repair/cleanup policies), `views: {guest-readonly: {path: \"\", rights: [read, traverse]}}`, `attachments: []` (no Guest attachment); abort on terminal error with `ConfigVolumeReady=False/config-volume-error`. Wait for Volume backing to reach `Ready`; requeue on `Degraded`/`Failed` with `ConfigVolumeReady=False/volume-not-ready`. Write rendered config content through Volume write service (no raw host paths). Phase 2 — create Guest upsert with `systemArtifactId` from REQUIRED `Network.spec.netVmSystemArtifactId`. Wait for Guest `Ready`. Then update Volume with Guest attachment: `attachments: [{executionRef: Guest/, transport: virtiofs, view: guest-readonly, access: read-only, mountPath: \"/run/d2b/net-config\", settings: {posixAcl: false, xattr: false, cache: auto, inodeFileHandles: never, threadPoolSize: null, socketGroup: null}}]`; wait for attachment `Ready`; requeue on `Degraded` with `ConfigVolumeReady=False/attachment-not-ready`. Create or update guest-agent Process `Process/net--agent` with `processClass: worker`, `sandbox: {namespaceClasses: [], capabilityClasses: [network-admin, network-bind, network-raw]}` (inherits Guest network namespace; `network-admin`→`CAP_NET_ADMIN`, `network-bind`→`CAP_NET_BIND_SERVICE`, `network-raw`→`CAP_NET_RAW`, all effective in Guest network namespace only; INV-NET-009), `mounts: [{volumeRef: Volume/net--config, view: guest-readonly, mountPath: \"/run/d2b/net-config\", access: read-only, required: true}]`. mDNS Process upsert when `spec.mdns.enable = true` (D-NETWORK-001) → `SetBridgePortFlags` per tap. Each broker op returns typed audit evidence. `observe()` re-reads `firewallDigest` (host-side), bridge isolation flags, IPv6 sysctls, and guest-agent Process status (`dnsmasq-bound`, `firewall-applied` predicates); queues reconcile on drift. **Finalizer** (strictly child-first): `NetworkDraining` → delete guest-agent Process and mDNS Processes; wait for each Deleted watch event → update Volume to remove Guest attachment (`attachments: []`); wait for attachment removal confirmed → delete `Guest/`; wait for Deleted watch event → delete `Volume/net--config`; wait for Deleted watch event → `ApplyNftables` (empty) → `ApplyNmUnmanaged` (empty) → `UpdateHostsFile` (empty) → `DeleteBridge` for each bridge (idempotent) → clear finalizer. No USBIP rules installed by Network; `UsbipBindFirewallRule` issued by device-usbip directly (D-NETWORK-002).", - "integration": "Controller process registers descriptor, watches `Network` resources via d2b-bus/ComponentSession/ResourceClient. Owned Guest and Process mutations trigger owner reconciliation. Device-usbip Provider reads Network status for USBIP boundary (D-NETWORK-002).", - "dataMigration": "None after full reset", - "validation": "`ADR046-reconcile-001` toolkit conformance; latency gates (p95 ≤5 ms hint-to-handler); Network-specific: CIDR conflict blocks reconcile, `CreateBridge` failure sets `FabricReady=False`, Volume creation failure sets `ConfigVolumeReady=False/config-volume-error`, `User/net-local-controller` not Ready aborts with `ConfigVolumeReady=False/user-not-ready`, Volume schema round-trip (kind=ephemeral, source.settings.kind=tmpfs, quota.enforcement=hard, layout type=file entries, views.guest-readonly.rights=[read,traverse]), tmpfs quota charged to Host memory budget (test Volume creation fails when Host memory budget exceeded), Guest not created before Volume backing `Ready`, Guest attachment not added before Guest `Ready`, guest-agent Process created after attachment `Ready` (`processClass: worker`, `namespaceClasses: []`, `capabilityClasses: [network-admin, network-bind, network-raw]`, `access: read-only`, `required: true`), host-capability leakage test: no host-netns process gains `CAP_NET_ADMIN`/`CAP_NET_BIND_SERVICE`/`CAP_NET_RAW` as result of guest-agent launch (INV-NET-009; `tests/host-integration/guest-agent-cap-confinement.nix`), `DeleteBridge` called on finalizer, Volume attachment removed before Guest deletion in finalizer (test order: agent Deleted → attachment removed → Guest Deleted → Volume Deleted → bridges), east-west invariant (INV-NET-003), hostBlocklist enforcement (INV-NET-004), macvtap attachment status (delegated to runtime-ch), mDNS Process created/deleted with `spec.mdns.enable` toggle, broker INV-NET-002 tests, config-only spec change updates Volume content and triggers agent reload without Guest restart (INV-NET-008); golden tests updated for v3 IfNames", - "removalProof": "Daemon-orchestrated network/bridge lifecycle removed only after controller passes conformance and parity tests" - }, - { - "workItemId": "ADR046-network-006", - "specId": "ADR-046-resources-network", - "specPath": "docs/specs/ADR-046-resources-network.md", - "dependencyOwner": "ADR046-network-001, ADR046-network-005; test owner", - "currentSource": "`tests/unit/nix/cases/net-vm-network.nix`; `tests/golden/pinned/net-vm-bundle-gate.txt`; `tests/golden/pinned/net-canaries.txt`; `tests/golden/pinned/host-prepare-network.txt`; `tests/host-integration/bridge-isolation.nix`; `tests/integration/live/network-isolation.sh`", - "reuseSource": null, - "reuseAction": "adapt", - "destination": "`tests/unit/nix/cases/net-vm-network.nix` (adapted to v3 resource API); updated golden pins; `tests/host-integration/bridge-isolation.nix` (adapted); `packages/d2b-priv-broker/tests/bridge_lifecycle.rs` (new hermetic broker tests). Provider crate test directories: `packages/d2b-provider-network-local/tests/` — hermetic Cargo integration tests (conformance suite, controller state machine, CIDR validation vectors, IfName determinism, invariant tests INV-NET-001–007, reconcile/observe/finalize with deterministic clock, fault injection); `packages/d2b-provider-network-local/integration/` — container/Host/Guest lifecycle fixtures invoked by `make test-integration` (bridge isolation, east-west double opt-in, nftables drift detection, macvtap lifecycle). Both directories required by package policy.", - "detailedDesign": "Rust integration tests: NetworkSpec CIDR validation golden vectors; AttachmentSpec index uniqueness; ExternalAttachmentSpec mutual-exclusion validation; IfName derivation determinism; CIDR overlap arithmetic; INV-NET-001 through INV-NET-009 invariant tests; reconcile/observe/finalize state machine (deterministic clock). Broker tests: `create_bridge_applies_ipv6_sysctl` (INV-NET-002 layer 1); `delete_bridge_is_idempotent`; `create_bridge_parameters_match_spec` (MTU, STP disabled, multicast snooping disabled). Controller tests: `reconcile_applies_sysctl_defense_in_depth` (INV-NET-002 layer 2); `volume_created_before_guest`; `guest_not_created_until_volume_ready`; `agent_process_created_after_guest`; `finalizer_order_agent_then_guest_then_volume_then_bridges`; `config_only_spec_change_updates_volume_no_guest_restart` (INV-NET-008); `finalizer_calls_delete_bridge`; `mdns_process_created_on_enable`; `mdns_process_deleted_on_disable`; `host_capability_leakage` (INV-NET-009). nix-unit: INV-NET-001 lib.mkForce assertion; net-VM artifact has no inline mDNS service and no per-Network dnsmasq/nftables data (INV-NET-008); Network emitter CIDR constraint assertions; no `systemd.network.netdevs` bridge entries emitted. Host integration: bridge isolation with east-west opt-in; nftables drift detection; macvtap create/delete lifecycle; config Volume update propagates to guest-agent without Guest restart; `tests/host-integration/guest-agent-cap-confinement.nix` (INV-NET-009 zero leakage to host netns).", - "integration": "Pinned tests registered in `tests/golden/pinned/`; nix-unit cases in `tests/unit/nix/cases/`; host integration in `tests/host-integration/`", - "dataMigration": "None — full d2b 3.0 reset; no prior state to migrate", - "validation": "All listed tests must pass before `nixos-modules/network.nix` removal is eligible", - "removalProof": "Not applicable (this work item IS the test successor)" - }, - { - "workItemId": "ADR046-network-007", - "specId": "ADR-046-resources-network", - "specPath": "docs/specs/ADR-046-resources-network.md", - "dependencyOwner": "ADR046-network-005; device-usbip Provider dossier; D-NETWORK-002 resolved", - "currentSource": "`nixos-modules/network.nix` lines 444–461 (USBIP host firewall); `packages/d2b-core/src/host.rs` lines 324–328 (usbip_backend_port, usbip_busid_locks in NetEnv); `packages/d2b-host/src/` usbip_argv.rs", - "reuseSource": null, - "reuseAction": "extract and adapt", - "destination": "Network controller exposes `status.network.hostUplinkIp` and `status.network.uplinkBridge.ifName` as read-only dependency inputs for `Provider/device-usbip`. `packages/d2b-provider-device-usbip/src/controller.rs` reads these through a ResourceClient `networkRef` dependency watch and issues the closed `UsbipBindFirewallRule` broker operation for the TCP/3240 carve-out rule. Network spec is not mutated by device-usbip. Full crate layout required for `packages/d2b-provider-device-usbip/` (see [Package and crate boundary](#package-and-crate-boundary)): `src/` (controller and usbip runner + unit tests), `tests/` (hermetic conformance, dependency-watch state machine, UsbipBindFirewallRule round-trip), `integration/` (Host/Guest USBIP attach/detach lifecycle fixtures), `README.md` (Provider identity, UsbipDevice ResourceType, USBIP daemon Process, Network read-only dependency contract, RBAC, security invariants, standalone-repo path).", - "detailedDesign": "device-usbip Provider reads `status.network.uplinkBridge.ifName` and `status.network.hostUplinkIp` from the Network resource via `networkRef` dependency watch. It declares its own `Process` resources for the usbip-daemon and proxy and issues `UsbipBindFirewallRule` through the broker for the TCP/3240 host-side carve-out. This keeps device-usbip self-contained: it does not write to `Network.spec`, does not set any extension field, and cannot be blocked by a Network reconcile cycle. The broker op `UsbipBindFirewallRule` is the only host firewall mutation surface for USBIP.", - "integration": "device-usbip Provider reads Network status → issues UsbipBindFirewallRule → owns USBIP Processes", - "dataMigration": "Current network.nix USBIP carve-out replaced by UsbipBindFirewallRule broker op", - "validation": "device-usbip conformance tests; network-local nftables tests confirm no USBIP carve-out emitted by network controller; existing pinned `usbip-firewall-skeleton.txt` golden updated for direct broker-op path", - "removalProof": "Network.nix USBIP sections removed only after UsbipBindFirewallRule mechanism passes conformance" - }, - { - "workItemId": "ADR046-network-008", - "specId": "ADR-046-resources-network", - "specPath": "docs/specs/ADR-046-resources-network.md", - "dependencyOwner": "ADR046-network-004, ADR046-network-005; Zone runtime integrator", - "currentSource": "No current v3 source: generation lifecycle and `managedBy`/`configurationGeneration` classification do not exist on the pre-ADR45 v3 baseline. The v3 baseline uses NixOS activation scripts that atomically replace all host JSON artifacts; there is no per-resource generation tracking, no async cleanup queue, and no `managedBy` field.", - "reuseSource": null, - "reuseAction": "new", - "destination": "`packages/d2b-core-controller/src/configuration.rs`: bundle application, diff, generation-transition logic (including per-item name-conflict handling), prior-bundle retention under `/var/lib/d2b/zones//configuration/prior/`; `packages/d2b-core-controller/src/cleanup.rs`: removal scheduling and `PendingCleanup` condition tracking; `packages/d2b-contracts/src/generation_bundle.rs`: `ZoneBundle`/`BundleResource`/`BundleMetadata` **input** DTOs — MUST NOT include `managedBy` or `configurationGeneration` (both are persisted resource metadata set by core at activation, not bundle input fields); `ManagedBy` closed enum `{ Configuration, Controller, Api }` and `configurationGeneration: u64` live in `packages/d2b-core-controller/src/resource_store.rs` as persisted resource metadata fields; `nixos-modules/resources-network.nix` (emits bundle with `managedBy`/`configurationGeneration` absent; core sets both at activation per ADR046-network-004); `d2b.zones..retainedGenerations` Nix/compiler-level Zone option (outside `Zone.spec`; default `3`, range `1..16`); `tests/unit/nix/cases/generation-cleanup-absent-network.nix`; `packages/d2b-contracts/tests/generation_bundle.rs`; `tests/host-integration/nix-generation-cleanup.nix`", - "detailedDesign": "**Core generation tracking** (`packages/d2b-core-controller/src/configuration.rs`): core maintains a monotone `configurationGeneration` counter per Zone in its durable state. On each bundle application it compares the incoming `contentHash` against the prior applied hash. If different, it increments `configurationGeneration`, sets `managedBy = \"configuration\"` and the new counter value on each resource in the bundle, and performs the resource diff (create/update/delete scheduling). The `managedBy` and `configurationGeneration` fields are absent from the Nix-emitted bundle and are set exclusively by core at activation time. **`managedBy` field and per-item name-conflict handling**: `ManagedBy` is a closed enum (`Configuration`, `Controller`, `Api`) persisted in resource metadata at `packages/d2b-core-controller/src/resource_store.rs`. It is NOT a field in `ZoneBundle`/`BundleResource` input DTOs; core sets it at activation. Controllers set `ManagedBy::Controller` when creating owned children (net-VM Guest, config Volume, guest-agent Process, mDNS Processes); exact controller identity/UID/generation are tracked in separate internal metadata, not embedded in the `managedBy` value. API-created resources carry `ManagedBy::Api` and persist until explicit delete with no bundle-driven lifecycle. Core's generation-transition logic only schedules bundle-driven Delete for `ManagedBy::Configuration` resources. **Per-item name-conflict handling**: when a bundle item's `(zone, name)` already exists with `managedBy ≠ \"configuration\"`, core skips that item and records it with `phase = Degraded, reason: name-conflict`; a `ResourceConflictSkipped` audit record is emitted for that item. All non-conflicting items in the bundle proceed normally (Provider-state contract). The existing resource is left completely untouched. The operator deletes the conflicting resource via the resource API; the next bundle application applies the item. **Removal scheduling**: on generation N+1 activation, core performs a set difference: `prev_configuration_managed - new_configuration_managed` = resources to delete. For each, it sets `metadata.deletionRequestedAt` in the resource store and emits a `ResourceDeletionScheduled` audit record. Normal finalizer-path Delete proceeds asynchronously. **`PendingCleanup` condition**: the Zone self resource carries a `PendingCleanup = True` condition while any `managedBy = Configuration` resource has `deletionRequestedAt` set and has not yet been atomically removed. Aggregate Zone `phase = Degraded` applies. The condition transitions to `False` and Zone phase returns to `Ready` when all scheduled deletions complete. **Prior generation bundle retention** (`cleanup.rs`): count-based (`d2b.zones..retainedGenerations`, outside `Zone.spec`, default 3, range 1..16); no TTL. Core copies prior bundles to `/var/lib/d2b/zones//configuration/prior/.json`. A generation is eligible for pruning when all configuration-managed resources from it have either been atomically removed or are present unchanged in a newer generation, AND the count would be exceeded. **`BundleActivated` audit record**: emitted at each generation transition with `contentHash`, `configurationGeneration`, `resourceCount`, and `providerSchemaDigests` map (digests from `type=provider` artifacts via `Provider.spec.artifactId`); no spec contents, CIDRs, or resource names appear in the record. Provider schema digests in the bundle are re-verified against installed Provider artifact digests at application time; a mismatch aborts application with a `BundleRejected` audit record.", - "integration": "ADR046-network-004 (emitter writes bundle format; core sets `managedBy`/`configurationGeneration` at activation) → ADR046-network-008 (runtime reads and applies). ADR046-network-005 (controller Delete path) is invoked by ADR046-network-008 removal scheduling for Network resources. Zone `PendingCleanup` condition and `Degraded` phase are read by CLI `d2b zone status`.", - "dataMigration": "None on v3 initial install (no prior generation state). Host upgrades from the pre-ADR45 v3 baseline perform a reset: core starts with `configurationGeneration = 1` and no prior bundle. All declared resources are treated as new Creates.", - "validation": "**nix-unit**: `tests/unit/nix/cases/generation-cleanup-absent-network.nix` — verifies that a Network resource present in generation N and absent from generation N+1 receives `deletionRequestedAt` and appears in the `PendingCleanup` condition; verifies that a controller-owned `Guest` (`managedBy = \"controller\"`) does NOT receive a direct bundle-driven Delete; verifies that a re-declared (identical spec) Network is NOT scheduled for Delete; verifies `retainedGenerations` default is 3. **Rust contract tests**: Two separate test files — (1) `packages/d2b-contracts/tests/generation_bundle.rs`: tests the **input** bundle DTO only: `ZoneBundle`/`BundleResource`/`BundleMetadata` JSON round-trip, `contentHash` stability across serialization, `providerSchemaDigests` presence, `managedBy` and `configurationGeneration` fields ABSENT from `BundleResource` input struct (verified by both compile-time type check: the fields must not exist on the `BundleResource` type, and runtime JSON serialization: the serialized object must not contain those keys). (2) `packages/d2b-core-controller/tests/resource_metadata.rs`: `ManagedBy` closed enum round-trip with `\"configuration\"`/`\"controller\"`/`\"api\"` values tested separately here since `ManagedBy` is persisted resource metadata in `resource_store.rs`, not a field of the input bundle DTO. **Controller integration tests**: async Delete triggered through finalizers for Network; mDNS Process child deleted before Network finalizer clears; bridge `DeleteBridge` broker call made exactly once during finalizer; controller waits for Deleted watch event (not a persistent phase=Deleted row) before proceeding. **Host integration**: `tests/host-integration/nix-generation-cleanup.nix` — runNixOSTest scenario: apply generation 1 with Network resource, then apply generation 2 with that Network absent; assert Zone enters `Degraded/PendingCleanup`; assert Network `phase = Degraded` with `NetworkDraining = True` and `deletionRequestedAt` set and `reason: configuration-generation-removed`; assert cleanup completes (single store transaction: Deleted REVISION event + row/index removal; dedup-guarded audit append follows committed transaction) and Zone returns to `Ready`; assert no controller-owned children deleted directly by core; assert prior bundle copied to `/var/lib/d2b/zones//configuration/prior/` and retained until cleanup complete; assert bundle pruned when `retainedGenerations` exceeded and generation eligible. **INV-NET-LIFECYCLE-001**: core never schedules bundle-driven Delete for `managedBy ≠ \"configuration\"` resources; verified by static analysis of core's generation-transition diff function, which is bounded at compile time to iterate only the `configuration_managed_resources` set. **INV-NET-LIFECYCLE-002**: per-item name-conflict — when a bundle item collides with `managedBy = \"controller\"` or `\"api\"`, that item is recorded as `Degraded/name-conflict`; the existing resource is left untouched; non-conflicting items continue to activate; tested by `packages/d2b-core-controller/tests/configuration_name_conflict.rs` (three cases: collision with a controller-owned child, an API-created resource, and a same-name configuration resource from a prior generation that completed deletion; each case asserts non-conflicting items still activate).", - "removalProof": "Not applicable (this is a new capability). The `PendingCleanup` condition and zone cleanup audit path have no prior equivalent to remove." - }, - { - "workItemId": "ADR046-network-009", - "specId": "ADR-046-provider-network-local", - "specPath": "docs/specs/providers/ADR-046-provider-network-local.md", - "dependencyOwner": "Provider; Process resource builders owned by `d2b-provider-network-local`.", - "currentSource": "v1 dnsmasq and mDNS process shape came from `nixos-modules/net.nix` and static NixOS services; no v3 Process builder exists.", - "reuseSource": null, - "reuseAction": "port service semantics into canonical Process resource specs", - "destination": "`packages/d2b-provider-network-local/src/process_specs.rs`.", - "detailedDesign": "Implement canonical Process spec builders for agent, dnsmasq, mdns-reflector, mdns-dnsbridge.", - "integration": "Controller creates agent service, dnsmasq worker, and optional mDNS workers as owned Process resources on the net VM; Process Provider reports readiness and lifecycle status.", - "dataMigration": "Full d2b 3.0 reset; no v2 state/config import.", - "validation": "`tests/controller_state.rs`, `integration/mdns_reflector.rs`, and eval case `process-sandbox-netns.nix` validate Process shape, optional mDNS, and guest-netns capability isolation.", - "removalProof": "Supersedes static per-env systemd services; old duplicate service tests are retired after successor Process coverage passes." - }, - { - "workItemId": "ADR046-network-010", - "specId": "ADR-046-provider-network-local", - "specPath": "docs/specs/providers/ADR-046-provider-network-local.md", - "dependencyOwner": "net-vm artifact; owns generic `net-vm-base` nixos-system artifact and shared net-local-controller UID/GID reservation.", - "currentSource": "Reuse lib.mkForce NIC bootstrap from `nixos-modules/base.nix` `10-eth-dhcp` override and account reservation documented in `nixos-modules/host-users.nix`; v1 per-Network config does not carry forward.", - "reuseSource": "`nixos-modules/base.nix` `10-eth-dhcp` lib.mkForce override and host-users reservation table.", - "reuseAction": "preserve generic boot/safety invariants; exclude per-Network static config", - "destination": "`net-vm-base` nixos-system artifact and artifact catalog entry `d2b.artifacts.net-vm-base`.", - "detailedDesign": "Build generic `net-vm-base` nixos-system artifact with net-agent binary, agent-service endpoint, guest-agent binary, standard NIC bootstrap, lib.mkForce override; bake `net-local-controller` account with the UID/GID allocated from the host-users reservation table (documented in `nixos-modules/host-users.nix`).", - "integration": "Network resource `spec.netVmSystemArtifactId` points to `net-vm-base`; runtime-cloud-hypervisor consumes the artifact ID, and the config Volume provides all per-Network DHCP/firewall/routing content.", - "dataMigration": "Full d2b 3.0 reset; no per-Network v2 net-VM config import.", - "validation": "Eval cases `net-vm-artifact-id-eval.nix` and `network-spec-eval.nix`, plus `tests/net-vm-network-eval.sh` for the lib.mkForce invariant.", - "removalProof": "Supersedes implicit microvm/Nix path coupling; remove `net-vm-base` artifact catalog entry only after all Network resources and provider references are gone." - }, - { - "workItemId": "ADR046-network-011", - "specId": "ADR-046-provider-network-local", - "specPath": "docs/specs/providers/ADR-046-provider-network-local.md", - "dependencyOwner": "Nix; owns resource declaration, User declaration, host account provisioning, and artifact catalog wiring.", - "currentSource": "Existing NixOS modules `nixos-modules/network.nix`, `nixos-modules/net.nix`, and `nixos-modules/host-users.nix` provide v1 static declarations and user/account patterns.", - "reuseSource": null, - "reuseAction": "adapt Nix option/resource emission and account provisioning to v3 resources", - "destination": "Nix module resource emission for `Provider/network-local`, `User/net-local-controller`, host OS account, `provider-network-local`, and `net-vm-base` artifacts.", - "detailedDesign": "Nix module for `Provider/network-local` resource declaration; `User/net-local-controller` declaration; OS account provisioning; artifact catalog entries.", - "integration": "Nix compiler emits Provider/User/Network resources and host prerequisites; core ProviderDeployment starts controller Process; system-core reconciles the User resource.", - "dataMigration": "Full d2b 3.0 reset; no v2 state/config import.", - "validation": "Eval cases `network-spec-eval.nix`, `user-no-managed-by-eval.nix`, `net-vm-artifact-id-eval.nix`, and `make test-policy` for artifact/package paths.", - "removalProof": "Supersedes v1 NixOS module declarations; removal proof is deletion of old resource emission and account/artifact entries when provider is retired." - }, - { - "workItemId": "ADR046-network-012", - "specId": "ADR-046-provider-network-local", - "specPath": "docs/specs/providers/ADR-046-provider-network-local.md", - "dependencyOwner": "Nix; depends on Network resource schema and CIDR validation rules.", - "currentSource": "`nixos-modules/lib.nix:cidrOverlaps` lines 429–462 provides CIDR overlap logic in the v1 module layer.", - "reuseSource": "`nixos-modules/lib.nix:cidrOverlaps`.", - "reuseAction": "port/reuse overlap semantics in v3 eval checks and provider validation", - "destination": "Nix flake/resource schema checks for declared Networks and provider `validate.rs` parity.", - "detailedDesign": "Build-time CIDR overlap check for declared Networks in flake check.", - "integration": "Nix compiler rejects overlapping declared Network CIDRs before resource publication; runtime `validateSpec` re-checks full overlap matrix.", - "dataMigration": "None — docs/tooling only; no runtime state.", - "validation": "Eval case `network-cidr-overlap-eval.nix` and `tests/cidr_overlap.rs` cover same-Network, cross-Network, external CIDR, and adjacency cases.", - "removalProof": "None — validation net-new in v3 resource compiler; no prior owner to remove." - }, - { - "workItemId": "ADR046-network-013", - "specId": "ADR-046-provider-network-local", - "specPath": "docs/specs/providers/ADR-046-provider-network-local.md", - "dependencyOwner": "Tests; owned by `d2b-provider-network-local` hermetic test suite.", - "currentSource": "Reusable semantic assertions come from §25.1 IfName/CIDR reuse inventory and Network schema defined in this spec.", - "reuseSource": null, - "reuseAction": "adapt existing IfName/CIDR assertions into provider conformance tests", - "destination": "`packages/d2b-provider-network-local/tests/schema_roundtrip.rs`, `tests/ifname_derive.rs`, and `tests/cidr_overlap.rs`.", - "detailedDesign": "Conformance suite: NetworkSpec round-trip, IfName derivation, CIDR validation matrix.", - "integration": "Test suite runs under `cargo test -p d2b-provider-network-local --lib --tests` and validates provider schema before integration gates.", - "dataMigration": "None — docs/tooling only; no runtime state.", - "validation": "The listed conformance tests themselves are the validation, with workspace policy ensuring `tests/` exists.", - "removalProof": "Retire replaced current-code tests only after successor hermetic tests cover the minimum reusable assertions and gate manifests/pins are updated." - }, - { - "workItemId": "ADR046-network-014", - "specId": "ADR-046-provider-network-local", - "specPath": "docs/specs/providers/ADR-046-provider-network-local.md", - "dependencyOwner": "Tests; depends on ADR046-nl-006 controller and fake `NetworkEffectPort` from `d2b-contracts`.", - "currentSource": "None — net-new v3 controller state-machine test; v1 shell/Nix gates are not a controller-reconcile equivalent.", - "reuseSource": null, - "reuseAction": "net-new", - "destination": "`packages/d2b-provider-network-local/tests/controller_state.rs`.", - "detailedDesign": "Controller state-machine unit tests with fake `NetworkEffectPort` (from d2b-contracts mock) and deterministic clock.", - "integration": "Hermetic fake effect port drives reconcile, observe, finalizer, and adoption transitions without real broker, systemd, container, or network dependencies.", - "dataMigration": "None — docs/tooling only; no runtime state.", - "validation": "`tests/controller_state.rs` covers normal path, CIDR conflict, User not Ready, Volume error, Guest timeout, agent reload failure, finalizer sequence, adoption, and drift.", - "removalProof": "None — net-new; no prior owner to remove." - }, - { - "workItemId": "ADR046-network-015", - "specId": "ADR-046-provider-network-local", - "specPath": "docs/specs/providers/ADR-046-provider-network-local.md", - "dependencyOwner": "Tests; integration coverage for the complete Network lifecycle.", - "currentSource": "Existing Layer-1 eval and shell gates cover fragments; no v3 provider lifecycle integration test exists.", - "reuseSource": null, - "reuseAction": "adapt reusable semantic assertions into v3 integration coverage", - "destination": "`packages/d2b-provider-network-local/integration/host_fabric.rs`, `guest_lifecycle.rs`, `agent_reload.rs`, and `delete_sequence.rs`.", - "detailedDesign": "Integration tests: full Network lifecycle (create, config update, agent Reload, delete sequence) in container environment.", - "integration": "Integration tests exercise resource publication, host fabric effects, config Volume updates, ComponentSession reload, Process lifecycle, and finalizer cleanup through the provider stack.", - "dataMigration": "None — docs/tooling only; no runtime state.", - "validation": "`make test-integration` for container tests and `make test-host-integration` where guest lifecycle requires host/KVM coverage.", - "removalProof": "Old duplicate tests, shell gates, fixtures, static artifacts, CI jobs, manifests, and pins are deleted once successor coverage and removal proof pass." - }, - { - "workItemId": "ADR046-network-016", - "specId": "ADR-046-provider-network-local", - "specPath": "docs/specs/providers/ADR-046-provider-network-local.md", - "dependencyOwner": "Security; depends on Process sandbox schema and `Provider/system-minijail` guest namespace inheritance.", - "currentSource": "The invariant is specified as INV-NET-008 and has eval coverage named `tests/unit/nix/cases/process-sandbox-netns.nix`.", - "reuseSource": null, - "reuseAction": "preserve and verify existing guest-netns isolation invariant in v3 Process specs", - "destination": "Process templates for agent and dnsmasq plus sandbox/eval tests.", - "detailedDesign": "Verify INV-NET-008 (Guest-network-admin isolation): Process Provider correctly inherits Guest netns for agent/dnsmasq.", - "integration": "`Provider/network-local` emits `namespaceClasses: []` and guest-only capability classes; Process Provider starts agent/dnsmasq inside the net VM network namespace only.", - "dataMigration": "None — docs/tooling only; no runtime state.", - "validation": "`tests/unit/nix/cases/process-sandbox-netns.nix` and provider Process-template tests assert no host capability or host network namespace grant.", - "removalProof": "None — security invariant preserved; no prior owner to remove." - }, - { - "workItemId": "ADR046-network-017", - "specId": "ADR-046-provider-network-local", - "specPath": "docs/specs/providers/ADR-046-provider-network-local.md", - "dependencyOwner": "Docs; owned by `d2b-provider-network-local` package documentation.", - "currentSource": "None — provider crate README is net-new for v3 packaging; this dossier supplies the required topics.", - "reuseSource": null, - "reuseAction": "net-new documentation", - "destination": "`packages/d2b-provider-network-local/README.md`.", - "detailedDesign": "`packages/d2b-provider-network-local/README.md` covering all 7 required topics.", - "integration": "Workspace policy requires the README alongside `src/`, `tests/`, and `integration/`; operators and contributors use it for provider identity, build, test, integration, state, RBAC, and standalone-repo path.", - "dataMigration": "None — docs/tooling only; no runtime state.", - "validation": "`make test-policy` / `xtask workspace-policy` verifies required provider crate paths and README presence.", - "removalProof": "None — net-new documentation; no prior owner to remove." - }, - { - "workItemId": "ADR046-network-018", - "specId": "ADR-046-provider-network-local", - "specPath": "docs/specs/providers/ADR-046-provider-network-local.md", - "dependencyOwner": "Broker plus device provider boundary; `UsbipBindFirewallRule` remains owned by `Provider/device-usbip`.", - "currentSource": "Existing `UsbipBindFirewallRule` broker operation and USBIP provider boundary; network-local supplies only the generic TCP/3240 host firewall carve-out.", - "reuseSource": null, - "reuseAction": "preserve ownership separation; do not move USBIP broker op into network-local", - "destination": "Integration/boundary tests for `Provider/network-local` and `Provider/device-usbip`; no new network-local broker op destination.", - "detailedDesign": "`UsbipBindFirewallRule` broker op stays with `Provider/device-usbip`; verify separation in integration tests.", - "integration": "`Provider/device-usbip` consumes `Network/` via `networkRef`, while `Provider/network-local` emits only generic uplink bridge allow-rule semantics and never calls `UsbipBindFirewallRule`.", - "dataMigration": "Full d2b 3.0 reset; no v2 state/config import.", - "validation": "Integration tests assert the network-local controller does not call `UsbipBindFirewallRule` and that USBIP attach/firewall behavior remains under device-usbip ownership.", - "removalProof": "None — existing USBIP owner remains; proof is absence of network-local calls and unchanged device-usbip ownership." - }, - { - "workItemId": "ADR046-network-019", - "specId": "ADR-046-provider-network-local", - "specPath": "docs/specs/providers/ADR-046-provider-network-local.md", - "dependencyOwner": "Provider; depends on D087 ProviderStateSet and status-first storage rules.", - "currentSource": "None — net-new v3 ProviderStateSet/status contract; v1 network module did not expose Provider state Volumes.", - "reuseSource": null, - "reuseAction": "net-new status-first provider-state conformance", - "destination": "Provider descriptor, controller-main deployment, `tests/state_schema_roundtrip.rs`, and eval case `provider-state-volume-eval.nix`.", - "detailedDesign": "Confirm `controller-main` declares no stateNamespace and core ProviderDeployment creates no Provider state Volume or state mount; validate ProviderStateSet query returns empty for `Provider/network-local`; validate bounded operational state is written to revisioned/redacted status and the core Operation ledger with `status-oversize` conformance; confirm per-Network config Volumes remain `ownerRef: Network/` runtime/config operational Volumes outside the ProviderStateSet and `Volume` is not in `ResourceTypes implemented`.", - "integration": "Core ProviderDeployment starts controller without `/state`; controller uses Network status and Operation ledger for bounded observations; ProviderStateSet query excludes per-Network config Volumes.", - "dataMigration": "Full d2b 3.0 reset; no v2 state/config import.", - "validation": "`tests/state_schema_roundtrip.rs` and `tests/unit/nix/cases/provider-state-volume-eval.nix` validate empty ProviderStateSet, status bounds/redaction, and config Volume exclusion.", - "removalProof": "None — net-new; no Provider state Volume or prior owner to remove." - }, - { - "workItemId": "ADR046-nix-001", - "specId": "ADR-046-nix-configuration", - "specPath": "docs/specs/ADR-046-nix-configuration.md", - "dependencyOwner": "W0; `d2b-contracts` identities (ADR046-identities-001, ADR046-identities-002)", - "currentSource": "`nixos-modules/options-realms.nix` (`RealmId`/`RealmPath`/`RealmControllerPlacement`/`EntrypointMode` labels); `options-realms-workloads.nix` (`WorkloadId`/`WorkloadProviderKind`/`IsolationPosture`/`WorkloadExecutionPosture`); `options-realms-network.nix`; `d2b-realm-core/src/realm.rs`, `workload.rs`, `capability.rs`, `ids.rs` (symbols to adapt)", - "reuseSource": null, - "reuseAction": "adapt", - "destination": "`nixos-modules/options-zones.nix` (Zone-level options: `label`, `retainedGenerations`, `trustedPublishers` — compiler settings, not Zone spec fields); `nixos-modules/options-zones-resources.nix` (unified `resources` attrset)", - "detailedDesign": "`d2b.zones..resources. = { type = \"\"; spec = { ... }; }` — single attrset covering all ResourceTypes; `type` discriminates dispatch; `spec` fields mirror exact ResourceTypeSchema field names and nesting; Nix option types/defaults/docs generated from `docs/reference/schemas/v3/.json`; no Nix-only fields inside resource declarations; `metadata.name` derives from attr key; `metadata.zone` derives from enclosing zone attr key; `apiVersion` defaulted; `uid`/`generation`/`revision`/`status`/`managedBy` never in Nix; `resource_name` regex `^[a-z][a-z0-9-]*$`; ref validation assertions; `WorkloadProviderKind` → Guest/Host mapping per disposition table above; `Capability` → Role verb mapping per resource-api/authz foundation spec; Zone self-resource spec is `{}`; `retainedGenerations`/`trustedPublishers` are Zone-level compiler settings not emitted in Zone spec", - "integration": "`nixos-modules/default.nix` imports new options files; old realms options coexist until ADR046-nix-002", - "dataMigration": "Operator configs migrate `d2b.realms.*` → `d2b.zones.*`; `d2b.vms.*` → `d2b.zones..resources.*` with `type = \"Guest\"`", - "validation": "nix-unit vectors for each ResourceType; ref-validation rejection vectors; malformed ref error shape; ZoneLink `childZoneName` resolves declared Zone; missing `transportProviderRef` fails eval; `managedBy` in spec rejected at eval; Zone spec is `{}` (no `parentRef`, `retainedGenerations`, etc.)", - "removalProof": "`options-realms*.nix` removed after `options-zones*.nix` achieves parity and parity drift test passes" - }, - { - "workItemId": "ADR046-nix-002", - "specId": "ADR-046-nix-configuration", - "specPath": "docs/specs/ADR-046-nix-configuration.md", - "dependencyOwner": "ADR046-nix-001; env/VM migration", - "currentSource": "`nixos-modules/options-envs.nix` (`lanSubnet`/`uplinkSubnet`/`mtu`/`mssClamp`/`externalNetwork.*`); `nixos-modules/options-vms.nix` (`d2b.vms..*`; `ProcessRole` toggle options for components)", - "reuseSource": null, - "reuseAction": "adapt", - "destination": "`Network` resource fields in `nixos-modules/options-zones-resources.nix`; `Guest` resource fields", - "detailedDesign": "`d2b.envs.work.lanSubnet` → `d2b.zones.work.resources.work-lan = { type = \"Network\"; spec = { lanSubnet = \"...\"; ... }; }`; CIDR overlap assertion migrated; `sys-` reserved prefix and VM-name regex retained; `d2b.vms..tpm.enable` → `d2b.zones..resources.vm-tpm = { type = \"Device\"; spec = { providerRef = \"Provider/device-tpm\"; ... }; }`", - "integration": "`options-zones-resources.nix` emits Network/Guest resources consumed by the rewritten index/bundle and Provider/network-local/device Providers; assertions run during eval before bundle emission.", - "dataMigration": "Operator configs migrate env/VM component options into Zone Network/Guest/Device resources; full d2b 3.0 reset means no v2 runtime state/config import beyond rewritten Nix declarations.", - "validation": "nix-unit CIDR rejection; eval assertion for `sys-` prefix; VM-name regex", - "removalProof": "`options-envs.nix`, `options-vms.nix` removed after migration parity test passes" - }, - { - "workItemId": "ADR046-nix-003", - "specId": "ADR-046-nix-configuration", - "specPath": "docs/specs/ADR-046-nix-configuration.md", - "dependencyOwner": "ADR046-nix-001; site options", - "currentSource": "`nixos-modules/options-daemon.nix`, `options-site.nix`, `options-host.nix`", - "reuseSource": null, - "reuseAction": "retain and extend", - "destination": "`nixos-modules/options-site.nix` (retained); per-Zone options in `options-zones.nix`", - "detailedDesign": "`d2b.zones..retainedGenerations` (default 3, range 1..16, compiler setting — not emitted in Zone spec); `d2b.site.stateDir` maps to Zone storage roots; `d2b.site.usePrebuiltHostTools` retained", - "integration": "`options-site.nix` retains site defaults consumed by `options-zones.nix` and `bundle-zones.nix`; activation-helper and Zone storage roots read `stateDir`/`usePrebuiltHostTools` settings.", - "dataMigration": "Operator site options stay in place; new per-Zone `retainedGenerations` defaults apply on first v3 activation; no v2 runtime state import.", - "validation": "nix-unit case verifies `retainedGenerations` default/range, `site.stateDir` mapping, and `usePrebuiltHostTools` retention.", - "removalProof": "No removal; file extended only" - }, - { - "workItemId": "ADR046-nix-004", - "specId": "ADR-046-nix-configuration", - "specPath": "docs/specs/ADR-046-nix-configuration.md", - "dependencyOwner": "ADR046-nix-001, ADR046-nix-002", - "currentSource": "`nixos-modules/index.nix` — `cfg._index.*`: `enabledEnvs`, `enabledVms`, `netMeta` (derives bridge names/IPs from `lanSubnet`/`uplinkSubnet`), `declaredRealms`, `enabledRealms`, `workloadsInEnv`, `runtimeRows`", - "reuseSource": null, - "reuseAction": "rewrite", - "destination": "`nixos-modules/index.nix` (rewritten); emits `/etc/d2b/index.json`", - "detailedDesign": "Cross-Zone normalized index: zone/host/guest/network/closure entries; executionIndex; networkIndex; closureIndex; sorted output; `cfg._index` attribute tree retained as internal helper during migration", - "integration": "`options-zones-resources.nix` feeds rewritten `index.nix`; `bundle-zones.nix`, resource emitters, and activation-helper consume `/etc/d2b/index.json`.", - "dataMigration": "Index is rebuilt from v3 Zone resources at activation; full d2b 3.0 reset; no v2 index state import.", - "validation": "nix-unit golden vectors for index shape; drift gate: `xtask gen-index` round-trip", - "removalProof": "`cfg._index.envMeta`, `cfg._index.realms.*` sub-trees removed after all callers migrate to Zone resource lookups" - }, - { - "workItemId": "ADR046-nix-005", - "specId": "ADR-046-nix-configuration", - "specPath": "docs/specs/ADR-046-nix-configuration.md", - "dependencyOwner": "ADR046-nix-004", - "currentSource": "`nixos-modules/bundle-artifacts.nix` (`artifactModule` submodule, `installFileName`, `mode 0640` ownership); `nixos-modules/bundle.nix` (bundle derivation, SHA256SUMS, `d2b._bundle` integrity chain)", - "reuseSource": null, - "reuseAction": "extend and rewrite", - "destination": "`nixos-modules/bundle-zones.nix` (per-Zone bundle derivation); common helpers retained in `bundle-artifacts.nix`", - "detailedDesign": "Per-Zone `bundle.json` with `candidateId`/`contentId` binding; SHA256 digest chain; `generationIndex`; atomic activation pointer; `manifestVersion` → `schemaVersion` rename", - "integration": "`d2b-activation-helper` reads `bundle.json` per Zone; validates digest chain before staging", - "dataMigration": "Per-Zone bundles are generated from v3 resource declarations; full d2b 3.0 reset; no v2 monolithic bundle state import.", - "validation": "Artifact-shape contract tests in `packages/d2b-contract-tests/tests/`; determinism test (build twice, diff outputs)", - "removalProof": "Monolithic `bundle.json` and `d2b._bundle` artifact table retired after all Zone bundle tests pass" - }, - { - "workItemId": "ADR046-nix-006", - "specId": "ADR-046-nix-configuration", - "specPath": "docs/specs/ADR-046-nix-configuration.md", - "dependencyOwner": "ADR046-nix-005; Process Provider work items (ADR046-primitives-002)", - "currentSource": "`nixos-modules/processes-json.nix` (`VmProcessDag`, `ProcessRole`, `binaryPath`, `argv`, `share.source == \"/nix/store\"` sentinel); `packages/d2b-core/src/processes.rs` (`ProcessRole` enum — all variants in disposition table above)", - "reuseSource": null, - "reuseAction": "extract and adapt", - "destination": "`nixos-modules/resources-zones-processes.nix`; emits `zones//processes.json`", - "detailedDesign": "Process/EphemeralProcess resource serialization per disposition table; no free-form `binaryPath` or `argv`; template refs; mounts from `volumeRef`; sandbox from named profile; VsockRelay → `Process` under `Provider/transport-vsock`; GuestSshReadiness retired at v3 cutover; Usbip long-lived backend/proxy → `Process`, Usbip per-busid attach/detach → `EphemeralProcess`, all owned by `Provider/device-usbip`", - "integration": "`processes.json` replaces `cfg._bundle.processesJson`; Process Providers read the new format", - "dataMigration": "Processes are emitted from v3 Process/EphemeralProcess resources; full d2b 3.0 reset; no v2 process artifact import.", - "validation": "Process resource schema vectors; no-raw-path assertion; ProcessRole parity test (every variant has a test case)", - "removalProof": "`processes-json.nix` and current `processes.json` schema removed after all Process Providers consume `zones//processes.json`" - }, - { - "workItemId": "ADR046-nix-007", - "specId": "ADR-046-nix-configuration", - "specPath": "docs/specs/ADR-046-nix-configuration.md", - "dependencyOwner": "ADR046-nix-005; Volume Provider work items (ADR046-primitives-003)", - "currentSource": "`nixos-modules/storage-json.nix` (all `mkPath` calls, `PrincipalRef` `uid`/`gid`/`user` kinds, `StorageRoot`/`StoragePathSpec`/`repairPolicy`/`cleanupPolicy`); `packages/d2b-core/src/storage.rs` (`StorageJson`, `StoragePathSpec`); `nixos-modules/sync-json.nix` (`SyncJson` OFD lock rows); `packages/d2b-core/src/sync.rs`", - "reuseSource": null, - "reuseAction": "extract storage policy → adapt; retire sync rows", - "destination": "`nixos-modules/resources-zones-volumes.nix`; emits `zones//volumes.json`; OFD lock rows move to `d2b-contracts` internals", - "detailedDesign": "Volume layout/views/ACL/no-follow/repair preserving current policy; `PrincipalRef { kind: \"uid\" }` → `User/` typed ref only; OFD rows removed from Nix artifacts", - "integration": "Volume resources feed `zones//volumes.json`; the Volume Provider consumes it for path/view/ACL reconciliation; `d2b-contracts` internals own OFD lock rows.", - "dataMigration": "Volume resources are regenerated from v3 config; full d2b 3.0 reset; no v2 storage/sync state import.", - "validation": "Volume schema vectors; ACL/no-follow/view policy tests", - "removalProof": "`storage-json.nix`, `sync-json.nix`, and `/etc/d2b/storage.json`/`sync.json` removed after Volume controller parity" - }, - { - "workItemId": "ADR046-nix-008", - "specId": "ADR-046-nix-configuration", - "specPath": "docs/specs/ADR-046-nix-configuration.md", - "dependencyOwner": "ADR046-nix-004; Zone/Network migration", - "currentSource": "`nixos-modules/allocator-json.nix` (realm/env bridge assignments, `allocatorStateDir`, socket paths, `providerPlacement`); `nixos-modules/realm-controller-config-json.nix` (emits `realm-controllers.json`); `packages/d2b-core/src/realm_controller_config.rs` (`RealmControllersJson`, `RealmControllerMetadataSummary`; read live by `d2bd/src/realm_access_resolver.rs` from `/etc/d2b/realm-controllers.json`)", - "reuseSource": null, - "reuseAction": "adapt and retire", - "destination": "Zone self-resource in `zones//zone.json`; allocator concept retired from Nix; socket paths in Zone resource spec; `realm-controllers.json` RETAINED during migration (live d2bd reads it)", - "detailedDesign": "Zone runtime derives socket paths from Zone name; broker row → Network/Host resource; `realm-controllers.json` must remain published until `realm_access_resolver` is replaced by Zone resource API", - "integration": "Zone bootstrap config publishes socket paths consumed by the Zone runtime and `realm_access_resolver` during migration; the replacement Zone resource API takes over before legacy artifacts are removed.", - "dataMigration": "Zone bootstrap data is regenerated from v3 Zone resources; full d2b 3.0 reset; no allocator/realm-controller state import.", - "validation": "Zone resource round-trip; bootstrap socket path regression tests", - "removalProof": "`allocator-json.nix`, `realm-controller-config-json.nix`, and `/etc/d2b/allocator.json`/`realm-controllers.json` removed ONLY after `realm_access_resolver` is replaced by Zone bundle reader" - }, - { - "workItemId": "ADR046-nix-009", - "specId": "ADR-046-nix-configuration", - "specPath": "docs/specs/ADR-046-nix-configuration.md", - "dependencyOwner": "ADR046-nix-006; display/credential Provider work items", - "currentSource": "`nixos-modules/realm-workloads-launcher-v2-json.nix` (`RealmWorkloadsLauncherV2Json`, `LauncherWorkloadSummary`; live); `nixos-modules/realm-identity-config-json.nix` (`RealmIdentityConfigJson`; live, read by d2bd from `/etc/d2b/realm-identity.json`); `packages/d2b-core/src/realm_workloads_launcher.rs`; `packages/d2b-realm-core/src/identity_config.rs`", - "reuseSource": null, - "reuseAction": "adapt", - "destination": "Provider/display-wayland and Provider/shell-terminal Process configs in `zones//processes.json`; `Provider/credential-entra` Credential resource; `realm-identity.json` RETAINED during migration", - "detailedDesign": "Launcher metadata folded into Process resource annotations; identity config → Credential resource fields (`providerRef`, `scope`, `audience`, `allowedOperations`, canonical `spec.provider` extension where Provider schema declares it; no secret bytes); `realm-identity.json` must remain until d2bd `RealmIdentityConfigJson` loading is replaced by Credential resource reader", - "integration": "Provider/display-wayland, Provider/shell-terminal, and Provider/credential-entra consume Process/Credential resources; `d2bd` continues reading `realm-identity.json` until the Credential reader lands.", - "dataMigration": "Launcher and identity config are re-emitted as v3 resources; full d2b 3.0 reset; no v2 launcher/identity state import.", - "validation": "Launcher metadata shape regression; no-secret assertion vectors", - "removalProof": "`realm-workloads-launcher-v2-json.nix`/`realm-identity-config-json.nix` and `/etc/d2b/realm-workloads-launcher-v2.json`/`realm-identity.json` removed ONLY after display/credential Providers read resource configs" - }, - { - "workItemId": "ADR046-nix-010", - "specId": "ADR-046-nix-configuration", - "specPath": "docs/specs/ADR-046-nix-configuration.md", - "dependencyOwner": "ADR046-nix-001; unsafe-local migration", - "currentSource": "`nixos-modules/unsafe-local-workloads-json.nix` (`WorkloadProviderKind::UnsafeLocal`/`IsolationPosture::UnsafeLocal`; current `unsafe-local-workloads.json` artifact); `nixos-modules/unsafe-local-helper.nix` (user-domain process/helper definitions); `packages/d2b-core/src/unsafe_local_workloads.rs`", - "reuseSource": null, - "reuseAction": "adapt", - "destination": "User-only `Host` resource in `zones//hosts.json` (`spec.isolationPosture: \"none\"`, `defaultDomain: user`, `allowedDomains: [user]`, `defaultUserRef: User/`); child `Process` resources in `zones//processes.json` using normal Process Providers; shell session supervisor → `Process` under `Provider/shell-terminal`; never a `Guest`; not a v3 Provider", - "detailedDesign": "`isolationPosture: \"none\"` is a promoted Host base field declared at top-level `spec.isolationPosture` in the Host schema; enforced at eval time; user-only Host rejects system-domain Process refs; `NoIsolation` condition in Host status; `status.isolationPosture: none`; every `ProcessEffect` audit event under this Host carries `no_isolation=true`; OTEL telemetry never carries an isolation label; CLI/UI warning non-suppressible", - "integration": "The unsafe-local Nix emitter produces a user-only Host plus Process resources; Provider/system-core and shell-terminal consume them; CLI/UI and audit/telemetry surfaces read resulting status/events.", - "dataMigration": "unsafe-local declarations migrate to Host/Process resources in Nix; full d2b 3.0 reset; no helper runtime state import.", - "validation": "User-only Host rejection of system-domain Process refs; `spec.isolationPosture != \"none\"` assertion rejection for user-only system-core Hosts; `NoIsolation` condition present in status; `no_isolation=true` in `ProcessEffect` audit event; OTEL attribute absent; no Guest emitted for unsafe-local declaration", - "removalProof": "`unsafe-local-workloads-json.nix` and unsafe-local-specific Nix code removed after user-only Host/Process resources pass all `tests/host-integration/unsafe-local-helper.nix` tests" - }, - { - "workItemId": "ADR046-nix-011", - "specId": "ADR-046-nix-configuration", - "specPath": "docs/specs/ADR-046-nix-configuration.md", - "dependencyOwner": "Broker privileges owner", - "currentSource": "`nixos-modules/privileges-json.nix`", - "reuseSource": null, - "reuseAction": "retain", - "destination": "`nixos-modules/privileges-json.nix` (retained); `/etc/d2b/privileges.json` (retained, site-wide)", - "detailedDesign": "Broker op catalog is not Zone-scoped; no structural change required", - "integration": "`privileges-json.nix` remains a site-wide bundle artifact consumed by broker authorization and drift gates; Zone resource emission references it without Zone scoping.", - "dataMigration": "Full d2b 3.0 reset; no v2 state/config import", - "validation": "Existing `tests/unit/gates/drift-check.sh`", - "removalProof": "Not removed in this spec" - }, - { - "workItemId": "ADR046-nix-012", - "specId": "ADR-046-nix-configuration", - "specPath": "docs/specs/ADR-046-nix-configuration.md", - "dependencyOwner": "ADR046-nix-005; ADR046-nix-022 (artifact catalog emitter); Provider/volume-virtiofs", - "currentSource": "`nixos-modules/closures-json.nix` (keyed by `d2b.vms.`); `nixos-modules/minijail-profiles.nix`", - "reuseSource": null, - "reuseAction": "adapt", - "destination": "`nixos-modules/closures-json.nix` (rewritten, keyed by artifact ID from `d2b.artifacts` with `type = \"nixos-system\"`); `nixos-modules/minijail-profiles.nix` (retained, adapted to reference Zone Guests)", - "detailedDesign": "Closure emitter iterates `d2b.artifacts` entries with `type = \"nixos-system\"`, computes `pkgs.closureInfo`, records `storePath`/digest/size in artifact catalog (private root:d2bd 0640 field; absent from all public ResourceSpecs/status/audit/OTEL); `Guest.spec.systemArtifactId` links Guest to artifact; `Volume.source.systemArtifactId` links Volume to artifact; minijail profile emitter structurally unchanged; old `d2b.vms.` keying retired", - "integration": "Artifact catalog and Guest/Volume resources reference closure entries; activation-helper and Provider/volume-virtiofs consume private `storePath` metadata while public surfaces remain path-free.", - "dataMigration": "Closure entries are regenerated by artifact ID from `d2b.artifacts`; full d2b 3.0 reset; no v2 VM-keyed closure state import.", - "validation": "Closure map round-trip; per-VM store hardlink integrity; `storePath` present in private catalog; `storePath` absent from all emitted public ResourceSpecs and status/audit/OTEL surfaces", - "removalProof": "Old `d2b.vms.*`-keyed closure entries removed after all Guests use `zones//guests.json` and artifact catalog" - }, - { - "workItemId": "ADR046-nix-013", - "specId": "ADR-046-nix-configuration", - "specPath": "docs/specs/ADR-046-nix-configuration.md", - "dependencyOwner": "ADR046-nix-005; manifest contract", - "currentSource": "`nixos-modules/manifest.nix` (`manifestVersion` pinned contract); `nixos-modules/host-json.nix`", - "reuseSource": null, - "reuseAction": "replace", - "destination": "Per-Zone `zones//bundle.json` (`schemaVersion`); Host resource in `zones//hosts.json`", - "detailedDesign": "`manifestVersion` → `schemaVersion`; `host.json` host config folded into Host resource spec; CHANGELOG entry for rename required", - "integration": "`bundle-zones.nix` writes `schemaVersion` into per-Zone `bundle.json`; activation-helper validates it and Host resource consumers replace `host.json` readers.", - "dataMigration": "Manifest/host artifacts are regenerated as Zone bundle/Host resources; full d2b 3.0 reset; no v2 manifest state import.", - "validation": "Schema drift gate; CHANGELOG enforcement", - "removalProof": "`manifest.nix` and `host.json` emitters removed after Zone bundle activation path passes" - }, - { - "workItemId": "ADR046-nix-014", - "specId": "ADR-046-nix-configuration", - "specPath": "docs/specs/ADR-046-nix-configuration.md", - "dependencyOwner": "ADR046-nix-001, ADR046-nix-002", - "currentSource": "`nixos-modules/assertions.nix` (CIDR overlap, `sys-` prefix, VM-name regex, platform gate)", - "reuseSource": null, - "reuseAction": "extend in place", - "destination": "`nixos-modules/assertions.nix`", - "detailedDesign": "Migrate existing assertions to Zone/Resource terminology; add ref validation, owner cycles, CIDR overlap (Zones), provider resolution, RoleBinding verb set assertions", - "integration": "`assertions.nix` validates Zone/Resource declarations before index, bundle, and schema validation derivations run; failing configs block activation artifacts.", - "dataMigration": "None — docs/tooling only; no runtime state", - "validation": "Each new assertion has a failing-config test vector", - "removalProof": "No removal; extended only" - }, - { - "workItemId": "ADR046-nix-015", - "specId": "ADR-046-nix-configuration", - "specPath": "docs/specs/ADR-046-nix-configuration.md", - "dependencyOwner": "ADR046-nix-001; host activation", - "currentSource": "`nixos-modules/host.nix`, `host-daemon.nix` (fixed local-root endpoint set), `host-activation.nix`, `host-users.nix`", - "reuseSource": null, - "reuseAction": "retain and adapt", - "destination": "Same files; updated to use Zone bundle activation path and Zone resource state dirs", - "detailedDesign": "`d2b-activation-helper` updated to validate/stage per-Zone bundles; `d2bd.service` updated to read Zone bundle; `d2b` group retained for `SO_PEERCRED`", - "integration": "Host activation units call `d2b-activation-helper` against per-Zone bundles, then `d2bd.service` reads the staged Zone bundle; `SO_PEERCRED` authorization remains via the `d2b` group.", - "dataMigration": "Host activation reads newly staged Zone bundles on first v3 switch; full d2b 3.0 reset; no v2 daemon runtime state import.", - "validation": "Host-integration test with Zone bundle activation and daemon readiness", - "removalProof": "No removal; adapted in place" - }, - { - "workItemId": "ADR046-nix-016", - "specId": "ADR-046-nix-configuration", - "specPath": "docs/specs/ADR-046-nix-configuration.md", - "dependencyOwner": "ADR046-nix-002; Provider/network-local dossier", - "currentSource": "`nixos-modules/network.nix`, `nixos-modules/net.nix` (including `lib.mkForce` DHCP neutralizer and per-env MTU/MSS/east-west wiring)", - "reuseSource": null, - "reuseAction": "retain until Provider successor", - "destination": "Network reconciliation by `Provider/network-local` Process resources", - "detailedDesign": "Current bridge/NAT/DHCP/firewall Nix units retained; `Provider/network-local` controller emits equivalent configuration from Network resources; `lib.mkForce` neutralization preserved", - "integration": "Network resources feed Provider/network-local parity implementation while `net.nix`/`network.nix` remain the fallback; network eval tests compare both paths.", - "dataMigration": "Network resource specs are generated from v3 Nix declarations; full d2b 3.0 reset; no v2 bridge/NAT runtime state import.", - "validation": "`tests/net-vm-network-eval.sh` passes against Network resource spec", - "removalProof": "`network.nix`/`net.nix` removed after `Provider/network-local` parity and `tests/net-vm-network-eval.sh` passes" - }, - { - "workItemId": "ADR046-nix-017", - "specId": "ADR-046-nix-configuration", - "specPath": "docs/specs/ADR-046-nix-configuration.md", - "dependencyOwner": "ADR046-nix-012; Provider/volume-virtiofs", - "currentSource": "`nixos-modules/store.nix` (`ProcessRole::Virtiofsd` and `share.source == \"/nix/store\"` sentinel; per-VM hardlink farm)", - "reuseSource": null, - "reuseAction": "retain until Provider successor", - "destination": "Per-VM store reconciliation by `Provider/volume-virtiofs` EphemeralProcess/Process resources", - "detailedDesign": "`store.nix` retained; `Provider/volume-virtiofs` controller creates equivalent EphemeralProcess; per-VM store path derived from Zone stateDir + Guest name via Provider, not raw path in spec", - "integration": "Guest/Volume resources drive Provider/volume-virtiofs, which stages hardlink farm processes consumed by Guest process resources; `store.nix` remains until parity.", - "dataMigration": "Full d2b 3.0 reset; no v2 store metadata import", - "validation": "Store hardlink integrity; no direct `/nix/store` export", - "removalProof": "`store.nix` removed after `Provider/volume-virtiofs` manages farm lifecycle and existing store tests pass" - }, - { - "workItemId": "ADR046-nix-018", - "specId": "ADR-046-nix-configuration", - "specPath": "docs/specs/ADR-046-nix-configuration.md", - "dependencyOwner": "ADR046-nix-002; Provider dossiers for graphics, tpm, usbip, audio", - "currentSource": "`nixos-modules/components/graphics.nix` (`ProcessRole::Gpu`, `GpuRenderNode`, `Video`); `components/tpm.nix` (`ProcessRole::Swtpm`, `SwtpmPreStartFlush`); `components/usbip.nix` (`ProcessRole::Usbip`); `components/audio/` (`ProcessRole::Audio`)", - "reuseSource": null, - "reuseAction": "each component becomes a Provider install resource + Device/Guest spec field", - "destination": "`Provider/device-tpm`, `Provider/device-usbip`, `Provider/device-gpu`, `Provider/audio-pipewire` resource install declarations in `options-zones-resources.nix`", - "detailedDesign": "`d2b.vms..tpm.enable = true` → `d2b.zones..resources.vm-tpm = { type = \"Device\"; spec = { providerRef = \"Provider/device-tpm\"; ... }; }`; all component eval assertions migrated to `assertions.nix`; GuestSshReadiness retired at v3 cutover; Usbip long-lived backend/proxy → `Process`, per-busid attach/detach → `EphemeralProcess`, both owned by `Provider/device-usbip`", - "integration": "Device/Guest specs feed Provider/device-* and Provider/audio-pipewire install declarations; migrated assertions/tests consume Zone resource configs instead of component toggles.", - "dataMigration": "Component toggles migrate to v3 Device/Guest resource declarations; full d2b 3.0 reset; no v2 component runtime state import.", - "validation": "Existing component eval tests; `tests/usbip-gating-eval.sh`; `tests/video-contract-eval.sh`", - "removalProof": "`components/` Nix units removed after Provider resource install achieves parity and all component eval tests pass against Zone resource configs" - }, - { - "workItemId": "ADR046-nix-019", - "specId": "ADR-046-nix-configuration", - "specPath": "docs/specs/ADR-046-nix-configuration.md", - "dependencyOwner": "ADR046-nix-005; ADR046-nix-001; `d2b-contracts` schema generation (ADR046-nix-027)", - "currentSource": "`nixos-modules/bundle-artifacts.nix` (`artifactModule` submodule, mode/ownership); `nixos-modules/bundle.nix` (digest chain, SHA256SUMS); `packages/xtask/src/main.rs` (`gen-schemas`); no current per-ResourceType JSON Schema under `docs/reference/schemas/v3/`", - "reuseSource": null, - "reuseAction": "extend xtask schema generation; new Nix eval/build validation hooks", - "destination": "`docs/reference/schemas/v3/.json` for each ResourceType; `nixos-modules/resource-schema-validation.nix` (validates emitted spec against committed JSON Schema at build time); `nixos-modules/provider-settings-validation.nix` (validates `spec.provider.settings` where declared in schema, and Provider `config`, against Provider-embedded schema at build time); `nixos-modules/assertions.nix` (Credential ref enforcement, secret-pattern rejection)", - "detailedDesign": "`cargo xtask gen-schemas` emits one JSON Schema per ResourceType under `docs/reference/schemas/v3/`; Nix derivation reads these schemas from `pkgs.d2b-resource-schemas` and validates every emitted `spec` JSON before producing the Zone bundle; Provider-settings validation reads `settingsSchemaDigest` from `provider-catalog.json` and resolves the schema from the Provider package closure; Credential ref enforcement: eval assertion rejects any `spec` string field matching `-----BEGIN`, `eyJ`, or a hex string ≥ 32 bytes in a secret-typed field; `managedBy` in any input spec rejected at eval (core-set runtime field, never in Nix input); bundle integrity: `candidateId`/`contentId` computed over canonical sorted output", - "integration": "Validation hooks wired into `bundle-zones.nix` derivation; `d2b-activation-helper` re-verifies digest chain at staging", - "dataMigration": "None — docs/tooling only; no runtime state", - "validation": "Schema round-trip: emit spec, validate against schema, verify byte-identical re-emit; `spec.provider.settings` rejection test (unknown field, out-of-bounds value, raw store path, secret bytes) where schema declares the field; Provider `config` rejection test (unknown field); Credential ref enforcement: PEM-in-spec rejected; secret-pattern-in-spec rejected; valid `Credential/` ref accepted; `managedBy` in spec input rejected at eval", - "removalProof": "Not removed; extended as new ResourceTypes are added" - }, - { - "workItemId": "ADR046-nix-020", - "specId": "ADR-046-nix-configuration", - "specPath": "docs/specs/ADR-046-nix-configuration.md", - "dependencyOwner": "ADR046-nix-005; ADR046-nix-001; configuration-publication controller (ADR-046-core-controllers)", - "currentSource": "No current equivalent; current `bundle.nix` replaces all artifacts atomically with no per-resource cleanup tracking", - "reuseSource": null, - "reuseAction": "new", - "destination": "Configuration-publication controller handler in `packages/d2bd/src/config_publication.rs`; `ConfigurationOwnedClassifier`; `AbsentResourceReaper`; `Zone` status conditions in `d2b-contracts/src/v3/zone_status.rs`; cleanup audit emitter in `d2b-state/src/audit_segments.rs`", - "detailedDesign": "`ConfigurationOwnedClassifier`: classify resources by core-set `managedBy` field only — `managedBy=configuration` resources are owned by config publication; `managedBy=controller` and `managedBy=api` resources are never touched. At activation, diff new-generation bundle name+type set against all resources with `managedBy=configuration` in the Zone store; resources absent from the new bundle are enqueued for Delete. Never infer ownership from `ownerRef`, labels, or absence from emitted files. `AbsentResourceReaper`: processes the Absent queue asynchronously; does not block pointer swap (step 4); sets `status.phase=Pending` + `PendingDeletion` condition (`reason: AbsentFromConfiguration`); waits for all finalizers to clear; commits a single store transaction that appends the `Deleted` revision and removes the resource row and its indexes; subsequent `Get` returns not-found; the `ResourceDelete` audit event is appended afterward with dedup/exactly-once recovery. Zone phase: `Pending` during pointer-swap-to-first-reconcile window; `Degraded` while any `managedBy=configuration` resource carries `PendingDeletion` or a ZoneLink lags; `Ready` when all reconciled. Generation pruning: prune when `generationIndex ≤ activeIndex - retainedGenerations` AND all enqueued resources from that generation have reached `Deleted`. Rollback: re-adopt `managedBy=configuration` resources in `Pending/PendingDeletion` back to the rollback target generation's owned set", - "integration": "`d2b-activation-helper` sets `managedBy=configuration` + `configurationGeneration` on every resource it activates; controller reads these fields to determine owned set — never `ownerRef` or bundle membership alone", - "dataMigration": "Full d2b 3.0 reset; existing resources gain `managedBy`/`configurationGeneration` only when first v3 activation creates them; no v2 state/config import.", - "validation": "Classification: `managedBy=controller` resource never enqueued (even if absent from bundle). `managedBy=api` resource never enqueued. `managedBy=configuration` resource absent from new bundle always enqueued. Finalizer safety: resource with active finalizer enters DeletionBlocked; not force-deleted; stays in `Pending`. Final deletion: a single store transaction appends the `Deleted` revision and removes the resource row and its indexes; subsequent `Get` returns not-found; `ResourceDelete` audit event is appended afterward with dedup/exactly-once recovery. Zone status: `Pending` during activation; `Degraded` while PendingDeletion outstanding; `Ready` when clean. Audit: `ResourceDelete` event includes `configurationGeneration` field.", - "removalProof": "Not removed; extended as new ResourceTypes are added" - }, - { - "workItemId": "ADR046-nix-021", - "specId": "ADR-046-nix-configuration", - "specPath": "docs/specs/ADR-046-nix-configuration.md", - "dependencyOwner": "ADR046-nix-001; `d2b-contracts` workspace policy tests", - "currentSource": "`packages/d2b-contract-tests/tests/` (existing workspace policy lints: `tests/workspace-member-sort.rs`, `tests/crate-naming.rs`); no current Provider crate layout gate exists", - "reuseSource": null, - "reuseAction": "new", - "destination": "`packages/d2b-contract-tests/tests/provider-crate-layout.rs`; workspace scan in `packages/xtask/src/main.rs` (extend `check-workspace` or add `check-provider-layout` subcommand)", - "detailedDesign": "Parse the root `packages/Cargo.toml` workspace member list; for every member path matching `packages/d2b-provider-*-*`: assert (1) `src/` directory exists and contains at least one `.rs` file; (2) `tests/` directory exists and contains at least one `.rs` file; (3) `integration/` directory exists and contains at least one `.rs` or fixture file; (4) `README.md` exists and is ≥ 200 bytes. All four conditions required; any single failure fails the test with a structured message naming the crate and missing path. Test runs as `cargo test -p d2b-contract-tests provider_crate_layout`; wired into `make test-policy`.", - "integration": "Wired into `make test-policy` (same gate family as existing workspace policy tests); no new `Makefile` target needed unless `test-policy` does not yet exist", - "dataMigration": "None — docs/tooling only; no runtime state", - "validation": "Fixture: add a stub `packages/d2b-provider-test-missing-integration/` with `src/lib.rs` and `tests/smoke.rs` but no `integration/` and no `README.md`; assert test fails naming both missing paths. Add complete stub with all four paths; assert test passes.", - "removalProof": "Not removed; extended as new Provider crates are added to the workspace" - }, - { - "workItemId": "ADR046-nix-022", - "specId": "ADR-046-nix-configuration", - "specPath": "docs/specs/ADR-046-nix-configuration.md", - "dependencyOwner": "ADR046-nix-005 (bundle derivation); `d2b-contracts` schema generation (ADR046-nix-027)", - "currentSource": "No current equivalent for a separate artifact catalog. Current `nixos-modules/bundle.nix` embeds package derivation references inline. Current `nixos-modules/closures-json.nix` uses `pkgs.closureInfo` keyed by `d2b.vms.`.", - "reuseSource": null, - "reuseAction": "new", - "destination": "`nixos-modules/artifact-catalog.nix` (new emitter); `nixos-modules/options-artifacts.nix` (new option: `d2b.artifacts. = { package; type; }`); `/etc/d2b/artifact-catalog.json` (output artifact, `root:d2bd` 0640); `nixos-modules/bundle-zones.nix` (extend to include artifact catalog digest in `bundle.json`); `nixos-modules/options-zones-resources.nix` (replace `closureRef` / `nixosSystem` helpers with `systemArtifactId` validation)", - "detailedDesign": "`d2b.artifacts.` attrset option: `id` matches `^[a-z][a-z0-9-]*$`; `type ∈ { \"provider\", \"nixos-system\", \"nixos-module-set\", \"config-bundle\" }`; no other fields. Emitter computes `pkgs.closureInfo` for each entry and writes `artifact-catalog.json` with sorted entries (by `artifactId`) containing `artifactId`, `type`, `storePath` (private, for activation-helper staging), `packageDigest`, `closureDigest`, `closureSize`. `storePath` is a private field of the root:d2bd 0640 file; it is never emitted in public ResourceSpecs, status fields, audit records, or OTEL telemetry. The `bundle.json` manifest includes the artifact catalog file entry and its SHA256 digest. `d2b-activation-helper` reads `storePath` from the catalog to resolve and stage each artifact; verifies catalog digest before staging. Build-time validation: `artifactId` / `systemArtifactId` / `source.systemArtifactId` fields in resource specs resolve against `d2b.artifacts`; type-mismatch fails with a structured error. `d2b.providerCatalog..package` option is removed; replaced by `d2b.providerCatalog..artifactId`. `Guest.spec.systemArtifactId` replaces the former `nixosSystem` Nix-only helper. `Volume.source.systemArtifactId` replaces `source.closureRef`.", - "integration": "`nixos-modules/closures-json.nix` rewritten (ADR046-nix-012) to key by artifact ID; `nixos-modules/bundle-zones.nix` includes artifact catalog in integrity chain", - "dataMigration": "Artifact catalog entries are generated from new `d2b.artifacts` declarations; full d2b 3.0 reset; no v2 `closureRef`/`nixosSystem` metadata import.", - "validation": "Artifact catalog round-trip: declare artifact, build, verify JSON entry present with correct type/digests/storePath; missing artifact ID fails build with structured error; wrong-type artifact fails build; `storePath` absent from all public ResourceSpecs and status/audit/OTEL surfaces", - "removalProof": "Not removed; extended as new artifact types are added" - }, - { - "workItemId": "ADR046-nix-023", - "specId": "ADR-046-nix-configuration", - "specPath": "docs/specs/ADR-046-nix-configuration.md", - "dependencyOwner": "ADR-046-componentsession-and-bus spec; ADR046-nix-027 (wire contract) must land first", - "currentSource": "Main commit source row below: `packages/d2b-session/` at `a1cc0b2d`, including session engine, handshake, lifecycle, scheduler, record protection, fragmentation, transport, driver, server, and metrics modules.", - "reuseSource": null, - "reuseAction": "Reuse from main commit `a1cc0b2d`; adapt ADR45 EndpointPurpose/ServicePackage names listed in `ADR45 exclusions` while preserving wire tag values.", - "destination": "`packages/d2b-bus/src/session/` (new crate `d2b-bus`); `ComponentSessionDriver` becomes the central abstraction for all Zone bus sessions.", - "detailedDesign": "Complete session protocol: preface negotiation, Noise handshake profiles, record protection, replay cache, fair scheduler, fragmentation/reassembly, keepalive, ttrpc bridge, generation discovery, and `ComponentSessionDriver` application surface.", - "integration": "`d2b-bus` session runtime is used by Zone bus sessions for local-root controller to broker, controller to guest agent, and controller to provider agent; `serve_ttrpc_services` exposes service handlers.", - "dataMigration": "Full d2b 3.0 reset; no v2 session runtime state/config import", - "validation": "Port `component_session.rs` and `noise_vectors.rs`; run `make test-rust`; Noise KAT vectors must pass after copy.", - "removalProof": "ADR45 EndpointPurpose/ServicePackage realm names listed in `ADR45 exclusions` are adapted during copy; no prior v3 session owner to remove." - }, - { - "workItemId": "ADR046-nix-024", - "specId": "ADR-046-nix-configuration", - "specPath": "docs/specs/ADR-046-nix-configuration.md", - "dependencyOwner": "ADR046-nix-023; ADR046-nix-027", - "currentSource": "Main commit source row below: `packages/d2b-session/src/attachment.rs` and `packages/d2b-session/src/streams.rs` at `a1cc0b2d`.", - "reuseSource": null, - "reuseAction": "Reuse from main commit `a1cc0b2d`; keep attachment/stream semantics and descriptor validation, with no ADR45 realm adaptation required for this item.", - "destination": "`packages/d2b-bus/src/session/` (same crate as ADR046-nix-023).", - "detailedDesign": "Attachment ownership validates descriptors only after authenticated decrypt; unconsumed payloads close on drop. NamedStreamMux preserves half-close, reset, per-stream limits, aggregate limits, and credit-based flow control.", - "integration": "`d2b-bus` session transport validates attachments after authenticated decrypt and exposes named streams to ttrpc/services; Unix/vsock transports provide payloads.", - "dataMigration": "Full d2b 3.0 reset; no v2 attachment or stream state/config import", - "validation": "Port `component_session.rs` attachment ownership, stream mux, and credit-accounting tests.", - "removalProof": "None — net-new; no prior owner to remove" - }, - { - "workItemId": "ADR046-nix-025", - "specId": "ADR-046-nix-configuration", - "specPath": "docs/specs/ADR-046-nix-configuration.md", - "dependencyOwner": "ADR046-nix-023; ADR046-nix-027", - "currentSource": "Main commit source row below: `packages/d2b-session/src/cancellation.rs`, `deadline.rs`, and `bootstrap.rs` at `a1cc0b2d`.", - "reuseSource": null, - "reuseAction": "Reuse from main commit `a1cc0b2d`; adapt `CancelRequest`/`CancelResponse` module paths if v3 services move the common proto types.", - "destination": "`packages/d2b-bus/src/session/`.", - "detailedDesign": "Per-generation `RequestRegistry`, request-wide cancellation, `DeadlineBudget::admit_metadata()` as the inbound metadata gate, and single-use `BootstrapAdmission::consume()` replay protection.", - "integration": "`RequestRegistry`, `DeadlineBudget`, and `BootstrapAdmission` feed all `d2b-bus` inbound ttrpc calls and resource API cancellation paths.", - "dataMigration": "Full d2b 3.0 reset; no v2 cancellation/deadline/bootstrap state import", - "validation": "Port cancel-before-dispatch, cancel-after-dispatch, generation-mismatch cancel, and deadline admit tests; add v3 common-proto path adapter test.", - "removalProof": "ADR45 common proto type paths are adapted to the v3 service module; no prior v3 owner to remove." - }, - { - "workItemId": "ADR046-nix-026", - "specId": "ADR-046-nix-configuration", - "specPath": "docs/specs/ADR-046-nix-configuration.md", - "dependencyOwner": "ADR046-nix-023", - "currentSource": "Main commit source row below: `packages/d2b-session-unix/src/{adapter,credit,descriptor,pidfd,socket,systemd,vsock}.rs` at `a1cc0b2d`.", - "reuseSource": null, - "reuseAction": "Reuse from main commit `a1cc0b2d`; retain host-socket/native-vsock feature gates and replace only ADR45 socket-name strings with Zone bootstrap config.", - "destination": "`packages/d2b-bus/src/transport/unix/`.", - "detailedDesign": "Unix seqpacket/stream transports, pidfd identity verification, six-scope credit pool with host/process bounds, systemd-activated seqpacket listener support, and length-prefixed vsock framing.", - "integration": "`d2b-bus` listeners and Provider/guest sessions instantiate UnixSeqpacket/UnixStream/vsock transports; credit pool enforces attachment FD budgets before handing payloads to session runtime.", - "dataMigration": "Full d2b 3.0 reset; no v2 Unix transport state/config import", - "validation": "Port `unix_session.rs` seqpacket, stream, pidfd, FD attachment, and credit pool tests; add Zone bootstrap socket-name fixture.", - "removalProof": "ADR45 socket-name strings listed in exclusions are replaced by Zone bootstrap config; no prior v3 transport owner to remove." - }, - { - "workItemId": "ADR046-nix-027", - "specId": "ADR-046-nix-configuration", - "specPath": "docs/specs/ADR-046-nix-configuration.md", - "dependencyOwner": "ADR-046-componentsession-and-bus; naming and wire enumeration decisions per ADR-046-componentsession-and-bus owning spec before final adoption", - "currentSource": "Main commit source row below: `packages/d2b-contracts/src/v2_component_session.rs` at `a1cc0b2d` plus committed session/noise tests.", - "reuseSource": null, - "reuseAction": "Reuse wire constants/types from main commit `a1cc0b2d`; rename ADR45 realm-oriented variants only as directed by the bus owning spec and preserve numeric tags.", - "destination": "`packages/d2b-contracts/src/v3/component_session.rs`.", - "detailedDesign": "Canonical wire values, bounded vectors, binary size constants, closed-enum tag codecs, and fail-closed validation for the ComponentSession protocol.", - "integration": "`d2b-contracts` v3 component_session module is consumed by `d2b-bus`, `d2b-session`, `d2b-session-unix`, `d2b-client`, and provider toolkit for shared wire constants/enums.", - "dataMigration": "Full d2b 3.0 reset; no v2 ComponentSession contract state import", - "validation": "Port component session and Noise vector tests; add wire-tag stability gates for renamed role/service/purpose variants.", - "removalProof": "ADR45 realm-named enum variants are renamed only if the owning spec requires while preserving wire tags; no prior v3 contract owner to remove." - }, - { - "workItemId": "ADR046-nix-028", - "specId": "ADR-046-nix-configuration", - "specPath": "docs/specs/ADR-046-nix-configuration.md", - "dependencyOwner": "ADR046-nix-027; Zone service naming per ADR-046-componentsession-and-bus owning spec", - "currentSource": "Main commit source row below: `packages/d2b-contracts/src/generated_v2_services/`, `v2_guest_services.rs`, and `v2_component_session.rs` service inventory at `a1cc0b2d`.", - "reuseSource": null, - "reuseAction": "Reuse generated ttrpc service tables from main commit `a1cc0b2d`; adapt ADR45 Realm/Workload fields and service names to v3 Zone/Resource naming per owning bus spec.", - "destination": "`packages/d2b-contracts/src/v3/services/`.", - "detailedDesign": "Versioned ttrpc service modules preserve server dispatch tables, shared request/cancel/outcome/error types, and service fingerprint inventory.", - "integration": "Generated v3 services feed `d2b-bus` server/client dispatch, provider-toolkit conformance, and `SERVICE_INVENTORY` fingerprint validation.", - "dataMigration": "Full d2b 3.0 reset; no v2 generated service state import", - "validation": "Port provider-toolkit conformance and service fingerprint assertions; add v3 service-name gate for Zone service and ResourceName field adaptation.", - "removalProof": "ADR45 realm/user WorkloadId fields listed in exclusions are adapted to Zone/ResourceName; `generated_v2_services` remains only as reuse source." - }, - { - "workItemId": "ADR046-nix-029", - "specId": "ADR-046-nix-configuration", - "specPath": "docs/specs/ADR-046-nix-configuration.md", - "dependencyOwner": "ADR046-nix-027; ADR046-nix-028; EndpointRole naming per ADR-046-componentsession-and-bus owning spec", - "currentSource": "Main commit source row below: `packages/d2b-provider/src/{registry,rpc,instance,context}.rs` at `a1cc0b2d`.", - "reuseSource": null, - "reuseAction": "Reuse ProviderRegistry/RPC runtime from main commit `a1cc0b2d`; adapt `SessionIdentity` to Zone naming and update EndpointRole/ServicePackage names per owning bus spec.", - "destination": "`packages/d2b-provider/src/` (adapt in place).", - "detailedDesign": "ProviderRegistry snapshot admission/drain, bounded in-flight caps, typed RpcProviderProxy dispatch, and build-time RegistryLimits validation.", - "integration": "Zone controller publishes active ProviderRegistry snapshots; provider-toolkit RPC proxy admits sessions through `d2b-bus` and dispatches typed Provider operations.", - "dataMigration": "Full d2b 3.0 reset; no v2 ProviderRegistry runtime state import", - "validation": "Port `d2b-provider` `runtime.rs` tests; add v3 AdmissionOptions peer-role and SessionIdentity zone-name gates.", - "removalProof": "ADR45 role/service names are adapted per owning bus spec; no prior v3 ProviderRegistry owner to remove." - }, - { - "workItemId": "ADR046-nix-030", - "specId": "ADR-046-nix-configuration", - "specPath": "docs/specs/ADR-046-nix-configuration.md", - "dependencyOwner": "ADR046-nix-029; ADR046-nix-028; EndpointRole and ServicePackage naming per ADR-046-componentsession-and-bus owning spec", - "currentSource": "Main commit source row below: `packages/d2b-provider-toolkit/src/{adapter,server,conformance,registration}.rs` at `a1cc0b2d`.", - "reuseSource": null, - "reuseAction": "Reuse provider toolkit from main commit `a1cc0b2d`; adapt endpoint role/service variant names and v2_identity imports while preserving wire tags.", - "destination": "`packages/d2b-provider-toolkit/src/` (adapt in place).", - "detailedDesign": "ProviderAgentAdapter validates ComponentSession identity, GeneratedProviderServiceServer dispatches all ProviderType method families with bounded stores/in-flight state, and conformance kit gates every Provider implementation.", - "integration": "Provider processes embed `GeneratedProviderServiceServer`; `ProviderAgentAdapter` validates ComponentSession identity before invoking registry instances; conformance kit gates every Provider crate.", - "dataMigration": "Full d2b 3.0 reset; no v2 provider-toolkit runtime state import", - "validation": "Port `conformance.rs`; assert endpoint role/service wire tags remain 7/4 after any variant rename.", - "removalProof": "ADR45 `v2_identity` imports are adapted to ZoneId/ResourceName; no prior v3 toolkit owner to remove." - }, - { - "workItemId": "ADR046-nix-031", - "specId": "ADR-046-nix-configuration", - "specPath": "docs/specs/ADR-046-nix-configuration.md", - "dependencyOwner": "ADR046-provider-004, ADR046-zone-control-019, ADR046-zone-control-020, ADR046-zone-control-024; Nix resource compiler owner", - "currentSource": "None — D096/D098 semantic Service/Binding authoring and projection factories are net-new in v3", - "reuseSource": null, - "reuseAction": "net-new", - "destination": "`nixos-modules/resources-sharing.nix`; `nixos-modules/assertions.nix`; `tests/unit/nix/cases/resource-sharing.nix`", - "detailedDesign": "Compile the eight exact D098 semantic Service/Binding types plus `ResourceExport`/`ResourceImport` without a second vocabulary. Nix authors authority Services and consumer Bindings, never projection Services; Binding requires same-Zone `serviceRef` and allowed target, while all observed realization remains absent from spec. Core receives a stable projection name/factory fingerprint and creates exactly one same-type Service with ResourceImport ownership, `providerRef`, semantic base/import fields, and no `spec.provider`. Reject Device/Endpoint/Binding export/projection, implementation-qualified/former `*State` aliases, implementation detail in base schemas, cross-Zone refs, USB export without every policy opt-in, and any USB/security-key physical resource that does not use the exact Core-derived `physical-usb-backing` tuple class.", - "integration": "The per-Zone generation uses ADR046-provider-004 common schema fingerprints and ADR046-zone-control-019/020 factory metadata; the four initial Provider modules contribute only strict Provider extensions and implementation config.", - "dataMigration": "Full d2b 3.0 reset; old audio/USB/security-key/observability options lower to new authority Service plus authored Binding only where the owning dossier explicitly preserves migration, and no old public type alias survives", - "validation": "Fast Nix eval/build tests cover all four exact pairs, common base schema discovery, canonical minimal base without `spec.provider`, stable lowering across repeated evaluations, same-Zone refs/targets, strict Provider extension placement, Service-only export, exactly-one Core projection metadata with no `spec.provider`, no auto-Binding, status-field rejection in spec, USB/security-key resolution to one byte-identical Host-global `(Host, physical-usb-backing, opaqueKeyDigest)` collision before effects, Provider-private-class bypass rejection, and rejection of all forbidden names/aliases.", - "removalProof": "Provider-local duplicate Nix base schemas and old direct Device/Endpoint/`*State` projection emitters are removed after the shared emitter and all four dossier migration tests pass." - }, - { - "workItemId": "ADR046-nl-001", - "specId": "ADR-046-provider-network-local", - "specPath": "docs/specs/providers/ADR-046-provider-network-local.md", - "dependencyOwner": "Core; owns `NetworkEffectPort` contract/versioning in `d2b-contracts` and adapter implementation in `d2b-core`.", - "currentSource": "None — net-new v3 work; no pre-ADR45 baseline equivalent for a provider-neutral `NetworkEffectPort` core adapter.", - "reuseSource": null, - "reuseAction": "net-new", - "destination": "`d2b-contracts` trait plus `d2b-core` core adapter; maps to broker wire operations and audit emission.", - "detailedDesign": "Implement `NetworkEffectPort` core adapter in `d2b-core`; map to broker wire ops; emit audit records. Versioning: minor releases may add methods with default impls; major releases require Provider upgrade. The trait lives in `d2b-contracts`; the adapter in `d2b-core`.", - "integration": "`Provider/network-local` reconcile calls injected `NetworkEffectPort`; the core adapter resolves opaque Network intents to closed broker wire ops and emits broker-level audit records.", - "dataMigration": "Full d2b 3.0 reset; no v2 state/config import.", - "validation": "`packages/d2b-provider-network-local/tests/fault_injection.rs` verifies fake `NetworkEffectPort` behavior, error mapping, no broker socket in provider context, and audit-safe adapter boundaries.", - "removalProof": "None — net-new; no prior owner to remove." - }, - { - "workItemId": "ADR046-nl-002", - "specId": "ADR-046-provider-network-local", - "specPath": "docs/specs/providers/ADR-046-provider-network-local.md", - "dependencyOwner": "Core; broker/core contract work consumed by ADR046-nl-001.", - "currentSource": "Existing broker wire has related ApplyNftables, ApplyRoute, ApplySysctl, ApplyNmUnmanaged, UpdateHostsFile, and SeedDnsmasqLease operations, but no `CreateBridge`, `DeleteBridge`, `ReadNftablesDigest`, `ReadSysctlState`, or `ReadBridgePortFlags` v3 ops.", - "reuseSource": null, - "reuseAction": "extend broker wire with net-new operations and reuse existing closed broker-operation dispatch shape", - "destination": "Broker wire contract and broker/core adapter operation table for `CreateBridge`, `DeleteBridge`, `ReadNftablesDigest`, `ReadSysctlState`, and `ReadBridgePortFlags`.", - "detailedDesign": "Add `CreateBridge`, `DeleteBridge`, `ReadNftablesDigest`, `ReadSysctlState`, `ReadBridgePortFlags` broker ops.", - "integration": "`NetworkEffectPort` core adapter invokes these broker ops for bridge lifecycle and observe/drift checks; `Provider/network-local` receives only typed results and opaque digests/handles.", - "dataMigration": "Full d2b 3.0 reset; no v2 state/config import.", - "validation": "`integration/host_fabric.rs` covers bridge create/delete, nftables apply/digest, IPv6 suppression, NetworkManager unmanaged handling, and real `NetworkEffectPort` implementation.", - "removalProof": "None — net-new broker ops; remove only if no Provider consumes them per the removal checklist." - }, - { - "workItemId": "ADR046-nl-003", - "specId": "ADR-046-provider-network-local", - "specPath": "docs/specs/providers/ADR-046-provider-network-local.md", - "dependencyOwner": "Core; handle DTOs are owned by `d2b-contracts` and consumed by `d2b-core` plus `Provider/network-local`.", - "currentSource": "None — net-new v3 work; no public pre-ADR45 baseline equivalent for opaque `AttachmentHandle` or `FabricHandle`.", - "reuseSource": null, - "reuseAction": "net-new", - "destination": "`d2b-contracts` opaque byte-array newtypes; core-held HMAC key and provider-facing redacted handle types.", - "detailedDesign": "Implement `AttachmentHandle` and `FabricHandle` as opaque byte-array newtypes (32 bytes of HMAC-SHA-256 over internal identity material; key held by core). Each handle is single-use; revocation is implicit when the owning Network is deleted. These types are declared in `d2b-contracts`, not in the provider crate.", - "integration": "Core creates handles from Network and attachment identity, stores them only in internal state/status-resource attachment realization, and supplies resolved tap FDs through LaunchTicket without exposing IfNames or MACs.", - "dataMigration": "Full d2b 3.0 reset; no v2 state/config import.", - "validation": "`tests/fault_injection.rs` and `tests/controller_state.rs` cover opaque-handle mismatch, revocation-on-delete, and no raw IfName/IP/MAC public surface.", - "removalProof": "None — net-new; no prior owner to remove." - }, - { - "workItemId": "ADR046-nl-004", - "specId": "ADR-046-provider-network-local", - "specPath": "docs/specs/providers/ADR-046-provider-network-local.md", - "dependencyOwner": "Core; depends on ADR046-nl-003 handles and runtime-cloud-hypervisor LaunchTicket consumption.", - "currentSource": "Existing v1 runtime tap handling is broker/runtime-specific; no v3 LaunchTicket owner-graph FD resolution surface exists.", - "reuseSource": null, - "reuseAction": "net-new LaunchTicket integration; reuse existing LaunchTicket mechanism without adding provider/runtime API surface", - "destination": "Core LaunchTicket builder and dependency resolver that walks `Guest.ownerRef: Network/` to resolved tap FDs.", - "detailedDesign": "Implement LaunchTicket FD resolution: when core builds the LaunchTicket for a Guest with `ownerRef: Network/`, it walks the owner graph, locates the Network, reads its internally-held `AttachmentHandle` set, and includes the corresponding tap FDs in the ticket. No API surface for the provider or runtime is required beyond the existing LaunchTicket mechanism.", - "integration": "Runtime-cloud-hypervisor starts Guests using tap FDs supplied in LaunchTickets; `Provider/network-local` declares attachments and core resolves handles privately.", - "dataMigration": "Full d2b 3.0 reset; no v2 state/config import.", - "validation": "`integration/guest_lifecycle.rs` validates net-VM/workload Guest lifecycle, opaque attachment handle resolution, and `systemArtifactId` binding.", - "removalProof": "None — net-new; no prior owner to remove." - }, - { - "workItemId": "ADR046-nl-005", - "specId": "ADR-046-provider-network-local", - "specPath": "docs/specs/providers/ADR-046-provider-network-local.md", - "dependencyOwner": "Provider plus Core; provider validates through `d2b_host::ifname::derive_ifname`, while core adapter consumes host networking modules.", - "currentSource": "Reuse modules listed in §25.1: `packages/d2b-host/src/ifname.rs:derive_ifname`, `packages/d2b-host/src/nftables.rs`, `packages/d2b-host/src/bridge_port.rs`, `packages/d2b-host/src/routes.rs`, and `packages/d2b-host/src/netlink.rs`.", - "reuseSource": "`d2b-host` IfName, nftables, bridge-port, route preflight, and sysctl/netlink modules.", - "reuseAction": "reuse directly in core adapter; provider re-exports only `derive_ifname` for validation", - "destination": "Core adapter imports `d2b-host` modules; `packages/d2b-provider-network-local/src/ifname.rs` re-exports `d2b_host::ifname::derive_ifname` only.", - "detailedDesign": "The `d2b-host` IfName/nftables/bridge/route modules are consumed directly by the core adapter (not by the provider crate). The provider crate re-exports only `d2b_host::ifname::derive_ifname` for validation purposes. No additional extraction work is required beyond confirming the `d2b-host` API surface is stable.", - "integration": "Provider validateSpec uses deterministic IfName derivation for collision checks; core adapter applies bridge, nftables, route, and sysctl effects through reused `d2b-host` helpers.", - "dataMigration": "Full d2b 3.0 reset; no v2 state/config import.", - "validation": "`tests/ifname_derive.rs`, `tests/fault_injection.rs`, and `integration/host_fabric.rs` prove derivation, adapter reuse, and real host-fabric behavior.", - "removalProof": "None — reused modules remain owned by `d2b-host`; no prior provider-local copy to remove." - }, - { - "workItemId": "ADR046-nl-006", - "specId": "ADR-046-provider-network-local", - "specPath": "docs/specs/providers/ADR-046-provider-network-local.md", - "dependencyOwner": "Provider; depends on ADR046-nl-001 through ADR046-nl-005 and owns the Network reconcile/observe/finalize handlers.", - "currentSource": "None — net-new v3 provider controller; v1 behavior lived in `nixos-modules/network.nix` and `nixos-modules/net.nix` static NixOS module logic.", - "reuseSource": null, - "reuseAction": "port semantics into provider reconcile state machine; do not reuse static per-env systemd/Nix ownership", - "destination": "`packages/d2b-provider-network-local/src/controller.rs`.", - "detailedDesign": "Implement `controller.rs` reconcile/observe/finalize handlers with `NetworkEffectPort` injection.", - "integration": "Controller watches Network, Guest, Volume, Process, User, Host, and Zone resources; creates child resources, writes status, invokes `NetworkEffectPort`, and drives finalizers.", - "dataMigration": "Full d2b 3.0 reset; no v2 state/config import.", - "validation": "`tests/controller_state.rs` covers normal reconcile, errors, finalizer ordering, adoption on restart, and observe/drift cycles with deterministic clock.", - "removalProof": "Supersedes static per-env lifecycle in `nixos-modules/network.nix` and `nixos-modules/net.nix`; removal proof is successor controller coverage plus deletion of duplicate old gates when this provider lands." - }, - { - "workItemId": "ADR046-nl-007", - "specId": "ADR-046-provider-network-local", - "specPath": "docs/specs/providers/ADR-046-provider-network-local.md", - "dependencyOwner": "Provider; owns net-agent ComponentSession service and depends on ComponentSession/bus and net-VM Process lifecycle.", - "currentSource": "None — net-new v3 NetworkAgentService; v1 net-VM behavior was encoded in NixOS services and scripts under `nixos-modules/net.nix`.", - "reuseSource": null, - "reuseAction": "net-new service; preserve semantic nftables/routes reload behavior from v1 net VM configuration", - "destination": "`packages/d2b-provider-network-local/src/process_specs.rs` agent template plus agent service implementation in the net-VM artifact.", - "detailedDesign": "Implement `NetworkAgentService` Noise-KK vsock ComponentSession (Reload + ReadinessQuery methods). Agent reconnect policy: if the controller cannot reach the agent vsock (Guest restart in progress), it retries with exponential backoff up to `drainTimeout` of the agent Process; after timeout it deletes and re-creates the agent Process resource.", - "integration": "Controller writes config Volume content, resolves `Endpoint/net--agent-service`, calls `Reload(config_digest)`, and uses readiness predicates to set Network conditions.", - "dataMigration": "Full d2b 3.0 reset; no v2 state/config import.", - "validation": "`integration/agent_reload.rs` validates Reload, `nft_applied` and `routes_applied` predicates, reconnect behavior, and config digest matching.", - "removalProof": "None — net-new ComponentSession service; no prior service endpoint to remove." - }, - { - "workItemId": "ADR046-nl-008", - "specId": "ADR-046-provider-network-local", - "specPath": "docs/specs/providers/ADR-046-provider-network-local.md", - "dependencyOwner": "Provider; config rendering owned by `Provider/network-local`, storage reconciliation owned by `Provider/volume-local`.", - "currentSource": "Reuse semantics from `nixos-modules/net.nix` lines 168–296 for nftables and lines 302–441 for dnsmasq; runtime volume model is net-new.", - "reuseSource": "`nixos-modules/net.nix` dnsmasq, nftables, routing, and attachment configuration semantics.", - "reuseAction": "port and render into bounded config Volume files", - "destination": "`packages/d2b-provider-network-local/src/config_volume.rs`.", - "detailedDesign": "Implement config Volume content rendering (dnsmasq.conf, nftables.rules, routing.conf, attachments.json).", - "integration": "Controller creates `Volume/net--config`, writes four files through the Volume service, attaches the read-only view to the net VM, and triggers agent reload plus dnsmasq restart.", - "dataMigration": "Full d2b 3.0 reset; config Volume is runtime tmpfs content regenerated from Network spec.", - "validation": "`tests/controller_state.rs`, `integration/agent_reload.rs`, and `integration/delete_sequence.rs` validate rendering, write flow, reload, and cleanup ordering.", - "removalProof": "Supersedes static per-env config generation in `nixos-modules/net.nix`; successor coverage retires duplicate old Nix/service assertions." - }, - { - "workItemId": "ADR046-notify-001", - "specId": "ADR-046-provider-notification-desktop", - "specPath": "docs/specs/providers/ADR-046-provider-notification-desktop.md", - "dependencyOwner": "W0 shared contract root; session/bus owner", - "currentSource": "`packages/d2b-notify/src/{events,state,notifications,nonce}.rs`", - "reuseSource": null, - "reuseAction": "copy/adapt", - "destination": "`packages/d2b-provider-notification-desktop/src/{types,redact,action_nonce}.rs`", - "detailedDesign": "`NotificationRequest`/`NotificationResult` DTOs and stream record types; bounded fields; closed category set; icon catalog contract; `ActionNonce`/`ActionNonceStore` adapted from main; no ResourceType DTO", - "integration": "Zone bus service; host-sink stream consumer; guest-source stream producer", - "dataMigration": "No v2 compatibility; reset", - "validation": "`tests/stream_record.rs` — DTO schema vectors; `tests/action_nonce.rs` — single-use/TTL/capacity/replay", - "removalProof": "v2 `d2b.notify.v2` generated stubs removed after v3 service established" - }, - { - "workItemId": "ADR046-notify-002", - "specId": "ADR-046-provider-notification-desktop", - "specPath": "docs/specs/providers/ADR-046-provider-notification-desktop.md", - "dependencyOwner": "ADR046-session-001, ADR046-bus-001; session/bus wiring", - "currentSource": "`packages/d2b-notify/src/services/`", - "reuseSource": null, - "reuseAction": "copy/adapt", - "destination": "`packages/d2b-provider-notification-desktop/src/stream_admission.rs`", - "detailedDesign": "Session admission checks, Noise profile enforcement, transport class validation", - "integration": "ComponentSession/d2b-bus", - "dataMigration": "None — full d2b 3.0 reset; no prior state to migrate", - "validation": "`tests/stream_admission.rs` — all rejection vectors", - "removalProof": "Old `DesktopServices` session admitted under v2 contract removed when v3 session established" - }, - { - "workItemId": "ADR046-notify-003", - "specId": "ADR-046-provider-notification-desktop", - "specPath": "docs/specs/providers/ADR-046-provider-notification-desktop.md", - "dependencyOwner": "ADR046-notify-001, ADR046-reconcile-001; controller owner", - "currentSource": "`packages/d2b-notify/src/services/observer.rs`", - "reuseSource": null, - "reuseAction": "new", - "destination": "`packages/d2b-provider-notification-desktop/src/controller.rs`", - "detailedDesign": "Async Process placement controller; watches `guestSources` Guest refs; creates/drains/deletes guest-source Processes; creates/stops host-sink Process on display-wayland readiness change; declares no Provider state Volume and does not own/add/create/delete Volumes; bounded non-secret operational state lives in `status`/the core Operation ledger (D087); notification delivery state (in-memory projection, action nonce store) is host-sink process memory only; no ResourceType reconcile loop", - "integration": "Zone resource store (Process API); d2b-bus; display-wayland dependency watch", - "dataMigration": "None — full d2b 3.0 reset; no prior state to migrate", - "validation": "Unit tests for placement FSM in `tests/stream_record.rs`; Volume creation/deletion lifecycle in `tests/volume_lifecycle.rs`; see also `integration/cross_zone_source.rs` end-to-end", - "removalProof": "Not applicable (new controller)" - }, - { - "workItemId": "ADR046-notify-004", - "specId": "ADR-046-provider-notification-desktop", - "specPath": "docs/specs/providers/ADR-046-provider-notification-desktop.md", - "dependencyOwner": "ADR046-notify-002, ADR046-notify-003; host-sink owner", - "currentSource": "`packages/d2b-notify/src/services/actions.rs`, `packages/d2b-notify/src/bin/`", - "reuseSource": null, - "reuseAction": "adapt", - "destination": "`packages/d2b-provider-notification-desktop/src/host_sink.rs`", - "detailedDesign": "D-Bus client; `DesktopNotificationSink` stream consumer; action nonce issuance; `DesktopNotificationObserver` projection (in-memory, not persisted); display-wayland ComponentSession bootstrap for pre-opened D-Bus FD", - "integration": "D-Bus session (pre-opened FD via ComponentSession bootstrap); ComponentSession named streams", - "dataMigration": "None — full d2b 3.0 reset; no prior state to migrate", - "validation": "`integration/dbus_sink.rs`, `integration/observer_client.rs`, `integration/action_invoke.rs`", - "removalProof": "`nixos-modules/notifications.nix` state-dir tmpfiles rule retired; all notification state is in-memory per-session with no Volume replacement" - }, - { - "workItemId": "ADR046-notify-005", - "specId": "ADR-046-provider-notification-desktop", - "specPath": "docs/specs/providers/ADR-046-provider-notification-desktop.md", - "dependencyOwner": "ADR046-notify-002; guest-source owner", - "currentSource": "v3 security-key guest vsock path (conceptual similarity only; not copied directly)", - "reuseSource": null, - "reuseAction": "new", - "destination": "`packages/d2b-provider-notification-desktop/src/guest_source.rs`", - "detailedDesign": "Guest-side vsock ComponentSession; `NotificationRequest` record validation and field bounding; category filter; `DesktopNotificationSink` stream forwarding; `NotificationResult` handling; no host-side resource creation", - "integration": "Guest process vsock → host ComponentSession", - "dataMigration": "None — full d2b 3.0 reset; no prior state to migrate", - "validation": "`integration/cross_zone_source.rs`", - "removalProof": "v3 baseline security-key notification path in `d2b-notify` is superseded; clipd direct `notify_rust` call superseded" - }, - { - "workItemId": "ADR046-notify-006", - "specId": "ADR-046-provider-notification-desktop", - "specPath": "docs/specs/providers/ADR-046-provider-notification-desktop.md", - "dependencyOwner": "ADR046-notify-001; Nix/telemetry owner", - "currentSource": "`nixos-modules/notifications.nix`; `packages/d2bd/src/metrics.rs`", - "reuseSource": null, - "reuseAction": "adapt", - "destination": "Nix: Zone resource authoring in `nixos-modules/`; metrics: `packages/d2b-provider-notification-desktop/src/`", - "detailedDesign": "Zone Provider resource and RoleBinding Nix compiler output; `spec.config.guestSources` authoring and eval-time assertions; OTEL metric emitters with closed labels; audit record emitters", - "integration": "Nix configuration compiler; OTEL emitter ring; authoritative audit", - "dataMigration": "`d2b.notifications.*` Nix options retired; `d2b.zones..resources.notification-desktop` with `spec.config.guestSources` replaces", - "validation": "Eval tests for category enforcement, displayWaylandRef assertion, guestRef resolution; `tests/stream_redaction.rs` for content-free telemetry", - "removalProof": "`nixos-modules/notifications.nix` removed after Zone resource equivalence confirmed by eval test" - }, - { - "workItemId": "ADR046-object-001", - "specId": "ADR-046-resource-object-model", - "specPath": "docs/specs/ADR-046-resource-object-model.md", - "dependencyOwner": "W0 shared contract root; `d2b-contracts`", - "currentSource": "`packages/d2b-realm-core/src/ids.rs`, `workload.rs`, `error.rs`; `packages/d2b-core/src/storage.rs`, `processes.rs`", - "reuseSource": null, - "reuseAction": "extract and adapt", - "destination": "`packages/d2b-contracts/src/v3/resource.rs`, `resource_status.rs`, `resource_schema.rs`", - "detailedDesign": "Implement strict ResourceEnvelope, metadata, the three-layer spec shape (universal envelope + ResourceType base `spec.*` incl. `spec.providerRef` + optional canonical `spec.provider` `{ schemaId, schemaVersion, settings }`), the three-layer status shape (universal base + `status.resource` + optional `status.provider` with `providerRef`/`schemaId`/`schemaVersion`/`observedProviderGeneration`/`details`), phase/condition/outcome, canonical JSON, per-layer bounds/redaction, ownerRef/UID fields", - "integration": "Store/API/SDK/Nix/codegen consume one contract", - "dataMigration": "Full d2b 3.0 reset; no v2 resource import", - "validation": "Golden JSON/protobuf vectors; serde unknown-field; three-layer spec shape round-trip; canonical minimal base-spec acceptance; base-schema version/fingerprint conformance; `spec.provider` deny-unknown/version-mismatch/shadow rejection and providerRef-binding; three-layer status shape round-trip; base-only projection (universal + `status.resource`) ignores/omits `status.provider`; `status.provider` unknown-field/version-mismatch rejection; status redaction/size/time/phase tests; `status.update` currency object round-trip (state/reasons/disruption/preserveState/owned+dependency refs bounded); `spec.updatePolicy` base round-trip", - "removalProof": "Old DTOs removed per owning ResourceType wave only after rendered/runtime consumers move" - }, - { - "workItemId": "ADR046-object-002", - "specId": "ADR-046-resource-object-model", - "specPath": "docs/specs/ADR-046-resource-object-model.md", - "dependencyOwner": "ADR046-object-001; native resource store", - "currentSource": "`packages/d2b-realm-core/src/allocator_engine.rs`, `d2b-realm-router/src/lib.rs` shared ownership/idempotency precedents", - "reuseSource": null, - "reuseAction": "adapt", - "destination": "`packages/d2b-resource-store-redb/src/ownership.rs`, `packages/d2b-controller-toolkit/src/owner_hints.rs`", - "detailedDesign": "Singular ownerRef resolution/UID binding, cycle/depth property checks, reverse index, owner hints, child-first deletion", - "integration": "Every store mutation updates owner index and hint dispatcher atomically", - "dataMigration": "None after reset", - "validation": "Property tests for cycles/reparent/name reuse; integration tests for child drift repair and owner cascades", - "removalProof": "Not applicable" - }, - { - "workItemId": "ADR046-otel-001", - "specId": "ADR-046-provider-observability-otel", - "specPath": "docs/specs/providers/ADR-046-provider-observability-otel.md", - "dependencyOwner": "ADR046-telem-001 (BoundedEmitter crate); W0/W2; telemetry/observability owner", - "currentSource": "`packages/d2b-host/src/otel_host_bridge_argv.rs` (`OtelHostBridgeArgvInputs`, `otel_host_bridge_argv`, `OtelHostBridgeArgvError`); `packages/d2bd/src/otel_host_bridge_readiness.rs` (`OtelHostBridgeReadiness`, `otel_host_bridge_read`); `packages/d2b-contracts/src/broker_wire.rs::RunnerRole::OtelHostBridge`; `packages/d2b-core/src/processes.rs::ProcessRole::OtelHostBridge`", - "reuseSource": null, - "reuseAction": "adapt (`OtelHostBridgeArgvInputs` vsock logic → native Rust OTLP relay); adapt (`OtelHostBridgeReadiness` → `TelemetryBinding` conditions); delete-after-cutover (`RunnerRole::OtelHostBridge`, `ProcessRole::OtelHostBridge`)", - "destination": "`packages/d2b-provider-observability-otel/src/{forwarder_bin,controller,binding}.rs`", - "detailedDesign": "Binding-owned forwarder: accept OTLP only from the exact Guest producer, relay through the Binding-private Endpoint/Volume to its edge collector, enforce bounded frames/quota/session timeout, and use no OTEL SDK. Map forwarder readiness to `status.provider`, then derive provider-neutral Binding ingest readiness; Process Provider owns launch/pidfd.", - "integration": "Controller creates vsock-forwarder long-lived `Process` → ProviderSupervisor → system-minijail/systemd launch → vsock socket bind → Guest side connects", - "dataMigration": "Full reset; existing socat bridge retired after cutover", - "validation": "`integration/scenario_obs_zone_forwarding.rs`; adapted `minijail_relay_otel.rs` shape test for Provider-managed runner; assert `RunnerRole::OtelHostBridge` is absent from `d2b-contracts` after removal", - "removalProof": "Legacy symbols removed only after Binding ownership/readiness and forwarding integration pass" - }, - { - "workItemId": "ADR046-otel-002", - "specId": "ADR-046-provider-observability-otel", - "specPath": "docs/specs/providers/ADR-046-provider-observability-otel.md", - "dependencyOwner": "ADR046-otel-001 + ADR046-telem-001 + ADR046-provider-001 (Provider toolkit) + ADR046-provider-004 (common telemetry Service/Binding base) + resource/Endpoint/Volume contracts; W2; observability owner", - "currentSource": "`nixos-modules/components/observability/host.nix` (`otelRuntimeDir`, `hostEgressSocket`, `setfacl` ACL pattern, `scrapeJournal` option, `identityName`); `nixos-modules/components/observability/stack.nix` (`ingressSources`, `vmName`, `receiverGrpcPort`, loopback binding, `signoz.listenPort`)", - "reuseSource": null, - "reuseAction": "adapt Nix pipeline shape (replace per-VM `vmName` with per-Zone name; replace socat runner with vsock-forwarder long-lived Process; adapt `ingressSources` per-Zone entry)", - "destination": "`packages/d2b-provider-observability-otel/src/{collector_bin,emitter_socket,exporter,controller,service,binding}.rs`; updated Nix observability modules", - "detailedDesign": "Register the initial implementation of both provider-neutral qualified ResourceTypes by binding the exact ADR046-provider-004 base versions/fingerprints, then define only strict observability-otel Service/Binding spec and status extensions. Reconcile each Binding into an edge collector, private Endpoints, runtime Volume, and optional forwarder. Collector links the full OTEL SDK, resolves `serviceRef`, stamps trusted producer identity, and enforces common signals/quota/policy plus strict batching extension. Write generic Service/Binding observations to `status.resource` and SigNoz/OTLP/OTEL observations only to `status.provider`; no state file or Provider state Volume. Provider root config remains installation-only.", - "integration": "`BoundedEmitter` → Binding-private Endpoint → edge collector/OTEL SDK → same-Zone authority or projected Service → SigNoz", - "dataMigration": "Existing SigNoz data not migrated; v3 starts fresh per Zone", - "validation": "Common-fixture/fingerprint and canonical-minimal-base conformance (including a fake alternate telemetry Provider); `tests/emitter_socket_receive.rs`; `tests/exporter_outage.rs`; `tests/exporter_backpressure.rs`; `integration/scenario_full_pipeline.rs`; adapted `policy_observability.rs` (retain all existing assertions; add new `d2b.zone`, `d2b.provider` allowlist entries)", - "removalProof": "`guest.nix` per-VM guest collector retired after `integration/scenario_obs_zone_forwarding.rs` passes" - }, - { - "workItemId": "ADR046-otel-003", - "specId": "ADR-046-provider-observability-otel", - "specPath": "docs/specs/providers/ADR-046-provider-observability-otel.md", - "dependencyOwner": "ADR046-otel-002 + ADR046-telem-001 + ADR046-volume-001; Nix/observability owner", - "currentSource": "`nixos-modules/components/observability/host.nix::journaldStorageDir`, `scrapeJournal` option; journald cgroup-path filtering pattern", - "reuseSource": null, - "reuseAction": "adapt journald receiver config for per-Zone cgroup filter", - "destination": "`packages/d2b-provider-observability-otel/src/nix/journald.nix`; `packages/d2b-provider-observability-otel/src/journald.rs`", - "detailedDesign": "Per `ADR-046-telemetry-audit-and-support` §journald stdout/stderr ingestion: cgroup filter derived from trusted `producerRef`; redaction drops credential/secret/path fields, `_CMDLINE`, `_EXE`, and `INVOCATION_ID`; the strict `TelemetryBinding.spec.provider.settings.journald.enable` defaults false.", - "integration": "Collector binary journald receiver config path → cgroup filter expression → OTel Collector journald receiver → redaction filter → OTLP export", - "dataMigration": "None — full d2b 3.0 reset; no prior state to migrate", - "validation": "Nix eval test: filter expression set when enabled; assert `_CMDLINE` and `INVOCATION_ID` in drop list; `tests/redaction.rs` for journald field redaction", - "removalProof": "Not applicable" - }, - { - "workItemId": "ADR046-otel-004", - "specId": "ADR-046-provider-observability-otel", - "specPath": "docs/specs/providers/ADR-046-provider-observability-otel.md", - "dependencyOwner": "ADR046-otel-002; policy/contract-tests owner", - "currentSource": "`packages/d2b-contract-tests/tests/policy_observability.rs` (`loki_native_otel_resource_attributes` allowlist; `tempo_stack_signoz_backend_and_collector`; `startup_tracing_avoids_host_path_fields`); `packages/d2b-contract-tests/tests/policy_metrics.rs` (`EXPECTED_METRICS` table); `packages/d2b-contract-tests/tests/minijail_relay_otel.rs`", - "reuseSource": null, - "reuseAction": "adapt and extend existing tests; keep existing test assertions", - "destination": "`packages/d2b-contract-tests/tests/policy_observability.rs` (updated); `packages/d2b-contract-tests/tests/policy_telemetry_redaction.rs` (new, per ADR046-telem-008); `packages/d2b-provider-observability-otel/tests/no_vm_label_in_metrics.rs`", - "detailedDesign": "(1) Extend `loki_native_otel_resource_attributes` allowlist with `d2b.zone`, `d2b.provider`, `d2b.component`, `service.version`. (2) Add gate: `no_isolation` must not appear in any Provider `MetricDescriptor` label or span attribute catalog. (3) Adapt `minijail_relay_otel.rs` shape test for Provider-managed runner (no broker `RunnerRole::OtelHostBridge`). (4) Add metric inventory gates for `d2b_otel_*` instruments from this spec. (5) Retain: `startup_tracing_avoids_host_path_fields`; SigNoz-only backend assertion; `tempo_guest_collector_shape`; `config_source = \"realm-controllers\"` absence gate.", - "integration": "Contract-tests run in workspace `make test-drift` and `make test-lint`", - "dataMigration": "None — full d2b 3.0 reset; no prior state to migrate", - "validation": "All contract-tests pass after update; existing allowlist test does not regress", - "removalProof": "Not applicable" - }, - { - "workItemId": "ADR046-otel-005", - "specId": "ADR-046-provider-observability-otel", - "specPath": "docs/specs/providers/ADR-046-provider-observability-otel.md", - "dependencyOwner": "ADR046-zone-control-019, ADR046-zone-control-020; observability Provider owner", - "currentSource": "None — net-new ADR 0046 cross-Zone sharing (D096)", - "reuseSource": "SigNoz ingest authority (this dossier); `packages/d2b-provider/src/share_adapter.rs` `ExportAdapter`/`ImportAdapter` traits", - "reuseAction": "net-new (implement the signed observability export/import adapter)", - "destination": "`packages/d2b-provider-observability-otel/src/share_adapter.rs`", - "detailedDesign": "Implement the signed adapter: `sys-obs` exports only the authority `TelemetryService`; its local ingest Endpoint remains Service-owned implementation transport and is never an Export field. ResourceExport uses only canonical `serviceType`, `projectionSchemaFingerprint`, and `factoryFingerprint`; ResourceImport uses the corresponding `expectedServiceType`, `expectedProjectionSchemaFingerprint`, and `expectedFactoryFingerprint`. Every producer import causes Core to create one local `TelemetryService` projection with ResourceImport ownership, `providerRef`, semantic base/import fields, and no `spec.provider`. Routing derives from the signed local descriptor and ResourceImport record. The semantic factory fingerprint binds factory metadata plus projection-protocol version, never Provider/adapter identity, which the signed descriptor authenticates separately. Binding `serviceRef` targets that projection. Enforce many-to-one quota/credit/backpressure/schema/source-stamp/redaction/cardinality over bounded encrypted streams; no FD/socket crosses a Zone and audit authority stays local.", - "integration": "Core export/import controller (ADR046-zone-control-019); local projection lifecycle (ADR046-zone-control-020); ComponentSession bounded encrypted named streams", - "dataMigration": "None — full d2b 3.0 reset", - "validation": "Fast `projection_chain.rs` proves Service projection semantics with a fake stream, exact canonical Export/Import type and fingerprint fields, rejection of obsolete `endpointRef`/`exportedType`/`baseSchemaFingerprint`/`expectedType`/`expectedBaseSchemaFingerprint`/`projectionType`, rejection of projection `spec.provider`, and semantic-fingerprint stability under Provider/adapter identity mutation while signed descriptor authentication remains exact; integration alone runs real encrypted streams and SigNoz; revocation/reconnect, quotas, source stamp, redaction/cardinality, no FD crossing, and audit locality", - "removalProof": "Not applicable" - }, - { - "workItemId": "ADR046-otel-006", - "specId": "ADR-046-provider-observability-otel", - "specPath": "docs/specs/providers/ADR-046-provider-observability-otel.md", - "dependencyOwner": "ADR046-otel-005, ADR046-zone-control-019, ADR046-zone-control-020; observability Provider owner", - "currentSource": "`nixos-modules/components/observability/{stack,host,guest}.nix` (SigNoz/ClickHouse/Keeper loopback authority, per-source vsock ingress, source-identity `upsert`, `sending_queue`/`retry_on_failure`, allow-list projection); `nixos-modules/{index,observability-vm}.nix`; `packages/d2b-host/src/{otel_host_bridge_argv,vsock_relay_argv}.rs` (pre-opened vsock fds; broker rejects source VM ≠ obs VM); `packages/d2bd/src/otel_host_bridge_readiness.rs`", - "reuseSource": "Same baseline stack/bridge/readiness symbols; `packages/d2b-provider/src/share_adapter.rs` traits", - "reuseAction": "net-new universal provider-neutral `telemetry.d2bus.org.TelemetryService`/`telemetry.d2bus.org.TelemetryBinding` pair; adapt existing authority/edge behavior as the initial observability-otel implementation", - "destination": "`packages/d2b-provider-observability-otel/src/{authority,service,binding,projection}.rs`; `AuthorityDescriptor` on the `sys-obs` `TelemetryService`", - "detailedDesign": "Implement one provider-neutral D097 authority Service with generic telemetry-ingest Endpoint refs and common service/signals/quota/policy fields. Its strict observability-otel `spec.provider` alone selects the loopback SigNoz stack, backend Endpoints, and OTLP. Core rejects duplicates and adopts by owner proof. Implement core-owned imported Service projections with no `spec.provider` or backend ownership. Implement per-producer Bindings with common service/producer/signals/quota/policy fields and strict implementation extension; Bindings own/cause edge collector/forwarder/private Endpoints/runtime Volume. Keep generic observations in `status.resource` and SigNoz/OTLP/OTEL observations in `status.provider`. Preserve trusted source upsert, retry/queue, bounded cardinality/redaction, audit non-transfer, status-first state, and no OTEL SDK in core. Endpoint is transport only.", - "integration": "Ingest authority + export (ADR046-otel-005); core export/import controller and projection lifecycle (ADR046-zone-control-019/020); ComponentSession per-import encrypted streams; d2b-telemetry closed-label metrics", - "dataMigration": "None — full d2b 3.0 reset", - "validation": "Fast `resource_service_binding.rs` and `projection_chain.rs` plus reused nix-unit/policy tests prove provider-neutral names, base/Provider field separation, projection `spec.provider` rejection, exact D088 placement of semantic observations under `status.resource` and implementation observations under `status.provider`, ownership, schemas, stamping, quotas, redaction, and projection chain. Real SigNoz and real stream scenarios are integration-only.", - "removalProof": "Legacy fixed per-source vsock ingress and old gates are removed only after the Service/Binding/ComponentSession successor passes; neither old provider-qualified ResourceType name nor any Endpoint-projection or ResourceType alias remains, and no duplicate suite remains." - }, - { - "workItemId": "ADR046-pkg-001", - "specId": "ADR-046-resources-zone-control", - "specPath": "docs/specs/ADR-046-resources-zone-control.md", - "dependencyOwner": "ADR046-zone-control-003; workspace policy owner", - "currentSource": "`packages/d2b-contract-tests/tests/policy_contracts.rs` lines 5–6 (D2B_FIXTURES gate / workspace-checks integration pattern — `implemented-and-reachable`, baseline `b5ddbed6`); `packages/d2b-contract-tests/tests/static_invariants.rs` (hermetic policy test structure — `implemented-and-reachable`); `tests/tools/rust-workspace-checks.sh` (D2B_FIXTURES step shell harness — `implemented-and-reachable`); AGENTS.md \"Naming conventions\" section (`-` workspace sort rules — `implemented-and-reachable`); `packages/d2b-realm-core/src/ids.rs` `LABEL_PATTERN` / `MAX_ID_LEN` (name regex reused for crate name token validation — `implemented-and-reachable`)", - "reuseSource": "None from main; workspace policy tests are repo-specific", - "reuseAction": "net-new", - "destination": "`packages/d2b-contract-tests/tests/policy_provider_crate_layout.rs` (new file; gated under D2B_FIXTURES in existing `tests/tools/rust-workspace-checks.sh`)", - "detailedDesign": "Implement `policy_provider_crate_layout.rs` with the following test functions: (1) `every_provider_crate_has_src` — walk `packages/d2b-provider-*/` directories in the workspace, assert each contains `src/`; failure names crate and missing path; (2) `every_provider_crate_has_tests` — assert `tests/` present; (3) `every_provider_crate_has_integration` — assert `integration/` present; (4) `every_provider_crate_has_readme` — assert `README.md` present; (5) `every_provider_readme_has_required_sections` — read `README.md`, check for all nine section headings from §4.8.3 (case-insensitive, after stripping `#` and whitespace); failure names the missing heading(s); (6) `every_integration_file_has_target_declaration` — for each `integration/*.rs` file, scan first 20 lines for exactly one `//! integration-target: (container|host-integration)` declaration; failure names the file and the violation (missing/multiple/invalid value); (7) `non_provider_crates_exempt` — verify the check does not run on non-`d2b-provider-*` crates. All checks are filesystem-only (no compilation). Workspace member list is discovered by parsing `packages/Cargo.toml` `[workspace].members`. Gate: add the new test file to `tests/tools/rust-workspace-checks.sh` D2B_FIXTURES list alongside existing policy tests", - "integration": "`make test-policy` and `make check` both fail if any provider crate violates §4.8; consistent with existing `no-bash-ast-walker` and workspace-sort gates; ADR046-zone-control-003 references §4.8 for Provider package conventions", - "dataMigration": "Additive; no existing `d2b-provider-*` crates in the pre-ADR45 baseline; first Provider crate created must comply from inception", - "validation": "§15.3 layout conformance tests: `provider-crate-layout-src-required`, `provider-crate-layout-tests-required`, `provider-crate-layout-integration-required`, `provider-crate-layout-readme-required`, `provider-readme-sections-all-present`, `provider-readme-sections-partial-missing`, `provider-integration-target-declared`, `provider-integration-target-unique`, `provider-integration-target-valid-values`, `provider-crate-naming-convention`, `provider-crate-layout-non-provider-exempt`", - "removalProof": "No existing code removed; additive policy test only" - }, - { - "workItemId": "ADR046-primitives-001", - "specId": "ADR-046-primitive-resource-composition", - "specPath": "docs/specs/ADR-046-primitive-resource-composition.md", - "dependencyOwner": "W0; resource contracts", - "currentSource": "`packages/d2b-core/src/processes.rs`, `minijail_profile.rs`, `storage.rs`; `d2b-contracts/src/broker_wire.rs`", - "reuseSource": null, - "reuseAction": "extract and adapt", - "destination": "`packages/d2b-contracts/src/v3/host.rs`, `guest.rs`, `execution_policy.rs`, `process.rs`, `volume.rs`, `user.rs`, `network.rs`, `device.rs`, `credential.rs`", - "detailedDesign": "Complete minimal ResourceType schemas and shared execution/Volume sub-schemas", - "integration": "Provider dossiers/controller descriptors bind exact types", - "dataMigration": "Full reset", - "validation": "Schema vectors and folded-field/no-duplicate-type policy tests", - "removalProof": "Old DTOs removed only by owning future slices" - }, - { - "workItemId": "ADR046-primitives-002", - "specId": "ADR-046-primitive-resource-composition", - "specPath": "docs/specs/ADR-046-primitive-resource-composition.md", - "dependencyOwner": "Process contracts; system Provider slices", - "currentSource": "broker SpawnRunner/pidfd; d2bd supervisor; unsafe-local helper; guest exec runner", - "reuseSource": null, - "reuseAction": "extract and adapt", - "destination": "`packages/d2b-provider-system-systemd/`, `packages/d2b-provider-system-minijail/`, shared neutral process conformance library", - "detailedDesign": "Common Process/EphemeralProcess, provider-specific launch/pidfd/wait/adoption/status", - "integration": "Process controller registration under Host/Guest; d2b-bus ResourceClient/status", - "dataMigration": "Current ProcessRoles converted by exact disposition table", - "validation": "Shared conformance plus Host/Guest/user integration", - "removalProof": "Role branches removed only after successor Provider tests" - }, - { - "workItemId": "ADR046-primitives-003", - "specId": "ADR-046-primitive-resource-composition", - "specPath": "docs/specs/ADR-046-primitive-resource-composition.md", - "dependencyOwner": "Volume contract; Volume Provider slices", - "currentSource": "`storage-json.nix`, `d2b-core/src/storage.rs`, store/TPM/runtime path owners, virtiofsd argv/ProcessRole", - "reuseSource": null, - "reuseAction": "extract and adapt", - "destination": "`packages/d2b-provider-volume-*/`; `nixos-modules/resources-volume.nix`", - "detailedDesign": "Fine-grained Volume layout/views, host-path policy, virtiofs attachments/status/owned Process", - "integration": "Host/Guest/Process refs and Volume controller", - "dataMigration": "Full reset; Provider-specific state export only where separately specified", - "validation": "ACL/no-follow/marker, sharing/views, virtiofs host/guest mount tests", - "removalProof": "storage.json rows removed only after Volume successor parity" - }, - { - "workItemId": "ADR046-process-001", - "specId": "ADR-046-components-processes-and-sandbox", - "specPath": "docs/specs/ADR-046-components-processes-and-sandbox.md", - "dependencyOwner": "W0/W2; Process contracts/supervisor", - "currentSource": "`d2b-core/src/{processes,process_builder,minijail_profile}.rs`; `d2bd/src/supervisor/*`; broker `spawn_runner.rs`", - "reuseSource": "Useful main ProviderSupervisor/session/process code named by sub-items", - "reuseAction": "extract and adapt", - "destination": "`packages/d2b-process/src/`, `packages/d2b-provider-supervisor/src/`", - "detailedDesign": "Common spec/status/tickets/pidfd/adoption/naming/cgroup/fast path", - "integration": "Process Provider controllers → supervisor/broker/systemd → async status", - "dataMigration": "Full reset; no role snapshot import", - "validation": "Shared conformance/fault/latency tests", - "removalProof": "Role/DAG path removed only per role disposition" - }, - { - "workItemId": "ADR046-process-002", - "specId": "ADR-046-components-processes-and-sandbox", - "specPath": "docs/specs/ADR-046-components-processes-and-sandbox.md", - "dependencyOwner": "ADR046-process-001; systemd/minijail Provider owners", - "currentSource": "unsafe-local helper runtime/systemd; guest exec systemd-run; broker SpawnRunner/minijail", - "reuseSource": "Main process/session helpers if selected by exact sub-items", - "reuseAction": "extract/adapt", - "destination": "`packages/d2b-provider-system-systemd/`, `packages/d2b-provider-system-minijail/`", - "detailedDesign": "Two Process/EphemeralProcess implementations, pidfd/wait ownership, system/user domains", - "integration": "Zone-installed Providers/controller instances per Host/Guest", - "dataMigration": "None — full d2b 3.0 reset; no prior state to migrate", - "validation": "Identical schema/status conformance plus provider-specific adoption", - "removalProof": "Old helpers retained until host/user/guest parity" - }, - { - "workItemId": "ADR046-provider-001", - "specId": "ADR-046-provider-model-and-packaging", - "specPath": "docs/specs/ADR-046-provider-model-and-packaging.md", - "dependencyOwner": "W0; Provider contract/catalog owner", - "currentSource": "`packages/d2b-realm-provider/src/{provider,capabilities,error,credential,rate_limit,conformance}.rs`", - "reuseSource": null, - "reuseAction": "extract and adapt", - "destination": "`packages/d2b-contracts/src/v3/provider.rs`, `packages/d2b-provider/src/lib.rs`, `packages/d2b-provider-toolkit/`", - "detailedDesign": "Provider resource/manifest/components/dependencies/services/trust/compatibility/toolkit", - "integration": "Zone config/catalog → Provider resource → Process components → bus/resource routes", - "dataMigration": "Full reset", - "validation": "Contract vectors, fake/malicious Provider, one-crate/one-identity policy", - "removalProof": "Old trait crate retired only after all Provider dossiers migrate" - }, - { - "workItemId": "ADR046-provider-002", - "specId": "ADR-046-provider-model-and-packaging", - "specPath": "docs/specs/ADR-046-provider-model-and-packaging.md", - "dependencyOwner": "Provider contract; package/Nix integrator", - "currentSource": "`packages/Cargo.toml`; `flake.nix`; `nixos-modules/host-daemon.nix`; current source package derivations", - "reuseSource": null, - "reuseAction": "adapt", - "destination": "one `packages/d2b-provider--/` per Provider with mandatory src/, tests/, integration/, README.md; generic Nix Provider package/catalog emitter", - "detailedDesign": "Split current combined/composition crates; exact outputs/manifests/conformance/layout/documentation", - "integration": "Provider package installed/registered per Zone", - "dataMigration": "No package compatibility path", - "validation": "Workspace naming/dependency/output/dossier/catalog parity policy", - "removalProof": "Combined crate removed only after every live implementation has a Provider successor" - }, - { - "workItemId": "ADR046-provider-003", - "specId": "ADR-046-provider-model-and-packaging", - "specPath": "docs/specs/ADR-046-provider-model-and-packaging.md", - "dependencyOwner": "Process contracts; system Provider owners", - "currentSource": "`d2bd` DAG/broker spawn; unsafe-local helper; guestd/exec runner; `d2b-host` runtime provider", - "reuseSource": null, - "reuseAction": "extract and adapt", - "destination": "`packages/d2b-provider-system-core/`, `d2b-provider-system-systemd/`, `d2b-provider-system-minijail/`", - "detailedDesign": "Bootstrap system-core; common Process/EphemeralProcess providers and pidfd conformance", - "integration": "Host/Guest providerRef/domain/userRef, local supervisors, resource status", - "dataMigration": "Current roles converted under reset", - "validation": "Shared conformance and host/user/non-Host tests", - "removalProof": "Current role launch paths removed after parity" - }, - { - "workItemId": "ADR046-provider-004", - "specId": "ADR-046-provider-model-and-packaging", - "specPath": "docs/specs/ADR-046-provider-model-and-packaging.md", - "dependencyOwner": "ADR046-provider-001; shared semantic Service/Binding contract owner", - "currentSource": "None — D098 common semantic Service/Binding bases are net-new ADR 0046 contracts", - "reuseSource": null, - "reuseAction": "net-new", - "destination": "`packages/d2b-contracts/src/v3/semantic_services/{mod,audio,security_key,telemetry,usb}.rs`; generated schema artifacts for the eight exact qualified ResourceTypes", - "detailedDesign": "Define one shared strict base spec/status DTO and schema contract for each frozen D098 Service/Binding pair, including exact semantic type/schema IDs, versions, fingerprints, minimal valid base fixtures without `spec.provider`, authority/projection Service union, same-Zone Binding `serviceRef`/target rules, D088 `status.resource` layering, status-only observations, and projection-factory type binding. A Core-generated projection permits only `providerRef`, semantic base/import fields, and ResourceImport ownership; it rejects `spec.provider`. Register no implementation-qualified or former `*State` alias.", - "integration": "Provider manifests and ResourceApiBindings consume the common catalog fingerprint; ADR046-zone-control-019/020 use the same factory metadata to admit an owner Service and core-create one same-type projection Service; the four initial Provider dossiers supply only strict implementation extensions/controllers.", - "dataMigration": "Full d2b 3.0 reset; no prior public Service/Binding names or aliases are imported", - "validation": "Shared contract tests cover exact names, strict serde/schema round trips, common base discoverability without any Provider package, canonical minimal base acceptance without `spec.provider`, same-Zone refs/targets, owner/projection discrimination, Core projection rejection of `spec.provider`, common fields only under `status.resource`, implementation observation only under `status.provider`, status-only observations, no Device/Endpoint/Binding projection, implementation-detail rejection, semantic factory-fingerprint stability under Provider/adapter identity changes, and rejection of every implementation-qualified/former `*State` alias. Each initial and fake alternate Provider must pass the identical base conformance fixture.", - "removalProof": "Any Provider-local duplicate base DTO/schema is removed before that Provider is registered; only strict Provider extension DTOs remain implementation-owned." - }, - { - "workItemId": "ADR046-provider-agent-001", - "specId": "ADR-046-resources-zone-control", - "specPath": "docs/specs/ADR-046-resources-zone-control.md", - "dependencyOwner": "ADR046-zone-control-011, ADR046-zone-control-017, ADR046-zone-control-018", - "currentSource": "main `a1cc0b2d`: `packages/d2b-gateway-runtime/src/provider_agent.rs` (`ProviderAgentError::{UnregisteredAdapter,RegistryNotAccepting,RegistrationRejected,InvalidAuditCapacity,SessionClosed,ProtocolViolation}`, `ProviderAgentAuditOutcome`, `ProviderAgentAuditEvent`, `ProviderAgentProcess::from_registry/from_registry_with/provider_type/service_names/audit_snapshot/serve`, `run_registered`, bounded in-memory audit ring, frame dispatch loop: semaphore in-flight limit, service/method routing, negative-timeout guard, `SessionClosed` termination, `ProtocolViolation` audit + terminate — lines 31–452; tests `standalone_entrypoint_fails_without_registration`, `audit_capacity_is_bounded` — lines 454–486); `packages/d2b-contracts/src/provider_registry_v2.rs` (`ProviderRegistryV2` wire contract, `ProviderRegistryEntryV2::validate` with provider-id derivation rule, schema fingerprint, scope-digest, generation exactness, `TrustedFirstPartyInProcess` placement requirement, `ProviderIntentId` label rules `max 128 bytes`, `MAX_PROVIDER_REGISTRY_ENTRIES`, `MAX_PROVIDER_MAPPING_IDS=64`, `ProviderBindingV2` non-exhaustive + `UnsupportedProviderBindingV2` fallback, `ProviderRegistryV2::validate` sort/unique/count checks — lines 23–566; tests `validates_closed_local_runtime_mapping`, `validates_closed_local_observability_mapping`, `serializes_declared_mapping_axes_as_closed_variants`, `rejects_duplicate_or_unbounded_mapping_ids`, `local_storage_binding_realm_must_match_descriptor_placement`, `rejects_generation_and_exact_identity_mismatches`, `contradictory_binding_realm_json_is_unrepresentable`, `unknown_binding_axis_remains_rejected_on_the_wire`, `identity_mismatch_messages_name_the_failed_contract`, `accepts_explicit_empty_registry` — lines 722–1044)", - "reuseSource": null, - "reuseAction": "copy + adapt", - "destination": "`packages/d2b-provider/src/agent.rs` (v3 provider agent dispatch); `packages/d2b-contracts/src/v3/provider_registry.rs` (v3 provider registry wire contract)", - "detailedDesign": "**Selected**: `ProviderAgentProcess::serve` dispatch loop with semaphore in-flight limit; unsupported-service/method → ttrpc error; negative-timeout rejection; `SessionClosed` clean termination; `ProtocolViolation` audit-and-terminate path; bounded audit ring; `GeneratedProviderServiceServer` single-service-per-agent requirement; `ProviderRegistryV2` entry validation: provider-id derivation, schema fingerprint, scope digest, generation exactness; `MAX_PROVIDER_MAPPING_IDS=64` → Provider component mapping bound; `ProviderBindingV2` non-exhaustive + explicit `UnsupportedProviderBindingV2` fallback (never panics on unknown axis); `ProviderIntentId` `max 128 bytes` label rules → Provider component `spec.intentRef`; all 10 `provider_registry_v2.rs` tests ported. **Excluded ADR45 assumptions**: `aca_workload.rs` (`AcaGatewayWorkload`): ADR45 ACA external provider adapter, excluded entirely. `waypipe_display.rs` (`WaypipeDisplayProvider`): ADR45 display provider, excluded. `ProviderRegistryV2.registry_generation` / `configuration_fingerprint` bind to ADR45 bundle generation; v3 Provider resource version is tracked in redb store, not a JSON bundle. `run()` binary entrypoint uses the fixed `d2b-provider-agent` command; v3 provider processes use normal Zone runtime Process launch. `TrustedFirstPartyInProcess` is the only placement in v2; v3 Provider resources extend to `HostLocal`/`GatewayVm` etc.", - "integration": "Zone runtime spawns each Provider component Process via normal Process launch; Process binary calls `ProviderAgentProcess::from_registry` then `serve()` on established ComponentSession; on `SessionClosed` the process exits and Zone runtime observes `status.phase` transition", - "dataMigration": "Not applicable; new implementation", - "validation": "`provider-agent-dispatch-unsupported-service-returns-ttrpc-error`, `provider-agent-negative-timeout-rejected`, `provider-agent-session-closed-terminates-serve-loop`, `provider-agent-audit-ring-capacity-bounded` (ported from `audit_capacity_is_bounded`), `provider-registry-entry-fingerprint-generation-exact` (ported from `rejects_generation_and_exact_identity_mismatches`), `provider-registry-unknown-axis-fallback-non-exhaustive` (ported from `unknown_binding_axis_remains_rejected_on_the_wire`), `provider-registry-duplicate-ids-rejected` (ported from `rejects_duplicate_or_unbounded_mapping_ids`)", - "removalProof": "Not applicable; new implementation" - }, - { - "workItemId": "ADR046-provider-device-gpu-01", - "specId": "ADR-046-provider-device-gpu", - "specPath": "docs/specs/providers/ADR-046-provider-device-gpu.md", - "dependencyOwner": "`ADR046-resources-device` accepted; `ADR046-provider-model-and-packaging` accepted; workspace root must add crate", - "currentSource": "No v3 source; `packages/d2b-host/src/gpu_argv.rs` and `packages/d2b-host/src/video_argv.rs` (implemented-and-reachable) provide argv generators", - "reuseSource": "`packages/d2b-host/src/gpu_argv.rs` (baseline `b5ddbed`), `packages/d2b-host/src/video_argv.rs` (baseline `b5ddbed`)", - "reuseAction": "`extract` both argv files into `d2b-provider-device-gpu/src/argv.rs` as re-exports; do not copy logic", - "destination": "`packages/d2b-provider-device-gpu/` with `src/`, `tests/`, `integration/`, `README.md`; add to workspace `Cargo.toml` members list (alphanumerically sorted)", - "detailedDesign": "Crate scaffold: `Cargo.toml` with `d2b-host`, `d2b-contracts`, `d2b-provider-toolkit`, `d2b-core` dependencies; `lib.rs` exporting controller binary entry points; `error.rs` with `DeviceGpuError` closed-set enum; placeholder `controller.rs`", - "integration": "Workspace policy test must pass; crate must build; `src/`, `tests/`, `integration/`, `README.md` must exist", - "dataMigration": "None — full d2b 3.0 reset; no prior state to migrate", - "validation": "`cargo build -p d2b-provider-device-gpu`; workspace policy crate-layout check passes", - "removalProof": "N/A (new crate)" - }, - { - "workItemId": "ADR046-provider-device-gpu-02", - "specId": "ADR-046-provider-device-gpu", - "specPath": "docs/specs/providers/ADR-046-provider-device-gpu.md", - "dependencyOwner": "ADR046-provider-device-gpu-01; `ADR-046-resource-reconciliation` implementation present; Provider toolkit `ResourceClient` available", - "currentSource": "`packages/d2bd/src/usbip_state_machine.rs` (implemented-and-reachable) as reconcile loop pattern reference. GPU/video reconcile state is `ADR-only`.", - "reuseSource": "Pattern only: `packages/d2bd/src/usbip_state_machine.rs` (baseline). No code copy.", - "reuseAction": "`adapt` — implement the five-trigger reconcile loop using Provider toolkit async reconciler", - "destination": "`packages/d2b-provider-device-gpu/src/controller.rs`", - "detailedDesign": "Five triggers: `spec-generation-changed`, `deletion-requested`, `dependency-changed`, `scheduled-observe`, `owned-resource-changed`. Each trigger handler writes optimistic `ResourceMutationBatch`. Status writer in `status.rs`. Async watch task + per-resource reconcile tasks. Independent resources in parallel.", - "integration": "Resource API (ADR046 store) must be present; fake ResourceClient available from Provider toolkit; `tests/combined_reconcile.rs` validates trigger dispatch", - "dataMigration": "None — full d2b 3.0 reset; no prior state to migrate", - "validation": "`cargo test -p d2b-provider-device-gpu --test combined_reconcile`; all five trigger handlers must reach their expected output state", - "removalProof": "Current ProcessRole::Gpu/Video/GpuRenderNode retained until this test passes; see ProcessRole disposition table" - }, - { - "workItemId": "ADR046-provider-device-gpu-03", - "specId": "ADR-046-provider-device-gpu", - "specPath": "docs/specs/providers/ADR-046-provider-device-gpu.md", - "dependencyOwner": "ADR046-provider-device-gpu-02", - "currentSource": "`nixos-modules/assertions.nix` x86_64-linux guard; `packages/d2b-core/src/processes.rs` ProcessRole::Gpu/GpuRenderNode; no existing sysfs probe module", - "reuseSource": "None; probe is `ADR-only`", - "reuseAction": "`new`", - "destination": "`packages/d2b-provider-device-gpu/src/probe.rs`", - "detailedDesign": "Call `GpuEffectPort::probe_drm_device(selector)` on each `scheduled-observe` trigger; the effect port resolves device presence against the trusted device table and returns a presence/health result without exposing raw sysfs or device paths to the controller. Three-strike failure counter; `observe_interval_secs` (10–60, default 30); emit `DevicePresent` condition and update `lastProbedAt`.", - "integration": "`scheduled-observe` trigger from reconcile loop calls `probe::check_drm_device`", - "dataMigration": "None — full d2b 3.0 reset; no prior state to migrate", - "validation": "`tests/conformance.rs` contains probe-mock path; `cargo test` passes", - "removalProof": "N/A (new module)" - }, - { - "workItemId": "ADR046-provider-device-gpu-04", - "specId": "ADR-046-provider-device-gpu", - "specPath": "docs/specs/providers/ADR-046-provider-device-gpu.md", - "dependencyOwner": "ADR046-provider-device-gpu-02", - "currentSource": "`packages/d2b-core/src/bundle_resolver.rs` `validate_graphics_vm_invariants` (assertion guard) — `ADR-only` for resource-level arbitration", - "reuseSource": "None", - "reuseAction": "`new`", - "destination": "`packages/d2b-provider-device-gpu/src/arbitration.rs`", - "detailedDesign": "On `spec-generation-changed` and each new claim: check `arbitration` vs `maxConcurrentClaims` vs current `holderRefs` length. Exclusive: reject any second claim with `ClaimConflict` condition, set requesting Device phase `Degraded`. Shared render-node: accept up to `maxConcurrentClaims`. Admission: `shared + renderNodeOnly=false` fails with `shared-arbitration-requires-render-node-only`.", - "integration": "Tested by `tests/arbitration_conflict.rs`; integration fixture `render_node_shared/`", - "dataMigration": "None — full d2b 3.0 reset; no prior state to migrate", - "validation": "`cargo test -p d2b-provider-device-gpu --test arbitration_conflict`; `cargo test -p d2b-provider-device-gpu --test render_node_enforcement`", - "removalProof": "N/A (new module)" - }, - { - "workItemId": "ADR046-provider-device-gpu-05", - "specId": "ADR-046-provider-device-gpu", - "specPath": "docs/specs/providers/ADR-046-provider-device-gpu.md", - "dependencyOwner": "ADR046-provider-device-gpu-02; `ADR046-components-processes-and-sandbox` (Provider/system-minijail present and able to handle Process resources with `gpu-worker`/`render-node-worker` templates)", - "currentSource": "`packages/d2b-host/src/gpu_argv.rs` (implemented-and-reachable); `packages/d2b-core/src/bundle_resolver.rs` lines 1888–1894 (device token set); `packages/d2b-core/src/processes.rs` `ProcessRole::Gpu`, `ProcessRole::GpuRenderNode` (implemented-and-reachable); `nixos-modules/minijail-profiles.nix` gpu/gpu-render-node profiles (implemented-and-reachable)", - "reuseSource": "`packages/d2b-host/src/gpu_argv.rs` (baseline `b5ddbed`): `GpuArgvInput`, `GpuParams`, `GpuContextType`, `GpuDisplayConfig`; `packages/d2b-core/src/bundle_resolver.rs` device token constant comment", - "reuseAction": "`extract` argv builder logic into `argv.rs` as re-export from `d2b-host` (used by Provider/system-minijail at LaunchTicket resolution time; the signed component descriptor is static and is not rewritten per Device); `adapt` device allowlist token set from `bundle_resolver.rs` into `worker_gpu.rs` `GPU_DEVICE_ALLOWLIST` constant for `deviceUsage` population", - "destination": "`packages/d2b-provider-device-gpu/src/worker_gpu.rs`", - "detailedDesign": "Build and commit `Process` resource record with `template: gpu-worker` or `template: render-node-worker`; set `sandbox.seccompClass` (`w1-gpu` or `w1-gpu-render-node`), `sandbox.userNamespace: {mappingClass: process-principal-root}` (uid/gid resolved privately by core from signed worker template — controller does NOT write numeric values), `sandbox.namespaceClasses`, `sandbox.capabilityClasses=[]`, `sandbox.startRoot=false`; set `deviceUsage[{deviceRef,access,purpose}]`, `networkUsage: null`, `endpoints[{name,transport,purpose}]`, `budget` (including `pids` and `fds` bounded limits), `readiness` (with `class`, `initialDelay`, `timeout`, `failureThreshold`, `successThreshold`), and `restartPolicy` (with `class`, `backoffBase`, `backoffMax`, `backoffMultiplier`, `maxRestarts`, `resetAfter`). Provider/system-minijail validates and resolves the LaunchTicket and sends effect requests via `MinijailProcessEffectPort`; the core EffectPort adapter routes them to the **privileged broker** which performs `SpawnRunner`, `OpenDevice`, `clone3`, `uid_map`/`gid_map` writes, and fd transfer — the device-gpu controller does not have execution authority or fd access. `crossDomainTrusted` gating: the signed descriptor is static; `crossDomainTrusted` is projected from the Device setting into the LaunchTicket by Provider/system-minijail, which omits `GpuContextType::CrossDomain` from runtime argv when false.", - "integration": "`integration/gpu_worker_start/`; `integration/render_node_shared/`; `packages/d2b-contract-tests/tests/minijail_gpu.rs` (reused existing test)", - "dataMigration": "None — full d2b 3.0 reset; no prior state to migrate", - "validation": "`cargo test -p d2b-provider-device-gpu`; `cargo test -p d2b-contract-tests --test minijail_gpu` continues to pass", - "removalProof": "`ProcessRole::Gpu` and `ProcessRole::GpuRenderNode` removed from `processes.rs` only after both integration tests pass and the ProcessRole disposition contract test confirms zero remaining references" - }, - { - "workItemId": "ADR046-provider-device-gpu-06", - "specId": "ADR-046-provider-device-gpu", - "specPath": "docs/specs/providers/ADR-046-provider-device-gpu.md", - "dependencyOwner": "ADR046-provider-device-gpu-05 (video depends on GPU Process being Ready)", - "currentSource": "`packages/d2b-host/src/video_argv.rs` (implemented-and-reachable): `VideoArgvInput`, `VideoBackend`, `wire_contract_snapshot()`; `packages/d2b-contract-tests/tests/video_binary_contract.rs` (implemented-and-reachable); `packages/d2b-contract-tests/tests/minijail_swtpm_video.rs` video section (implemented-and-reachable); `nixos-modules/minijail-profiles.nix` video profile (implemented-and-reachable)", - "reuseSource": "`packages/d2b-host/src/video_argv.rs` (baseline `b5ddbed`): argv generator, wire-contract constants, `wire_contract_snapshot()`", - "reuseAction": "`extract` argv generator (re-export from `argv.rs`); `copy-unchanged` wire-contract constants into `tests/wire_constant_snapshot.rs` golden comparison", - "destination": "`packages/d2b-provider-device-gpu/src/worker_video.rs`, `tests/wire_constant_snapshot.rs`", - "detailedDesign": "Controller creates `Process/device--video` only after `GpuWorkerReady=True`. `worker_video.rs` builds `VideoArgvInput` from resolved device spec and signed descriptor binary path. Validates `wire_contract_snapshot()` matches committed golden at startup; fails closed if mismatch (error `device-wire-contract-mismatch`). NVIDIA device gating: include `nvidia-ctl`, `nvidia-device`, `nvidia-uvm` tokens in `deviceUsage[]` entries only when `videoNvidiaDecode=true`; the **privileged broker** opens the fds when executing the effect request from the core EffectPort adapter. Distinct allocator-assigned principal enforced by LaunchTicket (internal invariant; not expressed in the resource spec); `template: video-worker` descriptor declares no Wayland/audio endpoint capability. `sandbox.seccompClass: w1-video`; `sandbox.namespaceClasses` includes `pid`; `userNamespace: null` (explicit, tested invariant).", - "integration": "`integration/video_dependency/`; `packages/d2b-contract-tests/tests/video_binary_contract.rs` (reused); `packages/d2b-contract-tests/tests/minijail_swtpm_video.rs` video section (reused)", - "dataMigration": "None — full d2b 3.0 reset; no prior state to migrate", - "validation": "`cargo test -p d2b-provider-device-gpu --test video_dependency`; `cargo test -p d2b-provider-device-gpu --test wire_constant_snapshot`; `cargo test -p d2b-contract-tests --test video_binary_contract` continues to pass", - "removalProof": "`ProcessRole::Video` removed from `processes.rs` only after `integration/video_dependency/` passes and the video Process reaches `Ready` in a live Zone" - }, - { - "workItemId": "ADR046-provider-device-gpu-07", - "specId": "ADR-046-provider-device-gpu", - "specPath": "docs/specs/providers/ADR-046-provider-device-gpu.md", - "dependencyOwner": "ADR046-provider-device-gpu-01; ADR 0046 Zone Nix emitter wired; `ADR-046-nix-configuration` Nix emitter present", - "currentSource": "`nixos-modules/options-realms-workloads.nix` `d2b.vms..graphics.*` options (generated-or-eval-contract); `nixos-modules/assertions.nix` graphics assertions; `nixos-modules/components/graphics.nix` (host-side); `nixos-modules/components/video/guest.nix` (guest-side)", - "reuseSource": "Settings schema field names/defaults/bounds from `nixos-modules/options-realms-workloads.nix` options documentation", - "reuseAction": "`adapt` — map old `d2b.vms..graphics.*` fields to `d2b.zones..resources.` Device spec settings fields; add eval assertions", - "destination": "`nixos-modules/assertions.nix` (new GPU Device eval assertions); `tests/unit/nix/cases/device-gpu-eval.nix` (new Nix eval case); committed settings schema `docs/reference/schemas/v3/providers/device-gpu.settings.json`", - "detailedDesign": "Eval assertions as documented in § Nix configuration / Eval-time assertions. Canonical JSON golden as documented. Settings schema drift gate via `make test-drift`. `d2b.vms..graphics.*` options are deprecated (emit deprecation warning) until a transition generation removes them; they are not removed in the same commit that adds the Device spec option.", - "integration": "`nix flake check`; `tests/unit/nix/cases/device-gpu-eval.nix`; `make test-drift`", - "dataMigration": "Consumer config migration guide: replace `d2b.vms..graphics.enable = true` with a Device resource declaration. Old options emit deprecation warnings, not hard failures, during the transition window.", - "validation": "`nix-unit tests/unit/nix/cases/device-gpu-eval.nix`; `make test-drift`; `make test-flake`", - "removalProof": "`d2b.vms..graphics.*` options removed only after migration guide ships and the deprecation warning has been live for one minor release" - }, - { - "workItemId": "ADR046-provider-device-gpu-08", - "specId": "ADR-046-provider-device-gpu", - "specPath": "docs/specs/providers/ADR-046-provider-device-gpu.md", - "dependencyOwner": "ADR046-provider-device-gpu-01; D087 status-first state model present in the foundational ADR-046 specs", - "currentSource": "None — net-new v3 work; no pre-ADR45 baseline equivalent", - "reuseSource": "None", - "reuseAction": "`new` — status-first state assertions in the component descriptor and controller tests", - "destination": "`packages/d2b-provider-device-gpu/` component descriptor; controller/status tests", - "detailedDesign": "Do **not** declare a controller Provider state Volume. The device-gpu component descriptor declares an empty ProviderStateSet; controller and worker Process templates contain no `/state` mount. Bounded non-secret operational state is published to Device/Provider status and the core Operation ledger. GPU has no Device-payload Volume; render-node access remains a Device attachment resolved by LaunchTicket and broker policy.", - "integration": "`tests/status_state.rs`; `integration/gpu_worker_start/` verifies controller startup is gated by resource dependencies and status writer authority, not by a Provider state Volume", - "dataMigration": "None — no Provider state Volume exists to migrate.", - "validation": "`cargo test -p d2b-provider-device-gpu --test status_state`; component descriptor golden has no Provider state Volume declaration; controller Process template has no `/state` mount; ProviderStateSet query is empty; status/core-ledger fields carry bounded operational observations", - "removalProof": "`StorageRoot`/`StoragePathSpec` lifecycle tracking entries for GPU/video roles in `d2b-core/src/storage.rs` removed after Device/Process status-first lifecycle and restart-adoption integration tests pass in a live Zone" - }, - { - "workItemId": "ADR046-provider-device-gpu-09", - "specId": "ADR-046-provider-device-gpu", - "specPath": "docs/specs/providers/ADR-046-provider-device-gpu.md", - "dependencyOwner": "ADR046-provider-device-gpu-01", - "currentSource": "None; new file", - "reuseSource": "None", - "reuseAction": "`new`", - "destination": "`packages/d2b-provider-device-gpu/README.md`", - "detailedDesign": "Must include: Provider identity, supported ResourceTypes, controller/service/worker binary descriptions, placement (Host, system domain), dependencies (system-minijail, volume-local, observability-otel), RBAC roles, security model summary, state/telemetry contract, build command (`cargo build -p d2b-provider-device-gpu`), test commands (`cargo test -p d2b-provider-device-gpu`), integration command (`make test-integration`), hardware test note (see `integration/README.md`), standalone-repository consumption stub.", - "integration": "Workspace policy checks for `README.md` presence", - "dataMigration": "None — full d2b 3.0 reset; no prior state to migrate", - "validation": "`make test-policy` (workspace crate layout policy check)", - "removalProof": "N/A (new file)" - }, - { - "workItemId": "ADR046-pstate-001", - "specId": "ADR-046-provider-state", - "specPath": "docs/specs/ADR-046-provider-state.md", - "dependencyOwner": "ADR046-primitives-001; v3 contracts owner", - "currentSource": "`packages/d2b-core/src/storage.rs` (`StoragePathSpec` with `scope: ContractId` currently keyed by `ProcessRole`/Workload; `SensitivityClass`; `StorageLifecycle`; `StorageRootClass`); `packages/d2b-state/src/atomic.rs` (main, 6faa5256; absent from v3 baseline)", - "reuseSource": null, - "reuseAction": "extract and adapt", - "destination": "`packages/d2b-contracts/src/v3/volume_state.rs`", - "detailedDesign": "`VolumeStateSchema` struct (`schemaId`, `schemaVersion`, `schemaDigest`, `migrationPolicy`); `PersistenceClass` and `SensitivityClass` enums; `VolumeStateStatus` extension (`stateSchemaPhase`, `installedSchemaVersion`, `markerStatus`, `sealingStatus`, `quotaUsage`, `lastMigrationAt`); `StateEnvelope` (replaces v2 `StateEnvelope`); canonical JSON serde and digest helpers", - "integration": "Volume spec and status structs embed these types; Provider descriptor component stateNamespace declaration uses the same types", - "dataMigration": "Full v3 reset; no v2 state schema import", - "validation": "Schema golden vectors; phase/reason round-trip; StateEnvelope digest tests", - "removalProof": "`d2b-core/src/storage.rs` StoragePathSpec/SensitivityClass removed only after all Provider descriptor consumers are on v3 Volume spec" - }, - { - "workItemId": "ADR046-pstate-002", - "specId": "ADR-046-provider-state", - "specPath": "docs/specs/ADR-046-provider-state.md", - "dependencyOwner": "ADR046-pstate-001; Provider contracts owner", - "currentSource": "`packages/d2b-core/src/processes.rs` (`VmProcessDag`, `ProcessNode`, `ProcessRole`: each current `ProcessRole` variant maps to a `Process` or `EphemeralProcess` resource under its owning Provider; `ProcessRole::Swtpm`/`Virtiofsd`/`CloudHypervisorRunner` → Process resources under `Provider/device-tpm`/`Provider/volume-virtiofs`/`Provider/runtime-cloud-hypervisor`); Provider descriptor component model from ADR046-provider-001", - "reuseSource": null, - "reuseAction": "adapt", - "destination": "`packages/d2b-contracts/src/v3/provider.rs` (component descriptor `stateNamespaces` field)", - "detailedDesign": "Add `stateNamespaces: Vec` to the component descriptor (zero or more entries; a component declares an entry only when a payload passes the storage-need test; stateless components declare none); each entry includes `id`, `kind` (always `state`), `schemaId` (non-null), `schemaVersion`, `schemaDigest`, `persistenceClass` (must be `persistent`; `ephemeral`/`cache` rejected), `sensitivityClass`, `migrationPolicy`, `quotaBytes` (nonzero; minimum 4096), `storageNeed` (`secret` \\| `large-binary` \\| `private-unsafe-for-status` \\| `revision-unsuitable`), `sealingRequired`, `placementMode` (`guest-local` or `host-backed-guest` for Guest-targeted; omitted for Host-targeted), `hostCustodyPermitted` (required `true` for `host-backed-guest`; absent/false for `guest-local`), and `views`; there is no empty-payload (`schemaId: null`) namespace", - "integration": "Provider package build emits component descriptors with state namespaces; Provider controller creates Volumes from descriptors at install time", - "dataMigration": "Full reset", - "validation": "Descriptor schema golden vectors; descriptor-Volume consistency property test; stateless-component-declares-no-namespace round-trip; storage-need justification enforcement (namespace whose payload is derivable from spec/status/core ledger/external observation → `component-state-not-justified`); `kind != state` → `component-kind-invalid`; `persistenceClass: ephemeral` → `component-persistence-class-forbidden`; `quotaBytes: 0` or `1024` → `component-quota-too-small`; base `quota.maxBytes == quotaBytes` and `quota.maxInodes > 0`; Guest-targeted with `placementMode: guest-local` → source.executionRef=Guest; Guest-targeted with `host-backed-guest` + `hostCustodyPermitted: true` → source on Host, Export created; `host-backed-guest` without `hostCustodyPermitted: true` → `placement-host-custody-violation`; credential/audit schema with `host-backed-guest` → `guest-local-required`; `placementMode` change → descriptor version increment enforced", - "removalProof": "Not applicable (new)" - }, - { - "workItemId": "ADR046-pstate-003", - "specId": "ADR-046-provider-state", - "specPath": "docs/specs/ADR-046-provider-state.md", - "dependencyOwner": "ADR046-pstate-001; volume-local Provider owner", - "currentSource": "`packages/d2b-state/src/atomic.rs`, `path.rs`, `lock.rs` (main, 6faa5256); `packages/d2b-priv-broker/src/ops/swtpm_dir.rs` (marker algorithm, v3 baseline)", - "reuseSource": null, - "reuseAction": "copy-unchanged (path.rs) / adapt (atomic.rs, lock.rs) / adapt (swtpm_dir.rs marker algorithm)", - "destination": "`packages/d2b-provider-volume-local/` (new crate, full scaffold required): `src/{atomic.rs, path.rs, lock.rs, marker.rs, effect_port.rs}`; `tests/volume_local.rs` (marker missing/replaced/mismatch, domain-isolation rejection, quota enforcement); `integration/volume_local.rs` (real Host filesystem provision, broker-maintained marker check, domain-isolation rejection cross-process); `README.md`", - "detailedDesign": "Anchored Volume root provision, identity marker write/check, quota soft-check on write, domain-isolation validation, fd-relative layout creation/repair/cleanup, broker-maintained marker root protocol; layout `ownerRef`/`groupRef` must reference a Nix-preprovisioned User principal or bounded system pool — Volume admission rejects runtime-created principals; `VolumeEffectPort` returns opaque IDs and named view dirfds only — no raw host path returned by any EffectPort operation; volume-local must support `source.executionRef: Guest/` for `guest-local` placement (controller running inside the Guest): when executing in a Guest domain, volume-local may not create, read, or hold dirfds/paths for Volumes sourced in another domain; `host-backed-guest` placement creates a `virtiofs.d2bus.org.Export` child per attachment entry and validates `hostCustodyPermitted: true` in the signed descriptor", - "integration": "`d2b-priv-broker` calls `volume_local::marker::provision_marker` at broker-maintained Volume creation; `d2b-provider-volume-local` controller calls `marker::verify_marker` on every daemon restart via reconcile startup relist", - "dataMigration": "New marker written for each Volume at v3 first-boot; TPM marker path adapted from current swtpm-markers root", - "validation": "Marker missing/replaced/mismatch tests; domain-isolation rejection tests; quota enforcement tests; crash at every provision step", - "removalProof": "`swtpm_dir.rs` marker implementation retired only after device-tpm Provider Volume is live and marker-check parity is confirmed" - }, - { - "workItemId": "ADR046-pstate-004", - "specId": "ADR-046-provider-state", - "specPath": "docs/specs/ADR-046-provider-state.md", - "dependencyOwner": "ADR046-reconcile-001, ADR046-pstate-003; volume-local Provider and controller-toolkit owners", - "currentSource": "`packages/d2b-core/src/storage_lifecycle.rs` (`StorageLifecycleReport` issue detection); `packages/d2b-state/src/atomic.rs` (main, 6faa5256)", - "reuseSource": null, - "reuseAction": "adapt", - "destination": "`packages/d2b-provider-volume-local/src/migration.rs`; `packages/d2b-provider-volume-local/tests/migration_unit.rs` (hermetic staging Volume and prepare/commit/rollback); `packages/d2b-provider-volume-local/integration/migration.rs` (real Host crash-injection at each migration step, N-Volume cross-component coordination); `packages/d2b-controller-toolkit/src/state_migration.rs`", - "detailedDesign": "Pre-launch migration EphemeralProcess template, staging Volume create/destroy lifecycle, prepare/commit/rollback protocol implementation, roll-forward on restart detection, migration idempotency", - "integration": "Provider controller's reconcile handler calls toolkit `state_migration::plan` and dispatches EphemeralProcess via ResourceClient; volume-local Provider reports `stateSchemaPhase` transitions", - "dataMigration": "None (new protocol)", - "validation": "Migration with and without crash at each step; rollback after failed EphemeralProcess; roll-forward after interrupted commit; cross-component N-Volume coordination", - "removalProof": "`StorageLifecycleReport` and storage contract validation in `d2b-core` removed only after v3 Volume migration path is live" - }, - { - "workItemId": "ADR046-pstate-005", - "specId": "ADR-046-provider-state", - "specPath": "docs/specs/ADR-046-provider-state.md", - "dependencyOwner": "ADR046-pstate-003; volume-local Provider and Credential Provider owners", - "currentSource": "`packages/d2b-state/src/lease.rs` (main, 6faa5256); Credential ResourceType from ADR046-primitives-001", - "reuseSource": null, - "reuseAction": "adapt", - "destination": "`packages/d2b-provider-volume-local/src/sealing.rs`; `packages/d2b-provider-volume-local/tests/sealing_unit.rs` (hermetic seal/rotation state machine); `packages/d2b-provider-volume-local/integration/sealing.rs` (real Credential lease flow, interrupted key rotation with live Credential Provider)", - "detailedDesign": "Envelope encryption on write using Credential lease key material; key rotation EphemeralProcess re-encrypt; no raw key on disk; `sealingStatus` transitions; `rotation-failed` fail-safe", - "integration": "Volume controller reads Credential status/lease before provisioning; sealing wraps `StateEnvelope` writes in atomic.rs", - "dataMigration": "Sealed Volumes are new (no existing sealing to migrate)", - "validation": "Seal/read/rotation tests; rotation interrupted at commit; credential revoked during runtime", - "removalProof": "Not applicable (new)" - }, - { - "workItemId": "ADR046-pstate-006", - "specId": "ADR-046-provider-state", - "specPath": "docs/specs/ADR-046-provider-state.md", - "dependencyOwner": "ADR046-pstate-003, ADR046-pstate-004; volume-local Provider and snapshot toolkit owners", - "currentSource": "`packages/d2b-state/src/atomic.rs` (main, 6faa5256): `AtomicFilesystem` read snapshot; no existing snapshot infrastructure in v3", - "reuseSource": null, - "reuseAction": "adapt", - "destination": "`packages/d2b-provider-volume-local/src/snapshot.rs`; `packages/d2b-provider-volume-local/tests/snapshot_unit.rs` (hermetic `snapshotPolicy` enforcement, retention count and TTL logic); `packages/d2b-provider-volume-local/integration/snapshot.rs` (real Host filesystem snapshot creation, retention expiry, pre-migration auto-snapshot with interrupted migration)", - "detailedDesign": "Snapshot EphemeralProcess; bounded `.snapshots/` sub-tree; `snapshotPolicy` enforcement; retention count and TTL cleanup; snapshot status tracking in Volume status", - "integration": "Provider controller creates snapshot EphemeralProcess before migration and relocation based on `snapshotPolicy.triggerOnMigration` and `triggerOnRelocation`; status populated via Volume status update", - "dataMigration": "No existing snapshots; new infrastructure only", - "validation": "Create/read/expire/retention-limit tests; snapshot before interrupted migration; snapshot list in Volume status", - "removalProof": "Not applicable (new)" - }, - { - "workItemId": "ADR046-pstate-007", - "specId": "ADR-046-provider-state", - "specPath": "docs/specs/ADR-046-provider-state.md", - "dependencyOwner": "ADR046-pstate-003; volume-local Provider owner", - "currentSource": "`packages/d2b-priv-broker/src/ops/swtpm_dir.rs` (v3, marker pattern); `packages/d2b-state/src/path.rs` (main)", - "reuseSource": null, - "reuseAction": "adapt", - "destination": "`packages/d2b-provider-volume-local/src/relocation.rs`; `packages/d2b-provider-volume-local/tests/relocation_unit.rs` (hermetic finalizer set/clear, commit/failure state machine); `packages/d2b-provider-volume-local/integration/relocation.rs` (real Host-to-Host anchored file copy, crash at copy midpoint, virtiofs source re-point after successful relocation)", - "detailedDesign": "Relocation EphemeralProcess; source finalizer; anchored copy; source Volume (volume-local) relocation for components with Guest attachment (the attachment Volume backed by volume-virtiofs is re-pointed to the new source after copy; see `ADR-046-primitive-resource-composition` Volume attachment spec); commit/failure handling", - "integration": "Provider controller adds `Relocating` finalizer before creating relocation EphemeralProcess; removes finalizer after successful destination Volume activation", - "dataMigration": "Not applicable", - "validation": "Relocation with crash at copy midpoint; failed relocation source preservation; virtiofs source relocation test", - "removalProof": "Not applicable (new)" - }, - { - "workItemId": "ADR046-pstate-008", - "specId": "ADR-046-provider-state", - "specPath": "docs/specs/ADR-046-provider-state.md", - "dependencyOwner": "ADR046-pstate-001; Zone audit stream, OTEL provider owners", - "currentSource": "`packages/d2b-state/src/audit.rs` (main, 6faa5256): `AuditAppender`, `AuditRecordInput`, `SegmentBuilder`, `checkpoint`, `decide_retention`, `detect_gap`, `read_audit_segment`; Zone audit stream interface from ADR046-bus contracts (see ADR-046-componentsession-and-bus); OTEL cardinality model from `packages/d2b-provider-observability-local/src/` (main, a1cc0b2d)", - "reuseSource": null, - "reuseAction": "adapt", - "destination": "`packages/d2b-provider-volume-local/src/audit.rs`; `packages/d2b-provider-volume-local/src/otel.rs`; `packages/d2b-provider-volume-local/tests/audit_unit.rs` (hermetic audit golden records, OTEL cardinality label tests)", - "detailedDesign": "Volume-state audit event types and Zone audit emission; OTEL metric definitions with closed cardinality label sets", - "integration": "Every state lifecycle transition calls `audit::emit_volume_event`; OTEL metrics exported via `observability-otel` Provider", - "dataMigration": "None — full d2b 3.0 reset; no prior state to migrate", - "validation": "Audit event golden records; no content/path/credential in audit payload; OTEL cardinality label tests", - "removalProof": "Not applicable" - }, - { - "workItemId": "ADR046-pstate-009", - "specId": "ADR-046-provider-state", - "specPath": "docs/specs/ADR-046-provider-state.md", - "dependencyOwner": "ADR046-pstate-001 through ADR046-pstate-008; integration test owner", - "currentSource": "`packages/d2b-state/tests/state.rs`, `async_state.rs` (main, 6faa5256): atomic, lock, quarantine, audit, lease tests; `packages/d2b-provider-toolkit/src/fixture.rs` (main, a1cc0b2d): `Fixture`, `FakeProvider`, `DeterministicClock`, `ProviderValues`, `Redacted`, `Secret`, `sample_lease_request`", - "reuseSource": null, - "reuseAction": "adapt", - "destination": "`packages/d2b-provider-volume-local/tests/state.rs` (ported hermetic atomic/lock/quarantine/lease tests); `packages/d2b-provider-volume-local/tests/migration.rs` (ported migration fault-injection, cross-component N-Volume coordination); `packages/d2b-provider-volume-local/integration/provider_state.rs` (end-to-end: live daemon, real Host Volume mount, cross-process worker subview, full audit stream); `packages/d2b-provider-volume-local/integration/audit.rs` (live Zone audit stream emission and OTEL export)", - "detailedDesign": "Port all d2b-state integration tests replacing ADR 0045 contract setup with v3 Volume/StateEnvelope; add provider-state-specific migration, marker, quota, sealing, relocation, snapshot, incident-hold, and unclaimed-GC tests; include cross-component N-Volume coordination test", - "integration": "Tests run against the real volume-local Provider over a fake Zone runtime (no live daemon required) using the standard controller-toolkit fake clients", - "dataMigration": "None — full d2b 3.0 reset; no prior state to migrate", - "validation": "All ported tests pass under v3 contracts; test coverage includes every fault-injection scenario listed in d2b-state/tests/state.rs plus new provider-state cases; stateless-component-declares-no-Volume test passes; shared-Volume attempt rejected; `guest-local` Volume creation inside Guest domain (source.executionRef=Guest, no Export created, Host volume-local holds no dirfd/path); `host-backed-guest` Volume creation (source on Host, Export created, Export reaches Ready, Guest Process mounts source Volume view); `host-backed-guest` without `hostCustodyPermitted: true` → `placement-host-custody-violation`; credential/audit schema with `host-backed-guest` → `guest-local-required`; cross-domain isolation: Guest-local volume-local does not create or observe Host-domain Volumes", - "removalProof": "`d2b-state` crate retired from workspace only after every caller migrates to v3 Volume state helpers and all ported tests pass" - }, - { - "workItemId": "ADR046-pstate-010", - "specId": "ADR-046-provider-state", - "specPath": "docs/specs/ADR-046-provider-state.md", - "dependencyOwner": "ADR046-pstate-001; Zone resource bundle owner, NixOS module owner, `d2b-core-controller` owner (ADR-046-core-controllers)", - "currentSource": "`nixos-modules/manifest.nix` (current `manifest.json` emitter, v3 baseline `fd5b0067`); `packages/d2b-core/src/storage.rs` (`StorageAuthority::NixModule`-owned rows); `packages/xtask/src/main.rs` (`gen-schemas` command, same baseline)", - "reuseSource": null, - "reuseAction": "adapt", - "destination": "`nixos-modules/zone-resources.nix` (per-Zone bundle emitter NixOS module); `packages/d2b-core/src/v3/zone_bundle.rs` (shared bundle DTOs: `ZoneResourceBundle`, `BundleResource`, `contentId` computation); `packages/d2b-core-controller/src/configuration.rs` (diff/apply loop, name-conflict detection, `pending-cleanup` Zone status, `maxFinalizerDurationSeconds` stall detection — NOT in `d2b-provider-volume-local`); `packages/d2b-core-controller/tests/configuration.rs` (hermetic bundle diff, absent-resource Delete dispatch, name-conflict `Degraded/name-conflict` item, integrity-failure abort); `packages/d2b-core-controller/integration/configuration.rs` (container-based generation activation with running Providers, absent-resource cleanup, rollback, finalizer-timeout stall detection)", - "detailedDesign": "Generic `d2b.zones..resources. = { type = \"…\"; spec = { …exact ResourceTypeSpec fields… }; }` attrset; `metadata.name` derived from attr key, `metadata.zone` from Zone key, `apiVersion` defaulted; `status` omitted (read-only); Nix option types for `spec.*` generated from committed `ResourceTypeSchema` for each `type`; Nix option types for `spec.config.*` generated from the Provider artifact's config schema module (resolved at eval time via `spec.artifactId` from `d2b.artifacts`); credential-ref guard (`credentialRef: true` schema fields accept only `Credential/[a-z][a-z0-9-]*`); build-phase full JSON Schema validation of rendered `spec` against Provider manifest; canonical sorted bundle emission with `contentId` (SHA-256 of sorted `resources` array); configuration service sets `metadata.managedBy: configuration` and `configurationGeneration` in the resource store when persisting activated bundle resources (not in the bundle input; user authors only `type` + `spec`); diff compares new configured set against persisted resource store entries where `managedBy: configuration` (not against the prior bundle file); name-conflict detection: `(type, name)` collision with existing `managedBy: controller` or `managedBy: api` resource → `Degraded/name-conflict` activation item, existing resource untouched, `managedBy` never seized; unchanged-spec resources receive updated `configurationGeneration` with no controller reconcile triggered; absent-resource async Delete with owner-child/finalizer ordering; `Degraded/pending-cleanup` Zone status condition; per-Zone prior bundle count retention (range 1..16, default 3; no TTL); `maxFinalizerDurationSeconds` stall detection and `Degraded/finalizer-timeout` condition (no force-clear)", - "integration": "NixOS build emits `/etc/d2b/zones//resources.json`; Zone daemon watches path via systemd path unit; reconcile loop runs generation diff on change; Provider controller receives `deletionRequestedAt` watch event when configuration-owned Provider is absent from new bundle", - "dataMigration": "`nixos-modules/manifest.nix` provider-registration and storage-authority rows superseded by `zone-resources.nix`; prior `manifest.json` format retired after Zone daemon migration to bundle format", - "validation": "All eight removed-resource cleanup tests enumerated in [Required tests for removed-resource cleanup](#required-tests-for-removed-resource-cleanup); eval credential-ref guard (test: raw value in `credentialRef` field → NixOS eval error, no bundle emitted); Provider schema conformance golden vector (test: unknown `config` key → build fails); `contentId` determinism (test: two independent builds of identical Nix inputs produce byte-identical bundles)", - "removalProof": "`nixos-modules/manifest.nix` provider-registration rows retired only after all Provider registrations use the bundle format and all consumers (broker, Zone daemon) complete bundle-format migration" - }, - { - "workItemId": "ADR046-pstate-011", - "specId": "ADR-046-provider-state", - "specPath": "docs/specs/ADR-046-provider-state.md", - "dependencyOwner": "ADR046-pstate-003; workspace policy and xtask owners", - "currentSource": "`packages/xtask/src/main.rs` (`gen-schemas` and workspace-policy checks, v3 baseline `fd5b0067`); `tests/unit/gates/drift-check.sh` (schema drift gate, same baseline); `packages/d2b-contract-tests/` (contract-test policy, frozen per AGENTS.md)", - "reuseSource": null, - "reuseAction": "extend", - "destination": "`packages/xtask/src/provider_crate_policy.rs`; `tests/unit/gates/provider-crate-layout-check.sh`", - "detailedDesign": "`cargo xtask check-provider-crate-layout` command: walks every workspace member matching `d2b-provider-*`; for each, asserts presence of `src/`, `tests/`, `integration/`, and `README.md`; asserts `integration/` contains at least one `.rs` file and a `README.md`; fails closed with a typed `missing-provider-crate-path` error listing every absent path; wired into `make test-policy` via `tests/unit/gates/provider-crate-layout-check.sh`; output is machine-readable JSON (`{ \"crate\": \"…\", \"missing\": [\"integration/\"] }` per violation)", - "integration": "`make test-policy` runs `cargo xtask check-provider-crate-layout`; GitHub CI runs `make test-policy` on every PR; `make check` includes `test-policy` as a required Layer-1 shard; workspace policy tests in `packages/d2b-contract-tests/` are extended with a static manifest check that asserts `provider_crate_layout` policy version is current", - "dataMigration": "Not applicable", - "validation": "Policy gate detects missing `src/` → error; missing `tests/` → error; missing `integration/` → error; missing `README.md` → error; empty `integration/` (no `.rs` files) → error; all four paths present and non-empty → pass; existing non-provider `d2b-*` crates not flagged; gate is idempotent across re-runs", - "removalProof": "Not applicable (permanent gate)" - }, - { - "workItemId": "ADR046-pstate-012", - "specId": "ADR-046-provider-state", - "specPath": "docs/specs/ADR-046-provider-state.md", - "dependencyOwner": "ADR046-pstate-001, ADR046-pstate-002; Zone runtime owner (`d2b-core-controller`), volume-local Provider owner", - "currentSource": "`packages/d2b-core/src/status.rs` (v3 common status/observedGeneration/conditions); Provider descriptor state-namespace declaration from ADR046-pstate-002", - "reuseSource": null, - "reuseAction": "adapt", - "destination": "`packages/d2b-core-controller/src/optional_state_admission.rs` (storage-need admission: reject a declared namespace whose payload is derivable from spec/status/core ledger/external observation with `component-state-not-justified`; only declared namespaces produce a Volume; stateless components produce none); `packages/d2b-core-controller/tests/optional_state_admission.rs` (hermetic: stateless component → no Volume; declared `storageNeed` variants accepted; unjustified namespace rejected; status-first restart revalidation — controller re-derives observed state from status/core ledger/external observation after restart and never treats a status field as authority); `packages/d2b-provider-volume-local/tests/status_bounds.rs` (hermetic: total canonical serialized status cap and provider-specific detail cap enforced; oversize status write → typed rejection; status carries no secret/path/argv/PID/unit/stream/ring content)", - "detailedDesign": "Optional state-Volume admission is a fixed step in Core ProviderDeployment: for each component, if it declares no `stateNamespaces` entry it gets no Volume; for each declared entry, verify the `storageNeed` justification (`secret` \\| `large-binary` \\| `private-unsafe-for-status` \\| `revision-unsuitable`) and reject a namespace whose payload is fully derivable from spec/status/core ledger/external observation with `component-state-not-justified`. Fixed bootstrap components (`system-core`, `system-minijail`, first `volume-local` instance) declare no state Volume and reach Ready using resource `status`, the core Operation ledger, and external observation only; there is no bootstrap-storage mechanism. Status-bound enforcement: reject a status write whose total canonical serialized size exceeds the single canonical status cap, or whose provider-specific detail exceeds the detail cap, or whose condition/count/list/map entries exceed the bounded limits, with the typed status-oversize rejection; status writes occur only on material change.", - "integration": "`d2b-core-controller` runs optional state-Volume admission before creating any declared Volume and before launching a component Process; the status-bound check is applied on every status subresource write in the resource store.", - "dataMigration": "New; no prior bootstrap artifacts to migrate", - "validation": "Stateless component → no Volume created; each `storageNeed` variant accepted with a declared Volume; unjustified namespace → `component-state-not-justified`; status-first restart: controller re-derives observed state and reverifies against external reality, never treating status as authority; oversize/over-detail/over-cardinality status write → typed rejection; status contains no secret/path/argv/PID/unit/stream/ring content", - "removalProof": "Not applicable (permanent admission + status-bound enforcement)" - }, - { - "workItemId": "ADR046-reconcile-001", - "specId": "ADR-046-resource-reconciliation", - "specPath": "docs/specs/ADR-046-resource-reconciliation.md", - "dependencyOwner": "W0/W1a; controller toolkit owner", - "currentSource": "`packages/d2b-realm-router/src/lib.rs`, `mux_session.rs`, `session_lifecycle.rs`; `packages/d2bd/src/supervisor/dag.rs`", - "reuseSource": null, - "reuseAction": "extract and adapt", - "destination": "`packages/d2b-controller-toolkit/src/lib.rs`, `runner.rs`, `queue.rs`, `context.rs`, `result.rs`", - "detailedDesign": "Async ResourceReconciler, watch receiver, coalescing, per-resource serialization, parallel tasks, retry/checkpoint/finalize; expedited priority lane and `CommittedRevisionProof`-gated effects (D090); `assess_update`/`plan_upgrade`/`execute_upgrade` methods serialized in the same single-flight (D091)", - "integration": "Provider controller binaries wrap handlers with toolkit", - "dataMigration": "None — full d2b 3.0 reset; no prior state to migrate", - "validation": "Golden state-machine vectors, deterministic clocks, conflict/restart/queue tests; D090: commit-fails/Abort → no effect, controller finishes-before-commit gated on proof, effects-gate, status-write-delayed (`statusPersistence: pending`), normal-queued no-op/rejoin, concurrent mutation, delete event-only projection, expedited timeout committed-but-pending, restart re-entry no duplicate; D091: current/non-disruptive/each-trigger assess, UpgradeRequired-not-in-place, dependency propagation/topological drain-recycle-restart, GPU blocking, state/TPM preservation, crash/re-entry resume, single-flight reconcile-vs-upgrade serialization", - "removalProof": "Current per-role orchestration removed only after ResourceType successors" - }, - { - "workItemId": "ADR046-reconcile-002", - "specId": "ADR-046-resource-reconciliation", - "specPath": "docs/specs/ADR-046-resource-reconciliation.md", - "dependencyOwner": "Store/API + ADR046-reconcile-001; core controller", - "currentSource": "`d2b-realm-core/src/route_engine.rs`, `allocator_engine.rs`; `d2b-realm-router/tests/transport_topology_harness.rs`", - "reuseSource": null, - "reuseAction": "adapt", - "destination": "`packages/d2b-core-controller/src/hints.rs`, `dependencies.rs`, `owner_reconcile.rs`", - "detailedDesign": "Watch-plan validation, indexes, suppression, owner/dependency hints, leases, startup relist, fair admission", - "integration": "Store post-commit dispatcher → d2b-bus controller streams", - "dataMigration": "None — full d2b 3.0 reset; no prior state to migrate", - "validation": "Owner/dependency chains, suppression/no-loss, restart/relist, lease withdrawal", - "removalProof": "Not applicable" - }, - { - "workItemId": "ADR046-reconcile-003", - "specId": "ADR-046-resource-reconciliation", - "specPath": "docs/specs/ADR-046-resource-reconciliation.md", - "dependencyOwner": "Process Providers + benchmark owner", - "currentSource": "`d2bd/src/supervisor/dag.rs`, `pidfd.rs`, unsafe-local blocked supervisor, guest exec runner", - "reuseSource": null, - "reuseAction": "adapt", - "destination": "`packages/d2b-controller-toolkit/benches/reaction.rs`, Process Provider integration tests", - "detailedDesign": "Commit-to-handler/launch fast path, nonblocking watch, parallel ready resources", - "integration": "Resource store → bus/session → controller → Process effect/status", - "dataMigration": "None — full d2b 3.0 reset; no prior state to migrate", - "validation": "Hard <=5 ms/<=20 ms p95 gates and 1/10/100 Process concurrency", - "removalProof": "Not applicable" - }, - { - "workItemId": "ADR046-reset-001", - "specId": "ADR-046-reset-and-cutover", - "specPath": "docs/specs/ADR-046-reset-and-cutover.md", - "dependencyOwner": "W0 shared contract root; storage/broker integrator", - "currentSource": "`packages/d2bd/src/storage_lifecycle.rs` (`run_startup_contract_check`, bundle-versioned contract validation pattern); `packages/d2bd/src/ownership_preflight.rs` (`EntrySpec`, legacy-recovery-artifact optionality); `packages/d2b/src/lib.rs` `build_storage_migration_plan`/`storage_migration_checkpoint_id`", - "reuseSource": "None from main; this is a v3-only cross-cutting concern with no main-branch equivalent", - "reuseAction": "extract and adapt", - "destination": "`packages/d2b-cutover/src/{inventory,snapshot,checkpoint}.rs`", - "detailedDesign": "Implement the seven closed inventories of [Authoritative inventories](#authoritative-inventories); the `checkpoint_id` digest algorithm of [Preflight and immutable snapshot](#preflight-and-immutable-snapshot); the atomic snapshot-write sequence (temp file, fsync, rename, parent fsync, post-rename immutability)", - "integration": "`d2b host cutover preflight`/`plan` CLI commands consume this crate exclusively; no other crate re-implements inventory walking", - "dataMigration": "New; no prior inventory/snapshot format exists", - "validation": "`checkpoint_id` determinism property test; snapshot atomic-write crash-injection test; `cutover_preflight_refuses_dirty_flake_check`", - "removalProof": "Not applicable (net-new capability)" - }, - { - "workItemId": "ADR046-reset-002", - "specId": "ADR-046-reset-and-cutover", - "specPath": "docs/specs/ADR-046-reset-and-cutover.md", - "dependencyOwner": "ADR046-reset-001; `d2b-resource-store-redb` owner; `d2b-provider` catalog owner", - "currentSource": "`nixos-modules/bundle-artifacts.nix`, `nixos-modules/assertions.nix` (existing eval-time validation precedent); `ADR-046-nix-configuration` \"Bundle and generation emission\"", - "reuseSource": "None from main", - "reuseAction": "adapt", - "destination": "`packages/d2b-cutover/src/{bundle_validate,trust_preflight}.rs`", - "detailedDesign": "Independent legacy-flake-check gate; candidate v3 bundle schema/cross-ref/determinism validation per [Config/artifact/schema validation](#configartifactschema-validation); Provider trust preflight per `ADR-046-provider-model-and-packaging` \"Trust\"", - "integration": "Invoked by `preflight` before the snapshot is written; failures block `plan` from being offered", - "dataMigration": "None — full d2b 3.0 reset; no prior state to migrate", - "validation": "`cutover-candidate-bundle-validation.nix`; trust-preflight rejection tests for each of digest/publisher/signature/deny/provenance/conformance failure modes", - "removalProof": "Not applicable" - }, - { - "workItemId": "ADR046-reset-003", - "specId": "ADR-046-reset-and-cutover", - "specPath": "docs/specs/ADR-046-reset-and-cutover.md", - "dependencyOwner": "ADR046-reset-001, ADR046-reset-002; Process/Guest lifecycle owner", - "currentSource": "`packages/d2b/src/lib.rs` `require_explicit_mutation_flag`, `cmd_host_destroy` (dry-run/apply precondition pattern); [ADR 0040](../adr/0040-graceful-vm-shutdown.md) graceful shutdown path", - "reuseSource": "None from main", - "reuseAction": "extract and adapt", - "destination": "`packages/d2b-cutover/src/{consent,drain,disposition}.rs`", - "detailedDesign": "Exact-consent-phrase gate bound to `checkpoint_id`; Phase 3 drain algorithm (§ [Old daemon/unit/process drain](#old-daemonunitprocess-drain)); the [Disposition framework](#disposition-framework)'s Adopt/Preserve/Destroy executor, delegating every Adopt to ADR046-reset-004", - "integration": "`d2b host cutover apply` orchestrates drain then disposition execution then hands off to Phase 5 (ADR046-reset-005)", - "dataMigration": "Destructive; this is where Phase 3/4 boundary-of-no-return-approach begins (rollback still open through end of Phase 4)", - "validation": "`cutover_apply_requires_exact_consent_phrase`; `cutover_drain_refuses_on_live_process`", - "removalProof": "Not applicable" - }, - { - "workItemId": "ADR046-reset-004", - "specId": "ADR-046-reset-and-cutover", - "specPath": "docs/specs/ADR-046-reset-and-cutover.md", - "dependencyOwner": "ADR046-reset-003; `ADR-046-provider-state` owner; `device-tpm`/`volume-local` Provider owners", - "currentSource": "`packages/d2b-priv-broker/src/ops/swtpm_dir.rs` (marker fail-closed pattern); `packages/d2b-host/src/hardlink_farm.rs` (same-filesystem rename pattern)", - "reuseSource": "None from main", - "reuseAction": "adapt", - "destination": "`packages/d2b-cutover/src/adopt.rs`, thin wrapper invoking `ADR-046-provider-state`'s migration `EphemeralProcess` prepare/stage/commit/rollback machinery with cutover-specific source paths", - "detailedDesign": "One Adopt invocation per matrix row tagged `Adopt` in the [migration/disposition matrix](#migrationdisposition-matrix); marker re-validation before every step; new-marker-before-old-removal ordering; idempotent re-run safety per [Crash/power-loss/retry/idempotency journals](#crashpower-lossretryidempotency-journals)", - "integration": "Called by ADR046-reset-003's disposition executor for every Adopt row; writes to the state Volumes ADR046-device-tpm-004/ADR046-vl-004/ADR046-vl-006 define", - "dataMigration": "This work item *is* the data migration mechanism for TPM/store-view/disk-image/unsafe-local-scope bytes", - "validation": "Crash-injection at every step boundary (Type 10 `cutover-crash-resume.nix`); TPM/durable-Volume Destroy-exclusion property test", - "removalProof": "Not applicable (the mechanism is retained permanently for later Full/Provider/Guest reset relocation use, not retired after first use)" - }, - { - "workItemId": "ADR046-reset-005", - "specId": "ADR-046-reset-and-cutover", - "specPath": "docs/specs/ADR-046-reset-and-cutover.md", - "dependencyOwner": "ADR046-reset-004; `d2b-resource-store-redb` owner (`ADR046-store-003`); core-controller owner (`ADR046-core-001`)", - "currentSource": "None (bootstrap sequencing over Zone runtime startup, which is itself ADR-only)", - "reuseSource": "None from main", - "reuseAction": "new", - "destination": "`packages/d2b-cutover/src/{store_bootstrap,provider_sequence}.rs`", - "detailedDesign": "Phase 5 store creation per [Resource-store initialization](#resource-store-initialization); Phase 6 topological Provider install per [Provider install/topological start](#provider-installtopological-start), including the fixed staged default order and cycle-rejection check", - "integration": "Invoked immediately after ADR046-reset-003/004 complete; hands off to Phase 7 (ADR046-reset-006)", - "dataMigration": "Destructive v3 bootstrap; no v2 resource import (per `ADR046-store-003`, `ADR046-object-001`)", - "validation": "Provider install topological-order determinism test; cycle-rejection test; store-identity mismatch fail-closed test", - "removalProof": "Not applicable" - }, - { - "workItemId": "ADR046-reset-006", - "specId": "ADR-046-reset-and-cutover", - "specPath": "docs/specs/ADR-046-reset-and-cutover.md", - "dependencyOwner": "ADR046-reset-005; `ADR-046-zone-routing` owner; `ADR-046-resources-zone-control` owner", - "currentSource": "ADR 0032 gateway guest custody evidence (`d2b-realm-router/src/service_v2.rs` `CredentialCustody`); `ADR-046-zone-routing` §3 evidence", - "reuseSource": "None from main", - "reuseAction": "adapt", - "destination": "`packages/d2b-cutover/src/{zonelink_cutover,guest_activation}.rs`", - "detailedDesign": "Phase 7 ZoneLink translation from `EntrypointMode::GatewayBacked`/`HostResident` per [Zone/ZoneLink cutover](#zonezonelink-cutover); Phase 8 Network→Volume→Device→Guest ordering per [Guest/runtime/network/store view activation](#guestruntimenetworkstore-view-activation); enforcement that the parent inventory never enumerates gateway-guest-internal credential/audit state", - "integration": "Consumes Providers installed by ADR046-reset-005; hands off to ADR046-reset-007 (verification)", - "dataMigration": "None (ZoneLink resources are ordinary Nix-authored configuration, not migrated credential bytes)", - "validation": "Gateway-custody-boundary test asserting the parent inventory never contains a gateway-guest-internal path; ZoneLink `Degraded/waiting-on-remote` non-blocking test", - "removalProof": "Not applicable" - }, - { - "workItemId": "ADR046-reset-007", - "specId": "ADR-046-reset-and-cutover", - "specPath": "docs/specs/ADR-046-reset-and-cutover.md", - "dependencyOwner": "ADR046-reset-006; telemetry-audit-and-support owner (`d2b-audit`)", - "currentSource": "ADR 0034 degraded-state ledger taxonomy and repair-never-trusts-ledger-paths invariant", - "reuseSource": "None from main", - "reuseAction": "extract and adapt", - "destination": "`packages/d2b-cutover/src/{verify,doctor,degraded}.rs`", - "detailedDesign": "The ten `verify` checks in [Post-cutover verification](#post-cutover-verification); the `cutover-quarantined` degraded class and `doctor` reporting in [Failure/quarantine/manual recovery](#failurequarantinemanual-recovery); audit chain closure/genesis-record cross-check (check 9)", - "integration": "`d2b host cutover verify`/`doctor` CLI commands; consumed by the Phase 10 finalize gate table", - "dataMigration": "None — full d2b 3.0 reset; no prior state to migrate", - "validation": "Injected-digest-mismatch test for TPM/durable-Volume verify checks; audit-genesis-cross-check test; `cutover-full-rehearsal.nix`", - "removalProof": "Not applicable" - }, - { - "workItemId": "ADR046-reset-008", - "specId": "ADR-046-reset-and-cutover", - "specPath": "docs/specs/ADR-046-reset-and-cutover.md", - "dependencyOwner": "ADR046-reset-007; owner of each retiring artifact's ADR 0046 successor work item", - "currentSource": "`ADR-046-cli-and-operations` \"Removal notes\" (live-successor-before-deletion criterion)", - "reuseSource": "None from main", - "reuseAction": "new", - "destination": "`packages/d2b-cutover/src/finalize.rs`", - "detailedDesign": "Per-candidate independent gate evaluation exactly as tabled in [Old artifact/unit/schema removal gates](#old-artifactunitschema-removal-gates); separate consent phrase from `apply`; never partial-destroys a candidate", - "integration": "`d2b host cutover finalize` CLI command; reads gate status from ADR046-reset-007's verify results plus each named policy-lint/integration test's pass/fail recorded in CI", - "dataMigration": "This work item is where every previously-Preserved legacy artifact is finally Destroyed, one gate at a time", - "validation": "`policy_no_destroy_without_gate`; `policy_legacy_cli_verbs_absent_after_gate`; `tpm-adopt-retirement.nix`", - "removalProof": "Each candidate's own row in [Old artifact/unit/schema removal gates](#old-artifactunitschema-removal-gates) states its exact removal proof" - }, - { - "workItemId": "ADR046-reset-009", - "specId": "ADR-046-reset-and-cutover", - "specPath": "docs/specs/ADR-046-reset-and-cutover.md", - "dependencyOwner": "ADR046-reset-003 through ADR046-reset-006; `ADR-046-provider-state` incident-hold owner", - "currentSource": "ADR 0034 \"dry-run and preflight output print the checkpoint id and exact rollback command before any apply step begins\"", - "reuseSource": "None from main", - "reuseAction": "adapt", - "destination": "`packages/d2b-cutover/src/{journal,rollback,hold}.rs`", - "detailedDesign": "Append-only journal per [Crash/power-loss/retry/idempotency journals](#crashpower-lossretryidempotency-journals); [Rollback boundary](#rollback-boundary) enforcement (`cutover-rollback-window-closed` past phase 4); cutover-wide incident hold per [Incident hold (cutover-wide)](#incident-hold-cutover-wide)", - "integration": "`d2b host cutover rollback`/`hold` CLI commands; consulted by ADR046-reset-003's disposition executor and ADR046-reset-008's finalize gate before every mutating step", - "dataMigration": "None — full d2b 3.0 reset; no prior state to migrate", - "validation": "`cutover_rollback_window_closes_after_phase_5`; incident-hold-blocks-destructive-step test", - "removalProof": "Not applicable" - }, - { - "workItemId": "ADR046-reset-010", - "specId": "ADR-046-reset-and-cutover", - "specPath": "docs/specs/ADR-046-reset-and-cutover.md", - "dependencyOwner": "ADR046-reset-005 through ADR046-reset-007; `ADR-046-resources-zone-control` owner (§2.6/§9.4)", - "currentSource": "`ADR-046-resources-zone-control` §2.6 (`core.zone-drain` finalizer algorithm), §9.4 (out-of-band destructive reset, uid=0 authentication)", - "reuseSource": "None from main", - "reuseAction": "adapt", - "destination": "`packages/d2b-cutover/src/reset_scope.rs`; `d2b host reset` CLI command", - "detailedDesign": "The three reset scopes and their comparison table in [Full Zone reset vs Provider reset vs Guest reset](#full-zone-reset-vs-provider-reset-vs-guest-reset); durable-Volume preserve-by-default with explicit `--destroy-durable-volumes`/`--destroy-volumes` opt-in; OS-level authentication for the zone scope only", - "integration": "Standalone from the cutover Phases 0-10 above; usable at any later time as a recovery/maintenance lever once a Zone exists", - "dataMigration": "None (this is a post-cutover recovery operation, not part of the cutover data migration itself)", - "validation": "`host_reset_scope_isolation`; `zone-provider-guest-reset-isolation.nix`; durable-Volume-preserved-by-default property test for both Provider and Guest scopes", - "removalProof": "Not applicable" - }, - { - "workItemId": "ADR046-reset-011", - "specId": "ADR-046-reset-and-cutover", - "specPath": "docs/specs/ADR-046-reset-and-cutover.md", - "dependencyOwner": "ADR046-reset-001 through ADR046-reset-010, fully landed", - "currentSource": "`tests/integration/live/` conventions; `tests/host-integration/hardware/` conventions", - "reuseSource": "None from main", - "reuseAction": "new", - "destination": "`tests/integration/live/cutover-real-host.sh`, `tests/integration/live/cutover-real-host-cloud-guest.sh`, `tests/host-integration/hardware/cutover-real-tpm.sh`, `tests/host-integration/hardware/cutover-real-usbip-security-key.sh`", - "detailedDesign": "Manual, `D2B_LIVE=1`/hardware-gated validation scripts described in [Tests](#tests) Type 11/12 rows; never run in CI; require operator sign-off and an independent out-of-band backup before execution", - "integration": "Run manually by an operator against a real host/device before the reset-and-cutover implementation is declared production-ready", - "dataMigration": "None (validation only)", - "validation": "Manual pass/fail sign-off recorded per the project's existing live-host/hardware validation conventions", - "removalProof": "Not applicable" - }, - { - "workItemId": "ADR046-reuse-001", - "specId": "ADR-046-telemetry-audit-and-support", - "specPath": "docs/specs/ADR-046-telemetry-audit-and-support.md", - "dependencyOwner": "ADR046-telem-001 (d2b-telemetry must exist first for MetricsSink injection); session owner", - "currentSource": "Main commit source row below: `packages/d2b-session/` and `packages/d2b-contracts/src/v2_component_session.rs` at `a1cc0b2d`.", - "reuseSource": null, - "reuseAction": "Copy/adopt the core ComponentSession runtime; adapt EndpointPurpose/ServicePackage/RealmSessionAuthority names listed in Excluded ADR45 assumptions and inject a `d2b-telemetry` MetricsSink.", - "destination": "`packages/d2b-session/` copied verbatim; v3 contract extension renames endpoint purpose values while preserving backward wire compatibility during transition.", - "detailedDesign": "Noise handshake, record protection, scheduler, named streams, cancellation, attachments, deadlines, metrics injection, ttrpc serving, and generation-discovery behavior from the selected source.", - "integration": "`d2b-bus` route handler calls `serve_ttrpc_services`; `d2b-session-unix` provides `OwnedTransport` impl; `d2b-telemetry` `MetricsSink` impl feeds `d2b_session_*` metrics inventory from this spec.", - "dataMigration": "Full d2b 3.0 reset; no v2 session runtime state/config import", - "validation": "Adopt `tests/component_session.rs` and `tests/noise_vectors.rs` unchanged; extend with v3 `EndpointPurpose` enum gate test; add `d2b-contract-tests/tests/component_session_v2_vectors.rs` (existing at `a1cc0b2d`) as-is.", - "removalProof": "ADR45 topology enum names listed in Excluded ADR45 assumptions are adapted; no prior v3 session runtime owner to remove." - }, - { - "workItemId": "ADR046-reuse-002", - "specId": "ADR-046-telemetry-audit-and-support", - "specPath": "docs/specs/ADR-046-telemetry-audit-and-support.md", - "dependencyOwner": "ADR046-reuse-001; Unix transport owner", - "currentSource": "Main commit source row below: `packages/d2b-session-unix/` at `a1cc0b2d`.", - "reuseSource": null, - "reuseAction": "Copy verbatim; retain `host-socket`/`native-vsock` feature flags; no ADR45 topology adaptation required.", - "destination": "`packages/d2b-session-unix/` copied verbatim.", - "detailedDesign": "SO_PASSCRED verification, pidfd identity verification, multi-scope credit reservation, emergency headroom, seqpacket atomic CLOEXEC transfer, stream reassembly, vsock transport, and descriptor policy enforcement.", - "integration": "`d2b-bus` Zone-local listeners use `UnixSeqpacketTransport`; Provider agent connections use vsock transport from this crate; `CreditPool`/`CreditScopeSet` enforce per-Zone attachment FD budgets.", - "dataMigration": "Full d2b 3.0 reset; no v2 transport runtime state/config import", - "validation": "Adopt all `unix_session.rs` tests unchanged.", - "removalProof": "None — net-new; no prior owner to remove" - }, - { - "workItemId": "ADR046-reuse-003", - "specId": "ADR-046-telemetry-audit-and-support", - "specPath": "docs/specs/ADR-046-telemetry-audit-and-support.md", - "dependencyOwner": "ADR046-reuse-001; client owner", - "currentSource": "Main commit source row below: `packages/d2b-client/` at `a1cc0b2d`.", - "reuseSource": null, - "reuseAction": "Copy client layer; adapt service package/method sets, local-root Zone identity, and TraceContext propagation for v3.", - "destination": "`packages/d2b-client/` copied; DaemonClient/GuestClient adapted to v3 service packages; `MetadataInput::with_trace` drives `TraceContext` propagation.", - "detailedDesign": "MetadataInput trace/correlation/idempotency, retry policy, cancellation token, generic client over resolver/connector/clock, typed service proxies, stream terminal, and host-socket verifier.", - "integration": "Every controller/service that makes outbound calls uses `Client`; `MetadataInput::with_trace` feeds `d2b_api_request_duration_seconds` trace-id into `d2b.bus.route` span.", - "dataMigration": "Full d2b 3.0 reset; no v2 client state/config import", - "validation": "Adopt typed-route, proxy-reuse, and cancel tests unchanged. Add v3 service-package name gate test.", - "removalProof": "ADR45 Daemon/Guest wrappers and RealmPath identity assumptions are adapted to v3 service packages/Zone name; no prior v3 client owner to remove." - }, - { - "workItemId": "ADR046-reuse-004", - "specId": "ADR-046-telemetry-audit-and-support", - "specPath": "docs/specs/ADR-046-telemetry-audit-and-support.md", - "dependencyOwner": "ADR046-reuse-001 + ADR046-reuse-003; Provider owner", - "currentSource": "Main commit source row below: `packages/d2b-provider/` and `packages/d2b-provider-toolkit/` at `a1cc0b2d`.", - "reuseSource": null, - "reuseAction": "Copy provider runtime/toolkit; adapt SessionIdentity Zone name and route through `d2b-bus` instead of daemon embedding.", - "destination": "`packages/d2b-provider/` and `packages/d2b-provider-toolkit/` copied with v3 session admission and bus routing adaptations.", - "detailedDesign": "ProviderRegistry factory registration, bounded admission, RPC proxy fail-closed behavior, generated provider service admission/dispatch, redaction wrappers, and conformance gates.", - "integration": "Each v3 Provider process embeds `ProviderRegistry` + `GeneratedProviderServiceServer`; `check_provider_conformance` runs in Provider install-time conformance check (feeds `d2b_provider_reconcile_total{outcome=\"error\"}` on failure).", - "dataMigration": "Full d2b 3.0 reset; no v2 ProviderRegistry runtime state import", - "validation": "Adopt all `conformance.rs` and `runtime.rs` tests unchanged. Add v3 `SessionIdentity` zone-name gate. Add conformance-failure → `d2b_provider_reconcile_total` metric integration test.", - "removalProof": "Daemon-embedded ADR45 ProviderRegistry wiring is excluded; v3 provider-process registry replaces it without importing state." - }, - { - "workItemId": "ADR046-reuse-005", - "specId": "ADR-046-telemetry-audit-and-support", - "specPath": "docs/specs/ADR-046-telemetry-audit-and-support.md", - "dependencyOwner": "ADR046-reuse-004 + ADR046-audit-003; Provider agent / observability-otel owner", - "currentSource": "Main commit source row below: `packages/d2b-gateway-runtime/src/provider_agent.rs` at `a1cc0b2d` plus `tests/provider_agent_v2.rs`.", - "reuseSource": null, - "reuseAction": "Adapt provider-agent session wrapper and audit ring; replace gateway-runtime audit envelope with v3 `d2b-audit` records.", - "destination": "`packages/d2b-provider-observability-otel/src/agent.rs` adapted; ComponentSessionDriver mock becomes shared Provider session fixture.", - "detailedDesign": "Session-bound ProviderAgentProcess construction, bounded audit snapshot, closed-set ProviderAgentError paths, and full ComponentSessionDriver mock for hermetic Provider tests.", - "integration": "`observability-otel` Provider embeds a `ProviderAgentProcess`; session connect/disconnect emits `SessionConnect` audit records via `d2b-audit`; `ProviderAgentAuditEvent` ring feeds `d2b_provider_reconcile_total` metric on session error.", - "dataMigration": "Full d2b 3.0 reset; no v2 provider-agent runtime state import", - "validation": "Adopt `provider_agent_v2.rs` mock harness unchanged as shared v3 Provider session fixture. Add v3 audit-bridge test: provider-agent session → `SessionConnect{transport_class=\"zone_link\"}` record emitted.", - "removalProof": "ADR45 gateway-runtime AuditEnvelope/realm custody and launch path are excluded; v3 provider agent is supervised by Provider/system-core or the owning Provider supervisor." - }, - { - "workItemId": "ADR046-reuse-006", - "specId": "ADR-046-telemetry-audit-and-support", - "specPath": "docs/specs/ADR-046-telemetry-audit-and-support.md", - "dependencyOwner": "ADR046-reuse-001 + ADR046-bus-001; bus routing owner", - "currentSource": "Main commit source row below: `packages/d2b-realm-router/` at `a1cc0b2d`.", - "reuseSource": null, - "reuseAction": "Adapt realm service routing to Zone service routing; preserve credential-custody, bounded routing, and stable error-code behavior.", - "destination": "`packages/d2b-bus/src/routing.rs` adapted from `service_v2.rs`; RemoteNodeErrorKind stable code pattern becomes v3 BusErrorKind.", - "detailedDesign": "Realm/Zone service ttrpc handler table, session authority constructors, host/gateway credential custody split, stable low-cardinality error codes, and binding/shortcut bounds.", - "integration": "`d2b-bus` route handler adapts `RealmServiceServer` dispatch table; `RemoteNodeErrorKind::code()` values feed `d2b_bus_route_total{outcome}` metric labels; `CredentialCustody::Host` maps to `purpose_class=local` in `d2b_session_connect_total`.", - "dataMigration": "Full d2b 3.0 reset; no v2 routing state import", - "validation": "Adopt `authority_keeps_remote_credentials_in_gateway_guests` test renamed to `authority_keeps_remote_credentials_in_zone_link_sessions`; adapt `RealmId` → Zone name; adopt `authenticated_bootstrap_enrollment_route_and_shortcut_lifecycle` renamed with zone terminology.", - "removalProof": "ADR45 RealmId and RealmService names are adapted to Zone/resource UID routing; no prior v3 bus routing owner to remove." - }, - { - "workItemId": "ADR046-reuse-007", - "specId": "ADR-046-telemetry-audit-and-support", - "specPath": "docs/specs/ADR-046-telemetry-audit-and-support.md", - "dependencyOwner": "ADR046-reuse-004 + ADR046-reuse-006; core-controller routing owner", - "currentSource": "Main commit source row below: `packages/d2bd/` routing/provider effects files and tests at `a1cc0b2d`.", - "reuseSource": null, - "reuseAction": "Adapt route-gate and ProviderLifecycleDispatch patterns; exclude daemon-embedded effect composition and route through v3 bus/provider processes.", - "destination": "`packages/d2b-bus/src/service_router.rs` and `packages/d2b-core-controller/src/provider_effects.rs`.", - "detailedDesign": "Closed-set service.package route gates, provider lifecycle effect-adapter composition, closed-set error mapping, fixed local policy invariants, and bootstrap/enrolled credential binding shape.", - "integration": "Bus service router uses `service.package` closed-set matching from route-gate pattern; `ProviderLifecycleDispatch` feeds `d2b_provider_component_phase` metric.", - "dataMigration": "Full d2b 3.0 reset; no v2 daemon routing/provider-effects state import", - "validation": "Port `local_daemon_policy_is_fixed_and_has_no_negotiation_or_fd_surface` invariant to v3 bus local policy test; port `every_generated_daemon_method_has_one_typed_adapter` to v3 bus method adapter completeness test.", - "removalProof": "ADR45 daemon-embedded effect adapters and realm child supervisor assumptions are excluded; v3 bus/provider effects own the adapted behavior." - }, - { - "workItemId": "ADR046-reuse-008", - "specId": "ADR-046-telemetry-audit-and-support", - "specPath": "docs/specs/ADR-046-telemetry-audit-and-support.md", - "dependencyOwner": "ADR046-reuse-001; contract-tests owner", - "currentSource": "Main commit source row below: ComponentSession vector/conformance tests at `a1cc0b2d`.", - "reuseSource": null, - "reuseAction": "Copy the vector and conformance tests verbatim; add v3 constant gate for ComponentSession major/minor version.", - "destination": "`packages/d2b-contract-tests/tests/component_session_v2_vectors.rs` and `tests/noise_vectors.rs` copied verbatim.", - "detailedDesign": "Pinned Noise KAT vectors, transcript/PSK mutation rejection, typed bootstrap admission fixture, and public-key corruption rejection remain the session wire security ground truth.", - "integration": "These tests run in `make test-rust` / `cargo test -p d2b-contract-tests` and `cargo test -p d2b-session`. They are gating for any Noise library update.", - "dataMigration": "None — docs/tooling only; no runtime state", - "validation": "These tests are self-validating. Add one gate: assert `COMPONENT_SESSION_MAJOR = 2` and `COMPONENT_SESSION_MINOR = 0` constants are unchanged in v3 contract.", - "removalProof": "None — net-new; no prior owner to remove" - }, - { - "workItemId": "ADR046-reuse-009", - "specId": "ADR-046-telemetry-audit-and-support", - "specPath": "docs/specs/ADR-046-telemetry-audit-and-support.md", - "dependencyOwner": "ADR046-reuse-001 + ADR046-telem-001 + ADR046-telem-003; telemetry/session owner", - "currentSource": "Main commit source row below: `packages/d2b-session/src/metrics.rs` and `MetricLabels` in `packages/d2b-contracts/src/v2_component_session.rs` at `a1cc0b2d`.", - "reuseSource": null, - "reuseAction": "Adapt MetricsSink bridge to v3 OTEL instruments and rename ADR45 endpoint-purpose label values to v3 closed-set labels.", - "destination": "`packages/d2b-telemetry/src/session_metrics_sink.rs`.", - "detailedDesign": "Map every MetricEvent variant to the `d2b_session_*` inventory; convert MetricLabels closed sets to allowed v3 label strings; keep NoopMetrics for hermetic tests.", - "integration": "`serve_ttrpc_services` receives a `Box` from `d2b-telemetry`; all session endpoints call through this bridge.", - "dataMigration": "Full d2b 3.0 reset; no v2 telemetry metric state import", - "validation": "New test `packages/d2b-telemetry/tests/session_sink_bridge.rs`: drive `MetricEvent` variants through the sink; assert OTEL counter/gauge values; assert `MetricLabels` closed-set values map only to allowed label strings (no `DaemonLocal` string in v3 metric output).", - "removalProof": "ADR45 EndpointPurpose metric label strings are replaced by v3 purpose names; no prior v3 MetricsSink bridge owner to remove." - }, - { - "workItemId": "ADR046-routing-001", - "specId": "ADR-046-zone-routing", - "specPath": "docs/specs/ADR-046-zone-routing.md", - "dependencyOwner": "W0/W1a; zone routing contract owner", - "currentSource": "`packages/d2b-realm-core/src/routing.rs`: `RouteAdvertisement`, `RouteNamespaceAllocation`, `RealmTreeEdge`, `DescendantRoute`, `TreeRoutePath`, `TreeRouteHop`, `RouteFailClosedReason`, `DirectShortcutAuthorizationRequest`, `DirectShortcutAuthorizationDecision`, `DirectShortcutTeardown`, `DirectShortcutTeardownReason`, `RouteAuditEventKind`, `RouteRealmClass`, `RoutePlacementClass`, `RouteAuditEventMetadata`, all route newtypes; `packages/d2b-realm-core/src/realm.rs`: `RealmPath`, `MAX_REALM_LABELS`, `MAX_REALM_PATH_BYTES`, `RealmControllerPlacement`, `EntrypointMode`; `packages/d2b-realm-core/src/ids.rs`: `RealmId`, `RouteId`, `ControllerGenerationId`, `WorkloadId`, `NodeId`, `ProviderId` (evidence: **A** for ids.rs — used in production; **B** for routing.rs — types exist with tests but no production daemon routing callers)", - "reuseSource": "Same v3 baseline commit `b5ddbed6`", - "reuseAction": "extract and adapt", - "destination": "`packages/d2b-contracts/src/v3/zone_routing.rs`", - "detailedDesign": "Rename RealmPath → ZonePath, RealmId → ZoneLabelId, RouteId → ZoneRouteId, ControllerGenerationId → ZoneLinkControllerGeneration; preserve all bounds/validation/serde; add ZoneLink-specific advertisement envelope fields (v3 schema version, capability ceiling field); preserve `RouteFailClosedReason` + add `zone-link-disconnected`, `hop-limit-exceeded`, `relay-denied`, `attachment-not-permitted-over-zone-link`; freeze v3 protobuf numbers separately from v2", - "integration": "d2b-bus zone route engine and ZoneLink controller consume these types", - "dataMigration": "Full reset; no v2 Realm route compatibility", - "validation": "Golden advertisement/path/failure vectors shared by Rust/Nix; property tests for NCA/loop/ceiling; replay-window tests; hop-count tests", - "removalProof": "v3 old `RealmPath` route types retired after zone-routing engine is live and all callers switched" - }, - { - "workItemId": "ADR046-routing-002", - "specId": "ADR-046-zone-routing", - "specPath": "docs/specs/ADR-046-zone-routing.md", - "dependencyOwner": "ADR046-routing-001; zone route engine owner", - "currentSource": "`packages/d2b-realm-core/src/route_engine.rs`: full `RouteTreeEngine` struct and impl; `RouteInventoryEntry`, `RoutePruneReport`, `DirectShortcutAuthorizationRequest/Decision/Teardown`; all helper functions", - "reuseSource": "Same v3 baseline commit `b5ddbed6`", - "reuseAction": "extract and adapt", - "destination": "`packages/d2b-zone-routing/src/engine.rs`", - "detailedDesign": "Adapt RouteTreeEngine to ZoneRouteEngine using ZonePath/ZoneRouteId/ZoneLinkControllerGeneration from ADR046-routing-001; preserve all NCA/loop/multi-parent/capability/replay/capacity logic; add `decide_route` enforcement of `maxHops` parameter; add hop-counter decrement in relay path; expose `ZoneRoutePath` in v3 types", - "integration": "d2b-bus calls `ZoneRouteEngine::decide_route` for every cross-Zone ResourceClient call; ZoneLink controller calls `admit_advertisement`/`admit_withdrawal`", - "dataMigration": "None (pure in-memory engine)", - "validation": "Copy exact `route_engine.rs` test suite adapted to ZonePath; add relay/hop-count/RBAC-narrowing/shortcut integration tests", - "removalProof": "`RouteTreeEngine` on v3 RealmPath types retired after ZoneRouteEngine is exercised in all bus routing paths" - }, - { - "workItemId": "ADR046-routing-003", - "specId": "ADR-046-zone-routing", - "specPath": "docs/specs/ADR-046-zone-routing.md", - "dependencyOwner": "ADR046-routing-001, ADR046-routing-002; ZoneLink resource owner", - "currentSource": "`packages/d2b-realm-core/src/routing.rs`: `RouteNamespaceAllocation`; `packages/d2b-realm-core/src/access.rs`: `RealmAccessResolverRequest/Response/Error`, `RealmAccessBinding`, `RealmTransportBinding`, `RealmAccessClientContract`, `UnixSocketPath`, `AccessBindingRef`, all access types (evidence: **B** — complete implementation, no production callers); `packages/d2bd/src/realm_access_resolver.rs`: `resolve_local_root_realm_access()`, `local_root_realm_access_client_contract()` (evidence: **B** — `pub mod` at `d2bd/src/lib.rs:117`, no callers from running daemon); `packages/d2b-realm-router/src/target_resolver.rs`: `RealmEntrypointTable`, `DispatchTarget`, `RealmEntrypoint`, `ResolveError` (evidence: **A**); `packages/d2b/src/lib.rs:5240`: `load_realm_entrypoint_table()` (evidence: **A**); `packages/d2b/src/target_routing.rs`: `Route::Local`/`Route::GatewayBacked` dispatch (evidence: **A**); `packages/d2b-realm-core/src/realm.rs`: `EntrypointMode`, `RealmControllerPlacement` (evidence: **A** as types; routing use **B**); `packages/d2b-core/src/realm_controller_config.rs`: `RealmControllersJson` (evidence: **C**); `nixos-modules/realm-controller-config-json.nix` (evidence: **C**)", - "reuseSource": "Same v3 baseline commit `b5ddbed6`", - "reuseAction": "extract and adapt", - "destination": "`packages/d2b-contracts/src/v3/zone_link.rs` (ZoneLink spec/status types, intent types, namespace allocation); `packages/d2b-zone-routing/src/resolver.rs` (ZoneEntrypointResolver)", - "detailedDesign": "ZoneLink spec/status fields; ZoneLinkIntent record; ZoneLinkNamespaceAllocation; ZoneEntrypointResolver with longest-suffix match over ZonePath (adapted from `RealmEntrypointTable::resolve`); fail-closed on unknown Zone", - "integration": "Core-controller ZoneLink handler manages ZoneLink resources; ZoneEntrypointResolver in d2b-bus for per-call dispatch decision", - "dataMigration": "None; ZoneLink resources created from Nix configuration at v3 reset", - "validation": "Longest-suffix match vectors; ZoneLink spec validation tests; resolver fail-closed test", - "removalProof": "`RealmEntrypointTable` retired after all host-daemon routing paths use ZoneEntrypointResolver" - }, - { - "workItemId": "ADR046-routing-004", - "specId": "ADR-046-zone-routing", - "specPath": "docs/specs/ADR-046-zone-routing.md", - "dependencyOwner": "ADR046-routing-003; core-controller ZoneLink handler owner", - "currentSource": "`packages/d2b-realm-router/src/remote_node.rs`: `RemoteNodeRegistry`, `RemoteNodeEntry`, `RemoteNodeAvailability`, `RemoteNodeErrorKind`, `RemoteRetryAction`, `ensure_remote_execution_generation` (evidence: **B** — only in `d2bd/src/realm_stubs.rs` dead_code seam); `packages/d2b-realm-router/src/session_lifecycle.rs`: `SessionLifecycle`, `SessionPhase` (evidence: **B** — same seam); `packages/d2bd/src/realm_stubs.rs`: compile-only seam (`#[allow(dead_code)]`, declared at `d2bd/src/lib.rs:249`); `packages/d2b-realm-core/src/enrollment.rs`: `EnrollmentRecord`, `EnrollmentStatus`, `KeyRotationPlan`, `RevocationRecord`, `SessionTeardownDirective`, `RecoveryProcedure`, `IdentityAuditEventKind` (evidence: **B** — consumed by `RealmIdentityStore` which itself has no production callers); `packages/d2b-realm-core/src/identity_store.rs`: `RealmIdentityStore` (evidence: **B** — no production callers); `packages/d2b-realm-core/src/identity_config.rs`: `RealmIdentityConfigJson` (evidence: **A** — loaded at d2bd/priv-broker startup, routing inert); `nixos-modules/realm-identity-config-json.nix` (evidence: **C**); `packages/d2bd/src/workload_target_index.rs`: `WorkloadTargetIndex` (evidence: **A** — called at `d2bd/src/lib.rs:16745`; this is the live bridge from realm metadata to VM-name dispatch; retires with Guest/Host resource lookups)", - "reuseSource": "Same v3 baseline commit `b5ddbed6`", - "reuseAction": "extract and adapt", - "destination": "`packages/d2b-core-controller/src/zone_links.rs`", - "detailedDesign": "ZoneLink handler in core-controller: manages ZoneLink ResourceSpec→session→advertisement lifecycle; session state machine (Pending/Established/Disconnected/Reconnecting/Revoked); reconnect backoff per `reconnectPolicy`; advertisement issuance/renewal/withdrawal using enrolled KK ComponentSession; route cursor tracking; intent queue; capability ceiling change handling; status writer; no storage of child resource content", - "integration": "Core-controller process → d2b-bus ZoneLink ComponentSession → child Zone runtime; ZoneLink handler triggers ZoneRouteEngine advertisement admission/withdrawal", - "dataMigration": "New ZoneLink resources from Nix configuration; no prior enrollment compatibility", - "validation": "Session lifecycle tests; reconnect/revocation/ceiling-change; intent queue drain; cursor resync; advertisement renewal timing; fake-child tests", - "removalProof": "`RemoteNodeRegistry` retired after all enrolled peer routing moves to ZoneLink handler" - }, - { - "workItemId": "ADR046-routing-005", - "specId": "ADR-046-zone-routing", - "specPath": "docs/specs/ADR-046-zone-routing.md", - "dependencyOwner": "ADR046-routing-002, ADR046-routing-007 (from ComponentSession spec); d2b-bus owner", - "currentSource": "`packages/d2b-realm-router/src/lib.rs`: `OperationRouter`, `RouteDecision`, `DEFAULT_RETENTION`, `DEFAULT_NO_REUSE_HORIZON`, `DEFAULT_MAX_DEDUP_RECORDS` (evidence: **B** — only in dead_code seam); `packages/d2b-realm-router/src/mux_session.rs`: `MuxSession` stream/operation forwarding (evidence: **B** for zone relay — stream-forwarding path for Zone relay does not exist yet; **D** within realm-router display-session tests); `packages/d2b-realm-core/src/frame.rs`: `ConstellationFrame`, `Handshake*`, `OperationRequest/Response`, `StreamOpen/Data/Flow/Close/Resume`, `OperationKind` (evidence: **B** from d2bd routing perspective; **A** within realm-router + `d2b-gateway-runtime` for individual types); `packages/d2b-realm-router/src/session.rs`: `PeerSession` (evidence: **B** from d2bd; **A** within realm-router tests); `packages/d2b-realm-router/src/secure_session.rs`: `SecurePeerSession`, `SecureSessionKey`, `NonceReplayGuard` (evidence: **B** from d2bd; reachable within realm-router)", - "reuseSource": "Same v3 baseline commit `b5ddbed6`", - "reuseAction": "extract and adapt", - "destination": "`packages/d2b-bus/src/zone_route.rs` (cross-Zone bus routing), `packages/d2b-bus/src/relay.rs` (per-hop relay handler)", - "detailedDesign": "Cross-Zone routing path in d2b-bus: ZoneEntrypointResolver → ZoneRouteEngine::decide_route → ZoneLink ComponentSession per hop; hop-counter decrement and enforcement; RBAC `relay` verb check at each intermediate hop; idempotency key namespace (full 6-tuple) in ZoneLinkIdempotencyKey; pinned reverse path tracking; cancellation forwarding; watch cursor forwarding and revision-expired handling; no-FD/credential structural rejection at serialization boundary", - "integration": "ResourceClient → d2b-bus → ZoneLink CS → intermediate zone → target zone; cancel/watch/stream all use the same routing path", - "dataMigration": "None — full d2b 3.0 reset; no prior state to migrate", - "validation": "End-to-end K0→K1→K2 resource call; relay-denied/hop-limit/FD-rejection tests; idempotency namespace collision tests; cancellation delivery tests; watch resync tests", - "removalProof": "Old direct-dispatch and gateway-backed paths retired per bus routing parity" - }, - { - "workItemId": "ADR046-routing-006", - "specId": "ADR-046-zone-routing", - "specPath": "docs/specs/ADR-046-zone-routing.md", - "dependencyOwner": "ADR046-routing-002; benchmark owner", - "currentSource": "`packages/d2b-realm-core/src/route_engine.rs` inline `#[cfg(test)] mod tests` block at line 1202 (45 test functions covering NCA, advertisement admission/withdrawal, loop/multi-parent detection, capability ceiling, replay window, DirectShortcut authorization/teardown; evidence: **implemented-but-unwired** — tests are in-file, not in a separate `tests/` directory; no external test crate at `packages/d2b-realm-core/tests/*.rs`)", - "reuseSource": "Same v3 baseline commit `b5ddbed6`", - "reuseAction": "adapt", - "destination": "`packages/d2b-zone-routing/tests/route_engine_vectors.rs`, `packages/d2b-zone-routing/benches/route_decision.rs`", - "detailedDesign": "Copy exact advertisement/NCA/loop/capability/replay test vectors adapted to ZonePath; add K0/K1/K2 topology scenarios; add hop-count boundary tests; benchmark: p95 route decision for 1/10/100 active Zone tree entries <= 1 ms", - "integration": "Zone route engine correctness gate; bus relay integration tests", - "dataMigration": "None — full d2b 3.0 reset; no prior state to migrate", - "validation": "All v3 baseline route_engine test cases must pass; p95 benchmark gate", - "removalProof": "Not applicable" - }, - { - "workItemId": "ADR046-routing-007", - "specId": "ADR-046-zone-routing", - "specPath": "docs/specs/ADR-046-zone-routing.md", - "dependencyOwner": "W0; d2b-bus owner", - "currentSource": "None in v3 pre-ADR45 baseline.", - "reuseSource": null, - "reuseAction": "copy and adapt", - "destination": "`packages/d2b-bus/src/session/`", - "detailedDesign": "Copy `d2b-session` crate wholesale into `d2b-bus/src/session/`; adapt `EndpointPurpose`/`EndpointRole`/`ServicePackage` closed-enum tags for v3 purposes; strip `GUEST_SESSION_CREDENTIAL_*` types; strip `serve_ttrpc_services` fixed-endpoint binding (replaced by allocator-issued FD bootstrap); adapt `SessionEngine` as ZoneLink session drive loop; keep all Noise profiles (Nn/Kk/IKpsk2), generation discovery, record/fragment/keepalive/credit/cancellation/attachment logic verbatim", - "integration": "ZoneLink controller instantiates one `ComponentSessionDriver` per ZoneLink, typed as Kk for enrolled peers and Nn for initial bootstrap; d2b-bus routes ResourceClient calls through these drivers", - "dataMigration": "None (new infrastructure)", - "validation": "Port all `component_session.rs` tests; port `noise_vectors.rs`; add ZoneLink-specific KK enrollment test; add ZoneLink reconnect/revocation integration test", - "removalProof": "Not applicable (new crate)" - }, - { - "workItemId": "ADR046-routing-008", - "specId": "ADR-046-zone-routing", - "specPath": "docs/specs/ADR-046-zone-routing.md", - "dependencyOwner": "ADR046-routing-007; transport-provider owner", - "currentSource": "None in v3 pre-ADR45 baseline (vsock and seqpacket paths are provider-specific in v3).", - "reuseSource": null, - "reuseAction": "copy and adapt", - "destination": "`packages/d2b-bus/src/transport/unix.rs`, `packages/d2b-bus/src/transport/credit.rs`", - "detailedDesign": "Copy `UnixSeqpacketTransport`/`UnixStreamTransport`/credit modules verbatim; adapt `PeerIdentityPolicy` for v3 Zone principal model; adapt `InheritedSocketTransport` to receive allocator-issued FD directly (not SD_LISTEN_FDS); vsock paths adapted as transport-Provider-specific implementations (not hardcoded); `PidfdIdentityPolicy` adapted for v3 Process resource pidfd model", - "integration": "Unix transport Provider instantiates `UnixSeqpacketTransport`; vsock transport Provider instantiates vsock transports; both implement `OwnedTransport` consumed by `SessionEngine` in ADR046-routing-007", - "dataMigration": "None — full d2b 3.0 reset; no prior state to migrate", - "validation": "Port all `unix_session.rs` tests; add allocator-issued FD handoff test; add inherited-socket no-SD-listen test", - "removalProof": "Not applicable (new infrastructure)" - }, - { - "workItemId": "ADR046-routing-009", - "specId": "ADR-046-zone-routing", - "specPath": "docs/specs/ADR-046-zone-routing.md", - "dependencyOwner": "ADR046-routing-007; contracts owner", - "currentSource": "None in v3 pre-ADR45 baseline.", - "reuseSource": null, - "reuseAction": "copy and adapt", - "destination": "`packages/d2b-contracts/src/v3/zone_session.rs`", - "detailedDesign": "Copy all protocol constants verbatim; copy `BoundedVec`, `BinaryError`, `ContractError`; copy `ComponentSessionPreface`, `LimitProfile`, `AttachmentDescriptor`/`AttachmentKind`/`AttachmentCreditClass`/`ChannelId`/`RecordHeader`/`FragmentHeader`/`SessionErrorCode`/`CloseReason`/`Remediation` verbatim; extend `ServicePackage` closed-enum with `ZoneV3`/`ResourceV3`/`ZoneLinkV3` variants at new tag values; extend `EndpointRole` with `ZoneController`/`ZoneRelay`/`ZoneBootstrap` variants; extend `EndpointPurpose`/`PurposeClass` with v3 Zone purposes; strip `GUEST_SESSION_CREDENTIAL_*` constants and types; re-freeze protobuf field numbers for v3 services independently from v2 assignments", - "integration": "All d2b-bus session/transport code imports from `d2b-contracts::v3::zone_session`", - "dataMigration": "None (new contract module)", - "validation": "Updated `negotiate_offer`/`validate_exact` round-trip tests for v3 purposes; canonical encoding stability test; closed-enum exhaustiveness tests", - "removalProof": "v2 contracts remain; v3 module is additive" - }, - { - "workItemId": "ADR046-routing-010", - "specId": "ADR-046-zone-routing", - "specPath": "docs/specs/ADR-046-zone-routing.md", - "dependencyOwner": "ADR046-routing-007, ADR046-routing-009; resource-client owner", - "currentSource": "None in v3 pre-ADR45 baseline.", - "reuseSource": null, - "reuseAction": "copy and adapt", - "destination": "`packages/d2b-resource-client/`", - "detailedDesign": "Copy `Client` generic structure; rename `ServiceOwner::Realm(RealmId)` → `ServiceOwner::Zone(ZonePath)`, `ServiceOwner::Workload{realm,workload}` → `ServiceOwner::Guest{zone,guest}`, `ServiceOwner::LocalRoot` → `ServiceOwner::ZoneLocal`; rename `TargetInput` variants to match; add `TargetInput::ZoneService(ZonePath, ZoneServiceKind)` for cross-Zone service targeting; replace `ServiceKind` (25 ADR45 variants) with v3 service inventory (`Resource`, `Zone`, `ZoneLink`, `Provider`, plus retained guest/daemon variants); adapt `RouteTable` to route by `ZonePath`; replace `HostSocketConnector` uid-based trust with allocator-issued FD + KK static key pinning; keep `SessionFailure`/retry/cancellation/`MetadataInput`/`RetryPolicy` logic verbatim", - "integration": "Zone runtime uses `ResourceClient` for all cross-Zone ResourceType calls; d2b-bus wraps `ComponentSessionDriver` (ADR046-routing-007) as the underlying session", - "dataMigration": "None — full d2b 3.0 reset; no prior state to migrate", - "validation": "Port `client.rs` tests; add ZonePath routing test; add cross-Zone K0→K1 end-to-end test; add retry/cancellation forwarding test", - "removalProof": "v2 `d2b-client` package remains for ADR45 callers; v3 `d2b-resource-client` is additive" - }, - { - "workItemId": "ADR046-routing-011", - "specId": "ADR-046-zone-routing", - "specPath": "docs/specs/ADR-046-zone-routing.md", - "dependencyOwner": "ADR046-routing-001; Nix module owner", - "currentSource": "`nixos-modules/options-realms-workloads.nix` (v3 baseline `b5ddbed6`): `d2b.realms..*` option declarations (evidence: **C** — eval contract; bespoke field names that do NOT mirror canonical ResourceSpec); `nixos-modules/assertions.nix`: realm-name regex, platform-gate, CIDR assertions (evidence: **A**); `nixos-modules/realm-controller-config-json.nix`: `realmControllersJson` emitter, bundle.nix:59 wiring (evidence: **C**)", - "reuseSource": "Same v3 baseline `b5ddbed6`; `assertions.nix` pattern reused for Zone assertions; `realm-controller-config-json.nix` is the structural template", - "reuseAction": "new module following same pattern", - "destination": "`nixos-modules/options-zones.nix` (new structural base), `nixos-modules/generated/options-zones-.nix` (generated per ResourceType by `xtask gen-zone-nix-options`), `nixos-modules/assertions.nix` (new Zone assertions)", - "detailedDesign": "Declare the structural base option `d2b.zones..resources. = { type = ...; spec = {}; }` as specified in the \"Option schema\" section above. Wire `options-zones.nix` and all `generated/options-zones-*.nix` files into `nixos-modules/default.nix`. Add a new `xtask gen-zone-nix-options` command that reads `docs/reference/schemas/v3/.schema.json` for each ResourceType and emits a generated submodule overlaying typed spec options (types, bounds, enum constraints, defaults, docs) onto `d2b.zones..resources..spec`. These generated modules are committed and kept in sync by `xtask gen-zone-nix-options && git diff --exit-code` wired into `make test-drift`. Because the generated options carry field-level type constraints, field-level eval errors (wrong enum, out-of-range int, non-hex fingerprint) are caught without explicit assertions. Explicit assertions in `nixos-modules/assertions.nix` cover cross-resource invariants only: zone/resource key name regex, reserved names, multi-parent detection, ref resolution, count limits, and transportSettings secret-key exclusion (listed in the eval-time assertions table). Each new assertion must have a matching case in `tests/unit/nix/cases/zone-assertions.nix` (nix-unit auto-discovered). `d2b.realms` option namespace is NOT removed in this work item.", - "integration": "`nixos-modules/zone-resources-json.nix` (ADR046-routing-012) iterates `d2b.zones..resources.*` to emit the bundle", - "dataMigration": "None; Zone options are new; Realm options retained until migration PR", - "validation": "`nix-unit: zone-name-regex`, `nix-unit: zone-link-fingerprint`, `nix-unit: zone-link-multi-parent`, `nix-unit: capability-ceiling-unknown-verb`, `nix-unit: transport-settings-secret-key`; add `drift: zone-nix-options` (`xtask gen-zone-nix-options && git diff --exit-code`); run `make nix-unit-pin` after adding eval cases", - "removalProof": "`nixos-modules/options-realms-workloads.nix` `d2b.realms` namespace retires after all hosts migrate to `d2b.zones`" - }, - { - "workItemId": "ADR046-routing-012", - "specId": "ADR-046-zone-routing", - "specPath": "docs/specs/ADR-046-zone-routing.md", - "dependencyOwner": "ADR046-routing-011, ADR046-routing-001; bundle emitter owner", - "currentSource": "`nixos-modules/realm-controller-config-json.nix` (v3 baseline `b5ddbed6`): `builtins.toJSON` emitter for `realm-controllers.json` (bundle.nix:59); `nixos-modules/bundle-artifacts.nix`: install table (root:d2bd 0640); `nixos-modules/bundle.nix`: artifact wiring (evidence: **C**); `packages/xtask/src/main.rs` `gen-schemas` subcommand (evidence: **A** — wired into `make test-drift`)", - "reuseSource": "`realm-controller-config-json.nix` structural template; `xtask gen-schemas` extension point (main `a1cc0b2d` unchanged in this area)", - "reuseAction": "extend and adapt", - "destination": "`nixos-modules/zone-resources-json.nix` (new), `nixos-modules/bundle-artifacts.nix` (new row for per-Zone `resource-bundle.json`), `packages/xtask/src/main.rs` (`gen-zone-schemas` subcommand emitting `docs/reference/schemas/v3/.schema.json` for Zone and ZoneLink; `gen-zone-nix-options` subcommand emitting `nixos-modules/generated/options-zones-.nix`)", - "detailedDesign": "`zone-resources-json.nix` iterates `d2b.zones..resources.*` to produce the canonical sorted resource list: for each entry, render `{ apiVersion, type, metadata: { name, zone, ownerRef: , labels: , annotations: }, spec: }`. The bundle JSON omits `managedBy` and `configurationGeneration`; the configuration service/core sets those fields when activating the validated bundle. Sort all resources by `(type, zone, name)`. Compute `generationId` as SHA-256 (lower hex) of the UTF-8 bytes of the sorted `resources` array JSON. Compute `integrity` as SHA-256 (base64url, no padding) of the full bundle JSON with integrity field zeroed. Install at `/etc/d2b/zones//resource-bundle.json` root:d2bd 0640. Canonical form: all object keys sorted lexicographically; order-significant arrays preserved; schema-declared set-like arrays sorted lexicographically; all optional fields emitted with defaults; no field renaming or restructuring. Build-time validation runs in a Nix derivation: (1) validate each resource against the committed JSON Schema; (2) validate `transportSettings` for each ZoneLink against the Provider's `transportSettingsSchema` — `transportProviderRef` is always explicit, never inferred or defaulted; (3) verify capability ceilings are subsets of parent grants; (4) check for duplicate `(type, zone, name)` tuples. Providers MUST commit their `transportSettingsSchema` before any ZoneLink can reference them. Drift gates: `xtask gen-zone-schemas && git diff --exit-code` and `xtask gen-zone-nix-options && git diff --exit-code` both wired into `make test-drift`. Add `checks.${system}.zone-schema-drift` to `flake.nix`.", - "integration": "`nixos-modules/bundle-artifacts.nix` installs per-Zone `resource-bundle.json`; ADR046-routing-013 Zone runtime reads it on startup", - "dataMigration": "None; new artifact file", - "validation": "`drift: zone-resource-schema`, `drift: zone-nix-options`, `build: zone-bundle-deterministic`, `build: transport-settings-unknown-field`, `build: capability-ceiling-superset`, `build: missing-transport-provider`; run `make flake-matrix-pin` after adding flake checks", - "removalProof": "`realm-controllers.json` artifact retires after Zone runtime is live and all hosts migrated" - }, - { - "workItemId": "ADR046-routing-013", - "specId": "ADR-046-zone-routing", - "specPath": "docs/specs/ADR-046-zone-routing.md", - "dependencyOwner": "ADR046-routing-012, ADR046-routing-003; `d2b-core-controller` owner (ADR-046-core-controllers)", - "currentSource": "`packages/d2b-realm-core/src/realm_controller_config.rs`: `RealmControllersJson`, `RealmControllerRow`, `RealmControllerConfigError` (evidence: **C** — loaded but routing inert); `packages/d2bd/src/realm_access_resolver.rs`: `resolve_local_root_realm_access()`, `RealmAccessResolverRequest`, `RealmAccessBinding` (evidence: **B** — pub mod at lib.rs:117, no callers); `packages/d2b-state/src/` (both baselines): atomic state, OFD locks, lease primitives (evidence: **A** for locks/leases, **B** for realm-specific storage); `nixos-modules/host-daemon.nix:220–221`: bundle artifact install paths, daemon SIGHUP wiring (evidence: **A**)", - "reuseSource": null, - "reuseAction": "extract and adapt", - "destination": "`packages/d2b-core-controller/src/configuration.rs` (defined by ADR-046-core-controllers); shared bundle DTOs may live in `packages/d2b-core/`", - "detailedDesign": "Implement the configuration ownership and cleanup contract from the \"Configuration ownership and cleanup contract\" section. `configuration.rs` owns: (1) reading and integrity-verifying `/etc/d2b/zones//resource-bundle.json` on startup and SIGHUP; (2) diffing against active generation by `generationId` (no-op if unchanged); (3) queuing Create/UpdateSpec/Delete intents — core sets `configurationGeneration` and `managedBy` when applying Create/UpdateSpec; Delete targets only resources where BOTH `managedBy` equals the configuration service's value AND `configurationGeneration` matches the prior bundle — resources with `managedBy=controller` or `managedBy=api` are never seized; (4) setting `deletionRequestedAt` on pending-delete resources immediately and adding a Pending condition; (5) writing the prior bundle into the capped ring at `/var/lib/d2b/zones//configuration/prior/.json` (default retentionCount=3, range 1..16, no TTL; prune oldest when count would exceed limit); (6) enforcing boundary invariants (no diff-delete for absent `configurationGeneration`, `managedBy` collision guard, live controller-child teardown guard); (7) driving finalizer drain + controller-child cascade before completing a Delete; (8) on successful deletion: one store transaction writes the `Deleted` revision/change event and removes the resource row and all index entries; the authoritative audit record (`zone-resource-cleanup`) is appended from the committed revision with dedup/exactly-once recovery and is NOT part of the store transaction; (9) tracking `deletionRequestedAt`/`cleanupConfigGeneration`/`cleanupError`/`cleanupAttempt` per resource; (10) on rollback: clearing `deletionRequestedAt` and Pending condition for revived resources; (11) never pruning a prior bundle while a Delete intent from its `configurationGeneration` is in flight. OFD lock on the bundle file prevents concurrent activation races. Generation state persisted atomically at `/var/lib/d2b/zones//configuration/generation.json` (root:d2bd 0640). The `spec` object comparison for UpdateSpec detection uses the canonical JSON form so two identical specs always compare equal regardless of Nix rendering order. Resource phase transitions: Pending while Create/UpdateSpec in-flight; Degraded while cleanup pending; Ready when clean; Failed on permanent error.", - "integration": "`d2b-core-controller` configuration service activates on bundle install and SIGHUP; zone-controller reconcile loops in `d2b-core-controller` consume the queued intents; d2b-bus resource API exposes `status.phase` and `pendingCleanup` via Get/Watch on the active generation resource", - "dataMigration": "None; new runtime component", - "validation": "`host-integration: cleanup-removed-zonelink`, `host-integration: rollback-restores-zonelink`, `host-integration: dynamic-child-not-deleted`; unit tests: deterministic generationId, no-op on same generationId, cross-ownership invariant enforcement, prior-bundle write/prune cycle, UpdateSpec canonical comparison, store-transaction-then-audit-append ordering, exactly-once audit dedup", - "removalProof": "`realm_access_resolver.rs` (B) retires after `d2b-core-controller` configuration tracking is live; `RealmControllersJson` (C) retires after all hosts migrated to Zone bundles" - }, - { - "workItemId": "ADR046-routing-014", - "specId": "ADR-046-zone-routing", - "specPath": "docs/specs/ADR-046-zone-routing.md", - "dependencyOwner": "ADR046-routing-007; Provider resource owner", - "currentSource": "None in v3 pre-ADR45 baseline (provider traits exist in `d2b-realm-provider` but are unregistered).", - "reuseSource": null, - "reuseAction": "copy and adapt", - "destination": "`packages/d2b-provider/src/` (adapted in place)", - "detailedDesign": "Retain `ProviderRegistry`/`ProviderRegistryBuilder`/`ProviderRegistryManager` lifecycle verbatim; adapt `SessionIdentity` to carry `ZonePath` instead of `RealmId`; adapt `AdmissionOptions::peer_role` to v3 Zone principal + RBAC binding; adapt `ProviderDescriptor` schema version to v3; `RegistryLimits` unchanged; `RpcProviderProxy` field adaptations to match v3 session identity; `ProviderInstance` variants retain all 11 types", - "integration": "Zone runtime `ProviderComposition` builds a `ProviderRegistry` per Zone; Provider resource controller admits calls through `ProviderRegistry::admit()`", - "dataMigration": "None (pure runtime)", - "validation": "Port inline registry lifecycle/drain/shutdown tests; add v3 ZonePath routing test; add RBAC relay-verb check at provider admission", - "removalProof": "Provider registry is v3 core infrastructure; no retirement" - }, - { - "workItemId": "ADR046-routing-015", - "specId": "ADR-046-zone-routing", - "specPath": "docs/specs/ADR-046-zone-routing.md", - "dependencyOwner": "ADR046-routing-014; Provider agent process owner", - "currentSource": "None in v3 pre-ADR45 baseline.", - "reuseSource": null, - "reuseAction": "copy and adapt", - "destination": "`packages/d2b-provider-toolkit/src/` (adapted in place)", - "detailedDesign": "Retain `GeneratedProviderServiceServer` ttrpc dispatch verbatim; adapt `ProviderAgentProcess::from_registry` to receive ComponentSession FD from Zone allocator bootstrap instead of SD_LISTEN_FDS; adapt audit event types for v3 Zone principal; `ProviderAgentAdapter` (client-side proxy) adapted for v3 ZoneLink session; conformance kit extended for v3 Provider resource conformance checks; redaction helpers unchanged", - "integration": "Provider Processes (EphemeralProcess or Process resources with `executionRef`) spawn the provider agent entrypoint; Zone bus instantiates `ProviderAgentAdapter` as the proxy inside the Zone runtime", - "dataMigration": "None — full d2b 3.0 reset; no prior state to migrate", - "validation": "Port `audit_capacity_is_closed_and_bounded`; add v3 bootstrap-via-allocator test; add conformance test for new Provider ResourceType schema", - "removalProof": "Provider toolkit is v3 core infrastructure; no retirement" - }, - { - "workItemId": "ADR046-routing-016", - "specId": "ADR-046-zone-routing", - "specPath": "docs/specs/ADR-046-zone-routing.md", - "dependencyOwner": "ADR046-routing-007, ADR046-routing-001; Zone service owner", - "currentSource": "`packages/d2b-realm-router/src/service_v2.rs` (v3 baseline `b5ddbed6`): `RealmServiceServer` (bootstrap/enroll/resolve_route/authorize_shortcut/revoke_shortcut/report_shortcut_close/inspect/cancel), `RealmServiceProcess`, `RealmSessionAuthority`, `CredentialCustody`, `RealmServiceLimits`, `RealmAuditEvent`/`RealmMethod`/`RealmAuditOutcome`, `BootstrapBinding`/`EnrollmentBinding`/`ShortcutBinding`/`MutationRecord`; constants `DEFAULT_MAX_REALM_BINDINGS=256`, `DEFAULT_MAX_SHORTCUTS=256`, `DEFAULT_MAX_MUTATION_RECORDS=1024`, `DEFAULT_AUDIT_CAPACITY=1024`, `MAX_CONFIGURED_BOUND=4096`, `MAX_DISPATCH_IN_FLIGHT=64`, `SHUTDOWN_TIMEOUT=5s` (evidence: v3 baseline, not main; see Baseline section — **B** from d2bd/CLI perspective, **A** within realm-router display-session use)", - "reuseSource": null, - "reuseAction": "copy and adapt", - "destination": "`packages/d2b-zone-routing/src/service.rs`", - "detailedDesign": "Rename `RealmServiceServer` → `ZoneServiceServer`; service wire name `d2b.realm.v2.RealmService` → `d2b.zone.v3.ZoneService`; rename methods (bootstrap→zone-bootstrap, enroll→zone-enroll, resolve_route→resolve-zone-route, authorize_shortcut→authorize-zone-shortcut, revoke_shortcut→revoke-zone-shortcut, report_shortcut_close→report-zone-shortcut-close, inspect→zone-inspect); replace `RealmSessionAuthority` with Zone principal + RBAC binding; replace `BootstrapBinding` with ZoneLink allocator-issued PSK binding; replace `EnrollmentBinding` with ZoneLink KK enrollment record; add `relay` verb RBAC check per hop; adapt shortcut model to ZonePath addressing; `RealmServiceLimits` defaults preserved; `MAX_DISPATCH_IN_FLIGHT=64`, `SHUTDOWN_TIMEOUT=5s` preserved; `CredentialCustody::GatewayGuest` excluded (all ZoneLink sessions are direct KK)", - "integration": "Zone runtime instantiates one `ZoneServiceServer` per Zone; d2b-bus routes `d2b.zone.v3.ZoneService` calls to this server; CLI uses `ZoneServiceClient` (from ADR046-routing-010) for zone inspect/enroll/route-resolve", - "dataMigration": "None; v3 Zone service is new; no v2 realm-service compatibility", - "validation": "Bootstrap/enroll/resolve-route/shortcut integration tests against fake ZoneLink; relay-verb RBAC test; KK enrollment test; shortcut ZonePath addressing test; concurrent dispatch bound test (64 in-flight)", - "removalProof": "`RealmServiceServer` on `d2b.realm.v2` retires after `ZoneServiceServer` handles all routing; display-session path migrates separately as part of Provider resource work" - }, - { - "workItemId": "ADR046-security-001", - "specId": "ADR-046-security-and-threat-model", - "specPath": "docs/specs/ADR-046-security-and-threat-model.md", - "dependencyOwner": "All ResourceType/Provider specs; owned by the security/telemetry integrator alongside `ADR046-telem-008`", - "currentSource": "`packages/d2b-contract-tests/tests/policy_observability.rs` (existing v3 cardinality/label policy gate)", - "reuseSource": "None (new cross-cutting gate; no equivalent exists in main)", - "reuseAction": "extract and adapt", - "destination": "`packages/d2b-contract-tests/tests/policy_telemetry_redaction.rs`", - "detailedDesign": "One policy test enumerating every forbidden metric-label/audit-field value from §21 and the content-secrecy table in §20 (store paths, `no_isolation`, credential bytes, raw paths/argv/PID/cgroup, CTAP/clipboard/terminal/notification content) and asserting, by static scan of instrumentation call sites plus a redaction-guard runtime test, that no `ADR046-*` Provider crate emits any of them", - "integration": "Runs as part of `make test-lint`/`make test-rust`; every Provider crate's own redaction test (e.g. `tests/stream_redaction.rs`) is a per-Provider instance of the same closed list", - "dataMigration": "None — full d2b 3.0 reset; no prior state to migrate", - "validation": "Hermetic (`cargo test -p d2b-contract-tests policy_telemetry_redaction`); fails the build if a new Provider crate is added without a corresponding redaction test file under its `tests/`", - "removalProof": "Not applicable — this is a permanent gate, not a migration" - }, - { - "workItemId": "ADR046-security-002", - "specId": "ADR-046-security-and-threat-model", - "specPath": "docs/specs/ADR-046-security-and-threat-model.md", - "dependencyOwner": "`ADR046-session-001`/`ADR046-bus-001` (ComponentSession/d2b-bus implementation)", - "currentSource": "main `a1cc0b2d`: `d2b-session/tests/noise_vectors.rs`, `d2b-session/tests/component_session.rs`, `d2b-session-unix/tests/unix_session.rs`", - "reuseSource": "Same main commit/paths", - "reuseAction": "copy and adapt", - "destination": "`packages/d2b-session/tests/noise_conformance.rs`, `packages/d2b-session/fuzz/fuzz_targets/{handshake_offer,record_frame}.rs`", - "detailedDesign": "Property/fuzz test suite over the three Noise profiles (§7): exact NN/KK/IKpsk2 vectors and rejection mutations (copied), plus new `cargo-fuzz` targets mutating the canonical handshake offer, preface, and encrypted record frame to assert no panic/UB and that every malformed input is a typed rejection (never a partial accept)", - "integration": "Wired into `make test-rust` (vectors) and a separate `make test-fuzz` target (new; time-boxed nightly run, not part of the PR-blocking gate)", - "dataMigration": "None — full d2b 3.0 reset; no prior state to migrate", - "validation": "Hermetic vector tests plus fuzz corpus with a minimum 4-hour nightly run and zero crashes/hangs as acceptance", - "removalProof": "Not applicable" - }, - { - "workItemId": "ADR046-security-003", - "specId": "ADR-046-security-and-threat-model", - "specPath": "docs/specs/ADR-046-security-and-threat-model.md", - "dependencyOwner": "`ADR046-zone-control-004`/`ADR046-zone-control-005` (Role/RoleBinding implementation)", - "currentSource": "`packages/d2bd/src/admission.rs` (`verb_requires_admin()` baseline verb table)", - "reuseSource": "None beyond the verb-table adaptation already tracked by `ADR046-zone-control-004`", - "reuseAction": "extract and adapt", - "destination": "`packages/d2b-resource-store/tests/rbac_property.rs`", - "detailedDesign": "Property test asserting, for a randomly generated Role/RoleBinding/request corpus: (1) no request whose payload sets a subject/role field ever changes the resolved `AuthenticatedSubjectContext.subjectRef`; (2) no non-core Role with a wildcard grant is ever admitted; (3) `scopeNarrowing` never widens beyond the referenced Role; (4) RoleBinding deletion never leaves an observable intermediate state under concurrent readers", - "integration": "Runs against the real redb-backed resource store test harness, not a mock", - "dataMigration": "None — full d2b 3.0 reset; no prior state to migrate", - "validation": "Hermetic property test (`proptest`/`quickcheck`-style, minimum 10,000 cases per property)", - "removalProof": "Not applicable" - }, - { - "workItemId": "ADR046-security-004", - "specId": "ADR-046-security-and-threat-model", - "specPath": "docs/specs/ADR-046-security-and-threat-model.md", - "dependencyOwner": "`ADR046-routing-005`/`ADR046-bus-001` (ZoneLink/d2b-bus relay implementation)", - "currentSource": "None (v3 ZoneLink relay is `ADR-only`)", - "reuseSource": "None", - "reuseAction": "extract and adapt (design copied from `ADR-046-zone-routing.md` structural-rejection sections)", - "destination": "`packages/d2b-bus/fuzz/fuzz_targets/zonelink_frame.rs`, `packages/d2b-bus/tests/zonelink_structural_rejection.rs`", - "detailedDesign": "Fuzz + property suite asserting that no mutation of a ZoneLink-bound frame (attachment count, credential-shaped byte runs, path-shaped strings, PID-shaped integers) is ever forwarded — every such mutation is rejected at serialization with `attachment-not-permitted-over-zone-link` or the transport-specific equivalent, never silently dropped or partially forwarded", - "integration": "`make test-fuzz`; a companion container test (`tests/integration/containers/zonelink-cross-zone.rs`) runs two real Zone runtime containers connected by a real ZoneLink and asserts the same property end to end over the wire, not just in the frame-serialization unit", - "dataMigration": "None — full d2b 3.0 reset; no prior state to migrate", - "validation": "Fuzz corpus (`cargo fuzz run zonelink_frame -- -runs=1000000`, zero crashes); container test passes in `make test-integration`", - "removalProof": "Not applicable" - }, - { - "workItemId": "ADR046-security-005", - "specId": "ADR-046-security-and-threat-model", - "specPath": "docs/specs/ADR-046-security-and-threat-model.md", - "dependencyOwner": "Every Provider dossier's own crate; owned by the Provider packaging integrator (`ADR-046-provider-model-and-packaging`)", - "currentSource": "None (compile-time dependency audit does not exist yet; per-Provider \"imports no broker DTO\" claims are currently prose-only in each dossier)", - "reuseSource": "`cargo-deny`/`cargo tree`-style dependency graph tooling already used by `nix flake check`'s `rust-deny`/`rust-audit` derivations (`AGENTS.md` \"Disk hygiene contract\")", - "reuseAction": "adapt", - "destination": "`packages/xtask/src/effectport_boundary_check.rs`, wired into `make test-policy`", - "detailedDesign": "For every crate under `packages/d2b-provider-*`, walk its `Cargo.toml` dependency graph and fail the build if it transitively depends on `d2b-priv-broker` or any crate exposing a raw broker client/DTO type; separately, grep-scan for direct syscalls forbidden per dossier (e.g. `socket(AF_VSOCK` in `transport-vsock`, `Command::new(\"systemctl\"` in `system-systemd`)", - "integration": "`make test-policy`; blocks any PR adding a forbidden dependency edge or forbidden syscall string to a Provider crate", - "dataMigration": "None — full d2b 3.0 reset; no prior state to migrate", - "validation": "Hermetic (`cargo xtask effectport-boundary-check`); a negative test intentionally adds a forbidden dependency to a scratch crate and asserts the check fails", - "removalProof": "Not applicable — permanent gate" - }, - { - "workItemId": "ADR046-security-006", - "specId": "ADR-046-security-and-threat-model", - "specPath": "docs/specs/ADR-046-security-and-threat-model.md", - "dependencyOwner": "`ADR046-minijail-002`/`ADR046-minijail-003` (LaunchTicket/EffectPort implementation)", - "currentSource": "`packages/d2b-priv-broker/src/sys.rs` (`clone3_spawn_runner` user-namespace path); `packages/d2b-host/src/virtiofsd_argv.rs`", - "reuseSource": "Same v3 paths (already `implemented-and-reachable` per the migration map)", - "reuseAction": "adapt", - "destination": "`packages/d2b-provider-system-minijail/tests/launchticket_toctou.rs`", - "detailedDesign": "Fault-injection test that issues a `LaunchTicket`, then mutates the referenced `CompiledSandboxPlan` digest (simulating a race between issue and exec) before the broker execs, and asserts the spawn fails closed rather than launching with the old plan; a companion test kills the broker mid-`clone3` and asserts no half-initialized process (missing cgroup placement, non-zero host capabilities) is ever observable by a concurrent reader", - "integration": "`make test-rust` (unit-level fault injection via a fake clock/fault-injecting `EffectPort` test double); a host/KVM integration test (`tests/host-integration/launchticket-toctou.nix`) repeats the same scenario against the real broker and real `clone3(2)`", - "dataMigration": "None — full d2b 3.0 reset; no prior state to migrate", - "validation": "Hermetic fault-injection test plus `make test-host-integration` NixOS/KVM test; acceptance is zero observable non-zero-capability or missing-cgroup-placement windows across 10,000 injected-fault iterations", - "removalProof": "Not applicable" - }, - { - "workItemId": "ADR046-security-007", - "specId": "ADR-046-security-and-threat-model", - "specPath": "docs/specs/ADR-046-security-and-threat-model.md", - "dependencyOwner": "`ADR046-minijail-005`, `ADR046-systemd-002`, `ADR046-aca-001`, `ADR046-volume-001` (every quarantine-on-ambiguity implementation)", - "currentSource": "`packages/d2b-priv-broker/src/ops/swtpm_dir.rs` (existing fail-closed marker/quarantine pattern, `implemented-and-reachable`)", - "reuseSource": "Same v3 path, generalized", - "reuseAction": "extract and adapt", - "destination": "`packages/d2b-contract-tests/tests/quarantine_not_kill_matrix.rs`", - "detailedDesign": "One parameterized fault-injection matrix test, run once per adoption-capable Provider (`system-minijail`, `system-systemd`, `runtime-cloud-hypervisor`, `runtime-azure-container-apps`, `volume-local`), that restarts the controller with a deliberately ambiguous adoption candidate (duplicate InvocationID, mismatched marker inode, stale ACA operation handle) and asserts: (a) the resource transitions to `Degraded`/`Quarantined`, never `Deleted` or silently re-adopted; (b) no signal is sent to the ambiguous candidate process; (c) a `runtime-security-violation`-class audit record is emitted", - "integration": "`make test-rust` for the in-process cases; `make test-host-integration` for the real-pidfd/real-cgroup cases (`tests/host-integration/quarantine-not-kill.nix`)", - "dataMigration": "None — full d2b 3.0 reset; no prior state to migrate", - "validation": "Matrix covers all five Providers listed; acceptance is 100% pass across all five with no signal sent to the ambiguous candidate in any case", - "removalProof": "Not applicable" - }, - { - "workItemId": "ADR046-security-008", - "specId": "ADR-046-security-and-threat-model", - "specPath": "docs/specs/ADR-046-security-and-threat-model.md", - "dependencyOwner": "`ADR046-exec-002` (Host `isolationPosture` propagation)", - "currentSource": "`packages/d2b-contracts/src/public_wire.rs:267` (`WorkloadPublicSummary.execution_posture`, `implemented-and-reachable`); `packages/d2bd/src/unsafe_local_helper.rs` (`HelperRegistry::dispatch_launch`, current gap: does not emit a `ProcessEffect`-class event)", - "reuseSource": "Same v3 paths", - "reuseAction": "adapt", - "destination": "`packages/d2b-provider-system-core/tests/no_isolation_propagation.rs`", - "detailedDesign": "Integration test that creates a user-only `Host`, launches a Process on it, and asserts all three non-suppressible surfaces simultaneously: `status.isolationPosture == \"none\"` in JSON output; the unconditional stderr warning string is present with `--json` and non-JSON CLI invocation and cannot be suppressed by any combination of flags; the corresponding `ProcessEffect` audit record carries `no_isolation: true`; and a companion negative assertion that `no_isolation` never appears in any OTEL metric/span emitted during the same test run", - "integration": "`make test-rust` (CLI/status/audit assertions) plus a Nix eval test (`tests/unit/nix/cases/no-isolation-null-posture-rejected.nix`) asserting the D042/D067 bidirectional-rejection eval assertions fire", - "dataMigration": "None (closes the current `HelperRegistry::dispatch_launch` audit gap noted in `ADR-046-telemetry-audit-and-support.md`)", - "validation": "Hermetic CLI/audit integration test; Nix eval test; acceptance is zero code paths reaching a live user-only-Host Process without all three surfaces firing", - "removalProof": "Legacy `d2b-unsafe-local-helper` v2-protocol warning path removed only after this test passes against the v3 replacement and the legacy crate has no remaining callers" - }, - { - "workItemId": "ADR046-security-009", - "specId": "ADR-046-security-and-threat-model", - "specPath": "docs/specs/ADR-046-security-and-threat-model.md", - "dependencyOwner": "`ADR046-pstate-003` (Volume identity marker)", - "currentSource": "`tests/unit/nix/cases/per-vm-state-ownership.nix` (existing v3 ownership test, adapted target)", - "reuseSource": "None new", - "reuseAction": "adapt", - "destination": "`packages/d2b-provider-volume-local/tests/marker_tamper_fault_injection.rs`", - "detailedDesign": "Fault-injection test that provisions a Volume, then out-of-band (as a simulated attacker with filesystem access) replaces the marker file, swaps the backing directory for a different inode on the same `st_dev`, and deletes the marker entirely — three separate scenarios — and asserts each transitions the Volume to `Failed` with `markerStatus: missing`/`replaced` respectively, never a silent re-provision, and that operator-only remediation is the only recovery path exercised", - "integration": "`make test-rust`; a host-integration variant (`tests/host-integration/volume-marker-tamper.nix`) repeats the inode-swap scenario against the real broker-maintained marker root on a real filesystem", - "dataMigration": "None — full d2b 3.0 reset; no prior state to migrate", - "validation": "Hermetic + host/KVM fault-injection test; acceptance is 100% fail-closed across all three tamper scenarios", - "removalProof": "Not applicable" - }, - { - "workItemId": "ADR046-security-010", - "specId": "ADR-046-security-and-threat-model", - "specPath": "docs/specs/ADR-046-security-and-threat-model.md", - "dependencyOwner": "`ADR046-credential-001` through `ADR046-credential-007`", - "currentSource": "None (zero-secret invariant has no existing automated gate)", - "reuseSource": "None", - "reuseAction": "extract and adapt (design from `ADR-046-resources-credential.md` §1.1)", - "destination": "`packages/d2b-contract-tests/tests/zero_secret_invariant.rs`", - "detailedDesign": "Static + dynamic gate: (1) static — every DTO type reachable from a `Credential`-adjacent module must implement a hand-written redacted `Debug` and must not derive `Debug`, enforced by a `#[forbid(clippy::derive_debug_ambient)]`-style custom lint or an `xtask` AST scan; (2) dynamic — a property test that generates random `Credential` delivery sessions and asserts the delivered token/`SignChallenge` byte sequence never appears, byte-for-byte, in any captured audit record, OTEL span, log line, or resource-store row taken during the same test run", - "integration": "`make test-lint` (static scan) and `make test-rust` (dynamic property test)", - "dataMigration": "None — full d2b 3.0 reset; no prior state to migrate", - "validation": "Hermetic; the dynamic test additionally runs as a canary-byte test (a unique random marker is embedded in the token and searched for across every observability surface)", - "removalProof": "Not applicable" - }, - { - "workItemId": "ADR046-security-011", - "specId": "ADR-046-security-and-threat-model", - "specPath": "docs/specs/ADR-046-security-and-threat-model.md", - "dependencyOwner": "`ADR046-notify-004`, clipboard/terminal/security-key Provider work items", - "currentSource": "`packages/d2b-unsafe-local-helper/src/{shell_runtime,shell_supervisor}.rs` (existing terminal-byte redaction discipline, `implemented-and-reachable`, adapted target)", - "reuseSource": "Same v3 paths", - "reuseAction": "adapt", - "destination": "`packages/d2b-provider-{clipboard-wayland,shell-terminal,device-security-key,notification-desktop}/tests/stream_redaction.rs` (one per Provider, same shared test helper crate)", - "detailedDesign": "Shared canary-byte test helper: each Provider's test injects a unique random marker into its sensitive content path (clipboard bytes, terminal output, CTAP payload, notification body) and asserts the marker never appears in audit, OTEL, Debug output, or CLI error text captured during the test", - "integration": "`make test-rust`; a container integration test (`tests/integration/containers/content-secrecy.rs`) runs a real Wayland/D-Bus mock session end to end for the clipboard/notification cases", - "dataMigration": "None — full d2b 3.0 reset; no prior state to migrate", - "validation": "Hermetic canary-byte test per Provider (4 Providers, shared helper crate); container test for the two D-Bus/Wayland-mediated cases", - "removalProof": "Not applicable" - }, - { - "workItemId": "ADR046-security-012", - "specId": "ADR-046-security-and-threat-model", - "specPath": "docs/specs/ADR-046-security-and-threat-model.md", - "dependencyOwner": "`ADR046-audit-002` (privileged audit durability)", - "currentSource": "`packages/d2bd/src/daemon_audit.rs` (existing audit-write path, adapted target)", - "reuseSource": "Same v3 path", - "reuseAction": "adapt", - "destination": "`packages/d2b-audit/tests/privileged_fail_closed.rs`", - "detailedDesign": "Fault-injection test that makes the audit sink's fsync fail (simulated ENOSPC/EIO) during a privileged `ResourceMutation`/`RBACChange`/`StateReset` write, and asserts the originating operation itself fails with `audit-unavailable` rather than completing with a lost audit record; a companion test floods `Standard`/`Best-effort` records past `DEFAULT_AUDIT_WRITES_PER_SECOND` and asserts privileged records are never dropped or delayed by the resulting backpressure", - "integration": "`make test-rust`", - "dataMigration": "None — full d2b 3.0 reset; no prior state to migrate", - "validation": "Hermetic fault-injection test; acceptance is zero privileged-class operations that complete despite a failed durable audit write", - "removalProof": "Not applicable" - }, - { - "workItemId": "ADR046-security-013", - "specId": "ADR-046-security-and-threat-model", - "specPath": "docs/specs/ADR-046-security-and-threat-model.md", - "dependencyOwner": "`ADR046-zone-control-009`/`ADR046-zone-control-010` (Quota/EmergencyPolicy), `ADR046-session-001` (session limits)", - "currentSource": "None new; ceiling values are already enumerated in `ADR-046-zone-routing.md`/`ADR-046-cli-and-operations.md`", - "reuseSource": "main `a1cc0b2d`: `d2b-session` credit-accounting/priority-scheduling tests, copied for the checked-arithmetic/priority-ordering assertions", - "reuseAction": "copy and adapt", - "destination": "`packages/d2b-bus/tests/dos_ceiling_fault_injection.rs`", - "detailedDesign": "Fault-injection/load test suite: (1) attachment-credit exhaustion at each of the six scopes (Packet/Request/Operation/Session/Process/Host), asserting typed rejection never a panic; (2) reconnect-storm exceeding `MAX_RECONNECT_ATTEMPTS`/`MAX_RECONNECT_WINDOW_MS`, asserting the session fails closed rather than looping; (3) ZoneLink hop-count/route-advertisement replay flood, asserting `hop-limit-exceeded`/`zone-advertisement-replay` rather than unbounded forwarding; (4) a stalled data stream under load, asserting control/cancellation traffic is never starved (priority-scheduling property)", - "integration": "`make test-rust`; item (4) additionally runs as a container load test (`tests/integration/containers/backpressure-priority.rs`) with a real slow consumer", - "dataMigration": "None — full d2b 3.0 reset; no prior state to migrate", - "validation": "Hermetic fault-injection suite; container load test; acceptance is zero panics/unbounded-growth across all four scenarios", - "removalProof": "Not applicable" - }, - { - "workItemId": "ADR046-security-014", - "specId": "ADR-046-security-and-threat-model", - "specPath": "docs/specs/ADR-046-security-and-threat-model.md", - "dependencyOwner": "`ADR046-doctor-001`/`ADR046-doctor-002` (doctor/support-bundle commands)", - "currentSource": "None (both commands are `ADR-only`)", - "reuseSource": "None", - "reuseAction": "extract and adapt (design from `ADR-046-telemetry-audit-and-support.md`)", - "destination": "`packages/d2b/src/commands/{doctor,support_bundle}.rs`", - "detailedDesign": "`d2b zone doctor` performs read-only status/audit-hash-chain checks with the redaction rules from §21 enforced on every field it prints; `d2b zone support-bundle` assembles a bounded archive of metadata+status (never spec bytes or `metadata.name`) and sets `bundle_completeness: \"partial\"` when any Provider in scope is quarantined, rather than omitting the gap silently", - "integration": "`make test-rust` (CLI integration tests); a container test (`tests/integration/containers/support-bundle-quarantined.rs`) runs a real Zone with one quarantined Provider and asserts the bundle correctly reports `partial`", - "dataMigration": "None — full d2b 3.0 reset; no prior state to migrate", - "validation": "Hermetic CLI test asserting no spec byte or `metadata.name` appears in a generated bundle; container test for the quarantined-Provider case", - "removalProof": "Not applicable" - }, - { - "workItemId": "ADR046-security-015", - "specId": "ADR-046-security-and-threat-model", - "specPath": "docs/specs/ADR-046-security-and-threat-model.md", - "dependencyOwner": "`ADR046-audit-001` (`StateReset` record), Volume/Credential lifecycle work items", - "currentSource": "Historical main ADR 0045 factory-reset design (`a1cc0b2d^:docs/adr/0045-provider-and-transport-framework.md`, reset process overview and apply-command verification steps) — reused only as a design precedent for atomicity/fail-closed sequencing, not as v3 architecture (see [Reset boundary](#reset-boundary) for the explicitly excluded assumptions)", - "reuseSource": "Same historical commit, sequencing pattern only (no code reuse; historical implementation was bash/systemd-generation-based and does not exist in any Rust crate)", - "reuseAction": "adapt (pattern only)", - "destination": "`packages/d2b-core-controller/src/reset.rs`, `packages/d2b-core-controller/tests/reset_atomicity.rs`", - "detailedDesign": "Implements the `scope` (`zone`, `provider`, `host`, or `guest`) `StateReset` flow from §25: quiesce via `EmergencyPolicy`, revoke open Credential leases in scope, destroy Volumes in scope (key-shred first), commit the `StateReset` audit record durably, and only then report the reset complete. A crash-recovery path re-derives \"was this reset already committed?\" solely from the durable `StateReset` record, never from partial filesystem state", - "integration": "`make test-rust` (unit-level state machine); a host/KVM integration test (`tests/host-integration/reset-atomicity.nix`) kills the process mid-reset at each of the four phases (quiesce, credential revoke, Volume destroy, audit commit) and asserts recovery never double-destroys, never silently completes without the audit record, and never leaves an orphaned sealed-Volume-without-key state", - "dataMigration": "None (v3-native; no v1/v2 reset-generation state to migrate)", - "validation": "Hermetic state-machine test; host/KVM crash-injection test at all four phases; acceptance is zero non-atomic outcomes across all injected crash points", - "removalProof": "Not applicable" - }, - { - "workItemId": "ADR046-security-016", - "specId": "ADR-046-security-and-threat-model", - "specPath": "docs/specs/ADR-046-security-and-threat-model.md", - "dependencyOwner": "Documentation/CI integrator; depends on every Provider dossier's own `Security` section existing", - "currentSource": "None (cross-reference check does not exist)", - "reuseSource": "None", - "reuseAction": "adapt (pattern from `tests/unit/gates/drift-check.sh`)", - "destination": "`tests/unit/gates/security-matrix-coverage.sh`", - "detailedDesign": "A drift-style gate that parses [Per-ResourceType threat matrix](#per-resourcetype-threat-matrix) and [Per-Provider-family threat matrix](#per-provider-family-threat-matrix), confirms every one of the 19 standard ResourceTypes and all 27 Provider dossiers under `docs/specs/providers/` has a row, and confirms every referenced dossier file actually contains a `## Security`-class section (by heading grep) — failing the gate if a new ResourceType/Provider is added without a corresponding row and dossier section", - "integration": "`make test-drift`", - "dataMigration": "None — full d2b 3.0 reset; no prior state to migrate", - "validation": "Hermetic shell-script gate; a negative test adds a scratch Provider dossier missing a Security section and asserts the gate fails", - "removalProof": "Not applicable — permanent gate" - }, - { - "workItemId": "ADR046-security-017", - "specId": "ADR-046-security-and-threat-model", - "specPath": "docs/specs/ADR-046-security-and-threat-model.md", - "dependencyOwner": "`ADR046-routing-004`, gateway-custody Provider work items (`ADR046-aca-*`, `ADR046-azure-vm-*`, `ADR046-transport-relay-*`)", - "currentSource": "None — net-new v3 work; no pre-ADR45 baseline equivalent", - "reuseSource": "None", - "reuseAction": "extract and adapt", - "destination": "`tests/integration/containers/malicious-child-zone.rs`", - "detailedDesign": "Container-based penetration test running a real parent Zone and a deliberately malicious child Zone container that attempts, over a real ZoneLink: FD smuggling, credential-shaped byte injection, cross-Zone `ownerRef` forgery, capability-ceiling widening claims, and route-advertisement replay. Every attempt must be rejected by the parent with the specific typed error named in §10, and none may reach the parent's resource store, Credential state, or Host substrate", - "integration": "`make test-integration` (requires podman, per `AGENTS.md` \"Local Layer 1 + container integration\")", - "dataMigration": "None — full d2b 3.0 reset; no prior state to migrate", - "validation": "Container integration test; acceptance is zero successful attacks across all five attempted vectors", - "removalProof": "Not applicable" - }, - { - "workItemId": "ADR046-security-018", - "specId": "ADR-046-security-and-threat-model", - "specPath": "docs/specs/ADR-046-security-and-threat-model.md", - "dependencyOwner": "Manual validation owner; depends on `ADR046-azure-vm-*`, `ADR046-transport-relay-*`, `ADR046-aca-*`, device Provider work items reaching a testable state", - "currentSource": "None (manual cloud/hardware validation has no automated equivalent by design)", - "reuseSource": "None", - "reuseAction": "adapt (checklist pattern from `SECURITY.md`'s existing portability-roadmap manual milestones and `tests/README.md`'s manual hardware tier)", - "destination": "`docs/reference/security-manual-validation-checklist.md` (new reference doc, out of scope for this spec's own file but named here as the required destination for the future implementation PR)", - "detailedDesign": "A checklist covering the scenarios that cannot be hermetically or even container-tested: (1) real Azure Container Apps/Azure VM credential rotation and revocation under `AzureEffectPort`, confirming zeroization on a real managed-identity/Entra token; (2) real TPM 2.0 hardware NVRAM persistence/tamper-marker behavior across a real host reboot; (3) real USBIP/security-key hardware mutual-exclusion enforcement with a physical FIDO2 device; (4) real Azure Relay listener/sender credential acquisition and relay-identity-not-local-auth verification against a live relay namespace", - "integration": "Run manually before each tagged release touching a cloud/hardware Provider, per the existing `tests/README.md` manual-tier convention", - "dataMigration": "None — full d2b 3.0 reset; no prior state to migrate", - "validation": "Checklist sign-off recorded in the release's validation evidence, not a CI gate (matches `D2b_LIVE=1` manual-tier precedent in `AGENTS.md`)", - "removalProof": "Not applicable" - }, - { - "workItemId": "ADR046-session-001", - "specId": "ADR-046-componentsession-and-bus", - "specPath": "docs/specs/ADR-046-componentsession-and-bus.md", - "dependencyOwner": "W0 shared contract root", - "currentSource": "v3 `d2b-realm-router/src/{session,secure_session,mux_session,lifecycle}.rs`, guest auth/transport", - "reuseSource": "main `a1cc0b2d`: `d2b-contracts/src/v2_component_session.rs`, `d2b-session/src/{handshake,bootstrap,record,engine,scheduler,streams,lifecycle,transport}.rs`, Noise/component tests/vectors", - "reuseAction": "copy and adapt", - "destination": "`packages/d2b-contracts/src/v3/component_session.rs`, `packages/d2b-session/`", - "detailedDesign": "Reversion canonical offer/records; retain NN/KK/IKpsk2; add canonical subject/authorization context hooks", - "integration": "d2b-bus, resource/controller/Provider services", - "dataMigration": "No v2 session compatibility; reconnect on v3", - "validation": "Copied exact vectors/rejections plus subject/RBAC/revocation tests", - "removalProof": "v3 old Realm PeerSession removed only after all v3 peer routes move" - }, - { - "workItemId": "ADR046-session-002", - "specId": "ADR-046-componentsession-and-bus", - "specPath": "docs/specs/ADR-046-componentsession-and-bus.md", - "dependencyOwner": "ADR046-session-001; transport owner", - "currentSource": "v3 guest vsock/local seqpacket implementations and d2b-realm-transport traits", - "reuseSource": "main `a1cc0b2d`: `d2b-session-unix/src/{adapter,socket,descriptor,pidfd,vsock,systemd,credit}.rs`, `tests/unix_session.rs`", - "reuseAction": "copy and adapt", - "destination": "`packages/d2b-session-unix/`, future enrolled transport adapter crates", - "detailedDesign": "Unix/socketpair/vsock owned transports, peer evidence, fd/pidfd/object validation, credits", - "integration": "ProviderSupervisor/Host/Guest/Zone listeners hand owned transports to session", - "dataMigration": "None — full d2b 3.0 reset; no prior state to migrate", - "validation": "Copied fd/peer/credit tests plus Host/Guest subject mapping", - "removalProof": "Ad hoc guest/public/helper transport removed only per service cutover" - }, - { - "workItemId": "ADR046-sterm-001", - "specId": "ADR-046-provider-shell-terminal", - "specPath": "docs/specs/providers/ADR-046-provider-shell-terminal.md", - "dependencyOwner": "Resource schemas area; owned by `d2b-provider-shell-terminal` resource modules.", - "currentSource": "None — net-new v3 qualified `ShellPool` and `ShellSession` resource schemas; superseded draft and legacy shell code do not define these canonical resources.", - "reuseSource": null, - "reuseAction": "net-new", - "destination": "`packages/d2b-provider-shell-terminal/src/resources/{pool,session}.rs`", - "detailedDesign": "Implement `shell-terminal.d2bus.org.ShellPool` and `shell-terminal.d2bus.org.ShellSession` schemas with qualified names, common phases, and typed detail fields.", - "integration": "Nix resource compiler, resource API admission, controller reconcile, status writers, and d2b-bus routing all consume the qualified pool/session schemas. Integration path: `packages/d2b-provider-shell-terminal/integration/resource-shape/`.", - "dataMigration": "Full d2b 3.0 reset; no v2 state/config import.", - "validation": "`packages/d2b-provider-shell-terminal/tests/resource_schema.rs`", - "removalProof": "None — net-new; no prior owner to remove." - }, - { - "workItemId": "ADR046-sterm-002", - "specId": "ADR-046-provider-shell-terminal", - "specPath": "docs/specs/providers/ADR-046-provider-shell-terminal.md", - "dependencyOwner": "Controller binary area; owned by `d2b-provider-shell-terminal` controller and core Operation ledger integration.", - "currentSource": "None — net-new v3 controller; legacy guestd and unsafe-local helper shell paths are not the controller/state authority.", - "reuseSource": null, - "reuseAction": "net-new controller; preserve status-first ProviderStateSet-empty rule", - "destination": "`packages/d2b-provider-shell-terminal/src/bin/d2b-shell-terminal-controller.rs`", - "detailedDesign": "Implement `d2b-shell-terminal-controller` with pool/session reconcile loops; assert ProviderStateSet is empty; publish bounded non-secret operational state to resource status and the core Operation ledger; no controller Provider state Volume or `/state` mount exists.", - "integration": "Core ProviderDeployment starts the controller Process; controller reconciles ShellPool/ShellSession resources, writes status, registers routes, and records operations without a Provider state Volume. Integration path: `packages/d2b-provider-shell-terminal/integration/controller-restart/`.", - "dataMigration": "Full d2b 3.0 reset; no v2 state/config import.", - "validation": "`packages/d2b-provider-shell-terminal/tests/controller_reconcile.rs`", - "removalProof": "None — net-new controller; legacy controller-equivalent state owner does not exist." - }, - { - "workItemId": "ADR046-sterm-003", - "specId": "ADR-046-provider-shell-terminal", - "specPath": "docs/specs/providers/ADR-046-provider-shell-terminal.md", - "dependencyOwner": "Supervisor binary area; owned by `d2b-provider-shell-terminal` session supervisor runtime.", - "currentSource": "Reuse narrow ring/runtime ideas from `packages/d2b-guestd/src/shell.rs` and adoption-shape ideas from `packages/d2b-unsafe-local-helper/src/runtime.rs`; both legacy authorities are superseded.", - "reuseSource": "`packages/d2b-guestd/src/shell.rs`; `packages/d2b-unsafe-local-helper/src/runtime.rs`.", - "reuseAction": "reuse narrow mechanics only; move PTY authority into per-session supervisor and exclude legacy protocols/identities/state storage", - "destination": "`packages/d2b-provider-shell-terminal/src/bin/d2b-shell-session-supervisor.rs`", - "detailedDesign": "Implement `d2b-shell-session-supervisor` as the sole PTY owner for Host and Guest pools.", - "integration": "Controller creates one user-domain supervisor Process per ShellSession; supervisor owns PTY, login shell, ring, attach bookkeeping, and private ComponentSession service. Integration path: `packages/d2b-provider-shell-terminal/integration/supervisor-host-guest/`.", - "dataMigration": "Full d2b 3.0 reset; no v2 shell state import; PTY/ring state is live process memory only.", - "validation": "`packages/d2b-provider-shell-terminal/tests/supervisor_runtime.rs`", - "removalProof": "Supersedes `guestd/src/shell.rs` managed runtime and unsafe-local helper shell supervisor; removed once successor supervisor coverage passes." - }, - { - "workItemId": "ADR046-sterm-004", - "specId": "ADR-046-provider-shell-terminal", - "specPath": "docs/specs/providers/ADR-046-provider-shell-terminal.md", - "dependencyOwner": "Process templates area; owned by Nix compiler plus shell-terminal controller.", - "currentSource": "Superseded draft templates included pool-wide/system-domain or management-worker concepts; canonical v3 templates are defined in this spec.", - "reuseSource": null, - "reuseAction": "replace incorrect draft templates with canonical controller and user-domain supervisor Process templates", - "destination": "`packages/d2b-provider-shell-terminal/src/process_templates.rs`", - "detailedDesign": "Teach the Nix compiler and controller to emit the canonical controller and user-domain supervisor `Process` templates.", - "integration": "Nix compiler emits controller Process/Endpoint resources; controller emits per-session user-domain supervisor Processes; Provider/system-systemd realizes them. Integration path: `packages/d2b-provider-shell-terminal/integration/process-placement/`.", - "dataMigration": "Full d2b 3.0 reset; no v2 state/config import.", - "validation": "`packages/d2b-provider-shell-terminal/tests/process_templates.rs`", - "removalProof": "Removes forbidden pool-wide supervisor, disguised system-domain supervisor, management EphemeralProcess, and sealed output Volume concepts from the template surface." - }, - { - "workItemId": "ADR046-sterm-005", - "specId": "ADR-046-provider-shell-terminal", - "specPath": "docs/specs/providers/ADR-046-provider-shell-terminal.md", - "dependencyOwner": "OpenSession lifecycle area; owned by controller service implementation.", - "currentSource": "None — net-new v3 `OpenSession` lifecycle; legacy shell protocols do not create ShellSession resources with inherited-field freeze.", - "reuseSource": null, - "reuseAction": "net-new", - "destination": "`packages/d2b-provider-shell-terminal/src/service/open_session.rs`", - "detailedDesign": "Create sessions from pools, freeze inherited fields, and return `supervisorGeneration` to callers.", - "integration": "`shell-terminal.v3.OpenSession` validates pool capacity and policy, creates ShellSession and supervisor Process, registers route data, and returns session/supervisor references to clients. Integration path: `packages/d2b-provider-shell-terminal/integration/open-session/`.", - "dataMigration": "Full d2b 3.0 reset; no v2 state/config import.", - "validation": "`packages/d2b-provider-shell-terminal/tests/open_session.rs`", - "removalProof": "None — net-new resource lifecycle; no prior owner to remove." - }, - { - "workItemId": "ADR046-sterm-006", - "specId": "ADR-046-provider-shell-terminal", - "specPath": "docs/specs/providers/ADR-046-provider-shell-terminal.md", - "dependencyOwner": "PTY and ring area; owned by per-session supervisor runtime.", - "currentSource": "Ring buffer mechanics may reuse ideas from `packages/d2b-guestd/src/shell.rs`; management workers and sealed output Volumes from prior draft are removed.", - "reuseSource": "`packages/d2b-guestd/src/shell.rs` bounded ring ideas only.", - "reuseAction": "reuse ring semantics; keep bytes in supervisor memory and remove management worker/EphemeralProcess model", - "destination": "`packages/d2b-provider-shell-terminal/src/session/{pty,ring}.rs`", - "detailedDesign": "Implement the in-memory PTY owner model, bounded ring buffer, replay, and eviction counters; do not create any management worker or `EphemeralProcess`.", - "integration": "Supervisor named terminal stream replays bounded ring tail then streams live PTY I/O; status publishes only ring byte counters and attach count. Integration path: `packages/d2b-provider-shell-terminal/integration/ring-overflow/`.", - "dataMigration": "Full d2b 3.0 reset; no terminal byte or ring-state import.", - "validation": "`packages/d2b-provider-shell-terminal/tests/ring_buffer.rs`", - "removalProof": "Proves no controller-owned PTY, management worker, EphemeralProcess, or sealed output Volume remains for shell output/management responses." - }, - { - "workItemId": "ADR046-sterm-007", - "specId": "ADR-046-provider-shell-terminal", - "specPath": "docs/specs/providers/ADR-046-provider-shell-terminal.md", - "dependencyOwner": "Adoption and routing area; owned by controller session adoption/routing module.", - "currentSource": "Reuse verification/adoption shape only from `packages/d2b-unsafe-local-helper/src/runtime.rs` `ScopeRuntime` and `PersistedScope`; exclude helper protocol and state storage.", - "reuseSource": "`packages/d2b-unsafe-local-helper/src/runtime.rs` `ScopeRuntime` and `PersistedScope` adoption pattern.", - "reuseAction": "adapt identity-verification shape; do not reuse helper protocol, identities, or state storage assumptions", - "destination": "`packages/d2b-provider-shell-terminal/src/session/adopt.rs`", - "detailedDesign": "Implement restart adoption, InvocationID plus cgroup verification, route registration, and stale-generation invalidation.", - "integration": "Controller restart scans supervisor Processes, verifies owner/session/generation identity, re-registers exact d2b-bus routes, and rejects stale or ambiguous handles. Integration path: `packages/d2b-provider-shell-terminal/integration/adopt-after-restart/`.", - "dataMigration": "Full d2b 3.0 reset; no v2 state/config import.", - "validation": "`packages/d2b-provider-shell-terminal/tests/adoption.rs`", - "removalProof": "Supersedes unsafe-local helper adoption storage/protocol; proof is degraded-ambiguity and stale-generation tests with no helper state dependency." - }, - { - "workItemId": "ADR046-sterm-008", - "specId": "ADR-046-provider-shell-terminal", - "specPath": "docs/specs/providers/ADR-046-provider-shell-terminal.md", - "dependencyOwner": "Host rules area; owned by shell-terminal Host policy module.", - "currentSource": "Supersedes unsafe-local Host shell path from `packages/d2b-unsafe-local-helper/src/services/shell/` while preserving explicit non-isolation warning semantics.", - "reuseSource": null, - "reuseAction": "replace unsafe-local helper shell policy with resource-backed Host pool warnings and same-UID checks", - "destination": "`packages/d2b-provider-shell-terminal/src/host_rules.rs`", - "detailedDesign": "Emit Host `isolationPosture=none` warnings, same-UID verification, and relay denial for Host user-domain pools.", - "integration": "Pool reconcile reads Host posture and User identity, writes warning status/audit, and admission rejects relay-authenticated Host user-domain access. Integration path: `packages/d2b-provider-shell-terminal/integration/host-warning/`.", - "dataMigration": "Full d2b 3.0 reset; no unsafe-local shell state/config import.", - "validation": "`packages/d2b-provider-shell-terminal/tests/host_rules.rs`", - "removalProof": "Supersedes unsafe-local Host shell supervisor path; proof requires no SSH/direct-host fallback and relay denial coverage." - }, - { - "workItemId": "ADR046-sterm-009", - "specId": "ADR-046-provider-shell-terminal", - "specPath": "docs/specs/providers/ADR-046-provider-shell-terminal.md", - "dependencyOwner": "Guest rules area; owned by shell-terminal Guest policy module.", - "currentSource": "Supersedes guest persistent-shell runtime in `packages/d2b-guestd/src/shell.rs`; Guest user-domain placement moves to Provider/system-systemd.", - "reuseSource": null, - "reuseAction": "replace guestd shell authority with Guest resource user-domain admission and supervisor placement", - "destination": "`packages/d2b-provider-shell-terminal/src/guest_rules.rs`", - "detailedDesign": "Require Guest `allowedDomains` to include `user`, require `defaultUserRef`, and place supervisors through the Guest user manager.", - "integration": "Pool admission validates Guest capabilities and default user; controller creates user-domain supervisor Processes through the Guest user manager exposed by Provider/system-systemd. Integration path: `packages/d2b-provider-shell-terminal/integration/guest-user-domain/`.", - "dataMigration": "Full d2b 3.0 reset; no guestd shell runtime state/config import.", - "validation": "`packages/d2b-provider-shell-terminal/tests/guest_rules.rs`", - "removalProof": "Supersedes independent guestd session-limit/runtime authority; proof is rejection of Guests without `user` domain and no guestd-managed shell path." - }, - { - "workItemId": "ADR046-sterm-010", - "specId": "ADR-046-provider-shell-terminal", - "specPath": "docs/specs/providers/ADR-046-provider-shell-terminal.md", - "dependencyOwner": "RBAC and relay denial area; owned by shell-terminal authorization module.", - "currentSource": "Existing public-wire `ShellOp`/unsafe-local surfaces are superseded; v3 requires ComponentSession service authorization.", - "reuseSource": null, - "reuseAction": "replace legacy shell operation authorization with Role/shell-admin or Zone-admin service gates", - "destination": "`packages/d2b-provider-shell-terminal/src/authz.rs`", - "detailedDesign": "Gate all verbs on `Role/shell-admin` or Zone-admin superset and fail closed for relay-authenticated Host user-domain callers.", - "integration": "Controller and supervisor ComponentSession methods authorize before capacity or route lookup, preserving stale-handle non-disclosure and Host relay denial. Integration path: `packages/d2b-provider-shell-terminal/integration/authz/`.", - "dataMigration": "Full d2b 3.0 reset; no v2 state/config import.", - "validation": "`packages/d2b-provider-shell-terminal/tests/authz.rs`", - "removalProof": "Supersedes public-wire shell protocol authorization; proof requires no `ShellOp` or `ShellOpResponse` path remains." - }, - { - "workItemId": "ADR046-sterm-011", - "specId": "ADR-046-provider-shell-terminal", - "specPath": "docs/specs/providers/ADR-046-provider-shell-terminal.md", - "dependencyOwner": "Audit and telemetry area; owned by shell-terminal audit/telemetry modules.", - "currentSource": "None — net-new v3 closed-label/redacted observability for shell-terminal; legacy shell paths must not leak names, paths, PIDs, or terminal bytes.", - "reuseSource": null, - "reuseAction": "net-new redacted observability", - "destination": "`packages/d2b-provider-shell-terminal/src/{audit,telemetry}.rs`", - "detailedDesign": "Implement closed-label metrics, redacted spans, and audit events with no usernames, session names, paths, or terminal bytes.", - "integration": "Reconcile, OpenSession, Attach, Detach, Kill, terminal exit, degradation, and Host posture warnings emit only digest/enum surfaces consumed by audit and OTEL collectors. Integration path: `packages/d2b-provider-shell-terminal/integration/support-redaction/`.", - "dataMigration": "Full d2b 3.0 reset; no v2 audit/telemetry state import.", - "validation": "`packages/d2b-provider-shell-terminal/tests/redaction.rs`", - "removalProof": "None — net-new observability surface; legacy paths must be removed or adapted to pass redaction tests." - }, - { - "workItemId": "ADR046-sterm-012", - "specId": "ADR-046-provider-shell-terminal", - "specPath": "docs/specs/providers/ADR-046-provider-shell-terminal.md", - "dependencyOwner": "Baseline removal area; owned by migration/removal implementation for shell-terminal.", - "currentSource": "Superseded sources: `packages/d2b-guestd/src/shell.rs`, `packages/d2b-unsafe-local-helper/src/services/shell/`, and `packages/d2b-contracts/src/public_wire.rs` `ShellOp`/`ShellOpResponse`.", - "reuseSource": null, - "reuseAction": "delete superseded runtime/protocol surfaces after successor parity", - "destination": "`packages/d2b-provider-shell-terminal/src/migration.rs`", - "detailedDesign": "Delete superseded guestd shell runtime, unsafe-local helper shell supervisor, and public-wire `ShellOp` or `ShellOpResponse` shell protocol.", - "integration": "Removal runs after shell-terminal resource, supervisor, service, RBAC, and integration coverage prove parity; workspace manifests, CI shards, and pins are updated so old and new suites do not run indefinitely. Integration path: `packages/d2b-provider-shell-terminal/integration/migration-baseline/`.", - "dataMigration": "Full d2b 3.0 reset; no v2 shell state/config import.", - "validation": "`packages/d2b-provider-shell-terminal/tests/migration.rs`", - "removalProof": "Delete `packages/d2b-guestd/src/shell.rs` managed runtime flow, `packages/d2b-unsafe-local-helper/src/services/shell/`, and `ShellOp`/`ShellOpResponse`; update closed gate manifests, flake/matrix/Nix-unit pins, generated ledgers, and CI workflow shards." - }, - { - "workItemId": "ADR046-sterm-013", - "specId": "ADR-046-provider-shell-terminal", - "specPath": "docs/specs/providers/ADR-046-provider-shell-terminal.md", - "dependencyOwner": "Supervisor service area; owned by shell-terminal controller/supervisor service modules and ComponentSession contracts.", - "currentSource": "Reuse service-shape ideas only from main commit `a1cc0b2da4a08ca3240a770a972fe4da6f912bef` generated v2 shell services; exclude ADR 0045 session, realm, and constellation assumptions.", - "reuseSource": "`a1cc0b2da4a08ca3240a770a972fe4da6f912bef` `packages/d2b-contracts/src/generated_v2_services/shell.rs` and `shell_ttrpc.rs`.", - "reuseAction": "adapt service-shape ideas into v3 ComponentSession services and named terminal stream", - "destination": "`packages/d2b-provider-shell-terminal/src/service/{controller,supervisor}.rs`", - "detailedDesign": "Define and implement `shell-terminal.v3` and `shell-session-supervisor.v1` ComponentSession services and the named `terminal` stream contract.", - "integration": "Controller service handles OpenSession/ListSessions/PoolStatus; supervisor service handles Attach/Detach/DetachAll/Kill/SupervisorStatus and terminal named stream routed by d2b-bus generation identity. Integration path: `packages/d2b-provider-shell-terminal/integration/service-contract/`.", - "dataMigration": "Full d2b 3.0 reset; no v2 service/session state import.", - "validation": "`packages/d2b-provider-shell-terminal/tests/service_contract.rs`", - "removalProof": "Supersedes public-wire shell protocol and helper supervisor protocol once ComponentSession service-contract tests pass." - }, - { - "workItemId": "ADR046-store-001", - "specId": "ADR-046-resource-store-redb", - "specPath": "docs/specs/ADR-046-resource-store-redb.md", - "dependencyOwner": "W0; store integrator", - "currentSource": "`packages/d2b-core/src/storage.rs`, `sync.rs`; `packages/d2bd/src/supervisor/state.rs`, `daemon_audit.rs`; `d2b-realm-router/src/lib.rs`", - "reuseSource": null, - "reuseAction": "extract and adapt", - "destination": "`packages/d2b-resource-store/src/lib.rs`, `packages/d2b-resource-store-redb/src/lib.rs`, `schema.rs`, `keys.rs`, `transaction.rs`", - "detailedDesign": "redb tables/encodings, fd backend, store identity, fair actor, MVCC reads, atomic indexes/revisions/operations/conflicts", - "integration": "Zone runtime owns store; resource API is sole caller", - "dataMigration": "Full reset; logical backup only for v3 stores", - "validation": "Unit/property/fault tests and hard benchmark", - "removalProof": "Existing ledgers removed only by owning future work items" - }, - { - "workItemId": "ADR046-store-002", - "specId": "ADR-046-resource-store-redb", - "specPath": "docs/specs/ADR-046-resource-store-redb.md", - "dependencyOwner": "ADR046-store-001; watch/reconciliation integrator", - "currentSource": "`packages/d2b-realm-core/src/mux.rs`, `d2b-realm-router/src/mux_session.rs`, `route_engine.rs`", - "reuseSource": null, - "reuseAction": "adapt", - "destination": "`packages/d2b-resource-store-redb/src/revision_log.rs`, `packages/d2b-resource-api/src/watch.rs`", - "detailedDesign": "replay/live no-gap watch, cursors, owner hints, compaction floor, expired relist", - "integration": "d2b-bus named streams; controller toolkit", - "dataMigration": "None — full d2b 3.0 reset; no prior state to migrate", - "validation": "deterministic watch/compaction/disconnect/fan-in tests", - "removalProof": "Not applicable" - }, - { - "workItemId": "ADR046-store-003", - "specId": "ADR-046-resource-store-redb", - "specPath": "docs/specs/ADR-046-resource-store-redb.md", - "dependencyOwner": "ADR046-store-001; storage/broker integrator", - "currentSource": "`nixos-modules/storage-json.nix`, `packages/d2b-priv-broker/src/ops/storage_contract.rs`, existing marker/ownership tests", - "reuseSource": null, - "reuseAction": "adapt", - "destination": "`packages/d2b-resource-store-redb/src/backup.rs`, `migration.rs`; generated v3 storage row", - "detailedDesign": "fd-backed provision/open, marker identity, logical backup, staged restore/upgrade, corruption quarantine", - "integration": "Broker/Host/Guest storage owner passes File to Zone runtime", - "dataMigration": "Destructive v3 bootstrap; v3-to-v3 logical restore", - "validation": "marker replacement, crash publication, backup/restore/upgrade tests", - "removalProof": "Not applicable" - }, - { - "workItemId": "ADR046-streamline-001", - "specId": "ADR-046-streamline", - "specPath": "docs/specs/ADR-046-streamline.md", - "dependencyOwner": "`packages/xtask` owner; no dependency on any other streamline item", - "currentSource": "None — net-new ADR 0046 spec tooling; no pre-ADR45 baseline equivalent", - "reuseSource": null, - "reuseAction": "net-new", - "destination": "`docs/specs/ADR-046-spec-set.json`, `docs/specs/ADR-046-work-items.json`, `docs/specs/ADR-046-implementation-graph.json`, `docs/specs/ADR-046-implementation-graph.md` (generated, committed non-member artifacts; named in `docs/specs/README.md`)", - "detailedDesign": "New `cargo run -p xtask -- spec-registry` subcommand parsing every spec's metadata table + work-item tables with a Markdown-table parser, emitting the two manifest JSON files; the `implementation-graph` generator then maps every member spec and every work item exactly once to a wave and file-disjoint parallel group, emits typed edges (`spec-depends-on`, `work-item-depends-on`, `implements-spec`, `shared-contract`, `file-overlap-order`), computes topological rank, and renders the Mermaid/table human view; output is deterministic with sorted keys and no timestamps or host paths", - "integration": "`tests/unit/gates/` drift gate (`xtask spec-registry`/`xtask implementation-graph` + `git diff --exit-code`) added to the existing drift-gate set; the graph is consumed by ADR046-streamline-005/006/008/013 and the ready-wave query in `ADR-046-validation-and-delivery` instead of each re-parsing Markdown independently", - "dataMigration": "None — docs/tooling only; no runtime state", - "validation": "`spec_registry::parse_metadata_table`, `spec_registry::acyclic_dependency_graph`, `xtask_spec_registry_regenerates_clean`, `spec_registry_json_schema_matches_doc`, `implementation_graph::every_spec_and_work_item_mapped_once`, `implementation_graph::acyclic_and_wave_monotonic`, `implementation_graph::parallel_groups_are_file_disjoint`, `implementation_graph_regenerates_clean`", - "removalProof": "None — net-new; no prior owner to remove; this is the foundational generator for the remaining items" - }, - { - "workItemId": "ADR046-streamline-002", - "specId": "ADR-046-streamline", - "specPath": "docs/specs/ADR-046-streamline.md", - "dependencyOwner": "ADR046-streamline-001; `d2b-core` owner (Tier B only)", - "currentSource": "Decision-register D010/D032/D075/D076/D080 prose is the Tier A source; no existing generated ResourceType schema source until Tier B", - "reuseSource": null, - "reuseAction": "net-new Tier A schema source; later replace source with generated output from real d2b-core ResourceType types without changing consumers", - "destination": "`docs/specs/schemas/*.schema.json` (Tier A: hand-authored-once canonical source checked into the tree, matching the frozen D010/D032/D075/D076/D080 decisions exactly); `packages/d2b-core/src/resource_schema/*.rs` (Tier B: the eventual Rust source of truth once ResourceType implementation exists, at which point the Tier A JSON becomes generated from Rust instead of hand-authored)", - "detailedDesign": "Tier A: author the schema JSON once per primitive directly from the already-frozen decision-register entries (D010, D032, D075, D076, D080); Tier B: `xtask gen-spec-schemas` derives the same JSON from real Rust `#[derive(JsonSchema)]`-equivalent types once they land, replacing the hand-authored Tier A source without changing its consumers", - "integration": "Consumed by ADR046-streamline-003 as the validation target and by ADR046-streamline-004's scaffold as the snippet source", - "dataMigration": "None — docs/tooling only; no runtime state", - "validation": "`spec_schema_check::canonical_field_set`", - "removalProof": "Tier A hand-authored JSON is superseded (not deleted) by Tier B generated JSON once real Rust types exist; consumers are unaffected since the file shape is identical" - }, - { - "workItemId": "ADR046-streamline-003", - "specId": "ADR-046-streamline", - "specPath": "docs/specs/ADR-046-streamline.md", - "dependencyOwner": "ADR046-streamline-001, ADR046-streamline-002", - "currentSource": "None — net-new spec Markdown/schema lint; no existing fenced ResourceSpec extractor", - "reuseSource": null, - "reuseAction": "net-new; reuse only the established xtask dispatch pattern and Rust Markdown-parser dependency class", - "destination": "`packages/xtask/src/bin/spec_schema_check.rs`; wired as a Layer-1 policy lint at `packages/d2b-contract-tests/tests/policy_spec_schema.rs`", - "detailedDesign": "Reuse the CommonMark parser already available to the Rust toolchain (the same class of dependency `xtask`'s existing `gen-*` commands already use for structured generation) to walk fenced blocks; classify each block by its enclosing heading (`## Nix authoring and configuration cleanup` vs. any other heading) to apply the current-evidence exclusion without a manual per-block marker", - "integration": "Added to `tests/tools/tier0-first-pass.sh` as an additional fast pre-check (closes F17) and to the standing `packages/d2b-contract-tests` policy-lint suite", - "dataMigration": "None — docs/tooling only; no runtime state", - "validation": "`spec_schema_check::extract_fenced_blocks`, `spec_schema_check::canonical_field_set`, `policy_spec_schema` (Type 4/5)", - "removalProof": "None — net-new; no prior owner to remove" - }, - { - "workItemId": "ADR046-streamline-004", - "specId": "ADR-046-streamline", - "specPath": "docs/specs/ADR-046-streamline.md", - "dependencyOwner": "ADR046-streamline-001, ADR046-streamline-002", - "currentSource": "None — net-new provider-dossier scaffold; canonical snippets come from ADR046-streamline-002", - "reuseSource": null, - "reuseAction": "net-new scaffold; reuse canonical schema/snippet source from ADR046-streamline-002", - "destination": "`docs/specs/providers/TEMPLATE.md` (committed, non-normative reference); `packages/xtask/src/bin/new_provider_dossier.rs` (`cargo run -p xtask -- new-provider-dossier --name `)", - "detailedDesign": "Scaffold generator emits the template pre-filled with the requesting Provider's name, D080 qualification-grammar examples, and the canonical state-Volume snippet; does not attempt to author Provider-specific semantic sections (those remain the dossier author's normative content)", - "integration": "Referenced from `docs/specs/README.md` \"Required metadata\" section (a follow-up doc edit outside this task's scope, tracked as a required cross-reference)", - "dataMigration": "None — docs/tooling only; no runtime state", - "validation": "`xtask_new_provider_dossier_scaffold`; the emitted scaffold must independently pass `spec_schema_check`/`work_item_validator` on first generation", - "removalProof": "None — net-new; no prior owner to remove" - }, - { - "workItemId": "ADR046-streamline-005", - "specId": "ADR-046-streamline", - "specPath": "docs/specs/ADR-046-streamline.md", - "dependencyOwner": "ADR046-streamline-001", - "currentSource": "Decision-register D080/D081/D082/D083/D084/D037 invariants and ADR046-streamline-001 registry output; no existing cross-spec lint", - "reuseSource": null, - "reuseAction": "net-new policy lints over generated registry and raw Markdown", - "destination": "`packages/d2b-contract-tests/tests/policy_spec_vocabulary.rs`, `policy_spec_ownership.rs`, `policy_spec_finalizer_phase.rs`, `policy_spec_source_policy.rs`", - "detailedDesign": "Four focused lint files (one invariant family per file, matching the existing `packages/d2b-contract-tests/tests/policy_*.rs` one-concern-per-file convention) each scanning the registry output plus raw Markdown text for the specific violation patterns named in F2/F4/F5/F7/F9", - "integration": "Standing Layer-1 policy-lint suite; runs on every PR touching `docs/specs/**`", - "dataMigration": "None — docs/tooling only; no runtime state", - "validation": "`policy_spec_vocabulary`, `policy_spec_ownership`, `policy_spec_finalizer_phase`, `policy_spec_source_policy` (all Type 5)", - "removalProof": "None — net-new; no prior owner to remove; graduates to permanent per [Permanent methodology](#permanent-methodology-after-accepted)" - }, - { - "workItemId": "ADR046-streamline-006", - "specId": "ADR-046-streamline", - "specPath": "docs/specs/ADR-046-streamline.md", - "dependencyOwner": "The Zone resource-store implementation work item (not yet filed; blocked on W0-W10 implementation request per D024); ADR046-streamline-001 for the doc-level invariant source", - "currentSource": "None — real Zone resource-store/controller-registration graph not implemented yet; doc-level invariant source is ADR046-streamline-005", - "reuseSource": null, - "reuseAction": "net-new future runtime graph checker", - "destination": "`packages/d2b-resource-store-redb/tests/provider_state_graph.rs` (or the eventual crate implementing Zone resource storage)", - "detailedDesign": "A graph-walk over the real controller-registration/resource-ownership index (not Markdown) asserting the D076/D086/D087 invariants; the doc-level half of this check (dossier text describing the invariant correctly) is covered now by ADR046-streamline-005's `policy_spec_ownership`", - "integration": "Runs as a Type 3 integration test against the real resource-store crate once it exists", - "dataMigration": "None — docs/tooling only; no runtime state", - "validation": "New test asserting: zero `ProviderStateSet` rows in the store; every state Volume corresponds to a declared component namespace; no empty identity-only Volume; no bootstrap state Volume or bootstrap-storage mechanism exists", - "removalProof": "None — net-new; no prior owner to remove" - }, - { - "workItemId": "ADR046-streamline-007", - "specId": "ADR-046-streamline", - "specPath": "docs/specs/ADR-046-streamline.md", - "dependencyOwner": "The Provider-toolkit/EffectPort implementation work item (not yet filed; blocked on the W0-W10 implementation request); ADR046-streamline-001 for the doc-level invariant source", - "currentSource": "Existing policy-broker lint pattern in packages/d2b-contract-tests/tests/policy_broker_dispositions.rs and policy_broker_schema.rs; no Provider crate source exists yet", - "reuseSource": null, - "reuseAction": "adapt existing policy-broker lint pattern to Provider/Worker boundary checks", - "destination": "`packages/d2b-contract-tests/tests/policy_effectport_boundary.rs`, `policy_worker_bus_boundary.rs`", - "detailedDesign": "Static source scan (import-graph/symbol-use analysis) over compiled Provider crates, mirroring the existing `policy_broker_dispositions.rs` pattern already proven against `packages/d2bd`/`packages/d2b-priv-broker`", - "integration": "Standing Layer-1 policy lint once Provider crates exist", - "dataMigration": "None — docs/tooling only; no runtime state", - "validation": "New tests asserting zero direct-broker-import matches in any Provider crate; zero disallowed-capability matches in any Worker binary", - "removalProof": "None — net-new; no prior owner to remove" - }, - { - "workItemId": "ADR046-streamline-008", - "specId": "ADR-046-streamline", - "specPath": "docs/specs/ADR-046-streamline.md", - "dependencyOwner": "ADR046-streamline-001", - "currentSource": "None — no existing work-item required-field or unique-ID validator", - "reuseSource": null, - "reuseAction": "net-new validator consuming ADR046-streamline-001 output", - "destination": "`packages/d2b-contract-tests/tests/policy_work_items.rs`", - "detailedDesign": "Consumes `docs/specs/ADR-046-work-items.json` (generated by ADR046-streamline-001); flags duplicate IDs, missing/placeholder fields, and dangling cross-references", - "integration": "Standing Layer-1 policy lint", - "dataMigration": "None — docs/tooling only; no runtime state", - "validation": "`work_item_validator::unique_ids`, `work_item_validator::required_fields`", - "removalProof": "None — net-new; no prior owner to remove; graduates to permanent" - }, - { - "workItemId": "ADR046-streamline-009", - "specId": "ADR-046-streamline", - "specPath": "docs/specs/ADR-046-streamline.md", - "dependencyOwner": "ADR046-streamline-001", - "currentSource": "None — no existing generated Provider catalog/index", - "reuseSource": null, - "reuseAction": "net-new generator consuming registry and decision-register rows", - "destination": "`docs/specs/ADR-046-provider-catalog.md` (generated, committed)", - "detailedDesign": "`cargo run -p xtask -- gen-provider-catalog` reads the registry and decision-register D043-D049 rows and renders the table; drift-gated like the existing `gen-schemas`/`gen-migration-ledger` pattern", - "integration": "`tests/unit/gates/` drift gate", - "dataMigration": "None — docs/tooling only; no runtime state", - "validation": "`provider_catalog::frozen_family_membership`, `provider_catalog_matches_frozen_families`", - "removalProof": "None — net-new; no prior owner to remove" - }, - { - "workItemId": "ADR046-streamline-010", - "specId": "ADR-046-streamline", - "specPath": "docs/specs/ADR-046-streamline.md", - "dependencyOwner": "ADR046-streamline-001", - "currentSource": "None — no existing stale-base/current-parent reconcile helper", - "reuseSource": null, - "reuseAction": "net-new read-only git report", - "destination": "`tests/tools/reconcile-stale-base.sh` (reporting only) plus a documented `git town sync`/`git town` restack procedure this report feeds into, since this repository does not yet use Git Town and F1/F11 show plain rebase/cherry-pick was insufficient to prevent duplicate reconciliation", - "detailedDesign": "`git merge-base ` plus `git diff --name-only` intersection reporting; emits a JSON report (branch, divergence SHA, commits-behind count, file-overlap list) consumed by ADR046-streamline-013's ready/launched/blocker report", - "integration": "Referenced from `AGENTS.md` once graduated (see [Permanent methodology](#permanent-methodology-after-accepted)); used manually before opening or restacking any future dossier branch", - "dataMigration": "None — docs/tooling only; no runtime state", - "validation": "`reconcile_helper_reports_stale_base`", - "removalProof": "None — net-new; no prior owner to remove" - }, - { - "workItemId": "ADR046-streamline-011", - "specId": "ADR-046-streamline", - "specPath": "docs/specs/ADR-046-streamline.md", - "dependencyOwner": "No prerequisite; `packages/xtask` schema/validator owner", - "currentSource": "None — no existing agent handoff manifest schema or validator", - "reuseSource": null, - "reuseAction": "net-new schema and validator", - "destination": "`packages/xtask/src/bin/handoff_manifest.rs` (schema/validator only); manifest instances are per-round artifacts referenced from PR bodies, not committed to the tree (consistent with this repository's \"Screenshot and visual artifact hygiene\"-style external-evidence posture)", - "detailedDesign": "A small JSON schema (`assigned_files: [string]`, `commit_sha: string`, `test_result: {command, exit_code}`, `base_sha: string`) plus a validator rejecting incomplete records; no attribution field for any AI/tool/model, consistent with this repository's commit/PR-body attribution rule", - "integration": "Referenced from `AGENTS.md` \"Worktrees for parallel agents\" once graduated", - "dataMigration": "None — docs/tooling only; no runtime state", - "validation": "`handoff_manifest_rejects_incomplete_record`", - "removalProof": "None — net-new; no prior owner to remove" - }, - { - "workItemId": "ADR046-streamline-012", - "specId": "ADR-046-streamline", - "specPath": "docs/specs/ADR-046-streamline.md", - "dependencyOwner": "ADR046-streamline-011 (shares the manifest schema for commit-SHA binding)", - "currentSource": "None — no existing task DB to git consistency importer", - "reuseSource": null, - "reuseAction": "net-new read-only importer/checker", - "destination": "`tests/tools/import-task-db-consistency.sh`", - "detailedDesign": "Reads the session database's todo table, resolves each `done` row's expected file set against `git log --name-only` for the current branch, and reports mismatches; read-only, no database or git mutation", - "integration": "Run manually at the end of each authoring round and before any pre-panel gate", - "dataMigration": "None — docs/tooling only; no runtime state", - "validation": "New test seeding a synthetic task DB with one orphaned \"done\" row and confirming the importer flags it", - "removalProof": "None — net-new; no prior owner to remove" - }, - { - "workItemId": "ADR046-streamline-013", - "specId": "ADR-046-streamline", - "specPath": "docs/specs/ADR-046-streamline.md", - "dependencyOwner": "ADR046-streamline-001, ADR046-streamline-010", - "currentSource": "None — no existing anti-serialization ready/launched/blocker report", - "reuseSource": null, - "reuseAction": "net-new report combining registry, branch, and staleness data", - "destination": "`tests/tools/anti-serialization-report.sh`", - "detailedDesign": "Cross-references the spec registry's dependency graph (ADR046-streamline-001) against the set of currently open `adr0046-*` branches and the reconcile helper's (ADR046-streamline-010) staleness report; emits a per-scope ready/launched/blocked classification", - "integration": "Run at the start of each authoring round and after any foundation-spec change", - "dataMigration": "None — docs/tooling only; no runtime state", - "validation": "New test seeding a synthetic registry + branch list with one ready-but-unlaunched scope and confirming it is reported", - "removalProof": "None — net-new; no prior owner to remove" - }, - { - "workItemId": "ADR046-streamline-014", - "specId": "ADR-046-streamline", - "specPath": "docs/specs/ADR-046-streamline.md", - "dependencyOwner": "The Provider-toolkit implementation work item (blocked on the W0-W10 implementation request)", - "currentSource": "Existing bounded-parallel test-runner precedent in tests/tools/run-layer.sh and layer1-jobs.py; Provider toolkit fake crates are net-new", - "reuseSource": null, - "reuseAction": "adapt bounded-parallel shard pattern; add net-new fake EffectPort/ResourceClient crates", - "destination": "`tests/tools/run-layer.sh` extension (this repository already has `tests/tools/run-layer.sh` and `layer1-jobs.py` bounded-parallelism precedent) plus fake `EffectPort`/`ResourceClient` stub crates under `packages/d2b-provider-toolkit-fakes/`", - "detailedDesign": "Extend the existing `layer1-jobs.py` bounded-parallel-shard pattern with a per-Provider-crate shard definition; fake dependency crates implement the same `EffectPort`/`ResourceClient` trait surface with in-memory stand-ins", - "integration": "`make test-unit`/`make check` shard addition once Provider crates exist", - "dataMigration": "None — docs/tooling only; no runtime state", - "validation": "New integration test proving a single Provider crate's test run succeeds with zero other Provider crates built", - "removalProof": "None — net-new; no prior owner to remove" - }, - { - "workItemId": "ADR046-streamline-015", - "specId": "ADR-046-streamline", - "specPath": "docs/specs/ADR-046-streamline.md", - "dependencyOwner": "ADR046-streamline-001, ADR046-streamline-009, ADR046-streamline-002 (Tier A part)", - "currentSource": "Existing packages/xtask gen-* and drift-gate pattern; no existing generated-artifact conflict detector", - "reuseSource": null, - "reuseAction": "adapt existing generator/drift-gate pattern with net-new input-hash conflict detection", - "destination": "Shared `packages/xtask` regeneration-conflict-detection helper consumed by every `gen-*`/`spec-registry` subcommand", - "detailedDesign": "Each generator hashes its input set before writing; if a concurrent regeneration on a sibling branch (detected via the reconcile helper's file-overlap report) would produce a different hash for the same output path, the generator refuses to overwrite and reports the divergent input set", - "integration": "Wired into every drift gate this spec and ADR046-streamline-001/009 introduce", - "dataMigration": "None — docs/tooling only; no runtime state", - "validation": "New test simulating two divergent input sets producing conflicting output for the same generated file and confirming the conflict is reported, not silently resolved", - "removalProof": "None — net-new; no prior owner to remove" - }, - { - "workItemId": "ADR046-streamline-016", - "specId": "ADR-046-streamline", - "specPath": "docs/specs/ADR-046-streamline.md", - "dependencyOwner": "ADR046-streamline-001, ADR046-streamline-008", - "currentSource": "D014/docs/specs/README.md zero-open-decision requirement; no existing automated pre-panel gate", - "reuseSource": null, - "reuseAction": "net-new policy lint and precondition script", - "destination": "`packages/d2b-contract-tests/tests/policy_no_leaked_decision_prefix.rs`; invoked as a required precondition script `tests/tools/pre-panel-gate.sh`", - "detailedDesign": "Scans registry output plus raw Markdown for `decision-required`, `TBD`, `TODO`, and any decision-ID-shaped token (`[A-Z]+-\\d+`) not matching the canonical `D\\d+` register numbering", - "integration": "Required precondition before requesting the ADR 0046 documentation set's panel round (D014)", - "dataMigration": "None — docs/tooling only; no runtime state", - "validation": "`policy_no_leaked_decision_prefix`", - "removalProof": "None — net-new; no prior owner to remove; graduates to permanent panel-gate precondition" - }, - { - "workItemId": "ADR046-streamline-017", - "specId": "ADR-046-streamline", - "specPath": "docs/specs/ADR-046-streamline.md", - "dependencyOwner": "No prerequisite; `docs/specs` evidence-command documentation owner", - "currentSource": "This spec's Observed friction evidence command prose; no reusable evidence-command artifact exists yet", - "reuseSource": null, - "reuseAction": "extract/adapt the already-cited evidence commands into a net-new documentation artifact", - "destination": "`docs/specs/ADR-046-streamline-evidence-commands.md` (a follow-up artifact outside this task's file scope; tracked here as a required future addition, not authored by this spec)", - "detailedDesign": "A plain Markdown list of the exact commands used to produce each F1-F17 citation above (already reproduced verbatim in this spec's prose), organized by friction ID, so re-running them is copy-paste rather than re-derivation", - "integration": "Referenced from PR bodies as external evidence per this repository's \"PR bodies contain... check-status summaries only... may link to external evidence\" convention", - "dataMigration": "None — docs/tooling only; no runtime state", - "validation": "Manual reviewer re-run of at least one command per friction ID during panel, confirmed to reproduce the cited count/SHA", - "removalProof": "None — net-new; no prior owner to remove" - }, - { - "workItemId": "ADR046-streamline-018", - "specId": "ADR-046-streamline", - "specPath": "docs/specs/ADR-046-streamline.md", - "dependencyOwner": "ADR046-streamline-010 (shares branch-staleness detection)", - "currentSource": "Existing disk-hygiene operator guidance; no ADR 0046 worktree disk report exists yet", - "reuseSource": null, - "reuseAction": "adapt existing disk-hygiene reporting concepts into a net-new non-mutating script", - "destination": "`tests/tools/worktree-disk-report.sh`", - "detailedDesign": "`git worktree list --porcelain` plus `du -sh` per worktree's `packages/target/`; output is a report only, never a `git worktree remove`/`rm -rf` invocation", - "integration": "Run manually before requesting garbage collection or worktree removal, consistent with this repository's existing \"Disk hygiene contract\"-equivalent operator guidance for code waves", - "dataMigration": "None — docs/tooling only; no runtime state", - "validation": "New test confirming the reporting script's exit code and output never include a mutating command string, and confirming it correctly flags a fixture worktree with a real (non-symlink) `packages/target/`", - "removalProof": "None — net-new; no prior owner to remove" - }, - { - "workItemId": "ADR046-streamline-019", - "specId": "ADR-046-streamline", - "specPath": "docs/specs/ADR-046-streamline.md", - "dependencyOwner": "ADR046-streamline-001", - "currentSource": "ADR-046 terminology and current-code migration-map specs; no automated old-to-new terminology helper exists yet", - "reuseSource": null, - "reuseAction": "net-new terminology checker over existing mapping specs", - "destination": "`packages/xtask/src/bin/terminology_check.rs` (`cargo run -p xtask -- terminology-check`)", - "detailedDesign": "Parses every \"Current-code fit\" table's \"Current anchor\" cell for symbol/crate names, cross-references against the migration map's disposition rows, and flags any current-source citation absent from the map", - "integration": "Standing Layer-1 policy lint once wired; also usable ad hoc when authoring a new dossier's evidence section", - "dataMigration": "None — docs/tooling only; no runtime state", - "validation": "New test seeding a fixture spec citing a current-source symbol absent from the migration map and confirming it is flagged", - "removalProof": "None — net-new; no prior owner to remove" - }, - { - "workItemId": "ADR046-streamline-020", - "specId": "ADR-046-streamline", - "specPath": "docs/specs/ADR-046-streamline.md", - "dependencyOwner": "ADR046-streamline-001", - "currentSource": "D094 test-placement requirement; no existing hermetic test-placement policy lint", - "reuseSource": null, - "reuseAction": "net-new policy lint", - "destination": "`packages/d2b-contract-tests/tests/policy_test_placement.rs`", - "detailedDesign": "Scans hermetic-tier Rust sources for banned API surfaces (`std::process::Command`, socket/container/DBus/systemd helpers, `#[ignore]`) and for `integration/`-only markers appearing outside `integration/`", - "integration": "`make test-policy` row; no new top-level `tests/*.sh` gate", - "dataMigration": "None — docs/tooling only; no runtime state", - "validation": "Fixture crate with an intentional process-spawning hermetic test is rejected naming the file/line; a correct crate passes", - "removalProof": "None — net-new; no prior owner to remove" - }, - { - "workItemId": "ADR046-streamline-021", - "specId": "ADR-046-streamline", - "specPath": "docs/specs/ADR-046-streamline.md", - "dependencyOwner": "ADR046-streamline-020", - "currentSource": "D094 deterministic-clock requirement; no existing wall-clock sleep/retry policy lint", - "reuseSource": null, - "reuseAction": "net-new policy lint layered on ADR046-streamline-020", - "destination": "`packages/d2b-contract-tests/tests/policy_test_determinism.rs`", - "detailedDesign": "Scans hermetic sources for banned time/sleep APIs and asserts the deterministic fake-clock/RNG fixtures from the toolkit are used; classified crypto/property exceptions are allow-listed by explicit name with a declared per-test budget", - "integration": "`make test-policy` row", - "dataMigration": "None — docs/tooling only; no runtime state", - "validation": "Fixture hermetic test using `thread::sleep` is rejected; a classified crypto test on the allow-list passes", - "removalProof": "None — net-new; no prior owner to remove" - }, - { - "workItemId": "ADR046-streamline-022", - "specId": "ADR-046-streamline", - "specPath": "docs/specs/ADR-046-streamline.md", - "dependencyOwner": "ADR046-delivery-007", - "currentSource": "ADR046-delivery-007 timing-ledger design; no existing execution-only test-runtime ledger in this spec set", - "reuseSource": null, - "reuseAction": "share/adapt ADR046-delivery-007 timing-ledger implementation for this gate", - "destination": "`packages/xtask/src/test_runtime_ledger.rs` (shared with `ADR046-delivery-007`)", - "detailedDesign": "Parses per-test JSON timings, aggregates per test/crate/shard, compares against pinned budgets and the previous ledger, and fails on regression beyond the threshold", - "integration": "Consumed by wave entry/exit (`ADR-046-validation-and-delivery` §4/§10.16); `make test-rust` and Layer-1 shards run concurrently", - "dataMigration": "None — docs/tooling only; no runtime state", - "validation": "Synthetic timing regression fails the gate; ledger output is deterministic and machine-readable", - "removalProof": "None — net-new; no prior owner to remove" - }, - { - "workItemId": "ADR046-streamline-023", - "specId": "ADR-046-streamline", - "specPath": "docs/specs/ADR-046-streamline.md", - "dependencyOwner": "ADR046-streamline-008, ADR046-streamline-022", - "currentSource": "Current-code migration-map disposition rows and existing gate manifests; no legacy-test retirement generator exists yet", - "reuseSource": null, - "reuseAction": "net-new generator reading existing migration rows and gate manifests without mutating them", - "destination": "`packages/xtask/src/bin/legacy_test_retirement.rs` (`cargo run -p xtask -- legacy-test-retirement`)", - "detailedDesign": "Reads the migration map's disposition rows, cross-references the live `tests/layer1-jobs.json`/gate manifests, and emits the delete set plus an absence assertion; never deletes automatically — it produces the gated checklist and the failing test that proves incomplete retirement", - "integration": "`make test-policy`/`make test-drift` row; wired to every current-code migration work item's removal proof", - "dataMigration": "None — docs/tooling only; no runtime state", - "validation": "Fixture with a replaced behavior whose old selector still appears in `tests/layer1-jobs.json` fails; once removed, the absence assertion passes", - "removalProof": "None — net-new; no prior owner to remove" - }, - { - "workItemId": "ADR046-streamline-024", - "specId": "ADR-046-streamline", - "specPath": "docs/specs/ADR-046-streamline.md", - "dependencyOwner": "ADR046-streamline-001", - "currentSource": "The graph is regenerated by hand today; `ADR-046-spec-set.json` and the test-runtime ledger (ADR046-delivery-007) each have their own emitter — duplicate-generator finding, retained here for reconciliation", - "reuseSource": null, - "reuseAction": "net-new generator that consumes the two manifests and the wave topology; folds the duplicate spec-set/test-runtime emitters into one canonical path", - "destination": "`packages/xtask/src/bin/implementation_graph.rs` (`cargo run -p xtask -- implementation-graph`); folds the spec-set and test-runtime emitters into the one `xtask` dispatch", - "detailedDesign": "Deterministic (sorted keys, no timestamps/host paths); regenerated after any spec or work-item change and after the two manifests; a drift gate runs it and `git diff --exit-code`. Retains the duplicate-generator findings for the spec-set manifest and the test-runtime ledger and reconciles them to the single `xtask` path rather than leaving three independent emitters", - "integration": "`tests/unit/gates/` drift gate alongside `xtask spec-registry`; consumed by the ready-wave query in `ADR-046-validation-and-delivery` §3.5.1", - "dataMigration": "None — docs/tooling only; no runtime state", - "validation": "`implementation_graph::every_spec_and_work_item_mapped_once`; `implementation_graph::acyclic_and_wave_monotonic`; `implementation_graph::parallel_groups_are_file_disjoint`; `implementation_graph_regenerates_clean`; duplicate-generator reconciliation asserts one canonical emitter path for spec-set and test-runtime", - "removalProof": "The ad hoc separate emitters for spec-set and test-runtime are removed after the single `xtask implementation-graph`/`spec-registry` dispatch reaches parity" - }, - { - "workItemId": "ADR046-systemd-001", - "specId": "ADR-046-provider-system-systemd", - "specPath": "docs/specs/providers/ADR-046-provider-system-systemd.md", - "dependencyOwner": "`ADR046-process-002`; Process contracts/supervisor owner; effect port interface owner", - "currentSource": "`packages/d2b-unsafe-local-helper/src/systemd.rs` — `SystemdUserScopeManager`, `VerifiedScope`; `packages/d2bd/src/supervisor/` — pidfd adoption, restart backoff", - "reuseSource": "Main `a1cc0b2d`: `d2b-session/src/engine.rs`, `d2b-session-unix/src/adapter.rs` (effect port test double session/transport)", - "reuseAction": "extract and adapt", - "destination": "`packages/d2b-provider-system-systemd/src/controller.rs` (async reconcile loop), `src/launch.rs` (launch algorithm via effect port), `src/effect_port.rs` (`SystemdProcessEffectPort` trait + test double), `src/adoption.rs` (adoption algorithm), `src/sandbox.rs` (SandboxSpec → unit property compiler)", - "detailedDesign": "Full §6 launch algorithm (effect port integration); §7 EphemeralProcess; §8 restart/adoption (effect port `locate_by_identity`); §9 drain (effect port `stop`/`kill`); §10 sandbox compilation; §11 bus services; ProviderSupervisor LaunchTicket integration", - "integration": "Core ProviderDeployment creates controller Process via Provider/system-minijail and creates/deletes one private Volume per component per execution target before/after component Processes (naming: `system-systemd--controller--main-state--`; ownerRef: Provider/system-systemd; kind=state; persistenceClass=persistent; minimal nonzero quota; identity marker; migrationPolicy=none; layout principal: Nix-preprovisioned `User/` or bounded principal pool; no ComponentPrincipal ResourceRef); Provider/volume-local is sole Volume reconciler; system-systemd controller does NOT create, own, or reconcile its Volume; controller consumes only the `controller-rw` view dirfd via its Process mounts; controller watches Process/EphemeralProcess; ProviderSupervisor calls LaunchProcess; effect port implementation injected by core supervisor spec", - "dataMigration": "No state migration; controller relists and adopts on restart", - "validation": "`tests/conformance.rs` (shared conformance kit); `tests/identity_binding.rs` (InvocationID/cgroup/MainPID/start-time golden vectors via mock effect port); `tests/adoption.rs` (quarantine/identity-mismatch cases); `tests/restart.rs` (backoff/maxRestarts); latency assertions (p95 ≤5 ms hint→handler, ≤20 ms commit→effect port `start` call)", - "removalProof": "`VmProcessDag` supervisor roles removed per role disposition table after each succeeds in conformance" - }, - { - "workItemId": "ADR046-systemd-002", - "specId": "ADR-046-provider-system-systemd", - "specPath": "docs/specs/providers/ADR-046-provider-system-systemd.md", - "dependencyOwner": "`ADR046-systemd-001`; Nix/package integrator", - "currentSource": "`nixos-modules/unsafe-local-helper.nix`; `nixos-modules/processes-json.nix`", - "reuseSource": null, - "reuseAction": "adapt", - "destination": "`nixos-modules/` (Provider ResourceSpec emission for `system-systemd`); `packages/d2b-provider-system-systemd/` package derivation and catalog entry", - "detailedDesign": "§16 Nix configuration; `d2b.artifacts.system-systemd` catalog entry; Provider and Process ResourceSpec emission; eval/build validation rules; drift gate update (`xtask gen-nix-options` + `make test-drift`)", - "integration": "Zone configuration activates Provider/system-systemd; Process resources reference it via `spec.providerRef = \"Provider/system-systemd\"`", - "dataMigration": "No configuration compatibility path; full reset at v3 cutover", - "validation": "`tests/unit/nix/cases/provider-system-systemd.nix` (eval-time validation); `tests/unit/gates/drift-check.sh` covers generated option schema", - "removalProof": "`nixos-modules/unsafe-local-helper.nix` removed after user-domain Host/Process parity" - }, - { - "workItemId": "ADR046-systemd-003", - "specId": "ADR-046-provider-system-systemd", - "specPath": "docs/specs/providers/ADR-046-provider-system-systemd.md", - "dependencyOwner": "`ADR046-systemd-001`; conformance kit / test infrastructure", - "currentSource": "`packages/d2bd/src/supervisor/` (existing process lifecycle tests); `packages/d2b-unsafe-local-helper/src/systemd.rs` (existing scope tests)", - "reuseSource": null, - "reuseAction": "copy/adapt", - "destination": "`packages/d2b-provider-system-systemd/tests/conformance.rs`, `tests/fault.rs`, `tests/ephemeral.rs`, `tests/sandbox_compile.rs`; `integration/host_scenario.rs`, `integration/guest_scenario.rs`", - "detailedDesign": "Full §19 test/integration requirements", - "integration": "`cargo test -p d2b-provider-system-systemd`; `make test-integration -- provider-system-systemd`; `make test-host-integration -- provider-system-systemd`", - "dataMigration": "None — full d2b 3.0 reset; no prior state to migrate", - "validation": "All conformance vectors pass; all fault injection scenarios reach expected phase/condition; all §19 Host and Guest test scenarios pass", - "removalProof": "No removal; tests are permanent" - }, - { - "workItemId": "ADR046-telem-001", - "specId": "ADR-046-telemetry-audit-and-support", - "specPath": "docs/specs/ADR-046-telemetry-audit-and-support.md", - "dependencyOwner": "W0/W1a; telemetry crate owner", - "currentSource": "`packages/d2b-realm-core/src/trace_context.rs` (`TraceContext`, `MAX_TRACE_FIELD_LEN`); `packages/d2b-realm-core/src/audit.rs` (`AuditHash`, `AuditHashError`, `AuditChainLink`, `AuditChainRecord`); `packages/d2b-realm-core/src/ids.rs` (`OperationId`, `CorrelationId`); `packages/d2b-realm-codec-protobuf/src/lib.rs` (`encode_trace_context`, `decode_trace_context`); `packages/d2b-contract-tests/tests/policy_observability.rs::startup_tracing_avoids_host_path_fields`", - "reuseSource": null, - "reuseAction": "extract unchanged (`TraceContext`, `AuditHash`, `AuditChainLink`); adapt (`OperationId`/`CorrelationId` for v3 record contract); add bounded emitter", - "destination": "`packages/d2b-telemetry/src/{trace_context.rs,audit_hash.rs,emitter.rs,meter_registry.rs,redaction_guard.rs}`", - "detailedDesign": "`d2b-telemetry` provides: (1) `TraceContext` / `AuditHash` / `AuditChainLink` extracted unchanged; (2) `BoundedEmitter`: `tracing`-subscriber layer that serializes span/metric events into compact frames and writes them over a private Unix datagram socket to the `observability-otel` Provider — no `opentelemetry_sdk` dependency; (3) `RedactionGuard` span wrapper that asserts the v3 resource attribute allowlist at span creation. No OTEL SDK in this crate.", - "integration": "Every v3 core process initializes a `BoundedEmitter` pointing at `$ZONE_STATE/telemetry/emitter.sock`; v3 audit records use `AuditHash`/`AuditChainLink` from this crate", - "dataMigration": "Full d2b 3.0 reset; no v2 state/config import", - "validation": "Unit test for `RedactionGuard` attribute gate; unit test for `BoundedEmitter` ring-full drop and FIFO drain; `policy_telemetry_redaction.rs::startup_tracing_avoids_host_path_fields` port; assert `config_source = \"realm-controllers\"` absent; assert no `opentelemetry_sdk` dependency in `d2b-telemetry` Cargo.toml", - "removalProof": "None — net-new; no prior owner to remove" - }, - { - "workItemId": "ADR046-telem-002", - "specId": "ADR-046-telemetry-audit-and-support", - "specPath": "docs/specs/ADR-046-telemetry-audit-and-support.md", - "dependencyOwner": "ADR046-telem-001 + ADR046-store-001; store owner", - "currentSource": "`packages/d2bd/src/metrics.rs` (`MetricDescriptor`, `MetricKind`, `VM_START_BUCKETS_SECONDS`, `BROKER_REQUEST_BUCKETS_SECONDS`, `ACTIVATION_PHASE_BUCKETS_SECONDS` — for bucket pattern reference only; the `vm` labels are not reused)", - "reuseSource": null, - "reuseAction": "adapt bucket boundary constants (rename; remove `vm` labels); replace hand-rolled `Registry` with `d2b-telemetry` `BoundedEmitter` meter API", - "destination": "`packages/d2b-resource-store-redb/src/metrics.rs`, `packages/d2b-resource-store-redb/src/tracing.rs`", - "detailedDesign": "Instrument the store actor, write/read/group-commit paths with the metric inventory from this spec via `d2b-telemetry` `BoundedEmitter`. Emit `d2b.store.*` spans. The p95 `d2b_store_write_duration_seconds` hard target (≤10 ms) feeds the benchmark fixture. No `vm` label; `resource_type` label only from closed catalog. No OTEL SDK in the store crate.", - "integration": "Store actor calls `d2b-telemetry` meter/tracer via `BoundedEmitter`; spans linked to API request spans via `TraceContext`", - "dataMigration": "Full d2b 3.0 reset; no v2 state/config import", - "validation": "p95 write ≤10 ms benchmark fixture; metric inventory policy test asserting no `vm` label; assert old `d2b_daemon_vm_state` shape absent", - "removalProof": "Hand-rolled registry in `d2bd/src/metrics.rs` retained until daemon-level ADR 0046 cutover" - }, - { - "workItemId": "ADR046-telem-003", - "specId": "ADR-046-telemetry-audit-and-support", - "specPath": "docs/specs/ADR-046-telemetry-audit-and-support.md", - "dependencyOwner": "ADR046-telem-001 + ADR046-session-001 + ADR046-bus-001; session/bus owner", - "currentSource": "`packages/d2b-realm-core/src/ids.rs` (`OperationId`, `CorrelationId`); `packages/d2b-realm-codec-protobuf/src/lib.rs` (`encode_trace_context`, `decode_trace_context` for v3 codec adaptation); `packages/d2b-realm-router/src/mux_session.rs`, `route_engine.rs` (current implemented-but-unwired generic routing)", - "reuseSource": null, - "reuseAction": "adapt `TraceContext` protobuf codec for v3 resource API framing; adapt routing metrics patterns", - "destination": "`packages/d2b-resource-api/src/metrics.rs`, `packages/d2b-session/src/metrics.rs`, `packages/d2b-bus/src/metrics.rs`", - "detailedDesign": "Instrument resource API verb dispatch, watch delivery, bus route resolution, and session handshake/reconnect per the metric/span catalog in this spec. Propagate `TraceContext` from incoming bus request to store write transaction span as child context.", - "integration": "ResourceClient → bus → API → store span chain via `TraceContext`", - "dataMigration": "Full d2b 3.0 reset; no v2 state/config import", - "validation": "API request metric inventory test; session profile/outcome label cardinality gate; bus direction label gate; assert no `realm` field in span attributes", - "removalProof": "None — net-new; no prior owner to remove" - }, - { - "workItemId": "ADR046-telem-004", - "specId": "ADR-046-telemetry-audit-and-support", - "specPath": "docs/specs/ADR-046-telemetry-audit-and-support.md", - "dependencyOwner": "ADR046-telem-001 + ADR046-core-001; core-controller owner", - "currentSource": "`packages/d2bd/src/metrics.rs` (`BROKER_REQUEST_BUCKETS_SECONDS` for reference; `d2b_daemon_broker_request_duration_seconds` labels `[\"op\"]` as a cardinality-safe example); `packages/d2b-realm-core/src/allocator_engine.rs` (field `trace: Option` at line 873 — existing trace context wiring pattern)", - "reuseSource": null, - "reuseAction": "adapt bucket patterns; adapt trace-context-in-reconcile pattern from `allocator_engine.rs`", - "destination": "`packages/d2b-core-controller/src/metrics.rs`, `packages/d2b-core-controller/src/tracing.rs`", - "detailedDesign": "Emit `d2b.controller.hint` span at the instant the post-commit dispatcher fires; emit `d2b.controller.reconcile` child span at handler entry. Interval = p95 ≤5 ms target. `handler` label from closed set; no resource name labels.", - "integration": "Post-commit dispatcher creates hint span; handler creates child reconcile span via `TraceContext`", - "dataMigration": "Full d2b 3.0 reset; no v2 state/config import", - "validation": "`hint_to_handler_latency` benchmark with p95 ≤5 ms assertion; closed `handler` label set gate", - "removalProof": "None — net-new; no prior owner to remove" - }, - { - "workItemId": "ADR046-telem-005", - "specId": "ADR-046-telemetry-audit-and-support", - "specPath": "docs/specs/ADR-046-telemetry-audit-and-support.md", - "dependencyOwner": "ADR046-telem-001 + ADR046-process-001; Process Provider owner", - "currentSource": "`packages/d2bd/src/metrics.rs` (`d2b_daemon_vm_start_duration_seconds` `VM_START_BUCKETS_SECONDS`; `d2b_daemon_vm_shutdown_duration_seconds` `VM_SHUTDOWN_BUCKETS_SECONDS`; `d2b_daemon_vm_shutdown_total` labels `[\"vm\", \"vmm\", \"outcome\"]` — `vmm` = current `RunnerRole` → v3 `provider`); `packages/d2bd/src/supervisor/pidfd.rs` (pidfd adoption/launch call sites); `packages/d2b-contracts/src/broker_wire.rs::RunnerRole` (`CloudHypervisor`, `QemuMedia`, `OtelHostBridge` etc. → v3 `provider` label values)", - "reuseSource": null, - "reuseAction": "adapt launch histogram bucket constants; rename `vm` label to no label (process identity in resource attributes); rename `vmm`/`RunnerRole` → `provider` closed enum", - "destination": "`packages/d2b-provider-supervisor/src/metrics.rs`, `packages/d2b-provider-supervisor/src/tracing.rs`", - "detailedDesign": "`d2b_process_launch_duration_seconds`: start = instant Process controller receives commit-to-Ready hint; end = first OS spawn call (clone3 or systemd unit start). This implements p95 ≤20 ms. `provider` label replaces `vmm`/`RunnerRole` with the closed set `{minijail,systemd}`. No `vm` name label. A separate `d2b_process_ready_duration_seconds` histogram covers launch-attempt → readiness signal (not a hard target).", - "integration": "Process Provider controller start handler → supervisor ticket delivery → first spawn call", - "dataMigration": "Full d2b 3.0 reset; no v2 state/config import", - "validation": "`commit_to_launch_latency` benchmark with p95 ≤20 ms assertion; assert no `vm` label in process metrics; `vmm→provider` label rename gate", - "removalProof": "`d2b_daemon_vm_start_duration_seconds` (with `vm` label) retained in `d2bd/src/metrics.rs` until daemon-level ADR 0046 cutover" - }, - { - "workItemId": "ADR046-telem-006", - "specId": "ADR-046-telemetry-audit-and-support", - "specPath": "docs/specs/ADR-046-telemetry-audit-and-support.md", - "dependencyOwner": "ADR046-process-001 + ADR046-provider-001; `observability-otel` Provider owner", - "currentSource": "`nixos-modules/components/observability/host.nix` (`otelRuntimeDir = \"/run/d2b/otel\"`, `hostEgressSocket`, ACL `setfacl` pattern, `scrapeJournal`, `hostCfg.identityName`); `nixos-modules/components/observability/stack.nix` (SigNoz stack, `ingressSources`, per-source `vmName`, `receiverGrpcPort`/`receiverHttpPort`, loopback binding, `cfg.signoz.listenPort`); `nixos-modules/components/observability/guest.nix` (`vm.name`/`vm.env`/`vm.role` identity stamping, guest collector); `packages/d2b-host/src/otel_host_bridge_argv.rs` (`OtelHostBridgeArgvInputs`; vsock forwarding); `packages/d2bd/src/otel_host_bridge_readiness.rs` (readiness gate pattern: `OtelHostBridgeReadiness::{Ready,Pending,Failed}`); `packages/d2b-core/src/processes.rs::ProcessRole::OtelHostBridge`; `packages/d2b-contracts/src/broker_wire.rs::RunnerRole::OtelHostBridge`; `packages/d2b-contract-tests/tests/{policy_observability.rs,minijail_relay_otel.rs}`", - "reuseSource": null, - "reuseAction": "adapt Nix pipeline shape (replace per-VM `vmName` with per-Zone naming); adapt `OtelHostBridgeArgvInputs` vsock forwarding to native OTLP/gRPC-over-vsock; adapt readiness gate pattern (`OtelHostBridgeReadiness::Ready` → Provider phase `Ready`); adapt `ingressSources` per-VM → per-Zone", - "destination": "`packages/d2b-provider-observability-otel/src/`, `nixos-modules/components/observability/` (adapted files)", - "detailedDesign": "`Provider/observability-otel` is an **ordinary optional non-bootstrap Process** (not counted toward the ≤64 MiB mandatory core aggregate). It owns: (1) per-Zone datagram receiver socket at `$ZONE_STATE/telemetry/emitter.sock` (drains frames from core emitters) and OTLP/gRPC Unix socket at `$ZONE_STATE/telemetry/otlp.sock`; (2) the full OTEL SDK with OTLP exporter — only this process links `opentelemetry_sdk`; (3) OTel Collector pipeline per Zone and per Host; (4) vsock OTLP forwarding to obs Zone (replaces socat-based `OtelHostBridgeArgvInputs`); (5) SigNoz stack Nix adapted from `stack.nix` with per-Zone `ingressSources` replacing per-VM `vmName`; (6) journald scrape (optional, disabled by default); (7) self-metrics endpoint. Zone/controller startup does not wait for this Provider. If absent or unready, Zone health is `Degraded` (not `Failed`). Readiness: socket exists and first drain cycle completes successfully. `d2b.observability.host.identityName` option preserved; `vmName` in `ingressSources` populated from Zone name.", - "integration": "Core process `BoundedEmitter` → `emitter.sock` → observability-otel collector → `otlp.sock` → vsock → obs Zone SigNoz; Zone startup independent of Provider readiness", - "dataMigration": "Existing SigNoz data not migrated; v3 starts fresh", - "validation": "`emitter_socket_receive`, `emitter_ring_drains_on_socket_available`, `emitter_ring_drop_on_overflow`, `no_vm_label_in_metrics`, `zone_startup_proceeds_without_provider` tests; adapted `policy_observability.rs` tests (retain `loki_native_otel_resource_attributes` and SigNoz-only backend assertions); adapted `minijail_relay_otel.rs` shape test for Provider-managed runner", - "removalProof": "`otel_host_bridge_argv.rs` socat runner and `otel_host_bridge_readiness.rs` retired after `observability-otel` Provider delivers native OTLP/vsock and passes conformance; `ProcessRole::OtelHostBridge` and `RunnerRole::OtelHostBridge` retired from `d2b-core/src/processes.rs` and `d2b-contracts/src/broker_wire.rs` after Provider migration" - }, - { - "workItemId": "ADR046-telem-007", - "specId": "ADR-046-telemetry-audit-and-support", - "specPath": "docs/specs/ADR-046-telemetry-audit-and-support.md", - "dependencyOwner": "ADR046-telem-006; Nix/observability owner", - "currentSource": "`nixos-modules/components/observability/host.nix` (`scrapeJournal = hostCfg.scrapeJournal`, `journaldStorageDir = \"/var/lib/d2b-host-otel-collector/journald\"`, cgroup-path filtering pattern for host units)", - "reuseSource": null, - "reuseAction": "adapt journald receiver config for per-Zone cgroup filter (`z-/*`, `s-/*`)", - "destination": "`packages/d2b-provider-observability-otel/src/nix/journald.nix` (new Nix fragment)", - "detailedDesign": "`d2b.zones..observability.journald.enable = false` (default). When enabled: journald receiver follows `z-/*` and `s-/*` cgroup filters. Collector applies redaction: drops `MESSAGE` credential/path patterns, `_CMDLINE`, `_EXE`, `INVOCATION_ID`. Current `scrapeJournal` host option is preserved unchanged.", - "integration": "`observability-otel` Provider Nix config → OTel Collector journald receiver → redaction filter → obs Zone", - "dataMigration": "Full d2b 3.0 reset; no v2 state/config import", - "validation": "Nix eval test: filter expression set when enabled; test that `_CMDLINE` and `INVOCATION_ID` appear in drop list", - "removalProof": "None — net-new; no prior owner to remove" - }, - { - "workItemId": "ADR046-telem-008", - "specId": "ADR-046-telemetry-audit-and-support", - "specPath": "docs/specs/ADR-046-telemetry-audit-and-support.md", - "dependencyOwner": "ADR046-telem-001; policy/contract-tests owner", - "currentSource": "`packages/d2b-contract-tests/tests/policy_observability.rs` (`loki_native_otel_resource_attributes` allowlist: `[\"deployment.environment\",\"host.name\",\"service.name\",\"service.namespace\",\"source\",\"vm.env\",\"vm.name\",\"vm.role\"]`; `tempo_stack_signoz_backend_and_collector` SigNoz-only assertion; `startup_tracing_avoids_host_path_fields` forbidden fields); `packages/d2b-contract-tests/tests/policy_metrics.rs` (`EXPECTED_METRICS` table parity with `docs/reference/daemon-metrics.md`)", - "reuseSource": null, - "reuseAction": "adapt and extend; keep existing tests; add new policy gates", - "destination": "`packages/d2b-contract-tests/tests/policy_telemetry_redaction.rs` (new); updated `policy_observability.rs`; updated `policy_metrics.rs`", - "detailedDesign": "(1) Extend `loki_native_otel_resource_attributes` allowlist to include `d2b.zone`, `d2b.provider`, `d2b.component`, `service.version`. (2) Add redaction lint: scan all v3 instrumentation call sites for `realm`, `workload_id`, `node_id`, `vm` (as label key), `path`, `socket`, `argv`, `pid`, `exe`. (3) Add metric label gate: assert no v3 `MetricDescriptor` carries a `vm` label. (4) Add bucket boundary gates for 5 ms and 20 ms. (5) Retain: `startup_tracing_avoids_host_path_fields`; SigNoz-only backend assertion; `tempo_guest_collector_shape`; `config_source = \"realm-controllers\"` absence gate.", - "integration": "Contract-tests run in workspace check and `make test-drift`", - "dataMigration": "Full d2b 3.0 reset; no v2 state/config import", - "validation": "These tests are their own validation artifact", - "removalProof": "None — net-new; no prior owner to remove" - }, - { - "workItemId": "ADR046-telem-009", - "specId": "ADR-046-telemetry-audit-and-support", - "specPath": "docs/specs/ADR-046-telemetry-audit-and-support.md", - "dependencyOwner": "W0/W1a; Nix integrator (adapts `nixos-modules/options-observability.nix` and `options-realms.nix`)", - "currentSource": "`nixos-modules/options-observability.nix` (`d2b.observability.host.identityName`, `scrapeJournal`, `otlpIngest.enable`, `signoz.jwtSecretFile`, `signoz.rootPasswordFile`, `signoz.clickhousePasswordFile`, `retention.*`, `sampling.*` — all predecessor options); `nixos-modules/options-realms.nix` (submodule/option-type/assertion pattern); `nixos-modules/components/observability/{host,stack,guest}.nix` (Nix pipeline shape, ACL pattern, `identityName`/`vmName`); `nixos-modules/manifest.nix` (resource-bundle emission pattern); `packages/d2b-contract-tests/tests/policy_observability.rs::loki_native_otel_resource_attributes` (closed allowlist enforcement — adapt as bundle schema policy test)", - "reuseSource": null, - "reuseAction": "Implement uniform `d2b.zones..resources. = { type; spec; }` option with schema-driven `spec.*` generated option types; adapt option submodule pattern from `options-realms.nix`; adapt pipeline shape from `{host,stack,guest}.nix`; emit canonical sorted ResourceSpec JSON from `resources-bundle.nix`", - "destination": "`nixos-modules/resources.nix` (uniform `d2b.zones..resources` schema-aware option; `spec.*` option types generated from `ResourceTypeSchema` for each `type`); `nixos-modules/resources-bundle.nix` (ADR-only: sorted integrity-pinned bundle derivation)", - "detailedDesign": "(1) Implement `d2b.zones..resources = lib.mkOption { type = lib.types.attrsOf (schemaAwareResourceSubmodule); }` where the submodule, given `config.type`, loads the registered `ResourceTypeSchema` and generates `spec.*` option types from it. For `type = \"Provider\"`, `spec.config.*` options are generated from the signed Provider schema for the package identified by `spec.artifactId` (see ADR-046-provider-model-and-packaging). No second bespoke vocabulary; `spec` fields mirror the canonical JSON fields exactly. (2) `resources-bundle.nix` derivation: serialize each resource to canonical sorted JSON (keys alphabetically sorted at every level); sort resources by `(type, name)`; compute generation digest; emit `zone-resources-.json` as Nix store output. Publication handler sets `metadata.managedBy = \"configuration\"` and `metadata.configurationGeneration` on activation — these fields are NOT authored in Nix. (3) `status`, UID, generation, revision, and timestamps are absent from Nix authoring; core fills them.", - "integration": "`d2b-core-controller` reads the Nix store path from the activated system closure; secrets never appear in the bundle", - "dataMigration": "Current `d2b.observability.*` options are retained with compat warnings (same pattern as current `retention.*`/`sampling.*` compat options); the v3 `d2b.zones..resources.*` option is the authoritative surface", - "validation": "`eval_rejects_unknown_type`, `eval_rejects_invalid_emitter_ring_size`, `eval_rejects_unknown_provider_settings`, `eval_rejects_inline_secret_in_settings`, `eval_rejects_unresolved_credential_ref`, `eval_rejects_duplicate_resource_name` nix-unit cases; `bundle_is_sorted_canonically`, `bundle_digest_is_deterministic`, `bundle_contains_no_secret_values`, `bundle_schema_validates_against_provider_schema` contract tests", - "removalProof": "`nixos-modules/options-observability.nix` predecessor options retained with compat warnings until `d2b.observability.enable` migration is complete" - }, - { - "workItemId": "ADR046-telem-010", - "specId": "ADR-046-telemetry-audit-and-support", - "specPath": "docs/specs/ADR-046-telemetry-audit-and-support.md", - "dependencyOwner": "ADR046-telem-009 + ADR046-telem-006 + ADR046-store-001; schema/validation owner", - "currentSource": "`packages/d2b-contract-tests/tests/policy_observability.rs::startup_tracing_avoids_host_path_fields` (forbidden-field pattern enforcement — adapt as bundle forbidden-field gate); `packages/d2b-contract-tests/tests/policy_metrics.rs` (metric inventory policy test pattern); `packages/d2b-priv-broker/src/runtime.rs` (current runtime schema load/verify pattern); `packages/d2b-contracts/src/provider_registry_v2.rs::ProviderBindingV2` (non-exhaustive signed schema contract)", - "reuseSource": null, - "reuseAction": "Adapt `startup_tracing_avoids_host_path_fields` forbidden-field pattern for bundle schema gate; adapt `ProviderBindingV2` non-exhaustive contract for Provider-specific settings schema fingerprint", - "destination": "`nixos-modules/resources-bundle.nix` (build-time validation step 4 in the `resources-bundle` derivation); `packages/d2b-core-controller/src/configuration.rs` (runtime activation checks)", - "detailedDesign": "Build-time: (1) For each `Provider` resource, fetch the `resourceTypeSchema` output from the package; validate `settings` JSON against the JSON Schema; fail the build on schema mismatch or unknown fields. (2) Assert no resource spec field contains a bare secret/path/argv (forbidden-field pattern from `startup_tracing_avoids_host_path_fields`). Runtime: (3) Core-controller re-validates Provider package identity (per ADR-046-provider-model-and-packaging) against the installed package; resolves Credential refs; checks conflict/bounds; rejects with closed-enum `generation-rejected` reason on any failure; no partial activation. (4) Provider schema mismatch between the bundle's schema and the installed Provider's live schema → reject, emit `generation-rejected{reason=\"package-identity-mismatch\"}`.", - "integration": "Nix `resources-bundle.nix` derivation gate + core-controller `configuration.rs` activation path", - "dataMigration": "Full d2b 3.0 reset; no v2 state/config import", - "validation": "`bundle_schema_validates_against_provider_schema` bundle contract test; `generation_rejected_emits_audit_record` cleanup contract test with a `schema-validation-failed` reason; add a nix-unit case `eval_rejects_unknown_fields_against_signed_schema` that runs the bundle derivation with a schema mismatch and asserts build failure", - "removalProof": "None — net-new; no prior owner to remove; this is new tooling" - }, - { - "workItemId": "ADR046-telem-011", - "specId": "ADR-046-telemetry-audit-and-support", - "specPath": "docs/specs/ADR-046-telemetry-audit-and-support.md", - "dependencyOwner": "ADR046-telem-009 + ADR046-telem-010 + ADR046-audit-001 + ADR046-store-001; core-controller owner", - "currentSource": "`packages/d2bd/src/daemon_audit.rs` (hash-chain `ResourceMutation`-like append pattern — adapt for cleanup audit records); `packages/d2b-priv-broker/src/audit.rs` (`AuditWriteClass::{Standard,Unprivileged}` — cleanup audit records use `Standard` durability); `packages/d2b-realm-core/src/audit.rs::AuditChainLink::new` (hash-chain append for cleanup audit records); `nixos-modules/manifest.nix` (prior-generation retention pattern in the current bundle contract)", - "reuseSource": null, - "reuseAction": "Adapt hash-chain append from `daemon_audit.rs` for `ResourceMutation{trigger=\"config-cleanup\"}` records; adapt prior-generation retention window from `manifest.nix` pattern", - "destination": "`packages/d2b-core-controller/src/{configuration.rs, ownership.rs}`", - "detailedDesign": "(1) On new generation activation, every stored `managedBy=configuration` resource absent from the new configured set receives `deletionRequestedAt` plus `deletion-pending`; controller/API-managed resources are untouched. (2) Activation returns after durable intent queueing and does not wait for cleanup. (3) The ownership handler drives child-before-parent finalizers. (4) When finalizers clear, one atomic store transaction writes the `Deleted` revision/change event and removes the row and indexes. After commit, the audit subsystem appends `ResourceMutation{event=\"deleted\", trigger=\"config-cleanup\"}` from that revision using a dedup/exactly-once recovery key; audit append is not part of the store transaction. (5) Stall detection sets `cleanup-stalled` without force-removing finalizers. (6) Prior generations use count retention, default 3 and range 1..16, with no TTL. (7) Core sets `managedBy`/`configurationGeneration` in persisted resources; input bundles omit both.", - "integration": "`d2b-core-controller::configuration.rs` (generation activation); `d2b-core-controller::ownership.rs` (cleanup ordering and atomic final deletion); `d2b-audit` sink (cleanup audit records)", - "dataMigration": "None — the `managedBy`/`configurationGeneration`/`deletionRequestedAt` fields are new; existing resources gain them on first v3 activation", - "validation": "All tests in \"Configuration-owned cleanup contract tests\" subsection; additionally: `managedby_configuration_set_on_activated_resources`, `controller_created_resources_have_managedby_controller`, `absent_resource_receives_delete_on_new_generation`, `deletion_sets_deletionrequestedat_not_phase`, `final_deletion_is_atomic`, `cleanup_does_not_touch_controller_children`, `pending_cleanup_condition_set_on_zone`, `zone_is_degraded_not_failed_during_cleanup`, `pending_cleanup_cleared_after_deletion_completes`, `prior_generation_retained_count_based`, `rollback_schedules_delete_for_new_generation_resources`, `audit_segments_preserved_on_provider_delete`, `cleanup_stall_condition_set`, `generation_rejected_emits_audit_record`", - "removalProof": "None — net-new; no prior owner to remove; this is new behavior" - }, - { - "workItemId": "ADR046-transport-relay-001", - "specId": "ADR-046-provider-transport-azure-relay", - "specPath": "docs/specs/providers/ADR-046-provider-transport-azure-relay.md", - "dependencyOwner": "W0 shared contract root; ComponentSession transport adapter owner", - "currentSource": "`packages/d2b-provider-relay/src/lib.rs` (`RelayEndpoint`, `RelayCredential`, `RelayRole`, `RelayStream`, `connect()`, `listen()`, `mint_sas()`)", - "reuseSource": null, - "reuseAction": "extract and adapt", - "destination": "`packages/d2b-provider-transport-azure-relay/src/relay_transport.rs`", - "detailedDesign": "Adapt `RelayStream` as relay transport service process; expose named opaque byte stream on the `transport-service` Unix endpoint; add 2-byte length-prefixed framing; preserve credential redaction; TLS/WebSocket state stays in-process — only Noise record bytes traverse the named stream; register named stream with d2b-bus as `TransportHandle`; transport descriptor: `attachment_support: false`, `locality: Remote`, `atomic: false`; expose `OpenTransport`/`CloseTransport`/`ObserveTransport` interface to core; long-lived service process multiplexes sessions internally", - "integration": "Core ZoneLink controller calls `OpenTransport(spec.provider.settings)` → receives named byte stream handle; relay service cannot interpret plaintext bytes; one carriage per call; WebSocket loss closes the named stream", - "dataMigration": "No compatibility with current relay sessions; v3 sessions are independent", - "validation": "`tests/fake_relay_transport.rs`: connect/accept, framing, credential redaction, named stream roundtrip; `tests/listener_sender_conformance.rs`: named stream contract; Noise KK binding; relay identity exclusion", - "removalProof": "`d2b-provider-relay/src/lib.rs` relay plumbing retained until ACA display migration completes" - }, - { - "workItemId": "ADR046-transport-relay-002", - "specId": "ADR-046-provider-transport-azure-relay", - "specPath": "docs/specs/providers/ADR-046-provider-transport-azure-relay.md", - "dependencyOwner": "ADR046-transport-relay-001; Credential KK session; ComponentSession/d2b-bus owner", - "currentSource": "None (new)", - "reuseSource": null, - "reuseAction": "new", - "destination": "`packages/d2b-provider-transport-azure-relay/src/credential_client.rs`", - "detailedDesign": "Async Credential KK session client for service components (service processes have d2b-bus access, enabling Credential KK; workers do not); acquire listener credential via KK inside the gateway Guest using `config.credentialBindings[listenerCredentialAlias]`; acquire sender credential independently inside the child's gateway Guest via the same KK model using `config.credentialBindings[senderCredentialAlias]`; raw credential bytes held in zeroizing memory inside the gateway Guest, presented to Azure Relay, then immediately zeroized; no credential bytes cross process, network, or Guest boundary; redacted Debug; no credential bytes in logs/audit/OTEL; core ProviderDeployment creates a private persistent Volume (per ADR-046-provider-state) for each component before its Process starts — the transport Provider does not own or create these Volumes; `Provider/volume-local` reconciles them; `migrationPolicy: none` means no migration worker is ever spawned; no relay auth token, WebSocket handle, session key, or credential byte is written to that Volume; all relay session state remains transient in-process memory", - "integration": "Listener service invokes before each relay connect attempt inside gateway Guest; child Zone's sender service acquires its own credential independently inside its own gateway Guest", - "dataMigration": "None — full d2b 3.0 reset; no prior state to migrate", - "validation": "`tests/credential_redaction.rs`: credential bytes never reach any Debug/log/audit/OTEL path; `src/tests/integration/credential_delivery.rs`: end-to-end credential delivery using injected fake Credential effect port", - "removalProof": "N/A; new module" - }, - { - "workItemId": "ADR046-transport-relay-003", - "specId": "ADR-046-provider-transport-azure-relay", - "specPath": "docs/specs/providers/ADR-046-provider-transport-azure-relay.md", - "dependencyOwner": "ADR046-transport-relay-001; reconnect contract; ZoneLink handler", - "currentSource": "None (new; core drives reconnect, not the transport Provider)", - "reuseSource": null, - "reuseAction": "new", - "destination": "`packages/d2b-provider-transport-azure-relay/src/reconnect.rs`", - "detailedDesign": "Relay service responds to `CloseTransport`+`OpenTransport` cycle from core; core owns reconnect policy and backoff scheduling; relay service tears down the current WebSocket when core calls `CloseTransport` and establishes a new WebSocket connection when core calls `OpenTransport`; relay service does not maintain a backoff state machine or independently retry — it starts a new WebSocket on demand and emits the connect result via `ObserveTransport`; listener and sender are long-lived service processes that do not re-spawn on reconnect", - "integration": "`ObserveTransport` delivers `TransportObservation::Disconnected` to core; core drives reconnect via `CloseTransport` then `OpenTransport` after applying its own backoff", - "dataMigration": "None — full d2b 3.0 reset; no prior state to migrate", - "validation": "`tests/reconnect_backoff.rs`: relay responds to CloseTransport/OpenTransport cycle; WebSocket starts on demand; ObserveTransport reports connect result; `src/tests/integration/reconnect_scenario.rs`: full reconnect cycle including Credential re-acquisition", - "removalProof": "N/A; new module" - }, - { - "workItemId": "ADR046-transport-relay-004", - "specId": "ADR-046-provider-transport-azure-relay", - "specPath": "docs/specs/providers/ADR-046-provider-transport-azure-relay.md", - "dependencyOwner": "ADR046-transport-relay-001; transport settings schema; Nix configuration owner", - "currentSource": "`docs/specs/ADR-046-zone-routing.md` transport settings Nix example", - "reuseSource": null, - "reuseAction": "new", - "destination": "`packages/d2b-provider-transport-azure-relay/src/transport_settings.rs`; `docs/reference/schemas/v3/providers/transport-azure-relay.transport-settings.json`", - "detailedDesign": "`AzureRelayTransportSettings` Rust struct with serde; validation against committed JSON Schema; reject `secret`-annotated fields; enforce `^[a-z][a-z0-9-]*$` pattern for `listenerCredentialAlias`/`senderCredentialAlias` alias ID fields (never `Credential/` refs); xtask `gen-provider-transport-schemas` integration", - "integration": "`make test-drift` gate: `xtask gen-provider-transport-schemas && git diff --exit-code`", - "dataMigration": "None — full d2b 3.0 reset; no prior state to migrate", - "validation": "`tests/transport_settings_schema.rs`: valid/invalid schema vectors; eval-time Nix assertion coverage from `nix-unit: transport-settings-secret-key` test (see zone-routing spec)", - "removalProof": "N/A; new contract" - }, - { - "workItemId": "ADR046-transport-relay-005", - "specId": "ADR-046-provider-transport-azure-relay", - "specPath": "docs/specs/providers/ADR-046-provider-transport-azure-relay.md", - "dependencyOwner": "ADR046-transport-relay-001; backpressure/credit contract", - "currentSource": "`packages/d2b-session/src/scheduler.rs`, `streams.rs` (main commit `a1cc0b2d`)", - "reuseSource": null, - "reuseAction": "adapt", - "destination": "`packages/d2b-provider-transport-azure-relay/src/backpressure.rs`", - "detailedDesign": "Outbound WebSocket send buffer bounded at `MAX_AGGREGATE_NAMED_STREAM_QUEUE_BYTES`; relay WebSocket write backpressure propagates to `FairScheduler` credit; `d2b_relay_transport_backpressure_events_total` counter emitted; no unbounded memory growth under slow relay", - "integration": "Named stream send on `transport-service` Unix endpoint blocks on relay WebSocket write; d2b-bus `FairScheduler` observes backpressure via credit stall", - "dataMigration": "None — full d2b 3.0 reset; no prior state to migrate", - "validation": "`tests/backpressure_credit.rs`: slow relay writer saturates outbound queue; named-stream credit stalls before unbounded growth; source Zone never buffers beyond aggregate limit", - "removalProof": "N/A; new module" - }, - { - "workItemId": "ADR046-transport-relay-006", - "specId": "ADR-046-provider-transport-azure-relay", - "specPath": "docs/specs/providers/ADR-046-provider-transport-azure-relay.md", - "dependencyOwner": "ADR046-transport-relay-001 through ADR046-transport-relay-005; telemetry/audit owner", - "currentSource": "`packages/d2bd/src/metrics.rs` (hand-rolled Prometheus; baseline)", - "reuseSource": null, - "reuseAction": "new", - "destination": "`packages/d2b-provider-transport-azure-relay/src/{metrics.rs, audit.rs}`", - "detailedDesign": "Emit all OTEL metrics and audit records listed in §OTEL and §Audit; closed label sets; never label secret bytes; provider audit covers **carriage authentication and health observations only** — Azure auth events, WebSocket lifecycle, credential acquisition outcomes — and is **separate from resource audit** (resource lifecycle events are owned by core); audit records appended through the Zone runtime audit log interface (no atomicity guarantee with Zone resource state in redb; best-effort delivery per the Zone's audit provider configuration); OTEL via lightweight emitter ring (no direct OTEL SDK dependency in Provider)", - "integration": "`Provider/observability-otel` receives emitter ring frames; audit log via Zone runtime `d2b.audit.transport` category", - "dataMigration": "None — full d2b 3.0 reset; no prior state to migrate", - "validation": "`tests/credential_redaction.rs` extended to cover audit/OTEL paths; `tests/fake_relay_transport.rs` asserts audit record fields against schema", - "removalProof": "N/A; new module" - }, - { - "workItemId": "ADR046-transport-relay-007", - "specId": "ADR-046-provider-transport-azure-relay", - "specPath": "docs/specs/providers/ADR-046-provider-transport-azure-relay.md", - "dependencyOwner": "Provider crate owner; integration test owner", - "currentSource": "None — net-new v3 work; no pre-ADR45 baseline equivalent", - "reuseSource": null, - "reuseAction": "new", - "destination": "`packages/d2b-provider-transport-azure-relay/src/tests/integration/README`", - "detailedDesign": "Required content: fake relay server setup and teardown using the injected fake Relay effect port; how to run hermetic integration tests without a live Azure service; how to configure the injected fake Credential effect port for credential delivery tests; how to run with a real Azure namespace (requires a `Credential` resource declared in `spec.config.credentialBindings`, not environment-variable credential paths); integration test scenarios and expected outcomes; CI/local execution instructions", - "integration": "`make test-integration` invokes `tests/integration/containers/` scenarios which inject the fake relay and credential port implementations from `src/tests/integration/fake_relay_server.rs`", - "dataMigration": "None — full d2b 3.0 reset; no prior state to migrate", - "validation": "File must be present; workspace policy gate enforces `src/tests/integration/README`", - "removalProof": "N/A; mandatory layout" - }, - { - "workItemId": "ADR046-transport-unix-001", - "specId": "ADR-046-provider-transport-unix", - "specPath": "docs/specs/providers/ADR-046-provider-transport-unix.md", - "dependencyOwner": "ADR046-session-001 (v3 ComponentSession contracts); W0 shared contract root", - "currentSource": "`packages/d2b-contracts/src/v2_component_session.rs` (protocol constants, credit-class constants) at main `a1cc0b2d`", - "reuseSource": "Same file; constants copied into `packages/d2b-contracts/src/v3/zone_session.rs` by ADR046-session-001", - "reuseAction": "Dependency on ADR046-session-001 output", - "destination": "`packages/d2b-provider-transport-unix/src/credit.rs` (imports `MAX_PACKET_ATTACHMENTS=32`, `RESERVED_CONTROL_FDS=64`, credit-class constants from v3 contract); `src/portal.rs` (imports `MAX_PACKET_ATTACHMENTS` for portal validation)", - "detailedDesign": "Import credit scope capacities and headroom from `v3_zone_session.rs`; add `MAX_OPEN_TRANSPORTS: usize = 256` local constant for handle table bound", - "integration": "`CreditScopeSet` constructed from imported constants at session setup", - "dataMigration": "None; v3 constants freeze independently", - "validation": "`tests/credit.rs::ancillary_capacity_is_derived_from_closed_hard_bounds` passes against v3 constants", - "removalProof": "No current code imports v3 transport constants; new import" - }, - { - "workItemId": "ADR046-transport-unix-002", - "specId": "ADR-046-provider-transport-unix", - "specPath": "docs/specs/providers/ADR-046-provider-transport-unix.md", - "dependencyOwner": "ADR046-transport-unix-001; d2b-bus transport layer (ADR046-bus-001)", - "currentSource": "`packages/d2b-session-unix/src/{adapter,socket,descriptor}.rs`, `tests/unix_session.rs` at main `a1cc0b2d`", - "reuseSource": "Same; `UnixSeqpacketTransport`, `PeerIdentityPolicy`, `UnixAttachmentPayload`, `OwnedUnixAttachment`, `SeqpacketSocket`, `PeerCredentials`, `ObjectIdentity`, `AcceptedAttachment`, `VerifiedPacket`", - "reuseAction": "copy and adapt", - "destination": "`packages/d2b-provider-transport-unix/src/{seqpacket,identity,socket}.rs`", - "detailedDesign": "Copy transport structs verbatim; adapt `PeerIdentityPolicy` to report `PeerCredentials` upward to ComponentSession for subject mapping (not for direct resource lookup — that is core's responsibility); maintain `SO_PASSCRED` setup and first-packet credential extraction as documented; CLOEXEC enforcement uses `rustix` syscall wrappers over `libc` where available", - "integration": "`portal.rs::open_transport` calls `SeqpacketSocket::getsockopt(SO_TYPE)` and `setsockopt(SO_PASSCRED)`, constructs `UnixSeqpacketTransport`, hands OwnedTransport FD back to caller", - "dataMigration": "None — full d2b 3.0 reset; no prior state to migrate", - "validation": "Copy all 12 test functions; add `peercred_reported_to_componentsession_not_resolved_to_subject_here`", - "removalProof": "`d2b-realm-transport` seqpacket path retired after ZoneLink sessions migrate" - }, - { - "workItemId": "ADR046-transport-unix-003", - "specId": "ADR-046-provider-transport-unix", - "specPath": "docs/specs/providers/ADR-046-provider-transport-unix.md", - "dependencyOwner": "ADR046-transport-unix-001", - "currentSource": "`packages/d2b-session-unix/src/adapter.rs` `UnixStreamTransport`, `src/socket.rs` `StreamSocket` at main `a1cc0b2d`", - "reuseSource": "Same", - "reuseAction": "copy unchanged", - "destination": "`packages/d2b-provider-transport-unix/src/{stream,socket}.rs`", - "detailedDesign": "Copy verbatim; add `attachment_support: false` in `TransportDescriptor` (stream never carries SCM_RIGHTS regardless of route class); `admission.rs::validate_route_class` rejects `attachments_enabled=true` for stream", - "integration": "Same path as seqpacket but without SCM_RIGHTS paths", - "dataMigration": "None — full d2b 3.0 reset; no prior state to migrate", - "validation": "`tests/portal.rs::stream_open_transport_forces_no_attachments`; `tests/identity.rs::stream_transport_reassembles_partial_and_coalesced_records`", - "removalProof": "No current stream ZoneLink path exists; stream is net-new" - }, - { - "workItemId": "ADR046-transport-unix-004", - "specId": "ADR-046-provider-transport-unix", - "specPath": "docs/specs/providers/ADR-046-provider-transport-unix.md", - "dependencyOwner": "ADR046-transport-unix-001", - "currentSource": "`packages/d2b-session-unix/src/credit.rs` at main `a1cc0b2d`", - "reuseSource": "Same; `CreditPool`, `CreditScopeSet`, `CreditBundle`, `ProcessCreditLimit`, `CreditScope`", - "reuseAction": "copy unchanged", - "destination": "`packages/d2b-provider-transport-unix/src/credit.rs`", - "detailedDesign": "Copy all five types verbatim; import scope-capacity constants from v3 contract; add `#[derive(Debug)]` with redacted Display (no raw counts in Debug output)", - "integration": "`CreditScopeSet` created per active ComponentSession; `CreditBundle` per packet receive; credits released in `UnixAttachmentPayload::close()`", - "dataMigration": "None — full d2b 3.0 reset; no prior state to migrate", - "validation": "Copy all 4 credit test functions; add `credit_released_on_attachment_close` and `emergency_headroom_constant_across_fd_counts`", - "removalProof": "No current code path uses this crate directly; new" - }, - { - "workItemId": "ADR046-transport-unix-005", - "specId": "ADR-046-provider-transport-unix", - "specPath": "docs/specs/providers/ADR-046-provider-transport-unix.md", - "dependencyOwner": "ADR046-transport-unix-002", - "currentSource": "`packages/d2b-session-unix/src/descriptor.rs` `PidfdIdentityPolicy`, `DescriptorPolicy` at main `a1cc0b2d`", - "reuseSource": "Same", - "reuseAction": "copy and adapt", - "destination": "`packages/d2b-provider-transport-unix/src/descriptor.rs`", - "detailedDesign": "Copy verbatim; adapt `DescriptorPolicy::verify` to produce `AcceptedAttachment` carrying `ObjectIdentity` binding for v3 ComponentSession attachment descriptor model; `pid` not stored beyond liveness check", - "integration": "Called by seqpacket transport after decrypting attachment descriptor", - "dataMigration": "None — full d2b 3.0 reset; no prior state to migrate", - "validation": "Copy `pidfd_identity_requires_live_launch_evidence_and_rejects_unrelated_process` and `duplicate_kernel_objects_are_rejected_and_cleaned_up`", - "removalProof": "Broker pidfd-open path in `d2b-priv-broker/src/sys.rs` serves different purpose (process supervision); no removal dependency" - }, - { - "workItemId": "ADR046-transport-unix-006", - "specId": "ADR-046-provider-transport-unix", - "specPath": "docs/specs/providers/ADR-046-provider-transport-unix.md", - "dependencyOwner": "ADR046-transport-unix-002", - "currentSource": "No existing v3 socket-kind admission module", - "reuseSource": "`getsockopt(SO_TYPE)` pattern widely used; no specific main reuse source", - "reuseAction": "new", - "destination": "`packages/d2b-provider-transport-unix/src/admission.rs`", - "detailedDesign": "`validate_route_class(route_class, socket_kind, attachments_enabled, received_fd)` calls `getsockopt(SO_TYPE)` (blocking adapter) on `received_fd`: `SOCK_SEQPACKET` must match `\"seqpacket\"`, `SOCK_STREAM` must match `\"stream\"`, any other type fails `invalid-socket-fd`; if `route_class == RouteClass::ZoneLink && attachments_enabled == true` fail `attachment-policy-conflict` with detail `cross-zone-attachments-forbidden`; if `socket_kind == \"stream\" && attachments_enabled == true` fail `attachment-policy-conflict`; no Noise profile enforcement (that is ComponentSession's responsibility); returns `Ok(RouteAdmission { route_class, socket_kind, attachments_enabled })`", - "integration": "Called by `portal.rs::open_transport` before the monitoring dup and handle allocation", - "dataMigration": "None — full d2b 3.0 reset; no prior state to migrate", - "validation": "`tests/admission.rs::seqpacket_fd_passes_seqpacket_kind`; `stream_fd_passes_stream_kind`; `seqpacket_fd_rejects_stream_kind_declaration`; `zone_link_with_attachments_enabled_fails`; `local_portal_seqpacket_with_attachments_accepted`; `stream_with_attachments_enabled_rejected`", - "removalProof": "No current code has this gate; new path" - }, - { - "workItemId": "ADR046-transport-unix-007", - "specId": "ADR-046-provider-transport-unix", - "specPath": "docs/specs/providers/ADR-046-provider-transport-unix.md", - "dependencyOwner": "ADR046-transport-unix-002 through 006; ADR046-bus-001 (d2b-bus ComponentSession method dispatch); ADR046-session-001 (named-stream protocol)", - "currentSource": "No portal service in v3 baseline; `d2b-provider-toolkit/src/server.rs` `GeneratedProviderServiceServer` dispatch pattern (main `a1cc0b2d`) for service entry pattern", - "reuseSource": "main `a1cc0b2d` `d2b-provider-toolkit/src/server.rs` service dispatch pattern", - "reuseAction": "adapt dispatch pattern; implement portal methods as new", - "destination": "`packages/d2b-provider-transport-unix/src/{portal,service}.rs`", - "detailedDesign": "`portal.rs`: `PortalHandler` struct owns a bounded `HashMap` (capacity `MAX_OPEN_TRANSPORTS=256`); `open_transport(req, attachment_fd)` validates via `admission.rs`, dups FD, allocates handle, stores `MonitorState { dup_fd, observation_senders: Vec }`; `close_transport(handle)` closes dup FD, half-closes all observation senders, removes entry; `observe_transport(handle)` registers a new `NamedStreamSender` and spawns an async epoll-watcher task on the dup FD; `TransportHandle` is a `[u8; 16]` random token; redacted in all Debug impls; `service.rs` is the binary entry: accepts the allocator-issued portal endpoint FD at launch, runs `GeneratedTransportServiceServer` over it, dispatches to `PortalHandler`", - "integration": "Core ZoneLink controller calls the three methods via d2b-bus; portal endpoint FD is supplied by Zone runtime/allocator at Process spawn, not SD_LISTEN_FDS", - "dataMigration": "None — full d2b 3.0 reset; no prior state to migrate", - "validation": "`tests/portal.rs::open_transport_zone_link_validates_and_returns_ownedtransport`; `open_transport_local_portal_seqpacket_with_attachments_accepted`; `open_transport_zone_link_attachments_enabled_rejected`; `close_transport_is_idempotent_after_handle_removed`; `observe_transport_delivers_pollhup_as_peer_disconnected`; `handle_table_rejects_at_max_capacity`; `restart_clears_all_handles`", - "removalProof": "Ad-hoc IPC stubs in `d2bd/src/` retired after portal migration" - }, - { - "workItemId": "ADR046-transport-unix-008", - "specId": "ADR-046-provider-transport-unix", - "specPath": "docs/specs/providers/ADR-046-provider-transport-unix.md", - "dependencyOwner": "ADR046-transport-unix-007; ADR046-provider-003 (system Provider framework); Provider/system-minijail (ADR046-provider-003)", - "currentSource": "`packages/d2b-priv-broker/src/` minijail spawn patterns (v3 baseline); `packages/d2b-host/src/` process arg patterns; current package derivations in `flake.nix`", - "reuseSource": "Minijail sandbox semantic class patterns from current v3 broker; Process resource schema from ADR-046-resources-host-guest-process-user", - "reuseAction": "adapt; no direct symbol copy", - "destination": "`packages/d2b-provider-transport-unix/` crate Cargo.toml binary target `d2b-transport-unix-service`; Provider component descriptor JSON committed at `packages/d2b-provider-transport-unix/descriptor/unix-transport-service.json`; Nix package derivation at `packages/d2b-provider-transport-unix/`", - "detailedDesign": "Component descriptor declares: `processClass=service`, `template=unix-transport-service`, `stateNamespaces=[]` (no Provider state Volume; bounded non-secret operational state in status/core ledger, D087), `sandbox.capabilityClasses=[]`, `sandbox.namespaceClasses=[mount]`, `sandbox.seccompClass=strict`, `budget.memory.limit=\"16Mi\"`, `budget.cpu.limit=\"200m\"`, `budget.fds.limit=512`, `endpoints=[{name:portal,transport:unix,purpose:transport-unix-portal}]`, `readiness={class:provider-defined,initialDelay:\"0s\",timeout:\"5s\",failureThreshold:1,successThreshold:1}`, `restartPolicy={class:always,backoffBase:\"2s\",backoffMax:\"60s\",backoffMultiplier:2.0,maxRestarts:10,resetAfter:\"1h\"}`; Provider package bundles descriptor digest; core ProviderDeployment creates the Process with empty `mounts` when `Provider/transport-unix` is installed", - "integration": "Provider resource installed → core ProviderDeployment reads component descriptor → creates child `Process/transport-unix-service` (no state-Volume prerequisite) → ProviderSupervisor spawns binary with portal FD in inherited FD table. On delete: Process terminal first → ProviderDeployment finalizer cleared last; the service `status` disappears with the resource row", - "dataMigration": "None (fresh Provider resource)", - "validation": "`tests/conformance.rs::process_resource_matches_component_descriptor`; `tests/conformance.rs::provider_state_set_is_empty`; `tests/conformance.rs::no_state_volume_mount`; sandbox policy tests against minijail conformance kit", - "removalProof": "No current transport-service Process exists; new path" - }, - { - "workItemId": "ADR046-transport-unix-009", - "specId": "ADR-046-provider-transport-unix", - "specPath": "docs/specs/providers/ADR-046-provider-transport-unix.md", - "dependencyOwner": "ADR046-transport-unix-001; Nix/build integrator", - "currentSource": "`nixos-modules/options-realms.nix` realm options (v3 baseline); `nixos-modules/assertions.nix`", - "reuseSource": "None; new schema file", - "reuseAction": "new", - "destination": "`docs/reference/schemas/v3/providers/transport-unix.transport-binding.json`; `nixos-modules/assertions.nix` (assertion additions); generated `nixos-modules/generated/options-zones-ZoneLink.nix` provider-settings submodule", - "detailedDesign": "Commit the JSON Schema; run `xtask gen-zone-schemas` and `xtask gen-zone-nix-options` to regenerate committed files; add two assertions to `assertions.nix` (stream+attachments conflict; sensitive key names); `xtask gen-zone-resources` adds provider-specific settings validation step", - "integration": "Build emitter validates `spec.provider.settings` against schema before computing `generationId`; drift gate enforces sync", - "dataMigration": "`d2b.realms.*` Nix options superseded by `d2b.zones.*`; no compatibility bridge (v3 reset)", - "validation": "All seven eval/build tests in the Nix section", - "removalProof": "`nixos-modules/options-realms.nix` realm wiring retired after Zone resource bundle activation replaces it" - }, - { - "workItemId": "ADR046-transport-unix-010", - "specId": "ADR-046-provider-transport-unix", - "specPath": "docs/specs/providers/ADR-046-provider-transport-unix.md", - "dependencyOwner": "ADR046-transport-unix-007; ADR-046-telemetry-audit-and-support", - "currentSource": "v3 baseline `tracing` crate patterns; v3 `d2b-realm-router/src/service_v2.rs` audit field shapes", - "reuseSource": "None; new per v3 telemetry separation invariant", - "reuseAction": "new", - "destination": "`packages/d2b-provider-transport-unix/src/{audit,metrics}.rs`", - "detailedDesign": "`AuditRecordKind` enum with 6 event kinds from Audit section; `AuditRecord` carries only the fields listed (no uid/gid/pid/path/handle/ZoneLink name); emit via Zone runtime `emit_audit_record()` interface; `MetricCounter`/`MetricHistogram` with closed label types per Metrics section; emit via bounded in-process ring to OTEL Provider datagram socket; `tracing::instrument` spans on `PortalHandler` methods with the 3 permitted span attributes only", - "integration": "`portal.rs` calls `audit.rs::emit_*` before returning from each portal method; `seqpacket.rs` calls `metrics.rs::record_*` on every accept/packet/attachment", - "dataMigration": "Existing `d2bd/src/metrics.rs` VM-label metrics superseded by v3 metrics; not migrated", - "validation": "`tests/conformance.rs::audit_records_contain_no_pid_uid_or_handle`; `tests/conformance.rs::metric_labels_are_closed_set`; `tests/conformance.rs::span_attributes_contain_no_sensitive_fields`", - "removalProof": "`d2bd/src/metrics.rs` hand-rolled registry retired after metric surface migration" - }, - { - "workItemId": "ADR046-transport-unix-011", - "specId": "ADR-046-provider-transport-unix", - "specPath": "docs/specs/providers/ADR-046-provider-transport-unix.md", - "dependencyOwner": "ADR046-transport-unix-007 through 010; test orchestration owner", - "currentSource": "No existing integration tests for Unix portal scenarios", - "reuseSource": "Test scenario shapes from `d2b-session-unix/tests/unix_session.rs` end-to-end test (main `a1cc0b2d`)", - "reuseAction": "new", - "destination": "`packages/d2b-provider-transport-unix/integration/` and `integration/README.md`", - "detailedDesign": "Four scenarios: `transport_open.rs` (fake Zone portal, allocator-socketpair FD attachment in → OwnedTransport attachment out → verify socket kind, CLOEXEC, SO_PASSCRED enabled; p95 latency assertion ≤2 ms); `fd_transfer.rs` (seqpacket `SCM_RIGHTS` transfer through opened transport, credit accounting, scavenge on error injection); `reconnect.rs` (CloseTransport + re-OpenTransport with fresh socketpair, verify previous handle is unknown, verify monitoring dup closed); `observation_stream.rs` (ObserveTransport stream receives `PEER_DISCONNECTED` event when peer closes socketpair end within 5 ms p95). `integration/README.md` documents prerequisites (no KVM required; all scenarios use in-process socketpairs and fake Zone API endpoint stub), invocation (`cargo test -p d2b-provider-transport-unix --test integration`), environment variables, and expected output", - "integration": "Invoked by `make test-integration`; no host mutation; each scenario creates its own socketpairs", - "dataMigration": "None — full d2b 3.0 reset; no prior state to migrate", - "validation": "All four scenarios pass in CI; latency assertions enforced using monotonic timestamps; scavenge correctness verified by open-FD count before/after error injection", - "removalProof": "Ad-hoc IPC test stubs retired after scenario parity" - }, - { - "workItemId": "ADR046-usbip-001", - "specId": "ADR-046-provider-device-usbip", - "specPath": "docs/specs/providers/ADR-046-provider-device-usbip.md", - "dependencyOwner": "d2b-contracts crate shape stabilised by shared root contract; d2b-contracts owner", - "currentSource": "None — net-new v3 work; no pre-ADR45 baseline equivalent", - "reuseSource": null, - "reuseAction": "net-new trait definition", - "destination": "packages/d2b-contracts/src/usbip_effect_port.rs", - "detailedDesign": "Define UsbipEffectPort and UsbipGuestEffectPort in d2b-contracts with DeviceUid, NetworkUid, UsbBindingUid, LeaseToken, FirewallToken, KernelModuleClass, DeviceProbeResult, and UsbipEffectError; export traits/types only with no implementation; keep attach/detach Binding-addressed and all fd/path/busid values private.", - "integration": "Provider/device-usbip controller depends on this trait for injected semantic effects; the framework core adapter implements it in ADR046-usbip-002.", - "dataMigration": "None — docs/tooling only; no runtime state", - "validation": "d2b-contracts tests for trait object safety, redacted Debug behavior, method signatures, and no implementation leakage.", - "removalProof": "None — net-new; no prior owner to remove" - }, - { - "workItemId": "ADR046-usbip-002", - "specId": "ADR-046-provider-device-usbip", - "specPath": "docs/specs/providers/ADR-046-provider-device-usbip.md", - "dependencyOwner": "ADR046-usbip-001, ADR046-zone-control-024; UsbipBindFirewallRule broker op; d2b-host usbip argv support; framework core adapter owner", - "currentSource": "packages/d2bd/src/usbip_state_machine.rs, packages/d2bd/src/usbip_reconcile_state.rs, packages/d2b-host/src/usbip_argv.rs, packages/d2b-priv-broker/src/ops/usbip_firewall.rs, usbip_host.rs, and usbip_lock.rs", - "reuseSource": null, - "reuseAction": "extract and adapt into framework-internal adapter", - "destination": "packages/d2b-core/src/device_usbip_adapter.rs", - "detailedDesign": "Implement UsbipEffectPort in the core adapter: signed-bundle busid lookup, same-Zone validation, trusted physical-USB identity resolution, mandatory Core-derived `physical-usb-backing/v1` digest and exact `(Host, physical-usb-backing, opaqueKeyDigest)` claim shared with every security-key/USB Provider, exclusive OFD claim, broker firewall/sysfs effects, anti-spoof probe, one shared Host module/backend authority, one reference-counted TCP 3240 relay authority per Network, D097 authority-index preflight/adoption, and post-effect audit; complete the shared claim before any open, withhold, bind, module, relay, or attachment effect; never expose raw busid, identity digest, path, fd, bind address, nftables body, audit structs, or broker wire types.", - "integration": "Reconcile framework injects the adapter into Provider/device-usbip; D097 authority index gates effects; adapter calls privileged broker and d2b-host argv helpers behind the semantic trait.", - "dataMigration": "Full d2b 3.0 reset; adapter resumes from Service/Binding status and authority owner proofs rather than daemon-coupled snapshots", - "validation": "Fast packages/d2b-core/tests/device_usbip_adapter.rs covers same-Zone gate, exact shared physical backing tuple derivation, byte-identical keys for USB/security-key views of one fake token, Provider-private-class/digest bypass rejection, `physical-usb-backing-conflict` before effects, separate USBIP module/relay authorities, one-module/one-relay reuse, anti-spoof, redaction, broker mapping, and no digest/busid/path/fd exposure.", - "removalProof": "Old daemon-coupled adapter call sites are removed by ADR046-usbip-009 after Provider wiring and adapter tests pass." - }, - { - "workItemId": "ADR046-usbip-003", - "specId": "ADR-046-provider-device-usbip", - "specPath": "docs/specs/providers/ADR-046-provider-device-usbip.md", - "dependencyOwner": "ADR046-usbip-001, ADR046-provider-004; Provider model crate structure; device-usbip provider owner", - "currentSource": "None — net-new Provider crate; no pre-ADR45 baseline equivalent", - "reuseSource": null, - "reuseAction": "net-new crate skeleton with contract reuse", - "destination": "packages/d2b-provider-device-usbip/", - "detailedDesign": "Create the required crate layout; bind the shared D098 `UsbService`/`UsbBinding` base versions/fingerprints from ADR046-provider-004 and implement only strict USBIP Provider extensions; sign/register extension schemas and advertise explicit export only for authority `UsbService` resources implemented by this Provider; implement validation.rs and compile-checked EffectPort injection. Declare the controller user/User resource in Nix activation.", - "integration": "Workspace manifests, Provider artifact catalog, Nix module, and ProviderDeployment consume the crate and component descriptor.", - "dataMigration": "None — docs/tooling only; no runtime state", - "validation": "make test-policy passes; Cargo.toml has no d2b-priv-broker dependency; fast schema/manifest tests consume the common fixtures, accept canonical minimal base without `spec.provider`, prove a fake direct-local Provider can implement the same base, and cover Service-only exportability, Binding non-exportability, Core projection ownerRef/base fields with explicit `spec.provider` rejection, D088 status layering, semantic factory-fingerprint stability across Provider/adapter identity changes, strict refs, and trait injection.", - "removalProof": "None — net-new; no prior owner to remove" - }, - { - "workItemId": "ADR046-usbip-004", - "specId": "ADR-046-provider-device-usbip", - "specPath": "docs/specs/providers/ADR-046-provider-device-usbip.md", - "dependencyOwner": "ADR046-usbip-001, ADR046-usbip-002, ADR046-usbip-003, and ADR046-zone-control-024; device-usbip controller owner", - "currentSource": "packages/d2bd/src/usbip_state_machine.rs and packages/d2bd/src/usbip_reconcile_state.rs", - "reuseSource": null, - "reuseAction": "extract and adapt step machine into Provider reconcile loop", - "destination": "packages/d2b-provider-device-usbip/src/{controller,reconcile,export_import}.rs", - "detailedDesign": "Reconcile provider-neutral authority/projection `UsbService` and per-Guest `UsbBinding` resources through strict USBIP Provider extensions, consuming UsbipEffectPort, the exact shared Host-global physical USB tuple, USBIP-private D097 authorities, and signed D096 ExportAdapter/ImportAdapter. Enforce same-Zone base and provider refs; ResourceExport authority-Service-only target; same-type ResourceImport-owned projection with `providerRef`, semantic base/import fields, no `spec.provider`, and no physical fields/effects; route selection from the signed local descriptor/import record; semantic factory fingerprint independent of separately authenticated adapter identity; exclusive fair Binding admission; encrypted bounded named-stream control/data; D088 layered status; Service/Binding finalizers; restart adoption; declared/explicit modes; no session/transfer resources.", - "integration": "Controller watches Device/Host/Network/Guest/Endpoint/Export/Import dependencies, calls injected EffectPorts, commits ResourceMutationBatch updates, coordinates children from ADR046-usbip-005, and delegates only semantic export/import admission to the Provider adapter while core owns D096 routing/lifecycle.", - "dataMigration": "Full d2b 3.0 reset; no direct import of d2bd usbip_reconcile_state snapshots", - "validation": "Fast tests/controller_state_machine.rs, service_binding_schema.rs, export_import.rs, authority_conflict.rs, async_loop.rs, finalizer.rs, and wrong_zone.rs cover authority/projection/Binding lifecycle, Service-only export, projection `spec.provider` rejection, semantic fingerprint stability under adapter identity changes plus separate descriptor authentication, D088 `status.resource`/`status.provider` placement, shared physical tuple collision before effects, encrypted fake streams, no physical projection effect, exclusivity, restart, and WrongZone degradation.", - "removalProof": "packages/d2bd/src/usbip_state_machine.rs and usbip_reconcile_state.rs are deleted by ADR046-usbip-009 once Provider parity tests pass." - }, - { - "workItemId": "ADR046-usbip-005", - "specId": "ADR-046-provider-device-usbip", - "specPath": "docs/specs/providers/ADR-046-provider-device-usbip.md", - "dependencyOwner": "ADR046-usbip-003; Process ResourceType schema; device-usbip process lifecycle owner", - "currentSource": "None — net-new Process resources; templates derive from the Provider package descriptor", - "reuseSource": null, - "reuseAction": "adapt into singleton Host backend, per-Network relay, and per-Binding Guest proxy management", - "destination": "packages/d2b-provider-device-usbip/src/reconcile.rs", - "detailedDesign": "Create/adopt exactly one Host backend Process authority, exactly one Network relay Process/Endpoint authority bound to TCP 3240, and one Binding-owned Guest proxy/private Endpoint per attached Binding. Use canonical system-minijail specs, signed templates, bounded budgets/readiness/restart, no argv/path/address/fd fields; attach/detach remains a one-shot EffectPort operation, not a second Process.", - "integration": "Service controller registers physical busids with shared backend/relay; Binding controller creates its Guest proxy/private Endpoint; Process controller launches workers; UsbipGuestEffectPort attaches to the private Endpoint.", - "dataMigration": "Full d2b 3.0 reset; old per-env runners become Host/Network authorities and per-Device port-3240 workers are forbidden", - "validation": "Fast Process/Endpoint shape tests prove one backend per Host, one multiplexed TCP 3240 Endpoint per Network, Binding ownership/private Guest policy, no per-Device listener, no raw address/argv/path/fd, and readiness before bind/attach.", - "removalProof": "Old per-env usbipd autostart and ProcessRole::Usbip paths are removed by ADR046-usbip-009 after Process resource lifecycle tests pass." - }, - { - "workItemId": "ADR046-usbip-006", - "specId": "ADR-046-provider-device-usbip", - "specPath": "docs/specs/providers/ADR-046-provider-device-usbip.md", - "dependencyOwner": "ADR046-usbip-003; Device provider details plus `UsbService` and `UsbBinding` base/provider status schema owner", - "currentSource": "packages/d2bd/src/usbip_reconcile_state.rs state fields", - "reuseSource": null, - "reuseAction": "adapt state fields to typed status.provider.details", - "destination": "packages/d2b-provider-device-usbip/src/status.rs", - "detailedDesign": "Define provider-neutral `UsbService` `status.resource` with whole-device availability/access counts, authority-only `physical-usb-backing` claim state, and projection-only common import state; define `UsbBinding.status.resource` with generic attachment phase/queue/generation/timestamps. Define separate strict `status.provider` USBIP details: Device probe, owner backend/relay or imported-route observations, and Binding proxy/private Endpoint/subphase. No common access/import/attachment field appears directly under `status`; no USBIP module, Network, Endpoint, proxy, server/client, port, firewall, or busid field enters base status; no raw backing digest, busid, path, fd, address, session/transfer ID, remote identity, or payload appears anywhere.", - "integration": "Controller writes each extension atomically with its resource's common status; dependency/update propagation is Device/Export → Service/projection → Binding.", - "dataMigration": "Full d2b 3.0 reset; current d2bd reconcile state is not imported", - "validation": "Fast tests/status_serde.rs covers generic base status plus three strict USBIP detail schemas, exact `status.resource` placement for access/import/attachment/backing claim, exact `status.provider` placement for implementation observations, rejection of semantic fields directly under `status`, mode-dependent omissions, bounded counts/refs, unknown-field denial, and a deny corpus proving USBIP-only fields are rejected from base status.", - "removalProof": "Old d2bd USBIP reconcile-state structs are removed by ADR046-usbip-009 after status extension coverage passes." - }, - { - "workItemId": "ADR046-usbip-007", - "specId": "ADR-046-provider-device-usbip", - "specPath": "docs/specs/providers/ADR-046-provider-device-usbip.md", - "dependencyOwner": "ADR046-usbip-004 and ADR046-usbip-005; device-usbip integration owner", - "currentSource": "packages/d2b-contract-tests/tests/usbip_policy_network_scoping.rs plus new integration scenarios", - "reuseSource": null, - "reuseAction": "adapt existing network-scoping assertion and add new scenarios", - "destination": "packages/d2b-provider-device-usbip/{src,tests,integration/README.md}; tests/host-integration/usbip-service.nix; tests/host-integration/hardware/usbip-service.sh", - "detailedDesign": "Put provider-neutral Service/Binding base-schema separation, strict USBIP extensions, projection `spec.provider` rejection, D088 layered status, semantic factory-fingerprint independence from Provider/adapter identity, exact shared physical backing tuple/conflict, arbitration, same-type export/import, encrypted fake-stream, and process-shape coverage in fast Layer-1 Rust tests. Include a fake direct-local Provider proving the same base contract has no USBIP dependency. Reserve runNixOSTest for real Linux usbip_host/vhci_hcd, usbipd, namespaces/nftables, TCP 3240, and Guest checks; reserve the hardware script for an approved physical device. Use existing Make gates only.", - "integration": "Layer-2 lanes exercise actual kernel/backend/relay/Guest/device paths and do not duplicate pure controller/schema cases. Cross-Zone protocol logic remains hermetic with fake peers; the runNixOSTest only proves its real-system integration.", - "dataMigration": "None — docs/tooling only; no runtime state", - "validation": "`make test-host-integration` runs the non-hardware real-kernel case on a capable host; `make test-hardware` runs the explicit manual device case. No Layer-1 test opens a device, loads a module, creates a namespace, or listens on a socket.", - "removalProof": "Old usbip_policy_network_scoping coverage is retired only after the fast wrong-Zone admission test and `tests/host-integration/usbip-service.nix` successor both pass and the migration ledger is updated." - }, - { - "workItemId": "ADR046-usbip-008", - "specId": "ADR-046-provider-device-usbip", - "specPath": "docs/specs/providers/ADR-046-provider-device-usbip.md", - "dependencyOwner": "ADR046-usbip-003, ADR046-zone-control-024; ADR-046-nix-configuration; Nix integrator", - "currentSource": "nixos-modules/components/usbip.nix guest wiring and new Zone resource declarations", - "reuseSource": null, - "reuseAction": "adapt guest module, remove host-side option surface, and extend eval assertions", - "destination": "nixos-modules/components/usbip.nix, nixos-modules/options-zones.nix, nixos-modules/assertions.nix", - "detailedDesign": "Add Provider config; remove the old per-VM option; emit provider-neutral authority `UsbService`, per-Guest `UsbBinding`, strict USBIP Provider envelopes, and optional ResourceExport/ResourceImport authoring shapes; imported same-type projection Services remain Core-created with no `spec.provider`. Assert same-Zone base/provider refs, projection ownerRef/forbidden implementation and physical fields, Service-only export target, one Host backend/Network relay, exact Core-derived Host-global `(Host, physical-usb-backing, opaqueKeyDigest)` exclusion shared by USB/security-key, Provider-private-class bypass rejection, and retain guest vhci_hcd/tools.", - "integration": "Nix compiler emits Device plus provider-neutral `UsbService`/`UsbBinding` and explicit D096 resources consumed by core/Provider; guest runtime supplies USBIP proxy/attach tools; generated base and provider-extension schemas/fingerprints remain canonical.", - "dataMigration": "Full d2b 3.0 reset; operators reauthor old per-VM options as Device + authority/projection Service + per-Guest Binding", - "validation": "Fast tests/unit/nix/cases/usbip-*.nix cover schema shape, Core projection `spec.provider` rejection, D088 layered status fixtures, all reference/owner/export assertions, byte-identical USB/security-key tuple collision and private-class bypass rejection, 3240 singleton/multiplex metadata, old-option removal, and guest module retention.", - "removalProof": "d2b.vms..usbip.yubikey and host-side USBIP module paths are removed at reset once Zone resource emitter coverage passes." - }, - { - "workItemId": "ADR046-usbip-009", - "specId": "ADR-046-provider-device-usbip", - "specPath": "docs/specs/providers/ADR-046-provider-device-usbip.md", - "dependencyOwner": "ADR046-usbip-004 and ADR046-usbip-008; Provider fully wired and validated; daemon cleanup owner", - "currentSource": "packages/d2bd/src/usbipd_perenv_autostart.rs, packages/d2bd/src/usbip_state_machine.rs, packages/d2bd/src/usbip_reconcile_state.rs, nixos-modules/network.nix USBIP firewall block, and ProcessRole::Usbip in packages/d2b-core/src/processes.rs", - "reuseSource": null, - "reuseAction": "delete after Provider replacement reaches parity", - "destination": "packages/d2bd/src/, nixos-modules/network.nix, packages/d2b-core/src/processes.rs", - "detailedDesign": "Remove daemon-coupled USBIP after Provider tests and integration tests pass: delete per-env autostart, state machine, and reconcile state modules after migration; remove USBIP firewall block from network.nix; remove ProcessRole::Usbip; run Layer-1 gates and confirm no d2bd or network.nix references remain outside the adapter and contracts.", - "integration": "Provider/device-usbip, core D096/D097 adapters, Nix Device plus provider-neutral USB resource emitter, USBIP authority workers, and Binding-owned children are the sole USBIP lifecycle path after deletion.", - "dataMigration": "Full d2b 3.0 reset; no daemon-coupled USBIP runtime state import", - "validation": "make test-unit and make test-flake plus grep or contract checks for removed symbols and no residual d2bd/network.nix USBIP lifecycle references.", - "removalProof": "usbipd_perenv_autostart.rs, usbip_state_machine.rs, usbip_reconcile_state.rs, network.nix USBIP firewall block, and ProcessRole::Usbip are deleted after parity." - }, - { - "workItemId": "ADR046-user-session-001", - "specId": "ADR-046-resources-host-guest-process-user", - "specPath": "docs/specs/ADR-046-resources-host-guest-process-user.md", - "dependencyOwner": "ADR046-zone-control-019 (authority index); `Provider/system-systemd` (user manager) + core/user-agent owner", - "currentSource": "None — the fixed user-session authority is today ambient prose across the display/audio/clipboard/notification/secret-service dossiers; no named owner exists", - "reuseSource": "`Provider/system-systemd` user-manager scope; D077 EffectPort/LaunchTicket FD handoff", - "reuseAction": "net-new (name and implement the shared user-session authority)", - "destination": "`packages/d2b-core-controller/src/user_session_authority.rs` (or a core/user-agent per-session agent Process under `Provider/system-systemd`); `AuthorityDescriptor` on the session authority", - "detailedDesign": "Name and implement the **fixed user-session authority** (D097 desktop/session): `authorityScope: seat` bound to `(Host, User, login-session/seat)`, opaque `authorityKey` (never a raw socket path/XDG_RUNTIME_DIR/DISPLAY/seat name), `cardinality: exactly-one` per `(Host, User, login-session)`, `arbitration: exclusive`, owner = a core/user-agent per-user-session agent Process (NOT a new Provider), adoption by `ownerProof` (agent Process identity + login-session id), `exportability: forbidden`. It is the sole opener of the compositor/PipeWire/session-bus FDs and hands them to desktop Providers only via the EffectPort/LaunchTicket. Core's authority index rejects a duplicate session authority (or a duplicate same-user display portal, clipboard host, notification sink, audio mediator, systemd user manager, Secret Service, or seat-input claimant) with `duplicateConflict` before any FD open; multi-user/seat is admitted only up to the declared per-Host limit. Guest-stop invalidates every session authority/lease bound to that Guest across display/audio/notification/credential/shell in one dependency-aware cascade (D091), with no stale FD surviving. Host input (`wl_seat`/pointer constraints) is an `at-most-one`-per-seat authority under this session authority; pointer-constraint enforcement is a declared boundary until an interaction Provider implements it.", - "integration": "Core authority index (ADR046-zone-control-019); `Provider/system-systemd` user manager; display/audio/clipboard/notification/secret-service/shell Provider services bind to this single authority for their FDs; D091 Guest-stop cascade", - "dataMigration": "None — full d2b 3.0 reset", - "validation": "Single session authority per `(Host, User, session)`; duplicate same-user session authority / desktop service rejected with `duplicateConflict`; multi-seat declared-limit enforcement; Guest-stop invalidates all bound desktop/audio/notification/credential/shell authorities and leases (no stale compositor/PipeWire/session-bus FD); seat-input second claimant rejected; adoption by `ownerProof` and quarantine on ambiguity; hermetic with fakes", - "removalProof": "Not applicable (net-new named authority; replaces ambient prose)" - }, - { - "workItemId": "ADR046-vl-001", - "specId": "ADR-046-provider-volume-local", - "specPath": "docs/specs/providers/ADR-046-provider-volume-local.md", - "dependencyOwner": "ADR046-primitives-001; v3 contracts owner", - "currentSource": "`d2b-core/src/storage.rs` (`StoragePathSpec`, `StoragePathKind`, policy enums); `d2b-core/src/sync.rs` (`SyncJson`, `LockSpec`)", - "reuseSource": null, - "reuseAction": "extract and adapt", - "destination": "`d2b-contracts/src/v3/volume_layout.rs` (LayoutEntry, EntryType, all policy enums, AclGrant, Invariant, SensitivityClass); `d2b-contracts/src/v3/volume_spec.rs` (VolumeSpec, ViewSpec, Attachment, QuotaSpec, SourceKind, `SourcePolicyId` opaque newtype); `d2b-contracts/src/v3/effect_port.rs` (`VolumeEffectPort` trait, opaque ID newtypes `VolumeId`/`LayoutEntryId`/`UserId`/`ViewId` each with custom redacted Debug, and `VolumeMountToken` opaque handle with custom redacted Debug)", - "detailedDesign": "All LayoutEntry fields as documented in this dossier; enum value names preserved from `StoragePathKind`/policy enums with renames where noted; `User/` ACL principal (no numeric UID); `sourcePolicyId` opaque newtype replaces raw `hostPath` in `SourceKind::LocalPath` and `SourceKind::BlockImage`", - "integration": "Volume spec and status structs; Provider descriptor component stateNamespace; Nix resource compiler schema validation", - "dataMigration": "Full v3 reset; no row-level import", - "validation": "Schema golden vectors; round-trip serde; ACL principal validation rejects numeric forms; `sourcePolicyId` present; no `hostPath` field in any volume_spec contract", - "removalProof": "`d2b-core/src/storage.rs` StoragePathSpec/policy enums removed only after all Provider descriptor consumers are on v3 Volume spec" - }, - { - "workItemId": "ADR046-vl-002", - "specId": "ADR-046-provider-volume-local", - "specPath": "docs/specs/providers/ADR-046-provider-volume-local.md", - "dependencyOwner": "ADR046-vl-001; volume-local Provider owner", - "currentSource": "`d2b-state/src/{atomic,path,lock}.rs` (main `6faa5256`); `d2b-priv-broker/src/ops/swtpm_dir.rs` (marker algorithm)", - "reuseSource": null, - "reuseAction": "copy-unchanged (`path.rs`); adapt (`atomic.rs`, `lock.rs`); adapt swtpm_dir marker algorithm", - "destination": "Full `packages/d2b-provider-volume-local/` scaffold per §Crate layout: `src/`, `tests/`, `integration/`, `README.md`; crate `Cargo.toml` depends only on `d2b-contracts`, `d2b-provider`, `d2b-provider-toolkit`", - "detailedDesign": "`AnchoredDir`, `AnchoredResource`, `LeafName`, `RelativePath`; adapted `AtomicFilesystem`/`StateEnvelope`; adapted `LockGuard`/`LockSet`/`OfdTransfer`; marker write/verify/check; `src/effect_port.rs` re-exports `VolumeEffectPort` trait from `d2b-contracts::v3::effect_port` and provides Provider-side opaque ID construction helpers (no adapter implementation; adapter lives in host runtime); `sourcePolicyId` validation against declared policy list; no `openat2`/`setfacl`/`fallocate`/numeric-UID call sites in Provider crate", - "integration": "Controller binary receives `VolumeEffectPort` via ComponentSession injection; adapter calls `provision_marker` when a new Volume first appears in the `providerRef` reconcile queue (ProviderDeployment has already created the resource; volume-local provisions physical state) and `verify_marker` on restart relist", - "dataMigration": "New marker written for each Volume at v3 first-boot", - "validation": "All `tests/marker.rs`, `tests/state.rs` scenarios; all `integration/provision.rs` scenarios; `cargo deny check` verifies no `d2b-priv-broker`/`d2bd` dependency", - "removalProof": "`swtpm_dir.rs` marker implementation retired only after device-tpm Provider Volume is live and marker-check parity is confirmed" - }, - { - "workItemId": "ADR046-vl-003", - "specId": "ADR-046-provider-volume-local", - "specPath": "docs/specs/providers/ADR-046-provider-volume-local.md", - "dependencyOwner": "ADR046-vl-002; ADR046-reconcile-001; d2b-bus/ComponentSession owner", - "currentSource": "`d2b-priv-broker/src/ops/{state_dir,storage_contract}.rs` (broker layout ops); `d2b-core/src/storage_lifecycle.rs` (lifecycle report)", - "reuseSource": null, - "reuseAction": "adapt", - "destination": "`src/controller.rs`, `src/layout.rs`, `src/acl.rs`, `src/source.rs`", - "detailedDesign": "Async reconcile loop; topological LayoutEntry evaluation; `VolumeEffectPort` semantic op dispatch (no direct broker connection; no `openat2`/`setfacl` call sites); ACL reconciliation cycle via effect port; drift detection; status write with expected revision; `sourcePolicyId` validation against declared `sourcePolicies`; controller watch remains responsive while per-resource effect calls run concurrently; **single watch scope** `providerRef: Provider/volume-local` — physical state reconciliation for all served Volumes (layout/ACL/quota/marker); ProviderDeployment creates/deletes Volume instances; volume-local does not issue create/delete API calls; Nix-preprovisioned `User/` layout principals; no cross-component Volume sharing; each component consumes only its declared view; empty-payload stateNamespace Volumes use `migrationPolicy: none` — no migration EphemeralProcess dispatched", - "integration": "Controller binary instantiated by Zone runtime after Provider Ready; receives `VolumeEffectPort` implementation and d2b-bus `ResourceClient` via ComponentSession", - "dataMigration": "None — full d2b 3.0 reset; no prior state to migrate", - "validation": "`tests/layout_provision.rs`, `tests/layout_repair.rs`, `tests/layout_adopt.rs`, `tests/acl.rs`, `tests/view_rights.rs`, `tests/source.rs`, `integration/provision.rs`", - "removalProof": "`d2b-priv-broker/src/ops/storage_contract.rs` `reconcile_storage_scope` retired only after Volume controller parity confirmed" - }, - { - "workItemId": "ADR046-vl-004", - "specId": "ADR-046-provider-volume-local", - "specPath": "docs/specs/providers/ADR-046-provider-volume-local.md", - "dependencyOwner": "ADR046-vl-003; runtime-cloud-hypervisor Provider owner", - "currentSource": "`d2b-host/src/hardlink_farm.rs`; `d2b-priv-broker/src/ops/{store_sync,store_view_posture,store_view_farm,store_sync_audit,store_sync_export}.rs`; `nixos-modules/store.nix`", - "reuseSource": null, - "reuseAction": "adapt", - "destination": "`src/store_view.rs`; `tests/store_view.rs`; `integration/store_view.rs`", - "detailedDesign": "Store-view LayoutEntry matrix (see §Same-filesystem hardlink farm); private-NS sync via `run_store_sync` effect op; `StoreSyncComplete` semantic operation; OFD lock semantics; `gcroots/` and `state/` at store-view root; spec-correction enforcement", - "integration": "`runtime-cloud-hypervisor` Provider declares store-view Volume in its ProviderStateSet; volume-local controller handles sync", - "dataMigration": "None (format preserved; activation changed from Nix to Volume controller)", - "validation": "`tests/store_view.rs` all invariants; `integration/store_view.rs` same-filesystem boundary; private-NS sync with concurrent reader", - "removalProof": "`nixos-modules/store.nix` activation and `d2b-priv-broker/src/ops/store_sync.rs` retired only after store-view Volume controller is live and passes all parity tests" - }, - { - "workItemId": "ADR046-vl-005", - "specId": "ADR-046-provider-volume-local", - "specPath": "docs/specs/providers/ADR-046-provider-volume-local.md", - "dependencyOwner": "ADR046-vl-002; device-tpm Provider owner", - "currentSource": "`d2b-priv-broker/src/ops/swtpm_dir.rs`", - "reuseSource": null, - "reuseAction": "adapt", - "destination": "`src/swtpm_volume.rs`; `tests/swtpm_volume.rs`; `integration/swtpm_marker.rs`", - "detailedDesign": "TPM LayoutEntry matrix; `create-if-never-provisioned` + fail-closed repair; broker-maintained provisioning marker; ancestor traverse ACL; `previously-provisioned-swtpm-state-missing` fail-closed detection; `secret-adjacent` sensitivity enforcement", - "integration": "`device-tpm` Provider declares TPM Volume in ProviderStateSet; volume-local handles layout/marker lifecycle", - "dataMigration": "None (full v3 reset; TPM NVRAM must be backed up by operator)", - "validation": "`tests/swtpm_volume.rs` all scenarios; `integration/swtpm_marker.rs` real broker-maintained marker", - "removalProof": "`d2b-priv-broker/src/ops/swtpm_dir.rs` retired only after device-tpm Provider TPM Volume is live and fail-closed tests pass" - }, - { - "workItemId": "ADR046-vl-006", - "specId": "ADR-046-provider-volume-local", - "specPath": "docs/specs/providers/ADR-046-provider-volume-local.md", - "dependencyOwner": "ADR046-vl-003", - "currentSource": "No equivalent in baseline; new", - "reuseSource": null, - "reuseAction": "new", - "destination": "`src/source.rs` (block-image and tmpfs branches); `tests/source.rs`; `integration/block_image.rs`", - "detailedDesign": "`block-image`: image file create/verify via `provision_block_image` effect op; `fallocate` performed by adapter when `preallocate: true`; FD transfer to Guest runtime via LaunchTicket via `open_volume_mount_token` effect op; `tmpfs`: `mount_tmpfs`/`umount_tmpfs` effect ops; `size=` and `nr_inodes=` derived from quota fields; cleanup via `umount_tmpfs` op", - "integration": "Guest runtime Provider (cloud-hypervisor) receives block-image FD from volume-local via LaunchTicket; no path crosses the boundary", - "dataMigration": "None — full d2b 3.0 reset; no prior state to migrate", - "validation": "`tests/source.rs` allowlist pass/fail; block-image/tmpfs eval constraints; `integration/block_image.rs` real image lifecycle", - "removalProof": "Not applicable (new)" - }, - { - "workItemId": "ADR046-vl-007", - "specId": "ADR-046-provider-volume-local", - "specPath": "docs/specs/providers/ADR-046-provider-volume-local.md", - "dependencyOwner": "ADR046-vl-002; ADR046-vl-003; ADR046-pstate-004 through ADR046-pstate-006", - "currentSource": "`d2b-state/src/atomic.rs` (main); no existing migration/snapshot infrastructure in v3", - "reuseSource": null, - "reuseAction": "adapt", - "destination": "`src/{migration,snapshot}.rs`; `tests/{migration_unit,snapshot_unit}.rs`; `integration/{migration,snapshot}.rs`", - "detailedDesign": "Schema migration (see §Schema migration) via `volume-migration-worker` EphemeralProcess; Snapshot create/list/expire (see §Snapshots) via `volume-snapshot-worker` EphemeralProcess; controller reports `stateSchemaPhase` and `snapshots` in Volume status. Sealing lifecycle (`sealingStatus`, key-shred) is a core/framework concern dispatched through `VolumeEffectPort` semantic ops, not a Provider-owned EphemeralProcess worker.", - "integration": "Controller's reconcile handler dispatches EphemeralProcess via d2b-bus `ResourceClient`; volume-local reports `stateSchemaPhase`, `snapshots` in Volume status", - "dataMigration": "None (new protocol)", - "validation": "All `tests/migration_unit.rs`, `tests/snapshot_unit.rs`, `integration/migration.rs`, `integration/snapshot.rs` scenarios", - "removalProof": "Not applicable (new)" - }, - { - "workItemId": "ADR046-vl-008", - "specId": "ADR-046-provider-volume-local", - "specPath": "docs/specs/providers/ADR-046-provider-volume-local.md", - "dependencyOwner": "ADR046-vl-003; ADR046-vl-007", - "currentSource": "No equivalent; new", - "reuseSource": null, - "reuseAction": "new", - "destination": "`src/relocation.rs`; `tests/relocation_unit.rs`; `integration/relocation.rs`", - "detailedDesign": "As documented in §Relocation, §Retention, §Incident hold, §Unclaimed Volume GC, §Destruction", - "integration": "Controller adds `Relocating` finalizer; creates relocation EphemeralProcess; destruction sequence is ordered leaf-first with `fsync` after each step", - "dataMigration": "Not applicable", - "validation": "All `tests/relocation_unit.rs`, `integration/relocation.rs` scenarios; destruction ordering under fault injection", - "removalProof": "Not applicable (new)" - }, - { - "workItemId": "ADR046-vl-009", - "specId": "ADR-046-provider-volume-local", - "specPath": "docs/specs/providers/ADR-046-provider-volume-local.md", - "dependencyOwner": "ADR046-vl-001; Zone audit stream owner; `observability-otel` Provider owner", - "currentSource": "`d2b-state/src/audit.rs` (main `6faa5256`); OTEL cardinality model from `d2b-provider-observability-local/src/` (main `a1cc0b2d`)", - "reuseSource": null, - "reuseAction": "adapt", - "destination": "`src/audit.rs`; `src/otel.rs`; `src/error.rs`; `tests/audit_unit.rs`; `integration/audit.rs`", - "detailedDesign": "Event types and Zone audit emission per §Audit events; OTEL metric definitions per §OTEL metrics; error catalog per §Error catalog; no-path invariant enforced in all outputs", - "integration": "Every lifecycle transition calls `audit::emit_volume_event`; OTEL metrics exported via `observability-otel` Provider", - "dataMigration": "None — full d2b 3.0 reset; no prior state to migrate", - "validation": "`tests/audit_unit.rs` golden records; `tests/error_messages.rs` bounded messages; OTEL label cardinality; `integration/audit.rs` live stream", - "removalProof": "Not applicable" - }, - { - "workItemId": "ADR046-vl-010", - "specId": "ADR-046-provider-volume-local", - "specPath": "docs/specs/providers/ADR-046-provider-volume-local.md", - "dependencyOwner": "ADR046-vl-001; ADR046-pstate-010; NixOS module owner", - "currentSource": "`nixos-modules/storage-json.nix`; `nixos-modules/store.nix`; `packages/xtask/src/main.rs` (`gen-schemas`)", - "reuseSource": null, - "reuseAction": "adapt", - "destination": "`nixos-modules/zone-resources.nix` (per §ADR046-pstate-010); `root-config.schema.json` in the Provider package", - "detailedDesign": "`sourcePolicies` in Provider root config (opaque IDs; no raw host paths); path prefix injection by resource compiler into private bundle (never into ResourceSpec or operator-authored Nix); `controllerExecutionRef` in Provider config; all eval-time validation rules per §Nix configuration including `sourcePolicyId` validation; artifact catalog entry; Provider and Volume resource authoring shapes", - "integration": "NixOS build emits `/etc/d2b/zones//resources.json`; Zone daemon activates bundle and creates Volume resources; volume-local controller reconciles", - "dataMigration": "`nixos-modules/storage-json.nix` path rows superseded by Volume resources; `nixos-modules/store.nix` activation superseded by store-view Volume", - "validation": "All Nix eval-time validation rules; `contentId` determinism; credential-ref guard; unknown Provider config key → build fail", - "removalProof": "`nixos-modules/storage-json.nix` and `nixos-modules/store.nix` per-VM rows retired only after Volume resources replace every path row and all consumers complete bundle-format migration" - }, - { - "workItemId": "ADR046-vl-011", - "specId": "ADR-046-provider-volume-local", - "specPath": "docs/specs/providers/ADR-046-provider-volume-local.md", - "dependencyOwner": "ADR046-vl-002; ADR046-pstate-011; workspace policy owner", - "currentSource": "`packages/xtask/src/main.rs` (`gen-schemas`, workspace-policy checks); `tests/unit/gates/drift-check.sh`", - "reuseSource": null, - "reuseAction": "extend (per ADR046-pstate-011)", - "destination": "`packages/xtask/src/provider_crate_policy.rs`; `tests/unit/gates/provider-crate-layout-check.sh`", - "detailedDesign": "`cargo xtask check-provider-crate-layout` gate asserts `src/`, `tests/`, `integration/` (with at least one `.rs` file and a `README.md`), and `README.md` for every `packages/d2b-provider-*` workspace member; fails closed with typed `missing-provider-crate-path` error", - "integration": "`make test-policy` runs the gate; GitHub CI runs `make test-policy` on every PR", - "dataMigration": "Not applicable", - "validation": "Gate detects each missing path; idempotent across re-runs; existing non-provider `d2b-*` crates not flagged", - "removalProof": "Not applicable (permanent gate)" - }, - { - "workItemId": "ADR046-vl-012", - "specId": "ADR-046-provider-volume-local", - "specPath": "docs/specs/providers/ADR-046-provider-volume-local.md", - "dependencyOwner": "ADR046-vl-001; ADR046-vl-002; Zone broker/core owner", - "currentSource": "`d2b-priv-broker/src/ops/{state_dir,storage_contract,swtpm_dir,store_sync,store_view_posture}.rs`; `d2b-host/src/hardlink_farm.rs`", - "reuseSource": null, - "reuseAction": "adapt into adapter", - "destination": "`packages/d2b-host/src/volume_effect_adapter.rs` (or the equivalent host-runtime crate designated by the Zone broker owner); implements the `VolumeEffectPort` trait defined in `d2b-contracts`", - "detailedDesign": "Adapter holds trusted FD table keyed by `VolumeId`; resolves `SourcePolicyId` to host path prefix from private bundle; calls `openat2(RESOLVE_BENEATH)` anchored at retained FD for all FS ops; calls `setfacl`/`acl_set_fd`, `mount`/`umount`, `fallocate` from within adapter only; emits path-free audit records for each op (audit is never atomic with redb write); injected into controller via Zone runtime ComponentSession; blocking filesystem calls run in bounded blocking-thread pool", - "integration": "Zone runtime creates adapter with required FD table and bundle reference at provider startup; passes `Arc` to controller via ComponentSession bootstrap", - "dataMigration": "None (adapter replaces direct broker-op call sites)", - "validation": "Adapter hermetic tests: each effect op called with mock FD table and bundle; no path in any output; anchored-path rejection for RESOLVE_BENEATH violations; `cargo deny check` verifies adapter does not expose raw paths to Provider crate; `integration/provision.rs` exercises full adapter path", - "removalProof": "Baseline broker op handlers (`state_dir.rs`, `storage_contract.rs`, `swtpm_dir.rs`, `store_sync.rs`, `store_view_posture.rs`) retired only after Volume controller parity is confirmed and all callers are on the adapter" - }, - { - "workItemId": "ADR046-vl-013", - "specId": "ADR-046-provider-volume-local", - "specPath": "docs/specs/providers/ADR-046-provider-volume-local.md", - "dependencyOwner": "ADR046-vl-001; ADR046-vl-012; Zone broker/core owner", - "currentSource": "No equivalent; new", - "reuseSource": null, - "reuseAction": "new", - "destination": "Zone core ProviderDeployment controller-start path (outside `d2b-provider-volume-local`)", - "detailedDesign": "The volume-local controller declares no Provider state Volume, so there is no bootstrap Volume, no `BootstrapProviderStateVolume` broker op, no pre-provisioned controller Volume, and no bootstrap-storage exception (D086, superseded by D087). On first install and on every daemon restart, core ProviderDeployment starts the volume-local controller Process directly; the controller reaches `Ready` from its own resource `status`, the core Operation ledger, and a resource-store relist. Once Ready, it reconciles every Volume carrying `providerRef: Provider/volume-local` (operator-created Volumes and other Providers' declared state Volumes) as they appear in its `providerRef` watch, re-verifying identity markers against external reality, never creating them itself. A Guest bootstraps its own Guest-local volume-local instance from Guest-local primitives only.", - "integration": "Core ProviderDeployment spawns the controller Process with no state-Volume prerequisite; the controller's startup relist reconciles served Volumes and re-verifies markers", - "dataMigration": "None; pre-existing baseline `StorageRoot` rows for the volume-local controller are superseded on v3 reset", - "validation": "`integration/provider_state.rs`: controller starts and reaches Ready with no state Volume; served Volumes reconciled and markers re-verified after restart; no bootstrap Volume and no bootstrap Provider Process in the resource list", - "removalProof": "Not applicable (new)" - }, - { - "workItemId": "ADR046-volume-001", - "specId": "ADR-046-resources-volume", - "specPath": "docs/specs/ADR-046-resources-volume.md", - "dependencyOwner": "W0 shared contract root; `d2b-contracts`", - "currentSource": "`packages/d2b-core/src/storage.rs`, `sync.rs`; `nixos-modules/storage-json.nix`", - "reuseSource": null, - "reuseAction": "extract and adapt", - "destination": "`packages/d2b-contracts/src/v3/volume.rs`, `volume_layout.rs`, `volume_attachment.rs`", - "detailedDesign": "Complete Volume ResourceSpec, LayoutEntry, all policy enums (values preserved from baseline), AclGrant, ViewSpec, AttachmentSpec, quota placeholder, strict serde unknown-field rejection, canonicalization, bounds", - "integration": "Provider dossiers/controller descriptors bind exact types; Nix resource compiler emits canonical JSON", - "dataMigration": "Full d2b 3.0 reset; storage.json rows migrated per table above", - "validation": "Golden JSON spec vectors; serde unknown-field; path anchor/depth/traversal validators; ACL grant bounds; policy enum coverage", - "removalProof": "`d2b-core/src/storage.rs` and `sync.rs` removed only after all Volume-successor consumers are live" - }, - { - "workItemId": "ADR046-volume-002", - "specId": "ADR-046-resources-volume", - "specPath": "docs/specs/ADR-046-resources-volume.md", - "dependencyOwner": "ADR046-volume-001; volume-local Provider owner", - "currentSource": "`nixos-modules/storage-json.nix`, `nixos-modules/store.nix`, `packages/d2b-priv-broker/src/ops/swtpm_dir.rs`, `packages/d2b-priv-broker/src/ops/storage_contract.rs` (`reconcile_storage_scope`, `validate_lock_spec`), `packages/d2b-priv-broker/src/ops/store_sync.rs` (`run_store_sync`, `run_store_sync_repair`, `cleanup_store_view`, `prune_gcroots`), `packages/d2b-priv-broker/src/ops/store_sync_audit.rs`, `packages/d2b-priv-broker/src/ops/store_sync_export.rs`, `packages/d2b-priv-broker/src/ops/store_view_posture.rs` (`posture_store_view_matrix_paths`, `plant_live_marker_with_matrix_posture`), `packages/d2b-priv-broker/src/ops/state_dir.rs` (`PrepareStateDir`, `PrepareRuntimeDir`), `packages/d2b-host/src/hardlink_farm.rs` (`build_store_view`, `GenerationMarker`, `gcroots_dir`, `state_dir`), `packages/d2b-core/src/storage_lifecycle.rs` (`StorageLifecycleReport`, `StorageLifecycleIssue`), `packages/d2b-contract-tests/tests/storage_sync_contracts.rs`, `packages/d2bd/src/ownership_preflight.rs`", - "reuseSource": null, - "reuseAction": "extract and adapt", - "destination": "`packages/d2b-provider-volume-local/src/` (layout engine, store_view.rs, swtpm_volume.rs, broker op adapters); `packages/d2b-provider-volume-local/tests/` (hermetic layout/store-view/swtpm tests); `packages/d2b-provider-volume-local/integration/` (container Host-path and store-view FS fixtures); `packages/d2b-provider-volume-local/README.md`", - "detailedDesign": "volume-local controller: layout engine (provision/repair/cleanup/adopt per policy), store-view mode (hardlink farm from `hardlink_farm.rs`, private-NS sync, zero-length marker, `gcroots/` and `state/` at store-view root, `sync.lock` OFD), swtpm volume hardening (provisionIfNeverProvisioned + marker + fail-closed repair as in `swtpm_dir.rs`), path-free broker audit ops, storage lifecycle report, opaque BundleOpId contract preserved from `storage_contract.rs`", - "integration": "volume-local controller registered under Host/system-core; resource status written via ResourceClient", - "dataMigration": "Per-VM `storage.json` rows (scoped `\"vm:\"` → `ownerRef: Guest/`) replaced by Volume resources generated by Nix resource compiler; broker continues to own path operations", - "validation": "`tests/unit/nix/cases/per-vm-state-ownership.nix` adapted to Volume LayoutEntry matrix; `tests/unit/smoke/smoke-eval-tpm.nix` migrated to TPM Volume invariant; `d2b-contract-tests/tests/storage_sync_contracts.rs` parity tests adapted; new: store-view same-filesystem, zero-length marker existence, sync.lock preserve-OFD, gcroots at store-view root (not meta/), state/ dir existence, swtpm fail-closed-on-missing-after-provision, anchored-path validators", - "removalProof": "`nixos-modules/storage-json.nix` removed only after Volume resources replace all path rows and all consumers verified" - }, - { - "workItemId": "ADR046-volume-003", - "specId": "ADR-046-resources-volume", - "specPath": "docs/specs/ADR-046-resources-volume.md", - "dependencyOwner": "ADR046-volume-001; volume-virtiofs Provider owner", - "currentSource": "`packages/d2b-host/src/virtiofsd_argv.rs` (`VirtiofsdArgvInput`, `generate_virtiofsd_argv`), `nixos-modules/minijail-profiles.nix` (virtiofsdProfiles; principals `d2b--runner`, `d2b--gctlfs`), `nixos-modules/processes-json.nix` (virtiofsdRunner shape; `roStoreSharedDir` sentinel), `packages/d2b-core/src/processes.rs` (`ProcessRole::Virtiofsd`, `VmProcessDag`; the virtiofsd dag node is a `ProcessRole::Virtiofsd` entry in a WorkloadId-keyed `VmProcessDag`), `packages/d2b-priv-broker/src/ops/spawn_runner.rs` (`SpawnRunnerPlan` for virtiofsd; current `SpawnRunnerPlanInput` carries `adr_carve_out` for virtiofsd swtpm path), `packages/d2b-priv-broker/src/sys.rs` (clone3/user-NS pre-establishment), ADR 0021", - "reuseSource": null, - "reuseAction": "extract and adapt", - "destination": "`packages/d2b-provider-volume-virtiofs/src/` (controller, virtiofsd_argv.rs); `packages/d2b-provider-volume-virtiofs/tests/` (hermetic argv/lifecycle/ADR-0021 tests); `packages/d2b-provider-volume-virtiofs/integration/` (virtiofsd launch and guest-mount fixtures); `packages/d2b-provider-volume-virtiofs/README.md`", - "detailedDesign": "volume-virtiofs controller: attachment lifecycle, owned virtiofsd Process create/update/delete, argv generation (reuse current 14 tests), ADR 0021 invariant (`capabilityClasses: []`, `startRoot: false`, `sandbox: chroot`, user-NS via `userNamespace.mappingClass: process-principal-root`), per-attachment export socket readiness check (`unix-socket-exists` readiness kind; current v2 socket path: `/run/d2b/vms//-virtiofs-.sock`; v3: stable hash-derived private path under Zone runtime directory, never exposed in spec/status/API), guest-mount status observation, finalizer drain", - "integration": "volume-virtiofs registered under Host; virtiofsd Process resource owned by Volume; guest-control health integration for mount readiness", - "dataMigration": "Current `processes-json.nix` virtiofsd `VmProcessDag` nodes (keyed by `WorkloadId` = current VM name, role `ProcessRole::Virtiofsd`) replaced by virtiofsd Process resources owned by Volume", - "validation": "Migrated `virtiofsd_argv` unit tests (14 tests); `tests/tools/gen-migration-ledger.sh` virtiofsd-argv-shape gate adapted; `minijail-validator-virtiofsd` gate adapted to Process sandbox spec; new: attachment lifecycle (create/ready/delete), ADR 0021 invariant rejection test, multi-attachment isolation, readOnly flag per access mode, store-view shared-dir = store-view/live (never /nix/store)", - "removalProof": "`nixos-modules/processes-json.nix` virtiofsdRunner block removed only after virtiofsd Process resources pass parity; `packages/d2bd/src/supervisor/dag.rs` `ProcessRole::Virtiofsd` path removed after controller lifecycle covers all cases" - }, - { - "workItemId": "ADR046-volume-004", - "specId": "ADR-046-resources-volume", - "specPath": "docs/specs/ADR-046-resources-volume.md", - "dependencyOwner": "ADR046-volume-001; Nix integrator", - "currentSource": "`nixos-modules/storage-json.nix`, `nixos-modules/store.nix`, `nixos-modules/options-vms.nix` (`d2b.vms..*` — current VM Nix option namespace; virtiofs shares and TPM enable are configured here), `nixos-modules/options-realms-workloads.nix` (`d2b.realms..stateDir` — current realm workload state root), `packages/d2b-realm-core/src/workload.rs` (`WorkloadProviderKind::LocalVm`/`QemuMedia`/`UnsafeLocal` — informs which WorkloadIds need store-view Volumes vs. no Volume)", - "reuseSource": null, - "reuseAction": "adapt", - "destination": "`nixos-modules/resources-volume.nix`, `nixos-modules/options-volumes.nix`", - "detailedDesign": "Nix resource compiler for Volume/LayoutEntry/View/Attachment from d2b.zones config; strict schema validation; emit canonical JSON per Volume; generate store-view Volume per Guest (from current `d2b.vms.` → future flat `d2b.zones..resources.` with `type = \"Guest\"`) with hardlink-farm layout (gcroots/, state/ at root per `hardlink_farm.rs`); generate swtpm Volume for TPM-enabled Guests; emit volume-virtiofs attachment spec per virtiofs share; migration: store-view stateDir root configuration", - "integration": "`nixos-modules/default.nix` wires resources-volume.nix; Nix evaluation tests verify canonical output", - "dataMigration": "`d2b.vms..shares` (virtiofs entries) → Volume attachments; `d2b.vms..tpm.enable` → swtpm Volume", - "validation": "nix-unit cases for store-view Volume output (gcroots at root), TPM Volume spec, virtiofs attachment spec, anchored-path rejection; render parity with current storage.json path rows; canonical JSON golden vector; Provider schema validation rejection; symlink target validation; bundle digest coverage", - "removalProof": "Old `storage-json.nix` and `store.nix` emitters removed only after rendered Volume JSON passes all drift-check gates" - }, - { - "workItemId": "ADR046-volume-005", - "specId": "ADR-046-resources-volume", - "specPath": "docs/specs/ADR-046-resources-volume.md", - "dependencyOwner": "ADR046-volume-002, ADR046-volume-003; respective Provider owners", - "currentSource": "N/A (no baseline evidence for block-image, quota enforcement, snapshots, or tmpfs Volume paths)", - "reuseSource": null, - "reuseAction": "new", - "destination": "`packages/d2b-provider-volume-local/src/` (block-image, quota, snapshots, tmpfs, ACL reconciliation); `packages/d2b-provider-volume-local/tests/` (hermetic quota/tmpfs/ACL/block-image/snapshot tests); `packages/d2b-provider-volume-local/integration/` (block-image virtio-blk, FS-without-quota fixture, tmpfs memory-budget); `packages/d2b-provider-volume-virtiofs/src/` (single-writer enforcement, shared-write, private socket path contract); `packages/d2b-provider-volume-virtiofs/tests/` (single-writer rejection, shared-write capability gate, socket-path invariant); `packages/d2b-provider-volume-virtiofs/integration/` (shared-write cross-Guest fixture)", - "detailedDesign": "(1) **block-image SourceKind**: add `SourceKind::BlockImage` to volume-local; manage raw/qcow2 image file lifecycle; emit virtio-blk attachment spec consumed by Guest Provider; `quota.maxBytes` required; add store-overlay.img migration path for current `DiskInit` plan-op. (2) **Quota hard enforcement**: implement `enforcement: hard` capability check in volume-local at Volume creation time; query backing FS for quota/limits support; test with no-project-quota fixture; enforce `maxBytes`/`maxInodes` via xfs project quota or ext4 per-dir quota where available. (3) **Volume snapshots/migrations**: design and implement EphemeralProcess templates in volume-local catalog for snapshot (copy-on-write or rsync capture) and content migration (atomic rename + sync); no CLI-only path; all operations surface through resource API. (4) **Single-writer enforcement**: volume-virtiofs controller tracks active `read-write` attachment count; rejects a second `read-write` attachment while one is active (returns `ResourceConflict` error); `shared-write` mode accepted only if Provider declares `supportsSharedWrite: true`. (5) **tmpfs source**: implement tmpfs mount/unmount lifecycle in volume-local; `maxBytes` → `size=`, `maxInodes` → `nr_inodes=` mount options; charge memory against Host/Guest budget; cleanup unmounts on Volume deletion or restart. (6) **Bounds enforcement**: enforce max 1024 layout entries, 64 Views, 64 attachments at schema validation layer; add corresponding row to API request-size limit table in `ADR-046-resource-api-and-authorization`. (7) **File/symlink first-class lifecycle**: implement independent `createPolicy`/`repairPolicy`/`cleanupPolicy` for `file` and `symlink` entries; implement `target` field validation (relative, no `..`, must resolve within Volume root); `symlink` create writes the target link. (8) **ACL principal ResourceRef**: remove bare `{type,ref}` struct from AclGrant; implement `User/` ResourceRef resolution with User resource watch and re-reconcile on User revision change. (9) **Continuous ACL reconciliation**: implement `foreignChildPolicy: preserve|fail` in broker reconcile loop; re-apply `accessAcl`/`defaultAcl` to all existing entries and children on every repair cycle; emit `ForeignAclViolation` condition when `foreignChildPolicy: fail` and unexpected entries found. (10) **virtiofsd socket path contract**: implement stable hash-derived private socket path in volume-virtiofs (deterministic hash of Zone name + Volume name + attachment executionRef); assert path never appears in public status, spec, audit, or CLI output; validate with a dedicated security invariant test.", - "integration": "Each sub-item produces a focused spec amendment; resolved decisions already reflected in spec revision 2", - "dataMigration": "Per-sub-item; block-image and tmpfs are new capabilities with no legacy migration required", - "validation": "(1) `VirtioblkArgvInput` unit tests; block-image integration fixture. (2) Quota-enforcement fixture with FS-without-quota; hard-enforcement failure test. (3) EphemeralProcess snapshot lifecycle test; content-migration parity test. (4) Single-writer rejection test; shared-write capability gate test. (5) tmpfs mount/unmount lifecycle test; memory-budget accounting assertion. (6) Schema bound rejection tests (1025 entries, 65 views, 65 attachments). (7) File/symlink independent lifecycle tests; target validation (absolute rejected, `..` rejected, escape rejected). (8) ACL principal ResourceRef validation; numeric form rejected; User revision trigger test. (9) foreignChildPolicy preserve/fail tests; continuous repair cycle test. (10) Socket path invariant test; no-status-leak assertion.", - "removalProof": "None — net-new capabilities; no prior owner to remove" - }, - { - "workItemId": "ADR046-volume-006", - "specId": "ADR-046-resources-volume", - "specPath": "docs/specs/ADR-046-resources-volume.md", - "dependencyOwner": "ADR046-volume-001, ADR046-volume-004; Nix integrator and core-controller config-publication owner", - "currentSource": "`nixos-modules/storage-json.nix` (current Nix eval-time validation via `lib.asserts`; schema checked by `d2b-contract-tests/tests/storage_sync_contracts.rs`); `packages/d2bd/src/` (current config activation / host-prepare dispatch)", - "reuseSource": null, - "reuseAction": "new", - "destination": "`nixos-modules/resources-volume.nix` (Nix eval-time schema validation, canonical JSON emission, bundle digest); `packages/d2b-core-controller/src/configuration.rs` (config-publication handler cleanup logic); `packages/d2b-contracts/src/v3/zone_bundle.rs` (bundle index schema)", - "detailedDesign": "**Nix eval/build validation**: implement all 15 validation steps in §Nix eval/build validation as Nix assertions; abort build on any failure with structured error (Volume name + field path + error class); provider-specific settings schema (`root-config.schema.json`, `attachment.schema.json`) read from the private artifact catalog entry for each Provider's `artifactId` by the resource compiler; validate against `lib.evalModules`-compatible schema; emit canonical sorted JSON with all defaults materialized; compute SHA-256 per resource; emit Zone resource bundle with `generationId` and `bundleDigest`. **Config-publication handler cleanup**: on new bundle activation, diff resources with `metadata.managedBy = \"configuration\"` between new and prior bundle; issue async Delete for resources absent from new bundle; mark deleted resources with `ConfigurationRemoved` condition; track pending-cleanup set in Zone status; Zone status is `Degraded/PendingCleanup` while prior-generation deletions are in progress; activation is immediate but Zone readiness reflects cleanup completion. **Config-owned vs controller-created distinction**: `metadata.managedBy = \"configuration\"` is the authoritative marker set by core at activation; the bundle index carries only digests; controller-created resources have `metadata.managedBy = \"controller\"` and are never touched by the configuration cleanup pass. **Prior generation retention**: retain `priorGenerationCount` prior generations (default 3, range 1..16); no time-based TTL; when count is exceeded prune oldest generation from the store with a tamper-evident audit record. **Generation reactivation**: re-activate any retained prior bundle via `ActivateGeneration` operation; cancel in-flight Deletes for resources being reinstated; issue Deletes for resources added by the aborted new generation.", - "integration": "`nixos-modules/default.nix` wires resources-volume.nix; `d2b-core-controller` config-publication handler consumes the bundle; all Volume controllers observe `ConfigurationRemoved` condition and respond to finalizer triggers", - "dataMigration": "Full d2b 3.0 reset; no partial import of prior generation state", - "validation": "Tests per §Cleanup contract — Tests for removed-resource cleanup table (10 tests); nix-unit: `volume_canonical_json_golden_vector`, `volume_bundle_digest_covers_all_resources`, `provider_schema_validation_rejects_unknown_fields`, `symlink_target_escape_rejected_at_eval`, `tmpfs_without_quota_rejected_at_eval`, `layout_bounds_1025_entries_rejected`, `attachment_bounds_65_rejected`, `conflicting_host_paths_rejected`; integration: cleanup audit redaction, generation reactivation, prior-generation pruning", - "removalProof": "Old `storage-json.nix` schema assertions removed only after Nix eval-time Volume validation covers all prior `lib.asserts` paths; old config-activation code in `d2bd` removed after config-publication handler is live" - }, - { - "workItemId": "ADR046-vsock-001", - "specId": "ADR-046-provider-transport-vsock", - "specPath": "docs/specs/providers/ADR-046-provider-transport-vsock.md", - "dependencyOwner": "Title: Implement `VsockEffectPort` trait and `OpaqueEndpointId`/`OpaqueBindingId` newtypes; Phase 1; Priority P0; Depends on ADR046-bus-001 (OwnedTransport in d2b-session); Owner crate `d2b-provider-transport-vsock`.", - "currentSource": "Evidence class `test-only-or-preview`; baseline has no generic vsock transport Provider or opaque endpoint/binding ID trait.", - "reuseSource": null, - "reuseAction": "net-new trait/newtypes with redaction tests; no real vsock socket opened", - "destination": "`packages/d2b-provider-transport-vsock/src/effect_port.rs`; test fake in `tests/effect_port_mock.rs`; redaction checks in `tests/redaction.rs`.", - "detailedDesign": "Implement `VsockEffectPort` trait and `OpaqueEndpointId`/`OpaqueBindingId` newtypes. Define `VsockEffectPort` async trait and opaque ID newtypes in `effect_port.rs`; implement `FakeVsockEffectPort` for tests; `redaction.rs` asserts no raw `u32` in any `Debug`/`Display` output of opaque types; no real vsock socket opened.", - "integration": "Core ZoneLink/delegation controller calls the Provider service with opaque IDs; Provider calls injected `VsockEffectPort`; live AF_VSOCK resolution remains in core runtime, not the Provider crate.", - "dataMigration": "Full d2b 3.0 reset; no v2 state/config import.", - "validation": "Proof type: hermetic unit + redaction test; `tests/effect_port_mock.rs` and `tests/redaction.rs`.", - "removalProof": "None — net-new; no prior owner to remove." - }, - { - "workItemId": "ADR046-vsock-002", - "specId": "ADR-046-provider-transport-vsock", - "specPath": "docs/specs/providers/ADR-046-provider-transport-vsock.md", - "dependencyOwner": "Title: Implement framing utilities and bridge task in Provider crate; Phase 1; Priority P0; Depends on ADR046-vsock-001; Owner crate `d2b-provider-transport-vsock`.", - "currentSource": "Evidence class `implemented-but-unwired`; main commit `a1cc0b2da4a08ca3240a770a972fe4da6f912bef` `packages/d2b-session-unix/src/vsock.rs` contains `FramedVsockTransport` framing behavior, not current v3 baseline behavior.", - "reuseSource": "Main `a1cc0b2d` `packages/d2b-session-unix/src/vsock.rs` framing-only code and `packages/d2b-session-unix/tests/unix_session.rs` vsock framing subset.", - "reuseAction": "copy/adapt framing-only code; exclude raw AF_VSOCK socket calls and ADR 0045 endpoint-role assumptions", - "destination": "`packages/d2b-provider-transport-vsock/src/framing.rs` and `src/bridge.rs`; tests in `packages/d2b-provider-transport-vsock/tests/framing.rs`.", - "detailedDesign": "Implement framing utilities and bridge task in Provider crate. Copy `FramedVsockTransport` framing-only code (length-prefix encode/decode, bounded allocation, EOF/reset) from main `a1cc0b2d` → `framing.rs`; implement bridge task pumping bytes between an opaque `AsyncRead+AsyncWrite` stream from `VsockEffectPort::open` and the named ComponentSession stream; hermetic tests using `FakeVsockEffectPort` (no real socket).", - "integration": "OpenTransport creates a framed opaque stream, bridge task pumps to a named ComponentSession stream, and d2b-bus consumes it as an `OwnedTransport` without FD attachment support.", - "dataMigration": "Full d2b 3.0 reset; no v2 state/config import.", - "validation": "Proof type: hermetic framing tests; `tests/framing.rs` covers partial/coalesced records, oversized frames, EOF/reset classification, and no real socket.", - "removalProof": "None for framing; raw socket portions from the source are deliberately not copied into the Provider crate." - }, - { - "workItemId": "ADR046-vsock-003", - "specId": "ADR-046-provider-transport-vsock", - "specPath": "docs/specs/providers/ADR-046-provider-transport-vsock.md", - "dependencyOwner": "Title: Implement `VsockTransportService` (OpenTransport/CloseTransport/ObserveTransport); Phase 1; Priority P0; Depends on ADR046-vsock-002 and ADR046-bus-001; Owner crate `d2b-provider-transport-vsock`.", - "currentSource": "Evidence class `test-only-or-preview`; no current v3 generic `VsockTransportService` implementation exists.", - "reuseSource": null, - "reuseAction": "net-new service implementation over ComponentSession and fake effect port tests", - "destination": "`packages/d2b-provider-transport-vsock/src/service.rs`; tests `tests/open_close.rs`, `tests/observe.rs`, and conformance kit.", - "detailedDesign": "Implement `VsockTransportService` (OpenTransport/CloseTransport/ObserveTransport). Implement all three service methods in `service.rs`; `open_close.rs` and `observe.rs` test full service API against `FakeVsockEffectPort`; conformance kit passes.", - "integration": "Core ZoneLink/delegation controller is the only authorized caller; service opens named stream handles for d2b-bus, releases them on close, and streams transport events for observe.", - "dataMigration": "Full d2b 3.0 reset; no v2 state/config import.", - "validation": "Proof type: service round-trip test (mock); `tests/open_close.rs`, `tests/observe.rs`, and provider conformance tests.", - "removalProof": "None — net-new; no prior owner to remove." - }, - { - "workItemId": "ADR046-vsock-004", - "specId": "ADR-046-provider-transport-vsock", - "specPath": "docs/specs/providers/ADR-046-provider-transport-vsock.md", - "dependencyOwner": "Title: Implement `LiveVsockEffectPort` in Zone runtime; Phase 2; Priority P0; Depends on ADR046-vsock-001 and the Zone allocator (`ADR-046-resources-zone-control`); Owner crate `d2b-core-controller`.", - "currentSource": "Evidence class `ADR-only`; baseline has guest-control and relay vsock paths, but no allocator-backed `LiveVsockEffectPort` for ZoneLink transport.", - "reuseSource": null, - "reuseAction": "net-new core adapter; keep raw AF_VSOCK syscalls outside Provider crate", - "destination": "`d2b-core-controller` Zone runtime `LiveVsockEffectPort`; Provider receives it by dependency injection at startup.", - "detailedDesign": "Implement `LiveVsockEffectPort` in Zone runtime. Zone runtime provides `LiveVsockEffectPort` backed by core allocator state; resolves `OpaqueEndpointId` → CID and `OpaqueBindingId` → port; opens AF_VSOCK socket; injects into Provider service at startup; no raw CID/port exposed to Provider.", - "integration": "Zone allocator issues endpoint/binding IDs; core runtime resolves them, opens/accepts AF_VSOCK sockets, returns opaque streams to Provider service, and excludes reserved ports.", - "dataMigration": "Full d2b 3.0 reset; no v2 state/config import.", - "validation": "Proof type: integration test; `integration/host_guest.rs` exercises live open/close byte round-trip with the injected effect.", - "removalProof": "None — net-new core adapter; no prior owner to remove." - }, - { - "workItemId": "ADR046-vsock-005", - "specId": "ADR-046-provider-transport-vsock", - "specPath": "docs/specs/providers/ADR-046-provider-transport-vsock.md", - "dependencyOwner": "Title: Core ProviderDeployment creates/deletes service component state Volume; Phase 1; Priority P0; Depends on the volume-local Provider (`ADR-046-provider-volume-local`); Owner crate `d2b-provider-transport-vsock`.", - "currentSource": "Evidence class `test-only-or-preview`; no operator-authored v3 state Volume exists for transport-vsock in baseline.", - "reuseSource": null, - "reuseAction": "net-new ProviderDeployment/Volume integration and tests", - "destination": "ProviderDeployment Volume creation/deletion path plus `packages/d2b-provider-transport-vsock/tests/state_volume.rs`.", - "detailedDesign": "Core ProviderDeployment creates/deletes service component state Volume. Core ProviderDeployment creates `Volume/transport-vsock--service--empty-state--*` before the component Process and deletes it after the Process finalizer; transport-vsock Provider controller does not own Volume, does not add Volume to exported ResourceTypes, and does not create its prerequisite; Volume spec: empty schema, `kind: state`, `persistenceClass: persistent`, `migrationPolicy: none`, `User/d2b-transport-vsock` owner, minimal nonzero `quota.maxBytes`/`quota.maxInodes` with `enforcement: hard`, `private` sensitivity, `broker-maintained` identity marker; `state_volume.rs` test verifies Volume spec fields against canonical schema; integration test verifies marker written at install and removed at Provider deletion; no operator-authored Volume; component receives dirfd view only.", - "integration": "Core ProviderDeployment creates Volume before Process, volume-local reconciles it, Provider process receives only a dirfd view, and Provider deletion removes the Process before deleting the Volume/identity marker.", - "dataMigration": "Full d2b 3.0 reset; no v2 state/config import; state Volume is created fresh with `migrationPolicy: none`.", - "validation": "Proof type: unit + integration test; `tests/state_volume.rs` and Provider install/remove integration tests verify schema, user refs, marker lifecycle, and no ComponentPrincipal.", - "removalProof": "Remove the state Volume and its broker-maintained identity marker during Provider deletion; no operator-authored Volume remains." - }, - { - "workItemId": "ADR046-vsock-006", - "specId": "ADR-046-provider-transport-vsock", - "specPath": "docs/specs/providers/ADR-046-provider-transport-vsock.md", - "dependencyOwner": "Title: Integration test: real vsock socketpair + full ZoneLink open/close; Phase 2; Priority P1; Depends on ADR046-vsock-003 and ADR046-vsock-004; Owner crate `d2b-provider-transport-vsock`.", - "currentSource": "Evidence class `test-only-or-preview`; existing guest-control compile proof and socat relay tests are not full ZoneLink transport coverage.", - "reuseSource": null, - "reuseAction": "net-new integration coverage with no FD transfer over vsock", - "destination": "`packages/d2b-provider-transport-vsock/integration/host_guest.rs` and `integration/no_fd_transfer.rs`.", - "detailedDesign": "Integration test: real vsock socketpair + full ZoneLink open/close. `integration/host_guest.rs`: real vsock socketpair (Linux); `OpenTransport` + byte round-trip + `CloseTransport`; validates bridge throughput ≥ 512 MiB/s; `no_fd_transfer.rs`: structural rejection of attachment packets over vsock transport.", - "integration": "Test drives Provider service, LiveVsockEffectPort, d2b-bus `OwnedTransport`, byte bridge, close path, and attachment rejection across the integration lane.", - "dataMigration": "None — docs/tooling only; no runtime state.", - "validation": "Proof type: integration test; `make test-integration` runs `host_guest.rs` and `no_fd_transfer.rs`.", - "removalProof": "None — test coverage net-new; old duplicate vsock tests are retired only after successor assertions migrate." - }, - { - "workItemId": "ADR046-vsock-007", - "specId": "ADR-046-provider-transport-vsock", - "specPath": "docs/specs/providers/ADR-046-provider-transport-vsock.md", - "dependencyOwner": "Title: Delete legacy socat OTLP relay and CONNECT-proxy guest-control vsock; Phase 3; Priority P2; Depends on the observability-otel Provider (`ADR-046-provider-observability-otel`) and the Guest resource lifecycle (`ADR-046-resources-host-guest-process-user`); Owner crates `d2b-host`, `d2bd`.", - "currentSource": "Evidence class `implemented-and-reachable`; legacy sources are `packages/d2b-host/src/vsock_relay_argv.rs` socat OTLP relay and `packages/d2bd/src/guest_control_vsock.rs` CONNECT-proxy guest-control path.", - "reuseSource": null, - "reuseAction": "delete after replacement parity; preserve reserved guest-control/OTLP port exclusions until replacements own them", - "destination": "Remove legacy paths from `d2b-host` and `d2bd`; replacement lives in `observability-otel` Provider native vsock relay and Guest resource lifecycle/bootstrap.", - "detailedDesign": "Delete legacy socat OTLP relay and CONNECT-proxy guest-control vsock. Remove `vsock_relay_argv.rs` socat path after `observability-otel` Provider native vsock relay passes parity; remove `guest_control_vsock.rs` CONNECT-proxy after Guest resource lifecycle + guestd vsock bootstrap reach parity; no raw CID or socat vsock path remains.", - "integration": "Observability-otel owns OTLP vsock relay replacement; Guest lifecycle owns guest-control bootstrap replacement; transport-vsock ZoneLink allocator excludes ports 14317, 14318, and 14319.", - "dataMigration": "Full d2b 3.0 reset; no v2 relay or guest-control state/config import.", - "validation": "Proof type: deletion + parity test; parity tests for observability-otel and Guest lifecycle plus redaction checks that no raw CID/socat vsock path remains.", - "removalProof": "Delete `packages/d2b-host/src/vsock_relay_argv.rs` socat path after observability parity, delete `packages/d2bd/src/guest_control_vsock.rs` CONNECT-proxy after Guest lifecycle parity, and prove no raw CID or socat vsock path remains." - }, - { - "workItemId": "ADR046-vvfs-001", - "specId": "ADR-046-provider-volume-virtiofs", - "specPath": "docs/specs/providers/ADR-046-provider-volume-virtiofs.md", - "dependencyOwner": "ADR046-volume-001 (Volume contract types); ADR046-vvfs-export-001 (Export type); W1; volume-virtiofs Provider owner", - "currentSource": "`packages/d2b-host/src/virtiofsd_argv.rs` (VirtiofsdArgvInput, generate_virtiofsd_argv, 14 unit tests, golden argv.txt); `packages/d2b-host/src/lib.rs` (module declaration)", - "reuseSource": null, - "reuseAction": "extract and adapt", - "destination": "`packages/d2b-provider-volume-virtiofs/src/virtiofsd_argv.rs`; `packages/d2b-provider-volume-virtiofs/tests/argv_golden.rs`", - "detailedDesign": "Create crate skeleton with mandatory `src/`, `tests/`, `integration/`, `README.md`. Extract `VirtiofsdArgvInput` and `generate_virtiofsd_argv` with these changes: (1) replace `extra_args: Vec` with nothing (removed); (2) replace `socket_path: String` with `socket_path: SocketPath` newtype backed by `socket_path.rs`; (3) add `shared_dir_fd: i32` replacing `shared_dir: String` (FD-based); (4) replace `socket_group: Option` with `socket_group: Option`. Implement `socket_path.rs`: private path using SHA-256 of `\\x00\\x00`, truncated 8 hex chars, formatted as `/vms//vol-.vfd.sock`. Assert path length ≤ 108 bytes.", - "integration": "volume-virtiofs controller `export.rs` calls virtiofsd_argv.rs at spawn time; LaunchTicket carries resolved socket path as opaque sealed field", - "dataMigration": "v3.0 reset; socket path format changes", - "validation": "`tests/argv_golden.rs`: 14 migrated tests + `no_extra_args_ever_emitted`, `socket_path_is_not_in_args`, `shared_dir_is_fd_path`, `path_length_within_sunpath_limit`; `tests/socket_path_privacy.rs`: `socket_path_not_in_export_status`, `socket_path_not_in_volume_status`, `socket_path_not_in_audit_record`; `tests/schema_conformance.rs`: `process_spec_readiness_class_is_provider_defined`, `process_spec_readiness_has_no_kind_or_period_fields`, `process_spec_budget_cpu_request_limit_nested`, `process_spec_budget_memory_request_limit_nested`, `process_spec_budget_pids_limit_present`, `process_spec_budget_fds_limit_present`, `process_spec_sandbox_no_new_privileges_true`, `process_spec_sandbox_read_only_root_true`, `process_spec_no_host_uid_gid_in_spec`", - "removalProof": "`packages/d2b-host/src/virtiofsd_argv.rs` removed only after parity confirmed by argv-shape gate" - }, - { - "workItemId": "ADR046-vvfs-002", - "specId": "ADR-046-provider-volume-virtiofs", - "specPath": "docs/specs/providers/ADR-046-provider-volume-virtiofs.md", - "dependencyOwner": "ADR046-vvfs-001; ADR046-volume-001; W1; broker/spawn owner", - "currentSource": "`packages/d2b-priv-broker/src/sys.rs` (`clone3_spawn_runner`, user-NS pre-establishment block); `packages/d2b-priv-broker/src/ops/spawn_runner.rs` (`SpawnRunnerPlanInput.user_namespace`, `RunnerIsolationSpec.user_namespace`); ADR 0021 implementation contract", - "reuseSource": null, - "reuseAction": "extract conformance kit only; pre-establishment code stays in broker", - "destination": "`packages/d2b-provider-volume-virtiofs/src/user_ns.rs` (conformance kit); `packages/d2b-provider-volume-virtiofs/tests/adr021_invariant.rs`", - "detailedDesign": "`user_ns.rs` contains only the conformance check and template descriptor assertion: verify that the virtiofsd-worker Process template declares `capabilityClasses: []`, `startRoot: false`, `noNewPrivileges: true`, `readOnlyRoot: true`, and `sandbox.userNamespace.mappingClass: process-principal-root`. `hostUid`/`hostGid` are NOT set by the controller — system-minijail resolves the mapping from the `User/vol--vfd` principal when building the LaunchTicket via the effect port. The conformance check rejects any template mutation that adds host capability classes, sets `startRoot: true`, disables `noNewPrivileges`, or disables `readOnlyRoot`. The user-NS pre-establishment code itself remains in `d2b-priv-broker/src/sys.rs` and is invoked via the system-minijail effect port.", - "integration": "volume-virtiofs controller calls conformance check before emitting any Process Create; effect port adapter in system-minijail invokes broker spawn path", - "dataMigration": "v3.0 reset; current `adr_carve_out` field in `SpawnRunnerPlanInput` removed; ADR 0021 path is now the default", - "validation": "`tests/adr021_invariant.rs`: `virtiofsd_capability_classes_must_be_empty`, `virtiofsd_start_root_must_be_false`, `virtiofsd_no_new_privileges_must_be_true`, `virtiofsd_read_only_root_must_be_true`, `process_spec_has_no_host_uid_gid`, `sandbox_namespace_never_emitted`, `user_ns_single_entry_single_uid_mapping`, `uid_map_write_ordering_uid_setgroups_gid`, `child_setuid_in_ns_not_host_uid`, `clone_newns_not_in_clone3_flags`, `child_exits_user_ns_sync_on_pipe_eof`", - "removalProof": "`adr_carve_out` field and virtiofsd-specific branch in current `SpawnRunnerPlanInput` removed only after v3 LaunchTicket covers all virtiofsd spawn cases" - }, - { - "workItemId": "ADR046-vvfs-003", - "specId": "ADR-046-provider-volume-virtiofs", - "specPath": "docs/specs/providers/ADR-046-provider-volume-virtiofs.md", - "dependencyOwner": "ADR046-vvfs-001, ADR046-vvfs-002, ADR046-vvfs-export-001; ADR046-volume-001; W2; volume-virtiofs controller owner", - "currentSource": "`packages/d2bd/src/supervisor/dag.rs` (ProcessRole::Virtiofsd dag node); `nixos-modules/processes-json.nix` (virtiofsdRunner block; attachment-to-Process mapping)", - "reuseSource": null, - "reuseAction": "extract and adapt", - "destination": "`packages/d2b-provider-volume-virtiofs/src/controller.rs`; `packages/d2b-provider-volume-virtiofs/src/export.rs`", - "detailedDesign": "Implement volume-virtiofs-controller reconcile loop using toolkit ResourceClient. Watch selector: `virtiofs.d2bus.org.Export` resources (all in zone), owned Process resources, owned User resources, Volume resources (read-only for view/vcpu resolution), Guest resources (read-only for vcpu count). On `spec-generation-changed` for an Export: (1) resolve View from Volume; (2) check store-view marker if applicable; (3) resolve threadPoolSize from Guest vcpus; (4) ensure User/vol--vfd; (5) diff against current Process; (6) emit Create/UpdateSpec. On `owned-resource-changed` for a Process: update Export status. On `deletionRequestedAt` for Export: two-phase teardown (§6.2).", - "integration": "volume-virtiofs controller registered by core ProviderDeployment; receives owned-resource-changed trigger from Export; emits Process resources consumed by system-minijail", - "dataMigration": "Current `ProcessRole::Virtiofsd` dag nodes replaced by Export → Process resource lifecycle", - "validation": "`tests/export_lifecycle.rs`: `export_create_spawns_virtiofsd_process`, `export_ready_when_socket_present`, `export_delete_terminates_virtiofsd`, `export_delete_waits_for_guest_mount_absent`, `export_delete_with_guest_unreachable_holds_finalizer_degraded`, `export_proof_of_ns_death_clears_finalizer`; `tests/multi_attachment.rs`: `two_guests_get_separate_exports_and_processes`, `process_failure_does_not_affect_sibling_export`; `tests/schema_conformance.rs`: `provider_state_set_volume_created_on_install`, `provider_state_set_volume_owner_ref_is_provider`, `provider_state_set_volume_layout_principal_is_user_not_component_principal`, `provider_state_set_no_cross_component_volume_sharing`", - "removalProof": "`ProcessRole::Virtiofsd` branch in `d2bd/src/supervisor/dag.rs` removed only after v3 controller passes all lifecycle tests" - }, - { - "workItemId": "ADR046-vvfs-004", - "specId": "ADR-046-provider-volume-virtiofs", - "specPath": "docs/specs/providers/ADR-046-provider-volume-virtiofs.md", - "dependencyOwner": "ADR046-vvfs-003; guest-control integration owner; W2", - "currentSource": "`packages/d2bd/src/vm_readiness.rs` (`ReadinessKind::UnixSocketExists`); guest-control vsock health protocol", - "reuseSource": null, - "reuseAction": "extract and adapt", - "destination": "`packages/d2b-provider-volume-virtiofs/src/readiness.rs`; `packages/d2b-provider-volume-virtiofs/integration/guest_mount_readiness/`", - "detailedDesign": "`unix-socket-exists` readiness: check file existence at the private socket path via a bounded blocking adapter (e.g., `tokio::task::spawn_blocking` wrapping `fstatat(2)` relative to the zone runtime `OwnedFd`, or an async-safe fd-relative equivalent); no blocking syscall on the async executor thread. Probe period 1 s; timeout 30 s. On socket present → set `Export.status.exportReady: true`. Guest-mount readiness: send `VirtioFsMountReady?` probe to guest-control health endpoint over vsock. Response `MountReady` sets `guestMountReady: true`. Response `MountAbsent` or timeout sets `guestMountReady: false`. The vsock health probe is async-native. If Guest is down, set Export `phase: Unknown`. All readiness probes (unix-socket-exists, guest-mount health) use bounded blocking adapters or async-safe fd-relative equivalents; no blocking I/O on the reconcile executor thread.", - "integration": "`readiness.rs` called from `controller.rs` reconcile loop; uses toolkit health probe client", - "dataMigration": "Current `UnixSocketExists` readiness kind adapted to FD-based path resolution", - "validation": "`tests/export_lifecycle.rs` (extended); `integration/guest_mount_readiness/`: virtiofsd launches, socket appears, guest-control probe returns MountReady, guestMountReady flips to true; probe returns MountAbsent on umount", - "removalProof": "Current `UnixSocketExists` readiness path in `d2bd` retired after volume-virtiofs readiness covers all cases" - }, - { - "workItemId": "ADR046-vvfs-005", - "specId": "ADR-046-provider-volume-virtiofs", - "specPath": "docs/specs/providers/ADR-046-provider-volume-virtiofs.md", - "dependencyOwner": "ADR046-vvfs-003, ADR046-vvfs-004; ADR046-volume-002 (store-view Volume); W3", - "currentSource": "`packages/d2b-host/src/hardlink_farm.rs` (`live_dir()`, marker `live/.d2b-marker-`, zero-length); `nixos-modules/processes-json.nix` (`roStoreSharedDir` sentinel `share.source == \"/nix/store\"` → `store-view/live`); `nixos-modules/store.nix` (per-VM hardlink farm)", - "reuseSource": null, - "reuseAction": "adapt", - "destination": "`packages/d2b-provider-volume-virtiofs/src/controller.rs` (pre-launch prerequisite check); `packages/d2b-provider-volume-virtiofs/integration/store_view_readonly/`", - "detailedDesign": "Before issuing Process Create for a store-view virtiofs Export, check that `live/.d2b-marker-` exists (zero-length, correct mode) via a bounded blocking adapter (e.g., `tokio::task::spawn_blocking` wrapping `fstatat(2)` relative to the zone runtime directory, or an async-safe fd-relative equivalent); no blocking syscall on the async executor thread directly. If absent, requeue with exponential backoff. Assert `--shared-dir` resolves to `store-view/live` (the `ro-store` View path), never to `/nix/store`. Validate in `integration/store_view_readonly/` that virtiofsd serves only paths under `store-view/live`.", - "integration": "Pre-launch check in controller.rs; store-view Export recognized by `view == \"ro-store\"` and `access == \"read-only\"`", - "dataMigration": "Current `roStoreSharedDir` redirect in `processes-json.nix` replaced by `ro-store` View definition in the store-view Volume resource", - "validation": "`integration/store_view_readonly/`: mount from guest reads closure paths; no host-store path escapes; `tests/argv_golden.rs`: `store_view_shared_dir_is_live_not_nix_store`; `tests/export_lifecycle.rs`: `store_view_launch_waits_for_marker`", - "removalProof": "`nixos-modules/processes-json.nix` `virtiofsdRunner` block and `roStoreSharedDir` sentinel removed only after store-view virtiofs Export resources pass parity gate" - }, - { - "workItemId": "ADR046-vvfs-006", - "specId": "ADR-046-provider-volume-virtiofs", - "specPath": "docs/specs/providers/ADR-046-provider-volume-virtiofs.md", - "dependencyOwner": "ADR046-vvfs-001, ADR046-volume-004; Nix integrator; W3", - "currentSource": "`nixos-modules/processes-json.nix` (virtiofsdRunner block); `nixos-modules/minijail-profiles.nix` (virtiofsdProfiles); `nixos-modules/options-vms.nix` (`d2b.vms..shares.*`)", - "reuseSource": null, - "reuseAction": "adapt", - "destination": "`nixos-modules/resources-volume.nix` (store-view and user Volume attachment emission); `nixos-modules/options-volumes.nix` (optional user-facing volume/attachment options)", - "detailedDesign": "Extend the Nix resource compiler to: (1) auto-emit a store-view Volume (with `ro-store` and `meta` Views, virtiofs ro-store attachment) per Guest that has a VM runtime Provider; (2) emit virtiofs attachment entries for explicitly configured user Volumes; (3) emit `User/vol--vfd` resources for each Volume with virtiofs attachments; (4) emit `Provider/volume-virtiofs` as a Provider resource when any virtiofs attachment is configured. volume-local creates Export resources at runtime (not in Nix bundle); no `virtiofs.d2bus.org.Export` resources appear in the Nix-emitted bundle. All eval validation steps (§16.5) apply.", - "integration": "`nixos-modules/default.nix` wires resources-volume.nix; nix-unit tests verify canonical output", - "dataMigration": "`d2b.vms..shares` virtiofs entries → Volume attachments; `d2b.vms.` store-view auto-emission replaces `nixos-modules/store.nix` virtiofsd portion", - "validation": "nix-unit: `store_view_volume_auto_emitted_per_guest`, `volume_virtiofs_attachment_canonical_json`, `virtiofs_provider_emitted_when_attachment_configured`, `vfd_user_emitted_per_volume`, `second_read_write_attachment_rejected_at_eval`, `transport_virtiofs_requires_provider_installed`; drift-check gate for `nixos-modules/processes-json.nix` virtiofsdRunner removal", - "removalProof": "`nixos-modules/processes-json.nix` virtiofsdRunner block, `nixos-modules/minijail-profiles.nix` virtiofsdProfiles removed only after Nix resource compiler produces parity output and all nix-unit cases pass" - }, - { - "workItemId": "ADR046-vvfs-export-001", - "specId": "ADR-046-provider-volume-virtiofs", - "specPath": "docs/specs/providers/ADR-046-provider-volume-virtiofs.md", - "dependencyOwner": "ADR046-volume-001; W1; volume-virtiofs Provider owner", - "currentSource": "No analog; new ResourceType", - "reuseSource": null, - "reuseAction": "new", - "destination": "`packages/d2b-provider-volume-virtiofs/src/export.rs`; `packages/d2b-contracts/src/v3/virtiofs_export.rs`", - "detailedDesign": "Declare `virtiofs.d2bus.org.Export` ResourceType in `d2b-contracts`. Base fields: `providerRef`, `volumeRef`, `executionRef`, `view`, `access`, `mountPath`; virtiofs tunables live under `spec.provider.settings` (as in §4.2). Status fields: top-level `phase`/`conditions`, `status.resource.exportReady`, `status.resource.guestMountReady`, and `status.provider.details.workerProcessRef`. Strict serde `deny_unknown_fields`. Implement the conformance test fixture that validates schema fingerprint stability. The Export spec JSON schema and provider status extension schema are signed and included in the Provider package.", - "integration": "`d2b-contracts` exports the Export DTO; volume-virtiofs controller and volume-local both import it for ResourceClient typed operations", - "dataMigration": "None; new type", - "validation": "`tests/schema_conformance.rs`: `export_schema_canonical_json_stable`, `export_spec_denied_unknown_fields`, `export_status_exportready_is_boolean_not_path`, `export_owner_must_be_volume`", - "removalProof": "N/A (new type)" - }, - { - "workItemId": "ADR046-wire-001", - "specId": "ADR-046-resources-zone-control", - "specPath": "docs/specs/ADR-046-resources-zone-control.md", - "dependencyOwner": "ADR046-zone-control-013", - "currentSource": "main `a1cc0b2d`: `packages/d2b-contracts/src/v2_services.rs` (`MethodSpec{mutating,stream_kind,...}`, `ServiceSpec`, `SERVICE_INVENTORY` covering 20+ services and all provider services, `service_schema_fingerprint`, `public_daemon_schema_fingerprint`, `direct_guest_schema_fingerprint`, `StrictWireMessage`, `decode_strict`, `encode_strict`, `admit_metadata`, `TerminalStreamValidator`, `ServerStreamLease`, `RedactedTerminalFrame`, stream-name validators — lines 204–1004; tests `public_endpoint_fingerprint_binds_both_services_dependencies_and_order`, `public_endpoint_fingerprint_binds_daemon_and_guest_method_descriptors`, `direct_guest_fingerprint_binds_activation_and_remains_separate_from_public_endpoint` — lines 872–1030); `packages/d2b-contracts/src/v2_state.rs` (constants: `STATE_SCHEMA_VERSION=2`, `STATE_SCHEMA_GENERATION=1`, `MAX_JSON_DOCUMENT_BYTES=1_048_576`, `MAX_INVENTORY_ROWS=4096`, `MAX_LOCKS=1024`, `MAX_LOCK_DEPENDENCIES=32`, `MAX_DISCOVERY_OBSERVATIONS=4096`, `MAX_AUDIT_RECORD_BYTES=8192`, `MAX_AUDIT_RECORDS_PER_SEGMENT=16384`, `MAX_AUDIT_SEGMENT_BYTES=64*1024*1024`, `MAX_AUDIT_RETENTION_DAYS=14`, `MAX_LOCK_DEADLINE_MS=300_000`; types: `Digest`, `Generation`, `OwnershipEpoch`, `SafeJsonInteger`, `StorageCategory`, `StateEnvelope`, `CanonicalPayloadVerifier`, `AtomicWritePhase`, `RunnerEvidence`, audit types incl. `AuditRecord/Segment/Checkpoint/Gap`, `detect_audit_gap`, `AuditRetentionPolicy`, lock types: `LockClass`, `LockSpec`, `SyncInventory`, `LeaseRecord`); `packages/d2b-contracts/src/v2_identity.rs` (`IdentityError` 13 variants, canonical name rules `^[a-z][a-z0-9-]*$` max 63 bytes start-lowercase-letter, `RealmPath` label/separator rules, `ProviderType::ALL` 11 types + `as_str()` — lines 11–250); `packages/d2b-contracts/src/v2_provider.rs` (bounded opaque IDs `[a-z][a-z0-9-]{0,63}`: `ImplementationId`, `OperationId`, `IdempotencyKey`, `PlanId`, `HandleId`, `LeaseId`, `TransferId`, `PROVIDER_CONTRACT_FINGERPRINT`, `ProviderContractError` 34 variants, `Fingerprint` 64 lowercase-hex chars — lines 18–219); `packages/d2b-contracts/src/generated_v2_services/` (all 40+ generated ttrpc client/server stubs for v2 services)", - "reuseSource": null, - "reuseAction": "adapt", - "destination": "`packages/d2b-contracts/src/v3/{services,state,identity,provider}.rs` (new v3 namespace); generated stubs regenerated from v3 proto files in `packages/d2b-contracts/proto/v3/`", - "detailedDesign": "**Selected**: `MethodSpec`/`ServiceSpec`/fingerprinting infrastructure → v3 Zone API service schema fingerprinting; `StrictWireMessage`/`decode_strict`/`encode_strict`/`admit_metadata` → v3 wire decode/admit for all resource API requests; `CanonicalPayloadVerifier` payload-digest binding → v3 resource store integrity checks; audit chain types `AuditRecord`/`AuditSegment`/`AuditCheckpoint`/`detect_audit_gap` + `MAX_AUDIT_RETENTION_DAYS=14`/`MAX_AUDIT_RECORD_BYTES=8192`/`MAX_AUDIT_SEGMENT_BYTES=64MiB` → v3 Zone audit (§13.3); lock types `LockSpec`/`SyncInventory`/`LeaseRecord` + `MAX_LOCKS=1024`/`MAX_LOCK_DEADLINE_MS=300_000` → v3 resource store lock layer; bounded opaque ID pattern `[a-z][a-z0-9-]{0,63}` → v3 `OperationId`/`HandleId`/`PlanId`/`LeaseId`; `Fingerprint` 64-hex-char → v3 Provider `spec.configFingerprint`; `ProviderContractError` 34 variants → v3 Provider operation error taxonomy; canonical name constraint `^[a-z][a-z0-9-]*$` max 63 bytes → same as `ids.rs::is_label()` shared validator (§16.2). **Excluded ADR45 assumptions**: `RealmLabel`/`WorkloadName`/`RealmPath` identity types: ADR45 workload/realm address format; replaced by `metadata.name` + Zone `ResourceRef`. `ProviderType::ALL` fixed 11-type closed enum: v3 Provider type is an open string field in the Provider resource spec. `STATE_SCHEMA_VERSION=2`/`STATE_SCHEMA_GENERATION=1`: v3 store schema uses redb table versioning, not a JSON schema version field. v2 service fingerprint tests reference ADR45-specific proto files; v3 fingerprints use different proto inputs but the same `service_schema_fingerprint` seeding mechanism. Generated stubs in `generated_v2_services/` are v2-specific and excluded; v3 uses regenerated stubs from `proto/v3/`.", - "integration": "v3 Zone API service layer uses `MethodSpec.mutating` + `StrictWireMessage` for admission and fingerprinting; `v3/state.rs` `Digest`/`StateEnvelope` integrate with redb store writes; `CanonicalPayloadVerifier` validates resource payloads loaded from store; audit types feed §13.3 Zone audit segment; lock types wire into ADR 0034 lock lifecycle", - "dataMigration": "v2 and v3 wire type namespaces coexist in the same contracts crate; no migration", - "validation": "`v3-canonical-name-matches-ids-is-label-regex`, `v3-service-fingerprint-changes-on-method-mutation` (behavior ported from `public_endpoint_fingerprint_binds_daemon_and_guest_method_descriptors`), `v3-audit-gap-detection-covers-missing-segment`, `v3-strict-wire-rejects-unknown-fields`, `v3-state-envelope-digest-mismatch-rejected`, `v3-canonical-payload-verifier-binding-holds-under-mutation`", - "removalProof": "`d2b-contracts/src/v2_{services,state,identity,provider}.rs` and `v2_component_session.rs` deprecated in contracts crate after all v2 clients decommissioned" - }, - { - "workItemId": "ADR046-zone-control-001", - "specId": "ADR-046-resources-zone-control", - "specPath": "docs/specs/ADR-046-resources-zone-control.md", - "dependencyOwner": "ADR046-object-001 (resource envelope); ADR046-store-001 (redb store); ADR046-identities-001 (types)", - "currentSource": "`packages/d2b-realm-core/src/ids.rs` (`RealmId`, `LABEL_PATTERN = \"^[a-z][a-z0-9-]*$\"`, `is_label()`, `MAX_ID_LEN = 128`, `IdError` — `implemented-and-reachable`, baseline `b5ddbed6`); `packages/d2b-realm-core/src/realm.rs` (`RealmPath`, `MAX_REALM_LABELS = 16`, `RealmControllerPlacement`, `EntrypointMode` — `implemented-and-reachable`, baseline `b5ddbed6`); Zone resource schema: `ADR-only`", - "reuseSource": null, - "reuseAction": "extract and adapt (`RealmId`→`ResourceName` validators, `RealmPath` depth bound→ZoneLink depth, `RealmControllerPlacement`→Provider component placement); new (Zone resource schema/store table/handler)", - "destination": "`packages/d2b-contracts/src/v3/zone.rs`; `packages/d2b-core-controller/src/zone.rs`", - "detailedDesign": "Zone ResourceType schema with `spec = {}`; self-resource enforcement; store_meta binding checks; phase/conditions; cardinality-1 enforcement; `zone-drain` finalizer; Nix Zone options; canonical JSON schema", - "integration": "Zone runtime open/upgrade verifies `Zone/` self resource; configuration publication handler creates/reconciles Zone; resource API rejects cross-Zone refs", - "dataMigration": "Destructive d2b 3.0 reset; no v2/Realm Zone resource import", - "validation": "`zone-self-resource-enforced`, `zone-uid-mismatch-quarantine`, `zone-name-mismatch-rejected`, `zone-cardinality-one`, `zone-cross-zone-ref-rejected`, `zone-owner-rejected`, `zone-deletion-only-on-drain`", - "removalProof": "`d2b-realm-core` Realm struct removed only after Zone resource integration is live in Zone runtime" - }, - { - "workItemId": "ADR046-zone-control-002", - "specId": "ADR-046-resources-zone-control", - "specPath": "docs/specs/ADR-046-resources-zone-control.md", - "dependencyOwner": "ADR046-zone-control-001; ADR046-zone-control-018 (ComponentSession)", - "currentSource": "`packages/d2b-realm-router/src/session.rs` (`PeerSession` — `implemented-but-unwired`, baseline `b5ddbed6`; no live call sites in d2bd or d2b — only reachable through `realm_stubs.rs` dead code in d2bd); `packages/d2b-realm-router/src/secure_session.rs` (`SecurePeerSession`, Noise-based — `implemented-but-unwired`); `packages/d2b-realm-router/src/mux_session.rs` (`MuxSession` — `implemented-but-unwired`); `packages/d2b-realm-router/src/session_lifecycle.rs` (`SessionLifecycle`, `SessionPhase` — `implemented-but-unwired`); `packages/d2b-realm-core/src/route_engine.rs` (`RouteTreeEngine`, `admit_advertisement()`, `decide_route()`, `RoutePruneReport` — `implemented-but-unwired`; exported from `d2b_realm_core::lib.rs:122` but zero call sites in live daemon or CLI code, tests only); `packages/d2b-realm-core/src/identity_store.rs` (`RealmIdentityStore`, `EnrollmentRecord`, `ChildKeyPin` — `implemented-and-reachable`); `packages/d2b-realm-core/src/realm.rs` (`RealmPath`, `MAX_REALM_LABELS = 16` — `implemented-and-reachable`); `packages/d2b-realm-core/src/access.rs` (`RealmTransportBinding::{LocalUnixSocket,RemoteRealmTransport,ProviderRealmTransport}` — `implemented-and-reachable` in CLI routing path); ZoneLink resource schema and cursor tables: `ADR-only`", - "reuseSource": "main `a1cc0b2d`: `packages/d2b-session/src/lifecycle.rs`, `d2b-session-unix/src/adapter.rs` for reconnect/transport precedents", - "reuseAction": "extract and adapt (`SecurePeerSession` Noise model → ComponentSession Noise KK; `SessionLifecycle`/`SessionPhase` → ZoneLink session reconnect loop and connection detail fields; `Connecting`/`Established` current evidence phases drive `status.connected` and `status.phase` transitions to `Pending`/`Ready`, not direct phase values; `RouteTreeEngine.decide_route()` → cursor tracking; `RealmIdentityStore` enrollment → ZoneLink child key-pin)", - "destination": "`packages/d2b-contracts/src/v3/zone_link.rs`; `packages/d2b-core-controller/src/zone_link.rs`", - "detailedDesign": "ZoneLink schema with resolved `spec.transportProviderRef`, `spec.transportSettings`, and `spec.transportCredentials` fields; cursor tracking; reconnect loop with exponential backoff; local intent queue (max 256 entries); child UID change detection; drain finalizer; Nix ZoneLink options", - "integration": "core-controller zone_link handler; redb `zone_link_cursors` table; d2b-bus transport resolver; ComponentSession lifecycle", - "dataMigration": "Destructive reset; no v2 Realm peer migration", - "validation": "`zonelink-reconnect-child-uid-change`, `zonelink-disconnect-unknown-phase`, `zonelink-intent-queue-limit`, `zonelink-disabled-no-reconnect`, `zonelink-child-auth-denied-failed`, `zonelink-drain-closes-session`", - "removalProof": "`realm_stubs.rs` (`ApiFrontend`, `PeerOperationRouter`, `TargetResolver`) removed after ComponentSession integration (ADR046-zone-control-018); `realm_access_resolver.rs` module removed after ZoneLink replaces entrypoint-table resolution; gateway `PeerSession`/`SecurePeerSession` session types remain as dead code in d2b-realm-router until Provider session migration wave" - }, - { - "workItemId": "ADR046-zone-control-003", - "specId": "ADR-046-resources-zone-control", - "specPath": "docs/specs/ADR-046-resources-zone-control.md", - "dependencyOwner": "ADR046-zone-control-001; ADR046-api-001; ADR046-zone-control-017", - "currentSource": "`packages/d2b-realm-provider/src/provider.rs` (`HostSubstrateProvider`, `RuntimeProvider`, `WorkloadProvider`, `DurableExecutionProvider`, `InfrastructureProvider`, `NodeProvider` traits — `implemented-and-reachable`, baseline `b5ddbed6`); `packages/d2b-realm-provider/src/capabilities.rs` (`RuntimeCapabilitySet`, `WorkloadCapabilitySet`, `NodeCapabilitySet` — `implemented-and-reachable`); `packages/d2b-realm-provider/src/conformance.rs` (`workload_lists_and_advertises`, `display_fails_closed_when_unsupported` — `implemented-and-reachable`); `packages/d2b-realm-provider/src/error.rs` (`ProviderError`, `ErrorKind`, `RetryHint`, `ProviderDiagnostic` — `implemented-and-reachable`); `packages/d2b-realm-core/src/ids.rs` (`ProviderId`, label-shaped — `implemented-and-reachable`); `packages/d2b-core/src/processes.rs` (`ProcessRole::{CloudHypervisorRunner, Virtiofsd, Swtpm, GpuRenderNode, Audio, Video, QemuMediaRunner, VsockRelay, OtelHostBridge, Usbip}`, `VmProcessDag`, `ProcessNode` — `implemented-and-reachable`); Provider resource schema and API catalog: `ADR-only`", - "reuseSource": "main `a1cc0b2d`: any `d2b-provider-toolkit` registry/descriptor patterns named in ADR046-zone-control-017 sub-items", - "reuseAction": "extract and adapt (`workload_lists_and_advertises`/`display_fails_closed_when_unsupported` conformance behavior; `RuntimeCapabilitySet`/`WorkloadCapabilitySet`/`NodeCapabilitySet` → Provider component `supportedCapabilities`; `ProviderError`/`RetryHint` → Provider lifecycle error schema; `ProviderId` → Provider `metadata.name` validator; `ProcessRole` variants → Provider component type identifiers)", - "destination": "`packages/d2b-contracts/src/v3/provider.rs`; `packages/d2b-core-controller/src/provider_lifecycle.rs`; `packages/d2b-core-controller/src/api_catalog.rs`", - "detailedDesign": "Provider resource schema with all spec fields from §4.3, including resolved component bounds (max 8 controllers, 8 services, 32 worker templates, 16 ResourceTypes per controller); trust/conformance/config validation; component descriptor validation; dependency alias resolution; API binding with permission intersection; lifecycle policies; Nix Provider installation options; Provider crate layout enforcement per §4.8 (see ADR046-pkg-001)", - "integration": "Zone config publication installs Provider resources; API catalog handler binds exported ResourceTypes; Provider/system-core and Provider/system-minijail are bootstrap exceptions with pre-created records", - "dataMigration": "Full reset; Provider packages recompiled and re-registered per new schema", - "validation": "All §15.3 Provider tests including the resolved bounds checks", - "removalProof": "`d2b-realm-provider` trait crate removed per ADR046-zone-control-017 after Provider resource integration" - }, - { - "workItemId": "ADR046-zone-control-004", - "specId": "ADR-046-resources-zone-control", - "specPath": "docs/specs/ADR-046-resources-zone-control.md", - "dependencyOwner": "ADR046-zone-control-001; ADR046-api-002", - "currentSource": "`packages/d2bd/src/admission.rs` (`PeerRole::{Admin, Launcher, HostShutdown}`, `PeerIdentity`, `authorize_peer()`, `verb_requires_admin()`, `verb_allowed_for_host_shutdown()` — `implemented-and-reachable`, baseline `b5ddbed6`); `packages/d2b-daemon-access/src/lib.rs` (`LocalUnixAllowlistRole::{Admin, Launcher}`, `DaemonAccessPolicyRole::RealmAdmin`, `DaemonAccessDecision`, `MappedDaemonAccessPrincipal`, `map_local_unix_daemon_access()` — `implemented-and-reachable`); Role resource schema and RBAC index: `ADR-only`", - "reuseSource": null, - "reuseAction": "extract and adapt (`verb_requires_admin()` verb table → Role verb enum; `DaemonAccessDecision` error types → Role admission errors; `MappedDaemonAccessPrincipal` → subject identity model)", - "destination": "`packages/d2b-contracts/src/v3/role.rs`; `packages/d2b-core-controller/src/authz.rs`", - "detailedDesign": "Role resource schema with rule schema from §5.3 and resolved bounds (32 rules, 16 resourceTypes per rule, 64 resourceNames, 32 executionRefs); verb enum; explicit wildcard enforcement; index builder; phase/conditions; `role-binding-drain` finalizer; Nix Role options; audit/OTEL instrumentation", - "integration": "Authorization evaluator (ADR046-api-002) reads Role index entries; core `authz` handler owns reconcile loop", - "dataMigration": "Initial Roles generated from Nix config; no v2 Role resource import", - "validation": "All §15.4 Role tests including the resolved bounds checks", - "removalProof": "`daemon-access` capability enum removed after RBAC Role engine covers all access decisions" - }, - { - "workItemId": "ADR046-zone-control-005", - "specId": "ADR-046-resources-zone-control", - "specPath": "docs/specs/ADR-046-resources-zone-control.md", - "dependencyOwner": "ADR046-zone-control-004", - "currentSource": "`packages/d2b-daemon-access/src/lib.rs` (`DaemonAccessDecision::Authorized { role: DaemonAccessPolicyRole }`, `DaemonAccessAdmissionSource`, `map_remote_daemon_access()` — `implemented-and-reachable`, baseline `b5ddbed6`; these implement the current coarse binding decision per-connection); RoleBinding resource schema: `ADR-only`", - "reuseSource": null, - "reuseAction": "new (RoleBinding resource schema/store table/handler); adapt (`DaemonAccessAdmissionSource` identity fields → subject selector; `map_remote_daemon_access()` logic → subject UID-binding behavior)", - "destination": "`packages/d2b-contracts/src/v3/role_binding.rs`; `packages/d2b-core-controller/src/authz.rs` (shared with Role handler)", - "detailedDesign": "RoleBinding resource schema with no expiry field and max 128 subjects; subject resolution and UID binding; external principal selector; scope narrowing intersection; revocation; immediate deletion with cache invalidation; Nix RoleBinding options", - "integration": "Subject resolution uses store owner index; authorization evaluator reads combined Role+narrowing entry; revocation/update/delete flow uses the normal resource lifecycle", - "dataMigration": "Initial RoleBindings generated from Nix config", - "validation": "All §15.5 RoleBinding tests, including the 128-subject admission bound and no-expiry lifecycle model", - "removalProof": "Not applicable; new type" - }, - { - "workItemId": "ADR046-zone-control-006", - "specId": "ADR-046-resources-zone-control", - "specPath": "docs/specs/ADR-046-resources-zone-control.md", - "dependencyOwner": "ADR046-zone-control-004, ADR046-zone-control-005; Zone runtime", - "currentSource": "`packages/d2bd/src/admission.rs` (`authorize_peer()`, `verb_requires_admin()`, `verb_allowed_for_host_shutdown()`, `PeerRole::{Admin, Launcher, HostShutdown}` — `implemented-and-reachable`, baseline `b5ddbed6`); `packages/d2b-daemon-access/src/lib.rs` (`LocalUnixAllowlistRole`, `DaemonAccessDecision::Authorized/Denied`, `DaemonAccessAdmissionSource`, `MappedDaemonAccessPrincipal`, `map_local_unix_daemon_access()` — `implemented-and-reachable`); `packages/d2bd/src/lib.rs` (`LoadedRealmControllersConfig`, `LoadedRealmIdentityConfig` startup state — `implemented-and-reachable`); compiled bootstrap constant policy: `ADR-only`", - "reuseSource": null, - "reuseAction": "extract and adapt (`authorize_peer()`/`verb_requires_admin()` verb table → bootstrap constant policy verb table; `PeerRole` two-variant model → system-core/system-minijail bootstrap subjects; `map_local_unix_daemon_access()` SO_PEERCRED mapping → bootstrap subject derivation; `LoadedRealmControllersConfig` startup path → Zone runtime startup bootstrap init sequence)", - "destination": "`packages/d2b-resource-api/src/bootstrap_authz.rs`; Zone runtime startup path", - "detailedDesign": "Compiled bootstrap authorization as described in §9; exact subjects (system-core, system-minijail); closed verb table; non-configurable enforcement; atomic supersession after stored RBAC publishes; out-of-band reset path", - "integration": "Resource API authorization layer checks bootstrap policy before stored RBAC; supersession is triggered by first `IndexBuilt=True` event from authorization handler", - "dataMigration": "Bootstrap is always freshly compiled; no migration", - "validation": "All §15.6 bootstrap tests", - "removalProof": "`daemon-access` bootstrap stub removed after Zone runtime bootstrap authz integrates" - }, - { - "workItemId": "ADR046-zone-control-007", - "specId": "ADR-046-resources-zone-control", - "specPath": "docs/specs/ADR-046-resources-zone-control.md", - "dependencyOwner": "ADR046-identities-002; ADR046-zone-control-001; ADR046-zone-control-002; ADR046-zone-control-003; ADR046-zone-control-004; ADR046-zone-control-005; ADR046-zone-control-009; ADR046-zone-control-010", - "currentSource": "`nixos-modules/options-realms.nix` (`providerKind = \"^[a-z][a-z0-9-]*$\"` label regex matching `LABEL_PATTERN` in `ids.rs`; `providerType` submodule with `enable`/`kind`/`placement`/`freeformType` fields; `d2b.realms..providers.*` attrset — `generated-or-eval-contract`, baseline `b5ddbed6`); `nixos-modules/options-realms-workloads.nix` (`d2b.realms..workloads.*` submodule shape — `generated-or-eval-contract`); `d2b.zones.*` Nix options: `ADR-only`", - "reuseSource": null, - "reuseAction": "adapt (`providerType` submodule → Provider option submodule; label regex `^[a-z][a-z0-9-]*$` retained unchanged; `placement` option → Provider component placement; `d2b.realms.*` option namespace → `d2b.zones.*` option namespace)", - "destination": "`nixos-modules/options-zones.nix`; `nixos-modules/resources-zone-control.nix`; extended `nixos-modules/index.nix`", - "detailedDesign": "`d2b.zones..*` Nix options for Zone/ZoneLink/Provider/Role/RoleBinding/Quota/EmergencyPolicy authoring; Nix eval-time validation of ResourceRefs, verb enums, digest format, subject types, resolved ZoneLink transport ref constraints, and the no-expiry RoleBinding model; canonical JSON serialization; generation-bound resource bundle output", - "integration": "Nix resource compiler → configuration publication handler → Zone store; bootstrap Provider records auto-generated", - "dataMigration": "Full reset; Nix realm options (`d2b.realms.*`) remain until purge wave", - "validation": "nix-unit vectors for each Zone control type schema; cross-field constraint tests; rendered JSON contract tests (`make test-drift`)", - "removalProof": "`nixos-modules/options-realms.nix` and related realm options removed only after Zone resource Nix integration is live" - }, - { - "workItemId": "ADR046-zone-control-008", - "specId": "ADR-046-resources-zone-control", - "specPath": "docs/specs/ADR-046-resources-zone-control.md", - "dependencyOwner": "ADR046-zone-control-001 (Zone resource); ADR046-zone-control-003 (Provider/system-core installed)", - "currentSource": "`d2b-realm-core/src/workload.rs:13,27,83` (`WorkloadProviderKind::UnsafeLocal`, `IsolationPosture::UnsafeLocal`, `WorkloadExecutionPosture` with canonical unsafe-local tuple at lines 206–211: `isolation=unsafe-local`, `environment=systemd-user-manager-ambient`, `executionIdentity=authenticated-requester-uid`, `sessionPersistence=user-manager-lifetime` — `implemented-and-reachable`, baseline `b5ddbed6`); `d2b-core/src/unsafe_local_workloads.rs:16,47,106,150` (`UnsafeLocalWorkloadsJson`, `UnsafeLocalWorkload`, `UnsafeLocalShellPolicy`, `MAX_UNSAFE_LOCAL_SHELL_SESSIONS=64`, `MAX_UNSAFE_LOCAL_WORKLOADS=256` — `implemented-and-reachable`); `d2bd/src/unsafe_local_helper.rs` (`HelperRegistry`, `bind_helper_socket`, `dispatch_launch` — `implemented-and-reachable`, live in `d2bd/src/lib.rs:1346–1468`); `nixos-modules/options-realms.nix:346` (`policy.allowUnsafeLocal` — `generated-or-eval-contract`); `nixos-modules/options-realms-workloads.nix:221,233` (`kind = \"unsafe-local\"`, doc \"no isolation boundary\" — `generated-or-eval-contract`)", - "reuseSource": null, - "reuseAction": "adapt (`WorkloadExecutionPosture` unsafe-local posture tuple → Host `status.observedPosture`; posture details in audit record body, not OTEL labels; `UnsafeLocalShellPolicy.{defaultName,maxSessions}` → Host `spec.shellPolicy`; `HelperRegistry.dispatch_launch` → Zone runtime Host process launch broker; `policy.allowUnsafeLocal` → dedicated Host admission gate); new (Host ResourceType user-domain schema with `defaultDomain`, `allowedDomains`, `defaultUserRef`, `shellPolicy`, cardinality bounds)", - "destination": "`packages/d2b-contracts/src/v3/host.rs` (Host resource schema, user-domain variant); `packages/d2b-core-controller/src/host_user.rs` (reconciler owned by Provider/system-core); Nix Host authoring via `d2b.zones..resources. = { type = \"Host\"; spec = { ... }; };` (validated per ResourceTypeSchema; no separate `options-zones-hosts.nix` submodule)", - "detailedDesign": "Host ResourceType schema for user-domain variant: `spec.defaultDomain=user`, `spec.allowedDomains=[user]`, `spec.defaultUserRef=User/`, `spec.shellPolicy` (adapted from `UnsafeLocalShellPolicy`), `spec.launcherItems` (adapted from `UnsafeLocalLauncherItem`). No-isolation posture recorded in `status.observedPosture`. A dedicated Host admission gate blocks unsafe-local Host creation without opt-in. Mandatory no-isolation warning in Host `status.conditions[0].message` and CLI/UI output for all Host commands. No-isolation posture included in audit record body (`isolation=no-isolation`); it is never emitted as an OTEL metric label value or span attribute. Child processes use standard Process Providers — no Provider resource with name or kind `unsafe-local` is created. Cardinality bounds: max 256 user-domain Hosts per Zone, max 64 launcher items per Host, max 64 shell sessions per Host.", - "integration": "Provider/system-core bootstrap exception creates Host resources from Zone config publication; Host controller reconciles `defaultUserRef` via User resource lookup; `HelperRegistry` in Zone runtime becomes per-uid launch broker for Host Process launch; Host admission layer enforces the `allowUnsafeLocal` gate before Host creation", - "dataMigration": "Destructive reset; `unsafe-local-workloads.json` bundle artifact and `HelperRegistry` replaced by Host resource store entries + Process Provider launch", - "validation": "`host-user-domain-no-isolation-warning-required`, `host-user-only-disallows-system-domain`, `host-allowUnsafeLocal-gates-creation`, `host-defaultUserRef-user-type-required`, `host-shell-policy-max-sessions-bound`, `host-launcher-item-max-count-bound`, `host-audit-body-isolation-label-present`, `host-otel-no-posture-label`, `host-cli-no-isolation-warning-present`", - "removalProof": "`d2bd/src/unsafe_local_helper.rs` `HelperRegistry` removed after Zone runtime Host/Process broker integration; `unsafe-local-workloads.json` bundle artifact removed after Host resource store replaces it; `kind = \"unsafe-local\"` Nix enum value removed in purge wave" - }, - { - "workItemId": "ADR046-zone-control-009", - "specId": "ADR-046-resources-zone-control", - "specPath": "docs/specs/ADR-046-resources-zone-control.md", - "dependencyOwner": "ADR046-zone-control-001; Zone store (ADR046-store-001); Quota handler owner", - "currentSource": "`packages/d2b-realm-core/src/ids.rs` (`LABEL_PATTERN`, `MAX_ID_LEN` — ResourceName validation for quotaRef); `packages/d2b-core/src/unsafe_local_workloads.rs:16–164` (`MAX_UNSAFE_LOCAL_WORKLOADS=256`, etc. — bound evidence for quota ceiling defaults); no current quota ResourceType exists (`ADR-only`)", - "reuseSource": "None from main; Quota is a new ResourceType with no main-branch analog", - "reuseAction": "net-new", - "destination": "`packages/d2b-contracts/src/v3/quota.rs`; `packages/d2b-core-controller/src/quota.rs`; `packages/d2b-resource-api/src/quota_gate.rs`", - "detailedDesign": "Quota resource schema with all spec/status fields from §7; ceiling bounds enforcement at admission (hard policy: reject over-quota with `quota-exceeded`; soft policy: warn); usage index built from resource scan with `quotaRef` field; per-ResourceType ceiling in `perTypeCeilings`; `core.quota-drain` finalizer that blocks Quota deletion until all dependent resources with `spec.quotaRef` pointing to this Quota are reassigned or deleted by authorized owners/operators — the controller never issues spec-updates to clear `quotaRef` on other resources; `dependentCount` status field updated from dependency index; audit event `quota-check` per admission; Nix Quota options per §7.7", - "integration": "Resource API admission gate (`packages/d2b-resource-api/src/quota_gate.rs`) called for every `create` verb; Zone controller triggers quota reconcile on resource-created/deleted/quotaRef-changed events; quota handler registered in core-controller process", - "dataMigration": "Full reset; no prior Quota resources exist", - "validation": "`quota-ceiling-hard-reject`, `quota-ceiling-soft-warn`, `quota-ceiling-pertype`, `quota-drain-blocks-on-dependents`, `quota-over-quota-status`, `quota-nix-eval-bounds`, `quota-nix-build-pertype-unknown-type`", - "removalProof": "Additive; no existing code removed" - }, - { - "workItemId": "ADR046-zone-control-010", - "specId": "ADR-046-resources-zone-control", - "specPath": "docs/specs/ADR-046-resources-zone-control.md", - "dependencyOwner": "ADR046-zone-control-001; Zone store (ADR046-store-001); EmergencyPolicy handler owner", - "currentSource": "`packages/d2bd/src/lib.rs` admission gate (`authorize_peer()` — `implemented-and-reachable`, baseline `b5ddbed6`); `packages/d2b-daemon-access/src/lib.rs` (`DaemonAccessDecision::Denied` — basis for admission rejection pattern); no EmergencyPolicy ResourceType exists (`ADR-only`)", - "reuseSource": "None from main; EmergencyPolicy is a new ResourceType", - "reuseAction": "net-new", - "destination": "`packages/d2b-contracts/src/v3/emergency_policy.rs`; `packages/d2b-core-controller/src/emergency_policy.rs`; `packages/d2b-resource-api/src/emergency_gate.rs`", - "detailedDesign": "EmergencyPolicy resource schema from §8; union semantics: multiple `enabled=true` policies allowed simultaneously; effective scope = OR of all enabled policies' scope flags; effective `drainDeadlineSeconds` = minimum across enabled policies; scope flag evaluation: `stopNewAdmissions` signal to API admission gate, `disconnectZoneLinks` graceful signal to ZoneLink handler, `stopProviderProcesses` suppresses Process launch and sends stop signal to running Provider component Processes (does NOT set `deletionRequestedAt` on Process resources; reconciliation resumes on deactivation), `drainOngoingOperations` deadline drain; `core.emergency-drain` finalizer for enabled policies; audit events `emergency-policy-activated` / `emergency-policy-deactivated`; `reason` field stored in spec and included in audit record body (never in OTEL metric label values, structured log labels, or status fields); Nix EmergencyPolicy options per §8.7", - "integration": "API admission gate checks union of enabled EmergencyPolicies before every admitted request; ZoneLink handler subscribes to EmergencyPolicy watch triggers; Provider process lifecycle listens for effective `stopProviderProcesses` and resumes launch on deactivation", - "dataMigration": "Full reset; no prior EmergencyPolicy resources exist", - "validation": "`emergency-policy-activates-gate`, `emergency-policy-disconnects-zonelinks`, `emergency-policy-union-most-restrictive`, `emergency-policy-multi-enabled-combined-scope`, `emergency-policy-stop-processes-no-delete`, `emergency-policy-deactivation-restores-gate`, `emergency-policy-drain-finalizer`, `emergency-nix-eval-drain-deadline-bound-tightest`", - "removalProof": "Replaces the inline `emergencyDisable` field from the proposed Zone.spec option B; that option was not implemented" - }, - { - "workItemId": "ADR046-zone-control-011", - "specId": "ADR-046-resources-zone-control", - "specPath": "docs/specs/ADR-046-resources-zone-control.md", - "dependencyOwner": "ADR046-identities-001; ADR046-store-001", - "currentSource": "main `a1cc0b2d`: `packages/d2b-session/src/lifecycle.rs` (`SessionPhase::{Established,Disconnected,Reconnecting,Closing,Closed}`, `KeepaliveAction::{None,SendPing,Close}`, `SessionLifecycle::new/phase/on_activity/poll_keepalive/receive_pong/disconnect/begin_reconnect/reconnect_established/close` — lines 9–195); `packages/d2b-session/src/engine.rs` (`SessionEngine`, `SessionEvent`, `establish_initiator/responder`, `reconnect_initiator/responder`, `call/complete_call/cancel_call`, `send_ttrpc/receive`, `open/send/grant_credit/close/reset_named_stream`, `send_attachments`, `drive_keepalive`, `fail_closed`, encode/decode helpers for keepalive/cancel/close/stream-control/attachment — lines 33–995); `packages/d2b-session/src/driver.rs` (`ComponentSessionDriver` trait 18 methods, `SessionDriverHandle`, `DriverQueues` 4 event queues + named-send FIFO, `DRIVER_COMMAND_CAPACITY=128`, `DRIVER_EVENT_CAPACITY=128`, backpressure on enqueue and dequeue, test `cancelled_immediate_receiver_restores_queued_event` — lines 20–718); `packages/d2b-session/src/streams.rs` (`StreamId`, `StreamPhase::{Open,HalfClosedLocal,HalfClosedRemote,Closed,Reset}`, `StreamEvent::{Data,RemoteClosed,Reset}`, `NamedStreamMux::open/reserve_send/grant_send_credit/refund_send_credit/receive_data/close_local/receive_close/reset/remove_terminal/active` — lines 7–237); `packages/d2b-session/src/transport.rs` (`OwnedTransport` trait, `TransportDescriptor`, `TransportPacket`, `TransportError::{Disconnected,Truncated,LimitExceeded,InvalidAttachment,Other}` — lines 9–106); `packages/d2b-session/src/error.rs` (`SessionError`, all `From<>` mappings covering `ContractError`, `BinaryError`, `SequenceError`, `FragmentSequenceError`, `HandshakeRejectReason`, `TransportError` — lines 10–143); supporting modules: `attachment.rs`, `bootstrap.rs`, `cancellation.rs`, `deadline.rs`, `fragmentation.rs`, `metrics.rs`, `record.rs`, `scheduler.rs`, `server.rs`", - "reuseSource": null, - "reuseAction": "copy + adapt", - "destination": "`packages/d2b-bus/src/{lifecycle,engine,driver,streams,transport,error}.rs` (new crate `d2b-bus`)", - "detailedDesign": "**Selected**: full `SessionLifecycle` FSM including keepalive ping-nonce/timeout close (`poll_keepalive` lines 81–124), nonce-exhaustion close (`NonceExhausted` remediation `ReplaceGeneration`), reconnect attempt counting + window expiry (`begin_reconnect` lines 147–174), generation increment on reconnect; `ComponentSessionDriver` 18-method trait as the d2b-bus driver contract; `SessionEngine` full frame encode/decode, keepalive, named-stream credit/half-close/reset; `NamedStreamMux` credit model with send/receive credit, phase transitions, terminal removal; `DriverQueues` backpressure (`QueueBackpressure`) on both deliver and receive paths; all `SessionError`/`From<>` mapping chains. **Excluded ADR45 assumptions**: `establish_initiator_with_generation_discovery()` (lines 102–123): ADR45 initiator-probes-server for current generation; in v3 the generation lives in the Zone resource store — use `establish_initiator()` directly. OTEL labels in `metrics.rs` reference ADR45 realm/workload dimensions — replace with Zone/ZoneLink labels per §13.2.", - "integration": "`d2b-bus` crate is the sole session-transport dependency for ZoneLink (ADR046-zone-control-018), Provider component Processes (ADR046-zone-control-017), and the Zone resource API service layer (ADR046-api-001); `ComponentSessionDriver` is the interface ZoneLink uses to send/receive ttrpc frames and named streams", - "dataMigration": "Not applicable; new crate", - "validation": "`session-lifecycle-reconnect-attempts-exhausted`, `session-lifecycle-keepalive-timeout-closes`, `session-lifecycle-nonce-exhausted-close-with-replace-generation-remediation`, `session-driver-cancelled-receiver-restores-event` (ported from test at line 707), `named-stream-credit-half-close-then-remote-close-transitions-closed`, `named-stream-reset-cancels-pending-send`", - "removalProof": "`d2b-realm-router/src/{session.rs,secure_session.rs,mux_session.rs,session_lifecycle.rs}` removed after d2b-bus integration wave" - }, - { - "workItemId": "ADR046-zone-control-012", - "specId": "ADR-046-resources-zone-control", - "specPath": "docs/specs/ADR-046-resources-zone-control.md", - "dependencyOwner": "ADR046-zone-control-011", - "currentSource": "main `a1cc0b2d`: `packages/d2b-session-unix/src/adapter.rs` (`UnixSeqpacketTransport::new`, `UnixStreamTransport::new`, `PeerIdentityPolicy::{Pathname{verifier},InheritedSocketpair{expected_peer}}`, `OwnedUnixAttachment::file`, `UnixAttachmentPayload`, `consume_peer_credentials()` credential strip+verify, `map_validation_error`/`map_transport_error`, tests `inherited_first_packet_requires_credentials`/`inherited_first_packet_rejects_wrong_credentials` — lines 23–852); `packages/d2b-session-unix/src/socket.rs` (`AncillaryCapacity::from_policy`, `OutboundPacket::new`, `SeqpacketSocket`, `prearmed_seqpacket_pair()`, `SendBurst`/`PacketBurst`/`SentPacket`, `recv_one` with `DONTWAIT|CMSG_CLOEXEC`, `send_one` with `DONTWAIT|NOSIGNAL`, SCM_RIGHTS+SCM_CREDENTIALS, fairness-budget burst loops, tests `raw_control_scanner_rejects_unknown_and_partial_headers`/`raw_control_scanner_accepts_exact_rights_shape` — lines 35–698); `packages/d2b-session-unix/src/pidfd.rs` (`PidfdEvidence::new`, `ProcPidfdIdentityVerifier::verify` with double-read race guard, `ProcSelfFdInfoSource::read_pid_from_fdinfo`, `DigestEvidenceCallback` — lines 8–159); `packages/d2b-session-unix/src/credit.rs` (`CreditPool::new/reserve`, `CreditScope::{Packet,Request,Operation,Session,Process,Host}`, `CreditScopeSet::reserve/reserve_ingress`, `CreditBundle::acquire_dispatch`, `ProcessCreditLimit::derive` from `RLIMIT_NOFILE` — lines 13–286); `packages/d2b-session-unix/src/descriptor.rs` (`ReceivedPacket::verify/verify_first_packet_credentials`, `DescriptorPolicy`, `ObjectIdentity`, `PeerCredentials`, `FirstPacketCredentials`, sealed memfd `F_SEAL_WRITE|GROW|SHRINK|SEAL` — lines 21–617); `packages/d2b-session-unix/src/error.rs` (`UnixSessionError` 21 variants, Display strings — lines 4–92); `packages/d2b-session-unix/src/systemd.rs` (`ActivatedSeqpacketListener`, `SystemdActivationError`)", - "reuseSource": null, - "reuseAction": "copy + adapt", - "destination": "`packages/d2b-bus-unix/src/{adapter,socket,pidfd,credit,descriptor,error,systemd}.rs` (new crate `d2b-bus-unix`)", - "detailedDesign": "**Selected**: `UnixSeqpacketTransport` seqpacket adapter implementing `OwnedTransport`; `PeerIdentityPolicy` SO_PEERCRED verification (pathname + inherited socketpair); `prearmed_seqpacket_pair()` for bootstrap inherited-socketpair path; `CreditPool`/`CreditScopeSet` six-scope credit model with per-packet/request/operation/session/process/host reservation; `ProcessCreditLimit::derive()` from `RLIMIT_NOFILE` minus `RESERVED_CONTROL_FDS`; `ProcPidfdIdentityVerifier` double-read race guard on `/proc/self/fdinfo/` with executable + cgroup digest callbacks; sealed memfd four-seal enforcement; `AncillaryCapacity` derived from `AttachmentPolicy`; `UnixSessionError` 21-variant Display strings preserved verbatim for audit log compatibility; `consume_peer_credentials()` strips-and-verifies SCM_CREDENTIALS exactly once on first packet. **Excluded ADR45 assumptions**: `vsock.rs` (`FramedVsockTransport`, `NativeVsockListener`, `NativeVsockTransport`): ADR45 guest-control vsock transport; v3 ZoneLink uses Unix sockets for host-local; vsock for Guest connections is separate work. Specific socket paths (`PUBLIC_SOCKET_PATH`, `BROKER_SOCKET_PATH`) are replaced by Zone-resource-managed paths.", - "integration": "`d2b-bus-unix` provides `OwnedTransport` impl consumed by `d2b-bus` `SessionEngine`; Zone runtime passes accepted seqpacket socket to `UnixSeqpacketTransport::new`; Process pidfd identity verifier integrates with Host/Process broker launch", - "dataMigration": "Not applicable; new crate", - "validation": "`seqpacket-inherited-missing-credentials-rejected` (ported from `inherited_first_packet_requires_credentials`), `seqpacket-inherited-wrong-credentials-rejected` (ported from `inherited_first_packet_rejects_wrong_credentials`), `pidfd-double-read-race-guard-detects-pid-reuse`, `sealed-memfd-partial-seal-rejected`, `credit-scope-six-levels-ordered`, `ancillary-capacity-from-disabled-policy-is-zero`, `raw-control-unknown-header-rejected` (ported from `raw_control_scanner_rejects_unknown_and_partial_headers`), `raw-control-valid-rights-parsed` (ported from `raw_control_scanner_accepts_exact_rights_shape`)", - "removalProof": "`d2b-realm-router/src/secure_session.rs` `SecurePeerSession` seqpacket layer removed after d2b-bus-unix provides the replacement" - }, - { - "workItemId": "ADR046-zone-control-013", - "specId": "ADR-046-resources-zone-control", - "specPath": "docs/specs/ADR-046-resources-zone-control.md", - "dependencyOwner": "ADR046-identities-001", - "currentSource": "main `a1cc0b2d`: `packages/d2b-contracts/src/v2_component_session.rs` (all protocol constants: `PREFACE_LEN=16`, `PREFACE_MAGIC=b\"D2BCS2\\r\\n\"`, `COMPONENT_SESSION_MAJOR=2`, `COMPONENT_SESSION_MINOR=0`, `MAX_HANDSHAKE_OFFER_BYTES=16384`, `HANDSHAKE_OFFER_CANONICAL_LEN=148`, `ENDPOINT_POLICY_IDENTITY_CANONICAL_LEN=140`, `MAX_PROTECTED_CIPHERTEXT_BYTES=65535`, `NOISE_TAG_BYTES=16`, `RECORD_LENGTH_BYTES=2`, `MAX_PROTECTED_PLAINTEXT_BYTES=65519`, `MAX_LOGICAL_MESSAGE_BYTES=1_048_576`, `MAX_ACTIVE_NAMED_STREAMS=128`, `MAX_PACKET_ATTACHMENTS=32`, `MAX_REQUEST_ATTACHMENTS=64`, `MAX_OPERATION_ATTACHMENTS=128`, `MAX_SESSION_ATTACHMENTS=256`, `MAX_PROCESS_ATTACHMENT_CREDITS=2048`, `MAX_HOST_ATTACHMENT_CREDITS=8192`, named-stream/control queue limits, credential size/flag constants; closed enums: `EndpointPurpose` 19 tags, `PurposeClass` 3 tags, `EndpointRole` 19 tags, `ServicePackage` 15 tags, `NoiseProfile` 3 tags, `IdentityEvidenceRequirement` 3 tags, `Locality` 4 tags, `TransportClass` 7 tags, `AttachmentPolicyKind`; structs: `ComponentSessionPreface::parse/encode`, `HandshakeOffer` 11 fields, `HandshakeAccept`, `HandshakeReject`, `HandshakeRejectReason` 21 tags, `LimitProfile` 15 fields with `local_default()`, `AttachmentPolicy::validate/disabled`, `BoundedVec` — throughout); `packages/d2b-session/src/handshake.rs` (`HandshakeRole::{Initiator,Responder}`, `HandshakeCredentials::{Nn,Kk,IkPsk2Initiator,IkPsk2Responder}`, `NegotiatedOffer`, `NoiseHandshake::new/write_next/read_next/finish`, `EstablishedHandshake::transcript_hash/generation`, `encode_offer/negotiate_offer`, generation-discovery magics `b\"D2BGD2Q\\n\"`/`b\"D2BGD2A\\n\"`, `encode_generation_discovery_request/accept/response/decode_generation_discovery_response` — lines 39–433)", - "reuseSource": null, - "reuseAction": "copy + adapt", - "destination": "`packages/d2b-contracts/src/v3/component_session.rs` (new v3 namespace in existing contracts crate)", - "detailedDesign": "**Selected**: all numeric constants verbatim; all 21 `HandshakeRejectReason` tags; `ComponentSessionPreface::parse/encode` with exact magic/length layout; `HandshakeOffer` 11-field shape; `AttachmentPolicy::validate` transport constraints (packet-atomic requires seqpacket or inherited-socketpair); `LimitProfile` 15-field profile; `NoiseHandshake` for Nn/Kk/IkPsk2 profiles; `EstablishedHandshake::transcript_hash`; `BoundedVec`. **Excluded ADR45 assumptions**: `EndpointPurpose` 19 tags and `ServicePackage` 15 tags encode ADR45-specific service families — v3 will append new tags for Zone API endpoints without renumbering existing ones. `IdentityEvidenceRequirement` + `GuestSessionCredentialV1` / `GuestBootstrapPsk` / `GUEST_SESSION_CREDENTIAL_MAGIC`: ADR45 guest bootstrap credential formats, excluded until v3 Guest bootstrap work item. Generation-discovery protocol (`encode_generation_discovery_request` lines 138–149): ADR45 initiator probes server for current generation; v3 generation lives in Zone resource store — generation-discovery excluded from initial d2b-bus copy.", - "integration": "All d2b-bus, d2b-bus-unix, Provider registry, and client packages import v3 protocol constants; v3 Zone API service layer uses `EndpointPurpose`/`ServicePackage` tags for Zone API endpoints", - "dataMigration": "Additive: new v3 tags appended to existing closed enums; ADR45 v2 tag values remain valid during coexistence", - "validation": "`preface-magic-exact-16-bytes`, `preface-offer-len-zero-rejected`, `preface-offer-len-over-max-rejected`, `preface-offer-len-canonical-accepted`, `handshake-21-reject-reasons-all-covered`, `attachment-policy-packet-atomic-requires-seqpacket-or-inherited`, `limit-profile-local-default-all-fields-positive`", - "removalProof": "`d2b-contracts/src/v2_component_session.rs` ADR45-specific tags retired after all v2 sessions decommissioned" - }, - { - "workItemId": "ADR046-zone-control-014", - "specId": "ADR-046-resources-zone-control", - "specPath": "docs/specs/ADR-046-resources-zone-control.md", - "dependencyOwner": "ADR046-zone-control-001; Nix module owner", - "currentSource": "`nixos-modules/options-realms.nix` (option schema/assertion conventions); `nixos-modules/bundle-artifacts.nix` (bundle emit pattern); `nixos-modules/provider-registry-v2-json.nix` (provider-registry JSON emit); `nixos-modules/assertions.nix:730` (assertion style for unsafe-local); `packages/d2b-realm-core/src/ids.rs` (`LABEL_PATTERN`, `MAX_ID_LEN`, `is_label()` — eval-time name validation target)", - "reuseSource": "None from main; eval-time validation is Nix-native", - "reuseAction": "net-new", - "destination": "`nixos-modules/options-zones.nix`, `nixos-modules/resource-type-validators.nix`", - "detailedDesign": "Declare the unified `d2b.zones..resources.` option tree with two sub-options: `type` (string, required) and `spec` (attrset, required). Declare the `d2b.artifacts.` catalog option tree with `package` (types.package, required) and `type` (closed string enum, required); `d2b.artifacts` is a global option, not zone-local. For each core ResourceType (Zone, ZoneLink, Provider, Role, RoleBinding, Credential), a `genSchemaOptions` helper reads the corresponding ResourceTypeSchema JSON and emits per-type `spec` option declarations with correct Nix types, defaults, and documentation comments. Provider `spec.artifactId` is a plain string referencing the `d2b.artifacts` catalog; the `package` derivation is NOT a `spec` field. Non-core (Provider-registered) ResourceTypes are deferred to Phase 2 schema validation; Phase 1 accepts any non-empty `spec` attrset for non-core types. Implement all Phase 1 eval-time assertions (§14.10 Phase 1 table) via Nix `assert` and `lib.mkAssert`; `credentialRef` helper function producing `{ \"$credentialRef\": \"Credential/\" }` literals; `resourceRef` and `closedEnum` helpers; bootstrap provider exclusion assertions; eval assertion that Zone resource name must equal Zone attrset key. The runtime creates `Zone/`, `Provider/system-core`, and `Provider/system-minijail` directly with `managedBy=controller`; none is emitted or inferred from the configuration bundle. `allowUnsafeLocal` maps to a dedicated Host admission gate (current `options-realms.nix:346`). Provider manifest-derived fields (`spec.exports`, `spec.components`, `spec.dependencies`, `spec.permissionClaims`, `spec.upgradePolicy`, `spec.restartPolicy`) are declared as read-only/not-settable options; setting them is an eval assertion error", - "integration": "Unified `d2b.zones..resources.*` option tree consumed by ADR046-zone-control-015 resource compiler; Zone controller (ADR046-zone-control-001) reads resulting bundle; Provider package conventions from ADR046-zone-control-003", - "dataMigration": "Replace `nixos-modules/options-realms.nix`-derived option trees once Zone controller is live and has reached parity", - "validation": "All Phase 1 eval tests in §15.8 (`nix-eval-name-regex-enforced`, `nix-eval-verb-closed-enum`, `nix-eval-roleref-format`, `nix-eval-subject-type-restricted`, `nix-eval-no-duplicate-subjects`, `nix-eval-bootstrap-provider-rejected`, `nix-eval-provider-missing-artifact-id`, `nix-eval-artifact-id-not-in-catalog`, `nix-eval-artifact-wrong-type`, `nix-eval-artifact-id-format`, `nix-eval-credentialref-declared`, `nix-eval-dollar-key-rejected`, `nix-eval-zonelink-self-loop-rejected`, `nix-eval-zonelink-limits-maxpendingintents-bound`)", - "removalProof": "`nixos-modules/options-realms.nix`, `nixos-modules/realm-controller-config-json.nix`, `nixos-modules/realm-identity-config-json.nix` deleted after Zone controller and resource compiler reach full parity; `nixos-modules/assertions.nix` lines referencing `allowUnsafeLocal`/realm names removed after Host admission validation replaces them" - }, - { - "workItemId": "ADR046-zone-control-015", - "specId": "ADR-046-resources-zone-control", - "specPath": "docs/specs/ADR-046-resources-zone-control.md", - "dependencyOwner": "ADR046-zone-control-014; resource compiler owner", - "currentSource": "`nixos-modules/bundle-artifacts.nix` (artifact emit pattern); `nixos-modules/processes-json.nix` (canonical JSON serialization conventions); `packages/xtask/src/main.rs` `gen-schemas` subcommand (schema-from-derivation pattern); `packages/d2b-core/src/bundle.rs` (current bundle DTO shape for adaptation reference)", - "reuseSource": "None from main; the resource compiler is a new pure-Rust Nix derivation", - "reuseAction": "net-new", - "destination": "`packages/d2b-resource-compiler/src/{main,bundle,schema,validator,digest,sort,secret_lint,generation}.rs`; exposed as `pkgs.d2b-resource-compiler`; called from `nixos-modules/resource-compiler.nix`", - "detailedDesign": "Implement all Phase 2 build-time checks (§14.10 Phase 2 table): dispatch on `type` to look up ResourceTypeSchema; validate each resource's `spec` canonical JSON against the committed schema (build validation compares canonical rendered JSON against ResourceTypeSchema for each core type); compile the `d2b.artifacts.*` catalog: for each entry, build/include the derivation, verify `type` is a recognized value, compute `digest` over the derivation output, extract and hash manifest and config schema files, validate signature chain and conformance attestation; detect duplicate artifact IDs; for each Provider resource, look up `spec.artifactId` in the compiled catalog (build failure if absent or wrong type), verify `configSchemaDigest` matches schema SHA-256, validate operator `spec.config` against loaded JSON Schema using a pure-Rust validator bundled in the derivation, verify `manifestDigest` and signature chain, load manifest-derived fields (`exports`, `components`, `dependencies`, `permissionClaims`, `upgradePolicy`, `restartPolicy`) into the bundle envelope; emit private integrity-pinned artifact catalog (ID → type/digest/closure metadata) as a separate private file (never merged into the public resource bundle); check `spec.rules[*].resourceTypes` against installed Provider catalogs in the bundle (Role); verify `spec.roleRef` names an existing Role in the bundle (RoleBinding); verify `spec.subjects[*]` names resolve in bundle (RoleBinding); check ResourceType short-name collision across all Zone Providers; RFC 8785 canonical JSON serialization; per-resource `digest` computation; `bundleDigest` computation over sorted `resources` array; inline-secret heuristic lint (`--strict-secrets` flag); `generation` counter persistence in Nix module state; emit `zone-resources.json` bundle with all fields per §14.9", - "integration": "Reads from `d2b.zones..resources.*` (ADR046-zone-control-014); emits bundle consumed by ADR046-zone-control-001 configuration publication handler; generation counter stored as Nix module derivation input hash (hermetic) or in a NixOS state file (impure) — exact mechanism is implementation decision", - "dataMigration": "Full reset; no prior bundle state exists to carry forward", - "validation": "All Phase 2 build tests in §15.8 (`nix-build-artifact-id-missing-from-catalog`, `nix-build-artifact-wrong-type-rejected`, `nix-build-duplicate-artifact-id`, `nix-build-artifact-store-path-absent-from-bundle`, `nix-build-artifact-store-path-absent-from-config`, `nix-build-config-schema-failure`, `nix-build-schema-digest-mismatch`, `nix-build-manifest-digest-mismatch`, `nix-build-resourcetype-collision`, `nix-build-bundle-sorted`, `nix-build-bundle-digest-stable`, `nix-build-per-resource-digest-correct`, `nix-build-credential-ref-survives-build`, `nix-build-inline-secret-lint-warning`, `nix-build-inline-secret-strict-failure`)", - "removalProof": "No current equivalent; additive only; no prior code removed" - }, - { - "workItemId": "ADR046-zone-control-016", - "specId": "ADR-046-resources-zone-control", - "specPath": "docs/specs/ADR-046-resources-zone-control.md", - "dependencyOwner": "ADR046-zone-control-015; ADR046-zone-control-001; configuration publication handler owner", - "currentSource": "`packages/d2bd/src/lib.rs` lines 1408 and 16741 (`RealmControllersJson` load — live active generation load pattern); `nixos-modules/realm-controller-config-json.nix` (current config bundle emit to `/etc/d2b/`); `packages/d2b-realm-core/src/allocator_engine.rs` (generation/activation pattern); `packages/d2b-core/src/unsafe_local_workloads.rs:16–164` (`MAX_UNSAFE_LOCAL_WORKLOADS=256`, etc. — bounds reference for Credential/Host cleanup)", - "reuseSource": "main `a1cc0b2d`: `packages/d2b-session/src/lifecycle.rs` `begin_reconnect` exponential backoff logic (cleanup retry); `packages/d2b-state/` lock/lease types (ADR046-store-001 dependency for bundle file locking)", - "reuseAction": "extract exponential backoff from `begin_reconnect`", - "destination": "`packages/d2b-core-controller/src/configuration.rs` (Phase 3 activation, diff, delete dispatch); `packages/d2b-core-controller/src/cleanup.rs` (pending tracking, status, stuck detection, rollback verb handler)", - "detailedDesign": "Implement all Phase 3 runtime activation checks (§14.10 Phase 3 table); `bundleDigest` integrity verify; `zoneUid` consistency check; `generation` monotone check; per-resource `digest` re-verify; atomic generation advance in `store_meta`; diff computation (resources with `managedBy=configuration` absent from new bundle → async Delete; `managedBy=controller`/`managedBy=api` resources untouched); `managedBy` and `configurationGeneration` field maintenance on resources in redb store; `cleanupPendingCount` and `generationCleanupPending` maintenance; Zone.status.phase → Degraded while cleanup pending, reverts on completion; `GenerationCleanupPending`/`GenerationCleanupFailed` condition management; stuck-cleanup `GenerationCleanupFailed=True` at `cleanupStuckThreshold` (default 5 min) with exponential backoff retry; prior generation bundle retention and pruning up to configured `retainedPriorGenerationCount` (default 3, range 1..16); audit emission for all four cleanup audit kinds (§14.11); `zone.config-rollback` verb handler", - "integration": "Zone store / redb (ADR046-store-001); core-controller watch/trigger bus (ADR046-zone-control-011); Zone status writer (ADR046-zone-control-001); audit emitter (§13.2); Credential revocation hook (triggered when `deletionRequestedAt` is set on a Credential and `core.credential-revoke` finalizer is present; revocation completes before finalizer clearance)", - "dataMigration": "Full reset; no prior bundle activation state exists to carry forward", - "validation": "All Phase 3 runtime and cleanup tests in §15.8 (`nix-runtime-bundledigest-integrity`, `nix-runtime-generation-monotone`, `nix-runtime-zoneuid-mismatch-rejected`, `nix-runtime-zonename-mismatch-rejected`, `nix-runtime-activation-nonblocking`, `nix-runtime-provider-config-invalid-continues`, all `cleanup-*` and `rollback-*` tests)", - "removalProof": "`d2bd/src/lib.rs` config-load at lines 1408 and 16741 removed after Zone configuration publication handler reaches parity; `realm-controller-config-json.nix` and `realm-identity-config-json.nix` Nix bundle-emit removed after resource compiler reaches parity" - }, - { - "workItemId": "ADR046-zone-control-017", - "specId": "ADR-046-resources-zone-control", - "specPath": "docs/specs/ADR-046-resources-zone-control.md", - "dependencyOwner": "ADR046-zone-control-011, ADR046-zone-control-013, ADR046-zone-control-003", - "currentSource": "main `a1cc0b2d`: `packages/d2b-provider/src/registry.rs` (`RegistryLimits`, `AdmissionOptions`, `ProviderRegistryBuilder::new/limits/register_factory/register_instance/register_constructed/finish`, `ProviderRegistry::lifecycle/snapshot/instance/admit/shutdown`, `InFlightPermit`, `AdmittedProvider`, `ProviderRegistryManager::new/current/publish` — lines 33–568; tests `shutdown_closes_final_permit_notify_race`, `finish_drain_closes_final_permit_notify_race` — lines 683–691); `packages/d2b-provider/src/rpc.rs` (`SessionIdentity`, `ProviderClock`/`SystemProviderClock`, `RpcCall`, `RpcPayload`, `RpcResponse`, `AuthenticatedProviderRpc`, `RpcProviderProxy::new/preflight/call*`, `session_identity_matches_placement` — lines 27–895; test `provider_and_user_agent_session_identities_are_placement_exact` — lines 923–959); `packages/d2b-provider-toolkit/src/adapter.rs` (`ProviderAgentAdapter::new/invoke_session/invoke` — lines 18–194); `packages/d2b-provider-toolkit/src/conformance.rs` (`ConformanceError`, `check_descriptor_conformance`, `check_provider_conformance` — lines 8–126); `packages/d2b-provider-toolkit/src/fixture.rs` (`DeterministicClock`, `Fixture::new/from_descriptor/operation/request/call_context/session_identity`, `FakeProvider`, `sample_lease_request` — lines 39–262); `packages/d2b-provider-toolkit/src/registration.rs` (`ToolkitError`, `register_exact_instances` — lines 12–107); `packages/d2b-provider-toolkit/src/server.rs` (`GeneratedProviderServiceServer::from_session_handle/new/shutdown/generated_services` — lines 59–176; test `rpc_statuses_retain_closed_actionable_reasons`); `packages/d2b-provider-toolkit/src/values.rs` (`ProviderValues::new/descriptor/health/plan/handle_from_request/handle_from_plan/observation` — lines 18–192); `packages/d2b-provider-toolkit/src/redaction.rs` (`Redacted`, `Secret` — lines 3–37)", - "reuseSource": null, - "reuseAction": "copy + adapt", - "destination": "`packages/d2b-provider/src/{registry,rpc}.rs` (new v3 Provider package); `packages/d2b-provider-toolkit/src/{adapter,conformance,fixture,registration,server,values,redaction}.rs` (new v3 toolkit)", - "detailedDesign": "**Selected**: `ProviderRegistry` lifecycle `Accepting→Draining→Retired` with drain-waiter notify-race safety (ported from `shutdown_closes_final_permit_notify_race` / `finish_drain_closes_final_permit_notify_race` tests); `InFlightPermit` + global + per-provider in-flight quota; `ProviderRegistryManager::publish` validates snapshot before swap; `RpcProviderProxy::preflight` cancellation-check, deadline-check, method/capability match, session-identity/placement exactness; `ProviderAgentAdapter` rejects attachments with missing descriptors or non-increasing indexes (lines 79–99); `GeneratedProviderServiceServer::new` single-service-per-agent requirement + shutdown via atomic accept-flag + idle notify + timeout; `check_provider_conformance` health/inspection/observability check sequence; `Fixture`/`FakeProvider`/`DeterministicClock` as conformance harness; `Redacted`/`Secret` retained unchanged. **Excluded ADR45 assumptions**: `TrustedFirstPartyInProcess` as the only accepted placement (in `session_identity_matches_placement` lines 577–598): v3 Provider resources support multiple placements per `RealmControllerPlacement` mapping (§16.2); in-process placement is retained but not exclusive. Generated ttrpc stubs in `d2b-contracts/src/generated_v2_services/` are v2-service-specific; v3 Provider processes compile their own stubs from v3 proto files. ACA workload adapter (`d2b-gateway-runtime/src/aca_workload.rs`) excluded.", - "integration": "Provider/system-core and Provider/system-minijail use `ProviderRegistryBuilder::register_constructed` as bootstrap exceptions; other Providers register instances from Process-spawned agents via `ProviderAgentAdapter`; Zone runtime hosts one `ProviderRegistry` per installed Provider component; `ProviderRegistryManager::publish` swaps registry on Provider update", - "dataMigration": "Provider descriptors re-registered on Zone store bootstrap; no v2 provider state migration", - "validation": "`provider-registry-drain-waiter-race-safe` (ported from both notify-race tests), `provider-registry-publish-validates-snapshot-before-swap`, `provider-rpc-proxy-placement-exact` (ported from `provider_and_user_agent_session_identities_are_placement_exact`), `provider-agent-adapter-rejects-non-monotone-attachment-indexes`, `provider-server-shutdown-drains-in-flight-requests`, `provider-conformance-health-inspection-observability-sequence`", - "removalProof": "`d2b-realm-provider` trait crate removed after v3 Provider resource + registry integration (§16.5)" - }, - { - "workItemId": "ADR046-zone-control-018", - "specId": "ADR-046-resources-zone-control", - "specPath": "docs/specs/ADR-046-resources-zone-control.md", - "dependencyOwner": "ADR046-zone-control-011, ADR046-zone-control-012, ADR046-zone-control-013, ADR046-zone-control-002", - "currentSource": "main `a1cc0b2d`: `packages/d2b-session/src/lifecycle.rs` (`SessionPhase` 5-phase FSM, `begin_reconnect` window/attempt bounds — lines 9–195); `packages/d2b-realm-router/src/service_v2.rs` (`RealmSessionAuthority::local_controller/gateway_peer`, authority validation rejecting invalid combinations — lines 53–123; `RealmServiceServer::call` wire/request/generation/lifetime/attachment validation — lines 415–497; `RealmServiceLimits` 15 fields — lines 147–181; `RealmAuditEvent`/`RealmAuditOutcome` — lines 236–245); `packages/d2b-realm-router/src/session_lifecycle.rs` (`SessionPhase::{Allocating,TokenMinting,RelayConnecting,DisplayOpening,Running,Stopping,Stopped}`, `fail`/`stop`/`finish_stop`, tests `forward_sequence_reaches_running_then_refuses_to_advance`, `failure_mid_establishment_rolls_into_teardown_and_records_phase`, `stop_is_idempotent`, `finish_stop_without_stopping_is_a_no_op` — lines 31–220); `packages/d2b-client/src/session.rs` (`ComponentSessionConnector` trait — lines 79–86; `NamedStream` lifecycle/close/reset/send/receive — lines 426–576); `packages/d2b-daemon-access/src/component_session.rs` (`connect_component_session()` peer-UID verify + `HandshakeCredentials::Nn` + `TransportKind::LocalUnix` — lines 53–85)", - "reuseSource": null, - "reuseAction": "adapt", - "destination": "`packages/d2b-core-controller/src/zone_link.rs` (ZoneLink handler); `packages/d2b-resource-api/src/admission.rs` (request admission)", - "detailedDesign": "**Selected**: `SessionPhase` 5-phase FSM (from main `d2b-session`) drives ZoneLink session state; `Established` state → `status.connected=true` + ZoneLink `status.phase=Ready`; `Disconnected`/`Reconnecting` states → `status.connected=false` + ZoneLink `status.phase=Pending` (or `Degraded` if degraded capability); session-internal phases are not exposed as `ZoneLink.status.phase` values — only the common Resource phases (`Pending|Ready|Degraded|Failed|Unknown`) appear in `status.phase` (§3.5); `begin_reconnect` window/attempt logic → ZoneLink reconnect loop; `RealmSessionAuthority` local vs gateway pattern → ZoneLink authority types for host-local vs transport-bridge sessions; `RealmServiceServer::call` wire validation (generation, request lifetime, attachment) → Zone API request admission; `RealmServiceLimits` 15 fields → ZoneLink `spec.limits`; `connect_component_session()` Nn peer-UID path → Zone runtime bootstrap ComponentSession; `NamedStream` lifecycle → ZoneLink named-stream operations; session-lifecycle tests ported as ZoneLink phase regression tests. **Excluded ADR45 assumptions**: `RealmSessionAuthority::gateway_peer()` (lines 72–87): gateway custody and `Locality::Remote` + `CredentialCustody::GatewayGuest` are ADR45 realm-gateway patterns; v3 ZoneLink transport is bound by the resolved `spec.transportProviderRef`, `spec.transportSettings`, and `spec.transportCredentials` contract instead. Realm 7-phase `SessionLifecycle` (`Allocating→…→Running→Stopping→Stopped`) is the ADR45 realm-specific lifecycle; ZoneLink uses the 5-phase d2b-session model. `GuestBootstrapPsk`/`GuestSessionCredentialV1`: ADR45 guest bootstrap, excluded. `realm_stubs.rs` `ApiService`/`ApiFrontend` dead code excluded (§16.2).", - "integration": "Zone runtime startup creates bootstrap ComponentSession using `HandshakeCredentials::Nn` + local domain socket + peer-UID verification (adapted from `connect_component_session`); ZoneLink handler opens sessions for child Zone connections using d2b-bus `SessionEngine`; resource API admission layer validates requests using `RealmServiceServer::call` validation pattern", - "dataMigration": "Not applicable; new implementation", - "validation": "`zonelink-reconnect-child-uid-change`, `zonelink-disconnect-unknown-phase`, `zonelink-intent-queue-limit`, `zone-session-phase-forward-sequence-refuses-repeat` (ported from `forward_sequence_reaches_running_then_refuses_to_advance`), `zone-session-failure-records-phase` (ported from `failure_mid_establishment_rolls_into_teardown_and_records_phase`), `zone-session-stop-is-idempotent` (ported), `zone-bootstrap-session-nn-peer-uid-verified`", - "removalProof": "`d2b-realm-router/src/service_v2.rs` `RealmServiceServer` removed after Zone API service layer replaces realm v2 service; `d2b-realm-router/src/session_lifecycle.rs` removed after d2b-bus lifecycle replaces realm-specific one" - }, - { - "workItemId": "ADR046-zone-control-019", - "specId": "ADR-046-resources-zone-control", - "specPath": "docs/specs/ADR-046-resources-zone-control.md", - "dependencyOwner": "ADR046-zone-control-001, ADR046-provider-004; ADR046-zonelink owner; `d2b-core-controller` + `d2b-contracts` owners", - "currentSource": "None — net-new ADR 0046 cross-Zone sharing model (D096); no pre-ADR45 baseline equivalent", - "reuseSource": "ZoneLink reconcile/handler scaffolding (§3); `packages/d2b-session/src/streams.rs` `NamedStream` credit/backpressure (bounded encrypted stream carriage)", - "reuseAction": "net-new (extend ZoneLink controller)", - "destination": "`packages/d2b-contracts/src/v3/{resource_export,resource_import}.rs` (base schemas); `packages/d2b-core-controller/src/export_import.rs` (core ZoneLink export/import routing controller); shared adapter trait in `packages/d2b-provider/src/share_adapter.rs` (`ExportAdapter`/`ImportAdapter` signed-capability traits)", - "detailedDesign": "Implement the `ResourceExport` and `ResourceImport` standard ResourceTypes per §8A plus signed Provider `ProjectionFactory` metadata binding qualified Service type, qualified Binding type, allowed owner-Service backing refs, allowed Binding target refs, projection schema/fingerprint, and aggregate factory fingerprint. Admission accepts only an owner Service as `ResourceExport.resourceRef`; matches export/import/local-factory type and fingerprints; and creates exactly one same-qualified-type projection Service (`ownerRef: ResourceImport/`). It never projects Device/Endpoint/Binding and never creates Binding. Binding spec is desired consumer intent only; observations belong only in status. No cross-Zone Ref, FD, secret, path, locator, or resource grant crosses a Zone; payload bytes use bounded encrypted named streams and high-churn sessions/streams remain internal. Export removal/ZoneLink loss revokes leases and degrades the projection Service; reconnect revalidates generation and both fingerprints. D091 currency propagates Service → export → import → projection Service → authored Binding → children.", - "integration": "Zone store/redb (ADR046-store-001); shared D098 semantic base catalog (ADR046-provider-004); ZoneLink reconcile (§3); ComponentSession bounded encrypted named streams; signed projection factories/adapters for audio-pipewire, device-security-key, observability-otel, and policy-gated device-usbip; CLI graph rendering", - "dataMigration": "None — full d2b 3.0 reset; no prior cross-Zone sharing state", - "validation": "§8A.7: fast hermetic factory absent/mismatch/tamper, Service-only export target, exactly-one same-type projection Service, no Device/Endpoint/Binding projection, no auto-Binding, intent-only spec/status-only observations, backing/target allowlists, finalizer/update propagation, Provider classification, canonical Nix stability, quotas/reconnect/revoke, and no FD/secret/path tests; slower real encrypted-stream integration for audio/security-key/observability/policy-gated USBIP", - "removalProof": "Not applicable (new surface)" - }, - { - "workItemId": "ADR046-zone-control-020", - "specId": "ADR-046-resources-zone-control", - "specPath": "docs/specs/ADR-046-resources-zone-control.md", - "dependencyOwner": "ADR046-zone-control-019; `d2b-core-controller` owner", - "currentSource": "None — net-new ADR 0046 cross-Zone sharing model (D096)", - "reuseSource": "None from main; projection ownership reuses the core owner/child reconcile machinery (§11)", - "reuseAction": "net-new", - "destination": "`packages/d2b-core-controller/src/export_import_projection.rs` (local qualified Service projection lifecycle owned by `ResourceImport`)", - "detailedDesign": "Core creates exactly one same-qualified-type projection Service per `ResourceImport` and keeps it synchronized with the remote Service lease. Operators/Nix separately author same-Zone matching Binding resources with `serviceRef` plus an allowed Guest/User/Zone target; Binding specs hold desired intent only and Binding controllers write observations only to status while owning Process/Endpoint children. On revoke, mark the projection draining/revoked and let Binding controllers stop children. On delete, wait for Bindings to be deleted/retargeted (`BindingReferencesRemain`), release the lease, delete only the projection Service/provider-owned children, then clear the import finalizer. Never create/delete Binding or project Device/Endpoint.", - "integration": "ADR046-zone-control-019 controller; owner/dependency reconcile (§11, ADR046-reconcile-*); local semantic Provider import adapter", - "dataMigration": "None — full d2b 3.0 reset", - "validation": "Exactly one same-type Service projection owned by import; no Device/Endpoint/Binding projection; Binding never auto-created/deleted; Binding target allowlist; intent-only spec/status-only observations; owned Process/Endpoint child cleanup; pending finalizer while Binding refs remain; reconnect only after generation/factory/schema revalidation; hermetic fake-adapter + real-stream integration tiers", - "removalProof": "Not applicable (new surface)" - }, - { - "workItemId": "ADR046-zone-control-021", - "specId": "ADR-046-resources-zone-control", - "specPath": "docs/specs/ADR-046-resources-zone-control.md", - "dependencyOwner": "ADR046-zone-control-001, ADR046-zone-control-016; `d2b-core-controller` owner", - "currentSource": "`packages/d2bd/src/` process-global statics `USBIP_BACKGROUND_RECONCILE_ACTIVE`, `FORCE_SHUTDOWN_GENERATIONS`, and `activation_locks()`; current per-VM configuration staging symbols; ZoneLink cursor persistence in `zone_link_cursors`", - "reuseSource": "ZoneLink handler/cursor scaffolding (§3); core coordinator patterns", - "reuseAction": "`adapt` (move process-global state to per-Zone status/coordinator)", - "destination": "`packages/d2b-core-controller/src/{coordinator,configuration,zonelink}.rs`", - "detailedDesign": "Per D097 core-audit findings (§8B.2): move the process-global `USBIP_BACKGROUND_RECONCILE_ACTIVE`, `FORCE_SHUTDOWN_GENERATIONS`, and `activation_locks()` state into **per-Zone** provider/resource status or a per-Zone coordinator keyed by the authority index (no process-global singletons that ignore Zone boundaries). Migrate the configuration publisher's per-VM staging symbols to **per-Zone** staging under the single configuration-publisher authority. Make ZoneLink cursor persistence and restart adoption an authority owned by the ZoneLink handler (`ownerProof`; ambiguity quarantines). All coordinated through the core authority index; no direct broker path, no process-global lock.", - "integration": "Core authority index (ADR046-zone-control-019); ZoneLink reconcile (§3); configuration publication handler (ADR046-zone-control-016)", - "dataMigration": "Full d2b 3.0 reset; no process-global state persisted across the cutover", - "validation": "Two Zones on one host do not share `USBIP_BACKGROUND_RECONCILE_ACTIVE`/`FORCE_SHUTDOWN_GENERATIONS`/activation-lock state; per-Zone configuration staging isolation; ZoneLink cursor adoption by `ownerProof` and quarantine on ambiguity; hermetic with fakes", - "removalProof": "The process-global statics and per-VM staging symbols are deleted after the per-Zone coordinator reaches parity; confirmed by `cargo check` and a no-process-global lint" - }, - { - "workItemId": "ADR046-zone-control-022", - "specId": "ADR-046-resources-zone-control", - "specPath": "docs/specs/ADR-046-resources-zone-control.md", - "dependencyOwner": "ADR046-zone-control-019, ADR046-api-001; `d2b-core-controller` + resource API owners", - "currentSource": "None — net-new D097 admission (Provider cardinality)", - "reuseSource": "Core authority index (ADR046-zone-control-019); resource API admission (`ADR-046-resource-api-and-authorization`)", - "reuseAction": "net-new", - "destination": "`packages/d2b-core-controller/src/authority.rs`; resource API admission hook", - "detailedDesign": "Admission enforces **Provider controller cardinality** via the core authority index: most Providers are `exactly-one` per Zone; the observability Provider is `at-most-one` (zero-or-one). A `Create`/activation that would install a second controller for an `exactly-one` Provider (or a second observability Provider) is rejected with `duplicateConflict` naming the incumbent owner digest before any effect; config activation goes `Degraded`.", - "integration": "Core authority index; resource API admission; configuration activation", - "dataMigration": "None — full d2b 3.0 reset", - "validation": "Second Provider controller for an `exactly-one` Provider rejected with `duplicateConflict`; second observability Provider rejected; single controller admitted; `Degraded` config activation names the incumbent digest; hermetic", - "removalProof": "Not applicable (new surface)" - }, - { - "workItemId": "ADR046-zone-control-023", - "specId": "ADR-046-resources-zone-control", - "specPath": "docs/specs/ADR-046-resources-zone-control.md", - "dependencyOwner": "ADR046-zone-control-019; `d2b-core-controller` + `d2b-contracts` owners", - "currentSource": "None — `Quota` and `EmergencyPolicy` scope-uniqueness are specified (§7, §8) but not implemented/tested at baseline", - "reuseSource": "`Quota`/`EmergencyPolicy` schemas (§7, §8); core authority index", - "reuseAction": "net-new (implementation + tests)", - "destination": "`packages/d2b-core-controller/src/{quota,emergency_policy}.rs`; `packages/d2b-contracts/src/v3/{quota,emergency_policy}.rs`", - "detailedDesign": "Implement `Quota` and `EmergencyPolicy` scope-uniqueness as `exactly-one`-per-scope authorities in the core authority index: a second `Quota`/`EmergencyPolicy` claiming the same scope is a `duplicateConflict`. Add the scope-uniqueness admission, status, and the test matrix (per D094 fast hermetic tests).", - "integration": "Core authority index (ADR046-zone-control-019); resource API admission", - "dataMigration": "None — full d2b 3.0 reset", - "validation": "Duplicate-scope `Quota`/`EmergencyPolicy` rejected with `duplicateConflict`; single-scope admitted; union/individual scope flags honored; fast hermetic tests", - "removalProof": "Not applicable (new implementation)" - }, - { - "workItemId": "ADR046-zone-control-024", - "specId": "ADR-046-resources-zone-control", - "specPath": "docs/specs/ADR-046-resources-zone-control.md", - "dependencyOwner": "ADR046-zone-control-019, ADR046-zone-control-022; `d2b-core-controller` owner", - "currentSource": "None — net-new D097 hardware-audit contract; today physical/kernel backings are guarded per-Zone or per-process, not Host-global", - "reuseSource": "Core authority index (ADR046-zone-control-019)", - "reuseAction": "net-new (Host-global index scope for host/physical-device authorities)", - "destination": "`packages/d2b-core-controller/src/authority.rs` (Host-global index scope + hardware admission)", - "detailedDesign": "Extend the core authority index so `host`, `physical-device`, `seat`, and `external-service` authorities are keyed **Host-global** (`(Host, authorityClass, opaqueKeyDigest)`), admitting exactly one owner across all Zones on the host, while `zone`-scoped authorities stay Zone-local. Enforce the §8B.3 hardware rows: GPU full-device exclusive vs render-node shared; per-Guest swtpm and physical TPM exclusive (state never wiped); one Core-derived `physical-usb-backing/v1` identity digest claimed through the exact `(Host, physical-usb-backing, opaqueKeyDigest)` tuple by every USB or security-key implementation before effects, plus the separate host-global `usbip-host` module; macvtap/NIC `parentInterface` `passthru` globally exclusive across all Zones; host-shared `/dev/kvm` and `/dev/vhost-vsock` as `Provider/system-core` grants (no 28th Provider, no `kvm` busClass); globally-unique vsock CID; fixed listener ports as `Endpoint`s; host store + per-Guest store-view writer; Network TAP/bridge. A second Zone claiming the same physical backing receives `physical-usb-backing-conflict` before any open, bind, withhold, module, relay, or attachment effect; restart adopts by `ownerProof`; Guest-stop drains dependent leases. GPU-owned `udmabuf`/video and per-session `vhost-vsock` tokens stay authority subresources/DeviceGrants (not resources/Providers).", - "integration": "Core authority index (ADR046-zone-control-019); Provider cardinality admission (ADR046-zone-control-022); `Provider/system-core` KVM/vhost-vsock grant; `Provider/device-*` and `Network` authorities", - "dataMigration": "None — full d2b 3.0 reset", - "validation": "Two Zones on one host cannot both claim one GPU/TPM/USB/`/dev/kvm`/passthru NIC/vsock CID/fixed port — second is `duplicateConflict`; security-key and USB implementations resolving the same physical token submit byte-identical `physical-usb-backing` tuple keys and the loser receives `physical-usb-backing-conflict` before any effect; Provider-private authority classes/digests cannot bypass the collision; render-node shared admits bounded holders; per-Guest swtpm exclusive and marker never wiped; host-global adoption by `ownerProof`; hardware D096 exportability (GPU/KVM/TPM/store/macvtap non-exportable; semantic USB policy-gated); fast hermetic with fakes", - "removalProof": "Not applicable (new surface)" - }, - { - "workItemId": "W-N01", - "specId": "ADR-046-provider-device-security-key", - "specPath": "docs/specs/providers/ADR-046-provider-device-security-key.md", - "dependencyOwner": "ADR-046 provider-device-security-key crate owner; depends on provider-model/package workspace policy.", - "currentSource": "None — net-new v3 work; no pre-ADR45 baseline equivalent", - "reuseSource": null, - "reuseAction": "net-new", - "destination": "New crate `packages/d2b-provider-device-security-key/` with `src/`, `tests/`, `integration/`, `README.md` (workspace policy requires all four)", - "detailedDesign": "New crate `packages/d2b-provider-device-security-key/` with `src/`, `tests/`, `integration/`, `README.md` (workspace policy requires all four)", - "integration": "Workspace/package descriptor expose the crate to Core; W-N02 through W-N22 add controllers, resource contracts, relay/frontend, adapters, tests, and docs.", - "dataMigration": "Full d2b 3.0 reset; no v2 state/config import", - "validation": "Workspace package-policy check rejects missing `src/`, `tests/`, `integration/`, or `README.md`; `cargo test -p d2b-provider-device-security-key --lib --tests` discovers the hermetic suite; README acceptance criteria from the provider crate standard layout are satisfied.", - "removalProof": "None — net-new; no prior owner to remove" - }, - { - "workItemId": "W-N02", - "specId": "ADR-046-provider-device-security-key", - "specPath": "docs/specs/providers/ADR-046-provider-device-security-key.md", - "dependencyOwner": "Provider controller owner; depends on W-N06 probe, W-N09 templates, W-N18 effect port, W-N19 Service/Binding contracts, W-N20 status contract, and ADR-046-resource-reconciliation.", - "currentSource": "None — net-new v3 work; no pre-ADR45 baseline equivalent", - "reuseSource": null, - "reuseAction": "net-new", - "destination": "`packages/d2b-provider-device-security-key/src/controller.rs`", - "detailedDesign": "One controller implements standard reconcile for local physical Devices, authority/projection SecurityKeyServices, and SecurityKeyBindings. It observes Devices; realizes an authority Service as relay Process/Endpoint; accepts projection Services only from Core/import; realizes each Binding as frontend Process/private Endpoint; enforces child-first finalizers and never creates an import or Device projection.", - "integration": "Watches Device and both provider-neutral semantic types filtered by `providerRef=Provider/device-security-key`, plus Process, Endpoint, Guest/User, ResourceExport/Import, and dependency indexes; writes semantic base plus signed Provider-extension status/finalizers; drives relay-control messages.", - "dataMigration": "Full d2b 3.0 reset; no v2 state/config import", - "validation": "`controller_reconcile.rs`, `service_binding_projection.rs`, `mutual_exclusion.rs`, `status_binding.rs`, and deletion/finalizer tests cover authority/projection/Binding branches, no Device projection, and no Volume API calls.", - "removalProof": "None — net-new; no prior owner to remove" - }, - { - "workItemId": "W-N03", - "specId": "ADR-046-provider-device-security-key", - "specPath": "docs/specs/providers/ADR-046-provider-device-security-key.md", - "dependencyOwner": "Authority Service relay owner; depends on W-R01, W-R02, W-N04, W-N05, W-N07, W-N09, and W-N11.", - "currentSource": "None — net-new v3 work; no pre-ADR45 baseline equivalent", - "reuseSource": null, - "reuseAction": "net-new", - "destination": "`packages/d2b-provider-device-security-key/src/relay.rs`", - "detailedDesign": "Authority Service-owned relay entry point: bounded authenticated Binding connections, one LeaseId-guarded fair ceremony queue, CID translation/cancel-all-CIDs, hidraw fd from Core DeviceGrant, CTAPHID named stream, and internal relay-control channel.", - "integration": "Core injects the Service's physical DeviceGrant, relay Endpoint, and controller channel; Bindings that reference authority or projection Services connect through same-Zone Service Endpoints; Core releases grant on relay exit.", - "dataMigration": "Full d2b 3.0 reset; no relay session state import", - "validation": "`host_relay_guest_frontend/`, `fair_queue.rs`, `device_grant_no_path.rs`, `descriptor_validation.rs`, `cancel_propagation.rs`, and `cid_isolation.rs` prove one authority open, multi-Binding fair serialization, fd-only access, LeaseId cancel, and CID isolation.", - "removalProof": "Supersedes daemon-internal relay behavior removed by W-X01/W-X02 after relay Process tests pass." - }, - { - "workItemId": "W-N04", - "specId": "ADR-046-provider-device-security-key", - "specPath": "docs/specs/providers/ADR-046-provider-device-security-key.md", - "dependencyOwner": "Relay ceremony-session foundation owner; depends on W-R01 and W-N01.", - "currentSource": "None — net-new v3 work; no pre-ADR45 baseline equivalent", - "reuseSource": null, - "reuseAction": "net-new", - "destination": "`packages/d2b-provider-device-security-key/src/session.rs`", - "detailedDesign": "`SessionStateMachine` with Idle/Queued/Active/Completed/TimedOut, bounded FIFO queue, monotonic LeaseId stale-release guard, per-Binding session ring, timeout/cancel, and ring eviction. Ceremony rows are non-Resource records; DeviceGrant remains held for relay lifetime.", - "integration": "W-N03 consumes it; controller receives lifecycle messages; Service/Binding status receives aggregates only; session query/audit consumes bounded non-secret rows.", - "dataMigration": "Full d2b 3.0 reset; no session ring import", - "validation": "`session_state_machine.rs`, `session_ring.rs`, `fair_queue.rs`, `session_timeout.rs`, and `cancel_propagation.rs` cover queue fairness, eviction, stale LeaseId rejection, timeout, and cancel.", - "removalProof": "None — net-new; no prior owner to remove" - }, - { - "workItemId": "W-N05", - "specId": "ADR-046-provider-device-security-key", - "specPath": "docs/specs/providers/ADR-046-provider-device-security-key.md", - "dependencyOwner": "Relay CID-translation foundation owner; depends on W-R01 and W-N01.", - "currentSource": "None — net-new v3 work; no pre-ADR45 baseline equivalent", - "reuseSource": null, - "reuseAction": "net-new", - "destination": "`packages/d2b-provider-device-security-key/src/cid.rs`", - "detailedDesign": "CID translator: per-active-ceremony u32→u64 host-CID allocation, bimap, cancel-all-active-CIDs, and eviction on ceremony end", - "integration": "Relay rewrites frontend CTAPHID CIDs before sending to hidraw fd and reverses responses before writing the ComponentSession named stream; session teardown drops the map.", - "dataMigration": "Full d2b 3.0 reset; CID maps are transient and not imported", - "validation": "`cid_isolation.rs` verifies per-session allocation, round trip, no sharing across relays, and eviction on session end.", - "removalProof": "None — net-new; no prior owner to remove" - }, - { - "workItemId": "W-N06", - "specId": "ADR-046-provider-device-security-key", - "specPath": "docs/specs/providers/ADR-046-provider-device-security-key.md", - "dependencyOwner": "Probe/effect-port and activation owner; depends on W-N18 effect port and Core private bundle device table support.", - "currentSource": "None — net-new v3 work; no pre-ADR45 baseline equivalent", - "reuseSource": null, - "reuseAction": "net-new", - "destination": "`packages/d2b-provider-device-security-key/src/probe.rs`; Provider activation/Core private bundle device table population for label → `device_token`", - "detailedDesign": "hidraw probe: `probe.rs` — calls `SecurityKeyEffectPort::observe_inventory(&device_id, &policy_id)` with opaque types injected by Core; interprets `InventoryObservation`; never reads `/sys/class/hidraw/` directly; bundle device table population at activation time (Provider activation resolves label → `device_token` via Core; stored in private bundle)", - "integration": "Controller scheduled-observe invokes `probe.rs`; Core adapter implements `SecurityKeyEffectPort`; Nix activation emits private label-to-token bundle entries; Device status receives `DevicePresent` and phase updates.", - "dataMigration": "Full d2b 3.0 reset; no v2 probe state import", - "validation": "`controller_reconcile.rs` scheduled-observe tests, `descriptor_validation.rs` Debug-redaction capture, and path-safety tests prove Provider never reads sysfs and receives only opaque observations.", - "removalProof": "Supersedes provider-side or broker fallback sysfs scanning; W-R04/W-N11 removal proof verifies only bundle `device_token` lookup remains." - }, - { - "workItemId": "W-N07", - "specId": "ADR-046-provider-device-security-key", - "specPath": "docs/specs/providers/ADR-046-provider-device-security-key.md", - "dependencyOwner": "ComponentSession/security descriptor contract owner; depends on W-N01, W-R05, and ADR-046-componentsession-and-bus.", - "currentSource": "None — net-new v3 work; no pre-ADR45 baseline equivalent", - "reuseSource": null, - "reuseAction": "net-new", - "destination": "`packages/d2b-provider-device-security-key/src/descriptor.rs`", - "detailedDesign": "Declare relay↔controller service and relay↔Binding-frontend `d2b.security-key.v3` fingerprints, Noise profiles, canonical Service/Binding subject pairing, bounded encrypted-stream records, and descriptor validation; no ambient path or raw vsock CID.", - "integration": "Provider descriptor declares services and fingerprints; LaunchTicket injects internal channel and Endpoint transport; relay/controller/frontend validate descriptors and peer authority before exchanging messages.", - "dataMigration": "Full d2b 3.0 reset; no v2 transport/session state import", - "validation": "`descriptor_validation.rs` covers wrong service, wrong descriptor digest, wrong SO_PEERCRED uid, unenrolled key, oversized records, no ambient path, and redacted opaque IDs.", - "removalProof": "None — net-new; no prior owner to remove" - }, - { - "workItemId": "W-N08", - "specId": "ADR-046-provider-device-security-key", - "specPath": "docs/specs/providers/ADR-046-provider-device-security-key.md", - "dependencyOwner": "Sandbox/minijail foundation owner; depends on W-N01, W-R07, and ADR-046-components-processes-and-sandbox.", - "currentSource": "None — net-new v3 work; no pre-ADR45 baseline equivalent", - "reuseSource": null, - "reuseAction": "net-new", - "destination": "`nixos-modules/minijail-profiles.nix` entries for relay and controller; provider descriptor sandbox templates for relay/controller/frontend", - "detailedDesign": "Minijail profiles for relay and controller only; frontend uses `Provider/system-systemd` hardening directives compiled from `SandboxSpec` (no minijail profile for frontend). Add relay and controller entries to `nixos-modules/minijail-profiles.nix`; `capabilityClasses: []`; `seccompClass: sk-relay` and `seccompClass: sk-controller`", - "integration": "Nix minijail profiles feed system-minijail Process launches for controller/relay; frontend Process template feeds system-systemd hardening; provider tests assert the split.", - "dataMigration": "Full d2b 3.0 reset; no sandbox state import", - "validation": "`minijail_sk_frontend` successor tests, sandbox template tests, and zero-capability/seccomp assertions cover relay/controller minijail profiles and no frontend minijail profile.", - "removalProof": "Supersedes `ProcessRole::SecurityKeyFrontend`-centric minijail test ownership removed by W-X04/W-X05 after Process-resource coverage passes." - }, - { - "workItemId": "W-N09", - "specId": "ADR-046-provider-device-security-key", - "specPath": "docs/specs/providers/ADR-046-provider-device-security-key.md", - "dependencyOwner": "Provider process/Endpoint-template owner; depends on W-N01, W-N08, and W-N19 Service/Binding ownership contracts.", - "currentSource": "None — net-new v3 work; no pre-ADR45 baseline equivalent", - "reuseSource": null, - "reuseAction": "net-new", - "destination": "Provider descriptor Process templates and owned CTAPHID `Endpoint` template for `Provider/device-security-key`", - "detailedDesign": "Templates: Provider controller; authority Service-owned relay Process/relay Endpoint; Binding-owned frontend Process/private Endpoint; projection Service local Endpoint. Frontend requires Guest/User and the system-core UHID DeviceGrant; no virtual Device template exists.", - "integration": "Core creates controller; Provider controller realizes authority Services and Bindings plus each projection Service's ordinary local import-route Endpoint; Process Providers launch children and preserve ownerRef boundaries.", - "dataMigration": "Full d2b 3.0 reset; no v2 processes.json import", - "validation": "`controller_reconcile.rs`, Process template golden tests, Endpoint resource tests, and frontend `userRef` admission tests prove templates and Endpoint shape.", - "removalProof": "Supersedes the legacy readiness-only `ProcessRole::SecurityKeyFrontend` tracking node removed by W-X05 after v3 Process resources are live." - }, - { - "workItemId": "W-N10", - "specId": "ADR-046-provider-device-security-key", - "specPath": "docs/specs/providers/ADR-046-provider-device-security-key.md", - "dependencyOwner": "Provider package descriptor owner; depends on W-N01, W-R05, W-N07, W-N09, W-N19, W-N20, and ADR-046-provider-model-and-packaging.", - "currentSource": "None — net-new v3 work; no pre-ADR45 baseline equivalent", - "reuseSource": null, - "reuseAction": "net-new", - "destination": "Signed Provider descriptor JSON for `Provider/device-security-key` in the provider package", - "detailedDesign": "Signed descriptor: config; physical Device integration; implementation claim for the provider-neutral `security-key.d2bus.org` Service/Binding base schemas/fingerprints; strict `device-security-key.d2bus.org` spec/status extensions; authority/projection union and D097 descriptor; controller/relay/frontend/Endpoint templates; export/import adapter capability; ComponentSession services; empty ProviderStateSet; permission claims.", - "integration": "Core ProviderDeployment verifies the signed descriptor, installs ResourceApiBinding and component descriptors, exposes service fingerprints to ComponentSession validation, and supplies permission claims/RBAC bindings.", - "dataMigration": "Full d2b 3.0 reset; no provider descriptor import", - "validation": "Descriptor schema validation, semantic-base versus Provider-extension fingerprints, exact type/no-alias tests, service inventory tests, permission claim tests, empty ProviderStateSet tests, and README/provider package conformance checks.", - "removalProof": "None — net-new; no prior owner to remove" - }, - { - "workItemId": "W-N11", - "specId": "ADR-046-provider-device-security-key", - "specPath": "docs/specs/providers/ADR-046-provider-device-security-key.md", - "dependencyOwner": "Core LaunchTicket/broker owner; depends on W-R04, W-R05, W-N06, and ADR-046-resources-device.", - "currentSource": "None — net-new v3 work; no pre-ADR45 baseline equivalent", - "reuseSource": null, - "reuseAction": "net-new", - "destination": "v3 `SecurityKeyOpenDevice` broker op and Core LaunchTicket DeviceGrant resolution path", - "detailedDesign": "v3 `SecurityKeyOpenDevice` broker op update: add `zone` field; implement bundle device table `device_token` lookup as sole open path; remove iterative sysfs scan from broker; add post-open revalidation steps (fstat, HIDIOCGRAWINFO, HIDIOCGRDESC). This is an internal Core operation called by LaunchTicket; the Provider controller does not call it.", - "integration": "Authority Service controller derives relay `deviceUsage` from `spec.provider.settings.deviceRef`; Core admits authority then resolves DeviceGrant through the private bundle table; broker returns an fd to Core; projection Services never enter this path.", - "dataMigration": "Full d2b 3.0 reset; no v2 broker state import", - "validation": "Broker unit tests for zone field and token lookup, path-rejection tests, post-open revalidation tests, and provider tests proving no Provider broker call or sysfs path.", - "removalProof": "Superseded broker iterative sysfs scan behavior is removed; tests prove only bundle `device_token` lookup is accepted for `SecurityKeyOpenDevice`." - }, - { - "workItemId": "W-N12", - "specId": "ADR-046-provider-device-security-key", - "specPath": "docs/specs/providers/ADR-046-provider-device-security-key.md", - "dependencyOwner": "Nix resource compiler owner; depends on W-N10, W-N19, ADR046-zone-control-024, and ADR-046-nix-configuration.", - "currentSource": "None — net-new v3 work; no pre-ADR45 baseline equivalent", - "reuseSource": null, - "reuseAction": "net-new", - "destination": "`nixos-modules/` resource compiler/eval assertions for physical Device, authority Service, ResourceExport/Import, and consumer Binding", - "detailedDesign": "Compile the owner Device→Service→export and consumer import→projection-Service→Binding shape; reject authored projections, projection `spec.provider`, Device export/projection, cross-Zone refs, duplicate authorities/Bindings, paths, and any security-key/USB configuration that does not collide through the exact Core-derived `(Host, physical-usb-backing, opaqueKeyDigest)` tuple after trusted identity resolution.", - "integration": "Nix emits Device/authority Service/export/import/Binding; Core alone creates projection Service; bundle feeds Provider and authority-index admission.", - "dataMigration": "Full d2b 3.0 reset; current Nix options migrate to v3 Zone resources without state import", - "validation": "Nix eval tests for label resolution, `busClass=hidraw`, exclusive arbitration, Core-only projection without `spec.provider`, byte-identical USB/security-key physical backing tuple collision, Provider-private-class bypass rejection, prohibited fields, and providerRef resolution.", - "removalProof": "Supersedes current option shape only after v3 Zone resource option parity; legacy security-key/USBIP mutual-exclusion assertion is replaced by v3 resource assertion coverage." - }, - { - "workItemId": "W-N13", - "specId": "ADR-046-provider-device-security-key", - "specPath": "docs/specs/providers/ADR-046-provider-device-security-key.md", - "dependencyOwner": "Guest Nix migration owner; depends on W-R03, W-N09, and W-N19 Binding-owned frontend contract.", - "currentSource": "None — net-new v3 work; no pre-ADR45 baseline equivalent", - "reuseSource": null, - "reuseAction": "net-new", - "destination": "`nixos-modules/components/security-key-guest.nix` migration gate `d2b.securityKey._legacySystemdUnit`", - "detailedDesign": "Guest Nix module migration gate: `d2b.securityKey._legacySystemdUnit` option, defaulting to false when Provider is installed; remove `d2b-sk-frontend.service` unit", - "integration": "Guest Nix keeps `uhid` and the static frontend binary; Binding controller owns frontend lifecycle and system-core supplies the UHID DeviceGrant; no Device row or udev rule is emitted.", - "dataMigration": "Full d2b 3.0 reset; no legacy frontend unit state import", - "validation": "Nix eval tests show the legacy unit is absent by default with Provider installed, can be gated only during transition if required, and `uhid` module/binary wiring remains present.", - "removalProof": "W-X03 deletes the superseded `nixos-modules/components/security-key-guest.nix` `d2b-sk-frontend.service` declaration after the gate defaults to false." - }, - { - "workItemId": "W-N14", - "specId": "ADR-046-provider-device-security-key", - "specPath": "docs/specs/providers/ADR-046-provider-device-security-key.md", - "dependencyOwner": "Audit owner for Core device-grant and Service/Binding lifecycle; depends on W-N02, W-N11, and ADR-046-telemetry-audit-and-support.", - "currentSource": "None — net-new v3 work; no pre-ADR45 baseline equivalent", - "reuseSource": null, - "reuseAction": "net-new", - "destination": "Core `device-grant` audit and Provider controller Service/Binding ceremony lifecycle audit", - "detailedDesign": "Path-free authority-grant records from Core and bounded Service/Binding/session digests/outcomes from controller; no path, raw target identity, LeaseId, session content, or CTAP bytes.", - "integration": "Core emits grant audit; controller emits Service/Binding lifecycle audit; Zone stream stores bounded records; CLI/support consumes digests/outcomes.", - "dataMigration": "Full d2b 3.0 reset; no v2 audit import", - "validation": "Audit tests assert path-free fields, bounded digests, no guest name/session content/CTAP bytes, grant emitted by Core not Provider controller, and lifecycle emitted by controller.", - "removalProof": "None — net-new; no prior owner to remove" - }, - { - "workItemId": "W-N15", - "specId": "ADR-046-provider-device-security-key", - "specPath": "docs/specs/providers/ADR-046-provider-device-security-key.md", - "dependencyOwner": "Observability owner; depends on W-N03 relay, W-N02 controller, and ADR-046-telemetry-audit-and-support.", - "currentSource": "None — net-new v3 work; no pre-ADR45 baseline equivalent", - "reuseSource": null, - "reuseAction": "net-new", - "destination": "Provider/controller bounded telemetry emitter and observability-otel handoff for security-key metrics", - "detailedDesign": "OTEL metrics: `d2b_device_sk_session_total`, `d2b_device_sk_ceremony_duration_seconds`, `d2b_device_sk_relay_restarts_total` via bounded emitter ring", - "integration": "Relay/controller write metric events to the bounded ring; observability-otel Provider drains and exports; dashboards/CLI consume closed labels and bounded histograms.", - "dataMigration": "Full d2b 3.0 reset; no v2 telemetry import", - "validation": "Metrics tests assert closed label sets, no device/session/guest/path labels, bounded ring behavior, and correct session/ceremony/restart counters.", - "removalProof": "None — net-new; no prior owner to remove" - }, - { - "workItemId": "W-N16", - "specId": "ADR-046-provider-device-security-key", - "specPath": "docs/specs/providers/ADR-046-provider-device-security-key.md", - "dependencyOwner": "Provider documentation owner; depends on W-N01 through W-N15 and W-N17 through W-N22 for complete behavior.", - "currentSource": "None — net-new v3 work; no pre-ADR45 baseline equivalent", - "reuseSource": null, - "reuseAction": "net-new", - "destination": "`packages/d2b-provider-device-security-key/README.md`", - "detailedDesign": "README: initial Provider identity, provider-neutral Service/Binding catalog, strict Provider-extension fields, physical Device, owner/export/import/projection/Binding chain, process ownership, RBAC, invariants, status/telemetry, no-alias rule, and commands", - "integration": "Workspace/package policy and provider crate acceptance use the README as the human entry point; docs link to it for provider-local build/test/integration commands.", - "dataMigration": "None — docs/tooling only; no runtime state", - "validation": "README presence check from provider crate standard layout; documentation review verifies every listed section and command is present and matches the crate/package behavior.", - "removalProof": "None — net-new; no prior owner to remove" - }, - { - "workItemId": "W-N17", - "specId": "ADR-046-provider-device-security-key", - "specPath": "docs/specs/providers/ADR-046-provider-device-security-key.md", - "dependencyOwner": "Endpoint/ComponentSession integration owner; depends on W-R03, W-N03, W-N07, W-N09, W-N19, and ADR-046-componentsession-and-bus.", - "currentSource": "None — net-new v3 work; no pre-ADR45 baseline equivalent", - "reuseSource": null, - "reuseAction": "net-new", - "destination": "Authority/projection Service Endpoint and Binding private Endpoint resolution, including transport-vsock and ZoneLink encrypted streams", - "detailedDesign": "Resolve each Binding only through its same-Zone Service Endpoint; enroll Noise KK for Service/Binding frontend; authority uses transport-vsock locally, projection uses per-import bounded encrypted stream with credits/backpressure/generation/deadline/cancel.", - "integration": "Service/Binding-owned Endpoints produce opaque LaunchTicket attachments; the import adapter binds the projection Service's ordinary local import-route Endpoint; no remote Ref, FD, or raw locator is exposed.", - "dataMigration": "Full d2b 3.0 reset; no v2 transport state import", - "validation": "`host_relay_guest_frontend/` and `descriptor_validation.rs` verify Endpoint resolution, Noise KK enrollment, attachment opacity, and no raw vsock CID/port in status/spec.", - "removalProof": "Supersedes baseline `vsock.sock_14320` raw port usage; tests prove no `vsockPort` or raw AF_VSOCK framing remains for security-key transport." - }, - { - "workItemId": "W-N18", - "specId": "ADR-046-provider-device-security-key", - "specPath": "docs/specs/providers/ADR-046-provider-device-security-key.md", - "dependencyOwner": "`d2b-contracts` neutral effect-port foundation owner; depends on W-N01 and ADR-046-resources-device.", - "currentSource": "None — net-new v3 work; no pre-ADR45 baseline equivalent", - "reuseSource": null, - "reuseAction": "net-new", - "destination": "`d2b-contracts` neutral `SecurityKeyEffectPort` trait/types; `packages/d2b-provider-device-security-key/src/effect_port.rs` re-export; Core adapter implementation in `d2b-provider` or `d2b-provider-toolkit`", - "detailedDesign": "Define/re-export the opaque redacting `SecurityKeyEffectPort` types in the neutral contract crate and implement the Core adapter; inject per physical Device into the Provider controller; relay and projection Service do not receive the port.", - "integration": "Core resolves Zone/label to opaque IDs and injects the port into the controller; controller scheduled-observe calls the trait; Provider crate depends only on the neutral contract/re-export; relay path is unaffected.", - "dataMigration": "Full d2b 3.0 reset; no v2 effect-port state import", - "validation": "Unit tests assert Debug redaction, controller calls `observe_inventory` with injected IDs, relay has no port dependency, and fake Core adapter returns bounded `InventoryObservation`.", - "removalProof": "None — net-new; no prior owner to remove" - }, - { - "workItemId": "W-N19", - "specId": "ADR-046-provider-device-security-key", - "specPath": "docs/specs/providers/ADR-046-provider-device-security-key.md", - "dependencyOwner": "Device-security-key Service/Binding implementation owner; depends on ADR046-provider-004, W-N01, W-R05, resource object/Device/D096/D097 contracts.", - "currentSource": "None — net-new v3 work; no pre-ADR45 baseline equivalent", - "reuseSource": null, - "reuseAction": "net-new", - "destination": "`packages/d2b-provider-device-security-key/src/{resource_type,provider_extension,admission}.rs`; controller contracts; system-core Guest UHID authority-subresource DeviceGrant (common base lives under ADR046-provider-004)", - "detailedDesign": "Bind the shared semantic authority/projection Service and Binding base versions/fingerprints from ADR046-provider-004, then define only the initial strict Provider extension and admission. The owner/Binding extension references the local physical Device/relay Endpoint and owns CTAPHID/fairness/frontend settings and observations. Projection is Core-owned by ResourceImport with `providerRef` plus semantic base/import fields, no `spec.provider`, and no Device/open; routing derives from the signed local descriptor, `providerRef`, and import record. Binding is operator intent and the initial extension realizes its frontend Process/private Endpoint. Standard Device remains physical only; provider-named ResourceType aliases are rejected.", - "integration": "ResourceExport targets Service; ResourceImport creates projection Service; Binding references same-Zone Service; Core injects Guest UHID without a Device row.", - "dataMigration": "Full d2b 3.0 reset; no legacy Device/claim projection import", - "validation": "Fast schema/lifecycle conformance consumes the ADR046-provider-004 fixtures, accepts canonical minimal base without `spec.provider`, includes a fake alternate security-key Provider, and proves Device→provider-neutral Service→export→import→projection Service→provider-neutral Binding→frontend, projection `spec.provider` rejection, D088 status layering, strict base/Provider-extension separation, exact types with no aliases, strict ownership/finalizers, no Device projection, and no local hidraw open in consumer Zone.", - "removalProof": "Supersedes legacy frontend/import Device modeling; W-X06 removes udev mutation and W-X03 removes the legacy unit once Binding-owned realization is live." - }, - { - "workItemId": "W-N20", - "specId": "ADR-046-provider-device-security-key", - "specPath": "docs/specs/providers/ADR-046-provider-device-security-key.md", - "dependencyOwner": "Provider state/status contract owner; depends on W-N01, W-N19, and ADR-046-provider-state.", - "currentSource": "None — net-new v3 work; no pre-ADR45 baseline equivalent", - "reuseSource": null, - "reuseAction": "net-new", - "destination": "Provider descriptor state declaration, controller/status logic, Process templates, and Nix principal provisioning for `Provider/device-security-key`", - "detailedDesign": "Empty ProviderStateSet and strict bounded status schemas: physical presence in Device `status.resource`; semantic authority/import aggregates in Service `status.resource`; attachment aggregates in Binding `status.resource`; initial physical-backing claim, relay, Endpoint, queue, and ceremony observations only in `status.provider`. No semantic field appears directly under `status`, and Core projections contain no `spec.provider`. Ceremony rows remain high-churn non-Resource session records; CTAP/fd/LeaseId/CID data stays transient. No Process has `/state`.", - "integration": "W-N10 signs schemas; W-N02 writes resource-local status; Core Operation/session/audit surfaces own bounded records; Volume controllers see no request.", - "dataMigration": "Full d2b 3.0 reset; no v2 state/config import", - "validation": "`status_binding.rs` proves empty ProviderStateSet, no `/state` mounts, no Volume API calls, authority/import/attachment fields only under `status.resource`, implementation fields only under `status.provider`, no projection `spec.provider`, and no CTAP/fd/session secrets in status/log/audit/metrics.", - "removalProof": "None — net-new; no prior owner to remove" - }, - { - "workItemId": "W-N21", - "specId": "ADR-046-provider-device-security-key", - "specPath": "docs/specs/providers/ADR-046-provider-device-security-key.md", - "dependencyOwner": "Cross-Zone adapter owner; depends on W-N17, W-N19, W-N22, ADR046-zone-control-019, and ADR046-zone-control-020.", - "currentSource": "None — net-new ADR 0046 cross-Zone sharing (D096)", - "reuseSource": null, - "reuseAction": "net-new (implement the signed security-key export/import adapter)", - "destination": "`packages/d2b-provider-device-security-key/src/share_adapter.rs`", - "detailedDesign": "Signed adapters admit ResourceExport only for an authority SecurityKeyService. Core invokes the signed semantic factory to create one projection SecurityKeyService with `ownerRef: ResourceImport/`, `providerRef`, semantic base/import fields, and no `spec.provider`; route selection comes from the signed local descriptor and ResourceImport record. The semantic factory fingerprint binds factory metadata plus projection-protocol version only, while adapter identity is authenticated separately by the signed Provider descriptor. They never project Device or auto-create Binding. Route Binding ceremonies over bounded encrypted named streams to the single authority fair queue; no FD/USBIP/hidraw/ref crosses Zones.", - "integration": "Core export/import routing/projection lifecycle; W-N22 authority; W-N17 Endpoint streams; Nix/operator-authored Binding consumes the same-Zone projection.", - "dataMigration": "Full d2b 3.0 reset; no cross-Zone sharing state", - "validation": "Fast fake-stream conformance proves owner Service→export→import→projection Service→Binding→frontend, rejection of projection `spec.provider`, semantic factory-fingerprint stability when signed adapter identity changes, separate signed-descriptor identity authentication, one fair LeaseId-guarded ceremony, ciphertext to intermediaries, no Device projection/local hidraw/FD/USBIP, revocation degradation, and audit metadata only.", - "removalProof": "Not applicable (new surface)" - }, - { - "workItemId": "W-N22", - "specId": "ADR-046-provider-device-security-key", - "specPath": "docs/specs/providers/ADR-046-provider-device-security-key.md", - "dependencyOwner": "D097 authority foundation owner; depends on W-R01, W-R02, W-R03, W-R04, W-N11, W-N19, ADR046-zone-control-024, and the D097 authority contract.", - "currentSource": "`packages/d2bd/src/security_key.rs` (`CidTranslator`, `SecurityKeyState`, `LeaseId`/`LeaseState`, `CEREMONY_TIMEOUT` 120 s, `QUEUE_WAIT_TIMEOUT` 15 s, `parse_ctaphid_report`/`build_cancel_packet`); `packages/d2b-priv-broker/src/ops/security_key.rs` (`live_open_hidraw_security_key`, double `fstat` + FIDO usage-page 0xF1D0 + HID raw-info revalidation, `O_RDWR\\|O_NONBLOCK\\|O_NOFOLLOW`); `packages/d2b-sk-frontend/src/{main,uhid,vsock,framing}.rs` (UHID FIDO2 CTAPHID frontend, 64-byte report relay)", - "reuseSource": "Same baseline daemon/broker/frontend symbols", - "reuseAction": "`adapt` — relay becomes the D097 hidraw authority; transport moves to Endpoint/named-stream", - "destination": "`packages/d2b-provider-device-security-key/src/{authority,relay,streams}.rs`; D097 `AuthorityDescriptor` on authority SecurityKeyService", - "detailedDesign": "The provider-neutral authority Service, not Device/Endpoint/Process, is the stable D097 owner and carries the semantic opaque Host-scoped zero-or-one descriptor. The initial Provider extension references the local physical Device and relay Endpoint and supplies service-specific physical-key derivation, Service+relay ownerProof, and bounded-fairness details. After trusted USB identity resolution, Core additionally derives `physical-usb-backing/v1` and atomically claims the exact `(Host, physical-usb-backing, opaqueKeyDigest)` tuple used by every USB Provider before any open, withhold, bind, module, relay, or attachment effect; Provider-private claims cannot replace it. Preserve sole Core open with double-fstat/FIDO/HID validation, async fd I/O, per-session CidTranslator, LeaseId stale-release guard, cancel-all-CIDs, one ceremony, bounded FIFO wait, and Binding-owned UHID frontend. Ceremony rows are not Resources.", - "integration": "Authority Service owns relay/Endpoint; Core index admits it and LaunchTicket supplies physical DeviceGrant; W-N21 exports/imports Service; Binding owns frontend/private Endpoint; USBIP conflict remains Host-wide.", - "dataMigration": "Full d2b 3.0 reset; no per-session/lease state persisted", - "validation": "Fast hermetic tests adapt the existing `CidTranslator`/lease/cancel/UHID/broker-revalidation suites: CID alloc/translate/release, `LeaseId` stale-release, cancel-all-CIDs on disconnect, one-ceremony + 120 s timeout, 15 s fair-wait `ERR_CHANNEL_BUSY`, UHID frame round-trip, broker double-`fstat`+FIDO+HID revalidation, byte-identical USB/security-key backing tuple derivation for one fake token, and `physical-usb-backing-conflict` before effects under alternate labels/private authority classes — all with fakes/`FakeEffectPort`, no real hidraw. Integration proves cross-Zone CTAP ceremony **serialization** over the encrypted named stream and the shared physical USB collision.", - "removalProof": "The legacy daemon accept loop, raw CTAPHID framing, fixed `SK_VSOCK_PORT`, and broker sysfs `/sys/class/hidraw/` scan fallback are deleted only after the relay `Endpoint`/named-stream successor and the `device_token`-only broker open are green (coordinated with W-X05 `ProcessRole` removal and the W-R broker-op revalidation item)." - }, - { - "workItemId": "W-R01", - "specId": "ADR-046-provider-device-security-key", - "specPath": "docs/specs/providers/ADR-046-provider-device-security-key.md", - "dependencyOwner": "ADR-046 provider-device-security-key session/relay owner; depends on W-N01 and the ComponentSession/Process contracts.", - "currentSource": "`packages/d2bd/src/security_key.rs` — baseline internal `SecurityKeyState` (renamed `RelaySessionTable` in v3 so state terminology remains reserved for Resource status), `LeaseState`, `LeaseId`, `CidTranslator`, `try_acquire_lease`, `release_lease`, `CEREMONY_TIMEOUT`, `QUEUE_WAIT_TIMEOUT` (implemented-and-reachable)", - "reuseSource": null, - "reuseAction": "extract and adapt", - "destination": "Move to `packages/d2b-provider-device-security-key/src/session.rs` and `cid.rs`; adapt to Provider Process model (remove daemon Mutex wrapping, add async relay protocol)", - "detailedDesign": "Extract the baseline lease/session constants and CID mapping into provider-local modules. Preserve `LeaseId` stale-release protection, cancel-all-active-CIDs, `CEREMONY_TIMEOUT`, `QUEUE_WAIT_TIMEOUT`, and bounded fair queue semantics; remove daemon-global `Mutex` ownership; keep the authority relay's DeviceGrant/OFD lease for its lifetime. Ceremony rows remain high-churn session records, never Resources.", - "integration": "W-N03 owns the relay loop; W-N04/W-N05 consume this extracted foundation; W-N02 consumes lifecycle events and writes bounded Service/Binding observations; ComponentSession/encrypted named streams carry CTAPHID bytes.", - "dataMigration": "Full d2b 3.0 reset; no v2 state/config import", - "validation": "`session_state_machine.rs`, `session_ring.rs`, `cancel_propagation.rs`, `session_timeout.rs`, `fair_queue.rs`, and `cid_isolation.rs` verify queue/active/completed/timeout transitions, ring eviction, LeaseId stale-release denial, cancel-all-CIDs, fair timeout, and per-session CID isolation with no daemon-global lease state or ceremony Resource.", - "removalProof": "W-X01 deletes the superseded daemon-internal `packages/d2bd/src/security_key.rs` `SecurityKeyState`, `LeaseState`, `SkRegistry`, and accept-loop ownership after the provider relay/session tests pass." - }, - { - "workItemId": "W-R02", - "specId": "ADR-046-provider-device-security-key", - "specPath": "docs/specs/providers/ADR-046-provider-device-security-key.md", - "dependencyOwner": "ADR-046 provider-device-security-key relay extraction owner; depends on W-N01, W-R01, and the frozen ComponentSession/Endpoint contracts.", - "currentSource": "`packages/d2bd/src/security_key.rs` — CTAPHID relay loop, `SkAcceptHandle`, `relay_one_ceremony` (implemented-and-reachable)", - "reuseSource": null, - "reuseAction": "extract and adapt", - "destination": "Move to `packages/d2b-provider-device-security-key/src/relay.rs`; replace daemon-internal Unix socket proxy with ComponentSession over the owned Service Endpoint", - "detailedDesign": "Extract the CTAPHID ceremony relay behavior into the provider relay binary. Preserve one-ceremony-at-a-time proxy semantics and CTAPHID cancel handling, but replace daemon-internal Unix socket proxying with the `d2b.security-key.v3` ComponentSession over the owned CTAPHID Endpoint and named `ctaphid` stream.", - "integration": "Core launches the relay Process with a LaunchTicket DeviceGrant and Endpoint attachment; transport-vsock resolves `Endpoint/-ctaphid-relay`; frontend Process connects as ComponentSession initiator; controller receives session events over the manifest-declared internal channel.", - "dataMigration": "Full d2b 3.0 reset; no v2 state/config import", - "validation": "`host_relay_guest_frontend/` integration fixture, `device_grant_no_path.rs`, `descriptor_validation.rs`, and `cancel_propagation.rs` prove relay fd injection, ComponentSession transport, cancel propagation, and absence of daemon-internal socket proxying.", - "removalProof": "W-X01 and W-X02 remove `start_sk_accept_loop`, `SkAcceptHandle`, `relay_one_ceremony`, and the daemon-internal Unix socket proxy bind from `packages/d2bd/src/security_key.rs` and `packages/d2bd/src/lib.rs`." - }, - { - "workItemId": "W-R03", - "specId": "ADR-046-provider-device-security-key", - "specPath": "docs/specs/providers/ADR-046-provider-device-security-key.md", - "dependencyOwner": "ADR-046 provider-device-security-key frontend extraction owner; depends on W-N01 and frozen Process/ComponentSession contracts.", - "currentSource": "`packages/d2b-sk-frontend/src/` — `main.rs`, `uhid.rs` (implemented-and-reachable); `framing.rs` and `vsock.rs` are obsolete under v3 (replaced by ComponentSession transport)", - "reuseSource": null, - "reuseAction": "extract and adapt", - "destination": "Adopt `main.rs` and `uhid.rs` as the v3 Process binary entry point; replace `framing.rs`/`vsock.rs` with ComponentSession client from `d2b-session-unix/src/vsock.rs`; wire as Process service in Provider crate", - "detailedDesign": "Retain UHID creation and frontend entry behavior, but run it as a Binding-owned v3 user-domain Process receiving a pre-opened `/dev/uhid` fd from the `Provider/system-core` Guest-substrate DeviceGrant. Delete raw frame/vsock protocol and use the ComponentSession client/named `ctaphid` stream. No virtual/projected Device exists.", - "integration": "W-N19 defines Service/Binding ownership; W-N13/N17 wire the Binding-owned frontend Process/private Endpoint and same-Zone Service resolution; Core injects UHID from the Guest substrate.", - "dataMigration": "Full d2b 3.0 reset; no frontend session state import", - "validation": "`host_relay_guest_frontend/`, `device_grant_no_path.rs`, `descriptor_validation.rs`, and guest Nix migration tests prove UHID fd injection, no `/dev/uhid` path, ComponentSession client use, and no raw `framing.rs`/`vsock.rs` protocol.", - "removalProof": "W-X03 removes the legacy `d2b-sk-frontend.service` unit declaration, and the v3 frontend excludes the obsolete `packages/d2b-sk-frontend/src/framing.rs` and `vsock.rs` raw transport behavior." - }, - { - "workItemId": "W-R04", - "specId": "ADR-046-provider-device-security-key", - "specPath": "docs/specs/providers/ADR-046-provider-device-security-key.md", - "dependencyOwner": "Core LaunchTicket/privileged broker reuse owner; depends on ADR-046-resources-device and W-N06 probe/device-token population.", - "currentSource": "`packages/d2b-priv-broker/src/ops/security_key.rs` — `live_open_hidraw_security_key`, FIDO usage page revalidation, group validation, `ALLOWED_GROUPS` (implemented-and-reachable)", - "reuseSource": null, - "reuseAction": "adapt", - "destination": "Preserve revalidation logic; update `SecurityKeyOpenDevice` to use bundle device table `device_token` as sole open target (no iterative sysfs scan); add zone-field handling; remove sysfs fallback. **Core's LaunchTicket calls this internally; the Provider does not call it.**", - "detailedDesign": "Keep the FIDO usage-page and post-open revalidation logic, but make the trusted private bundle `device_token` the only open target. Add Zone-aware request handling, reject path/sysfs fallback inputs, and keep the operation internal to Core LaunchTicket DeviceGrant resolution rather than callable by the Provider controller.", - "integration": "Provider activation records label-to-`device_token` mappings; Core LaunchTicket resolves `deviceUsage` for the relay Process; broker opens and revalidates the hidraw fd; relay receives only an inherited fd; Core emits path-free `device-grant` audit.", - "dataMigration": "Full d2b 3.0 reset; no v2 device state import", - "validation": "`packages/d2b-priv-broker/tests/security_key_broker.rs` updates for bundle table lookup and zone-field round trip; `device_grant_no_path.rs` proves Provider code does not call the broker and sees no device path; audit tests prove path-free grant records.", - "removalProof": "The superseded iterative sysfs scan/fallback behavior in `packages/d2b-priv-broker/src/ops/security_key.rs` is removed once bundle-token lookup and revalidation tests pass." - }, - { - "workItemId": "W-R05", - "specId": "ADR-046-provider-device-security-key", - "specPath": "docs/specs/providers/ADR-046-provider-device-security-key.md", - "dependencyOwner": "`d2b-contracts` security-key ceremony/effect DTO owner; depends on ADR046-provider-004, ADR-046-resource-object-model, ADR-046-resources-device, and W-N01.", - "currentSource": "`packages/d2b-contracts/src/security_key.rs` — `SecurityKeySessionId`, `SecurityKeyDeviceLabel`, `SecurityKeySession`, `SecurityKeySessionResult`, `SecurityKeyStatusResponse`, `SecurityKeySessionsResponse`, `SecurityKeyOpenDeviceRequest`, `SecurityKeyEvent` (implemented-and-reachable)", - "reuseSource": null, - "reuseAction": "adapt", - "destination": "Adapt to v3 Zone/ResourceRef identifiers; preserve serde shapes for zero downstream breakage where possible; remove `SecurityKeyApplyUdevRulesRequest` (W-X06)", - "detailedDesign": "Rebase wire DTOs onto v3 Zone/ResourceRef identifiers; consume the shared ADR046-provider-004 `security-key.d2bus.org` Service/Binding bases and define only strict `device-security-key.d2bus.org` Provider-extension DTOs; reject `spec.provider` on Core projections; place authority/import/attachment semantic observations only under `status.resource` and implementation observations only under `status.provider`; preserve opaque bounded ceremony records as non-Resource DTOs; add `zone` to `SecurityKeyOpenDeviceRequest`; drop the udev-rules request because UHID comes from the Guest-substrate DeviceGrant. No provider-named ResourceType alias is admitted.", - "integration": "Core LaunchTicket, broker open op, Provider controller Service/Binding status/audit, CLI session readers, and provider tests consume the v3 DTOs.", - "dataMigration": "Full d2b 3.0 reset; no v2 DTO compatibility migration beyond serde-shape preservation where possible", - "validation": "DTO serde round trips, exact provider-neutral ResourceType identity, provider-named alias rejection, canonical minimal base acceptance, Core projection `spec.provider` rejection, D088 `status.resource`/`status.provider` layering, base/Provider-extension field separation, unknown-field denial, zone-field round trip, path-redaction tests, and updated `usb_sk_contract.rs` assertions in the provider crate.", - "removalProof": "W-X06 removes `SecurityKeyApplyUdevRulesRequest`, the `SecurityKeyApplyUdevRules` broker op, and related broker code after UHID DeviceGrant coverage is live." - }, - { - "workItemId": "W-R06", - "specId": "ADR-046-provider-device-security-key", - "specPath": "docs/specs/providers/ADR-046-provider-device-security-key.md", - "dependencyOwner": "Provider crate test owner; depends on W-R05 v3 DTOs and W-N01 provider crate layout.", - "currentSource": "`packages/d2b-contract-tests/tests/usb_sk_contract.rs` — DTO serde round-trips, unknown-field denial, broker capability set (implemented-and-reachable)", - "reuseSource": null, - "reuseAction": "move and adapt", - "destination": "Move to `packages/d2b-provider-device-security-key/tests/`; update imports and v3 type names", - "detailedDesign": "Move the reusable semantic assertions for security-key DTO serde, unknown-field denial, and broker capability shape into the provider crate's hermetic `tests/` suite, updating imports and names to the v3 contract modules without weakening assertions.", - "integration": "`cargo test -p d2b-provider-device-security-key --lib --tests` runs the moved contract tests with the provider's DTO/controller test matrix; old contract-test manifests point to the successor coverage before deletion.", - "dataMigration": "None — test-only move; no runtime state", - "validation": "Moved tests pass under the provider crate; contract assertions are retained; D094 disposition records moved/adapted coverage before old duplicate tests are deleted.", - "removalProof": "W-X04 deletes `packages/d2b-contract-tests/tests/usb_sk_contract.rs` only after the provider-crate successor test covers all prior assertions." - }, - { - "workItemId": "W-R07", - "specId": "ADR-046-provider-device-security-key", - "specPath": "docs/specs/providers/ADR-046-provider-device-security-key.md", - "dependencyOwner": "Provider crate test/minijail adaptation owner; depends on W-N01 and the frozen Process sandbox contract.", - "currentSource": "`packages/d2b-contract-tests/tests/minijail_sk_frontend.rs` — minijail profile shape, `ProcessRole::SecurityKeyFrontend` (implemented-and-reachable)", - "reuseSource": null, - "reuseAction": "move and adapt", - "destination": "Move to `packages/d2b-provider-device-security-key/tests/`; update for v3 Process resource minijail profile; retain zero-capabilities assertion", - "detailedDesign": "Move the reusable minijail/sandbox assertions into the provider crate and retarget them from `ProcessRole::SecurityKeyFrontend` to the v3 Process resource templates and relay/controller minijail profiles. Preserve zero-capabilities and seccomp-class assertions while recognizing the frontend uses `Provider/system-systemd` hardening rather than a minijail profile.", - "integration": "Provider tests validate Nix minijail profile entries, Process resource sandbox templates, and system-minijail/system-systemd conformance expectations before old contract tests are retired.", - "dataMigration": "None — test-only move; no runtime state", - "validation": "Provider-crate tests retain zero-capability and seccomp assertions for relay/controller and assert no minijail profile is used for the frontend Process.", - "removalProof": "W-X04 deletes `packages/d2b-contract-tests/tests/minijail_sk_frontend.rs` only after the provider-crate successor test covers all prior assertions." - }, - { - "workItemId": "W-X01", - "specId": "ADR-046-provider-device-security-key", - "specPath": "docs/specs/providers/ADR-046-provider-device-security-key.md", - "dependencyOwner": "Provider-device-security-key removal owner; depends on W-R01, W-R02, W-N03, W-N04, and W-N05 successor relay/session coverage.", - "currentSource": "`packages/d2bd/src/security_key.rs` — `start_sk_accept_loop`, `SecurityKeyState`, `LeaseState`, `SkRegistry`", - "reuseSource": null, - "reuseAction": "delete", - "destination": "Removed from daemon; successor behavior lives in `packages/d2b-provider-device-security-key/src/relay.rs`, `session.rs`, and `cid.rs`", - "detailedDesign": "Remove target `packages/d2bd/src/security_key.rs` — `start_sk_accept_loop`, `SecurityKeyState`, `LeaseState`, `SkRegistry` after v3 relay Process is live and stable; keep behind feature gate only if needed during transition.", - "integration": "d2bd no longer owns security-key accept/session state; Provider controller, authority Service relay, and Binding frontends own lifecycle; Core LaunchTicket owns hidraw/UHID grants.", - "dataMigration": "Full d2b 3.0 reset; no daemon session state migration", - "validation": "Provider relay/session tests pass; daemon build has no references to removed symbols; no legacy security-key accept loop starts under d2bd.", - "removalProof": "Concrete removed path/behavior: `packages/d2bd/src/security_key.rs` `start_sk_accept_loop`, `SecurityKeyState`, `LeaseState`, and `SkRegistry` daemon-internal accept/session ownership are absent." - }, - { - "workItemId": "W-X02", - "specId": "ADR-046-provider-device-security-key", - "specPath": "docs/specs/providers/ADR-046-provider-device-security-key.md", - "dependencyOwner": "d2bd integration removal owner; depends on W-X01.", - "currentSource": "`packages/d2bd/src/lib.rs` — `start_sk_accept_loop` call site and daemon-internal Unix socket proxy bind", - "reuseSource": null, - "reuseAction": "delete", - "destination": "Removed from daemon startup; successor launch path is ProviderDeployment/controller-created relay Process plus Endpoint/ComponentSession transport", - "detailedDesign": "Remove target `packages/d2bd/src/lib.rs` — `start_sk_accept_loop` call site and daemon-internal Unix socket proxy bind after W-X01.", - "integration": "d2bd startup no longer binds a security-key Unix socket proxy; Core/ProviderDeployment starts provider controller and relay Process resources; transport-vsock Endpoint supplies frontend connectivity.", - "dataMigration": "Full d2b 3.0 reset; no daemon socket state migration", - "validation": "d2bd startup tests/build prove no `start_sk_accept_loop` call or security-key proxy bind remains; provider integration test proves CTAPHID flow through Endpoint/ComponentSession.", - "removalProof": "Concrete removed path/behavior: `packages/d2bd/src/lib.rs` no longer calls `start_sk_accept_loop` and no longer binds the daemon-internal security-key Unix socket proxy." - }, - { - "workItemId": "W-X03", - "specId": "ADR-046-provider-device-security-key", - "specPath": "docs/specs/providers/ADR-046-provider-device-security-key.md", - "dependencyOwner": "Guest Nix module removal owner; depends on W-N13, W-R03, and W-N19 Binding frontend/UHID contract.", - "currentSource": "`nixos-modules/components/security-key-guest.nix` — `d2b-sk-frontend.service` systemd unit declaration", - "reuseSource": null, - "reuseAction": "delete", - "destination": "Removed from guest Nix module; successor is Binding-owned `Process/binding--sk-frontend`", - "detailedDesign": "Remove target `nixos-modules/components/security-key-guest.nix` — `d2b-sk-frontend.service` systemd unit declaration after W-N13 migration gate defaults to false.", - "integration": "Guest Nix keeps `uhid` and frontend binary only; Provider controller creates the Binding-owned frontend; system-systemd manages it.", - "dataMigration": "Full d2b 3.0 reset; no legacy unit state migration", - "validation": "Nix eval tests prove no static `d2b-sk-frontend.service` is emitted with Provider installed; frontend Process integration proves replacement lifecycle.", - "removalProof": "Concrete removed path/behavior: `nixos-modules/components/security-key-guest.nix` no longer declares the static `d2b-sk-frontend.service` unit." - }, - { - "workItemId": "W-X04", - "specId": "ADR-046-provider-device-security-key", - "specPath": "docs/specs/providers/ADR-046-provider-device-security-key.md", - "dependencyOwner": "Test-suite migration/removal owner; depends on W-R06 and W-R07 provider-crate successor tests.", - "currentSource": "`packages/d2b-contract-tests/tests/minijail_sk_frontend.rs` and `packages/d2b-contract-tests/tests/usb_sk_contract.rs`", - "reuseSource": null, - "reuseAction": "delete after move/adapt", - "destination": "Removed from `packages/d2b-contract-tests/tests/`; successor tests live in `packages/d2b-provider-device-security-key/tests/`", - "detailedDesign": "Remove target `packages/d2b-contract-tests/tests/minijail_sk_frontend.rs` and `packages/d2b-contract-tests/tests/usb_sk_contract.rs` after W-R06/W-R07 tests are in Provider crate and cover all prior assertions.", - "integration": "D094 disposition updates closed gate manifests, layer1 jobs, pins, ledgers, and CI shards so only the provider-crate successor suite remains.", - "dataMigration": "None — test-only move/delete; no runtime state", - "validation": "Provider-crate tests pass with retained assertions; old contract-test paths are absent from manifests/CI; no duplicate old/new suite runs indefinitely.", - "removalProof": "Concrete removed paths: `packages/d2b-contract-tests/tests/minijail_sk_frontend.rs` and `packages/d2b-contract-tests/tests/usb_sk_contract.rs` are deleted after provider-crate successor coverage passes." - }, - { - "workItemId": "W-X05", - "specId": "ADR-046-provider-device-security-key", - "specPath": "docs/specs/providers/ADR-046-provider-device-security-key.md", - "dependencyOwner": "Core ProcessRole removal owner; depends on W-N09 Process resources, W-N08 sandbox templates, and system-minijail/system-systemd conformance.", - "currentSource": "`ProcessRole::SecurityKeyFrontend` in `d2b-core/src/processes.rs`", - "reuseSource": null, - "reuseAction": "delete", - "destination": "Removed from `d2b-core/src/processes.rs`; successor frontend is a v3 Process resource owned by `Provider/device-security-key`", - "detailedDesign": "Remove target `ProcessRole::SecurityKeyFrontend` in `d2b-core/src/processes.rs` after relay and frontend are v3 Process resources; no other code reference expected.", - "integration": "ProcessRole disposition table confirms all security-key frontend lifecycle, sandbox, readiness, and DeviceGrant semantics are represented by Resource Process templates and Process Providers before enum removal.", - "dataMigration": "Full d2b 3.0 reset; no processes.json role migration", - "validation": "Workspace build proves no `ProcessRole::SecurityKeyFrontend` references; provider Process template tests prove the v3 replacement; process conformance passes.", - "removalProof": "Concrete removed path/behavior: `d2b-core/src/processes.rs` no longer contains `ProcessRole::SecurityKeyFrontend` or a security-key frontend role in the legacy ProcessRole/VmProcessDag model." - }, - { - "workItemId": "W-X06", - "specId": "ADR-046-provider-device-security-key", - "specPath": "docs/specs/providers/ADR-046-provider-device-security-key.md", - "dependencyOwner": "Broker/contracts/Nix removal owner; depends on W-R05, W-N11, W-N13, and W-N19 Guest-substrate UHID replacement.", - "currentSource": "`SecurityKeyApplyUdevRules` broker op, `SecurityKeyApplyUdevRulesRequest` DTO in `packages/d2b-contracts/src/security_key.rs`, and all related broker code", - "reuseSource": null, - "reuseAction": "delete", - "destination": "Removed from contracts and broker; successor access is static guest Nix `uhid` module plus Core pre-opened `/dev/uhid` DeviceGrant for the frontend Process", - "detailedDesign": "Remove `SecurityKeyApplyUdevRules`, its DTO, and related broker code after the Binding-owned frontend and system-core UHID DeviceGrant are live. Guest Nix loads `uhid` but emits no security-key udev rule.", - "integration": "Guest Nix/Process DeviceGrant path provides UHID access; contracts no longer expose the op/request; broker capability set drops the udev mutation; provider/contract tests assert absence.", - "dataMigration": "Full d2b 3.0 reset; no udev rule state migration", - "validation": "DTO unknown-field/capability tests prove `SecurityKeyApplyUdevRulesRequest` and op are absent; `device_grant_no_path.rs` proves frontend has UHID fd without udev/plugdev; broker build has no related code.", - "removalProof": "Concrete removed path/behavior: `SecurityKeyApplyUdevRules` broker operation, `SecurityKeyApplyUdevRulesRequest` in `packages/d2b-contracts/src/security_key.rs`, and related broker code are absent." - }, - { - "workItemId": "WI-001", - "specId": "ADR-046-provider-runtime-qemu-media", - "specPath": "docs/specs/providers/ADR-046-provider-runtime-qemu-media.md", - "dependencyOwner": "P0; blocks all other runtime-qemu-media work items; owner: `runtime-qemu-media` Provider crate", - "currentSource": "None — net-new v3 work; no pre-ADR45 baseline equivalent", - "reuseSource": null, - "reuseAction": "net-new", - "destination": "packages/d2b-provider-runtime-qemu-media/{src/lib.rs,tests/provider_layout.rs,integration/mod.rs,README.md}", - "detailedDesign": "Crate scaffold and layout gate: create the crate with the four required paths, commit a README.md stub meeting §1 requirements, and wire the workspace policy gate so the crate cannot land without `src/`, `tests/`, `integration/`, and `README.md`.", - "integration": "Workspace/Cargo policy consumes the new crate layout; later Guest schema, controller, QMP, Nix, and integration work items build inside this crate.", - "dataMigration": "Full d2b 3.0 reset; no v2 state/config import", - "validation": "`make test-policy` (workspace policy gate)", - "removalProof": "None — net-new; no prior owner to remove" - }, - { - "workItemId": "WI-002", - "specId": "ADR-046-provider-runtime-qemu-media", - "specPath": "docs/specs/providers/ADR-046-provider-runtime-qemu-media.md", - "dependencyOwner": "P0; depends on WI-001; owner: runtime-qemu-media type/schema implementation", - "currentSource": "`packages/d2b-core/src/host.rs` — `HostQemuMedia`, `QemuMediaSourceIntent` field names/types only; raw path/credential fields are discarded", - "reuseSource": null, - "reuseAction": "adapt selected baseline field concepts; discard raw paths, argv, and credential-carrying fields", - "destination": "packages/d2b-provider-runtime-qemu-media/src/types/guest.rs", - "detailedDesign": "Guest ResourceType schema and serde: define `GuestSpec`, `GuestStatus`, and `GuestProviderSpecSettings` with serde and `schemars` JSON Schema. Fields must match §4, §5, and §16 exactly. Enforce `bootMediaRef` as a `Volume/` ResourceRef, `removableVolumeRefs` max 4 entries, `providerPhase` max 64 chars with the closed value set, and no argv/path/credential bytes in any serialized type.", - "integration": "Nix-rendered Guest resources and ResourceAPI admission use these types; the controller consumes the validated spec and writes matching status; conformance and schema tests consume the generated schema.", - "dataMigration": "Full d2b 3.0 reset; media guests are reauthored as `Guest`/`Volume`/`Device` resources rather than importing v2 host media config", - "validation": "`tests/guest_schema_roundtrip.rs`; `tests/guest_provider_settings_bounds.rs`", - "removalProof": "`HostQemuMedia`/`QemuMediaSourceIntent` raw path surfaces are superseded once all media Guest specs use ResourceRefs and schema tests prove no path/argv/credential fields remain" - }, - { - "workItemId": "WI-003", - "specId": "ADR-046-provider-runtime-qemu-media", - "specPath": "docs/specs/providers/ADR-046-provider-runtime-qemu-media.md", - "dependencyOwner": "P0; depends on WI-001; owner: runtime-qemu-media Provider config/schema implementation", - "currentSource": "`packages/d2b-core/src/runtime.rs` — timeout/quota concepts only", - "reuseSource": null, - "reuseAction": "adapt bounded timeout/quota concepts into v3 Provider config; project only to the controller component", - "destination": "packages/d2b-provider-runtime-qemu-media/src/config.rs", - "detailedDesign": "Provider config schema and projection: define `ProviderConfig`, derive JSON Schema, require `controllerExecutionRef`, validate bounds, and project config only to the controller component. Worker processes receive no root config, no ResourceAPI authority, and no d2b-bus authority.", - "integration": "Provider ResourceSpec admission validates this schema; ProviderDeployment injects the projected config into the controller; controller uses the provider refs and quotas when reconciling Guest, Volume, Network, Device, Endpoint, and Process resources.", - "dataMigration": "Full d2b 3.0 reset; no v2 state/config import", - "validation": "`tests/config_schema_projection.rs`", - "removalProof": "None — config projection is a new Provider resource surface; no prior owner is removed by this item" - }, - { - "workItemId": "WI-003B", - "specId": "ADR-046-provider-runtime-qemu-media", - "specPath": "docs/specs/providers/ADR-046-provider-runtime-qemu-media.md", - "dependencyOwner": "P0; depends on WI-001 and WI-003; owner: runtime-qemu-media controller descriptor/state implementation", - "currentSource": "None — net-new v3 work; no pre-ADR45 baseline equivalent", - "reuseSource": null, - "reuseAction": "net-new", - "destination": "packages/d2b-provider-runtime-qemu-media/src/{descriptor.rs,state.rs}; no Volume management code for Provider state", - "detailedDesign": "Controller status-first operational state (no state Volume): controller component descriptor declares an empty `stateNamespaces` list; ProviderDeployment creates no controller state Volume; controller writes reconcile stage, per-Guest launch/adoption observations, bounded counters, and closed-enum error detail to `status` on material change without secrets, paths, argv, PIDs, or unit names; restart re-derives observed state from the Zone resource store, core Operation ledger, and independent external observation with fresh pidfds. Worker Processes and the controller receive no state-Volume mount.", - "integration": "ProviderDeployment reads the descriptor; the controller projects bounded observations to Guest status and the Operation ledger; restart/adoption logic consumes resource-store, ledger, and external runner observations rather than private state storage.", - "dataMigration": "None — status-first controller state only; no runtime state is migrated into a Provider state Volume", - "validation": "`tests/state_status_spec.rs`; `tests/state_status_restart.rs`; `tests/state_mount_exclusivity.rs`", - "removalProof": "None — this item prevents creation of a new Provider state Volume and has no prior state owner to remove" - }, - { - "workItemId": "WI-004", - "specId": "ADR-046-provider-runtime-qemu-media", - "specPath": "docs/specs/providers/ADR-046-provider-runtime-qemu-media.md", - "dependencyOwner": "P1; depends on WI-001 and WI-003; owner: runtime-qemu-media controller Volume reconciliation", - "currentSource": "`packages/d2b-host/src/qemu_media_argv.rs` — `run_dir` and socket naming pattern only; raw path construction is discarded", - "reuseSource": null, - "reuseAction": "adapt naming intent into controller-created Volume resources; replace raw runtime directory paths with `Volume` specs", - "destination": "packages/d2b-provider-runtime-qemu-media/src/controller/volume.rs", - "detailedDesign": "Runtime tmpfs Volume resource: controller creates the per-Guest runtime tmpfs Volume specified in §6.1. The emitted spec must exactly match the canonical YAML, including all layout entries, views, quota, and `cleanupPolicy: vm-stop-with-proof`.", - "integration": "Guest reconcile creates/updates this Volume through the ResourceAPI; `volume-local` materializes the tmpfs and returns attachments to the Process launch flow; finalize proves cleanup before Guest finalization.", - "dataMigration": "Full d2b 3.0 reset; runtime tmpfs state is ephemeral and not imported from v2 run directories", - "validation": "`tests/runtime_volume_spec.rs`; `tests/volume_cleanup_policy.rs`", - "removalProof": "Legacy raw run-directory handling from `qemu_media_argv.rs` is superseded once runtime storage is represented only by controller-created Volume resources" - }, - { - "workItemId": "WI-005", - "specId": "ADR-046-provider-runtime-qemu-media", - "specPath": "docs/specs/providers/ADR-046-provider-runtime-qemu-media.md", - "dependencyOwner": "P1; depends on WI-002 and WI-004; owner: runtime-qemu-media media dependency controller", - "currentSource": "`packages/d2b-core/src/host.rs` `QemuMediaSourceKind` — media kind enumeration only", - "reuseSource": null, - "reuseAction": "adapt media kind concepts to Volume source-kind assertions and ResourceRef watches", - "destination": "packages/d2b-provider-runtime-qemu-media/src/controller/media_watch.rs", - "detailedDesign": "Media Volume watch and virtio-blk attachment validation: controller watches `bootMediaRef` and `removableVolumeRefs` Volumes for `Ready` status and validates that each has a `virtio-blk` attachment for the owning Guest. It performs no path inspection.", - "integration": "Guest reconcile gates Process launch on watched Volume readiness; Volume attachment status feeds LaunchTicket media fd assembly and Guest conditions.", - "dataMigration": "Full d2b 3.0 reset; operator-authored media is declared as Volume resources rather than imported from raw qemu-media source paths", - "validation": "`tests/media_volume_watch.rs`; `tests/media_attachment_validation.rs`", - "removalProof": "Legacy media source path handling is superseded once media is delivered only through Volume ResourceRefs and virtio-blk attachments" - }, - { - "workItemId": "WI-006", - "specId": "ADR-046-provider-runtime-qemu-media", - "specPath": "docs/specs/providers/ADR-046-provider-runtime-qemu-media.md", - "dependencyOwner": "P1; depends on WI-002; owner: runtime-qemu-media Device dependency controller", - "currentSource": "None — net-new v3 work; no pre-ADR45 baseline equivalent", - "reuseSource": null, - "reuseAction": "net-new", - "destination": "packages/d2b-provider-runtime-qemu-media/src/controller/device_watch.rs", - "detailedDesign": "KVM Device watch: controller watches `Device/host-kvm` from `spec.deviceAttachments` for `Ready` status and gates runner launch on it, propagating Pending/Ready/Failed transitions to Guest conditions.", - "integration": "Device resource status drives Guest reconcile dependency gating; a Ready KVM Device contributes the sealed kvm fd slot to the LaunchTicket through the Process provider chain.", - "dataMigration": "Full d2b 3.0 reset; no v2 state/config import", - "validation": "`tests/kvm_device_watch.rs`", - "removalProof": "None — Device-gated KVM readiness is a new v3 Resource dependency, not a removal item" - }, - { - "workItemId": "WI-007", - "specId": "ADR-046-provider-runtime-qemu-media", - "specPath": "docs/specs/providers/ADR-046-provider-runtime-qemu-media.md", - "dependencyOwner": "P1; depends on WI-002 and the `display-wayland` Provider dossier; owner: runtime-qemu-media display integration", - "currentSource": "None — net-new v3 work; no pre-ADR45 baseline equivalent", - "reuseSource": null, - "reuseAction": "net-new against the display-wayland Resource contract", - "destination": "packages/d2b-provider-runtime-qemu-media/src/controller/display.rs", - "detailedDesign": "WaylandSession resource management: when `spec.provider.settings.displayWindow = true`, controller creates, updates, deletes, and watches a `display-wayland.d2bus.org.WaylandSession` resource using the exact ResourceSpec from the display-wayland dossier. It reads the EndpointRef attachment from status using only display-wayland-defined field names.", - "integration": "Guest reconcile produces WaylandSession resources; display-wayland publishes Endpoint attachments; LaunchTicket assembly consumes the display fd only when the session is Ready; finalize deletes the session.", - "dataMigration": "Full d2b 3.0 reset; no v2 state/config import", - "validation": "`tests/wayland_session_create.rs`; `tests/wayland_session_attachment_read.rs`; `tests/wayland_session_missing_provider.rs`", - "removalProof": "None — display proxy work is delegated to a new WaylandSession Resource dependency rather than removing a baseline owner in this item" - }, - { - "workItemId": "WI-008", - "specId": "ADR-046-provider-runtime-qemu-media", - "specPath": "docs/specs/providers/ADR-046-provider-runtime-qemu-media.md", - "dependencyOwner": "P1; depends on WI-002, WI-004, WI-005, WI-006, WI-007, and WI-011; owner: runtime-qemu-media Process launch builder", - "currentSource": "`packages/d2b-host/src/qemu_media_argv.rs` fd-index arg shape; `packages/d2b-core/src/processes.rs` `ProcessRole::QemuMediaRunner` sandbox/budget baseline", - "reuseSource": null, - "reuseAction": "adapt fd-slot and sandbox/budget concepts to canonical Process resources and sealed LaunchTickets; do not copy raw argv strings or path construction", - "destination": "packages/d2b-provider-runtime-qemu-media/src/controller/process_builder.rs", - "detailedDesign": "Process spec builder and LaunchTicket assembly: build the canonical `qemu-media-runner` Process ResourceSpec from §10.1 and assemble the LaunchTicket with sealed fd slots for kvm, tap, media, and optional display fds. No raw path, argv, executable path, or principal appears in any public field.", - "integration": "Controller emits Process resources; system-minijail/Process Provider consumes the spec and LaunchTicket; QEMU runner receives only sealed fds; Endpoint resources represent QMP/serial connections.", - "dataMigration": "Full d2b 3.0 reset; existing QEMU runner process state is not imported and launch state is rebuilt from resources", - "validation": "`tests/process_spec_golden.rs`; `tests/launch_ticket_fd_slots.rs`; `tests/no_raw_argv_in_spec.rs`", - "removalProof": "`ProcessRole::QemuMediaRunner` and raw qemu-media argv launch surfaces are removable after canonical Process specs and LaunchTickets cover every runner launch" - }, - { - "workItemId": "WI-009", - "specId": "ADR-046-provider-runtime-qemu-media", - "specPath": "docs/specs/providers/ADR-046-provider-runtime-qemu-media.md", - "dependencyOwner": "P1; depends on WI-008; owner: runtime-qemu-media QMP client implementation", - "currentSource": "`packages/d2b-host/src/media.rs` QMP command set; `packages/d2b-contracts/src/broker_wire.rs` `QemuMedia*` command payload shapes only", - "reuseSource": null, - "reuseAction": "adapt QMP command payloads to internal DTOs; discard broker wire ops and all socket path/fd-open code", - "destination": "packages/d2b-provider-runtime-qemu-media/src/qmp/", - "detailedDesign": "QMP endpoint attachment handling: consume `qmp` and `serial` Endpoint connection attachments delivered by the ProviderSupervisor ComponentSession channel; implement QMP capability negotiation, command dispatch, and health check using only the delivered fd, never direct socket path access.", - "integration": "Process Provider publishes Endpoint attachments; the controller QMP client consumes those fds through ComponentSession; Guest status and health checks reflect QMP outcomes.", - "dataMigration": "Full d2b 3.0 reset; no v2 QMP socket path/session state is imported", - "validation": "`tests/qmp_capability_negotiation.rs`; `tests/qmp_command_dispatch.rs`; `tests/qmp_greeting_timeout.rs`; `tests/qmp_health_check.rs`", - "removalProof": "`QemuMedia*` broker wire operations are superseded as public control surfaces once QMP is driven solely through Endpoint attachments and internal DTOs" - }, - { - "workItemId": "WI-010", - "specId": "ADR-046-provider-runtime-qemu-media", - "specPath": "docs/specs/providers/ADR-046-provider-runtime-qemu-media.md", - "dependencyOwner": "P2; depends on WI-005 and WI-009; owner: runtime-qemu-media hotplug controller", - "currentSource": "`packages/d2b-contracts/src/broker_wire.rs` `QemuMediaAttach` and `QemuMediaDetach` command bodies only", - "reuseSource": null, - "reuseAction": "adapt QMP hotplug command bodies; delete broker op wiring", - "destination": "packages/d2b-provider-runtime-qemu-media/src/controller/hotplug.rs", - "detailedDesign": "Hotplug attach/detach protocol: on `removableVolumeRefs` update, request a Volume fd from the `volume-local` ComponentSession service and issue `blockdev-add`/`device_add` QMP commands; reverse the sequence for detach; QMP failures set Degraded with `hotplug-media-failed`.", - "integration": "Guest spec updates trigger controller reconcile; volume-local supplies media fds; QMP client executes attach/detach; Guest status records hotplug outcomes.", - "dataMigration": "Full d2b 3.0 reset; removable media hotplug state is reconciled from Guest spec and Volume status, not imported from broker op history", - "validation": "`tests/hotplug_attach_sequence.rs`; `tests/hotplug_detach_sequence.rs`; `tests/hotplug_qmp_failure.rs`", - "removalProof": "`QemuMediaAttach`/`QemuMediaDetach` broker operations are removed after hotplug is implemented through Volume fd acquisition plus QMP Endpoint dispatch" - }, - { - "workItemId": "WI-011", - "specId": "ADR-046-provider-runtime-qemu-media", - "specPath": "docs/specs/providers/ADR-046-provider-runtime-qemu-media.md", - "dependencyOwner": "P1; depends on WI-002 and Provider config `networkProviderRef`; owner: runtime-qemu-media network dependency integration", - "currentSource": "None — net-new v3 work; no pre-ADR45 baseline equivalent", - "reuseSource": null, - "reuseAction": "net-new against the `network-local` ComponentSession contract", - "destination": "packages/d2b-provider-runtime-qemu-media/src/controller/network.rs", - "detailedDesign": "Network tap fd acquisition: call the `network-local` ComponentSession service to request a tap fd for a Guest MAC/bridge assignment and include the fd in the LaunchTicket. No bridge name or interface name appears in any public field.", - "integration": "Guest networkAttachments drive requests to network-local; network-local returns a sealed tap fd; Process LaunchTicket carries the fd to the QEMU runner; Guest conditions report unavailable taps.", - "dataMigration": "Full d2b 3.0 reset; no v2 state/config import", - "validation": "`tests/tap_fd_acquisition.rs`; `tests/tap_fd_unavailable.rs`", - "removalProof": "None — tap fd acquisition through `network-local` is a new v3 dependency path" - }, - { - "workItemId": "WI-012", - "specId": "ADR-046-provider-runtime-qemu-media", - "specPath": "docs/specs/providers/ADR-046-provider-runtime-qemu-media.md", - "dependencyOwner": "P1; depends on WI-004 through WI-011; owner: runtime-qemu-media controller", - "currentSource": "None — net-new v3 work; no pre-ADR45 baseline equivalent", - "reuseSource": null, - "reuseAction": "net-new reconcile/finalize implementation using the v3 Resource API", - "destination": "packages/d2b-provider-runtime-qemu-media/src/controller/reconcile.rs", - "detailedDesign": "Reconcile loop and finalize: implement the full async reconcile loop from §11.3 and finalize sequence from §11.4, including dependency gating, providerPhase transitions, condition management, runner exit handling, and WaylandSession cleanup.", - "integration": "Resource watches feed the controller; the controller creates/updates/deletes Volume, WaylandSession, Endpoint, and Process resources; Guest status and finalizers expose lifecycle outcomes to core and CLI.", - "dataMigration": "Full d2b 3.0 reset; lifecycle state is re-derived from Resource specs/status and Operation ledger rather than imported from v2 daemon state", - "validation": "`tests/reconcile_dependency_gating.rs`; `tests/reconcile_runner_exit_handling.rs`; `tests/finalize_sequence.rs`; `tests/finalize_wayland_session_cleanup.rs`", - "removalProof": "Legacy daemon-owned qemu-media lifecycle paths can be removed once reconcile/finalize owns all Guest lifecycle transitions" - }, - { - "workItemId": "WI-013", - "specId": "ADR-046-provider-runtime-qemu-media", - "specPath": "docs/specs/providers/ADR-046-provider-runtime-qemu-media.md", - "dependencyOwner": "P1; depends on WI-012; owner: runtime-qemu-media status/error implementation", - "currentSource": "None — net-new v3 work; no pre-ADR45 baseline equivalent", - "reuseSource": null, - "reuseAction": "net-new status/error projection for the v3 Guest ResourceType", - "destination": "packages/d2b-provider-runtime-qemu-media/src/controller/status.rs", - "detailedDesign": "Status, conditions, and error reporting: implement all phase transitions from §16.1, providerPhase values from §16.2, condition types from §16.3, error codes from §16.4, and bounds enforcement on `providerPhase`.", - "integration": "Controller reconcile writes Guest status; ResourceAPI stores bounded status; CLI/support tooling reads status without paths, argv, fds, socket paths, VM names as labels, or secret material.", - "dataMigration": "None — status schema is new v3 observation state; no v2 status import", - "validation": "`tests/status_phase_transitions.rs`; `tests/condition_reason_codes.rs`", - "removalProof": "None — this item adds v3 status projection and does not by itself remove a prior owner" - }, - { - "workItemId": "WI-014", - "specId": "ADR-046-provider-runtime-qemu-media", - "specPath": "docs/specs/providers/ADR-046-provider-runtime-qemu-media.md", - "dependencyOwner": "P2; depends on WI-012 and WI-013; owner: runtime-qemu-media audit integration", - "currentSource": "None — net-new v3 work; no pre-ADR45 baseline equivalent", - "reuseSource": null, - "reuseAction": "net-new audit emission for the Provider events in §17", - "destination": "packages/d2b-provider-runtime-qemu-media/src/audit.rs", - "detailedDesign": "Audit event emission: emit all audit events in §17 and verify that no sensitive fields such as paths, argv, fds, or socket paths appear in any payload.", - "integration": "Controller lifecycle and QMP/hotplug operations call audit helpers; the audit subsystem records bounded event kinds and outcomes; support tooling consumes redacted payloads.", - "dataMigration": "None — audit-only work; no runtime state import", - "validation": "`tests/audit_event_shapes.rs`; `tests/audit_no_sensitive_fields.rs`", - "removalProof": "None — audit helpers are new for this Provider; no prior owner to remove" - }, - { - "workItemId": "WI-015", - "specId": "ADR-046-provider-runtime-qemu-media", - "specPath": "docs/specs/providers/ADR-046-provider-runtime-qemu-media.md", - "dependencyOwner": "P2; depends on WI-012 and WI-013; owner: runtime-qemu-media telemetry integration", - "currentSource": "None — net-new v3 work; no pre-ADR45 baseline equivalent", - "reuseSource": null, - "reuseAction": "net-new telemetry emission for the Provider metrics and spans in §18", - "destination": "packages/d2b-provider-runtime-qemu-media/src/telemetry.rs", - "detailedDesign": "Metrics and OTEL spans: implement all metrics from §18 and OTEL trace spans with label cardinality enforcement and no VM name, user identity, path, or other sensitive value in any label or attribute.", - "integration": "Controller, QMP, hotplug, and dependency-watch paths call telemetry helpers; OTEL/metrics exporters consume only closed, bounded labels for support dashboards.", - "dataMigration": "None — telemetry-only work; no runtime state import", - "validation": "`tests/metrics_label_cardinality.rs`; `tests/otel_span_attributes.rs`", - "removalProof": "None — telemetry helpers are new for this Provider; no prior owner to remove" - }, - { - "workItemId": "WI-016", - "specId": "ADR-046-provider-runtime-qemu-media", - "specPath": "docs/specs/providers/ADR-046-provider-runtime-qemu-media.md", - "dependencyOwner": "P1; depends on WI-002 and WI-003; owner: Nix resource compiler and runtime-qemu-media options", - "currentSource": "`nixos-modules/components/qemu-media.nix` option names only; `nixos-modules/assertions.nix` assertion framework", - "reuseSource": null, - "reuseAction": "adapt option names into v3 Guest/Provider spec fields; remove raw path options; extend existing assertion predicates", - "destination": "nixos-modules/options-guest-qemu-media.nix; nixos-modules/assertions.nix", - "detailedDesign": "Nix module and assertions: implement the Guest resource declaration from §19 and eval-time assertions from §19.8. Rewrite qemu-media options as v3 spec fields and reject raw path options.", - "integration": "Nix authoring emits Provider, Guest, Volume, and Device resource JSON; assertions fail invalid configs before build; emitted resources feed ResourceAPI admission and controller reconcile.", - "dataMigration": "Full d2b 3.0 reset; users reauthor qemu-media configuration as v3 resources and raw path options are not imported", - "validation": "`tests/unit/nix/cases/guest-qemu-media-spec.nix`; `tests/assertions-eval.sh` new assertion cases", - "removalProof": "`nixos-modules/components/qemu-media.nix` raw path option surface is superseded once v3 Guest resource emission and assertions cover the configuration" - }, - { - "workItemId": "WI-017", - "specId": "ADR-046-provider-runtime-qemu-media", - "specPath": "docs/specs/providers/ADR-046-provider-runtime-qemu-media.md", - "dependencyOwner": "P2; depends on WI-012 through WI-015; owner: runtime-qemu-media conformance tests", - "currentSource": "d2b-provider-toolkit conformance kit", - "reuseSource": null, - "reuseAction": "reuse conformance harness; add runtime-qemu-media Guest ResourceType coverage", - "destination": "packages/d2b-provider-runtime-qemu-media/tests/conformance_guest.rs", - "detailedDesign": "d2b-provider-toolkit conformance: pass the Provider conformance kit for the Guest ResourceType axis, including reconcile/finalize contract, phase machine, condition typing, audit shape, and telemetry cardinality.", - "integration": "Conformance tests instantiate the Provider against fake ResourceAPI/ComponentSession dependencies and verify the public Provider contract consumed by core CI.", - "dataMigration": "None — test-only work; no runtime state import", - "validation": "`make test-rust` (runs conformance suite)", - "removalProof": "None — conformance coverage is additive test proof" - }, - { - "workItemId": "WI-018", - "specId": "ADR-046-provider-runtime-qemu-media", - "specPath": "docs/specs/providers/ADR-046-provider-runtime-qemu-media.md", - "dependencyOwner": "P2; depends on WI-004 through WI-017; owner: runtime-qemu-media integration fixtures", - "currentSource": "None — net-new v3 work; no pre-ADR45 baseline equivalent", - "reuseSource": null, - "reuseAction": "net-new integration fixtures", - "destination": "packages/d2b-provider-runtime-qemu-media/integration/", - "detailedDesign": "Integration tests: implement container/fake-Host scenarios for full reconcile from Created to Ready with fake dependencies, finalize sequence, hotplug attach/detach, and restart recovery.", - "integration": "Integration fixtures launch the Provider with fake or containerized Host/Guest/Volume/Network/Device dependencies; CI `make test-integration` consumes the fixtures as the cross-process proof lane.", - "dataMigration": "None — test-only work; no runtime state import", - "validation": "`make test-integration`", - "removalProof": "None — integration coverage is additive test proof" + "adr" : "0046", + "artifactKind" : "d2b-adr-work-items", + "items" : [ + { + "currentSource" : "`packages/d2b-provider-aca/src/lib.rs`: `AcaWorkloadProvider`, 2841 lines production-reachable; `packages/d2b-provider-runtime-azure-container-apps/src/provider.rs`: `AzureContainerAppsRuntimeProvider`, 2796 lines (test-only at v3 baseline)", + "dataMigration" : "Full d2b 3.0 reset; no v2 provider state compatibility", + "dependencyOwner" : "ADR046-provider-001; runtime-aca owner", + "destination" : "`packages/d2b-provider-runtime-azure-container-apps/src/controller.rs`", + "detailedDesign" : "Async `Guest` reconcile loop: `describe` → `validateSpec` → `plan` → `reconcile` → `observe` → `finalize`. Adoption before first `RuntimeEnsure`. Operation ledger persisted to Volume inside gateway Guest. Credential lease acquire/revoke per call. Noise KK enrollment via ZoneLink (from gateway Guest to managed ACA sandbox). `providerPhase` and `guestIdentityDigest` in `status.provider.details`; no raw endpoint/path in any status field. **ProviderDeployment creates both static Processes; ACA controller never instantiates its own Processes and never writes Provider status directly. All Processes run inside the gateway Guest. No Host Process, no Host Credential, no Host Azure HTTP socket. Long-running cloud ops return `progressing`/`requeue-at` immediately; never block watch loop.** Primary reuse disposition: `replace`. Preserved source-plan detail: REPLACE (old) + ADAPT (main types/traits).", + "integration" : "Zone ResourceClient → ProviderDeployment → Process launch inside gateway Guest → d2b-bus → deployment service", + "removalProof" : "`packages/d2b-provider-aca/` removed only after conformance suite green", + "reuseAction" : "replace", + "reuseSource" : null, + "specId" : "ADR-046-provider-runtime-azure-container-apps", + "specPath" : "docs/specs/providers/ADR-046-provider-runtime-azure-container-apps.md", + "validation" : "Controller conformance suite; adoption/ambiguity tests; deadline/cancellation matrix; redaction coverage; **gateway Guest placement validation: assert no Process has `executionRef: Host/*`**; Process spec field schema tests (`spec.template`, canonical `sandbox`/`budget`/`networkUsage`/`endpoints`/`readiness`/`restartPolicy` fields, `mounts` with `required: true`, `providerRef: Provider/system-minijail`); ProviderDeployment creates both Processes (controller never self-spawns); no raw endpoint/path in Guest status", + "workItemId" : "ADR046-aca-001" + }, + { + "currentSource" : "`packages/d2b-gateway-runtime/src/provider_agent.rs` (main)", + "dataMigration" : "None — full d2b 3.0 reset; no prior state to migrate", + "dependencyOwner" : "ADR046-aca-001; deployment service owner", + "destination" : "`packages/d2b-provider-runtime-azure-container-apps/src/deployment_service.rs`", + "detailedDesign" : "`ProviderAgentProcess`-shaped binary; bounded dispatch (64 in-flight); bounded audit ring (1024 capacity); shutdown within 5 s; serves `d2b.aca.v3.deployment` service schema including `GuestHealth` (health probing folded in from former health worker). All ACA API calls go through the injected `AcaControl` port — no ambient network call, no SDK default chain. Long-running ops return `progressing`/`requeue-at` to the caller; no blocking on Azure API completion. Primary reuse disposition: `adapt`. Preserved source-plan detail: COPY/ADAPT (partial); exclude `aca_workload.rs`.", + "integration" : "ProviderDeployment spawns service; d2b-bus routes GuestProvision/Start/Stop/Destroy/Adopt/Inspect/Health methods", + "removalProof" : "Old `GuestControlEndpointProvider` dispatch removed per ADR046-aca-001", + "reuseAction" : "adapt", + "reuseSource" : null, + "specId" : "ADR-046-provider-runtime-azure-container-apps", + "specPath" : "docs/specs/providers/ADR-046-provider-runtime-azure-container-apps.md", + "validation" : "Service dispatch matrix; RBAC refusal tests; redaction tests; shutdown deadline tests", + "workItemId" : "ADR046-aca-002" + }, + { + "currentSource" : "`packages/d2b-provider-runtime-azure-container-apps/src/control.rs` (main): `AcaCredentialLeaseClient`, `AcaCredentialLease`, `AcaCredentialLeaseRequest`, `AcaCredentialPurpose`", + "dataMigration" : "None — full d2b 3.0 reset; no prior state to migrate", + "dependencyOwner" : "ADR046-aca-001; credential integration owner", + "destination" : "`packages/d2b-contracts/src/provider_effects/aca.rs` (shared `d2b-contracts` provider-effects module; no new crate; provider crate remains one package)", + "detailedDesign" : "`AcaCredentialLeaseClient`, `AcaCredentialLease`, `AcaCredentialLeaseRequest`, and `AcaCredentialPurpose` live in the shared `d2b-contracts` provider-effects module. Adapt `CredentialLease` to v3 Credential resource opaque lease handle. `AcaCredentialPurpose` maps to `allowedOperations` check against `Credential.spec`. Lease expiry capped at call deadline. Cleanup job pattern retained verbatim. Primary reuse disposition: `adapt`. Preserved source-plan detail: RETAIN+ADAPT.", + "integration" : "Controller acquires lease per reconcile step via injected `AcaCredentialLeaseClient`; raw token delivered only via Noise KK E2E channel through `d2b.credential.v3.AcquireToken` method", + "removalProof" : "Old `CredentialProvider` trait deleted after `credential-managed-identity` Provider conformance", + "reuseAction" : "adapt", + "reuseSource" : null, + "specId" : "ADR-046-provider-runtime-azure-container-apps", + "specPath" : "docs/specs/providers/ADR-046-provider-runtime-azure-container-apps.md", + "validation" : "Mock credential client tests; lease cleanup timeout tests; token non-exposure assertion", + "workItemId" : "ADR046-aca-003" + }, + { + "currentSource" : "`packages/d2b-provider-relay/src/lib.rs`: `AzureRelayTransportProvider`; `packages/d2b-provider-aca/src/lib.rs`: `AcaRelayTransportConfig`", + "dataMigration" : "No relay session compatibility; re-enroll on first `RuntimeAdopt`", + "dependencyOwner" : "ADR046-session-001; ZoneLink transport owner", + "destination" : "ZoneLink resource `spec.transportSettings` (§15.4); `Provider/transport-azure-relay` dossier (separate)", + "detailedDesign" : "`AcaRelayTransportConfig` fields mapped to `transportSettings.relayNamespace`, `.relayHybridConnection`, `.relayCredentialRef`. KK enrollment replaces ZoneLink transport. Primary reuse disposition: `replace`. Preserved source-plan detail: REPLACE (both); ADAPT config fields to ZoneLink transport settings.", + "integration" : "ZoneLink controller resolves transport Provider; ACA controller establishes enrolled KK ComponentSession over relay transport after `GuestProvision`", + "removalProof" : "`packages/d2b-provider-relay/` removed after `transport-azure-relay` Provider conformance", + "reuseAction" : "replace", + "reuseSource" : null, + "specId" : "ADR-046-provider-runtime-azure-container-apps", + "specPath" : "docs/specs/providers/ADR-046-provider-runtime-azure-container-apps.md", + "validation" : "ZoneLink enrollment tests; relay unavailability tests; KK re-enrollment after sandbox restart", + "workItemId" : "ADR046-aca-004" + }, + { + "currentSource" : "`packages/d2b-provider-runtime-azure-container-apps/src/types.rs` (main): `AcaRuntimeConfig`, `AcaSandboxProfile`, `AcaResourceBinding`, `AcaWorkloadQuery` — test-only at v3 baseline", + "dataMigration" : "None — no state Volume at v3 `1.0`", + "dependencyOwner" : "ADR046-aca-001; state/migration owner", + "destination" : "`packages/d2b-provider-runtime-azure-container-apps/src/types.rs`", + "detailedDesign" : "Replace `RealmId`/`WorkloadId` with v3 `Zone`/`Guest` UID types. `AcaResourceBinding` keys the adoption query. The provider declares **no** Provider state Volume: bounded, non-secret sandbox binding/adoption metadata lives in `Guest.status` (latest bounded observed handle digests) and in-flight operation/requeue truth lives in the core Operation ledger (D087). Neither Process mounts a state Volume; there is no `sandbox-state`/`service-state` Volume, no `User/d2b-aca-controller`/`User/d2b-aca-deployment-service` state-layout principal, and no empty identity-only Volume. On restart the controller re-derives observed binding from `Guest.status`, the core Operation ledger, and an external `find_sandboxes` query, treating status as observation, never authority. Host never holds cloud binding, admission, PSK, or operation state. Primary reuse disposition: `adapt`. Preserved source-plan detail: RETAIN+ADAPT.", + "integration" : "No Provider state Volume is created before Processes start; the controller writes its bounded observed binding/adoption metadata to `Guest.status` on material change and reads in-flight operation state from the core Operation ledger adapter", + "removalProof" : "Old in-memory-only operation ledger removed after core Operation ledger adapter passes; `operation-ledger` stateNamespace absent from component descriptor", + "reuseAction" : "adapt", + "reuseSource" : null, + "specId" : "ADR-046-provider-runtime-azure-container-apps", + "specPath" : "docs/specs/providers/ADR-046-provider-runtime-azure-container-apps.md", + "validation" : "Controller/service declare empty `stateNamespaces`; no `sandbox-state`/`service-state` Volume created; neither Process mounts a state Volume; `Guest.status` binding/adoption fields are bounded, non-secret, and carry no credential/endpoint/poll-URL bytes; restart re-derivation from status/core ledger/external `find_sandboxes` without a Volume; core Operation ledger adapter integration test", + "workItemId" : "ADR046-aca-005" + }, + { + "currentSource" : "`nixos-modules/options-realms-workloads.nix`: `kind = \"ProviderManaged\"` → ACA; `packages/d2b-provider-aca/src/lib.rs`: tracing fields", + "dataMigration" : "None — full d2b 3.0 reset; no prior state to migrate", + "dependencyOwner" : "ADR046-aca-001; Nix/telemetry owner", + "destination" : "`nixos-modules/` (generated Guest resource options); `packages/d2b-provider-runtime-azure-container-apps/src/{audit,metrics}.rs`", + "detailedDesign" : "Eval-time assertions for ACA-specific invariants (§15.6). Closed OTEL label set (§13.4). Audit event schema (§13.3). Tracing target constant `d2b_provider_runtime_azure_container_apps::credential_lease_cleanup` retained. Primary reuse disposition: `replace`. Preserved source-plan detail: REPLACE (Nix emitter) + ADAPT (metric/audit shapes).", + "integration" : "Nix eval gate; `observability-otel` Provider OTEL pipeline", + "removalProof" : "Old Nix `ProviderManaged` workload options retired after Guest resource Nix emitter parity", + "reuseAction" : "replace", + "reuseSource" : null, + "specId" : "ADR-046-provider-runtime-azure-container-apps", + "specPath" : "docs/specs/providers/ADR-046-provider-runtime-azure-container-apps.md", + "validation" : "Label cardinality policy test; audit commit-before-complete test; Nix assertion eval tests", + "workItemId" : "ADR046-aca-006" + }, + { + "currentSource" : "n/a — new requirement (gateway Guest placement)", + "dataMigration" : "None — full d2b 3.0 reset; no prior state to migrate", + "dependencyOwner" : "ADR046-aca-001; Nix/gateway wiring owner", + "destination" : "`nixos-modules/` (gateway Guest declaration, Process template wiring, Credential scope assertion); eval-time validation module", + "detailedDesign" : "Nix eval-time assertions for: (a) `gatewayExecutionRef` resolves to a `Guest` resource, not `Host/*`; (b) Credential `scope.executionRef` matches `gatewayExecutionRef`; (c) all Process templates emitted for this Provider have `executionRef` equal to `gatewayExecutionRef`. No `User` resource or `users.users.*` declarations required — component principals are framework-assigned and not OS accounts. Gateway Guest NixOS closure includes only the two ACA component binaries (§15.6). Assertion error messages name the offending resource and the required `gatewayExecutionRef`.", + "integration" : "Nix eval gate; `d2b.zones.*.resources` validation pass; consumer flake usage example", + "removalProof" : "n/a — ongoing eval-time constraint", + "reuseAction" : "create", + "reuseSource" : null, + "specId" : "ADR-046-provider-runtime-azure-container-apps", + "specPath" : "docs/specs/providers/ADR-046-provider-runtime-azure-container-apps.md", + "validation" : "Nix eval assertion tests (wrong `executionRef` → assertion fires; correct setup → passes); §15.7 assertion coverage tests", + "workItemId" : "ADR046-aca-007" + }, + { + "currentSource" : "packages/d2b-host/src/bin/d2b-activation-helper.rs", + "dataMigration" : "Full d2b 3.0 reset; no v2 activation-helper invocation compatibility", + "dependencyOwner" : "Provider/activation-nixos runner owner; reused helper owner in d2b-host", + "destination" : "packages/d2b-host/src/bin/d2b-activation-helper.rs", + "detailedDesign" : "Replace the helper CLI flag interface with structured JSON input and JSON output, accept bounded systemArtifactId and activationMode, resolve store path internally, emit bounded outcome code, write no resource metadata, and preserve the no-bash-fallback invariant. Primary reuse disposition: `adapt`. Preserved source-plan detail: adapt in place.", + "integration" : "activation-runner invokes the helper through the pre-opened activation portal and system-manager effect resources, then reports structured outcome to activation-nixos status.", + "removalProof" : "Legacy flag-based helper invocation is removed from activation-nixos paths once runner JSON protocol tests pass.", + "reuseAction" : "adapt", + "reuseSource" : null, + "specId" : "ADR-046-provider-activation-nixos", + "specPath" : "docs/specs/providers/ADR-046-provider-activation-nixos.md", + "validation" : "Unit tests for JSON protocol, bounded outcomes, no resource metadata writes, and no Command::new bash fallback.", + "workItemId" : "ADR046-activation-001" + }, + { + "currentSource" : "None — net-new v3 ResourceType; no pre-ADR45 baseline equivalent", + "dataMigration" : "Full d2b 3.0 reset; no v2 generation resource import", + "dependencyOwner" : "ADR-046-resource-object-model and ADR-046-core-controllers; d2b-contracts activation-nixos owner", + "destination" : "docs/reference/schemas/v3/activation-nixos.d2bus.org.NixosGeneration.json and packages/d2b-contracts/src/activation_nixos.rs", + "detailedDesign" : "Define JSON schema and Rust DTOs for activation-nixos.d2bus.org.NixosGeneration, with systemArtifactId as the only artifact reference, no store path fields, activationDetail as a closed status.resource enum absent from spec, and phase constrained to the common framework enum values. Primary reuse disposition: `create`. Preserved source-plan detail: net-new schema and DTOs.", + "integration" : "Resource API, resource store, Nix compiler, activation-nixos controller, and CLI projections consume the schema and DTOs.", + "removalProof" : "None — net-new; no prior owner to remove", + "reuseAction" : "create", + "reuseSource" : null, + "specId" : "ADR-046-provider-activation-nixos", + "specPath" : "docs/specs/providers/ADR-046-provider-activation-nixos.md", + "validation" : "Schema golden vectors, serde unknown-field rejection, phase enum tests, activationDetail-not-in-spec test, and no-store-path-in-spec-or-status test.", + "workItemId" : "ADR046-activation-002" + }, + { + "currentSource" : "Current top-level activation behavior in packages/d2b/src/lib.rs and hardlink-farm/store ownership split described in this dossier", + "dataMigration" : "Full d2b 3.0 reset; adopt mode records an existing active generation but does not import v2 controller state", + "dependencyOwner" : "ADR046-activation-002; activation-nixos controller owner", + "destination" : "packages/d2b-provider-activation-nixos/src/controller/", + "detailedDesign" : "Implement the reconcile loop for activation-nixos.d2bus.org.NixosGeneration: validate executionRef, systemArtifactId, and priorGenerationRef; dispatch one activation-runner EphemeralProcess with canonical startRoot=true shape; observe runner status; mark superseded generations; prune by retainedGenerations through the finalizer protocol; never perform direct store-path operations, nix-collect-garbage, explicit VolumeGcRequest, raw argv composition, or store path writes to resources. Primary reuse disposition: `replace`. Preserved source-plan detail: replace top-level imperative activation flow with resource controller logic.", + "integration" : "Controller watches NixosGeneration resources through Zone resource API, creates activation-runner EphemeralProcesses, releases ownership references for Provider/volume-local, and writes bounded status.", + "removalProof" : "Direct hardlink-farm and garbage-collection calls from activation-nixos reachable paths are absent after controller and runner tests pass.", + "reuseAction" : "replace", + "reuseSource" : null, + "specId" : "ADR-046-provider-activation-nixos", + "specPath" : "docs/specs/providers/ADR-046-provider-activation-nixos.md", + "validation" : "Controller tests for retention, finalizer sequence, no TTL retention, no direct store ops, no store path in status, deleted event-only removal, and runner shape.", + "workItemId" : "ADR046-activation-003" + }, + { + "currentSource" : "packages/d2b-host/src/bin/d2b-activation-helper.rs for helper invocation; runner process is net-new", + "dataMigration" : "Full d2b 3.0 reset; no v2 runner state import", + "dependencyOwner" : "ADR046-activation-003; activation-runner owner", + "destination" : "packages/d2b-provider-activation-nixos/src/runner/", + "detailedDesign" : "Implement target-local activation-runner worker that executes on NixosGeneration.spec.executionRef for Host and Guest targets using the same contract, reads private artifact-catalog.json through the integrity channel, resolves systemArtifactId to a store path in memory only, invokes d2b-activation-helper through structured JSON, executes target-local switch-to-configuration through typed helper dispatch with no raw exec or SSH, emits structured outcome JSON, and never outputs store paths. Primary reuse disposition: `adapt`. Preserved source-plan detail: net-new runner invoking adapted helper.", + "integration" : "Controller-created EphemeralProcess runs under Provider/system-minijail on the target execution context and returns outcome through the activation portal for status update.", + "removalProof" : "No old SSH or raw command fallback path exists in activation-runner after tests assert typed helper dispatch only.", + "reuseAction" : "adapt", + "reuseSource" : null, + "specId" : "ADR-046-provider-activation-nixos", + "specPath" : "docs/specs/providers/ADR-046-provider-activation-nixos.md", + "validation" : "Runner tests for artifact lookup, JSON helper invocation, Host and Guest target parity, no raw argv, no SSH, no store path in output, and terminal nonzero handling.", + "workItemId" : "ADR046-activation-004" + }, + { + "currentSource" : "packages/d2b/src/lib.rs top-level cmd_switch, cmd_boot, cmd_test, cmd_rollback, cmd_build, cmd_generations, cmd_gc, and cmd_migrate", + "dataMigration" : "Full d2b 3.0 reset; CLI command surface changes with no runtime state import", + "dependencyOwner" : "ADR-046-cli-and-operations; activation CLI owner", + "destination" : "packages/d2b/src/activation.rs", + "detailedDesign" : "Implement d2b activation build, switch, boot, test, rollback, adopt, generations, gc, and migrate subcommands, projecting ADR-046 CLI and Operations behavior and ensuring no store path, digest, or artifact catalog field appears in JSON or human output. Primary reuse disposition: `replace`. Preserved source-plan detail: replace with grouped CLI projection.", + "integration" : "d2b CLI dispatcher calls resource API and activation-nixos controller by creating or listing NixosGeneration resources; legacy top-level verbs are removed by ADR046-activation-007 after integration tests pass.", + "removalProof" : "Legacy top-level verb removal is gated on ADR046-activation-007 after the d2b activation integration matrix passes.", + "reuseAction" : "replace", + "reuseSource" : null, + "specId" : "ADR-046-provider-activation-nixos", + "specPath" : "docs/specs/providers/ADR-046-provider-activation-nixos.md", + "validation" : "CLI integration tests for subcommand parsing, authorization, resource creation/listing, rollback priorGenerationRef, gc ownership release, and output redaction.", + "workItemId" : "ADR046-activation-005" + }, + { + "currentSource" : "Current VM Nix configuration emits activation inputs implicitly; this item creates the explicit Provider and NixosGeneration resource emitter", + "dataMigration" : "Full d2b 3.0 reset; existing d2b.vms activation settings are reauthored as Zone resources rather than imported", + "dependencyOwner" : "ADR-046-nix-configuration; activation-nixos Nix owner", + "destination" : "nixos-modules/providers/activation-nixos.nix", + "detailedDesign" : "Emit Provider spec and activation-nixos.d2bus.org.NixosGeneration resources per target, flow retainedGenerations only through Provider.spec.config.retainedGenerations, reference systems by systemArtifactId only, omit store paths from all emitted resources, and avoid dedicated state-layout User or ComponentPrincipal because ProviderStateSet is empty. Primary reuse disposition: `adapt`. Preserved source-plan detail: net-new resource emitter adapted from existing Nix activation inputs.", + "integration" : "Nix compiler emits Provider and NixosGeneration resources plus private artifact catalog entries consumed by core configuration publication and the activation-nixos controller.", + "removalProof" : "Old implicit activation Nix paths are unused by activation-nixos once resource emitter parity tests pass.", + "reuseAction" : "adapt", + "reuseSource" : null, + "specId" : "ADR-046-provider-activation-nixos", + "specPath" : "docs/specs/providers/ADR-046-provider-activation-nixos.md", + "validation" : "Nix eval tests for Provider config, NixosGeneration shape, retainedGenerations source, no systemStorePath in bundle, no state Volume or state-layout principal, and artifact ID resolution.", + "workItemId" : "ADR046-activation-006" + }, + { + "currentSource" : "packages/d2b/src/lib.rs cmd_switch, cmd_boot, cmd_test, cmd_rollback, cmd_build, cmd_generations, cmd_gc, cmd_migrate and dispatcher registrations", + "dataMigration" : "Full d2b 3.0 reset; no command alias compatibility window", + "dependencyOwner" : "ADR046-activation-005; d2b CLI dispatcher owner", + "destination" : "packages/d2b/src/lib.rs", + "detailedDesign" : "Remove the legacy top-level activation command functions and their dispatcher registrations after the grouped d2b activation namespace passes integration tests. Primary reuse disposition: `delete-after-cutover`. Preserved source-plan detail: delete legacy top-level commands.", + "integration" : "CLI dispatcher routes only d2b activation subcommands for activation operations; documentation and tests use the new namespace.", + "removalProof" : "cmd_switch, cmd_boot, cmd_test, cmd_rollback, cmd_build, cmd_generations, cmd_gc, cmd_migrate and their registrations are deleted from packages/d2b/src/lib.rs.", + "reuseAction" : "delete-after-cutover", + "reuseSource" : null, + "specId" : "ADR-046-provider-activation-nixos", + "specPath" : "docs/specs/providers/ADR-046-provider-activation-nixos.md", + "validation" : "CLI integration matrix for d2b activation passes; grep or contract test confirms old cmd_* symbols and dispatcher registrations are absent.", + "workItemId" : "ADR046-activation-007" + }, + { + "currentSource" : "`packages/d2b-contracts/src/public_wire.rs`, `broker_wire.rs`; `d2b-daemon-access/src/lib.rs`; `d2b-realm-router/src/lib.rs`", + "dataMigration" : "None; v3 clean break", + "dependencyOwner" : "W0; resource API integrator", + "destination" : "`packages/d2b-contracts/proto/d2b-resource-v3.proto`, `packages/d2b-resource-api/src/service.rs`, `client.rs`", + "detailedDesign" : "Async methods, contexts, preconditions, limits, errors, status/finalizer separation, batch API Primary reuse disposition: `adapt`. Preserved source-plan detail: extract and adapt.", + "integration" : "d2b-bus exact service → Zone auth → redb actor", + "removalProof" : "Old command/resource-equivalent paths removed only per integration wave", + "reuseAction" : "adapt", + "reuseSource" : null, + "specId" : "ADR-046-resource-api-and-authorization", + "specPath" : "docs/specs/ADR-046-resource-api-and-authorization.md", + "validation" : "Protocol vectors; malformed/oversize/conflict/status-owner tests", + "workItemId" : "ADR046-api-001" + }, + { + "currentSource" : "`d2bd` public admission; `d2b-daemon-access` policy evidence; `d2b-realm-core/src/access.rs`, `audit.rs`", + "dataMigration" : "Generate initial Roles/Bindings from Nix v3 config", + "dependencyOwner" : "ADR046-api-001; authorization integrator", + "destination" : "`packages/d2b-resource-api/src/authz.rs`, `packages/d2b-core-controller/src/rbac.rs`", + "detailedDesign" : "Role/RoleBinding schemas/evaluator/cache/revision invalidation, ComponentSession subject mapping, parent Zone access", + "integration" : "Every resource/runtime method invokes one native evaluator before structural checks", + "removalProof" : "Legacy auth remains until every v3 route is covered", + "reuseAction" : "adapt", + "reuseSource" : null, + "specId" : "ADR-046-resource-api-and-authorization", + "specPath" : "docs/specs/ADR-046-resource-api-and-authorization.md", + "validation" : "Decision matrix/property tests; revocation/cache/outage/parent-child tests", + "workItemId" : "ADR046-api-002" + }, + { + "currentSource" : "`packages/d2b-core/src/audio_policy.rs` (all symbols); `packages/d2b-core/tests/audio_policy.rs`", + "dataMigration" : "Parse v1/v2 once; require exactly one configured owner Service; write grants plus `serviceRef`; fail closed on missing/ambiguous Service; remove prior file only after successful commit.", + "dependencyOwner" : "No prerequisites; `d2b-provider-audio-pipewire` crate", + "destination" : "`packages/d2b-provider-audio-pipewire/src/audio_policy.rs`; re-exported from crate root", + "detailedDesign" : "`LevelPercent`, `AudioGrant`, `AudioPolicyState`, `parse_audio_state`, `to_v2_bytes`, `AudioPolicyError` copy unchanged. `AudioPolicyState` is the canonical in-memory representation of `AudioBinding.spec.grants`. `parse_audio_state`/`to_v2_bytes` are used only once during first-activation migration from a prior v1/v2 on-disk file; there is no ongoing state file in v3. Primary reuse disposition: `copy-unchanged`. Preserved source-plan detail: `copy-unchanged` (no daemon imports; pure DTO library).", + "integration" : "First-activation migration uses `parse_audio_state` and writes grants only into a per-Guest AudioBinding that also names the explicitly configured same-Zone owner AudioService.", + "removalProof" : "`d2b-core/src/audio_policy.rs` deleted when no `d2bd` caller references it; confirmed by `cargo check --no-default-features`.", + "reuseAction" : "copy-unchanged", + "reuseSource" : "Same baseline paths", + "specId" : "ADR-046-provider-audio-pipewire", + "specPath" : "docs/specs/providers/ADR-046-provider-audio-pipewire.md", + "validation" : "`tests/audio_policy.rs`: all existing tests from `d2b-core/tests/audio_policy.rs` plus AudioBinding spec serialization tests", + "workItemId" : "ADR046-audio-001" + }, + { + "currentSource" : "`packages/d2b-host/src/audio_argv.rs` (all symbols + tests); `tests/golden/runner-shape/audio-argv-minimal.txt`", + "dataMigration" : "No runtime state migration; argv template output is regenerated from the v3 component template, and live Process specs never store argv.", + "dependencyOwner" : "Depends on `ADR046-audio-001`; Process Provider template schema", + "destination" : "`packages/d2b-provider-audio-pipewire/src/argv.rs` (component template renderer)", + "detailedDesign" : "`generate_audio_argv` remains the canonical argv builder for the `vhost-user-sound-worker` component template. The resulting argv/env/executableRef are sealed into the LaunchTicket. The per-Guest binary copy path enforcement remains via the LaunchTicket verifier. The live Process resource spec contains no argv or executableRef. The `--socket` argument is removed; the vhost-user service identity is `Endpoint/corp-vm-audio-vhost-user`, while the backing locator is resolved into the LaunchTicket under authorization. Primary reuse disposition: `adapt`. Preserved source-plan detail: `adapt` — argv builder retained; becomes a signed component-template projection, not a live Process spec field.", + "integration" : "The component template for `vhost-user-sound-worker` embeds the output of `generate_audio_argv`; the Process Provider resolves arg0 from the artifact catalog.", + "removalProof" : "`d2b-host/src/audio_argv.rs` deleted after `d2bd` has no callers; confirmed by `cargo check -p d2b-host`.", + "reuseAction" : "adapt", + "reuseSource" : "Same baseline paths", + "specId" : "ADR-046-provider-audio-pipewire", + "specPath" : "docs/specs/providers/ADR-046-provider-audio-pipewire.md", + "validation" : "`tests/argv.rs`: rejection matrix (Nix store path, symlink, cross-guest copy, empty name); no-socket-in-argv assertion; no-argv-in-process-spec assertion", + "workItemId" : "ADR046-audio-002" + }, + { + "currentSource" : "`packages/d2bd/src/audio_host_controller.rs` `PipeWireHostController` enforcement logic; `QemuAudioController`", + "dataMigration" : "No state migration; mediator applies current AudioBinding grants and levels from resource state during reconcile, replacing host-controller direct writes.", + "dependencyOwner" : "Depends on `ADR046-audio-001`; ComponentSession service contract; libpipewire 1.x API", + "destination" : "`packages/d2b-provider-audio-pipewire/src/mediator/enforcement.rs`", + "detailedDesign" : "Owner AudioService only: speaker `SetGrant` maps `\"off\"` to `pw_node_set_param(SPA_PARAM_Props, mute=true, target.object=-1)` on the worker's node and `\"on\"` to `mute=false`; `SetLevel` maps to a bounded volume. Microphone `\"on\"` is applied only after the owner authority grants its single capture slot; queued/blocked consumers remain muted, and release mutes/disconnects before handoff. `captureAlias` resolves privately through the registry. A projection AudioService routes the operation to the remote owner over its import stream and is denied any local mediator/PipeWire open. `FakeAudioMediator` is the hermetic test double. No state file, wpctl, EphemeralProcess, or node ID in any external surface. Primary reuse disposition: `adapt`. Preserved source-plan detail: `adapt` — enforcement logic becomes a libpipewire API implementation behind the `SetGrant`/`SetLevel` ComponentSession service.", + "integration" : "AudioBinding controller calls its resolved AudioService. Owner Service dispatches locally to AudioMediator; projection Service dispatches over the encrypted import route to the remote owner.", + "removalProof" : "`d2bd/src/audio_host_controller.rs` retired after `d2bd` audio dispatch path is replaced.", + "reuseAction" : "adapt", + "reuseSource" : "Same baseline paths", + "specId" : "ADR-046-provider-audio-pipewire", + "specPath" : "docs/specs/providers/ADR-046-provider-audio-pipewire.md", + "validation" : "`tests/mediator.rs` and `tests/enforcement.rs`: owner-Service SetGrant/SetLevel round-trip; speaker mixing; microphone queued consumers remain muted; release/lease-expiry mute-before-handoff and no-overlap proof; projection routing with fake streams; projection-PipeWire-open denial; no-node-id-in-bus-message; ProviderSessionUnavailable; captureAlias registry resolution", + "workItemId" : "ADR046-audio-004" + }, + { + "currentSource" : "None (ADR-only); structured after `d2b-contracts/src/public_wire.rs` audio types", + "dataMigration" : "Full d2b 3.0 reset; owner Services and per-Guest AudioBindings are authored as new v3 resources; projection Services are core-generated from ResourceImport.", + "dependencyOwner" : "ADR046-provider-004 common audio Service/Binding base; Core resource-api foundation; `d2b-provider-audio-pipewire` crate", + "destination" : "`packages/d2b-provider-audio-pipewire/src/{resource_type,admission,provider_extension}.rs` (strict implementation extensions and binding only; common base lives under ADR046-provider-004)", + "detailedDesign" : "Bind the shared D098 `audio.d2bus.org.AudioService` and `audio.d2bus.org.AudioBinding` base schema versions/fingerprints from ADR046-provider-004 and define only strict audio-pipewire Provider extensions/admission. AudioService validates immutable `serviceRole`, same-Zone local Endpoint refs, owner-only D097 AuthorityDescriptor, projection-only `ownerRef: ResourceImport/`, Core-only projection creation, and projection `spec.provider` rejection. Initial-v3 owner admission requires speaker `multiplexed` and microphone `exclusive` and rejects multiplexed capture plus every consent/approval/priority placeholder. Owner Service status carries only bounded aggregate microphone active/request/Zone counts; Binding status carries only its own arbitration state. AudioBinding validates Guest ownership, required immutable same-Zone `serviceRef`, grants/levels/users, and forbids authority/export/projection semantics. Semantic authority/import/attachment observations stay under `status.resource`; implementation observations stay under `status.provider`. PipeWire fields are rejected from base spec/status. Register no provider-qualified or AudioState identifier and no serde/schema alias. Primary reuse disposition: `adapt`. Preserved source-plan detail: `adapt` provider-neutral closed enums; remove `AudioProviderKind` from base status; `ADR-only` for schema/admission.", + "integration" : "`Provider/audio-pipewire` signs and publishes implementation support for both neutral qualified ResourceTypeSchemas and strict provider-envelope schemas. Core import controller may create/delete only projection AudioService; ordinary resource API admission handles owner Services and AudioBindings.", + "removalProof" : "None — both ResourceTypes are net-new", + "reuseAction" : "adapt", + "reuseSource" : "`public_wire.rs` `AudioChannel`, `AudioEnforcementPosture`, `AudioErrorKind`, `AudioProviderKind`, `AudioSetApplied`; `AudioProviderKind` is removal evidence, not a base-status field", + "specId" : "ADR-046-provider-audio-pipewire", + "specPath" : "docs/specs/providers/ADR-046-provider-audio-pipewire.md", + "validation" : "`tests/resource_type.rs`: consume the ADR046-provider-004 common fixtures/fingerprints; canonical minimal base without `spec.provider`; neutral qualified-name registration; both schema/status round-trips including bounded aggregate owner queue status and per-Binding arbitration state; clean-break rejection of provider-qualified names, every AudioState spelling, and all aliases; fake alternate-provider base conformance; strict base/provider unknown-field matrices; projection `spec.provider` rejection; D088 `status.resource`/`status.provider` placement; PipeWire fields only in strict provider envelopes/config; Service role/AuthorityDescriptor/ownerRef/Endpoint-locality rules; initial-v3 exclusive-mic/mixed-speaker schema and consent/approval/priority/concurrent-capture rejection; Core-only projection admission; AudioBinding required same-Zone serviceRef and Guest owner; immutable refs; out-of-range levels/users; explicit tests that AudioBinding cannot be exported or projected", + "workItemId" : "ADR046-audio-005" + }, + { + "currentSource" : "`packages/d2bd/src/audio_dispatch.rs` lines 250–end (dispatch ordering reference)", + "dataMigration" : "v1/v2 audio policy file migration is handled by ADR046-audio-001 before reconcile; the controller keeps no Provider state Volume and imports no additional runtime state.", + "dependencyOwner" : "Depends on `ADR046-audio-001` through `ADR046-audio-005`; core ResourceImport controller; system Process Provider; AudioMediator (`ADR046-audio-007`); GuestAudioAgent (`ADR046-audio-011`); no Provider state Volume under D087", + "destination" : "`packages/d2b-provider-audio-pipewire/src/controller/audio_service.rs`; `src/controller/audio_binding.rs`", + "detailedDesign" : "One controller binary registers deterministic handlers for the two neutral ResourceTypes, constrained to immutable `spec.providerRef: Provider/audio-pipewire`. Service handler watches AudioService, its ResourceImport owner and local Endpoints; owner semantics claim/revalidate D097, local mediator, and aggregate exclusive-mic queue state, while projection semantics bind only encrypted import streams and deny PipeWire. It cannot create/delete projection Service. Binding handler watches AudioBinding, same-Zone serviceRef, Guest/User, owned Process, and private Endpoints; creates the vhost-user worker, GuestAudioAgents, and private Endpoints, then calls the resolved Service and guest agents. It maps `Applied|Queued|MicQueueFull` into the closed conditions/status/phase, and release/delete/revocation cancels queue state before child teardown. A Service event enqueues serviceRef-indexed Bindings. A resource selecting another conforming Provider is ignored and cannot be status/finalizer-mutated. Neither handler uses broker/pidfd/EphemeralProcess/Volume/User operations or direct filesystem access. Primary reuse disposition: `adapt`. Preserved source-plan detail: `adapt` — dispatch logic is the reference for step ordering only.", + "integration" : "Registered with Zone core as a controller under `Provider/audio-pipewire`.", + "removalProof" : "Supersedes `audio_dispatch.rs`; `d2bd` audio dispatch deleted after e2e parity test confirms", + "reuseAction" : "adapt", + "reuseSource" : null, + "specId" : "ADR-046-provider-audio-pipewire", + "specPath" : "docs/specs/providers/ADR-046-provider-audio-pipewire.md", + "validation" : "Fast hermetic `tests/audio_service_controller.rs`: neutral type/provider selection, foreign-provider ignore/deny, owner authority, bounded aggregate microphone status, projection ownerRef/import chain, core-only create/delete, projection no-PipeWire-open, revocation queue cancellation, and D091 propagation. `tests/audio_binding_controller.rs`: neutral type/provider selection, required same-Zone serviceRef, owner/projection dispatch, child Process/private Endpoint state machine, `Applied|Queued|MicQueueFull` status mapping, off/delete/revocation cancellation, grant changes, absence/failures/deletion. Conformance asserts no AudioBinding export/projection, no broker/pidfd/EphemeralProcess/Volume/User ops. ProviderDeployment integration remains fake-only and validates empty ProviderStateSet.", + "workItemId" : "ADR046-audio-006" + }, + { + "currentSource" : "`packages/d2bd/src/audio_host_controller.rs` PipeWire session access patterns (reference only)", + "dataMigration" : "No persisted mediator state migration; the service rebuilds its PipeWire node map from the registry on start and consumes current AudioBinding through controller calls.", + "dependencyOwner" : "Depends on `ADR046-audio-004`; ComponentSession service (ADR-046-componentsession-and-bus); libpipewire 1.x", + "destination" : "`packages/d2b-provider-audio-pipewire/src/mediator/mod.rs`; `src/bin/audio_pipewire_mediator.rs`", + "detailedDesign" : "Owner AudioService implementation only. Long-lived user-session Process maintains per-AudioBinding nodes under the single owner backing, receives the pre-opened local PipeWire portal FD, and exposes `SetGrant`/`SetLevel` through `Endpoint/audio-pipewire-authority`. It enforces the authority arbiter's single microphone slot and mute-before-handoff result while speaker nodes remain mixed. Projection Services never start/call a local mediator and cannot receive its FD. No EphemeralProcess, wpctl, remote Ref, or node identity in external surfaces.", + "integration" : "Second binary in the `d2b-provider-audio-pipewire` package. Registered as a user-session service under `Provider/audio-pipewire`.", + "removalProof" : "Supersedes `d2bd`'s `PipeWireHostController` direct session access; `d2bd` audio host controller deleted after e2e parity", + "reuseAction" : "create", + "reuseSource" : null, + "specId" : "ADR-046-provider-audio-pipewire", + "specPath" : "docs/specs/providers/ADR-046-provider-audio-pipewire.md", + "validation" : "`tests/mediator.rs`: owner-Service FD handoff and calls; captureAlias; node-id sealing; session-unavailable; concurrent speaker Guest isolation; exclusive microphone no-overlap and mute-before-handoff; teardown; projection Service cannot resolve mediator Endpoint or portal attachment", + "workItemId" : "ADR046-audio-007" + }, + { + "currentSource" : "`nixos-modules/components/audio/host.nix` and `guest.nix`", + "dataMigration" : "Full d2b 3.0 reset; legacy Nix audio options emit/deprecate to one owner AudioService plus per-Guest AudioBindings that reference it; no projection Service is authored.", + "dependencyOwner" : "Depends on `ADR046-audio-005`; Nix resource compilation framework; `ADR-046-nix-configuration`", + "destination" : "`nixos-modules/components/audio/v3-resource.nix`; `nixos-modules/components/audio/host-config.nix`; `nixos-modules/components/audio/guest-config.nix`", + "detailedDesign" : "`v3-resource.nix` emits only `audio.d2bus.org.AudioService` and `audio.d2bus.org.AudioBinding`, selected by `Provider/audio-pipewire`, with required same-Zone serviceRef. For projection identity, ResourceExport emits `resourceRef` plus canonical `serviceType`, `projectionSchemaFingerprint`, and `factoryFingerprint`; its Service retains the Endpoint. ResourceImport emits the matching three `expected*` fields. Projection Services are never authored: core materializes them from ResourceImport. Eval rejects every obsolete Export/Import field in the explicit rejection matrix, provider-qualified ResourceTypes, every AudioState spelling, aliases, PipeWire fields in neutral base spec/status, AudioBinding export/projection, non-exclusive microphone or consent/approval/priority/concurrent-capture fields, Service role/ownerRef/authority mismatches, cross-Zone Endpoint/service refs, and duplicate owner authority. Existing captureAlias stays in Provider config; guestUsers/group injection, runtime-audio derivation, host stream rules, and guest stack remain.", + "integration" : "`nixos-modules/default.nix` imports all three modules.", + "removalProof" : "`host.nix` and `guest.nix` kept as compat shims until v3 module deployed on all Zones", + "reuseAction" : "replace", + "reuseSource" : "Same", + "specId" : "ADR-046-provider-audio-pipewire", + "specPath" : "docs/specs/providers/ADR-046-provider-audio-pipewire.md", + "validation" : "`tests/unit/nix/cases/audio-v3-resource.nix`: exact neutral type names; provider-qualified/AudioState/alias rejection; strict provider-field placement; owner Service and Binding round-trip; same-Zone serviceRef; exact canonical Export/Import Service type and both fingerprints; obsolete Export/Import field rejection; Export Endpoint-field rejection; projection core-only ownerRef chain; AudioBinding export/projection rejection; exclusive-mic/mixed-speaker and consent/approval/priority/concurrent-capture rejection; authority uniqueness; Endpoint locality; plus existing grants/users/captureAlias/deprecation/no-wpctl/no-audioFrontend assertions", + "workItemId" : "ADR046-audio-008" + }, + { + "currentSource" : "`packages/d2b-contract-tests/tests/minijail_audio_usbip.rs` audio section", + "dataMigration" : "None — test migration only; no runtime state.", + "dependencyOwner" : "Depends on `ADR046-audio-005`; `d2b-contract-tests` crate", + "destination" : "`packages/d2b-provider-audio-pipewire/tests/minijail_contract.rs` (provider-local); retain cross-bundle source greps in `d2b-contract-tests`", + "detailedDesign" : "Retain worker sandbox assertions and add role-sensitive attachment checks: an owner-Service worker may receive only the local AudioMediator attachment; a projection-Service worker receives only its same-Zone route Endpoint and can never receive a PipeWire FD. Binding-owned private Endpoint ownership/producerRef is explicit. All Service/Binding/Process/Endpoint serialized forms remain locator-free.", + "integration" : "Provider-local contract tests run in `d2b-provider-audio-pipewire`; retained cross-bundle greps in `d2b-contract-tests` ensure bundle-wide invariants still hold.", + "removalProof" : "The superseded duplicate shell validator `tests/minijail-validator-audio.sh` is deleted only after the successor Rust gate (`minijail_audio_usbip.rs` cross-bundle + provider-local `minijail_contract.rs`) is green and its removal-proof check passes; the `seccomp_policy_ref == \"w1-audio\"` assertion migrates to `spec.sandbox.seccompClass == \"audio-pipewire-worker\"` before removal. Cross-bundle Rust tests are retained.", + "reuseAction" : "adapt", + "reuseSource" : "Same", + "specId" : "ADR-046-provider-audio-pipewire", + "specPath" : "docs/specs/providers/ADR-046-provider-audio-pipewire.md", + "validation" : "`cargo test -p d2b-provider-audio-pipewire -- minijail` must pass; existing cross-bundle tests must continue to pass", + "workItemId" : "ADR046-audio-009" + }, + { + "currentSource" : "`packages/d2bd/src/audio_dispatch.rs` audit call sites (redaction pattern reference)", + "dataMigration" : "No telemetry/audit data migration; v3 emits new closed-label OTEL/audit records after cutover and old audio_dispatch audit sites are removed.", + "dependencyOwner" : "Depends on `ADR046-audio-006`, `ADR046-audio-007`, `ADR046-audio-011`; `d2b-telemetry` lightweight emitter", + "destination" : "`packages/d2b-provider-audio-pipewire/src/telemetry.rs`", + "detailedDesign" : "Emit closed-label Service and Binding metrics plus post-commit audit. Service events distinguish only `owner\\|projection` and closed outcomes; microphone arbitration emits closed transition/outcome plus bounded aggregate counts only. Metrics expose active count `0|1`, aggregate queue depth, and closed arbitration state without Zone/Binding/handle/position labels. Events omit authority keys, import/export keys, remote identity, stream/session ids, endpoints, client timestamps, and queue entries. Enforcement metrics cover owner-local and projection-routed calls without exposing route identity. ProcessEffect audit remains Process Provider-owned.", + "integration" : "Audio controller and mediator call telemetry/audit emitters after commit or enforcement; d2b-telemetry exporter and policy_observability consume the resulting records.", + "removalProof" : "`audio_dispatch.rs` audit call sites deleted after cutover", + "reuseAction" : "adapt", + "reuseSource" : "Same; adapt redaction pattern", + "specId" : "ADR-046-provider-audio-pipewire", + "specPath" : "docs/specs/providers/ADR-046-provider-audio-pipewire.md", + "validation" : "`tests/audio_telemetry.rs`: Service/Binding event separation, microphone aggregate metrics and transition audit, redaction of Zone/Binding/handle/position/timestamp, post-commit ordering, label cardinality, forbidden authority/import/stream/path fields, no ProcessEffect duplication", + "workItemId" : "ADR046-audio-010" + }, + { + "currentSource" : "`packages/d2b-guestd/src/audio_set.rs` (guestd wpctl dispatch — reference only)", + "dataMigration" : "No guest runtime state migration; GuestAudioAgent reconnects to guest PipeWire and applies current AudioBinding grants and levels on reconcile, replacing guestd wpctl dispatch.", + "dependencyOwner" : "Depends on `ADR046-audio-004`; ComponentSession service contract; libpipewire 1.x; system-systemd Process Provider for guest domain", + "destination" : "`packages/d2b-provider-audio-pipewire/src/guest_agent/mod.rs`; `src/guest_agent/enforcement.rs`; `src/bin/audio_pipewire_guest_agent.rs`", + "detailedDesign" : "Long-lived user-domain Process running in the Guest under the guest workload user's UID. One Process resource per entry in `AudioBinding.spec.guestUsers`; each named by opaque UID digest (`ag-`) and carrying label `audio-pipewire.d2bus.org/role: guest-audio-agent`. `userRef` is the corresponding `User/` Zone resource. Opens a PipeWire connection in the Guest's compositor session (same-UID, natural access). Exposes a typed `AudioSet` ComponentSession service through an owned `Endpoint/ag--audio-set` (vsock transport, Guest→Zone d2b-bus). `AudioSet(mic, speaker, speakerLevel, micGain)` applies changes via libpipewire API (`pw_node_set_param` with `SPA_PARAM_Props`, `pw_stream_set_control`) on the guest virtio-snd PipeWire node. No wpctl binary, no command path, no EphemeralProcess. Controller calls ALL active GuestAudioAgent instances in parallel for each grant change and aggregates failures. `FakeGuestAudioAgent` is a test double behind `#[cfg(test)]`. Primary reuse disposition: `adapt`. Preserved source-plan detail: `ADR-only` (new component; supersedes guestd wpctl dispatch path).", + "integration" : "Third binary in the `d2b-provider-audio-pipewire` package. Declared as GuestAudioAgent Process resources by the audio-binding-controller (one per guestUser; template: `guest-audio-agent`). System Process Provider (`Provider/system-systemd`) launches each inside the Guest under the respective guest workload user's UID.", + "removalProof" : "`d2b-guestd` wpctl audio dispatch path deleted after all Guests have GuestAudioAgent deployed and e2e parity test passes", + "reuseAction" : "adapt", + "reuseSource" : "`packages/d2bd/src/audio_host_controller.rs` libpipewire enforcement patterns (reference only)", + "specId" : "ADR-046-provider-audio-pipewire", + "specPath" : "docs/specs/providers/ADR-046-provider-audio-pipewire.md", + "validation" : "`tests/guest_agent.rs`: AudioSet service call → libpipewire apply; mute/route/level; session-unavailable path; reconnect state restore; no wpctl binary; no command path; N-agent creation (one per guestUser); parallel call and aggregated failure", + "workItemId" : "ADR046-audio-011" + }, + { + "currentSource" : "None — net-new ADR 0046 cross-Zone sharing (D096)", + "dataMigration" : "None — full d2b 3.0 reset", + "dependencyOwner" : "ADR046-zone-control-019, ADR046-zone-control-020; audio Provider owner", + "destination" : "`packages/d2b-provider-audio-pipewire/src/share_adapter.rs`", + "detailedDesign" : "Implement signed `Provider/audio-pipewire` adapters only for canonical `serviceType: audio.d2bus.org.AudioService` when `spec.providerRef` selects this Provider. ResourceExport carries that `serviceType` plus the signed `projectionSchemaFingerprint` and semantic `factoryFingerprint`; ResourceImport carries the matching `expectedServiceType`, `expectedProjectionSchemaFingerprint`, and `expectedFactoryFingerprint`. Export adapter admits only the owner Service. Its local authority Endpoint remains Service-owned and is never an Export field. Core creates/deletes the projection AudioService with `ownerRef: ResourceImport/`, `providerRef`, and semantic base/import fields but no `spec.provider`; routing derives from the signed local descriptor and ResourceImport record. The semantic factory fingerprint binds factory metadata plus projection-protocol version, never Provider/adapter identity, which the signed descriptor authenticates separately. The import adapter reconciles its semantic route and never creates AudioBinding or opens PipeWire. Per-Guest AudioBindings are ordinary consumer resources with same-Zone serviceRef. No provider-qualified type alias, FD/path/socket/remote Ref crosses a Zone. Primary reuse disposition: `adapt`. Preserved source-plan detail: net-new (implement the signed audio export/import adapter).", + "integration" : "Core export/import controller (ADR046-zone-control-019); local projection lifecycle (ADR046-zone-control-020); ComponentSession bounded encrypted named streams", + "removalProof" : "Not applicable (new surface)", + "reuseAction" : "adapt", + "reuseSource" : "audio authority/mediator service (this dossier); `packages/d2b-provider/src/share_adapter.rs` `ExportAdapter`/`ImportAdapter` traits", + "specId" : "ADR-046-provider-audio-pipewire", + "specPath" : "docs/specs/providers/ADR-046-provider-audio-pipewire.md", + "validation" : "Fast hermetic `tests/share_adapter.rs`: exact neutral AudioService `serviceType`; exact projection-schema/factory fingerprint match; explicit rejection of obsolete `endpointRef`, `exportedType`, `baseSchemaFingerprint`, `expectedType`, `expectedBaseSchemaFingerprint`, and `projectionType`; reject every Export Endpoint field, provider-qualified alias, and AudioBinding export/projection; accept owner AudioService export; Core-only projection creation/deletion; exact ResourceImport -> projection AudioService ownerRef chain with no `spec.provider`; semantic factory fingerprint unchanged by Provider/adapter identity mutation while signed identity authentication remains exact; projection never opens PipeWire; reconnect/revocation/D091 propagation with fake streams. Only `integration/real_stream.rs` exercises a real encrypted named stream.", + "workItemId" : "ADR046-audio-012" + }, + { + "currentSource" : "`packages/d2bd/src/audio_host_controller.rs` (`HostAudioController` trait, `PipeWireHostController::{from_audio_node,find_audio_node}`, `QemuAudioController`, `FakeHostController`); `packages/d2b-core/src/audio_policy.rs` (`LevelPercent`, `AudioGrant`, `AudioPolicyState`)", + "dataMigration" : "None — full d2b 3.0 reset; grants are authoritative in `AudioBinding.spec` (no state file).", + "dependencyOwner" : "Depends on `ADR046-audio-001`, `ADR046-audio-004`, `ADR046-zone-control-019`; audio Provider owner", + "destination" : "`packages/d2b-provider-audio-pipewire/src/authority.rs` (speaker mixer + mic arbiter); `AuthorityDescriptor` on owner `AudioService`", + "detailedDesign" : "Exactly one owner AudioService holds the real PipeWire connection and D097 AuthorityDescriptor. Projection Services and AudioBindings cannot carry it. Speaker streams remain multiplexed/mixed. Microphone capture has exactly one slot across owner and importing Zones. The arbiter keys requests by authenticated Zone plus route-scoped opaque consumer handle, permits one pending entry per handle, bounds pending entries to 16 per Zone and 64 total, uses owner-sequenced FIFO per Zone and round-robin across non-empty Zones, and ignores client timestamps/priority. Its 30-second active lease renews only while no other Zone waits; contended expiry mutes/disconnects, dequeues the next Zone, and atomically requeues a still-requesting old holder at its Zone FIFO tail. Off/delete/revoke/disconnect cancel idempotently. Queue entries are memory-only and restart rebuild fails closed with capture muted. Multiplexed capture and consent/approval/priority/concurrent-capture surfaces are rejected; a future spec must define a concrete consent authorization ResourceType and resource-API verb before concurrent capture exists. Core rejects duplicate owner Services before open and adopts by ownerProof. No new ProcessRole, broker path, or state file. Primary reuse disposition: `adapt`. Preserved source-plan detail: `adapt` — the host controller becomes the single authority service; no daemon `Mutex`/state-file wrapping.", + "integration" : "Owner AudioService references `Endpoint/audio-pipewire-authority`; same-Zone AudioBindings and remote projection Services call it. Core authority index admits exactly one owner Service.", + "removalProof" : "`audio_host_controller.rs` daemon-side controller deleted after the authority service reaches parity; confirmed by `cargo check`.", + "reuseAction" : "adapt", + "reuseSource" : "Same baseline controller/policy symbols", + "specId" : "ADR-046-provider-audio-pipewire", + "specPath" : "docs/specs/providers/ADR-046-provider-audio-pipewire.md", + "validation" : "Fast hermetic `tests/authority.rs`: AuthorityDescriptor accepted only on owner AudioService; Binding/projection rejection; duplicate conflict; multiplexed speaker mix/quota; one active mic across local/imported Zones; per-Zone FIFO and cross-Zone round-robin; one-entry-per-handle, per-Zone/total bounds and `MicQueueFull`; idempotent cancellation; contended 30-second lease; mute-before-handoff/no overlap; restart-muted rebuild; multiplexed-capture and consent/approval/priority/concurrent-verb rejection; ownerProof adoption; D091 drain/recycle with fake clock/FakeHostController", + "workItemId" : "ADR046-audio-013" + }, + { + "currentSource" : "`packages/d2b-realm-core/src/stream.rs` (`StreamKind::{AudioPlayback,AudioCapture}` → `Capability::{AudioPlayback,AudioCapture}`, `StreamAuthz`, `StreamChannel` split-direction); `packages/d2b-realm-core/src/mux.rs` (credit-based flow); `packages/d2bd/src/guest_control_bridge.rs` (`audio_set_authenticated`/`audio_status_authenticated`, `GuestAudioSetRequest`/`GuestAudioStatus`)", + "dataMigration" : "None — full d2b 3.0 reset", + "dependencyOwner" : "Depends on `ADR046-audio-013`, `ADR046-zone-control-019`; audio Provider owner", + "destination" : "`packages/d2b-provider-audio-pipewire/src/streams.rs`", + "detailedDesign" : "Per-import audio frames flow only over bounded encrypted named streams: one stream with two `StreamChannel`s for playback/capture split direction and a single `StreamAuthz` (a consumer never opens two authz contexts to split direction), credit-based backpressure (a sender spends only receiver-granted credit), per-import session generation, cancel, and deadline. `StreamKind::AudioPlayback`/`AudioCapture` require `Capability::AudioPlayback`/`AudioCapture`. Playback streams may run concurrently; the owner activates capture frames for only its single granted opaque consumer handle, and revocation/disconnect cancels that Zone's active/pending requests before route teardown. No PipeWire FD/socket crosses a Zone; intermediaries see ciphertext. Guest audio calls (`audio_set`/`audio_status`) are issued to all active guests concurrently and results aggregated. Volume/gain (`LevelPercent`), queue identity/position, and node identity are redacted from audit/OTEL/logs. Primary reuse disposition: `adapt`. Preserved source-plan detail: `adapt` — audio frames ride the existing ComponentSession named-stream credit machinery.", + "integration" : "Owner and projection AudioService adapters allocate per-import streams over ComponentSession; AudioBinding only consumes the same-Zone Service Ref; core routes encrypted records only.", + "removalProof" : "Not applicable (new surface)", + "reuseAction" : "adapt", + "reuseSource" : "Same baseline stream/mux/bridge symbols", + "specId" : "ADR-046-provider-audio-pipewire", + "specPath" : "docs/specs/providers/ADR-046-provider-audio-pipewire.md", + "validation" : "Fast hermetic `tests/streams.rs`: projection-Service ownerRef/import binding, split-direction single-authz stream, credits, generation isolation, cancel/deadline, concurrent playback, one active capture across imports, route loss cancels that Zone's active/pending capture, ciphertext-only intermediary, redaction. Only `integration/real_stream.rs` runs the slower real encrypted stream.", + "workItemId" : "ADR046-audio-014" + }, + { + "currentSource" : "`d2bd/src/provider_registry.rs`: `AzureVmForbidden`, `AZURE_VM_IMPLEMENTATION_ID`; `d2b-realm-provider/src/provider.rs`: `InfrastructureProvider` (dead-reachable)", + "dataMigration" : "Full d2b 3.0 reset; no v2 state/config import. Existing registry sentinels are deleted only after the Provider resource model replaces them.", + "dependencyOwner" : "Provider contract owner", + "destination" : "`src/{lib.rs,config.rs,schema.rs,error.rs,effect/mod.rs}`", + "detailedDesign" : "Provider descriptor/manifest; `spec.config` schema; Guest spec.provider.settings schema; `AzureEffectPort` trait + `AzureOperationHandle`; `AzureVmError` enum; `SandboxSpec` with semantic classes; `BudgetSpec` with SI suffix memory fields; `restartPolicy` class/backoffBase/backoffMax; `networkUsage.allowEgress=false`; Endpoint ResourceType templates with name/transport/purpose Primary reuse disposition: `adapt`. Preserved source-plan detail: Extract and adapt; DELETE `InfrastructureProvider` after this Provider is operational.", + "integration" : "ProviderDeployment loads the descriptor/catalog and ResourceType schemas; Nix and Guest specs reference the provider settings; controller and EffectPort modules consume the shared config/error types.", + "removalProof" : "`InfrastructureProvider` deleted; `AzureVmForbidden` removed after Provider resource model replaces registry", + "reuseAction" : "adapt", + "reuseSource" : null, + "specId" : "ADR-046-provider-runtime-azure-virtual-machine", + "specPath" : "docs/specs/providers/ADR-046-provider-runtime-azure-virtual-machine.md", + "validation" : "Provider catalog; descriptor fingerprint; schema/conformance tests", + "workItemId" : "ADR046-azure-vm-001" + }, + { + "currentSource" : "`d2b-realm-provider/src/rate_limit.rs` (implemented-and-reachable)", + "dataMigration" : "No persistent data migration; in-flight ARM operation handles are new v3 status/core-ledger records and are re-derived or adopted on reconcile when absent.", + "dependencyOwner" : "ADR046-azure-vm-001", + "destination" : "`src/effect/{mod.rs,real.rs,fake.rs,rate_limit.rs}`", + "detailedDesign" : "`AzureEffectPort` async trait; opaque `AzureOperationHandle` (bounded bytes, no poll URL); real `azure_core`/`azure_mgmt_compute` impl; `FakeAzureEffectPort` for hermetic tests; ARM 429/503/409 handling Primary reuse disposition: `adapt`. Preserved source-plan detail: Copy and adapt.", + "integration" : "Azure VM controller lifecycle/idempotency code calls `AzureEffectPort`; the real implementation talks to ARM in production and `FakeAzureEffectPort` drives hermetic lifecycle tests.", + "removalProof" : "Old `InfrastructureProvider` ARM simulation deleted after parity", + "reuseAction" : "adapt", + "reuseSource" : null, + "specId" : "ADR-046-provider-runtime-azure-virtual-machine", + "specPath" : "docs/specs/providers/ADR-046-provider-runtime-azure-virtual-machine.md", + "validation" : "`tests/lifecycle_hermetic.rs`; all ARM paths via `FakeAzureEffectPort`; no ARM URL in test assertions", + "workItemId" : "ADR046-azure-vm-002" + }, + { + "currentSource" : "`d2b-realm-provider/src/conformance.rs`; main `a1cc0b2d`: `d2b-provider-toolkit/src/reconciler_loop.rs`", + "dataMigration" : "Full d2b 3.0 reset; old WorkloadProvider lifecycle state is not imported. Existing ARM resources may be adopted by tag/idempotency checks during reconcile.", + "dependencyOwner" : "ADR046-azure-vm-001; ADR046-azure-vm-002; Guest ResourceType controller contract", + "destination" : "`src/controller/{mod.rs,lifecycle.rs,idempotency.rs}`", + "detailedDesign" : "Non-blocking reconcile: `start_*(...)` → persist `AzureOperationHandle` → `requeue-at`; `poll_lro` on subsequent ticks; controller as authorized `update-status` writer for Guest resources; finalizer held until ARM delete confirmed; top-level `phase`, `status.resource`, and Azure `status.provider.details.providerPhase` written atomically Primary reuse disposition: `adapt`. Preserved source-plan detail: Copy/adapt main toolkit; adapt conformance shape.", + "integration" : "Zone core dispatches Guest resource events to the Azure VM controller; ResourceClient updates status/finalizers; `AzureEffectPort` starts, polls, and deletes ARM LROs.", + "removalProof" : "Old `WorkloadProvider::provision`/`deprovision` paths retired", + "reuseAction" : "adapt", + "reuseSource" : null, + "specId" : "ADR-046-provider-runtime-azure-virtual-machine", + "specPath" : "docs/specs/providers/ADR-046-provider-runtime-azure-virtual-machine.md", + "validation" : "`tests/lifecycle_hermetic.rs`; `tests/conformance.rs`", + "workItemId" : "ADR046-azure-vm-003" + }, + { + "currentSource" : "`d2b-realm-provider/src/types.rs`: `ProviderGuestdBootstrapContract` (implemented-and-reachable); main `a1cc0b2d`: `d2b-session/src/bootstrap.rs`", + "dataMigration" : "No v2 bootstrap state import; the new sealed recovery Volume is initialized on first v3 activation, and old vsock bootstrap material is retired at cutover.", + "dependencyOwner" : "ADR046-azure-vm-001; ComponentSession IKpsk2", + "destination" : "`src/controller/bootstrap.rs`; `src/bootstrap_svc/{mod.rs,admission.rs,enrollment.rs}`", + "detailedDesign" : "PSK generation; sealed PSK/admission/enrollment recovery material (ciphertext) in the controller's single guest-local sealed recovery Volume; `GrantBootstrapAdmission` typed bus call; IKpsk2 in bootstrap-svc; enrollment record; enrolled KK; the bootstrap-svc declares **no** state Volume (session state in process memory; obtains sealed PSK/admission from the controller only); the controller's sealed recovery Volume is an ordinary Volume resource created by core ProviderDeployment (before component Process start) from the controller's single `stateNamespaces` declaration with a Nix-preprovisioned `User/azure-vm-controller` layout principal; ARM operation/idempotency records live in the core Operation ledger and non-secret observed cloud phase lives in `Guest.status` (D087); controller does not own, create, or add Volume to exported ResourceTypes; it consumes its view dirfd only Primary reuse disposition: `adapt`. Preserved source-plan detail: Copy/adapt main `BootstrapPsk`/`BootstrapAdmission`.", + "integration" : "Controller creates and seals recovery material in its state Volume, grants bootstrap admission over the bus, and bootstrap-svc performs IKpsk2 enrollment for Guest sessions.", + "removalProof" : "Old vsock bootstrap path removed at v3 cutover", + "reuseAction" : "adapt", + "reuseSource" : null, + "specId" : "ADR-046-provider-runtime-azure-virtual-machine", + "specPath" : "docs/specs/providers/ADR-046-provider-runtime-azure-virtual-machine.md", + "validation" : "`tests/bootstrap_hermetic.rs`; `tests/error_redaction.rs`", + "workItemId" : "ADR046-azure-vm-004" + }, + { + "currentSource" : "`d2b-realm-provider/src/credential.rs`: `AzureControlPlaneRef`, `OpaqueAzureRef`, `ManagedIdentityRef` (implemented-and-reachable)", + "dataMigration" : "No ambient credential migration; v3 requires ResourceType Credential/ManagedIdentityRef plus enrolled KK, and the old direct IMDS fallback is removed.", + "dependencyOwner" : "ADR046-azure-vm-003; Credential ResourceType; D055/D056", + "destination" : "`src/credential.rs`", + "detailedDesign" : "ARM credential via enrolled KK `AcquireToken`; zeroizing token handling; no ambient credential fallback; `credential-managed-identity` guest-agent placement Primary reuse disposition: `adapt`. Preserved source-plan detail: Retain `OpaqueAzureRef` directly; adapt credential acquisition to enrolled KK.", + "integration" : "Controller obtains ARM credentials through enrolled KK and the Credential ResourceType before EffectPort operations; the credential-managed-identity guest agent provides the token source.", + "removalProof" : "Old direct IMDS calls from controller removed", + "reuseAction" : "adapt", + "reuseSource" : null, + "specId" : "ADR-046-provider-runtime-azure-virtual-machine", + "specPath" : "docs/specs/providers/ADR-046-provider-runtime-azure-virtual-machine.md", + "validation" : "`tests/credential_hermetic.rs`; `tests/error_redaction.rs`", + "workItemId" : "ADR046-azure-vm-005" + }, + { + "currentSource" : "`d2bd/src/provider_registry.rs`: `NEXT_LIFECYCLE_OPERATION_ID: AtomicU64` (production-reachable)", + "dataMigration" : "Old `AtomicU64` operation IDs are not imported; v3 operations use deterministic keys, while missing handles are re-derived or adopted from ARM.", + "dependencyOwner" : "ADR046-azure-vm-003", + "destination" : "`src/controller/idempotency.rs`", + "detailedDesign" : "Deterministic ARM request ID derivation; `AzureOperationHandle` opaque persistence (no poll URL in state); ARM 409 adoption; finalizer held through async deletion Primary reuse disposition: `adapt`. Preserved source-plan detail: Adapt to deterministic per-Guest keys.", + "integration" : "Lifecycle controller stores deterministic request IDs and opaque handles in the core Operation ledger/status; restart recovery reads them before polling or adopting ARM operations.", + "removalProof" : "`AtomicU64` lifecycle op ID removed after all ARM callers migrate", + "reuseAction" : "adapt", + "reuseSource" : null, + "specId" : "ADR-046-provider-runtime-azure-virtual-machine", + "specPath" : "docs/specs/providers/ADR-046-provider-runtime-azure-virtual-machine.md", + "validation" : "`tests/idempotency.rs`; restart-recovery scenario", + "workItemId" : "ADR046-azure-vm-006" + }, + { + "currentSource" : "`nixos-modules/options-realms-workloads.nix`: `WorkloadProviderKind::ProviderManaged`", + "dataMigration" : "Full d2b 3.0 reset; old `d2b.realms..workloads.` config is replaced by v3 resource authoring with no automatic v2 config import.", + "dependencyOwner" : "ADR046-azure-vm-001; ADR-046-provider-state; ADR-046-nix-configuration", + "destination" : "`nixos-modules/` (Provider/Guest resource emitters); crate Nix build", + "detailedDesign" : "Nix `spec.config` shape; `controllerExecutionRef`/`networkRef` eval-time assertions; no Volume refs for data disks; `systemArtifactId=null` enforcement; the single controller sealed recovery Volume is an ordinary Volume resource created by core ProviderDeployment (not in Zone bundle; not operator-authored); the bootstrap-svc declares no state Volume; guest-local placement — reconciled by the Guest-local volume-local instance and expressed by `source.executionRef` = config gateway Guest; host MUST NOT hold ARM binding, admission, PSK, or operation state; ARM operation/idempotency records live in the core Operation ledger and non-secret observed cloud phase in `Guest.status` (D087); no virtiofs or host-to-guest attachment; manifest freezes guest-local with no fallback; controller does not create, own, or list Volume in exported ResourceTypes; `Provider/volume-local` is the sole Volume reconciler; controller consumes required view dirfd only; **the recovery Volume is `kind: state`, `persistenceClass: persistent`, `storageNeed: secret`, sealed via `sealingCredentialRef`, with nonzero `quotaBytes`, `quota.maxBytes`, `quota.maxInodes`, and `source.settings.sourcePolicyId`; `persistenceClass: ephemeral` and zero quotas are rejected**; it survives component/Provider restart and participates in upgrade/destroy/reset; full canonical Volume spec including `stateSchema`, `source`, `layout` with a Nix-preprovisioned `User/` principal (not ComponentPrincipal), `views`, `identityMarker`, `snapshotPolicy: null`, `retentionPolicy: null`; `sensitivityClass: private` and `volume-domain-mismatch` isolation enforced; canonical `SandboxSpec` fields with `namespaceClasses`/`capabilityClasses`/`seccompClass`/`noNewPrivileges`/`startRoot`/`environmentClass`/`readOnlyRoot`; `BudgetSpec` with SI suffix; `restartPolicy` class/backoffBase/backoffMax; Endpoint ResourceType templates with name/transport/purpose", + "integration" : "Nix emitters produce Provider, Guest, Volume, and Endpoint resource specs consumed by ProviderDeployment, `Provider/volume-local`, the Process Provider, and the Azure VM controller.", + "removalProof" : "`d2b.realms..workloads.` removed at v3 cutover", + "reuseAction" : "adapt", + "reuseSource" : null, + "specId" : "ADR-046-provider-runtime-azure-virtual-machine", + "specPath" : "docs/specs/providers/ADR-046-provider-runtime-azure-virtual-machine.md", + "validation" : "Nix eval tests; `make test-flake`; `make test-drift`", + "workItemId" : "ADR046-azure-vm-007" + }, + { + "currentSource" : "`d2bd/src/metrics.rs` (production-reachable)", + "dataMigration" : "No metrics/audit data migration; new OTEL/audit surfaces start at v3 cutover and the old Prometheus registry is retired.", + "dependencyOwner" : "ADR046-azure-vm-003; ADR-046-telemetry-audit-and-support", + "destination" : "`src/{telemetry.rs,audit.rs}`", + "detailedDesign" : "Closed metric labels; OTEL span attributes; audit durability classes; `azure-vm-deleted` appended post-commit; no ARM URI, ARM resource ID, or cloud endpoint in any telemetry surface Primary reuse disposition: `adapt`. Preserved source-plan detail: Adapt audit shape; replace Prometheus with d2b-telemetry emitter.", + "integration" : "Controller/error paths call telemetry and audit emitters after status commits; d2b-telemetry consumes the metrics/spans and policy_observability enforces redaction.", + "removalProof" : "`d2bd/src/metrics.rs` hand-rolled registry removed after observability-otel Provider integration", + "reuseAction" : "adapt", + "reuseSource" : null, + "specId" : "ADR-046-provider-runtime-azure-virtual-machine", + "specPath" : "docs/specs/providers/ADR-046-provider-runtime-azure-virtual-machine.md", + "validation" : "`tests/error_redaction.rs`; `d2b-contract-tests/tests/policy_observability.rs` updated", + "workItemId" : "ADR046-azure-vm-008" + }, + { + "currentSource" : "No existing Azure VM tests at baseline; fake/hermetic patterns from main `a1cc0b2d`", + "dataMigration" : "None — test-only work; no runtime state. Old mock tests are removed only after parity.", + "dependencyOwner" : "All ADR046-azure-vm-*", + "destination" : "`tests/`; `integration/`", + "detailedDesign" : "See §Test requirements Primary reuse disposition: `adapt`. Preserved source-plan detail: Copy/adapt fake toolkit; write new tests.", + "integration" : "Provider crate tests, fake toolkit, and integration harness run under cargo/Layer-1 and validate all ADR046-azure-vm-* outputs together.", + "removalProof" : "Old `InfrastructureProvider` mock tests deleted after parity", + "reuseAction" : "adapt", + "reuseSource" : null, + "specId" : "ADR-046-provider-runtime-azure-virtual-machine", + "specPath" : "docs/specs/providers/ADR-046-provider-runtime-azure-virtual-machine.md", + "validation" : "All tests pass", + "workItemId" : "ADR046-azure-vm-009" + }, + { + "currentSource" : "`d2b-host/src/runtime_provider.rs`; `d2b-host/src/ch_argv.rs`; `d2bd/src/supervisor/dag.rs`", + "dataMigration" : "None (spike)", + "dependencyOwner" : "Provider toolkit / system-minijail; W1 spike owner", + "destination" : "`packages/d2b-provider-runtime-cloud-hypervisor/src/controller.rs`", + "detailedDesign" : "End-to-end: single Guest reconcile → synchronous dependency-readiness check via ResourceClient → VMM Process creation → guest-control health check in observe handler → status write. Uses fake bus/store/supervisor stubs from toolkit. Proves fast-path latency gates (≤5 ms hint, ≤20 ms VMM Process creation when all deps ready). No EphemeralProcess resources at any step. Primary reuse disposition: `adapt`. Preserved source-plan detail: Extract and adapt.", + "integration" : "Zone ResourceClient + system-minijail Process Provider + fake broker effect", + "removalProof" : "Not applicable (new crate)", + "reuseAction" : "adapt", + "reuseSource" : null, + "specId" : "ADR-046-provider-runtime-cloud-hypervisor", + "specPath" : "docs/specs/providers/ADR-046-provider-runtime-cloud-hypervisor.md", + "validation" : "Unit: reconcile state machine, fast-path latency, adoption/ambiguity, finalize ordering. Integration: end-to-end VMM boot with real KVM and guest-control session (requires `make test-host-integration`)", + "workItemId" : "ADR046-ch-001" + }, + { + "currentSource" : "`d2b-core/src/processes.rs`; `nixos-modules/processes-json.nix`; `d2b-priv-broker/src/ops/swtpm_dir.rs`; `d2b-host/src/swtpm_argv.rs`", + "dataMigration" : "v3 reset; no v2 process graph migration", + "dependencyOwner" : "ADR046-ch-001; Volume and Device foundation", + "destination" : "`packages/d2b-provider-runtime-cloud-hypervisor/src/bootstrap_graph.rs`", + "detailedDesign" : "Single owned VMM Process resource; synchronous ResourceClient dependency check (Device/kvm + all declared Devices, Networks, virtiofs Volumes); immediate Process creation when all deps ready; no EphemeralProcess resources; conditional net-VM Guest creation; per-dependency readiness tracking in reconcile loop Primary reuse disposition: `replace`. Preserved source-plan detail: EXTRACT and REPLACE.", + "integration" : "Depends on `Provider/volume-virtiofs`, `Provider/device-tpm`, `Provider/device-kvm`, `Provider/network-local` ResourceType readiness", + "removalProof" : "`ProcessRole::CloudHypervisor`, `ProcessRole::Swtpm`, `ProcessRole::NetVm` variant callers deleted; `nixos-modules/processes-json.nix` VMM emitter deleted after parity", + "reuseAction" : "replace", + "reuseSource" : null, + "specId" : "ADR-046-provider-runtime-cloud-hypervisor", + "specPath" : "docs/specs/providers/ADR-046-provider-runtime-cloud-hypervisor.md", + "validation" : "Golden VMM Process spec vectors; dependency-ordering tests; parallel Guest tests (8 concurrent); net-VM creation tests; Device/kvm explicit-ref enforcement", + "workItemId" : "ADR046-ch-002" + }, + { + "currentSource" : "`d2b-host/src/ch_argv.rs::ChArgvInput`, `generate_ch_argv`; `tests/golden/runner-shape/cloud-hypervisor-argv-*.txt`", + "dataMigration" : "None — full d2b 3.0 reset; no prior state to migrate", + "dependencyOwner" : "ADR046-ch-002; artifact catalog foundation", + "destination" : "`packages/d2b-provider-runtime-cloud-hypervisor/src/vmm_argv.rs`; `tests/vmm_argv_golden_test.rs`", + "detailedDesign" : "`VmmArgvInput` derived from validated `GuestSpec.spec.provider.settings`; kernel/initrd/rootfs paths resolved privately from artifact catalog at dispatch time; no path in spec/status; golden tests for headless/q35/microvm/gpu/video/macvtap variants Primary reuse disposition: `adapt`. Preserved source-plan detail: COPY/ADAPT.", + "integration" : "ProviderSupervisor LaunchTicket resolution", + "removalProof" : "`d2b-host/src/ch_argv.rs::generate_ch_argv` callers removed; old golden test files adapted", + "reuseAction" : "adapt", + "reuseSource" : null, + "specId" : "ADR-046-provider-runtime-cloud-hypervisor", + "specPath" : "docs/specs/providers/ADR-046-provider-runtime-cloud-hypervisor.md", + "validation" : "Golden argv vectors matching `cloud-hypervisor-argv-*.txt` shapes with v3 adaptations; redaction test (no store path in Debug output)", + "workItemId" : "ADR046-ch-003" + }, + { + "currentSource" : "`nixos-modules/options-realms-workloads.nix`; `nixos-modules/options-vms.nix`; `nixos-modules/processes-json.nix`; `nixos-modules/store.nix`", + "dataMigration" : "`d2b.vms.` → `d2b.zones..resources.` documented in migration guide", + "dependencyOwner" : "ADR046-ch-002; nix-configuration foundation (`ADR046-identities-002`)", + "destination" : "`packages/d2b-provider-runtime-cloud-hypervisor/nix/` (Nix emitter); `nixos-modules/` option extension for `runtime-cloud-hypervisor` Guest schema", + "detailedDesign" : "`d2b.zones..resources.` with `type = \"Guest\"` and `spec.provider.settings` validated against signed Provider schema; `spec.systemArtifactId` top-level field; artifact catalog `type = \"nixos-system\"` enforced by rule 17; Guest-control `Endpoint` resource emitted without raw locator; `make test-drift` gate for schema/Nix drift Primary reuse disposition: `adapt`. Preserved source-plan detail: ADAPT and REPLACE.", + "integration" : "Zone resource bundle emission; private artifact catalog; `xtask gen-resource-nix-options` for auto-generated Nix option types", + "removalProof" : "`options-vms.nix`; `options-realms-workloads.nix` (LocalVm path); `nixos-modules/processes-json.nix` (VMM emitter); `nixos-modules/store.nix` removed after integration parity", + "reuseAction" : "adapt", + "reuseSource" : null, + "specId" : "ADR-046-provider-runtime-cloud-hypervisor", + "specPath" : "docs/specs/providers/ADR-046-provider-runtime-cloud-hypervisor.md", + "validation" : "nix-unit eval tests: rule CH-1 through CH-4 + rules 1–17; golden resource bundle JSON (no store path); type-mismatch eval errors; raw locator rejection; `spec.systemArtifactId` at top-level in JSON (not in `spec.provider.settings`)", + "workItemId" : "ADR046-ch-004" + }, + { + "currentSource" : "`packages/d2bd/src/provider_shutdown.rs::GracefulVmShutdown`; `packages/d2b-host/src/runtime_provider.rs::RuntimeProvider::plan_guest_update`", + "dataMigration" : "None — full d2b 3.0 reset; no prior state to migrate", + "dependencyOwner" : "ADR046-ch-001; ComponentSession/d2b-bus (`ADR046-session-001`)", + "destination" : "`packages/d2b-provider-runtime-cloud-hypervisor/src/health.rs`; `src/adoption.rs`", + "detailedDesign" : "Authenticated KK ComponentSession health check over vsock; adoption verification (pid/cgroup/executable/generation) within `adoptionWindow`; ambiguity → Unknown/Degraded, never broad kill; graceful shutdown via guest-control session before SIGTERM", + "integration" : "ComponentSession enrolled KK; guest bootstrap credential from `d2b-gctl` virtiofs share; `GuestReachable` condition write", + "removalProof" : "`ProcessRole::GuestControlHealth` observation path; `ProcessRole::GuestSshReadiness` deleted at cutover", + "reuseAction" : "adapt", + "reuseSource" : null, + "specId" : "ADR-046-provider-runtime-cloud-hypervisor", + "specPath" : "docs/specs/providers/ADR-046-provider-runtime-cloud-hypervisor.md", + "validation" : "Fake guest-control server test; health check timeout/failure/retry; adoption property test (ambiguity, gone, stale pid); graceful shutdown ordering", + "workItemId" : "ADR046-ch-005" + }, + { + "currentSource" : "`packages/d2bd/src/metrics.rs` (`d2b_daemon_vm_*`); `packages/d2b-contract-tests/tests/policy_observability.rs`", + "dataMigration" : "`d2b_daemon_vm_*` metrics retired; consumers must update dashboards", + "dependencyOwner" : "ADR046-ch-001; telemetry foundation (`ADR046-telem-001`)", + "destination" : "`packages/d2b-provider-runtime-cloud-hypervisor/src/metrics.rs`; `src/audit.rs`", + "detailedDesign" : "`d2b_runtime_ch_*` metrics from §18.3; bounded durable audit records from §17.3; no `vm=` metric label; no path/argv/socket in any field; closed OTEL attribute allowlist extended per §18.4", + "integration" : "Zone lightweight bounded emitter; `Provider/observability-otel` forwarding", + "removalProof" : "Hand-rolled Prometheus registry (`d2bd/src/metrics.rs` `d2b_daemon_vm_*` section) deleted after migration", + "reuseAction" : "replace", + "reuseSource" : null, + "specId" : "ADR-046-provider-runtime-cloud-hypervisor", + "specPath" : "docs/specs/providers/ADR-046-provider-runtime-cloud-hypervisor.md", + "validation" : "`policy_observability.rs` updated with v3 allowlist; cardinality tests; bounded message/field tests; audit record schema golden vectors", + "workItemId" : "ADR046-ch-006" + }, + { + "currentSource" : "`packages/d2b-core/src/storage.rs` (`StoragePathSpec`, `SensitivityClass`) — to be retired", + "dataMigration" : "v3 reset; no v2 state storage migration", + "dependencyOwner" : "ADR046-ch-001; `ADR046-pstate-001` (common status types)", + "destination" : "`packages/d2b-provider-runtime-cloud-hypervisor/src/state.rs`; `packages/d2b-provider-runtime-cloud-hypervisor/tests/state_status_test.rs`", + "detailedDesign" : "`state.rs` owns the controller's bounded non-secret operational-state projection into the owning resource's `status` subresource (reconcile stage, per-Guest launch/adoption observations, bounded counters, closed-enum error detail) — the controller declares no Provider state Volume and mounts no `/state`; on restart it re-derives observed state from the Zone resource store, the core Operation ledger, and external observation (running VMM/virtiofsd re-adopted from cgroup leaves + fresh pidfds), treating `status` as observation, never authority (D087); status writes occur only on material change and stay within the status bounds. The superseded state-Volume integration, migration, validation, and removal rows are rejected: this Provider has no state Volume, state mount, or `StateEnvelope` startup path. Primary reuse disposition: `replace`. Preserved source-plan detail: REPLACE (storage.rs).", + "integration" : "The controller reads Volume/Device/Network dependency status through its ComponentSession/ResourceClient and writes its own bounded `status`; no Provider state Volume is provisioned or mounted", + "removalProof" : "`d2b-core/src/storage.rs` `StoragePathSpec` / `SensitivityClass` retired only after all Provider state consumers migrate to v3 status/optional-Volume helpers", + "reuseAction" : "replace", + "reuseSource" : null, + "specId" : "ADR-046-provider-runtime-cloud-hypervisor", + "specPath" : "docs/specs/providers/ADR-046-provider-runtime-cloud-hypervisor.md", + "validation" : "`state_status_test.rs` (hermetic): status projection round-trip and bound enforcement; restart re-derivation from store/ledger/external observation without a state Volume; no secret/path/argv/PID in status", + "workItemId" : "ADR046-ch-007" + }, + { + "currentSource" : "`packages/d2b/src/lib.rs`: `Context::from_env` (reads `D2B_PUBLIC_SOCKET` env or `/run/d2b/public.sock` default; single flat socket path — no zone-qualified path, no `ZoneContext`), `NativeCli`, `NativeCommand`, `dispatch`, `CliFailure`, `report_failure`, `stdout_is_tty`; socket path model: old `Context { public_socket, broker_socket }` → target: `ZoneContext { zone_name, socket_path, session_client }`", + "dataMigration" : "None; context discovery replaces env-var path lookups", + "dependencyOwner" : "ADR046-identities-001, ADR046-api-001; CLI crate owner", + "destination" : "`packages/d2b/src/lib.rs`, `packages/d2b/src/context.rs`, `packages/d2b/src/dispatch.rs`", + "detailedDesign" : "Introduce `ZoneContext` (zone name, socket path, ComponentSession client); implement `--zone`/`D2B_ZONE`/nearest-socket discovery using adapted `LocalUnixDaemonAccess::connect_component_session()` chain; introduce `ResourceRef` argument parser; introduce unified `--json`/`--human`/`--deadline` flag infrastructure bounded by `MAX_REQUEST_LIFETIME_MS=900s`; freeze `--json` schema version 1; stabilize exit code table. Excluded ADR 0045 assumptions: `TargetInput::Realm`, `TargetInput::Workload`, `TargetInput::Provider` variants; `RealmPath::parse(\"local-root\")` / `RealmId::derive` pattern; `RealmPath`-based service owner types.", + "integration" : "All command functions receive `ZoneContext`; resource API client (`Client`) is injected for testing", + "removalProof" : "Old `Context` struct removed only after all command functions use `ZoneContext`", + "reuseAction" : "adapt", + "reuseSource" : "main `a1cc0b2d` — copy/adapt these exact symbols: (1) `packages/d2b-client/src/client.rs` `Client`, `ConnectedClient`, `CallOptions`, `CancellationToken`, `MetadataInput`, `RetryPolicy`, `Response` — copy unchanged as the async client foundation; (2) `packages/d2b-client/src/host_socket.rs` `HostSocketConnector::from_seqpacket_fd`, `local_daemon_endpoint_identity` — adapt: replace fixed `d2b.daemon.v2` service lookup with zone-scoped service identity; replace hardcoded `RealmPath::parse(\"local-root\")` with `Zone/`-derived target; (3) `packages/d2b-daemon-access/src/component_session.rs` `LocalUnixDaemonAccess::connect_component_session()` connect chain — adapt: replace `TargetInput::LocalRoot(realm)` with the v3 Zone target variant; adapt socket path discovery for per-Zone paths; (4) `packages/d2b-client/src/session.rs` `ComponentSessionConnector`, `ConnectedSession`, `NamedStream`, `SessionCall`, `SessionReply`, `SharedDriver` — copy unchanged; (5) `packages/d2b-client/src/target.rs` `RouteTable`, `RouteRecord`, `TargetInput`, `ResolvedTarget`, `TransportKind`, `TransportSelection` — copy unchanged, excluding `TargetInput::Realm/Workload/Provider` variants which carry ADR 0045 assumptions; (6) `packages/d2b-contracts/src/v2_component_session.rs` `LimitProfile::local_default()` constants: `MAX_REQUEST_LIFETIME_MS=900000`, `LOCAL_HANDSHAKE_DEADLINE_MS=5000`, `MAX_RECONNECT_ATTEMPTS=10`, `MAX_ACTIVE_NAMED_STREAMS=128`, `MAX_LOGICAL_MESSAGE_BYTES=1048576`, `named_stream_queue_bytes=262144`, `aggregate_named_stream_queue_bytes=4194304` — copy unchanged; these bound every CLI deadline and stream operation", + "specId" : "ADR-046-cli-and-operations", + "specPath" : "docs/specs/ADR-046-cli-and-operations.md", + "validation" : "Zone-unavailable/fallback tests; ResourceRef parse/reject vectors; exit-code round-trip tests; TTY detection tests; adapt `client.rs:typed_routes_select_exact_transport_without_fallback` (line 1053), `connector_discovers_and_authenticates_the_driver_generation` (line 1254), `daemon_transport_rejects_ancillary_data_and_oversized_packets` (line 1312)", + "workItemId" : "ADR046-cli-001" + }, + { + "currentSource" : "`packages/d2b/src/lib.rs`: `cmd_vm_start`, `cmd_vm_stop`, `cmd_vm_restart`, `cmd_vm_status`, `cmd_vm_list`, `cmd_list`, `cmd_status`; wire types: `ListResponse { vms: Vec }`, `StatusResponse { entries: Vec }`, `VmLifecycleState` (old: Stopped/Starting/Booted/Running/Stopping/Restarting/Failed/Unknown) from `packages/d2b-contracts/src/public_wire.rs:2152,2158,2605`; `ListEntry.vm: String` = `WorkloadId`; `VmStatus.lifecycle.state: VmLifecycleState` → target Guest `phase` (Pending\\|Ready\\|Succeeded\\|Degraded\\|Failed\\|Deleted\\|Unknown; Starting/Stopping/Restarting → conditions/reasons); `WorkloadPublicSummary.execution_posture: WorkloadExecutionPosture` from `public_wire.rs:267` (carries `IsolationPosture`; unsafe-local entries have `IsolationPosture::UnsafeLocal`)", + "dataMigration" : "None — full d2b 3.0 reset; no prior state to migrate", + "dependencyOwner" : "ADR046-cli-001, ADR046-api-001; CLI crate owner", + "destination" : "`packages/d2b/src/guest.rs` (`d2b guest start/stop/restart/list/status`); unsafe-local workloads go to `packages/d2b/src/host.rs` (`d2b host list/status/get`), NOT guest.rs", + "detailedDesign" : "Route Guest lifecycle (WorkloadProviderKind: LocalVm/QemuMedia/ProviderManaged) through `d2b.resource.v3` Get/UpdateSpec/Watch; map dry-run/apply to resource API precondition; `--no-wait-ready` exits on accepted; with-wait uses `d2b status --watch` loop. WorkloadProviderKind::UnsafeLocal entries MUST route to `d2b host` commands only; any code path that would return an unsafe-local entry from `d2b guest list` is a correctness violation. v2 commands (`d2b up/down/restart/list/status`, `d2b vm start/stop/restart/list/status`) are deleted at 3.0; `d2b migrate-check` explains replacements.", + "integration" : "ZoneContext → resource API client → Guest resource; status watch uses Watch stream", + "removalProof" : "Old `cmd_vm_start/stop/restart` seqpacket paths removed after Guest resource API paths are live with full test coverage", + "reuseAction" : "adapt", + "reuseSource" : "main `a1cc0b2d` — copy/adapt: (1) `packages/d2b-client/src/daemon_service.rs` `DaemonClient::lifecycle()` (line 210), `DaemonClient::list_workloads()` (line 148), `DaemonClient::inspect()` (line 179); `DaemonMethod::Apply/Start/Stop/Restart/ListWorkloads` variants (lines 31-46) — adapt: replace `WorkloadLifecycleRequest`/`WorkloadName` with `Guest/` ResourceRef; replace `TargetInput::Workload`-scoped calls with zone-root resource API calls; (2) `packages/d2b-contracts/src/generated_v2_services/daemon.rs` `WorkloadLifecycleProjection`, `DeploymentProjection`, `RuntimeProjection` — adapt field mapping to Guest resource spec/status; (3) `packages/d2b/src/lib.rs` `cmd_launch` (`LaunchArgs`) — adapt: the typed ComponentSession target resolution pattern applies but realm/workload-model types (`RealmPath`, `WorkloadName`) are excluded; behavior selected: idempotent apply with dry-run/apply precondition", + "specId" : "ADR-046-cli-and-operations", + "specPath" : "docs/specs/ADR-046-cli-and-operations.md", + "validation" : "Dry-run/apply/wait/no-wait-ready tests; zone-unavailable degraded path; JSON output schema tests; confirm v2 command paths are absent (compilation failure if any cmd_vm_start/stop alias re-introduced)", + "workItemId" : "ADR046-cli-002" + }, + { + "currentSource" : "`packages/d2b/src/exec_client.rs` (entire FSM); `packages/d2b/src/terminal_client.rs`; `packages/d2b/src/lib.rs`: `cmd_vm_exec`, `cmd_vm_exec_management`, `VmExecArgs`", + "dataMigration" : "None — full d2b 3.0 reset; no prior state to migrate", + "dependencyOwner" : "ADR046-cli-001, ADR046-api-001; CLI crate owner", + "destination" : "`packages/d2b/src/exec.rs` (`d2b exec run/attach/wait/status/list/logs/kill`)", + "detailedDesign" : "Map EphemeralProcess resource lifecycle; `exec run` creates resource and returns ref; `exec attach` opens named stream via adapted `DaemonClient::open_terminal(DaemonMethod::Exec, ...)` → `DaemonTerminal`; retain full `exec_client.rs` FSM and TTY machinery from baseline; retain `--json` envelope fields `source`/`reason`/`guestExitCode`/`signal`/`transportExitCode`; retain reserved exit codes 42/69/70/75/76/77. v2 commands (`d2b vm exec *`) are deleted at 3.0; no dispatch wiring. Excluded ADR 0045: `GuestClient` vsock/guest-control proxy path; `TargetInput::Workload`; old `WorkloadName`-keyed exec management. Primary reuse disposition: `adapt`. Preserved source-plan detail: copy-then-adapt.", + "integration" : "ZoneContext → EphemeralProcess Create → named stream attach via `DaemonClient::open_terminal`", + "removalProof" : "Old `cmd_vm_exec` seqpacket path removed after `d2b exec` paths have equivalent coverage", + "reuseAction" : "adapt", + "reuseSource" : "main `a1cc0b2d` — copy/adapt: (1) `packages/d2b-client/src/daemon_service.rs` `DaemonClient::open_terminal(method, resource_id, operation_id, selection, options, cancellation)` returning `DaemonTerminal` (line 248) — copy-then-adapt: replaces the existing seqpacket Exec call; `DaemonMethod::Exec` (line 40) maps to `d2b exec run`; (2) `packages/d2b-contracts/src/generated_v2_services/terminal.rs` `TerminalOpenRequest`, `TerminalOpenResponse`, `TerminalStreamFrame`, `TerminalSelection`, `TerminalKind` — copy unchanged as the named-stream terminal wire protocol; (3) `packages/d2b-client/src/session.rs` `NamedStream` (`send`, `receive`, `cancel`, `close`, `is_terminal`) — copy unchanged; provides async stdio routing and cancel on disconnect; (4) `packages/d2b-client/src/daemon_service.rs` `GuestClient::inspect_exec()`, `cancel_exec()`, `open_exec_retained_log()` — adapt: rename from `WorkloadName`/`GuestClient` to `EphemeralProcess/` resource API; (5) `packages/d2b-session/src/streams.rs` `NamedStreamMux` limits (`MAX_ACTIVE_NAMED_STREAMS=128`, `named_stream_queue_bytes=262144`) — copy unchanged; bounds the exec stream pipeline; (6) `packages/d2b-session/src/cancellation.rs` `Cancellation`, `RequestRegistry` — copy unchanged; provides generation-bound per-request cancel; tests to adapt/import: `client.rs:terminal_uses_server_stream_and_validates_bidirectional_lifecycle`, `terminal_rejects_response_generation_and_non_server_stream_ids`, `invalid_terminal_selection_is_rejected_before_open_rpc`, `guest_exec_management_preserves_typed_state_and_cancel_correlation`, `guest_retained_log_open_binds_range_resource_and_selection`, `named_stream_fragments_over_queue_credit_and_has_terminal_actions`, `named_stream_grants_only_consumed_data_and_releases_blocked_sender`; excluded ADR 0045 assumptions: `GuestClient` internal `TargetInput::Workload`-scoped vsock routing (guest-control proxy uses old `WorkloadName`/`RealmPath` — these are excluded; v3 routes through resource API only)", + "specId" : "ADR-046-cli-and-operations", + "specPath" : "docs/specs/ADR-046-cli-and-operations.md", + "validation" : "Full `exec_client.rs` test suite migrated; adapted tests from main `client.rs:terminal_*` and `guest_exec_*`; TTY/raw-mode/RAII/signal tests; `--json` envelope/disambiguation tests; capacity/transport/auth/protocol exit-code tests; confirm v2 `cmd_vm_exec` path is absent", + "workItemId" : "ADR046-cli-003" + }, + { + "currentSource" : "`packages/d2b/src/lib.rs`: `cmd_shell` (`ShellArgs.vm: String` = `WorkloadId` or `RealmTarget`; routes through `route_vm_target()` → `VmTargetRoute::Local\\|Gateway`; gateway `Attach` fails closed via `shell_gateway_attach_failure()` with error class `gateway-shell-attach-unavailable` at lib.rs:1697,1780), `cmd_shell_attach`, `run_shell_fsm`, `ShellOwnerTransport`; wire: `ShellOp`, `ShellOpResponse`, `ShellAttachArgs { vm: String }`, `ShellListEntry`, `ShellSessionState` from `packages/d2b-contracts/src/public_wire.rs:1319,1394,1452,1409`; `exec_client.rs` signal/TTY machinery", + "dataMigration" : "None — full d2b 3.0 reset; no prior state to migrate", + "dependencyOwner" : "ADR046-cli-001, `shell-terminal` Provider dossier; CLI crate owner", + "destination" : "`packages/d2b/src/shell.rs` (`d2b shell open/attach/list/detach/kill/status`)", + "detailedDesign" : "Route ShellSession resource lifecycle through resource API using adapted `ShellService` generated types; `shell open` → `ShellCreate` → `DaemonClient::open_terminal(Shell)` → `DaemonTerminal`; retain FSM/TTY/signal/RAII behavior from `run_shell_fsm`; `--name` required for kill; SIGHUP detaches without kill. v2 commands (`d2b shell *`) are deleted at 3.0; no dispatch wiring. Excluded: gateway relay path (`VmTargetRoute::Gateway`); old `ShellOp`/`ShellOpResponse` seqpacket protocol; `TargetInput::Workload`-keyed realm routing. Primary reuse disposition: `adapt`. Preserved source-plan detail: copy-then-adapt.", + "integration" : "ZoneContext → ShellSession Create via `DaemonClient::open_terminal` → `NamedStream` I/O", + "removalProof" : "Old `cmd_shell` seqpacket path removed after new shell commands have equivalent coverage", + "reuseAction" : "adapt", + "reuseSource" : "main `a1cc0b2d` — copy/adapt: (1) `packages/d2b-client/src/daemon_service.rs` `DaemonClient::open_terminal(DaemonMethod::Shell, ...)` returning `DaemonTerminal` — copy-then-adapt; same `TerminalOpenRequest`/`TerminalOpenResponse`/`DaemonTerminal` flow as cli-003, applied to shell open/attach; (2) `packages/d2b-contracts/src/generated_v2_services/shell.rs` and `shell_ttrpc.rs` `ShellService` methods: `ShellCreate`, `ShellAttach`, `ShellDetach`, `ShellList`, `ShellInspect`, `ShellKill`, `ShellCancel` (service definition); `ShellCreateRequest`/`ShellAttachRequest`/`ShellListResponse`/`ShellInspectResponse` — copy-then-adapt: these are the target ShellSession resource CRUD wire types; adapt field names from `workload_id`/`shell_name` to `Guest/` ResourceRef; (3) `packages/d2b-client/src/session.rs` `NamedStream` — copy unchanged; used for shell I/O stream; (4) `packages/d2b-session/src/cancellation.rs` `Cancellation`, `RequestRegistry` — copy unchanged; (5) `packages/d2b-session/src/deadline.rs` `DeadlineBudget` — copy unchanged; shell sessions use per-operation deadline tracking; tests to adapt/import: `client.rs:shell_management_uses_typed_selection_result_and_terminal_outcome`, `named_stream_fragments_over_queue_credit_and_has_terminal_actions`, `named_stream_grants_only_consumed_data_and_releases_blocked_sender`, `concurrent_named_streams_route_events_without_cross_consumption`; excluded ADR 0045 assumptions: `VmTargetRoute::Gateway` shell routing and `realm_router` relay path; old `ShellOp`/`ShellOpResponse` seqpacket wire; unsafe-local helper shell protocol v2", + "specId" : "ADR-046-cli-and-operations", + "specPath" : "docs/specs/ADR-046-cli-and-operations.md", + "validation" : "Shell list/detach/kill/attach unit tests (adapted from existing); adapted `client.rs:shell_management_*` and `named_stream_*` tests; TTY RAII/signal tests; confirm v2 `cmd_shell` path is absent", + "workItemId" : "ADR046-cli-004" + }, + { + "currentSource" : "`packages/d2b/src/lib.rs`: `cmd_audio`, `cmd_clipboard_arm`, `cmd_vm_display`", + "dataMigration" : "None — full d2b 3.0 reset; no prior state to migrate", + "dependencyOwner" : "ADR046-cli-001, Provider model spec; CLI crate owner", + "destination" : "`packages/d2b/src/provider.rs` (`d2b provider list/get/status/inspect`; dynamic projection loading)", + "detailedDesign" : "`d2b provider list/get/status/inspect`; InspectSchema call returns dynamic projection descriptor using `ConnectedClient::invoke` with generated provider service types; projection bounds enforcement (64 KiB, 32 sub-verbs, 2s deadline, shell-escape, newline strip); define the single canonical built-in command registry consumed by parser/help/completion and Provider projection binding; make bind-time rejection the sole authority for built-in and Provider/Provider top-level-name collisions, with no dispatch fallback; render projected commands from their declared top-level subcommand names; audio/clipboard/display are the first providers to migrate their projections", + "integration" : "ZoneContext → Provider resource + InspectSchema via `ConnectedClient::invoke`", + "removalProof" : "Built-in `cmd_audio`/`cmd_clipboard_arm`/`cmd_vm_display` removed only after Provider projection paths pass equivalence tests", + "reuseAction" : "adapt", + "reuseSource" : "main `a1cc0b2d` — copy/adapt: (1) `packages/d2b-provider/src/rpc.rs` `RpcProviderProxy` — adapt: the CLI uses the inverse side (client calling provider via `ConnectedClient::invoke`); `RpcCall`, `RpcPayload`, `RpcResponse`, `RpcOperation` define the typed call shape for dynamic provider commands; (2) `packages/d2b-contracts/src/generated_v2_services/provider_runtime.rs`, `provider_display.rs`, `provider_audio.rs`, `provider_infrastructure.rs` — adapt: the generated service method types show what CLI projection verbs can be mapped to typed service calls; use as shape reference for the first audio/clipboard/display migration; (3) `packages/d2b-provider-toolkit/src/conformance.rs` `check_provider_conformance`, `check_descriptor_conformance` — copy-then-adapt into CLI-side projection conformance validation (bounds: 64 KiB, 32 sub-verbs, 2s deadline, shell-escape, newline strip); (4) `packages/d2b-provider-toolkit/src/server.rs` `GeneratedProviderServiceServer::generated_services()` — server-side only; use as reference for what CLI InspectSchema receives; excluded ADR 0045 assumptions: `ProviderRegistry`/`ProviderAgentAdapter` are server-side and not used in CLI; `RpcProviderProxy` internal `AuthenticatedProviderRpc` pattern is server-side; tests to adapt: `conformance.rs:every_axis_passes_identical_in_process_and_rpc_conformance`, `generated_server_dispatches_closed_methods_over_authenticated_session`", + "specId" : "ADR-046-cli-and-operations", + "specPath" : "docs/specs/ADR-046-cli-and-operations.md", + "validation" : "Projection size/name/timeout bounds tests; registry-completeness test asserting exact equality with all built-in parser commands; bind-time built-in and Provider/Provider collision rejection tests; negative test proving no collision fallback dispatch exists; audio/clipboard/display projection conformance tests asserting `d2b audio ...`, `d2b clipboard ...`, and `d2b display ...` rendering; completion script safety tests; adapted `conformance.rs` tests", + "workItemId" : "ADR046-cli-005" + }, + { + "currentSource" : "None (no completion exists in v3 baseline)", + "dataMigration" : "None — full d2b 3.0 reset; no prior state to migrate", + "dependencyOwner" : "ADR046-cli-001; CLI crate owner", + "destination" : "`packages/d2b/src/complete.rs` (`d2b complete bash/zsh/fish`)", + "detailedDesign" : "`d2b complete ` emits completion script; uses clap `CommandFactory::command()` plus dynamic projection fetch (2s per-Provider, 10s total); result bounded at 256 KiB; shell-escaped; newlines stripped Primary reuse disposition: `adapt`. Preserved source-plan detail: copy-unchanged (clap_complete).", + "integration" : "Standalone command; no Zone API required for static completion; Zone API used for dynamic Provider projection", + "removalProof" : "Not applicable", + "reuseAction" : "adapt", + "reuseSource" : "Optional: clap_complete crate (version to be pinned); no main-branch source", + "specId" : "ADR-046-cli-and-operations", + "specPath" : "docs/specs/ADR-046-cli-and-operations.md", + "validation" : "Completion script tests (bash/zsh/fish syntax valid); projection injection safety tests; deadline/partial-Provider tests", + "workItemId" : "ADR046-cli-006" + }, + { + "currentSource" : "`packages/d2b/src/lib.rs`: `cmd_build`, `cmd_generations`, `cmd_switch`, `cmd_boot`, `cmd_test`, `cmd_rollback`, `cmd_gc`, `cmd_migrate`, `cmd_keys_*`, `cmd_keys_trust`, `cmd_keys_rotate_known_host`, `ConfigCommand` variants", + "dataMigration" : "None — full d2b 3.0 reset; no prior state to migrate", + "dependencyOwner" : "ADR046-cli-001; CLI/activation Provider owner", + "destination" : "`packages/d2b/src/activation.rs` (`d2b activation build/generations/switch/boot/test/rollback/gc/migrate/keys/trust/rotate-known-host/config`)", + "detailedDesign" : "Route through `activation-nixos` Provider service via `ConnectedClient::invoke` using adapted `DaemonMethod::Apply`/lifecycle dispatch pattern; retain dry-run/apply; retain guest-control transport for config sync (no SSH). v2 top-level activation commands (`d2b build/switch/boot/test/rollback/gc/migrate/keys/trust/rotate-known-host/config`) are deleted at 3.0; no dispatch wiring.", + "integration" : "ZoneContext → activation-nixos Provider service → resource API via `ConnectedClient::invoke`", + "removalProof" : "Old top-level activation verbs removed only after `d2b activation *` paths have equivalent coverage", + "reuseAction" : "adapt", + "reuseSource" : "main `a1cc0b2d` — copy/adapt: (1) `packages/d2b-client/src/daemon_service.rs` `DaemonClient::lifecycle()` with `DaemonMethod::Apply`, `DaemonMethod::Start`, `DaemonMethod::Stop`, `DaemonMethod::Restart` — copy-then-adapt: the apply/lifecycle dispatch pattern maps cleanly to `d2b activation switch/boot/test/rollback`; retain idempotency token and dry-run/apply precondition from `DaemonMethod::Apply`; (2) `packages/d2b-contracts/src/generated_v2_services/activation.rs` activation service method types — adapt: map `ActivationBuildRequest`, `ActivationSwitchRequest`, `ActivationGenerationsRequest` to typed CLI args; excluded ADR 0045 assumptions: `DaemonMethod::ListRealms` / `DaemonMethod::ListWorkloads` are not used; old `WorkloadName`-keyed dispatch is excluded", + "specId" : "ADR-046-cli-and-operations", + "specPath" : "docs/specs/ADR-046-cli-and-operations.md", + "validation" : "All existing switch/boot/test/rollback/keys tests adapted; config sync/diff/approve/reject tests; confirm v2 top-level activation paths are absent; adapted `client.rs:daemon_typed_list_preserves_projection_and_truncation` apply pattern", + "workItemId" : "ADR046-cli-007" + }, + { + "currentSource" : "`packages/d2b/src/lib.rs`: `cmd_host_check`, `cmd_host_prepare`, `cmd_host_destroy`, `cmd_host_doctor`, `cmd_host_install`, `cmd_host_reconcile`, `cmd_host_validate`; `host_validate.rs`", + "dataMigration" : "None — full d2b 3.0 reset; no prior state to migrate", + "dependencyOwner" : "ADR046-cli-001; CLI crate owner", + "destination" : "`packages/d2b/src/host.rs` (all `d2b host` subcommands)", + "detailedDesign" : "Route `host prepare/destroy` through Zone resource API Host reconcile operation via `ConnectedClient::invoke`; retain broker-mediated mutation and ownership-marker semantics; `host doctor` prefers Zone resource API status, falls back to local state files; `host check` retains exit-code 3; `host validate` retains wave/evidence-dir/scripts-dir/signature", + "integration" : "ZoneContext → Host resource; broker op path retained for emergency/shutdown-hook", + "removalProof" : "Raw broker-socket paths removed only after Host resource API routes have equivalent coverage", + "reuseAction" : "adapt", + "reuseSource" : "main `a1cc0b2d` — copy/adapt: (1) `packages/d2b-daemon-access/src/component_session.rs` `LocalUnixDaemonAccess::connect_component_session()` connect chain — adapt: the zone-local host commands use the same connect chain as cli-001; `d2b host prepare` and `d2b host doctor` both require a live `ZoneContext`; (2) `packages/d2b-client/src/client.rs` `ConnectedClient::invoke()` with `CallOptions` and `CancellationToken` — copy unchanged; used for Host resource Get/UpdateSpec/Status calls and broker-op dispatch; (3) `packages/d2b-contracts/src/generated_v2_services/broker.rs` broker operation request/response types — adapt: `BrokerHostPrepareRequest`, `BrokerHostDestroyRequest`, `BrokerHostDoctorRequest` (or equivalent) types define the CLI argument shape; retain broker-mediated ownership-marker semantics from baseline; excluded ADR 0045 assumptions: `TargetInput::Workload`-scoped broker routing is excluded; broker operation routing uses zone-root LocalRoot pattern only", + "specId" : "ADR-046-cli-and-operations", + "specPath" : "docs/specs/ADR-046-cli-and-operations.md", + "validation" : "All existing host-check/prepare/destroy/doctor/install/reconcile/validate tests; exit-code 3 regression; doctor Zone-fallback/local-state-fallback tests", + "workItemId" : "ADR046-cli-008" + }, + { + "currentSource" : "`packages/d2b/src/lib.rs`: `cmd_realm_list` (reads static `realm-entrypoints.json` via `realm_policy_rows_raw()`), `cmd_realm_inspect`, `cmd_realm_enter` (→ `realm_gateway_exec_args` → `cmd_vm_exec` with `-it bash -l`), `cmd_realm_run` (→ `cmd_vm_exec` with caller argv); wire output types: `RealmListOutputV1 { realms: Vec }`, `RealmInspectOutputV1 { realm: RealmPolicyOutputV1 }` from `packages/d2b-contracts/src/cli_output.rs:285,292,345`; `RealmPolicyOutputV1` fields: `realm` (= `RealmId`), `mode`, `gateway_vm`, `gateway_target`, `gateway_state`, `cross_realm_policy`, `credential_boundary`; `target_routing.rs`: `Route::Local { vm }`, `Route::Gateway { gateway, target }`, `resolve_access_route()`, `VmTargetRoute`; `d2b-realm-router::RealmEntrypointTable`", + "dataMigration" : "None — full d2b 3.0 reset; no prior state to migrate", + "dependencyOwner" : "ADR046-cli-001; CLI crate owner", + "destination" : "`packages/d2b/src/zone.rs` (`d2b zone get/list/status`)", + "detailedDesign" : "`d2b zone get []` fetches Zone self resource via `ConnectedClient::invoke`; `d2b zone list` lists ZoneLink resources. v2 commands (`d2b realm list/inspect/enter/run`) are deleted at 3.0; no dispatch wiring. Excluded ADR 0045: `RealmServiceServer`/`RealmServiceProcess` multi-realm service; `RemoteNodeRegistration` constellation routing; `TargetInput::Realm`; `RealmMethod::ResolveRoute`/`AuthorizeShortcut`/`RevokeShortcut`.", + "integration" : "ZoneContext → Zone resource Get/List via `ConnectedClient::invoke`", + "removalProof" : "`cmd_realm_*` and `target_routing.rs` removed only after zone routes pass equivalence tests", + "reuseAction" : "adapt", + "reuseSource" : "main `a1cc0b2d` — reference only (no copy): `packages/d2b-realm-router/src/service_v2.rs` `RealmServiceServer`, `RealmServiceProcess`, `RealmMethod::Inspect`, `RealmMethod::ResolveRoute` — server-side multi-realm routing; this is the ADR 0045 multi-Zone topology and is **excluded** from v3 CLI as a direct reuse source; `packages/d2b-realm-router/src/remote_node.rs` `RemoteNodeRegistration`, `RemoteNodeEntry` — constellation remote routing; also excluded; note: `packages/d2b-client/src/daemon_service.rs` `DaemonClient::list_workloads()` and `DaemonMethod::ListRealms` are the closest live list-call patterns, but their zone/workload scoping uses `RealmPath`/`RealmId` types that are ADR 0045-specific; adapt `ConnectedClient::invoke()` with a v3 Zone List request type instead; no main symbols are copied unchanged for cli-009; the zone resource API type design is an ADR-only deliverable pending Zone resource spec", + "specId" : "ADR-046-cli-and-operations", + "specPath" : "docs/specs/ADR-046-cli-and-operations.md", + "validation" : "Zone get/list tests; confirm v2 `cmd_realm_*` paths are absent", + "workItemId" : "ADR046-cli-009" + }, + { + "currentSource" : "None — net-new v3 work; no pre-ADR45 baseline equivalent", + "dataMigration" : "None — full d2b 3.0 reset; no prior state to migrate", + "dependencyOwner" : "ADR046-cli-001; CLI crate owner", + "destination" : "`packages/d2b/src/resource.rs` (standard `d2b get/list/watch/create/update-spec/delete/status` top-level verbs)", + "detailedDesign" : "Generic typed dispatch to resource API Get/List/Watch/Create/UpdateSpec/Delete using `ConnectedClient::invoke` (Get/List/Create/UpdateSpec/Delete) and `ConnectedClient::open_server_stream` + `NamedStream` (Watch); ResourceRef argument parsing and validation; page token pagination; `--phase`/`--label-selector` filters; `--deadline` bounded by `MAX_REQUEST_LIFETIME_MS=900s` via `DeadlineBudget`; Watch output streams resource events as JSON lines; JSON schema version field; `CancellationToken` wired to process signal handlers. Excluded: `GuestClient` vsock exec/shell routing; `TargetInput` realm/workload/provider variants. Primary reuse disposition: `adapt`. Preserved source-plan detail: copy-then-adapt.", + "integration" : "ZoneContext → `ConnectedClient` → resource API", + "removalProof" : "Not applicable (new surface)", + "reuseAction" : "adapt", + "reuseSource" : "main `a1cc0b2d` — copy/adapt: (1) `packages/d2b-client/src/client.rs` `ConnectedClient::invoke()`, `ConnectedClient::invoke_with_attachments()`, `ConnectedClient::open_server_stream()` — copy unchanged; these are the three primitives for resource Get/List/Watch respectively; (2) `packages/d2b-client/src/session.rs` `NamedStream` (`send`, `receive`, `cancel`, `close`, `is_terminal`) — copy unchanged; Watch stream output arrives over a named stream; (3) `packages/d2b-session/src/deadline.rs` `DeadlineBudget` — copy unchanged; `--deadline` flag maps to `DeadlineBudget::admit_metadata` wall deadline; `MAX_REQUEST_LIFETIME_MS=900000` caps all Watch/List deadlines; (4) `packages/d2b-client/src/client.rs` `CancellationToken::cancel()` — copy unchanged; `SIGINT`/SIGTERM → `CancellationToken::cancel()` → propagated to `ConnectedClient::invoke` and `NamedStream`; (5) `packages/d2b-client/src/client.rs` `MetadataInput`, `RetryPolicy`, `CallOptions` — copy unchanged; `--idempotency-token` maps to `MetadataInput`; `RetryPolicy::mutating_once()` is the default for Create/UpdateSpec/Delete; tests to adapt/import: `client.rs:metadata_retries_and_cancellation_use_canonical_driver`, `mutating_retries_require_stable_idempotency`, `concurrent_named_streams_route_events_without_cross_consumption`, `named_stream_grants_only_consumed_data_and_releases_blocked_sender`; excluded ADR 0045 assumptions: `TargetInput::Workload/Realm/Provider` routing variants; `GuestClient` cross-realm proxy routing; old `DeploymentProjection`/`RuntimeProjection` ADR 0045-specific field types", + "specId" : "ADR-046-cli-and-operations", + "specPath" : "docs/specs/ADR-046-cli-and-operations.md", + "validation" : "Get/list/watch/create/update-spec/delete tests per ResourceType; pagination/filter/watch-deadline tests; error-class/exit-code tests; adapted `client.rs:metadata_retries_*` and `mutating_retries_*` and `concurrent_named_streams_*` tests", + "workItemId" : "ADR046-cli-010" + }, + { + "currentSource" : "Nix emitters: `nixos-modules/options-realms-workloads.nix` (current `d2b.envs..vms..*`), `nixos-modules/options-realms.nix` (`d2b.realms.*`), `nixos-modules/unsafe-local-workloads-json.nix` (unsafe-local source), `nixos-modules/bundle-artifacts.nix`, `nixos-modules/manifest.nix`, `nixos-modules/assertions.nix`; JSON output: `/etc/d2b/processes.json` (old bundle), `/etc/d2b/realm-entrypoints.json` (static realm index); Zone runtime apply path: `packages/d2bd/src/` (activation apply handler — pre-ADR 0046 path through `cmd_host_prepare`/broker; no live resource bundle apply); cleanup: no current resource-deletion-on-bundle-apply path at baseline", + "dataMigration" : "Full reset from current manifest/processes/realm-entrypoints JSON format; prior Nix-generated artifacts (`/etc/d2b/processes.json`, `/etc/d2b/realm-entrypoints.json`) deleted after Zone resource bundle activates", + "dependencyOwner" : "ADR046-identities-002, ADR046-cli-001, ADR046-cli-002, ADR046-cli-007; Nix module owner + Zone runtime owner", + "destination" : "Nix: `nixos-modules/options-zones.nix` (unified `d2b.zones..resources` attrset; per-type `spec` sub-options generated from ResourceTypeSchema/Provider schema), `nixos-modules/bundle-emit.nix` (canonical JSON emit + SHA256 pin), `nixos-modules/assertions.nix` (updated); core controller: `packages/d2b-core-controller/src/configuration.rs`, `packages/d2b-core-controller/src/cleanup.rs`; Contracts: `packages/d2b-contracts/src/zone_bundle.rs` (new)", + "detailedDesign" : "**Nix shape:** `d2b.zones..resources` is `attrsOf (submodule { type; optional metadata { ownerRef; labels; annotations }; spec })`. `spec` sub-options per `type` are generated from ResourceTypeSchema and signed Provider schemas; field names remain identical. `metadata.name`/`metadata.zone`/`apiVersion` are derived; status and all core metadata are rejected in input. Vendor-qualified types are admitted only when their schema is installed. **Nix emit:** `bundle-emit.nix` emits `/etc/d2b/zones//resource-bundle.json` plus its integrity pin with canonical resource ordering and schema digests. **Core-controller apply:** `configuration.rs` verifies bundle/catalog integrity, applies Create/Update/no-op intents with bounded async concurrency, refreshes `configurationGeneration` for unchanged configuration-managed resources without waking their controller, handles controller/API name collisions per-item without seizing them, and asynchronously deletes only persisted `managedBy=configuration` resources absent from the new configured set. `cleanup.rs` consumes `Deleted` revision watches and maintains `PendingCleanup`; it never force-removes finalizers. **Prior generation retention:** `d2b.zones..retainedGenerations`, default 3 and range 1–16, is a compiler setting outside `Zone.spec`; no TTL. Rollback reapplies a retained bundle as a new higher generation.", + "integration" : "Nix build → per-Zone `resource-bundle.json` + global private artifact catalog → `d2b activation switch` → `d2b-core-controller` configuration service → resource API Create/Update/Delete → owner controllers → finalizer cascade → cleanup watcher → Zone status update", + "removalProof" : "Old `nixos-modules/manifest.nix`, `nixos-modules/bundle-artifacts.nix` emitters removed only after `bundle-emit.nix` produces equivalent-or-superseding output and all downstream consumers of the old bundle format are migrated", + "reuseAction" : "replace", + "reuseSource" : null, + "specId" : "ADR-046-cli-and-operations", + "specPath" : "docs/specs/ADR-046-cli-and-operations.md", + "validation" : "Runtime integration: all CLI-visible cleanup/status/rollback/gc/audit tests (§CLI-visible tests for activation and cleanup), including no force-finalizer path; Nix unit and build tests owned by ADR-046-nix-configuration spec", + "workItemId" : "ADR046-cli-011" + }, + { + "currentSource" : "None — net-new v3 work; `Endpoint` is a new standard ResourceType added by D092; no pre-ADR45 baseline equivalent", + "dataMigration" : "None — full d2b 3.0 reset; no prior state to migrate", + "dependencyOwner" : "ADR046-cli-001, ADR046-cli-010; CLI crate owner", + "destination" : "`packages/d2b/src/endpoint.rs` (`d2b endpoint get/list/watch/status/resolve`)", + "detailedDesign" : "Add `Endpoint` to the frozen standard ResourceType set (17 types; local ResourceRef parser accepts `Endpoint/` with no Zone round-trip). Implement `d2b endpoint get/list/watch/status` over the ADR046-cli-010 generic verbs and `d2b endpoint resolve ` as a provider-neutral resolution projection printing `producerRef`, `endpointClass`, transport class, readiness, and capability/locality observations. Endpoint output is the base envelope only (base spec + base status per D092); any `status.provider` projection is bounded, redacted, and deny-unknown. **No raw locator** (path/address/CID/port/fd/credential) appears in any CLI field or the `resolve` projection. `status.update` currency and `d2b upgrade Endpoint/` visibility follow the standard verbs and D091. `create`/`update-spec`/`delete` are rejected on the operator surface except for statically Nix/API-authored Endpoints whose schema permits it.", + "integration" : "ZoneContext → `ConnectedClient` → resource API for `Endpoint`; consumers reference endpoints only through `Endpoint/` ResourceRefs", + "removalProof" : "Not applicable (new surface)", + "reuseAction" : "adapt", + "reuseSource" : "main `a1cc0b2d` — copy/adapt: (1) `packages/d2b-client/src/client.rs` `ConnectedClient::invoke()` and `ConnectedClient::open_server_stream()` — copy unchanged; used for Endpoint Get/List/Status and Watch; (2) `packages/d2b-client/src/session.rs` `NamedStream` — copy unchanged; Watch stream output; (3) the ADR046-cli-010 generic resource-verb dispatch — reuse for `d2b endpoint get/list/watch/status`; the `d2b endpoint resolve` projection verb is net-new. No raw transport/FD handle type is imported into the CLI; resolution to a private transport/FD remains inside Core/ProviderSupervisor via EffectPort/LaunchTicket", + "specId" : "ADR-046-cli-and-operations", + "specPath" : "docs/specs/ADR-046-cli-and-operations.md", + "validation" : "`Endpoint` accepted as a frozen standard type; `Endpoint/` parses locally without a Zone round-trip; `d2b endpoint get/list/watch/status/resolve` parse and route correctly; `get`/`resolve` output carries no raw path/address/CID/port/fd/credential; provider status projection is bounded/redacted; `d2b list Endpoint --updates` and `d2b upgrade Endpoint/` expose `status.update` currency and disruption; operator `create`/`update-spec`/`delete` rejected for controller-owned Endpoints", + "workItemId" : "ADR046-cli-012" + }, + { + "currentSource" : "None — net-new v3 work; `ResourceExport`/`ResourceImport` are new standard ResourceTypes added by D096; no pre-ADR45 baseline equivalent", + "dataMigration" : "None — full d2b 3.0 reset; no prior state to migrate", + "dependencyOwner" : "ADR046-cli-001, ADR046-cli-010, ADR046-zone-control-019; CLI crate owner", + "destination" : "`packages/d2b/src/share.rs` (`d2b export …` and `d2b import …` nouns)", + "detailedDesign" : "Add `ResourceExport` and `ResourceImport` to the frozen 19-type standard set while leaving qualified semantic/provider-neutral Service/Binding types dynamically cataloged. Implement export/import generic verbs, `d2b import projection`, and `d2b import graph`. Export accepts only a factory-bound owner Service. Import status exposes the same-semantic-type projection Service and verified factory/schema fingerprints; local `providerRef` selects the implementation. Graph renders import → projection Service → authored Bindings → controller-owned Process/Endpoint children with deterministic ownership/dependency edges. Binding spec is desired intent only and observations are status-only. Reject Device/Endpoint/Binding export targets, cross-Zone refs, absent/mismatched factory/type/fingerprints, implementation detail in the base, forbidden Providers, USB policy denial, unauthorized Zones, and capability-ceiling violations. No backing/remote refs, internal sessions/streams, FD, secret, path, locator, or bytes in output. Delete waits for authored Bindings to be removed/retargeted and never cascades them.", + "integration" : "ZoneContext → resource API; signed Provider factory catalog; owner/dependency indexes for graph rendering; consumers use authored Binding → local projection Service, never ResourceImport directly", + "removalProof" : "Not applicable (new surface)", + "reuseAction" : "adapt", + "reuseSource" : "ADR046-cli-010 generic resource-verb dispatch; `packages/d2b-client/src/client.rs` `ConnectedClient::invoke`/`open_server_stream` — copy unchanged", + "specId" : "ADR-046-cli-and-operations", + "specPath" : "docs/specs/ADR-046-cli-and-operations.md", + "validation" : "Both standard types parse locally; qualified semantic Service/Binding resolve dynamically; verbs route; Service-only export and factory/type/fingerprint/policy/capability rejections; projection preserves semantic Service type across local implementation selection; canonical minimal base works without `spec.provider`; graph shape exact and bounded; Binding never auto-created/deleted; Binding spec intent-only/status observations; no implementation detail/backing/remote-ref/session/stream/FD/secret/path/locator/bytes leakage", + "workItemId" : "ADR046-cli-013" + }, + { + "currentSource" : "None — net-new v3 work; no pre-ADR45 baseline equivalent for the provider crate skeleton", + "dataMigration" : "None — docs/tooling only; no runtime state", + "dependencyOwner" : "ADR-046-provider-model-and-packaging; Provider/clipboard-wayland crate owner", + "destination" : "packages/d2b-provider-clipboard-wayland/ with src, tests, integration, README.md, and binaries clipboard-controller, clipd-host, picker-session", + "detailedDesign" : "Create the provider crate skeleton, required source layout, three binaries, and README covering purpose, component map, local build instructions, test commands, and display-wayland fake dependency for integration tests.", + "integration" : "Workspace package manifest and Provider artifact catalog consume the crate; provider packaging registers component templates for core ProviderDeployment.", + "removalProof" : "None — net-new; no prior owner to remove", + "reuseAction" : "create", + "reuseSource" : null, + "specId" : "ADR-046-provider-clipboard-wayland", + "specPath" : "docs/specs/providers/ADR-046-provider-clipboard-wayland.md", + "validation" : "Workspace provider layout policy plus README content review and follow-on make test-rust -p d2b-provider-clipboard-wayland once implementation exists.", + "workItemId" : "ADR046-clipboard-001" + }, + { + "currentSource" : "packages/d2b-clipd/ algorithms and types listed in the Reuse from baseline section", + "dataMigration" : "Full d2b 3.0 reset; clipboard history is bounded process memory and no v2 clipboard runtime state is imported", + "dependencyOwner" : "ADR046-clipboard-001; clipd-host service owner", + "destination" : "packages/d2b-provider-clipboard-wayland/src/clipd_host/ service binary modules such as service, display_client, bridge, picker_coord, policy, fd, audit, history", + "detailedDesign" : "Adapt d2b-clipd into clipd-host: call RequestPickerSession over d2b.clipboard.picker-coord.v3, consume d2b.display.host-clipboard.v3 instead of WAYLAND_DISPLAY or NIRI_SOCKET, serve d2b.clipboard.bridge.v3 on Endpoint/clipboard-bridge, remove filesystem bridge and peer group ACL logic, and preserve MIME allowlist, FD safety, fail-closed audit, loop suppression, and LRU history algorithms. Primary reuse disposition: `adapt`. Preserved source-plan detail: port and adapt algorithms; replace direct compositor, picker subprocess, Unix bridge, SO_PEERCRED, bridge directory, and group ACL paths.", + "integration" : "Core creates Process/clipd-host; display-wayland wayland-proxy consumes Endpoint/clipboard-bridge; clipd-host consumes display-wayland host-clipboard service and controller picker coordination service.", + "removalProof" : "Baseline picker subprocess, direct compositor/Niri clients, Unix bridge socket server, bridge directories, SO_PEERCRED peer config, and per-Guest groups are absent from the provider crate and covered by invariant tests.", + "reuseAction" : "adapt", + "reuseSource" : null, + "specId" : "ADR-046-provider-clipboard-wayland", + "specPath" : "docs/specs/providers/ADR-046-provider-clipboard-wayland.md", + "validation" : "make test-rust -p d2b-provider-clipboard-wayland plus unit coverage for MIME policy, FD safety, audit fail-closed queue, history bounds, lifecycle purge and suspension, no filesystem bridge, and no bytes in status or audit.", + "workItemId" : "ADR046-clipboard-002" + }, + { + "currentSource" : "None — net-new v3 controller; no pre-ADR45 baseline equivalent", + "dataMigration" : "Full d2b 3.0 reset; no controller durable state import because ProviderStateSet is empty", + "dependencyOwner" : "ADR046-clipboard-001; clipboard-controller owner", + "destination" : "packages/d2b-provider-clipboard-wayland/src/controller/ and clipboard-controller binary", + "detailedDesign" : "Implement Process/clipboard-controller as a system-domain system-minijail Process serving d2b.clipboard.picker-coord.v3, creating picker EphemeralProcesses from the signed picker-session template, observing picker status, relaying Guest lifecycle messages to clipd-host, creating clipboard RBAC Roles and RoleBindings, writing only bounded redacted operational observations through the optimistic status writer, and never owning or mounting Provider state Volumes. Primary reuse disposition: `create`. Preserved source-plan detail: net-new controller using existing resource API and ComponentSession contracts.", + "integration" : "Core ProviderDeployment creates the controller Process; controller uses Zone resource API for EphemeralProcess and RBAC resources and ComponentSession to clipd-host for picker and purge or suspend coordination.", + "removalProof" : "None — net-new; no prior controller owner to remove", + "reuseAction" : "create", + "reuseSource" : null, + "specId" : "ADR-046-provider-clipboard-wayland", + "specPath" : "docs/specs/providers/ADR-046-provider-clipboard-wayland.md", + "validation" : "Controller unit tests for picker request validation, EphemeralProcess spec shape, terminal status callback, GuestStopped and GuestLocked handling, RBAC resources, status bounds, and empty ProviderStateSet.", + "workItemId" : "ADR046-clipboard-003" + }, + { + "currentSource" : "packages/d2b-clipd/src/picker.rs subprocess flow is replacement context only; picker-session worker is net-new", + "dataMigration" : "Full d2b 3.0 reset; picker state is per-operation EphemeralProcess status only", + "dependencyOwner" : "ADR046-clipboard-001 and ADR046-clipboard-003; picker worker owner", + "destination" : "packages/d2b-provider-clipboard-wayland/src/picker_session/ and picker-session binary", + "detailedDesign" : "Implement picker-session as a user-domain worker EphemeralProcess with minimal environment, metadata over inherited ComponentSession named stream, restricted WAYLAND_SOCKET FD from display-wayland presentation portal, GTK4 closure-contained runtime, one Select or Cancel response, no clipboard FDs, no clipboard bytes, no compositor credentials, no socket paths, and typed PickerStartFailed on install or start failure instead of bypass. Primary reuse disposition: `adapt`. Preserved source-plan detail: rewrite as EphemeralProcess worker.", + "integration" : "clipboard-controller creates picker EphemeralProcess per paste request; ProviderSupervisor pre-opens restricted Wayland FD; picker returns result to controller which notifies clipd-host.", + "removalProof" : "Old d2b-clipd subprocess picker path is absent once RequestPickerSession and picker EphemeralProcess tests pass.", + "reuseAction" : "adapt", + "reuseSource" : null, + "specId" : "ADR-046-provider-clipboard-wayland", + "specPath" : "docs/specs/providers/ADR-046-provider-clipboard-wayland.md", + "validation" : "Contract test that picker cannot bind zwlr_data_control_manager_v1 plus unit tests for processClass worker, no FDs or payload in picker config, TTL defaults, response framing, and requirePickerForPaste false bypass semantics in clipd-host.", + "workItemId" : "ADR046-clipboard-004" + }, + { + "currentSource" : "None — net-new clipboard-wayland ComponentSession service definitions; display-wayland client stubs are consumed from the display-wayland contract", + "dataMigration" : "None — docs/tooling only; no runtime state", + "dependencyOwner" : "ADR046-clipboard-001; ADR-046-componentsession-and-bus; clipboard service contract owner", + "destination" : "packages/d2b-provider-clipboard-wayland service descriptors and generated Rust async ttrpc bindings, plus any shared contracts crate selected by ADR-046-componentsession-and-bus", + "detailedDesign" : "Generate stubs for d2b.clipboard.bridge.v3, d2b.clipboard.picker-coord.v3, and d2b.clipboard.v3, consume display-wayland d2b.display.host-clipboard.v3 client stubs, reject service-name collisions, and declare attachment classes clipboard-transfer-fd, host-selection-transfer-fd, and host-selection-supply-fd in the signed descriptor for ComponentSession handshake validation. Primary reuse disposition: `create`. Preserved source-plan detail: net-new generation of service stubs and named-stream types.", + "integration" : "Service registry, Zone ComponentSession enrollment, clipd-host, clipboard-controller, display-wayland wayland-proxy, and CLI/operator clients all consume the generated bindings.", + "removalProof" : "Shared filesystem bridge path and SO_PEERCRED contract tests are removed in ADR046-clipboard-011 after ComponentSession contracts pass.", + "reuseAction" : "create", + "reuseSource" : null, + "specId" : "ADR-046-provider-clipboard-wayland", + "specPath" : "docs/specs/providers/ADR-046-provider-clipboard-wayland.md", + "validation" : "Contract tests for wire format, service-name collision rejection, attachment class matching, and descriptor handshake validation.", + "workItemId" : "ADR046-clipboard-005" + }, + { + "currentSource" : "nixos-modules/clipboard.nix and current d2b.clipboard.* options", + "dataMigration" : "Full d2b 3.0 reset; operators translate old d2b.clipboard.* options using the dossier mapping table", + "dependencyOwner" : "ADR-046-nix-configuration; Provider/clipboard-wayland Nix owner", + "destination" : "nixos-modules/providers/clipboard-wayland.nix and d2b.artifacts.clipboard-wayland catalog entry", + "detailedDesign" : "Implement Nix module emitting d2b.zones..resources. Provider resources with spec.artifactId and spec.config, validate hostExecutionRef, hostUserRef, displayWaylandRef, and pickerArtifactId, forbid spec.componentPlacements, spec.settings, and spec.status, and remove nixos-modules/clipboard.nix in the same landing sequence as the new module. Primary reuse disposition: `replace`. Preserved source-plan detail: replace option surface with Provider resource Nix module.", + "integration" : "Nix resource compiler emits Provider resource and artifact catalog data consumed by core configuration publication and ProviderDeployment.", + "removalProof" : "nixos-modules/clipboard.nix import is removed and examples/static checks no longer reference old d2b.clipboard.* options.", + "reuseAction" : "replace", + "reuseSource" : null, + "specId" : "ADR-046-provider-clipboard-wayland", + "specPath" : "docs/specs/providers/ADR-046-provider-clipboard-wayland.md", + "validation" : "Nix eval tests for resource shape, reference validation, null displayWaylandRef host-only mode, artifact catalog lookup, and absence of deprecated spec fields.", + "workItemId" : "ADR046-clipboard-006" + }, + { + "currentSource" : "None — net-new Zone RBAC resources for clipboard-wayland; no pre-ADR45 baseline equivalent", + "dataMigration" : "Full d2b 3.0 reset; no v2 RBAC state import", + "dependencyOwner" : "ADR046-clipboard-003; clipboard RBAC owner", + "destination" : "packages/d2b-provider-clipboard-wayland/src/controller/rbac.rs or equivalent controller reconcile module", + "detailedDesign" : "Controller creates Role/clipboard-admin, Role/clipboard-viewer, Role/clipboard-bridge-peer, Role/clipboard-picker-worker and RoleBindings display-wayland-bridge, host-admin-clipboard, picker-session-worker, all Zone-scoped, owned by Process/clipboard-controller, selector-bound for Process/picker-*, and cleaned up when Provider is deleted.", + "integration" : "Resource API stores RBAC resources; ComponentSession authorization checks consume Roles and RoleBindings for management, bridge, and picker worker services.", + "removalProof" : "None — net-new; no prior owner to remove", + "reuseAction" : "create", + "reuseSource" : null, + "specId" : "ADR-046-provider-clipboard-wayland", + "specPath" : "docs/specs/providers/ADR-046-provider-clipboard-wayland.md", + "validation" : "Controller RBAC reconcile tests for create, idempotent update, provider deletion cleanup, selector scoping, bridge peer authorization, and denied unauthorized management calls.", + "workItemId" : "ADR046-clipboard-007" + }, + { + "currentSource" : "packages/d2b-clipd/src/audit.rs and policy types from packages/d2b-clipd/src/policy.rs", + "dataMigration" : "Full d2b 3.0 reset; audit stream is v3 Zone-local and no v2 audit records are imported", + "dependencyOwner" : "ADR046-clipboard-002; ADR-046-telemetry-audit-and-support; clipboard observability owner", + "destination" : "packages/d2b-provider-clipboard-wayland/src/service/audit.rs and packages/d2b-provider-clipboard-wayland/src/service/metrics.rs", + "detailedDesign" : "Implement ClipboardAuditEvent and fail-closed Zone audit queue by porting baseline audit code, renaming realm fields to source_zone_id and dest_zone_id, making ReasonCode a closed enum with unknown protobuf fields rejected, replacing exact byte counts with SizeBucket, emitting to d2b.audit.v3, and adding closed-label OTEL metrics and spans from the dossier tables. Primary reuse disposition: `adapt`. Preserved source-plan detail: port and adapt audit plus metrics with zone names and redaction changes.", + "integration" : "clipd-host emits audit events to the Zone audit sink and OTEL metrics/spans to the observability Provider pipeline during clipboard operations.", + "removalProof" : "Old audit shape with realm field names and exact byte counts is absent after ported tests assert the v3 ClipboardAuditEvent schema.", + "reuseAction" : "adapt", + "reuseSource" : null, + "specId" : "ADR-046-provider-clipboard-wayland", + "specPath" : "docs/specs/providers/ADR-046-provider-clipboard-wayland.md", + "validation" : "Audit tests for no bytes in events, closed ReasonCode deserialization, fail-closed queue rejection, SizeBucket discretization, allowed metric labels, and excluded span attributes.", + "workItemId" : "ADR046-clipboard-008" + }, + { + "currentSource" : "packages/d2b-clipd/ behavior and this dossier invariants; no single baseline test path is declared for every assertion", + "dataMigration" : "None — docs/tooling only; no runtime state", + "dependencyOwner" : "ADR046-clipboard-001 through ADR046-clipboard-008; clipboard test owner", + "destination" : "packages/d2b-provider-clipboard-wayland/tests/", + "detailedDesign" : "Create hermetic unit and Cargo integration tests covering closed MIME policy, secret-hint suppression, FD validation and bounds, LRU and TTL history, fail-closed audit, lifecycle purge and suspend, picker EphemeralProcess invariants, no filesystem bridge, core-created Processes, empty ProviderStateSet, no state mounts or state-layout principals, status-first observation, and no clipboard bytes in status, audit, metrics, Operations, or Volumes. Primary reuse disposition: `extract`. Preserved source-plan detail: extract semantic assertions into hermetic provider tests.", + "integration" : "cargo test -p d2b-provider-clipboard-wayland --lib --tests consumes the provider crate and fake clocks/effect ports without live Wayland, systemd, broker, or Nix eval.", + "removalProof" : "Replaced current-code tests receive explicit keep/adapt/move/delete dispositions and old duplicate tests are deleted once successor coverage passes.", + "reuseAction" : "extract", + "reuseSource" : null, + "specId" : "ADR-046-provider-clipboard-wayland", + "specPath" : "docs/specs/providers/ADR-046-provider-clipboard-wayland.md", + "validation" : "All tests listed in the Required test coverage table must pass under make test-rust -p d2b-provider-clipboard-wayland.", + "workItemId" : "ADR046-clipboard-009" + }, + { + "currentSource" : "None — net-new v3 provider integration scenarios; no pre-ADR45 baseline equivalent", + "dataMigration" : "None — docs/tooling only; no runtime state", + "dependencyOwner" : "ADR046-clipboard-009; display-wayland fake and clipboard integration owner", + "destination" : "packages/d2b-provider-clipboard-wayland/integration/", + "detailedDesign" : "Implement e2e paste, host capture, bridge backpressure, rate limiting, echo suppression, dependency absent host-only mode, GuestDestroyed purge, audit fail-closed, picker start timeout, and cross-zone denied scenarios using fake d2b.display.host-clipboard.v3 server and fake wayland-proxy bridge client without requiring a live compositor. Primary reuse disposition: `create`. Preserved source-plan detail: net-new integration suite with fake display-wayland and fake wayland-proxy.", + "integration" : "Provider integration lane exercises clipd-host, clipboard-controller, generated ComponentSession services, fake display-wayland service, and fake bridge client end-to-end.", + "removalProof" : "None — net-new; no prior owner to remove", + "reuseAction" : "create", + "reuseSource" : null, + "specId" : "ADR-046-provider-clipboard-wayland", + "specPath" : "docs/specs/providers/ADR-046-provider-clipboard-wayland.md", + "validation" : "All integration scenarios in the dossier table pass and assert no live Wayland compositor dependency.", + "workItemId" : "ADR046-clipboard-010" + }, + { + "currentSource" : "packages/d2b-contract-tests/tests/policy_clipboard.rs", + "dataMigration" : "None — docs/tooling only; no runtime state", + "dependencyOwner" : "ADR046-clipboard-005; packages/d2b-contract-tests owner", + "destination" : "packages/d2b-contract-tests/tests/policy_clipboard.rs", + "detailedDesign" : "Add contract tests for d2b.clipboard.bridge.v3 and d2b.clipboard.picker-coord.v3 wire formats, ReasonCode numeric stability, and attachment class descriptor names while removing tests that assume shared filesystem bridge paths or SO_PEERCRED config. Primary reuse disposition: `adapt`. Preserved source-plan detail: adapt contract tests and delete obsolete filesystem bridge assumptions.", + "integration" : "Contract test suite consumes generated service descriptors and guards downstream ComponentSession consumers.", + "removalProof" : "Tests for shared filesystem bridge paths and SO_PEERCRED config are removed from policy_clipboard.rs after v3 ComponentSession contract coverage lands.", + "reuseAction" : "adapt", + "reuseSource" : null, + "specId" : "ADR-046-provider-clipboard-wayland", + "specPath" : "docs/specs/providers/ADR-046-provider-clipboard-wayland.md", + "validation" : "packages/d2b-contract-tests policy_clipboard.rs passes with v3 wire format and attachment descriptor assertions.", + "workItemId" : "ADR046-clipboard-011" + }, + { + "currentSource" : "nixos-modules/clipboard.nix and its import in nixos-modules/default.nix", + "dataMigration" : "Full d2b 3.0 reset; no v2 clipboard runtime state or option import", + "dependencyOwner" : "ADR046-clipboard-006; Nix/module removal owner", + "destination" : "nixos-modules/default.nix, docs/how-to/ migration guide, tests/static.sh example iteration expectations, CHANGELOG.md", + "detailedDesign" : "After the new Provider Nix module validates, delete nixos-modules/clipboard.nix, remove its default.nix import, update migration guide with the option mapping table, ensure tests/static.sh examples no longer rely on removed paths, and add an Unreleased changelog entry. Primary reuse disposition: `delete-after-cutover`. Preserved source-plan detail: delete superseded module and update migration docs.", + "integration" : "Nix module aggregator, docs migration flow, static example iteration, and changelog all reflect Provider/clipboard-wayland as the only clipboard surface.", + "removalProof" : "nixos-modules/clipboard.nix is deleted, default.nix import removed, and grep/static checks show no old d2b.clipboard option references in examples.", + "reuseAction" : "delete-after-cutover", + "reuseSource" : null, + "specId" : "ADR-046-provider-clipboard-wayland", + "specPath" : "docs/specs/providers/ADR-046-provider-clipboard-wayland.md", + "validation" : "New module eval tests pass and tests/static.sh example iteration has no references to removed option paths.", + "workItemId" : "ADR046-clipboard-012" + }, + { + "currentSource" : "`packages/d2bd/src/lib.rs`, `provider_registry.rs` if present on source ref, supervisor state, operations; `d2b-realm-core/src/{allocator_engine,identity_store}.rs`", + "dataMigration" : "Full reset", + "dependencyOwner" : "W0/W1a; core-controller owner", + "destination" : "`packages/d2b-core-controller/src/{main,configuration,api_catalog,authz,providers,controllers,ownership,watches,cleanup,zone_links,budgets,store}.rs`", + "detailedDesign" : "One fixed process, isolated handlers, async ResourceClient, health/startup/restart Primary reuse disposition: `adapt`. Preserved source-plan detail: extract and adapt.", + "integration" : "Zone runtime local bus/session; Provider/system-core resource identity", + "removalProof" : "Current daemon branches removed after handler/Provider parity", + "reuseAction" : "adapt", + "reuseSource" : "Useful pure handler/toolkit code from main named in implementation sub-items", + "specId" : "ADR-046-core-controllers", + "specPath" : "docs/specs/ADR-046-core-controllers.md", + "validation" : "Per-handler unit/property tests plus multi-process startup/restart", + "workItemId" : "ADR046-core-001" + }, + { + "currentSource" : "Nix host/Realm options/index; host check/provider code; user/group lookup and unsafe-local eligibility", + "dataMigration" : "New v3 resources from Nix", + "dependencyOwner" : "ADR046-core-001; system-core Provider owner", + "destination" : "`packages/d2b-provider-system-core/src/{host,user}.rs` linked into fixed core controller", + "detailedDesign" : "Host and User schemas/reconcile/status/capabilities Primary reuse disposition: `adapt`. Preserved source-plan detail: extract/adapt.", + "integration" : "Bootstrap Provider/system-core; other controllers target Host/Guest/User refs", + "removalProof" : "Host grouping/user helper policy removed only after resource parity", + "reuseAction" : "adapt", + "reuseSource" : "Any main local-host/user-provider code selected by exact sub-items", + "specId" : "ADR-046-core-controllers", + "specPath" : "docs/specs/ADR-046-core-controllers.md", + "validation" : "Multiple Hosts, system/user restrictions, UID/session drift", + "workItemId" : "ADR046-core-002" + }, + { + "currentSource" : "`d2b-realm-provider/src/credential.rs:OpaqueAzureRef` remains a bounded opaque identifier reuse source only; no Host login/token implementation is retained", + "dataMigration" : "Full v3 reset of d2b Credential metadata; Entrablau Guest state is preserved unless explicitly destroyed by the sibling-owned reset flow", + "dependencyOwner" : "`ADR046-credential-001` (Credential base fields and status), `ADR046-credential-002` (`d2b.credential.v3` service), D092 Endpoint, D093 Entrablau identity Guest decision, credential-entra owner", + "destination" : "`packages/d2b-provider-credential-entra/src/{lib.rs,controller.rs,service.rs,controller_main.rs,agent_main.rs,audit.rs,telemetry.rs}` and corresponding tests/integration docs", + "detailedDesign" : "Implement secret-free controller/helper; require Credential base `identityGuestRef` and `loginEndpointRef`; resolve dependency alias `entra-login-token`; validate same-Zone Guest placement and exact `consumerRef`; implement typed `EntraTokenLeaseClient` whose production implementation is the Entrablau Endpoint; implement `BeginLogin`/`ObserveLogin`/`CancelLogin` projection to Credential base interaction status; route access-token leases end-to-end from Entrablau service to exact consumer over Noise_KK; keep all refresh/login/TPM state inside the Entrablau Guest; declare no Provider state Volume; reject Host placement and all ambient fallback chains. Primary reuse disposition: `adapt`. Preserved source-plan detail: Adapt tests/types where non-secret; replace production token acquisition with Entrablau Endpoint client.", + "integration" : "Consumer composes `inputs.entrablau.nixosModules.default` into the identity Guest; sibling package declares login/token Process and Endpoint; d2b Credential resource binds `identityGuestRef`, `loginEndpointRef`, and `consumerRef`", + "removalProof" : "Old abstract Host credential paths and any direct Entra effect client are deleted only after the Entrablau Endpoint-backed provider passes the test matrix", + "reuseAction" : "adapt", + "reuseSource" : null, + "specId" : "ADR-046-provider-credential-entra", + "specPath" : "docs/specs/providers/ADR-046-provider-credential-entra.md", + "validation" : "See §13", + "workItemId" : "ADR046-cred-entra-001" + }, + { + "currentSource" : "`packages/d2b-realm-provider/src/credential.rs:ManagedIdentityRef` (reachable); `packages/d2b-provider-aca/src/lib.rs:managed_identity_client_id` line 112 (reachable ACA config); `packages/d2bd/src/lib.rs:managed_identity_client_id` lines 3960, 4173 (reachable)", + "dataMigration" : "Full v3 reset. `d2b-provider-aca:managed_identity_client_id` raw field migrated to a Credential resource reference in the v3 ACA Provider config; see removal precondition below.", + "dependencyOwner" : "`ADR046-credential-001` (contracts), `ADR046-credential-002` (service); `ADR046-reconcile-001`; `ADR046-cred-mi-005`; `credential-managed-identity` crate owner", + "destination" : "`packages/d2b-provider-credential-managed-identity/src/{lib.rs, controller.rs, agent.rs, service.rs, audit.rs, telemetry.rs}`; `packages/d2b-provider-credential-managed-identity/{controller/main.rs, agent/main.rs}`; `packages/d2b-provider-credential-managed-identity/tests/{lifecycle.rs, conformance.rs, faults.rs, canary.rs, delivery.rs, placement.rs, topology.rs}`; `packages/d2b-provider-credential-managed-identity/integration/{container-service.sh, host-guest-placement.nix, aca-credential-ref.sh, cleanup-rollback.sh}`; `packages/d2b-provider-credential-managed-identity/README.md`", + "detailedDesign" : "(1) Adapt `ManagedIdentityCredentialProvider` to `d2b.credential.v3` service interface; split controller and agent roles (see `ADR046-cred-mi-005`). (2) Enforce `ManagedIdentityCredentialOwner::ExactSdkConsumer` in agent via `AuthenticatedSubjectContext` from ComponentSession, independently of scope fields. (3) Reject `user-agent` placement: `scope.domainFilter=user` returns `credential-placement-mismatch` before agent spawn. (4) Validate `clientId` using `OpaqueAzureRef::parse` from v3 baseline; artifact IDs match `^[a-z][a-z0-9-]*$`. (5) Validate `imdsEndpointAlias` against closed enum `{azure-imds, azure-imds-aca}`; project into LaunchTicket at spawn time (never into Process spec config or env); co-located runtime Provider constructs `ManagedIdentityCredentialClient` from LaunchTicket projection and supplies via effect port; resolved URL never in any output surface. (6) Agent Process declares `networkUsage.allowEgress=false`; uses canonical Process template shape (see `ADR046-cred-mi-005` design item 6). (7) Reject `sign-challenge` with `credential-schema-invalid` immediately. (8) Map `ManagedIdentityClientState::Unavailable` to `credential-provider-unavailable`; no `InteractionRequired` state. (9) Implement `ManagedIdentityLeaseHandle` as opaque bounded newtype with redacted `Debug`. (10) All token bytes held by injected client in agent; delivered only via agent-terminated `Noise_KK` delivery session. (11) Integrate with Provider resource descriptor and controller toolkit. (12) Confirm `credential_canary` never appears in any service response, status field, delivery record outer header, or audit record. (13) Apply D087 status-first state: declare no Provider state Volume, keep ProviderStateSet empty, and write only bounded non-secret lease observation to `Credential.status` plus the Operation ledger. Primary reuse disposition: `adapt`. Preserved source-plan detail: copy and adapt.", + "integration" : "Agent `Process` resource under `Host` or `Guest` executionRef; controller `Process` resource at Zone system host; d2b-bus routes `d2b.credential.v3` token-delivery calls to agent; Credential controller reconciles status; ACA `Provider/runtime-azure-container-apps` holds `credentialRef` pointing to a `credential-managed-identity`-backed Credential resource", + "removalProof" : "`d2b-provider-aca:managed_identity_client_id` raw field removed only after the `credential-managed-identity` controller and agent are integrated and the ACA Provider config uses `credentialRef` exclusively; `ProviderWorkloadIdentity::ManagedIdentity` bootstrap path superseded only after the ACA Provider controller uses the Credential resource for token acquisition", + "reuseAction" : "adapt", + "reuseSource" : "main `a1cc0b2d`: `packages/d2b-provider-credential-managed-identity/src/lib.rs` (full implementation); `src/tests.rs` (full test suite)", + "specId" : "ADR-046-provider-credential-managed-identity", + "specPath" : "docs/specs/providers/ADR-046-provider-credential-managed-identity.md", + "validation" : "See §Test matrix", + "workItemId" : "ADR046-cred-mi-001" + }, + { + "currentSource" : "`packages/d2b-realm-provider/src/provider.rs:CredentialProvider` status/enrollment-only trait; main `a1cc0b2d` managed-identity controller/test behavior listed in §Source reuse", + "dataMigration" : "None — controller lifecycle code only; no runtime state import", + "dependencyOwner" : "ADR046-credential-001, ADR046-credential-002; ADR046-reconcile-001, ADR046-reconcile-002; ADR046-cred-mi-005; owner: Credential controller toolkit and managed-identity controller", + "destination" : "packages/d2b-provider-credential-managed-identity/src/controller.rs; packages/d2b-contracts/src/v3/credential_controller.rs", + "detailedDesign" : "Managed-identity-specific controller design: implement async reconcile and agent spawn/teardown from §Async reconcile; enforce system-only domain; spawn agent on Credential admission plus dependency-ready, not on `phase=Ready`; implement `observeInterval=30s` health-check RPC to the agent, which calls `InspectMetadata` on the injected client; controller never calls IMDS; derive idempotency key as `SHA-256(UID \\|\\| \":\" \\|\\| rotationGeneration.to_le_bytes() \\|\\| \":\" \\|\\| operation_class_byte)`; enforce `MAX_LOCAL_LEASES=256` in the resource store; implement Deleted-phase closure by clearing `provider-revoke` only after agent Process deletion and revocation confirmation while core/audit own Deleted revision and deletion record. Primary reuse disposition: `adapt`. Preserved source-plan detail: adapt shared Credential controller lifecycle to managed-identity controller/agent spawn and teardown.", + "integration" : "Shared Credential controller contract produces reconcile events; managed-identity controller consumes them, manages agent Process resources, and writes Credential/agent status; generated controller contracts are consumed by all Credential Providers.", + "removalProof" : "V2 `CredentialProvider` status/enrollment trait lifecycle is superseded after shared controller reconcile and managed-identity agent lifecycle pass parity", + "reuseAction" : "adapt", + "reuseSource" : null, + "specId" : "ADR-046-provider-credential-managed-identity", + "specPath" : "docs/specs/providers/ADR-046-provider-credential-managed-identity.md", + "validation" : "Managed-identity reconcile/controller tests; shared Credential reconciliation tests; topology tests validating agent spawn/teardown and Deleted-phase cleanup", + "workItemId" : "ADR046-cred-mi-002" + }, + { + "currentSource" : "`packages/d2b-provider-aca/src/lib.rs:managed_identity_client_id` and `packages/d2bd/src/lib.rs:managed_identity_client_id` are the current raw ACA managed-identity config surfaces; v3 Provider/Credential resource authoring is net-new", + "dataMigration" : "Full d2b 3.0 reset; raw ACA `managed_identity_client_id` config is replaced by a newly authored Credential resource reference rather than imported in place", + "dependencyOwner" : "Depends on ADR046-credential-001, ADR046-credential-002, and ADR046-cred-mi-001; owner: Nix resource compiler and activation cleanup", + "destination" : "nixos-modules/options-resources.nix; nixos-modules/activation-nixos-cleanup.nix; integration/aca-credential-ref.sh", + "detailedDesign" : "Shared Nix and cleanup: implement Nix eval-time assertions 1–12 from §Eval-time assertions, closed enum schema for `imdsEndpointAlias`, `clientId` validation via `OpaqueAzureRef` charset, generation cleanup contract, and artifact catalog validation for `credential-managed-identity-bin`. Integration fixture asserts that the migrated ACA Provider config carries `credentialRef: \"Credential/aca-relay-mi\"` and the raw `managed_identity_client_id` string field is absent from rendered Provider config JSON and ACA runtime bundle. Primary reuse disposition: `replace`. Preserved source-plan detail: replace raw ACA managed identity client-id fields with Credential resource references and v3 Provider/Credential Nix emission.", + "integration" : "Nix compiler emits Provider/Credential/ACA resource config; ResourceAPI admission validates it; activation cleanup deletes old generation resources through finalizers; ACA Provider consumes `credentialRef` to obtain tokens from the managed-identity Credential Provider.", + "removalProof" : "`managed_identity_client_id` raw fields in ACA config and daemon plumbing are removed only after ACA Provider config uses `credentialRef` exclusively", + "reuseAction" : "replace", + "reuseSource" : null, + "specId" : "ADR-046-provider-credential-managed-identity", + "specPath" : "docs/specs/providers/ADR-046-provider-credential-managed-identity.md", + "validation" : "Nix eval assertion tests; artifact catalog validation tests; `integration/aca-credential-ref.sh`", + "workItemId" : "ADR046-cred-mi-003" + }, + { + "currentSource" : "`packages/d2b-realm-provider/src/error.rs:contains_sensitive_shape`; `packages/d2b-core/src/realm_workloads_launcher.rs:LauncherMetadataInvariants.no_secrets_or_credentials`; main `a1cc0b2d` managed-identity canary tests listed in §Source reuse", + "dataMigration" : "None — audit/telemetry only; no runtime state import", + "dependencyOwner" : "Depends on ADR046-cred-mi-001 and ADR046-cred-mi-005; owner: credential-managed-identity audit/telemetry implementation", + "destination" : "packages/d2b-provider-credential-managed-identity/src/{audit.rs,telemetry.rs}; packages/d2b-contract-tests/tests/credential_audit.rs", + "detailedDesign" : "Shared audit/OTEL: emit audit records for all methods and controller events per §Audit; emit OTEL spans and metrics per §OTEL and metrics; add `d2b_credential_imds_calls_total` counter with bounded `alias` label; enforce `contains_sensitive_shape` on all string fields in audit records and metric labels; add canary tests for `managed-identity-canary`, `credential_canary`, and `imds-endpoint-canary` in `canary.rs`. Primary reuse disposition: `adapt`. Preserved source-plan detail: adapt existing sensitive-shape guard and canary pattern to v3 audit, OTEL, and metric surfaces.", + "integration" : "Controller and agent service methods call audit/telemetry helpers; audit subsystem and OTEL exporters consume bounded redacted records; contract tests validate credential audit shape across providers.", + "removalProof" : "None — audit/telemetry helpers are additive; no prior owner to remove", + "reuseAction" : "adapt", + "reuseSource" : null, + "specId" : "ADR-046-provider-credential-managed-identity", + "specPath" : "docs/specs/providers/ADR-046-provider-credential-managed-identity.md", + "validation" : "`packages/d2b-contract-tests/tests/credential_audit.rs`; managed-identity `canary.rs`; audit/OTEL unit tests for labels and sensitive-shape rejection", + "workItemId" : "ADR046-cred-mi-004" + }, + { + "currentSource" : "`packages/d2b-realm-provider/src/provider.rs:CredentialProvider` status/enrollment-only trait; main `a1cc0b2d` managed-identity Provider implementation and tests listed in §Source reuse", + "dataMigration" : "Full d2b 3.0 reset; no v2 managed-identity process/session state import", + "dependencyOwner" : "ADR046-credential-001 and ADR046-credential-002; owner: credential-managed-identity controller/agent topology. This topology contract is consumed by the later managed-identity controller work.", + "destination" : "packages/d2b-provider-credential-managed-identity/src/{controller.rs,agent.rs}; packages/d2b-provider-credential-managed-identity/{controller/main.rs,agent/main.rs}; packages/d2b-provider-credential-managed-identity/tests/topology.rs", + "detailedDesign" : "Implement the controller/agent process split: separate `d2b-managed-identity-controller` binary with no IMDS client and no KK delivery, and `d2b-managed-identity-agent` binary with injected IMDS client via effect port and KK delivery. Controller manages Credential resources, spawns/monitors agent Processes, uses canonical Process templates, attaches LaunchTickets projecting `imdsEndpointAlias` and `credentialRef`, monitors agent Process health with bounded backoff, performs Deleted-phase cleanup without emitting Deleted closure audit, and applies D087 status-first state with no Provider state Volume. Agent validates `ExactSdkConsumer` via `AuthenticatedSubjectContext`, serves token-delivery methods, terminates Noise_KK delivery sessions, reports lease state, declares no direct IMDS egress, and keeps token bytes transient. Primary reuse disposition: `adapt`. Preserved source-plan detail: copy and adapt the main managed-identity Provider; replace v2 provider registry/session assumptions with v3 controller/agent Process topology and `d2b.credential.v3` service.", + "integration" : "ProviderDeployment starts the controller Process; controller reconciles Credential resources and creates agent Process/Endpoint resources at the declared executionRef; d2b-bus routes `d2b.credential.v3` calls to the agent; co-located runtime Provider injects the IMDS client through the LaunchTicket/effect port; core aggregates Provider status and audit subsystem appends deletion records.", + "removalProof" : "V2 single-process/trait topology is superseded once controller and agent Process split is integrated and all token delivery terminates in the agent", + "reuseAction" : "adapt", + "reuseSource" : null, + "specId" : "ADR-046-provider-credential-managed-identity", + "specPath" : "docs/specs/providers/ADR-046-provider-credential-managed-identity.md", + "validation" : "`tests/topology.rs`; `integration/host-guest-placement.nix`; `make test-rust`; `make test-integration`; `make test-host-integration`", + "workItemId" : "ADR046-cred-mi-005" + }, + { + "currentSource" : "`packages/d2b-realm-provider/src/provider.rs:CredentialProvider`; `packages/d2b-realm-provider/src/credential.rs` opaque credential refs and `OpaqueAzureRef` helpers", + "dataMigration" : "Full d2b 3.0 reset; no v2 CredentialProvider status/config import", + "dependencyOwner" : "Dependency for ADR046-cred-ss-003; owner: `packages/d2b-contracts` Credential ResourceType contract", + "destination" : "packages/d2b-contracts/src/v3/credential.rs", + "detailedDesign" : "Contract types: define `CredentialSpec`, `CredentialStatus`, `CredentialLeaseHandle`, `OperationClass`, `PlacementBinding`, `CredentialConditionType`, and serde/validation/redaction helpers. Reuse `OpaqueAzureRef` from the v3 baseline. Full detail remains in `ADR-046-resources-credential` §Implementation work items. Primary reuse disposition: `adapt`. Preserved source-plan detail: adapt current credential/status concepts into v3 ResourceType DTOs; reuse `OpaqueAzureRef` directly where applicable.", + "integration" : "Nix compiler emits these DTOs; ResourceAPI stores them; credential-secret-service controller/service consumes them; CLI and conformance tests validate base Credential spec/status behavior.", + "removalProof" : "Old `d2b-realm-provider:CredentialProvider` trait and `CredentialStatus` enum are removed only after all three v3 Credential Provider controllers reach full reconcile parity", + "reuseAction" : "adapt", + "reuseSource" : null, + "specId" : "ADR-046-provider-credential-secret-service", + "specPath" : "docs/specs/providers/ADR-046-provider-credential-secret-service.md", + "validation" : "Credential ResourceType schema/serde/redaction tests from `ADR-046-resources-credential`; credential-secret-service conformance consumes the shared types", + "workItemId" : "ADR046-cred-ss-001" + }, + { + "currentSource" : "None — net-new v3 `d2b.credential.v3` service; no pre-ADR45 baseline service proto equivalent", + "dataMigration" : "Full d2b 3.0 reset; no v2 service state import", + "dependencyOwner" : "Dependency for ADR046-cred-ss-003; owner: credential service contract/codegen", + "destination" : "packages/d2b-contracts/proto/v3/credential.proto; packages/d2b-credential-service/", + "detailedDesign" : "Service proto: define the `d2b.credential.v3` protobuf/ttrpc service and generate typed client/server code. Full detail remains in `ADR-046-resources-credential` §Implementation work items. Primary reuse disposition: `create`. Preserved source-plan detail: net-new service contract replacing the v2 in-process `CredentialProvider` trait.", + "integration" : "d2b-bus routes Credential service calls to credential-secret-service Process instances; generated client/server types bind the controller/service implementation to ComponentSession delivery.", + "removalProof" : "V2 `CredentialProvider` trait calls are superseded by `d2b.credential.v3` only after all credential providers reach parity", + "reuseAction" : "create", + "reuseSource" : null, + "specId" : "ADR-046-provider-credential-secret-service", + "specPath" : "docs/specs/providers/ADR-046-provider-credential-secret-service.md", + "validation" : "Generated-code compile tests and credential service contract tests from `ADR-046-resources-credential`; credential-secret-service lifecycle/delivery tests consume the generated service", + "workItemId" : "ADR046-cred-ss-002" + }, + { + "currentSource" : "`packages/d2b-realm-provider/src/provider.rs:CredentialProvider` (minimal v3 baseline)", + "dataMigration" : "Full reset; no migration from old `CredentialProvider` trait", + "dependencyOwner" : "`ADR046-cred-ss-001` (contract types); `ADR046-cred-ss-002` (service proto); `ADR046-reconcile-001`; credential-secret-service owner", + "destination" : "`packages/d2b-provider-credential-secret-service/src/{lib.rs, controller.rs, service.rs, main.rs}`; `packages/d2b-provider-credential-secret-service/tests/{lifecycle.rs, conformance.rs, faults.rs, canary.rs, delivery.rs, placement.rs}`; `packages/d2b-provider-credential-secret-service/integration/{container-service.sh, host-placement.nix, guest-placement.nix, cleanup-rollback.sh}`; `packages/d2b-provider-credential-secret-service/README.md`", + "detailedDesign" : "Adapt `SecretServiceCredentialProvider` and `SecretServiceCredentialProviderFactory` to v3 `d2b.credential.v3` service; replace v2 `CredentialProvider` trait with v3 controller/service handler; retain `Oo7SecretServicePort` trait methods unchanged; ensure `SecretServiceOwner::Userd` placement guard rejects system-domain and guest-agent construction; validate `collectionAlias` against provider-internal charset (not `OpaqueAzureRef`; collection aliases may include spaces); integrate with Provider resource descriptor and controller toolkit; test that `credential_canary` never appears in any service response; create a Process resource per `(Zone, User, executionRef)` triple with `template = \"secret-service-controller\"` (plain string), canonical `sandbox` fields (`namespaceClasses`, `capabilityClasses`, `seccompClass`, `noNewPrivileges`, `startRoot`, `environmentClass`, `readOnlyRoot`), `budget` with nested `cpu`/`memory`/`pids`/`fds` sub-fields, `networkUsage: null`, no inline endpoint fields, an owned credential-service `Endpoint` resource, and `readiness.class = \"provider-defined\"`; component descriptor declares the required authenticated `dbus-session` FD attachment carried privately by the LaunchTicket; D087 status-first state model: no Provider state Volume is declared, ProviderStateSet is optional/query-time and empty, no Volume mount or layout principal is required, and the storage-need test is not met; bounded non-secret lease/acquisition/retry observation lives in `Credential.status` plus the core Operation ledger; any opaque status handle is non-secret, non-authorizing, bounded, safe for authorized status readers, and independently revalidated; no token/object-path/lease bytes persist anywhere; finalize() emits revoke outcome audit but MUST NOT emit the resource-deleted closure audit (audit subsystem only); controller writes only scoped Credential/Process health (core aggregates Provider status) Primary reuse disposition: `adapt`. Preserved source-plan detail: copy and adapt.", + "integration" : "Target: user-domain `Process` resource under `Host` or `Guest` (ADR-only ResourceType); d2b-bus routes `d2b.credential.v3` calls to this process; Credential controller reconciles status. Current v3 has no user-credential host process: v3 `d2b-userd` is a guest exec stub (exits 78 in service mode; no credential functionality; `test-only-or-preview`). This integration path is fully new (ADR-only) work.", + "removalProof" : "Old `d2b-realm-provider:CredentialProvider` trait removed only after all three v3 Credential Provider controllers reach full reconcile parity", + "reuseAction" : "adapt", + "reuseSource" : "main `a1cc0b2d`: `packages/d2b-provider-credential-secret-service/src/lib.rs` (full implementation); `src/tests.rs` (full test suite including `FakeOo7Port`, lease lifecycle, locked state, canary enforcement, cardinality limits)", + "specId" : "ADR-046-provider-credential-secret-service", + "specPath" : "docs/specs/providers/ADR-046-provider-credential-secret-service.md", + "validation" : "See §16", + "workItemId" : "ADR046-cred-ss-003" + }, + { + "currentSource" : "ADR-only controller pattern from `ADR-046-resource-reconciliation`; no concrete secret-service baseline controller to import", + "dataMigration" : "None — controller toolkit code only; no runtime state migration", + "dependencyOwner" : "Dependency for ADR046-cred-ss-003; owner: common Credential controller/reconciliation toolkit", + "destination" : "packages/d2b-provider-credential-/src/controller.rs", + "detailedDesign" : "Controller toolkit: implement the common Credential controller handler conforming to the `ADR-046-resource-reconciliation` async loop. Secret-service-specific controller code plugs into this handler while keeping provider bytes out of status/store/audit. Primary reuse disposition: `create`. Preserved source-plan detail: net-new shared controller handler pattern specialized by each Credential Provider.", + "integration" : "Resource watches and Operation ledger drive the controller loop; credential-secret-service handler uses the toolkit to reconcile Credential status, finalizers, Process health, and service lifecycle.", + "removalProof" : "None — shared toolkit is additive; v2 trait removal is tracked by ADR046-cred-ss-003/001 parity", + "reuseAction" : "create", + "reuseSource" : null, + "specId" : "ADR-046-provider-credential-secret-service", + "specPath" : "docs/specs/providers/ADR-046-provider-credential-secret-service.md", + "validation" : "Shared reconciliation tests from `ADR-046-resources-credential`; credential-secret-service lifecycle/fault tests verify the handler integration", + "workItemId" : "ADR046-cred-ss-004" + }, + { + "currentSource" : "None — net-new v3 `d2b.zones..resources.` Credential/Provider authoring surface; no pre-ADR45 baseline equivalent", + "dataMigration" : "Full d2b 3.0 reset; no old credential config is imported into v3 resources", + "dependencyOwner" : "Dependency for ADR046-cred-ss-003; owner: Nix resource compiler and activation cleanup", + "destination" : "nixos-modules/options-resources.nix; nixos-modules/activation-nixos-cleanup.nix", + "detailedDesign" : "Nix compiler: implement `d2b.zones..resources.` authoring, eval-time assertions, canonical JSON emission, artifact catalog, bundle digest, and generation cleanup contract. Full detail remains in `ADR-046-resources-credential` §Implementation work items. Primary reuse disposition: `create`. Preserved source-plan detail: net-new Nix resource emission and cleanup contract.", + "integration" : "Nix emits Provider/Credential resource JSON and artifact catalog entries; ResourceAPI admission and credential-secret-service controller consume the rendered resources; activation cleanup issues async Delete/finalizer flow on generation removal.", + "removalProof" : "None — new v3 Nix resource surface; old trait removal waits for controller parity", + "reuseAction" : "create", + "reuseSource" : null, + "specId" : "ADR-046-provider-credential-secret-service", + "specPath" : "docs/specs/providers/ADR-046-provider-credential-secret-service.md", + "validation" : "Nix eval/assertion/golden tests from `ADR-046-resources-credential`; credential-secret-service cleanup rollback integration fixture", + "workItemId" : "ADR046-cred-ss-005" + }, + { + "currentSource" : "`packages/d2b-core/src/realm_workloads_launcher.rs:LauncherMetadataInvariants.no_secrets_or_credentials`; secret-service main reuse canary tests listed in §14", + "dataMigration" : "None — audit/telemetry only; no runtime state migration", + "dependencyOwner" : "Dependency for ADR046-cred-ss-003; owner: credential-secret-service audit and telemetry implementation", + "destination" : "packages/d2b-provider-credential-secret-service/src/{audit.rs,telemetry.rs}", + "detailedDesign" : "Audit/OTEL: emit audit records and OTEL spans/metrics for all credential service methods and controller events, with canary enforcement and no token/object-path/lease bytes in status, delivery outer headers, audit, metrics, spans, or logs. Full detail remains in `ADR-046-resources-credential` §Implementation work items. Primary reuse disposition: `adapt`. Preserved source-plan detail: adapt zero-secret invariant and canary test pattern to credential-secret-service audit/OTEL surfaces.", + "integration" : "Controller and service methods call audit/telemetry helpers; audit subsystem and OTEL exporters consume bounded event/span/metric records; canary tests verify every public observable surface stays secret-free.", + "removalProof" : "None — audit/telemetry helpers are new; no prior owner to remove", + "reuseAction" : "adapt", + "reuseSource" : null, + "specId" : "ADR-046-provider-credential-secret-service", + "specPath" : "docs/specs/providers/ADR-046-provider-credential-secret-service.md", + "validation" : "Credential audit/OTEL tests from `ADR-046-resources-credential`; `tests/canary.rs` and `tests/delivery.rs` for credential-secret-service", + "workItemId" : "ADR046-cred-ss-006" + }, + { + "currentSource" : "`packages/d2b-realm-provider/src/credential.rs`: `OpaqueAzureRef`, parse/deserialize/charset validation, tests", + "dataMigration" : "Full d2b 3.0 reset; no v2 credential import", + "dependencyOwner" : "`ADR046-object-001` (resource envelope); `ADR046-identities-001` (types); W0 shared contract root; `d2b-contracts`", + "destination" : "`packages/d2b-contracts/src/v3/credential.rs`", + "detailedDesign" : "Define `CredentialSpec`, `CredentialStatus`, `CredentialLeaseHandle` (opaque bounded newtype), `CredentialRotationPolicy`, `CredentialRevocationPolicy`, `CredentialScope`, `OperationClass` enum, `CredentialLeaseState`, `PlacementBinding`, `CredentialConditionType`, and all serde/validation/redaction helpers; reuse `OpaqueAzureRef` from v3 baseline directly; enforce zero-secret-bytes charset validation on all string fields at construction Primary reuse disposition: `adapt`. Preserved source-plan detail: extract and adapt.", + "integration" : "Credential controller, Provider dossier schemas, Nix compiler, and resource store all consume one canonical contract", + "removalProof" : "Old `CredentialProvider` trait and `CredentialStatus` enum removed only after all v3 Credential Provider controllers consume this contract", + "reuseAction" : "adapt", + "reuseSource" : "main `a1cc0b2d`: `d2b-provider-credential-secret-service/src/lib.rs` types: `SecretServiceLeaseRequest`, `SecretServiceLeaseRef`, `SecretServiceLeaseGrant`, `SecretServiceLeaseInspection`, `SecretServiceLeaseRenewal`, `SecretServiceLeaseRevocation`, `SecretServiceLeaseState`; parallel entra/managed-identity types; `CredentialLease`, `CredentialLeaseRequest`, `CredentialLeaseState` from `d2b-contracts/src/v2_provider.rs`", + "specId" : "ADR-046-resources-credential", + "specPath" : "docs/specs/ADR-046-resources-credential.md", + "validation" : "Schema golden vectors; charset/length tests; serde unknown-field rejection; `OpaqueAzureRef` round-trip and secret-shape rejection parity; `leaseHandle` and `sourceVersion` opaque newtype tests; status redaction tests", + "workItemId" : "ADR046-credential-001" + }, + { + "currentSource" : "`packages/d2b-realm-provider/src/provider.rs:CredentialProvider` (status-only trait); `d2b-contracts/proto/v2/provider_credential.proto` (main: Health, Capabilities, Status, AcquireLease, RefreshLease, RevokeLease)", + "dataMigration" : "None — full d2b 3.0 reset; no prior state to migrate", + "dependencyOwner" : "`ADR046-credential-001`; `ADR046-api-001` (resource API); `ADR046-session-003` (d2b-bus); Credential service owner", + "destination" : "`packages/d2b-contracts/proto/v3/credential.proto`; `packages/d2b-credential-service/src/{service.rs, client.rs, server.rs}`", + "detailedDesign" : "Define `d2b.credential.v3` protobuf service with methods: `Status`, `AcquireToken`, `RefreshToken`, `RevokeToken`, `SignChallenge`, `InspectMetadata`; each request carries `credential_ref`, `operation_class`, `operation_id`, `idempotency_key`, `requested_expiry_unix_ms`, `deadline_unix_ms`; `Status`, `RevokeToken`, and `InspectMetadata` responses carry only non-secret metadata (leaseHandle digest, rotationGeneration, sourceVersion, expiresAtUnixMs, state, outcome code); `AcquireToken`, `RefreshToken`, and `SignChallenge` responses additionally include a `delivery_session_params` field carrying the binding contract fields required to establish the end-to-end credential-delivery ComponentSession (see §Credential-delivery endpoint contract); the token bytes themselves travel in the separate Noise-encrypted delivery session, never in the outer DTO; strict unknown-field rejection; bounded message sizes; all record wrappers for delivery sessions must be zeroizing types", + "integration" : "d2b-bus routes `d2b.credential.v3` service to the exact credential provider Process identified by `Credential.spec.providerRef`; RBAC checks `use-credential` verb before dispatch; for `AcquireToken`/`RefreshToken`/`SignChallenge`, bus additionally authorizes the credential-delivery endpoint route and forwards opaque Noise-encrypted delivery records without terminating or buffering them; bus never stores or inspects delivery record plaintext", + "removalProof" : "Old v2 `CredentialProviderService` proto removed only after all v3 callers migrate", + "reuseAction" : "adapt", + "reuseSource" : "main `a1cc0b2d`: `packages/d2b-contracts/proto/v2/provider_credential.proto` method names", + "specId" : "ADR-046-resources-credential", + "specPath" : "docs/specs/ADR-046-resources-credential.md", + "validation" : "Protocol golden vectors for each method; malformed/oversize rejection; `leaseHandle` opacity tests (secret-canary must not appear in outer DTO or delivery routing metadata); locked/unavailable/denied/expired state tests; delivery session binding contract round-trip; zeroizing record type unit tests; delivery channel never materialized in non-delivery method tests", + "workItemId" : "ADR046-credential-002" + }, + { + "currentSource" : "`packages/d2b-realm-provider/src/provider.rs:CredentialProvider` (minimal v3 baseline)", + "dataMigration" : "Full reset; no migration from old `CredentialProvider` trait", + "dependencyOwner" : "`ADR046-credential-001`, `ADR046-credential-002`; `ADR046-reconcile-001`; credential-secret-service owner", + "destination" : "`packages/d2b-provider-credential-secret-service/src/{lib.rs, controller.rs, service.rs, main.rs}` (implementation + binary); `packages/d2b-provider-credential-secret-service/tests/{lifecycle.rs, conformance.rs, faults.rs, canary.rs, delivery.rs, placement.rs}` (hermetic Cargo integration); `packages/d2b-provider-credential-secret-service/integration/{container-service.sh, host-placement.nix, cleanup-rollback.sh}` (orchestration fixtures); `packages/d2b-provider-credential-secret-service/README.md` (all §Provider README required sections)", + "detailedDesign" : "Adapt `SecretServiceCredentialProvider` and `SecretServiceCredentialProviderFactory` to use v3 `d2b.credential.v3` service interface; replace v2 `CredentialProvider` trait impl with v3 controller/service handler; adapt `Oo7SecretServicePort` trait (retain all methods unchanged); ensure `SecretServiceOwner::Userd` placement restriction rejects system-domain and guest-agent construction; validate `collectionAlias` against provider-internal charset (not `OpaqueAzureRef`; collection aliases may include spaces); integrate with Provider resource descriptor and controller toolkit; test `credential_canary` never appears in any service response Primary reuse disposition: `adapt`. Preserved source-plan detail: copy and adapt.", + "integration" : "Target: user-domain Process (ADR-only `Process` ResourceType) under Host (ADR-only `Host` ResourceType); d2b-bus (ADR-only) routes `d2b.credential.v3` calls to this process; Credential controller reconciles status. Current v3 has no user-credential host process: v3 `d2b-userd` (`packages/d2b-userd/src/lib.rs`) is a guest exec stub only (exits 78 in service mode; `UserAttachRequest`/`UserExecSession` guest wire primitives only; evidence class: `test-only-or-preview`); no credential or keyring functionality. This integration path is fully new (ADR-only) work.", + "removalProof" : "Old `d2b-realm-provider:CredentialProvider` trait removed only after this controller and the other two Credential controllers reach full reconcile parity", + "reuseAction" : "adapt", + "reuseSource" : "main `a1cc0b2d`: `packages/d2b-provider-credential-secret-service/src/lib.rs` (full implementation); `src/tests.rs` (full test suite including `FakeOo7Port`, lease lifecycle, locked state, canary enforcement, cardinality limits)", + "specId" : "ADR-046-resources-credential", + "specPath" : "docs/specs/ADR-046-resources-credential.md", + "validation" : "**`src/` unit** (`#[cfg(test)]` in `src/`): `Oo7SecretServicePort` trait API surface, `SecretServiceOwner` placement guard, `collectionAlias` charset, `lockPolicy` state transitions. **`tests/` Cargo integration** (`cargo test -p d2b-provider-credential-secret-service`): copied test suite from main with v3 type substitutions; add `lifecycle.rs` (acquire/refresh/revoke/inspect end-to-end with `FakeOo7Port`); `conformance.rs` (all 11 `check_provider_conformance` arms pass); `faults.rs` (locked state → `credential-provider-unavailable`, unavailable, cardinality limit); `canary.rs` (`credential_canary` and `object_path_canary` absent from every response, status field, and delivery record); `delivery.rs` (delivery-session binding contract, zeroizing buffer, replay-safe sequence); `placement.rs` (system-domain and guest-agent construction rejected). **`integration/` fixtures**: `container-service.sh` (container-backed Provider service start/stop/drain); `host-placement.nix` (user-domain Host/Process placement in runNixOSTest); `cleanup-rollback.sh` (Nix-generation removal triggers async Delete and Provider-revoke finalizer).", + "workItemId" : "ADR046-credential-003" + }, + { + "currentSource" : "`packages/d2b-realm-provider/src/credential.rs:AzureControlPlaneRef`, `OpaqueAzureRef` (v3 baseline, reachable)", + "dataMigration" : "Full reset", + "dependencyOwner" : "`ADR046-credential-001`, `ADR046-credential-002`; `ADR046-reconcile-001`; credential-entra owner", + "destination" : "`packages/d2b-provider-credential-entra/src/{lib.rs, controller.rs, service.rs, main.rs}`; `packages/d2b-provider-credential-entra/tests/{lifecycle.rs, conformance.rs, faults.rs, canary.rs, delivery.rs, placement.rs}`; `packages/d2b-provider-credential-entra/integration/{container-service.sh, guest-placement.nix, cleanup-rollback.sh}`; `packages/d2b-provider-credential-entra/README.md` (all §Provider README required sections)", + "detailedDesign" : "Adapt `EntraCredentialProvider` and `EntraCredentialProviderFactory` to v3 service; replace v2 `AgentPlacementBinding` with v3 `PlacementBinding` enum (user-agent, guest-agent only; reject host-system); validate `tenantId` config field using `OpaqueAzureRef::parse` from v3 baseline `d2b-realm-provider/src/credential.rs` (note: current v3 source field is named via `AzureControlPlaneRef`; target field name is `tenantId`); retain `EntraCredentialClient` trait unchanged; map `EntraClientError::InteractionRequired` to `credential-provider-unavailable` (not denied); enforce `EntraCredentialOwner::ExactConsumer` so only the declared `consumerRef` may acquire Primary reuse disposition: `adapt`. Preserved source-plan detail: copy and adapt.", + "integration" : "User-domain or system-domain Process under Guest; d2b-bus routing; Credential controller", + "removalProof" : "Same as ADR046-credential-003", + "reuseAction" : "adapt", + "reuseSource" : "main `a1cc0b2d`: `packages/d2b-provider-credential-entra/src/lib.rs` (full implementation); `src/tests.rs` (full test suite including `FakeEntraClient`, `credential_canary`/`endpoint_canary`, interaction-required, colocated-consumer, generation-mismatch tests)", + "specId" : "ADR-046-resources-credential", + "specPath" : "docs/specs/ADR-046-resources-credential.md", + "validation" : "**`src/` unit**: `EntraCredentialClient` trait API, `OpaqueAzureRef::parse` on `tenantId`, `EntraCredentialOwner::ExactConsumer` guard, `EntraClientState` transitions. **`tests/` Cargo integration**: `lifecycle.rs` (acquire/refresh/revoke/inspect with `FakeEntraClient`); `conformance.rs` (all conformance arms); `faults.rs` (interaction-required → unavailable, generation-mismatch, colocated-consumer rejection); `canary.rs` (`credential_canary` and `endpoint_canary` absent from every response and delivery record); `delivery.rs` (delivery-session binding, zeroizing, replay-safe); `placement.rs` (host-system placement rejected). **`integration/` fixtures**: `container-service.sh`; `guest-placement.nix` (user-domain and system-domain Process on Guest in runNixOSTest); `cleanup-rollback.sh`.", + "workItemId" : "ADR046-credential-004" + }, + { + "currentSource" : "`packages/d2b-realm-provider/src/credential.rs:ManagedIdentityRef` (v3 baseline, reachable); `d2bd/src/lib.rs:managed_identity_client_id` (reachable ACA config); `d2b-provider-aca/src/lib.rs:managed_identity_client_id` (reachable)", + "dataMigration" : "Full reset; `d2b-provider-aca` managed_identity_client_id config field migrated to a Credential resource reference in the v3 ACA Provider config", + "dependencyOwner" : "`ADR046-credential-001`, `ADR046-credential-002`; `ADR046-reconcile-001`; credential-managed-identity owner", + "destination" : "`packages/d2b-provider-credential-managed-identity/src/{lib.rs, controller.rs, service.rs, main.rs}`; `packages/d2b-provider-credential-managed-identity/tests/{lifecycle.rs, conformance.rs, faults.rs, canary.rs, delivery.rs, placement.rs}`; `packages/d2b-provider-credential-managed-identity/integration/{container-service.sh, host-guest-placement.nix, aca-credential-ref.sh, cleanup-rollback.sh}`; `packages/d2b-provider-credential-managed-identity/README.md` (all §Provider README required sections)", + "detailedDesign" : "Adapt `ManagedIdentityCredentialProvider` to v3 service; enforce `ManagedIdentityCredentialOwner::ExactSdkConsumer`; reject user-agent placement (IMDS is machine-local, not user-session); validate `clientId` config using `OpaqueAzureRef::parse` directly from v3 baseline (note: current v3 source field is named via `ManagedIdentityRef.client_id`; target field name is `clientId`); retain `ManagedIdentityCredentialClient` trait unchanged; map `ManagedIdentityClientState::Unavailable` to `credential-provider-unavailable`; `sign-challenge` operation class returns schema-invalid immediately Primary reuse disposition: `adapt`. Preserved source-plan detail: copy and adapt.", + "integration" : "System-domain Process under Host or Guest; d2b-bus routing; Credential controller; ACA runtime-azure-container-apps Provider may hold a reference to this Credential resource", + "removalProof" : "`d2b-provider-aca:managed_identity_client_id` raw field removed only after `credential-managed-identity` Provider controller is integrated and the ACA Provider config uses `credentialRef`", + "reuseAction" : "adapt", + "reuseSource" : "main `a1cc0b2d`: `packages/d2b-provider-credential-managed-identity/src/lib.rs` (full implementation); `src/tests.rs` (full test suite)", + "specId" : "ADR-046-resources-credential", + "specPath" : "docs/specs/ADR-046-resources-credential.md", + "validation" : "**`src/` unit**: `ManagedIdentityCredentialClient` trait, `OpaqueAzureRef::parse` on `clientId`, `ManagedIdentityCredentialOwner::ExactSdkConsumer` guard, `imdsEndpointAlias` validation, `sign-challenge` schema-invalid fast path. **`tests/` Cargo integration**: `lifecycle.rs` (acquire/refresh/revoke/inspect with `FakeClient`); `conformance.rs`; `faults.rs` (unavailable state, colocated-consumer rejection); `canary.rs` (canary absent from all responses and delivery records); `delivery.rs`; `placement.rs` (user-agent placement rejected). **`integration/` fixtures**: `container-service.sh`; `host-guest-placement.nix` (system-domain Host and Guest placement in runNixOSTest); `aca-credential-ref.sh` (ACA Provider config uses `credentialRef`; raw `managed_identity_client_id` absent); `cleanup-rollback.sh`.", + "workItemId" : "ADR046-credential-005" + }, + { + "currentSource" : "No direct v3 current source; controller is `ADR-only`", + "dataMigration" : "None; v3 reset", + "dependencyOwner" : "`ADR046-credential-001`, `ADR046-credential-002`; `ADR046-reconcile-001`, `ADR046-reconcile-002`; Credential controller owner", + "destination" : "`packages/d2b-provider-credential-/src/controller.rs`; `packages/d2b-contracts/src/v3/credential_controller.rs`", + "detailedDesign" : "Implement Credential controller handler conforming to `ADR-046-resource-reconciliation` async loop; implement `reconcile`, `observe`, `finalize`, `drain`, and `health` handlers; implement rotation state machine (proactive/on-expiry/on-demand policies); implement `provider-revoke` finalizer execution with `revocation.onOwnerDelete` policy; implement provider-generation-change detection and revocation; implement `CredentialReady`, `RotationDue`, `ProviderUnavailable`, `LeaseRevoked` condition logic; implement bounded idempotency key derivation (Credential UID + rotationGeneration + operation class, no secret material); implement `observeInterval=30s` health check calling `InspectMetadata`; bounded retry/backpressure with typed `credential-rotation-failed` outcome; enforce `MAX_LOCAL_LEASES=256` per controller provider instance", + "integration" : "Provider controller Process → d2b-bus → `d2b.credential.v3` service in provider process → injected client/port; status updates through resource API; watch subscription on Credential, Provider, Host/Guest dependency types", + "removalProof" : "Not applicable (new controller)", + "reuseAction" : "adapt", + "reuseSource" : "main `a1cc0b2d`: `packages/d2b-provider-toolkit/src/conformance.rs` (provider conformance pattern); `packages/d2b-provider-toolkit/src/adapter.rs` (controller toolkit pattern)", + "specId" : "ADR-046-resources-credential", + "specPath" : "docs/specs/ADR-046-resources-credential.md", + "validation" : "Controller state-machine golden vectors; rotation-policy matrix (proactive/on-demand/on-expiry × success/locked/unavailable/expired); finalizer execution tests; provider-generation-change revocation tests; idempotency key derivation tests; observe-interval drift detection test; canary tests confirm zero secret bytes in all controller-written status fields", + "workItemId" : "ADR046-credential-006" + }, + { + "currentSource" : "`nixos-modules/assertions.nix` (secret-shape assertions, `implemented-and-reachable`). Note: `nixos-modules/options-realms.nix` line 318 contains `d2b.realms..relay.credentialRef` — this uses the current v3 **Realm** terminology (current symbol for Zone) and is a relay credential **state-directory path** reference for gateway relay provisioning, not a Zone Credential resource declaration. It is not a source for this work item. No current v3 source exists for Zone Credential resource Nix declarations (ADR-only).", + "dataMigration" : "None; v3 reset", + "dependencyOwner" : "`ADR046-credential-001`; `ADR046-identities-002` (Nix resource compiler); `ADR046-api-001` (resource API, for create/update/delete); `ADR046-reconcile-001` (activation-nixos controller); Nix integrator", + "destination" : "`nixos-modules/options-resources.nix` (generic schema-derived resource options; not type-specific), `nixos-modules/activation-nixos-cleanup.nix`", + "detailedDesign" : "**(1) Schema-derived options and eval-time validation**: implement `d2b.zones..resources. = { type = \"...\"; spec = { ... }; }` as a generic attrset option; an optional `metadata` sub-attr may contain `ownerRef` and/or presentation `labels`/`annotations`. Nix option types, defaults, and inline docs for `spec` fields are generated from the committed `ResourceTypeSchema` JSON (`docs/reference/schemas/v3/credential.json`) and the signed Provider schema — no bespoke options module is maintained separately. `metadata.name` derives from the attr key; `metadata.zone` from the Zone attr key; `apiVersion` defaults to `resources.d2bus.org/v3`; `status`/`uid`/`generation`/`revision`/timestamps/`managedBy`/`configurationGeneration` are not authored. Core assigns `metadata.managedBy = \"configuration\"` and `metadata.configurationGeneration = ` to all configuration-managed resources at create/update time; these are never authored in Nix. Eval-time assertions (applied to all entries with `type = \"Credential\"`): `spec.providerRef` resolves a Provider in the same Zone whose `credentialDomains` includes `spec.scope.domainFilter` and whose `spec.artifactId` (a sibling of `spec.config` on the Provider resource, not inside `spec.config`) resolves an artifact catalog entry of `type = \"provider\"`; `spec.audience` charset (`^[A-Za-z0-9._:/@-]+$`, max 256); `spec.rotation.proactiveWindowMs < maxLeaseLifetimeMs / 2`; `spec.consumerRef`/`scope.executionRef`/`scope.userRef` resolve declared Zone resources; duplicate `(providerRef, executionRef, userRef, audience)` tuple rejected; `contains_sensitive_shape` on all string fields; Provider-specific placement constraints; `allowedOperations` ⊆ `providerRef.supportedOperations`. **(2) Canonical JSON and bundle emission**: render `spec` attr directly to `spec` object in canonical JSON (no field renames/re-nesting); `metadata` in output contains only derived `name`/`zone` and optionally Nix-authored `ownerRef`/`labels`/`annotations`; `apiVersion` is top-level, not inside `metadata`; `finalizers` is omitted from the Nix-rendered input (core manages finalizers, never accepts them from the bundle); no management labels are emitted by Nix; sort bundle by `(type, name)`; write to `/etc/d2b/zones//resource-bundle.json` with digest. **(2b) Artifact catalog emission**: derivation-valued inputs (`d2b.artifacts.`) are compiled separately into an integrity-pinned artifact catalog (`/etc/d2b/zones//artifact-catalog.json`) with its own digest header; each entry records `id`, `type`, `sha256`, and bounded closure metadata; store paths are private catalog implementation data absent from the resource bundle, status, audit, and logs; `activation-nixos` verifies both digests before any create/update; missing or wrong-type `artifactId` references fail the NixOS build. **(3) Build-time schema validation**: validate rendered JSON against `docs/reference/schemas/v3/credential.json` and Provider-specific schema; enforce `secretRef` fields use `Credential/` refs; enforce no store paths in any resource bundle or status output; drift gate (`make test-drift`) regenerates schemas with `cargo xtask gen-schemas` and asserts `git diff --exit-code`; Nix options module drift checked in the same gate. **(4) Generation transition and cleanup contract**: activation-nixos controller verifies SHA-256 digest of both resource bundle and artifact catalog, creates/updates desired-set resources, activates without blocking on cleanup, issues async Delete for absent configuration-managed resources (those with `metadata.managedBy = \"configuration\"`), sets Degraded/Cleanup=True on removed resources; retains up to `retainedConfigurationMax` (default 3, range 1..16) prior bundles; oldest prune when count exceeded; no time-based rollback window. **(5) Configuration-managed vs controller/API isolation**: `managedBy` and `ownerRef` are orthogonal; configuration-managed and API-created resources may each carry an optional same-Zone `ownerRef` and participate in owner cascade. Cleanup checks `metadata.managedBy = \"configuration\"` before issuing Delete; resources with `metadata.managedBy = \"controller\"` or `metadata.managedBy = \"api\"` are never deleted by this path; API-created resources persist until explicit Delete and are never generation-swept.", + "integration" : "`activation-nixos` Provider creates/updates Credential resources from emitted envelopes; Credential controller `provider-revoke` finalizer handles cleanup Deletes; owner controller reconciles children of deleted configuration-managed Credentials", + "removalProof" : "Not applicable (new module)", + "reuseAction" : "adapt", + "reuseSource" : null, + "specId" : "ADR-046-resources-credential", + "specPath" : "docs/specs/ADR-046-resources-credential.md", + "validation" : "**(eval/build)**: nix-unit golden JSON envelope for each example (spec shape, no management labels in Nix output, sort, digest); assertion-failure tests for secret-shaped audience, mismatched providerRef/domainFilter, proactiveWindow > half maxLifetime, duplicate binding tuple, unresolved refs; artifact catalog: assertion-failure for missing `artifactId`, wrong-type `artifactId`, duplicate catalog ID; bundle + artifact catalog digest round-trip; artifact catalog store-path absence from resource bundle and status; Provider-specific schema cross-check; `make test-drift` schema drift gate. **(runtime integration in `tests/host-integration/`)**: `credential-cleanup-basic` (removed resource reaches Deleted); `credential-cleanup-nonblocking` (activation Ready before cleanup finalizer finishes); `credential-cleanup-pending-status` (Cleanup=True on removed resource, PendingCleanup=True on Provider); `credential-cleanup-stalled` (Degraded stall detection and recovery); `credential-cleanup-controller-children-preserved` (ownerRef children cleaned by Credential controller); `credential-cleanup-no-dynamic-deletion` (controller-created Credential with `managedBy = \"controller\"` not deleted); `credential-retained-generation-count` (up to retainedConfigurationMax bundles retained; rollback re-creates from retained bundle; oldest pruned when count exceeded); `credential-bundle-digest-mismatch` (tampered bundle aborts activation).", + "workItemId" : "ADR046-credential-007" + }, + { + "currentSource" : "`packages/d2b-core/src/privileges.rs:SecretAccess` (implemented-and-reachable); `d2b-realm-provider/src/error.rs:ProviderDiagnostic`/`contains_sensitive_shape` (implemented-and-reachable); `packages/d2b-contract-tests/tests/policy_observability.rs` (reachable audit policy tests)", + "dataMigration" : "None — full d2b 3.0 reset; no prior state to migrate", + "dependencyOwner" : "`ADR046-credential-001`, `ADR046-credential-006`; audit/OTEL integrator", + "destination" : "`packages/d2b-provider-credential-/src/audit.rs`, `telemetry.rs`; `packages/d2b-contract-tests/tests/credential_audit.rs`", + "detailedDesign" : "Implement audit record emission for all credential service methods and controller events using the field set defined in §Audit; implement OTEL span/metric emission using the closed label set in §OTEL and metrics; implement `contains_sensitive_shape` check in all string fields of audit records and metric label values (adapted from `d2b-realm-provider/src/error.rs:contains_sensitive_shape`); add canary-enforcement tests that verify `\"secret-canary\"`, `\"entra-token-canary\"`, and `\"managed-identity-canary\"` values never appear in any audit record, metric label, span attribute, log line, or status field across all Provider test suites", + "integration" : "Credential controller and service handlers emit audit records and telemetry through Zone audit/OTEL paths", + "removalProof" : "Not applicable", + "reuseAction" : "adapt", + "reuseSource" : null, + "specId" : "ADR-046-resources-credential", + "specPath" : "docs/specs/ADR-046-resources-credential.md", + "validation" : "Canary tests across all three Provider crates; audit record field-presence tests; metric label cardinality tests; span attribute absence tests for forbidden fields", + "workItemId" : "ADR046-credential-008" + }, + { + "currentSource" : "v3 evidence baseline plus parent/spec contradictions", + "dataMigration" : "None — full d2b 3.0 reset; no prior state to migrate", + "dependencyOwner" : "ADR 0046 integrator", + "destination" : "`docs/specs/ADR-046-decision-register.md`", + "detailedDesign" : "Record every evidence-underdetermined choice before dependent spec work proceeds", + "integration" : "Parent decision summary and all affected specs link the decision ID", + "removalProof" : "Not applicable", + "reuseAction" : "adapt", + "reuseSource" : null, + "specId" : "ADR-046-decision-register", + "specPath" : "docs/specs/ADR-046-decision-register.md", + "validation" : "Zero unresolved entries before pre-panel review; manifest/link consistency", + "workItemId" : "ADR046-decisions-001" + }, + { + "currentSource" : "none in this repository; `Makefile` heavy-lane targets do not yet exist", + "dataMigration" : "None — net-new tooling", + "dependencyOwner" : "`ADR046-W0`; delivery-tooling integrator", + "destination" : "`packages/xtask/src/heavy_gate.rs`; `Makefile` targets `heavy-check`, `heavy-test-integration`, `heavy-test-host-integration`, `heavy-test-hardware`, `heavy-cargo-test`, `heavy-flake-check`", + "detailedDesign" : "Two-slot per-UID OFD-locked semaphore, 250 ms nonblocking retry up to 30 minutes, fail-closed on unsupported locking, duplicated locked-FD handoff to child, wrapper-owned group-signal/reap, as specified in §11 Primary reuse disposition: `adapt`. Preserved source-plan detail: copy-unchanged, then adapt paths/crate names to this repository's `packages/xtask` layout.", + "integration" : "Every heavy lane in §10.4/§10.10/§10.11 routes through this one binary; no wave adds a second lock mechanism", + "removalProof" : "Not applicable (net-new; nothing to remove)", + "reuseAction" : "adapt", + "reuseSource" : "sibling-lineage `cargo xtask heavy-gate` implementation (per D001/D041 unrestricted-reuse policy)", + "specId" : "ADR-046-validation-and-delivery", + "specPath" : "docs/specs/ADR-046-validation-and-delivery.md", + "validation" : "Unit tests for slot acquisition/timeout/fail-closed paths; integration test spawning two concurrent heavy-gate invocations and asserting the second blocks until the first releases", + "workItemId" : "ADR046-delivery-001" + }, + { + "currentSource" : "none in this repository", + "dataMigration" : "None — full d2b 3.0 reset; no prior state to migrate", + "dependencyOwner" : "`ADR046-W0`; delivery-tooling integrator", + "destination" : "`packages/xtask/src/delivery/snapshot.rs`", + "detailedDesign" : "Binds base/head OIDs, dependency graph, repository set into `candidate_id`/`content_id`/`snapshot_sha256` per §12.1 Primary reuse disposition: `adapt`. Preserved source-plan detail: copy-unchanged, then adapt.", + "integration" : "Called by the integrator immediately after PR opening (§13.1), before any validator/panel lane starts", + "removalProof" : "Not applicable", + "reuseAction" : "adapt", + "reuseSource" : "sibling-lineage `cargo xtask delivery wave snapshot` implementation", + "specId" : "ADR-046-validation-and-delivery", + "specPath" : "docs/specs/ADR-046-validation-and-delivery.md", + "validation" : "Unit tests asserting identical inputs produce identical digests and any single-byte content change produces a different `content_id`", + "workItemId" : "ADR046-delivery-002" + }, + { + "currentSource" : "none in this repository", + "dataMigration" : "None — full d2b 3.0 reset; no prior state to migrate", + "dependencyOwner" : "`ADR046-delivery-002`; delivery-tooling integrator", + "destination" : "`packages/xtask/src/delivery/validate_import.rs`; external candidate-ID-addressed evidence directory (never under Git)", + "detailedDesign" : "Imports CI/local/host validator command/result evidence, keyed by `candidate_id`, per §12.2 Primary reuse disposition: `adapt`. Preserved source-plan detail: copy-unchanged, then adapt.", + "integration" : "Consumed by `wave seal` (§ADR046-delivery-005) as one of the seal's required inputs", + "removalProof" : "Not applicable", + "reuseAction" : "adapt", + "reuseSource" : "sibling-lineage `cargo xtask delivery wave validate-import` implementation", + "specId" : "ADR-046-validation-and-delivery", + "specPath" : "docs/specs/ADR-046-validation-and-delivery.md", + "validation" : "Test asserting evidence for a stale `candidate_id` is rejected; test asserting raw command output never lands in a tracked file", + "workItemId" : "ADR046-delivery-003" + }, + { + "currentSource" : "`docs/specs/README.md`'s described-but-not-yet-generated `ADR-046-spec-set.json`/`ADR-046-work-items.json` contract", + "dataMigration" : "None — full d2b 3.0 reset; no prior state to migrate", + "dependencyOwner" : "`ADR046-delivery-002`; spec-set integrator", + "destination" : "`packages/xtask/src/gen_spec_set.rs`; `docs/specs/ADR-046-spec-set.json`, `docs/specs/ADR-046-work-items.json`", + "detailedDesign" : "Enumerates every `docs/specs/ADR-046-*.md` and `docs/specs/providers/ADR-046-provider-*.md` file, its metadata table, registered globally unique `workItemPrefix`, content digest, and every `### ADR046--` work item, per §8. Primary reuse disposition: `adapt`. Preserved source-plan detail: adapt (new generator, following the existing `xtask gen-schemas`/`gen-nix-options` pattern already used for other generated artifacts).", + "integration" : "`make test-drift` gains a row running this generator and `git diff --exit-code`; every wave's exit criteria (§4) require it committed as the wave's last commit", + "removalProof" : "Not applicable", + "reuseAction" : "adapt", + "reuseSource" : null, + "specId" : "ADR-046-validation-and-delivery", + "specPath" : "docs/specs/ADR-046-validation-and-delivery.md", + "validation" : "Golden-fixture test against a small synthetic spec directory; drift test against the real `docs/specs/` tree", + "workItemId" : "ADR046-delivery-004" + }, + { + "currentSource" : "none in this repository; this repository's existing `AGENTS.md` panel-review process is host-local script tooling (`/etc/nixos/scripts/panel-review.{md,sh}`), not a candidate-bound `xtask` subcommand", + "dataMigration" : "None — full d2b 3.0 reset; no prior state to migrate", + "dependencyOwner" : "`ADR046-delivery-002`, `ADR046-delivery-003`; panel-tooling integrator", + "destination" : "`packages/xtask/src/delivery/panel.rs`", + "detailedDesign" : "`panel-request` writes the candidate-bound request naming the exact ten roles and required model; `panel-attest` validates a directory of exactly ten strict 13-field records, rejecting wrong model/candidate binding, duplicate provider/run provenance, or inconsistent `signoff`/`recommendations`, per §12.3 Primary reuse disposition: `adapt`. Preserved source-plan detail: copy-unchanged, then adapt to bind the fixed `gemini-3.1-pro-preview`/`github-copilot` model/provider pair and this repository's existing ten-role roster (§12.3).", + "integration" : "Every wave's exit criteria (§4) require ten unanimous attested records before `wave seal`", + "removalProof" : "Not applicable", + "reuseAction" : "adapt", + "reuseSource" : "sibling-lineage `cargo xtask delivery wave panel-request`/`panel-attest` implementation", + "specId" : "ADR-046-validation-and-delivery", + "specPath" : "docs/specs/ADR-046-validation-and-delivery.md", + "validation" : "Unit tests for every rejection class (wrong model, missing role, duplicate run_id, `signoff:true` with non-empty `recommendations`); integration test with ten synthetic valid records passing", + "workItemId" : "ADR046-delivery-005" + }, + { + "currentSource" : "none in this repository", + "dataMigration" : "None — full d2b 3.0 reset; no prior state to migrate", + "dependencyOwner" : "`ADR046-delivery-002`, `ADR046-delivery-004`, `ADR046-delivery-005`; delivery-tooling integrator", + "destination" : "`packages/xtask/src/delivery/{seal,eligibility,history_proof}.rs`", + "detailedDesign" : "`seal` requires all ten panel records unanimous and bound to the same candidate/content/snapshot digests plus every validator lane passing; `merge-eligibility` checks each stacked PR's current base/head against the sealed OIDs or a passing history-proof; `history_proof` verifies byte-identical integrated content/generated artifacts/dependency diff/repository set across a rebase, per §12.4/§12.6 Primary reuse disposition: `adapt`. Preserved source-plan detail: copy-unchanged, then adapt.", + "integration" : "`make check` gains no new required step for ordinary contributors; this tooling is invoked only by the wave integrator per §4/§13", + "removalProof" : "Not applicable", + "reuseAction" : "adapt", + "reuseSource" : "sibling-lineage `cargo xtask delivery wave seal`, `merge-eligibility`, and history/byte-identity proof implementation", + "specId" : "ADR-046-validation-and-delivery", + "specPath" : "docs/specs/ADR-046-validation-and-delivery.md", + "validation" : "Unit tests for seal rejection on any missing/mismatched record; integration test proving a history-only rebase with identical content passes `history_proof` and reuses panel evidence, while any content change fails it", + "workItemId" : "ADR046-delivery-006" + }, + { + "currentSource" : "none in this repository; this codebase's existing `tests/tools/` timing logs (`d2b-static-timing.$$/`) are ad hoc, not a candidate-bound ledger", + "dataMigration" : "None — full d2b 3.0 reset; no prior state to migrate", + "dependencyOwner" : "`ADR046-W0`; delivery-tooling integrator", + "destination" : "`packages/xtask/src/test_runtime_ledger.rs`; a `make`-invokable timing gate reusing `make test-rust`/Layer-1 shard targets", + "detailedDesign" : "Measures execution-only time (after build, warm cache) per test/crate/shard against §10.16 budgets, records the reference runner/repetitions/p95, reports the top slow tests, applies a historical regression threshold, and emits a machine-readable CI artifact; the placement lint rejects a hermetic-tier test that sleeps, spawns a process, or touches network/containers/DBus/systemd/broker/Nix/KVM/hardware/live cloud, and the deterministic-clock/sleep lint rejects wall-clock sleep/retry in `src/`/`tests/`", + "integration" : "Every wave's entry/exit criteria (§4) consume the ledger artifact; `make test-rust` and Layer-1 shards run concurrently; no new top-level `tests/*.sh` gate is added", + "removalProof" : "Not applicable", + "reuseAction" : "adapt", + "reuseSource" : "existing `xtask`/`libtest --format=json` timing output; no new test framework", + "specId" : "ADR-046-validation-and-delivery", + "specPath" : "docs/specs/ADR-046-validation-and-delivery.md", + "validation" : "Policy self-tests: an intentional slow/sleep/process/network hermetic test is rejected; a synthetic timing regression fails the gate; parallel isolation holds under shuffled/parallel execution; a retired legacy selector is absent from `tests/layer1-jobs.json`, closed gate manifests, and CI shards", + "workItemId" : "ADR046-delivery-007" + }, + { + "currentSource" : "none in this repository; launch order and parallelism were previously derived only from this spec's §3/§6 prose", + "dataMigration" : "None — docs/tooling only; no runtime state", + "dependencyOwner" : "`ADR046-streamline-001`, `ADR046-W0`; delivery-tooling integrator", + "destination" : "`docs/specs/ADR-046-implementation-graph.json`, `docs/specs/ADR-046-implementation-graph.md` (generated by `ADR046-streamline-001`'s `xtask implementation-graph`); the artifact contract, generation, validation, and ready-wave query are owned by §3.5 of this spec", + "detailedDesign" : "Owns the D095 implementation-graph contract: `artifactKind`/`schemaVersion`/`adr`/`status`; one node per member spec and per work item mapped exactly once to a `W0`–`W7` wave and a file-disjoint `parallelGroup`, with `owner`/`destinations`/`entryContracts`/`prerequisites`/`blockers`/`exitGate`/`topologicalRank`; typed `spec-depends-on`/`shared-contract`/`work-item-depends-on`/`implements-spec`/`file-overlap-order` edges; the §3.5.1 ready-wave query; and the anti-serialization invariant that every ready file-disjoint group launches concurrently while a same-wave dependency is a prep barrier, not whole-wave serialization. The graph is a generated non-member artifact and does not change the 55-member `ADR-046-spec-set.json` count. Primary reuse disposition: `adapt`. Preserved source-plan detail: net-new (D095 artifact contract).", + "integration" : "Consumed by §4 wave entry/exit and §6 anti-serialization checks and by `ADR046-streamline-013`; a `tests/unit/gates/` drift gate regenerates and `git diff --exit-code`s the graph after any spec/work-item edit", + "removalProof" : "Not applicable", + "reuseAction" : "adapt", + "reuseSource" : "`ADR-046-spec-set.json`, `ADR-046-work-items.json`, and §3.1–§3.4/§3.5 of this spec; no new framework", + "specId" : "ADR-046-validation-and-delivery", + "specPath" : "docs/specs/ADR-046-validation-and-delivery.md", + "validation" : "Every 55 spec node and every work item present exactly once; all edge endpoints resolve; graph acyclic; waves monotonic (dependencies earlier or explicit same-wave prep barrier); parallel groups claim no ordering absent a dependency/file-overlap edge; deterministic JSON with no timestamps/host paths; every Mermaid node ID valid; the ready-wave query returns the expected concurrently-launchable groups on a seeded fixture", + "workItemId" : "ADR046-delivery-008" + }, + { + "currentSource" : "The fail-closed completeness, identity, and closed-action contract in `docs/specs/README.md`; the Proposed tree has no checked-in generator or policy test", + "dataMigration" : "None — documentation/build-policy contract only", + "dependencyOwner" : "`ADR046-delivery-004`, `ADR046-delivery-008`; spec-set policy-test owner", + "destination" : "`packages/xtask/src/gen_spec_set.rs`; `packages/d2b-contract-tests/tests/policy_adr046_work_items.rs`; generated spec-set, work-item, and implementation-graph drift checks", + "detailedDesign" : "Parse every normative member's declared work-item headings and tables. Require an exact Markdown/manifest bijection; exact `specId`, `specPath`, and registered prefix; globally unique prefixes and IDs; three-digit nonzero ordinals; every mandatory field exactly once and nonempty; one closed scalar `reuseAction`; and `reuseSource: null` for `create`. Reject dropped, extra, malformed, duplicate, ambiguous, or unconsumed items before writing any artifact. Validate all dependency endpoints, DAG acyclicity, wave monotonicity, and single-wave parallel groups before atomically publishing all generated files.", + "integration" : "`make test-policy` runs negative fixtures; `make test-drift` regenerates all ADR 0046 artifacts and requires a clean diff; `ADR046-delivery-008` consumes only a manifest that passed this policy", + "removalProof" : "Not applicable; the policy remains the permanent generated-artifact closure gate", + "reuseAction" : "adapt", + "reuseSource" : "`ADR046-delivery-004` generator shape and the existing `d2b-contract-tests` fixture-driven policy-test pattern", + "specId" : "ADR-046-validation-and-delivery", + "specPath" : "docs/specs/ADR-046-validation-and-delivery.md", + "validation" : "Fixtures fail for a dropped heading, extra manifest row, duplicate ID/prefix, wrong owner/path/prefix, two-digit/zero ordinal, missing/duplicate mandatory field, free-form/compound action, `create` with a reuse source, dangling dependency, cyclic DAG, backward-wave dependency, and cross-wave parallel group; the exact 55-spec real tree passes with every item once", + "workItemId" : "ADR046-delivery-009" + }, + { + "currentSource" : "`packages/d2b-contracts/src/security_key.rs` (SecurityKeyStatusResponse, SecurityKeySession, SecurityKeyLeaseState, SecurityKeyVmSessionState DTOs; implemented-and-reachable), `usbip.rs`, `broker_wire.rs`; `packages/d2b-core/src/privileges_w3.rs` (W3BrokerOperation: SecurityKeyOpenDevice, SecurityKeyApplyUdevRules, UsbipBindFirewallRule — implemented-and-reachable); `packages/d2b-core/src/manifest_v04.rs` VmEntry device fields (tpm, usbip_yubikey, security_key, graphics — old Workload manifest, generated-or-eval-contract)", + "dataMigration" : "Full reset; no v2 device object import", + "dependencyOwner" : "W0 shared contract root; `d2b-contracts`", + "destination" : "`packages/d2b-contracts/src/v3/device.rs`", + "detailedDesign" : "Device ResourceType schema (spec/status/conditions/claims/inventory); closed-set error codes; Device RBAC verbs; broker operation effect-limit constants Primary reuse disposition: `adapt`. Preserved source-plan detail: extract and adapt.", + "integration" : "Provider dossiers, resource API/store, CLI status surfaces", + "removalProof" : "Old ProcessRole/DTO branches retained until Provider integrations are live", + "reuseAction" : "adapt", + "reuseSource" : null, + "specId" : "ADR-046-resources-device", + "specPath" : "docs/specs/ADR-046-resources-device.md", + "validation" : "Schema golden vectors; unknown-field denial; exclusive/shared conflict rejection; arbitration/maxClaims invariant", + "workItemId" : "ADR046-device-001" + }, + { + "currentSource" : "`packages/d2b-host/src/swtpm_argv.rs`; `packages/d2b-priv-broker/src/ops/swtpm_dir.rs`; `nixos-modules/components/tpm.nix`", + "dataMigration" : "State dir and tamper markers preserved across reset", + "dependencyOwner" : "ADR046-device-001; device-tpm provider owner", + "destination" : "`packages/d2b-provider-device-tpm/src/` (controller, swtpm runner, state-dir logic); `packages/d2b-provider-device-tpm/tests/` (hermetic Cargo integration); `packages/d2b-provider-device-tpm/integration/` (container/Host scenarios); `packages/d2b-provider-device-tpm/README.md`", + "detailedDesign" : "Device spec/status; flush EphemeralProcess → swtpm Process sequencing; state-dir hardening; tamper-marker; finalizer non-deletion of Volume; Nix emitter; all four required crate paths present (see \"Provider crate layout\") Primary reuse disposition: `adapt`. Preserved source-plan detail: extract and adapt.", + "integration" : "Zone resource store; Process controller; Volume lifecycle", + "removalProof" : "ProcessRole::Swtpm and SwtpmPreStartFlush removed after parity", + "reuseAction" : "adapt", + "reuseSource" : null, + "specId" : "ADR-046-resources-device", + "specPath" : "docs/specs/ADR-046-resources-device.md", + "validation" : "`src/`: swtpm argv golden, state-dir, flush sequencing, finalizer no-delete; `tests/`: `controller_state_machine.rs`, `conformance.rs`, `fault_swtpm_missing.rs`; `integration/`: `provision_and_reboot/`, `tamper_marker_survives/`, `finalizer_no_delete/`; workspace policy check: `make test-policy` passes with all four paths present", + "workItemId" : "ADR046-device-002" + }, + { + "currentSource" : "`packages/d2b-contracts/src/usbip.rs` (USBIP DTOs, SYSFS_BUS_ID_MAX, bus-ID validation — implemented-and-reachable); `packages/d2b-core/src/bundle_resolver.rs` USBIP intents; `packages/d2b-core/src/privileges.rs` authz rows; `packages/d2bd/src/usbip_state_machine.rs` (typed per-busid bring-up state machine, step order: modprobe→lock→withhold→firewall→backend→bind→proxy — implemented-and-reachable); `packages/d2bd/src/usbipd_perenv_autostart.rs` (per-env usbipd daemon autostart — implemented-and-reachable); `packages/d2bd/src/usbip_reconcile_state.rs` (restart-safe reconciler state model — implemented-but-unwired); old Workload Nix option: `nixos-modules/options-realms-workloads.nix` `d2b.vms..usbip.*` (generated-or-eval-contract); `nixos-modules/components/usbip.nix`", + "dataMigration" : "None; full reset", + "dependencyOwner" : "ADR046-device-001; device-usbip provider owner", + "destination" : "`packages/d2b-provider-device-usbip/src/` (controller, daemon Process, bind/unbind EphemeralProcess, firewall); `packages/d2b-provider-device-usbip/tests/` (hermetic Cargo integration); `packages/d2b-provider-device-usbip/integration/` (container/Host scenarios); `packages/d2b-provider-device-usbip/README.md`", + "detailedDesign" : "Device spec/status; bus ID validation; firewall rule ownership-marker; bind/unbind EphemeralProcess; per-Device daemon Process (owned by device-usbip; Network supplies dependency/firewall interface); Nix emitter; all four required crate paths present (see \"Provider crate layout\") Primary reuse disposition: `adapt`. Preserved source-plan detail: extract and adapt.", + "integration" : "Zone resource store; broker `UsbipBindFirewallRule`; nftables marker", + "removalProof" : "ProcessRole::Usbip removed after parity", + "reuseAction" : "adapt", + "reuseSource" : null, + "specId" : "ADR-046-resources-device", + "specPath" : "docs/specs/ADR-046-resources-device.md", + "validation" : "`src/`: bus ID corpus, firewall marker format, EphemeralProcess creation; `tests/`: `arbitration_conflict.rs`, `conformance.rs`, `firewall_marker.rs`, `explicit_attach_split.rs`; `integration/`: `arbitration_conflict/`, `busid_bind_cycle/`, `network_firewall_coexistence/`; workspace policy check: `make test-policy` passes with all four paths present", + "workItemId" : "ADR046-device-003" + }, + { + "currentSource" : "`packages/d2b-contracts/src/security_key.rs` (DTOs — implemented-and-reachable); `packages/d2b-core/src/privileges_w3.rs` (W3BrokerOperation — implemented-and-reachable); **KEY: relay is in d2bd** — `packages/d2bd/src/security_key.rs` (CTAPHID relay: CID translation, SO_PEERCRED, hidraw async fd, accept loop — implemented-and-reachable) and `packages/d2bd/src/lib.rs:start_sk_accept_loop` (ProcessRole::SecurityKeyFrontend dispatch — implemented-and-reachable); **guest binary**: `packages/d2b-sk-frontend/src/` (static UHID frontend — implemented-and-reachable); old Workload Nix option: `nixos-modules/options-realms-workloads.nix` `d2b.vms..security_key.*`; `nixos-modules/components/security-key-guest.nix`", + "dataMigration" : "None; full reset", + "dependencyOwner" : "ADR046-device-001; device-security-key provider owner", + "destination" : "`packages/d2b-provider-device-security-key/src/` (controller, relay Process, guest frontend Process, lease/session ring); `packages/d2b-provider-device-security-key/tests/` (hermetic Cargo integration); `packages/d2b-provider-device-security-key/integration/` (container/Host/Guest scenarios); `packages/d2b-provider-device-security-key/README.md`", + "detailedDesign" : "Device spec/status; unprivileged relay Process (`device--sk-relay`); guest frontend Process (`device--sk-frontend`, `executionRef: Guest/`); ceremony/CID/lease/session ring (max 1 session per Device); broker hidraw-only access; mandatory Core-derived `(Host, physical-usb-backing, opaqueKeyDigest)` claim shared with every USB Provider before effects; Nix emitter; all four required crate paths present (see \"Provider crate layout\") Primary reuse disposition: `adapt`. Preserved source-plan detail: extract and adapt.", + "integration" : "Zone resource store; broker `SecurityKeyOpenDevice`/`SecurityKeyApplyUdevRules`; Guest frontend module", + "removalProof" : "ProcessRole::SecurityKeyFrontend removed after parity", + "reuseAction" : "adapt", + "reuseSource" : null, + "specId" : "ADR-046-resources-device", + "specPath" : "docs/specs/ADR-046-resources-device.md", + "validation" : "`src/`: lease transitions, session ring eviction, broker op path-free, CID round-trip; `tests/`: `lease_state_machine.rs`, `session_ring.rs`, `mutual_exclusion.rs` proves security-key and USB implementations resolve one token to a byte-identical physical backing key and the loser receives `physical-usb-backing-conflict` before any effect, `conformance.rs`, `guest_frontend_process.rs`; `integration/`: `lease_acquire_cancel/`, `session_ring_capacity/`, `guest_frontend_connect/`; workspace policy check: `make test-policy` passes with all four paths present", + "workItemId" : "ADR046-device-004" + }, + { + "currentSource" : "`packages/d2b-host/src/gpu_argv.rs`, `video_argv.rs`; `packages/d2b-core/src/bundle_resolver.rs` Gpu/GpuRenderNode/Video; `nixos-modules/components/graphics.nix`, `video/guest.nix`", + "dataMigration" : "None; full reset", + "dependencyOwner" : "ADR046-device-001; device-gpu provider owner", + "destination" : "`packages/d2b-provider-device-gpu/src/` (controller, GPU/render-node/video worker Processes, broker token set); `packages/d2b-provider-device-gpu/tests/` (hermetic Cargo integration); `packages/d2b-provider-device-gpu/integration/` (container/Host/Guest scenarios); `packages/d2b-provider-device-gpu/README.md`", + "detailedDesign" : "Combined Device spec/status; GPU worker Process (`device--gpu`, exclusive); render-node Process (`device--render-node`, exclusive default, shared when explicit); video-decoder Process (`device--video`); broker token set; wire-contract constants; shared render-node arbitration enforcement; Nix emitter; all four required crate paths present (see \"Provider crate layout\") Primary reuse disposition: `adapt`. Preserved source-plan detail: extract and adapt.", + "integration" : "Zone resource store; broker `SpawnRunner`/`OpenDevice`; Display Provider device consumption", + "removalProof" : "ProcessRole::Gpu, GpuRenderNode, Video removed after parity", + "reuseAction" : "adapt", + "reuseSource" : null, + "specId" : "ADR-046-resources-device", + "specPath" : "docs/specs/ADR-046-resources-device.md", + "validation" : "`src/`: process role selection, wire-constant snapshot, render-node vs full-GPU branching; `tests/`: `combined_reconcile.rs`, `render_node_enforcement.rs`, `wire_constant_snapshot.rs`, `conformance.rs`; `integration/`: `gpu_worker_start/`, `render_node_shared/`, `video_dependency/`; workspace policy check: `make test-policy` passes with all four paths present", + "workItemId" : "ADR046-device-005" + }, + { + "currentSource" : "`nixos-modules/components/tpm.nix`, `usbip.nix`, `security-key-guest.nix`, `video/guest.nix`, `graphics.nix`; `nixos-modules/assertions.nix`; **old Workload Nix namespace**: `nixos-modules/options-realms-workloads.nix` (`d2b.vms..tpm.enable`, `d2b.vms..graphics.enable`, `d2b.vms..usbip.*` — generated-or-eval-contract; v3 replaces `d2b.vms.*` with `d2b.zones.*`)", + "dataMigration" : "Consumers migrate from per-VM options to Zone Device declarations; data migration guide references \"Nix configuration\" section migration table", + "dependencyOwner" : "ADR046-device-001 through ADR046-device-005; Nix integrator", + "destination" : "`nixos-modules/resources-device.nix`; `nixos-modules/bundle-artifacts.nix` (bundle emission for resource store); `nixos-modules/assertions.nix` (six eval-time device assertions)", + "detailedDesign" : "Nix authoring shape `d2b.zones..resources. = { type = \"Device\"; metadata.ownerRef = ...; spec = { ...exact ResourceSpec fields... }; };` as specified in \"Nix configuration\" section; `metadata.name`/`metadata.zone`/`apiVersion` derived automatically; `status` and Core management fields (`managedBy`, `configurationGeneration`, uid, generation, revision, timestamps, finalizers) omitted from emitted JSON; `spec` field names/types/defaults identical to ResourceTypeSchema with no renaming; per-Provider `spec.provider.settings` validated against signed Provider schema; no `artifactId` or store-path fields in Device `spec.provider.settings` — binary paths are resolved from Provider package closures; Credential-ref enforcement; artifact catalog emitted as a separate private integrity-pinned map (ID→type/digest/closure) by its own emitter; six eval-time validation assertions; canonical sorted-key full resource-envelope JSON emission (`apiVersion`, `type`, `metadata`, `spec` only); Zone resource bundle with `contentDigest` as specified in \"Zone resource bundle/generation\" section; Core sets `metadata.managedBy=configuration` and `metadata.configurationGeneration` at activation", + "integration" : "Resource store Nix emitter; artifact catalog emitted separately by Provider/system resource emitter (not by this emitter); device resource JSON output; Zone generation object including `priorGeneration`, `pendingDeletion`, `cleanupStatus` fields; cleanup contract logic belongs in the Core configuration handler (`packages/d2b-core-controller/src/configuration.rs`) consuming the generation from this emitter", + "removalProof" : "Nix option `d2b.vms..tpm.enable` etc. retained until v3 reset", + "reuseAction" : "adapt", + "reuseSource" : null, + "specId" : "ADR-046-resources-device", + "specPath" : "docs/specs/ADR-046-resources-device.md", + "validation" : "nix-unit: `device-tpm-eval.nix`, `device-usbip-eval.nix`, `device-security-key-eval.nix`, `device-gpu-eval.nix`, `device-schema-validation.nix`, `device-gen-cleanup-eval.nix`, `device-bundle-canonical.nix`, `device-inline-secret-rejected.nix`, `device-artifact-catalog.nix`; contract tests: `device_resource_schema.rs`, `device_bundle_canonical.rs`, `device_gen_cleanup.rs`", + "workItemId" : "ADR046-device-006" + }, + { + "currentSource" : "None (new work; no equivalent in v3 baseline or main a1cc0b2d)", + "dataMigration" : "Full d2b 3.0 reset; no prior generation cleanup state or v2 Device resource import", + "dependencyOwner" : "ADR046-device-006; Zone runtime implementer", + "destination" : "`packages/d2b-core-controller/src/configuration.rs`; `packages/d2b-contract-tests/tests/device_gen_cleanup.rs`", + "detailedDesign" : "Implement the cleanup contract described in \"Zone generation and cleanup\": (1) on new generation activation, diff `resources` against prior generation's `resources` by (type, name) — resources absent from new generation that have `managedBy=configuration` go into `pendingDeletion`; (2) Zone phase transitions to `Degraded/pending-cleanup` until all items in `pendingDeletion` reach terminal Delete; (3) items in `pendingDeletion` with `managedBy=controller` or `managedBy=api` are rejected with `cleanup-config-ownership-mismatch`; (4) `managedBy=controller` and `managedBy=api` resources are never touched by generation cleanup — `cleanup-controller-resource-protected` emitted if attempted; `managedBy=api` resources persist until explicit Delete; (5) prior generations retained by count: default 3, range 1..16, configured via `d2b.zones..retainedGenerations`; (6) each deletion is non-blocking; (7) finalizer-stuck timeout emits `cleanup-finalizer-stuck` and leaves Zone in `Degraded/pending-cleanup`; (8) all deletions emit `config-resource-deletion-requested` audit record with digested resource identity", + "integration" : "Consumes Zone resource bundle from ADR046-device-006 emitter; drives Device Provider finalizers via normal resource-Delete protocol; feeds Zone status conditions `GenerationCleanPending` and `GenerationCleanError`", + "removalProof" : "None — net-new; no prior owner to remove", + "reuseAction" : "create", + "reuseSource" : null, + "specId" : "ADR-046-resources-device", + "specPath" : "docs/specs/ADR-046-resources-device.md", + "validation" : "Fast hermetic (D094): in-process Rust selectors under the owning crate `tests/` — `packages/d2b-core-controller/tests/gen_cleanup.rs`, `packages/d2b-core-controller/tests/gen_cleanup_controller_protected.rs`, `packages/d2b-core-controller/tests/gen_cleanup_audit.rs` — driven by a fake Zone store/clock with deterministic ordering and no wall-clock sleep; slower coverage reuses the existing integration directory scenarios `tests/integration/containers/device-gen-cleanup.sh`, `device-controller-resource-protected.sh`, and `device-gen-cleanup-audit.sh`; feasibility proof: generation cleanup with fake Zone runtime. No new top-level shell gate is added.", + "workItemId" : "ADR046-device-007" + }, + { + "currentSource" : "`packages/xtask/src/main.rs` (existing `gen-*` and `check-*` commands — implemented-and-reachable); `packages/d2b-contract-tests/tests/workspace_policy.rs` (existing workspace policy tests — implemented-and-reachable)", + "dataMigration" : "None — full d2b 3.0 reset; no prior state to migrate", + "dependencyOwner" : "ADR046-device-002 through ADR046-device-005; workspace/tooling maintainer", + "destination" : "`packages/xtask/src/main.rs` (`check-provider-layout` subcommand); `packages/d2b-contract-tests/tests/workspace_policy.rs` (provider-layout policy assertions)", + "detailedDesign" : "Add `cargo xtask check-provider-layout`: enumerate workspace members matching `d2b-provider-*`; for each, assert `src/`, `tests/`, `integration/`, and `README.md` all exist relative to the crate root; report all missing paths before failing; no opt-out flag. Add companion test in `workspace_policy.rs` that asserts the same invariant against the static crate list in `Cargo.toml`. Wire `cargo xtask check-provider-layout` into `make test-policy` alongside existing workspace naming and sort checks. The check must also run in CI as part of the policy gate. Primary reuse disposition: `adapt`. Preserved source-plan detail: extend.", + "integration" : "`make test-policy`; `make check`; GitHub CI policy job", + "removalProof" : "N/A (new tooling; not removed)", + "reuseAction" : "adapt", + "reuseSource" : null, + "specId" : "ADR-046-resources-device", + "specPath" : "docs/specs/ADR-046-resources-device.md", + "validation" : "Policy gate passes once all four Device Provider crates exist with required paths; gate fails predictably when any path is removed from a Provider crate fixture; test fixture crate with one missing path must produce a named error identifying the exact missing path", + "workItemId" : "ADR046-device-008" + }, + { + "currentSource" : "No existing provider crate; baseline TPM behavior is in `nixos-modules/components/tpm.nix`, `packages/d2b-host/src/swtpm_argv.rs`, and broker/daemon swtpm paths listed in §18", + "dataMigration" : "None — scaffold only; TPM state migration is covered by later Volume/Nix work per §17.3", + "dependencyOwner" : "P0; unblocked; owner: `d2b-provider-device-tpm` crate", + "destination" : "packages/d2b-provider-device-tpm/{src/,tests/,integration/README.md,README.md}; Cargo workspace membership", + "detailedDesign" : "Crate scaffold: create `packages/d2b-provider-device-tpm/` with `src/`, `tests/`, `integration/README.md`, and `README.md`; add it to the Cargo workspace; workspace policy test must pass. Primary reuse disposition: `adapt`. Preserved source-plan detail: net-new crate scaffold; later items adapt baseline TPM behavior into the new Provider boundary.", + "integration" : "Workspace policy and Cargo consume the new crate; all controller, effect-port, resource-builder, status, Nix, and integration work lands under this scaffold.", + "removalProof" : "None — net-new crate scaffold; legacy TPM owners are removed only by later parity/removal items", + "reuseAction" : "adapt", + "reuseSource" : null, + "specId" : "ADR-046-provider-device-tpm", + "specPath" : "docs/specs/providers/ADR-046-provider-device-tpm.md", + "validation" : "Workspace policy test for required crate paths and Cargo workspace membership", + "workItemId" : "ADR046-device-tpm-001" + }, + { + "currentSource" : "`PrepareSwtpmDir` in `packages/d2b-priv-broker/src/ops/swtpm_dir.rs` and `SpawnRunner { role: Swtpm }` in `packages/d2b-priv-broker/src/ops/spawn_runner.rs` remain privileged executors, but the controller must not import broker crates", + "dataMigration" : "Existing TPM state migration follows §17.3: the old `/var/lib/d2b/vms//swtpm/` directory moves to the controller-created Volume path with the provisioning marker preserved and re-keyed; this item must not silently recreate missing state.", + "dependencyOwner" : "P0; blocked by ADR046-device-tpm-001; owner: device-tpm effect boundary", + "destination" : "packages/d2b-provider-device-tpm/src/{effect_port.rs,effect_impl.rs}; packages/d2b-provider-device-tpm/tests/effect_fake.rs", + "detailedDesign" : "TpmEffectPort and FakeTpmEffectPort: implement the effect trait, typed TPM EndpointRef handoff, and fake test port. Prove non-test files contain no `use d2b_priv_broker::` and the controller sees only opaque resource IDs and EndpointRefs. Primary reuse disposition: `wrap`. Preserved source-plan detail: wrap privileged effects behind an injected async `TpmEffectPort`; keep broker operations only behind `volume-local` and `system-minijail`.", + "integration" : "Device controller calls `TpmEffectPort`; ResourceClient-backed implementation talks to ResourceAPI/ComponentSession; `volume-local` and `system-minijail` translate resource operations into broker effects.", + "removalProof" : "Direct broker references in controller/daemon TPM paths are superseded by the effect-port/resource-provider boundary; final deletion is ADR046-device-tpm-013", + "reuseAction" : "wrap", + "reuseSource" : null, + "specId" : "ADR-046-provider-device-tpm", + "specPath" : "docs/specs/providers/ADR-046-provider-device-tpm.md", + "validation" : "`tests/effect_fake.rs`; static proof that non-test files do not import `d2b_priv_broker`", + "workItemId" : "ADR046-device-tpm-002" + }, + { + "currentSource" : "Current direct daemon/broker swtpm lifecycle call sites in `packages/d2bd/src/*` are superseded; controller algorithm is specified in §11.1", + "dataMigration" : "Existing TPM state migration follows §17.3: the old `/var/lib/d2b/vms//swtpm/` directory moves to the controller-created Volume path with the provisioning marker preserved and re-keyed; this item must not silently recreate missing state.", + "dependencyOwner" : "P0; blocked by ADR046-device-tpm-002; owner: device-tpm controller FSM", + "destination" : "packages/d2b-provider-device-tpm/src/controller.rs; packages/d2b-provider-device-tpm/tests/controller_fsm.rs", + "detailedDesign" : "Controller reconcile state machine: implement the Device reconcile algorithm from §11.1 against `FakeTpmEffectPort`, covering happy path, Volume not-ready, marker fail-closed, flush failure, swtpm maxRestarts, and finalizer behavior where Process is deleted and Volume retained. Primary reuse disposition: `replace`. Preserved source-plan detail: replace direct daemon lifecycle with Provider reconcile against `FakeTpmEffectPort` and resource status.", + "integration" : "Resource watches drive the controller; controller creates/observes Volume, Process, EphemeralProcess, and Endpoint resources through `TpmEffectPort`; Device status/finalizers expose outcomes to the ResourceAPI.", + "removalProof" : "Direct daemon swtpm lifecycle logic is removable after this Provider reconcile FSM reaches parity and ADR046-device-tpm-013 removes the old call sites", + "reuseAction" : "replace", + "reuseSource" : null, + "specId" : "ADR-046-provider-device-tpm", + "specPath" : "docs/specs/providers/ADR-046-provider-device-tpm.md", + "validation" : "`tests/controller_fsm.rs` covering happy path, Volume not-ready, marker fail-closed, flush failed, swtpm maxRestarts, and finalizer behavior", + "workItemId" : "ADR046-device-tpm-003" + }, + { + "currentSource" : "`nixos-modules/components/tpm.nix` declares TPM enablement today; §17.3 defines migration from the existing swtpm directory and marker", + "dataMigration" : "Existing TPM state migration follows §17.3: the old `/var/lib/d2b/vms//swtpm/` directory moves to the controller-created Volume path with the provisioning marker preserved and re-keyed; this item must not silently recreate missing state.", + "dependencyOwner" : "P0; blocked by ADR046-device-tpm-001; owner: device-tpm resource builders", + "destination" : "packages/d2b-provider-device-tpm/src/resources.rs; packages/d2b-provider-device-tpm/tests/volume_create.rs", + "detailedDesign" : "Controller-created Volume spec: implement `build_tpm_state_volume_spec` with `cleanupPolicy: never`, `repairPolicy: fail-closed`, `adoptionPolicy: quarantine-on-ambiguity`, `sensitivity: secret-adjacent`, required invariants, `source.sourceId`, no `hostPath`, no top-level identityMarker/persistenceClass/quotaBytes/stateSchema, `ownerRef: Device/`, `managedBy: controller`, empty attachments, and `quota: null`. Primary reuse disposition: `replace`. Preserved source-plan detail: replace VM-level TPM option/state path with controller-created Device-owned Volume spec.", + "integration" : "Device controller creates the TPM data Volume; `volume-local` materializes/protects state and marker; swtpm Process mounts the Volume; Guest runtime receives only EndpointRefs.", + "removalProof" : "`nixos-modules/components/tpm.nix` state-path ownership is superseded by the Device-owned Volume once migration and Nix roundtrip are complete", + "reuseAction" : "replace", + "reuseSource" : null, + "specId" : "ADR-046-provider-device-tpm", + "specPath" : "docs/specs/providers/ADR-046-provider-device-tpm.md", + "validation" : "`tests/volume_create.rs` proving every canonical Volume field and forbidden field listed in this item", + "workItemId" : "ADR046-device-tpm-004" + }, + { + "currentSource" : "`SwtpmArgvInput` in `packages/d2b-host/src/swtpm_argv.rs`; `ProcessRole::Swtpm` in `packages/d2b-core/src/processes.rs`; `minijail_swtpm_video.rs` contract tests", + "dataMigration" : "Existing TPM state migration follows §17.3: the old `/var/lib/d2b/vms//swtpm/` directory moves to the controller-created Volume path with the provisioning marker preserved and re-keyed; this item must not silently recreate missing state.", + "dependencyOwner" : "P0; blocked by ADR046-device-tpm-004; owner: device-tpm Process resource builder", + "destination" : "packages/d2b-provider-device-tpm/src/resources.rs; Process spec tests under packages/d2b-provider-device-tpm/tests/", + "detailedDesign" : "Canonical swtpm Process spec: implement `build_swtpm_process_spec` with `readOnlyRoot: true`, `userNamespace.mappingClass: process-principal-root`, namespace classes `[pid, mount, user]`, empty capability classes, `seccompClass: w1-swtpm`, two Device-owned Endpoint resources (`tpm` and `ctrl`), `mounts[0].required: true`, and no socket path, binary path, UID integer, or GID integer in any spec field. Primary reuse disposition: `adapt`. Preserved source-plan detail: extract swtpm argv/sandbox intent into canonical Process resources; remove caller-supplied binary path, UID, GID, and socket path fields.", + "integration" : "Controller emits the Process spec; `system-minijail` consumes it and invokes broker `SpawnRunner`; Endpoint resources publish TPM and control sockets for downstream consumers.", + "removalProof" : "`ProcessRole::Swtpm` and swtpm argv builder call sites can be retired after the canonical Process resource covers runner launch", + "reuseAction" : "adapt", + "reuseSource" : null, + "specId" : "ADR-046-provider-device-tpm", + "specPath" : "docs/specs/providers/ADR-046-provider-device-tpm.md", + "validation" : "Process spec golden tests proving all required and forbidden fields; preserved `minijail_swtpm_video.rs` contract tests", + "workItemId" : "ADR046-device-tpm-005" + }, + { + "currentSource" : "`SwtpmIoctlFlushInput` in `packages/d2b-host/src/swtpm_argv.rs`; `ProcessRole::SwtpmPreStartFlush` in `packages/d2b-core/src/processes.rs`", + "dataMigration" : "Existing TPM state migration follows §17.3: the old `/var/lib/d2b/vms//swtpm/` directory moves to the controller-created Volume path with the provisioning marker preserved and re-keyed; this item must not silently recreate missing state.", + "dependencyOwner" : "P0; blocked by ADR046-device-tpm-003; owner: device-tpm EphemeralProcess resource builder", + "destination" : "packages/d2b-provider-device-tpm/src/resources.rs; packages/d2b-provider-device-tpm/tests/flush_mandatory.rs", + "detailedDesign" : "Mandatory flush EphemeralProcess spec: implement `build_flush_ephemeral_process_spec`; no `startupClear` field exists; flush is always created before swtpm Process start with no skip path; TTLs are `successfulTtl: \"1h\"` and `failedTtl: \"24h\"`; no userNamespace on flush Process; deadlines are `startDeadline: \"30s\"` and `runtimeDeadline: \"60s\"`. Primary reuse disposition: `adapt`. Preserved source-plan detail: extract flush intent into mandatory EphemeralProcess resource; remove configurable `startupClear` path and caller-supplied binary path fields.", + "integration" : "Controller inserts the EphemeralProcess before every swtpm activation cycle; Process provider runs the flush against the control Endpoint fd before the long-lived swtpm Process becomes Ready.", + "removalProof" : "`ProcessRole::SwtpmPreStartFlush` and any optional startup-clear wiring are retired after mandatory EphemeralProcess coverage is complete", + "reuseAction" : "adapt", + "reuseSource" : null, + "specId" : "ADR-046-provider-device-tpm", + "specPath" : "docs/specs/providers/ADR-046-provider-device-tpm.md", + "validation" : "`tests/flush_mandatory.rs` plus contract proof that user-NS is long-lived only", + "workItemId" : "ADR046-device-tpm-006" + }, + { + "currentSource" : "Existing status/path observations from direct daemon TPM paths are superseded; status fields are defined in §10", + "dataMigration" : "None — status is re-derived during v3 reconcile; TPM state migration remains the Volume/marker migration in §17.3", + "dependencyOwner" : "P1; blocked by ADR046-device-tpm-003; owner: device-tpm status builder", + "destination" : "packages/d2b-provider-device-tpm/src/status.rs; packages/d2b-provider-device-tpm/tests/{endpoint_ref.rs,redaction.rs}", + "detailedDesign" : "Device status builder: implement `build_device_status`; `tpmEndpointRef` is an `Endpoint/` ResourceRef with no opaque endpoint ID compatibility alias and never a filesystem path; `stateVolumeRef` and `swtpmProcessRef` are canonical ResourceRef strings; no path, socket name, UID, GID, PID, or pidfd appears in status; `markerStatus` is one of `verified`, `missing`, `replaced`, or `unknown`. Primary reuse disposition: `create`. Preserved source-plan detail: net-new bounded Device status projection; do not reuse path/socket/UID/GID/PID observations.", + "integration" : "Controller writes Device status; Guest runtime Provider reads EndpointRef from Device status; CLI/support tooling reads bounded non-secret status.", + "removalProof" : "Legacy path/socket status compatibility aliases are absent; removal is proven by redaction/status tests", + "reuseAction" : "create", + "reuseSource" : null, + "specId" : "ADR-046-provider-device-tpm", + "specPath" : "docs/specs/providers/ADR-046-provider-device-tpm.md", + "validation" : "`tests/endpoint_ref.rs`; `tests/redaction.rs`; status builder tests for allowed `markerStatus` values", + "workItemId" : "ADR046-device-tpm-007" + }, + { + "currentSource" : "Baseline guest wiring consumed socket paths from TPM sidecar state; v3 handoff is the Endpoint resource contract in §8.4 and §10.5", + "dataMigration" : "None — endpoint handoff has no state migration; TPM data migration remains §17.3", + "dependencyOwner" : "P1; blocked by ADR046-device-tpm-007; owner: device-tpm endpoint handoff integration", + "destination" : "packages/d2b-provider-device-tpm/src/{effect_port.rs,status.rs}; packages/d2b-provider-device-tpm/integration/guest_endpoint.rs", + "detailedDesign" : "EndpointRef handoff: hermetic tests prove `tpmEndpointRef` is an EndpointRef and never a path; integration proves Guest runtime Provider reads `tpmEndpointRef` and obtains the socket fd from the Zone runtime endpoint resolver with no path string in Guest spec or LaunchTicket API surface. Primary reuse disposition: `replace`. Preserved source-plan detail: replace path handoff with EndpointRef and Zone runtime endpoint resolver fd acquisition.", + "integration" : "Device status publishes EndpointRef; Guest runtime Provider resolves the Endpoint through the Zone endpoint resolver; LaunchTicket receives an fd, not a socket path.", + "removalProof" : "Socket-path handoff is superseded when all Guest runtime TPM attachment uses EndpointRef resolution", + "reuseAction" : "replace", + "reuseSource" : null, + "specId" : "ADR-046-provider-device-tpm", + "specPath" : "docs/specs/providers/ADR-046-provider-device-tpm.md", + "validation" : "`tests/endpoint_ref.rs`; `integration/guest_endpoint.rs`", + "workItemId" : "ADR046-device-tpm-008" + }, + { + "currentSource" : "Existing provisioning marker behavior in `/var/lib/d2b/swtpm-markers/` is preserved and re-keyed by volume-local per §17.3", + "dataMigration" : "Existing TPM state migration follows §17.3: the old `/var/lib/d2b/vms//swtpm/` directory moves to the controller-created Volume path with the provisioning marker preserved and re-keyed; this item must not silently recreate missing state.", + "dependencyOwner" : "P0; blocked by ADR046-device-tpm-004; owner: device-tpm marker/fail-closed tests", + "destination" : "packages/d2b-provider-device-tpm/tests/marker_fail_closed.rs; packages/d2b-provider-device-tpm/integration/marker_tamper.rs", + "detailedDesign" : "Marker fail-closed test: FakeTpmEffectPort returning `markerStatus: replaced` makes Device Failed, prevents a second `ensure_state_volume` call, and prevents swtpm Process creation. Integration physically replaces `swtpm/`; volume-local sets Volume Failed; Device fails with no auto-recovery. Primary reuse disposition: `adapt`. Preserved source-plan detail: preserve fail-closed marker semantics while moving ownership to Volume/Device resources.", + "integration" : "volume-local observes marker state and reports Volume/marker status; controller maps that to Device failure and blocks Process creation; integration exercises the actual filesystem marker path through volume-local.", + "removalProof" : "Any silent marker recreation path is removed/proven absent by the fail-closed tests", + "reuseAction" : "adapt", + "reuseSource" : null, + "specId" : "ADR-046-provider-device-tpm", + "specPath" : "docs/specs/providers/ADR-046-provider-device-tpm.md", + "validation" : "`tests/marker_fail_closed.rs`; `integration/marker_tamper.rs`", + "workItemId" : "ADR046-device-tpm-009" + }, + { + "currentSource" : "None — net-new v3 work; no pre-ADR45 baseline equivalent", + "dataMigration" : "None — controller has no Provider state Volume to migrate; Device data migration remains §17.3", + "dependencyOwner" : "P1; blocked by ADR046-device-tpm-001; owner: device-tpm controller Process descriptor", + "destination" : "packages/d2b-provider-device-tpm/src/resources.rs; packages/d2b-provider-device-tpm/tests/controller_process.rs", + "detailedDesign" : "Controller Process (status-first; no Provider state Volume): implement the controller Process spec from §4.1 with `processClass: controller`, `readOnlyRoot: true`, empty mounts, no controller-scratch namespace, no scratch mount, no `User/device-tpm-controller-system` state-layout principal, no permission to create Provider-owned Volumes, and restart re-derivation from resource store plus external marker/process observations while treating status as observation. Primary reuse disposition: `create`. Preserved source-plan detail: net-new status-first controller Process spec; no Provider state Volume is reused or created.", + "integration" : "ProviderDeployment creates the controller Process from the descriptor; controller status and Operation ledger carry bounded non-secret observations; Device-owned TPM data Volume remains separate from ProviderStateSet.", + "removalProof" : "None — this item prevents introduction of a Provider state Volume and has no prior state owner to remove", + "reuseAction" : "create", + "reuseSource" : null, + "specId" : "ADR-046-provider-device-tpm", + "specPath" : "docs/specs/providers/ADR-046-provider-device-tpm.md", + "validation" : "Controller Process spec tests proving the bullets in this item", + "workItemId" : "ADR046-device-tpm-010" + }, + { + "currentSource" : "`nixos-modules/components/tpm.nix` current `d2b.vms..tpm.enable` option is replaced by the Device declaration in §17.1", + "dataMigration" : "Existing TPM state migration follows §17.3: the old `/var/lib/d2b/vms//swtpm/` directory moves to the controller-created Volume path with the provisioning marker preserved and re-keyed; this item must not silently recreate missing state.", + "dependencyOwner" : "P1; blocked by ADR046-device-tpm-001; owner: Nix Resource compiler for Device declarations", + "destination" : "nixos-modules/options-resources.nix and Nix eval/golden tests for §17.1 Device JSON", + "detailedDesign" : "Nix roundtrip test: Device Nix spec from §17.1 round-trips through the Nix emitter to expected resource JSON; emitted bundle contains no Volume, Process, or EphemeralProcess resources because controller-managed resources are not in the Nix bundle. Primary reuse disposition: `replace`. Preserved source-plan detail: replace VM-level TPM enable option with v3 Device resource emission and assertions.", + "integration" : "Nix authoring emits only the Device and Provider resources; ResourceAPI admission hands the Device to the controller; controller creates managed resources at runtime.", + "removalProof" : "`d2b.vms..tpm.enable` in `nixos-modules/components/tpm.nix` is superseded after Device Nix declaration roundtrip and migration are in place", + "reuseAction" : "replace", + "reuseSource" : null, + "specId" : "ADR-046-provider-device-tpm", + "specPath" : "docs/specs/providers/ADR-046-provider-device-tpm.md", + "validation" : "Nix roundtrip/golden test for §17.1 and emitted-bundle absence of controller-managed resources", + "workItemId" : "ADR046-device-tpm-011" + }, + { + "currentSource" : "Current TPM state retention behavior is tied to the swtpm directory and marker; v3 retention is `cleanupPolicy: never` on the Device-owned Volume", + "dataMigration" : "Existing TPM state migration follows §17.3: the old `/var/lib/d2b/vms//swtpm/` directory moves to the controller-created Volume path with the provisioning marker preserved and re-keyed; this item must not silently recreate missing state.", + "dependencyOwner" : "P0; blocked by ADR046-device-tpm-003; owner: device-tpm finalizer lifecycle", + "destination" : "packages/d2b-provider-device-tpm/src/controller.rs; packages/d2b-provider-device-tpm/tests/finalizer.rs", + "detailedDesign" : "Finalizer: Volume retained on Device deletion. Hermetic tests cover Device deletion finalizer leading to swtpm Process deletion, TPM state Volume not deleted because `cleanupPolicy: never`, Volume persists, Core emits `phase=Deleted` for Device after finalizer clears, and audit carries no path/UID. Primary reuse disposition: `adapt`. Preserved source-plan detail: preserve TPM identity retention while moving deletion sequencing to Resource finalizers.", + "integration" : "Resource deletion sets finalizer; controller deletes Process and retains Volume; core completes Deleted revision after finalizer clears; audit subsystem records redacted deletion outcome.", + "removalProof" : "Any cleanup path that deletes TPM state on Device deletion is removed/proven absent by the finalizer test", + "reuseAction" : "adapt", + "reuseSource" : null, + "specId" : "ADR-046-provider-device-tpm", + "specPath" : "docs/specs/providers/ADR-046-provider-device-tpm.md", + "validation" : "`tests/finalizer.rs` proving Process deletion, retained Volume, core Deleted phase, and redacted audit", + "workItemId" : "ADR046-device-tpm-012" + }, + { + "currentSource" : "`packages/d2bd/src/*` direct broker/swtpm call sites; `packages/d2b-core/src/processes.rs` `ProcessRole::Swtpm` and `ProcessRole::SwtpmPreStartFlush`; `packages/d2b-host/src/swtpm_argv.rs` argv builders", + "dataMigration" : "Existing TPM state migration follows §17.3: the old `/var/lib/d2b/vms//swtpm/` directory moves to the controller-created Volume path with the provisioning marker preserved and re-keyed; this item must not silently recreate missing state.", + "dependencyOwner" : "P0; blocked by ADR046-device-tpm-002; owner: device-tpm migration/removal cleanup", + "destination" : "packages/d2bd/src/*; packages/d2b-core/src/processes.rs; packages/d2b-provider-device-tpm/src/; packages/d2b-host/src/swtpm_argv.rs", + "detailedDesign" : "Remove direct broker references: remove pre-ADR-0046 daemon swtpm broker call sites, retire `ProcessRole::Swtpm` and `ProcessRole::SwtpmPreStartFlush`, move argv builders from `d2b-host/src/swtpm_argv.rs` to `d2b-provider-device-tpm/src/` with binary path fields removed, while retaining `d2b-priv-broker/src/ops/swtpm_dir.rs` for `volume-local` and `spawn_runner.rs` for `system-minijail`. Primary reuse disposition: `delete-after-cutover`. Preserved source-plan detail: remove direct broker references from daemon and move argv builders into the Provider with binary path fields removed; retain broker ops only behind resource providers.", + "integration" : "After controller/effect-port parity, daemon no longer calls TPM broker ops; Resource providers invoke broker effects from Volume and Process reconciliation; contract tests ensure swtpm sandbox/readiness still hold.", + "removalProof" : "Direct daemon swtpm call sites removed; `ProcessRole::Swtpm` and `ProcessRole::SwtpmPreStartFlush` retired; `d2b-host/src/swtpm_argv.rs` no longer owns Provider argv builders", + "reuseAction" : "delete-after-cutover", + "reuseSource" : null, + "specId" : "ADR-046-provider-device-tpm", + "specPath" : "docs/specs/providers/ADR-046-provider-device-tpm.md", + "validation" : "Static search/proof for no direct broker swtpm references in daemon/controller plus preserved swtpm contract tests", + "workItemId" : "ADR046-device-tpm-013" + }, + { + "currentSource" : "`packages/d2b-wayland-proxy/`, `packages/d2b-host/src/wayland_proxy_argv.rs`, `packages/d2b-host-providers/src/lib.rs`, `packages/d2b-realm-provider/src/{conformance,mock}.rs`", + "dataMigration" : "Full reset; no v2 session compatibility", + "dependencyOwner" : "`ADR046-provider-001`, `ADR046-process-001`; display Provider owner", + "destination" : "`packages/d2b-provider-display-wayland/src/`", + "detailedDesign" : "Create Provider crate layout (`src/`, `tests/`, `integration/`, `README.md`); extract `FilterPolicy`, `PolicyInput`, `DecorationManager`, `BridgeConfig`, `ProxyReadinessEvent`, `ProxyIdentity`, `ClipboardGlobalDisposition` from `d2b-wayland-proxy`; implement single `display-controller` using toolkit `ResourceClient`/`Reconciler` to manage both `WaylandSession` and `WaylandPolicy` resources; implement `display-user-portal` as a separately sandboxed user-domain service that receives pre-opened compositor connections from the fixed user session supervisor (never reads `WAYLAND_DISPLAY`), validates same-user via `SO_PEERCRED`, and issues bounded per-session compositor connection attachment grants to `ProviderSupervisor`; implement LaunchTicket composition with opaque attachment grant handles (compositor, GPU endpoint) so no fd transits through the controller; implement pool-slot acquisition using opaque hash-derived account names (`d2b-wlp-` for bundle sessions, `d2b-wlp-p` for pool) that fails closed with `NoPrincipalAvailable` when all pool slots are occupied; implement `wl-cross-domain-proxy` guest frontend binary at `src/bin/wl-cross-domain-proxy.rs` within the Provider package; implement provider-neutral `display_fails_closed_when_unsupported` conformance; assert D087 status-first state: ProviderStateSet is empty and bounded operational state is in resource status plus the core Operation ledger Primary reuse disposition: `adapt`. Preserved source-plan detail: extract and adapt.", + "integration" : "Zone Provider resource/catalog → `WaylandSession` controller (in `display-controller`) → Process resources → supervisor ticket; framework enrollment creates no Provider state Volume for display components", + "removalProof" : "`packages/d2b-host-providers/src/lib.rs` `LocalCrossDomainWaylandProvider` removed only after `display-controller` passes conformance; `packages/d2b-host/src/wayland_proxy_argv.rs` removed only after Process template sealing verified", + "reuseAction" : "adapt", + "reuseSource" : null, + "specId" : "ADR-046-provider-display-wayland", + "specPath" : "docs/specs/providers/ADR-046-provider-display-wayland.md", + "validation" : "conformance vectors, fake-bus tests, filter policy golden tests (migrate from `packages/d2b-wayland-proxy/`), redaction/audit contract tests, no-fallback test, `controller_unknown_interface_fails_closed`, `controller_finalizer_ambiguous_retained`, `user_portal_unavailable_blocks_pending`, `provider_state_set_empty_status_first`", + "workItemId" : "ADR046-display-001" + }, + { + "currentSource" : "`nixos-modules/components/graphics.nix` `graphics.waylandProxy.*`; `nixos-modules/ui-colors.nix` VM border color resolution; `nixos-modules/options-vms.nix` `graphics.*` options", + "dataMigration" : "Legacy `graphics.waylandProxy.*` Nix options accepted with deprecation warning during migration window; removed after parity", + "dependencyOwner" : "`ADR046-display-001`; Nix integrator", + "destination" : "Zone bundle emitter for `WaylandSession` / `WaylandPolicy` ResourceSpecs under `d2b.zones..resources.*`; `WaylandSession` color resolution in Nix bundle emitter", + "detailedDesign" : "Emit `WaylandSession` and `WaylandPolicy` ResourceSpecs from Nix; derive colors from `d2b-niri-border` palette (§16.2); enforce `crossDomainTrusted = true` at eval time; emit v3 `display-wayland-provider` artifact catalog entry with `spec.config.principalPoolSize` (default 4, bounds 1..32) and `spec.config.runtimeVolumePolicyId`; provision opaque hash-derived OS accounts (`d2b-wlp-` for bundle-declared sessions, `d2b-wlp-p` for pool; `isSystemUser = true`, no raw UID/GID) for all sessions and pool slots; validate that account names are unique and do not expose guest/session identity; validate signed Provider config schema; enforce eval-time bound that bundle session count + pool size does not exceed provisioned principal count; configure `display-controller` and `display-user-portal` in the signed ProviderDeployment manifest; nix-unit tests for color derivation, opaque principal name derivation, spec shape, JSON round-trip, and principal provisioning uniqueness", + "integration" : "Zone NixOS module system → bundle emitter → `/etc/d2b/zones//bundle/generation-N.json`", + "removalProof" : "`nixos-modules/components/graphics.nix` `graphics.waylandProxy.*` and `nixos-modules/ui-colors.nix` VM color resolution removed only after Zone bundle emitter parity verified by a full nix-unit pass", + "reuseAction" : "adapt", + "reuseSource" : null, + "specId" : "ADR-046-provider-display-wayland", + "specPath" : "docs/specs/providers/ADR-046-provider-display-wayland.md", + "validation" : "nix-unit spec-shape tests, eval-time guard tests (crossDomainTrusted=false rejected), color derivation golden tests, principal provisioning count bound test", + "workItemId" : "ADR046-display-002" + }, + { + "currentSource" : "`packages/d2b-wayland-proxy/src/diag.rs` (rate-limited bounded diagnostics)", + "dataMigration" : "None — full d2b 3.0 reset; no prior state to migrate", + "dependencyOwner" : "`ADR046-display-001`; telemetry/audit owner", + "destination" : "`packages/d2b-provider-display-wayland/src/audit.rs`, `packages/d2b-provider-display-wayland/src/metrics.rs`", + "detailedDesign" : "Implement audit record types for all events in §14.1; implement OTEL metric counters/gauges in §14.2; adapt `DiagRateLimiter` to use closed label sets; validate that no socket path, user identity, window title, or app-id appears in any log/audit/metric surface Primary reuse disposition: `adapt`. Preserved source-plan detail: extract and adapt.", + "integration" : "Providers emit via Zone telemetry emitter; audit records committed before operation completion", + "removalProof" : "N/A (new code)", + "reuseAction" : "adapt", + "reuseSource" : null, + "specId" : "ADR-046-provider-display-wayland", + "specPath" : "docs/specs/providers/ADR-046-provider-display-wayland.md", + "validation" : "Redaction contract tests (`policy_observability.rs` pattern), audit record schema tests, label-cardinality tests", + "workItemId" : "ADR046-display-003" + }, + { + "currentSource" : "`tests/integration/` test orchestration structure", + "dataMigration" : "None — full d2b 3.0 reset; no prior state to migrate", + "dependencyOwner" : "`ADR046-display-001`; integration test owner", + "destination" : "`packages/d2b-provider-display-wayland/integration/`", + "detailedDesign" : "Container/Host/Guest/cross-process integration fixtures for: (a) end-to-end WaylandSession create → proxy Process ready → guest frontend ready; (b) GPU endpoint unavailable → Pending; (c) proxy crash → Failed backoff; (d) policy policy warning production; (e) clipboard boundary denial; (f) crossDomainTrusted=false admission rejection. Follows `ADR-046-provider-model-and-packaging` integration/ convention.", + "integration" : "Invoked by existing repository test orchestration (`make test-integration` / container lane)", + "removalProof" : "N/A (new code)", + "reuseAction" : "create", + "reuseSource" : null, + "specId" : "ADR-046-provider-display-wayland", + "specPath" : "docs/specs/providers/ADR-046-provider-display-wayland.md", + "validation" : "All six scenarios above pass; no socket paths in test output", + "workItemId" : "ADR046-display-004" + }, + { + "currentSource" : "`packages/d2b-core/src/processes.rs`: `ProcessRole` (18 variants), `ProcessNode`, `RoleProfile`, `NamespaceSet`, `MountPolicy`, `CgroupPlacement`, `ReadinessPredicate`; `packages/d2b-core/src/minijail_profile.rs`: `MinijailProfile`, `UserNamespaceProfile`, `NamespaceSet`, `MountPolicy`, `BindMount`, `CgroupPlacement`; `packages/d2b-core/src/storage.rs`: `StoragePathSpec`, `AclGrant`, `CleanupPolicy`, `RepairPolicy`; `packages/d2b-realm-core/src/ids.rs`: `RealmId`, `WorkloadId` (→ GuestRef), `NodeId` (→ HostRef), `ProviderId` (→ Provider ResourceRef), `ExecutionId` (→ EphemeralProcess exec identity), `PrincipalId` (→ User ResourceRef), `AllocatorLeaseId`, `ControllerGenerationId`; `packages/d2b-realm-core/src/workload.rs`: `WorkloadProviderKind` (`LocalVm`→runtime-cloud-hypervisor Provider, `QemuMedia`→runtime-qemu-media Provider, `ProviderManaged`→ACA/relay Providers, `UnsafeLocal`→user-only Host `isolationPosture=\"none\"`), `IsolationPosture` (`VirtualMachine`→Guest, `ProviderManaged`→Guest, `UnsafeLocal`→Host `isolationPosture=\"none\"`), `WorkloadExecutionPosture`, `WorkloadSummary`, `WorkloadState`; `packages/d2b-realm-core/src/target.rs`: `RealmTarget`, `TargetName`, `RealmTargetParser` (current analog for `/` ResourceRef parsing); `packages/d2b-realm-core/src/realm.rs`: `RealmPath`, `RealmControllerPlacement`, `EntrypointMode` (current Zone hierarchy analog); `packages/d2b-core/src/workload_identity.rs`: `WorkloadIdentity`, `WorkloadTarget` (= `RealmTarget`), `WorkloadBackend`, `WorkloadRuntimeIntent` (identity/backend separation reuse model for Host/Guest ResourceType split)", + "dataMigration" : "Full reset; no v2 resource import", + "dependencyOwner" : "W0 shared contract root; `d2b-contracts`", + "destination" : "`packages/d2b-contracts/src/v3/host.rs`, `packages/d2b-contracts/src/v3/guest.rs`, `packages/d2b-contracts/src/v3/execution_policy.rs`, `packages/d2b-contracts/src/v3/process.rs`, `packages/d2b-contracts/src/v3/ephemeral_process.rs`, `packages/d2b-contracts/src/v3/user.rs`", + "detailedDesign" : "Implement strict typed Rust structs for HostSpec, GuestSpec, ExecutionPolicy, ExecutionSpec, SandboxSpec, BudgetSpec, MountSpec, NetworkUsageSpec, DeviceUsageSpec, EndpointSpec, TelemetrySpec, ProcessSpec, EphemeralProcessSpec, UserSpec; strict serde deny_unknown_fields; bounds/redaction on all string fields; stable error types; `UserSpec.osUsername` validated as OS username (1..255 bytes, no NUL/control/path-separator), not ResourceName grammar Primary reuse disposition: `adapt`. Preserved source-plan detail: extract and adapt.", + "integration" : "Provider dossiers, controller descriptors, Zone resource API, Nix resource compiler", + "removalProof" : "Old DTO types removed only after owning Resource/Provider integrations are live", + "reuseAction" : "adapt", + "reuseSource" : "`packages/d2b-contracts/src/v3/` as destination; no equivalent main source for Host/Guest/Process ResourceType contracts", + "specId" : "ADR-046-resources-host-guest-process-user", + "specPath" : "docs/specs/ADR-046-resources-host-guest-process-user.md", + "validation" : "Golden JSON vectors for each ResourceType; serde unknown-field rejection; bounds enforcement; `UserSpec.osUsername` OS-username validation (underscore allowed, NUL rejected)", + "workItemId" : "ADR046-exec-001" + }, + { + "currentSource" : "`packages/d2b-core/src/processes.rs`: `ReadinessPredicate`, `VmProcessInvariants`; `packages/d2b-core/src/processes.rs`: `SpawnRunnerPlanOp`; `packages/d2b-priv-broker/src/ops/` SpawnRunner", + "dataMigration" : "Full reset", + "dependencyOwner" : "ADR046-exec-001; `d2b-contracts`", + "destination" : "`packages/d2b-contracts/src/v3/process_provider.rs`: LaunchTicket, ProcessIdentityDigest, AdoptionCandidate, PidfdEvidence, WaitReapOwner, BrokerTerminalResult, ProcessOutcome, ExitClass", + "detailedDesign" : "LaunchTicket (Process/EphemeralProcess ref/UID/revision/generation, owner Provider/component/template, executionRef/domain/userRef, providerRef, compiled sandbox/budget/mount/device/network/endpoint digest, inherited FD table, operation/deadline/cancellation, expected identity/readiness); ProcessIdentityDigest (opaque bounded hex string); AdoptionCandidate (cgroup leaf path relative to controller root, start-time token, executable hash); BrokerTerminalResult binds process identity/operation to the clone3 parent's exact-once wait/reap status and cannot be constructed from pidfd readability; all types zeroizing where credential-adjacent Primary reuse disposition: `adapt`. Preserved source-plan detail: extract and adapt.", + "integration" : "ProviderSupervisor adapter; system-systemd and system-minijail Process Providers", + "removalProof" : "None — net-new types; no prior owner to remove", + "reuseAction" : "adapt", + "reuseSource" : null, + "specId" : "ADR-046-resources-host-guest-process-user", + "specPath" : "docs/specs/ADR-046-resources-host-guest-process-user.md", + "validation" : "Golden LaunchTicket and BrokerTerminalResult vectors; field redaction test; digest-binding test; duplicate/mismatched/non-parent terminal relay rejection", + "workItemId" : "ADR046-exec-002" + }, + { + "currentSource" : "`packages/d2b-core/src/host_check.rs`: `HostCheckReport`, `HostCheckSummary`, `HostCheckFinding`, `HostCheckSeverity`; `packages/d2bd/src/pidfs_probe.rs`; `packages/d2bd/src/kernel_module_check.rs`; `packages/d2b-core/src/provider_capabilities.rs`; `packages/d2b-realm-core/src/ids.rs`: `HostResourceId` (current host-identity handle), `NodeId` (execution node identity); `packages/d2b-realm-core/src/node.rs`: `NodeKind::FullHost`, `NodeSummary` (host node's capability advertisement — direct reuse model for Host status `capabilities[]`)", + "dataMigration" : "New Host resources from Nix; no v2 state import", + "dependencyOwner" : "ADR046-exec-001; system-core Provider owner", + "destination" : "`packages/d2b-provider-system-core/src/host.rs`: HostReconciler; status/conditions/capability probe implementation; `packages/d2b-provider-system-core/tests/`: hermetic reconcile/conformance/fault tests; `packages/d2b-provider-system-core/integration/`: Host probe and lifecycle integration scenarios; `packages/d2b-provider-system-core/README.md`: Provider identity, `spec.provider.settings` schema, ResourceTypes, placement, RBAC, security posture, telemetry labels, build/test commands (provider crate standard layout — see §Provider crate standard layout)", + "detailedDesign" : "Async Host reconcile loop per this spec's Reconcile section; HostCapabilityClass probe set (kvm/pidfd/cgroup-v2/user-namespace/wayland/audio-pipewire/gpu-render/tpm2/usbip); bounded OS probes with timeout; mandatory system-minijail placement gate for Linux ≥5.14 plus writable delegated-leaf `cgroup.kill` independent of optional `kernelVersionMin`; `isolationPosture` validation and status; aggregate budget reservation tracking via List; status write with expected revision Primary reuse disposition: `adapt`. Preserved source-plan detail: extract and adapt.", + "integration" : "Provider/system-core fixed bootstrap process; ResourceClient Get/List/UpdateStatus", + "removalProof" : "Current host capability checks in `d2bd` removed after Host reconcile parity", + "reuseAction" : "adapt", + "reuseSource" : null, + "specId" : "ADR-046-resources-host-guest-process-user", + "specPath" : "docs/specs/ADR-046-resources-host-guest-process-user.md", + "validation" : "Multiple Hosts per Zone; system-only and user-only Hosts; capability probe mocks; Linux <5.14 and missing/unwritable `cgroup.kill` reject system-minijail before spawn; Linux ≥5.14 positive probe; `isolationPosture=\"none\"` rejection of system processes; budget overcommit rejection; `tests/` all pass under `cargo test`; `integration/` scenario passes in container fixture; `README.md` present and covers all required sections (provider crate standard layout acceptance)", + "workItemId" : "ADR046-exec-003" + }, + { + "currentSource" : "`packages/d2b-userd/src/lib.rs`: `UserdConfig`, `UserSessionIdentity`; `packages/d2bd/src/admission.rs` (`SO_PEERCRED` UID/GID lookup); `packages/d2b-realm-core/src/ids.rs`: `PrincipalId` (current host-local principal identity); `packages/d2b-realm-core/src/access.rs`: `HostLocalPeerCredentialSemantics`, `HostLocalPeerCredentialSource`, `RealmAccessClientContract` (current host-local credential/uid resolution model; direct reuse precedent for v3 User → Process userRef resolution)", + "dataMigration" : "New User resources from Nix; no v2 state import", + "dependencyOwner" : "ADR046-exec-001; system-core Provider owner", + "destination" : "`packages/d2b-provider-system-core/src/user.rs`: UserReconciler; NSS lookup implementation", + "detailedDesign" : "Async User reconcile loop per this spec's Reconcile section; NSS `getpwnam(spec.osUsername)` with bounded timeout (default 5 s); `spec.osUsername` validated on admission (1..255 bytes, no NUL/control/path-separator); `metadata.name` used only as Zone-local resource identity and `User/` ref; uid/gid/home/shell/group discovery written to status; session manager availability check via fixed user supervisor; status write; phase Degraded on SessionManagerReady=False Primary reuse disposition: `adapt`. Preserved source-plan detail: extract and adapt.", + "integration" : "Provider/system-core fixed bootstrap process; ResourceClient Get/List/UpdateStatus; other controllers resolve User status via ResourceClient", + "removalProof" : "Current local uid/group lookup in `d2bd/src/admission.rs` removed after User resource parity", + "reuseAction" : "adapt", + "reuseSource" : null, + "specId" : "ADR-046-resources-host-guest-process-user", + "specPath" : "docs/specs/ADR-046-resources-host-guest-process-user.md", + "validation" : "User found/not-found; group membership; sessionManagerAvailable; multiple Users; deletion blocked by Process userRef; `spec.osUsername` with underscore succeeds NSS lookup where ResourceName grammar would reject it; `metadata.name` and `spec.osUsername` differ (e.g. `alice-admin` / `alice_admin`); `spec.osUsername` containing NUL/control/path-separator rejected at admission", + "workItemId" : "ADR046-exec-004" + }, + { + "currentSource" : "`packages/d2bd/src/lib.rs` (daemon startup); `packages/d2b-core/src/host_check.rs`; `nixos-modules/host.nix` (host activation); `nixos-modules/options-host.nix` (host options)", + "dataMigration" : "Full v3 reset", + "dependencyOwner" : "ADR046-exec-001 + ADR046-exec-004; system-core Provider owner", + "destination" : "`packages/d2b-provider-system-core/src/host.rs` (continued); bootstrap startup sequence", + "detailedDesign" : "ProviderSystem-core fixed bootstrap: start with embedded Zone runtime and compiled bootstrap authorization; run Host reconcile before any Process Provider launches; create initial User resources from Nix; publish initial Role/RoleBinding from Nix config; hand off to stored RBAC", + "integration" : "Zone runtime startup sequence; system-minijail bootstrap process launch", + "removalProof" : "Current d2bd initialization sequence removed after bootstrap parity", + "reuseAction" : "adapt", + "reuseSource" : null, + "specId" : "ADR-046-resources-host-guest-process-user", + "specPath" : "docs/specs/ADR-046-resources-host-guest-process-user.md", + "validation" : "Bootstrap without prior Host; User-before-Process ordering; compilation bootstrap authorization closed-set tests", + "workItemId" : "ADR046-exec-005" + }, + { + "currentSource" : "`packages/d2b-unsafe-local-helper/src/systemd.rs`; `packages/d2b-guestd/src/exec.rs`: `SystemdRunUnitManager`, `ManagedUnit`, `ExecPolicy`; `packages/d2b-guestd/src/exec_linux.rs`; `packages/d2bd/src/supervisor/` (transient unit management)", + "dataMigration" : "Current ProcessRole/systemd unit roles converted by ProcessRole disposition table after parity", + "dependencyOwner" : "ADR046-exec-001 + ADR046-exec-002; system-systemd Process Provider owner", + "destination" : "`packages/d2b-provider-system-systemd/src/`: launch.rs, adoption.rs, pidfd.rs, wait.rs, user_supervisor.rs; `packages/d2b-provider-system-systemd/tests/`: hermetic lifecycle/conformance/fault tests; `packages/d2b-provider-system-systemd/integration/`: transient-unit and user-supervisor integration scenarios; `packages/d2b-provider-system-systemd/README.md`: Provider identity, `spec.provider.settings` schema, ResourceTypes, placement, RBAC, security posture, telemetry labels, build/test commands (provider crate standard layout — see §Provider crate standard layout)", + "detailedDesign" : "system-systemd Process/EphemeralProcess provider conformance per this spec's system-systemd conformance section; transient system unit (Type=exec); InvocationID+cgroup+MainPID+start-time binding before pidfd_open; systemd-owned wait/reap; user domain via fixed user supervisor; adoption re-verification; sandboxSpec compilation to systemd hardening directives; runtimeDeadline enforcement; drainTimeout SIGTERM/SIGKILL sequence Primary reuse disposition: `adapt`. Preserved source-plan detail: extract and adapt.", + "integration" : "Zone-installed Provider/system-systemd; ProviderSupervisor LaunchTicket interface; ResourceClient UpdateStatus", + "removalProof" : "ProcessRole roles using systemd (Audio, WaylandProxy, VsockRelay, etc.) removed per disposition table after system-systemd parity", + "reuseAction" : "adapt", + "reuseSource" : null, + "specId" : "ADR-046-resources-host-guest-process-user", + "specPath" : "docs/specs/ADR-046-resources-host-guest-process-user.md", + "validation" : "Shared process conformance test matrix (lifecycle/readiness/crash/drain/adoption/user-domain/sandboxSpec/pidfd); system-specific InvocationID binding; no-static-unit test; `tests/` all pass under `cargo test`; `integration/` scenario passes in container fixture; `README.md` present and covers all required sections (provider crate standard layout acceptance)", + "workItemId" : "ADR046-exec-006" + }, + { + "currentSource" : "`packages/d2b-core/src/processes.rs`: `ProcessNode`, `RoleProfile`, `NamespaceSet`, `MountPolicy`, `CgroupPlacement`; `packages/d2b-core/src/minijail_profile.rs`: full; `packages/d2b-priv-broker/src/ops/spawn_runner.rs` (if present at baseline); `packages/d2bd/src/supervisor/` pidfd/wait; `packages/d2b-core/src/process_builder.rs`", + "dataMigration" : "Current RoleProfile/NamespaceSet/MountPolicy/CgroupPlacement adapted to SandboxSpec/BudgetSpec", + "dependencyOwner" : "ADR046-exec-001 + ADR046-exec-002; system-minijail Process Provider owner", + "destination" : "`packages/d2b-provider-system-minijail/src/`: sandbox_compiler.rs, launch.rs, adoption.rs, pidfd.rs, wait.rs, user_ns.rs; `packages/d2b-provider-system-minijail/tests/`: hermetic sandbox-compilation/lifecycle/conformance/fault tests; `packages/d2b-provider-system-minijail/integration/`: clone3/user-namespace and broker-spawn integration scenarios; `packages/d2b-provider-system-minijail/README.md`: Provider identity, `spec.provider.settings` schema, ResourceTypes, placement, RBAC, security posture (capabilities, namespaces, seccomp), telemetry labels, build/test commands (provider crate standard layout — see §Provider crate standard layout)", + "detailedDesign" : "system-minijail Process/EphemeralProcess provider conformance per this spec's system-minijail conformance section; SandboxSpec-to-minijail plan compilation; Linux ≥5.14/cgroup.kill placement gate; broker clone3(CLONE_PIDFD|CLONE_INTO_CGROUP) parent retains sole waitid(P_PIDFD)/reap/exit-status ownership and relays a typed terminal result; ProviderSupervisor polls a verified duplicate and retains exact-main pidfd_send_signal semantics but never waits/reaps; adoption verifies original broker parent; runtimeDeadline/drainTimeout use graceful main signal then anchored leaf cgroup.kill, broker wait/reap, and empty-leaf proof; no PID/PGID fallback; EphemeralProcess one-shot launch Primary reuse disposition: `adapt`. Preserved source-plan detail: extract and adapt.", + "integration" : "Zone-installed Provider/system-minijail fixed bootstrap process; ProviderSupervisor LaunchTicket; privileged broker effect adapter", + "removalProof" : "ProcessRole roles using minijail (Virtiofsd, Swtpm, SecurityKeyFrontend, etc.) removed per disposition table after system-minijail parity", + "reuseAction" : "adapt", + "reuseSource" : null, + "specId" : "ADR-046-resources-host-guest-process-user", + "specPath" : "docs/specs/ADR-046-resources-host-guest-process-user.md", + "validation" : "Shared process conformance test matrix; minijail-specific sandbox compilation tests; user namespace tests; clone3 parent-only wait/reap and broker-relay tests; poll-readability-not-status test; pidfd_send_signal duplicate-holder test; setsid descendant/recycled-PGID cgroup.kill teardown test; Linux 5.14/cgroup.kill platform-gate tests; adoption quarantine asserts no signal/cgroup.kill; `tests/` all pass under `cargo test`; `integration/` scenario passes; `README.md` present and covers all required sections (provider crate standard layout acceptance)", + "workItemId" : "ADR046-exec-007" + }, + { + "currentSource" : "`packages/d2b-core/src/processes.rs` test coverage; `packages/d2bd/src/supervisor/` tests; minijail/seccomp test vectors", + "dataMigration" : "Full d2b 3.0 reset; no v2 state/config import", + "dependencyOwner" : "ADR046-exec-006 + ADR046-exec-007; conformance test owner", + "destination" : "`packages/d2b-process-conformance/src/`: shared conformance test matrix run against both system-systemd and system-minijail providers", + "detailedDesign" : "Full process lifecycle tests: start/ready/crash/restart/maxRestarts/drain/stop/delete; EphemeralProcess start/succeed/fail/ttl/cleanup; adoption after controller restart (fresh/quarantine); sandboxSpec compilation contract; pidfd rules (never-serialized/never-exported/re-verified-after-supervisor-restart; clone3 broker parent alone waits/reaps; non-parent readability is not status; verified duplicate holder may pidfd_send_signal exact main); system-minijail intentional teardown uses anchored cgroup.kill against setsid descendant and recycled-PGID fixtures, with no kill on ambiguity; Linux ≥5.14/cgroup.kill platform gate; user domain (system-systemd only); desiredLifecycle=stopped; fast path latency gate (<=5ms/<=20ms p95); 1/10/100 concurrent Process start", + "integration" : "system-systemd and system-minijail providers must both pass all shared tests", + "removalProof" : "None — permanent conformance tests; no prior owner to remove", + "reuseAction" : "adapt", + "reuseSource" : null, + "specId" : "ADR-046-resources-host-guest-process-user", + "specPath" : "docs/specs/ADR-046-resources-host-guest-process-user.md", + "validation" : "Hard pass/fail per-test; latency gates enforced; no exception for partial conformance", + "workItemId" : "ADR046-exec-008" + }, + { + "currentSource" : "`packages/d2b-unsafe-local-helper/src/lib.rs`: `UserdConfig`, protocol traits; `packages/d2b-unsafe-local-helper/src/runtime.rs` (`ScopeRuntime`, `SupervisorSpec`); `packages/d2b-unsafe-local-helper/src/systemd.rs`; `packages/d2bd/src/unsafe_local_helper.rs`; `packages/d2b-realm-core/src/workload.rs`: `WorkloadProviderKind::UnsafeLocal`, `IsolationPosture::UnsafeLocal` (current evidence that the no-isolation posture exists and is classified separately from VM isolation — the exact semantics this spec's `Host.spec.isolationPosture=\"none\"` preserves); `packages/d2b-core/src/workload_identity.rs`: `WorkloadBackend::UnsafeLocal`; `nixos-modules/options-realms-workloads.nix` (`d2b.realms..workloads..kind = \"unsafe-local\"`)", + "dataMigration" : "Full reset; no unsafe-local session state migration", + "dependencyOwner" : "ADR046-exec-001; unsafe-local migration owner", + "destination" : "`packages/d2b-provider-system-core/src/host.rs` (user-only no-isolation Host); `nixos-modules/options-zones.nix` (Nix unsafe-local Host declaration)", + "detailedDesign" : "v3 unsafe-local migration: `kind = \"unsafe-local\"` in the current Nix Realm workload model becomes a Host resource with `providerRef: Provider/system-core`, `spec.isolationPosture: \"none\"`, `defaultDomain: user`, `allowedDomains: [user]`, `defaultUserRef: User/`. This is a Host ResourceType, not a Guest and not a v3 Provider. Child Process and EphemeralProcess resources on this Host use the normal Process Providers (Provider/system-systemd for user-domain transient user scope; Provider/system-minijail is also valid for callers that explicitly request namespace isolation within the user session). No special unsafe-local-specific Provider is introduced. The explicit no-isolation posture and its warnings are preserved: Host status reflects `isolationPosture=\"none\"` and this is surfaced in every operator CLI/UI view as an explicit \"no isolation boundary\" warning; `ProcessEffect` audit records (launch, stop, adopt, quarantine) for child Processes and EphemeralProcesses carry the stable `no_isolation=true` attribute; operator CLI/UI always shows the warning and may not suppress it. The `no_isolation=true` attribute belongs on ProcessEffect records only — it must NOT appear on OTEL metric labels, span attributes, log fields, or audit records for other event kinds. The legacy helper protocol (`d2b-unsafe-local-helper`) is not exposed as a v3 ComponentSession service.", + "integration" : "Host resource reconcile; User resource; system-systemd user-domain Process launch", + "removalProof" : "`d2b-unsafe-local-helper` helper binary and protocol removed after user-only Host + shell-terminal Provider parity; `options-realms-workloads.nix` unsafe-local kind removed in Nix reset", + "reuseAction" : "adapt", + "reuseSource" : null, + "specId" : "ADR-046-resources-host-guest-process-user", + "specPath" : "docs/specs/ADR-046-resources-host-guest-process-user.md", + "validation" : "User-only no-isolation Host rejected for system processes; `isolationPosture=\"none\"` Host rejected for `allowedDomains` containing `system`; `allowedDomains=[\"user\"]`+`defaultDomain=user`+`defaultUserRef` set with `isolationPosture=null` rejected at eval time (bidirectional evasion test); posture warning visible in CLI/UI status; `no_isolation=true` attribute present on ProcessEffect launch/stop/adopt/quarantine audit records for child Processes/EphemeralProcesses; `no_isolation=true` absent from OTEL span attributes, metric labels, log fields, and non-ProcessEffect audit records; user-domain Process under user-only Host starts correctly with normal Process Provider", + "workItemId" : "ADR046-exec-009" + }, + { + "currentSource" : "`packages/d2b-guestd/src/exec.rs`: `ExecPolicy`, `ExecError`, `ExecState`, `ExitOutcome`, `ValidatedCommand`, `SpawnedProcess`, `RingChunk`, `ExecSnapshot`, `ExecCreateInput`; `packages/d2b-guestd/src/exec_linux.rs`; `packages/d2b-guestd/src/exec_pty.rs`; `packages/d2b-guestd/src/detached.rs`: `ManagedUnit`, `UnitError`, `UnitIdentity`; `packages/d2b-guestd/src/detached_registry.rs`; `packages/d2b-guestd/src/service.rs`; `packages/d2b-guestd/src/auth.rs`; `packages/d2b-guestd/src/login_session.rs`", + "dataMigration" : "Full reset; no guestd session state migration", + "dependencyOwner" : "ADR046-exec-001 + ADR046-exec-007; guestd migration owner", + "destination" : "`packages/d2b-provider-system-systemd/src/guest_exec.rs` (guest-domain EphemeralProcess launch via systemd-run inside guest); `packages/d2b-session/` (ComponentSession replacing ad-hoc guest ttrpc); runtime Provider guest bootstrap Process", + "detailedDesign" : "Guest-side execution transitions to EphemeralProcess/Process resources under the owning Guest; guestd service becomes a fixed bootstrap Process owned by the runtime Provider controller; exec operations become EphemeralProcess creates via the Zone ResourceClient; detached sessions become EphemeralProcess with failedTtl=24h; shell sessions become shell-terminal Provider Processes; guestd auth.rs becomes ComponentSession via d2b-session (copy/adapt from ADR046-session-001 per ComponentSession spec) Primary reuse disposition: `adapt`. Preserved source-plan detail: extract and adapt.", + "integration" : "runtime-cloud-hypervisor Guest controller creates guest-bootstrap Process; Zone ResourceClient in guest-bootstrap creates EphemeralProcess/Process on behalf of guest workloads", + "removalProof" : "`d2b-guestd` binary removed after all guest-side behaviors have Process/EphemeralProcess/ComponentSession successors and tests pass", + "reuseAction" : "adapt", + "reuseSource" : null, + "specId" : "ADR-046-resources-host-guest-process-user", + "specPath" : "docs/specs/ADR-046-resources-host-guest-process-user.md", + "validation" : "Guest EphemeralProcess exec lifecycle; detached exec TTL; guestd auth replaced by ComponentSession; no SSH fallback", + "workItemId" : "ADR046-exec-010" + }, + { + "currentSource" : "`packages/d2b-userd/src/lib.rs`: `UserdConfig`, `UserSessionIdentity`, `UserAttachRequest`, `UserOutputCursor`, `UserExecSession` trait, `UserdError`, `UserdTransport`, `UnixSocketOnly`, `validate_attach_request`; `packages/d2b-userd/src/main.rs`", + "dataMigration" : "Full reset; no session state import", + "dependencyOwner" : "ADR046-exec-004 + ADR046-exec-010; userd migration owner", + "destination" : "guest-domain process attachment becomes a ComponentSession named stream to the EphemeralProcess running in the guest; `UserExecSession` trait reimplemented as a typed ResourceClient+ComponentSession attachment", + "detailedDesign" : "`UserdConfig.socket_name` replaced by Zone-local EphemeralProcess ResourceRef and ComponentSession attach verb; `UserSessionIdentity.uid`/`gid` become Process `userRef` resolved against User resource status; `UserAttachRequest.exec_id`/`tty`/`initial_size` become ComponentSession method parameters on the EphemeralProcess attachment service; `UserdError` becomes stable v3 error codes; `validate_attach_request` tty/size validation retained in the ComponentSession session schema Primary reuse disposition: `adapt`. Preserved source-plan detail: adapt (logic retained; protocol replaced).", + "integration" : "CLI/client uses ResourceClient to Get/Watch EphemeralProcess; attaches via d2b-bus ComponentSession attach verb", + "removalProof" : "`d2b-userd` binary removed after ComponentSession attach parity on EphemeralProcess; all d2b exec CLI paths must use new attach mechanism", + "reuseAction" : "adapt", + "reuseSource" : null, + "specId" : "ADR-046-resources-host-guest-process-user", + "specPath" : "docs/specs/ADR-046-resources-host-guest-process-user.md", + "validation" : "Attach/detach; TTY size; stream close; error path parity with UserdError variants", + "workItemId" : "ADR046-exec-011" + }, + { + "currentSource" : "`nixos-modules/options-realms-workloads.nix`: `d2b.realms..workloads..kind` = `local-vm`/`qemu-media` → `d2b.zones..resources.` (flat, `type=\"Guest\"`); `unsafe-local` → `d2b.zones..resources.` (flat, `type=\"Host\"`, user-only; see unsafe-local anchor); `nixos-modules/options-realms.nix`: top-level `d2b.realms` option shape including `providerKind` regex `^[a-z][a-z0-9-]*$` and `realmPath` regex `^[a-z][a-z0-9-]*(\\\\.[a-z][a-z0-9-]*)*$` (Zone hierarchy path encoding); `nixos-modules/processes-json.nix`; `nixos-modules/options-host.nix`; `nixos-modules/host.nix`; `packages/d2b-realm-core/src/realm.rs`: `RealmPath`, `RealmControllerPlacement` (current Zone declaration structure analog)", + "dataMigration" : "Full reset; Realm/Workload options removed in the Nix reset wave", + "dependencyOwner" : "ADR046-exec-001; Nix resource compiler owner", + "destination" : "`nixos-modules/options-zones.nix`: `d2b.zones..resources` option as `types.attrsOf (types.submodule resourceModule)` where each resource module has `type` (required enum), optional `metadata` submodule (`ownerRef`, `labels`, `annotations`), and `spec` (type-dependent, auto-generated submodule); `nixos-modules/zone-bundle.nix`: zone resource bundle emitter (see ADR046-exec-014); `nixos-modules/resource-schemas/`: generated per-type Nix option modules imported by `options-zones.nix`", + "detailedDesign" : "`d2b.zones..resources` is a flat attrset; each entry has `type`, optional `metadata` (`ownerRef`, `labels`, `annotations`), and `spec`. The attrset key is the resource name. `spec` submodule fields and their Nix types/defaults/docs are auto-generated from the committed `packages/d2b-contracts/src/v3/schemas/.json` via `xtask gen-resource-nix-options`; no second vocabulary and no renaming of `spec.*` fields. `spec.provider.settings` sub-fields are constrained to the specific Provider's `providerNixSettingsSchema` attribute if present. All 17 eval-time validation rules from the \"Eval-time validation rules\" section are enforced by `lib.assertMsg` on the flat resource attrset. The `spec` object in the emitted JSON is the direct 1:1 serialization of the `spec` submodule. `metadata.ownerRef`, `metadata.labels`, `metadata.annotations` are serialized into the `metadata` object of the ResourceEnvelope JSON. `metadata.managedBy` and `metadata.configurationGeneration` are NOT in the bundle; they are set by the activation controller at runtime. `metadata.name` is the attrset key; `metadata.zone` is the enclosing zone key. Eval errors carry stable rule codes (1–17). Status is never present in the Nix option. `Guest.spec.systemArtifactId` (top-level spec field, not in `spec.provider.settings`) is validated against `d2b.artifacts` by rule 17; the `spec` submodule never contains derivation values or store paths.", + "integration" : "Zone Nix configuration → eval-time validation → Nix-to-ResourceEnvelope compilers → zone bundle emitter (ADR046-exec-014) → configuration publication controller (ADR046-exec-015)", + "removalProof" : "Realm/Workload Nix options removed only after Zone resource Nix option parity and successful eval tests", + "reuseAction" : "adapt", + "reuseSource" : null, + "specId" : "ADR-046-resources-host-guest-process-user", + "specPath" : "docs/specs/ADR-046-resources-host-guest-process-user.md", + "validation" : "nix-unit eval/build tests 1–18 from the \"Tests\" section; eval validation rule tests 1–17 with expected error messages and stable codes; `spec` fields in emitted JSON match `spec` submodule values exactly (1:1 invariant test); `type` field in JSON matches `type` option value; `metadata.name` = attrset key; `metadata.zone` = enclosing zone key; `Guest.spec.systemArtifactId` plain string in resource bundle JSON at top-level spec (no store path, not in `spec.provider.settings`); missing/wrong-type artifact ID raises rule 17 eval error", + "workItemId" : "ADR046-exec-012" + }, + { + "currentSource" : "No direct current equivalent; current processes.json has no TTL/cleanup concept", + "dataMigration" : "Full d2b 3.0 reset; no v2 state/config import", + "dependencyOwner" : "ADR046-exec-008; EphemeralProcess cleanup controller owner", + "destination" : "`packages/d2b-core-controller/src/cleanup.rs`: EphemeralProcess TTL cleanup controller handler", + "detailedDesign" : "Cleanup controller handler as specified in core-controllers spec; watches EphemeralProcess resources for terminal phase; computes cleanupEligibleAt from successfulTtl/failedTtl + completedAt; handles incidentHold; respects finalizers; issues normal Delete via ResourceClient; does not remove rows directly; bounded requeue-at for TTL expiry", + "integration" : "core-controller process; ResourceClient Watch(EphemeralProcess); ResourceClient Delete", + "removalProof" : "None — net-new controller; no prior owner to remove", + "reuseAction" : "create", + "reuseSource" : null, + "specId" : "ADR-046-resources-host-guest-process-user", + "specPath" : "docs/specs/ADR-046-resources-host-guest-process-user.md", + "validation" : "Succeeded TTL default 1h; Failed TTL default 24h; incidentHold blocking; finalizer blocking; Delete with expected revision; cleanup controller restart recovery", + "workItemId" : "ADR046-exec-013" + }, + { + "currentSource" : "`nixos-modules/processes-json.nix` (current Nix-to-JSON serialization pattern); `nixos-modules/manifest.nix` (current bundle manifest emitter with `bundleVersion`, `manifestVersion`, integrity-tracking pattern); `packages/xtask/src/main.rs` (`gen-schemas`) (current schema-fingerprint generation method); `packages/d2b-core/src/bundle.rs`: `Bundle`, `BundleVersion`, `BundleManifest`, `BundleArtifact` (current bundle integrity model)", + "dataMigration" : "Full d2b 3.0 reset; new artifact replaces current `processes.json` and `manifest.json` pattern for Zone resources without importing v2 state", + "dependencyOwner" : "ADR046-exec-001 + ADR046-exec-012; Nix resource compiler owner", + "destination" : "`nixos-modules/zone-bundle.nix`: Zone resource bundle emitter; `nixos-modules/resource-schemas/`: generated per-type Nix option submodules; `packages/d2b-contracts/src/v3/resource_bundle.rs`: `ResourceBundle`, `ResourceEnvelope`, `BundleManifest`, `BundleIntegrityPin` Rust types; `packages/xtask/src/gen_resource_schemas.rs`: `xtask gen-resource-schemas` (generates schema JSON and Nix option modules)", + "detailedDesign" : "`zone-bundle.nix` iterates the flat `d2b.zones..resources` attrset (all types share the same attrset; each entry has `type`, optional `metadata`, and `spec`). For each entry it serializes the `spec` submodule to canonical JSON 1:1 (field names unchanged; keys sorted at every level; order-significant arrays preserve declaration order; semantically unordered arrays sorted). `metadata.ownerRef`, `metadata.labels`, `metadata.annotations` are serialized from the author-supplied `metadata` submodule into the envelope's `metadata` object. `metadata.managedBy` and `metadata.configurationGeneration` are NOT in the bundle envelope; they are set by the activation controller at runtime. `metadata.name` is the attrset key; `metadata.zone` is the zone key. `xtask gen-resource-schemas` generates both the schema JSON files AND the per-type Nix option submodule files under `nixos-modules/resource-schemas/` from the Rust DTO definitions; both must be regenerated when any ResourceType spec or Rust struct changes (same drift-gate pattern as current `make test-drift` / `xtask gen-schemas`). The bundle sorts all envelopes by `type` then `metadata.name` alphabetically, computes `bundleSha256` from the canonical JSON of the `resources` array, and emits the bundle manifest as a NixOS store artifact. No secret values, credentials, or OS paths not already declared in `spec` or `metadata` fields may appear in any envelope JSON. The bundle file includes private integrity metadata (resource type schema fingerprints, per-Provider schema fingerprints) alongside the `resources` array; these are bundle-level fields, never per-envelope fields. Alongside the resource bundle, the emitter installs the global private artifact catalog at `/etc/d2b/artifact-catalog.json` (root:d2bd 0640) mapping each `d2b.artifacts.` to `{ \"sha256\", \"size\", \"storePath\", \"type\" }`; this catalog is never included in public resource bundle envelopes. The bundle outer wrapper includes a `catalogSha256` binding the catalog to the bundle for activation-time integrity verification.", + "integration" : "Zone Nix configuration → ADR046-exec-012 compilers → ADR046-exec-014 bundle emitter → `/etc/d2b/zones//resource-bundle.json` symlink → ADR046-exec-015 activation controller", + "removalProof" : "`nixos-modules/processes-json.nix` and current `manifest.nix` bundle artifact for Host/Guest/Process retained in parallel until Zone resource bundle replaces all roles; then removed", + "reuseAction" : "adapt", + "reuseSource" : null, + "specId" : "ADR-046-resources-host-guest-process-user", + "specPath" : "docs/specs/ADR-046-resources-host-guest-process-user.md", + "validation" : "nix-unit test: bundle sort order (Host < Guest < Process; within type, names alphabetical); nix-unit test: `bundleSha256` recomputed from resources array matches recorded value; nix-unit test: schema fingerprints appear in private bundle file fields, not in any individual ResourceEnvelope `metadata` or `spec` object; nix-unit test: `catalogSha256` changes when any artifact derivation changes; nix-unit test: `schemaFingerprint` changes when ResourceTypeSchema JSON changes; nix-unit test: `providerSchemaFingerprint` changes when Provider settings schema changes and is null when Provider declares no schema; nix-unit test: no inline secret value passes through to bundle JSON; nix-unit test: identical configuration produces byte-identical bundle JSON across two builds; nix-unit test: artifact catalog JSON contains `storePath` field for each entry; nix-unit test: no envelope in `resources` array contains `storePath`, `nixSystem`, `schemaFingerprint`, or `providerSchemaFingerprint`", + "workItemId" : "ADR046-exec-014" + }, + { + "currentSource" : "No direct current equivalent; current realm/workload configuration is applied at NixOS activation time directly, not through a Resource API generation controller", + "dataMigration" : "Full d2b 3.0 reset; no v2 state/config import", + "dependencyOwner" : "ADR046-exec-014 + ADR046-exec-001 + ADR046-exec-022; configuration generation controller owner", + "destination" : "`packages/d2b-core-controller/src/configuration.rs`: `ZoneConfigController`, `GenerationState`, `PendingCleanup`, `BundleActivation`, `ActivationResult`, `ActivationError`, `GenerationDiff`, `DiffEntry`, `DiffKind`, `CleanupRecord`, `CleanupPhase`, `CleanupOutcome`, `RetentionPolicy`, `RetentionState`; `packages/d2b-core-controller/src/audit.rs`: audit event emission per the \"Audit events\" section", + "detailedDesign" : "The Zone configuration controller (`packages/d2b-core-controller/src/configuration.rs`) runs as a fixed process in the `d2b-core-controller` crate. It watches `/etc/d2b/zones//resource-bundle.json` (inotify or polling). On change: (1) read and verify `bundleSha256` integrity; fail closed on mismatch, emit `d2b.zone.config.activate.error` with `config-bundle-integrity-failed`, make no changes. (2) Compare candidate `bundleSha256` against the currently active bundle record; if identical, this is a no-op re-activation (return immediately). (3) Verify resource type schema fingerprints (from bundle private fields) against committed schemas at `packages/d2b-contracts/src/v3/schemas/.json`; any mismatch fails the entire bundle closed with `config-schema-mismatch`, emits error, makes no changes. (4) Verify Provider schema fingerprints against installed Provider schemas; any mismatch fails the entire bundle closed with `provider-schema-mismatch`, emits error, makes no changes. (5) Verify `catalogSha256` binding against `/etc/d2b/artifact-catalog.json`; mismatch fails bundle closed. (6) Fetch current `metadata.managedBy=\"configuration\"` resources via ResourceClient List (one call per type). (7) Compute `GenerationDiff` (new/changed/unchanged/removed) by type+name key. (8) Submit Create, UpdateSpec, and Delete intents concurrently with bounded async concurrency (default max 32 in-flight); activation returns after all intents are durably queued by the resource store, without waiting for reconcile loops to complete. For unchanged specs: issue UpdateConfigGeneration to refresh `metadata.configurationGeneration` to the new generation number; no controller reconcile triggered. For new resources: core sets `metadata.managedBy=\"configuration\"` and `metadata.configurationGeneration=` on Create; if a same-name resource already exists with `metadata.managedBy=\"controller\"` or `metadata.managedBy=\"api\"`, record a per-item `config-collision` error for that resource without seizing it, emit error, continue other intents. For changed specs: submit UpdateSpec with `expectedRevision`; retry on optimistic lock conflict. For removed resources: submit Delete; set `metadata.deletionRequestedAt`. (9) Set Zone `phase=Pending` while create/update intents are outstanding. (10) Return after durable queue commit; do not block on reconcile completion. (11) The cleanup controller watches ResourceClient Watch streams for `Deleted` revision events (not polling GET) for each pending-cleanup resource by type+name+expectedRevision. When all finalizers release, the resource store commits the `Deleted` revision event atomically with row and index removal in a single transaction; following this commit, the audit subsystem appends the deletion audit record using a dedup/exactly-once recovery key (audit is NOT part of the atomic store transaction). Zone transitions to `phase=Degraded` immediately whenever any pending-cleanup item is outstanding. When all pending-cleanup items receive `Deleted` Watch events, `pendingCleanup` empties and Zone transitions to `phase=Ready`. Cleanup-stuck threshold: 10 min default; configurable; stuck resources remain Degraded without blocking later activations. Prior generation retention: controller retains the N most recently activated, cleanup-complete bundle records (default N=3; range 1..16; no time-based TTL).", + "integration" : "Fixed process in `packages/d2b-core-controller`; ResourceClient (Create/UpdateSpec/UpdateConfigGeneration/UpdateStatus/Delete/List/Watch) per ADR046-exec-022; Zone resource status UpdateStatus (configGeneration, pendingCleanup, lastActivatedAt, lastActivationError); Zone `phase` transitions (Pending while outstanding intents; Degraded immediately when cleanup remains; Ready when create/update complete and cleanup empty); audit segment per ADR046-exec-014 audit events table", + "removalProof" : "None — net-new controller; no prior owner to remove", + "reuseAction" : "create", + "reuseSource" : null, + "specId" : "ADR-046-resources-host-guest-process-user", + "specPath" : "docs/specs/ADR-046-resources-host-guest-process-user.md", + "validation" : "Runtime/integration tests 19–23 from the \"Tests for Nix configuration and ResourceType-specific lifecycle\" section in this spec; additionally: `GenerationDiff` hermetic unit tests (new/changed/unchanged/removed classification); `bundleSha256` integrity failure aborts and emits correct audit event; `catalogSha256` mismatch aborts bundle; UpdateSpec optimistic lock conflict retried correctly; Watch `Deleted` revision events consumed (not polling GET) to track cleanup completion; Zone `phase=Pending` while intents outstanding; Zone `phase=Degraded` immediately when any cleanup outstanding (no grace window); Zone `phase=Ready` when complete; activation returns after durable queue commit, not after reconcile; same-name `managedBy=controller` OR `managedBy=api` collision emits per-item `config-collision` error without seizing resource, other intents continue; unchanged spec refreshes `configurationGeneration` without triggering controller reconcile; final deletion: atomic tx commits `Deleted` revision event + row/index removal only; audit append follows committed revision via dedup/exactly-once recovery (NOT part of atomic tx); recovery retry produces no duplicate audit record; prior bundle record released after cleanup-complete and retention count exceeded; activation with zero diff and identical bundleSha256 is a no-op", + "workItemId" : "ADR046-exec-015" + }, + { + "currentSource" : "No pre-ADR45 v3 baseline equivalent; ADR-only", + "dataMigration" : "Full d2b 3.0 reset; no v2 state/config import", + "dependencyOwner" : "ADR046-exec-001; `d2b-session` v3 crate owner", + "destination" : "`packages/d2b-bus-session/src/`: all above modules verbatim; `packages/d2b-bus-session/tests/`: all above tests verbatim", + "detailedDesign" : "The entire `d2b-session` portable ComponentSession runtime is transport-agnostic and contains no ADR 0045 realm-specific types. All Noise handshake parameters, record framing, fragmentation, named stream mux, fair scheduler, cancellation, deadlines, bootstrap PSK, and attachment bindings are directly reusable. `ComponentSessionDriver` trait and `serve_ttrpc_services` are the primary integration surface for every v3 bus service. The `sessions/lib.rs` re-export boundary (`pub use d2b_contracts::v2_component_session as contract`) must be updated to point at the v3 wire contract module. Primary reuse disposition: `adapt`. Preserved source-plan detail: copy verbatim; rename crate from `d2b-session` to `d2b-bus-session` or retain name.", + "integration" : "All v3 bus service implementations (`d2b-zone-service`, `d2b-provider-agent`, `d2b-bus-client`) depend on this crate; EphemeralProcess attach service; Process Provider launch ticket channel", + "removalProof" : "None — net-new; no prior owner to remove", + "reuseAction" : "adapt", + "reuseSource" : null, + "specId" : "ADR-046-resources-host-guest-process-user", + "specPath" : "docs/specs/ADR-046-resources-host-guest-process-user.md", + "validation" : "Copy tests verbatim; all tests must pass on v3 baseline without modification; re-run `tests/noise_vectors.rs` golden vectors; add one v3-specific test: endpoint policy identity uses v3 `ZoneId`/zone-name binding rather than ADR45 `RealmId`", + "workItemId" : "ADR046-exec-016" + }, + { + "currentSource" : "`packages/d2b-session-unix/src/` at pre-ADR45 baseline `b5ddbed6` (partially equivalent: `d2b-session-unix` existed with `d2b-session-unix/src/adapter.rs` `UnixSeqpacketTransport`, `d2b-session-unix/src/descriptor.rs`, `d2b-session-unix/src/pidfd.rs`); main commit extends with credit pools, full pidfd identity verifier, and `host-socket` feature gate", + "dataMigration" : "Full d2b 3.0 reset; no v2 state/config import", + "dependencyOwner" : "ADR046-exec-016; `d2b-bus-session-unix` crate owner", + "destination" : "`packages/d2b-bus-session-unix/src/`: all above modules verbatim; `packages/d2b-bus-session-unix/tests/`: all above tests verbatim", + "detailedDesign" : "Provides the Linux-specific `OwnedTransport` implementation for Unix seqpacket and stream sockets. `CreditPool`/`ProcessCreditLimit` enforces per-scope FD attachment budget (ADR45 constants: `MAX_PROCESS_ATTACHMENT_CREDITS = 2048`, `MAX_HOST_ATTACHMENT_CREDITS = 8192`, `RESERVED_CONTROL_FDS = 64`). `PidfdIdentityVerifier` provides the `/proc//fdinfo/` parse path that Process controllers use to verify process identity before `pidfd_open(2)` — this is a direct dependency of system-systemd and system-minijail Process Providers (ADR046-exec-006, ADR046-exec-007). Primary reuse disposition: `adapt`. Preserved source-plan detail: copy verbatim; rename crate from `d2b-session-unix` to `d2b-bus-session-unix` or retain name.", + "integration" : "v3 Zone runtime public socket listener; system-minijail/system-systemd pidfd identity verification; EphemeralProcess attach named stream", + "removalProof" : "If the crate is renamed, the superseded `packages/d2b-session-unix/` owner is removed or reduced to a compatibility wrapper after `packages/d2b-bus-session-unix/` passes copied and v3-specific tests; if the name is retained, no prior owner is removed.", + "reuseAction" : "adapt", + "reuseSource" : null, + "specId" : "ADR-046-resources-host-guest-process-user", + "specPath" : "docs/specs/ADR-046-resources-host-guest-process-user.md", + "validation" : "Copy all 20+ tests verbatim; all must pass; add v3-specific test: `PathnamePeerVerifier` verifies against `d2b-zonert` daemon uid; credit pool per-Zone-runtime limits match v3 constants", + "workItemId" : "ADR046-exec-017" + }, + { + "currentSource" : "No pre-ADR45 v3 baseline equivalent for v3 bus wire types; ADR-only", + "dataMigration" : "Full d2b 3.0 reset; no existing constants module state to migrate", + "dependencyOwner" : "ADR046-exec-016; `d2b-bus-wire` contract owner", + "destination" : "`packages/d2b-bus-wire/src/session.rs`: v3 bus protocol constants and wire types; all numeric constants copied verbatim; `PREFACE_MAGIC` retained; `EndpointPolicy` and `EndpointPolicyIdentity` adapted to use v3 `ZoneId`/`ProviderId` instead of ADR45 `RealmId` in the policy identity fingerprint", + "detailedDesign" : "All numeric constants (frame sizes, credit limits, deadline values, reconnect limits) are directly reusable without change — they are derived from protocol analysis, not from realm semantics. `LimitProfile::local_default()` is the source for `serve_ttrpc_services` capacity; retain the value. `EndpointPolicy` carries the Noise static key and schema fingerprint; the fingerprint computation does not embed realm names and is reusable. The `EndpointPolicyIdentity` type carries the zone runtime's static public key — update from ADR45 `RealmId` to v3 `ZoneId` string encoding. Primary reuse disposition: `adapt`. Preserved source-plan detail: copy and adapt.", + "integration" : "`d2b-bus-session` imports constants from here; all v3 bus service and client crates import protocol constants from `d2b-bus-wire`", + "removalProof" : "None — net-new; no prior owner to remove", + "reuseAction" : "adapt", + "reuseSource" : null, + "specId" : "ADR-046-resources-host-guest-process-user", + "specPath" : "docs/specs/ADR-046-resources-host-guest-process-user.md", + "validation" : "Compile-time assertions on all copied numeric constants matching source values; `EndpointPolicyIdentity` golden-vector test with v3 zone name encoding; `LimitProfile::local_default()` round-trip test", + "workItemId" : "ADR046-exec-018" + }, + { + "currentSource" : "`packages/d2b-realm-provider/src/provider.rs` at baseline `b5ddbed6` (`HostSubstrateProvider`, `RuntimeProvider`, `WorkloadProvider` traits — baseline, unwired); ADR-only for the runtime registry and RPC proxy", + "dataMigration" : "Full d2b 3.0 reset; no v2 state/config import", + "dependencyOwner" : "ADR046-exec-016 + ADR046-exec-018; `d2b-provider-runtime` crate owner", + "destination" : "`packages/d2b-provider-runtime/src/`: `registry.rs`, `rpc.rs`, `instance.rs`, `context.rs`, `error.rs`; provider trait objects moved to `d2b-bus-wire` or `d2b-provider-contracts`", + "detailedDesign" : "`ProviderRegistry` manages in-flight permits, draining, and provider lifecycle. `RegistryLimits { total_in_flight, per_provider_in_flight }` is directly reusable. `RpcProviderProxy` wraps a `ComponentSessionDriver` and dispatches typed `RpcCall` to an `AuthenticatedProviderRpc` implementation — the proxy pattern is fully reusable for v3 Provider resource controllers. `SessionIdentity { peer_role, service, provider_id, provider_type, provider_generation }` maps directly to a v3 Provider session credential. `InFlightPermit` RAII guard is directly reusable. The provider trait object set (`RuntimeProvider`, `StorageProvider`, etc.) adapts to v3 Provider resource typed methods; the trait hierarchy is preserved but `ProviderMethod` enum variant names may be renamed to drop ADR45 workload terminology. `ProviderRegistry::MAX_PROVIDER_REGISTRY_ENTRIES` bound from v2_provider is retained. Primary reuse disposition: `adapt`. Preserved source-plan detail: copy and adapt.", + "integration" : "Zone runtime provider-agent ComponentSession; system-core Provider controller; every v3 Provider resource controller", + "removalProof" : "Supersedes the baseline `packages/d2b-realm-provider/src/provider.rs` runtime/provider trait owner after `packages/d2b-provider-runtime/` registry/RPC tests pass and no v3 registry path imports ADR45 workload terminology.", + "reuseAction" : "adapt", + "reuseSource" : null, + "specId" : "ADR-046-resources-host-guest-process-user", + "specPath" : "docs/specs/ADR-046-resources-host-guest-process-user.md", + "validation" : "Copy `d2b-provider` tests; `RegistryLimits::validate` enforces non-zero and per≤total; `InFlightPermit` RAII release; drain/retire state machine; `RpcProviderProxy` round-trip over `FakeProvider` (from toolkit ADR046-exec-020)", + "workItemId" : "ADR046-exec-019" + }, + { + "currentSource" : "`packages/d2b-realm-provider/src/conformance.rs` at baseline `b5ddbed6` (`check_provider_conformance`); ADR-only for toolkit server and fixture", + "dataMigration" : "Full d2b 3.0 reset; no v2 state/config import", + "dependencyOwner" : "ADR046-exec-019; `d2b-provider-toolkit` owner", + "destination" : "`packages/d2b-provider-toolkit/src/`: retain all modules verbatim; adapt `ProviderAgentAdapter` to use v3 `ProviderRegistry` (ADR046-exec-019) and v3 bus wire types; adapt `GeneratedProviderServiceServer` to use v3 generated service stubs (ADR046-exec-021); `packages/d2b-provider-agent/src/`: adapted from `gateway-runtime/src/provider_agent.rs`", + "detailedDesign" : "`ProviderAgentAdapter` is the core: it drives a `ComponentSessionDriver` receive loop, dispatches decoded ttrpc frames to `ProviderRegistry`, and forwards responses. `GeneratedProviderServiceServer` closes the loop by registering all generated service stubs with `serve_ttrpc_services`. `FakeProvider` implements every v2 Provider trait with deterministic outputs — adapt each trait method to v3 Provider resource semantics while retaining the fixture pattern. `Redacted` / `Secret` zero-copy wrappers are used in every audit log path; copy verbatim. Provider conformance check pattern is retained: descriptor validation, capability publication, fixture round-trip, observability query result. Primary reuse disposition: `adapt`. Preserved source-plan detail: copy and adapt.", + "integration" : "Every v3 Provider resource controller uses `ProviderAgentAdapter` + `ProviderAgentProcess`; conformance tests gate Provider dossier acceptance; `FakeProvider` is used in all Provider controller hermetic tests", + "removalProof" : "Supersedes baseline `packages/d2b-realm-provider/src/conformance.rs` ownership only after `packages/d2b-provider-toolkit/` conformance coverage passes; ADR45 provider-agent registration behavior is not retained in the v3 provider-agent path.", + "reuseAction" : "adapt", + "reuseSource" : null, + "specId" : "ADR-046-resources-host-guest-process-user", + "specPath" : "docs/specs/ADR-046-resources-host-guest-process-user.md", + "validation" : "Copy conformance tests verbatim; `check_descriptor_conformance` passes on a `FakeProvider` descriptor; `check_provider_conformance` covers all five `ConformanceError` variants; `ProviderAgentProcess` shutdown within deadline test; `MAX_DISPATCH_IN_FLIGHT` semaphore back-pressure test", + "workItemId" : "ADR046-exec-020" + }, + { + "currentSource" : "No pre-ADR45 v3 baseline equivalent; ADR-only", + "dataMigration" : "Full d2b 3.0 reset; no v2 state/config import", + "dependencyOwner" : "ADR046-exec-016 + ADR046-exec-018; v3 Zone service contract owner", + "destination" : "`packages/d2b-bus-contracts/src/generated_v3_services/`: v3 generated ttrpc stubs for Zone service methods (Resource CRUD, Watch, ComponentSession service verbs); `packages/d2b-zone-service/src/`: Zone runtime service handler adapted from `DaemonServiceV2` pattern; `packages/d2b-zone-service/src/admission.rs`, `handler.rs`, `routing.rs`", + "detailedDesign" : "`StrictWireMessage` trait (decode_strict, encode_strict) is directly reusable — it enforces deny-unknown-fields decode and schema-pinned fingerprint validation. `ServiceInventoryDocument` / `service_schema_fingerprint` pattern provides the service schema publication mechanism that v3 Provider resources use to advertise their ComponentSession service interface. `DaemonServiceV2` / `DaemonOperationHandler` pattern becomes the v3 Zone service handler base: `DaemonCallContext` → v3 `ZoneCallContext` with `ZoneId`, principal `User/`, operation deadline; `DaemonMethod` enum → v3 `ZoneMethod` (ResourceGet, ResourceList, ResourceWatch, ResourceCreate, ResourceUpdateSpec, ResourceUpdateStatus, ResourceDelete, BusAttach); `daemon_endpoint_policy` → v3 zone endpoint policy with v3 `ZoneId`-bound static key and schema fingerprint. `server_stream_name`/`parse_server_stream_name` for Watch stream naming is reusable verbatim. `TerminalStreamValidator` / `ServerStreamLease` for terminal byte stream safety is reusable for EphemeralProcess attach. Primary reuse disposition: `adapt`. Preserved source-plan detail: adapt; v3 generates new protobuf definitions and new ttrpc stubs; reuse message shaping, method dispatch patterns, and service inventory pattern.", + "integration" : "Zone runtime ttrpc service over public socket ComponentSession; Provider resource controller attaches via `RpcProviderProxy`; ResourceClient Watch uses server stream naming convention", + "removalProof" : "None — net-new; no prior owner to remove", + "reuseAction" : "adapt", + "reuseSource" : null, + "specId" : "ADR-046-resources-host-guest-process-user", + "specPath" : "docs/specs/ADR-046-resources-host-guest-process-user.md", + "validation" : "`StrictWireMessage` decode rejects unknown fields on all v3 message types; `service_schema_fingerprint` is stable across builds; `ZoneCallContext` deadline enforcement test; `DaemonSeqpacketTransport` → `ZoneSeqpacketTransport` end-to-end roundtrip; generated v3 stub compile check", + "workItemId" : "ADR046-exec-021" + }, + { + "currentSource" : "No pre-ADR45 v3 baseline equivalent; ADR-only", + "dataMigration" : "Full d2b 3.0 reset; no v2 state/config import", + "dependencyOwner" : "ADR046-exec-016 + ADR046-exec-021; `d2b-bus-client` crate owner", + "destination" : "`packages/d2b-bus-client/src/`: all above modules; `DaemonClient` → `ZoneClient` (v3 Resource CRUD/Watch verbs); `GuestClient` → `ProcessAttachClient`; `HostSocketConnector` → `ZoneSocketConnector`; `LocalDaemonSession` → `LocalZoneSession`", + "detailedDesign" : "`Client` is the core typed async client with bounded retry, wall-clock injection, and cancellation. `ConnectedSession` wraps a `ComponentSessionDriver` and provides `call()`, `open_stream()`, and `close()`. `ComponentSessionConnector` trait decouples connection establishment from the client — v3 `ZoneSocketConnector` implements this for the local Zone runtime public socket. `TargetResolver`/`RouteTable` provides request routing to local vs remote Zone runtimes. `HostSocketConnector::local_daemon_endpoint_identity` provides the peer identity pinning that prevents MITM on the local socket — this is a security-critical invariant; copy verbatim, rename from `d2bd` to `d2b-zonert` uid. `DaemonClient` method table adapts to v3 Resource verbs: `ResourceGet`, `ResourceList`, `ResourceWatch` (streaming), `ResourceCreate`, `ResourceUpdateSpec`, `ResourceUpdateStatus`, `ResourceDelete`. `ServiceHandle`/`MethodHandle`/`GeneratedClient` provide the typed client stub generation pattern. `RetryPolicy`/`RetryClass`/`RemoteErrorKind` error classification is directly reusable. Primary reuse disposition: `adapt`. Preserved source-plan detail: copy and adapt.", + "integration" : "CLI (`d2b` binary), external Zone API callers, Process/EphemeralProcess controller ResourceClient, all consumer-facing API paths", + "removalProof" : "None — net-new; no prior owner to remove", + "reuseAction" : "adapt", + "reuseSource" : null, + "specId" : "ADR-046-resources-host-guest-process-user", + "specPath" : "docs/specs/ADR-046-resources-host-guest-process-user.md", + "validation" : "Copy `tests/client.rs` verbatim; add v3-specific tests: `ResourceWatch` streaming teardown; `ZoneSocketConnector` peer-uid mismatch rejection; retry policy respects `RetryClass::Transient`/`Permanent`; `TargetInput`→`ResolvedTarget` for local-only v3 Zone; `local_daemon_endpoint_identity` returns correct v3 zone-rt uid", + "workItemId" : "ADR046-exec-022" + }, + { + "currentSource" : "`packages/d2b-realm-router/src/` at baseline `b5ddbed6` (`OperationRouter`, `RouteDecision`, `OperationRoutePlan`, `ReconcilableLease` — unwired in daemon); ADR-only for Zone service routing with idempotency", + "dataMigration" : "Full d2b 3.0 reset; no v2 state/config import", + "dependencyOwner" : "ADR046-exec-021 + ADR046-exec-019; Zone bus routing owner", + "destination" : "`packages/d2b-zone-router/src/`: `router.rs` (v3 `ZoneOperationRouter` — idempotency semantics copied verbatim; dedup key namespace adapted from `(realm, principal, node, kind, key)` to `(zone, resource-type, resource-name, verb, idempotency-key)`); `service.rs` (v3 `ZoneServiceLimits`, `ZoneServiceServer`, `ZoneAuditEvent`); `resolver.rs` (v3 `ZoneTargetResolver`, `ZoneEntrypointTable`)", + "detailedDesign" : "The idempotency/dedup semantics from `OperationRouter` are security-critical and must be copied exactly: (1) dedup key is the full 5-tuple namespace — reusing a key under a different principal is a conflict, not a replay; (2) expired keys leave tombstones for a no-reuse horizon; (3) same-key/same-request returns the original `operation_id` and recorded result; (4) same-key/different-request returns conflict error fail-closed. These semantics apply to all v3 Resource mutation verbs (Create, UpdateSpec, UpdateStatus, Delete) that carry an idempotency key. `RealmServiceLimits` numeric bounds are copied verbatim: `MAX_DISPATCH_IN_FLIGHT=64` gates concurrent resource mutations per Zone session. `RealmSessionAuthority` principal-binding model (session principal MUST match request principal field, derived in trusted code from authenticated session) maps directly to v3 Zone RBAC: `ZoneCallContext` carries the authenticated `User/` principal from `SO_PEERCRED`; no caller-supplied principal field is accepted. `DurableExecTable`/`DEFAULT_MAX_EXECUTIONS` from `execution.rs` provides the EphemeralProcess in-flight table bound for the Zone router.", + "integration" : "Zone runtime ttrpc service (ADR046-exec-021); Zone ResourceClient (ADR046-exec-022); every Resource mutation verb", + "removalProof" : "Supersedes the baseline `packages/d2b-realm-router/src/` routing owner after `packages/d2b-zone-router/` passes idempotency, dispatch-limit, and principal-binding tests; ADR45 realm/workload route tables are not imported into v3.", + "reuseAction" : "adapt", + "reuseSource" : null, + "specId" : "ADR-046-resources-host-guest-process-user", + "specPath" : "docs/specs/ADR-046-resources-host-guest-process-user.md", + "validation" : "Idempotency replay returns original result; conflict returns error; expired tombstone fails closed; `MAX_DISPATCH_IN_FLIGHT` semaphore back-pressure; principal-binding enforcement (mismatched principal returns auth-denied); `DurableExecTable` capacity limit; v3 5-tuple dedup key golden vector test", + "workItemId" : "ADR046-exec-023" + }, + { + "currentSource" : "None — the fixed user-session authority is today ambient prose across the display/audio/clipboard/notification/secret-service dossiers; no named owner exists", + "dataMigration" : "None — full d2b 3.0 reset", + "dependencyOwner" : "ADR046-zone-control-019 (authority index); `Provider/system-systemd` (user manager) + core/user-agent owner", + "destination" : "`packages/d2b-core-controller/src/user_session_authority.rs` (or a core/user-agent per-session agent Process under `Provider/system-systemd`); `AuthorityDescriptor` on the session authority", + "detailedDesign" : "Name and implement the **fixed user-session authority** (D097 desktop/session): `authorityScope: seat` bound to `(Host, User, login-session/seat)`, opaque `authorityKey` (never a raw socket path/XDG_RUNTIME_DIR/DISPLAY/seat name), `cardinality: exactly-one` per `(Host, User, login-session)`, `arbitration: exclusive`, owner = a core/user-agent per-user-session agent Process (NOT a new Provider), adoption by `ownerProof` (agent Process identity + login-session id), `exportability: forbidden`. It is the sole opener of the compositor/PipeWire/session-bus FDs and hands them to desktop Providers only via the EffectPort/LaunchTicket. Core's authority index rejects a duplicate session authority (or a duplicate same-user display portal, clipboard host, notification sink, audio mediator, systemd user manager, Secret Service, or seat-input claimant) with `duplicateConflict` before any FD open; multi-user/seat is admitted only up to the declared per-Host limit. Guest-stop invalidates every session authority/lease bound to that Guest across display/audio/notification/credential/shell in one dependency-aware cascade (D091), with no stale FD surviving. Host input (`wl_seat`/pointer constraints) is an `at-most-one`-per-seat authority under this session authority; pointer-constraint enforcement is a declared boundary until an interaction Provider implements it. Primary reuse disposition: `adapt`. Preserved source-plan detail: net-new (name and implement the shared user-session authority).", + "integration" : "Core authority index (ADR046-zone-control-019); `Provider/system-systemd` user manager; display/audio/clipboard/notification/secret-service/shell Provider services bind to this single authority for their FDs; D091 Guest-stop cascade", + "removalProof" : "Not applicable (net-new named authority; replaces ambient prose)", + "reuseAction" : "adapt", + "reuseSource" : "`Provider/system-systemd` user-manager scope; D077 EffectPort/LaunchTicket FD handoff", + "specId" : "ADR-046-resources-host-guest-process-user", + "specPath" : "docs/specs/ADR-046-resources-host-guest-process-user.md", + "validation" : "Single session authority per `(Host, User, session)`; duplicate same-user session authority / desktop service rejected with `duplicateConflict`; multi-seat declared-limit enforcement; Guest-stop invalidates all bound desktop/audio/notification/credential/shell authorities and leases (no stale compositor/PipeWire/session-bus FD); seat-input second claimant rejected; adoption by `ownerProof` and quarantine on ambiguity; hermetic with fakes", + "workItemId" : "ADR046-exec-024" + }, + { + "currentSource" : "`packages/d2b-core/src/{storage,sync}.rs` (atomic/idempotency reference shape, E4); no redb usage exists anywhere in this repository at any inspected commit", + "dataMigration" : "None (disposable fixture data only)", + "dependencyOwner" : "W0 shared contract root; store/reconciliation integrator", + "destination" : "`proofs/redb-resource-store-spike/`", + "detailedDesign" : "Implements SPIKE-01 and SPIKE-02: the eight-table schema, fair write queue, blocking store-actor, watch registrar, and hint bus described in those two spike entries Primary reuse disposition: `adapt`. Preserved source-plan detail: `adapt` (the atomic-write/idempotency discipline in `storage.rs`/`sync.rs` is adapted into the spike's write-transaction algorithm; redb itself is used unmodified).", + "integration" : "None (standalone; no d2b-bus/ComponentSession/broker dependency)", + "removalProof" : "Per SPIKE-01/SPIKE-02 Cleanup rows: deleted once `packages/d2b-resource-store-redb` and `packages/d2b-controller-toolkit/benches/reaction.rs` reproduce equal-or-stricter coverage", + "reuseAction" : "adapt", + "reuseSource" : null, + "specId" : "ADR-046-feasibility-and-spikes", + "specPath" : "docs/specs/ADR-046-feasibility-and-spikes.md", + "validation" : "SPIKE-01 metrics (1)-(5) and SPIKE-02 metrics (1) across all 3 concurrency profiles, per those entries' exact pass/fail thresholds", + "workItemId" : "ADR046-feasibility-001" + }, + { + "currentSource" : "`packages/d2bd/src/supervisor/{dag,pidfd}.rs` (current DAG/pidfd reference shape, E4)", + "dataMigration" : "None — full d2b 3.0 reset; no prior state to migrate", + "dependencyOwner" : "`ADR046-feasibility-001`; reconciliation/process integrator", + "destination" : "`proofs/process-fastlaunch-spike/`", + "detailedDesign" : "Implements SPIKE-03: the fake Process controller loop, fake `ProcessLaunchEffectPort`, and the 1/10/100-concurrency commit-to-launch-attempt and next-dispatch-independence benchmarks Primary reuse disposition: `adapt`. Preserved source-plan detail: `adapt` (current DAG ordering/readiness concepts are adapted into the spike's per-resource single-flight/parallel-semaphore loop).", + "integration" : "Consumes `ADR046-feasibility-001`'s hint-bus shape as its watch-receiver input", + "removalProof" : "Deleted once `packages/d2b-controller-toolkit/benches/reaction.rs` and the Process Provider integration tests named by `ADR046-reconcile-003` reproduce equal-or-stricter coverage", + "reuseAction" : "adapt", + "reuseSource" : null, + "specId" : "ADR-046-feasibility-and-spikes", + "specPath" : "docs/specs/ADR-046-feasibility-and-spikes.md", + "validation" : "SPIKE-03 metrics (1)-(3) and thresholds", + "workItemId" : "ADR046-feasibility-002" + }, + { + "currentSource" : "`packages/d2b-priv-broker/src/ops/spawn_runner.rs` and `tests/pidfd_real_spawner.rs` (current blocking-call reference shape, E4)", + "dataMigration" : "None — full d2b 3.0 reset; no prior state to migrate", + "dependencyOwner" : "Independent of `-001`/`-002`; EffectPort/ProviderSupervisor integrator", + "destination" : "`proofs/effectport-async-spike/`", + "detailedDesign" : "Implements SPIKE-04: the four fake EffectPort traits, the deliberately slow blocking-primitive backends, and the current-thread-runtime heartbeat-jitter detector", + "integration" : "None (standalone)", + "removalProof" : "Deleted once `packages/d2b-provider-supervisor` and the volume-domain effect adapter each carry an equal-or-stricter in-tree blocking-adapter regression test", + "reuseAction" : "adapt", + "reuseSource" : null, + "specId" : "ADR-046-feasibility-and-spikes", + "specPath" : "docs/specs/ADR-046-feasibility-and-spikes.md", + "validation" : "SPIKE-04 heartbeat-jitter metric and threshold", + "workItemId" : "ADR046-feasibility-003" + }, + { + "currentSource" : "None in v3 at `b5ddbed6` (no generic Provider registry exists; per parent ADR context, this is explicitly listed as missing)", + "dataMigration" : "None — full d2b 3.0 reset; no prior state to migrate", + "dependencyOwner" : "Independent of `-001`/`-002`/`-003`; Provider packaging/toolkit integrator", + "destination" : "`proofs/provider-packaging-spike/`", + "detailedDesign" : "Implements SPIKE-05: the two-binary crate, hand-authored manifest, fake `ProviderDeployment`, and the `cargo metadata` dependency-edge check Primary reuse disposition: `adapt`. Preserved source-plan detail: `adapt` (the crate-layout policy check reuses the same `src/`/`tests/`/`integration/`/`README.md` structure already enforced elsewhere in this repository's workspace policy tests).", + "integration" : "None (standalone)", + "removalProof" : "Deleted once the real Provider-toolkit crate (`ADR046-provider-001` destination) ships equal-or-stricter manifest-parsing/enumeration/workspace-policy coverage", + "reuseAction" : "adapt", + "reuseSource" : null, + "specId" : "ADR-046-feasibility-and-spikes", + "specPath" : "docs/specs/ADR-046-feasibility-and-spikes.md", + "validation" : "SPIKE-05 metrics (1)-(4) across 20 repeated randomized-order manifest loads", + "workItemId" : "ADR046-feasibility-004" + }, + { + "currentSource" : "`packages/d2b-realm-router/src/{session,secure_session,mux_session}.rs` (current routing reference shape, E4); main `a1cc0b2d` `packages/d2b-session/**`, `packages/d2b-session-unix/**` (re-verified green in E3)", + "dataMigration" : "None — full d2b 3.0 reset; no prior state to migrate", + "dependencyOwner" : "Independent of `-001` through `-004`; bus/session/transport/credential integrator", + "destination" : "`proofs/bus-routing-noise-spike/`, `proofs/transport-opaque-streams-spike/`, `proofs/credential-kk-e2e-spike/`", + "detailedDesign" : "Implements SPIKE-06 (exact-addressed routing + per-recipient Noise isolation), SPIKE-07 (Unix/vsock/relay-shaped opaque byte-stream conformance across 3 backends), and SPIKE-08 (Credential Provider → consumer Provider KK delivery with the 13-field binding contract) Primary reuse disposition: `adapt`. Preserved source-plan detail: `copy-unchanged` for the Noise/record/transport machinery (path-dependency on a pinned local checkout of `a1cc0b2d`); `adapt` for the fake router/relay/credential-delivery wrapper code that SPIKE-06/07/08 add on top.", + "integration" : "SPIKE-07's Unix backend and SPIKE-08's session machinery both depend on the same pinned `a1cc0b2d` path-dependency established for SPIKE-06", + "removalProof" : "Deleted per each spike's own Cleanup row: `packages/d2b-bus/src/router.rs` for SPIKE-06; the three real transport Provider crates for SPIKE-07; the real Credential Provider crates for SPIKE-08", + "reuseAction" : "adapt", + "reuseSource" : "main `a1cc0b2d`: `packages/d2b-session/src/{handshake,bootstrap,record,engine,scheduler,streams,lifecycle,transport}.rs`, `packages/d2b-session-unix/src/{adapter,vsock,pidfd,socket,systemd,credit,descriptor}.rs`, and the exact test files listed in E3's table", + "specId" : "ADR-046-feasibility-and-spikes", + "specPath" : "docs/specs/ADR-046-feasibility-and-spikes.md", + "validation" : "SPIKE-06 metrics (1)-(3), SPIKE-07 metrics (1)-(4) against the exact numeric gates already committed in the transport-unix/vsock dossiers, SPIKE-08 metrics (1)-(6)", + "workItemId" : "ADR046-feasibility-005" + }, + { + "currentSource" : "None in v3 at `b5ddbed6` (ProviderStateSet and the generalized Volume ResourceType are both ADR-only)", + "dataMigration" : "None — full d2b 3.0 reset; no prior state to migrate", + "dependencyOwner" : "Independent of `-001` through `-005`; Provider-state/Volume integrator", + "destination" : "`proofs/provider-state-export-spike/`, `proofs/volume-policy-spike/`", + "detailedDesign" : "Implements SPIKE-09 (optional declared state-Volume creation order, guest-local/host-backed-guest placement, virtiofs Export child ownership) and SPIKE-10 (Volume ACL/`sourcePolicyId`/quota/lifecycle-marker policy conformance)", + "integration" : "None between the two spikes beyond sharing the same fake resource-store oracle shape", + "removalProof" : "Deleted per each spike's Cleanup row: the real `ADR-046-provider-state` work-item destination for SPIKE-09; `d2b-provider-volume-local`'s own `tests/`/`integration/` suite for SPIKE-10", + "reuseAction" : "adapt", + "reuseSource" : null, + "specId" : "ADR-046-feasibility-and-spikes", + "specPath" : "docs/specs/ADR-046-feasibility-and-spikes.md", + "validation" : "SPIKE-09 metrics (1)-(4); SPIKE-10 metrics (1)-(5), zero-tolerance on path leakage", + "workItemId" : "ADR046-feasibility-006" + }, + { + "currentSource" : "`packages/d2b-priv-broker/src/ops/spawn_runner.rs` (real-spawn reference shape, E4); current unsafe-local helper runtime/systemd invocation shape", + "dataMigration" : "None — full d2b 3.0 reset; no prior state to migrate", + "dependencyOwner" : "Independent of `-001` through `-006`; Process Provider integrator", + "destination" : "`proofs/process-provider-conformance-spike/`", + "detailedDesign" : "Implements SPIKE-11: the shared `ProcessProviderHarness` trait, the minijail-shaped `clone3(CLONE_PIDFD)` launcher, and the systemd transient-user-scope launcher, plus the identity-drift/quarantine and clean-exit cases", + "integration" : "None (standalone; requires a Linux host with `clone3`/`pidfd_open`, and optionally a running `systemd --user` instance behind the `systemd-user` feature)", + "removalProof" : "Deleted once `packages/d2b-provider-system-systemd` and `packages/d2b-provider-system-minijail` each carry this exact shared conformance suite in their own `tests/`", + "reuseAction" : "adapt", + "reuseSource" : null, + "specId" : "ADR-046-feasibility-and-spikes", + "specPath" : "docs/specs/ADR-046-feasibility-and-spikes.md", + "validation" : "SPIKE-11 metrics (1)-(4), zero-tolerance on false adoption", + "workItemId" : "ADR046-feasibility-007" + }, + { + "currentSource" : "`nixos-modules/assertions.nix` pattern, `packages/xtask` `gen-schemas` pattern, `make test-drift` gate (existing generated-or-eval-contract precedent)", + "dataMigration" : "None — full d2b 3.0 reset; no prior state to migrate", + "dependencyOwner" : "Independent of `-001` through `-007`; Nix/xtask integrator", + "destination" : "`proofs/nix-authoring-spike/`", + "detailedDesign" : "Implements SPIKE-12: the minimal flake, the two synthetic ResourceTypes, the hand-written committed schemas, the standalone `gen-schemas`-shaped drift check, and the two-generation removed-resource cleanup simulation", + "integration" : "None (standalone flake; no dependency on the main `flake.nix`)", + "removalProof" : "Deleted once the real `nixos-modules/resources.nix` and `packages/xtask` `gen-schemas` implementation reproduce these metrics as part of `make test-drift`/`make test-flake`", + "reuseAction" : "adapt", + "reuseSource" : null, + "specId" : "ADR-046-feasibility-and-spikes", + "specPath" : "docs/specs/ADR-046-feasibility-and-spikes.md", + "validation" : "SPIKE-12 metrics (1)-(5), byte-for-byte reproducibility across 3 hermetic builds", + "workItemId" : "ADR046-feasibility-008" + }, + { + "currentSource" : "`packages/d2b/src/lib.rs` `cmd_audio`/`cmd_clipboard_arm` and the current command-table shape (current CLI reference shape)", + "dataMigration" : "None — full d2b 3.0 reset; no prior state to migrate", + "dependencyOwner" : "Independent of `-001` through `-008`; CLI integrator", + "destination" : "`proofs/cli-discovery-spike/`, `proofs/clean-cutover-spike/`", + "detailedDesign" : "Implements SPIKE-13 (dynamic Provider-projection discovery, bounds, latency isolation) and SPIKE-14 (zero v2 dispatch, fresh Zone bootstrap ignoring legacy state)", + "integration" : "None between the two spikes beyond sharing the same fixture command-table shape", + "removalProof" : "Deleted per each spike's Cleanup row: the real `d2b` CLI crate's own discovery conformance test for SPIKE-13; the real CLI crate's workspace-policy/lint gate plus the real bootstrap sequence for SPIKE-14", + "reuseAction" : "adapt", + "reuseSource" : null, + "specId" : "ADR-046-feasibility-and-spikes", + "specPath" : "docs/specs/ADR-046-feasibility-and-spikes.md", + "validation" : "SPIKE-13 metrics (1)-(7); SPIKE-14 metrics (1)-(3), zero-tolerance on legacy-file access", + "workItemId" : "ADR046-feasibility-009" + }, + { + "currentSource" : "None (this is a pure integration of the other nine work items' fixtures)", + "dataMigration" : "None — full d2b 3.0 reset; no prior state to migrate", + "dependencyOwner" : "`ADR046-feasibility-001` through `ADR046-feasibility-009` (integrates their fakes; must run last)", + "destination" : "`proofs/e2e-composition-spike/`", + "detailedDesign" : "Implements SPIKE-15: the three representative compositions (local/cloud-hypervisor, cloud/azure, interaction/shell-terminal-or-wayland), wired from the fakes built by `-001` through `-009`, plus the combined 3-Zone aggregate RSS measurement", + "integration" : "Depends on and imports the fake shapes from `proofs/redb-resource-store-spike/`, `proofs/process-fastlaunch-spike/`, `proofs/effectport-async-spike/`, `proofs/provider-packaging-spike/`, `proofs/bus-routing-noise-spike/`, `proofs/transport-opaque-streams-spike/`, `proofs/credential-kk-e2e-spike/`, `proofs/provider-state-export-spike/`, `proofs/volume-policy-spike/`, and `proofs/process-provider-conformance-spike/`", + "removalProof" : "Deleted once the real integration test suites named by the individual Provider dossiers (`integration/` per D059) collectively reproduce all three compositions against real, non-fake Zone/store/bus/broker code", + "reuseAction" : "adapt", + "reuseSource" : null, + "specId" : "ADR-046-feasibility-and-spikes", + "specPath" : "docs/specs/ADR-046-feasibility-and-spikes.md", + "validation" : "SPIKE-15 metrics (1)-(4) across all three compositions", + "workItemId" : "ADR046-feasibility-010" + }, + { + "currentSource" : "this codebase's ad hoc `tests/tools/` timing logs (`d2b-static-timing.$$/`), which are not a candidate-bound, reference-runner-recorded ledger", + "dataMigration" : "None — full d2b 3.0 reset; no prior state to migrate", + "dependencyOwner" : "`ADR046-delivery-007`; delivery/test-tooling integrator", + "destination" : "`proofs/test-runtime-budget-spike/`; the committed baseline ledger consumed by `ADR046-delivery-007`", + "detailedDesign" : "Establishes the D094 measurement baseline: records the reference runner class, repetition count, and per-test/crate/shard p95 for a representative hermetic crate; proves the §10.16 budgets (individual normal test p95 ≤50 ms, per-crate `--lib --tests` ≤2 s, Layer-1 hermetic shard ≤60 s) are met on the reference runner and that an injected slow/sleeping test is detected as a regression", + "integration" : "Output ledger shape is consumed by the runtime ledger/timing gate; establishes the historical threshold seed", + "removalProof" : "Deleted once `ADR046-delivery-007`'s in-tree ledger/timing gate reproduces the baseline against the real crate set", + "reuseAction" : "adapt", + "reuseSource" : "existing `libtest --format=json` timing output and `xtask` (no new test framework)", + "specId" : "ADR-046-feasibility-and-spikes", + "specPath" : "docs/specs/ADR-046-feasibility-and-spikes.md", + "validation" : "The representative crate meets every budget; a synthetic slow/sleep/process/network test is flagged; cold compile time is recorded on a separate line and excluded from the execution budgets", + "workItemId" : "ADR046-feasibility-011" + }, + { + "currentSource" : "No v3 source; `packages/d2b-host/src/gpu_argv.rs` and `packages/d2b-host/src/video_argv.rs` (implemented-and-reachable) provide argv generators", + "dataMigration" : "None — full d2b 3.0 reset; no prior state to migrate", + "dependencyOwner" : "`ADR046-resources-device` accepted; `ADR046-provider-model-and-packaging` accepted; workspace root must add crate", + "destination" : "`packages/d2b-provider-device-gpu/` with `src/`, `tests/`, `integration/`, `README.md`; add to workspace `Cargo.toml` members list (alphanumerically sorted)", + "detailedDesign" : "Crate scaffold: `Cargo.toml` with `d2b-host`, `d2b-contracts`, `d2b-provider-toolkit`, `d2b-core` dependencies; `lib.rs` exporting controller binary entry points; `error.rs` with `DeviceGpuError` closed-set enum; placeholder `controller.rs` Primary reuse disposition: `extract`. Preserved source-plan detail: `extract` both argv files into `d2b-provider-device-gpu/src/argv.rs` as re-exports; do not copy logic.", + "integration" : "Workspace policy test must pass; crate must build; `src/`, `tests/`, `integration/`, `README.md` must exist", + "removalProof" : "N/A (new crate)", + "reuseAction" : "extract", + "reuseSource" : "`packages/d2b-host/src/gpu_argv.rs` (baseline `b5ddbed`), `packages/d2b-host/src/video_argv.rs` (baseline `b5ddbed`)", + "specId" : "ADR-046-provider-device-gpu", + "specPath" : "docs/specs/providers/ADR-046-provider-device-gpu.md", + "validation" : "`cargo build -p d2b-provider-device-gpu`; workspace policy crate-layout check passes", + "workItemId" : "ADR046-gpu-001" + }, + { + "currentSource" : "`packages/d2bd/src/usbip_state_machine.rs` (implemented-and-reachable) as reconcile loop pattern reference. GPU/video reconcile state is `ADR-only`.", + "dataMigration" : "None — full d2b 3.0 reset; no prior state to migrate", + "dependencyOwner" : "ADR046-gpu-001; `ADR-046-resource-reconciliation` implementation present; Provider toolkit `ResourceClient` available", + "destination" : "`packages/d2b-provider-device-gpu/src/controller.rs`", + "detailedDesign" : "Five triggers: `spec-generation-changed`, `deletion-requested`, `dependency-changed`, `scheduled-observe`, `owned-resource-changed`. Each trigger handler writes optimistic `ResourceMutationBatch`. Status writer in `status.rs`. Async watch task + per-resource reconcile tasks. Independent resources in parallel. Primary reuse disposition: `adapt`. Preserved source-plan detail: `adapt` — implement the five-trigger reconcile loop using Provider toolkit async reconciler.", + "integration" : "Resource API (ADR046 store) must be present; fake ResourceClient available from Provider toolkit; `tests/combined_reconcile.rs` validates trigger dispatch", + "removalProof" : "Current ProcessRole::Gpu/Video/GpuRenderNode retained until this test passes; see ProcessRole disposition table", + "reuseAction" : "adapt", + "reuseSource" : "Pattern only: `packages/d2bd/src/usbip_state_machine.rs` (baseline). No code copy.", + "specId" : "ADR-046-provider-device-gpu", + "specPath" : "docs/specs/providers/ADR-046-provider-device-gpu.md", + "validation" : "`cargo test -p d2b-provider-device-gpu --test combined_reconcile`; all five trigger handlers must reach their expected output state", + "workItemId" : "ADR046-gpu-002" + }, + { + "currentSource" : "`nixos-modules/assertions.nix` x86_64-linux guard; `packages/d2b-core/src/processes.rs` ProcessRole::Gpu/GpuRenderNode; no existing sysfs probe module", + "dataMigration" : "None — full d2b 3.0 reset; no prior state to migrate", + "dependencyOwner" : "ADR046-gpu-002", + "destination" : "`packages/d2b-provider-device-gpu/src/probe.rs`", + "detailedDesign" : "Call `GpuEffectPort::probe_drm_device(selector)` on each `scheduled-observe` trigger; the effect port resolves device presence against the trusted device table and returns a presence/health result without exposing raw sysfs or device paths to the controller. Three-strike failure counter; `observe_interval_secs` (10–60, default 30); emit `DevicePresent` condition and update `lastProbedAt`.", + "integration" : "`scheduled-observe` trigger from reconcile loop calls `probe::check_drm_device`", + "removalProof" : "N/A (new module)", + "reuseAction" : "create", + "reuseSource" : null, + "specId" : "ADR-046-provider-device-gpu", + "specPath" : "docs/specs/providers/ADR-046-provider-device-gpu.md", + "validation" : "`tests/conformance.rs` contains probe-mock path; `cargo test` passes", + "workItemId" : "ADR046-gpu-003" + }, + { + "currentSource" : "`packages/d2b-core/src/bundle_resolver.rs` `validate_graphics_vm_invariants` (assertion guard) — `ADR-only` for resource-level arbitration", + "dataMigration" : "None — full d2b 3.0 reset; no prior state to migrate", + "dependencyOwner" : "ADR046-gpu-002", + "destination" : "`packages/d2b-provider-device-gpu/src/arbitration.rs`", + "detailedDesign" : "On `spec-generation-changed` and each new claim: check `arbitration` vs `maxConcurrentClaims` vs current `holderRefs` length. Exclusive: reject any second claim with `ClaimConflict` condition, set requesting Device phase `Degraded`. Shared render-node: accept up to `maxConcurrentClaims`. Admission: `shared + renderNodeOnly=false` fails with `shared-arbitration-requires-render-node-only`.", + "integration" : "Tested by `tests/arbitration_conflict.rs`; integration fixture `render_node_shared/`", + "removalProof" : "N/A (new module)", + "reuseAction" : "create", + "reuseSource" : null, + "specId" : "ADR-046-provider-device-gpu", + "specPath" : "docs/specs/providers/ADR-046-provider-device-gpu.md", + "validation" : "`cargo test -p d2b-provider-device-gpu --test arbitration_conflict`; `cargo test -p d2b-provider-device-gpu --test render_node_enforcement`", + "workItemId" : "ADR046-gpu-004" + }, + { + "currentSource" : "`packages/d2b-host/src/gpu_argv.rs` (implemented-and-reachable); `packages/d2b-core/src/bundle_resolver.rs` lines 1888–1894 (device token set); `packages/d2b-core/src/processes.rs` `ProcessRole::Gpu`, `ProcessRole::GpuRenderNode` (implemented-and-reachable); `nixos-modules/minijail-profiles.nix` gpu/gpu-render-node profiles (implemented-and-reachable)", + "dataMigration" : "None — full d2b 3.0 reset; no prior state to migrate", + "dependencyOwner" : "ADR046-gpu-002; `ADR046-components-processes-and-sandbox` (Provider/system-minijail present and able to handle Process resources with `gpu-worker`/`render-node-worker` templates)", + "destination" : "`packages/d2b-provider-device-gpu/src/worker_gpu.rs`", + "detailedDesign" : "Build and commit `Process` resource record with `template: gpu-worker` or `template: render-node-worker`; set `sandbox.seccompClass` (`w1-gpu` or `w1-gpu-render-node`), `sandbox.userNamespace: {mappingClass: process-principal-root}` (uid/gid resolved privately by core from signed worker template — controller does NOT write numeric values), `sandbox.namespaceClasses`, `sandbox.capabilityClasses=[]`, `sandbox.startRoot=false`; set `deviceUsage[{deviceRef,access,purpose}]`, `networkUsage: null`, `endpoints[{name,transport,purpose}]`, `budget` (including `pids` and `fds` bounded limits), `readiness` (with `class`, `initialDelay`, `timeout`, `failureThreshold`, `successThreshold`), and `restartPolicy` (with `class`, `backoffBase`, `backoffMax`, `backoffMultiplier`, `maxRestarts`, `resetAfter`). Provider/system-minijail validates and resolves the LaunchTicket and sends effect requests via `MinijailProcessEffectPort`; the core EffectPort adapter routes them to the **privileged broker** which performs `SpawnRunner`, `OpenDevice`, `clone3`, `uid_map`/`gid_map` writes, and fd transfer — the device-gpu controller does not have execution authority or fd access. `crossDomainTrusted` gating: the signed descriptor is static; `crossDomainTrusted` is projected from the Device setting into the LaunchTicket by Provider/system-minijail, which omits `GpuContextType::CrossDomain` from runtime argv when false. Primary reuse disposition: `adapt`. Preserved source-plan detail: `extract` argv builder logic into `argv.rs` as re-export from `d2b-host` (used by Provider/system-minijail at LaunchTicket resolution time; the signed component descriptor is static and is not rewritten per Device); `adapt` device allowlist token set from `bundle_resolver.rs` into `worker_gpu.rs` `GPU_DEVICE_ALLOWLIST` constant for `deviceUsage` population.", + "integration" : "`integration/gpu_worker_start/`; `integration/render_node_shared/`; `packages/d2b-contract-tests/tests/minijail_gpu.rs` (reused existing test)", + "removalProof" : "`ProcessRole::Gpu` and `ProcessRole::GpuRenderNode` removed from `processes.rs` only after both integration tests pass and the ProcessRole disposition contract test confirms zero remaining references", + "reuseAction" : "adapt", + "reuseSource" : "`packages/d2b-host/src/gpu_argv.rs` (baseline `b5ddbed`): `GpuArgvInput`, `GpuParams`, `GpuContextType`, `GpuDisplayConfig`; `packages/d2b-core/src/bundle_resolver.rs` device token constant comment", + "specId" : "ADR-046-provider-device-gpu", + "specPath" : "docs/specs/providers/ADR-046-provider-device-gpu.md", + "validation" : "`cargo test -p d2b-provider-device-gpu`; `cargo test -p d2b-contract-tests --test minijail_gpu` continues to pass", + "workItemId" : "ADR046-gpu-005" + }, + { + "currentSource" : "`packages/d2b-host/src/video_argv.rs` (implemented-and-reachable): `VideoArgvInput`, `VideoBackend`, `wire_contract_snapshot()`; `packages/d2b-contract-tests/tests/video_binary_contract.rs` (implemented-and-reachable); `packages/d2b-contract-tests/tests/minijail_swtpm_video.rs` video section (implemented-and-reachable); `nixos-modules/minijail-profiles.nix` video profile (implemented-and-reachable)", + "dataMigration" : "None — full d2b 3.0 reset; no prior state to migrate", + "dependencyOwner" : "ADR046-gpu-005 (video depends on GPU Process being Ready)", + "destination" : "`packages/d2b-provider-device-gpu/src/worker_video.rs`, `tests/wire_constant_snapshot.rs`", + "detailedDesign" : "Controller creates `Process/device--video` only after `GpuWorkerReady=True`. `worker_video.rs` builds `VideoArgvInput` from resolved device spec and signed descriptor binary path. Validates `wire_contract_snapshot()` matches committed golden at startup; fails closed if mismatch (error `device-wire-contract-mismatch`). NVIDIA device gating: include `nvidia-ctl`, `nvidia-device`, `nvidia-uvm` tokens in `deviceUsage[]` entries only when `videoNvidiaDecode=true`; the **privileged broker** opens the fds when executing the effect request from the core EffectPort adapter. Distinct allocator-assigned principal enforced by LaunchTicket (internal invariant; not expressed in the resource spec); `template: video-worker` descriptor declares no Wayland/audio endpoint capability. `sandbox.seccompClass: w1-video`; `sandbox.namespaceClasses` includes `pid`; `userNamespace: null` (explicit, tested invariant). Primary reuse disposition: `adapt`. Preserved source-plan detail: `extract` argv generator (re-export from `argv.rs`); `copy-unchanged` wire-contract constants into `tests/wire_constant_snapshot.rs` golden comparison.", + "integration" : "`integration/video_dependency/`; `packages/d2b-contract-tests/tests/video_binary_contract.rs` (reused); `packages/d2b-contract-tests/tests/minijail_swtpm_video.rs` video section (reused)", + "removalProof" : "`ProcessRole::Video` removed from `processes.rs` only after `integration/video_dependency/` passes and the video Process reaches `Ready` in a live Zone", + "reuseAction" : "adapt", + "reuseSource" : "`packages/d2b-host/src/video_argv.rs` (baseline `b5ddbed`): argv generator, wire-contract constants, `wire_contract_snapshot()`", + "specId" : "ADR-046-provider-device-gpu", + "specPath" : "docs/specs/providers/ADR-046-provider-device-gpu.md", + "validation" : "`cargo test -p d2b-provider-device-gpu --test video_dependency`; `cargo test -p d2b-provider-device-gpu --test wire_constant_snapshot`; `cargo test -p d2b-contract-tests --test video_binary_contract` continues to pass", + "workItemId" : "ADR046-gpu-006" + }, + { + "currentSource" : "`nixos-modules/options-realms-workloads.nix` `d2b.vms..graphics.*` options (generated-or-eval-contract); `nixos-modules/assertions.nix` graphics assertions; `nixos-modules/components/graphics.nix` (host-side); `nixos-modules/components/video/guest.nix` (guest-side)", + "dataMigration" : "Consumer config migration guide: replace `d2b.vms..graphics.enable = true` with a Device resource declaration. Old options emit deprecation warnings, not hard failures, during the transition window.", + "dependencyOwner" : "ADR046-gpu-001; ADR 0046 Zone Nix emitter wired; `ADR-046-nix-configuration` Nix emitter present", + "destination" : "`nixos-modules/assertions.nix` (new GPU Device eval assertions); `tests/unit/nix/cases/device-gpu-eval.nix` (new Nix eval case); committed settings schema `docs/reference/schemas/v3/providers/device-gpu.settings.json`", + "detailedDesign" : "Eval assertions as documented in § Nix configuration / Eval-time assertions. Canonical JSON golden as documented. Settings schema drift gate via `make test-drift`. `d2b.vms..graphics.*` options are deprecated (emit deprecation warning) until a transition generation removes them; they are not removed in the same commit that adds the Device spec option. Primary reuse disposition: `adapt`. Preserved source-plan detail: `adapt` — map old `d2b.vms..graphics.*` fields to `d2b.zones..resources.` Device spec settings fields; add eval assertions.", + "integration" : "`nix flake check`; `tests/unit/nix/cases/device-gpu-eval.nix`; `make test-drift`", + "removalProof" : "`d2b.vms..graphics.*` options removed only after migration guide ships and the deprecation warning has been live for one minor release", + "reuseAction" : "adapt", + "reuseSource" : "Settings schema field names/defaults/bounds from `nixos-modules/options-realms-workloads.nix` options documentation", + "specId" : "ADR-046-provider-device-gpu", + "specPath" : "docs/specs/providers/ADR-046-provider-device-gpu.md", + "validation" : "`nix-unit tests/unit/nix/cases/device-gpu-eval.nix`; `make test-drift`; `make test-flake`", + "workItemId" : "ADR046-gpu-007" + }, + { + "currentSource" : "None — net-new v3 work; no pre-ADR45 baseline equivalent", + "dataMigration" : "None — no Provider state Volume exists to migrate.", + "dependencyOwner" : "ADR046-gpu-001; D087 status-first state model present in the foundational ADR-046 specs", + "destination" : "`packages/d2b-provider-device-gpu/` component descriptor; controller/status tests", + "detailedDesign" : "Do **not** declare a controller Provider state Volume. The device-gpu component descriptor declares an empty ProviderStateSet; controller and worker Process templates contain no `/state` mount. Bounded non-secret operational state is published to Device/Provider status and the core Operation ledger. GPU has no Device-payload Volume; render-node access remains a Device attachment resolved by LaunchTicket and broker policy. Primary reuse disposition: `create`. Preserved source-plan detail: `new` — status-first state assertions in the component descriptor and controller tests.", + "integration" : "`tests/status_state.rs`; `integration/gpu_worker_start/` verifies controller startup is gated by resource dependencies and status writer authority, not by a Provider state Volume", + "removalProof" : "`StorageRoot`/`StoragePathSpec` lifecycle tracking entries for GPU/video roles in `d2b-core/src/storage.rs` removed after Device/Process status-first lifecycle and restart-adoption integration tests pass in a live Zone", + "reuseAction" : "create", + "reuseSource" : null, + "specId" : "ADR-046-provider-device-gpu", + "specPath" : "docs/specs/providers/ADR-046-provider-device-gpu.md", + "validation" : "`cargo test -p d2b-provider-device-gpu --test status_state`; component descriptor golden has no Provider state Volume declaration; controller Process template has no `/state` mount; ProviderStateSet query is empty; status/core-ledger fields carry bounded operational observations", + "workItemId" : "ADR046-gpu-008" + }, + { + "currentSource" : "None; new file", + "dataMigration" : "None — full d2b 3.0 reset; no prior state to migrate", + "dependencyOwner" : "ADR046-gpu-001", + "destination" : "`packages/d2b-provider-device-gpu/README.md`", + "detailedDesign" : "Must include: Provider identity, supported ResourceTypes, controller/service/worker binary descriptions, placement (Host, system domain), dependencies (system-minijail, volume-local, observability-otel), RBAC roles, security model summary, state/telemetry contract, build command (`cargo build -p d2b-provider-device-gpu`), test commands (`cargo test -p d2b-provider-device-gpu`), integration command (`make test-integration`), hardware test note (see `integration/README.md`), standalone-repository consumption stub.", + "integration" : "Workspace policy checks for `README.md` presence", + "removalProof" : "N/A (new file)", + "reuseAction" : "create", + "reuseSource" : null, + "specId" : "ADR-046-provider-device-gpu", + "specPath" : "docs/specs/providers/ADR-046-provider-device-gpu.md", + "validation" : "`make test-policy` (workspace crate layout policy check)", + "workItemId" : "ADR046-gpu-009" + }, + { + "currentSource" : "`packages/d2b-realm-core/src/ids.rs`, `realm.rs`, `target.rs`, `workload.rs`", + "dataMigration" : "Destructive d2b 3.0 reset; no RealmRef parser compatibility", + "dependencyOwner" : "W0 shared contract root; `d2b-contracts`", + "destination" : "`packages/d2b-contracts/src/v3/identity.rs`, `packages/d2b-contracts/src/v3/resource_ref.rs`", + "detailedDesign" : "Add ZoneId, ResourceTypeName, ResourceName, ResourceUid, ResourceRef, generation/revision newtypes, exact parsing/serde/Debug/redaction, and golden vectors Primary reuse disposition: `adapt`. Preserved source-plan detail: extract and adapt.", + "integration" : "Resource API/store/controllers/SDK/Nix import only these canonical types", + "removalProof" : "Old public Realm target types removed only after all v3 callers consume Zone/ResourceRef", + "reuseAction" : "adapt", + "reuseSource" : null, + "specId" : "ADR-046-terminology-and-identities", + "specPath" : "docs/specs/ADR-046-terminology-and-identities.md", + "validation" : "Rust property/vector tests; pure-Nix vector parity; malformed/collision/UID-recreate tests", + "workItemId" : "ADR046-identities-001" + }, + { + "currentSource" : "`nixos-modules/options-realms.nix`, `options-realms-workloads.nix`, `index.nix`", + "dataMigration" : "Full reset and new Zone declarations", + "dependencyOwner" : "ADR046-identities-001; Nix integrator", + "destination" : "`nixos-modules/options-zones.nix`, `nixos-modules/resources.nix`, `nixos-modules/index.nix`", + "detailedDesign" : "Validate Zone names, ResourceTypes/names/refs, shared Host/Guest ExecutionPolicy, and canonical sorted resource identities", + "integration" : "Nix resource objects serialize exactly the Rust contract", + "removalProof" : "Realm-facing declarations removed only in the reset/purge wave", + "reuseAction" : "adapt", + "reuseSource" : null, + "specId" : "ADR-046-terminology-and-identities", + "specPath" : "docs/specs/ADR-046-terminology-and-identities.md", + "validation" : "nix-unit vectors and rendered contract tests", + "workItemId" : "ADR046-identities-002" + }, + { + "currentSource" : "`d2b-core/src/minijail_profile.rs`; `d2b-core/src/processes.rs` (NamespaceSet, MountPolicy, CgroupPlacement); `d2b-priv-broker/src/ops/spawn_runner.rs`", + "dataMigration" : "Full reset; current `MinijailProfile` not import-compatible with v3 SandboxSpec", + "dependencyOwner" : "`ADR046-process-001` (common spec/status types); `ADR046-provider-001` (toolkit/contracts); system-minijail Provider owner", + "destination" : "`packages/d2b-provider-system-minijail/src/sandbox_compiler.rs`", + "detailedDesign" : "Accept `SandboxSpec` from common contracts; compile NamespaceClass/CapabilityClass/SeccompClass/UserNamespaceSpec/mount/environment/rlimit/umask into a versioned `CompiledSandboxPlan`; compute `sandboxRevisionDigest`; all rejection conditions from §12.1; no raw bitmask/BPF/argv/path in any output type; golden round-trip test vectors Primary reuse disposition: `adapt`. Preserved source-plan detail: EXTRACT/ADAPT.", + "integration" : "LaunchTicket builder (ADR046-minijail-002); effect port integration (ADR046-minijail-003)", + "removalProof" : "Current `MinijailProfile`/`NamespaceSet` types in `d2b-core` removed after all callers migrate to SandboxSpec", + "reuseAction" : "adapt", + "reuseSource" : null, + "specId" : "ADR-046-provider-system-minijail", + "specPath" : "docs/specs/providers/ADR-046-provider-system-minijail.md", + "validation" : "`tests/sandbox_compilation.rs`; `tests/schema.rs`; golden vectors", + "workItemId" : "ADR046-minijail-001" + }, + { + "currentSource" : "`d2b-core/src/process_builder.rs`; `d2bd/src/supervisor/*.rs` (ticket generation)", + "dataMigration" : "None — full d2b 3.0 reset; no prior state to migrate", + "dependencyOwner" : "ADR046-minijail-001; common `LaunchTicket` contract", + "destination" : "`packages/d2b-provider-system-minijail/src/launch.rs`", + "detailedDesign" : "LaunchTicket construction with compiled sandbox/budget/mount digests; ticket verification on ProviderSupervisor receipt; `d2b.supervisor.v3/IssueLaunchTicket` service call; expired/revoked/malformed ticket rejection", + "integration" : "`ProviderSupervisor` local adapter; minijail controller (ADR046-minijail-005)", + "removalProof" : "Current `process_builder.rs` removed after parity", + "reuseAction" : "adapt", + "reuseSource" : null, + "specId" : "ADR-046-provider-system-minijail", + "specPath" : "docs/specs/providers/ADR-046-provider-system-minijail.md", + "validation" : "`tests/lifecycle.rs`; `tests/fault_injection.rs`; `tests/fast_path.rs`", + "workItemId" : "ADR046-minijail-002" + }, + { + "currentSource" : "`d2b-priv-broker/src/ops/spawn_runner.rs`; `d2b-priv-broker/src/sys.rs` (`clone3_spawn_runner`, user namespace setup)", + "dataMigration" : "None — full d2b 3.0 reset; no prior state to migrate", + "dependencyOwner" : "ADR046-minijail-001; broker integration owner", + "destination" : "Broker-side: `d2b-priv-broker` retains `SpawnRunner` op, invoked by the `MinijailProcessEffectPort` implementation owned by core/ProviderSupervisor; Provider-side: `packages/d2b-provider-system-minijail/src/launch.rs` calls `MinijailProcessEffectPort` with opaque Process/LaunchTicket/profile IDs; `user_ns.rs` implements the user namespace pre-establishment protocol", + "detailedDesign" : "Linux ≥5.14 and delegated-leaf `cgroup.kill` platform gate; `clone3(CLONE_PIDFD | CLONE_INTO_CGROUP)` with pre-declared cgroup leaf FD; broker retained as child parent and sole `waitid(P_PIDFD)`/reap/exit-status owner; verified duplicate returned privately to ProviderSupervisor for poll/readiness and exact-main `pidfd_send_signal`; anchored `cgroup.kill` write for unambiguous intentional teardown; user namespace pre-establishment sequence (§7.7) when `userNamespace` set; host UID 0 rejection; parent name-to-inode re-validation; zero-host-capability invariant (ADR 0021); `MinijailProcessEffectPort` privately maps opaque IDs to SpawnRunner/OpenDevice/clone3/uid-map/FD effects; Provider crate imports no broker service/client/DTO", + "integration" : "ADR046-minijail-002 (LaunchTicket); real cgroup/broker fixture in `integration/clone3_pidfd/` and `integration/user_namespace/`", + "removalProof" : "Old broker `SpawnRunner` direct-caller paths in `d2bd` removed after system-minijail Provider integration", + "reuseAction" : "adapt", + "reuseSource" : null, + "specId" : "ADR-046-provider-system-minijail", + "specPath" : "docs/specs/providers/ADR-046-provider-system-minijail.md", + "validation" : "`tests/fault_injection.rs`; `tests/platform_gate.rs`; `tests/broker_wait_contract.rs`; `tests/cgroup_kill_finalize.rs`; `integration/clone3_pidfd/`; `integration/user_namespace/`; `integration/broker_parent_reap/`; `integration/cgroup_kill_subtree/`; `integration/kernel_platform_gate/`", + "workItemId" : "ADR046-minijail-003" + }, + { + "currentSource" : "`d2bd/src/supervisor/pidfd_table.rs` (PidfdTable, WaitTermination, BrokerReapLog)", + "dataMigration" : "None — full d2b 3.0 reset; no prior state to migrate", + "dependencyOwner" : "ADR046-minijail-003; wait/pidfd owner", + "destination" : "Broker-side parent wait/reap and typed terminal relay in `packages/d2b-priv-broker/src/`; non-parent observation/status consumption in `packages/d2b-provider-system-minijail/src/{pidfd,wait}.rs`", + "detailedDesign" : "Broker that called `clone3` alone calls `waitid(P_PIDFD)`, collects exit status, and reaps exactly once; ProviderSupervisor `AsyncFd` readability is a hint only and never a wait/status source; controller consumes the identity-bound broker relay and holds no raw pidfd; ProviderSupervisor duplicate reacquisition is dispatched through a bounded blocking adapter with explicit timeout; pidfd never serialized; verified broker/ProviderSupervisor holder retains exact-main `pidfd_send_signal`; no PID/PGID fallback; graceful deadline followed by mandatory anchored leaf `cgroup.kill`; empty-leaf proof before rmdir; exit class classification (clean-exit/crash/signal/timeout/unknown) Primary reuse disposition: `adapt`. Preserved source-plan detail: EXTRACT/ADAPT.", + "integration" : "Controller restart → adoption (ADR046-minijail-005); finalize (§8.6)", + "removalProof" : "Old `PidfdTable` in `d2bd` supervisor removed after Provider integration", + "reuseAction" : "adapt", + "reuseSource" : null, + "specId" : "ADR-046-provider-system-minijail", + "specPath" : "docs/specs/providers/ADR-046-provider-system-minijail.md", + "validation" : "`tests/lifecycle.rs`; `tests/broker_wait_contract.rs` (only clone3 parent calls waitid/reaps; poll readability cannot supply status); `tests/cgroup_kill_finalize.rs` (setsid descendant and PGID reuse); `tests/redaction.rs` (PID never in log/status/audit); `tests/blocking_adapter.rs` (duplicate/status relay via adapter; timeout → error)", + "workItemId" : "ADR046-minijail-004" + }, + { + "currentSource" : "`d2bd/src/supervisor/*.rs` (DagExecutor, NodeOutcome); `d2bd/src/supervisor/pidfd_table.rs`; `d2b-realm-core/src/allocator_engine.rs` (adoption/identity concepts)", + "dataMigration" : "Full reset; current DAG/role snapshot import not required", + "dependencyOwner" : "All of ADR046-minijail-001 through ADR046-minijail-004; ComponentSession/d2b-bus (ADR046-session-001, ADR046-session-003); bootstrap authz", + "destination" : "`packages/d2b-provider-system-minijail/src/` — controller binary entry point; reconcile loop; adoption; quarantine; bootstrap authz; health/status; restart; finalize", + "detailedDesign" : "Full Process/EphemeralProcess reconcile algorithm (§8); fast path ≤5/≤20 ms gates; spawn via `MinijailProcessEffectPort` (opaque IDs; no broker DTO imported); adoption algorithm (§8.5) with `/proc` reads, cgroup enumeration, and original-broker-parent verification via bounded blocking adapters; quarantine on ambiguity; quarantine reuse blocked until externally established process-absence proof or full Zone reset; no signal or cgroup.kill write to quarantined/ambiguous identity; restart/backoff driven only by broker-relayed terminal status; finalize (§8.6) with exact-main SIGTERM, bounded grace, mandatory cgroup.kill, broker wait/reap, empty-leaf proof, and no PGID ownership; EphemeralProcess continuation recovery (§9); bootstrap authz scope (§3); post-bootstrap RBAC; metric label closed-set enforcement (no `zone` label); controller writes status only on Process/EphemeralProcess resources; Provider resource status aggregated by core; the controller declares no Provider state Volume and mounts none — its bounded non-secret operational state lives in `status`/the core Operation ledger (§5.1, D087) and running units are re-adopted from cgroup leaves + fresh pidfds on restart", + "integration" : "Zone runtime startup (bootstrap); all v3 ResourceClient/bus/session paths", + "removalProof" : "Current `d2bd` DAG executor and direct spawn paths removed only after all ProcessRoles in the role-disposition table (ADR-046-components-processes-and-sandbox, §Representative baseline mapping) reach parity under system-minijail or system-systemd", + "reuseAction" : "adapt", + "reuseSource" : null, + "specId" : "ADR-046-provider-system-minijail", + "specPath" : "docs/specs/providers/ADR-046-provider-system-minijail.md", + "validation" : "`tests/lifecycle.rs`; `tests/ephemeral_lifecycle.rs`; `tests/conformance.rs`; `tests/adoption_quarantine.rs`; `tests/broker_wait_contract.rs`; `tests/cgroup_kill_finalize.rs`; `tests/platform_gate.rs`; `tests/bootstrap_authz.rs`; `tests/fast_path.rs`; `tests/blocking_adapter.rs`; `integration/adoption_restart/`; `integration/quarantine_scenario/`; `integration/broker_parent_reap/`; `integration/cgroup_kill_subtree/`; `integration/kernel_platform_gate/`; `integration/latency_gate/`; shared conformance suite in `d2b-process-conformance`", + "workItemId" : "ADR046-minijail-005" + }, + { + "currentSource" : "`nixos-modules/processes-json.nix`; `nixos-modules/minijail-profiles.nix`; `packages/d2b-contract-tests/tests/policy_observability.rs`", + "dataMigration" : "Current `nixos-modules/processes-json.nix` and minijail profile Nix removed at cutover", + "dependencyOwner" : "ADR046-minijail-005; Nix integrator; test infrastructure owner", + "destination" : "`nixos-modules/` — v3 Nix `Process`/`EphemeralProcess` resource authoring; Provider catalog entry; `docs/reference/schemas/v3/Process.json`; `docs/reference/schemas/v3/EphemeralProcess.json`; `make test-drift` schema drift gate", + "detailedDesign" : "Nix module accepts `d2b.zones..resources.` with `type = \"Process\"` or `\"EphemeralProcess\"`; eval-time validation rules (§16.4); build-time JSON validation (§16.5); artifact catalog integration; cleanup contract tests (§16.5)", + "integration" : "`d2b.artifacts` catalog; Zone bundle emission; `make test-drift`", + "removalProof" : "`processes-json.nix`, `minijail-profiles.nix`, and `programs-json.nix` removed after v3 Nix parity", + "reuseAction" : "adapt", + "reuseSource" : null, + "specId" : "ADR-046-provider-system-minijail", + "specPath" : "docs/specs/providers/ADR-046-provider-system-minijail.md", + "validation" : "`nix-unit` eval cases for every validation rule; schema drift gate; `tests/schema.rs`", + "workItemId" : "ADR046-minijail-006" + }, + { + "currentSource" : "`packages/d2b-core/src/host.rs` lines 290–520 (`NetEnv`, `IfName`, `ExternalNetworkPolicy`, `NftablesModel`, `BridgePortFlags`, `TapRole`, `Ipv6SysctlEntry`, `IfNameMapping` lines 242–256; **also** `VmRuntimeRow` lines 155–167 with `tap`/`bridge`/`net_vm`/`env` fields — attachment status precursors); `packages/d2b-core/src/processes.rs` lines 98–141 (`ProcessNetworkInterface`, `ProcessNetworkInterfaceType`, `ProcessMacvtapInterface` — current VMM runner network interface DTOs; these are per-Guest VMM fields, not Network-level fields, and migrate to Guest spec under `Provider/runtime-cloud-hypervisor`); `packages/d2b-contracts/src/broker_wire.rs` (authoritative broker op list; network-relevant: `ApplyNftables`, `ApplyNmUnmanaged`, `ApplyRoute`, `ApplySysctl`, `SetBridgePortFlags`, `UpdateHostsFile`, `SeedDnsmasqLease`, `CreatePersistentTap`, `CreateTapFd`); `nixos-modules/lib.nix` lines 396–460 (`subnetIp`, `subnetMask`, `mkMac`, `cidrOverlaps`)", + "dataMigration" : "Full d2b 3.0 reset; no env→Network import", + "dependencyOwner" : "W0 shared contract root; `d2b-contracts`", + "destination" : "`packages/d2b-contracts/src/v3/network.rs`: NetworkSpec, NetworkStatus, AttachmentSpec, AttachmentStatus, ExternalAttachmentSpec, ExternalAttachmentStatus, PortForwardSpec, NetworkConditionType; `packages/d2b-contracts/src/v3/ifname.rs`: IfName newtype, derivation, collision detection (extracted from `d2b-host/src/ifname.rs`). Also defines `User/net-local-controller` as a proper Resource with explicit lifecycle: `Provider/network-local`'s Nix package/module provisions the reserved `net-local-controller` OS account with a private fixed UID/GID in Host prerequisites and in the generic net-VM nixos-system artifact (same account, same UID/GID inside the Guest); the network-local controller creates and owns the User Resource (`spec.osUsername: net-local-controller`, `ownerRef: Provider/network-local`, `managedBy: controller`); `Provider/system-core` verifies the account via NSS lookup and reconciles the User Resource to Ready — it does not provision the OS account. No numeric UID/GID enters any ResourceSpec field, authz check, or audit record; `User.status` MAY carry diagnostic `uid`/`gid` values discovered by NSS lookup, but those are informational only and are never authorization inputs. The network-local controller waits for `User/net-local-controller` to reach `Ready` before creating any config Volume (reconcile precondition, not a bootstrap side effect).", + "detailedDesign" : "Strict ResourceEnvelope with Network-specific spec/status. IfName newtype: IFNAMSIZ-1 validated, FNV-1a 64-bit derivation, base32 Crockford, 8-char suffix, bridge/tap role prefixes, detect_collisions over IfNameMapping slice. cidrOverlaps: pure Rust IPv4 arithmetic, same algorithm as lib.nix. NetworkSpec validators: /24 lanCidr with .0 base, /30 uplinkCidr, unique attachment indices 2–250, default hostBlocklist enforcement. Primary reuse disposition: `adapt`. Preserved source-plan detail: extract and adapt.", + "integration" : "Provider dossiers, Nix resource compiler, resource store/API bind these canonical types", + "removalProof" : "Old `d2b_core::host::NetEnv` and related types removed only after v3 resource API consumers use `d2b_contracts::v3::network` types", + "reuseAction" : "adapt", + "reuseSource" : null, + "specId" : "ADR-046-resources-network", + "specPath" : "docs/specs/ADR-046-resources-network.md", + "validation" : "Golden JSON/CBOR vectors; CIDR overlap property tests; IfName collision and derivation determinism tests; default hostBlocklist enforcement; attachment index uniqueness; `User/net-local-controller` User resource lifecycle/readiness test: controller creates User Resource with `spec.osUsername = \"net-local-controller\"` (`ownerRef: Provider/network-local`); controller waits for User resource to reach `Ready` before proceeding; controller aborts with `ConfigVolumeReady=False/user-not-ready` if User resource is not Ready; verifies no numeric UID/GID appears in the Resource spec, authz check, or audit record; verifies that any diagnostic `uid`/`gid` in `User.status` is never used as an authorization input", + "workItemId" : "ADR046-network-001" + }, + { + "currentSource" : "`packages/d2b-host/src/ifname.rs` (FNV-1a derivation, detect_collisions, DEFAULT_PREFIX, BRIDGE_TAG, TAP_TAG); `packages/d2b-host/src/bridge_port.rs` (BridgePortReadback, east-west policy, TapRole defaults); `packages/d2b-host/src/nftables.rs` (NftBatch, hash_inet_d2b_table, coexistence policy); `packages/d2b-host/src/routes.rs` (route/dnsmasq-bound/IPv6 preflight); `packages/d2b-host/src/netlink.rs` (IPv6 sysctl sequence)", + "dataMigration" : "None (behavior preserved; host bridge names change from `br--*` to `d2b-b` after cutover)", + "dependencyOwner" : "ADR046-network-001; d2b-host network modules owner", + "destination" : "`packages/d2b-provider-network-local/src/ifname.rs`, `bridge_port.rs`, `nftables.rs`, `routes.rs`, `netlink.rs`", + "detailedDesign" : "Move IfName derivation to `d2b-contracts` (ADR046-network-001); keep bridge_port, nftables, routes, netlink in Provider crate. nftables: retain the Network-owned `inet d2b` chain layout, ownership markers, and coexistence matrix; emit no USBIP/TCP-3240 rule and compute drift over only the Network UID ownership projection. routes: adapt dnsmasq-bound check to use Network status instead of `HostJson.environments`. netlink: keep IPv6-off sequence; add defense-in-depth re-application path. Primary reuse disposition: `adapt`. Preserved source-plan detail: extract (ifname, bridge_port, nftables) into shared network-local Provider library; adapt (routes, netlink) into controller observe loop.", + "integration" : "Controller observe loop uses nftables digest drift, bridge_port readback, and IPv6 sysctl to drive `FirewallReady`, `FabricReady` conditions", + "removalProof" : "`packages/d2b-host/src/{ifname,bridge_port,nftables,routes,netlink}.rs` removed only after Provider conformance tests pass", + "reuseAction" : "adapt", + "reuseSource" : null, + "specId" : "ADR-046-resources-network", + "specPath" : "docs/specs/ADR-046-resources-network.md", + "validation" : "Existing `bridge_port::tests::readback_matches_defaults`, `ops::tap::tests::set_bridge_port_flags_readback_drift_fails_closed`, `netlink::tests::ipv6_off_sequence_runs_in_order`, nftables coexistence matrix tests; all pinned in `tests/golden/pinned/host-prepare-network.txt` and `tests/golden/pinned/net-canaries.txt`", + "workItemId" : "ADR046-network-002" + }, + { + "currentSource" : "`nixos-modules/net.nix` (full file, 450 lines); `nixos-modules/net-mdns.nix`; `nixos-modules/lib.nix` subnetIp/mkMac/cidrOverlaps", + "dataMigration" : "Destructive v3 reset; existing net VMs are re-created under new IfNames", + "dependencyOwner" : "ADR046-network-001, ADR046-network-002; Provider/runtime-cloud-hypervisor dossier owner", + "destination" : "`packages/d2b-provider-network-local/` — artifact catalog integration for net-VM nixos-system artifact resolution; `packages/d2b-provider-network-local/nix/` — default net-VM NixOS module (parameterized successor to net.nix), built and registered as a nixos-system artifact in `d2b.artifacts`", + "detailedDesign" : "`Network.spec.netVmSystemArtifactId` is REQUIRED. It must reference a declared `d2b.artifacts` entry with `type = \"nixos-system\"`; verified at Nix build time (Stage 2 check, hard build error if absent or wrong type). No implicit default exists; Provider artifacts cannot silently provide a separately typed system artifact. The controller sets `Guest.spec.systemArtifactId` to the artifact ID value at reconcile time (the value is already validated by the build; the controller fails closed if absent at runtime). The net-VM nixos-system artifact is **generic** (INV-NET-008): it contains the guest-agent binary and runtime, kernel, base NixOS services, systemd-networkd NIC bootstrap, and the `net-local-controller` **OS account** provisioned by `Provider/network-local`'s Nix module (same private fixed UID/GID as on the Host, so that virtiofs view ACLs on config Volume layout entries are enforced consistently inside the Guest; `Provider/system-core` performs NSS lookup reconciliation, not OS account provisioning; no numeric UID/GID appears in any ResourceSpec field, authz check, or audit record; `User.status` MAY carry diagnostic `uid`/`gid` from NSS lookup but those are informational only and never authorization inputs). It does NOT encode per-Network desired data; per-Network config (dnsmasq, nftables, routing, attachments) is delivered via the controller-owned config Volume and applied by the guest-agent Process. The artifact preserves compile-time-fixed content: `lib.mkForce` on 10-eth-dhcp (INV-NET-001); two systemd-networkd interface units matched by MAC; IPv6 suppression sysctls on NIC interfaces; ip6 filter table drop-all policy. **mDNS reflector and local dnsmasq DNS bridge are separate owned Process resources** (D-NETWORK-001); they are not inline services in the artifact.", + "integration" : "Network controller resolves artifact ID → sets `Guest.spec.systemArtifactId`. Controller separately creates `Volume/net--config` with per-Network config and `Process/net--agent` (guest-agent). `Provider/runtime-cloud-hypervisor` reads `systemArtifactId` to produce the net-VM bundle and mounts the Volume view into the Guest.", + "removalProof" : "`nixos-modules/net.nix` and `nixos-modules/net-mdns.nix` removed only after net-VM artifact parity tests pass", + "reuseAction" : "adapt", + "reuseSource" : null, + "specId" : "ADR-046-resources-network", + "specPath" : "docs/specs/ADR-046-resources-network.md", + "validation" : "nix-unit: `tests/unit/nix/cases/net-vm-network.nix` (adapted to v3 resource API); INV-NET-001 assertion in new nix-unit case; no mDNS inline service appears in the generated artifact; no per-Network dnsmasq or nftables data in artifact (INV-NET-008); integration test: mDNS Process resources are created when `spec.mdns.enable = true`; Stage 2 build test: absent `netVmSystemArtifactId` fails with required-field build error; wrong artifact type fails with `artifact-type-mismatch` error; `packages/d2b-provider-network-local/tests/net_vm_artifact_is_generic.rs` — two Networks with different CIDRs produce same `systemArtifactId` and different config Volume content", + "workItemId" : "ADR046-network-003" + }, + { + "currentSource" : "`nixos-modules/network.nix` (full file, 500+ lines; bridge/netdev/sysctl/NM-unmanaged/route sections); `nixos-modules/host-json.nix` (emits `host.json` `environments[].nftables`, `environments[].ifNameMappings`, `environments[].usbipBusidLocks` — superseded by Network status API); `nixos-modules/processes-json.nix` (emits `processes.json` `ProcessNetworkInterface`/`ProcessMacvtapInterface` per runner — superseded by Guest spec network fields); `nixos-modules/index.nix` envMeta/netMeta sections; `nixos-modules/options-envs.nix`; `nixos-modules/options-realms-network.nix`; `nixos-modules/options-vms.nix` (`d2b.vms..env` line 944, `d2b.vms..index` line 962 — current attachment semantics source; maps to `Network.spec.attachments[].executionRef` + `index`); `nixos-modules/options-site.nix` (`d2b.site.allowUnsafeEastWest` line 48 — maps to per-Network `Network.spec.isolation.allowEastWest`; Zone.spec is empty in v3; `d2b.hostLanCidrs` line 382 — maps to Host resource network inventory at runtime; not a Zone.spec field)", + "dataMigration" : "Full v3 reset; `d2b.envs.*` declarations must be rewritten as Network resources", + "dependencyOwner" : "ADR046-network-001, ADR046-network-002, ADR046-network-003; Nix integrator", + "destination" : "`nixos-modules/resources-network.nix`: Nix resource object emitter for Network ResourceType; `nixos-modules/index.nix`: network resource compilation section", + "detailedDesign" : "The emitter replaces `d2b.envs.` with `d2b.zones..resources. = { type = \"Network\"; spec = { ... }; }` (attr key = resource name; `type` explicit field; `spec` fields identical to the canonical ResourceSpec JSON — no bespoke Nix vocabulary). It validates CIDR shape, attachment index uniqueness, external attachment constraints, and CIDR overlap (reusing `cidrOverlaps` from `lib.nix`). **Bridges are NOT emitted as `systemd.network.netdevs` entries** (D-NETWORK-003 resolved; bridges are created dynamically by the broker `CreateBridge` op at reconcile time). The Nix emitter provisions only bootstrap/static prerequisites that do not require runtime bridge IfNames: `networking.networkmanager.unmanaged` pattern for the `d2b-*` prefix (covers all dynamically-created bridges and taps regardless of specific IfNames; emitted to `00-d2b-unmanaged.conf`); schema validation and controller binary deployment artifacts. Current `d2b.vms..env` + `d2b.vms..index` attachment semantics (`options-vms.nix` lines 944/962) become `Network.spec.attachments[].executionRef` + `index`. Current `d2b.site.allowUnsafeEastWest` (`options-site.nix` line 48) moves to the per-Network `isolation.allowEastWest` field; Zone.spec is empty in v3. Current `d2b.hostLanCidrs` (`options-site.nix` line 382) becomes the Host resource's network inventory, queried at runtime; at Nix build time the eval may validate CIDRs against declared host configuration input. The emitter does not emit `boot.kernel.sysctl` entries per bridge IfName (bridges do not exist at activation time; IPv6 suppression is applied by `CreateBridge` and `ApplySysctl` per INV-NET-002). **Nix option types** for `spec.*` fields are generated from `Network.schema.json`; they are not hand-written. **Bundle generation**: the emitter collects all declared `Network` resource objects, sorts them lexicographically by `(type, name)`, serializes each as canonical JSON **omitting `managedBy` and `configurationGeneration`** (core sets these at activation), and assembles the Zone resource bundle at `$out/bundle.json` (see [Nix configuration contract — Stage 3](#stage-3--build-output-zone-resource-bundle)). The emitter records a `providerSchemaDigest` entry for `Provider/network-local` in the bundle resolved from the artifact catalog. The bundle's `contentHash` is a SHA-256 of the sorted canonical resource array; the derivation is fixed-output so that identical configuration always produces the same store path. The `managedBy` field is NOT set by the emitter; core sets `managedBy = \"configuration\"` and assigns the `configurationGeneration` counter when applying the bundle. The core controller retains prior bundle copies under `/var/lib/d2b/zones//configuration/prior/` per [Generation lifecycle and cleanup contract](#generation-lifecycle-and-cleanup-contract).", + "integration" : "Nix resource objects serialize exactly the Rust NetworkSpec contract (ADR046-network-001). The provider install declares the schema digest. Zone runtime generation-transition logic (ADR046-network-008) reads the bundle at activation.", + "removalProof" : "`nixos-modules/network.nix`, `nixos-modules/options-envs.nix`, and `nixos-modules/options-realms-network.nix` removed only after `resources-network.nix` and controller reach parity; `d2b.envs` consumer migration guide updated", + "reuseAction" : "adapt", + "reuseSource" : null, + "specId" : "ADR-046-resources-network", + "specPath" : "docs/specs/ADR-046-resources-network.md", + "validation" : "nix-unit CIDR overlap, assertion eval, and bridge-sysctl cases; `make test-flake` with updated examples; `make test-drift` for schema/emitter parity; `packages/d2b-contracts/tests/generation_bundle.rs` for bundle format and `contentHash` stability; nix-unit `tests/unit/nix/cases/generation-cleanup-absent-network.nix` for removed-resource scheduling (added by ADR046-network-008)", + "workItemId" : "ADR046-network-004" + }, + { + "currentSource" : "`nixos-modules/network.nix` (tap/sysctl sections); `packages/d2b-host/src/{bridge_port,nftables,netlink,routes}.rs`; broker ops in `packages/d2b-contracts/src/broker_wire.rs`: **real runtime ops** `ApplyNftables`, `ApplyNmUnmanaged`, `ApplyRoute`, `ApplySysctl`, `SetBridgePortFlags`, `UpdateHostsFile`, `SeedDnsmasqLease` (all `implemented-and-reachable`); **new ops to author**: `CreateBridge`, `DeleteBridge` (do not exist in v3 baseline; must be added to `broker_wire.rs` and implemented as `RealBrokerRequest` handlers in `packages/d2b-priv-broker/src/runtime.rs`); **NOT current ops**: `CreateMacvtap` does not exist — macvtap is created inside broker's `SpawnRunner` dispatch (`packages/d2b-priv-broker/src/runtime.rs` line 5097 `live_create_macvtap_fd`)", + "dataMigration" : "None after full reset", + "dependencyOwner" : "ADR046-network-001–004; network-local controller owner; D-NETWORK-001, D-NETWORK-002, and D-NETWORK-003 resolved", + "destination" : "`packages/d2b-provider-network-local/src/controller.rs`: async NetworkReconciler; `packages/d2b-provider-network-local/src/plan.rs`: ReconcilePlan computation; `packages/d2b-provider-network-local/src/observe.rs`: drift-detection observe loop. Full crate layout required (see [Package and crate boundary](#package-and-crate-boundary)): `src/` (controller/plan/observe + colocated unit tests), `tests/` (hermetic conformance and state-machine tests), `integration/` (provider-system reconcile fixtures), `README.md` (Network ResourceType, controller binary, placement, RBAC, security invariants, build/test/integration commands).", + "detailedDesign" : "Implements full async reconcile interface from `ADR-046-resource-reconciliation`. `plan()` computes desired vs. actual bridge-presence, sysctl, host-side nftables, hosts-file, NM-unmanaged, config Volume content, Guest, guest-agent Process, and mDNS-Process states. `reconcile()` dispatches in order: `CreateBridge` for each bridge not present (broker applies IPv6 sysctls atomically; `CreateBridge` failure sets `FabricReady=False/bridge-create-error` and aborts) → `ApplySysctl` (defense-in-depth IPv6) → `ApplyNftables` (host-side `inet d2b` table) → `ApplyNmUnmanaged` → `ApplyRoute` → `UpdateHostsFile` → `SeedDnsmasqLease` for new reservations → **Volume upsert** (two-phase): Phase 1 — create `Volume/net--config` with `kind: ephemeral`, `source.executionRef: Host/`, `source.settings.kind: tmpfs`, `quota: {maxBytes: 4194304, maxInodes: 128, enforcement: hard}` (tmpfs quota charged to Host memory budget), `layout` entries (root directory with `type: directory` and four config files each with `type: file`, `ownerRef: User/net-local-controller`, `groupRef: User/net-local-controller`, `mode: \"0640\"`, `accessAcl: []`, `defaultAcl: []`, `noFollow: true`, conservative create/repair/cleanup policies), `views: {guest-readonly: {path: \"\", rights: [read, traverse]}}`, `attachments: []` (no Guest attachment); abort on terminal error with `ConfigVolumeReady=False/config-volume-error`. Wait for Volume backing to reach `Ready`; requeue on `Degraded`/`Failed` with `ConfigVolumeReady=False/volume-not-ready`. Write rendered config content through Volume write service (no raw host paths). Phase 2 — create Guest upsert with `systemArtifactId` from REQUIRED `Network.spec.netVmSystemArtifactId`. Wait for Guest `Ready`. Then update Volume with Guest attachment: `attachments: [{executionRef: Guest/, transport: virtiofs, view: guest-readonly, access: read-only, mountPath: \"/run/d2b/net-config\", settings: {posixAcl: false, xattr: false, cache: auto, inodeFileHandles: never, threadPoolSize: null, socketGroup: null}}]`; wait for attachment `Ready`; requeue on `Degraded` with `ConfigVolumeReady=False/attachment-not-ready`. Create or update guest-agent Process `Process/net--agent` with `processClass: worker`, `sandbox: {namespaceClasses: [], capabilityClasses: [network-admin, network-bind, network-raw]}` (inherits Guest network namespace; `network-admin`→`CAP_NET_ADMIN`, `network-bind`→`CAP_NET_BIND_SERVICE`, `network-raw`→`CAP_NET_RAW`, all effective in Guest network namespace only; INV-NET-009), `mounts: [{volumeRef: Volume/net--config, view: guest-readonly, mountPath: \"/run/d2b/net-config\", access: read-only, required: true}]`. mDNS Process upsert when `spec.mdns.enable = true` (D-NETWORK-001) → `SetBridgePortFlags` per tap. Each broker op returns typed audit evidence. `observe()` re-reads `firewallDigest` (host-side), bridge isolation flags, IPv6 sysctls, and guest-agent Process status (`dnsmasq-bound`, `firewall-applied` predicates); queues reconcile on drift. **Finalizer** (strictly child-first): `NetworkDraining` → delete guest-agent Process and mDNS Processes; wait for each Deleted watch event → update Volume to remove Guest attachment (`attachments: []`); wait for attachment removal confirmed → delete `Guest/`; wait for Deleted watch event → delete `Volume/net--config`; wait for Deleted watch event → `ApplyNftables` (empty) → `ApplyNmUnmanaged` (empty) → `UpdateHostsFile` (empty) → `DeleteBridge` for each bridge (idempotent) → clear finalizer. No USBIP rules installed by Network; `UsbipBindFirewallRule` issued by device-usbip directly (D-NETWORK-002).", + "integration" : "Controller process registers descriptor, watches `Network` resources via d2b-bus/ComponentSession/ResourceClient. Owned Guest and Process mutations trigger owner reconciliation. Device-usbip watches only Network identity/readiness/generation; its Core adapter privately resolves relay/firewall effects (D-NETWORK-002).", + "removalProof" : "Daemon-orchestrated network/bridge lifecycle removed only after controller passes conformance and parity tests", + "reuseAction" : "adapt", + "reuseSource" : null, + "specId" : "ADR-046-resources-network", + "specPath" : "docs/specs/ADR-046-resources-network.md", + "validation" : "`ADR046-reconcile-001` toolkit conformance; latency gates (p95 ≤5 ms hint-to-handler); Network-specific: CIDR conflict blocks reconcile, `CreateBridge` failure sets `FabricReady=False`, Volume creation failure sets `ConfigVolumeReady=False/config-volume-error`, `User/net-local-controller` not Ready aborts with `ConfigVolumeReady=False/user-not-ready`, Volume schema round-trip (kind=ephemeral, source.settings.kind=tmpfs, quota.enforcement=hard, layout type=file entries, views.guest-readonly.rights=[read,traverse]), tmpfs quota charged to Host memory budget (test Volume creation fails when Host memory budget exceeded), Guest not created before Volume backing `Ready`, Guest attachment not added before Guest `Ready`, guest-agent Process created after attachment `Ready` (`processClass: worker`, `namespaceClasses: []`, `capabilityClasses: [network-admin, network-bind, network-raw]`, `access: read-only`, `required: true`), host-capability leakage test: no host-netns process gains `CAP_NET_ADMIN`/`CAP_NET_BIND_SERVICE`/`CAP_NET_RAW` as result of guest-agent launch (INV-NET-009; `tests/host-integration/guest-agent-cap-confinement.nix`), `DeleteBridge` called on finalizer, Volume attachment removed before Guest deletion in finalizer (test order: agent Deleted → attachment removed → Guest Deleted → Volume Deleted → bridges), east-west invariant (INV-NET-003), hostBlocklist enforcement (INV-NET-004), macvtap attachment status (delegated to runtime-ch), mDNS Process created/deleted with `spec.mdns.enable` toggle, broker INV-NET-002 tests, config-only spec change updates Volume content and triggers agent reload without Guest restart (INV-NET-008); golden tests updated for v3 IfNames", + "workItemId" : "ADR046-network-005" + }, + { + "currentSource" : "`tests/unit/nix/cases/net-vm-network.nix`; `tests/golden/pinned/net-vm-bundle-gate.txt`; `tests/golden/pinned/net-canaries.txt`; `tests/golden/pinned/host-prepare-network.txt`; `tests/host-integration/bridge-isolation.nix`; `tests/integration/live/network-isolation.sh`", + "dataMigration" : "None — full d2b 3.0 reset; no prior state to migrate", + "dependencyOwner" : "ADR046-network-001, ADR046-network-005; test owner", + "destination" : "`tests/unit/nix/cases/net-vm-network.nix` (adapted to v3 resource API); updated golden pins; `tests/host-integration/bridge-isolation.nix` (adapted); `packages/d2b-priv-broker/tests/bridge_lifecycle.rs` (new hermetic broker tests). Provider crate test directories: `packages/d2b-provider-network-local/tests/` — hermetic Cargo integration tests (conformance suite, controller state machine, CIDR validation vectors, IfName determinism, invariant tests INV-NET-001–007, reconcile/observe/finalize with deterministic clock, fault injection); `packages/d2b-provider-network-local/integration/` — container/Host/Guest lifecycle fixtures invoked by `make test-integration` (bridge isolation, east-west double opt-in, nftables drift detection, macvtap lifecycle). Both directories required by package policy.", + "detailedDesign" : "Rust integration tests: NetworkSpec CIDR validation golden vectors; AttachmentSpec index uniqueness; ExternalAttachmentSpec mutual-exclusion validation; IfName derivation determinism; CIDR overlap arithmetic; INV-NET-001 through INV-NET-009 invariant tests; reconcile/observe/finalize state machine (deterministic clock). Broker tests: `create_bridge_applies_ipv6_sysctl` (INV-NET-002 layer 1); `delete_bridge_is_idempotent`; `create_bridge_parameters_match_spec` (MTU, STP disabled, multicast snooping disabled). Controller tests: `reconcile_applies_sysctl_defense_in_depth` (INV-NET-002 layer 2); `volume_created_before_guest`; `guest_not_created_until_volume_ready`; `agent_process_created_after_guest`; `finalizer_order_agent_then_guest_then_volume_then_bridges`; `config_only_spec_change_updates_volume_no_guest_restart` (INV-NET-008); `finalizer_calls_delete_bridge`; `mdns_process_created_on_enable`; `mdns_process_deleted_on_disable`; `host_capability_leakage` (INV-NET-009). nix-unit: INV-NET-001 lib.mkForce assertion; net-VM artifact has no inline mDNS service and no per-Network dnsmasq/nftables data (INV-NET-008); Network emitter CIDR constraint assertions; no `systemd.network.netdevs` bridge entries emitted. Host integration: bridge isolation with east-west opt-in; nftables drift detection; macvtap create/delete lifecycle; config Volume update propagates to guest-agent without Guest restart; `tests/host-integration/guest-agent-cap-confinement.nix` (INV-NET-009 zero leakage to host netns).", + "integration" : "Pinned tests registered in `tests/golden/pinned/`; nix-unit cases in `tests/unit/nix/cases/`; host integration in `tests/host-integration/`", + "removalProof" : "Not applicable (this work item IS the test successor)", + "reuseAction" : "adapt", + "reuseSource" : null, + "specId" : "ADR-046-resources-network", + "specPath" : "docs/specs/ADR-046-resources-network.md", + "validation" : "All listed tests must pass before `nixos-modules/network.nix` removal is eligible", + "workItemId" : "ADR046-network-006" + }, + { + "currentSource" : "`nixos-modules/network.nix` lines 444–461 (USBIP host firewall); `packages/d2b-core/src/host.rs` lines 324–328 (usbip_backend_port, usbip_busid_locks in NetEnv); `packages/d2b-host/src/` usbip_argv.rs", + "dataMigration" : "Current network.nix USBIP carve-out replaced by UsbipBindFirewallRule broker op", + "dependencyOwner" : "ADR046-network-005; device-usbip Provider dossier; D-NETWORK-002 resolved", + "destination" : "`Provider/device-usbip` owns one relay Process/Endpoint authority per Network plus the typed EffectPort adapter for `UsbipBindFirewallRule`. The controller watches only the `networkRef` resource's identity/readiness/generation; Core privately resolves Network UID to relay attachment and firewall intent. Network spec/status is not mutated with USBIP fields. Full crate layout required for `packages/d2b-provider-device-usbip/` (see [Package and crate boundary](#package-and-crate-boundary)): `src/` (controller and usbip runner + unit tests), `tests/` (hermetic conformance, dependency-watch state machine, UsbipBindFirewallRule round-trip), `integration/` (Host/Guest USBIP attach/detach lifecycle fixtures), `README.md` (Provider identity, provider-neutral USB Service/Binding types, USBIP Processes/Endpoints, Network least-privilege dependency contract, RBAC, security invariants, standalone-repo path).", + "detailedDesign" : "Device-usbip's typed EffectPort is the sole semantic owner of every USBIP TCP/3240 rule. Its Core adapter resolves the opaque per-Network/per-busid intent and issues `UsbipBindFirewallRule`; its strict provider status owns firewall digest/drift. Network-local emits no generic host or net-VM TCP/3240 allow and ignores device-usbip ownership markers in Network drift. The device Provider owns exactly one multiplexed relay Endpoint authority per Network and supplies Binding proxies only authorized connected streams through LaunchTickets. Primary reuse disposition: `adapt`. Preserved source-plan detail: extract and adapt.", + "integration" : "device-usbip watches Network readiness → Core adapter resolves opaque Network attachment → `UsbipBindFirewallRule` + one relay Endpoint authority → Binding proxy LaunchTicket", + "removalProof" : "Network.nix USBIP sections removed only after UsbipBindFirewallRule mechanism passes conformance", + "reuseAction" : "adapt", + "reuseSource" : null, + "specId" : "ADR-046-resources-network", + "specPath" : "docs/specs/ADR-046-resources-network.md", + "validation" : "device-usbip conformance tests cover exact per-Network/per-busid scoping, one relay Endpoint authority, ownership-scoped drift/status, foreign-marker rejection, and release; network-local nftables tests assert no TCP/3240/USBIP rule on host or net VM and prove USBIP rule churn does not change Network `FirewallReady`; the pinned USBIP firewall golden moves to device-usbip ownership", + "workItemId" : "ADR046-network-007" + }, + { + "currentSource" : "No current v3 source: generation lifecycle and `managedBy`/`configurationGeneration` classification do not exist on the pre-ADR45 v3 baseline. The v3 baseline uses NixOS activation scripts that atomically replace all host JSON artifacts; there is no per-resource generation tracking, no async cleanup queue, and no `managedBy` field.", + "dataMigration" : "None on v3 initial install (no prior generation state). Host upgrades from the pre-ADR45 v3 baseline perform a reset: core starts with `configurationGeneration = 1` and no prior bundle. All declared resources are treated as new Creates.", + "dependencyOwner" : "ADR046-network-004, ADR046-network-005; Zone runtime integrator", + "destination" : "`packages/d2b-core-controller/src/configuration.rs`: bundle application, diff, generation-transition logic (including per-item name-conflict handling), prior-bundle retention under `/var/lib/d2b/zones//configuration/prior/`; `packages/d2b-core-controller/src/cleanup.rs`: removal scheduling and `PendingCleanup` condition tracking; `packages/d2b-contracts/src/generation_bundle.rs`: `ZoneBundle`/`BundleResource`/`BundleMetadata` **input** DTOs — MUST NOT include `managedBy` or `configurationGeneration` (both are persisted resource metadata set by core at activation, not bundle input fields); `ManagedBy` closed enum `{ Configuration, Controller, Api }` and `configurationGeneration: u64` live in `packages/d2b-core-controller/src/resource_store.rs` as persisted resource metadata fields; `nixos-modules/resources-network.nix` (emits bundle with `managedBy`/`configurationGeneration` absent; core sets both at activation per ADR046-network-004); `d2b.zones..retainedGenerations` Nix/compiler-level Zone option (outside `Zone.spec`; default `3`, range `1..16`); `tests/unit/nix/cases/generation-cleanup-absent-network.nix`; `packages/d2b-contracts/tests/generation_bundle.rs`; `tests/host-integration/nix-generation-cleanup.nix`", + "detailedDesign" : "**Core generation tracking** (`packages/d2b-core-controller/src/configuration.rs`): core maintains a monotone `configurationGeneration` counter per Zone in its durable state. On each bundle application it compares the incoming `contentHash` against the prior applied hash. If different, it increments `configurationGeneration`, sets `managedBy = \"configuration\"` and the new counter value on each resource in the bundle, and performs the resource diff (create/update/delete scheduling). The `managedBy` and `configurationGeneration` fields are absent from the Nix-emitted bundle and are set exclusively by core at activation time. **`managedBy` field and per-item name-conflict handling**: `ManagedBy` is a closed enum (`Configuration`, `Controller`, `Api`) persisted in resource metadata at `packages/d2b-core-controller/src/resource_store.rs`. It is NOT a field in `ZoneBundle`/`BundleResource` input DTOs; core sets it at activation. Controllers set `ManagedBy::Controller` when creating owned children (net-VM Guest, config Volume, guest-agent Process, mDNS Processes); exact controller identity/UID/generation are tracked in separate internal metadata, not embedded in the `managedBy` value. API-created resources carry `ManagedBy::Api` and persist until explicit delete with no bundle-driven lifecycle. Core's generation-transition logic only schedules bundle-driven Delete for `ManagedBy::Configuration` resources. **Per-item name-conflict handling**: when a bundle item's `(zone, name)` already exists with `managedBy ≠ \"configuration\"`, core skips that item and records it with `phase = Degraded, reason: name-conflict`; a `ResourceConflictSkipped` audit record is emitted for that item. All non-conflicting items in the bundle proceed normally (Provider-state contract). The existing resource is left completely untouched. The operator deletes the conflicting resource via the resource API; the next bundle application applies the item. **Removal scheduling**: on generation N+1 activation, core performs a set difference: `prev_configuration_managed - new_configuration_managed` = resources to delete. For each, it sets `metadata.deletionRequestedAt` in the resource store and emits a `ResourceDeletionScheduled` audit record. Normal finalizer-path Delete proceeds asynchronously. **`PendingCleanup` condition**: the Zone self resource carries a `PendingCleanup = True` condition while any `managedBy = Configuration` resource has `deletionRequestedAt` set and has not yet been atomically removed. Aggregate Zone `phase = Degraded` applies. The condition transitions to `False` and Zone phase returns to `Ready` when all scheduled deletions complete. **Prior generation bundle retention** (`cleanup.rs`): count-based (`d2b.zones..retainedGenerations`, outside `Zone.spec`, default 3, range 1..16); no TTL. Core copies prior bundles to `/var/lib/d2b/zones//configuration/prior/.json`. A generation is eligible for pruning when all configuration-managed resources from it have either been atomically removed or are present unchanged in a newer generation, AND the count would be exceeded. **`BundleActivated` audit record**: emitted at each generation transition with `contentHash`, `configurationGeneration`, `resourceCount`, and `providerSchemaDigests` map (digests from `type=provider` artifacts via `Provider.spec.artifactId`); no spec contents, CIDRs, or resource names appear in the record. Provider schema digests in the bundle are re-verified against installed Provider artifact digests at application time; a mismatch aborts application with a `BundleRejected` audit record.", + "integration" : "ADR046-network-004 (emitter writes bundle format; core sets `managedBy`/`configurationGeneration` at activation) → ADR046-network-008 (runtime reads and applies). ADR046-network-005 (controller Delete path) is invoked by ADR046-network-008 removal scheduling for Network resources. Zone `PendingCleanup` condition and `Degraded` phase are read by CLI `d2b zone status`.", + "removalProof" : "Not applicable (this is a new capability). The `PendingCleanup` condition and zone cleanup audit path have no prior equivalent to remove.", + "reuseAction" : "create", + "reuseSource" : null, + "specId" : "ADR-046-resources-network", + "specPath" : "docs/specs/ADR-046-resources-network.md", + "validation" : "**nix-unit**: `tests/unit/nix/cases/generation-cleanup-absent-network.nix` — verifies that a Network resource present in generation N and absent from generation N+1 receives `deletionRequestedAt` and appears in the `PendingCleanup` condition; verifies that a controller-owned `Guest` (`managedBy = \"controller\"`) does NOT receive a direct bundle-driven Delete; verifies that a re-declared (identical spec) Network is NOT scheduled for Delete; verifies `retainedGenerations` default is 3. **Rust contract tests**: Two separate test files — (1) `packages/d2b-contracts/tests/generation_bundle.rs`: tests the **input** bundle DTO only: `ZoneBundle`/`BundleResource`/`BundleMetadata` JSON round-trip, `contentHash` stability across serialization, `providerSchemaDigests` presence, `managedBy` and `configurationGeneration` fields ABSENT from `BundleResource` input struct (verified by both compile-time type check: the fields must not exist on the `BundleResource` type, and runtime JSON serialization: the serialized object must not contain those keys). (2) `packages/d2b-core-controller/tests/resource_metadata.rs`: `ManagedBy` closed enum round-trip with `\"configuration\"`/`\"controller\"`/`\"api\"` values tested separately here since `ManagedBy` is persisted resource metadata in `resource_store.rs`, not a field of the input bundle DTO. **Controller integration tests**: async Delete triggered through finalizers for Network; mDNS Process child deleted before Network finalizer clears; bridge `DeleteBridge` broker call made exactly once during finalizer; controller waits for Deleted watch event (not a persistent phase=Deleted row) before proceeding. **Host integration**: `tests/host-integration/nix-generation-cleanup.nix` — runNixOSTest scenario: apply generation 1 with Network resource, then apply generation 2 with that Network absent; assert Zone enters `Degraded/PendingCleanup`; assert Network `phase = Degraded` with `NetworkDraining = True` and `deletionRequestedAt` set and `reason: configuration-generation-removed`; assert cleanup completes (single store transaction: Deleted REVISION event + row/index removal; dedup-guarded audit append follows committed transaction) and Zone returns to `Ready`; assert no controller-owned children deleted directly by core; assert prior bundle copied to `/var/lib/d2b/zones//configuration/prior/` and retained until cleanup complete; assert bundle pruned when `retainedGenerations` exceeded and generation eligible. **INV-NET-LIFECYCLE-001**: core never schedules bundle-driven Delete for `managedBy ≠ \"configuration\"` resources; verified by static analysis of core's generation-transition diff function, which is bounded at compile time to iterate only the `configuration_managed_resources` set. **INV-NET-LIFECYCLE-002**: per-item name-conflict — when a bundle item collides with `managedBy = \"controller\"` or `\"api\"`, that item is recorded as `Degraded/name-conflict`; the existing resource is left untouched; non-conflicting items continue to activate; tested by `packages/d2b-core-controller/tests/configuration_name_conflict.rs` (three cases: collision with a controller-owned child, an API-created resource, and a same-name configuration resource from a prior generation that completed deletion; each case asserts non-conflicting items still activate).", + "workItemId" : "ADR046-network-008" + }, + { + "currentSource" : "Existing macvtap spawn path resolves `parentInterface` but has no cross-Zone authority admission or compatible-sharing contract", + "dataMigration" : "Full d2b 3.0 reset; no authority ledger import", + "dependencyOwner" : "D097 Host-global authority index; ADR046-network-001, ADR046-network-005; Provider/network-local and Core authority owners", + "destination" : "`packages/d2b-contracts/src/v3/network.rs` external-attachment sharing schema/status; `packages/d2b-core-controller/src/authority.rs` Core-derived physical-NIC identity and Host-global claim; Provider/network-local descriptor/reconcile/finalizer", + "detailedDesign" : "Resolve operator-declared `parentInterface` against trusted Host inventory and derive an opaque `external-physical-nic/v1` digest; index `(Host, external-physical-nic, opaqueKeyDigest)` before any macvtap/VMM effect. `passthru`, `private`, and `vepa` are exclusive. `bridge` defaults exclusive and is multiplexed only under explicitly authored compatible policy. Use typed `external-physical-nic-conflict`; expose only bounded authority availability/holder-count/queue/arbitration/update-currency and conditions; keep digest, interface identity, and owner proof private. Parent/mode/policy update drains and releases the old claim before replacement; deletion closes macvtap/VMM ownership before releasing the claim; restart adopts exact owner proof and quarantines ambiguity. Primary reuse disposition: `adapt`. Preserved source-plan detail: adapt the existing private macvtap-FD spawn path; add authority admission before it.", + "integration" : "Network validation and Core authority preflight gate runtime-cloud-hypervisor LaunchTicket/`SpawnRunner`; the finalizer and D091 update planner release in dependency order", + "removalProof" : "None — authority admission is new; existing direct macvtap spawn becomes unreachable without a claim.", + "reuseAction" : "adapt", + "reuseSource" : null, + "specId" : "ADR-046-resources-network", + "specPath" : "docs/specs/ADR-046-resources-network.md", + "validation" : "Hermetic authority tests cover same-Zone and cross-Zone exclusive collisions, mixed-policy conflicts, non-bridge multiplex rejection, explicit compatible bridge multiplex admission, Core-derived key equality for two selectors resolving to one fake NIC, caller-supplied digest rejection, no-effect conflict, owner-proof adoption/ambiguity, disruptive update, and release-after-close ordering. Nix eval covers schema and declared cross-Zone conflicts; host integration covers create/update/delete with a fake macvtap parent and status/condition transitions without raw identity exposure.", + "workItemId" : "ADR046-network-009" + }, + { + "currentSource" : "`nixos-modules/options-realms.nix` (`RealmId`/`RealmPath`/`RealmControllerPlacement`/`EntrypointMode` labels); `options-realms-workloads.nix` (`WorkloadId`/`WorkloadProviderKind`/`IsolationPosture`/`WorkloadExecutionPosture`); `options-realms-network.nix`; `d2b-realm-core/src/realm.rs`, `workload.rs`, `capability.rs`, `ids.rs` (symbols to adapt)", + "dataMigration" : "Operator configs migrate `d2b.realms.*` → `d2b.zones.*`; `d2b.vms.*` → `d2b.zones..resources.*` with `type = \"Guest\"`", + "dependencyOwner" : "W0; `d2b-contracts` identities (ADR046-identities-001, ADR046-identities-002)", + "destination" : "`nixos-modules/options-zones.nix` (Zone-level options: `label`, `parentZone`, `retainedGenerations`, `trustedPublishers` — compiler settings, not Zone spec fields); `nixos-modules/options-zones-resources.nix` (unified `resources` attrset)", + "detailedDesign" : "`d2b.zones..resources. = { type = \"\"; spec = { ... }; }` — single attrset covering all ResourceTypes; `type` discriminates dispatch; `spec` fields mirror exact ResourceTypeSchema field names and nesting; Nix option types/defaults/docs generated from `docs/reference/schemas/v3/.json`; no Nix-only fields inside resource declarations; `metadata.name` derives from attr key; `metadata.zone` derives from enclosing zone attr key; `apiVersion` defaulted; `uid`/`generation`/`revision`/`status`/`managedBy` never in Nix; `resource_name` regex `^[a-z][a-z0-9-]*$`; ref validation assertions; `WorkloadProviderKind` → Guest/Host mapping per disposition table above; `Capability` → Role verb mapping per resource-api/authz foundation spec; Zone self-resource spec is `{}`; `parentZone` is a required non-root/forbidden-root compiler-only plain Zone name compiled into sealed allocator topology; `retainedGenerations`/`trustedPublishers` are likewise Zone-level compiler settings not emitted in Zone spec", + "integration" : "`nixos-modules/default.nix` imports new options files; old realms options coexist until ADR046-nix-002", + "removalProof" : "`options-realms*.nix` removed after `options-zones*.nix` achieves parity and parity drift test passes", + "reuseAction" : "adapt", + "reuseSource" : null, + "specId" : "ADR-046-nix-configuration", + "specPath" : "docs/specs/ADR-046-nix-configuration.md", + "validation" : "nix-unit vectors for each ResourceType; ref-validation rejection vectors; malformed ref error shape; `parentZone` is missing on non-root/forbidden on local-root/unknown/self/cyclic/over-depth rejection; scalar module conflicts prove one parent; child-local ZoneLink `childZoneName` must equal its enclosing Zone key; a second uplink resource (even disabled) and a local-root uplink fail eval; missing/local-unresolved `transportProviderRef` fails eval; `managedBy` in spec rejected at eval; Zone spec is `{}` (no `parentZone`, `parentRef`, `retainedGenerations`, etc.)", + "workItemId" : "ADR046-nix-001" + }, + { + "currentSource" : "`nixos-modules/options-envs.nix` (`lanSubnet`/`uplinkSubnet`/`mtu`/`mssClamp`/`externalNetwork.*`); `nixos-modules/options-vms.nix` (`d2b.vms..*`; `ProcessRole` toggle options for components)", + "dataMigration" : "Operator configs migrate env/VM component options into Zone Network/Guest/Device resources; full d2b 3.0 reset means no v2 runtime state/config import beyond rewritten Nix declarations.", + "dependencyOwner" : "ADR046-nix-001; env/VM migration", + "destination" : "`Network` resource fields in `nixos-modules/options-zones-resources.nix`; `Guest` resource fields", + "detailedDesign" : "`d2b.envs.work.lanSubnet` → `d2b.zones.work.resources.work-lan = { type = \"Network\"; spec = { lanSubnet = \"...\"; ... }; }`; CIDR overlap assertion migrated; `sys-` reserved prefix and VM-name regex retained; `d2b.vms..tpm.enable` → `d2b.zones..resources.vm-tpm = { type = \"Device\"; spec = { providerRef = \"Provider/device-tpm\"; ... }; }`", + "integration" : "`options-zones-resources.nix` emits Network/Guest resources consumed by the rewritten index/bundle and Provider/network-local/device Providers; assertions run during eval before bundle emission.", + "removalProof" : "`options-envs.nix`, `options-vms.nix` removed after migration parity test passes", + "reuseAction" : "adapt", + "reuseSource" : null, + "specId" : "ADR-046-nix-configuration", + "specPath" : "docs/specs/ADR-046-nix-configuration.md", + "validation" : "nix-unit CIDR rejection; eval assertion for `sys-` prefix; VM-name regex", + "workItemId" : "ADR046-nix-002" + }, + { + "currentSource" : "`nixos-modules/options-daemon.nix`, `options-site.nix`, `options-host.nix`", + "dataMigration" : "Operator site options stay in place; new per-Zone `retainedGenerations` defaults apply on first v3 activation; no v2 runtime state import.", + "dependencyOwner" : "ADR046-nix-001; site options", + "destination" : "`nixos-modules/options-site.nix` (retained); per-Zone options in `options-zones.nix`", + "detailedDesign" : "`d2b.zones..retainedGenerations` (default 3, range 1..16, compiler setting — not emitted in Zone spec); `d2b.site.stateDir` maps to Zone storage roots; `d2b.site.usePrebuiltHostTools` retained Primary reuse disposition: `adapt`. Preserved source-plan detail: retain and extend.", + "integration" : "`options-site.nix` retains site defaults consumed by `options-zones.nix` and `bundle-zones.nix`; activation-helper and Zone storage roots read `stateDir`/`usePrebuiltHostTools` settings.", + "removalProof" : "No removal; file extended only", + "reuseAction" : "adapt", + "reuseSource" : null, + "specId" : "ADR-046-nix-configuration", + "specPath" : "docs/specs/ADR-046-nix-configuration.md", + "validation" : "nix-unit case verifies `retainedGenerations` default/range, `site.stateDir` mapping, and `usePrebuiltHostTools` retention.", + "workItemId" : "ADR046-nix-003" + }, + { + "currentSource" : "`nixos-modules/index.nix` — `cfg._index.*`: `enabledEnvs`, `enabledVms`, `netMeta` (derives bridge names/IPs from `lanSubnet`/`uplinkSubnet`), `declaredRealms`, `enabledRealms`, `workloadsInEnv`, `runtimeRows`", + "dataMigration" : "Index is rebuilt from v3 Zone resources at activation; full d2b 3.0 reset; no v2 index state import.", + "dependencyOwner" : "ADR046-nix-001, ADR046-nix-002", + "destination" : "`nixos-modules/index.nix` (rewritten); emits `/etc/d2b/index.json`", + "detailedDesign" : "Cross-Zone normalized index: zone/host/guest/network/closure entries; executionIndex; networkIndex; closureIndex; sorted output; `cfg._index` attribute tree retained as internal helper during migration Primary reuse disposition: `adapt`. Preserved source-plan detail: rewrite.", + "integration" : "`options-zones-resources.nix` feeds rewritten `index.nix`; `bundle-zones.nix`, resource emitters, and activation-helper consume `/etc/d2b/index.json`.", + "removalProof" : "`cfg._index.envMeta`, `cfg._index.realms.*` sub-trees removed after all callers migrate to Zone resource lookups", + "reuseAction" : "adapt", + "reuseSource" : null, + "specId" : "ADR-046-nix-configuration", + "specPath" : "docs/specs/ADR-046-nix-configuration.md", + "validation" : "nix-unit golden vectors for index shape; drift gate: `xtask gen-index` round-trip", + "workItemId" : "ADR046-nix-004" + }, + { + "currentSource" : "`nixos-modules/bundle-artifacts.nix` (`artifactModule` submodule, `installFileName`, `mode 0640` ownership); `nixos-modules/bundle.nix` (bundle derivation, SHA256SUMS, `d2b._bundle` integrity chain)", + "dataMigration" : "Per-Zone bundles are generated from v3 resource declarations; full d2b 3.0 reset; no v2 monolithic bundle state import.", + "dependencyOwner" : "ADR046-nix-004", + "destination" : "`nixos-modules/bundle-zones.nix` (per-Zone bundle derivation); common helpers retained in `bundle-artifacts.nix`", + "detailedDesign" : "Per-Zone `bundle.json` with `candidateId`/`contentId` binding; SHA256 digest chain; `generationIndex`; atomic activation pointer; `manifestVersion` → `schemaVersion` rename Primary reuse disposition: `adapt`. Preserved source-plan detail: extend and rewrite.", + "integration" : "`d2b-activation-helper` reads `bundle.json` per Zone; validates digest chain before staging", + "removalProof" : "Monolithic `bundle.json` and `d2b._bundle` artifact table retired after all Zone bundle tests pass", + "reuseAction" : "adapt", + "reuseSource" : null, + "specId" : "ADR-046-nix-configuration", + "specPath" : "docs/specs/ADR-046-nix-configuration.md", + "validation" : "Artifact-shape contract tests in `packages/d2b-contract-tests/tests/`; determinism test (build twice, diff outputs)", + "workItemId" : "ADR046-nix-005" + }, + { + "currentSource" : "`nixos-modules/processes-json.nix` (`VmProcessDag`, `ProcessRole`, `binaryPath`, `argv`, `share.source == \"/nix/store\"` sentinel); `packages/d2b-core/src/processes.rs` (`ProcessRole` enum — all variants in disposition table above)", + "dataMigration" : "Processes are emitted from v3 Process/EphemeralProcess resources; full d2b 3.0 reset; no v2 process artifact import.", + "dependencyOwner" : "ADR046-nix-005; Process Provider work items (ADR046-primitives-002)", + "destination" : "`nixos-modules/resources-zones-processes.nix`; emits `zones//processes.json`", + "detailedDesign" : "Process/EphemeralProcess resource serialization per disposition table and the frozen `ExecutionSpec`: exact `providerRef`/`executionRef`/domain/user/processClass/template/config/credential/mount/sandbox/budget/`networkUsage`/`deviceUsage`/telemetry names; semantic owner from `metadata.ownerRef` with template fallback through the Process Provider; no `packageRef`, free-form `binaryPath`, `argv`, renamed network/device fields, or inline endpoints; stable produced endpoints are separate owned `Endpoint` resources with `producerRef`; Process uses canonical `restartPolicy` fields; EphemeralProcess uses `runtimeDeadline` and has no restart policy; VsockRelay → `Process` under `Provider/transport-vsock`; GuestSshReadiness retired at v3 cutover; Usbip long-lived backend/proxy → `Process`, Usbip per-busid attach/detach → `EphemeralProcess`, all owned by `Provider/device-usbip` Primary reuse disposition: `adapt`. Preserved source-plan detail: extract and adapt.", + "integration" : "`processes.json` replaces `cfg._bundle.processesJson`; Process Providers read the new format", + "removalProof" : "`processes-json.nix` and current `processes.json` schema removed after all Process Providers consume `zones//processes.json`", + "reuseAction" : "adapt", + "reuseSource" : null, + "specId" : "ADR-046-nix-configuration", + "specPath" : "docs/specs/ADR-046-nix-configuration.md", + "validation" : "Process and EphemeralProcess exact-schema vectors; reject `packageRef`, `network`, `devices`, `endpoints`, `restart`, and `runDeadline`; accept canonical `networkUsage`, `deviceUsage`, `restartPolicy`, `runtimeDeadline`, and separately owned Endpoint with `producerRef`; no-raw-path assertion; ProcessRole parity test (every variant has a test case)", + "workItemId" : "ADR046-nix-006" + }, + { + "currentSource" : "`nixos-modules/storage-json.nix` (all `mkPath` calls, `PrincipalRef` `uid`/`gid`/`user` kinds, `StorageRoot`/`StoragePathSpec`/`repairPolicy`/`cleanupPolicy`); `packages/d2b-core/src/storage.rs` (`StorageJson`, `StoragePathSpec`); `nixos-modules/sync-json.nix` (`SyncJson` OFD lock rows); `packages/d2b-core/src/sync.rs`", + "dataMigration" : "Volume resources are regenerated from v3 config; full d2b 3.0 reset; no v2 storage/sync state import.", + "dependencyOwner" : "ADR046-nix-005; Volume Provider work items (ADR046-primitives-003)", + "destination" : "`nixos-modules/resources-zones-volumes.nix`; emits `zones//volumes.json`; OFD lock rows move to `d2b-contracts` internals", + "detailedDesign" : "Volume layout/views/ACL/no-follow/repair preserving current policy; `PrincipalRef { kind: \"uid\" }` → `User/` typed ref only; OFD rows removed from Nix artifacts Primary reuse disposition: `adapt`. Preserved source-plan detail: extract storage policy → adapt; retire sync rows.", + "integration" : "Volume resources feed `zones//volumes.json`; the Volume Provider consumes it for path/view/ACL reconciliation; `d2b-contracts` internals own OFD lock rows.", + "removalProof" : "`storage-json.nix`, `sync-json.nix`, and `/etc/d2b/storage.json`/`sync.json` removed after Volume controller parity", + "reuseAction" : "adapt", + "reuseSource" : null, + "specId" : "ADR-046-nix-configuration", + "specPath" : "docs/specs/ADR-046-nix-configuration.md", + "validation" : "Volume schema vectors; ACL/no-follow/view policy tests", + "workItemId" : "ADR046-nix-007" + }, + { + "currentSource" : "`nixos-modules/allocator-json.nix` (realm/env bridge assignments, `allocatorStateDir`, socket paths, `providerPlacement`); `nixos-modules/realm-controller-config-json.nix` (emits `realm-controllers.json`); `packages/d2b-core/src/realm_controller_config.rs` (`RealmControllersJson`, `RealmControllerMetadataSummary`; read live by `d2bd/src/realm_access_resolver.rs` from `/etc/d2b/realm-controllers.json`)", + "dataMigration" : "Topology is regenerated from `d2b.zones..parentZone`; full d2b 3.0 reset; no allocator/realm-controller state import.", + "dependencyOwner" : "ADR046-nix-004; Zone/Network migration", + "destination" : "Compiler-only `parentZone` map in `nixos-modules/options-zones.nix`; local-root allocator bootstrap compiler/sealer; runtime-created Zone self-resource with `spec = {}`; child-local ZoneLink resource; `realm-controllers.json` RETAINED during migration (live d2bd reads it)", + "detailedDesign" : "Resolve each non-root `parentZone` to one declared Zone; reject a root value, missing/unknown/self/conflicting/cyclic parent, and ancestry deeper than 16 names. Canonicalize sorted child→parent rows and feed them to the local-root allocator's private bootstrap sealer. Parent allocators derive/pre-bind privileged socket/listener state from that topology; no socket path or `parentZone` enters `Zone.spec` or a ResourceRef. Child-local ZoneLinks provide transport/local route state. `realm-controllers.json` remains published until `realm_access_resolver` is replaced. Primary reuse disposition: `adapt`. Preserved source-plan detail: adapt and retire.", + "integration" : "Nix Zone options → validated topology compiler → local-root allocator bootstrap sealer; ZoneLink controller consumes only its sealed selected-parent binding. During migration, `realm-controllers.json` remains available to the legacy resolver until the Zone topology/ZoneLink path takes over.", + "removalProof" : "Legacy `allocator-json.nix`, `realm-controller-config-json.nix`, and `/etc/d2b/allocator.json`/`realm-controllers.json` removed ONLY after the sealed `parentZone` topology and ZoneLink resolver are live; the allocator bootstrap mechanism itself remains", + "reuseAction" : "adapt", + "reuseSource" : null, + "specId" : "ADR-046-nix-configuration", + "specPath" : "docs/specs/ADR-046-nix-configuration.md", + "validation" : "Zone self-resource round-trip with exact empty spec; `parentZone` required/root-forbidden/existence/one-parent/self/cycle/16-name-depth vectors; topology seal excludes `parentZone` from Zone/resource bundles; bootstrap socket/listener regression tests", + "workItemId" : "ADR046-nix-008" + }, + { + "currentSource" : "`nixos-modules/realm-workloads-launcher-v2-json.nix` (`RealmWorkloadsLauncherV2Json`, `LauncherWorkloadSummary`; live); `nixos-modules/realm-identity-config-json.nix` (`RealmIdentityConfigJson`; live, read by d2bd from `/etc/d2b/realm-identity.json`); `packages/d2b-core/src/realm_workloads_launcher.rs`; `packages/d2b-realm-core/src/identity_config.rs`", + "dataMigration" : "Launcher and identity config are re-emitted as v3 resources; full d2b 3.0 reset; no v2 launcher/identity state import.", + "dependencyOwner" : "ADR046-nix-006; display/credential Provider work items", + "destination" : "Provider/display-wayland and Provider/shell-terminal Process configs in `zones//processes.json`; `Provider/credential-entra` Credential resource; `realm-identity.json` RETAINED during migration", + "detailedDesign" : "Launcher metadata folded into Process resource annotations; identity config → Credential resource fields (`providerRef`, `scope`, `audience`, `allowedOperations`, canonical `spec.provider` extension where Provider schema declares it; no secret bytes); `realm-identity.json` must remain until d2bd `RealmIdentityConfigJson` loading is replaced by Credential resource reader", + "integration" : "Provider/display-wayland, Provider/shell-terminal, and Provider/credential-entra consume Process/Credential resources; `d2bd` continues reading `realm-identity.json` until the Credential reader lands.", + "removalProof" : "`realm-workloads-launcher-v2-json.nix`/`realm-identity-config-json.nix` and `/etc/d2b/realm-workloads-launcher-v2.json`/`realm-identity.json` removed ONLY after display/credential Providers read resource configs", + "reuseAction" : "adapt", + "reuseSource" : null, + "specId" : "ADR-046-nix-configuration", + "specPath" : "docs/specs/ADR-046-nix-configuration.md", + "validation" : "Launcher metadata shape regression; no-secret assertion vectors", + "workItemId" : "ADR046-nix-009" + }, + { + "currentSource" : "`nixos-modules/unsafe-local-workloads-json.nix` (`WorkloadProviderKind::UnsafeLocal`/`IsolationPosture::UnsafeLocal`; current `unsafe-local-workloads.json` artifact); `nixos-modules/unsafe-local-helper.nix` (user-domain process/helper definitions); `packages/d2b-core/src/unsafe_local_workloads.rs`", + "dataMigration" : "unsafe-local declarations migrate to Host/Process resources in Nix; full d2b 3.0 reset; no helper runtime state import.", + "dependencyOwner" : "ADR046-nix-001; unsafe-local migration", + "destination" : "User-only `Host` resource in `zones//hosts.json` (`spec.isolationPosture: \"none\"`, `defaultDomain: user`, `allowedDomains: [user]`, `defaultUserRef: User/`); child `Process` resources in `zones//processes.json` using normal Process Providers; shell session supervisor → `Process` under `Provider/shell-terminal`; never a `Guest`; not a v3 Provider", + "detailedDesign" : "`isolationPosture: \"none\"` is a promoted Host base field declared at top-level `spec.isolationPosture` in the Host schema; enforced at eval time; user-only Host rejects system-domain Process refs; `NoIsolation` condition in Host status; `status.isolationPosture: none`; every `ProcessEffect` audit event under this Host carries `no_isolation=true`; OTEL telemetry never carries an isolation label; CLI/UI warning non-suppressible", + "integration" : "The unsafe-local Nix emitter produces a user-only Host plus Process resources; Provider/system-core and shell-terminal consume them; CLI/UI and audit/telemetry surfaces read resulting status/events.", + "removalProof" : "`unsafe-local-workloads-json.nix` and unsafe-local-specific Nix code removed after user-only Host/Process resources pass all `tests/host-integration/unsafe-local-helper.nix` tests", + "reuseAction" : "adapt", + "reuseSource" : null, + "specId" : "ADR-046-nix-configuration", + "specPath" : "docs/specs/ADR-046-nix-configuration.md", + "validation" : "User-only Host rejection of system-domain Process refs; `spec.isolationPosture != \"none\"` assertion rejection for user-only system-core Hosts; `NoIsolation` condition present in status; `no_isolation=true` in `ProcessEffect` audit event; OTEL attribute absent; no Guest emitted for unsafe-local declaration", + "workItemId" : "ADR046-nix-010" + }, + { + "currentSource" : "`nixos-modules/privileges-json.nix`", + "dataMigration" : "Full d2b 3.0 reset; no v2 state/config import", + "dependencyOwner" : "Broker privileges owner", + "destination" : "`nixos-modules/privileges-json.nix` (retained); `/etc/d2b/privileges.json` (retained, site-wide)", + "detailedDesign" : "Broker op catalog is not Zone-scoped; no structural change required Primary reuse disposition: `copy-unchanged`. Preserved source-plan detail: retain.", + "integration" : "`privileges-json.nix` remains a site-wide bundle artifact consumed by broker authorization and drift gates; Zone resource emission references it without Zone scoping.", + "removalProof" : "Not removed in this spec", + "reuseAction" : "copy-unchanged", + "reuseSource" : null, + "specId" : "ADR-046-nix-configuration", + "specPath" : "docs/specs/ADR-046-nix-configuration.md", + "validation" : "Existing `tests/unit/gates/drift-check.sh`", + "workItemId" : "ADR046-nix-011" + }, + { + "currentSource" : "`nixos-modules/closures-json.nix` (keyed by `d2b.vms.`); `nixos-modules/minijail-profiles.nix`", + "dataMigration" : "Closure entries are regenerated by artifact ID from `d2b.artifacts`; full d2b 3.0 reset; no v2 VM-keyed closure state import.", + "dependencyOwner" : "ADR046-nix-005; ADR046-nix-022 (artifact catalog emitter); Provider/volume-virtiofs", + "destination" : "`nixos-modules/closures-json.nix` (rewritten, keyed by artifact ID from `d2b.artifacts` with `type = \"nixos-system\"`); `nixos-modules/minijail-profiles.nix` (retained, adapted to reference Zone Guests)", + "detailedDesign" : "Closure emitter iterates `d2b.artifacts` entries with `type = \"nixos-system\"`, computes `pkgs.closureInfo`, records `storePath`/digest/size in artifact catalog (private root:d2bd 0640 field; absent from all public ResourceSpecs/status/audit/OTEL); `Guest.spec.systemArtifactId` links Guest to artifact; `Volume.source.systemArtifactId` links Volume to artifact; minijail profile emitter structurally unchanged; old `d2b.vms.` keying retired", + "integration" : "Artifact catalog and Guest/Volume resources reference closure entries; activation-helper and Provider/volume-virtiofs consume private `storePath` metadata while public surfaces remain path-free.", + "removalProof" : "Old `d2b.vms.*`-keyed closure entries removed after all Guests use `zones//guests.json` and artifact catalog", + "reuseAction" : "adapt", + "reuseSource" : null, + "specId" : "ADR-046-nix-configuration", + "specPath" : "docs/specs/ADR-046-nix-configuration.md", + "validation" : "Closure map round-trip; per-VM store hardlink integrity; `storePath` present in private catalog; `storePath` absent from all emitted public ResourceSpecs and status/audit/OTEL surfaces", + "workItemId" : "ADR046-nix-012" + }, + { + "currentSource" : "`nixos-modules/manifest.nix` (`manifestVersion` pinned contract); `nixos-modules/host-json.nix`", + "dataMigration" : "Manifest/host artifacts are regenerated as Zone bundle/Host resources; full d2b 3.0 reset; no v2 manifest state import.", + "dependencyOwner" : "ADR046-nix-005; manifest contract", + "destination" : "Per-Zone `zones//bundle.json` (`schemaVersion`); Host resource in `zones//hosts.json`", + "detailedDesign" : "`manifestVersion` → `schemaVersion`; `host.json` host config folded into Host resource spec; CHANGELOG entry for rename required", + "integration" : "`bundle-zones.nix` writes `schemaVersion` into per-Zone `bundle.json`; activation-helper validates it and Host resource consumers replace `host.json` readers.", + "removalProof" : "`manifest.nix` and `host.json` emitters removed after Zone bundle activation path passes", + "reuseAction" : "replace", + "reuseSource" : null, + "specId" : "ADR-046-nix-configuration", + "specPath" : "docs/specs/ADR-046-nix-configuration.md", + "validation" : "Schema drift gate; CHANGELOG enforcement", + "workItemId" : "ADR046-nix-013" + }, + { + "currentSource" : "`nixos-modules/assertions.nix` (CIDR overlap, `sys-` prefix, VM-name regex, platform gate)", + "dataMigration" : "None — docs/tooling only; no runtime state", + "dependencyOwner" : "ADR046-nix-001, ADR046-nix-002", + "destination" : "`nixos-modules/assertions.nix`", + "detailedDesign" : "Migrate existing assertions to Zone/Resource terminology; add ref validation, owner cycles, CIDR overlap (Zones), provider resolution, RoleBinding verb set assertions Primary reuse disposition: `adapt`. Preserved source-plan detail: extend in place.", + "integration" : "`assertions.nix` validates Zone/Resource declarations before index, bundle, and schema validation derivations run; failing configs block activation artifacts.", + "removalProof" : "No removal; extended only", + "reuseAction" : "adapt", + "reuseSource" : null, + "specId" : "ADR-046-nix-configuration", + "specPath" : "docs/specs/ADR-046-nix-configuration.md", + "validation" : "Each new assertion has a failing-config test vector", + "workItemId" : "ADR046-nix-014" + }, + { + "currentSource" : "`nixos-modules/host.nix`, `host-daemon.nix` (fixed local-root endpoint set), `host-activation.nix`, `host-users.nix`", + "dataMigration" : "Host activation reads newly staged Zone bundles on first v3 switch; full d2b 3.0 reset; no v2 daemon runtime state import.", + "dependencyOwner" : "ADR046-nix-001; host activation", + "destination" : "Same files; updated to use Zone bundle activation path and Zone resource state dirs", + "detailedDesign" : "`d2b-activation-helper` updated to validate/stage per-Zone bundles; `d2bd.service` updated to read Zone bundle; `d2b` group retained for `SO_PEERCRED` Primary reuse disposition: `adapt`. Preserved source-plan detail: retain and adapt.", + "integration" : "Host activation units call `d2b-activation-helper` against per-Zone bundles, then `d2bd.service` reads the staged Zone bundle; `SO_PEERCRED` authorization remains via the `d2b` group.", + "removalProof" : "No removal; adapted in place", + "reuseAction" : "adapt", + "reuseSource" : null, + "specId" : "ADR-046-nix-configuration", + "specPath" : "docs/specs/ADR-046-nix-configuration.md", + "validation" : "Host-integration test with Zone bundle activation and daemon readiness", + "workItemId" : "ADR046-nix-015" + }, + { + "currentSource" : "`nixos-modules/network.nix`, `nixos-modules/net.nix` (including `lib.mkForce` DHCP neutralizer and per-env MTU/MSS/east-west wiring)", + "dataMigration" : "Network resource specs are generated from v3 Nix declarations; full d2b 3.0 reset; no v2 bridge/NAT runtime state import.", + "dependencyOwner" : "ADR046-nix-002; Provider/network-local dossier", + "destination" : "Network reconciliation by `Provider/network-local` Process resources", + "detailedDesign" : "Current bridge/NAT/DHCP/firewall Nix units retained; `Provider/network-local` controller emits equivalent configuration from Network resources; `lib.mkForce` neutralization preserved Primary reuse disposition: `copy-unchanged`. Preserved source-plan detail: retain until Provider successor.", + "integration" : "Network resources feed Provider/network-local parity implementation while `net.nix`/`network.nix` remain the fallback; network eval tests compare both paths.", + "removalProof" : "`network.nix`/`net.nix` removed after `Provider/network-local` parity and `tests/net-vm-network-eval.sh` passes", + "reuseAction" : "copy-unchanged", + "reuseSource" : null, + "specId" : "ADR-046-nix-configuration", + "specPath" : "docs/specs/ADR-046-nix-configuration.md", + "validation" : "`tests/net-vm-network-eval.sh` passes against Network resource spec", + "workItemId" : "ADR046-nix-016" + }, + { + "currentSource" : "`nixos-modules/store.nix` (`ProcessRole::Virtiofsd` and `share.source == \"/nix/store\"` sentinel; per-VM hardlink farm)", + "dataMigration" : "Full d2b 3.0 reset; no v2 store metadata import", + "dependencyOwner" : "ADR046-nix-012; Provider/volume-virtiofs", + "destination" : "Per-VM store reconciliation by `Provider/volume-virtiofs` EphemeralProcess/Process resources", + "detailedDesign" : "`store.nix` retained; `Provider/volume-virtiofs` controller creates equivalent EphemeralProcess; per-VM store path derived from Zone stateDir + Guest name via Provider, not raw path in spec Primary reuse disposition: `copy-unchanged`. Preserved source-plan detail: retain until Provider successor.", + "integration" : "Guest/Volume resources drive Provider/volume-virtiofs, which stages hardlink farm processes consumed by Guest process resources; `store.nix` remains until parity.", + "removalProof" : "`store.nix` removed after `Provider/volume-virtiofs` manages farm lifecycle and existing store tests pass", + "reuseAction" : "copy-unchanged", + "reuseSource" : null, + "specId" : "ADR-046-nix-configuration", + "specPath" : "docs/specs/ADR-046-nix-configuration.md", + "validation" : "Store hardlink integrity; no direct `/nix/store` export", + "workItemId" : "ADR046-nix-017" + }, + { + "currentSource" : "`nixos-modules/components/graphics.nix` (`ProcessRole::Gpu`, `GpuRenderNode`, `Video`); `components/tpm.nix` (`ProcessRole::Swtpm`, `SwtpmPreStartFlush`); `components/usbip.nix` (`ProcessRole::Usbip`); `components/audio/` (`ProcessRole::Audio`)", + "dataMigration" : "Component toggles migrate to v3 Device/Guest resource declarations; full d2b 3.0 reset; no v2 component runtime state import.", + "dependencyOwner" : "ADR046-nix-002; Provider dossiers for graphics, tpm, usbip, audio", + "destination" : "`Provider/device-tpm`, `Provider/device-usbip`, `Provider/device-gpu`, `Provider/audio-pipewire` resource install declarations in `options-zones-resources.nix`", + "detailedDesign" : "`d2b.vms..tpm.enable = true` → `d2b.zones..resources.vm-tpm = { type = \"Device\"; spec = { providerRef = \"Provider/device-tpm\"; ... }; }`; all component eval assertions migrated to `assertions.nix`; GuestSshReadiness retired at v3 cutover; Usbip long-lived backend/proxy → `Process`, per-busid attach/detach → `EphemeralProcess`, both owned by `Provider/device-usbip` Primary reuse disposition: `replace`. Preserved source-plan detail: each component becomes a Provider install resource + Device/Guest spec field.", + "integration" : "Device/Guest specs feed Provider/device-* and Provider/audio-pipewire install declarations; migrated assertions/tests consume Zone resource configs instead of component toggles.", + "removalProof" : "`components/` Nix units removed after Provider resource install achieves parity and all component eval tests pass against Zone resource configs", + "reuseAction" : "replace", + "reuseSource" : null, + "specId" : "ADR-046-nix-configuration", + "specPath" : "docs/specs/ADR-046-nix-configuration.md", + "validation" : "Existing component eval tests; `tests/usbip-gating-eval.sh`; `tests/video-contract-eval.sh`", + "workItemId" : "ADR046-nix-018" + }, + { + "currentSource" : "`nixos-modules/bundle-artifacts.nix` (`artifactModule` submodule, mode/ownership); `nixos-modules/bundle.nix` (digest chain, SHA256SUMS); `packages/xtask/src/main.rs` (`gen-schemas`); no current per-ResourceType JSON Schema under `docs/reference/schemas/v3/`", + "dataMigration" : "None — docs/tooling only; no runtime state", + "dependencyOwner" : "ADR046-nix-005; ADR046-nix-001; `d2b-contracts` schema generation (ADR046-nix-027)", + "destination" : "`docs/reference/schemas/v3/.json` for each ResourceType; `nixos-modules/resource-schema-validation.nix` (validates emitted spec against committed JSON Schema at build time); `nixos-modules/provider-settings-validation.nix` (validates `spec.provider.settings` where declared in schema, and Provider `config`, against Provider-embedded schema at build time); `nixos-modules/assertions.nix` (Credential ref enforcement, secret-pattern rejection)", + "detailedDesign" : "`cargo xtask gen-schemas` emits one JSON Schema per ResourceType under `docs/reference/schemas/v3/`; Nix derivation reads these schemas from `pkgs.d2b-resource-schemas` and validates every emitted `spec` JSON before producing the Zone bundle; Provider-settings validation reads `settingsSchemaDigest` from `provider-catalog.json` and resolves the schema from the Provider package closure; Credential ref enforcement: eval assertion rejects any `spec` string field matching `-----BEGIN`, `eyJ`, or a hex string ≥ 32 bytes in a secret-typed field; `managedBy` in any input spec rejected at eval (core-set runtime field, never in Nix input); bundle integrity: `candidateId`/`contentId` computed over canonical sorted output Primary reuse disposition: `adapt`. Preserved source-plan detail: extend xtask schema generation; new Nix eval/build validation hooks.", + "integration" : "Validation hooks wired into `bundle-zones.nix` derivation; `d2b-activation-helper` re-verifies digest chain at staging", + "removalProof" : "Not removed; extended as new ResourceTypes are added", + "reuseAction" : "adapt", + "reuseSource" : null, + "specId" : "ADR-046-nix-configuration", + "specPath" : "docs/specs/ADR-046-nix-configuration.md", + "validation" : "Schema round-trip: emit spec, validate against schema, verify byte-identical re-emit; `spec.provider.settings` rejection test (unknown field, out-of-bounds value, raw store path, secret bytes) where schema declares the field; Provider `config` rejection test (unknown field); Credential ref enforcement: PEM-in-spec rejected; secret-pattern-in-spec rejected; valid `Credential/` ref accepted; `managedBy` in spec input rejected at eval", + "workItemId" : "ADR046-nix-019" + }, + { + "currentSource" : "No current equivalent; current `bundle.nix` replaces all artifacts atomically with no per-resource cleanup tracking", + "dataMigration" : "Full d2b 3.0 reset; existing resources gain `managedBy`/`configurationGeneration` only when first v3 activation creates them; no v2 state/config import.", + "dependencyOwner" : "ADR046-nix-005; ADR046-nix-001; configuration-publication controller (ADR-046-core-controllers)", + "destination" : "Configuration-publication controller handler in `packages/d2bd/src/config_publication.rs`; `ConfigurationOwnedClassifier`; `AbsentResourceReaper`; `Zone` status conditions in `d2b-contracts/src/v3/zone_status.rs`; cleanup audit emitter in `d2b-state/src/audit_segments.rs`", + "detailedDesign" : "`ConfigurationOwnedClassifier`: classify resources by core-set `managedBy` field only — `managedBy=configuration` resources are owned by config publication; `managedBy=controller` and `managedBy=api` resources are never touched. At activation, diff new-generation bundle name+type set against all resources with `managedBy=configuration` in the Zone store; resources absent from the new bundle are enqueued for Delete. Never infer ownership from `ownerRef`, labels, or absence from emitted files. `AbsentResourceReaper`: processes the Absent queue asynchronously; does not block pointer swap (step 4); sets `status.phase=Pending` + `PendingDeletion` condition (`reason: AbsentFromConfiguration`); waits for all finalizers to clear; commits a single store transaction that appends the `Deleted` revision and removes the resource row and its indexes; subsequent `Get` returns not-found; the `ResourceDelete` audit event is appended afterward with dedup/exactly-once recovery. Zone phase: `Pending` during pointer-swap-to-first-reconcile window; `Degraded` while any `managedBy=configuration` resource carries `PendingDeletion` or a ZoneLink lags; `Ready` when all reconciled. Generation pruning: prune when `generationIndex ≤ activeIndex - retainedGenerations` AND all enqueued resources from that generation have reached `Deleted`. Rollback: re-adopt `managedBy=configuration` resources in `Pending/PendingDeletion` back to the rollback target generation's owned set", + "integration" : "`d2b-activation-helper` sets `managedBy=configuration` + `configurationGeneration` on every resource it activates; controller reads these fields to determine owned set — never `ownerRef` or bundle membership alone", + "removalProof" : "Not removed; extended as new ResourceTypes are added", + "reuseAction" : "create", + "reuseSource" : null, + "specId" : "ADR-046-nix-configuration", + "specPath" : "docs/specs/ADR-046-nix-configuration.md", + "validation" : "Classification: `managedBy=controller` resource never enqueued (even if absent from bundle). `managedBy=api` resource never enqueued. `managedBy=configuration` resource absent from new bundle always enqueued. Finalizer safety: resource with active finalizer enters DeletionBlocked; not force-deleted; stays in `Pending`. Final deletion: a single store transaction appends the `Deleted` revision and removes the resource row and its indexes; subsequent `Get` returns not-found; `ResourceDelete` audit event is appended afterward with dedup/exactly-once recovery. Zone status: `Pending` during activation; `Degraded` while PendingDeletion outstanding; `Ready` when clean. Audit: `ResourceDelete` event includes `configurationGeneration` field.", + "workItemId" : "ADR046-nix-020" + }, + { + "currentSource" : "`packages/d2b-contract-tests/tests/` (existing workspace policy lints: `tests/workspace-member-sort.rs`, `tests/crate-naming.rs`); no current Provider crate layout gate exists", + "dataMigration" : "None — docs/tooling only; no runtime state", + "dependencyOwner" : "ADR046-nix-001; `d2b-contracts` workspace policy tests", + "destination" : "`packages/d2b-contract-tests/tests/provider-crate-layout.rs`; workspace scan in `packages/xtask/src/main.rs` (extend `check-workspace` or add `check-provider-layout` subcommand)", + "detailedDesign" : "Parse the root `packages/Cargo.toml` workspace member list; for every member path matching `packages/d2b-provider-*-*`: assert (1) `src/` directory exists and contains at least one `.rs` file; (2) `tests/` directory exists and contains at least one `.rs` file; (3) `integration/` directory exists and contains at least one `.rs` or fixture file; (4) `README.md` exists and is ≥ 200 bytes. All four conditions required; any single failure fails the test with a structured message naming the crate and missing path. Test runs as `cargo test -p d2b-contract-tests provider_crate_layout`; wired into `make test-policy`.", + "integration" : "Wired into `make test-policy` (same gate family as existing workspace policy tests); no new `Makefile` target needed unless `test-policy` does not yet exist", + "removalProof" : "Not removed; extended as new Provider crates are added to the workspace", + "reuseAction" : "create", + "reuseSource" : null, + "specId" : "ADR-046-nix-configuration", + "specPath" : "docs/specs/ADR-046-nix-configuration.md", + "validation" : "Fixture: add a stub `packages/d2b-provider-test-missing-integration/` with `src/lib.rs` and `tests/smoke.rs` but no `integration/` and no `README.md`; assert test fails naming both missing paths. Add complete stub with all four paths; assert test passes.", + "workItemId" : "ADR046-nix-021" + }, + { + "currentSource" : "No current equivalent for a separate artifact catalog. Current `nixos-modules/bundle.nix` embeds package derivation references inline. Current `nixos-modules/closures-json.nix` uses `pkgs.closureInfo` keyed by `d2b.vms.`.", + "dataMigration" : "Artifact catalog entries are generated from new `d2b.artifacts` declarations; full d2b 3.0 reset; no v2 `closureRef`/`nixosSystem` metadata import.", + "dependencyOwner" : "ADR046-nix-005 (bundle derivation); `d2b-contracts` schema generation (ADR046-nix-027)", + "destination" : "`nixos-modules/artifact-catalog.nix` (new emitter); `nixos-modules/options-artifacts.nix` (new option: `d2b.artifacts. = { package; type; }`); `/etc/d2b/artifact-catalog.json` (output artifact, `root:d2bd` 0640); `nixos-modules/bundle-zones.nix` (extend to include artifact catalog digest in `bundle.json`); `nixos-modules/options-zones-resources.nix` (replace `closureRef` / `nixosSystem` helpers with `systemArtifactId` validation)", + "detailedDesign" : "`d2b.artifacts.` attrset option: `id` matches `^[a-z][a-z0-9-]*$`; `type ∈ { \"provider\", \"nixos-system\", \"nixos-module-set\", \"config-bundle\" }`; no other fields. Emitter computes `pkgs.closureInfo` for each entry and writes `artifact-catalog.json` with sorted entries (by `artifactId`) containing `artifactId`, `type`, `storePath` (private, for activation-helper staging), `packageDigest`, `closureDigest`, `closureSize`. `storePath` is a private field of the root:d2bd 0640 file; it is never emitted in public ResourceSpecs, status fields, audit records, or OTEL telemetry. The `bundle.json` manifest includes the artifact catalog file entry and its SHA256 digest. `d2b-activation-helper` reads `storePath` from the catalog to resolve and stage each artifact; verifies catalog digest before staging. Build-time validation: `artifactId` / `systemArtifactId` / `source.systemArtifactId` fields in resource specs resolve against `d2b.artifacts`; type-mismatch fails with a structured error. `d2b.providerCatalog..package` option is removed; replaced by `d2b.providerCatalog..artifactId`. `Guest.spec.systemArtifactId` replaces the former `nixosSystem` Nix-only helper. `Volume.source.systemArtifactId` replaces `source.closureRef`.", + "integration" : "`nixos-modules/closures-json.nix` rewritten (ADR046-nix-012) to key by artifact ID; `nixos-modules/bundle-zones.nix` includes artifact catalog in integrity chain", + "removalProof" : "Not removed; extended as new artifact types are added", + "reuseAction" : "create", + "reuseSource" : null, + "specId" : "ADR-046-nix-configuration", + "specPath" : "docs/specs/ADR-046-nix-configuration.md", + "validation" : "Artifact catalog round-trip: declare artifact, build, verify JSON entry present with correct type/digests/storePath; missing artifact ID fails build with structured error; wrong-type artifact fails build; `storePath` absent from all public ResourceSpecs and status/audit/OTEL surfaces", + "workItemId" : "ADR046-nix-022" + }, + { + "currentSource" : "Main commit source row below: `packages/d2b-session/` at `a1cc0b2d`, including session engine, handshake, lifecycle, scheduler, record protection, fragmentation, transport, driver, server, and metrics modules.", + "dataMigration" : "Full d2b 3.0 reset; no v2 session runtime state/config import", + "dependencyOwner" : "ADR-046-componentsession-and-bus spec; ADR046-nix-027 (wire contract) must land first", + "destination" : "`packages/d2b-bus/src/session/` (new crate `d2b-bus`); `ComponentSessionDriver` becomes the central abstraction for all Zone bus sessions.", + "detailedDesign" : "Complete session protocol: preface negotiation, Noise handshake profiles, record protection, replay cache, fair scheduler, fragmentation/reassembly, keepalive, ttrpc bridge, generation discovery, and `ComponentSessionDriver` application surface. Primary reuse disposition: `adapt`. Preserved source-plan detail: Reuse from main commit `a1cc0b2d`; adapt ADR45 EndpointPurpose/ServicePackage names listed in `ADR45 exclusions` while preserving wire tag values..", + "integration" : "`d2b-bus` session runtime is used by Zone bus sessions for local-root controller to broker, controller to guest agent, and controller to provider agent; `serve_ttrpc_services` exposes service handlers.", + "removalProof" : "ADR45 EndpointPurpose/ServicePackage realm names listed in `ADR45 exclusions` are adapted during copy; no prior v3 session owner to remove.", + "reuseAction" : "adapt", + "reuseSource" : null, + "specId" : "ADR-046-nix-configuration", + "specPath" : "docs/specs/ADR-046-nix-configuration.md", + "validation" : "Port `component_session.rs` and `noise_vectors.rs`; run `make test-rust`; Noise KAT vectors must pass after copy.", + "workItemId" : "ADR046-nix-023" + }, + { + "currentSource" : "Main commit source row below: `packages/d2b-session/src/attachment.rs` and `packages/d2b-session/src/streams.rs` at `a1cc0b2d`.", + "dataMigration" : "Full d2b 3.0 reset; no v2 attachment or stream state/config import", + "dependencyOwner" : "ADR046-nix-023; ADR046-nix-027", + "destination" : "`packages/d2b-bus/src/session/` (same crate as ADR046-nix-023).", + "detailedDesign" : "Attachment ownership validates descriptors only after authenticated decrypt; unconsumed payloads close on drop. NamedStreamMux preserves half-close, reset, per-stream limits, aggregate limits, and credit-based flow control. Primary reuse disposition: `adapt`. Preserved source-plan detail: Reuse from main commit `a1cc0b2d`; keep attachment/stream semantics and descriptor validation, with no ADR45 realm adaptation required for this item..", + "integration" : "`d2b-bus` session transport validates attachments after authenticated decrypt and exposes named streams to ttrpc/services; Unix/vsock transports provide payloads.", + "removalProof" : "None — net-new; no prior owner to remove", + "reuseAction" : "adapt", + "reuseSource" : null, + "specId" : "ADR-046-nix-configuration", + "specPath" : "docs/specs/ADR-046-nix-configuration.md", + "validation" : "Port `component_session.rs` attachment ownership, stream mux, and credit-accounting tests.", + "workItemId" : "ADR046-nix-024" + }, + { + "currentSource" : "Main commit source row below: `packages/d2b-session/src/cancellation.rs`, `deadline.rs`, and `bootstrap.rs` at `a1cc0b2d`.", + "dataMigration" : "Full d2b 3.0 reset; no v2 cancellation/deadline/bootstrap state import", + "dependencyOwner" : "ADR046-nix-023; ADR046-nix-027", + "destination" : "`packages/d2b-bus/src/session/`.", + "detailedDesign" : "Per-generation `RequestRegistry`, request-wide cancellation, `DeadlineBudget::admit_metadata()` as the inbound metadata gate, and single-use `BootstrapAdmission::consume()` replay protection. Primary reuse disposition: `adapt`. Preserved source-plan detail: Reuse from main commit `a1cc0b2d`; adapt `CancelRequest`/`CancelResponse` module paths if v3 services move the common proto types..", + "integration" : "`RequestRegistry`, `DeadlineBudget`, and `BootstrapAdmission` feed all `d2b-bus` inbound ttrpc calls and resource API cancellation paths.", + "removalProof" : "ADR45 common proto type paths are adapted to the v3 service module; no prior v3 owner to remove.", + "reuseAction" : "adapt", + "reuseSource" : null, + "specId" : "ADR-046-nix-configuration", + "specPath" : "docs/specs/ADR-046-nix-configuration.md", + "validation" : "Port cancel-before-dispatch, cancel-after-dispatch, generation-mismatch cancel, and deadline admit tests; add v3 common-proto path adapter test.", + "workItemId" : "ADR046-nix-025" + }, + { + "currentSource" : "Main commit source row below: `packages/d2b-session-unix/src/{adapter,credit,descriptor,pidfd,socket,systemd,vsock}.rs` at `a1cc0b2d`.", + "dataMigration" : "Full d2b 3.0 reset; no v2 Unix transport state/config import", + "dependencyOwner" : "ADR046-nix-023", + "destination" : "`packages/d2b-bus/src/transport/unix/`.", + "detailedDesign" : "Unix seqpacket/stream transports, pidfd identity verification, six-scope credit pool with host/process bounds, systemd-activated seqpacket listener support, and length-prefixed vsock framing. Primary reuse disposition: `adapt`. Preserved source-plan detail: Reuse from main commit `a1cc0b2d`; retain host-socket/native-vsock feature gates and replace only ADR45 socket-name strings with Zone bootstrap config..", + "integration" : "`d2b-bus` listeners and Provider/guest sessions instantiate UnixSeqpacket/UnixStream/vsock transports; credit pool enforces attachment FD budgets before handing payloads to session runtime.", + "removalProof" : "ADR45 socket-name strings listed in exclusions are replaced by Zone bootstrap config; no prior v3 transport owner to remove.", + "reuseAction" : "adapt", + "reuseSource" : null, + "specId" : "ADR-046-nix-configuration", + "specPath" : "docs/specs/ADR-046-nix-configuration.md", + "validation" : "Port `unix_session.rs` seqpacket, stream, pidfd, FD attachment, and credit pool tests; add Zone bootstrap socket-name fixture.", + "workItemId" : "ADR046-nix-026" + }, + { + "currentSource" : "Main commit source row below: `packages/d2b-contracts/src/v2_component_session.rs` at `a1cc0b2d` plus committed session/noise tests.", + "dataMigration" : "Full d2b 3.0 reset; no v2 ComponentSession contract state import", + "dependencyOwner" : "ADR-046-componentsession-and-bus; naming and wire enumeration decisions per ADR-046-componentsession-and-bus owning spec before final adoption", + "destination" : "`packages/d2b-contracts/src/v3/component_session.rs`.", + "detailedDesign" : "Canonical wire values, bounded vectors, binary size constants, closed-enum tag codecs, and fail-closed validation for the ComponentSession protocol. Primary reuse disposition: `adapt`. Preserved source-plan detail: Reuse wire constants/types from main commit `a1cc0b2d`; rename ADR45 realm-oriented variants only as directed by the bus owning spec and preserve numeric tags..", + "integration" : "`d2b-contracts` v3 component_session module is consumed by `d2b-bus`, `d2b-session`, `d2b-session-unix`, `d2b-client`, and provider toolkit for shared wire constants/enums.", + "removalProof" : "ADR45 realm-named enum variants are renamed only if the owning spec requires while preserving wire tags; no prior v3 contract owner to remove.", + "reuseAction" : "adapt", + "reuseSource" : null, + "specId" : "ADR-046-nix-configuration", + "specPath" : "docs/specs/ADR-046-nix-configuration.md", + "validation" : "Port component session and Noise vector tests; add wire-tag stability gates for renamed role/service/purpose variants.", + "workItemId" : "ADR046-nix-027" + }, + { + "currentSource" : "Main commit source row below: `packages/d2b-contracts/src/generated_v2_services/`, `v2_guest_services.rs`, and `v2_component_session.rs` service inventory at `a1cc0b2d`.", + "dataMigration" : "Full d2b 3.0 reset; no v2 generated service state import", + "dependencyOwner" : "ADR046-nix-027; Zone service naming per ADR-046-componentsession-and-bus owning spec", + "destination" : "`packages/d2b-contracts/src/v3/services/`.", + "detailedDesign" : "Versioned ttrpc service modules preserve server dispatch tables, shared request/cancel/outcome/error types, and service fingerprint inventory. Primary reuse disposition: `adapt`. Preserved source-plan detail: Reuse generated ttrpc service tables from main commit `a1cc0b2d`; adapt ADR45 Realm/Workload fields and service names to v3 Zone/Resource naming per owning bus spec..", + "integration" : "Generated v3 services feed `d2b-bus` server/client dispatch, provider-toolkit conformance, and `SERVICE_INVENTORY` fingerprint validation.", + "removalProof" : "ADR45 realm/user WorkloadId fields listed in exclusions are adapted to Zone/ResourceName; `generated_v2_services` remains only as reuse source.", + "reuseAction" : "adapt", + "reuseSource" : null, + "specId" : "ADR-046-nix-configuration", + "specPath" : "docs/specs/ADR-046-nix-configuration.md", + "validation" : "Port provider-toolkit conformance and service fingerprint assertions; add v3 service-name gate for Zone service and ResourceName field adaptation.", + "workItemId" : "ADR046-nix-028" + }, + { + "currentSource" : "Main commit source row below: `packages/d2b-provider/src/{registry,rpc,instance,context}.rs` at `a1cc0b2d`.", + "dataMigration" : "Full d2b 3.0 reset; no v2 ProviderRegistry runtime state import", + "dependencyOwner" : "ADR046-nix-027; ADR046-nix-028; EndpointRole naming per ADR-046-componentsession-and-bus owning spec", + "destination" : "`packages/d2b-provider/src/` (adapt in place).", + "detailedDesign" : "ProviderRegistry snapshot admission/drain, bounded in-flight caps, typed RpcProviderProxy dispatch, and build-time RegistryLimits validation. Primary reuse disposition: `adapt`. Preserved source-plan detail: Reuse ProviderRegistry/RPC runtime from main commit `a1cc0b2d`; adapt `SessionIdentity` to Zone naming and update EndpointRole/ServicePackage names per owning bus spec..", + "integration" : "Zone controller publishes active ProviderRegistry snapshots; provider-toolkit RPC proxy admits sessions through `d2b-bus` and dispatches typed Provider operations.", + "removalProof" : "ADR45 role/service names are adapted per owning bus spec; no prior v3 ProviderRegistry owner to remove.", + "reuseAction" : "adapt", + "reuseSource" : null, + "specId" : "ADR-046-nix-configuration", + "specPath" : "docs/specs/ADR-046-nix-configuration.md", + "validation" : "Port `d2b-provider` `runtime.rs` tests; add v3 AdmissionOptions peer-role and SessionIdentity zone-name gates.", + "workItemId" : "ADR046-nix-029" + }, + { + "currentSource" : "Main commit source row below: `packages/d2b-provider-toolkit/src/{adapter,server,conformance,registration}.rs` at `a1cc0b2d`.", + "dataMigration" : "Full d2b 3.0 reset; no v2 provider-toolkit runtime state import", + "dependencyOwner" : "ADR046-nix-029; ADR046-nix-028; EndpointRole and ServicePackage naming per ADR-046-componentsession-and-bus owning spec", + "destination" : "`packages/d2b-provider-toolkit/src/` (adapt in place).", + "detailedDesign" : "ProviderAgentAdapter validates ComponentSession identity, GeneratedProviderServiceServer dispatches all ProviderType method families with bounded stores/in-flight state, and conformance kit gates every Provider implementation. Primary reuse disposition: `adapt`. Preserved source-plan detail: Reuse provider toolkit from main commit `a1cc0b2d`; adapt endpoint role/service variant names and v2_identity imports while preserving wire tags..", + "integration" : "Provider processes embed `GeneratedProviderServiceServer`; `ProviderAgentAdapter` validates ComponentSession identity before invoking registry instances; conformance kit gates every Provider crate.", + "removalProof" : "ADR45 `v2_identity` imports are adapted to ZoneId/ResourceName; no prior v3 toolkit owner to remove.", + "reuseAction" : "adapt", + "reuseSource" : null, + "specId" : "ADR-046-nix-configuration", + "specPath" : "docs/specs/ADR-046-nix-configuration.md", + "validation" : "Port `conformance.rs`; assert endpoint role/service wire tags remain 7/4 after any variant rename.", + "workItemId" : "ADR046-nix-030" + }, + { + "currentSource" : "None — D096/D098 semantic Service/Binding authoring and projection factories are net-new in v3", + "dataMigration" : "Full d2b 3.0 reset; old audio/USB/security-key/observability options lower to new authority Service plus authored Binding only where the owning dossier explicitly preserves migration, and no old public type alias survives", + "dependencyOwner" : "ADR046-provider-004, ADR046-zone-control-019, ADR046-zone-control-020, ADR046-zone-control-024; Nix resource compiler owner", + "destination" : "`nixos-modules/resources-sharing.nix`; `nixos-modules/assertions.nix`; `tests/unit/nix/cases/resource-sharing.nix`", + "detailedDesign" : "Compile the eight exact D098 semantic Service/Binding types plus `ResourceExport`/`ResourceImport` without a second vocabulary. Nix authors authority Services and consumer Bindings, never projection Services; Binding requires same-Zone `serviceRef` and allowed target, while all observed realization remains absent from spec. Core receives a stable projection name/factory fingerprint and creates exactly one same-type Service with ResourceImport ownership, `providerRef`, semantic base/import fields, and no `spec.provider`. Reject Device/Endpoint/Binding export/projection, implementation-qualified/former `*State` aliases, implementation detail in base schemas, cross-Zone refs, USB export without every policy opt-in, and any USB/security-key physical resource that does not use the exact Core-derived `physical-usb-backing` tuple class.", + "integration" : "The per-Zone generation uses ADR046-provider-004 common schema fingerprints and ADR046-zone-control-019/020 factory metadata; the four initial Provider modules contribute only strict Provider extensions and implementation config.", + "removalProof" : "Provider-local duplicate Nix base schemas and old direct Device/Endpoint/`*State` projection emitters are removed after the shared emitter and all four dossier migration tests pass.", + "reuseAction" : "create", + "reuseSource" : null, + "specId" : "ADR-046-nix-configuration", + "specPath" : "docs/specs/ADR-046-nix-configuration.md", + "validation" : "Fast Nix eval/build tests cover all four exact pairs, common base schema discovery, canonical minimal base without `spec.provider`, stable lowering across repeated evaluations, same-Zone refs/targets, strict Provider extension placement, Service-only export, exactly-one Core projection metadata with no `spec.provider`, no auto-Binding, status-field rejection in spec, USB/security-key resolution to one byte-identical Host-global `(Host, physical-usb-backing, opaqueKeyDigest)` collision before effects, Provider-private-class bypass rejection, and rejection of all forbidden names/aliases.", + "workItemId" : "ADR046-nix-031" + }, + { + "currentSource" : "Main commit source row below: `packages/d2b-client/src/{client,session,target,service,daemon_service,guest_service,host_socket}.rs` at `a1cc0b2d`.", + "dataMigration" : "Full d2b 3.0 reset; no v2 client state/config import", + "dependencyOwner" : "ADR046-nix-029; ADR046-nix-027; TargetInput v3 shape per ADR-046-componentsession-and-bus owning spec", + "destination" : "`packages/d2b-client/src/` (adapt in place).", + "detailedDesign" : "Transport-neutral typed async client, ComponentSessionConnector abstraction, precise SessionFailure classification, signed metadata envelopes, named-stream client abstraction, and host-socket connector. Primary reuse disposition: `adapt`. Preserved source-plan detail: Reuse async client from main commit `a1cc0b2d`; adapt TargetInput, ServiceOwner, service packages, and local socket identity to Zone/ResourceRef semantics..", + "integration" : "CLI/controllers construct TargetInput/MetadataInput; `d2b-client` resolves Zone/Resource routes via `d2b-bus` and reuses ComponentSessionConnector for calls/streams.", + "removalProof" : "ADR45 TargetInput Realm/Workload forms are replaced by Zone/ResourceRef forms; no prior v3 client owner to remove.", + "reuseAction" : "adapt", + "reuseSource" : null, + "specId" : "ADR-046-nix-configuration", + "specPath" : "docs/specs/ADR-046-nix-configuration.md", + "validation" : "Port `d2b-client` tests; add ResourceRef TargetInput and Zone bootstrap socket identity gates.", + "workItemId" : "ADR046-nix-032" + }, + { + "currentSource" : "Main commit source row below: `packages/d2b-realm-router/src/service_v2.rs` and tests at `a1cc0b2d`.", + "dataMigration" : "Full d2b 3.0 reset; no v2 routing state import", + "dependencyOwner" : "ADR046-nix-023; ADR046-nix-027; naming and service name per ADR-046-componentsession-and-bus owning spec", + "destination" : "`packages/d2b-bus/src/routing/zone_service.rs`.", + "detailedDesign" : "ZoneSessionAuthority preserves no-host-credential custody, gateway/ZoneLink custody behavior, bounded concurrent dispatch, and graceful shutdown semantics. Primary reuse disposition: `adapt`. Preserved source-plan detail: Reuse routing service behavior from main commit `a1cc0b2d`; rename RealmSessionAuthority/service identifiers to Zone terms per owning bus spec while preserving custody behavior..", + "integration" : "`d2b-bus` Zone service handles route admission and shortcut lifecycle; ZoneSessionAuthority feeds session/audit/metric labels with stable custody state.", + "removalProof" : "ADR45 RealmService name and RealmId fields are adapted to Zone service; no prior v3 router owner to remove.", + "reuseAction" : "adapt", + "reuseSource" : null, + "specId" : "ADR-046-nix-configuration", + "specPath" : "docs/specs/ADR-046-nix-configuration.md", + "validation" : "Port `realm_service_v2.rs` and topology harness tests with Zone naming; add service-name wire gate.", + "workItemId" : "ADR046-nix-033" + }, + { + "currentSource" : "Main commit source row below: `packages/d2bd/src/provider_registry.rs` at `a1cc0b2d`.", + "dataMigration" : "Full d2b 3.0 reset; provider bundle format bumps to v3 and no v2 provider registry state import.", + "dependencyOwner" : "ADR046-nix-029; ADR046-nix-028; ADR046-nix-027; PROVIDER_BUNDLE_VERSION bump required on any bundle artifact format change", + "destination" : "`packages/d2bd/src/provider_registry.rs` (adapt in place).", + "detailedDesign" : "Named ProviderCompositionError variants, first-party factory instantiation, bundle resolver validation, version/schema constants, and monotone lifecycle operation IDs. Primary reuse disposition: `adapt`. Preserved source-plan detail: Reuse fail-closed provider composition from main commit `a1cc0b2d`; adapt v2 identity/bundle artifact types to v3 provider registry contracts and bump bundle versions..", + "integration" : "`provider_registry.rs` composes the v3 provider catalog into active ProviderRegistry snapshots consumed by `d2b-bus`/provider runtime and bundle validation.", + "removalProof" : "ADR45 `provider_registry_v2` bundle artifact types are replaced by `d2b_contracts::v3::provider_registry` after v3 bundle schema lands.", + "reuseAction" : "adapt", + "reuseSource" : null, + "specId" : "ADR-046-nix-configuration", + "specPath" : "docs/specs/ADR-046-nix-configuration.md", + "validation" : "Port provider_registry composition tests; add PROVIDER_BUNDLE_VERSION/SCHEMA_VERSION v3 bump gate and ZoneId/ResourceName binding test.", + "workItemId" : "ADR046-nix-034" + }, + { + "currentSource" : "Main commit source row below: `packages/d2bd/src/provider_effects.rs` at `a1cc0b2d`.", + "dataMigration" : "Full d2b 3.0 reset; no v2 lifecycle mutation state import", + "dependencyOwner" : "ADR046-nix-034; ADR046-nix-029; GuestLifecycleRequest ResourceRef addressing per ADR-046-componentsession-and-bus owning spec", + "destination" : "`packages/d2bd/src/provider_effects.rs` (adapt in place).", + "detailedDesign" : "DaemonEffectAdapters bind semantic effect ports; ProviderLifecycleDispatch bounds lifecycle mutations and routes broker start/stop through blocking adapters with idempotency-key deduplication. Primary reuse disposition: `adapt`. Preserved source-plan detail: Reuse descriptor-bound effect dispatch from main commit `a1cc0b2d`; adapt lifecycle request wire type from VM name to Zone-scoped Guest ResourceRef..", + "integration" : "`ProviderLifecycleDispatch` receives v3 Guest lifecycle operations from `d2b-bus`/core controller and routes broker start/stop through descriptor-bound effect ports.", + "removalProof" : "`VmLifecycleRequest`/`vm_name` path is replaced by a v3 ResourceRef Guest lifecycle op after broker/public wire contract versioning.", + "reuseAction" : "adapt", + "reuseSource" : null, + "specId" : "ADR-046-nix-configuration", + "specPath" : "docs/specs/ADR-046-nix-configuration.md", + "validation" : "Port `provider_effects.rs` lifecycle dispatch tests; add ResourceRef Guest lifecycle request and BrokerCallerRole compatibility gates.", + "workItemId" : "ADR046-nix-035" + }, + { + "currentSource" : "None — net-new v3 work; no pre-ADR45 baseline equivalent for a provider-neutral `NetworkEffectPort` core adapter.", + "dataMigration" : "Full d2b 3.0 reset; no v2 state/config import.", + "dependencyOwner" : "Core; owns `NetworkEffectPort` contract/versioning in `d2b-contracts` and adapter implementation in `d2b-core`.", + "destination" : "`d2b-contracts` trait plus `d2b-core` core adapter; maps to broker wire operations and audit emission.", + "detailedDesign" : "Implement `NetworkEffectPort` core adapter in `d2b-core`; map to broker wire ops; emit audit records. Versioning: minor releases may add methods with default impls; major releases require Provider upgrade. The trait lives in `d2b-contracts`; the adapter in `d2b-core`.", + "integration" : "`Provider/network-local` reconcile calls injected `NetworkEffectPort`; the core adapter resolves opaque Network intents to closed broker wire ops and emits broker-level audit records.", + "removalProof" : "None — net-new; no prior owner to remove.", + "reuseAction" : "create", + "reuseSource" : null, + "specId" : "ADR-046-provider-network-local", + "specPath" : "docs/specs/providers/ADR-046-provider-network-local.md", + "validation" : "`packages/d2b-provider-network-local/tests/fault_injection.rs` verifies fake `NetworkEffectPort` behavior, error mapping, no broker socket in provider context, and audit-safe adapter boundaries.", + "workItemId" : "ADR046-nl-001" + }, + { + "currentSource" : "Existing broker wire has related ApplyNftables, ApplyRoute, ApplySysctl, ApplyNmUnmanaged, UpdateHostsFile, and SeedDnsmasqLease operations, but no `CreateBridge`, `DeleteBridge`, `ReadNftablesDigest`, `ReadSysctlState`, or `ReadBridgePortFlags` v3 ops.", + "dataMigration" : "Full d2b 3.0 reset; no v2 state/config import.", + "dependencyOwner" : "Core; broker/core contract work consumed by ADR046-nl-001.", + "destination" : "Broker wire contract and broker/core adapter operation table for `CreateBridge`, `DeleteBridge`, `ReadNftablesDigest`, `ReadSysctlState`, and `ReadBridgePortFlags`.", + "detailedDesign" : "Add `CreateBridge`, `DeleteBridge`, `ReadNftablesDigest`, `ReadSysctlState`, `ReadBridgePortFlags` broker ops. Primary reuse disposition: `adapt`. Preserved source-plan detail: extend broker wire with net-new operations and reuse existing closed broker-operation dispatch shape.", + "integration" : "`NetworkEffectPort` core adapter invokes these broker ops for bridge lifecycle and observe/drift checks; `Provider/network-local` receives only typed results and opaque digests/handles.", + "removalProof" : "None — net-new broker ops; remove only if no Provider consumes them per the removal checklist.", + "reuseAction" : "adapt", + "reuseSource" : null, + "specId" : "ADR-046-provider-network-local", + "specPath" : "docs/specs/providers/ADR-046-provider-network-local.md", + "validation" : "`integration/host_fabric.rs` covers bridge create/delete, nftables apply/digest, IPv6 suppression, NetworkManager unmanaged handling, and real `NetworkEffectPort` implementation.", + "workItemId" : "ADR046-nl-002" + }, + { + "currentSource" : "None — net-new v3 work; no public pre-ADR45 baseline equivalent for opaque `AttachmentHandle` or `FabricHandle`.", + "dataMigration" : "Full d2b 3.0 reset; no v2 state/config import.", + "dependencyOwner" : "Core; handle DTOs are owned by `d2b-contracts` and consumed by `d2b-core` plus `Provider/network-local`.", + "destination" : "`d2b-contracts` opaque byte-array newtypes; core-held HMAC key and provider-facing redacted handle types.", + "detailedDesign" : "Implement `AttachmentHandle` and `FabricHandle` as opaque byte-array newtypes (32 bytes of HMAC-SHA-256 over internal identity material; key held by core). Each handle is single-use; revocation is implicit when the owning Network is deleted. These types are declared in `d2b-contracts`, not in the provider crate.", + "integration" : "Core creates handles from Network and attachment identity, stores them only in internal state/status-resource attachment realization, and supplies resolved tap FDs through LaunchTicket without exposing IfNames or MACs.", + "removalProof" : "None — net-new; no prior owner to remove.", + "reuseAction" : "create", + "reuseSource" : null, + "specId" : "ADR-046-provider-network-local", + "specPath" : "docs/specs/providers/ADR-046-provider-network-local.md", + "validation" : "`tests/fault_injection.rs` and `tests/controller_state.rs` cover opaque-handle mismatch, revocation-on-delete, and no raw IfName/IP/MAC public surface.", + "workItemId" : "ADR046-nl-003" + }, + { + "currentSource" : "Existing v1 runtime tap handling is broker/runtime-specific; no v3 LaunchTicket owner-graph FD resolution surface exists.", + "dataMigration" : "Full d2b 3.0 reset; no v2 state/config import.", + "dependencyOwner" : "Core; depends on ADR046-nl-003 handles and runtime-cloud-hypervisor LaunchTicket consumption.", + "destination" : "Core LaunchTicket builder and dependency resolver that walks `Guest.ownerRef: Network/` to resolved tap FDs.", + "detailedDesign" : "Implement LaunchTicket FD resolution: when core builds the LaunchTicket for a Guest with `ownerRef: Network/`, it walks the owner graph, locates the Network, reads its internally-held `AttachmentHandle` set, and includes the corresponding tap FDs in the ticket. No API surface for the provider or runtime is required beyond the existing LaunchTicket mechanism. Primary reuse disposition: `create`. Preserved source-plan detail: net-new LaunchTicket integration; reuse existing LaunchTicket mechanism without adding provider/runtime API surface.", + "integration" : "Runtime-cloud-hypervisor starts Guests using tap FDs supplied in LaunchTickets; `Provider/network-local` declares attachments and core resolves handles privately.", + "removalProof" : "None — net-new; no prior owner to remove.", + "reuseAction" : "create", + "reuseSource" : null, + "specId" : "ADR-046-provider-network-local", + "specPath" : "docs/specs/providers/ADR-046-provider-network-local.md", + "validation" : "`integration/guest_lifecycle.rs` validates net-VM/workload Guest lifecycle, opaque attachment handle resolution, and `systemArtifactId` binding.", + "workItemId" : "ADR046-nl-004" + }, + { + "currentSource" : "Reuse modules listed in §25.1: `packages/d2b-host/src/ifname.rs:derive_ifname`, `packages/d2b-host/src/nftables.rs`, `packages/d2b-host/src/bridge_port.rs`, `packages/d2b-host/src/routes.rs`, and `packages/d2b-host/src/netlink.rs`.", + "dataMigration" : "Full d2b 3.0 reset; no v2 state/config import.", + "dependencyOwner" : "Provider plus Core; provider validates through `d2b_host::ifname::derive_ifname`, while core adapter consumes host networking modules.", + "destination" : "Core adapter imports `d2b-host` modules; `packages/d2b-provider-network-local/src/ifname.rs` re-exports `d2b_host::ifname::derive_ifname` only.", + "detailedDesign" : "The `d2b-host` IfName/nftables/bridge/route modules are consumed directly by the core adapter (not by the provider crate). The provider crate re-exports only `d2b_host::ifname::derive_ifname` for validation purposes. No additional extraction work is required beyond confirming the `d2b-host` API surface is stable. Primary reuse disposition: `adapt`. Preserved source-plan detail: reuse directly in core adapter; provider re-exports only `derive_ifname` for validation.", + "integration" : "Provider validateSpec uses deterministic IfName derivation for collision checks; core adapter applies bridge, nftables, route, and sysctl effects through reused `d2b-host` helpers.", + "removalProof" : "None — reused modules remain owned by `d2b-host`; no prior provider-local copy to remove.", + "reuseAction" : "adapt", + "reuseSource" : "`d2b-host` IfName, nftables, bridge-port, route preflight, and sysctl/netlink modules.", + "specId" : "ADR-046-provider-network-local", + "specPath" : "docs/specs/providers/ADR-046-provider-network-local.md", + "validation" : "`tests/ifname_derive.rs`, `tests/fault_injection.rs`, and `integration/host_fabric.rs` prove derivation, adapter reuse, and real host-fabric behavior.", + "workItemId" : "ADR046-nl-005" + }, + { + "currentSource" : "None — net-new v3 provider controller; v1 behavior lived in `nixos-modules/network.nix` and `nixos-modules/net.nix` static NixOS module logic.", + "dataMigration" : "Full d2b 3.0 reset; no v2 state/config import.", + "dependencyOwner" : "Provider; depends on ADR046-nl-001 through ADR046-nl-005 and owns the Network reconcile/observe/finalize handlers.", + "destination" : "`packages/d2b-provider-network-local/src/controller.rs`.", + "detailedDesign" : "Implement `controller.rs` reconcile/observe/finalize handlers with `NetworkEffectPort` injection. Primary reuse disposition: `adapt`. Preserved source-plan detail: port semantics into provider reconcile state machine; do not reuse static per-env systemd/Nix ownership.", + "integration" : "Controller watches Network, Guest, Volume, Process, User, Host, and Zone resources; creates child resources, writes status, invokes `NetworkEffectPort`, and drives finalizers.", + "removalProof" : "Supersedes static per-env lifecycle in `nixos-modules/network.nix` and `nixos-modules/net.nix`; removal proof is successor controller coverage plus deletion of duplicate old gates when this provider lands.", + "reuseAction" : "adapt", + "reuseSource" : null, + "specId" : "ADR-046-provider-network-local", + "specPath" : "docs/specs/providers/ADR-046-provider-network-local.md", + "validation" : "`tests/controller_state.rs` covers normal reconcile, errors, finalizer ordering, adoption on restart, and observe/drift cycles with deterministic clock.", + "workItemId" : "ADR046-nl-006" + }, + { + "currentSource" : "None — net-new v3 NetworkAgentService; v1 net-VM behavior was encoded in NixOS services and scripts under `nixos-modules/net.nix`.", + "dataMigration" : "Full d2b 3.0 reset; no v2 state/config import.", + "dependencyOwner" : "Provider; owns net-agent ComponentSession service and depends on ComponentSession/bus and net-VM Process lifecycle.", + "destination" : "`packages/d2b-provider-network-local/src/process_specs.rs` agent template plus agent service implementation in the net-VM artifact.", + "detailedDesign" : "Implement `NetworkAgentService` Noise-KK vsock ComponentSession (Reload + ReadinessQuery methods). Agent reconnect policy: if the controller cannot reach the agent vsock (Guest restart in progress), it retries with exponential backoff up to `drainTimeout` of the agent Process; after timeout it deletes and re-creates the agent Process resource. Primary reuse disposition: `create`. Preserved source-plan detail: net-new service; preserve semantic nftables/routes reload behavior from v1 net VM configuration.", + "integration" : "Controller writes config Volume content, resolves `Endpoint/net--agent-service`, calls `Reload(config_digest)`, and uses readiness predicates to set Network conditions.", + "removalProof" : "None — net-new ComponentSession service; no prior service endpoint to remove.", + "reuseAction" : "create", + "reuseSource" : null, + "specId" : "ADR-046-provider-network-local", + "specPath" : "docs/specs/providers/ADR-046-provider-network-local.md", + "validation" : "`integration/agent_reload.rs` validates Reload, `nft_applied` and `routes_applied` predicates, reconnect behavior, and config digest matching.", + "workItemId" : "ADR046-nl-007" + }, + { + "currentSource" : "Reuse semantics from `nixos-modules/net.nix` lines 168–296 for nftables and lines 302–441 for dnsmasq; runtime volume model is net-new.", + "dataMigration" : "Full d2b 3.0 reset; config Volume is runtime tmpfs content regenerated from Network spec.", + "dependencyOwner" : "Provider; config rendering owned by `Provider/network-local`, storage reconciliation owned by `Provider/volume-local`.", + "destination" : "`packages/d2b-provider-network-local/src/config_volume.rs`.", + "detailedDesign" : "Implement config Volume content rendering (dnsmasq.conf, nftables.rules, routing.conf, attachments.json). Primary reuse disposition: `adapt`. Preserved source-plan detail: port and render into bounded config Volume files.", + "integration" : "Controller creates `Volume/net--config`, writes four files through the Volume service, attaches the read-only view to the net VM, and triggers agent reload plus dnsmasq restart.", + "removalProof" : "Supersedes static per-env config generation in `nixos-modules/net.nix`; successor coverage retires duplicate old Nix/service assertions.", + "reuseAction" : "adapt", + "reuseSource" : "`nixos-modules/net.nix` dnsmasq, nftables, routing, and attachment configuration semantics.", + "specId" : "ADR-046-provider-network-local", + "specPath" : "docs/specs/providers/ADR-046-provider-network-local.md", + "validation" : "`tests/controller_state.rs`, `integration/agent_reload.rs`, and `integration/delete_sequence.rs` validate rendering, write flow, reload, and cleanup ordering.", + "workItemId" : "ADR046-nl-008" + }, + { + "currentSource" : "v1 dnsmasq and mDNS process shape came from `nixos-modules/net.nix` and static NixOS services; no v3 Process builder exists.", + "dataMigration" : "Full d2b 3.0 reset; no v2 state/config import.", + "dependencyOwner" : "Provider; Process resource builders owned by `d2b-provider-network-local`.", + "destination" : "`packages/d2b-provider-network-local/src/process_specs.rs`.", + "detailedDesign" : "Implement canonical Process spec builders for agent, dnsmasq, mdns-reflector, mdns-dnsbridge. Primary reuse disposition: `adapt`. Preserved source-plan detail: port service semantics into canonical Process resource specs.", + "integration" : "Controller creates agent service, dnsmasq worker, and optional mDNS workers as owned Process resources on the net VM; Process Provider reports readiness and lifecycle status.", + "removalProof" : "Supersedes static per-env systemd services; old duplicate service tests are retired after successor Process coverage passes.", + "reuseAction" : "adapt", + "reuseSource" : null, + "specId" : "ADR-046-provider-network-local", + "specPath" : "docs/specs/providers/ADR-046-provider-network-local.md", + "validation" : "`tests/controller_state.rs`, `integration/mdns_reflector.rs`, and eval case `process-sandbox-netns.nix` validate Process shape, optional mDNS, and guest-netns capability isolation.", + "workItemId" : "ADR046-nl-009" + }, + { + "currentSource" : "Reuse lib.mkForce NIC bootstrap from `nixos-modules/base.nix` `10-eth-dhcp` override and account reservation documented in `nixos-modules/host-users.nix`; v1 per-Network config does not carry forward.", + "dataMigration" : "Full d2b 3.0 reset; no per-Network v2 net-VM config import.", + "dependencyOwner" : "net-vm artifact; owns generic `net-vm-base` nixos-system artifact and shared net-local-controller UID/GID reservation.", + "destination" : "`net-vm-base` nixos-system artifact and artifact catalog entry `d2b.artifacts.net-vm-base`.", + "detailedDesign" : "Build generic `net-vm-base` nixos-system artifact with net-agent binary, agent-service endpoint, guest-agent binary, standard NIC bootstrap, lib.mkForce override; bake `net-local-controller` account with the UID/GID allocated from the host-users reservation table (documented in `nixos-modules/host-users.nix`). Primary reuse disposition: `adapt`. Preserved source-plan detail: preserve generic boot/safety invariants; exclude per-Network static config.", + "integration" : "Network resource `spec.netVmSystemArtifactId` points to `net-vm-base`; runtime-cloud-hypervisor consumes the artifact ID, and the config Volume provides all per-Network DHCP/firewall/routing content.", + "removalProof" : "Supersedes implicit microvm/Nix path coupling; remove `net-vm-base` artifact catalog entry only after all Network resources and provider references are gone.", + "reuseAction" : "adapt", + "reuseSource" : "`nixos-modules/base.nix` `10-eth-dhcp` lib.mkForce override and host-users reservation table.", + "specId" : "ADR-046-provider-network-local", + "specPath" : "docs/specs/providers/ADR-046-provider-network-local.md", + "validation" : "Eval cases `net-vm-artifact-id-eval.nix` and `network-spec-eval.nix`, plus `tests/net-vm-network-eval.sh` for the lib.mkForce invariant.", + "workItemId" : "ADR046-nl-010" + }, + { + "currentSource" : "Existing NixOS modules `nixos-modules/network.nix`, `nixos-modules/net.nix`, and `nixos-modules/host-users.nix` provide v1 static declarations and user/account patterns.", + "dataMigration" : "Full d2b 3.0 reset; no v2 state/config import.", + "dependencyOwner" : "Nix; owns resource declaration, User declaration, host account provisioning, and artifact catalog wiring.", + "destination" : "Nix module resource emission for `Provider/network-local`, `User/net-local-controller`, host OS account, `provider-network-local`, and `net-vm-base` artifacts.", + "detailedDesign" : "Nix module for `Provider/network-local` resource declaration; `User/net-local-controller` declaration; OS account provisioning; artifact catalog entries. Primary reuse disposition: `adapt`. Preserved source-plan detail: adapt Nix option/resource emission and account provisioning to v3 resources.", + "integration" : "Nix compiler emits Provider/User/Network resources and host prerequisites; core ProviderDeployment starts controller Process; system-core reconciles the User resource.", + "removalProof" : "Supersedes v1 NixOS module declarations; removal proof is deletion of old resource emission and account/artifact entries when provider is retired.", + "reuseAction" : "adapt", + "reuseSource" : null, + "specId" : "ADR-046-provider-network-local", + "specPath" : "docs/specs/providers/ADR-046-provider-network-local.md", + "validation" : "Eval cases `network-spec-eval.nix`, `user-no-managed-by-eval.nix`, `net-vm-artifact-id-eval.nix`, and `make test-policy` for artifact/package paths.", + "workItemId" : "ADR046-nl-011" + }, + { + "currentSource" : "`nixos-modules/lib.nix:cidrOverlaps` lines 429–462 provides CIDR overlap logic in the v1 module layer.", + "dataMigration" : "None — docs/tooling only; no runtime state.", + "dependencyOwner" : "Nix; depends on Network resource schema and CIDR validation rules.", + "destination" : "Nix flake/resource schema checks for declared Networks and provider `validate.rs` parity.", + "detailedDesign" : "Build-time CIDR overlap check for declared Networks in flake check. Primary reuse disposition: `adapt`. Preserved source-plan detail: port/reuse overlap semantics in v3 eval checks and provider validation.", + "integration" : "Nix compiler rejects overlapping declared Network CIDRs before resource publication; runtime `validateSpec` re-checks full overlap matrix.", + "removalProof" : "None — validation net-new in v3 resource compiler; no prior owner to remove.", + "reuseAction" : "adapt", + "reuseSource" : "`nixos-modules/lib.nix:cidrOverlaps`.", + "specId" : "ADR-046-provider-network-local", + "specPath" : "docs/specs/providers/ADR-046-provider-network-local.md", + "validation" : "Eval case `network-cidr-overlap-eval.nix` and `tests/cidr_overlap.rs` cover same-Network, cross-Network, external CIDR, and adjacency cases.", + "workItemId" : "ADR046-nl-012" + }, + { + "currentSource" : "Reusable semantic assertions come from §25.1 IfName/CIDR reuse inventory and Network schema defined in this spec.", + "dataMigration" : "None — docs/tooling only; no runtime state.", + "dependencyOwner" : "Tests; owned by `d2b-provider-network-local` hermetic test suite.", + "destination" : "`packages/d2b-provider-network-local/tests/schema_roundtrip.rs`, `tests/ifname_derive.rs`, and `tests/cidr_overlap.rs`.", + "detailedDesign" : "Conformance suite: NetworkSpec round-trip, IfName derivation, CIDR validation matrix. Primary reuse disposition: `adapt`. Preserved source-plan detail: adapt existing IfName/CIDR assertions into provider conformance tests.", + "integration" : "Test suite runs under `cargo test -p d2b-provider-network-local --lib --tests` and validates provider schema before integration gates.", + "removalProof" : "Retire replaced current-code tests only after successor hermetic tests cover the minimum reusable assertions and gate manifests/pins are updated.", + "reuseAction" : "adapt", + "reuseSource" : null, + "specId" : "ADR-046-provider-network-local", + "specPath" : "docs/specs/providers/ADR-046-provider-network-local.md", + "validation" : "The listed conformance tests themselves are the validation, with workspace policy ensuring `tests/` exists.", + "workItemId" : "ADR046-nl-013" + }, + { + "currentSource" : "None — net-new v3 controller state-machine test; v1 shell/Nix gates are not a controller-reconcile equivalent.", + "dataMigration" : "None — docs/tooling only; no runtime state.", + "dependencyOwner" : "Tests; depends on ADR046-nl-006 controller and fake `NetworkEffectPort` from `d2b-contracts`.", + "destination" : "`packages/d2b-provider-network-local/tests/controller_state.rs`.", + "detailedDesign" : "Controller state-machine unit tests with fake `NetworkEffectPort` (from d2b-contracts mock) and deterministic clock.", + "integration" : "Hermetic fake effect port drives reconcile, observe, finalizer, and adoption transitions without real broker, systemd, container, or network dependencies.", + "removalProof" : "None — net-new; no prior owner to remove.", + "reuseAction" : "create", + "reuseSource" : null, + "specId" : "ADR-046-provider-network-local", + "specPath" : "docs/specs/providers/ADR-046-provider-network-local.md", + "validation" : "`tests/controller_state.rs` covers normal path, CIDR conflict, User not Ready, Volume error, Guest timeout, agent reload failure, finalizer sequence, adoption, and drift.", + "workItemId" : "ADR046-nl-014" + }, + { + "currentSource" : "Existing Layer-1 eval and shell gates cover fragments; no v3 provider lifecycle integration test exists.", + "dataMigration" : "None — docs/tooling only; no runtime state.", + "dependencyOwner" : "Tests; integration coverage for the complete Network lifecycle.", + "destination" : "`packages/d2b-provider-network-local/integration/host_fabric.rs`, `guest_lifecycle.rs`, `agent_reload.rs`, and `delete_sequence.rs`.", + "detailedDesign" : "Integration tests: full Network lifecycle (create, config update, agent Reload, delete sequence) in container environment. Primary reuse disposition: `adapt`. Preserved source-plan detail: adapt reusable semantic assertions into v3 integration coverage.", + "integration" : "Integration tests exercise resource publication, host fabric effects, config Volume updates, ComponentSession reload, Process lifecycle, and finalizer cleanup through the provider stack.", + "removalProof" : "Old duplicate tests, shell gates, fixtures, static artifacts, CI jobs, manifests, and pins are deleted once successor coverage and removal proof pass.", + "reuseAction" : "adapt", + "reuseSource" : null, + "specId" : "ADR-046-provider-network-local", + "specPath" : "docs/specs/providers/ADR-046-provider-network-local.md", + "validation" : "`make test-integration` for container tests and `make test-host-integration` where guest lifecycle requires host/KVM coverage.", + "workItemId" : "ADR046-nl-015" + }, + { + "currentSource" : "The invariant is specified as INV-NET-008 and has eval coverage named `tests/unit/nix/cases/process-sandbox-netns.nix`.", + "dataMigration" : "None — docs/tooling only; no runtime state.", + "dependencyOwner" : "Security; depends on Process sandbox schema and `Provider/system-minijail` guest namespace inheritance.", + "destination" : "Process templates for agent and dnsmasq plus sandbox/eval tests.", + "detailedDesign" : "Verify INV-NET-008 (Guest-network-admin isolation): Process Provider correctly inherits Guest netns for agent/dnsmasq. Primary reuse disposition: `adapt`. Preserved source-plan detail: preserve and verify existing guest-netns isolation invariant in v3 Process specs.", + "integration" : "`Provider/network-local` emits `namespaceClasses: []` and guest-only capability classes; Process Provider starts agent/dnsmasq inside the net VM network namespace only.", + "removalProof" : "None — security invariant preserved; no prior owner to remove.", + "reuseAction" : "adapt", + "reuseSource" : null, + "specId" : "ADR-046-provider-network-local", + "specPath" : "docs/specs/providers/ADR-046-provider-network-local.md", + "validation" : "`tests/unit/nix/cases/process-sandbox-netns.nix` and provider Process-template tests assert no host capability or host network namespace grant.", + "workItemId" : "ADR046-nl-016" + }, + { + "currentSource" : "None — provider crate README is net-new for v3 packaging; this dossier supplies the required topics.", + "dataMigration" : "None — docs/tooling only; no runtime state.", + "dependencyOwner" : "Docs; owned by `d2b-provider-network-local` package documentation.", + "destination" : "`packages/d2b-provider-network-local/README.md`.", + "detailedDesign" : "`packages/d2b-provider-network-local/README.md` covering all 7 required topics. Primary reuse disposition: `create`. Preserved source-plan detail: net-new documentation.", + "integration" : "Workspace policy requires the README alongside `src/`, `tests/`, and `integration/`; operators and contributors use it for provider identity, build, test, integration, state, RBAC, and standalone-repo path.", + "removalProof" : "None — net-new documentation; no prior owner to remove.", + "reuseAction" : "create", + "reuseSource" : null, + "specId" : "ADR-046-provider-network-local", + "specPath" : "docs/specs/providers/ADR-046-provider-network-local.md", + "validation" : "`make test-policy` / `xtask workspace-policy` verifies required provider crate paths and README presence.", + "workItemId" : "ADR046-nl-017" + }, + { + "currentSource" : "Existing `UsbipBindFirewallRule` owns per-busid `inet d2b` exposure, while legacy `network.nix` and `net.nix` add broader TCP/3240 allows.", + "dataMigration" : "Full d2b 3.0 reset; no v2 state/config import.", + "dependencyOwner" : "Broker plus device provider boundary; `UsbipBindFirewallRule` remains owned by `Provider/device-usbip`.", + "destination" : "Device-usbip EffectPort/adapter owns USBIP rules, drift, and strict provider status; network-local host/net-VM renderers and status cover only Network-owned policy.", + "detailedDesign" : "`UsbipBindFirewallRule` remains the sole broker mutation path for exact per-Network/per-busid TCP/3240 exposure. Network-local emits no TCP/3240 match, excludes device-usbip ownership markers from its digest, and never reports USBIP drift. Primary reuse disposition: `adapt`. Preserved source-plan detail: preserve the typed closed broker op; remove both generic network-local USBIP allows.", + "integration" : "`Provider/device-usbip` watches only Network identity/readiness/generation; Core privately resolves the Network attachment for the one relay Endpoint authority and firewall op. Binding proxies receive authorized connected streams through LaunchTickets.", + "removalProof" : "Legacy generic `network.nix` and `net.nix` USBIP allow fragments and any golden expectation for them are removed after device-usbip host integration passes.", + "reuseAction" : "adapt", + "reuseSource" : null, + "specId" : "ADR-046-provider-network-local", + "specPath" : "docs/specs/providers/ADR-046-provider-network-local.md", + "validation" : "Network-local host and net-VM firewall intent tests assert no TCP/3240/USBIP rule; USBIP rule churn leaves Network digest/`FirewallReady` unchanged; device-usbip tests own exact scoping, drift, status, and release.", + "workItemId" : "ADR046-nl-018" + }, + { + "currentSource" : "None — net-new v3 ProviderStateSet/status contract; v1 network module did not expose Provider state Volumes.", + "dataMigration" : "Full d2b 3.0 reset; no v2 state/config import.", + "dependencyOwner" : "Provider; depends on D087 ProviderStateSet and status-first storage rules.", + "destination" : "Provider descriptor, controller-main deployment, `tests/state_schema_roundtrip.rs`, and eval case `provider-state-volume-eval.nix`.", + "detailedDesign" : "Confirm `controller-main` declares no stateNamespace and core ProviderDeployment creates no Provider state Volume or state mount; validate ProviderStateSet query returns empty for `Provider/network-local`; validate bounded operational state is written to revisioned/redacted status and the core Operation ledger with `status-oversize` conformance; confirm per-Network config Volumes remain `ownerRef: Network/` runtime/config operational Volumes outside the ProviderStateSet and `Volume` is not in `ResourceTypes implemented`. Primary reuse disposition: `create`. Preserved source-plan detail: net-new status-first provider-state conformance.", + "integration" : "Core ProviderDeployment starts controller without `/state`; controller uses Network status and Operation ledger for bounded observations; ProviderStateSet query excludes per-Network config Volumes.", + "removalProof" : "None — net-new; no Provider state Volume or prior owner to remove.", + "reuseAction" : "create", + "reuseSource" : null, + "specId" : "ADR-046-provider-network-local", + "specPath" : "docs/specs/providers/ADR-046-provider-network-local.md", + "validation" : "`tests/state_schema_roundtrip.rs` and `tests/unit/nix/cases/provider-state-volume-eval.nix` validate empty ProviderStateSet, status bounds/redaction, and config Volume exclusion.", + "workItemId" : "ADR046-nl-019" + }, + { + "currentSource" : "Current macvtap spawn resolves a raw parent interface but has no Host-global duplicate admission, sharing policy, authority status, or owner-proof lifecycle.", + "dataMigration" : "Full d2b 3.0 reset; no legacy authority import.", + "dependencyOwner" : "D097 Core authority index, Network contracts, runtime-cloud-hypervisor private attachment path", + "destination" : "Network schema/Provider descriptor, Core authority index, Network reconcile/update/finalizer, runtime LaunchTicket resolver, and authority tests", + "detailedDesign" : "Register the external physical-NIC `AuthorityDescriptor`: Host-global `external-physical-nic/v1` Core-derived identity, `zero-or-one` authority, exclusive `passthru`/`private`/`vepa`, exclusive-by-default `bridge`, explicitly compatible bounded multiplexing only for bridge, `external-physical-nic-conflict`, exact owner proof, drain-release-reacquire update, forbidden export, and bounded FIFO holder policy. Primary reuse disposition: `adapt`. Preserved source-plan detail: adapt the existing broker-internal macvtap-FD creation path behind mandatory Core authority admission.", + "integration" : "Core preflight gates every runtime LaunchTicket/`SpawnRunner`; status reports bounded authority state and conditions; D091 update and finalizer close macvtap ownership before release.", + "removalProof" : "The old direct macvtap spawn path is unreachable unless Core supplies an admitted authority claim in the LaunchTicket.", + "reuseAction" : "adapt", + "reuseSource" : null, + "specId" : "ADR-046-provider-network-local", + "specPath" : "docs/specs/providers/ADR-046-provider-network-local.md", + "validation" : "`external_nic_authority.rs` covers Core-derived identity, same-/cross-Zone conflicts, explicit bridge multiplexing, incompatible policy, non-bridge multiplex denial, no-effect rejection, adoption ambiguity, owner transfer, disruptive update, release ordering, and redaction; Nix eval and host integration cover declared configuration and lifecycle.", + "workItemId" : "ADR046-nl-020" + }, + { + "currentSource" : "`packages/d2b-notify/src/{events,state,notifications,nonce}.rs`", + "dataMigration" : "No v2 compatibility; reset", + "dependencyOwner" : "W0 shared contract root; session/bus owner", + "destination" : "`packages/d2b-provider-notification-desktop/src/{types,redact,action_nonce}.rs`", + "detailedDesign" : "`NotificationRequest`/`NotificationResult` DTOs and stream record types; bounded fields; closed category set; icon catalog contract; `ActionNonce`/`ActionNonceStore` adapted from main; no ResourceType DTO Primary reuse disposition: `adapt`. Preserved source-plan detail: copy/adapt.", + "integration" : "Zone bus service; host-sink stream consumer; guest-source stream producer", + "removalProof" : "v2 `d2b.notify.v2` generated stubs removed after v3 service established", + "reuseAction" : "adapt", + "reuseSource" : null, + "specId" : "ADR-046-provider-notification-desktop", + "specPath" : "docs/specs/providers/ADR-046-provider-notification-desktop.md", + "validation" : "`tests/stream_record.rs` — DTO schema vectors; `tests/action_nonce.rs` — single-use/TTL/capacity/replay", + "workItemId" : "ADR046-notify-001" + }, + { + "currentSource" : "`packages/d2b-notify/src/services/`", + "dataMigration" : "None — full d2b 3.0 reset; no prior state to migrate", + "dependencyOwner" : "ADR046-session-001, ADR046-session-003; session/bus wiring", + "destination" : "`packages/d2b-provider-notification-desktop/src/stream_admission.rs`", + "detailedDesign" : "Session admission checks, Noise profile enforcement, transport class validation Primary reuse disposition: `adapt`. Preserved source-plan detail: copy/adapt.", + "integration" : "ComponentSession/d2b-bus", + "removalProof" : "Old `DesktopServices` session admitted under v2 contract removed when v3 session established", + "reuseAction" : "adapt", + "reuseSource" : null, + "specId" : "ADR-046-provider-notification-desktop", + "specPath" : "docs/specs/providers/ADR-046-provider-notification-desktop.md", + "validation" : "`tests/stream_admission.rs` — all rejection vectors", + "workItemId" : "ADR046-notify-002" + }, + { + "currentSource" : "`packages/d2b-notify/src/services/observer.rs`", + "dataMigration" : "None — full d2b 3.0 reset; no prior state to migrate", + "dependencyOwner" : "ADR046-notify-001, ADR046-reconcile-001; controller owner", + "destination" : "`packages/d2b-provider-notification-desktop/src/controller.rs`", + "detailedDesign" : "Async Process placement controller; watches `guestSources` Guest refs; creates/drains/deletes guest-source Processes; creates/stops host-sink Process on display-wayland readiness change; declares no Provider state Volume and does not own/add/create/delete Volumes; bounded non-secret operational state lives in `status`/the core Operation ledger (D087); notification delivery state (in-memory projection, action nonce store) is host-sink process memory only; no ResourceType reconcile loop", + "integration" : "Zone resource store (Process API); d2b-bus; display-wayland dependency watch", + "removalProof" : "Not applicable (new controller)", + "reuseAction" : "create", + "reuseSource" : null, + "specId" : "ADR-046-provider-notification-desktop", + "specPath" : "docs/specs/providers/ADR-046-provider-notification-desktop.md", + "validation" : "Unit tests for placement FSM in `tests/stream_record.rs`; Volume creation/deletion lifecycle in `tests/volume_lifecycle.rs`; see also `integration/cross_zone_source.rs` end-to-end", + "workItemId" : "ADR046-notify-003" + }, + { + "currentSource" : "`packages/d2b-notify/src/services/actions.rs`, `packages/d2b-notify/src/bin/`", + "dataMigration" : "None — full d2b 3.0 reset; no prior state to migrate", + "dependencyOwner" : "ADR046-notify-002, ADR046-notify-003; host-sink owner", + "destination" : "`packages/d2b-provider-notification-desktop/src/host_sink.rs`", + "detailedDesign" : "D-Bus client; `DesktopNotificationSink` stream consumer; action nonce issuance; `DesktopNotificationObserver` projection (in-memory, not persisted); display-wayland ComponentSession bootstrap for pre-opened D-Bus FD", + "integration" : "D-Bus session (pre-opened FD via ComponentSession bootstrap); ComponentSession named streams", + "removalProof" : "`nixos-modules/notifications.nix` state-dir tmpfiles rule retired; all notification state is in-memory per-session with no Volume replacement", + "reuseAction" : "adapt", + "reuseSource" : null, + "specId" : "ADR-046-provider-notification-desktop", + "specPath" : "docs/specs/providers/ADR-046-provider-notification-desktop.md", + "validation" : "`integration/dbus_sink.rs`, `integration/observer_client.rs`, `integration/action_invoke.rs`", + "workItemId" : "ADR046-notify-004" + }, + { + "currentSource" : "v3 security-key guest vsock path (conceptual similarity only; not copied directly)", + "dataMigration" : "None — full d2b 3.0 reset; no prior state to migrate", + "dependencyOwner" : "ADR046-notify-002; guest-source owner", + "destination" : "`packages/d2b-provider-notification-desktop/src/guest_source.rs`", + "detailedDesign" : "Guest-side vsock ComponentSession; `NotificationRequest` record validation and field bounding; category filter; `DesktopNotificationSink` stream forwarding; `NotificationResult` handling; no host-side resource creation", + "integration" : "Guest process vsock → host ComponentSession", + "removalProof" : "v3 baseline security-key notification path in `d2b-notify` is superseded; clipd direct `notify_rust` call superseded", + "reuseAction" : "create", + "reuseSource" : null, + "specId" : "ADR-046-provider-notification-desktop", + "specPath" : "docs/specs/providers/ADR-046-provider-notification-desktop.md", + "validation" : "`integration/cross_zone_source.rs`", + "workItemId" : "ADR046-notify-005" + }, + { + "currentSource" : "`nixos-modules/notifications.nix`; `packages/d2bd/src/metrics.rs`", + "dataMigration" : "`d2b.notifications.*` Nix options retired; `d2b.zones..resources.notification-desktop` with `spec.config.guestSources` replaces", + "dependencyOwner" : "ADR046-notify-001; Nix/telemetry owner", + "destination" : "Nix: Zone resource authoring in `nixos-modules/`; metrics: `packages/d2b-provider-notification-desktop/src/`", + "detailedDesign" : "Zone Provider resource and RoleBinding Nix compiler output; `spec.config.guestSources` authoring and eval-time assertions; OTEL metric emitters with closed labels; audit record emitters", + "integration" : "Nix configuration compiler; OTEL emitter ring; authoritative audit", + "removalProof" : "`nixos-modules/notifications.nix` removed after Zone resource equivalence confirmed by eval test", + "reuseAction" : "adapt", + "reuseSource" : null, + "specId" : "ADR-046-provider-notification-desktop", + "specPath" : "docs/specs/providers/ADR-046-provider-notification-desktop.md", + "validation" : "Eval tests for category enforcement, displayWaylandRef assertion, guestRef resolution; `tests/stream_redaction.rs` for content-free telemetry", + "workItemId" : "ADR046-notify-006" + }, + { + "currentSource" : "`packages/d2b-realm-core/src/ids.rs`, `workload.rs`, `error.rs`; `packages/d2b-core/src/storage.rs`, `processes.rs`", + "dataMigration" : "Full d2b 3.0 reset; no v2 resource import", + "dependencyOwner" : "W0 shared contract root; `d2b-contracts`", + "destination" : "`packages/d2b-contracts/src/v3/resource.rs`, `resource_status.rs`, `resource_schema.rs`", + "detailedDesign" : "Implement strict ResourceEnvelope, metadata, the three-layer spec shape (universal envelope + ResourceType base `spec.*` incl. `spec.providerRef` + optional canonical `spec.provider` `{ schemaId, schemaVersion, settings }`), the three-layer status shape (universal base + `status.resource` + optional `status.provider` with `providerRef`/`schemaId`/`schemaVersion`/`observedProviderGeneration`/`details`), phase/condition/outcome, canonical JSON, per-layer bounds/redaction, ownerRef/UID fields Primary reuse disposition: `adapt`. Preserved source-plan detail: extract and adapt.", + "integration" : "Store/API/SDK/Nix/codegen consume one contract", + "removalProof" : "Old DTOs removed per owning ResourceType wave only after rendered/runtime consumers move", + "reuseAction" : "adapt", + "reuseSource" : null, + "specId" : "ADR-046-resource-object-model", + "specPath" : "docs/specs/ADR-046-resource-object-model.md", + "validation" : "Golden JSON/protobuf vectors; serde unknown-field; three-layer spec shape round-trip; canonical minimal base-spec acceptance; base-schema version/fingerprint conformance; `spec.provider` deny-unknown/version-mismatch/shadow rejection and providerRef-binding; three-layer status shape round-trip; base-only projection (universal + `status.resource`) ignores/omits `status.provider`; `status.provider` unknown-field/version-mismatch rejection; status redaction/size/time/phase tests; `status.update` currency object round-trip (state/reasons/disruption/preserveState/owned+dependency refs bounded); `spec.updatePolicy` base round-trip", + "workItemId" : "ADR046-object-001" + }, + { + "currentSource" : "`packages/d2b-realm-core/src/allocator_engine.rs`, `d2b-realm-router/src/lib.rs` shared ownership/idempotency precedents", + "dataMigration" : "None after reset", + "dependencyOwner" : "ADR046-object-001; native resource store", + "destination" : "`packages/d2b-resource-store-redb/src/ownership.rs`, `packages/d2b-controller-toolkit/src/owner_hints.rs`", + "detailedDesign" : "Singular ownerRef resolution/UID binding, cycle/depth property checks, reverse index, owner hints, child-first deletion", + "integration" : "Every store mutation updates owner index and hint dispatcher atomically", + "removalProof" : "Not applicable", + "reuseAction" : "adapt", + "reuseSource" : null, + "specId" : "ADR-046-resource-object-model", + "specPath" : "docs/specs/ADR-046-resource-object-model.md", + "validation" : "Property tests for cycles/reparent/name reuse; integration tests for child drift repair and owner cascades", + "workItemId" : "ADR046-object-002" + }, + { + "currentSource" : "`packages/d2b-host/src/otel_host_bridge_argv.rs` (`OtelHostBridgeArgvInputs`, `otel_host_bridge_argv`, `OtelHostBridgeArgvError`); `packages/d2bd/src/otel_host_bridge_readiness.rs` (`OtelHostBridgeReadiness`, `otel_host_bridge_read`); `packages/d2b-contracts/src/broker_wire.rs::RunnerRole::OtelHostBridge`; `packages/d2b-core/src/processes.rs::ProcessRole::OtelHostBridge`", + "dataMigration" : "Full reset; existing socat bridge retired after cutover", + "dependencyOwner" : "ADR046-telem-001 (BoundedEmitter crate); W0/W2; telemetry/observability owner", + "destination" : "`packages/d2b-provider-observability-otel/src/{forwarder_bin,controller,binding}.rs`", + "detailedDesign" : "Binding-owned forwarder: accept OTLP only from the exact Guest producer, relay through the Binding-private Endpoint/Volume to its edge collector, enforce bounded frames/quota/session timeout, and use no OTEL SDK. Map forwarder readiness to `status.provider`, then derive provider-neutral Binding ingest readiness; Process Provider owns launch/pidfd. Primary reuse disposition: `adapt`. Preserved source-plan detail: adapt (`OtelHostBridgeArgvInputs` vsock logic → native Rust OTLP relay); adapt (`OtelHostBridgeReadiness` → `TelemetryBinding` conditions); delete-after-cutover (`RunnerRole::OtelHostBridge`, `ProcessRole::OtelHostBridge`).", + "integration" : "Controller creates vsock-forwarder long-lived `Process` → ProviderSupervisor → system-minijail/systemd launch → vsock socket bind → Guest side connects", + "removalProof" : "Legacy symbols removed only after Binding ownership/readiness and forwarding integration pass", + "reuseAction" : "adapt", + "reuseSource" : null, + "specId" : "ADR-046-provider-observability-otel", + "specPath" : "docs/specs/providers/ADR-046-provider-observability-otel.md", + "validation" : "`integration/scenario_obs_zone_forwarding.rs`; adapted `minijail_relay_otel.rs` shape test for Provider-managed runner; assert `RunnerRole::OtelHostBridge` is absent from `d2b-contracts` after removal", + "workItemId" : "ADR046-otel-001" + }, + { + "currentSource" : "`nixos-modules/components/observability/host.nix` (`otelRuntimeDir`, `hostEgressSocket`, `setfacl` ACL pattern, `scrapeJournal` option, `identityName`); `nixos-modules/components/observability/stack.nix` (`ingressSources`, `vmName`, `receiverGrpcPort`, loopback binding, `signoz.listenPort`)", + "dataMigration" : "Existing SigNoz data not migrated; v3 starts fresh per Zone", + "dependencyOwner" : "ADR046-otel-001 + ADR046-telem-001 + ADR046-provider-001 (Provider toolkit) + ADR046-provider-004 (common telemetry Service/Binding base) + resource/Endpoint/Volume contracts; W2; observability owner", + "destination" : "`packages/d2b-provider-observability-otel/src/{collector_bin,emitter_socket,exporter,controller,service,binding}.rs`; updated Nix observability modules", + "detailedDesign" : "Register the initial implementation of both provider-neutral qualified ResourceTypes by binding the exact ADR046-provider-004 base versions/fingerprints, then define only strict observability-otel Service/Binding spec and status extensions. Reconcile each Binding into an edge collector, private Endpoints, runtime Volume, and optional forwarder. Collector links the full OTEL SDK, resolves `serviceRef`, stamps trusted producer identity, and enforces common signals/quota/policy plus strict batching extension. Write generic Service/Binding observations to `status.resource` and SigNoz/OTLP/OTEL observations only to `status.provider`; no state file or Provider state Volume. Provider root config remains installation-only. Primary reuse disposition: `adapt`. Preserved source-plan detail: adapt Nix pipeline shape (replace per-VM `vmName` with per-Zone name; replace socat runner with vsock-forwarder long-lived Process; adapt `ingressSources` per-Zone entry).", + "integration" : "`BoundedEmitter` → Binding-private Endpoint → edge collector/OTEL SDK → same-Zone authority or projected Service → SigNoz", + "removalProof" : "`guest.nix` per-VM guest collector retired after `integration/scenario_obs_zone_forwarding.rs` passes", + "reuseAction" : "adapt", + "reuseSource" : null, + "specId" : "ADR-046-provider-observability-otel", + "specPath" : "docs/specs/providers/ADR-046-provider-observability-otel.md", + "validation" : "Common-fixture/fingerprint and canonical-minimal-base conformance (including a fake alternate telemetry Provider); `tests/emitter_socket_receive.rs`; `tests/exporter_outage.rs`; `tests/exporter_backpressure.rs`; `integration/scenario_full_pipeline.rs`; adapted `policy_observability.rs` (retain all existing assertions; add new `d2b.zone`, `d2b.provider` allowlist entries)", + "workItemId" : "ADR046-otel-002" + }, + { + "currentSource" : "`nixos-modules/components/observability/host.nix::journaldStorageDir`, `scrapeJournal` option; journald cgroup-path filtering pattern", + "dataMigration" : "None — full d2b 3.0 reset; no prior state to migrate", + "dependencyOwner" : "ADR046-otel-002 + ADR046-telem-001 + ADR046-volume-001; Nix/observability owner", + "destination" : "`packages/d2b-provider-observability-otel/src/nix/journald.nix`; `packages/d2b-provider-observability-otel/src/journald.rs`", + "detailedDesign" : "Per `ADR-046-telemetry-audit-and-support` §journald stdout/stderr ingestion: cgroup filter derived from trusted `producerRef`; redaction drops credential/secret/path fields, `_CMDLINE`, `_EXE`, and `INVOCATION_ID`; the strict `TelemetryBinding.spec.provider.settings.journald.enable` defaults false. Primary reuse disposition: `adapt`. Preserved source-plan detail: adapt journald receiver config for per-Zone cgroup filter.", + "integration" : "Collector binary journald receiver config path → cgroup filter expression → OTel Collector journald receiver → redaction filter → OTLP export", + "removalProof" : "Not applicable", + "reuseAction" : "adapt", + "reuseSource" : null, + "specId" : "ADR-046-provider-observability-otel", + "specPath" : "docs/specs/providers/ADR-046-provider-observability-otel.md", + "validation" : "Nix eval test: filter expression set when enabled; assert `_CMDLINE` and `INVOCATION_ID` in drop list; `tests/redaction.rs` for journald field redaction", + "workItemId" : "ADR046-otel-003" + }, + { + "currentSource" : "`packages/d2b-contract-tests/tests/policy_observability.rs` (`loki_native_otel_resource_attributes` allowlist; `tempo_stack_signoz_backend_and_collector`; `startup_tracing_avoids_host_path_fields`); `packages/d2b-contract-tests/tests/policy_metrics.rs` (`EXPECTED_METRICS` table); `packages/d2b-contract-tests/tests/minijail_relay_otel.rs`", + "dataMigration" : "None — full d2b 3.0 reset; no prior state to migrate", + "dependencyOwner" : "ADR046-otel-002; policy/contract-tests owner", + "destination" : "`packages/d2b-contract-tests/tests/policy_observability.rs` (updated); `packages/d2b-contract-tests/tests/policy_telemetry_redaction.rs` (new, per ADR046-telem-008); `packages/d2b-provider-observability-otel/tests/no_vm_label_in_metrics.rs`", + "detailedDesign" : "(1) Extend `loki_native_otel_resource_attributes` allowlist with `d2b.zone`, `d2b.provider`, `d2b.component`, `service.version`. (2) Add gate: `no_isolation` must not appear in any Provider `MetricDescriptor` label or span attribute catalog. (3) Adapt `minijail_relay_otel.rs` shape test for Provider-managed runner (no broker `RunnerRole::OtelHostBridge`). (4) Add metric inventory gates for `d2b_otel_*` instruments from this spec. (5) Retain: `startup_tracing_avoids_host_path_fields`; SigNoz-only backend assertion; `tempo_guest_collector_shape`; `config_source = \"realm-controllers\"` absence gate. Primary reuse disposition: `adapt`. Preserved source-plan detail: adapt and extend existing tests; keep existing test assertions.", + "integration" : "Contract-tests run in workspace `make test-drift` and `make test-lint`", + "removalProof" : "Not applicable", + "reuseAction" : "adapt", + "reuseSource" : null, + "specId" : "ADR-046-provider-observability-otel", + "specPath" : "docs/specs/providers/ADR-046-provider-observability-otel.md", + "validation" : "All contract-tests pass after update; existing allowlist test does not regress", + "workItemId" : "ADR046-otel-004" + }, + { + "currentSource" : "None — net-new ADR 0046 cross-Zone sharing (D096)", + "dataMigration" : "None — full d2b 3.0 reset", + "dependencyOwner" : "ADR046-zone-control-019, ADR046-zone-control-020; observability Provider owner", + "destination" : "`packages/d2b-provider-observability-otel/src/share_adapter.rs`", + "detailedDesign" : "Implement the signed adapter: `sys-obs` exports only the authority `TelemetryService`; its local ingest Endpoint remains Service-owned implementation transport and is never an Export field. ResourceExport uses only canonical `serviceType`, `projectionSchemaFingerprint`, and `factoryFingerprint`; ResourceImport uses the corresponding `expectedServiceType`, `expectedProjectionSchemaFingerprint`, and `expectedFactoryFingerprint`. Every producer import causes Core to create one local `TelemetryService` projection with ResourceImport ownership, `providerRef`, semantic base/import fields, and no `spec.provider`. Routing derives from the signed local descriptor and ResourceImport record. The semantic factory fingerprint binds factory metadata plus projection-protocol version, never Provider/adapter identity, which the signed descriptor authenticates separately. Binding `serviceRef` targets that projection. Enforce many-to-one quota/credit/backpressure/schema/source-stamp/redaction/cardinality over bounded encrypted streams; no FD/socket crosses a Zone and audit authority stays local. Primary reuse disposition: `adapt`. Preserved source-plan detail: net-new (implement the signed observability export/import adapter).", + "integration" : "Core export/import controller (ADR046-zone-control-019); local projection lifecycle (ADR046-zone-control-020); ComponentSession bounded encrypted named streams", + "removalProof" : "Not applicable", + "reuseAction" : "adapt", + "reuseSource" : "SigNoz ingest authority (this dossier); `packages/d2b-provider/src/share_adapter.rs` `ExportAdapter`/`ImportAdapter` traits", + "specId" : "ADR-046-provider-observability-otel", + "specPath" : "docs/specs/providers/ADR-046-provider-observability-otel.md", + "validation" : "Fast `projection_chain.rs` proves Service projection semantics with a fake stream, exact canonical Export/Import type and fingerprint fields, rejection of obsolete `endpointRef`/`exportedType`/`baseSchemaFingerprint`/`expectedType`/`expectedBaseSchemaFingerprint`/`projectionType`, rejection of projection `spec.provider`, and semantic-fingerprint stability under Provider/adapter identity mutation while signed descriptor authentication remains exact; integration alone runs real encrypted streams and SigNoz; revocation/reconnect, quotas, source stamp, redaction/cardinality, no FD crossing, and audit locality", + "workItemId" : "ADR046-otel-005" + }, + { + "currentSource" : "`nixos-modules/components/observability/{stack,host,guest}.nix` (SigNoz/ClickHouse/Keeper loopback authority, per-source vsock ingress, source-identity `upsert`, `sending_queue`/`retry_on_failure`, allow-list projection); `nixos-modules/{index,observability-vm}.nix`; `packages/d2b-host/src/{otel_host_bridge_argv,vsock_relay_argv}.rs` (pre-opened vsock fds; broker rejects source VM ≠ obs VM); `packages/d2bd/src/otel_host_bridge_readiness.rs`", + "dataMigration" : "None — full d2b 3.0 reset", + "dependencyOwner" : "ADR046-otel-005, ADR046-zone-control-019, ADR046-zone-control-020; observability Provider owner", + "destination" : "`packages/d2b-provider-observability-otel/src/{authority,service,binding,projection}.rs`; `AuthorityDescriptor` on the `sys-obs` `TelemetryService`", + "detailedDesign" : "Implement one provider-neutral D097 authority Service with generic telemetry-ingest Endpoint refs and common service/signals/quota/policy fields. Its strict observability-otel `spec.provider` alone selects the loopback SigNoz stack, backend Endpoints, and OTLP. Core rejects duplicates and adopts by owner proof. Implement core-owned imported Service projections with no `spec.provider` or backend ownership. Implement per-producer Bindings with common service/producer/signals/quota/policy fields and strict implementation extension; Bindings own/cause edge collector/forwarder/private Endpoints/runtime Volume. Keep generic observations in `status.resource` and SigNoz/OTLP/OTEL observations in `status.provider`. Preserve trusted source upsert, retry/queue, bounded cardinality/redaction, audit non-transfer, status-first state, and no OTEL SDK in core. Endpoint is transport only. Primary reuse disposition: `adapt`. Preserved source-plan detail: net-new universal provider-neutral `telemetry.d2bus.org.TelemetryService`/`telemetry.d2bus.org.TelemetryBinding` pair; adapt existing authority/edge behavior as the initial observability-otel implementation.", + "integration" : "Ingest authority + export (ADR046-otel-005); core export/import controller and projection lifecycle (ADR046-zone-control-019/020); ComponentSession per-import encrypted streams; d2b-telemetry closed-label metrics", + "removalProof" : "Legacy fixed per-source vsock ingress and old gates are removed only after the Service/Binding/ComponentSession successor passes; neither old provider-qualified ResourceType name nor any Endpoint-projection or ResourceType alias remains, and no duplicate suite remains.", + "reuseAction" : "adapt", + "reuseSource" : "Same baseline stack/bridge/readiness symbols; `packages/d2b-provider/src/share_adapter.rs` traits", + "specId" : "ADR-046-provider-observability-otel", + "specPath" : "docs/specs/providers/ADR-046-provider-observability-otel.md", + "validation" : "Fast `resource_service_binding.rs` and `projection_chain.rs` plus reused nix-unit/policy tests prove provider-neutral names, base/Provider field separation, projection `spec.provider` rejection, exact D088 placement of semantic observations under `status.resource` and implementation observations under `status.provider`, ownership, schemas, stamping, quotas, redaction, and projection chain. Real SigNoz and real stream scenarios are integration-only.", + "workItemId" : "ADR046-otel-006" + }, + { + "currentSource" : "`packages/d2b-core/src/processes.rs`, `minijail_profile.rs`, `storage.rs`; `d2b-contracts/src/broker_wire.rs`", + "dataMigration" : "Full reset", + "dependencyOwner" : "W0; resource contracts", + "destination" : "`packages/d2b-contracts/src/v3/host.rs`, `guest.rs`, `execution_policy.rs`, `process.rs`, `volume.rs`, `user.rs`, `network.rs`, `device.rs`, `credential.rs`", + "detailedDesign" : "Complete minimal ResourceType schemas and shared execution/Volume sub-schemas Primary reuse disposition: `adapt`. Preserved source-plan detail: extract and adapt.", + "integration" : "Provider dossiers/controller descriptors bind exact types", + "removalProof" : "Old DTOs removed only by owning future slices", + "reuseAction" : "adapt", + "reuseSource" : null, + "specId" : "ADR-046-primitive-resource-composition", + "specPath" : "docs/specs/ADR-046-primitive-resource-composition.md", + "validation" : "Schema vectors and folded-field/no-duplicate-type policy tests", + "workItemId" : "ADR046-primitives-001" + }, + { + "currentSource" : "broker SpawnRunner/pidfd; d2bd supervisor; unsafe-local helper; guest exec runner", + "dataMigration" : "Current ProcessRoles converted by exact disposition table", + "dependencyOwner" : "Process contracts; system Provider slices", + "destination" : "`packages/d2b-provider-system-systemd/`, `packages/d2b-provider-system-minijail/`, shared neutral process conformance library", + "detailedDesign" : "Common Process/EphemeralProcess, provider-specific launch/pidfd/wait/adoption/status Primary reuse disposition: `adapt`. Preserved source-plan detail: extract and adapt.", + "integration" : "Process controller registration under Host/Guest; d2b-bus ResourceClient/status", + "removalProof" : "Role branches removed only after successor Provider tests", + "reuseAction" : "adapt", + "reuseSource" : null, + "specId" : "ADR-046-primitive-resource-composition", + "specPath" : "docs/specs/ADR-046-primitive-resource-composition.md", + "validation" : "Shared conformance plus Host/Guest/user integration", + "workItemId" : "ADR046-primitives-002" + }, + { + "currentSource" : "`storage-json.nix`, `d2b-core/src/storage.rs`, store/TPM/runtime path owners, virtiofsd argv/ProcessRole", + "dataMigration" : "Full reset; Provider-specific state export only where separately specified", + "dependencyOwner" : "Volume contract; Volume Provider slices", + "destination" : "`packages/d2b-provider-volume-*/`; `nixos-modules/resources-volume.nix`", + "detailedDesign" : "Fine-grained Volume layout/views, host-path policy, virtiofs attachments/status/owned Process Primary reuse disposition: `adapt`. Preserved source-plan detail: extract and adapt.", + "integration" : "Host/Guest/Process refs and Volume controller", + "removalProof" : "storage.json rows removed only after Volume successor parity", + "reuseAction" : "adapt", + "reuseSource" : null, + "specId" : "ADR-046-primitive-resource-composition", + "specPath" : "docs/specs/ADR-046-primitive-resource-composition.md", + "validation" : "ACL/no-follow/marker, sharing/views, virtiofs host/guest mount tests", + "workItemId" : "ADR046-primitives-003" + }, + { + "currentSource" : "`d2b-core/src/{processes,process_builder,minijail_profile}.rs`; `d2bd/src/supervisor/*`; broker `spawn_runner.rs`", + "dataMigration" : "Full reset; no role snapshot import", + "dependencyOwner" : "W0/W2; Process contracts/supervisor", + "destination" : "`packages/d2b-process/src/`, `packages/d2b-provider-supervisor/src/`", + "detailedDesign" : "Common spec/status/tickets/pidfd/adoption/naming/cgroup/fast path Primary reuse disposition: `adapt`. Preserved source-plan detail: extract and adapt.", + "integration" : "Process Provider controllers → supervisor/broker/systemd → async status", + "removalProof" : "Role/DAG path removed only per role disposition", + "reuseAction" : "adapt", + "reuseSource" : "Useful main ProviderSupervisor/session/process code named by sub-items", + "specId" : "ADR-046-components-processes-and-sandbox", + "specPath" : "docs/specs/ADR-046-components-processes-and-sandbox.md", + "validation" : "Shared conformance/fault/latency tests", + "workItemId" : "ADR046-process-001" + }, + { + "currentSource" : "unsafe-local helper runtime/systemd; guest exec systemd-run; broker SpawnRunner/minijail", + "dataMigration" : "None — full d2b 3.0 reset; no prior state to migrate", + "dependencyOwner" : "ADR046-process-001; systemd/minijail Provider owners", + "destination" : "`packages/d2b-provider-system-systemd/`, `packages/d2b-provider-system-minijail/`", + "detailedDesign" : "Two Process/EphemeralProcess implementations, pidfd/wait ownership, system/user domains Primary reuse disposition: `adapt`. Preserved source-plan detail: extract/adapt.", + "integration" : "Zone-installed Providers/controller instances per Host/Guest", + "removalProof" : "Old helpers retained until host/user/guest parity", + "reuseAction" : "adapt", + "reuseSource" : "Main process/session helpers if selected by exact sub-items", + "specId" : "ADR-046-components-processes-and-sandbox", + "specPath" : "docs/specs/ADR-046-components-processes-and-sandbox.md", + "validation" : "Identical schema/status conformance plus provider-specific adoption", + "workItemId" : "ADR046-process-002" + }, + { + "currentSource" : "`packages/d2b-realm-provider/src/{provider,capabilities,error,credential,rate_limit,conformance}.rs`", + "dataMigration" : "Full reset", + "dependencyOwner" : "W0; Provider contract/catalog owner", + "destination" : "`packages/d2b-contracts/src/v3/provider.rs`, `packages/d2b-provider/src/lib.rs`, `packages/d2b-provider-toolkit/`", + "detailedDesign" : "Provider resource/manifest/components/dependencies/services/trust/compatibility/toolkit Primary reuse disposition: `adapt`. Preserved source-plan detail: extract and adapt.", + "integration" : "Zone config/catalog → Provider resource → Process components → bus/resource routes", + "removalProof" : "Old trait crate retired only after all Provider dossiers migrate", + "reuseAction" : "adapt", + "reuseSource" : null, + "specId" : "ADR-046-provider-model-and-packaging", + "specPath" : "docs/specs/ADR-046-provider-model-and-packaging.md", + "validation" : "Contract vectors, fake/malicious Provider, one-crate/one-identity policy", + "workItemId" : "ADR046-provider-001" + }, + { + "currentSource" : "`packages/Cargo.toml`; `flake.nix`; `nixos-modules/host-daemon.nix`; current source package derivations", + "dataMigration" : "No package compatibility path", + "dependencyOwner" : "Provider contract; package/Nix integrator", + "destination" : "one `packages/d2b-provider--/` per Provider with mandatory src/, tests/, integration/, README.md; generic Nix Provider package/catalog emitter", + "detailedDesign" : "Split current combined/composition crates; exact outputs/manifests/conformance/layout/documentation", + "integration" : "Provider package installed/registered per Zone", + "removalProof" : "Combined crate removed only after every live implementation has a Provider successor", + "reuseAction" : "adapt", + "reuseSource" : null, + "specId" : "ADR-046-provider-model-and-packaging", + "specPath" : "docs/specs/ADR-046-provider-model-and-packaging.md", + "validation" : "Workspace naming/dependency/output/dossier/catalog parity policy", + "workItemId" : "ADR046-provider-002" + }, + { + "currentSource" : "`d2bd` DAG/broker spawn; unsafe-local helper; guestd/exec runner; `d2b-host` runtime provider", + "dataMigration" : "Current roles converted under reset", + "dependencyOwner" : "Process contracts; system Provider owners", + "destination" : "`packages/d2b-provider-system-core/`, `d2b-provider-system-systemd/`, `d2b-provider-system-minijail/`", + "detailedDesign" : "Bootstrap system-core; common Process/EphemeralProcess providers and pidfd conformance Primary reuse disposition: `adapt`. Preserved source-plan detail: extract and adapt.", + "integration" : "Host/Guest providerRef/domain/userRef, local supervisors, resource status", + "removalProof" : "Current role launch paths removed after parity", + "reuseAction" : "adapt", + "reuseSource" : null, + "specId" : "ADR-046-provider-model-and-packaging", + "specPath" : "docs/specs/ADR-046-provider-model-and-packaging.md", + "validation" : "Shared conformance and host/user/non-Host tests", + "workItemId" : "ADR046-provider-003" + }, + { + "currentSource" : "None — D098 common semantic Service/Binding bases are net-new ADR 0046 contracts", + "dataMigration" : "Full d2b 3.0 reset; no prior public Service/Binding names or aliases are imported", + "dependencyOwner" : "ADR046-provider-001; shared semantic Service/Binding contract owner", + "destination" : "`packages/d2b-contracts/src/v3/semantic_services/{mod,audio,security_key,telemetry,usb}.rs`; generated schema artifacts for the eight exact qualified ResourceTypes", + "detailedDesign" : "Define one shared strict base spec/status DTO and schema contract for each frozen D098 Service/Binding pair, including exact semantic type/schema IDs, versions, fingerprints, minimal valid base fixtures without `spec.provider`, authority/projection Service union, same-Zone Binding `serviceRef`/target rules, D088 `status.resource` layering, status-only observations, and projection-factory type binding. A Core-generated projection permits only `providerRef`, semantic base/import fields, and ResourceImport ownership; it rejects `spec.provider`. Register no implementation-qualified or former `*State` alias.", + "integration" : "Provider manifests and ResourceApiBindings consume the common catalog fingerprint; ADR046-zone-control-019/020 use the same factory metadata to admit an owner Service and core-create one same-type projection Service; the four initial Provider dossiers supply only strict implementation extensions/controllers.", + "removalProof" : "Any Provider-local duplicate base DTO/schema is removed before that Provider is registered; only strict Provider extension DTOs remain implementation-owned.", + "reuseAction" : "create", + "reuseSource" : null, + "specId" : "ADR-046-provider-model-and-packaging", + "specPath" : "docs/specs/ADR-046-provider-model-and-packaging.md", + "validation" : "Shared contract tests cover exact names, strict serde/schema round trips, common base discoverability without any Provider package, canonical minimal base acceptance without `spec.provider`, same-Zone refs/targets, owner/projection discrimination, Core projection rejection of `spec.provider`, common fields only under `status.resource`, implementation observation only under `status.provider`, status-only observations, no Device/Endpoint/Binding projection, implementation-detail rejection, semantic factory-fingerprint stability under Provider/adapter identity changes, and rejection of every implementation-qualified/former `*State` alias. Each initial and fake alternate Provider must pass the identical base conformance fixture.", + "workItemId" : "ADR046-provider-004" + }, + { + "currentSource" : "`packages/d2b-core/src/storage.rs` (`StoragePathSpec` with `scope: ContractId` currently keyed by `ProcessRole`/Workload; `SensitivityClass`; `StorageLifecycle`; `StorageRootClass`); `packages/d2b-state/src/atomic.rs` (main, 6faa5256; absent from v3 baseline)", + "dataMigration" : "Full v3 reset; no v2 state schema import", + "dependencyOwner" : "ADR046-primitives-001; v3 contracts owner", + "destination" : "`packages/d2b-contracts/src/v3/volume_state.rs`", + "detailedDesign" : "`VolumeStateSchema` struct (`schemaId`, `schemaVersion`, `schemaDigest`, `migrationPolicy`); `PersistenceClass` and `SensitivityClass` enums; `VolumeStateStatus` extension (`stateSchemaPhase`, `installedSchemaVersion`, `markerStatus`, `sealingStatus`, `quotaUsage`, `lastMigrationAt`); `StateEnvelope` (replaces v2 `StateEnvelope`); canonical JSON serde and digest helpers Primary reuse disposition: `adapt`. Preserved source-plan detail: extract and adapt.", + "integration" : "Volume spec and status structs embed these types; Provider descriptor component stateNamespace declaration uses the same types", + "removalProof" : "`d2b-core/src/storage.rs` StoragePathSpec/SensitivityClass removed only after all Provider descriptor consumers are on v3 Volume spec", + "reuseAction" : "adapt", + "reuseSource" : null, + "specId" : "ADR-046-provider-state", + "specPath" : "docs/specs/ADR-046-provider-state.md", + "validation" : "Schema golden vectors; phase/reason round-trip; StateEnvelope digest tests", + "workItemId" : "ADR046-pstate-001" + }, + { + "currentSource" : "`packages/d2b-core/src/processes.rs` (`VmProcessDag`, `ProcessNode`, `ProcessRole`: each current `ProcessRole` variant maps to a `Process` or `EphemeralProcess` resource under its owning Provider; `ProcessRole::Swtpm`/`Virtiofsd`/`CloudHypervisorRunner` → Process resources under `Provider/device-tpm`/`Provider/volume-virtiofs`/`Provider/runtime-cloud-hypervisor`); Provider descriptor component model from ADR046-provider-001", + "dataMigration" : "Full reset", + "dependencyOwner" : "ADR046-pstate-001; Provider contracts owner", + "destination" : "`packages/d2b-contracts/src/v3/provider.rs` (component descriptor `stateNamespaces` field)", + "detailedDesign" : "Add `stateNamespaces: Vec` to the component descriptor (zero or more entries; a component declares an entry only when a payload passes the storage-need test; stateless components declare none); each entry includes `id`, `kind` (always `state`), `schemaId` (non-null), `schemaVersion`, `schemaDigest`, `persistenceClass` (must be `persistent`; `ephemeral`/`cache` rejected), `sensitivityClass`, `migrationPolicy`, `quotaBytes` (nonzero; minimum 4096), `storageNeed` (`secret` \\| `large-binary` \\| `private-unsafe-for-status` \\| `revision-unsuitable`), `sealingRequired`, `placementMode` (`guest-local` or `host-backed-guest` for Guest-targeted; omitted for Host-targeted), `hostCustodyPermitted` (required `true` for `host-backed-guest`; absent/false for `guest-local`), and `views`; there is no empty-payload (`schemaId: null`) namespace", + "integration" : "Provider package build emits component descriptors with state namespaces; Provider controller creates Volumes from descriptors at install time", + "removalProof" : "Not applicable (new)", + "reuseAction" : "adapt", + "reuseSource" : null, + "specId" : "ADR-046-provider-state", + "specPath" : "docs/specs/ADR-046-provider-state.md", + "validation" : "Descriptor schema golden vectors; descriptor-Volume consistency property test; stateless-component-declares-no-namespace round-trip; storage-need justification enforcement (namespace whose payload is derivable from spec/status/core ledger/external observation → `component-state-not-justified`); `kind != state` → `component-kind-invalid`; `persistenceClass: ephemeral` → `component-persistence-class-forbidden`; `quotaBytes: 0` or `1024` → `component-quota-too-small`; base `quota.maxBytes == quotaBytes` and `quota.maxInodes > 0`; Guest-targeted with `placementMode: guest-local` → source.executionRef=Guest; Guest-targeted with `host-backed-guest` + `hostCustodyPermitted: true` → source on Host, Export created; `host-backed-guest` without `hostCustodyPermitted: true` → `placement-host-custody-violation`; credential/audit schema with `host-backed-guest` → `guest-local-required`; `placementMode` change → descriptor version increment enforced", + "workItemId" : "ADR046-pstate-002" + }, + { + "currentSource" : "`packages/d2b-state/src/atomic.rs`, `path.rs`, `lock.rs` (main, 6faa5256); `packages/d2b-priv-broker/src/ops/swtpm_dir.rs` (marker algorithm, v3 baseline)", + "dataMigration" : "New marker written for each Volume at v3 first-boot; TPM marker path adapted from current swtpm-markers root", + "dependencyOwner" : "ADR046-pstate-001; volume-local Provider owner", + "destination" : "`packages/d2b-provider-volume-local/` (new crate, full scaffold required): `src/{atomic.rs, path.rs, lock.rs, marker.rs, effect_port.rs}`; `tests/volume_local.rs` (marker missing/replaced/mismatch, domain-isolation rejection, quota enforcement); `integration/volume_local.rs` (real Host filesystem provision, broker-maintained marker check, domain-isolation rejection cross-process); `README.md`", + "detailedDesign" : "Anchored Volume root provision, identity marker write/check, quota soft-check on write, domain-isolation validation, fd-relative layout creation/repair/cleanup, broker-maintained marker root protocol; layout `ownerRef`/`groupRef` must reference a Nix-preprovisioned User principal or bounded system pool — Volume admission rejects runtime-created principals; `VolumeEffectPort` returns opaque IDs and named view dirfds only — no raw host path returned by any EffectPort operation; volume-local must support `source.executionRef: Guest/` for `guest-local` placement (controller running inside the Guest): when executing in a Guest domain, volume-local may not create, read, or hold dirfds/paths for Volumes sourced in another domain; `host-backed-guest` placement creates a `virtiofs.d2bus.org.Export` child per attachment entry and validates `hostCustodyPermitted: true` in the signed descriptor Primary reuse disposition: `adapt`. Preserved source-plan detail: copy-unchanged (path.rs) / adapt (atomic.rs, lock.rs) / adapt (swtpm_dir.rs marker algorithm).", + "integration" : "`d2b-priv-broker` calls `volume_local::marker::provision_marker` at broker-maintained Volume creation; `d2b-provider-volume-local` controller calls `marker::verify_marker` on every daemon restart via reconcile startup relist", + "removalProof" : "`swtpm_dir.rs` marker implementation retired only after device-tpm Provider Volume is live and marker-check parity is confirmed", + "reuseAction" : "adapt", + "reuseSource" : null, + "specId" : "ADR-046-provider-state", + "specPath" : "docs/specs/ADR-046-provider-state.md", + "validation" : "Marker missing/replaced/mismatch tests; domain-isolation rejection tests; quota enforcement tests; crash at every provision step", + "workItemId" : "ADR046-pstate-003" + }, + { + "currentSource" : "`packages/d2b-core/src/storage_lifecycle.rs` (`StorageLifecycleReport` issue detection); `packages/d2b-state/src/atomic.rs` (main, 6faa5256)", + "dataMigration" : "None (new protocol)", + "dependencyOwner" : "ADR046-reconcile-001, ADR046-pstate-003; volume-local Provider and controller-toolkit owners", + "destination" : "`packages/d2b-provider-volume-local/src/migration.rs`; `packages/d2b-provider-volume-local/tests/migration_unit.rs` (hermetic staging Volume and prepare/commit/rollback); `packages/d2b-provider-volume-local/integration/migration.rs` (real Host crash-injection at each migration step, N-Volume cross-component coordination); `packages/d2b-controller-toolkit/src/state_migration.rs`", + "detailedDesign" : "Pre-launch migration EphemeralProcess template, staging Volume create/destroy lifecycle, prepare/commit/rollback protocol implementation, roll-forward on restart detection, migration idempotency", + "integration" : "Provider controller's reconcile handler calls toolkit `state_migration::plan` and dispatches EphemeralProcess via ResourceClient; volume-local Provider reports `stateSchemaPhase` transitions", + "removalProof" : "`StorageLifecycleReport` and storage contract validation in `d2b-core` removed only after v3 Volume migration path is live", + "reuseAction" : "adapt", + "reuseSource" : null, + "specId" : "ADR-046-provider-state", + "specPath" : "docs/specs/ADR-046-provider-state.md", + "validation" : "Migration with and without crash at each step; rollback after failed EphemeralProcess; roll-forward after interrupted commit; cross-component N-Volume coordination", + "workItemId" : "ADR046-pstate-004" + }, + { + "currentSource" : "`packages/d2b-state/src/lease.rs` (main, 6faa5256); Credential ResourceType from ADR046-primitives-001", + "dataMigration" : "Sealed Volumes are new (no existing sealing to migrate)", + "dependencyOwner" : "ADR046-pstate-003; volume-local Provider and Credential Provider owners", + "destination" : "`packages/d2b-provider-volume-local/src/sealing.rs`; `packages/d2b-provider-volume-local/tests/sealing_unit.rs` (hermetic seal/rotation state machine); `packages/d2b-provider-volume-local/integration/sealing.rs` (real Credential lease flow, interrupted key rotation with live Credential Provider)", + "detailedDesign" : "Envelope encryption on write using Credential lease key material; key rotation EphemeralProcess re-encrypt; no raw key on disk; `sealingStatus` transitions; `rotation-failed` fail-safe", + "integration" : "Volume controller reads Credential status/lease before provisioning; sealing wraps `StateEnvelope` writes in atomic.rs", + "removalProof" : "Not applicable (new)", + "reuseAction" : "adapt", + "reuseSource" : null, + "specId" : "ADR-046-provider-state", + "specPath" : "docs/specs/ADR-046-provider-state.md", + "validation" : "Seal/read/rotation tests; rotation interrupted at commit; credential revoked during runtime", + "workItemId" : "ADR046-pstate-005" + }, + { + "currentSource" : "`packages/d2b-state/src/atomic.rs` (main, 6faa5256): `AtomicFilesystem` read snapshot; no existing snapshot infrastructure in v3", + "dataMigration" : "No existing snapshots; new infrastructure only", + "dependencyOwner" : "ADR046-pstate-003, ADR046-pstate-004; volume-local Provider and snapshot toolkit owners", + "destination" : "`packages/d2b-provider-volume-local/src/snapshot.rs`; `packages/d2b-provider-volume-local/tests/snapshot_unit.rs` (hermetic `snapshotPolicy` enforcement, retention count and TTL logic); `packages/d2b-provider-volume-local/integration/snapshot.rs` (real Host filesystem snapshot creation, retention expiry, pre-migration auto-snapshot with interrupted migration)", + "detailedDesign" : "Snapshot EphemeralProcess; bounded `.snapshots/` sub-tree; `snapshotPolicy` enforcement; retention count and TTL cleanup; snapshot status tracking in Volume status", + "integration" : "Provider controller creates snapshot EphemeralProcess before migration and relocation based on `snapshotPolicy.triggerOnMigration` and `triggerOnRelocation`; status populated via Volume status update", + "removalProof" : "Not applicable (new)", + "reuseAction" : "adapt", + "reuseSource" : null, + "specId" : "ADR-046-provider-state", + "specPath" : "docs/specs/ADR-046-provider-state.md", + "validation" : "Create/read/expire/retention-limit tests; snapshot before interrupted migration; snapshot list in Volume status", + "workItemId" : "ADR046-pstate-006" + }, + { + "currentSource" : "`packages/d2b-priv-broker/src/ops/swtpm_dir.rs` (v3, marker pattern); `packages/d2b-state/src/path.rs` (main)", + "dataMigration" : "Not applicable", + "dependencyOwner" : "ADR046-pstate-003; volume-local Provider owner", + "destination" : "`packages/d2b-provider-volume-local/src/relocation.rs`; `packages/d2b-provider-volume-local/tests/relocation_unit.rs` (hermetic finalizer set/clear, commit/failure state machine); `packages/d2b-provider-volume-local/integration/relocation.rs` (real Host-to-Host anchored file copy, crash at copy midpoint, virtiofs source re-point after successful relocation)", + "detailedDesign" : "Relocation EphemeralProcess; source finalizer; anchored copy; source Volume (volume-local) relocation for components with Guest attachment (the attachment Volume backed by volume-virtiofs is re-pointed to the new source after copy; see `ADR-046-primitive-resource-composition` Volume attachment spec); commit/failure handling", + "integration" : "Provider controller adds `Relocating` finalizer before creating relocation EphemeralProcess; removes finalizer after successful destination Volume activation", + "removalProof" : "Not applicable (new)", + "reuseAction" : "adapt", + "reuseSource" : null, + "specId" : "ADR-046-provider-state", + "specPath" : "docs/specs/ADR-046-provider-state.md", + "validation" : "Relocation with crash at copy midpoint; failed relocation source preservation; virtiofs source relocation test", + "workItemId" : "ADR046-pstate-007" + }, + { + "currentSource" : "`packages/d2b-state/src/audit.rs` (main, 6faa5256): `AuditAppender`, `AuditRecordInput`, `SegmentBuilder`, `checkpoint`, `decide_retention`, `detect_gap`, `read_audit_segment`; Zone audit stream interface from ADR046-bus contracts (see ADR-046-componentsession-and-bus); OTEL cardinality model from `packages/d2b-provider-observability-local/src/` (main, a1cc0b2d)", + "dataMigration" : "None — full d2b 3.0 reset; no prior state to migrate", + "dependencyOwner" : "ADR046-pstate-001; Zone audit stream, OTEL provider owners", + "destination" : "`packages/d2b-provider-volume-local/src/audit.rs`; `packages/d2b-provider-volume-local/src/otel.rs`; `packages/d2b-provider-volume-local/tests/audit_unit.rs` (hermetic audit golden records, OTEL cardinality label tests)", + "detailedDesign" : "Volume-state audit event types and Zone audit emission; OTEL metric definitions with closed cardinality label sets", + "integration" : "Every state lifecycle transition calls `audit::emit_volume_event`; OTEL metrics exported via `observability-otel` Provider", + "removalProof" : "Not applicable", + "reuseAction" : "adapt", + "reuseSource" : null, + "specId" : "ADR-046-provider-state", + "specPath" : "docs/specs/ADR-046-provider-state.md", + "validation" : "Audit event golden records; no content/path/credential in audit payload; OTEL cardinality label tests", + "workItemId" : "ADR046-pstate-008" + }, + { + "currentSource" : "`packages/d2b-state/tests/state.rs`, `async_state.rs` (main, 6faa5256): atomic, lock, quarantine, audit, lease tests; `packages/d2b-provider-toolkit/src/fixture.rs` (main, a1cc0b2d): `Fixture`, `FakeProvider`, `DeterministicClock`, `ProviderValues`, `Redacted`, `Secret`, `sample_lease_request`", + "dataMigration" : "None — full d2b 3.0 reset; no prior state to migrate", + "dependencyOwner" : "ADR046-pstate-001 through ADR046-pstate-008; integration test owner", + "destination" : "`packages/d2b-provider-volume-local/tests/state.rs` (ported hermetic atomic/lock/quarantine/lease tests); `packages/d2b-provider-volume-local/tests/migration.rs` (ported migration fault-injection, cross-component N-Volume coordination); `packages/d2b-provider-volume-local/integration/provider_state.rs` (end-to-end: live daemon, real Host Volume mount, cross-process worker subview, full audit stream); `packages/d2b-provider-volume-local/integration/audit.rs` (live Zone audit stream emission and OTEL export)", + "detailedDesign" : "Port all d2b-state integration tests replacing ADR 0045 contract setup with v3 Volume/StateEnvelope; add provider-state-specific migration, marker, quota, sealing, relocation, snapshot, incident-hold, and unclaimed-GC tests; include cross-component N-Volume coordination test", + "integration" : "Tests run against the real volume-local Provider over a fake Zone runtime (no live daemon required) using the standard controller-toolkit fake clients", + "removalProof" : "`d2b-state` crate retired from workspace only after every caller migrates to v3 Volume state helpers and all ported tests pass", + "reuseAction" : "adapt", + "reuseSource" : null, + "specId" : "ADR-046-provider-state", + "specPath" : "docs/specs/ADR-046-provider-state.md", + "validation" : "All ported tests pass under v3 contracts; test coverage includes every fault-injection scenario listed in d2b-state/tests/state.rs plus new provider-state cases; stateless-component-declares-no-Volume test passes; shared-Volume attempt rejected; `guest-local` Volume creation inside Guest domain (source.executionRef=Guest, no Export created, Host volume-local holds no dirfd/path); `host-backed-guest` Volume creation (source on Host, Export created, Export reaches Ready, Guest Process mounts source Volume view); `host-backed-guest` without `hostCustodyPermitted: true` → `placement-host-custody-violation`; credential/audit schema with `host-backed-guest` → `guest-local-required`; cross-domain isolation: Guest-local volume-local does not create or observe Host-domain Volumes", + "workItemId" : "ADR046-pstate-009" + }, + { + "currentSource" : "`nixos-modules/manifest.nix` (current `manifest.json` emitter, v3 baseline `fd5b0067`); `packages/d2b-core/src/storage.rs` (`StorageAuthority::NixModule`-owned rows); `packages/xtask/src/main.rs` (`gen-schemas` command, same baseline)", + "dataMigration" : "`nixos-modules/manifest.nix` provider-registration and storage-authority rows superseded by `zone-resources.nix`; prior `manifest.json` format retired after Zone daemon migration to bundle format", + "dependencyOwner" : "ADR046-pstate-001; Zone resource bundle owner, NixOS module owner, `d2b-core-controller` owner (ADR-046-core-controllers)", + "destination" : "`nixos-modules/zone-resources.nix` (per-Zone bundle emitter NixOS module); `packages/d2b-core/src/v3/zone_bundle.rs` (shared bundle DTOs: `ZoneResourceBundle`, `BundleResource`, `contentId` computation); `packages/d2b-core-controller/src/configuration.rs` (diff/apply loop, name-conflict detection, `pending-cleanup` Zone status, `maxFinalizerDurationSeconds` stall detection — NOT in `d2b-provider-volume-local`); `packages/d2b-core-controller/tests/configuration.rs` (hermetic bundle diff, absent-resource Delete dispatch, name-conflict `Degraded/name-conflict` item, integrity-failure abort); `packages/d2b-core-controller/integration/configuration.rs` (container-based generation activation with running Providers, absent-resource cleanup, rollback, finalizer-timeout stall detection)", + "detailedDesign" : "Generic `d2b.zones..resources. = { type = \"…\"; spec = { …exact ResourceTypeSpec fields… }; }` attrset; `metadata.name` derived from attr key, `metadata.zone` from Zone key, `apiVersion` defaulted; `status` omitted (read-only); Nix option types for `spec.*` generated from committed `ResourceTypeSchema` for each `type`; Nix option types for `spec.config.*` generated from the Provider artifact's config schema module (resolved at eval time via `spec.artifactId` from `d2b.artifacts`); credential-ref guard (`credentialRef: true` schema fields accept only `Credential/[a-z][a-z0-9-]*`); build-phase full JSON Schema validation of rendered `spec` against Provider manifest; canonical sorted bundle emission with `contentId` (SHA-256 of sorted `resources` array); configuration service sets `metadata.managedBy: configuration` and `configurationGeneration` in the resource store when persisting activated bundle resources (not in the bundle input; user authors only `type` + `spec`); diff compares new configured set against persisted resource store entries where `managedBy: configuration` (not against the prior bundle file); name-conflict detection: `(type, name)` collision with existing `managedBy: controller` or `managedBy: api` resource → `Degraded/name-conflict` activation item, existing resource untouched, `managedBy` never seized; unchanged-spec resources receive updated `configurationGeneration` with no controller reconcile triggered; absent-resource async Delete with owner-child/finalizer ordering; `Degraded/pending-cleanup` Zone status condition; per-Zone prior bundle count retention (range 1..16, default 3; no TTL); `maxFinalizerDurationSeconds` stall detection and `Degraded/finalizer-timeout` condition (no force-clear)", + "integration" : "NixOS build emits `/etc/d2b/zones//resources.json`; Zone daemon watches path via systemd path unit; reconcile loop runs generation diff on change; Provider controller receives `deletionRequestedAt` watch event when configuration-owned Provider is absent from new bundle", + "removalProof" : "`nixos-modules/manifest.nix` provider-registration rows retired only after all Provider registrations use the bundle format and all consumers (broker, Zone daemon) complete bundle-format migration", + "reuseAction" : "adapt", + "reuseSource" : null, + "specId" : "ADR-046-provider-state", + "specPath" : "docs/specs/ADR-046-provider-state.md", + "validation" : "All eight removed-resource cleanup tests enumerated in [Required tests for removed-resource cleanup](#required-tests-for-removed-resource-cleanup); eval credential-ref guard (test: raw value in `credentialRef` field → NixOS eval error, no bundle emitted); Provider schema conformance golden vector (test: unknown `config` key → build fails); `contentId` determinism (test: two independent builds of identical Nix inputs produce byte-identical bundles)", + "workItemId" : "ADR046-pstate-010" + }, + { + "currentSource" : "`packages/xtask/src/main.rs` (`gen-schemas` and workspace-policy checks, v3 baseline `fd5b0067`); `tests/unit/gates/drift-check.sh` (schema drift gate, same baseline); `packages/d2b-contract-tests/` (contract-test policy, frozen per AGENTS.md)", + "dataMigration" : "Not applicable", + "dependencyOwner" : "ADR046-pstate-003; workspace policy and xtask owners", + "destination" : "`packages/xtask/src/provider_crate_policy.rs`; `tests/unit/gates/provider-crate-layout-check.sh`", + "detailedDesign" : "`cargo xtask check-provider-crate-layout` command: walks every workspace member matching `d2b-provider-*`; for each, asserts presence of `src/`, `tests/`, `integration/`, and `README.md`; asserts `integration/` contains at least one `.rs` file and a `README.md`; fails closed with a typed `missing-provider-crate-path` error listing every absent path; wired into `make test-policy` via `tests/unit/gates/provider-crate-layout-check.sh`; output is machine-readable JSON (`{ \"crate\": \"…\", \"missing\": [\"integration/\"] }` per violation) Primary reuse disposition: `adapt`. Preserved source-plan detail: extend.", + "integration" : "`make test-policy` runs `cargo xtask check-provider-crate-layout`; GitHub CI runs `make test-policy` on every PR; `make check` includes `test-policy` as a required Layer-1 shard; workspace policy tests in `packages/d2b-contract-tests/` are extended with a static manifest check that asserts `provider_crate_layout` policy version is current", + "removalProof" : "Not applicable (permanent gate)", + "reuseAction" : "adapt", + "reuseSource" : null, + "specId" : "ADR-046-provider-state", + "specPath" : "docs/specs/ADR-046-provider-state.md", + "validation" : "Policy gate detects missing `src/` → error; missing `tests/` → error; missing `integration/` → error; missing `README.md` → error; empty `integration/` (no `.rs` files) → error; all four paths present and non-empty → pass; existing non-provider `d2b-*` crates not flagged; gate is idempotent across re-runs", + "workItemId" : "ADR046-pstate-011" + }, + { + "currentSource" : "`packages/d2b-core/src/status.rs` (v3 common status/observedGeneration/conditions); Provider descriptor state-namespace declaration from ADR046-pstate-002", + "dataMigration" : "New; no prior bootstrap artifacts to migrate", + "dependencyOwner" : "ADR046-pstate-001, ADR046-pstate-002; Zone runtime owner (`d2b-core-controller`), volume-local Provider owner", + "destination" : "`packages/d2b-core-controller/src/optional_state_admission.rs` (storage-need admission: reject a declared namespace whose payload is derivable from spec/status/core ledger/external observation with `component-state-not-justified`; only declared namespaces produce a Volume; stateless components produce none); `packages/d2b-core-controller/tests/optional_state_admission.rs` (hermetic: stateless component → no Volume; declared `storageNeed` variants accepted; unjustified namespace rejected; status-first restart revalidation — controller re-derives observed state from status/core ledger/external observation after restart and never treats a status field as authority); `packages/d2b-provider-volume-local/tests/status_bounds.rs` (hermetic: total canonical serialized status cap and provider-specific detail cap enforced; oversize status write → typed rejection; status carries no secret/path/argv/PID/unit/stream/ring content)", + "detailedDesign" : "Optional state-Volume admission is a fixed step in Core ProviderDeployment: for each component, if it declares no `stateNamespaces` entry it gets no Volume; for each declared entry, verify the `storageNeed` justification (`secret` \\| `large-binary` \\| `private-unsafe-for-status` \\| `revision-unsuitable`) and reject a namespace whose payload is fully derivable from spec/status/core ledger/external observation with `component-state-not-justified`. Fixed bootstrap components (`system-core`, `system-minijail`, first `volume-local` instance) declare no state Volume and reach Ready using resource `status`, the core Operation ledger, and external observation only; there is no bootstrap-storage mechanism. Status-bound enforcement: reject a status write whose total canonical serialized size exceeds the single canonical status cap, or whose provider-specific detail exceeds the detail cap, or whose condition/count/list/map entries exceed the bounded limits, with the typed status-oversize rejection; status writes occur only on material change.", + "integration" : "`d2b-core-controller` runs optional state-Volume admission before creating any declared Volume and before launching a component Process; the status-bound check is applied on every status subresource write in the resource store.", + "removalProof" : "Not applicable (permanent admission + status-bound enforcement)", + "reuseAction" : "adapt", + "reuseSource" : null, + "specId" : "ADR-046-provider-state", + "specPath" : "docs/specs/ADR-046-provider-state.md", + "validation" : "Stateless component → no Volume created; each `storageNeed` variant accepted with a declared Volume; unjustified namespace → `component-state-not-justified`; status-first restart: controller re-derives observed state and reverifies against external reality, never treating status as authority; oversize/over-detail/over-cardinality status write → typed rejection; status contains no secret/path/argv/PID/unit/stream/ring content", + "workItemId" : "ADR046-pstate-012" + }, + { + "currentSource" : "None — net-new v3 work; no pre-ADR45 baseline equivalent", + "dataMigration" : "Full d2b 3.0 reset; no v2 state/config import", + "dependencyOwner" : "P0; blocks all other runtime-qemu-media work items; owner: `runtime-qemu-media` Provider crate", + "destination" : "packages/d2b-provider-runtime-qemu-media/{src/lib.rs,tests/provider_layout.rs,integration/mod.rs,README.md}", + "detailedDesign" : "Crate scaffold and layout gate: create the crate with the four required paths, commit a README.md stub meeting §1 requirements, and wire the workspace policy gate so the crate cannot land without `src/`, `tests/`, `integration/`, and `README.md`.", + "integration" : "Workspace/Cargo policy consumes the new crate layout; later Guest schema, controller, QMP, Nix, and integration work items build inside this crate.", + "removalProof" : "None — net-new; no prior owner to remove", + "reuseAction" : "create", + "reuseSource" : null, + "specId" : "ADR-046-provider-runtime-qemu-media", + "specPath" : "docs/specs/providers/ADR-046-provider-runtime-qemu-media.md", + "validation" : "`make test-policy` (workspace policy gate)", + "workItemId" : "ADR046-qemu-media-001" + }, + { + "currentSource" : "`packages/d2b-core/src/host.rs` — `HostQemuMedia`, `QemuMediaSourceIntent` field names/types only; raw path/credential fields are discarded", + "dataMigration" : "Full d2b 3.0 reset; media guests are reauthored as `Guest`/`Volume`/`Device` resources rather than importing v2 host media config", + "dependencyOwner" : "P0; depends on ADR046-qemu-media-001; owner: runtime-qemu-media type/schema implementation", + "destination" : "packages/d2b-provider-runtime-qemu-media/src/types/guest.rs", + "detailedDesign" : "Guest ResourceType schema and serde: define `GuestSpec`, `GuestStatus`, and `GuestProviderSpecSettings` with serde and `schemars` JSON Schema. Fields must match §4, §5, and §16 exactly. Enforce `bootMediaRef` as a `Volume/` ResourceRef, `removableVolumeRefs` max 4 entries, `providerPhase` max 64 chars with the closed value set, and no argv/path/credential bytes in any serialized type. Primary reuse disposition: `adapt`. Preserved source-plan detail: adapt selected baseline field concepts; discard raw paths, argv, and credential-carrying fields.", + "integration" : "Nix-rendered Guest resources and ResourceAPI admission use these types; the controller consumes the validated spec and writes matching status; conformance and schema tests consume the generated schema.", + "removalProof" : "`HostQemuMedia`/`QemuMediaSourceIntent` raw path surfaces are superseded once all media Guest specs use ResourceRefs and schema tests prove no path/argv/credential fields remain", + "reuseAction" : "adapt", + "reuseSource" : null, + "specId" : "ADR-046-provider-runtime-qemu-media", + "specPath" : "docs/specs/providers/ADR-046-provider-runtime-qemu-media.md", + "validation" : "`tests/guest_schema_roundtrip.rs`; `tests/guest_provider_settings_bounds.rs`", + "workItemId" : "ADR046-qemu-media-002" + }, + { + "currentSource" : "`packages/d2b-core/src/runtime.rs` — timeout/quota concepts only", + "dataMigration" : "Full d2b 3.0 reset; no v2 state/config import", + "dependencyOwner" : "P0; depends on ADR046-qemu-media-001; owner: runtime-qemu-media Provider config/schema implementation", + "destination" : "packages/d2b-provider-runtime-qemu-media/src/config.rs", + "detailedDesign" : "Provider config schema and projection: define `ProviderConfig`, derive JSON Schema, require `controllerExecutionRef`, validate bounds, and project config only to the controller component. Worker processes receive no root config, no ResourceAPI authority, and no d2b-bus authority. Primary reuse disposition: `adapt`. Preserved source-plan detail: adapt bounded timeout/quota concepts into v3 Provider config; project only to the controller component.", + "integration" : "Provider ResourceSpec admission validates this schema; ProviderDeployment injects the projected config into the controller; controller uses the provider refs and quotas when reconciling Guest, Volume, Network, Device, Endpoint, and Process resources.", + "removalProof" : "None — config projection is a new Provider resource surface; no prior owner is removed by this item", + "reuseAction" : "adapt", + "reuseSource" : null, + "specId" : "ADR-046-provider-runtime-qemu-media", + "specPath" : "docs/specs/providers/ADR-046-provider-runtime-qemu-media.md", + "validation" : "`tests/config_schema_projection.rs`", + "workItemId" : "ADR046-qemu-media-003" + }, + { + "currentSource" : "None — net-new v3 work; no pre-ADR45 baseline equivalent", + "dataMigration" : "None — status-first controller state only; no runtime state is migrated into a Provider state Volume", + "dependencyOwner" : "P0; depends on ADR046-qemu-media-001 and ADR046-qemu-media-003; owner: runtime-qemu-media controller descriptor/state implementation", + "destination" : "packages/d2b-provider-runtime-qemu-media/src/{descriptor.rs,state.rs}; no Volume management code for Provider state", + "detailedDesign" : "Controller status-first operational state (no state Volume): controller component descriptor declares an empty `stateNamespaces` list; ProviderDeployment creates no controller state Volume; controller writes reconcile stage, per-Guest launch/adoption observations, bounded counters, and closed-enum error detail to `status` on material change without secrets, paths, argv, PIDs, or unit names; restart re-derives observed state from the Zone resource store, core Operation ledger, and independent external observation with fresh pidfds. Worker Processes and the controller receive no state-Volume mount.", + "integration" : "ProviderDeployment reads the descriptor; the controller projects bounded observations to Guest status and the Operation ledger; restart/adoption logic consumes resource-store, ledger, and external runner observations rather than private state storage.", + "removalProof" : "None — this item prevents creation of a new Provider state Volume and has no prior state owner to remove", + "reuseAction" : "create", + "reuseSource" : null, + "specId" : "ADR-046-provider-runtime-qemu-media", + "specPath" : "docs/specs/providers/ADR-046-provider-runtime-qemu-media.md", + "validation" : "`tests/state_status_spec.rs`; `tests/state_status_restart.rs`; `tests/state_mount_exclusivity.rs`", + "workItemId" : "ADR046-qemu-media-004" + }, + { + "currentSource" : "`packages/d2b-host/src/qemu_media_argv.rs` — `run_dir` and socket naming pattern only; raw path construction is discarded", + "dataMigration" : "Full d2b 3.0 reset; runtime tmpfs state is ephemeral and not imported from v2 run directories", + "dependencyOwner" : "P1; depends on ADR046-qemu-media-001 and ADR046-qemu-media-003; owner: runtime-qemu-media controller Volume reconciliation", + "destination" : "packages/d2b-provider-runtime-qemu-media/src/controller/volume.rs", + "detailedDesign" : "Runtime tmpfs Volume resource: controller creates the per-Guest runtime tmpfs Volume specified in §6.1. The emitted spec must exactly match the canonical YAML, including all layout entries, views, quota, and `cleanupPolicy: vm-stop-with-proof`. Primary reuse disposition: `adapt`. Preserved source-plan detail: adapt naming intent into controller-created Volume resources; replace raw runtime directory paths with `Volume` specs.", + "integration" : "Guest reconcile creates/updates this Volume through the ResourceAPI; `volume-local` materializes the tmpfs and returns attachments to the Process launch flow; finalize proves cleanup before Guest finalization.", + "removalProof" : "Legacy raw run-directory handling from `qemu_media_argv.rs` is superseded once runtime storage is represented only by controller-created Volume resources", + "reuseAction" : "adapt", + "reuseSource" : null, + "specId" : "ADR-046-provider-runtime-qemu-media", + "specPath" : "docs/specs/providers/ADR-046-provider-runtime-qemu-media.md", + "validation" : "`tests/runtime_volume_spec.rs`; `tests/volume_cleanup_policy.rs`", + "workItemId" : "ADR046-qemu-media-005" + }, + { + "currentSource" : "`packages/d2b-core/src/host.rs` `QemuMediaSourceKind` — media kind enumeration only", + "dataMigration" : "Full d2b 3.0 reset; operator-authored media is declared as Volume resources rather than imported from raw qemu-media source paths", + "dependencyOwner" : "P1; depends on ADR046-qemu-media-002 and ADR046-qemu-media-005; owner: runtime-qemu-media media dependency controller", + "destination" : "packages/d2b-provider-runtime-qemu-media/src/controller/media_watch.rs", + "detailedDesign" : "Media Volume watch and virtio-blk attachment validation: controller watches `bootMediaRef` and `removableVolumeRefs` Volumes for `Ready` status and validates that each has a `virtio-blk` attachment for the owning Guest. It performs no path inspection. Primary reuse disposition: `adapt`. Preserved source-plan detail: adapt media kind concepts to Volume source-kind assertions and ResourceRef watches.", + "integration" : "Guest reconcile gates Process launch on watched Volume readiness; Volume attachment status feeds LaunchTicket media fd assembly and Guest conditions.", + "removalProof" : "Legacy media source path handling is superseded once media is delivered only through Volume ResourceRefs and virtio-blk attachments", + "reuseAction" : "adapt", + "reuseSource" : null, + "specId" : "ADR-046-provider-runtime-qemu-media", + "specPath" : "docs/specs/providers/ADR-046-provider-runtime-qemu-media.md", + "validation" : "`tests/media_volume_watch.rs`; `tests/media_attachment_validation.rs`", + "workItemId" : "ADR046-qemu-media-006" + }, + { + "currentSource" : "None — net-new v3 work; no pre-ADR45 baseline equivalent", + "dataMigration" : "Full d2b 3.0 reset; no v2 state/config import", + "dependencyOwner" : "P1; depends on ADR046-qemu-media-002; owner: runtime-qemu-media Device dependency controller", + "destination" : "packages/d2b-provider-runtime-qemu-media/src/controller/device_watch.rs", + "detailedDesign" : "KVM Device watch: controller watches `Device/host-kvm` from `spec.deviceAttachments` for `Ready` status and gates runner launch on it, propagating Pending/Ready/Failed transitions to Guest conditions.", + "integration" : "Device resource status drives Guest reconcile dependency gating; a Ready KVM Device contributes the sealed kvm fd slot to the LaunchTicket through the Process provider chain.", + "removalProof" : "None — Device-gated KVM readiness is a new v3 Resource dependency, not a removal item", + "reuseAction" : "create", + "reuseSource" : null, + "specId" : "ADR-046-provider-runtime-qemu-media", + "specPath" : "docs/specs/providers/ADR-046-provider-runtime-qemu-media.md", + "validation" : "`tests/kvm_device_watch.rs`", + "workItemId" : "ADR046-qemu-media-007" + }, + { + "currentSource" : "None — net-new v3 work; no pre-ADR45 baseline equivalent", + "dataMigration" : "Full d2b 3.0 reset; no v2 state/config import", + "dependencyOwner" : "P1; depends on ADR046-qemu-media-002 and the `display-wayland` Provider dossier; owner: runtime-qemu-media display integration", + "destination" : "packages/d2b-provider-runtime-qemu-media/src/controller/display.rs", + "detailedDesign" : "WaylandSession resource management: when `spec.provider.settings.displayWindow = true`, controller creates, updates, deletes, and watches a `display-wayland.d2bus.org.WaylandSession` resource using the exact ResourceSpec from the display-wayland dossier. It reads the EndpointRef attachment from status using only display-wayland-defined field names. Primary reuse disposition: `create`. Preserved source-plan detail: net-new against the display-wayland Resource contract.", + "integration" : "Guest reconcile produces WaylandSession resources; display-wayland publishes Endpoint attachments; LaunchTicket assembly consumes the display fd only when the session is Ready; finalize deletes the session.", + "removalProof" : "None — display proxy work is delegated to a new WaylandSession Resource dependency rather than removing a baseline owner in this item", + "reuseAction" : "create", + "reuseSource" : null, + "specId" : "ADR-046-provider-runtime-qemu-media", + "specPath" : "docs/specs/providers/ADR-046-provider-runtime-qemu-media.md", + "validation" : "`tests/wayland_session_create.rs`; `tests/wayland_session_attachment_read.rs`; `tests/wayland_session_missing_provider.rs`", + "workItemId" : "ADR046-qemu-media-008" + }, + { + "currentSource" : "`packages/d2b-host/src/qemu_media_argv.rs` fd-index arg shape; `packages/d2b-core/src/processes.rs` `ProcessRole::QemuMediaRunner` sandbox/budget baseline", + "dataMigration" : "Full d2b 3.0 reset; existing QEMU runner process state is not imported and launch state is rebuilt from resources", + "dependencyOwner" : "P1; depends on ADR046-qemu-media-002, ADR046-qemu-media-005, ADR046-qemu-media-006, ADR046-qemu-media-007, ADR046-qemu-media-008, and ADR046-qemu-media-012; owner: runtime-qemu-media Process launch builder", + "destination" : "packages/d2b-provider-runtime-qemu-media/src/controller/process_builder.rs", + "detailedDesign" : "Process spec builder and LaunchTicket assembly: build the canonical `qemu-media-runner` Process ResourceSpec from §10.1 and assemble the LaunchTicket with sealed fd slots for kvm, tap, media, and optional display fds. No raw path, argv, executable path, or principal appears in any public field. Primary reuse disposition: `adapt`. Preserved source-plan detail: adapt fd-slot and sandbox/budget concepts to canonical Process resources and sealed LaunchTickets; do not copy raw argv strings or path construction.", + "integration" : "Controller emits Process resources; system-minijail/Process Provider consumes the spec and LaunchTicket; QEMU runner receives only sealed fds; Endpoint resources represent QMP/serial connections.", + "removalProof" : "`ProcessRole::QemuMediaRunner` and raw qemu-media argv launch surfaces are removable after canonical Process specs and LaunchTickets cover every runner launch", + "reuseAction" : "adapt", + "reuseSource" : null, + "specId" : "ADR-046-provider-runtime-qemu-media", + "specPath" : "docs/specs/providers/ADR-046-provider-runtime-qemu-media.md", + "validation" : "`tests/process_spec_golden.rs`; `tests/launch_ticket_fd_slots.rs`; `tests/no_raw_argv_in_spec.rs`", + "workItemId" : "ADR046-qemu-media-009" + }, + { + "currentSource" : "`packages/d2b-host/src/media.rs` QMP command set; `packages/d2b-contracts/src/broker_wire.rs` `QemuMedia*` command payload shapes only", + "dataMigration" : "Full d2b 3.0 reset; no v2 QMP socket path/session state is imported", + "dependencyOwner" : "P1; depends on ADR046-qemu-media-009; owner: runtime-qemu-media QMP client implementation", + "destination" : "packages/d2b-provider-runtime-qemu-media/src/qmp/", + "detailedDesign" : "QMP endpoint attachment handling: consume `qmp` and `serial` Endpoint connection attachments delivered by the ProviderSupervisor ComponentSession channel; implement QMP capability negotiation, command dispatch, and health check using only the delivered fd, never direct socket path access. Primary reuse disposition: `adapt`. Preserved source-plan detail: adapt QMP command payloads to internal DTOs; discard broker wire ops and all socket path/fd-open code.", + "integration" : "Process Provider publishes Endpoint attachments; the controller QMP client consumes those fds through ComponentSession; Guest status and health checks reflect QMP outcomes.", + "removalProof" : "`QemuMedia*` broker wire operations are superseded as public control surfaces once QMP is driven solely through Endpoint attachments and internal DTOs", + "reuseAction" : "adapt", + "reuseSource" : null, + "specId" : "ADR-046-provider-runtime-qemu-media", + "specPath" : "docs/specs/providers/ADR-046-provider-runtime-qemu-media.md", + "validation" : "`tests/qmp_capability_negotiation.rs`; `tests/qmp_command_dispatch.rs`; `tests/qmp_greeting_timeout.rs`; `tests/qmp_health_check.rs`", + "workItemId" : "ADR046-qemu-media-010" + }, + { + "currentSource" : "`packages/d2b-contracts/src/broker_wire.rs` `QemuMediaAttach` and `QemuMediaDetach` command bodies only", + "dataMigration" : "Full d2b 3.0 reset; removable media hotplug state is reconciled from Guest spec and Volume status, not imported from broker op history", + "dependencyOwner" : "P2; depends on ADR046-qemu-media-006 and ADR046-qemu-media-010; owner: runtime-qemu-media hotplug controller", + "destination" : "packages/d2b-provider-runtime-qemu-media/src/controller/hotplug.rs", + "detailedDesign" : "Hotplug attach/detach protocol: on `removableVolumeRefs` update, request a Volume fd from the `volume-local` ComponentSession service and issue `blockdev-add`/`device_add` QMP commands; reverse the sequence for detach; QMP failures set Degraded with `hotplug-media-failed`. Primary reuse disposition: `adapt`. Preserved source-plan detail: adapt QMP hotplug command bodies; delete broker op wiring.", + "integration" : "Guest spec updates trigger controller reconcile; volume-local supplies media fds; QMP client executes attach/detach; Guest status records hotplug outcomes.", + "removalProof" : "`QemuMediaAttach`/`QemuMediaDetach` broker operations are removed after hotplug is implemented through Volume fd acquisition plus QMP Endpoint dispatch", + "reuseAction" : "adapt", + "reuseSource" : null, + "specId" : "ADR-046-provider-runtime-qemu-media", + "specPath" : "docs/specs/providers/ADR-046-provider-runtime-qemu-media.md", + "validation" : "`tests/hotplug_attach_sequence.rs`; `tests/hotplug_detach_sequence.rs`; `tests/hotplug_qmp_failure.rs`", + "workItemId" : "ADR046-qemu-media-011" + }, + { + "currentSource" : "None — net-new v3 work; no pre-ADR45 baseline equivalent", + "dataMigration" : "Full d2b 3.0 reset; no v2 state/config import", + "dependencyOwner" : "P1; depends on ADR046-qemu-media-002 and Provider config `networkProviderRef`; owner: runtime-qemu-media network dependency integration", + "destination" : "packages/d2b-provider-runtime-qemu-media/src/controller/network.rs", + "detailedDesign" : "Network tap fd acquisition: call the `network-local` ComponentSession service to request a tap fd for a Guest MAC/bridge assignment and include the fd in the LaunchTicket. No bridge name or interface name appears in any public field. Primary reuse disposition: `create`. Preserved source-plan detail: net-new against the `network-local` ComponentSession contract.", + "integration" : "Guest networkAttachments drive requests to network-local; network-local returns a sealed tap fd; Process LaunchTicket carries the fd to the QEMU runner; Guest conditions report unavailable taps.", + "removalProof" : "None — tap fd acquisition through `network-local` is a new v3 dependency path", + "reuseAction" : "create", + "reuseSource" : null, + "specId" : "ADR-046-provider-runtime-qemu-media", + "specPath" : "docs/specs/providers/ADR-046-provider-runtime-qemu-media.md", + "validation" : "`tests/tap_fd_acquisition.rs`; `tests/tap_fd_unavailable.rs`", + "workItemId" : "ADR046-qemu-media-012" + }, + { + "currentSource" : "None — net-new v3 work; no pre-ADR45 baseline equivalent", + "dataMigration" : "Full d2b 3.0 reset; lifecycle state is re-derived from Resource specs/status and Operation ledger rather than imported from v2 daemon state", + "dependencyOwner" : "P1; depends on ADR046-qemu-media-005 through ADR046-qemu-media-012; owner: runtime-qemu-media controller", + "destination" : "packages/d2b-provider-runtime-qemu-media/src/controller/reconcile.rs", + "detailedDesign" : "Reconcile loop and finalize: implement the full async reconcile loop from §11.3 and finalize sequence from §11.4, including dependency gating, providerPhase transitions, condition management, runner exit handling, and WaylandSession cleanup. Primary reuse disposition: `create`. Preserved source-plan detail: net-new reconcile/finalize implementation using the v3 Resource API.", + "integration" : "Resource watches feed the controller; the controller creates/updates/deletes Volume, WaylandSession, Endpoint, and Process resources; Guest status and finalizers expose lifecycle outcomes to core and CLI.", + "removalProof" : "Legacy daemon-owned qemu-media lifecycle paths can be removed once reconcile/finalize owns all Guest lifecycle transitions", + "reuseAction" : "create", + "reuseSource" : null, + "specId" : "ADR-046-provider-runtime-qemu-media", + "specPath" : "docs/specs/providers/ADR-046-provider-runtime-qemu-media.md", + "validation" : "`tests/reconcile_dependency_gating.rs`; `tests/reconcile_runner_exit_handling.rs`; `tests/finalize_sequence.rs`; `tests/finalize_wayland_session_cleanup.rs`", + "workItemId" : "ADR046-qemu-media-013" + }, + { + "currentSource" : "None — net-new v3 work; no pre-ADR45 baseline equivalent", + "dataMigration" : "None — status schema is new v3 observation state; no v2 status import", + "dependencyOwner" : "P1; depends on ADR046-qemu-media-013; owner: runtime-qemu-media status/error implementation", + "destination" : "packages/d2b-provider-runtime-qemu-media/src/controller/status.rs", + "detailedDesign" : "Status, conditions, and error reporting: implement all phase transitions from §16.1, providerPhase values from §16.2, condition types from §16.3, error codes from §16.4, and bounds enforcement on `providerPhase`. Primary reuse disposition: `create`. Preserved source-plan detail: net-new status/error projection for the v3 Guest ResourceType.", + "integration" : "Controller reconcile writes Guest status; ResourceAPI stores bounded status; CLI/support tooling reads status without paths, argv, fds, socket paths, VM names as labels, or secret material.", + "removalProof" : "None — this item adds v3 status projection and does not by itself remove a prior owner", + "reuseAction" : "create", + "reuseSource" : null, + "specId" : "ADR-046-provider-runtime-qemu-media", + "specPath" : "docs/specs/providers/ADR-046-provider-runtime-qemu-media.md", + "validation" : "`tests/status_phase_transitions.rs`; `tests/condition_reason_codes.rs`", + "workItemId" : "ADR046-qemu-media-014" + }, + { + "currentSource" : "None — net-new v3 work; no pre-ADR45 baseline equivalent", + "dataMigration" : "None — audit-only work; no runtime state import", + "dependencyOwner" : "P2; depends on ADR046-qemu-media-013 and ADR046-qemu-media-014; owner: runtime-qemu-media audit integration", + "destination" : "packages/d2b-provider-runtime-qemu-media/src/audit.rs", + "detailedDesign" : "Audit event emission: emit all audit events in §17 and verify that no sensitive fields such as paths, argv, fds, or socket paths appear in any payload. Primary reuse disposition: `create`. Preserved source-plan detail: net-new audit emission for the Provider events in §17.", + "integration" : "Controller lifecycle and QMP/hotplug operations call audit helpers; the audit subsystem records bounded event kinds and outcomes; support tooling consumes redacted payloads.", + "removalProof" : "None — audit helpers are new for this Provider; no prior owner to remove", + "reuseAction" : "create", + "reuseSource" : null, + "specId" : "ADR-046-provider-runtime-qemu-media", + "specPath" : "docs/specs/providers/ADR-046-provider-runtime-qemu-media.md", + "validation" : "`tests/audit_event_shapes.rs`; `tests/audit_no_sensitive_fields.rs`", + "workItemId" : "ADR046-qemu-media-015" + }, + { + "currentSource" : "None — net-new v3 work; no pre-ADR45 baseline equivalent", + "dataMigration" : "None — telemetry-only work; no runtime state import", + "dependencyOwner" : "P2; depends on ADR046-qemu-media-013 and ADR046-qemu-media-014; owner: runtime-qemu-media telemetry integration", + "destination" : "packages/d2b-provider-runtime-qemu-media/src/telemetry.rs", + "detailedDesign" : "Metrics and OTEL spans: implement all metrics from §18 and OTEL trace spans with label cardinality enforcement and no VM name, user identity, path, or other sensitive value in any label or attribute. Primary reuse disposition: `create`. Preserved source-plan detail: net-new telemetry emission for the Provider metrics and spans in §18.", + "integration" : "Controller, QMP, hotplug, and dependency-watch paths call telemetry helpers; OTEL/metrics exporters consume only closed, bounded labels for support dashboards.", + "removalProof" : "None — telemetry helpers are new for this Provider; no prior owner to remove", + "reuseAction" : "create", + "reuseSource" : null, + "specId" : "ADR-046-provider-runtime-qemu-media", + "specPath" : "docs/specs/providers/ADR-046-provider-runtime-qemu-media.md", + "validation" : "`tests/metrics_label_cardinality.rs`; `tests/otel_span_attributes.rs`", + "workItemId" : "ADR046-qemu-media-016" + }, + { + "currentSource" : "`nixos-modules/components/qemu-media.nix` option names only; `nixos-modules/assertions.nix` assertion framework", + "dataMigration" : "Full d2b 3.0 reset; users reauthor qemu-media configuration as v3 resources and raw path options are not imported", + "dependencyOwner" : "P1; depends on ADR046-qemu-media-002 and ADR046-qemu-media-003; owner: Nix resource compiler and runtime-qemu-media options", + "destination" : "nixos-modules/options-guest-qemu-media.nix; nixos-modules/assertions.nix", + "detailedDesign" : "Nix module and assertions: implement the Guest resource declaration from §19 and eval-time assertions from §19.8. Rewrite qemu-media options as v3 spec fields and reject raw path options. Primary reuse disposition: `adapt`. Preserved source-plan detail: adapt option names into v3 Guest/Provider spec fields; remove raw path options; extend existing assertion predicates.", + "integration" : "Nix authoring emits Provider, Guest, Volume, and Device resource JSON; assertions fail invalid configs before build; emitted resources feed ResourceAPI admission and controller reconcile.", + "removalProof" : "`nixos-modules/components/qemu-media.nix` raw path option surface is superseded once v3 Guest resource emission and assertions cover the configuration", + "reuseAction" : "adapt", + "reuseSource" : null, + "specId" : "ADR-046-provider-runtime-qemu-media", + "specPath" : "docs/specs/providers/ADR-046-provider-runtime-qemu-media.md", + "validation" : "`tests/unit/nix/cases/guest-qemu-media-spec.nix`; `tests/assertions-eval.sh` new assertion cases", + "workItemId" : "ADR046-qemu-media-017" + }, + { + "currentSource" : "d2b-provider-toolkit conformance kit", + "dataMigration" : "None — test-only work; no runtime state import", + "dependencyOwner" : "P2; depends on ADR046-qemu-media-013 through ADR046-qemu-media-016; owner: runtime-qemu-media conformance tests", + "destination" : "packages/d2b-provider-runtime-qemu-media/tests/conformance_guest.rs", + "detailedDesign" : "d2b-provider-toolkit conformance: pass the Provider conformance kit for the Guest ResourceType axis, including reconcile/finalize contract, phase machine, condition typing, audit shape, and telemetry cardinality. Primary reuse disposition: `adapt`. Preserved source-plan detail: reuse conformance harness; add runtime-qemu-media Guest ResourceType coverage.", + "integration" : "Conformance tests instantiate the Provider against fake ResourceAPI/ComponentSession dependencies and verify the public Provider contract consumed by core CI.", + "removalProof" : "None — conformance coverage is additive test proof", + "reuseAction" : "adapt", + "reuseSource" : null, + "specId" : "ADR-046-provider-runtime-qemu-media", + "specPath" : "docs/specs/providers/ADR-046-provider-runtime-qemu-media.md", + "validation" : "`make test-rust` (runs conformance suite)", + "workItemId" : "ADR046-qemu-media-018" + }, + { + "currentSource" : "None — net-new v3 work; no pre-ADR45 baseline equivalent", + "dataMigration" : "None — test-only work; no runtime state import", + "dependencyOwner" : "P2; depends on ADR046-qemu-media-005 through ADR046-qemu-media-018; owner: runtime-qemu-media integration fixtures", + "destination" : "packages/d2b-provider-runtime-qemu-media/integration/", + "detailedDesign" : "Integration tests: implement container/fake-Host scenarios for full reconcile from Created to Ready with fake dependencies, finalize sequence, hotplug attach/detach, and restart recovery. Primary reuse disposition: `create`. Preserved source-plan detail: net-new integration fixtures.", + "integration" : "Integration fixtures launch the Provider with fake or containerized Host/Guest/Volume/Network/Device dependencies; CI `make test-integration` consumes the fixtures as the cross-process proof lane.", + "removalProof" : "None — integration coverage is additive test proof", + "reuseAction" : "create", + "reuseSource" : null, + "specId" : "ADR-046-provider-runtime-qemu-media", + "specPath" : "docs/specs/providers/ADR-046-provider-runtime-qemu-media.md", + "validation" : "`make test-integration`", + "workItemId" : "ADR046-qemu-media-019" + }, + { + "currentSource" : "`packages/d2b-realm-router/src/lib.rs`, `mux_session.rs`, `session_lifecycle.rs`; `packages/d2bd/src/supervisor/dag.rs`", + "dataMigration" : "None — full d2b 3.0 reset; no prior state to migrate", + "dependencyOwner" : "W0/W1a; controller toolkit owner", + "destination" : "`packages/d2b-controller-toolkit/src/lib.rs`, `runner.rs`, `queue.rs`, `context.rs`, `result.rs`", + "detailedDesign" : "Async ResourceReconciler, watch receiver, coalescing, per-resource serialization, parallel tasks, retry/checkpoint/finalize; expedited priority lane and `CommittedRevisionProof`-gated effects (D090); `assess_update`/`plan_upgrade`/`execute_upgrade` methods serialized in the same single-flight (D091) Primary reuse disposition: `adapt`. Preserved source-plan detail: extract and adapt.", + "integration" : "Provider controller binaries wrap handlers with toolkit", + "removalProof" : "Current per-role orchestration removed only after ResourceType successors", + "reuseAction" : "adapt", + "reuseSource" : null, + "specId" : "ADR-046-resource-reconciliation", + "specPath" : "docs/specs/ADR-046-resource-reconciliation.md", + "validation" : "Golden state-machine vectors, deterministic clocks, conflict/restart/queue tests; D090: commit-fails/Abort → no effect, controller finishes-before-commit gated on proof, effects-gate, status-write-delayed (`statusPersistence: pending`), normal-queued no-op/rejoin, concurrent mutation, delete event-only projection, expedited timeout committed-but-pending, restart re-entry no duplicate; D091: current/non-disruptive/each-trigger assess, UpgradeRequired-not-in-place, dependency propagation/topological drain-recycle-restart, GPU blocking, state/TPM preservation, crash/re-entry resume, single-flight reconcile-vs-upgrade serialization", + "workItemId" : "ADR046-reconcile-001" + }, + { + "currentSource" : "`d2b-realm-core/src/route_engine.rs`, `allocator_engine.rs`; `d2b-realm-router/tests/transport_topology_harness.rs`", + "dataMigration" : "None — full d2b 3.0 reset; no prior state to migrate", + "dependencyOwner" : "Store/API + ADR046-reconcile-001; core controller", + "destination" : "`packages/d2b-core-controller/src/hints.rs`, `dependencies.rs`, `owner_reconcile.rs`", + "detailedDesign" : "Watch-plan validation, indexes, suppression, owner/dependency hints, leases, startup relist, fair admission", + "integration" : "Store post-commit dispatcher → d2b-bus controller streams", + "removalProof" : "Not applicable", + "reuseAction" : "adapt", + "reuseSource" : null, + "specId" : "ADR-046-resource-reconciliation", + "specPath" : "docs/specs/ADR-046-resource-reconciliation.md", + "validation" : "Owner/dependency chains, suppression/no-loss, restart/relist, lease withdrawal", + "workItemId" : "ADR046-reconcile-002" + }, + { + "currentSource" : "`d2bd/src/supervisor/dag.rs`, `pidfd.rs`, unsafe-local blocked supervisor, guest exec runner", + "dataMigration" : "None — full d2b 3.0 reset; no prior state to migrate", + "dependencyOwner" : "Process Providers + benchmark owner", + "destination" : "`packages/d2b-controller-toolkit/benches/reaction.rs`, Process Provider integration tests", + "detailedDesign" : "Commit-to-handler/launch fast path, nonblocking watch, parallel ready resources", + "integration" : "Resource store → bus/session → controller → Process effect/status", + "removalProof" : "Not applicable", + "reuseAction" : "adapt", + "reuseSource" : null, + "specId" : "ADR-046-resource-reconciliation", + "specPath" : "docs/specs/ADR-046-resource-reconciliation.md", + "validation" : "Hard <=5 ms/<=20 ms p95 gates and 1/10/100 Process concurrency", + "workItemId" : "ADR046-reconcile-003" + }, + { + "currentSource" : "`packages/d2bd/src/storage_lifecycle.rs` (`run_startup_contract_check`, bundle-versioned contract validation pattern); `packages/d2bd/src/ownership_preflight.rs` (`EntrySpec`, legacy-recovery-artifact optionality); `packages/d2b/src/lib.rs` `build_storage_migration_plan`/`storage_migration_checkpoint_id`", + "dataMigration" : "New; no prior inventory/snapshot format exists", + "dependencyOwner" : "W0 shared contract root; storage/broker integrator", + "destination" : "`packages/d2b-cutover/src/{inventory,snapshot,checkpoint}.rs`", + "detailedDesign" : "Implement the seven closed inventories of [Authoritative inventories](#authoritative-inventories); the `checkpoint_id` digest algorithm of [Preflight and immutable snapshot](#preflight-and-immutable-snapshot); the atomic snapshot-write sequence (temp file, fsync, rename, parent fsync, post-rename immutability) Primary reuse disposition: `adapt`. Preserved source-plan detail: extract and adapt.", + "integration" : "`d2b host cutover preflight`/`plan` CLI commands consume this crate exclusively; no other crate re-implements inventory walking", + "removalProof" : "Not applicable (net-new capability)", + "reuseAction" : "adapt", + "reuseSource" : null, + "specId" : "ADR-046-reset-and-cutover", + "specPath" : "docs/specs/ADR-046-reset-and-cutover.md", + "validation" : "`checkpoint_id` determinism property test; snapshot atomic-write crash-injection test; `cutover_preflight_refuses_dirty_flake_check`", + "workItemId" : "ADR046-reset-001" + }, + { + "currentSource" : "`nixos-modules/bundle-artifacts.nix`, `nixos-modules/assertions.nix` (existing eval-time validation precedent); `ADR-046-nix-configuration` \"Bundle and generation emission\"", + "dataMigration" : "None — full d2b 3.0 reset; no prior state to migrate", + "dependencyOwner" : "ADR046-reset-001; `d2b-resource-store-redb` owner; `d2b-provider` catalog owner", + "destination" : "`packages/d2b-cutover/src/{bundle_validate,trust_preflight}.rs`", + "detailedDesign" : "Independent legacy-flake-check gate; candidate v3 bundle schema/cross-ref/determinism validation per [Config/artifact/schema validation](#configartifactschema-validation); Provider trust preflight per `ADR-046-provider-model-and-packaging` \"Trust\"", + "integration" : "Invoked by `preflight` before the snapshot is written; failures block `plan` from being offered", + "removalProof" : "Not applicable", + "reuseAction" : "adapt", + "reuseSource" : null, + "specId" : "ADR-046-reset-and-cutover", + "specPath" : "docs/specs/ADR-046-reset-and-cutover.md", + "validation" : "`cutover-candidate-bundle-validation.nix`; trust-preflight rejection tests for each of digest/publisher/signature/deny/provenance/conformance failure modes", + "workItemId" : "ADR046-reset-002" + }, + { + "currentSource" : "`packages/d2b/src/lib.rs` `require_explicit_mutation_flag`, `cmd_host_destroy` (dry-run/apply precondition pattern); [ADR 0040](../adr/0040-graceful-vm-shutdown.md) graceful shutdown path", + "dataMigration" : "Destructive; this is where Phase 3/4 boundary-of-no-return-approach begins (rollback still open through end of Phase 4)", + "dependencyOwner" : "ADR046-reset-001, ADR046-reset-002; Process/Guest lifecycle owner", + "destination" : "`packages/d2b-cutover/src/{consent,drain,disposition}.rs`", + "detailedDesign" : "Exact-consent-phrase gate bound to `checkpoint_id`; Phase 3 drain algorithm (§ [Old daemon/unit/process drain](#old-daemonunitprocess-drain)); the [Disposition framework](#disposition-framework)'s Adopt/Preserve/Destroy executor, delegating every Adopt to ADR046-reset-004 Primary reuse disposition: `adapt`. Preserved source-plan detail: extract and adapt.", + "integration" : "`d2b host cutover apply` orchestrates drain then disposition execution then hands off to Phase 5 (ADR046-reset-005)", + "removalProof" : "Not applicable", + "reuseAction" : "adapt", + "reuseSource" : null, + "specId" : "ADR-046-reset-and-cutover", + "specPath" : "docs/specs/ADR-046-reset-and-cutover.md", + "validation" : "`cutover_apply_requires_exact_consent_phrase`; `cutover_drain_refuses_on_live_process`", + "workItemId" : "ADR046-reset-003" + }, + { + "currentSource" : "`packages/d2b-priv-broker/src/ops/swtpm_dir.rs` (marker fail-closed pattern); `packages/d2b-host/src/hardlink_farm.rs` (same-filesystem rename pattern)", + "dataMigration" : "This work item *is* the data migration mechanism for TPM/store-view/disk-image/unsafe-local-scope bytes", + "dependencyOwner" : "ADR046-reset-003; `ADR-046-provider-state` owner; `device-tpm`/`volume-local` Provider owners", + "destination" : "`packages/d2b-cutover/src/adopt.rs`, thin wrapper invoking `ADR-046-provider-state`'s migration `EphemeralProcess` prepare/stage/commit/rollback machinery with cutover-specific source paths", + "detailedDesign" : "One Adopt invocation per matrix row tagged `Adopt` in the [migration/disposition matrix](#migrationdisposition-matrix); marker re-validation before every step; new-marker-before-old-removal ordering; idempotent re-run safety per [Crash/power-loss/retry/idempotency journals](#crashpower-lossretryidempotency-journals)", + "integration" : "Called by ADR046-reset-003's disposition executor for every Adopt row; writes to the state Volumes ADR046-device-tpm-004/ADR046-vl-004/ADR046-vl-006 define", + "removalProof" : "Not applicable (the mechanism is retained permanently for later Full/Provider/Guest reset relocation use, not retired after first use)", + "reuseAction" : "adapt", + "reuseSource" : null, + "specId" : "ADR-046-reset-and-cutover", + "specPath" : "docs/specs/ADR-046-reset-and-cutover.md", + "validation" : "Crash-injection at every step boundary (Type 10 `cutover-crash-resume.nix`); TPM/durable-Volume Destroy-exclusion property test", + "workItemId" : "ADR046-reset-004" + }, + { + "currentSource" : "None (bootstrap sequencing over Zone runtime startup, which is itself ADR-only)", + "dataMigration" : "Destructive v3 bootstrap; no v2 resource import (per `ADR046-store-003`, `ADR046-object-001`)", + "dependencyOwner" : "ADR046-reset-004; `d2b-resource-store-redb` owner (`ADR046-store-003`); core-controller owner (`ADR046-core-001`)", + "destination" : "`packages/d2b-cutover/src/{store_bootstrap,provider_sequence}.rs`", + "detailedDesign" : "Phase 5 store creation per [Resource-store initialization](#resource-store-initialization); Phase 6 topological Provider install per [Provider install/topological start](#provider-installtopological-start), including the fixed staged default order and cycle-rejection check", + "integration" : "Invoked immediately after ADR046-reset-003/004 complete; hands off to Phase 7 (ADR046-reset-006)", + "removalProof" : "Not applicable", + "reuseAction" : "create", + "reuseSource" : null, + "specId" : "ADR-046-reset-and-cutover", + "specPath" : "docs/specs/ADR-046-reset-and-cutover.md", + "validation" : "Provider install topological-order determinism test; cycle-rejection test; store-identity mismatch fail-closed test", + "workItemId" : "ADR046-reset-005" + }, + { + "currentSource" : "ADR 0032 gateway guest custody evidence (`d2b-realm-router/src/service_v2.rs` `CredentialCustody`); `ADR-046-zone-routing` §3 evidence", + "dataMigration" : "None (ZoneLink resources are ordinary Nix-authored configuration, not migrated credential bytes)", + "dependencyOwner" : "ADR046-reset-005; `ADR-046-zone-routing` owner; `ADR-046-resources-zone-control` owner", + "destination" : "`packages/d2b-cutover/src/{zonelink_cutover,guest_activation}.rs`", + "detailedDesign" : "Phase 7 ZoneLink translation from `EntrypointMode::GatewayBacked`/`HostResident` per [Zone/ZoneLink cutover](#zonezonelink-cutover); Phase 8 Network→Volume→Device→Guest ordering per [Guest/runtime/network/store view activation](#guestruntimenetworkstore-view-activation); enforcement that the parent inventory never enumerates gateway-guest-internal credential/audit state", + "integration" : "Consumes Providers installed by ADR046-reset-005; hands off to ADR046-reset-007 (verification)", + "removalProof" : "Not applicable", + "reuseAction" : "adapt", + "reuseSource" : null, + "specId" : "ADR-046-reset-and-cutover", + "specPath" : "docs/specs/ADR-046-reset-and-cutover.md", + "validation" : "Gateway-custody-boundary test asserting the parent inventory never contains a gateway-guest-internal path; ZoneLink `Degraded/waiting-on-remote` non-blocking test", + "workItemId" : "ADR046-reset-006" + }, + { + "currentSource" : "ADR 0034 degraded-state ledger taxonomy and repair-never-trusts-ledger-paths invariant", + "dataMigration" : "None — full d2b 3.0 reset; no prior state to migrate", + "dependencyOwner" : "ADR046-reset-006; telemetry-audit-and-support owner (`d2b-audit`)", + "destination" : "`packages/d2b-cutover/src/{verify,doctor,degraded}.rs`", + "detailedDesign" : "The ten `verify` checks in [Post-cutover verification](#post-cutover-verification); the `cutover-quarantined` degraded class and `doctor` reporting in [Failure/quarantine/manual recovery](#failurequarantinemanual-recovery); audit chain closure/genesis-record cross-check (check 9) Primary reuse disposition: `adapt`. Preserved source-plan detail: extract and adapt.", + "integration" : "`d2b host cutover verify`/`doctor` CLI commands; consumed by the Phase 10 finalize gate table", + "removalProof" : "Not applicable", + "reuseAction" : "adapt", + "reuseSource" : null, + "specId" : "ADR-046-reset-and-cutover", + "specPath" : "docs/specs/ADR-046-reset-and-cutover.md", + "validation" : "Injected-digest-mismatch test for TPM/durable-Volume verify checks; audit-genesis-cross-check test; `cutover-full-rehearsal.nix`", + "workItemId" : "ADR046-reset-007" + }, + { + "currentSource" : "`ADR-046-cli-and-operations` \"Removal notes\" (live-successor-before-deletion criterion)", + "dataMigration" : "This work item is where every previously-Preserved legacy artifact is finally Destroyed, one gate at a time", + "dependencyOwner" : "ADR046-reset-007; owner of each retiring artifact's ADR 0046 successor work item", + "destination" : "`packages/d2b-cutover/src/finalize.rs`", + "detailedDesign" : "Per-candidate independent gate evaluation exactly as tabled in [Old artifact/unit/schema removal gates](#old-artifactunitschema-removal-gates); separate consent phrase from `apply`; never partial-destroys a candidate", + "integration" : "`d2b host cutover finalize` CLI command; reads gate status from ADR046-reset-007's verify results plus each named policy-lint/integration test's pass/fail recorded in CI", + "removalProof" : "Each candidate's own row in [Old artifact/unit/schema removal gates](#old-artifactunitschema-removal-gates) states its exact removal proof", + "reuseAction" : "create", + "reuseSource" : null, + "specId" : "ADR-046-reset-and-cutover", + "specPath" : "docs/specs/ADR-046-reset-and-cutover.md", + "validation" : "`policy_no_destroy_without_gate`; `policy_legacy_cli_verbs_absent_after_gate`; `tpm-adopt-retirement.nix`", + "workItemId" : "ADR046-reset-008" + }, + { + "currentSource" : "ADR 0034 \"dry-run and preflight output print the checkpoint id and exact rollback command before any apply step begins\"", + "dataMigration" : "None — full d2b 3.0 reset; no prior state to migrate", + "dependencyOwner" : "ADR046-reset-003 through ADR046-reset-006; `ADR-046-provider-state` incident-hold owner", + "destination" : "`packages/d2b-cutover/src/{journal,rollback,hold}.rs`", + "detailedDesign" : "Append-only journal per [Crash/power-loss/retry/idempotency journals](#crashpower-lossretryidempotency-journals); [Rollback boundary](#rollback-boundary) enforcement (`cutover-rollback-window-closed` past phase 4); cutover-wide incident hold per [Incident hold (cutover-wide)](#incident-hold-cutover-wide)", + "integration" : "`d2b host cutover rollback`/`hold` CLI commands; consulted by ADR046-reset-003's disposition executor and ADR046-reset-008's finalize gate before every mutating step", + "removalProof" : "Not applicable", + "reuseAction" : "adapt", + "reuseSource" : null, + "specId" : "ADR-046-reset-and-cutover", + "specPath" : "docs/specs/ADR-046-reset-and-cutover.md", + "validation" : "`cutover_rollback_window_closes_after_phase_5`; incident-hold-blocks-destructive-step test", + "workItemId" : "ADR046-reset-009" + }, + { + "currentSource" : "`ADR-046-resources-zone-control` §2.6 (`core.zone-drain` finalizer algorithm), §9.4 (out-of-band destructive reset, uid=0 authentication)", + "dataMigration" : "None (this is a post-cutover recovery operation, not part of the cutover data migration itself)", + "dependencyOwner" : "ADR046-reset-005 through ADR046-reset-007; `ADR-046-resources-zone-control` owner (§2.6/§9.4)", + "destination" : "`packages/d2b-cutover/src/reset_scope.rs`; `d2b host reset` CLI command", + "detailedDesign" : "The three reset scopes and their comparison table in [Full Zone reset vs Provider reset vs Guest reset](#full-zone-reset-vs-provider-reset-vs-guest-reset); durable-Volume preserve-by-default with explicit `--destroy-durable-volumes`/`--destroy-volumes` opt-in; OS-level authentication for the zone scope only", + "integration" : "Standalone from the cutover Phases 0-10 above; usable at any later time as a recovery/maintenance lever once a Zone exists", + "removalProof" : "Not applicable", + "reuseAction" : "adapt", + "reuseSource" : null, + "specId" : "ADR-046-reset-and-cutover", + "specPath" : "docs/specs/ADR-046-reset-and-cutover.md", + "validation" : "`host_reset_scope_isolation`; `zone-provider-guest-reset-isolation.nix`; durable-Volume-preserved-by-default property test for both Provider and Guest scopes", + "workItemId" : "ADR046-reset-010" + }, + { + "currentSource" : "`tests/integration/live/` conventions; `tests/host-integration/hardware/` conventions", + "dataMigration" : "None (validation only)", + "dependencyOwner" : "ADR046-reset-001 through ADR046-reset-010, fully landed", + "destination" : "`tests/integration/live/cutover-real-host.sh`, `tests/integration/live/cutover-real-host-cloud-guest.sh`, `tests/host-integration/hardware/cutover-real-tpm.sh`, `tests/host-integration/hardware/cutover-real-usbip-security-key.sh`", + "detailedDesign" : "Manual, `D2B_LIVE=1`/hardware-gated validation scripts described in [Tests](#tests) Type 11/12 rows; never run in CI; require operator sign-off and an independent out-of-band backup before execution", + "integration" : "Run manually by an operator against a real host/device before the reset-and-cutover implementation is declared production-ready", + "removalProof" : "Not applicable", + "reuseAction" : "create", + "reuseSource" : null, + "specId" : "ADR-046-reset-and-cutover", + "specPath" : "docs/specs/ADR-046-reset-and-cutover.md", + "validation" : "Manual pass/fail sign-off recorded per the project's existing live-host/hardware validation conventions", + "workItemId" : "ADR046-reset-011" + }, + { + "currentSource" : "`packages/d2b-realm-core/src/routing.rs`: `RouteAdvertisement`, `RouteNamespaceAllocation`, `RealmTreeEdge`, `DescendantRoute`, `TreeRoutePath`, `TreeRouteHop`, `RouteFailClosedReason`, `DirectShortcutAuthorizationRequest`, `DirectShortcutAuthorizationDecision`, `DirectShortcutTeardown`, `DirectShortcutTeardownReason`, `RouteAuditEventKind`, `RouteRealmClass`, `RoutePlacementClass`, `RouteAuditEventMetadata`, all route newtypes; `packages/d2b-realm-core/src/realm.rs`: `RealmPath`, `MAX_REALM_LABELS`, `MAX_REALM_PATH_BYTES`, `RealmControllerPlacement`, `EntrypointMode`; `packages/d2b-realm-core/src/ids.rs`: `RealmId`, `RouteId`, `ControllerGenerationId`, `WorkloadId`, `NodeId`, `ProviderId` (evidence: **A** for ids.rs — used in production; **B** for routing.rs — types exist with tests but no production daemon routing callers)", + "dataMigration" : "Full reset; no v2 Realm route compatibility", + "dependencyOwner" : "W0/W1a; zone routing contract owner", + "destination" : "`packages/d2b-contracts/src/v3/zone_routing.rs`", + "detailedDesign" : "Rename RealmPath → ZonePath, RealmId → ZoneLabelId, RouteId → ZoneRouteId, ControllerGenerationId → ZoneLinkControllerGeneration; preserve all bounds/validation/serde; add ZoneLink-specific advertisement envelope fields (v3 schema version, capability ceiling field); preserve `RouteFailClosedReason` + add `zone-link-disconnected`, `hop-limit-exceeded`, `relay-denied`, `attachment-not-permitted-over-zone-link`; freeze v3 protobuf numbers separately from v2 Primary reuse disposition: `adapt`. Preserved source-plan detail: extract and adapt.", + "integration" : "d2b-bus zone route engine and ZoneLink controller consume these types", + "removalProof" : "v3 old `RealmPath` route types retired after zone-routing engine is live and all callers switched", + "reuseAction" : "adapt", + "reuseSource" : "Same v3 baseline commit `b5ddbed6`", + "specId" : "ADR-046-zone-routing", + "specPath" : "docs/specs/ADR-046-zone-routing.md", + "validation" : "Golden advertisement/path/failure vectors shared by Rust/Nix; property tests for NCA/loop/ceiling; replay-window tests; hop-count tests", + "workItemId" : "ADR046-routing-001" + }, + { + "currentSource" : "`packages/d2b-realm-core/src/route_engine.rs`: full `RouteTreeEngine` struct and impl; `RouteInventoryEntry`, `RoutePruneReport`, `DirectShortcutAuthorizationRequest/Decision/Teardown`; all helper functions", + "dataMigration" : "None (pure in-memory engine)", + "dependencyOwner" : "ADR046-routing-001; zone route engine owner", + "destination" : "`packages/d2b-zone-routing/src/engine.rs`", + "detailedDesign" : "Adapt RouteTreeEngine to ZoneRouteEngine using ZonePath/ZoneRouteId/ZoneLinkControllerGeneration from ADR046-routing-001; preserve all NCA/loop/multi-parent/capability/replay/capacity logic; add `decide_route` enforcement of `maxHops` parameter; add hop-counter decrement in relay path; expose `ZoneRoutePath` in v3 types Primary reuse disposition: `adapt`. Preserved source-plan detail: extract and adapt.", + "integration" : "d2b-bus calls `ZoneRouteEngine::decide_route` for every cross-Zone ResourceClient call; ZoneLink controller calls `admit_advertisement`/`admit_withdrawal`", + "removalProof" : "`RouteTreeEngine` on v3 RealmPath types retired after ZoneRouteEngine is exercised in all bus routing paths", + "reuseAction" : "adapt", + "reuseSource" : "Same v3 baseline commit `b5ddbed6`", + "specId" : "ADR-046-zone-routing", + "specPath" : "docs/specs/ADR-046-zone-routing.md", + "validation" : "Copy exact `route_engine.rs` test suite adapted to ZonePath; add relay/hop-count/RBAC-narrowing/shortcut integration tests", + "workItemId" : "ADR046-routing-002" + }, + { + "currentSource" : "`packages/d2b-realm-core/src/routing.rs`: `RouteNamespaceAllocation`; `packages/d2b-realm-core/src/access.rs`: `RealmAccessResolverRequest/Response/Error`, `RealmAccessBinding`, `RealmTransportBinding`, `RealmAccessClientContract`, `UnixSocketPath`, `AccessBindingRef`, all access types (evidence: **B** — complete implementation, no production callers); `packages/d2bd/src/realm_access_resolver.rs`: `resolve_local_root_realm_access()`, `local_root_realm_access_client_contract()` (evidence: **B** — `pub mod` at `d2bd/src/lib.rs:117`, no callers from running daemon); `packages/d2b-realm-router/src/target_resolver.rs`: `RealmEntrypointTable`, `DispatchTarget`, `RealmEntrypoint`, `ResolveError` (evidence: **A**); `packages/d2b/src/lib.rs:5240`: `load_realm_entrypoint_table()` (evidence: **A**); `packages/d2b/src/target_routing.rs`: `Route::Local`/`Route::GatewayBacked` dispatch (evidence: **A**); `packages/d2b-realm-core/src/realm.rs`: `EntrypointMode`, `RealmControllerPlacement` (evidence: **A** as types; routing use **B**); `packages/d2b-core/src/realm_controller_config.rs`: `RealmControllersJson` (evidence: **C**); `nixos-modules/realm-controller-config-json.nix` (evidence: **C**)", + "dataMigration" : "None; ZoneLink resources created from Nix configuration at v3 reset", + "dependencyOwner" : "ADR046-routing-001, ADR046-routing-002; ZoneLink resource owner", + "destination" : "`packages/d2b-contracts/src/v3/zone_link.rs` (ZoneLink spec/status types, intent types, namespace allocation); `packages/d2b-zone-routing/src/resolver.rs` (ZoneEntrypointResolver)", + "detailedDesign" : "Child-local ZoneLink spec/status fields; self-name and one-uplink invariants; ZoneLinkIntent record stored in the child; ZoneLinkNamespaceAllocation issued by the exact parent allocator selected in sealed Nix-compiled `parentZone` topology; ZoneEntrypointResolver with longest-suffix match over ZonePath (adapted from `RealmEntrypointTable::resolve`); no reciprocal parent-store resource; fail-closed on unknown Zone Primary reuse disposition: `adapt`. Preserved source-plan detail: extract and adapt.", + "integration" : "Core-controller ZoneLink handler manages ZoneLink resources; ZoneEntrypointResolver in d2b-bus for per-call dispatch decision", + "removalProof" : "`RealmEntrypointTable` retired after all host-daemon routing paths use ZoneEntrypointResolver", + "reuseAction" : "adapt", + "reuseSource" : "Same v3 baseline commit `b5ddbed6`", + "specId" : "ADR-046-zone-routing", + "specPath" : "docs/specs/ADR-046-zone-routing.md", + "validation" : "Longest-suffix match vectors; ZoneLink spec validation tests; resolver fail-closed test", + "workItemId" : "ADR046-routing-003" + }, + { + "currentSource" : "`packages/d2b-realm-router/src/remote_node.rs`: `RemoteNodeRegistry`, `RemoteNodeEntry`, `RemoteNodeAvailability`, `RemoteNodeErrorKind`, `RemoteRetryAction`, `ensure_remote_execution_generation` (evidence: **B** — only in `d2bd/src/realm_stubs.rs` dead_code seam); `packages/d2b-realm-router/src/session_lifecycle.rs`: `SessionLifecycle`, `SessionPhase` (evidence: **B** — same seam); `packages/d2bd/src/realm_stubs.rs`: compile-only seam (`#[allow(dead_code)]`, declared at `d2bd/src/lib.rs:249`); `packages/d2b-realm-core/src/enrollment.rs`: `EnrollmentRecord`, `EnrollmentStatus`, `KeyRotationPlan`, `RevocationRecord`, `SessionTeardownDirective`, `RecoveryProcedure`, `IdentityAuditEventKind` (evidence: **B** — consumed by `RealmIdentityStore` which itself has no production callers); `packages/d2b-realm-core/src/identity_store.rs`: `RealmIdentityStore` (evidence: **B** — no production callers); `packages/d2b-realm-core/src/identity_config.rs`: `RealmIdentityConfigJson` (evidence: **A** — loaded at d2bd/priv-broker startup, routing inert); `nixos-modules/realm-identity-config-json.nix` (evidence: **C**); `packages/d2bd/src/workload_target_index.rs`: `WorkloadTargetIndex` (evidence: **A** — called at `d2bd/src/lib.rs:16745`; this is the live bridge from realm metadata to VM-name dispatch; retires with Guest/Host resource lookups)", + "dataMigration" : "New ZoneLink resources from Nix configuration; no prior enrollment compatibility", + "dependencyOwner" : "ADR046-routing-003; core-controller ZoneLink handler owner", + "destination" : "`packages/d2b-core-controller/src/zone_links.rs`", + "detailedDesign" : "Child-local ZoneLink handler in core-controller: manages local ResourceSpec→allocator-bound session→advertisement lifecycle; session state machine (Pending/Established/Disconnected/Reconnecting/Revoked); reconnect backoff per `reconnectPolicy`; advertisement issuance/renewal/withdrawal using enrolled KK ComponentSession; child-store route cursor and outbound intent queue; capability ceiling change handling; status writer; no copied parent/child resource content; Nix-compiled `parentZone` selects the parent allocator, which alone owns privileged listeners, placement, and route namespace and creates no reciprocal resource Primary reuse disposition: `adapt`. Preserved source-plan detail: extract and adapt.", + "integration" : "Child core-controller process → local transport Provider → sealed binding for the allocator selected by `parentZone` → d2b-bus ComponentSession; child ZoneLink handler exchanges advertisements while that parent ZoneRouteEngine admits/withdraws them", + "removalProof" : "`RemoteNodeRegistry` retired after all enrolled peer routing moves to ZoneLink handler", + "reuseAction" : "adapt", + "reuseSource" : "Same v3 baseline commit `b5ddbed6`", + "specId" : "ADR-046-zone-routing", + "specPath" : "docs/specs/ADR-046-zone-routing.md", + "validation" : "Session lifecycle tests; reconnect/revocation/ceiling-change; intent queue drain; cursor resync; advertisement renewal timing; fake-child tests", + "workItemId" : "ADR046-routing-004" + }, + { + "currentSource" : "`packages/d2b-realm-router/src/lib.rs`: `OperationRouter`, `RouteDecision`, `DEFAULT_RETENTION`, `DEFAULT_NO_REUSE_HORIZON`, `DEFAULT_MAX_DEDUP_RECORDS` (evidence: **B** — only in dead_code seam); `packages/d2b-realm-router/src/mux_session.rs`: `MuxSession` stream/operation forwarding (evidence: **B** for zone relay — stream-forwarding path for Zone relay does not exist yet; **D** within realm-router display-session tests); `packages/d2b-realm-core/src/frame.rs`: `ConstellationFrame`, `Handshake*`, `OperationRequest/Response`, `StreamOpen/Data/Flow/Close/Resume`, `OperationKind` (evidence: **B** from d2bd routing perspective; **A** within realm-router + `d2b-gateway-runtime` for individual types); `packages/d2b-realm-router/src/session.rs`: `PeerSession` (evidence: **B** from d2bd; **A** within realm-router tests); `packages/d2b-realm-router/src/secure_session.rs`: `SecurePeerSession`, `SecureSessionKey`, `NonceReplayGuard` (evidence: **B** from d2bd; reachable within realm-router)", + "dataMigration" : "None — full d2b 3.0 reset; no prior state to migrate", + "dependencyOwner" : "ADR046-routing-002, ADR046-routing-007 (from ComponentSession spec); d2b-bus owner", + "destination" : "`packages/d2b-bus/src/zone_route.rs` (cross-Zone bus routing), `packages/d2b-bus/src/relay.rs` (per-hop relay handler)", + "detailedDesign" : "Cross-Zone routing path in d2b-bus: ZoneEntrypointResolver → ZoneRouteEngine::decide_route → ZoneLink ComponentSession per hop; hop-counter decrement and enforcement; RBAC `relay` verb check at each intermediate hop; idempotency key namespace (full 6-tuple) in ZoneLinkIdempotencyKey; pinned reverse path tracking; cancellation forwarding; watch cursor forwarding and revision-expired handling; no-FD/credential structural rejection at serialization boundary Primary reuse disposition: `adapt`. Preserved source-plan detail: extract and adapt.", + "integration" : "ResourceClient → d2b-bus → ZoneLink CS → intermediate zone → target zone; cancel/watch/stream all use the same routing path", + "removalProof" : "Old direct-dispatch and gateway-backed paths retired per bus routing parity", + "reuseAction" : "adapt", + "reuseSource" : "Same v3 baseline commit `b5ddbed6`", + "specId" : "ADR-046-zone-routing", + "specPath" : "docs/specs/ADR-046-zone-routing.md", + "validation" : "End-to-end K0→K1→K2 resource call; relay-denied/hop-limit/FD-rejection tests; idempotency namespace collision tests; cancellation delivery tests; watch resync tests", + "workItemId" : "ADR046-routing-005" + }, + { + "currentSource" : "`packages/d2b-realm-core/src/route_engine.rs` inline `#[cfg(test)] mod tests` block at line 1202 (45 test functions covering NCA, advertisement admission/withdrawal, loop/multi-parent detection, capability ceiling, replay window, DirectShortcut authorization/teardown; evidence: **implemented-but-unwired** — tests are in-file, not in a separate `tests/` directory; no external test crate at `packages/d2b-realm-core/tests/*.rs`)", + "dataMigration" : "None — full d2b 3.0 reset; no prior state to migrate", + "dependencyOwner" : "ADR046-routing-002; benchmark owner", + "destination" : "`packages/d2b-zone-routing/tests/route_engine_vectors.rs`, `packages/d2b-zone-routing/benches/route_decision.rs`", + "detailedDesign" : "Copy exact advertisement/NCA/loop/capability/replay test vectors adapted to ZonePath; add K0/K1/K2 topology scenarios; add hop-count boundary tests; benchmark: p95 route decision for 1/10/100 active Zone tree entries <= 1 ms", + "integration" : "Zone route engine correctness gate; bus relay integration tests", + "removalProof" : "Not applicable", + "reuseAction" : "adapt", + "reuseSource" : "Same v3 baseline commit `b5ddbed6`", + "specId" : "ADR-046-zone-routing", + "specPath" : "docs/specs/ADR-046-zone-routing.md", + "validation" : "All v3 baseline route_engine test cases must pass; p95 benchmark gate", + "workItemId" : "ADR046-routing-006" + }, + { + "currentSource" : "None in v3 pre-ADR45 baseline.", + "dataMigration" : "None (new infrastructure)", + "dependencyOwner" : "W0; d2b-bus owner", + "destination" : "`packages/d2b-bus/src/session/`", + "detailedDesign" : "Copy `d2b-session` crate wholesale into `d2b-bus/src/session/`; adapt `EndpointPurpose`/`EndpointRole`/`ServicePackage` closed-enum tags for v3 purposes; strip `GUEST_SESSION_CREDENTIAL_*` types; strip `serve_ttrpc_services` fixed-endpoint binding (replaced by allocator-issued FD bootstrap); adapt `SessionEngine` as ZoneLink session drive loop; keep all Noise profiles (Nn/Kk/IKpsk2), generation discovery, record/fragment/keepalive/credit/cancellation/attachment logic verbatim Primary reuse disposition: `adapt`. Preserved source-plan detail: copy and adapt.", + "integration" : "ZoneLink controller instantiates one `ComponentSessionDriver` per ZoneLink, typed as Kk for enrolled peers and Nn for initial bootstrap; d2b-bus routes ResourceClient calls through these drivers", + "removalProof" : "Not applicable (new crate)", + "reuseAction" : "adapt", + "reuseSource" : null, + "specId" : "ADR-046-zone-routing", + "specPath" : "docs/specs/ADR-046-zone-routing.md", + "validation" : "Port all `component_session.rs` tests; port `noise_vectors.rs`; add ZoneLink-specific KK enrollment test; add ZoneLink reconnect/revocation integration test", + "workItemId" : "ADR046-routing-007" + }, + { + "currentSource" : "None in v3 pre-ADR45 baseline (vsock and seqpacket paths are provider-specific in v3).", + "dataMigration" : "None — full d2b 3.0 reset; no prior state to migrate", + "dependencyOwner" : "ADR046-routing-007; transport-provider owner", + "destination" : "`packages/d2b-bus/src/transport/unix.rs`, `packages/d2b-bus/src/transport/credit.rs`", + "detailedDesign" : "Copy `UnixSeqpacketTransport`/`UnixStreamTransport`/credit modules verbatim; adapt `PeerIdentityPolicy` for v3 Zone principal model; adapt `InheritedSocketTransport` to receive allocator-issued FD directly (not SD_LISTEN_FDS); vsock paths adapted as transport-Provider-specific implementations (not hardcoded); `PidfdIdentityPolicy` adapted for v3 Process resource pidfd model Primary reuse disposition: `adapt`. Preserved source-plan detail: copy and adapt.", + "integration" : "Unix transport Provider instantiates `UnixSeqpacketTransport`; vsock transport Provider instantiates vsock transports; both implement `OwnedTransport` consumed by `SessionEngine` in ADR046-routing-007", + "removalProof" : "Not applicable (new infrastructure)", + "reuseAction" : "adapt", + "reuseSource" : null, + "specId" : "ADR-046-zone-routing", + "specPath" : "docs/specs/ADR-046-zone-routing.md", + "validation" : "Port all `unix_session.rs` tests; add allocator-issued FD handoff test; add inherited-socket no-SD-listen test", + "workItemId" : "ADR046-routing-008" + }, + { + "currentSource" : "None in v3 pre-ADR45 baseline.", + "dataMigration" : "None (new contract module)", + "dependencyOwner" : "ADR046-routing-007; contracts owner", + "destination" : "`packages/d2b-contracts/src/v3/zone_session.rs`", + "detailedDesign" : "Copy all protocol constants verbatim; copy `BoundedVec`, `BinaryError`, `ContractError`; copy `ComponentSessionPreface`, `LimitProfile`, `AttachmentDescriptor`/`AttachmentKind`/`AttachmentCreditClass`/`ChannelId`/`RecordHeader`/`FragmentHeader`/`SessionErrorCode`/`CloseReason`/`Remediation` verbatim; extend `ServicePackage` closed-enum with `ZoneV3`/`ResourceV3`/`ZoneLinkV3` variants at new tag values; extend `EndpointRole` with `ZoneController`/`ZoneRelay`/`ZoneBootstrap` variants; extend `EndpointPurpose`/`PurposeClass` with v3 Zone purposes; strip `GUEST_SESSION_CREDENTIAL_*` constants and types; re-freeze protobuf field numbers for v3 services independently from v2 assignments Primary reuse disposition: `adapt`. Preserved source-plan detail: copy and adapt.", + "integration" : "All d2b-bus session/transport code imports from `d2b-contracts::v3::zone_session`", + "removalProof" : "v2 contracts remain; v3 module is additive", + "reuseAction" : "adapt", + "reuseSource" : null, + "specId" : "ADR-046-zone-routing", + "specPath" : "docs/specs/ADR-046-zone-routing.md", + "validation" : "Updated `negotiate_offer`/`validate_exact` round-trip tests for v3 purposes; canonical encoding stability test; closed-enum exhaustiveness tests", + "workItemId" : "ADR046-routing-009" + }, + { + "currentSource" : "None in v3 pre-ADR45 baseline.", + "dataMigration" : "None — full d2b 3.0 reset; no prior state to migrate", + "dependencyOwner" : "ADR046-routing-007, ADR046-routing-009; resource-client owner", + "destination" : "`packages/d2b-resource-client/`", + "detailedDesign" : "Copy `Client` generic structure; rename `ServiceOwner::Realm(RealmId)` → `ServiceOwner::Zone(ZonePath)`, `ServiceOwner::Workload{realm,workload}` → `ServiceOwner::Guest{zone,guest}`, `ServiceOwner::LocalRoot` → `ServiceOwner::ZoneLocal`; rename `TargetInput` variants to match; add `TargetInput::ZoneService(ZonePath, ZoneServiceKind)` for cross-Zone service targeting; replace `ServiceKind` (25 ADR45 variants) with v3 service inventory (`Resource`, `Zone`, `ZoneLink`, `Provider`, plus retained guest/daemon variants); adapt `RouteTable` to route by `ZonePath`; replace `HostSocketConnector` uid-based trust with allocator-issued FD + KK static key pinning; keep `SessionFailure`/retry/cancellation/`MetadataInput`/`RetryPolicy` logic verbatim Primary reuse disposition: `adapt`. Preserved source-plan detail: copy and adapt.", + "integration" : "Zone runtime uses `ResourceClient` for all cross-Zone ResourceType calls; d2b-bus wraps `ComponentSessionDriver` (ADR046-routing-007) as the underlying session", + "removalProof" : "v2 `d2b-client` package remains for ADR45 callers; v3 `d2b-resource-client` is additive", + "reuseAction" : "adapt", + "reuseSource" : null, + "specId" : "ADR-046-zone-routing", + "specPath" : "docs/specs/ADR-046-zone-routing.md", + "validation" : "Port `client.rs` tests; add ZonePath routing test; add cross-Zone K0→K1 end-to-end test; add retry/cancellation forwarding test", + "workItemId" : "ADR046-routing-010" + }, + { + "currentSource" : "`nixos-modules/options-realms-workloads.nix` (v3 baseline `b5ddbed6`): `d2b.realms..*` option declarations (evidence: **C** — eval contract; bespoke field names that do NOT mirror canonical ResourceSpec); `nixos-modules/assertions.nix`: realm-name regex, platform-gate, CIDR assertions (evidence: **A**); `nixos-modules/realm-controller-config-json.nix`: `realmControllersJson` emitter, bundle.nix:59 wiring (evidence: **C**)", + "dataMigration" : "None; Zone options are new; Realm options retained until migration PR", + "dependencyOwner" : "ADR046-routing-001; Nix module owner", + "destination" : "`nixos-modules/options-zones.nix` (new structural base), `nixos-modules/generated/options-zones-.nix` (generated per ResourceType by `xtask gen-zone-nix-options`), `nixos-modules/assertions.nix` (new Zone assertions)", + "detailedDesign" : "Declare compiler-only scalar `d2b.zones..parentZone` plus structural `d2b.zones..resources. = { type = ...; spec = {}; }` as specified in the \"Option schema\" section above. `parentZone` has no default, is required for every non-root Zone, forbidden on `local-root`, resolves to one declared Zone, and never enters a ResourceSpec. Wire `options-zones.nix` and all `generated/options-zones-*.nix` files into `nixos-modules/default.nix`. Add a new `xtask gen-zone-nix-options` command that reads `docs/reference/schemas/v3/.schema.json` for each ResourceType and emits a generated submodule overlaying typed spec options (types, bounds, enum constraints, defaults, docs) onto `d2b.zones..resources..spec`. These generated modules are committed and kept in sync by `xtask gen-zone-nix-options && git diff --exit-code` wired into `make test-drift`. Because the generated options carry field-level type constraints, field-level eval errors (wrong enum, out-of-range int, non-hex fingerprint) are caught without explicit assertions. Explicit assertions in `nixos-modules/assertions.nix` cover cross-resource invariants only: zone/resource key name regex, reserved names, `parentZone` required/forbidden/existence/self/cycle/16-name-depth constraints, child-local `childZoneName == zone`, at most one uplink resource per non-root Zone and none in local root, ref resolution, count limits, and transportSettings secret-key exclusion (listed in the eval-time assertions table). Conflicting parent scalar definitions fail through standard Nix module merging. Each new assertion must have a matching case in `tests/unit/nix/cases/zone-assertions.nix` (nix-unit auto-discovered). `d2b.realms` option namespace is NOT removed in this work item. Primary reuse disposition: `adapt`. Preserved source-plan detail: new module following same pattern.", + "integration" : "ADR046-routing-012 consumes the validated `parentZone` map for private allocator bootstrap sealing and iterates `d2b.zones..resources.*` for resource-bundle emission", + "removalProof" : "`nixos-modules/options-realms-workloads.nix` `d2b.realms` namespace retires after all hosts migrate to `d2b.zones`", + "reuseAction" : "adapt", + "reuseSource" : "Same v3 baseline `b5ddbed6`; `assertions.nix` pattern reused for Zone assertions; `realm-controller-config-json.nix` is the structural template", + "specId" : "ADR-046-zone-routing", + "specPath" : "docs/specs/ADR-046-zone-routing.md", + "validation" : "`nix-unit: zone-name-regex`, all five `nix-unit: zone-parent-*` vectors, `nix-unit: zone-link-fingerprint`, `nix-unit: zone-link-child-name`, `nix-unit: zone-link-one-uplink`, `nix-unit: capability-ceiling-unknown-verb`, `nix-unit: transport-settings-secret-key`; add `drift: zone-nix-options` (`xtask gen-zone-nix-options && git diff --exit-code`); run `make nix-unit-pin` after adding eval cases", + "workItemId" : "ADR046-routing-011" + }, + { + "currentSource" : "`nixos-modules/realm-controller-config-json.nix` (v3 baseline `b5ddbed6`): `builtins.toJSON` emitter for `realm-controllers.json` (bundle.nix:59); `nixos-modules/bundle-artifacts.nix`: install table (root:d2bd 0640); `nixos-modules/bundle.nix`: artifact wiring (evidence: **C**); `packages/xtask/src/main.rs` `gen-schemas` subcommand (evidence: **A** — wired into `make test-drift`)", + "dataMigration" : "None; new artifact file", + "dependencyOwner" : "ADR046-routing-011, ADR046-routing-001; bundle emitter owner", + "destination" : "`nixos-modules/zone-resources-json.nix` (new), private local-root allocator bootstrap compiler/sealer input (not a ResourceSpec or public bundle), `nixos-modules/bundle-artifacts.nix` (new row for per-Zone `resource-bundle.json`), `packages/xtask/src/main.rs` (`gen-zone-schemas` subcommand emitting `docs/reference/schemas/v3/.schema.json` for Zone and ZoneLink; `gen-zone-nix-options` subcommand emitting `nixos-modules/generated/options-zones-.nix`)", + "detailedDesign" : "`zone-resources-json.nix` iterates `d2b.zones..resources.*` to produce the canonical sorted resource list: for each entry, render `{ apiVersion, type, metadata: { name, zone, ownerRef: , labels: , annotations: }, spec: }`. Separately canonicalize sorted `{ childZone, parentZone }` rows from the compiler-only topology and seal them into the private allocator bootstrap input; `parentZone` never enters a resource bundle or `Zone.spec`, and a topology digest change releases/reallocates affected edges independently of resource `generationId`. The bundle JSON omits `managedBy` and `configurationGeneration`; the configuration service/core sets those fields when activating the validated bundle. Sort all resources by `(type, zone, name)`. Compute `generationId` as SHA-256 (lower hex) of the UTF-8 bytes of the sorted `resources` array JSON. Compute `integrity` as SHA-256 (base64url, no padding) of the full bundle JSON with integrity field zeroed. Install at `/etc/d2b/zones//resource-bundle.json` root:d2bd 0640. Canonical form: all object keys sorted lexicographically; order-significant arrays preserved; schema-declared set-like arrays sorted lexicographically; all optional fields emitted with defaults; no field renaming or restructuring. Build-time validation runs in a Nix derivation: (1) validate the complete parent map (non-root required, local-root forbidden, declared target, one scalar parent, not self, acyclic, max 16 names); (2) validate each resource against the committed JSON Schema; (3) validate `transportSettings` for each child-local ZoneLink against its same-Zone Provider's `transportSettingsSchema` — `transportProviderRef` is always explicit, never inferred or defaulted; (4) verify capability ceilings are subsets of the sealed selected-parent allocator grants; (5) verify `childZoneName == metadata.zone`, at most one uplink resource per non-root Zone, and no local-root uplink; (6) check for duplicate `(type, zone, name)` tuples. Providers MUST commit their `transportSettingsSchema` before any ZoneLink can reference them. Drift gates: `xtask gen-zone-schemas && git diff --exit-code` and `xtask gen-zone-nix-options && git diff --exit-code` both wired into `make test-drift`. Add `checks.${system}.zone-schema-drift` to `flake.nix`. Primary reuse disposition: `adapt`. Preserved source-plan detail: extend and adapt.", + "integration" : "The local-root allocator consumes sealed parent topology independently of resource bundles; `nixos-modules/bundle-artifacts.nix` installs each per-Zone `resource-bundle.json`; ADR046-routing-013 Zone runtime reads it on startup", + "removalProof" : "`realm-controllers.json` artifact retires after Zone runtime is live and all hosts migrated", + "reuseAction" : "adapt", + "reuseSource" : "`realm-controller-config-json.nix` structural template; `xtask gen-schemas` extension point (main `a1cc0b2d` unchanged in this area)", + "specId" : "ADR-046-zone-routing", + "specPath" : "docs/specs/ADR-046-zone-routing.md", + "validation" : "`drift: zone-resource-schema`, `drift: zone-nix-options`, `build: zone-bundle-deterministic`, `build: parent-topology-sealed`, `build: transport-settings-unknown-field`, `build: capability-ceiling-superset`, `build: missing-transport-provider`; run `make flake-matrix-pin` after adding flake checks", + "workItemId" : "ADR046-routing-012" + }, + { + "currentSource" : "`packages/d2b-realm-core/src/realm_controller_config.rs`: `RealmControllersJson`, `RealmControllerRow`, `RealmControllerConfigError` (evidence: **C** — loaded but routing inert); `packages/d2bd/src/realm_access_resolver.rs`: `resolve_local_root_realm_access()`, `RealmAccessResolverRequest`, `RealmAccessBinding` (evidence: **B** — pub mod at lib.rs:117, no callers); `packages/d2b-state/src/` (both baselines): atomic state, OFD locks, lease primitives (evidence: **A** for locks/leases, **B** for realm-specific storage); `nixos-modules/host-daemon.nix:220–221`: bundle artifact install paths, daemon SIGHUP wiring (evidence: **A**)", + "dataMigration" : "None; new runtime component", + "dependencyOwner" : "ADR046-routing-012, ADR046-routing-003; `d2b-core-controller` owner (ADR-046-core-controllers)", + "destination" : "`packages/d2b-core-controller/src/configuration.rs` (defined by ADR-046-core-controllers); shared bundle DTOs may live in `packages/d2b-core/`", + "detailedDesign" : "Implement the configuration ownership and cleanup contract from the \"Configuration ownership and cleanup contract\" section. `configuration.rs` owns: (1) reading and integrity-verifying `/etc/d2b/zones//resource-bundle.json` on startup and SIGHUP; (2) diffing against active generation by `generationId` (no-op if unchanged); (3) queuing Create/UpdateSpec/Delete intents — core sets `configurationGeneration` and `managedBy` when applying Create/UpdateSpec; Delete targets only resources where BOTH `managedBy` equals the configuration service's value AND `configurationGeneration` matches the prior bundle — resources with `managedBy=controller` or `managedBy=api` are never seized; (4) setting `deletionRequestedAt` on pending-delete resources immediately and adding a Pending condition; (5) writing the prior bundle into the capped ring at `/var/lib/d2b/zones//configuration/prior/.json` (default retentionCount=3, range 1..16, no TTL; prune oldest when count would exceed limit); (6) enforcing boundary invariants (no diff-delete for absent `configurationGeneration`, `managedBy` collision guard, live controller-child teardown guard); (7) driving finalizer drain + controller-child cascade before completing a Delete; (8) on successful deletion: one store transaction writes the `Deleted` revision/change event and removes the resource row and all index entries; the authoritative audit record (`zone-resource-cleanup`) is appended from the committed revision with dedup/exactly-once recovery and is NOT part of the store transaction; (9) tracking `deletionRequestedAt`/`cleanupConfigGeneration`/`cleanupError`/`cleanupAttempt` per resource; (10) on rollback: clearing `deletionRequestedAt` and Pending condition for revived resources; (11) never pruning a prior bundle while a Delete intent from its `configurationGeneration` is in flight. OFD lock on the bundle file prevents concurrent activation races. Generation state persisted atomically at `/var/lib/d2b/zones//configuration/generation.json` (root:d2bd 0640). The `spec` object comparison for UpdateSpec detection uses the canonical JSON form so two identical specs always compare equal regardless of Nix rendering order. Resource phase transitions: Pending while Create/UpdateSpec in-flight; Degraded while cleanup pending; Ready when clean; Failed on permanent error. Primary reuse disposition: `adapt`. Preserved source-plan detail: extract and adapt.", + "integration" : "`d2b-core-controller` configuration service activates on bundle install and SIGHUP; zone-controller reconcile loops in `d2b-core-controller` consume the queued intents; d2b-bus resource API exposes `status.phase` and `pendingCleanup` via Get/Watch on the active generation resource", + "removalProof" : "`realm_access_resolver.rs` (B) retires after `d2b-core-controller` configuration tracking is live; `RealmControllersJson` (C) retires after all hosts migrated to Zone bundles", + "reuseAction" : "adapt", + "reuseSource" : null, + "specId" : "ADR-046-zone-routing", + "specPath" : "docs/specs/ADR-046-zone-routing.md", + "validation" : "`host-integration: cleanup-removed-zonelink`, `host-integration: rollback-restores-zonelink`, `host-integration: dynamic-child-not-deleted`, `host-integration: zonelink-no-reciprocal-row`; unit tests: deterministic generationId, no-op on same generationId, cross-ownership invariant enforcement, prior-bundle write/prune cycle, UpdateSpec canonical comparison, store-transaction-then-audit-append ordering, exactly-once audit dedup", + "workItemId" : "ADR046-routing-013" + }, + { + "currentSource" : "None in v3 pre-ADR45 baseline (provider traits exist in `d2b-realm-provider` but are unregistered).", + "dataMigration" : "None (pure runtime)", + "dependencyOwner" : "ADR046-routing-007; Provider resource owner", + "destination" : "`packages/d2b-provider/src/` (adapted in place)", + "detailedDesign" : "Retain `ProviderRegistry`/`ProviderRegistryBuilder`/`ProviderRegistryManager` lifecycle verbatim; adapt `SessionIdentity` to carry `ZonePath` instead of `RealmId`; adapt `AdmissionOptions::peer_role` to v3 Zone principal + RBAC binding; adapt `ProviderDescriptor` schema version to v3; `RegistryLimits` unchanged; `RpcProviderProxy` field adaptations to match v3 session identity; `ProviderInstance` variants retain all 11 types Primary reuse disposition: `adapt`. Preserved source-plan detail: copy and adapt.", + "integration" : "Zone runtime `ProviderComposition` builds a `ProviderRegistry` per Zone; Provider resource controller admits calls through `ProviderRegistry::admit()`", + "removalProof" : "Provider registry is v3 core infrastructure; no retirement", + "reuseAction" : "adapt", + "reuseSource" : null, + "specId" : "ADR-046-zone-routing", + "specPath" : "docs/specs/ADR-046-zone-routing.md", + "validation" : "Port inline registry lifecycle/drain/shutdown tests; add v3 ZonePath routing test; add RBAC relay-verb check at provider admission", + "workItemId" : "ADR046-routing-014" + }, + { + "currentSource" : "None in v3 pre-ADR45 baseline.", + "dataMigration" : "None — full d2b 3.0 reset; no prior state to migrate", + "dependencyOwner" : "ADR046-routing-014; Provider agent process owner", + "destination" : "`packages/d2b-provider-toolkit/src/` (adapted in place)", + "detailedDesign" : "Retain `GeneratedProviderServiceServer` ttrpc dispatch verbatim; adapt `ProviderAgentProcess::from_registry` to receive ComponentSession FD from Zone allocator bootstrap instead of SD_LISTEN_FDS; adapt audit event types for v3 Zone principal; `ProviderAgentAdapter` (client-side proxy) adapted for v3 ZoneLink session; conformance kit extended for v3 Provider resource conformance checks; redaction helpers unchanged Primary reuse disposition: `adapt`. Preserved source-plan detail: copy and adapt.", + "integration" : "Provider Processes (EphemeralProcess or Process resources with `executionRef`) spawn the provider agent entrypoint; Zone bus instantiates `ProviderAgentAdapter` as the proxy inside the Zone runtime", + "removalProof" : "Provider toolkit is v3 core infrastructure; no retirement", + "reuseAction" : "adapt", + "reuseSource" : null, + "specId" : "ADR-046-zone-routing", + "specPath" : "docs/specs/ADR-046-zone-routing.md", + "validation" : "Port `audit_capacity_is_closed_and_bounded`; add v3 bootstrap-via-allocator test; add conformance test for new Provider ResourceType schema", + "workItemId" : "ADR046-routing-015" + }, + { + "currentSource" : "`packages/d2b-realm-router/src/service_v2.rs` (v3 baseline `b5ddbed6`): `RealmServiceServer` (bootstrap/enroll/resolve_route/authorize_shortcut/revoke_shortcut/report_shortcut_close/inspect/cancel), `RealmServiceProcess`, `RealmSessionAuthority`, `CredentialCustody`, `RealmServiceLimits`, `RealmAuditEvent`/`RealmMethod`/`RealmAuditOutcome`, `BootstrapBinding`/`EnrollmentBinding`/`ShortcutBinding`/`MutationRecord`; constants `DEFAULT_MAX_REALM_BINDINGS=256`, `DEFAULT_MAX_SHORTCUTS=256`, `DEFAULT_MAX_MUTATION_RECORDS=1024`, `DEFAULT_AUDIT_CAPACITY=1024`, `MAX_CONFIGURED_BOUND=4096`, `MAX_DISPATCH_IN_FLIGHT=64`, `SHUTDOWN_TIMEOUT=5s` (evidence: v3 baseline, not main; see Baseline section — **B** from d2bd/CLI perspective, **A** within realm-router display-session use)", + "dataMigration" : "None; v3 Zone service is new; no v2 realm-service compatibility", + "dependencyOwner" : "ADR046-routing-007, ADR046-routing-001; Zone service owner", + "destination" : "`packages/d2b-zone-routing/src/service.rs`", + "detailedDesign" : "Rename `RealmServiceServer` → `ZoneServiceServer`; service wire name `d2b.realm.v2.RealmService` → `d2b.zone.v3.ZoneService`; rename methods (bootstrap→zone-bootstrap, enroll→zone-enroll, resolve_route→resolve-zone-route, authorize_shortcut→authorize-zone-shortcut, revoke_shortcut→revoke-zone-shortcut, report_shortcut_close→report-zone-shortcut-close, inspect→zone-inspect); replace `RealmSessionAuthority` with Zone principal + RBAC binding; replace `BootstrapBinding` with ZoneLink allocator-issued PSK binding; replace `EnrollmentBinding` with ZoneLink KK enrollment record; add `relay` verb RBAC check per hop; adapt shortcut model to ZonePath addressing; `RealmServiceLimits` defaults preserved; `MAX_DISPATCH_IN_FLIGHT=64`, `SHUTDOWN_TIMEOUT=5s` preserved; `CredentialCustody::GatewayGuest` excluded (all ZoneLink sessions are direct KK) Primary reuse disposition: `adapt`. Preserved source-plan detail: copy and adapt.", + "integration" : "Zone runtime instantiates one `ZoneServiceServer` per Zone; d2b-bus routes `d2b.zone.v3.ZoneService` calls to this server; CLI uses `ZoneServiceClient` (from ADR046-routing-010) for zone inspect/enroll/route-resolve", + "removalProof" : "`RealmServiceServer` on `d2b.realm.v2` retires after `ZoneServiceServer` handles all routing; display-session path migrates separately as part of Provider resource work", + "reuseAction" : "adapt", + "reuseSource" : null, + "specId" : "ADR-046-zone-routing", + "specPath" : "docs/specs/ADR-046-zone-routing.md", + "validation" : "Bootstrap/enroll/resolve-route/shortcut integration tests against fake ZoneLink; relay-verb RBAC test; KK enrollment test; shortcut ZonePath addressing test; concurrent dispatch bound test (64 in-flight)", + "workItemId" : "ADR046-routing-016" + }, + { + "currentSource" : "`packages/d2b-contract-tests/tests/policy_observability.rs` (existing v3 cardinality/label policy gate)", + "dataMigration" : "None — full d2b 3.0 reset; no prior state to migrate", + "dependencyOwner" : "All ResourceType/Provider specs; owned by the security/telemetry integrator alongside `ADR046-telem-008`", + "destination" : "`packages/d2b-contract-tests/tests/policy_telemetry_redaction.rs`", + "detailedDesign" : "One policy test enumerating every forbidden metric-label/audit-field value from §21 and the content-secrecy table in §20 (store paths, `no_isolation`, credential bytes, raw paths/argv/PID/cgroup, CTAP/clipboard/terminal/notification content) and asserting, by static scan of instrumentation call sites plus a redaction-guard runtime test, that no `ADR046-*` Provider crate emits any of them Primary reuse disposition: `adapt`. Preserved source-plan detail: extract and adapt.", + "integration" : "Runs as part of `make test-lint`/`make test-rust`; every Provider crate's own redaction test (e.g. `tests/stream_redaction.rs`) is a per-Provider instance of the same closed list", + "removalProof" : "Not applicable — this is a permanent gate, not a migration", + "reuseAction" : "adapt", + "reuseSource" : null, + "specId" : "ADR-046-security-and-threat-model", + "specPath" : "docs/specs/ADR-046-security-and-threat-model.md", + "validation" : "Hermetic (`cargo test -p d2b-contract-tests policy_telemetry_redaction`); fails the build if a new Provider crate is added without a corresponding redaction test file under its `tests/`", + "workItemId" : "ADR046-security-001" + }, + { + "currentSource" : "main `a1cc0b2d`: `d2b-session/tests/noise_vectors.rs`, `d2b-session/tests/component_session.rs`, `d2b-session-unix/tests/unix_session.rs`", + "dataMigration" : "None — full d2b 3.0 reset; no prior state to migrate", + "dependencyOwner" : "`ADR046-session-001`/`ADR046-session-003` (ComponentSession/d2b-bus implementation)", + "destination" : "`packages/d2b-session/tests/noise_conformance.rs`, `packages/d2b-session/fuzz/fuzz_targets/{handshake_offer,record_frame}.rs`", + "detailedDesign" : "Property/fuzz test suite over the three Noise profiles (§7): exact NN/KK/IKpsk2 vectors and rejection mutations (copied), plus new `cargo-fuzz` targets mutating the canonical handshake offer, preface, and encrypted record frame to assert no panic/UB and that every malformed input is a typed rejection (never a partial accept) Primary reuse disposition: `adapt`. Preserved source-plan detail: copy and adapt.", + "integration" : "Wired into `make test-rust` (vectors) and a separate `make test-fuzz` target (new; time-boxed nightly run, not part of the PR-blocking gate)", + "removalProof" : "Not applicable", + "reuseAction" : "adapt", + "reuseSource" : "Same main commit/paths", + "specId" : "ADR-046-security-and-threat-model", + "specPath" : "docs/specs/ADR-046-security-and-threat-model.md", + "validation" : "Hermetic vector tests plus fuzz corpus with a minimum 4-hour nightly run and zero crashes/hangs as acceptance", + "workItemId" : "ADR046-security-002" + }, + { + "currentSource" : "`packages/d2bd/src/admission.rs` (`verb_requires_admin()` baseline verb table)", + "dataMigration" : "None — full d2b 3.0 reset; no prior state to migrate", + "dependencyOwner" : "`ADR046-zone-control-004`/`ADR046-zone-control-005` (Role/RoleBinding implementation)", + "destination" : "`packages/d2b-resource-store/tests/rbac_property.rs`", + "detailedDesign" : "Property test asserting, for a randomly generated Role/RoleBinding/request corpus: (1) no request whose payload sets a subject/role field ever changes the resolved `AuthenticatedSubjectContext.subjectRef`; (2) no non-core Role with a wildcard grant is ever admitted; (3) `scopeNarrowing` never widens beyond the referenced Role; (4) RoleBinding deletion never leaves an observable intermediate state under concurrent readers Primary reuse disposition: `adapt`. Preserved source-plan detail: extract and adapt.", + "integration" : "Runs against the real redb-backed resource store test harness, not a mock", + "removalProof" : "Not applicable", + "reuseAction" : "adapt", + "reuseSource" : null, + "specId" : "ADR-046-security-and-threat-model", + "specPath" : "docs/specs/ADR-046-security-and-threat-model.md", + "validation" : "Hermetic property test (`proptest`/`quickcheck`-style, minimum 10,000 cases per property)", + "workItemId" : "ADR046-security-003" + }, + { + "currentSource" : "None (v3 ZoneLink relay is `ADR-only`)", + "dataMigration" : "None — full d2b 3.0 reset; no prior state to migrate", + "dependencyOwner" : "`ADR046-routing-005`/`ADR046-session-003` (ZoneLink/d2b-bus relay implementation)", + "destination" : "`packages/d2b-bus/fuzz/fuzz_targets/zonelink_frame.rs`, `packages/d2b-bus/tests/zonelink_structural_rejection.rs`", + "detailedDesign" : "Fuzz + property suite asserting that no mutation of a ZoneLink-bound frame (attachment count, credential-shaped byte runs, path-shaped strings, PID-shaped integers) is ever forwarded — every such mutation is rejected at serialization with `attachment-not-permitted-over-zone-link` or the transport-specific equivalent, never silently dropped or partially forwarded Primary reuse disposition: `adapt`. Preserved source-plan detail: extract and adapt (design copied from `ADR-046-zone-routing.md` structural-rejection sections).", + "integration" : "`make test-fuzz`; a companion container test (`tests/integration/containers/zonelink-cross-zone.rs`) runs two real Zone runtime containers connected by a real ZoneLink and asserts the same property end to end over the wire, not just in the frame-serialization unit", + "removalProof" : "Not applicable", + "reuseAction" : "adapt", + "reuseSource" : null, + "specId" : "ADR-046-security-and-threat-model", + "specPath" : "docs/specs/ADR-046-security-and-threat-model.md", + "validation" : "Fuzz corpus (`cargo fuzz run zonelink_frame -- -runs=1000000`, zero crashes); container test passes in `make test-integration`", + "workItemId" : "ADR046-security-004" + }, + { + "currentSource" : "None (compile-time dependency audit does not exist yet; per-Provider \"imports no broker DTO\" claims are currently prose-only in each dossier)", + "dataMigration" : "None — full d2b 3.0 reset; no prior state to migrate", + "dependencyOwner" : "Every Provider dossier's own crate; owned by the Provider packaging integrator (`ADR-046-provider-model-and-packaging`)", + "destination" : "`packages/xtask/src/effectport_boundary_check.rs`, wired into `make test-policy`", + "detailedDesign" : "For every crate under `packages/d2b-provider-*`, walk its `Cargo.toml` dependency graph and fail the build if it transitively depends on `d2b-priv-broker` or any crate exposing a raw broker client/DTO type; separately, grep-scan for direct syscalls forbidden per dossier (e.g. `socket(AF_VSOCK` in `transport-vsock`, `Command::new(\"systemctl\"` in `system-systemd`)", + "integration" : "`make test-policy`; blocks any PR adding a forbidden dependency edge or forbidden syscall string to a Provider crate", + "removalProof" : "Not applicable — permanent gate", + "reuseAction" : "adapt", + "reuseSource" : "`cargo-deny`/`cargo tree`-style dependency graph tooling already used by `nix flake check`'s `rust-deny`/`rust-audit` derivations (`AGENTS.md` \"Disk hygiene contract\")", + "specId" : "ADR-046-security-and-threat-model", + "specPath" : "docs/specs/ADR-046-security-and-threat-model.md", + "validation" : "Hermetic (`cargo xtask effectport-boundary-check`); a negative test intentionally adds a forbidden dependency to a scratch crate and asserts the check fails", + "workItemId" : "ADR046-security-005" + }, + { + "currentSource" : "`packages/d2b-priv-broker/src/sys.rs` (`clone3_spawn_runner` user-namespace path); `packages/d2b-host/src/virtiofsd_argv.rs`", + "dataMigration" : "None — full d2b 3.0 reset; no prior state to migrate", + "dependencyOwner" : "`ADR046-minijail-002`/`ADR046-minijail-003` (LaunchTicket/EffectPort implementation)", + "destination" : "`packages/d2b-provider-system-minijail/tests/launchticket_toctou.rs`", + "detailedDesign" : "Fault-injection test that issues a `LaunchTicket`, then mutates the referenced `CompiledSandboxPlan` digest (simulating a race between issue and exec) before the broker execs, and asserts the spawn fails closed rather than launching with the old plan; a companion test kills the broker mid-`clone3` and asserts no half-initialized process (missing cgroup placement, non-zero host capabilities) is ever observable by a concurrent reader", + "integration" : "`make test-rust` (unit-level fault injection via a fake clock/fault-injecting `EffectPort` test double); a host/KVM integration test (`tests/host-integration/launchticket-toctou.nix`) repeats the same scenario against the real broker and real `clone3(2)`", + "removalProof" : "Not applicable", + "reuseAction" : "adapt", + "reuseSource" : "Same v3 paths (already `implemented-and-reachable` per the migration map)", + "specId" : "ADR-046-security-and-threat-model", + "specPath" : "docs/specs/ADR-046-security-and-threat-model.md", + "validation" : "Hermetic fault-injection test plus `make test-host-integration` NixOS/KVM test; acceptance is zero observable non-zero-capability or missing-cgroup-placement windows across 10,000 injected-fault iterations", + "workItemId" : "ADR046-security-006" + }, + { + "currentSource" : "`packages/d2b-priv-broker/src/ops/swtpm_dir.rs` (existing fail-closed marker/quarantine pattern, `implemented-and-reachable`)", + "dataMigration" : "None — full d2b 3.0 reset; no prior state to migrate", + "dependencyOwner" : "`ADR046-minijail-005`, `ADR046-systemd-002`, `ADR046-aca-001`, `ADR046-volume-001` (every quarantine-on-ambiguity implementation)", + "destination" : "`packages/d2b-contract-tests/tests/quarantine_not_kill_matrix.rs`", + "detailedDesign" : "One parameterized fault-injection matrix test, run once per adoption-capable Provider (`system-minijail`, `system-systemd`, `runtime-cloud-hypervisor`, `runtime-azure-container-apps`, `volume-local`), that restarts the controller with a deliberately ambiguous adoption candidate (duplicate InvocationID, mismatched marker inode, stale ACA operation handle) and asserts: (a) the resource transitions to `Degraded`/`Quarantined`, never `Deleted` or silently re-adopted; (b) no signal is sent to the ambiguous candidate process; (c) a `runtime-security-violation`-class audit record is emitted Primary reuse disposition: `adapt`. Preserved source-plan detail: extract and adapt.", + "integration" : "`make test-rust` for the in-process cases; `make test-host-integration` for the real-pidfd/real-cgroup cases (`tests/host-integration/quarantine-not-kill.nix`)", + "removalProof" : "Not applicable", + "reuseAction" : "adapt", + "reuseSource" : "Same v3 path, generalized", + "specId" : "ADR-046-security-and-threat-model", + "specPath" : "docs/specs/ADR-046-security-and-threat-model.md", + "validation" : "Matrix covers all five Providers listed; acceptance is 100% pass across all five with no signal sent to the ambiguous candidate in any case", + "workItemId" : "ADR046-security-007" + }, + { + "currentSource" : "`packages/d2b-contracts/src/public_wire.rs:267` (`WorkloadPublicSummary.execution_posture`, `implemented-and-reachable`); `packages/d2bd/src/unsafe_local_helper.rs` (`HelperRegistry::dispatch_launch`, current gap: does not emit a `ProcessEffect`-class event)", + "dataMigration" : "None (closes the current `HelperRegistry::dispatch_launch` audit gap noted in `ADR-046-telemetry-audit-and-support.md`)", + "dependencyOwner" : "`ADR046-exec-002` (Host `isolationPosture` propagation)", + "destination" : "`packages/d2b-provider-system-core/tests/no_isolation_propagation.rs`", + "detailedDesign" : "Integration test that creates a user-only `Host`, launches a Process on it, and asserts all three non-suppressible surfaces simultaneously: `status.isolationPosture == \"none\"` in JSON output; the unconditional stderr warning string is present with `--json` and non-JSON CLI invocation and cannot be suppressed by any combination of flags; the corresponding `ProcessEffect` audit record carries `no_isolation: true`; and a companion negative assertion that `no_isolation` never appears in any OTEL metric/span emitted during the same test run", + "integration" : "`make test-rust` (CLI/status/audit assertions) plus a Nix eval test (`tests/unit/nix/cases/no-isolation-null-posture-rejected.nix`) asserting the D042/D067 bidirectional-rejection eval assertions fire", + "removalProof" : "Legacy `d2b-unsafe-local-helper` v2-protocol warning path removed only after this test passes against the v3 replacement and the legacy crate has no remaining callers", + "reuseAction" : "adapt", + "reuseSource" : "Same v3 paths", + "specId" : "ADR-046-security-and-threat-model", + "specPath" : "docs/specs/ADR-046-security-and-threat-model.md", + "validation" : "Hermetic CLI/audit integration test; Nix eval test; acceptance is zero code paths reaching a live user-only-Host Process without all three surfaces firing", + "workItemId" : "ADR046-security-008" + }, + { + "currentSource" : "`tests/unit/nix/cases/per-vm-state-ownership.nix` (existing v3 ownership test, adapted target)", + "dataMigration" : "None — full d2b 3.0 reset; no prior state to migrate", + "dependencyOwner" : "`ADR046-pstate-003` (Volume identity marker)", + "destination" : "`packages/d2b-provider-volume-local/tests/marker_tamper_fault_injection.rs`", + "detailedDesign" : "Fault-injection test that provisions a Volume, then out-of-band (as a simulated attacker with filesystem access) replaces the marker file, swaps the backing directory for a different inode on the same `st_dev`, and deletes the marker entirely — three separate scenarios — and asserts each transitions the Volume to `Failed` with `markerStatus: missing`/`replaced` respectively, never a silent re-provision, and that operator-only remediation is the only recovery path exercised", + "integration" : "`make test-rust`; a host-integration variant (`tests/host-integration/volume-marker-tamper.nix`) repeats the inode-swap scenario against the real broker-maintained marker root on a real filesystem", + "removalProof" : "Not applicable", + "reuseAction" : "adapt", + "reuseSource" : null, + "specId" : "ADR-046-security-and-threat-model", + "specPath" : "docs/specs/ADR-046-security-and-threat-model.md", + "validation" : "Hermetic + host/KVM fault-injection test; acceptance is 100% fail-closed across all three tamper scenarios", + "workItemId" : "ADR046-security-009" + }, + { + "currentSource" : "None (zero-secret invariant has no existing automated gate)", + "dataMigration" : "None — full d2b 3.0 reset; no prior state to migrate", + "dependencyOwner" : "`ADR046-credential-001` through `ADR046-credential-007`", + "destination" : "`packages/d2b-contract-tests/tests/zero_secret_invariant.rs`", + "detailedDesign" : "Static + dynamic gate: (1) static — every DTO type reachable from a `Credential`-adjacent module must implement a hand-written redacted `Debug` and must not derive `Debug`, enforced by a `#[forbid(clippy::derive_debug_ambient)]`-style custom lint or an `xtask` AST scan; (2) dynamic — a property test that generates random `Credential` delivery sessions and asserts the delivered token/`SignChallenge` byte sequence never appears, byte-for-byte, in any captured audit record, OTEL span, log line, or resource-store row taken during the same test run Primary reuse disposition: `adapt`. Preserved source-plan detail: extract and adapt (design from `ADR-046-resources-credential.md` §1.1).", + "integration" : "`make test-lint` (static scan) and `make test-rust` (dynamic property test)", + "removalProof" : "Not applicable", + "reuseAction" : "adapt", + "reuseSource" : null, + "specId" : "ADR-046-security-and-threat-model", + "specPath" : "docs/specs/ADR-046-security-and-threat-model.md", + "validation" : "Hermetic; the dynamic test additionally runs as a canary-byte test (a unique random marker is embedded in the token and searched for across every observability surface)", + "workItemId" : "ADR046-security-010" + }, + { + "currentSource" : "`packages/d2b-unsafe-local-helper/src/{shell_runtime,shell_supervisor}.rs` (existing terminal-byte redaction discipline, `implemented-and-reachable`, adapted target)", + "dataMigration" : "None — full d2b 3.0 reset; no prior state to migrate", + "dependencyOwner" : "`ADR046-notify-004`, clipboard/terminal/security-key Provider work items", + "destination" : "`packages/d2b-provider-{clipboard-wayland,shell-terminal,device-security-key,notification-desktop}/tests/stream_redaction.rs` (one per Provider, same shared test helper crate)", + "detailedDesign" : "Shared canary-byte test helper: each Provider's test injects a unique random marker into its sensitive content path (clipboard bytes, terminal output, CTAP payload, notification body) and asserts the marker never appears in audit, OTEL, Debug output, or CLI error text captured during the test", + "integration" : "`make test-rust`; a container integration test (`tests/integration/containers/content-secrecy.rs`) runs a real Wayland/D-Bus mock session end to end for the clipboard/notification cases", + "removalProof" : "Not applicable", + "reuseAction" : "adapt", + "reuseSource" : "Same v3 paths", + "specId" : "ADR-046-security-and-threat-model", + "specPath" : "docs/specs/ADR-046-security-and-threat-model.md", + "validation" : "Hermetic canary-byte test per Provider (4 Providers, shared helper crate); container test for the two D-Bus/Wayland-mediated cases", + "workItemId" : "ADR046-security-011" + }, + { + "currentSource" : "`packages/d2bd/src/daemon_audit.rs` (existing audit-write path, adapted target)", + "dataMigration" : "None — full d2b 3.0 reset; no prior state to migrate", + "dependencyOwner" : "`ADR046-telem-013` (privileged audit durability)", + "destination" : "`packages/d2b-audit/tests/privileged_fail_closed.rs`", + "detailedDesign" : "Fault-injection test that makes the audit sink's fsync fail (simulated ENOSPC/EIO) during a privileged `ResourceMutation`/`RBACChange`/`StateReset` write, and asserts the originating operation itself fails with `audit-unavailable` rather than completing with a lost audit record; a companion test floods `Standard`/`Best-effort` records past `DEFAULT_AUDIT_WRITES_PER_SECOND` and asserts privileged records are never dropped or delayed by the resulting backpressure", + "integration" : "`make test-rust`", + "removalProof" : "Not applicable", + "reuseAction" : "adapt", + "reuseSource" : "Same v3 path", + "specId" : "ADR-046-security-and-threat-model", + "specPath" : "docs/specs/ADR-046-security-and-threat-model.md", + "validation" : "Hermetic fault-injection test; acceptance is zero privileged-class operations that complete despite a failed durable audit write", + "workItemId" : "ADR046-security-012" + }, + { + "currentSource" : "None new; ceiling values are already enumerated in `ADR-046-zone-routing.md`/`ADR-046-cli-and-operations.md`", + "dataMigration" : "None — full d2b 3.0 reset; no prior state to migrate", + "dependencyOwner" : "`ADR046-zone-control-009`/`ADR046-zone-control-010` (Quota/EmergencyPolicy), `ADR046-session-001` (session limits)", + "destination" : "`packages/d2b-bus/tests/dos_ceiling_fault_injection.rs`", + "detailedDesign" : "Fault-injection/load test suite: (1) attachment-credit exhaustion at each of the six scopes (Packet/Request/Operation/Session/Process/Host), asserting typed rejection never a panic; (2) reconnect-storm exceeding `MAX_RECONNECT_ATTEMPTS`/`MAX_RECONNECT_WINDOW_MS`, asserting the session fails closed rather than looping; (3) ZoneLink hop-count/route-advertisement replay flood, asserting `hop-limit-exceeded`/`zone-advertisement-replay` rather than unbounded forwarding; (4) a stalled data stream under load, asserting control/cancellation traffic is never starved (priority-scheduling property) Primary reuse disposition: `adapt`. Preserved source-plan detail: copy and adapt.", + "integration" : "`make test-rust`; item (4) additionally runs as a container load test (`tests/integration/containers/backpressure-priority.rs`) with a real slow consumer", + "removalProof" : "Not applicable", + "reuseAction" : "adapt", + "reuseSource" : "main `a1cc0b2d`: `d2b-session` credit-accounting/priority-scheduling tests, copied for the checked-arithmetic/priority-ordering assertions", + "specId" : "ADR-046-security-and-threat-model", + "specPath" : "docs/specs/ADR-046-security-and-threat-model.md", + "validation" : "Hermetic fault-injection suite; container load test; acceptance is zero panics/unbounded-growth across all four scenarios", + "workItemId" : "ADR046-security-013" + }, + { + "currentSource" : "None (both commands are `ADR-only`)", + "dataMigration" : "None — full d2b 3.0 reset; no prior state to migrate", + "dependencyOwner" : "`ADR046-telem-016`/`ADR046-telem-017` (doctor/support-bundle commands)", + "destination" : "`packages/d2b/src/commands/{doctor,support_bundle}.rs`", + "detailedDesign" : "`d2b zone doctor` performs read-only status/audit-hash-chain checks with the redaction rules from §21 enforced on every field it prints; `d2b zone support-bundle` assembles a bounded archive of metadata+status (never spec bytes or `metadata.name`) and sets `bundle_completeness: \"partial\"` when any Provider in scope is quarantined, rather than omitting the gap silently Primary reuse disposition: `adapt`. Preserved source-plan detail: extract and adapt (design from `ADR-046-telemetry-audit-and-support.md`).", + "integration" : "`make test-rust` (CLI integration tests); a container test (`tests/integration/containers/support-bundle-quarantined.rs`) runs a real Zone with one quarantined Provider and asserts the bundle correctly reports `partial`", + "removalProof" : "Not applicable", + "reuseAction" : "adapt", + "reuseSource" : null, + "specId" : "ADR-046-security-and-threat-model", + "specPath" : "docs/specs/ADR-046-security-and-threat-model.md", + "validation" : "Hermetic CLI test asserting no spec byte or `metadata.name` appears in a generated bundle; container test for the quarantined-Provider case", + "workItemId" : "ADR046-security-014" + }, + { + "currentSource" : "Historical main ADR 0045 factory-reset design (`a1cc0b2d^:docs/adr/0045-provider-and-transport-framework.md`, reset process overview and apply-command verification steps) — reused only as a design precedent for atomicity/fail-closed sequencing, not as v3 architecture (see [Reset boundary](#reset-boundary) for the explicitly excluded assumptions)", + "dataMigration" : "None (v3-native; no v1/v2 reset-generation state to migrate)", + "dependencyOwner" : "`ADR046-telem-012` (`StateReset` record), Volume/Credential lifecycle work items", + "destination" : "`packages/d2b-core-controller/src/reset.rs`, `packages/d2b-core-controller/tests/reset_atomicity.rs`", + "detailedDesign" : "Implements the `scope` (`zone`, `provider`, `host`, or `guest`) `StateReset` flow from §25: quiesce via `EmergencyPolicy`, revoke open Credential leases in scope, destroy Volumes in scope (key-shred first), commit the `StateReset` audit record durably, and only then report the reset complete. A crash-recovery path re-derives \"was this reset already committed?\" solely from the durable `StateReset` record, never from partial filesystem state Primary reuse disposition: `adapt`. Preserved source-plan detail: adapt (pattern only).", + "integration" : "`make test-rust` (unit-level state machine); a host/KVM integration test (`tests/host-integration/reset-atomicity.nix`) kills the process mid-reset at each of the four phases (quiesce, credential revoke, Volume destroy, audit commit) and asserts recovery never double-destroys, never silently completes without the audit record, and never leaves an orphaned sealed-Volume-without-key state", + "removalProof" : "Not applicable", + "reuseAction" : "adapt", + "reuseSource" : "Same historical commit, sequencing pattern only (no code reuse; historical implementation was bash/systemd-generation-based and does not exist in any Rust crate)", + "specId" : "ADR-046-security-and-threat-model", + "specPath" : "docs/specs/ADR-046-security-and-threat-model.md", + "validation" : "Hermetic state-machine test; host/KVM crash-injection test at all four phases; acceptance is zero non-atomic outcomes across all injected crash points", + "workItemId" : "ADR046-security-015" + }, + { + "currentSource" : "None (cross-reference check does not exist)", + "dataMigration" : "None — full d2b 3.0 reset; no prior state to migrate", + "dependencyOwner" : "Documentation/CI integrator; depends on every Provider dossier's own `Security` section existing", + "destination" : "`tests/unit/gates/security-matrix-coverage.sh`", + "detailedDesign" : "A drift-style gate that parses [Per-ResourceType threat matrix](#per-resourcetype-threat-matrix) and [Per-Provider-family threat matrix](#per-provider-family-threat-matrix), confirms every one of the 19 standard ResourceTypes and all 27 Provider dossiers under `docs/specs/providers/` has a row, and confirms every referenced dossier file actually contains a `## Security`-class section (by heading grep) — failing the gate if a new ResourceType/Provider is added without a corresponding row and dossier section Primary reuse disposition: `adapt`. Preserved source-plan detail: adapt (pattern from `tests/unit/gates/drift-check.sh`).", + "integration" : "`make test-drift`", + "removalProof" : "Not applicable — permanent gate", + "reuseAction" : "adapt", + "reuseSource" : null, + "specId" : "ADR-046-security-and-threat-model", + "specPath" : "docs/specs/ADR-046-security-and-threat-model.md", + "validation" : "Hermetic shell-script gate; a negative test adds a scratch Provider dossier missing a Security section and asserts the gate fails", + "workItemId" : "ADR046-security-016" + }, + { + "currentSource" : "None — net-new v3 work; no pre-ADR45 baseline equivalent", + "dataMigration" : "None — full d2b 3.0 reset; no prior state to migrate", + "dependencyOwner" : "`ADR046-routing-004`, gateway-custody Provider work items (`ADR046-aca-*`, `ADR046-azure-vm-*`, `ADR046-transport-relay-*`)", + "destination" : "`tests/integration/containers/malicious-child-zone.rs`", + "detailedDesign" : "Container-based penetration test running a real parent Zone and a deliberately malicious child Zone container that attempts, over a real ZoneLink: FD smuggling, credential-shaped byte injection, cross-Zone `ownerRef` forgery, capability-ceiling widening claims, and route-advertisement replay. Every attempt must be rejected by the parent with the specific typed error named in §10, and none may reach the parent's resource store, Credential state, or Host substrate Primary reuse disposition: `adapt`. Preserved source-plan detail: extract and adapt.", + "integration" : "`make test-integration` (requires podman, per `AGENTS.md` \"Local Layer 1 + container integration\")", + "removalProof" : "Not applicable", + "reuseAction" : "adapt", + "reuseSource" : null, + "specId" : "ADR-046-security-and-threat-model", + "specPath" : "docs/specs/ADR-046-security-and-threat-model.md", + "validation" : "Container integration test; acceptance is zero successful attacks across all five attempted vectors", + "workItemId" : "ADR046-security-017" + }, + { + "currentSource" : "None (manual cloud/hardware validation has no automated equivalent by design)", + "dataMigration" : "None — full d2b 3.0 reset; no prior state to migrate", + "dependencyOwner" : "Manual validation owner; depends on `ADR046-azure-vm-*`, `ADR046-transport-relay-*`, `ADR046-aca-*`, device Provider work items reaching a testable state", + "destination" : "`docs/reference/security-manual-validation-checklist.md` (new reference doc, out of scope for this spec's own file but named here as the required destination for the future implementation PR)", + "detailedDesign" : "A checklist covering the scenarios that cannot be hermetically or even container-tested: (1) real Azure Container Apps/Azure VM credential rotation and revocation under `AzureEffectPort`, confirming zeroization on a real managed-identity/Entra token; (2) real TPM 2.0 hardware NVRAM persistence/tamper-marker behavior across a real host reboot; (3) real USBIP/security-key hardware mutual-exclusion enforcement with a physical FIDO2 device; (4) real Azure Relay listener/sender credential acquisition and relay-identity-not-local-auth verification against a live relay namespace Primary reuse disposition: `adapt`. Preserved source-plan detail: adapt (checklist pattern from `SECURITY.md`'s existing portability-roadmap manual milestones and `tests/README.md`'s manual hardware tier).", + "integration" : "Run manually before each tagged release touching a cloud/hardware Provider, per the existing `tests/README.md` manual-tier convention", + "removalProof" : "Not applicable", + "reuseAction" : "adapt", + "reuseSource" : null, + "specId" : "ADR-046-security-and-threat-model", + "specPath" : "docs/specs/ADR-046-security-and-threat-model.md", + "validation" : "Checklist sign-off recorded in the release's validation evidence, not a CI gate (matches `D2b_LIVE=1` manual-tier precedent in `AGENTS.md`)", + "workItemId" : "ADR046-security-018" + }, + { + "currentSource" : "`packages/d2bd/src/supervisor/pidfd_table.rs` (`BrokerReapLog`) and `packages/d2b-priv-broker/src/sys.rs` (`clone3_spawn_runner`)", + "dataMigration" : "None — full d2b 3.0 reset; no prior state to migrate", + "dependencyOwner" : "`ADR046-exec-003`/`ADR046-exec-007` and `ADR046-minijail-003` through `ADR046-minijail-005`; system-minijail/broker integration owner", + "destination" : "`packages/d2b-contract-tests/tests/minijail_process_ownership.rs`; `tests/host-integration/minijail-cgroup-kill.nix`", + "detailedDesign" : "Hermetic contract test proves only the broker that called `clone3` can produce the identity-bound `BrokerTerminalResult`; a non-parent poll-readable pidfd cannot be converted to status, while a verified duplicate holder can still request exact-main `pidfd_send_signal`. Host integration launches an owned descendant that calls `setsid(2)` plus an unrelated recycled-PGID decoy, performs graceful exact-main stop followed by anchored leaf `cgroup.kill`, and proves the owned leaf reaches `populated 0`, the broker reaps exactly once, the decoy survives, and rmdir/finalizer clearing wait for both proofs. Negative cases prove ambiguous adoption emits no signal/`cgroup.kill`, and Linux <5.14 or missing/unwritable `cgroup.kill` fails before spawn. Primary reuse disposition: `adapt`. Preserved source-plan detail: extract and adapt.", + "integration" : "Hermetic contract test in `make test-rust`; real pidfd/cgroup scenario in `make test-host-integration`", + "removalProof" : "Old ProviderSupervisor/controller wait/reap and PGID-kill paths are removed only after this gate passes against the replacement", + "reuseAction" : "adapt", + "reuseSource" : "Same v3 paths, adapted to the corrected ownership contract", + "specId" : "ADR-046-security-and-threat-model", + "specPath" : "docs/specs/ADR-046-security-and-threat-model.md", + "validation" : "Parent-only wait/reap, poll-readability-not-status, duplicate-holder signaling, setsid/PGID-reuse resistance, quarantine no-kill, exact-once reap, and Linux ≥5.14 platform-gate assertions all pass", + "workItemId" : "ADR046-security-019" + }, + { + "currentSource" : "`packages/d2bd/src/security_key.rs` — baseline internal `SecurityKeyState` (renamed `RelaySessionTable` in v3 so state terminology remains reserved for Resource status), `LeaseState`, `LeaseId`, `CidTranslator`, `try_acquire_lease`, `release_lease`, `CEREMONY_TIMEOUT`, `QUEUE_WAIT_TIMEOUT` (implemented-and-reachable)", + "dataMigration" : "Full d2b 3.0 reset; no v2 state/config import", + "dependencyOwner" : "ADR-046 provider-device-security-key session/relay owner; depends on ADR046-security-key-008 and the ComponentSession/Process contracts.", + "destination" : "Move to `packages/d2b-provider-device-security-key/src/session.rs` and `cid.rs`; adapt to Provider Process model (remove daemon Mutex wrapping, add async relay protocol)", + "detailedDesign" : "Extract the baseline lease/session constants and CID mapping into provider-local modules. Preserve `LeaseId` stale-release protection, cancel-all-active-CIDs, `CEREMONY_TIMEOUT`, `QUEUE_WAIT_TIMEOUT`, and bounded fair queue semantics; remove daemon-global `Mutex` ownership; keep the authority relay's DeviceGrant/OFD lease for its lifetime. Ceremony rows remain high-churn session records, never Resources. Primary reuse disposition: `adapt`. Preserved source-plan detail: extract and adapt.", + "integration" : "ADR046-security-key-010 owns the relay loop; ADR046-security-key-011/ADR046-security-key-012 consume this extracted foundation; ADR046-security-key-009 consumes lifecycle events and writes bounded Service/Binding observations; ComponentSession/encrypted named streams carry CTAPHID bytes.", + "removalProof" : "ADR046-security-key-030 deletes the superseded daemon-internal `packages/d2bd/src/security_key.rs` `SecurityKeyState`, `LeaseState`, `SkRegistry`, and accept-loop ownership after the provider relay/session tests pass.", + "reuseAction" : "adapt", + "reuseSource" : null, + "specId" : "ADR-046-provider-device-security-key", + "specPath" : "docs/specs/providers/ADR-046-provider-device-security-key.md", + "validation" : "`session_state_machine.rs`, `session_ring.rs`, `cancel_propagation.rs`, `session_timeout.rs`, `fair_queue.rs`, and `cid_isolation.rs` verify queue/active/completed/timeout transitions, ring eviction, LeaseId stale-release denial, cancel-all-CIDs, fair timeout, and per-session CID isolation with no daemon-global lease state or ceremony Resource.", + "workItemId" : "ADR046-security-key-001" + }, + { + "currentSource" : "`packages/d2bd/src/security_key.rs` — CTAPHID relay loop, `SkAcceptHandle`, `relay_one_ceremony` (implemented-and-reachable)", + "dataMigration" : "Full d2b 3.0 reset; no v2 state/config import", + "dependencyOwner" : "ADR-046 provider-device-security-key relay extraction owner; depends on ADR046-security-key-008, ADR046-security-key-001, and the frozen ComponentSession/Endpoint contracts.", + "destination" : "Move to `packages/d2b-provider-device-security-key/src/relay.rs`; replace daemon-internal Unix socket proxy with ComponentSession over the owned Service Endpoint", + "detailedDesign" : "Extract the CTAPHID ceremony relay behavior into the provider relay binary. Preserve one-ceremony-at-a-time proxy semantics and CTAPHID cancel handling, but replace daemon-internal Unix socket proxying with the `d2b.security-key.v3` ComponentSession over the owned CTAPHID Endpoint and named `ctaphid` stream. Primary reuse disposition: `adapt`. Preserved source-plan detail: extract and adapt.", + "integration" : "Core launches the relay Process with a LaunchTicket DeviceGrant and Endpoint attachment; transport-vsock resolves `Endpoint/-ctaphid-relay`; frontend Process connects as ComponentSession initiator; controller receives session events over the manifest-declared internal channel.", + "removalProof" : "ADR046-security-key-030 and ADR046-security-key-031 remove `start_sk_accept_loop`, `SkAcceptHandle`, `relay_one_ceremony`, and the daemon-internal Unix socket proxy bind from `packages/d2bd/src/security_key.rs` and `packages/d2bd/src/lib.rs`.", + "reuseAction" : "adapt", + "reuseSource" : null, + "specId" : "ADR-046-provider-device-security-key", + "specPath" : "docs/specs/providers/ADR-046-provider-device-security-key.md", + "validation" : "`host_relay_guest_frontend/` integration fixture, `device_grant_no_path.rs`, `descriptor_validation.rs`, and `cancel_propagation.rs` prove relay fd injection, ComponentSession transport, cancel propagation, and absence of daemon-internal socket proxying.", + "workItemId" : "ADR046-security-key-002" + }, + { + "currentSource" : "`packages/d2b-sk-frontend/src/` — `main.rs`, `uhid.rs` (implemented-and-reachable); `framing.rs` and `vsock.rs` are obsolete under v3 (replaced by ComponentSession transport)", + "dataMigration" : "Full d2b 3.0 reset; no frontend session state import", + "dependencyOwner" : "ADR-046 provider-device-security-key frontend extraction owner; depends on ADR046-security-key-008 and frozen Process/ComponentSession contracts.", + "destination" : "Adopt `main.rs` and `uhid.rs` as the v3 Process binary entry point; replace `framing.rs`/`vsock.rs` with ComponentSession client from `d2b-session-unix/src/vsock.rs`; wire as Process service in Provider crate", + "detailedDesign" : "Retain UHID creation and frontend entry behavior, but run it as a Binding-owned v3 user-domain Process receiving a pre-opened `/dev/uhid` fd from the `Provider/system-core` Guest-substrate DeviceGrant. Delete raw frame/vsock protocol and use the ComponentSession client/named `ctaphid` stream. No virtual/projected Device exists. Primary reuse disposition: `adapt`. Preserved source-plan detail: extract and adapt.", + "integration" : "ADR046-security-key-026 defines Service/Binding ownership; ADR046-security-key-020/N17 wire the Binding-owned frontend Process/private Endpoint and same-Zone Service resolution; Core injects UHID from the Guest substrate.", + "removalProof" : "ADR046-security-key-032 removes the legacy `d2b-sk-frontend.service` unit declaration, and the v3 frontend excludes the obsolete `packages/d2b-sk-frontend/src/framing.rs` and `vsock.rs` raw transport behavior.", + "reuseAction" : "adapt", + "reuseSource" : null, + "specId" : "ADR-046-provider-device-security-key", + "specPath" : "docs/specs/providers/ADR-046-provider-device-security-key.md", + "validation" : "`host_relay_guest_frontend/`, `device_grant_no_path.rs`, `descriptor_validation.rs`, and guest Nix migration tests prove UHID fd injection, no `/dev/uhid` path, ComponentSession client use, and no raw `framing.rs`/`vsock.rs` protocol.", + "workItemId" : "ADR046-security-key-003" + }, + { + "currentSource" : "`packages/d2b-priv-broker/src/ops/security_key.rs` — `live_open_hidraw_security_key`, FIDO usage page revalidation, group validation, `ALLOWED_GROUPS` (implemented-and-reachable)", + "dataMigration" : "Full d2b 3.0 reset; no v2 device state import", + "dependencyOwner" : "Core LaunchTicket/privileged broker reuse owner; depends on ADR-046-resources-device and ADR046-security-key-013 probe/device-token population.", + "destination" : "Preserve revalidation logic; update `SecurityKeyOpenDevice` to use bundle device table `device_token` as sole open target (no iterative sysfs scan); add zone-field handling; remove sysfs fallback. **Core's LaunchTicket calls this internally; the Provider does not call it.**", + "detailedDesign" : "Keep the FIDO usage-page and post-open revalidation logic, but make the trusted private bundle `device_token` the only open target. Add Zone-aware request handling, reject path/sysfs fallback inputs, and keep the operation internal to Core LaunchTicket DeviceGrant resolution rather than callable by the Provider controller.", + "integration" : "Provider activation records label-to-`device_token` mappings; Core LaunchTicket resolves `deviceUsage` for the relay Process; broker opens and revalidates the hidraw fd; relay receives only an inherited fd; Core emits path-free `device-grant` audit.", + "removalProof" : "The superseded iterative sysfs scan/fallback behavior in `packages/d2b-priv-broker/src/ops/security_key.rs` is removed once bundle-token lookup and revalidation tests pass.", + "reuseAction" : "adapt", + "reuseSource" : null, + "specId" : "ADR-046-provider-device-security-key", + "specPath" : "docs/specs/providers/ADR-046-provider-device-security-key.md", + "validation" : "`packages/d2b-priv-broker/tests/security_key_broker.rs` updates for bundle table lookup and zone-field round trip; `device_grant_no_path.rs` proves Provider code does not call the broker and sees no device path; audit tests prove path-free grant records.", + "workItemId" : "ADR046-security-key-004" + }, + { + "currentSource" : "`packages/d2b-contracts/src/security_key.rs` — `SecurityKeySessionId`, `SecurityKeyDeviceLabel`, `SecurityKeySession`, `SecurityKeySessionResult`, `SecurityKeyStatusResponse`, `SecurityKeySessionsResponse`, `SecurityKeyOpenDeviceRequest`, `SecurityKeyEvent` (implemented-and-reachable)", + "dataMigration" : "Full d2b 3.0 reset; no v2 DTO compatibility migration beyond serde-shape preservation where possible", + "dependencyOwner" : "`d2b-contracts` security-key ceremony/effect DTO owner; depends on ADR046-provider-004, ADR-046-resource-object-model, ADR-046-resources-device, and ADR046-security-key-008.", + "destination" : "Adapt to v3 Zone/ResourceRef identifiers; preserve serde shapes for zero downstream breakage where possible; remove `SecurityKeyApplyUdevRulesRequest` (ADR046-security-key-035)", + "detailedDesign" : "Rebase wire DTOs onto v3 Zone/ResourceRef identifiers; consume the shared ADR046-provider-004 `security-key.d2bus.org` Service/Binding bases and define only strict `device-security-key.d2bus.org` Provider-extension DTOs; reject `spec.provider` on Core projections; place authority/import/attachment semantic observations only under `status.resource` and implementation observations only under `status.provider`; preserve opaque bounded ceremony records as non-Resource DTOs; add `zone` to `SecurityKeyOpenDeviceRequest`; drop the udev-rules request because UHID comes from the Guest-substrate DeviceGrant. No provider-named ResourceType alias is admitted.", + "integration" : "Core LaunchTicket, broker open op, Provider controller Service/Binding status/audit, CLI session readers, and provider tests consume the v3 DTOs.", + "removalProof" : "ADR046-security-key-035 removes `SecurityKeyApplyUdevRulesRequest`, the `SecurityKeyApplyUdevRules` broker op, and related broker code after UHID DeviceGrant coverage is live.", + "reuseAction" : "adapt", + "reuseSource" : null, + "specId" : "ADR-046-provider-device-security-key", + "specPath" : "docs/specs/providers/ADR-046-provider-device-security-key.md", + "validation" : "DTO serde round trips, exact provider-neutral ResourceType identity, provider-named alias rejection, canonical minimal base acceptance, Core projection `spec.provider` rejection, D088 `status.resource`/`status.provider` layering, base/Provider-extension field separation, unknown-field denial, zone-field round trip, path-redaction tests, and updated `usb_sk_contract.rs` assertions in the provider crate.", + "workItemId" : "ADR046-security-key-005" + }, + { + "currentSource" : "`packages/d2b-contract-tests/tests/usb_sk_contract.rs` — DTO serde round-trips, unknown-field denial, broker capability set (implemented-and-reachable)", + "dataMigration" : "None — test-only move; no runtime state", + "dependencyOwner" : "Provider crate test owner; depends on ADR046-security-key-005 v3 DTOs and ADR046-security-key-008 provider crate layout.", + "destination" : "Move to `packages/d2b-provider-device-security-key/tests/`; update imports and v3 type names", + "detailedDesign" : "Move the reusable semantic assertions for security-key DTO serde, unknown-field denial, and broker capability shape into the provider crate's hermetic `tests/` suite, updating imports and names to the v3 contract modules without weakening assertions. Primary reuse disposition: `adapt`. Preserved source-plan detail: move and adapt.", + "integration" : "`cargo test -p d2b-provider-device-security-key --lib --tests` runs the moved contract tests with the provider's DTO/controller test matrix; old contract-test manifests point to the successor coverage before deletion.", + "removalProof" : "ADR046-security-key-033 deletes `packages/d2b-contract-tests/tests/usb_sk_contract.rs` only after the provider-crate successor test covers all prior assertions.", + "reuseAction" : "adapt", + "reuseSource" : null, + "specId" : "ADR-046-provider-device-security-key", + "specPath" : "docs/specs/providers/ADR-046-provider-device-security-key.md", + "validation" : "Moved tests pass under the provider crate; contract assertions are retained; D094 disposition records moved/adapted coverage before old duplicate tests are deleted.", + "workItemId" : "ADR046-security-key-006" + }, + { + "currentSource" : "`packages/d2b-contract-tests/tests/minijail_sk_frontend.rs` — minijail profile shape, `ProcessRole::SecurityKeyFrontend` (implemented-and-reachable)", + "dataMigration" : "None — test-only move; no runtime state", + "dependencyOwner" : "Provider crate test/minijail adaptation owner; depends on ADR046-security-key-008 and the frozen Process sandbox contract.", + "destination" : "Move to `packages/d2b-provider-device-security-key/tests/`; update for v3 Process resource minijail profile; retain zero-capabilities assertion", + "detailedDesign" : "Move the reusable minijail/sandbox assertions into the provider crate and retarget them from `ProcessRole::SecurityKeyFrontend` to the v3 Process resource templates and relay/controller minijail profiles. Preserve zero-capabilities and seccomp-class assertions while recognizing the frontend uses `Provider/system-systemd` hardening rather than a minijail profile. Primary reuse disposition: `adapt`. Preserved source-plan detail: move and adapt.", + "integration" : "Provider tests validate Nix minijail profile entries, Process resource sandbox templates, and system-minijail/system-systemd conformance expectations before old contract tests are retired.", + "removalProof" : "ADR046-security-key-033 deletes `packages/d2b-contract-tests/tests/minijail_sk_frontend.rs` only after the provider-crate successor test covers all prior assertions.", + "reuseAction" : "adapt", + "reuseSource" : null, + "specId" : "ADR-046-provider-device-security-key", + "specPath" : "docs/specs/providers/ADR-046-provider-device-security-key.md", + "validation" : "Provider-crate tests retain zero-capability and seccomp assertions for relay/controller and assert no minijail profile is used for the frontend Process.", + "workItemId" : "ADR046-security-key-007" + }, + { + "currentSource" : "None — net-new v3 work; no pre-ADR45 baseline equivalent", + "dataMigration" : "Full d2b 3.0 reset; no v2 state/config import", + "dependencyOwner" : "ADR-046 provider-device-security-key crate owner; depends on provider-model/package workspace policy.", + "destination" : "New crate `packages/d2b-provider-device-security-key/` with `src/`, `tests/`, `integration/`, `README.md` (workspace policy requires all four)", + "detailedDesign" : "New crate `packages/d2b-provider-device-security-key/` with `src/`, `tests/`, `integration/`, `README.md` (workspace policy requires all four)", + "integration" : "Workspace/package descriptor expose the crate to Core; ADR046-security-key-009 through ADR046-security-key-029 add controllers, resource contracts, relay/frontend, adapters, tests, and docs.", + "removalProof" : "None — net-new; no prior owner to remove", + "reuseAction" : "create", + "reuseSource" : null, + "specId" : "ADR-046-provider-device-security-key", + "specPath" : "docs/specs/providers/ADR-046-provider-device-security-key.md", + "validation" : "Workspace package-policy check rejects missing `src/`, `tests/`, `integration/`, or `README.md`; `cargo test -p d2b-provider-device-security-key --lib --tests` discovers the hermetic suite; README acceptance criteria from the provider crate standard layout are satisfied.", + "workItemId" : "ADR046-security-key-008" + }, + { + "currentSource" : "None — net-new v3 work; no pre-ADR45 baseline equivalent", + "dataMigration" : "Full d2b 3.0 reset; no v2 state/config import", + "dependencyOwner" : "Provider controller owner; depends on ADR046-security-key-013 probe, ADR046-security-key-016 templates, ADR046-security-key-025 effect port, ADR046-security-key-026 Service/Binding contracts, ADR046-security-key-027 status contract, and ADR-046-resource-reconciliation.", + "destination" : "`packages/d2b-provider-device-security-key/src/controller.rs`", + "detailedDesign" : "One controller implements standard reconcile for local physical Devices, authority/projection SecurityKeyServices, and SecurityKeyBindings. It observes Devices; realizes an authority Service as relay Process/Service-owned Endpoint; accepts projection Services only from Core/import after signed-factory admission; realizes each Binding as frontend Process/private Endpoint; enforces child-first finalizers and never creates an import or Device projection. Export/Import routing never treats an Endpoint as exported identity.", + "integration" : "Watches Device and both provider-neutral semantic types filtered by `providerRef=Provider/device-security-key`, plus Process, Endpoint, Guest/User, ResourceExport/Import, and Service/export/import-owner dependency indexes; writes semantic base plus signed Provider-extension status/finalizers; drives relay-control messages.", + "removalProof" : "None — net-new; no prior owner to remove", + "reuseAction" : "create", + "reuseSource" : null, + "specId" : "ADR-046-provider-device-security-key", + "specPath" : "docs/specs/providers/ADR-046-provider-device-security-key.md", + "validation" : "`controller_reconcile.rs`, `service_binding_projection.rs`, `mutual_exclusion.rs`, `status_binding.rs`, and deletion/finalizer tests cover authority/projection/Binding branches, signed-factory admission before projection reconcile, Service-owned Endpoint isolation from Export/Import identity, no Device projection, and no Volume API calls.", + "workItemId" : "ADR046-security-key-009" + }, + { + "currentSource" : "None — net-new v3 work; no pre-ADR45 baseline equivalent", + "dataMigration" : "Full d2b 3.0 reset; no relay session state import", + "dependencyOwner" : "Authority Service relay owner; depends on ADR046-security-key-001, ADR046-security-key-002, ADR046-security-key-011, ADR046-security-key-012, ADR046-security-key-014, ADR046-security-key-016, and ADR046-security-key-018.", + "destination" : "`packages/d2b-provider-device-security-key/src/relay.rs`", + "detailedDesign" : "Authority Service-owned relay entry point: bounded authenticated Binding connections, one LeaseId-guarded fair ceremony queue, CID translation/cancel-all-CIDs, hidraw fd from Core DeviceGrant, CTAPHID named stream, and internal relay-control channel.", + "integration" : "Core injects the Service's physical DeviceGrant, relay Endpoint, and controller channel; Bindings that reference authority or projection Services connect through same-Zone Service Endpoints; Core releases grant on relay exit.", + "removalProof" : "Supersedes daemon-internal relay behavior removed by ADR046-security-key-030/ADR046-security-key-031 after relay Process tests pass.", + "reuseAction" : "create", + "reuseSource" : null, + "specId" : "ADR-046-provider-device-security-key", + "specPath" : "docs/specs/providers/ADR-046-provider-device-security-key.md", + "validation" : "`host_relay_guest_frontend/`, `fair_queue.rs`, `device_grant_no_path.rs`, `descriptor_validation.rs`, `cancel_propagation.rs`, and `cid_isolation.rs` prove one authority open, multi-Binding fair serialization, fd-only access, LeaseId cancel, and CID isolation.", + "workItemId" : "ADR046-security-key-010" + }, + { + "currentSource" : "None — net-new v3 work; no pre-ADR45 baseline equivalent", + "dataMigration" : "Full d2b 3.0 reset; no session ring import", + "dependencyOwner" : "Relay ceremony-session foundation owner; depends on ADR046-security-key-001 and ADR046-security-key-008.", + "destination" : "`packages/d2b-provider-device-security-key/src/session.rs`", + "detailedDesign" : "`SessionStateMachine` with Idle/Queued/Active/Completed/TimedOut, bounded FIFO queue, monotonic LeaseId stale-release guard, per-Binding session ring, timeout/cancel, and ring eviction. Ceremony rows are non-Resource records; DeviceGrant remains held for relay lifetime.", + "integration" : "ADR046-security-key-010 consumes it; controller receives lifecycle messages; Service/Binding status receives aggregates only; session query/audit consumes bounded non-secret rows.", + "removalProof" : "None — net-new; no prior owner to remove", + "reuseAction" : "create", + "reuseSource" : null, + "specId" : "ADR-046-provider-device-security-key", + "specPath" : "docs/specs/providers/ADR-046-provider-device-security-key.md", + "validation" : "`session_state_machine.rs`, `session_ring.rs`, `fair_queue.rs`, `session_timeout.rs`, and `cancel_propagation.rs` cover queue fairness, eviction, stale LeaseId rejection, timeout, and cancel.", + "workItemId" : "ADR046-security-key-011" + }, + { + "currentSource" : "None — net-new v3 work; no pre-ADR45 baseline equivalent", + "dataMigration" : "Full d2b 3.0 reset; CID maps are transient and not imported", + "dependencyOwner" : "Relay CID-translation foundation owner; depends on ADR046-security-key-001 and ADR046-security-key-008.", + "destination" : "`packages/d2b-provider-device-security-key/src/cid.rs`", + "detailedDesign" : "CID translator: per-active-ceremony u32→u64 host-CID allocation, bimap, cancel-all-active-CIDs, and eviction on ceremony end", + "integration" : "Relay rewrites frontend CTAPHID CIDs before sending to hidraw fd and reverses responses before writing the ComponentSession named stream; session teardown drops the map.", + "removalProof" : "None — net-new; no prior owner to remove", + "reuseAction" : "create", + "reuseSource" : null, + "specId" : "ADR-046-provider-device-security-key", + "specPath" : "docs/specs/providers/ADR-046-provider-device-security-key.md", + "validation" : "`cid_isolation.rs` verifies per-session allocation, round trip, no sharing across relays, and eviction on session end.", + "workItemId" : "ADR046-security-key-012" + }, + { + "currentSource" : "None — net-new v3 work; no pre-ADR45 baseline equivalent", + "dataMigration" : "Full d2b 3.0 reset; no v2 probe state import", + "dependencyOwner" : "Probe/effect-port and activation owner; depends on ADR046-security-key-025 effect port and Core private bundle device table support.", + "destination" : "`packages/d2b-provider-device-security-key/src/probe.rs`; Provider activation/Core private bundle device table population for label → `device_token`", + "detailedDesign" : "hidraw probe: `probe.rs` — calls `SecurityKeyEffectPort::observe_inventory(&device_id, &policy_id)` with opaque types injected by Core; interprets `InventoryObservation`; never reads `/sys/class/hidraw/` directly; bundle device table population at activation time (Provider activation resolves label → `device_token` via Core; stored in private bundle)", + "integration" : "Controller scheduled-observe invokes `probe.rs`; Core adapter implements `SecurityKeyEffectPort`; Nix activation emits private label-to-token bundle entries; Device status receives `DevicePresent` and phase updates.", + "removalProof" : "Supersedes provider-side or broker fallback sysfs scanning; ADR046-security-key-004/ADR046-security-key-018 removal proof verifies only bundle `device_token` lookup remains.", + "reuseAction" : "create", + "reuseSource" : null, + "specId" : "ADR-046-provider-device-security-key", + "specPath" : "docs/specs/providers/ADR-046-provider-device-security-key.md", + "validation" : "`controller_reconcile.rs` scheduled-observe tests, `descriptor_validation.rs` Debug-redaction capture, and path-safety tests prove Provider never reads sysfs and receives only opaque observations.", + "workItemId" : "ADR046-security-key-013" + }, + { + "currentSource" : "None — net-new v3 work; no pre-ADR45 baseline equivalent", + "dataMigration" : "Full d2b 3.0 reset; no v2 transport/session state import", + "dependencyOwner" : "ComponentSession/security descriptor contract owner; depends on ADR046-security-key-008, ADR046-security-key-005, and ADR-046-componentsession-and-bus.", + "destination" : "`packages/d2b-provider-device-security-key/src/descriptor.rs`", + "detailedDesign" : "Declare relay↔controller service and relay↔Binding-frontend `d2b.security-key.v3` fingerprints, Noise profiles, canonical Service/Binding subject pairing, bounded encrypted-stream records, and descriptor validation; no ambient path or raw vsock CID.", + "integration" : "Provider descriptor declares services and fingerprints; LaunchTicket injects internal channel and Endpoint transport; relay/controller/frontend validate descriptors and peer authority before exchanging messages.", + "removalProof" : "None — net-new; no prior owner to remove", + "reuseAction" : "create", + "reuseSource" : null, + "specId" : "ADR-046-provider-device-security-key", + "specPath" : "docs/specs/providers/ADR-046-provider-device-security-key.md", + "validation" : "`descriptor_validation.rs` covers wrong service, wrong descriptor digest, wrong SO_PEERCRED uid, unenrolled key, oversized records, no ambient path, and redacted opaque IDs.", + "workItemId" : "ADR046-security-key-014" + }, + { + "currentSource" : "None — net-new v3 work; no pre-ADR45 baseline equivalent", + "dataMigration" : "Full d2b 3.0 reset; no sandbox state import", + "dependencyOwner" : "Sandbox/minijail foundation owner; depends on ADR046-security-key-008, ADR046-security-key-007, and ADR-046-components-processes-and-sandbox.", + "destination" : "`nixos-modules/minijail-profiles.nix` entries for relay and controller; provider descriptor sandbox templates for relay/controller/frontend", + "detailedDesign" : "Minijail profiles for relay and controller only; frontend uses `Provider/system-systemd` hardening directives compiled from `SandboxSpec` (no minijail profile for frontend). Add relay and controller entries to `nixos-modules/minijail-profiles.nix`; `capabilityClasses: []`; `seccompClass: sk-relay` and `seccompClass: sk-controller`", + "integration" : "Nix minijail profiles feed system-minijail Process launches for controller/relay; frontend Process template feeds system-systemd hardening; provider tests assert the split.", + "removalProof" : "Supersedes `ProcessRole::SecurityKeyFrontend`-centric minijail test ownership removed by ADR046-security-key-033/ADR046-security-key-034 after Process-resource coverage passes.", + "reuseAction" : "create", + "reuseSource" : null, + "specId" : "ADR-046-provider-device-security-key", + "specPath" : "docs/specs/providers/ADR-046-provider-device-security-key.md", + "validation" : "`minijail_sk_frontend` successor tests, sandbox template tests, and zero-capability/seccomp assertions cover relay/controller minijail profiles and no frontend minijail profile.", + "workItemId" : "ADR046-security-key-015" + }, + { + "currentSource" : "None — net-new v3 work; no pre-ADR45 baseline equivalent", + "dataMigration" : "Full d2b 3.0 reset; no v2 processes.json import", + "dependencyOwner" : "Provider process/Endpoint-template owner; depends on ADR046-security-key-008, ADR046-security-key-015, and ADR046-security-key-026 Service/Binding ownership contracts.", + "destination" : "Provider descriptor Process templates and owned CTAPHID `Endpoint` template for `Provider/device-security-key`", + "detailedDesign" : "Templates: Provider controller; authority Service-owned relay Process/relay Endpoint; Binding-owned frontend Process/private Endpoint; projection Service local Endpoint. Frontend requires Guest/User and the system-core UHID DeviceGrant; no virtual Device template exists.", + "integration" : "Core creates controller; Provider controller realizes authority Services and Bindings plus each projection Service's ordinary local import-route Endpoint; Process Providers launch children and preserve ownerRef boundaries.", + "removalProof" : "Supersedes the legacy readiness-only `ProcessRole::SecurityKeyFrontend` tracking node removed by ADR046-security-key-034 after v3 Process resources are live.", + "reuseAction" : "create", + "reuseSource" : null, + "specId" : "ADR-046-provider-device-security-key", + "specPath" : "docs/specs/providers/ADR-046-provider-device-security-key.md", + "validation" : "`controller_reconcile.rs`, Process template golden tests, Endpoint resource tests, and frontend `userRef` admission tests prove templates and Endpoint shape.", + "workItemId" : "ADR046-security-key-016" + }, + { + "currentSource" : "None — net-new v3 work; no pre-ADR45 baseline equivalent", + "dataMigration" : "Full d2b 3.0 reset; no provider descriptor import", + "dependencyOwner" : "Provider package descriptor owner; depends on ADR046-security-key-008, ADR046-security-key-005, ADR046-security-key-014, ADR046-security-key-016, ADR046-security-key-026, ADR046-security-key-027, and ADR-046-provider-model-and-packaging.", + "destination" : "Signed Provider descriptor JSON for `Provider/device-security-key` in the provider package", + "detailedDesign" : "Signed descriptor: config; physical Device integration; implementation claim for the provider-neutral `security-key.d2bus.org` Service/Binding base schemas/fingerprints; strict `device-security-key.d2bus.org` spec/status extensions; authority/projection union and D097 descriptor; a D096 projection factory with exact `serviceType`, `projectionSchemaFingerprint`, and semantic `factoryFingerprint`; controller/relay/frontend/Endpoint templates; export/import adapter capability; ComponentSession services; empty ProviderStateSet; permission claims. Provider/adapter identity is signed separately and Service-owned Endpoints are not factory or Export fields.", + "integration" : "Core ProviderDeployment verifies the signed descriptor, installs ResourceApiBinding and component descriptors, exposes service fingerprints to ComponentSession validation, and supplies permission claims/RBAC bindings.", + "removalProof" : "None — net-new; no prior owner to remove", + "reuseAction" : "create", + "reuseSource" : null, + "specId" : "ADR-046-provider-device-security-key", + "specPath" : "docs/specs/providers/ADR-046-provider-device-security-key.md", + "validation" : "Descriptor schema validation, semantic-base versus Provider-extension fingerprints, exact projection-schema/factory fingerprint derivation and stability under Provider/adapter identity changes, exact type/no-alias tests, service inventory tests, permission claim tests, empty ProviderStateSet tests, and README/provider package conformance checks.", + "workItemId" : "ADR046-security-key-017" + }, + { + "currentSource" : "None — net-new v3 work; no pre-ADR45 baseline equivalent", + "dataMigration" : "Full d2b 3.0 reset; no v2 broker state import", + "dependencyOwner" : "Core LaunchTicket/broker owner; depends on ADR046-security-key-004, ADR046-security-key-005, ADR046-security-key-013, and ADR-046-resources-device.", + "destination" : "v3 `SecurityKeyOpenDevice` broker op and Core LaunchTicket DeviceGrant resolution path", + "detailedDesign" : "v3 `SecurityKeyOpenDevice` broker op update: add `zone` field; implement bundle device table `device_token` lookup as sole open path; remove iterative sysfs scan from broker; add post-open revalidation steps (fstat, HIDIOCGRAWINFO, HIDIOCGRDESC). This is an internal Core operation called by LaunchTicket; the Provider controller does not call it.", + "integration" : "Authority Service controller derives relay `deviceUsage` from `spec.provider.settings.deviceRef`; Core admits authority then resolves DeviceGrant through the private bundle table; broker returns an fd to Core; projection Services never enter this path.", + "removalProof" : "Superseded broker iterative sysfs scan behavior is removed; tests prove only bundle `device_token` lookup is accepted for `SecurityKeyOpenDevice`.", + "reuseAction" : "create", + "reuseSource" : null, + "specId" : "ADR-046-provider-device-security-key", + "specPath" : "docs/specs/providers/ADR-046-provider-device-security-key.md", + "validation" : "Broker unit tests for zone field and token lookup, path-rejection tests, post-open revalidation tests, and provider tests proving no Provider broker call or sysfs path.", + "workItemId" : "ADR046-security-key-018" + }, + { + "currentSource" : "None — net-new v3 work; no pre-ADR45 baseline equivalent", + "dataMigration" : "Full d2b 3.0 reset; current Nix options migrate to v3 Zone resources without state import", + "dependencyOwner" : "Nix resource compiler owner; depends on ADR046-security-key-017, ADR046-security-key-026, ADR046-zone-control-024, and ADR-046-nix-configuration.", + "destination" : "`nixos-modules/` resource compiler/eval assertions for physical Device, authority Service, ResourceExport/Import, and consumer Binding", + "detailedDesign" : "Compile the owner Device→Service→export and consumer import→projection-Service→Binding shape. Emit Export `resourceRef`, `serviceType`, `projectionSchemaFingerprint`, and `factoryFingerprint`, and matching Import `expectedServiceType`, `expectedProjectionSchemaFingerprint`, and `expectedFactoryFingerprint`; the Import `exportKey` identifies the ResourceExport. Reject Export Endpoint/custom-key fields, authored projections, projection `spec.provider`, Device export/projection, cross-Zone refs, duplicate authorities/Bindings, paths, and any security-key/USB configuration that does not collide through the exact Core-derived `(Host, physical-usb-backing, opaqueKeyDigest)` tuple after trusted identity resolution.", + "integration" : "Nix emits Device/authority Service/export/import/Binding with canonical D096 fields; Core alone creates projection Service; the Service controller alone owns relay/import-route Endpoints; bundle feeds Provider and authority-index admission.", + "removalProof" : "Supersedes current option shape only after v3 Zone resource option parity; legacy security-key/USBIP mutual-exclusion assertion is replaced by v3 resource assertion coverage.", + "reuseAction" : "create", + "reuseSource" : null, + "specId" : "ADR-046-provider-device-security-key", + "specPath" : "docs/specs/providers/ADR-046-provider-device-security-key.md", + "validation" : "Nix eval tests for label resolution, `busClass=hidraw`, exclusive arbitration, exact canonical Export/Import field emission and fingerprint matching, rejection of obsolete Export `endpointRef`/`exportedType`/`baseSchemaFingerprint`/`exportKey` and Import `expectedType`/`expectedBaseSchemaFingerprint`/`projectionType`, Core-only projection without `spec.provider`, byte-identical USB/security-key physical backing tuple collision, Provider-private-class bypass rejection, prohibited fields, and providerRef resolution.", + "workItemId" : "ADR046-security-key-019" + }, + { + "currentSource" : "None — net-new v3 work; no pre-ADR45 baseline equivalent", + "dataMigration" : "Full d2b 3.0 reset; no legacy frontend unit state import", + "dependencyOwner" : "Guest Nix migration owner; depends on ADR046-security-key-003, ADR046-security-key-016, and ADR046-security-key-026 Binding-owned frontend contract.", + "destination" : "`nixos-modules/components/security-key-guest.nix` migration gate `d2b.securityKey._legacySystemdUnit`", + "detailedDesign" : "Guest Nix module migration gate: `d2b.securityKey._legacySystemdUnit` option, defaulting to false when Provider is installed; remove `d2b-sk-frontend.service` unit", + "integration" : "Guest Nix keeps `uhid` and the static frontend binary; Binding controller owns frontend lifecycle and system-core supplies the UHID DeviceGrant; no Device row or udev rule is emitted.", + "removalProof" : "ADR046-security-key-032 deletes the superseded `nixos-modules/components/security-key-guest.nix` `d2b-sk-frontend.service` declaration after the gate defaults to false.", + "reuseAction" : "create", + "reuseSource" : null, + "specId" : "ADR-046-provider-device-security-key", + "specPath" : "docs/specs/providers/ADR-046-provider-device-security-key.md", + "validation" : "Nix eval tests show the legacy unit is absent by default with Provider installed, can be gated only during transition if required, and `uhid` module/binary wiring remains present.", + "workItemId" : "ADR046-security-key-020" + }, + { + "currentSource" : "None — net-new v3 work; no pre-ADR45 baseline equivalent", + "dataMigration" : "Full d2b 3.0 reset; no v2 audit import", + "dependencyOwner" : "Audit owner for Core device-grant and Service/Binding lifecycle; depends on ADR046-security-key-009, ADR046-security-key-018, and ADR-046-telemetry-audit-and-support.", + "destination" : "Core `device-grant` audit and Provider controller Service/Binding ceremony lifecycle audit", + "detailedDesign" : "Path-free authority-grant records from Core and bounded Service/Binding/session digests/outcomes from controller; no path, raw target identity, LeaseId, session content, or CTAP bytes.", + "integration" : "Core emits grant audit; controller emits Service/Binding lifecycle audit; Zone stream stores bounded records; CLI/support consumes digests/outcomes.", + "removalProof" : "None — net-new; no prior owner to remove", + "reuseAction" : "create", + "reuseSource" : null, + "specId" : "ADR-046-provider-device-security-key", + "specPath" : "docs/specs/providers/ADR-046-provider-device-security-key.md", + "validation" : "Audit tests assert path-free fields, bounded digests, no guest name/session content/CTAP bytes, grant emitted by Core not Provider controller, and lifecycle emitted by controller.", + "workItemId" : "ADR046-security-key-021" + }, + { + "currentSource" : "None — net-new v3 work; no pre-ADR45 baseline equivalent", + "dataMigration" : "Full d2b 3.0 reset; no v2 telemetry import", + "dependencyOwner" : "Observability owner; depends on ADR046-security-key-010 relay, ADR046-security-key-009 controller, and ADR-046-telemetry-audit-and-support.", + "destination" : "Provider/controller bounded telemetry emitter and observability-otel handoff for security-key metrics", + "detailedDesign" : "OTEL metrics: `d2b_device_sk_session_total`, `d2b_device_sk_ceremony_duration_seconds`, `d2b_device_sk_relay_restarts_total` via bounded emitter ring", + "integration" : "Relay/controller write metric events to the bounded ring; observability-otel Provider drains and exports; dashboards/CLI consume closed labels and bounded histograms.", + "removalProof" : "None — net-new; no prior owner to remove", + "reuseAction" : "create", + "reuseSource" : null, + "specId" : "ADR-046-provider-device-security-key", + "specPath" : "docs/specs/providers/ADR-046-provider-device-security-key.md", + "validation" : "Metrics tests assert closed label sets, no device/session/guest/path labels, bounded ring behavior, and correct session/ceremony/restart counters.", + "workItemId" : "ADR046-security-key-022" + }, + { + "currentSource" : "None — net-new v3 work; no pre-ADR45 baseline equivalent", + "dataMigration" : "None — docs/tooling only; no runtime state", + "dependencyOwner" : "Provider documentation owner; depends on ADR046-security-key-008 through ADR046-security-key-022 and ADR046-security-key-024 through ADR046-security-key-029 for complete behavior.", + "destination" : "`packages/d2b-provider-device-security-key/README.md`", + "detailedDesign" : "README: initial Provider identity, provider-neutral Service/Binding catalog, strict Provider-extension fields, physical Device, owner/export/import/projection/Binding chain, process ownership, RBAC, invariants, status/telemetry, no-alias rule, and commands", + "integration" : "Workspace/package policy and provider crate acceptance use the README as the human entry point; docs link to it for provider-local build/test/integration commands.", + "removalProof" : "None — net-new; no prior owner to remove", + "reuseAction" : "create", + "reuseSource" : null, + "specId" : "ADR-046-provider-device-security-key", + "specPath" : "docs/specs/providers/ADR-046-provider-device-security-key.md", + "validation" : "README presence check from provider crate standard layout; documentation review verifies every listed section and command is present and matches the crate/package behavior.", + "workItemId" : "ADR046-security-key-023" + }, + { + "currentSource" : "None — net-new v3 work; no pre-ADR45 baseline equivalent", + "dataMigration" : "Full d2b 3.0 reset; no v2 transport state import", + "dependencyOwner" : "Endpoint/ComponentSession integration owner; depends on ADR046-security-key-003, ADR046-security-key-010, ADR046-security-key-014, ADR046-security-key-016, ADR046-security-key-026, and ADR-046-componentsession-and-bus.", + "destination" : "Authority/projection Service Endpoint and Binding private Endpoint resolution, including transport-vsock and ZoneLink encrypted streams", + "detailedDesign" : "Resolve each Binding only through its same-Zone Service Endpoint; enroll Noise KK for Service/Binding frontend; authority uses transport-vsock locally, projection uses per-import bounded encrypted stream with credits/backpressure/generation/deadline/cancel.", + "integration" : "Service/Binding-owned Endpoints produce opaque LaunchTicket attachments; the import adapter binds the projection Service's ordinary local import-route Endpoint; no remote Ref, FD, or raw locator is exposed.", + "removalProof" : "Supersedes baseline `vsock.sock_14320` raw port usage; tests prove no `vsockPort` or raw AF_VSOCK framing remains for security-key transport.", + "reuseAction" : "create", + "reuseSource" : null, + "specId" : "ADR-046-provider-device-security-key", + "specPath" : "docs/specs/providers/ADR-046-provider-device-security-key.md", + "validation" : "`host_relay_guest_frontend/` and `descriptor_validation.rs` verify Endpoint resolution, Noise KK enrollment, attachment opacity, and no raw vsock CID/port in status/spec.", + "workItemId" : "ADR046-security-key-024" + }, + { + "currentSource" : "None — net-new v3 work; no pre-ADR45 baseline equivalent", + "dataMigration" : "Full d2b 3.0 reset; no v2 effect-port state import", + "dependencyOwner" : "`d2b-contracts` neutral effect-port foundation owner; depends on ADR046-security-key-008 and ADR-046-resources-device.", + "destination" : "`d2b-contracts` neutral `SecurityKeyEffectPort` trait/types; `packages/d2b-provider-device-security-key/src/effect_port.rs` re-export; Core adapter implementation in `d2b-provider` or `d2b-provider-toolkit`", + "detailedDesign" : "Define/re-export the opaque redacting `SecurityKeyEffectPort` types in the neutral contract crate and implement the Core adapter; inject per physical Device into the Provider controller; relay and projection Service do not receive the port.", + "integration" : "Core resolves Zone/label to opaque IDs and injects the port into the controller; controller scheduled-observe calls the trait; Provider crate depends only on the neutral contract/re-export; relay path is unaffected.", + "removalProof" : "None — net-new; no prior owner to remove", + "reuseAction" : "create", + "reuseSource" : null, + "specId" : "ADR-046-provider-device-security-key", + "specPath" : "docs/specs/providers/ADR-046-provider-device-security-key.md", + "validation" : "Unit tests assert Debug redaction, controller calls `observe_inventory` with injected IDs, relay has no port dependency, and fake Core adapter returns bounded `InventoryObservation`.", + "workItemId" : "ADR046-security-key-025" + }, + { + "currentSource" : "None — net-new v3 work; no pre-ADR45 baseline equivalent", + "dataMigration" : "Full d2b 3.0 reset; no legacy Device/claim projection import", + "dependencyOwner" : "Device-security-key Service/Binding implementation owner; depends on ADR046-provider-004, ADR046-security-key-008, ADR046-security-key-005, resource object/Device/D096/D097 contracts.", + "destination" : "`packages/d2b-provider-device-security-key/src/{resource_type,provider_extension,admission}.rs`; controller contracts; system-core Guest UHID authority-subresource DeviceGrant (common base lives under ADR046-provider-004)", + "detailedDesign" : "Bind the shared semantic authority/projection Service and Binding base versions/fingerprints from ADR046-provider-004, then define only the initial strict Provider extension and admission. The owner/Binding extension references the local physical Device/relay Endpoint and owns CTAPHID/fairness/frontend settings and observations. Projection is Core-owned by ResourceImport with `providerRef` plus semantic base/import fields, no `spec.provider`, and no Device/open; routing derives from the signed local descriptor, `providerRef`, and import record. Export admission binds the authority Service's `resourceRef` and `serviceType` to the signed projection-schema and factory fingerprints, never to its Endpoint. Binding is operator intent and the initial extension realizes its frontend Process/private Endpoint. Standard Device remains physical only; provider-named ResourceType aliases are rejected.", + "integration" : "ResourceExport targets Service with canonical type/fingerprint fields; ResourceImport supplies matching expected fields and creates projection Service; Binding references same-Zone Service; Service controllers retain Endpoint ownership; Core injects Guest UHID without a Device row.", + "removalProof" : "Supersedes legacy frontend/import Device modeling; ADR046-security-key-035 removes udev mutation and ADR046-security-key-032 removes the legacy unit once Binding-owned realization is live.", + "reuseAction" : "create", + "reuseSource" : null, + "specId" : "ADR-046-provider-device-security-key", + "specPath" : "docs/specs/providers/ADR-046-provider-device-security-key.md", + "validation" : "Fast schema/lifecycle conformance consumes the ADR046-provider-004 fixtures, accepts canonical minimal base without `spec.provider`, includes a fake alternate security-key Provider, and proves Device→provider-neutral Service→export→import→projection Service→provider-neutral Binding→frontend, exact canonical Export/Import fields, no Endpoint export, projection `spec.provider` rejection, D088 status layering, strict base/Provider-extension separation, exact types with no aliases, strict ownership/finalizers, no Device projection, and no local hidraw open in consumer Zone.", + "workItemId" : "ADR046-security-key-026" + }, + { + "currentSource" : "None — net-new v3 work; no pre-ADR45 baseline equivalent", + "dataMigration" : "Full d2b 3.0 reset; no v2 state/config import", + "dependencyOwner" : "Provider state/status contract owner; depends on ADR046-security-key-008, ADR046-security-key-026, and ADR-046-provider-state.", + "destination" : "Provider descriptor state declaration, controller/status logic, Process templates, and Nix principal provisioning for `Provider/device-security-key`", + "detailedDesign" : "Empty ProviderStateSet and strict bounded status schemas: physical presence in Device `status.resource`; semantic authority/import aggregates in Service `status.resource`; attachment aggregates in Binding `status.resource`; initial physical-backing claim, relay, Endpoint, queue, and ceremony observations only in `status.provider`. No semantic field appears directly under `status`, and Core projections contain no `spec.provider`. Ceremony rows remain high-churn non-Resource session records; CTAP/fd/LeaseId/CID data stays transient. No Process has `/state`.", + "integration" : "ADR046-security-key-017 signs schemas; ADR046-security-key-009 writes resource-local status; Core Operation/session/audit surfaces own bounded records; Volume controllers see no request.", + "removalProof" : "None — net-new; no prior owner to remove", + "reuseAction" : "create", + "reuseSource" : null, + "specId" : "ADR-046-provider-device-security-key", + "specPath" : "docs/specs/providers/ADR-046-provider-device-security-key.md", + "validation" : "`status_binding.rs` proves empty ProviderStateSet, no `/state` mounts, no Volume API calls, authority/import/attachment fields only under `status.resource`, implementation fields only under `status.provider`, no projection `spec.provider`, and no CTAP/fd/session secrets in status/log/audit/metrics.", + "workItemId" : "ADR046-security-key-027" + }, + { + "currentSource" : "None — net-new ADR 0046 cross-Zone sharing (D096)", + "dataMigration" : "Full d2b 3.0 reset; no cross-Zone sharing state", + "dependencyOwner" : "Cross-Zone adapter owner; depends on ADR046-security-key-024, ADR046-security-key-026, ADR046-security-key-029, ADR046-zone-control-019, and ADR046-zone-control-020.", + "destination" : "`packages/d2b-provider-device-security-key/src/share_adapter.rs`", + "detailedDesign" : "Signed adapters admit ResourceExport only when `resourceRef` names an authority SecurityKeyService, `serviceType` is `security-key.d2bus.org.SecurityKeyService`, and `projectionSchemaFingerprint` plus `factoryFingerprint` match the signed semantic factory. ResourceImport must supply the corresponding `expectedServiceType`, `expectedProjectionSchemaFingerprint`, and `expectedFactoryFingerprint`; its `exportKey` identifies the ResourceExport. The Service's relay Endpoint stays a Service-owned implementation child and is never an Export field. Core invokes the factory to create one projection SecurityKeyService with `ownerRef: ResourceImport/`, `providerRef`, semantic base/import fields, and no `spec.provider`; route selection comes from the signed local descriptor and ResourceImport record. The semantic factory fingerprint binds factory metadata plus projection-protocol version only, while adapter identity is authenticated separately by the signed Provider descriptor. They never project Device or auto-create Binding. Route Binding ceremonies over bounded encrypted named streams to the single authority fair queue; no FD/USBIP/hidraw/ref crosses Zones. Primary reuse disposition: `adapt`. Preserved source-plan detail: net-new (implement the signed security-key export/import adapter).", + "integration" : "Core export/import routing/projection lifecycle; ADR046-security-key-029 authority; ADR046-security-key-024 Endpoint streams; Nix/operator-authored Binding consumes the same-Zone projection.", + "removalProof" : "Not applicable (new surface)", + "reuseAction" : "adapt", + "reuseSource" : null, + "specId" : "ADR-046-provider-device-security-key", + "specPath" : "docs/specs/providers/ADR-046-provider-device-security-key.md", + "validation" : "Fast fake-stream conformance proves owner Service→export→import→projection Service→Binding→frontend; exact canonical Export/Import type and fingerprint fields; rejection of Export `endpointRef`, `exportedType`, `baseSchemaFingerprint`, and `exportKey` plus Import `expectedType`, `expectedBaseSchemaFingerprint`, and `projectionType`; rejection of projection `spec.provider`; semantic factory-fingerprint stability when signed adapter identity changes; separate signed-descriptor identity authentication; one fair LeaseId-guarded ceremony; ciphertext to intermediaries; no Device projection/local hidraw/FD/USBIP; revocation degradation; and audit metadata only.", + "workItemId" : "ADR046-security-key-028" + }, + { + "currentSource" : "`packages/d2bd/src/security_key.rs` (`CidTranslator`, `SecurityKeyState`, `LeaseId`/`LeaseState`, `CEREMONY_TIMEOUT` 120 s, `QUEUE_WAIT_TIMEOUT` 15 s, `parse_ctaphid_report`/`build_cancel_packet`); `packages/d2b-priv-broker/src/ops/security_key.rs` (`live_open_hidraw_security_key`, double `fstat` + FIDO usage-page 0xF1D0 + HID raw-info revalidation, `O_RDWR\\|O_NONBLOCK\\|O_NOFOLLOW`); `packages/d2b-sk-frontend/src/{main,uhid,vsock,framing}.rs` (UHID FIDO2 CTAPHID frontend, 64-byte report relay)", + "dataMigration" : "Full d2b 3.0 reset; no per-session/lease state persisted", + "dependencyOwner" : "D097 authority foundation owner; depends on ADR046-security-key-001, ADR046-security-key-002, ADR046-security-key-003, ADR046-security-key-004, ADR046-security-key-018, ADR046-security-key-026, ADR046-zone-control-024, and the D097 authority contract.", + "destination" : "`packages/d2b-provider-device-security-key/src/{authority,relay,streams}.rs`; D097 `AuthorityDescriptor` on authority SecurityKeyService", + "detailedDesign" : "The provider-neutral authority Service, not Device/Endpoint/Process, is the stable D097 owner and carries the semantic opaque Host-scoped zero-or-one descriptor. The initial Provider extension references the local physical Device and relay Endpoint and supplies service-specific physical-key derivation, Service+relay ownerProof, and bounded-fairness details. After trusted USB identity resolution, Core additionally derives `physical-usb-backing/v1` and atomically claims the exact `(Host, physical-usb-backing, opaqueKeyDigest)` tuple used by every USB Provider before any open, withhold, bind, module, relay, or attachment effect; Provider-private claims cannot replace it. Preserve sole Core open with double-fstat/FIDO/HID validation, async fd I/O, per-session CidTranslator, LeaseId stale-release guard, cancel-all-CIDs, one ceremony, bounded FIFO wait, and Binding-owned UHID frontend. Ceremony rows are not Resources. Primary reuse disposition: `adapt`. Preserved source-plan detail: `adapt` — relay becomes the D097 hidraw authority; transport moves to Endpoint/named-stream.", + "integration" : "Authority Service owns relay/Endpoint; Core index admits it and LaunchTicket supplies physical DeviceGrant; ADR046-security-key-028 exports/imports Service; Binding owns frontend/private Endpoint; USBIP conflict remains Host-wide.", + "removalProof" : "The legacy daemon accept loop, raw CTAPHID framing, fixed `SK_VSOCK_PORT`, and broker sysfs `/sys/class/hidraw/` scan fallback are deleted only after the relay `Endpoint`/named-stream successor and the `device_token`-only broker open are green (coordinated with ADR046-security-key-034 `ProcessRole` removal and ADR046-security-key-004 broker-op revalidation).", + "reuseAction" : "adapt", + "reuseSource" : "Same baseline daemon/broker/frontend symbols", + "specId" : "ADR-046-provider-device-security-key", + "specPath" : "docs/specs/providers/ADR-046-provider-device-security-key.md", + "validation" : "Fast hermetic tests adapt the existing `CidTranslator`/lease/cancel/UHID/broker-revalidation suites: CID alloc/translate/release, `LeaseId` stale-release, cancel-all-CIDs on disconnect, one-ceremony + 120 s timeout, 15 s fair-wait `ERR_CHANNEL_BUSY`, UHID frame round-trip, broker double-`fstat`+FIDO+HID revalidation, byte-identical USB/security-key backing tuple derivation for one fake token, and `physical-usb-backing-conflict` before effects under alternate labels/private authority classes — all with fakes/`FakeEffectPort`, no real hidraw. Integration proves cross-Zone CTAP ceremony **serialization** over the encrypted named stream and the shared physical USB collision.", + "workItemId" : "ADR046-security-key-029" + }, + { + "currentSource" : "`packages/d2bd/src/security_key.rs` — `start_sk_accept_loop`, `SecurityKeyState`, `LeaseState`, `SkRegistry`", + "dataMigration" : "Full d2b 3.0 reset; no daemon session state migration", + "dependencyOwner" : "Provider-device-security-key removal owner; depends on ADR046-security-key-001, ADR046-security-key-002, ADR046-security-key-010, ADR046-security-key-011, and ADR046-security-key-012 successor relay/session coverage.", + "destination" : "Removed from daemon; successor behavior lives in `packages/d2b-provider-device-security-key/src/relay.rs`, `session.rs`, and `cid.rs`", + "detailedDesign" : "Remove target `packages/d2bd/src/security_key.rs` — `start_sk_accept_loop`, `SecurityKeyState`, `LeaseState`, `SkRegistry` after v3 relay Process is live and stable; keep behind feature gate only if needed during transition. Primary reuse disposition: `delete-after-cutover`. Preserved source-plan detail: delete.", + "integration" : "d2bd no longer owns security-key accept/session state; Provider controller, authority Service relay, and Binding frontends own lifecycle; Core LaunchTicket owns hidraw/UHID grants.", + "removalProof" : "Concrete removed path/behavior: `packages/d2bd/src/security_key.rs` `start_sk_accept_loop`, `SecurityKeyState`, `LeaseState`, and `SkRegistry` daemon-internal accept/session ownership are absent.", + "reuseAction" : "delete-after-cutover", + "reuseSource" : null, + "specId" : "ADR-046-provider-device-security-key", + "specPath" : "docs/specs/providers/ADR-046-provider-device-security-key.md", + "validation" : "Provider relay/session tests pass; daemon build has no references to removed symbols; no legacy security-key accept loop starts under d2bd.", + "workItemId" : "ADR046-security-key-030" + }, + { + "currentSource" : "`packages/d2bd/src/lib.rs` — `start_sk_accept_loop` call site and daemon-internal Unix socket proxy bind", + "dataMigration" : "Full d2b 3.0 reset; no daemon socket state migration", + "dependencyOwner" : "d2bd integration removal owner; depends on ADR046-security-key-030.", + "destination" : "Removed from daemon startup; successor launch path is ProviderDeployment/controller-created relay Process plus Endpoint/ComponentSession transport", + "detailedDesign" : "Remove target `packages/d2bd/src/lib.rs` — `start_sk_accept_loop` call site and daemon-internal Unix socket proxy bind after ADR046-security-key-030. Primary reuse disposition: `delete-after-cutover`. Preserved source-plan detail: delete.", + "integration" : "d2bd startup no longer binds a security-key Unix socket proxy; Core/ProviderDeployment starts provider controller and relay Process resources; transport-vsock Endpoint supplies frontend connectivity.", + "removalProof" : "Concrete removed path/behavior: `packages/d2bd/src/lib.rs` no longer calls `start_sk_accept_loop` and no longer binds the daemon-internal security-key Unix socket proxy.", + "reuseAction" : "delete-after-cutover", + "reuseSource" : null, + "specId" : "ADR-046-provider-device-security-key", + "specPath" : "docs/specs/providers/ADR-046-provider-device-security-key.md", + "validation" : "d2bd startup tests/build prove no `start_sk_accept_loop` call or security-key proxy bind remains; provider integration test proves CTAPHID flow through Endpoint/ComponentSession.", + "workItemId" : "ADR046-security-key-031" + }, + { + "currentSource" : "`nixos-modules/components/security-key-guest.nix` — `d2b-sk-frontend.service` systemd unit declaration", + "dataMigration" : "Full d2b 3.0 reset; no legacy unit state migration", + "dependencyOwner" : "Guest Nix module removal owner; depends on ADR046-security-key-020, ADR046-security-key-003, and ADR046-security-key-026 Binding frontend/UHID contract.", + "destination" : "Removed from guest Nix module; successor is Binding-owned `Process/binding--sk-frontend`", + "detailedDesign" : "Remove target `nixos-modules/components/security-key-guest.nix` — `d2b-sk-frontend.service` systemd unit declaration after ADR046-security-key-020 migration gate defaults to false. Primary reuse disposition: `delete-after-cutover`. Preserved source-plan detail: delete.", + "integration" : "Guest Nix keeps `uhid` and frontend binary only; Provider controller creates the Binding-owned frontend; system-systemd manages it.", + "removalProof" : "Concrete removed path/behavior: `nixos-modules/components/security-key-guest.nix` no longer declares the static `d2b-sk-frontend.service` unit.", + "reuseAction" : "delete-after-cutover", + "reuseSource" : null, + "specId" : "ADR-046-provider-device-security-key", + "specPath" : "docs/specs/providers/ADR-046-provider-device-security-key.md", + "validation" : "Nix eval tests prove no static `d2b-sk-frontend.service` is emitted with Provider installed; frontend Process integration proves replacement lifecycle.", + "workItemId" : "ADR046-security-key-032" + }, + { + "currentSource" : "`packages/d2b-contract-tests/tests/minijail_sk_frontend.rs` and `packages/d2b-contract-tests/tests/usb_sk_contract.rs`", + "dataMigration" : "None — test-only move/delete; no runtime state", + "dependencyOwner" : "Test-suite migration/removal owner; depends on ADR046-security-key-006 and ADR046-security-key-007 provider-crate successor tests.", + "destination" : "Removed from `packages/d2b-contract-tests/tests/`; successor tests live in `packages/d2b-provider-device-security-key/tests/`", + "detailedDesign" : "Remove target `packages/d2b-contract-tests/tests/minijail_sk_frontend.rs` and `packages/d2b-contract-tests/tests/usb_sk_contract.rs` after ADR046-security-key-006/ADR046-security-key-007 tests are in Provider crate and cover all prior assertions. Primary reuse disposition: `delete-after-cutover`. Preserved source-plan detail: delete after move/adapt.", + "integration" : "D094 disposition updates closed gate manifests, layer1 jobs, pins, ledgers, and CI shards so only the provider-crate successor suite remains.", + "removalProof" : "Concrete removed paths: `packages/d2b-contract-tests/tests/minijail_sk_frontend.rs` and `packages/d2b-contract-tests/tests/usb_sk_contract.rs` are deleted after provider-crate successor coverage passes.", + "reuseAction" : "delete-after-cutover", + "reuseSource" : null, + "specId" : "ADR-046-provider-device-security-key", + "specPath" : "docs/specs/providers/ADR-046-provider-device-security-key.md", + "validation" : "Provider-crate tests pass with retained assertions; old contract-test paths are absent from manifests/CI; no duplicate old/new suite runs indefinitely.", + "workItemId" : "ADR046-security-key-033" + }, + { + "currentSource" : "`ProcessRole::SecurityKeyFrontend` in `d2b-core/src/processes.rs`", + "dataMigration" : "Full d2b 3.0 reset; no processes.json role migration", + "dependencyOwner" : "Core ProcessRole removal owner; depends on ADR046-security-key-016 Process resources, ADR046-security-key-015 sandbox templates, and system-minijail/system-systemd conformance.", + "destination" : "Removed from `d2b-core/src/processes.rs`; successor frontend is a v3 Process resource owned by `Provider/device-security-key`", + "detailedDesign" : "Remove target `ProcessRole::SecurityKeyFrontend` in `d2b-core/src/processes.rs` after relay and frontend are v3 Process resources; no other code reference expected. Primary reuse disposition: `delete-after-cutover`. Preserved source-plan detail: delete.", + "integration" : "ProcessRole disposition table confirms all security-key frontend lifecycle, sandbox, readiness, and DeviceGrant semantics are represented by Resource Process templates and Process Providers before enum removal.", + "removalProof" : "Concrete removed path/behavior: `d2b-core/src/processes.rs` no longer contains `ProcessRole::SecurityKeyFrontend` or a security-key frontend role in the legacy ProcessRole/VmProcessDag model.", + "reuseAction" : "delete-after-cutover", + "reuseSource" : null, + "specId" : "ADR-046-provider-device-security-key", + "specPath" : "docs/specs/providers/ADR-046-provider-device-security-key.md", + "validation" : "Workspace build proves no `ProcessRole::SecurityKeyFrontend` references; provider Process template tests prove the v3 replacement; process conformance passes.", + "workItemId" : "ADR046-security-key-034" + }, + { + "currentSource" : "`SecurityKeyApplyUdevRules` broker op, `SecurityKeyApplyUdevRulesRequest` DTO in `packages/d2b-contracts/src/security_key.rs`, and all related broker code", + "dataMigration" : "Full d2b 3.0 reset; no udev rule state migration", + "dependencyOwner" : "Broker/contracts/Nix removal owner; depends on ADR046-security-key-005, ADR046-security-key-018, ADR046-security-key-020, and ADR046-security-key-026 Guest-substrate UHID replacement.", + "destination" : "Removed from contracts and broker; successor access is static guest Nix `uhid` module plus Core pre-opened `/dev/uhid` DeviceGrant for the frontend Process", + "detailedDesign" : "Remove `SecurityKeyApplyUdevRules`, its DTO, and related broker code after the Binding-owned frontend and system-core UHID DeviceGrant are live. Guest Nix loads `uhid` but emits no security-key udev rule. Primary reuse disposition: `delete-after-cutover`. Preserved source-plan detail: delete.", + "integration" : "Guest Nix/Process DeviceGrant path provides UHID access; contracts no longer expose the op/request; broker capability set drops the udev mutation; provider/contract tests assert absence.", + "removalProof" : "Concrete removed path/behavior: `SecurityKeyApplyUdevRules` broker operation, `SecurityKeyApplyUdevRulesRequest` in `packages/d2b-contracts/src/security_key.rs`, and related broker code are absent.", + "reuseAction" : "delete-after-cutover", + "reuseSource" : null, + "specId" : "ADR-046-provider-device-security-key", + "specPath" : "docs/specs/providers/ADR-046-provider-device-security-key.md", + "validation" : "DTO unknown-field/capability tests prove `SecurityKeyApplyUdevRulesRequest` and op are absent; `device_grant_no_path.rs` proves frontend has UHID fd without udev/plugdev; broker build has no related code.", + "workItemId" : "ADR046-security-key-035" + }, + { + "currentSource" : "v3 `d2b-realm-router/src/{session,secure_session,mux_session,lifecycle}.rs`, guest auth/transport", + "dataMigration" : "No v2 session compatibility; reconnect on v3", + "dependencyOwner" : "W0 shared contract root", + "destination" : "`packages/d2b-contracts/src/v3/component_session.rs`, `packages/d2b-session/`", + "detailedDesign" : "Reversion canonical offer/records; retain NN/KK/IKpsk2; add canonical subject/authorization context hooks Primary reuse disposition: `adapt`. Preserved source-plan detail: copy and adapt.", + "integration" : "d2b-bus, resource/controller/Provider services", + "removalProof" : "v3 old Realm PeerSession removed only after all v3 peer routes move", + "reuseAction" : "adapt", + "reuseSource" : "main `a1cc0b2d`: `d2b-contracts/src/v2_component_session.rs`, `d2b-session/src/{handshake,bootstrap,record,engine,scheduler,streams,lifecycle,transport}.rs`, Noise/component tests/vectors", + "specId" : "ADR-046-componentsession-and-bus", + "specPath" : "docs/specs/ADR-046-componentsession-and-bus.md", + "validation" : "Copied exact vectors/rejections plus subject/RBAC/revocation tests", + "workItemId" : "ADR046-session-001" + }, + { + "currentSource" : "v3 guest vsock/local seqpacket implementations and d2b-realm-transport traits", + "dataMigration" : "None — full d2b 3.0 reset; no prior state to migrate", + "dependencyOwner" : "ADR046-session-001; transport owner", + "destination" : "`packages/d2b-session-unix/`, future enrolled transport adapter crates", + "detailedDesign" : "Unix/socketpair/vsock owned transports, peer evidence, fd/pidfd/object validation, credits Primary reuse disposition: `adapt`. Preserved source-plan detail: copy and adapt.", + "integration" : "ProviderSupervisor/Host/Guest/Zone listeners hand owned transports to session", + "removalProof" : "Ad hoc guest/public/helper transport removed only per service cutover", + "reuseAction" : "adapt", + "reuseSource" : "main `a1cc0b2d`: `d2b-session-unix/src/{adapter,socket,descriptor,pidfd,vsock,systemd,credit}.rs`, `tests/unix_session.rs`", + "specId" : "ADR-046-componentsession-and-bus", + "specPath" : "docs/specs/ADR-046-componentsession-and-bus.md", + "validation" : "Copied fd/peer/credit tests plus Host/Guest subject mapping", + "workItemId" : "ADR046-session-002" + }, + { + "currentSource" : "v3 `d2b-realm-router`, target resolver, CLI routing, operation router", + "dataMigration" : "None — full d2b 3.0 reset; no prior state to migrate", + "dependencyOwner" : "Sessions + resource API; bus owner", + "destination" : "`packages/d2b-bus/src/{router,registry,authorization,streams,operations}.rs`", + "detailedDesign" : "Exact service/resource routes, RBAC, pinned reverse route, cancellation, named stream bridge, no wildcard pub/sub Primary reuse disposition: `adapt`. Preserved source-plan detail: extract/adapt.", + "integration" : "Every ResourceClient/controller/Provider/CLI service", + "removalProof" : "Old direct dispatch branches removed only after route parity", + "reuseAction" : "adapt", + "reuseSource" : "Any useful main d2b-client/provider/session routing symbols named by implementation sub-items", + "specId" : "ADR-046-componentsession-and-bus", + "specPath" : "docs/specs/ADR-046-componentsession-and-bus.md", + "validation" : "Message isolation, route/auth revocation, fairness, reconnect, no direct-store path", + "workItemId" : "ADR046-session-003" + }, + { + "currentSource" : "None — net-new v3 qualified `ShellPool` and `ShellSession` resource schemas; superseded draft and legacy shell code do not define these canonical resources.", + "dataMigration" : "Full d2b 3.0 reset; no v2 state/config import.", + "dependencyOwner" : "Resource schemas area; owned by `d2b-provider-shell-terminal` resource modules.", + "destination" : "`packages/d2b-provider-shell-terminal/src/resources/{pool,session}.rs`", + "detailedDesign" : "Implement `shell-terminal.d2bus.org.ShellPool` and `shell-terminal.d2bus.org.ShellSession` schemas with qualified names, common phases, and typed detail fields.", + "integration" : "Nix resource compiler, resource API admission, controller reconcile, status writers, and d2b-bus routing all consume the qualified pool/session schemas. Integration path: `packages/d2b-provider-shell-terminal/integration/resource-shape/`.", + "removalProof" : "None — net-new; no prior owner to remove.", + "reuseAction" : "create", + "reuseSource" : null, + "specId" : "ADR-046-provider-shell-terminal", + "specPath" : "docs/specs/providers/ADR-046-provider-shell-terminal.md", + "validation" : "`packages/d2b-provider-shell-terminal/tests/resource_schema.rs`", + "workItemId" : "ADR046-sterm-001" + }, + { + "currentSource" : "None — net-new v3 controller; legacy guestd and unsafe-local helper shell paths are not the controller/state authority.", + "dataMigration" : "Full d2b 3.0 reset; no v2 state/config import.", + "dependencyOwner" : "Controller binary area; owned by `d2b-provider-shell-terminal` controller and core Operation ledger integration.", + "destination" : "`packages/d2b-provider-shell-terminal/src/bin/d2b-shell-terminal-controller.rs`", + "detailedDesign" : "Implement `d2b-shell-terminal-controller` with pool/session reconcile loops; assert ProviderStateSet is empty; publish bounded non-secret operational state to resource status and the core Operation ledger; no controller Provider state Volume or `/state` mount exists. Primary reuse disposition: `create`. Preserved source-plan detail: net-new controller; preserve status-first ProviderStateSet-empty rule.", + "integration" : "Core ProviderDeployment starts the controller Process; controller reconciles ShellPool/ShellSession resources, writes status, registers routes, and records operations without a Provider state Volume. Integration path: `packages/d2b-provider-shell-terminal/integration/controller-restart/`.", + "removalProof" : "None — net-new controller; legacy controller-equivalent state owner does not exist.", + "reuseAction" : "create", + "reuseSource" : null, + "specId" : "ADR-046-provider-shell-terminal", + "specPath" : "docs/specs/providers/ADR-046-provider-shell-terminal.md", + "validation" : "`packages/d2b-provider-shell-terminal/tests/controller_reconcile.rs`", + "workItemId" : "ADR046-sterm-002" + }, + { + "currentSource" : "Reuse narrow ring/runtime ideas from `packages/d2b-guestd/src/shell.rs` and adoption-shape ideas from `packages/d2b-unsafe-local-helper/src/runtime.rs`; both legacy authorities are superseded.", + "dataMigration" : "Full d2b 3.0 reset; no v2 shell state import; PTY/ring state is live process memory only.", + "dependencyOwner" : "Supervisor binary area; owned by `d2b-provider-shell-terminal` session supervisor runtime.", + "destination" : "`packages/d2b-provider-shell-terminal/src/bin/d2b-shell-session-supervisor.rs`", + "detailedDesign" : "Implement `d2b-shell-session-supervisor` as the sole PTY owner for Host and Guest pools. Primary reuse disposition: `adapt`. Preserved source-plan detail: reuse narrow mechanics only; move PTY authority into per-session supervisor and exclude legacy protocols/identities/state storage.", + "integration" : "Controller creates one user-domain supervisor Process per ShellSession; supervisor owns PTY, login shell, ring, attach bookkeeping, and private ComponentSession service. Integration path: `packages/d2b-provider-shell-terminal/integration/supervisor-host-guest/`.", + "removalProof" : "Supersedes `guestd/src/shell.rs` managed runtime and unsafe-local helper shell supervisor; removed once successor supervisor coverage passes.", + "reuseAction" : "adapt", + "reuseSource" : "`packages/d2b-guestd/src/shell.rs`; `packages/d2b-unsafe-local-helper/src/runtime.rs`.", + "specId" : "ADR-046-provider-shell-terminal", + "specPath" : "docs/specs/providers/ADR-046-provider-shell-terminal.md", + "validation" : "`packages/d2b-provider-shell-terminal/tests/supervisor_runtime.rs`", + "workItemId" : "ADR046-sterm-003" + }, + { + "currentSource" : "Superseded draft templates included pool-wide/system-domain or management-worker concepts; canonical v3 templates are defined in this spec.", + "dataMigration" : "Full d2b 3.0 reset; no v2 state/config import.", + "dependencyOwner" : "Process templates area; owned by Nix compiler plus shell-terminal controller.", + "destination" : "`packages/d2b-provider-shell-terminal/src/process_templates.rs`", + "detailedDesign" : "Teach the Nix compiler and controller to emit the canonical controller and user-domain supervisor `Process` templates. Primary reuse disposition: `replace`. Preserved source-plan detail: replace incorrect draft templates with canonical controller and user-domain supervisor Process templates.", + "integration" : "Nix compiler emits controller Process/Endpoint resources; controller emits per-session user-domain supervisor Processes; Provider/system-systemd realizes them. Integration path: `packages/d2b-provider-shell-terminal/integration/process-placement/`.", + "removalProof" : "Removes forbidden pool-wide supervisor, disguised system-domain supervisor, management EphemeralProcess, and sealed output Volume concepts from the template surface.", + "reuseAction" : "replace", + "reuseSource" : null, + "specId" : "ADR-046-provider-shell-terminal", + "specPath" : "docs/specs/providers/ADR-046-provider-shell-terminal.md", + "validation" : "`packages/d2b-provider-shell-terminal/tests/process_templates.rs`", + "workItemId" : "ADR046-sterm-004" + }, + { + "currentSource" : "None — net-new v3 `OpenSession` lifecycle; legacy shell protocols do not create ShellSession resources with inherited-field freeze.", + "dataMigration" : "Full d2b 3.0 reset; no v2 state/config import.", + "dependencyOwner" : "OpenSession lifecycle area; owned by controller service implementation.", + "destination" : "`packages/d2b-provider-shell-terminal/src/service/open_session.rs`", + "detailedDesign" : "Create sessions from pools, freeze inherited fields, and return `supervisorGeneration` to callers.", + "integration" : "`shell-terminal.v3.OpenSession` validates pool capacity and policy, creates ShellSession and supervisor Process, registers route data, and returns session/supervisor references to clients. Integration path: `packages/d2b-provider-shell-terminal/integration/open-session/`.", + "removalProof" : "None — net-new resource lifecycle; no prior owner to remove.", + "reuseAction" : "create", + "reuseSource" : null, + "specId" : "ADR-046-provider-shell-terminal", + "specPath" : "docs/specs/providers/ADR-046-provider-shell-terminal.md", + "validation" : "`packages/d2b-provider-shell-terminal/tests/open_session.rs`", + "workItemId" : "ADR046-sterm-005" + }, + { + "currentSource" : "Ring buffer mechanics may reuse ideas from `packages/d2b-guestd/src/shell.rs`; management workers and sealed output Volumes from prior draft are removed.", + "dataMigration" : "Full d2b 3.0 reset; no terminal byte or ring-state import.", + "dependencyOwner" : "PTY and ring area; owned by per-session supervisor runtime.", + "destination" : "`packages/d2b-provider-shell-terminal/src/session/{pty,ring}.rs`", + "detailedDesign" : "Implement the in-memory PTY owner model, bounded ring buffer, replay, and eviction counters; do not create any management worker or `EphemeralProcess`. Primary reuse disposition: `adapt`. Preserved source-plan detail: reuse ring semantics; keep bytes in supervisor memory and remove management worker/EphemeralProcess model.", + "integration" : "Supervisor named terminal stream replays bounded ring tail then streams live PTY I/O; status publishes only ring byte counters and attach count. Integration path: `packages/d2b-provider-shell-terminal/integration/ring-overflow/`.", + "removalProof" : "Proves no controller-owned PTY, management worker, EphemeralProcess, or sealed output Volume remains for shell output/management responses.", + "reuseAction" : "adapt", + "reuseSource" : "`packages/d2b-guestd/src/shell.rs` bounded ring ideas only.", + "specId" : "ADR-046-provider-shell-terminal", + "specPath" : "docs/specs/providers/ADR-046-provider-shell-terminal.md", + "validation" : "`packages/d2b-provider-shell-terminal/tests/ring_buffer.rs`", + "workItemId" : "ADR046-sterm-006" + }, + { + "currentSource" : "Reuse verification/adoption shape only from `packages/d2b-unsafe-local-helper/src/runtime.rs` `ScopeRuntime` and `PersistedScope`; exclude helper protocol and state storage.", + "dataMigration" : "Full d2b 3.0 reset; no v2 state/config import.", + "dependencyOwner" : "Adoption and routing area; owned by controller session adoption/routing module.", + "destination" : "`packages/d2b-provider-shell-terminal/src/session/adopt.rs`", + "detailedDesign" : "Implement restart adoption, InvocationID plus cgroup verification, route registration, and stale-generation invalidation. Primary reuse disposition: `adapt`. Preserved source-plan detail: adapt identity-verification shape; do not reuse helper protocol, identities, or state storage assumptions.", + "integration" : "Controller restart scans supervisor Processes, verifies owner/session/generation identity, re-registers exact d2b-bus routes, and rejects stale or ambiguous handles. Integration path: `packages/d2b-provider-shell-terminal/integration/adopt-after-restart/`.", + "removalProof" : "Supersedes unsafe-local helper adoption storage/protocol; proof is degraded-ambiguity and stale-generation tests with no helper state dependency.", + "reuseAction" : "adapt", + "reuseSource" : "`packages/d2b-unsafe-local-helper/src/runtime.rs` `ScopeRuntime` and `PersistedScope` adoption pattern.", + "specId" : "ADR-046-provider-shell-terminal", + "specPath" : "docs/specs/providers/ADR-046-provider-shell-terminal.md", + "validation" : "`packages/d2b-provider-shell-terminal/tests/adoption.rs`", + "workItemId" : "ADR046-sterm-007" + }, + { + "currentSource" : "Supersedes unsafe-local Host shell path from `packages/d2b-unsafe-local-helper/src/services/shell/` while preserving explicit non-isolation warning semantics.", + "dataMigration" : "Full d2b 3.0 reset; no unsafe-local shell state/config import.", + "dependencyOwner" : "Host rules area; owned by shell-terminal Host policy module.", + "destination" : "`packages/d2b-provider-shell-terminal/src/host_rules.rs`", + "detailedDesign" : "Emit Host `isolationPosture=none` warnings, same-UID verification, and relay denial for Host user-domain pools. Primary reuse disposition: `replace`. Preserved source-plan detail: replace unsafe-local helper shell policy with resource-backed Host pool warnings and same-UID checks.", + "integration" : "Pool reconcile reads Host posture and User identity, writes warning status/audit, and admission rejects relay-authenticated Host user-domain access. Integration path: `packages/d2b-provider-shell-terminal/integration/host-warning/`.", + "removalProof" : "Supersedes unsafe-local Host shell supervisor path; proof requires no SSH/direct-host fallback and relay denial coverage.", + "reuseAction" : "replace", + "reuseSource" : null, + "specId" : "ADR-046-provider-shell-terminal", + "specPath" : "docs/specs/providers/ADR-046-provider-shell-terminal.md", + "validation" : "`packages/d2b-provider-shell-terminal/tests/host_rules.rs`", + "workItemId" : "ADR046-sterm-008" + }, + { + "currentSource" : "Supersedes guest persistent-shell runtime in `packages/d2b-guestd/src/shell.rs`; Guest user-domain placement moves to Provider/system-systemd.", + "dataMigration" : "Full d2b 3.0 reset; no guestd shell runtime state/config import.", + "dependencyOwner" : "Guest rules area; owned by shell-terminal Guest policy module.", + "destination" : "`packages/d2b-provider-shell-terminal/src/guest_rules.rs`", + "detailedDesign" : "Require Guest `allowedDomains` to include `user`, require `defaultUserRef`, and place supervisors through the Guest user manager. Primary reuse disposition: `replace`. Preserved source-plan detail: replace guestd shell authority with Guest resource user-domain admission and supervisor placement.", + "integration" : "Pool admission validates Guest capabilities and default user; controller creates user-domain supervisor Processes through the Guest user manager exposed by Provider/system-systemd. Integration path: `packages/d2b-provider-shell-terminal/integration/guest-user-domain/`.", + "removalProof" : "Supersedes independent guestd session-limit/runtime authority; proof is rejection of Guests without `user` domain and no guestd-managed shell path.", + "reuseAction" : "replace", + "reuseSource" : null, + "specId" : "ADR-046-provider-shell-terminal", + "specPath" : "docs/specs/providers/ADR-046-provider-shell-terminal.md", + "validation" : "`packages/d2b-provider-shell-terminal/tests/guest_rules.rs`", + "workItemId" : "ADR046-sterm-009" + }, + { + "currentSource" : "Existing public-wire `ShellOp`/unsafe-local surfaces are superseded; v3 requires ComponentSession service authorization.", + "dataMigration" : "Full d2b 3.0 reset; no v2 state/config import.", + "dependencyOwner" : "RBAC and relay denial area; owned by shell-terminal authorization module.", + "destination" : "`packages/d2b-provider-shell-terminal/src/authz.rs`", + "detailedDesign" : "Gate all verbs on `Role/shell-admin` or Zone-admin superset and fail closed for relay-authenticated Host user-domain callers. Primary reuse disposition: `replace`. Preserved source-plan detail: replace legacy shell operation authorization with Role/shell-admin or Zone-admin service gates.", + "integration" : "Controller and supervisor ComponentSession methods authorize before capacity or route lookup, preserving stale-handle non-disclosure and Host relay denial. Integration path: `packages/d2b-provider-shell-terminal/integration/authz/`.", + "removalProof" : "Supersedes public-wire shell protocol authorization; proof requires no `ShellOp` or `ShellOpResponse` path remains.", + "reuseAction" : "replace", + "reuseSource" : null, + "specId" : "ADR-046-provider-shell-terminal", + "specPath" : "docs/specs/providers/ADR-046-provider-shell-terminal.md", + "validation" : "`packages/d2b-provider-shell-terminal/tests/authz.rs`", + "workItemId" : "ADR046-sterm-010" + }, + { + "currentSource" : "None — net-new v3 closed-label/redacted observability for shell-terminal; legacy shell paths must not leak names, paths, PIDs, or terminal bytes.", + "dataMigration" : "Full d2b 3.0 reset; no v2 audit/telemetry state import.", + "dependencyOwner" : "Audit and telemetry area; owned by shell-terminal audit/telemetry modules.", + "destination" : "`packages/d2b-provider-shell-terminal/src/{audit,telemetry}.rs`", + "detailedDesign" : "Implement closed-label metrics, redacted spans, and audit events with no usernames, session names, paths, or terminal bytes. Primary reuse disposition: `create`. Preserved source-plan detail: net-new redacted observability.", + "integration" : "Reconcile, OpenSession, Attach, Detach, Kill, terminal exit, degradation, and Host posture warnings emit only digest/enum surfaces consumed by audit and OTEL collectors. Integration path: `packages/d2b-provider-shell-terminal/integration/support-redaction/`.", + "removalProof" : "None — net-new observability surface; legacy paths must be removed or adapted to pass redaction tests.", + "reuseAction" : "create", + "reuseSource" : null, + "specId" : "ADR-046-provider-shell-terminal", + "specPath" : "docs/specs/providers/ADR-046-provider-shell-terminal.md", + "validation" : "`packages/d2b-provider-shell-terminal/tests/redaction.rs`", + "workItemId" : "ADR046-sterm-011" + }, + { + "currentSource" : "Superseded sources: `packages/d2b-guestd/src/shell.rs`, `packages/d2b-unsafe-local-helper/src/services/shell/`, and `packages/d2b-contracts/src/public_wire.rs` `ShellOp`/`ShellOpResponse`.", + "dataMigration" : "Full d2b 3.0 reset; no v2 shell state/config import.", + "dependencyOwner" : "Baseline removal area; owned by migration/removal implementation for shell-terminal.", + "destination" : "`packages/d2b-provider-shell-terminal/src/migration.rs`", + "detailedDesign" : "Delete superseded guestd shell runtime, unsafe-local helper shell supervisor, and public-wire `ShellOp` or `ShellOpResponse` shell protocol. Primary reuse disposition: `delete-after-cutover`. Preserved source-plan detail: delete superseded runtime/protocol surfaces after successor parity.", + "integration" : "Removal runs after shell-terminal resource, supervisor, service, RBAC, and integration coverage prove parity; workspace manifests, CI shards, and pins are updated so old and new suites do not run indefinitely. Integration path: `packages/d2b-provider-shell-terminal/integration/migration-baseline/`.", + "removalProof" : "Delete `packages/d2b-guestd/src/shell.rs` managed runtime flow, `packages/d2b-unsafe-local-helper/src/services/shell/`, and `ShellOp`/`ShellOpResponse`; update closed gate manifests, flake/matrix/Nix-unit pins, generated ledgers, and CI workflow shards.", + "reuseAction" : "delete-after-cutover", + "reuseSource" : null, + "specId" : "ADR-046-provider-shell-terminal", + "specPath" : "docs/specs/providers/ADR-046-provider-shell-terminal.md", + "validation" : "`packages/d2b-provider-shell-terminal/tests/migration.rs`", + "workItemId" : "ADR046-sterm-012" + }, + { + "currentSource" : "Reuse service-shape ideas only from main commit `a1cc0b2da4a08ca3240a770a972fe4da6f912bef` generated v2 shell services; exclude ADR 0045 session, realm, and constellation assumptions.", + "dataMigration" : "Full d2b 3.0 reset; no v2 service/session state import.", + "dependencyOwner" : "Supervisor service area; owned by shell-terminal controller/supervisor service modules and ComponentSession contracts.", + "destination" : "`packages/d2b-provider-shell-terminal/src/service/{controller,supervisor}.rs`", + "detailedDesign" : "Define and implement `shell-terminal.v3` and `shell-session-supervisor.v1` ComponentSession services and the named `terminal` stream contract. Primary reuse disposition: `adapt`. Preserved source-plan detail: adapt service-shape ideas into v3 ComponentSession services and named terminal stream.", + "integration" : "Controller service handles OpenSession/ListSessions/PoolStatus; supervisor service handles Attach/Detach/DetachAll/Kill/SupervisorStatus and terminal named stream routed by d2b-bus generation identity. Integration path: `packages/d2b-provider-shell-terminal/integration/service-contract/`.", + "removalProof" : "Supersedes public-wire shell protocol and helper supervisor protocol once ComponentSession service-contract tests pass.", + "reuseAction" : "adapt", + "reuseSource" : "`a1cc0b2da4a08ca3240a770a972fe4da6f912bef` `packages/d2b-contracts/src/generated_v2_services/shell.rs` and `shell_ttrpc.rs`.", + "specId" : "ADR-046-provider-shell-terminal", + "specPath" : "docs/specs/providers/ADR-046-provider-shell-terminal.md", + "validation" : "`packages/d2b-provider-shell-terminal/tests/service_contract.rs`", + "workItemId" : "ADR046-sterm-013" + }, + { + "currentSource" : "`packages/d2b-core/src/storage.rs`, `sync.rs`; `packages/d2bd/src/supervisor/state.rs`, `daemon_audit.rs`; `d2b-realm-router/src/lib.rs`", + "dataMigration" : "Full reset; logical backup only for v3 stores", + "dependencyOwner" : "W0; store integrator", + "destination" : "`packages/d2b-resource-store/src/lib.rs`, `packages/d2b-resource-store-redb/src/lib.rs`, `schema.rs`, `keys.rs`, `transaction.rs`", + "detailedDesign" : "redb tables/encodings, fd backend, store identity, fair actor, MVCC reads, atomic indexes/revisions/operations/conflicts Primary reuse disposition: `adapt`. Preserved source-plan detail: extract and adapt.", + "integration" : "Zone runtime owns store; resource API is sole caller", + "removalProof" : "Existing ledgers removed only by owning future work items", + "reuseAction" : "adapt", + "reuseSource" : null, + "specId" : "ADR-046-resource-store-redb", + "specPath" : "docs/specs/ADR-046-resource-store-redb.md", + "validation" : "Unit/property/fault tests and hard benchmark", + "workItemId" : "ADR046-store-001" + }, + { + "currentSource" : "`packages/d2b-realm-core/src/mux.rs`, `d2b-realm-router/src/mux_session.rs`, `route_engine.rs`", + "dataMigration" : "None — full d2b 3.0 reset; no prior state to migrate", + "dependencyOwner" : "ADR046-store-001; watch/reconciliation integrator", + "destination" : "`packages/d2b-resource-store-redb/src/revision_log.rs`, `packages/d2b-resource-api/src/watch.rs`", + "detailedDesign" : "replay/live no-gap watch, cursors, owner hints, compaction floor, expired relist", + "integration" : "d2b-bus named streams; controller toolkit", + "removalProof" : "Not applicable", + "reuseAction" : "adapt", + "reuseSource" : null, + "specId" : "ADR-046-resource-store-redb", + "specPath" : "docs/specs/ADR-046-resource-store-redb.md", + "validation" : "deterministic watch/compaction/disconnect/fan-in tests", + "workItemId" : "ADR046-store-002" + }, + { + "currentSource" : "`nixos-modules/storage-json.nix`, `packages/d2b-priv-broker/src/ops/storage_contract.rs`, existing marker/ownership tests", + "dataMigration" : "Destructive v3 bootstrap; v3-to-v3 logical restore", + "dependencyOwner" : "ADR046-store-001; storage/broker integrator", + "destination" : "`packages/d2b-resource-store-redb/src/backup.rs`, `migration.rs`; generated v3 storage row", + "detailedDesign" : "fd-backed provision/open, marker identity, logical backup, staged restore/upgrade, corruption quarantine", + "integration" : "Broker/Host/Guest storage owner passes File to Zone runtime", + "removalProof" : "Not applicable", + "reuseAction" : "adapt", + "reuseSource" : null, + "specId" : "ADR-046-resource-store-redb", + "specPath" : "docs/specs/ADR-046-resource-store-redb.md", + "validation" : "marker replacement, crash publication, backup/restore/upgrade tests", + "workItemId" : "ADR046-store-003" + }, + { + "currentSource" : "None — net-new ADR 0046 spec tooling; no pre-ADR45 baseline equivalent", + "dataMigration" : "None — docs/tooling only; no runtime state", + "dependencyOwner" : "`packages/xtask` owner; no dependency on any other streamline item", + "destination" : "`docs/specs/ADR-046-spec-set.json`, `docs/specs/ADR-046-work-items.json`, `docs/specs/ADR-046-implementation-graph.json`, `docs/specs/ADR-046-implementation-graph.md` (generated, committed non-member artifacts; named in `docs/specs/README.md`)", + "detailedDesign" : "New `cargo run -p xtask -- spec-registry` subcommand parsing every spec's metadata table + work-item tables with a Markdown-table parser, emitting the two manifest JSON files; the `implementation-graph` generator then maps every member spec and every work item exactly once to a wave and file-disjoint parallel group, emits typed edges (`spec-depends-on`, `work-item-depends-on`, `implements-spec`, `shared-contract`, `file-overlap-order`), computes topological rank, and renders the Mermaid/table human view; output is deterministic with sorted keys and no timestamps or host paths", + "integration" : "`tests/unit/gates/` drift gate (`xtask spec-registry`/`xtask implementation-graph` + `git diff --exit-code`) added to the existing drift-gate set; the graph is consumed by ADR046-streamline-005/006/008/013 and the ready-wave query in `ADR-046-validation-and-delivery` instead of each re-parsing Markdown independently", + "removalProof" : "None — net-new; no prior owner to remove; this is the foundational generator for the remaining items", + "reuseAction" : "create", + "reuseSource" : null, + "specId" : "ADR-046-streamline", + "specPath" : "docs/specs/ADR-046-streamline.md", + "validation" : "`spec_registry::parse_metadata_table`, `spec_registry::acyclic_dependency_graph`, `xtask_spec_registry_regenerates_clean`, `spec_registry_json_schema_matches_doc`, `implementation_graph::every_spec_and_work_item_mapped_once`, `implementation_graph::acyclic_and_wave_monotonic`, `implementation_graph::parallel_groups_are_file_disjoint`, `implementation_graph_regenerates_clean`", + "workItemId" : "ADR046-streamline-001" + }, + { + "currentSource" : "Decision-register D010/D032/D075/D076/D080 prose is the Tier A source; no existing generated ResourceType schema source until Tier B", + "dataMigration" : "None — docs/tooling only; no runtime state", + "dependencyOwner" : "ADR046-streamline-001; `d2b-core` owner (Tier B only)", + "destination" : "`docs/specs/schemas/*.schema.json` (Tier A: hand-authored-once canonical source checked into the tree, matching the frozen D010/D032/D075/D076/D080 decisions exactly); `packages/d2b-core/src/resource_schema/*.rs` (Tier B: the eventual Rust source of truth once ResourceType implementation exists, at which point the Tier A JSON becomes generated from Rust instead of hand-authored)", + "detailedDesign" : "Tier A: author the schema JSON once per primitive directly from the already-frozen decision-register entries (D010, D032, D075, D076, D080); Tier B: `xtask gen-spec-schemas` derives the same JSON from real Rust `#[derive(JsonSchema)]`-equivalent types once they land, replacing the hand-authored Tier A source without changing its consumers Primary reuse disposition: `create`. Preserved source-plan detail: net-new Tier A schema source; later replace source with generated output from real d2b-core ResourceType types without changing consumers.", + "integration" : "Consumed by ADR046-streamline-003 as the validation target and by ADR046-streamline-004's scaffold as the snippet source", + "removalProof" : "Tier A hand-authored JSON is superseded (not deleted) by Tier B generated JSON once real Rust types exist; consumers are unaffected since the file shape is identical", + "reuseAction" : "create", + "reuseSource" : null, + "specId" : "ADR-046-streamline", + "specPath" : "docs/specs/ADR-046-streamline.md", + "validation" : "`spec_schema_check::canonical_field_set`", + "workItemId" : "ADR046-streamline-002" + }, + { + "currentSource" : "None — net-new spec Markdown/schema lint; no existing fenced ResourceSpec extractor", + "dataMigration" : "None — docs/tooling only; no runtime state", + "dependencyOwner" : "ADR046-streamline-001, ADR046-streamline-002", + "destination" : "`packages/xtask/src/bin/spec_schema_check.rs`; wired as a Layer-1 policy lint at `packages/d2b-contract-tests/tests/policy_spec_schema.rs`", + "detailedDesign" : "Reuse the CommonMark parser already available to the Rust toolchain (the same class of dependency `xtask`'s existing `gen-*` commands already use for structured generation) to walk fenced blocks; classify each block by its enclosing heading (`## Nix authoring and configuration cleanup` vs. any other heading) to apply the current-evidence exclusion without a manual per-block marker Primary reuse disposition: `create`. Preserved source-plan detail: net-new; reuse only the established xtask dispatch pattern and Rust Markdown-parser dependency class.", + "integration" : "Added to `tests/tools/tier0-first-pass.sh` as an additional fast pre-check (closes F17) and to the standing `packages/d2b-contract-tests` policy-lint suite", + "removalProof" : "None — net-new; no prior owner to remove", + "reuseAction" : "create", + "reuseSource" : null, + "specId" : "ADR-046-streamline", + "specPath" : "docs/specs/ADR-046-streamline.md", + "validation" : "`spec_schema_check::extract_fenced_blocks`, `spec_schema_check::canonical_field_set`, `policy_spec_schema` (Type 4/5)", + "workItemId" : "ADR046-streamline-003" + }, + { + "currentSource" : "None — net-new provider-dossier scaffold; canonical snippets come from ADR046-streamline-002", + "dataMigration" : "None — docs/tooling only; no runtime state", + "dependencyOwner" : "ADR046-streamline-001, ADR046-streamline-002", + "destination" : "`docs/specs/providers/TEMPLATE.md` (committed, non-normative reference); `packages/xtask/src/bin/new_provider_dossier.rs` (`cargo run -p xtask -- new-provider-dossier --name `)", + "detailedDesign" : "Scaffold generator emits the template pre-filled with the requesting Provider's name, D080 qualification-grammar examples, and the canonical state-Volume snippet; does not attempt to author Provider-specific semantic sections (those remain the dossier author's normative content) Primary reuse disposition: `create`. Preserved source-plan detail: net-new scaffold; reuse canonical schema/snippet source from ADR046-streamline-002.", + "integration" : "Referenced from `docs/specs/README.md` \"Required metadata\" section (a follow-up doc edit outside this task's scope, tracked as a required cross-reference)", + "removalProof" : "None — net-new; no prior owner to remove", + "reuseAction" : "create", + "reuseSource" : null, + "specId" : "ADR-046-streamline", + "specPath" : "docs/specs/ADR-046-streamline.md", + "validation" : "`xtask_new_provider_dossier_scaffold`; the emitted scaffold must independently pass `spec_schema_check`/`work_item_validator` on first generation", + "workItemId" : "ADR046-streamline-004" + }, + { + "currentSource" : "Decision-register D080/D081/D082/D083/D084/D037 invariants and ADR046-streamline-001 registry output; no existing cross-spec lint", + "dataMigration" : "None — docs/tooling only; no runtime state", + "dependencyOwner" : "ADR046-streamline-001", + "destination" : "`packages/d2b-contract-tests/tests/policy_spec_vocabulary.rs`, `policy_spec_ownership.rs`, `policy_spec_finalizer_phase.rs`, `policy_spec_source_policy.rs`", + "detailedDesign" : "Four focused lint files (one invariant family per file, matching the existing `packages/d2b-contract-tests/tests/policy_*.rs` one-concern-per-file convention) each scanning the registry output plus raw Markdown text for the specific violation patterns named in F2/F4/F5/F7/F9 Primary reuse disposition: `create`. Preserved source-plan detail: net-new policy lints over generated registry and raw Markdown.", + "integration" : "Standing Layer-1 policy-lint suite; runs on every PR touching `docs/specs/**`", + "removalProof" : "None — net-new; no prior owner to remove; graduates to permanent per [Permanent methodology](#permanent-methodology-after-accepted)", + "reuseAction" : "create", + "reuseSource" : null, + "specId" : "ADR-046-streamline", + "specPath" : "docs/specs/ADR-046-streamline.md", + "validation" : "`policy_spec_vocabulary`, `policy_spec_ownership`, `policy_spec_finalizer_phase`, `policy_spec_source_policy` (all Type 5)", + "workItemId" : "ADR046-streamline-005" + }, + { + "currentSource" : "None — real Zone resource-store/controller-registration graph not implemented yet; doc-level invariant source is ADR046-streamline-005", + "dataMigration" : "None — docs/tooling only; no runtime state", + "dependencyOwner" : "The Zone resource-store implementation work item (not yet filed; blocked on W0-W10 implementation request per D024); ADR046-streamline-001 for the doc-level invariant source", + "destination" : "`packages/d2b-resource-store-redb/tests/provider_state_graph.rs` (or the eventual crate implementing Zone resource storage)", + "detailedDesign" : "A graph-walk over the real controller-registration/resource-ownership index (not Markdown) asserting the D076/D086/D087 invariants; the doc-level half of this check (dossier text describing the invariant correctly) is covered now by ADR046-streamline-005's `policy_spec_ownership` Primary reuse disposition: `create`. Preserved source-plan detail: net-new future runtime graph checker.", + "integration" : "Runs as a Type 3 integration test against the real resource-store crate once it exists", + "removalProof" : "None — net-new; no prior owner to remove", + "reuseAction" : "create", + "reuseSource" : null, + "specId" : "ADR-046-streamline", + "specPath" : "docs/specs/ADR-046-streamline.md", + "validation" : "New test asserting: zero `ProviderStateSet` rows in the store; every state Volume corresponds to a declared component namespace; no empty identity-only Volume; no bootstrap state Volume or bootstrap-storage mechanism exists", + "workItemId" : "ADR046-streamline-006" + }, + { + "currentSource" : "Existing policy-broker lint pattern in packages/d2b-contract-tests/tests/policy_broker_dispositions.rs and policy_broker_schema.rs; no Provider crate source exists yet", + "dataMigration" : "None — docs/tooling only; no runtime state", + "dependencyOwner" : "The Provider-toolkit/EffectPort implementation work item (not yet filed; blocked on the W0-W10 implementation request); ADR046-streamline-001 for the doc-level invariant source", + "destination" : "`packages/d2b-contract-tests/tests/policy_effectport_boundary.rs`, `policy_worker_bus_boundary.rs`", + "detailedDesign" : "Static source scan (import-graph/symbol-use analysis) over compiled Provider crates, mirroring the existing `policy_broker_dispositions.rs` pattern already proven against `packages/d2bd`/`packages/d2b-priv-broker` Primary reuse disposition: `adapt`. Preserved source-plan detail: adapt existing policy-broker lint pattern to Provider/Worker boundary checks.", + "integration" : "Standing Layer-1 policy lint once Provider crates exist", + "removalProof" : "None — net-new; no prior owner to remove", + "reuseAction" : "adapt", + "reuseSource" : null, + "specId" : "ADR-046-streamline", + "specPath" : "docs/specs/ADR-046-streamline.md", + "validation" : "New tests asserting zero direct-broker-import matches in any Provider crate; zero disallowed-capability matches in any Worker binary", + "workItemId" : "ADR046-streamline-007" + }, + { + "currentSource" : "None — no existing work-item required-field or unique-ID validator", + "dataMigration" : "None — docs/tooling only; no runtime state", + "dependencyOwner" : "ADR046-streamline-001", + "destination" : "`packages/d2b-contract-tests/tests/policy_work_items.rs`", + "detailedDesign" : "Consumes `docs/specs/ADR-046-work-items.json` (generated by ADR046-streamline-001); flags duplicate IDs, missing/placeholder fields, and dangling cross-references Primary reuse disposition: `create`. Preserved source-plan detail: net-new validator consuming ADR046-streamline-001 output.", + "integration" : "Standing Layer-1 policy lint", + "removalProof" : "None — net-new; no prior owner to remove; graduates to permanent", + "reuseAction" : "create", + "reuseSource" : null, + "specId" : "ADR-046-streamline", + "specPath" : "docs/specs/ADR-046-streamline.md", + "validation" : "`work_item_validator::unique_ids`, `work_item_validator::required_fields`", + "workItemId" : "ADR046-streamline-008" + }, + { + "currentSource" : "None — no existing generated Provider catalog/index", + "dataMigration" : "None — docs/tooling only; no runtime state", + "dependencyOwner" : "ADR046-streamline-001", + "destination" : "`docs/specs/ADR-046-provider-catalog.md` (generated, committed)", + "detailedDesign" : "`cargo run -p xtask -- gen-provider-catalog` reads the registry and decision-register D043-D049 rows and renders the table; drift-gated like the existing `gen-schemas`/`gen-migration-ledger` pattern Primary reuse disposition: `create`. Preserved source-plan detail: net-new generator consuming registry and decision-register rows.", + "integration" : "`tests/unit/gates/` drift gate", + "removalProof" : "None — net-new; no prior owner to remove", + "reuseAction" : "create", + "reuseSource" : null, + "specId" : "ADR-046-streamline", + "specPath" : "docs/specs/ADR-046-streamline.md", + "validation" : "`provider_catalog::frozen_family_membership`, `provider_catalog_matches_frozen_families`", + "workItemId" : "ADR046-streamline-009" + }, + { + "currentSource" : "None — no existing stale-base/current-parent reconcile helper", + "dataMigration" : "None — docs/tooling only; no runtime state", + "dependencyOwner" : "ADR046-streamline-001", + "destination" : "`tests/tools/reconcile-stale-base.sh` (reporting only) plus a documented `git town sync`/`git town` restack procedure this report feeds into, since this repository does not yet use Git Town and F1/F11 show plain rebase/cherry-pick was insufficient to prevent duplicate reconciliation", + "detailedDesign" : "`git merge-base ` plus `git diff --name-only` intersection reporting; emits a JSON report (branch, divergence SHA, commits-behind count, file-overlap list) consumed by ADR046-streamline-013's ready/launched/blocker report Primary reuse disposition: `adapt`. Preserved source-plan detail: net-new read-only git report.", + "integration" : "Referenced from `AGENTS.md` once graduated (see [Permanent methodology](#permanent-methodology-after-accepted)); used manually before opening or restacking any future dossier branch", + "removalProof" : "None — net-new; no prior owner to remove", + "reuseAction" : "adapt", + "reuseSource" : null, + "specId" : "ADR-046-streamline", + "specPath" : "docs/specs/ADR-046-streamline.md", + "validation" : "`reconcile_helper_reports_stale_base`", + "workItemId" : "ADR046-streamline-010" + }, + { + "currentSource" : "None — no existing agent handoff manifest schema or validator", + "dataMigration" : "None — docs/tooling only; no runtime state", + "dependencyOwner" : "No prerequisite; `packages/xtask` schema/validator owner", + "destination" : "`packages/xtask/src/bin/handoff_manifest.rs` (schema/validator only); manifest instances are per-round artifacts referenced from PR bodies, not committed to the tree (consistent with this repository's \"Screenshot and visual artifact hygiene\"-style external-evidence posture)", + "detailedDesign" : "A small JSON schema (`assigned_files: [string]`, `commit_sha: string`, `test_result: {command, exit_code}`, `base_sha: string`) plus a validator rejecting incomplete records; no attribution field for any AI/tool/model, consistent with this repository's commit/PR-body attribution rule Primary reuse disposition: `create`. Preserved source-plan detail: net-new schema and validator.", + "integration" : "Referenced from `AGENTS.md` \"Worktrees for parallel agents\" once graduated", + "removalProof" : "None — net-new; no prior owner to remove", + "reuseAction" : "create", + "reuseSource" : null, + "specId" : "ADR-046-streamline", + "specPath" : "docs/specs/ADR-046-streamline.md", + "validation" : "`handoff_manifest_rejects_incomplete_record`", + "workItemId" : "ADR046-streamline-011" + }, + { + "currentSource" : "None — no existing task DB to git consistency importer", + "dataMigration" : "None — docs/tooling only; no runtime state", + "dependencyOwner" : "ADR046-streamline-011 (shares the manifest schema for commit-SHA binding)", + "destination" : "`tests/tools/import-task-db-consistency.sh`", + "detailedDesign" : "Reads the session database's todo table, resolves each `done` row's expected file set against `git log --name-only` for the current branch, and reports mismatches; read-only, no database or git mutation Primary reuse disposition: `create`. Preserved source-plan detail: net-new read-only importer/checker.", + "integration" : "Run manually at the end of each authoring round and before any pre-panel gate", + "removalProof" : "None — net-new; no prior owner to remove", + "reuseAction" : "create", + "reuseSource" : null, + "specId" : "ADR-046-streamline", + "specPath" : "docs/specs/ADR-046-streamline.md", + "validation" : "New test seeding a synthetic task DB with one orphaned \"done\" row and confirming the importer flags it", + "workItemId" : "ADR046-streamline-012" + }, + { + "currentSource" : "None — no existing anti-serialization ready/launched/blocker report", + "dataMigration" : "None — docs/tooling only; no runtime state", + "dependencyOwner" : "ADR046-streamline-001, ADR046-streamline-010", + "destination" : "`tests/tools/anti-serialization-report.sh`", + "detailedDesign" : "Cross-references the spec registry's dependency graph (ADR046-streamline-001) against the set of currently open `adr0046-*` branches and the reconcile helper's (ADR046-streamline-010) staleness report; emits a per-scope ready/launched/blocked classification Primary reuse disposition: `adapt`. Preserved source-plan detail: net-new report combining registry, branch, and staleness data.", + "integration" : "Run at the start of each authoring round and after any foundation-spec change", + "removalProof" : "None — net-new; no prior owner to remove", + "reuseAction" : "adapt", + "reuseSource" : null, + "specId" : "ADR-046-streamline", + "specPath" : "docs/specs/ADR-046-streamline.md", + "validation" : "New test seeding a synthetic registry + branch list with one ready-but-unlaunched scope and confirming it is reported", + "workItemId" : "ADR046-streamline-013" + }, + { + "currentSource" : "Existing bounded-parallel test-runner precedent in tests/tools/run-layer.sh and layer1-jobs.py; Provider toolkit fake crates are net-new", + "dataMigration" : "None — docs/tooling only; no runtime state", + "dependencyOwner" : "The Provider-toolkit implementation work item (blocked on the W0-W10 implementation request)", + "destination" : "`tests/tools/run-layer.sh` extension (this repository already has `tests/tools/run-layer.sh` and `layer1-jobs.py` bounded-parallelism precedent) plus fake `EffectPort`/`ResourceClient` stub crates under `packages/d2b-provider-toolkit-fakes/`", + "detailedDesign" : "Extend the existing `layer1-jobs.py` bounded-parallel-shard pattern with a per-Provider-crate shard definition; fake dependency crates implement the same `EffectPort`/`ResourceClient` trait surface with in-memory stand-ins Primary reuse disposition: `adapt`. Preserved source-plan detail: adapt bounded-parallel shard pattern; add net-new fake EffectPort/ResourceClient crates.", + "integration" : "`make test-unit`/`make check` shard addition once Provider crates exist", + "removalProof" : "None — net-new; no prior owner to remove", + "reuseAction" : "adapt", + "reuseSource" : null, + "specId" : "ADR-046-streamline", + "specPath" : "docs/specs/ADR-046-streamline.md", + "validation" : "New integration test proving a single Provider crate's test run succeeds with zero other Provider crates built", + "workItemId" : "ADR046-streamline-014" + }, + { + "currentSource" : "Existing packages/xtask gen-* and drift-gate pattern; no existing generated-artifact conflict detector", + "dataMigration" : "None — docs/tooling only; no runtime state", + "dependencyOwner" : "ADR046-streamline-001, ADR046-streamline-009, ADR046-streamline-002 (Tier A part)", + "destination" : "Shared `packages/xtask` regeneration-conflict-detection helper consumed by every `gen-*`/`spec-registry` subcommand", + "detailedDesign" : "Each generator hashes its input set before writing; if a concurrent regeneration on a sibling branch (detected via the reconcile helper's file-overlap report) would produce a different hash for the same output path, the generator refuses to overwrite and reports the divergent input set Primary reuse disposition: `adapt`. Preserved source-plan detail: adapt existing generator/drift-gate pattern with net-new input-hash conflict detection.", + "integration" : "Wired into every drift gate this spec and ADR046-streamline-001/009 introduce", + "removalProof" : "None — net-new; no prior owner to remove", + "reuseAction" : "adapt", + "reuseSource" : null, + "specId" : "ADR-046-streamline", + "specPath" : "docs/specs/ADR-046-streamline.md", + "validation" : "New test simulating two divergent input sets producing conflicting output for the same generated file and confirming the conflict is reported, not silently resolved", + "workItemId" : "ADR046-streamline-015" + }, + { + "currentSource" : "D014/docs/specs/README.md zero-open-decision requirement; no existing automated pre-panel gate", + "dataMigration" : "None — docs/tooling only; no runtime state", + "dependencyOwner" : "ADR046-streamline-001, ADR046-streamline-008", + "destination" : "`packages/d2b-contract-tests/tests/policy_no_leaked_decision_prefix.rs`; invoked as a required precondition script `tests/tools/pre-panel-gate.sh`", + "detailedDesign" : "Scans registry output plus raw Markdown for `decision-required`, `TBD`, `TODO`, and any decision-ID-shaped token (`[A-Z]+-\\d+`) not matching the canonical `D\\d+` register numbering Primary reuse disposition: `create`. Preserved source-plan detail: net-new policy lint and precondition script.", + "integration" : "Required precondition before requesting the ADR 0046 documentation set's panel round (D014)", + "removalProof" : "None — net-new; no prior owner to remove; graduates to permanent panel-gate precondition", + "reuseAction" : "create", + "reuseSource" : null, + "specId" : "ADR-046-streamline", + "specPath" : "docs/specs/ADR-046-streamline.md", + "validation" : "`policy_no_leaked_decision_prefix`", + "workItemId" : "ADR046-streamline-016" + }, + { + "currentSource" : "This spec's Observed friction evidence command prose; no reusable evidence-command artifact exists yet", + "dataMigration" : "None — docs/tooling only; no runtime state", + "dependencyOwner" : "No prerequisite; `docs/specs` evidence-command documentation owner", + "destination" : "`docs/specs/ADR-046-streamline-evidence-commands.md` (a follow-up artifact outside this task's file scope; tracked here as a required future addition, not authored by this spec)", + "detailedDesign" : "A plain Markdown list of the exact commands used to produce each F1-F17 citation above (already reproduced verbatim in this spec's prose), organized by friction ID, so re-running them is copy-paste rather than re-derivation Primary reuse disposition: `adapt`. Preserved source-plan detail: extract/adapt the already-cited evidence commands into a net-new documentation artifact.", + "integration" : "Referenced from PR bodies as external evidence per this repository's \"PR bodies contain... check-status summaries only... may link to external evidence\" convention", + "removalProof" : "None — net-new; no prior owner to remove", + "reuseAction" : "adapt", + "reuseSource" : null, + "specId" : "ADR-046-streamline", + "specPath" : "docs/specs/ADR-046-streamline.md", + "validation" : "Manual reviewer re-run of at least one command per friction ID during panel, confirmed to reproduce the cited count/SHA", + "workItemId" : "ADR046-streamline-017" + }, + { + "currentSource" : "Existing disk-hygiene operator guidance; no ADR 0046 worktree disk report exists yet", + "dataMigration" : "None — docs/tooling only; no runtime state", + "dependencyOwner" : "ADR046-streamline-010 (shares branch-staleness detection)", + "destination" : "`tests/tools/worktree-disk-report.sh`", + "detailedDesign" : "`git worktree list --porcelain` plus `du -sh` per worktree's `packages/target/`; output is a report only, never a `git worktree remove`/`rm -rf` invocation Primary reuse disposition: `adapt`. Preserved source-plan detail: adapt existing disk-hygiene reporting concepts into a net-new non-mutating script.", + "integration" : "Run manually before requesting garbage collection or worktree removal, consistent with this repository's existing \"Disk hygiene contract\"-equivalent operator guidance for code waves", + "removalProof" : "None — net-new; no prior owner to remove", + "reuseAction" : "adapt", + "reuseSource" : null, + "specId" : "ADR-046-streamline", + "specPath" : "docs/specs/ADR-046-streamline.md", + "validation" : "New test confirming the reporting script's exit code and output never include a mutating command string, and confirming it correctly flags a fixture worktree with a real (non-symlink) `packages/target/`", + "workItemId" : "ADR046-streamline-018" + }, + { + "currentSource" : "ADR-046 terminology and current-code migration-map specs; no automated old-to-new terminology helper exists yet", + "dataMigration" : "None — docs/tooling only; no runtime state", + "dependencyOwner" : "ADR046-streamline-001", + "destination" : "`packages/xtask/src/bin/terminology_check.rs` (`cargo run -p xtask -- terminology-check`)", + "detailedDesign" : "Parses every \"Current-code fit\" table's \"Current anchor\" cell for symbol/crate names, cross-references against the migration map's disposition rows, and flags any current-source citation absent from the map Primary reuse disposition: `create`. Preserved source-plan detail: net-new terminology checker over existing mapping specs.", + "integration" : "Standing Layer-1 policy lint once wired; also usable ad hoc when authoring a new dossier's evidence section", + "removalProof" : "None — net-new; no prior owner to remove", + "reuseAction" : "create", + "reuseSource" : null, + "specId" : "ADR-046-streamline", + "specPath" : "docs/specs/ADR-046-streamline.md", + "validation" : "New test seeding a fixture spec citing a current-source symbol absent from the migration map and confirming it is flagged", + "workItemId" : "ADR046-streamline-019" + }, + { + "currentSource" : "D094 test-placement requirement; no existing hermetic test-placement policy lint", + "dataMigration" : "None — docs/tooling only; no runtime state", + "dependencyOwner" : "ADR046-streamline-001", + "destination" : "`packages/d2b-contract-tests/tests/policy_test_placement.rs`", + "detailedDesign" : "Scans hermetic-tier Rust sources for banned API surfaces (`std::process::Command`, socket/container/DBus/systemd helpers, `#[ignore]`) and for `integration/`-only markers appearing outside `integration/` Primary reuse disposition: `create`. Preserved source-plan detail: net-new policy lint.", + "integration" : "`make test-policy` row; no new top-level `tests/*.sh` gate", + "removalProof" : "None — net-new; no prior owner to remove", + "reuseAction" : "create", + "reuseSource" : null, + "specId" : "ADR-046-streamline", + "specPath" : "docs/specs/ADR-046-streamline.md", + "validation" : "Fixture crate with an intentional process-spawning hermetic test is rejected naming the file/line; a correct crate passes", + "workItemId" : "ADR046-streamline-020" + }, + { + "currentSource" : "D094 deterministic-clock requirement; no existing wall-clock sleep/retry policy lint", + "dataMigration" : "None — docs/tooling only; no runtime state", + "dependencyOwner" : "ADR046-streamline-020", + "destination" : "`packages/d2b-contract-tests/tests/policy_test_determinism.rs`", + "detailedDesign" : "Scans hermetic sources for banned time/sleep APIs and asserts the deterministic fake-clock/RNG fixtures from the toolkit are used; classified crypto/property exceptions are allow-listed by explicit name with a declared per-test budget Primary reuse disposition: `create`. Preserved source-plan detail: net-new policy lint layered on ADR046-streamline-020.", + "integration" : "`make test-policy` row", + "removalProof" : "None — net-new; no prior owner to remove", + "reuseAction" : "create", + "reuseSource" : null, + "specId" : "ADR-046-streamline", + "specPath" : "docs/specs/ADR-046-streamline.md", + "validation" : "Fixture hermetic test using `thread::sleep` is rejected; a classified crypto test on the allow-list passes", + "workItemId" : "ADR046-streamline-021" + }, + { + "currentSource" : "ADR046-delivery-007 timing-ledger design; no existing execution-only test-runtime ledger in this spec set", + "dataMigration" : "None — docs/tooling only; no runtime state", + "dependencyOwner" : "ADR046-delivery-007", + "destination" : "`packages/xtask/src/test_runtime_ledger.rs` (shared with `ADR046-delivery-007`)", + "detailedDesign" : "Parses per-test JSON timings, aggregates per test/crate/shard, compares against pinned budgets and the previous ledger, and fails on regression beyond the threshold Primary reuse disposition: `adapt`. Preserved source-plan detail: share/adapt ADR046-delivery-007 timing-ledger implementation for this gate.", + "integration" : "Consumed by wave entry/exit (`ADR-046-validation-and-delivery` §4/§10.16); `make test-rust` and Layer-1 shards run concurrently", + "removalProof" : "None — net-new; no prior owner to remove", + "reuseAction" : "adapt", + "reuseSource" : null, + "specId" : "ADR-046-streamline", + "specPath" : "docs/specs/ADR-046-streamline.md", + "validation" : "Synthetic timing regression fails the gate; ledger output is deterministic and machine-readable", + "workItemId" : "ADR046-streamline-022" + }, + { + "currentSource" : "Current-code migration-map disposition rows and existing gate manifests; no legacy-test retirement generator exists yet", + "dataMigration" : "None — docs/tooling only; no runtime state", + "dependencyOwner" : "ADR046-streamline-008, ADR046-streamline-022", + "destination" : "`packages/xtask/src/bin/legacy_test_retirement.rs` (`cargo run -p xtask -- legacy-test-retirement`)", + "detailedDesign" : "Reads the migration map's disposition rows, cross-references the live `tests/layer1-jobs.json`/gate manifests, and emits the delete set plus an absence assertion; never deletes automatically — it produces the gated checklist and the failing test that proves incomplete retirement Primary reuse disposition: `adapt`. Preserved source-plan detail: net-new generator reading existing migration rows and gate manifests without mutating them.", + "integration" : "`make test-policy`/`make test-drift` row; wired to every current-code migration work item's removal proof", + "removalProof" : "None — net-new; no prior owner to remove", + "reuseAction" : "adapt", + "reuseSource" : null, + "specId" : "ADR-046-streamline", + "specPath" : "docs/specs/ADR-046-streamline.md", + "validation" : "Fixture with a replaced behavior whose old selector still appears in `tests/layer1-jobs.json` fails; once removed, the absence assertion passes", + "workItemId" : "ADR046-streamline-023" + }, + { + "currentSource" : "The graph is regenerated by hand today; `ADR-046-spec-set.json` and the test-runtime ledger (ADR046-delivery-007) each have their own emitter — duplicate-generator finding, retained here for reconciliation", + "dataMigration" : "None — docs/tooling only; no runtime state", + "dependencyOwner" : "ADR046-streamline-001", + "destination" : "`packages/xtask/src/bin/implementation_graph.rs` (`cargo run -p xtask -- implementation-graph`); folds the spec-set and test-runtime emitters into the one `xtask` dispatch", + "detailedDesign" : "Deterministic (sorted keys, no timestamps/host paths); regenerated after any spec or work-item change and after the two manifests; a drift gate runs it and `git diff --exit-code`. Retains the duplicate-generator findings for the spec-set manifest and the test-runtime ledger and reconciles them to the single `xtask` path rather than leaving three independent emitters Primary reuse disposition: `create`. Preserved source-plan detail: net-new generator that consumes the two manifests and the wave topology; folds the duplicate spec-set/test-runtime emitters into one canonical path.", + "integration" : "`tests/unit/gates/` drift gate alongside `xtask spec-registry`; consumed by the ready-wave query in `ADR-046-validation-and-delivery` §3.5.1", + "removalProof" : "The ad hoc separate emitters for spec-set and test-runtime are removed after the single `xtask implementation-graph`/`spec-registry` dispatch reaches parity", + "reuseAction" : "create", + "reuseSource" : null, + "specId" : "ADR-046-streamline", + "specPath" : "docs/specs/ADR-046-streamline.md", + "validation" : "`implementation_graph::every_spec_and_work_item_mapped_once`; `implementation_graph::acyclic_and_wave_monotonic`; `implementation_graph::parallel_groups_are_file_disjoint`; `implementation_graph_regenerates_clean`; duplicate-generator reconciliation asserts one canonical emitter path for spec-set and test-runtime", + "workItemId" : "ADR046-streamline-024" + }, + { + "currentSource" : "No canonical v3 equivalent. Adapt the Provider descriptor pattern from `packages/d2b-realm-provider/src/provider.rs` and the bounded audit-envelope pattern from `packages/d2bd/src/daemon_audit.rs`; do not carry forward daemon topology or unsafe-local helper protocol types.", + "dataMigration" : "Full d2b 3.0 reset; no Provider config, handler checkpoint, or audit state is imported.", + "dependencyOwner" : "`ADR046-provider-001`, `ADR046-exec-003`, `ADR046-exec-004`, `ADR046-exec-005`, `ADR046-pstate-012`, `ADR046-telem-001`, and `ADR046-telem-012`; `Provider/system-core` owner", + "destination" : "`packages/d2b-provider-system-core/src/manifest.rs`, `packages/d2b-provider-system-core/src/audit.rs`, and `packages/d2b-provider-system-core/tests/provider_boundary.rs`", + "detailedDesign" : "Compile the system-core Provider manifest, empty closed config schema, Host/User component descriptors, and empty state-namespace declaration. The manifest binds both library handlers to the fixed `d2b-core-controller` bootstrap process without declaring either handler as a Process resource. The audit adapter emits one bounded, redacted `ResourceReconciled` record after each Host/User reconcile. The boundary rejects Provider config fields and proves that handler call paths neither write `Provider.status` nor emit `ProcessEffect`; core-controller infrastructure owns the former and `ADR046-exec-006`/`ADR046-exec-007` own the latter. `ADR046-pstate-012` remains the owner of generic optional-state admission; this item only declares system-core's empty state set. Primary reuse disposition: `adapt`. Preserved source-plan detail: adapt descriptor/audit patterns; implement the v3 Provider-specific boundary.", + "integration" : "`ADR046-exec-003` and `ADR046-exec-004` call the audit adapter after reconcile; `ADR046-exec-005` and core-controller infrastructure load the manifest and derive the runtime-owned `Provider/system-core` resource/status.", + "removalProof" : "No independent destination removal. `ADR046-exec-009` owns unsafe-local helper/wire retirement, and `ADR046-telem-008` owns removal proofs for legacy unsafe-local and VM-name telemetry labels.", + "reuseAction" : "adapt", + "reuseSource" : null, + "specId" : "ADR-046-provider-system-core", + "specPath" : "docs/specs/providers/ADR-046-provider-system-core.md", + "validation" : "`config_schema_empty_only`, `provider_status_not_written_by_handlers`, `provider_state_set_empty`, `host_no_process_effect_emitted`, `host_resource_reconciled_audit`, and `user_resource_reconciled_audit`; manifest golden vector proves no Process descriptor and no state namespace for either handler.", + "workItemId" : "ADR046-system-core-001" + }, + { + "currentSource" : "`packages/d2b-unsafe-local-helper/src/systemd.rs` — `SystemdUserScopeManager`, `VerifiedScope`; `packages/d2bd/src/supervisor/` — pidfd adoption, restart backoff", + "dataMigration" : "No state migration; controller relists and adopts on restart", + "dependencyOwner" : "`ADR046-process-002`; Process contracts/supervisor owner; effect port interface owner", + "destination" : "`packages/d2b-provider-system-systemd/src/controller.rs` (async reconcile loop), `src/launch.rs` (launch algorithm via effect port), `src/effect_port.rs` (`SystemdProcessEffectPort` trait + test double), `src/adoption.rs` (adoption algorithm), `src/sandbox.rs` (SandboxSpec → unit property compiler)", + "detailedDesign" : "Full §6 launch algorithm (effect port integration); §7 EphemeralProcess; §8 restart/adoption (effect port `locate_by_identity`); §9 drain (effect port `stop`/`kill`); §10 sandbox compilation; §11 bus services; ProviderSupervisor LaunchTicket integration Primary reuse disposition: `adapt`. Preserved source-plan detail: extract and adapt.", + "integration" : "Core ProviderDeployment creates controller Process via Provider/system-minijail and creates/deletes one private Volume per component per execution target before/after component Processes (naming: `system-systemd--controller--main-state--`; ownerRef: Provider/system-systemd; kind=state; persistenceClass=persistent; minimal nonzero quota; identity marker; migrationPolicy=none; layout principal: Nix-preprovisioned `User/` or bounded principal pool; no ComponentPrincipal ResourceRef); Provider/volume-local is sole Volume reconciler; system-systemd controller does NOT create, own, or reconcile its Volume; controller consumes only the `controller-rw` view dirfd via its Process mounts; controller watches Process/EphemeralProcess; ProviderSupervisor calls LaunchProcess; effect port implementation injected by core supervisor spec", + "removalProof" : "`VmProcessDag` supervisor roles removed per role disposition table after each succeeds in conformance", + "reuseAction" : "adapt", + "reuseSource" : "Main `a1cc0b2d`: `d2b-session/src/engine.rs`, `d2b-session-unix/src/adapter.rs` (effect port test double session/transport)", + "specId" : "ADR-046-provider-system-systemd", + "specPath" : "docs/specs/providers/ADR-046-provider-system-systemd.md", + "validation" : "`tests/conformance.rs` (shared conformance kit); `tests/identity_binding.rs` (InvocationID/cgroup/MainPID/start-time golden vectors via mock effect port); `tests/adoption.rs` (quarantine/identity-mismatch cases); `tests/restart.rs` (backoff/maxRestarts); latency assertions (p95 ≤5 ms hint→handler, ≤20 ms commit→effect port `start` call)", + "workItemId" : "ADR046-systemd-001" + }, + { + "currentSource" : "`nixos-modules/unsafe-local-helper.nix`; `nixos-modules/processes-json.nix`", + "dataMigration" : "No configuration compatibility path; full reset at v3 cutover", + "dependencyOwner" : "`ADR046-systemd-001`; Nix/package integrator", + "destination" : "`nixos-modules/` (Provider ResourceSpec emission for `system-systemd`); `packages/d2b-provider-system-systemd/` package derivation and catalog entry", + "detailedDesign" : "§16 Nix configuration; `d2b.artifacts.system-systemd` catalog entry; Provider and Process ResourceSpec emission; eval/build validation rules; drift gate update (`xtask gen-nix-options` + `make test-drift`)", + "integration" : "Zone configuration activates Provider/system-systemd; Process resources reference it via `spec.providerRef = \"Provider/system-systemd\"`", + "removalProof" : "`nixos-modules/unsafe-local-helper.nix` removed after user-domain Host/Process parity", + "reuseAction" : "adapt", + "reuseSource" : null, + "specId" : "ADR-046-provider-system-systemd", + "specPath" : "docs/specs/providers/ADR-046-provider-system-systemd.md", + "validation" : "`tests/unit/nix/cases/provider-system-systemd.nix` (eval-time validation); `tests/unit/gates/drift-check.sh` covers generated option schema", + "workItemId" : "ADR046-systemd-002" + }, + { + "currentSource" : "`packages/d2bd/src/supervisor/` (existing process lifecycle tests); `packages/d2b-unsafe-local-helper/src/systemd.rs` (existing scope tests)", + "dataMigration" : "None — full d2b 3.0 reset; no prior state to migrate", + "dependencyOwner" : "`ADR046-systemd-001`; conformance kit / test infrastructure", + "destination" : "`packages/d2b-provider-system-systemd/tests/conformance.rs`, `tests/fault.rs`, `tests/ephemeral.rs`, `tests/sandbox_compile.rs`; `integration/host_scenario.rs`, `integration/guest_scenario.rs`", + "detailedDesign" : "Full §19 test/integration requirements Primary reuse disposition: `adapt`. Preserved source-plan detail: copy/adapt.", + "integration" : "`cargo test -p d2b-provider-system-systemd`; `make test-integration -- provider-system-systemd`; `make test-host-integration -- provider-system-systemd`", + "removalProof" : "No removal; tests are permanent", + "reuseAction" : "adapt", + "reuseSource" : null, + "specId" : "ADR-046-provider-system-systemd", + "specPath" : "docs/specs/providers/ADR-046-provider-system-systemd.md", + "validation" : "All conformance vectors pass; all fault injection scenarios reach expected phase/condition; all §19 Host and Guest test scenarios pass", + "workItemId" : "ADR046-systemd-003" + }, + { + "currentSource" : "`packages/d2b-realm-core/src/trace_context.rs` (`TraceContext`, `MAX_TRACE_FIELD_LEN`); `packages/d2b-realm-core/src/audit.rs` (`AuditHash`, `AuditHashError`, `AuditChainLink`, `AuditChainRecord`); `packages/d2b-realm-core/src/ids.rs` (`OperationId`, `CorrelationId`); `packages/d2b-realm-codec-protobuf/src/lib.rs` (`encode_trace_context`, `decode_trace_context`); `packages/d2b-contract-tests/tests/policy_observability.rs::startup_tracing_avoids_host_path_fields`", + "dataMigration" : "Full d2b 3.0 reset; no v2 state/config import", + "dependencyOwner" : "W0/W1a; telemetry crate owner", + "destination" : "`packages/d2b-telemetry/src/{trace_context.rs,audit_hash.rs,emitter.rs,meter_registry.rs,redaction_guard.rs}`", + "detailedDesign" : "`d2b-telemetry` provides: (1) `TraceContext` / `AuditHash` / `AuditChainLink` extracted unchanged; (2) `BoundedEmitter`: `tracing`-subscriber layer that serializes span/metric events into compact frames and writes them over a private Unix datagram socket to the `observability-otel` Provider — no `opentelemetry_sdk` dependency; (3) `RedactionGuard` span wrapper that asserts the v3 resource attribute allowlist at span creation. No OTEL SDK in this crate. Primary reuse disposition: `adapt`. Preserved source-plan detail: extract unchanged (`TraceContext`, `AuditHash`, `AuditChainLink`); adapt (`OperationId`/`CorrelationId` for v3 record contract); add bounded emitter.", + "integration" : "Every v3 core process initializes a `BoundedEmitter` pointing at `$ZONE_STATE/telemetry/emitter.sock`; v3 audit records use `AuditHash`/`AuditChainLink` from this crate", + "removalProof" : "None — net-new; no prior owner to remove", + "reuseAction" : "adapt", + "reuseSource" : null, + "specId" : "ADR-046-telemetry-audit-and-support", + "specPath" : "docs/specs/ADR-046-telemetry-audit-and-support.md", + "validation" : "Unit test for `RedactionGuard` attribute gate; unit test for `BoundedEmitter` ring-full drop and FIFO drain; `policy_telemetry_redaction.rs::startup_tracing_avoids_host_path_fields` port; assert `config_source = \"realm-controllers\"` absent; assert no `opentelemetry_sdk` dependency in `d2b-telemetry` Cargo.toml", + "workItemId" : "ADR046-telem-001" + }, + { + "currentSource" : "`packages/d2bd/src/metrics.rs` (`MetricDescriptor`, `MetricKind`, `VM_START_BUCKETS_SECONDS`, `BROKER_REQUEST_BUCKETS_SECONDS`, `ACTIVATION_PHASE_BUCKETS_SECONDS` — for bucket pattern reference only; the `vm` labels are not reused)", + "dataMigration" : "Full d2b 3.0 reset; no v2 state/config import", + "dependencyOwner" : "ADR046-telem-001 + ADR046-store-001; store owner", + "destination" : "`packages/d2b-resource-store-redb/src/metrics.rs`, `packages/d2b-resource-store-redb/src/tracing.rs`", + "detailedDesign" : "Instrument the store actor, write/read/group-commit paths with the metric inventory from this spec via `d2b-telemetry` `BoundedEmitter`. Emit `d2b.store.*` spans. The p95 `d2b_store_write_duration_seconds` hard target (≤10 ms) feeds the benchmark fixture. No `vm` label; `resource_type` label only from closed catalog. No OTEL SDK in the store crate. Primary reuse disposition: `adapt`. Preserved source-plan detail: adapt bucket boundary constants (rename; remove `vm` labels); replace hand-rolled `Registry` with `d2b-telemetry` `BoundedEmitter` meter API.", + "integration" : "Store actor calls `d2b-telemetry` meter/tracer via `BoundedEmitter`; spans linked to API request spans via `TraceContext`", + "removalProof" : "Hand-rolled registry in `d2bd/src/metrics.rs` retained until daemon-level ADR 0046 cutover", + "reuseAction" : "adapt", + "reuseSource" : null, + "specId" : "ADR-046-telemetry-audit-and-support", + "specPath" : "docs/specs/ADR-046-telemetry-audit-and-support.md", + "validation" : "p95 write ≤10 ms benchmark fixture; metric inventory policy test asserting no `vm` label; assert old `d2b_daemon_vm_state` shape absent", + "workItemId" : "ADR046-telem-002" + }, + { + "currentSource" : "`packages/d2b-realm-core/src/ids.rs` (`OperationId`, `CorrelationId`); `packages/d2b-realm-codec-protobuf/src/lib.rs` (`encode_trace_context`, `decode_trace_context` for v3 codec adaptation); `packages/d2b-realm-router/src/mux_session.rs`, `route_engine.rs` (current implemented-but-unwired generic routing)", + "dataMigration" : "Full d2b 3.0 reset; no v2 state/config import", + "dependencyOwner" : "ADR046-telem-001 + ADR046-session-001 + ADR046-session-003; session/bus owner", + "destination" : "`packages/d2b-resource-api/src/metrics.rs`, `packages/d2b-session/src/metrics.rs`, `packages/d2b-bus/src/metrics.rs`", + "detailedDesign" : "Instrument resource API verb dispatch, watch delivery, bus route resolution, and session handshake/reconnect per the metric/span catalog in this spec. Propagate `TraceContext` from incoming bus request to store write transaction span as child context. Primary reuse disposition: `adapt`. Preserved source-plan detail: adapt `TraceContext` protobuf codec for v3 resource API framing; adapt routing metrics patterns.", + "integration" : "ResourceClient → bus → API → store span chain via `TraceContext`", + "removalProof" : "None — net-new; no prior owner to remove", + "reuseAction" : "adapt", + "reuseSource" : null, + "specId" : "ADR-046-telemetry-audit-and-support", + "specPath" : "docs/specs/ADR-046-telemetry-audit-and-support.md", + "validation" : "API request metric inventory test; session profile/outcome label cardinality gate; bus direction label gate; assert no `realm` field in span attributes", + "workItemId" : "ADR046-telem-003" + }, + { + "currentSource" : "`packages/d2bd/src/metrics.rs` (`BROKER_REQUEST_BUCKETS_SECONDS` for reference; `d2b_daemon_broker_request_duration_seconds` labels `[\"op\"]` as a cardinality-safe example); `packages/d2b-realm-core/src/allocator_engine.rs` (field `trace: Option` at line 873 — existing trace context wiring pattern)", + "dataMigration" : "Full d2b 3.0 reset; no v2 state/config import", + "dependencyOwner" : "ADR046-telem-001 + ADR046-core-001; core-controller owner", + "destination" : "`packages/d2b-core-controller/src/metrics.rs`, `packages/d2b-core-controller/src/tracing.rs`", + "detailedDesign" : "Emit `d2b.controller.hint` span at the instant the post-commit dispatcher fires; emit `d2b.controller.reconcile` child span at handler entry. Interval = p95 ≤5 ms target. `handler` label from closed set; no resource name labels. Primary reuse disposition: `adapt`. Preserved source-plan detail: adapt bucket patterns; adapt trace-context-in-reconcile pattern from `allocator_engine.rs`.", + "integration" : "Post-commit dispatcher creates hint span; handler creates child reconcile span via `TraceContext`", + "removalProof" : "None — net-new; no prior owner to remove", + "reuseAction" : "adapt", + "reuseSource" : null, + "specId" : "ADR-046-telemetry-audit-and-support", + "specPath" : "docs/specs/ADR-046-telemetry-audit-and-support.md", + "validation" : "`hint_to_handler_latency` benchmark with p95 ≤5 ms assertion; closed `handler` label set gate", + "workItemId" : "ADR046-telem-004" + }, + { + "currentSource" : "`packages/d2bd/src/metrics.rs` (`d2b_daemon_vm_start_duration_seconds` `VM_START_BUCKETS_SECONDS`; `d2b_daemon_vm_shutdown_duration_seconds` `VM_SHUTDOWN_BUCKETS_SECONDS`; `d2b_daemon_vm_shutdown_total` labels `[\"vm\", \"vmm\", \"outcome\"]` — `vmm` = current `RunnerRole` → v3 `provider`); `packages/d2bd/src/supervisor/pidfd.rs` (pidfd adoption/launch call sites); `packages/d2b-contracts/src/broker_wire.rs::RunnerRole` (`CloudHypervisor`, `QemuMedia`, `OtelHostBridge` etc. → v3 `provider` label values)", + "dataMigration" : "Full d2b 3.0 reset; no v2 state/config import", + "dependencyOwner" : "ADR046-telem-001 + ADR046-process-001; Process Provider owner", + "destination" : "`packages/d2b-provider-supervisor/src/metrics.rs`, `packages/d2b-provider-supervisor/src/tracing.rs`", + "detailedDesign" : "`d2b_process_launch_duration_seconds`: start = instant Process controller receives commit-to-Ready hint; end = first OS spawn call (clone3 or systemd unit start). This implements p95 ≤20 ms. `provider` label replaces `vmm`/`RunnerRole` with the closed set `{minijail,systemd}`. No `vm` name label. A separate `d2b_process_ready_duration_seconds` histogram covers launch-attempt → readiness signal (not a hard target). Primary reuse disposition: `adapt`. Preserved source-plan detail: adapt launch histogram bucket constants; rename `vm` label to no label (process identity in resource attributes); rename `vmm`/`RunnerRole` → `provider` closed enum.", + "integration" : "Process Provider controller start handler → supervisor ticket delivery → first spawn call", + "removalProof" : "`d2b_daemon_vm_start_duration_seconds` (with `vm` label) retained in `d2bd/src/metrics.rs` until daemon-level ADR 0046 cutover", + "reuseAction" : "adapt", + "reuseSource" : null, + "specId" : "ADR-046-telemetry-audit-and-support", + "specPath" : "docs/specs/ADR-046-telemetry-audit-and-support.md", + "validation" : "`commit_to_launch_latency` benchmark with p95 ≤20 ms assertion; assert no `vm` label in process metrics; `vmm→provider` label rename gate", + "workItemId" : "ADR046-telem-005" + }, + { + "currentSource" : "`nixos-modules/components/observability/host.nix` (`otelRuntimeDir = \"/run/d2b/otel\"`, `hostEgressSocket`, ACL `setfacl` pattern, `scrapeJournal`, `hostCfg.identityName`); `nixos-modules/components/observability/stack.nix` (SigNoz stack, `ingressSources`, per-source `vmName`, `receiverGrpcPort`/`receiverHttpPort`, loopback binding, `cfg.signoz.listenPort`); `nixos-modules/components/observability/guest.nix` (`vm.name`/`vm.env`/`vm.role` identity stamping, guest collector); `packages/d2b-host/src/otel_host_bridge_argv.rs` (`OtelHostBridgeArgvInputs`; vsock forwarding); `packages/d2bd/src/otel_host_bridge_readiness.rs` (readiness gate pattern: `OtelHostBridgeReadiness::{Ready,Pending,Failed}`); `packages/d2b-core/src/processes.rs::ProcessRole::OtelHostBridge`; `packages/d2b-contracts/src/broker_wire.rs::RunnerRole::OtelHostBridge`; `packages/d2b-contract-tests/tests/{policy_observability.rs,minijail_relay_otel.rs}`", + "dataMigration" : "Existing SigNoz data not migrated; v3 starts fresh", + "dependencyOwner" : "ADR046-process-001 + ADR046-provider-001; `observability-otel` Provider owner", + "destination" : "`packages/d2b-provider-observability-otel/src/`, `nixos-modules/components/observability/` (adapted files)", + "detailedDesign" : "`Provider/observability-otel` is an **ordinary optional non-bootstrap Process** (not counted toward the ≤64 MiB mandatory core aggregate). It owns: (1) per-Zone datagram receiver socket at `$ZONE_STATE/telemetry/emitter.sock` (drains frames from core emitters) and OTLP/gRPC Unix socket at `$ZONE_STATE/telemetry/otlp.sock`; (2) the full OTEL SDK with OTLP exporter — only this process links `opentelemetry_sdk`; (3) OTel Collector pipeline per Zone and per Host; (4) vsock OTLP forwarding to obs Zone (replaces socat-based `OtelHostBridgeArgvInputs`); (5) SigNoz stack Nix adapted from `stack.nix` with per-Zone `ingressSources` replacing per-VM `vmName`; (6) journald scrape (optional, disabled by default); (7) self-metrics endpoint. Zone/controller startup does not wait for this Provider. If absent or unready, Zone health is `Degraded` (not `Failed`). Readiness: socket exists and first drain cycle completes successfully. `d2b.observability.host.identityName` option preserved; `vmName` in `ingressSources` populated from Zone name. Primary reuse disposition: `adapt`. Preserved source-plan detail: adapt Nix pipeline shape (replace per-VM `vmName` with per-Zone naming); adapt `OtelHostBridgeArgvInputs` vsock forwarding to native OTLP/gRPC-over-vsock; adapt readiness gate pattern (`OtelHostBridgeReadiness::Ready` → Provider phase `Ready`); adapt `ingressSources` per-VM → per-Zone.", + "integration" : "Core process `BoundedEmitter` → `emitter.sock` → observability-otel collector → `otlp.sock` → vsock → obs Zone SigNoz; Zone startup independent of Provider readiness", + "removalProof" : "`otel_host_bridge_argv.rs` socat runner and `otel_host_bridge_readiness.rs` retired after `observability-otel` Provider delivers native OTLP/vsock and passes conformance; `ProcessRole::OtelHostBridge` and `RunnerRole::OtelHostBridge` retired from `d2b-core/src/processes.rs` and `d2b-contracts/src/broker_wire.rs` after Provider migration", + "reuseAction" : "adapt", + "reuseSource" : null, + "specId" : "ADR-046-telemetry-audit-and-support", + "specPath" : "docs/specs/ADR-046-telemetry-audit-and-support.md", + "validation" : "`emitter_socket_receive`, `emitter_ring_drains_on_socket_available`, `emitter_ring_drop_on_overflow`, `no_vm_label_in_metrics`, `zone_startup_proceeds_without_provider` tests; adapted `policy_observability.rs` tests (retain `loki_native_otel_resource_attributes` and SigNoz-only backend assertions); adapted `minijail_relay_otel.rs` shape test for Provider-managed runner", + "workItemId" : "ADR046-telem-006" + }, + { + "currentSource" : "`nixos-modules/components/observability/host.nix` (`scrapeJournal = hostCfg.scrapeJournal`, `journaldStorageDir = \"/var/lib/d2b-host-otel-collector/journald\"`, cgroup-path filtering pattern for host units)", + "dataMigration" : "Full d2b 3.0 reset; no v2 state/config import", + "dependencyOwner" : "ADR046-telem-006; Nix/observability owner", + "destination" : "`packages/d2b-provider-observability-otel/src/nix/journald.nix` (new Nix fragment)", + "detailedDesign" : "`d2b.zones..observability.journald.enable = false` (default). When enabled: journald receiver follows `z-/*` and `s-/*` cgroup filters. Collector applies redaction: drops `MESSAGE` credential/path patterns, `_CMDLINE`, `_EXE`, `INVOCATION_ID`. Current `scrapeJournal` host option is preserved unchanged. Primary reuse disposition: `adapt`. Preserved source-plan detail: adapt journald receiver config for per-Zone cgroup filter (`z-/*`, `s-/*`).", + "integration" : "`observability-otel` Provider Nix config → OTel Collector journald receiver → redaction filter → obs Zone", + "removalProof" : "None — net-new; no prior owner to remove", + "reuseAction" : "adapt", + "reuseSource" : null, + "specId" : "ADR-046-telemetry-audit-and-support", + "specPath" : "docs/specs/ADR-046-telemetry-audit-and-support.md", + "validation" : "Nix eval test: filter expression set when enabled; test that `_CMDLINE` and `INVOCATION_ID` appear in drop list", + "workItemId" : "ADR046-telem-007" + }, + { + "currentSource" : "`packages/d2b-contract-tests/tests/policy_observability.rs` (`loki_native_otel_resource_attributes` allowlist: `[\"deployment.environment\",\"host.name\",\"service.name\",\"service.namespace\",\"source\",\"vm.env\",\"vm.name\",\"vm.role\"]`; `tempo_stack_signoz_backend_and_collector` SigNoz-only assertion; `startup_tracing_avoids_host_path_fields` forbidden fields); `packages/d2b-contract-tests/tests/policy_metrics.rs` (`EXPECTED_METRICS` table parity with `docs/reference/daemon-metrics.md`)", + "dataMigration" : "Full d2b 3.0 reset; no v2 state/config import", + "dependencyOwner" : "ADR046-telem-001; policy/contract-tests owner", + "destination" : "`packages/d2b-contract-tests/tests/policy_telemetry_redaction.rs` (new); updated `policy_observability.rs`; updated `policy_metrics.rs`", + "detailedDesign" : "(1) Extend `loki_native_otel_resource_attributes` allowlist to include `d2b.zone`, `d2b.provider`, `d2b.component`, `service.version`. (2) Add redaction lint: scan all v3 instrumentation call sites for `realm`, `workload_id`, `node_id`, `vm` (as label key), `path`, `socket`, `argv`, `pid`, `exe`. (3) Add metric label gate: assert no v3 `MetricDescriptor` carries a `vm` label. (4) Add bucket boundary gates for 5 ms and 20 ms. (5) Retain: `startup_tracing_avoids_host_path_fields`; SigNoz-only backend assertion; `tempo_guest_collector_shape`; `config_source = \"realm-controllers\"` absence gate. Primary reuse disposition: `adapt`. Preserved source-plan detail: adapt and extend; keep existing tests; add new policy gates.", + "integration" : "Contract-tests run in workspace check and `make test-drift`", + "removalProof" : "None — net-new; no prior owner to remove", + "reuseAction" : "adapt", + "reuseSource" : null, + "specId" : "ADR-046-telemetry-audit-and-support", + "specPath" : "docs/specs/ADR-046-telemetry-audit-and-support.md", + "validation" : "These tests are their own validation artifact", + "workItemId" : "ADR046-telem-008" + }, + { + "currentSource" : "`nixos-modules/options-observability.nix` (`d2b.observability.host.identityName`, `scrapeJournal`, `otlpIngest.enable`, `signoz.jwtSecretFile`, `signoz.rootPasswordFile`, `signoz.clickhousePasswordFile`, `retention.*`, `sampling.*` — all predecessor options); `nixos-modules/options-realms.nix` (submodule/option-type/assertion pattern); `nixos-modules/components/observability/{host,stack,guest}.nix` (Nix pipeline shape, ACL pattern, `identityName`/`vmName`); `nixos-modules/manifest.nix` (resource-bundle emission pattern); `packages/d2b-contract-tests/tests/policy_observability.rs::loki_native_otel_resource_attributes` (closed allowlist enforcement — adapt as bundle schema policy test)", + "dataMigration" : "Current `d2b.observability.*` options are retained with compat warnings (same pattern as current `retention.*`/`sampling.*` compat options); the v3 `d2b.zones..resources.*` option is the authoritative surface", + "dependencyOwner" : "W0/W1a; Nix integrator (adapts `nixos-modules/options-observability.nix` and `options-realms.nix`)", + "destination" : "`nixos-modules/resources.nix` (uniform `d2b.zones..resources` schema-aware option; `spec.*` option types generated from `ResourceTypeSchema` for each `type`); `nixos-modules/resources-bundle.nix` (ADR-only: sorted integrity-pinned bundle derivation)", + "detailedDesign" : "(1) Implement `d2b.zones..resources = lib.mkOption { type = lib.types.attrsOf (schemaAwareResourceSubmodule); }` where the submodule, given `config.type`, loads the registered `ResourceTypeSchema` and generates `spec.*` option types from it. For `type = \"Provider\"`, `spec.config.*` options are generated from the signed Provider schema for the package identified by `spec.artifactId` (see ADR-046-provider-model-and-packaging). No second bespoke vocabulary; `spec` fields mirror the canonical JSON fields exactly. (2) `resources-bundle.nix` derivation: serialize each resource to canonical sorted JSON (keys alphabetically sorted at every level); sort resources by `(type, name)`; compute generation digest; emit `zone-resources-.json` as Nix store output. Publication handler sets `metadata.managedBy = \"configuration\"` and `metadata.configurationGeneration` on activation — these fields are NOT authored in Nix. (3) `status`, UID, generation, revision, and timestamps are absent from Nix authoring; core fills them. Primary reuse disposition: `adapt`. Preserved source-plan detail: Implement uniform `d2b.zones..resources. = { type; spec; }` option with schema-driven `spec.*` generated option types; adapt option submodule pattern from `options-realms.nix`; adapt pipeline shape from `{host,stack,guest}.nix`; emit canonical sorted ResourceSpec JSON from `resources-bundle.nix`.", + "integration" : "`d2b-core-controller` reads the Nix store path from the activated system closure; secrets never appear in the bundle", + "removalProof" : "`nixos-modules/options-observability.nix` predecessor options retained with compat warnings until `d2b.observability.enable` migration is complete", + "reuseAction" : "adapt", + "reuseSource" : null, + "specId" : "ADR-046-telemetry-audit-and-support", + "specPath" : "docs/specs/ADR-046-telemetry-audit-and-support.md", + "validation" : "`eval_rejects_unknown_type`, `eval_rejects_invalid_emitter_ring_size`, `eval_rejects_unknown_provider_settings`, `eval_rejects_inline_secret_in_settings`, `eval_rejects_unresolved_credential_ref`, `eval_rejects_duplicate_resource_name` nix-unit cases; `bundle_is_sorted_canonically`, `bundle_digest_is_deterministic`, `bundle_contains_no_secret_values`, `bundle_schema_validates_against_provider_schema` contract tests", + "workItemId" : "ADR046-telem-009" + }, + { + "currentSource" : "`packages/d2b-contract-tests/tests/policy_observability.rs::startup_tracing_avoids_host_path_fields` (forbidden-field pattern enforcement — adapt as bundle forbidden-field gate); `packages/d2b-contract-tests/tests/policy_metrics.rs` (metric inventory policy test pattern); `packages/d2b-priv-broker/src/runtime.rs` (current runtime schema load/verify pattern); `packages/d2b-contracts/src/provider_registry_v2.rs::ProviderBindingV2` (non-exhaustive signed schema contract)", + "dataMigration" : "Full d2b 3.0 reset; no v2 state/config import", + "dependencyOwner" : "ADR046-telem-009 + ADR046-telem-006 + ADR046-store-001; schema/validation owner", + "destination" : "`nixos-modules/resources-bundle.nix` (build-time validation step 4 in the `resources-bundle` derivation); `packages/d2b-core-controller/src/configuration.rs` (runtime activation checks)", + "detailedDesign" : "Build-time: (1) For each `Provider` resource, fetch the `resourceTypeSchema` output from the package; validate `settings` JSON against the JSON Schema; fail the build on schema mismatch or unknown fields. (2) Assert no resource spec field contains a bare secret/path/argv (forbidden-field pattern from `startup_tracing_avoids_host_path_fields`). Runtime: (3) Core-controller re-validates Provider package identity (per ADR-046-provider-model-and-packaging) against the installed package; resolves Credential refs; checks conflict/bounds; rejects with closed-enum `generation-rejected` reason on any failure; no partial activation. (4) Provider schema mismatch between the bundle's schema and the installed Provider's live schema → reject, emit `generation-rejected{reason=\"package-identity-mismatch\"}`. Primary reuse disposition: `adapt`. Preserved source-plan detail: Adapt `startup_tracing_avoids_host_path_fields` forbidden-field pattern for bundle schema gate; adapt `ProviderBindingV2` non-exhaustive contract for Provider-specific settings schema fingerprint.", + "integration" : "Nix `resources-bundle.nix` derivation gate + core-controller `configuration.rs` activation path", + "removalProof" : "None — net-new; no prior owner to remove; this is new tooling", + "reuseAction" : "adapt", + "reuseSource" : null, + "specId" : "ADR-046-telemetry-audit-and-support", + "specPath" : "docs/specs/ADR-046-telemetry-audit-and-support.md", + "validation" : "`bundle_schema_validates_against_provider_schema` bundle contract test; `generation_rejected_emits_audit_record` cleanup contract test with a `schema-validation-failed` reason; add a nix-unit case `eval_rejects_unknown_fields_against_signed_schema` that runs the bundle derivation with a schema mismatch and asserts build failure", + "workItemId" : "ADR046-telem-010" + }, + { + "currentSource" : "`packages/d2bd/src/daemon_audit.rs` (hash-chain `ResourceMutation`-like append pattern — adapt for cleanup audit records); `packages/d2b-priv-broker/src/audit.rs` (`AuditWriteClass::{Standard,Unprivileged}` — cleanup audit records use `Standard` durability); `packages/d2b-realm-core/src/audit.rs::AuditChainLink::new` (hash-chain append for cleanup audit records); `nixos-modules/manifest.nix` (prior-generation retention pattern in the current bundle contract)", + "dataMigration" : "None — the `managedBy`/`configurationGeneration`/`deletionRequestedAt` fields are new; existing resources gain them on first v3 activation", + "dependencyOwner" : "ADR046-telem-009 + ADR046-telem-010 + ADR046-telem-012 + ADR046-store-001; core-controller owner", + "destination" : "`packages/d2b-core-controller/src/{configuration.rs, ownership.rs}`", + "detailedDesign" : "(1) On new generation activation, every stored `managedBy=configuration` resource absent from the new configured set receives `deletionRequestedAt` plus `deletion-pending`; controller/API-managed resources are untouched. (2) Activation returns after durable intent queueing and does not wait for cleanup. (3) The ownership handler drives child-before-parent finalizers. (4) When finalizers clear, one atomic store transaction writes the `Deleted` revision/change event and removes the row and indexes. After commit, the audit subsystem appends `ResourceMutation{event=\"deleted\", trigger=\"config-cleanup\"}` from that revision using a dedup/exactly-once recovery key; audit append is not part of the store transaction. (5) Stall detection sets `cleanup-stalled` without force-removing finalizers. (6) Prior generations use count retention, default 3 and range 1..16, with no TTL. (7) Core sets `managedBy`/`configurationGeneration` in persisted resources; input bundles omit both. Primary reuse disposition: `adapt`. Preserved source-plan detail: Adapt hash-chain append from `daemon_audit.rs` for `ResourceMutation{trigger=\"config-cleanup\"}` records; adapt prior-generation retention window from `manifest.nix` pattern.", + "integration" : "`d2b-core-controller::configuration.rs` (generation activation); `d2b-core-controller::ownership.rs` (cleanup ordering and atomic final deletion); `d2b-audit` sink (cleanup audit records)", + "removalProof" : "None — net-new; no prior owner to remove; this is new behavior", + "reuseAction" : "adapt", + "reuseSource" : null, + "specId" : "ADR-046-telemetry-audit-and-support", + "specPath" : "docs/specs/ADR-046-telemetry-audit-and-support.md", + "validation" : "All tests in \"Configuration-owned cleanup contract tests\" subsection; additionally: `managedby_configuration_set_on_activated_resources`, `controller_created_resources_have_managedby_controller`, `absent_resource_receives_delete_on_new_generation`, `deletion_sets_deletionrequestedat_not_phase`, `final_deletion_is_atomic`, `cleanup_does_not_touch_controller_children`, `pending_cleanup_condition_set_on_zone`, `zone_is_degraded_not_failed_during_cleanup`, `pending_cleanup_cleared_after_deletion_completes`, `prior_generation_retained_count_based`, `rollback_schedules_delete_for_new_generation_resources`, `audit_segments_preserved_on_provider_delete`, `cleanup_stall_condition_set`, `generation_rejected_emits_audit_record`", + "workItemId" : "ADR046-telem-011" + }, + { + "currentSource" : "`packages/d2b-realm-core/src/audit.rs` (`AuditHash::parse`, `AuditChainLink::new`/`verify`, `AuditChainRecord{stream: AuditStreamKind, realm: RealmPath, node: NodeId}`, `AuditStreamKind::{Gateway,RemoteNode,Daemon}`, `AuditSinkHealth`, `AuditRetentionFloorStatus`); `packages/d2bd/src/daemon_audit.rs` (hash-chain append algorithm, `prev_hash`/`record_hash` SHA-256 pattern, daily segment files `daemon-events-YYYY-MM-DD.jsonl`, `DaemonEvent` additive contract); `packages/d2b-priv-broker/src/audit.rs` (`AuditWriteClass::{Privileged,Unprivileged}`, `AuditDropSummary`, `DEFAULT_AUDIT_WRITES_PER_SECOND = 4096`, O_APPEND CLOEXEC file open, `AuditDropWarningState`); `packages/d2b-gateway-runtime/src/audit_jsonl.rs` (`JsonlGatewayAudit`, `DEFAULT_GATEWAY_AUDIT_RETENTION_DAYS = 14`, `prune_old` rotation algorithm); `packages/d2b-priv-broker/src/ops/audit_op.rs` (`OpAuditRecord`, `SwtpmDirAudit`, `SwtpmDirResult`, `SwtpmMarkerResult`); `packages/d2b-realm-core/src/ids.rs` (`OperationId`, `CorrelationId`, `PrincipalId` — `PrincipalId` becomes `subject_digest`); `packages/d2b/tests/audit_contract.rs`; `packages/d2b-priv-broker/tests/broker_export_audit.rs`", + "dataMigration" : "v3 bootstrap; existing daemon/broker JSONL files not migrated", + "dependencyOwner" : "W0/W1a; audit crate owner", + "destination" : "`packages/d2b-audit/src/{hash_chain.rs,segment.rs,rate_limit.rs,record_types.rs,sink.rs,export.rs}`", + "detailedDesign" : "`d2b-audit` provides: typed record structs per class; canonical serialization with `zone` replacing `realm: RealmPath`; SHA-256 hash chain (extracted from `daemon_audit.rs`); segment writer (O_APPEND CLOEXEC, 64 MiB / UTC-midnight rotation); 30-day compaction (adapts `prune_old` from `JsonlGatewayAudit`); `AuditWriteClass::{Privileged,Standard,BestEffort}` (extends current `{Privileged,Unprivileged}`); rate-limit with privileged-never-dropped invariant; export iterator with inline hash-break reporting. `AuditStreamKind` re-versioned: `Daemon→Zone`, `Gateway→ZoneLink`, `RemoteNode→RemoteZone`. `AuditChainRecord` re-versioned: `{zone: String}` replaces `{realm: RealmPath, node: NodeId}`. Primary reuse disposition: `adapt`. Preserved source-plan detail: extract unchanged: `AuditHash`, `AuditChainLink` from `d2b-realm-core/src/audit.rs`; copy hash-chain append algorithm from `daemon_audit.rs`; copy `AuditWriteClass`/rate-limit/rotation/prune from broker `audit.rs`; adapt `JsonlGatewayAudit` segment writer; adapt `OpAuditRecord` to `BrokerEffect` record class.", + "integration" : "Zone runtime, core-controller, Process Providers, broker effect bridge → `d2b-audit` sink; `d2b zone audit export` → export iterator", + "removalProof" : "`daemon_audit.rs`, broker `audit.rs`, `JsonlGatewayAudit` retired per-component after `d2b-audit` sink achieves parity", + "reuseAction" : "adapt", + "reuseSource" : null, + "specId" : "ADR-046-telemetry-audit-and-support", + "specPath" : "docs/specs/ADR-046-telemetry-audit-and-support.md", + "validation" : "`audit_record_hash_chain`, `audit_record_schema` (no `realm`/`node` fields), `audit_segment_rotation`, `audit_rate_limit_privileged_never_dropped`, `audit_unavailable_blocks_privileged`", + "workItemId" : "ADR046-telem-012" + }, + { + "currentSource" : "`packages/d2b-priv-broker/src/ops/audit_op.rs` (`OpAuditRecord` structural pattern — operation, peer_uid, decision, result fields); `packages/d2b-realm-core/src/audit.rs::AuditEnvelope{principal: PrincipalId, scope: AuthorizationScope, decision: AuthzDecision}` (principal → v3 `subject_digest`)", + "dataMigration" : "Full d2b 3.0 reset; no v2 state/config import", + "dependencyOwner" : "ADR046-telem-012 + ADR046-store-001; store/authz owner", + "destination" : "`packages/d2b-resource-store-redb/src/audit.rs`, `packages/d2b-core-controller/src/authz_audit.rs`", + "detailedDesign" : "`ResourceMutation` records emitted by the store actor inside the write transaction before commit returns. The audit sink must durably fsync the audit record before returning the commit success (privileged durability class). `RBACChange` emitted by the authz handler in the same write transaction. `subject_digest` = SHA-256 of normalized canonical subject string from v3 `AuthenticatedSubjectContext` (ADR-046-componentsession-and-bus). Primary reuse disposition: `adapt`. Preserved source-plan detail: adapt `OpAuditRecord` structural pattern for `ResourceMutation` / `RBACChange` record classes; adapt `PrincipalId` → `subject_digest` derivation.", + "integration" : "Store write transaction → `d2b-audit` sink → fsync → commit result", + "removalProof" : "None — net-new; no prior owner to remove", + "reuseAction" : "adapt", + "reuseSource" : null, + "specId" : "ADR-046-telemetry-audit-and-support", + "specPath" : "docs/specs/ADR-046-telemetry-audit-and-support.md", + "validation" : "Integration test: 100 mutations → verify hash-chained audit records with `zone` field, no `realm` field", + "workItemId" : "ADR046-telem-013" + }, + { + "currentSource" : "`packages/d2b-gateway/src/audit.rs` (`GatewayAuditEvent`, `GatewayAuditKind::{DisplaySessionOpenAdmitted,DisplaySessionOpenDenied,DisplaySessionRunning,DisplaySessionClosed}`, `GatewayAudit` trait, `NoopGatewayAudit`); `packages/d2b-realm-core/src/audit.rs::AuditEnvelope{realm, principal, scope, decision, trace}` (fields adapted to v3 `SessionConnect` record class)", + "dataMigration" : "Full d2b 3.0 reset; no v2 state/config import", + "dependencyOwner" : "ADR046-telem-012 + ADR046-session-001 + ADR046-session-003; session/bus owner", + "destination" : "`packages/d2b-session/src/audit.rs`, `packages/d2b-bus/src/audit.rs`", + "detailedDesign" : "`SessionConnect` records emitted at handshake completion. `GatewayAuditKind::DisplaySessionOpenAdmitted/Denied` → `event=\"connect\"`, `authz_decision=\"allowed/denied\"`. `GatewayAuditKind::DisplaySessionRunning` → informational `ProcessEffect`. `GatewayAuditKind::DisplaySessionClosed` → `event=\"close\"`. `transport_class=zone_link` covers what the current `AuditStreamKind::Gateway` stream recorded for gateway-backed realm sessions. `RouteAdmission` records emitted at bus route resolution for denied routes. Primary reuse disposition: `adapt`. Preserved source-plan detail: adapt `GatewayAudit` trait pattern for `SessionConnect` and `RouteAdmission` record classes; `NoopGatewayAudit` pattern reused for test sinks.", + "integration" : "Session engine and bus router → `d2b-audit` sink", + "removalProof" : "`NoopGatewayAudit` and gateway JSONL sink retired after gateway is on v3 resource API", + "reuseAction" : "adapt", + "reuseSource" : null, + "specId" : "ADR-046-telemetry-audit-and-support", + "specPath" : "docs/specs/ADR-046-telemetry-audit-and-support.md", + "validation" : "Session connect/close/auth-failure audit tests; `GatewayAuditKind` → `SessionConnect` mapping test", + "workItemId" : "ADR046-telem-014" + }, + { + "currentSource" : "`packages/d2b/tests/audit_contract.rs` (`d2b audit --strict` returns 78; `auditResponse` relay; `authz-audit-requires-admin` denial; daemon-down exit 1 without bash fallback); `packages/d2b-priv-broker/tests/broker_export_audit.rs` (`export_audit_requires_admin_and_exports_op_audit_records`: admin-only, path-free, NDJSON `ExportBrokerAuditOk` shape, `peer_uid` field, `ApplyNftables` operation name in records)", + "dataMigration" : "Full d2b 3.0 reset; no v2 state/config import", + "dependencyOwner" : "ADR046-telem-012; CLI owner", + "destination" : "`packages/d2b/src/zone_audit.rs` (new `d2b zone audit export` subcommand); `packages/d2b/tests/zone_audit_contract.rs`", + "detailedDesign" : "`d2b zone audit export` opens segments read-only (shared flock), streams NDJSON to stdout, validates hash chain inline, reports breaks as inline error records, enforces `audit-export` verb via resource API (admin-only, same `SO_PEERCRED`/Role check as current `ExportBrokerAuditOk`). Assert no `realm`, `node`, `workload_id` fields in exported records. Primary reuse disposition: `adapt`. Preserved source-plan detail: adapt audit CLI contract test (daemon-down/exit behavior); adapt broker export test to new `zone` field and v3 record schema.", + "integration" : "`d2b` CLI → resource API `audit-export` verb → `d2b-audit` export iterator → stdout", + "removalProof" : "`d2b audit` legacy command retained until `d2b zone audit export` covers all record classes", + "reuseAction" : "adapt", + "reuseSource" : null, + "specId" : "ADR-046-telemetry-audit-and-support", + "specPath" : "docs/specs/ADR-046-telemetry-audit-and-support.md", + "validation" : "`export_audit.rs`: admin-only, hash break inline, no old field names (`realm`/`node`/`workload_id`), no path/argv in output, exit 0 on clean chain", + "workItemId" : "ADR046-telem-015" + }, + { + "currentSource" : "`packages/d2b/tests/host_doctor_contract.rs` (env-redirect sandbox: `D2B_BROKER_SOCKET`, `D2B_PUBLIC_SOCKET`, `D2B_DAEMON_STATE_DIR`, `D2B_METRICS_URL`, `D2B_MANIFEST_PATH`; `doctor::render_summary` JSON envelope fields: `command`, `mode`, `broker_ready`, per-check `status`+`data`, `summary`, `exitCode`); `packages/d2bd/src/audit_check.rs` (`defects` array audit-chain validation pattern); `packages/d2bd/src/lib.rs` (doctor read-only path: `host doctor --read-only` reads from `D2B_DAEMON_STATE_DIR`, pidfd_table file, kernel-module check file, metrics URL)", + "dataMigration" : "Full d2b 3.0 reset; no v2 state/config import", + "dependencyOwner" : "ADR046-core-001 + ADR046-telem-012; CLI/doctor owner", + "destination" : "`packages/d2b/src/zone_doctor.rs`, `packages/d2b/tests/zone_doctor_contract.rs`", + "detailedDesign" : "`d2b zone doctor [--zone ] [--json]` reads resource status from Zone API (read-only verb), OTEL self-metrics from `observability-otel` Provider endpoint (optional), and audit segment inventory from `d2b-audit` segment reader. Named check set from this spec. Exit 0 on all-ready; 1 on any warn/error. Env-redirect sandbox for all test fixtures. Current `MANIFEST_JSON` fixture pattern adapted: `\"_observability\": {\"enabled\": false}` test ensures OTEL probe short-circuits cleanly. Primary reuse disposition: `adapt`. Preserved source-plan detail: adapt env-redirect sandbox test harness; adapt `defects` array pattern for `audit-hash-chain-clean` check; adapt `broker_ready` → `zone_phase` field.", + "integration" : "`d2b` CLI → Zone resource API status reads + audit segment reader", + "removalProof" : "`d2b host doctor` retained until `d2b zone doctor` covers all check parity", + "reuseAction" : "adapt", + "reuseSource" : null, + "specId" : "ADR-046-telemetry-audit-and-support", + "specPath" : "docs/specs/ADR-046-telemetry-audit-and-support.md", + "validation" : "`zone_doctor_contract.rs`: all-ready/degraded/quarantine/otel-absent/audit-absent fixtures; no resource names/paths/argv/PIDs; `zone_phase` field present; no legacy `broker_ready` field", + "workItemId" : "ADR046-telem-016" + }, + { + "currentSource" : "`packages/d2b/tests/host_doctor_contract.rs` (env-redirect sandbox scaffold — no current `support-bundle` equivalent exists)", + "dataMigration" : "Full d2b 3.0 reset; no v2 state/config import", + "dependencyOwner" : "ADR046-telem-016; CLI/doctor owner", + "destination" : "`packages/d2b/src/zone_support_bundle.rs`, `packages/d2b/tests/zone_support_bundle_contract.rs`", + "detailedDesign" : "`d2b zone support-bundle [--zone ]` requires `support-bundle` verb. Reads bounded resource status snapshots (32 per type, 512 total; metadata + status only; no spec bytes; no `metadata.name`), controller queue depths, schema catalog (names+versions only), audit segment inventory, OTEL collector metrics summary, bounded structured log ring (2000 entries). NDJSON output. On quarantine: `bundle_completeness: \"partial\"`, exit 1. Primary reuse disposition: `adapt`. Preserved source-plan detail: reuse env-redirect sandbox scaffold.", + "integration" : "`d2b` CLI → Zone resource API list (status subresource only) + controller introspection + audit segment reader + OTEL self-metrics", + "removalProof" : "None — net-new; no prior owner to remove", + "reuseAction" : "adapt", + "reuseSource" : null, + "specId" : "ADR-046-telemetry-audit-and-support", + "specPath" : "docs/specs/ADR-046-telemetry-audit-and-support.md", + "validation" : "`zone_support_bundle_contract.rs`: complete/partial bundles; no spec/name/path/argv; field completeness", + "workItemId" : "ADR046-telem-017" + }, + { + "currentSource" : "`packages/d2b-core/src/unsafe_local_workloads.rs` (`UnsafeLocalWorkloadsJson`, `UnsafeLocalWorkload`, `UnsafeLocalLauncherItem`, `UNSAFE_LOCAL_WORKLOADS_SCHEMA_VERSION = \"v2\"`, `MAX_UNSAFE_LOCAL_WORKLOADS = 256`); `packages/d2b-contracts/src/unsafe_local_wire.rs` (`HelperHello.uid: u32`, `HelperLaunchRequest`, `HelperShellRequest`, `HelperScopeKind::{Exec,Shell}`, `DaemonToUnsafeLocalHelper`, `UnsafeLocalHelperToDaemon`); `packages/d2bd/src/unsafe_local_helper.rs` (`HelperRegistry::new(daemon_uid, allowed_uids)`, `dispatch_launch`, `bind_helper_socket`); `packages/d2b-unsafe-local-helper/src/{main,protocol,runtime,systemd}.rs` (`HelperClient`, `ScopeRuntime`, `run_scope_supervisor`, `SystemdUserScopeManager`); `nixos-modules/options-realms-workloads.nix` (lines 221, 233–235 `kind = \"unsafe-local\"` description; lines 264–275 null `stateDir`/`runDir`); `nixos-modules/unsafe-local-workloads-json.nix` (`runtimeKind = \"unsafe-local\"`, `providerId = \"unsafe-local\"`); `nixos-modules/unsafe-local-helper.nix` (service unit)", + "dataMigration" : "Full d2b 3.0 reset; no v2 state/config import", + "dependencyOwner" : "ADR046-telem-012 + ADR046-core-001; `Provider/system-core` owner", + "destination" : "`packages/d2b-provider-system-core/src/{host_reconciler.rs,host_status.rs,host_process_audit.rs}`; adapted `nixos-modules/unsafe-local-workloads-json.nix`; `packages/d2b-provider-system-core/tests/host_posture_contract.rs`", + "detailedDesign" : "`Provider/system-core` reconciler: (1) On user-only `Host` resource creation (`defaultDomain=user`, `allowedDomains=[user]`), set `status.isolationPosture = \"none\"` and `status.isolationPostureMessage = \"...\"` unconditionally; reject any operator-supplied value for these fields. Host resources with other execution policies do not receive `isolationPosture`. (2) On every user-only Host process launch: emit `ProcessEffect{event:\"launch\", provider:\"system-core-user\", domain:\"user\", no_isolation:true, ...}` audit record. (3) On every user-only Host process stop: emit `ProcessEffect{event:\"stop\", ...}`. (4) `d2b zone list`/`inspect` CLI renders `⚠ no isolation boundary (user domain)` annotation only for `Host` resources with `isolationPosture: \"none\"`; annotation is not suppressible. (5) `isolation-posture-declared` doctor check: passes when user-only `Host` resource status has `isolationPosture: \"none\"`; omitted when Zone has no user-only `Host` resources. (6) `no_isolation=true` is emitted in `ProcessEffect` records only; it does not appear in any OTEL span attribute, log field, or metric label. Primary reuse disposition: `adapt`. Preserved source-plan detail: adapt `UnsafeLocalWorkload` private-bundle contract for the `Host` resource spec payload; adapt `HelperRegistry::allowed_uids` constraint as `defaultUserRef=User/` validation; adapt Nix `unsafe-local-workloads-json.nix` emitter for the new Host resource shape; gap-fill: add `ProcessEffect{no_isolation:true}` at `dispatch_launch` / stop call sites.", + "integration" : "`Provider/system-core` reconciler → `d2b-audit` sink; `d2b zone doctor` → resource status check; `d2b zone list`/`inspect` → CLI output renderer", + "removalProof" : "`d2b-unsafe-local-helper` binary and `DaemonToUnsafeLocalHelper`/`UnsafeLocalHelperToDaemon` wire types retired after `Provider/system-core` Process Provider supervisor ticket migration; `nixos-modules/unsafe-local-helper.nix` Nix unit retired after migration; `nixos-modules/unsafe-local-workloads-json.nix` adapted (not deleted) to emit Host resource spec format", + "reuseAction" : "adapt", + "reuseSource" : null, + "specId" : "ADR-046-telemetry-audit-and-support", + "specPath" : "docs/specs/ADR-046-telemetry-audit-and-support.md", + "validation" : "`host_posture_contract.rs` tests from the Host posture tests section of this spec; `d2b-contract-tests/tests/policy_telemetry_redaction.rs` asserts `no_isolation` key absent from all span/metric/log surfaces", + "workItemId" : "ADR046-telem-018" + }, + { + "currentSource" : "Main commit source row below: `packages/d2b-session/` and `packages/d2b-contracts/src/v2_component_session.rs` at `a1cc0b2d`.", + "dataMigration" : "Full d2b 3.0 reset; no v2 session runtime state/config import", + "dependencyOwner" : "ADR046-telem-001 (d2b-telemetry must exist first for MetricsSink injection); session owner", + "destination" : "`packages/d2b-session/` copied verbatim; v3 contract extension renames endpoint purpose values while preserving backward wire compatibility during transition.", + "detailedDesign" : "Noise handshake, record protection, scheduler, named streams, cancellation, attachments, deadlines, metrics injection, ttrpc serving, and generation-discovery behavior from the selected source. Primary reuse disposition: `adapt`. Preserved source-plan detail: Copy/adopt the core ComponentSession runtime; adapt EndpointPurpose/ServicePackage/RealmSessionAuthority names listed in Excluded ADR45 assumptions and inject a `d2b-telemetry` MetricsSink..", + "integration" : "`d2b-bus` route handler calls `serve_ttrpc_services`; `d2b-session-unix` provides `OwnedTransport` impl; `d2b-telemetry` `MetricsSink` impl feeds `d2b_session_*` metrics inventory from this spec.", + "removalProof" : "ADR45 topology enum names listed in Excluded ADR45 assumptions are adapted; no prior v3 session runtime owner to remove.", + "reuseAction" : "adapt", + "reuseSource" : null, + "specId" : "ADR-046-telemetry-audit-and-support", + "specPath" : "docs/specs/ADR-046-telemetry-audit-and-support.md", + "validation" : "Adopt `tests/component_session.rs` and `tests/noise_vectors.rs` unchanged; extend with v3 `EndpointPurpose` enum gate test; add `d2b-contract-tests/tests/component_session_v2_vectors.rs` (existing at `a1cc0b2d`) as-is.", + "workItemId" : "ADR046-telem-019" + }, + { + "currentSource" : "Main commit source row below: `packages/d2b-session-unix/` at `a1cc0b2d`.", + "dataMigration" : "Full d2b 3.0 reset; no v2 transport runtime state/config import", + "dependencyOwner" : "ADR046-telem-019; Unix transport owner", + "destination" : "`packages/d2b-session-unix/` copied verbatim.", + "detailedDesign" : "SO_PASSCRED verification, pidfd identity verification, multi-scope credit reservation, emergency headroom, seqpacket atomic CLOEXEC transfer, stream reassembly, vsock transport, and descriptor policy enforcement. Primary reuse disposition: `adapt`. Preserved source-plan detail: Copy verbatim; retain `host-socket`/`native-vsock` feature flags; no ADR45 topology adaptation required..", + "integration" : "`d2b-bus` Zone-local listeners use `UnixSeqpacketTransport`; Provider agent connections use vsock transport from this crate; `CreditPool`/`CreditScopeSet` enforce per-Zone attachment FD budgets.", + "removalProof" : "None — net-new; no prior owner to remove", + "reuseAction" : "adapt", + "reuseSource" : null, + "specId" : "ADR-046-telemetry-audit-and-support", + "specPath" : "docs/specs/ADR-046-telemetry-audit-and-support.md", + "validation" : "Adopt all `unix_session.rs` tests unchanged.", + "workItemId" : "ADR046-telem-020" + }, + { + "currentSource" : "Main commit source row below: `packages/d2b-client/` at `a1cc0b2d`.", + "dataMigration" : "Full d2b 3.0 reset; no v2 client state/config import", + "dependencyOwner" : "ADR046-telem-019; client owner", + "destination" : "`packages/d2b-client/` copied; DaemonClient/GuestClient adapted to v3 service packages; `MetadataInput::with_trace` drives `TraceContext` propagation.", + "detailedDesign" : "MetadataInput trace/correlation/idempotency, retry policy, cancellation token, generic client over resolver/connector/clock, typed service proxies, stream terminal, and host-socket verifier. Primary reuse disposition: `adapt`. Preserved source-plan detail: Copy client layer; adapt service package/method sets, local-root Zone identity, and TraceContext propagation for v3..", + "integration" : "Every controller/service that makes outbound calls uses `Client`; `MetadataInput::with_trace` feeds `d2b_api_request_duration_seconds` trace-id into `d2b.bus.route` span.", + "removalProof" : "ADR45 Daemon/Guest wrappers and RealmPath identity assumptions are adapted to v3 service packages/Zone name; no prior v3 client owner to remove.", + "reuseAction" : "adapt", + "reuseSource" : null, + "specId" : "ADR-046-telemetry-audit-and-support", + "specPath" : "docs/specs/ADR-046-telemetry-audit-and-support.md", + "validation" : "Adopt typed-route, proxy-reuse, and cancel tests unchanged. Add v3 service-package name gate test.", + "workItemId" : "ADR046-telem-021" + }, + { + "currentSource" : "Main commit source row below: `packages/d2b-provider/` and `packages/d2b-provider-toolkit/` at `a1cc0b2d`.", + "dataMigration" : "Full d2b 3.0 reset; no v2 ProviderRegistry runtime state import", + "dependencyOwner" : "ADR046-telem-019 + ADR046-telem-021; Provider owner", + "destination" : "`packages/d2b-provider/` and `packages/d2b-provider-toolkit/` copied with v3 session admission and bus routing adaptations.", + "detailedDesign" : "ProviderRegistry factory registration, bounded admission, RPC proxy fail-closed behavior, generated provider service admission/dispatch, redaction wrappers, and conformance gates. Primary reuse disposition: `adapt`. Preserved source-plan detail: Copy provider runtime/toolkit; adapt SessionIdentity Zone name and route through `d2b-bus` instead of daemon embedding..", + "integration" : "Each v3 Provider process embeds `ProviderRegistry` + `GeneratedProviderServiceServer`; `check_provider_conformance` runs in Provider install-time conformance check (feeds `d2b_provider_reconcile_total{outcome=\"error\"}` on failure).", + "removalProof" : "Daemon-embedded ADR45 ProviderRegistry wiring is excluded; v3 provider-process registry replaces it without importing state.", + "reuseAction" : "adapt", + "reuseSource" : null, + "specId" : "ADR-046-telemetry-audit-and-support", + "specPath" : "docs/specs/ADR-046-telemetry-audit-and-support.md", + "validation" : "Adopt all `conformance.rs` and `runtime.rs` tests unchanged. Add v3 `SessionIdentity` zone-name gate. Add conformance-failure → `d2b_provider_reconcile_total` metric integration test.", + "workItemId" : "ADR046-telem-022" + }, + { + "currentSource" : "Main commit source row below: `packages/d2b-gateway-runtime/src/provider_agent.rs` at `a1cc0b2d` plus `tests/provider_agent_v2.rs`.", + "dataMigration" : "Full d2b 3.0 reset; no v2 provider-agent runtime state import", + "dependencyOwner" : "ADR046-telem-022 + ADR046-telem-014; Provider agent / observability-otel owner", + "destination" : "`packages/d2b-provider-observability-otel/src/agent.rs` adapted; ComponentSessionDriver mock becomes shared Provider session fixture.", + "detailedDesign" : "Session-bound ProviderAgentProcess construction, bounded audit snapshot, closed-set ProviderAgentError paths, and full ComponentSessionDriver mock for hermetic Provider tests. Primary reuse disposition: `adapt`. Preserved source-plan detail: Adapt provider-agent session wrapper and audit ring; replace gateway-runtime audit envelope with v3 `d2b-audit` records..", + "integration" : "`observability-otel` Provider embeds a `ProviderAgentProcess`; session connect/disconnect emits `SessionConnect` audit records via `d2b-audit`; `ProviderAgentAuditEvent` ring feeds `d2b_provider_reconcile_total` metric on session error.", + "removalProof" : "ADR45 gateway-runtime AuditEnvelope/realm custody and launch path are excluded; v3 provider agent is supervised by Provider/system-core or the owning Provider supervisor.", + "reuseAction" : "adapt", + "reuseSource" : null, + "specId" : "ADR-046-telemetry-audit-and-support", + "specPath" : "docs/specs/ADR-046-telemetry-audit-and-support.md", + "validation" : "Adopt `provider_agent_v2.rs` mock harness unchanged as shared v3 Provider session fixture. Add v3 audit-bridge test: provider-agent session → `SessionConnect{transport_class=\"zone_link\"}` record emitted.", + "workItemId" : "ADR046-telem-023" + }, + { + "currentSource" : "Main commit source row below: `packages/d2b-realm-router/` at `a1cc0b2d`.", + "dataMigration" : "Full d2b 3.0 reset; no v2 routing state import", + "dependencyOwner" : "ADR046-telem-019 + ADR046-session-003; bus routing owner", + "destination" : "`packages/d2b-bus/src/routing.rs` adapted from `service_v2.rs`; RemoteNodeErrorKind stable code pattern becomes v3 BusErrorKind.", + "detailedDesign" : "Realm/Zone service ttrpc handler table, session authority constructors, host/gateway credential custody split, stable low-cardinality error codes, and binding/shortcut bounds. Primary reuse disposition: `adapt`. Preserved source-plan detail: Adapt realm service routing to Zone service routing; preserve credential-custody, bounded routing, and stable error-code behavior..", + "integration" : "`d2b-bus` route handler adapts `RealmServiceServer` dispatch table; `RemoteNodeErrorKind::code()` values feed `d2b_bus_route_total{outcome}` metric labels; `CredentialCustody::Host` maps to `purpose_class=local` in `d2b_session_connect_total`.", + "removalProof" : "ADR45 RealmId and RealmService names are adapted to Zone/resource UID routing; no prior v3 bus routing owner to remove.", + "reuseAction" : "adapt", + "reuseSource" : null, + "specId" : "ADR-046-telemetry-audit-and-support", + "specPath" : "docs/specs/ADR-046-telemetry-audit-and-support.md", + "validation" : "Adopt `authority_keeps_remote_credentials_in_gateway_guests` test renamed to `authority_keeps_remote_credentials_in_zone_link_sessions`; adapt `RealmId` → Zone name; adopt `authenticated_bootstrap_enrollment_route_and_shortcut_lifecycle` renamed with zone terminology.", + "workItemId" : "ADR046-telem-024" + }, + { + "currentSource" : "Main commit source row below: `packages/d2bd/` routing/provider effects files and tests at `a1cc0b2d`.", + "dataMigration" : "Full d2b 3.0 reset; no v2 daemon routing/provider-effects state import", + "dependencyOwner" : "ADR046-telem-022 + ADR046-telem-024; core-controller routing owner", + "destination" : "`packages/d2b-bus/src/service_router.rs` and `packages/d2b-core-controller/src/provider_effects.rs`.", + "detailedDesign" : "Closed-set service.package route gates, provider lifecycle effect-adapter composition, closed-set error mapping, fixed local policy invariants, and bootstrap/enrolled credential binding shape. Primary reuse disposition: `adapt`. Preserved source-plan detail: Adapt route-gate and ProviderLifecycleDispatch patterns; exclude daemon-embedded effect composition and route through v3 bus/provider processes..", + "integration" : "Bus service router uses `service.package` closed-set matching from route-gate pattern; `ProviderLifecycleDispatch` feeds `d2b_provider_component_phase` metric.", + "removalProof" : "ADR45 daemon-embedded effect adapters and realm child supervisor assumptions are excluded; v3 bus/provider effects own the adapted behavior.", + "reuseAction" : "adapt", + "reuseSource" : null, + "specId" : "ADR-046-telemetry-audit-and-support", + "specPath" : "docs/specs/ADR-046-telemetry-audit-and-support.md", + "validation" : "Port `local_daemon_policy_is_fixed_and_has_no_negotiation_or_fd_surface` invariant to v3 bus local policy test; port `every_generated_daemon_method_has_one_typed_adapter` to v3 bus method adapter completeness test.", + "workItemId" : "ADR046-telem-025" + }, + { + "currentSource" : "Main commit source row below: ComponentSession vector/conformance tests at `a1cc0b2d`.", + "dataMigration" : "None — docs/tooling only; no runtime state", + "dependencyOwner" : "ADR046-telem-019; contract-tests owner", + "destination" : "`packages/d2b-contract-tests/tests/component_session_v2_vectors.rs` and `tests/noise_vectors.rs` copied verbatim.", + "detailedDesign" : "Pinned Noise KAT vectors, transcript/PSK mutation rejection, typed bootstrap admission fixture, and public-key corruption rejection remain the session wire security ground truth. Primary reuse disposition: `adapt`. Preserved source-plan detail: Copy the vector and conformance tests verbatim; add v3 constant gate for ComponentSession major/minor version..", + "integration" : "These tests run in `make test-rust` / `cargo test -p d2b-contract-tests` and `cargo test -p d2b-session`. They are gating for any Noise library update.", + "removalProof" : "None — net-new; no prior owner to remove", + "reuseAction" : "adapt", + "reuseSource" : null, + "specId" : "ADR-046-telemetry-audit-and-support", + "specPath" : "docs/specs/ADR-046-telemetry-audit-and-support.md", + "validation" : "These tests are self-validating. Add one gate: assert `COMPONENT_SESSION_MAJOR = 2` and `COMPONENT_SESSION_MINOR = 0` constants are unchanged in v3 contract.", + "workItemId" : "ADR046-telem-026" + }, + { + "currentSource" : "Main commit source row below: `packages/d2b-session/src/metrics.rs` and `MetricLabels` in `packages/d2b-contracts/src/v2_component_session.rs` at `a1cc0b2d`.", + "dataMigration" : "Full d2b 3.0 reset; no v2 telemetry metric state import", + "dependencyOwner" : "ADR046-telem-019 + ADR046-telem-001 + ADR046-telem-003; telemetry/session owner", + "destination" : "`packages/d2b-telemetry/src/session_metrics_sink.rs`.", + "detailedDesign" : "Map every MetricEvent variant to the `d2b_session_*` inventory; convert MetricLabels closed sets to allowed v3 label strings; keep NoopMetrics for hermetic tests. Primary reuse disposition: `adapt`. Preserved source-plan detail: Adapt MetricsSink bridge to v3 OTEL instruments and rename ADR45 endpoint-purpose label values to v3 closed-set labels..", + "integration" : "`serve_ttrpc_services` receives a `Box` from `d2b-telemetry`; all session endpoints call through this bridge.", + "removalProof" : "ADR45 EndpointPurpose metric label strings are replaced by v3 purpose names; no prior v3 MetricsSink bridge owner to remove.", + "reuseAction" : "adapt", + "reuseSource" : null, + "specId" : "ADR-046-telemetry-audit-and-support", + "specPath" : "docs/specs/ADR-046-telemetry-audit-and-support.md", + "validation" : "New test `packages/d2b-telemetry/tests/session_sink_bridge.rs`: drive `MetricEvent` variants through the sink; assert OTEL counter/gauge values; assert `MetricLabels` closed-set values map only to allowed label strings (no `DaemonLocal` string in v3 metric output).", + "workItemId" : "ADR046-telem-027" + }, + { + "currentSource" : "`packages/d2b-provider-relay/src/lib.rs` (`RelayEndpoint`, `RelayCredential`, `RelayRole`, `RelayStream`, `connect()`, `listen()`, `mint_sas()`)", + "dataMigration" : "No compatibility with current relay sessions; v3 sessions are independent", + "dependencyOwner" : "W0 shared contract root; ComponentSession transport adapter owner", + "destination" : "`packages/d2b-provider-transport-azure-relay/src/relay_transport.rs`", + "detailedDesign" : "Adapt `RelayStream` as relay transport service process; expose named opaque byte stream on the `transport-service` Unix endpoint; add 2-byte length-prefixed framing; preserve credential redaction; TLS/WebSocket state stays in-process — only Noise record bytes traverse the named stream; register named stream with d2b-bus as `TransportHandle`; transport descriptor: `attachment_support: false`, `locality: Remote`, `atomic: false`; expose `OpenTransport`/`CloseTransport`/`ObserveTransport` interface to core; long-lived service process multiplexes sessions internally Primary reuse disposition: `adapt`. Preserved source-plan detail: extract and adapt.", + "integration" : "Core ZoneLink controller calls `OpenTransport(spec.provider.settings)` → receives named byte stream handle; relay service cannot interpret plaintext bytes; one carriage per call; WebSocket loss closes the named stream", + "removalProof" : "`d2b-provider-relay/src/lib.rs` relay plumbing retained until ACA display migration completes", + "reuseAction" : "adapt", + "reuseSource" : null, + "specId" : "ADR-046-provider-transport-azure-relay", + "specPath" : "docs/specs/providers/ADR-046-provider-transport-azure-relay.md", + "validation" : "`tests/fake_relay_transport.rs`: connect/accept, framing, credential redaction, named stream roundtrip; `tests/listener_sender_conformance.rs`: named stream contract; Noise KK binding; relay identity exclusion", + "workItemId" : "ADR046-transport-relay-001" + }, + { + "currentSource" : "None (new)", + "dataMigration" : "None — full d2b 3.0 reset; no prior state to migrate", + "dependencyOwner" : "ADR046-transport-relay-001; Credential KK session; ComponentSession/d2b-bus owner", + "destination" : "`packages/d2b-provider-transport-azure-relay/src/credential_client.rs`", + "detailedDesign" : "Async Credential KK session client for service components (service processes have d2b-bus access, enabling Credential KK; workers do not); acquire listener credential via KK inside the gateway Guest using `config.credentialBindings[listenerCredentialAlias]`; acquire sender credential independently inside the child's gateway Guest via the same KK model using `config.credentialBindings[senderCredentialAlias]`; raw credential bytes held in zeroizing memory inside the gateway Guest, presented to Azure Relay, then immediately zeroized; no credential bytes cross process, network, or Guest boundary; redacted Debug; no credential bytes in logs/audit/OTEL; core ProviderDeployment creates a private persistent Volume (per ADR-046-provider-state) for each component before its Process starts — the transport Provider does not own or create these Volumes; `Provider/volume-local` reconciles them; `migrationPolicy: none` means no migration worker is ever spawned; no relay auth token, WebSocket handle, session key, or credential byte is written to that Volume; all relay session state remains transient in-process memory", + "integration" : "Listener service invokes before each relay connect attempt inside gateway Guest; child Zone's sender service acquires its own credential independently inside its own gateway Guest", + "removalProof" : "N/A; new module", + "reuseAction" : "create", + "reuseSource" : null, + "specId" : "ADR-046-provider-transport-azure-relay", + "specPath" : "docs/specs/providers/ADR-046-provider-transport-azure-relay.md", + "validation" : "`tests/credential_redaction.rs`: credential bytes never reach any Debug/log/audit/OTEL path; `src/tests/integration/credential_delivery.rs`: end-to-end credential delivery using injected fake Credential effect port", + "workItemId" : "ADR046-transport-relay-002" + }, + { + "currentSource" : "None (new; core drives reconnect, not the transport Provider)", + "dataMigration" : "None — full d2b 3.0 reset; no prior state to migrate", + "dependencyOwner" : "ADR046-transport-relay-001; reconnect contract; ZoneLink handler", + "destination" : "`packages/d2b-provider-transport-azure-relay/src/reconnect.rs`", + "detailedDesign" : "Relay service responds to `CloseTransport`+`OpenTransport` cycle from core; core owns reconnect policy and backoff scheduling; relay service tears down the current WebSocket when core calls `CloseTransport` and establishes a new WebSocket connection when core calls `OpenTransport`; relay service does not maintain a backoff state machine or independently retry — it starts a new WebSocket on demand and emits the connect result via `ObserveTransport`; listener and sender are long-lived service processes that do not re-spawn on reconnect", + "integration" : "`ObserveTransport` delivers `TransportObservation::Disconnected` to core; core drives reconnect via `CloseTransport` then `OpenTransport` after applying its own backoff", + "removalProof" : "N/A; new module", + "reuseAction" : "create", + "reuseSource" : null, + "specId" : "ADR-046-provider-transport-azure-relay", + "specPath" : "docs/specs/providers/ADR-046-provider-transport-azure-relay.md", + "validation" : "`tests/reconnect_backoff.rs`: relay responds to CloseTransport/OpenTransport cycle; WebSocket starts on demand; ObserveTransport reports connect result; `src/tests/integration/reconnect_scenario.rs`: full reconnect cycle including Credential re-acquisition", + "workItemId" : "ADR046-transport-relay-003" + }, + { + "currentSource" : "`docs/specs/ADR-046-zone-routing.md` transport settings Nix example", + "dataMigration" : "None — full d2b 3.0 reset; no prior state to migrate", + "dependencyOwner" : "ADR046-transport-relay-001; transport settings schema; Nix configuration owner", + "destination" : "`packages/d2b-provider-transport-azure-relay/src/transport_settings.rs`; `docs/reference/schemas/v3/providers/transport-azure-relay.transport-settings.json`", + "detailedDesign" : "`AzureRelayTransportSettings` Rust struct with serde; validation against committed JSON Schema; reject `secret`-annotated fields; enforce `^[a-z][a-z0-9-]*$` pattern for `listenerCredentialAlias`/`senderCredentialAlias` alias ID fields (never `Credential/` refs); xtask `gen-provider-transport-schemas` integration", + "integration" : "`make test-drift` gate: `xtask gen-provider-transport-schemas && git diff --exit-code`", + "removalProof" : "N/A; new contract", + "reuseAction" : "create", + "reuseSource" : null, + "specId" : "ADR-046-provider-transport-azure-relay", + "specPath" : "docs/specs/providers/ADR-046-provider-transport-azure-relay.md", + "validation" : "`tests/transport_settings_schema.rs`: valid/invalid schema vectors; eval-time Nix assertion coverage from `nix-unit: transport-settings-secret-key` test (see zone-routing spec)", + "workItemId" : "ADR046-transport-relay-004" + }, + { + "currentSource" : "`packages/d2b-session/src/scheduler.rs`, `streams.rs` (main commit `a1cc0b2d`)", + "dataMigration" : "None — full d2b 3.0 reset; no prior state to migrate", + "dependencyOwner" : "ADR046-transport-relay-001; backpressure/credit contract", + "destination" : "`packages/d2b-provider-transport-azure-relay/src/backpressure.rs`", + "detailedDesign" : "Outbound WebSocket send buffer bounded at `MAX_AGGREGATE_NAMED_STREAM_QUEUE_BYTES`; relay WebSocket write backpressure propagates to `FairScheduler` credit; `d2b_relay_transport_backpressure_events_total` counter emitted; no unbounded memory growth under slow relay", + "integration" : "Named stream send on `transport-service` Unix endpoint blocks on relay WebSocket write; d2b-bus `FairScheduler` observes backpressure via credit stall", + "removalProof" : "N/A; new module", + "reuseAction" : "adapt", + "reuseSource" : null, + "specId" : "ADR-046-provider-transport-azure-relay", + "specPath" : "docs/specs/providers/ADR-046-provider-transport-azure-relay.md", + "validation" : "`tests/backpressure_credit.rs`: slow relay writer saturates outbound queue; named-stream credit stalls before unbounded growth; source Zone never buffers beyond aggregate limit", + "workItemId" : "ADR046-transport-relay-005" + }, + { + "currentSource" : "`packages/d2bd/src/metrics.rs` (hand-rolled Prometheus; baseline)", + "dataMigration" : "None — full d2b 3.0 reset; no prior state to migrate", + "dependencyOwner" : "ADR046-transport-relay-001 through ADR046-transport-relay-005; telemetry/audit owner", + "destination" : "`packages/d2b-provider-transport-azure-relay/src/{metrics.rs, audit.rs}`", + "detailedDesign" : "Emit all OTEL metrics and audit records listed in §OTEL and §Audit; closed label sets; never label secret bytes; provider audit covers **carriage authentication and health observations only** — Azure auth events, WebSocket lifecycle, credential acquisition outcomes — and is **separate from resource audit** (resource lifecycle events are owned by core); audit records appended through the Zone runtime audit log interface (no atomicity guarantee with Zone resource state in redb; best-effort delivery per the Zone's audit provider configuration); OTEL via lightweight emitter ring (no direct OTEL SDK dependency in Provider)", + "integration" : "`Provider/observability-otel` receives emitter ring frames; audit log via Zone runtime `d2b.audit.transport` category", + "removalProof" : "N/A; new module", + "reuseAction" : "create", + "reuseSource" : null, + "specId" : "ADR-046-provider-transport-azure-relay", + "specPath" : "docs/specs/providers/ADR-046-provider-transport-azure-relay.md", + "validation" : "`tests/credential_redaction.rs` extended to cover audit/OTEL paths; `tests/fake_relay_transport.rs` asserts audit record fields against schema", + "workItemId" : "ADR046-transport-relay-006" + }, + { + "currentSource" : "None — net-new v3 work; no pre-ADR45 baseline equivalent", + "dataMigration" : "None — full d2b 3.0 reset; no prior state to migrate", + "dependencyOwner" : "Provider crate owner; integration test owner", + "destination" : "`packages/d2b-provider-transport-azure-relay/src/tests/integration/README`", + "detailedDesign" : "Required content: fake relay server setup and teardown using the injected fake Relay effect port; how to run hermetic integration tests without a live Azure service; how to configure the injected fake Credential effect port for credential delivery tests; how to run with a real Azure namespace (requires a `Credential` resource declared in `spec.config.credentialBindings`, not environment-variable credential paths); integration test scenarios and expected outcomes; CI/local execution instructions", + "integration" : "`make test-integration` invokes `tests/integration/containers/` scenarios which inject the fake relay and credential port implementations from `src/tests/integration/fake_relay_server.rs`", + "removalProof" : "N/A; mandatory layout", + "reuseAction" : "create", + "reuseSource" : null, + "specId" : "ADR-046-provider-transport-azure-relay", + "specPath" : "docs/specs/providers/ADR-046-provider-transport-azure-relay.md", + "validation" : "File must be present; workspace policy gate enforces `src/tests/integration/README`", + "workItemId" : "ADR046-transport-relay-007" + }, + { + "currentSource" : "`packages/d2b-contracts/src/v2_component_session.rs` (protocol constants, credit-class constants) at main `a1cc0b2d`", + "dataMigration" : "None; v3 constants freeze independently", + "dependencyOwner" : "ADR046-session-001 (v3 ComponentSession contracts); W0 shared contract root", + "destination" : "`packages/d2b-provider-transport-unix/src/credit.rs` (imports `MAX_PACKET_ATTACHMENTS=32`, `RESERVED_CONTROL_FDS=64`, credit-class constants from v3 contract); `src/portal.rs` (imports `MAX_PACKET_ATTACHMENTS` for portal validation)", + "detailedDesign" : "Import credit scope capacities and headroom from `v3_zone_session.rs`; add `MAX_OPEN_TRANSPORTS: usize = 256` local constant for handle table bound. Primary reuse disposition: `adapt`. Preserved source-plan detail: Dependency on ADR046-session-001 output.", + "integration" : "`CreditScopeSet` constructed from imported constants at session setup", + "removalProof" : "No current code imports v3 transport constants; new import", + "reuseAction" : "adapt", + "reuseSource" : "Same file; constants copied into `packages/d2b-contracts/src/v3/zone_session.rs` by ADR046-session-001", + "specId" : "ADR-046-provider-transport-unix", + "specPath" : "docs/specs/providers/ADR-046-provider-transport-unix.md", + "validation" : "`tests/credit.rs::ancillary_capacity_is_derived_from_closed_hard_bounds` passes against v3 constants", + "workItemId" : "ADR046-transport-unix-001" + }, + { + "currentSource" : "`packages/d2b-session-unix/src/{adapter,socket,descriptor}.rs`, `tests/unix_session.rs` at main `a1cc0b2d`", + "dataMigration" : "None — full d2b 3.0 reset; no prior state to migrate", + "dependencyOwner" : "ADR046-transport-unix-001; d2b-bus transport layer (ADR046-session-003)", + "destination" : "`packages/d2b-provider-transport-unix/src/{seqpacket,identity,socket}.rs`", + "detailedDesign" : "Copy transport structs verbatim; adapt `PeerIdentityPolicy` to report `PeerCredentials` upward to ComponentSession for subject mapping (not for direct resource lookup — that is core's responsibility); maintain `SO_PASSCRED` setup and first-packet credential extraction as documented; CLOEXEC enforcement uses `rustix` syscall wrappers over `libc` where available Primary reuse disposition: `adapt`. Preserved source-plan detail: copy and adapt.", + "integration" : "`portal.rs::open_transport` calls `SeqpacketSocket::getsockopt(SO_TYPE)` and `setsockopt(SO_PASSCRED)`, constructs `UnixSeqpacketTransport`, hands OwnedTransport FD back to caller", + "removalProof" : "`d2b-realm-transport` seqpacket path retired after ZoneLink sessions migrate", + "reuseAction" : "adapt", + "reuseSource" : "Same; `UnixSeqpacketTransport`, `PeerIdentityPolicy`, `UnixAttachmentPayload`, `OwnedUnixAttachment`, `SeqpacketSocket`, `PeerCredentials`, `ObjectIdentity`, `AcceptedAttachment`, `VerifiedPacket`", + "specId" : "ADR-046-provider-transport-unix", + "specPath" : "docs/specs/providers/ADR-046-provider-transport-unix.md", + "validation" : "Copy all 12 test functions; add `peercred_reported_to_componentsession_not_resolved_to_subject_here`", + "workItemId" : "ADR046-transport-unix-002" + }, + { + "currentSource" : "`packages/d2b-session-unix/src/adapter.rs` `UnixStreamTransport`, `src/socket.rs` `StreamSocket` at main `a1cc0b2d`", + "dataMigration" : "None — full d2b 3.0 reset; no prior state to migrate", + "dependencyOwner" : "ADR046-transport-unix-001", + "destination" : "`packages/d2b-provider-transport-unix/src/{stream,socket}.rs`", + "detailedDesign" : "Copy verbatim; add `attachment_support: false` in `TransportDescriptor` (stream never carries SCM_RIGHTS regardless of route class); `admission.rs::validate_route_class` rejects `attachments_enabled=true` for stream Primary reuse disposition: `adapt`. Preserved source-plan detail: copy unchanged.", + "integration" : "Same path as seqpacket but without SCM_RIGHTS paths", + "removalProof" : "No current stream ZoneLink path exists; stream is net-new", + "reuseAction" : "adapt", + "reuseSource" : "Same", + "specId" : "ADR-046-provider-transport-unix", + "specPath" : "docs/specs/providers/ADR-046-provider-transport-unix.md", + "validation" : "`tests/portal.rs::stream_open_transport_forces_no_attachments`; `tests/identity.rs::stream_transport_reassembles_partial_and_coalesced_records`", + "workItemId" : "ADR046-transport-unix-003" + }, + { + "currentSource" : "`packages/d2b-session-unix/src/credit.rs` at main `a1cc0b2d`", + "dataMigration" : "None — full d2b 3.0 reset; no prior state to migrate", + "dependencyOwner" : "ADR046-transport-unix-001", + "destination" : "`packages/d2b-provider-transport-unix/src/credit.rs`", + "detailedDesign" : "Copy all five types verbatim; import scope-capacity constants from v3 contract; add `#[derive(Debug)]` with redacted Display (no raw counts in Debug output) Primary reuse disposition: `adapt`. Preserved source-plan detail: copy unchanged.", + "integration" : "`CreditScopeSet` created per active ComponentSession; `CreditBundle` per packet receive; credits released in `UnixAttachmentPayload::close()`", + "removalProof" : "No current code path uses this crate directly; new", + "reuseAction" : "adapt", + "reuseSource" : "Same; `CreditPool`, `CreditScopeSet`, `CreditBundle`, `ProcessCreditLimit`, `CreditScope`", + "specId" : "ADR-046-provider-transport-unix", + "specPath" : "docs/specs/providers/ADR-046-provider-transport-unix.md", + "validation" : "Copy all 4 credit test functions; add `credit_released_on_attachment_close` and `emergency_headroom_constant_across_fd_counts`", + "workItemId" : "ADR046-transport-unix-004" + }, + { + "currentSource" : "`packages/d2b-session-unix/src/descriptor.rs` `PidfdIdentityPolicy`, `DescriptorPolicy` at main `a1cc0b2d`", + "dataMigration" : "None — full d2b 3.0 reset; no prior state to migrate", + "dependencyOwner" : "ADR046-transport-unix-002", + "destination" : "`packages/d2b-provider-transport-unix/src/descriptor.rs`", + "detailedDesign" : "Copy verbatim; adapt `DescriptorPolicy::verify` to produce `AcceptedAttachment` carrying `ObjectIdentity` binding for v3 ComponentSession attachment descriptor model; `pid` not stored beyond liveness check Primary reuse disposition: `adapt`. Preserved source-plan detail: copy and adapt.", + "integration" : "Called by seqpacket transport after decrypting attachment descriptor", + "removalProof" : "Broker pidfd-open path in `d2b-priv-broker/src/sys.rs` serves different purpose (process supervision); no removal dependency", + "reuseAction" : "adapt", + "reuseSource" : "Same", + "specId" : "ADR-046-provider-transport-unix", + "specPath" : "docs/specs/providers/ADR-046-provider-transport-unix.md", + "validation" : "Copy `pidfd_identity_requires_live_launch_evidence_and_rejects_unrelated_process` and `duplicate_kernel_objects_are_rejected_and_cleaned_up`", + "workItemId" : "ADR046-transport-unix-005" + }, + { + "currentSource" : "No existing v3 socket-kind admission module", + "dataMigration" : "None — full d2b 3.0 reset; no prior state to migrate", + "dependencyOwner" : "ADR046-transport-unix-002", + "destination" : "`packages/d2b-provider-transport-unix/src/admission.rs`", + "detailedDesign" : "`validate_route_class(route_class, socket_kind, attachments_enabled, received_fd)` calls `getsockopt(SO_TYPE)` (blocking adapter) on `received_fd`: `SOCK_SEQPACKET` must match `\"seqpacket\"`, `SOCK_STREAM` must match `\"stream\"`, any other type fails `invalid-socket-fd`; if `route_class == RouteClass::ZoneLink && attachments_enabled == true` fail `attachment-policy-conflict` with detail `cross-zone-attachments-forbidden`; if `socket_kind == \"stream\" && attachments_enabled == true` fail `attachment-policy-conflict`; no Noise profile enforcement (that is ComponentSession's responsibility); returns `Ok(RouteAdmission { route_class, socket_kind, attachments_enabled })`", + "integration" : "Called by `portal.rs::open_transport` before the monitoring dup and handle allocation", + "removalProof" : "No current code has this gate; new path", + "reuseAction" : "adapt", + "reuseSource" : "`getsockopt(SO_TYPE)` pattern widely used; no specific main reuse source", + "specId" : "ADR-046-provider-transport-unix", + "specPath" : "docs/specs/providers/ADR-046-provider-transport-unix.md", + "validation" : "`tests/admission.rs::seqpacket_fd_passes_seqpacket_kind`; `stream_fd_passes_stream_kind`; `seqpacket_fd_rejects_stream_kind_declaration`; `zone_link_with_attachments_enabled_fails`; `local_portal_seqpacket_with_attachments_accepted`; `stream_with_attachments_enabled_rejected`", + "workItemId" : "ADR046-transport-unix-006" + }, + { + "currentSource" : "No portal service in v3 baseline; `d2b-provider-toolkit/src/server.rs` `GeneratedProviderServiceServer` dispatch pattern (main `a1cc0b2d`) for service entry pattern", + "dataMigration" : "None — full d2b 3.0 reset; no prior state to migrate", + "dependencyOwner" : "ADR046-transport-unix-002 through 006; ADR046-session-003 (d2b-bus ComponentSession method dispatch); ADR046-session-001 (named-stream protocol)", + "destination" : "`packages/d2b-provider-transport-unix/src/{portal,service}.rs`", + "detailedDesign" : "`portal.rs`: `PortalHandler` struct owns a bounded `HashMap` (capacity `MAX_OPEN_TRANSPORTS=256`); `open_transport(req, attachment_fd)` validates via `admission.rs`, dups FD, allocates handle, stores `MonitorState { dup_fd, observation_senders: Vec }`; `close_transport(handle)` closes dup FD, half-closes all observation senders, removes entry; `observe_transport(handle)` registers a new `NamedStreamSender` and spawns an async epoll-watcher task on the dup FD; `TransportHandle` is a `[u8; 16]` random token; redacted in all Debug impls; `service.rs` is the binary entry: accepts the allocator-issued portal endpoint FD at launch, runs `GeneratedTransportServiceServer` over it, dispatches to `PortalHandler` Primary reuse disposition: `adapt`. Preserved source-plan detail: adapt dispatch pattern; implement portal methods as new.", + "integration" : "Core ZoneLink controller calls the three methods via d2b-bus; portal endpoint FD is supplied by Zone runtime/allocator at Process spawn, not SD_LISTEN_FDS", + "removalProof" : "Ad-hoc IPC stubs in `d2bd/src/` retired after portal migration", + "reuseAction" : "adapt", + "reuseSource" : "main `a1cc0b2d` `d2b-provider-toolkit/src/server.rs` service dispatch pattern", + "specId" : "ADR-046-provider-transport-unix", + "specPath" : "docs/specs/providers/ADR-046-provider-transport-unix.md", + "validation" : "`tests/portal.rs::open_transport_zone_link_validates_and_returns_ownedtransport`; `open_transport_local_portal_seqpacket_with_attachments_accepted`; `open_transport_zone_link_attachments_enabled_rejected`; `close_transport_is_idempotent_after_handle_removed`; `observe_transport_delivers_pollhup_as_peer_disconnected`; `handle_table_rejects_at_max_capacity`; `restart_clears_all_handles`", + "workItemId" : "ADR046-transport-unix-007" + }, + { + "currentSource" : "`packages/d2b-priv-broker/src/` minijail spawn patterns (v3 baseline); `packages/d2b-host/src/` process arg patterns; current package derivations in `flake.nix`", + "dataMigration" : "None (fresh Provider resource)", + "dependencyOwner" : "ADR046-transport-unix-007; ADR046-provider-003 (system Provider framework); Provider/system-minijail (ADR046-provider-003)", + "destination" : "`packages/d2b-provider-transport-unix/` crate Cargo.toml binary target `d2b-transport-unix-service`; Provider component descriptor JSON committed at `packages/d2b-provider-transport-unix/descriptor/unix-transport-service.json`; Nix package derivation at `packages/d2b-provider-transport-unix/`", + "detailedDesign" : "Component descriptor declares: `processClass=service`, `template=unix-transport-service`, `stateNamespaces=[]` (no Provider state Volume; bounded non-secret operational state in status/core ledger, D087), `sandbox.capabilityClasses=[]`, `sandbox.namespaceClasses=[mount]`, `sandbox.seccompClass=strict`, `budget.memory.limit=\"16Mi\"`, `budget.cpu.limit=\"200m\"`, `budget.fds.limit=512`, `endpoints=[{name:portal,transport:unix,purpose:transport-unix-portal}]`, `readiness={class:provider-defined,initialDelay:\"0s\",timeout:\"5s\",failureThreshold:1,successThreshold:1}`, `restartPolicy={class:always,backoffBase:\"2s\",backoffMax:\"60s\",backoffMultiplier:2.0,maxRestarts:10,resetAfter:\"1h\"}`; Provider package bundles descriptor digest; core ProviderDeployment creates the Process with empty `mounts` when `Provider/transport-unix` is installed Primary reuse disposition: `adapt`. Preserved source-plan detail: adapt; no direct symbol copy.", + "integration" : "Provider resource installed → core ProviderDeployment reads component descriptor → creates child `Process/transport-unix-service` (no state-Volume prerequisite) → ProviderSupervisor spawns binary with portal FD in inherited FD table. On delete: Process terminal first → ProviderDeployment finalizer cleared last; the service `status` disappears with the resource row", + "removalProof" : "No current transport-service Process exists; new path", + "reuseAction" : "adapt", + "reuseSource" : "Minijail sandbox semantic class patterns from current v3 broker; Process resource schema from ADR-046-resources-host-guest-process-user", + "specId" : "ADR-046-provider-transport-unix", + "specPath" : "docs/specs/providers/ADR-046-provider-transport-unix.md", + "validation" : "`tests/conformance.rs::process_resource_matches_component_descriptor`; `tests/conformance.rs::provider_state_set_is_empty`; `tests/conformance.rs::no_state_volume_mount`; sandbox policy tests against minijail conformance kit", + "workItemId" : "ADR046-transport-unix-008" + }, + { + "currentSource" : "`nixos-modules/options-realms.nix` realm options (v3 baseline); `nixos-modules/assertions.nix`", + "dataMigration" : "`d2b.realms.*` Nix options superseded by `d2b.zones.*`; no compatibility bridge (v3 reset)", + "dependencyOwner" : "ADR046-transport-unix-001; Nix/build integrator", + "destination" : "`docs/reference/schemas/v3/providers/transport-unix.transport-binding.json`; `nixos-modules/assertions.nix` (assertion additions); generated `nixos-modules/generated/options-zones-ZoneLink.nix` provider-settings submodule", + "detailedDesign" : "Commit the JSON Schema; run `xtask gen-zone-schemas` and `xtask gen-zone-nix-options` to regenerate committed files; add two assertions to `assertions.nix` (stream+attachments conflict; sensitive key names); `xtask gen-zone-resources` adds provider-specific settings validation step", + "integration" : "Build emitter validates `spec.provider.settings` against schema before computing `generationId`; drift gate enforces sync", + "removalProof" : "`nixos-modules/options-realms.nix` realm wiring retired after Zone resource bundle activation replaces it", + "reuseAction" : "create", + "reuseSource" : null, + "specId" : "ADR-046-provider-transport-unix", + "specPath" : "docs/specs/providers/ADR-046-provider-transport-unix.md", + "validation" : "All seven eval/build tests in the Nix section", + "workItemId" : "ADR046-transport-unix-009" + }, + { + "currentSource" : "v3 baseline `tracing` crate patterns; v3 `d2b-realm-router/src/service_v2.rs` audit field shapes", + "dataMigration" : "Existing `d2bd/src/metrics.rs` VM-label metrics superseded by v3 metrics; not migrated", + "dependencyOwner" : "ADR046-transport-unix-007; ADR-046-telemetry-audit-and-support", + "destination" : "`packages/d2b-provider-transport-unix/src/{audit,metrics}.rs`", + "detailedDesign" : "`AuditRecordKind` enum with 6 event kinds from Audit section; `AuditRecord` carries only the fields listed (no uid/gid/pid/path/handle/ZoneLink name); emit via Zone runtime `emit_audit_record()` interface; `MetricCounter`/`MetricHistogram` with closed label types per Metrics section; emit via bounded in-process ring to OTEL Provider datagram socket; `tracing::instrument` spans on `PortalHandler` methods with the 3 permitted span attributes only", + "integration" : "`portal.rs` calls `audit.rs::emit_*` before returning from each portal method; `seqpacket.rs` calls `metrics.rs::record_*` on every accept/packet/attachment", + "removalProof" : "`d2bd/src/metrics.rs` hand-rolled registry retired after metric surface migration", + "reuseAction" : "create", + "reuseSource" : null, + "specId" : "ADR-046-provider-transport-unix", + "specPath" : "docs/specs/providers/ADR-046-provider-transport-unix.md", + "validation" : "`tests/conformance.rs::audit_records_contain_no_pid_uid_or_handle`; `tests/conformance.rs::metric_labels_are_closed_set`; `tests/conformance.rs::span_attributes_contain_no_sensitive_fields`", + "workItemId" : "ADR046-transport-unix-010" + }, + { + "currentSource" : "No existing integration tests for Unix portal scenarios", + "dataMigration" : "None — full d2b 3.0 reset; no prior state to migrate", + "dependencyOwner" : "ADR046-transport-unix-007 through 010; test orchestration owner", + "destination" : "`packages/d2b-provider-transport-unix/integration/` and `integration/README.md`", + "detailedDesign" : "Four scenarios: `transport_open.rs` (fake Zone portal, allocator-socketpair FD attachment in → OwnedTransport attachment out → verify socket kind, CLOEXEC, SO_PASSCRED enabled; p95 latency assertion ≤2 ms); `fd_transfer.rs` (seqpacket `SCM_RIGHTS` transfer through opened transport, credit accounting, scavenge on error injection); `reconnect.rs` (CloseTransport + re-OpenTransport with fresh socketpair, verify previous handle is unknown, verify monitoring dup closed); `observation_stream.rs` (ObserveTransport stream receives `PEER_DISCONNECTED` event when peer closes socketpair end within 5 ms p95). `integration/README.md` documents prerequisites (no KVM required; all scenarios use in-process socketpairs and fake Zone API endpoint stub), invocation (`cargo test -p d2b-provider-transport-unix --test integration`), environment variables, and expected output", + "integration" : "Invoked by `make test-integration`; no host mutation; each scenario creates its own socketpairs", + "removalProof" : "Ad-hoc IPC test stubs retired after scenario parity", + "reuseAction" : "adapt", + "reuseSource" : "Test scenario shapes from `d2b-session-unix/tests/unix_session.rs` end-to-end test (main `a1cc0b2d`)", + "specId" : "ADR-046-provider-transport-unix", + "specPath" : "docs/specs/providers/ADR-046-provider-transport-unix.md", + "validation" : "All four scenarios pass in CI; latency assertions enforced using monotonic timestamps; scavenge correctness verified by open-FD count before/after error injection", + "workItemId" : "ADR046-transport-unix-011" + }, + { + "currentSource" : "None — net-new v3 work; no pre-ADR45 baseline equivalent", + "dataMigration" : "None — docs/tooling only; no runtime state", + "dependencyOwner" : "d2b-contracts crate shape stabilised by shared root contract; d2b-contracts owner", + "destination" : "packages/d2b-contracts/src/usbip_effect_port.rs", + "detailedDesign" : "Define UsbipEffectPort and UsbipGuestEffectPort in d2b-contracts with DeviceUid, NetworkUid, UsbBindingUid, LeaseToken, FirewallToken, FirewallObservation, KernelModuleClass, DeviceProbeResult, and UsbipEffectError; export traits/types only with no implementation; keep firewall apply/observe/release Network/busid-scoped, attach/detach Binding-addressed, and all fd/path/busid values private. `TransientDetail` derives `Clone, PartialEq, Eq` while retaining manual redacted Debug/Display so `UsbipEffectError`'s derives compile without disclosure. Primary reuse disposition: `create`. Preserved source-plan detail: net-new trait definition.", + "integration" : "Provider/device-usbip controller depends on this trait for injected semantic effects; the framework core adapter implements it in ADR046-usbip-002.", + "removalProof" : "None — net-new; no prior owner to remove", + "reuseAction" : "create", + "reuseSource" : null, + "specId" : "ADR-046-provider-device-usbip", + "specPath" : "docs/specs/providers/ADR-046-provider-device-usbip.md", + "validation" : "d2b-contracts tests for trait object safety, firewall apply/observe/release signatures, `UsbipEffectError: Clone + PartialEq + Eq`, `TransientDetail` clone/equality, redacted Debug/Display behavior, and no implementation leakage.", + "workItemId" : "ADR046-usbip-001" + }, + { + "currentSource" : "packages/d2bd/src/usbip_state_machine.rs, packages/d2bd/src/usbip_reconcile_state.rs, packages/d2b-host/src/usbip_argv.rs, packages/d2b-priv-broker/src/ops/usbip_firewall.rs, usbip_host.rs, and usbip_lock.rs", + "dataMigration" : "Full d2b 3.0 reset; adapter resumes from Service/Binding status and authority owner proofs rather than daemon-coupled snapshots", + "dependencyOwner" : "ADR046-usbip-001, ADR046-zone-control-024; UsbipBindFirewallRule broker op; d2b-host usbip argv support; framework core adapter owner", + "destination" : "packages/d2b-core/src/device_usbip_adapter.rs", + "detailedDesign" : "Implement UsbipEffectPort in the core adapter: signed-bundle busid lookup, same-Zone validation, trusted physical-USB identity resolution, mandatory Core-derived `physical-usb-backing/v1` digest and exact `(Host, physical-usb-backing, opaqueKeyDigest)` claim shared with every security-key/USB Provider, exclusive OFD claim, sole ownership of all USBIP TCP/3240 and exact per-Network/per-busid `UsbipBindFirewallRule` effects plus ownership-scoped observe/release, anti-spoof probe, one shared Host module/backend authority, one Core-derived D097 relay Endpoint/firewall authority per Network, D097 authority-index preflight/adoption, and post-effect audit; complete the shared claim before any open, withhold, bind, module, relay, firewall, or attachment effect; never expose raw busid, identity digest, path, fd, bind address, nftables body, audit structs, or broker wire types. Primary reuse disposition: `adapt`. Preserved source-plan detail: extract and adapt into framework-internal adapter.", + "integration" : "Reconcile framework injects the adapter into Provider/device-usbip; D097 authority index gates effects; adapter calls privileged broker and d2b-host argv helpers behind the semantic trait.", + "removalProof" : "Old daemon-coupled adapter call sites are removed by ADR046-usbip-009 after Provider wiring and adapter tests pass.", + "reuseAction" : "adapt", + "reuseSource" : null, + "specId" : "ADR-046-provider-device-usbip", + "specPath" : "docs/specs/providers/ADR-046-provider-device-usbip.md", + "validation" : "Fast packages/d2b-core/tests/device_usbip_adapter.rs covers same-Zone gate, exact shared physical backing tuple derivation, byte-identical keys for USB/security-key views of one fake token, Provider-private-class/digest bypass rejection, `physical-usb-backing-conflict` before effects, separate USBIP module/relay authorities, one-module/one-relay reuse, exact Network/busid firewall scoping, foreign-marker failure, ownership-scoped drift/status, independent per-busid release, no network-local dependency, anti-spoof, redaction, broker mapping, and no digest/busid/path/fd exposure.", + "workItemId" : "ADR046-usbip-002" + }, + { + "currentSource" : "None — net-new Provider crate; no pre-ADR45 baseline equivalent", + "dataMigration" : "None — docs/tooling only; no runtime state", + "dependencyOwner" : "ADR046-usbip-001, ADR046-provider-004; Provider model crate structure; device-usbip provider owner", + "destination" : "packages/d2b-provider-device-usbip/", + "detailedDesign" : "Create the required crate layout; bind the shared D098 `UsbService`/`UsbBinding` base versions/fingerprints from ADR046-provider-004 and implement only strict USBIP Provider extensions; sign/register extension schemas and advertise explicit export only for authority `UsbService` resources implemented by this Provider; implement validation.rs and compile-checked EffectPort injection. Declare the controller user/User resource in Nix activation. Primary reuse disposition: `create`. Preserved source-plan detail: net-new crate skeleton with contract reuse.", + "integration" : "Workspace manifests, Provider artifact catalog, Nix module, and ProviderDeployment consume the crate and component descriptor.", + "removalProof" : "None — net-new; no prior owner to remove", + "reuseAction" : "create", + "reuseSource" : null, + "specId" : "ADR-046-provider-device-usbip", + "specPath" : "docs/specs/providers/ADR-046-provider-device-usbip.md", + "validation" : "make test-policy passes; Cargo.toml has no d2b-priv-broker dependency; fast schema/manifest tests consume the common fixtures, accept canonical minimal base without `spec.provider`, prove a fake direct-local Provider can implement the same base, and cover Service-only exportability, Binding non-exportability, Core projection ownerRef/base fields with explicit `spec.provider` rejection, D088 status layering, semantic factory-fingerprint stability across Provider/adapter identity changes, strict refs, and trait injection.", + "workItemId" : "ADR046-usbip-003" + }, + { + "currentSource" : "packages/d2bd/src/usbip_state_machine.rs and packages/d2bd/src/usbip_reconcile_state.rs", + "dataMigration" : "Full d2b 3.0 reset; no direct import of d2bd usbip_reconcile_state snapshots", + "dependencyOwner" : "ADR046-usbip-001, ADR046-usbip-002, ADR046-usbip-003, and ADR046-zone-control-024; device-usbip controller owner", + "destination" : "packages/d2b-provider-device-usbip/src/{controller,reconcile,export_import}.rs", + "detailedDesign" : "Reconcile provider-neutral authority/projection `UsbService` and per-Guest `UsbBinding` resources through strict USBIP Provider extensions, consuming UsbipEffectPort, the exact shared Host-global physical USB tuple, USBIP-private D097 authorities, and signed D096 ExportAdapter/ImportAdapter. Enforce same-Zone base and provider refs; ResourceExport authority-Service-only target; same-type ResourceImport-owned projection with `providerRef`, semantic base/import fields, no `spec.provider`, and no physical fields/effects; route selection from the signed local descriptor/import record; semantic factory fingerprint independent of separately authenticated adapter identity; exclusive fair Binding admission; encrypted bounded named-stream control/data; D088 layered status; Service/Binding finalizers; restart adoption; declared/explicit modes; no session/transfer resources. Primary reuse disposition: `adapt`. Preserved source-plan detail: extract and adapt step machine into Provider reconcile loop.", + "integration" : "Controller watches Device/Host/Network/Guest/Endpoint/Export/Import dependencies, calls injected EffectPorts, commits ResourceMutationBatch updates, coordinates children from ADR046-usbip-005, and delegates only semantic export/import admission to the Provider adapter while core owns D096 routing/lifecycle.", + "removalProof" : "packages/d2bd/src/usbip_state_machine.rs and usbip_reconcile_state.rs are deleted by ADR046-usbip-009 once Provider parity tests pass.", + "reuseAction" : "adapt", + "reuseSource" : null, + "specId" : "ADR-046-provider-device-usbip", + "specPath" : "docs/specs/providers/ADR-046-provider-device-usbip.md", + "validation" : "Fast tests/controller_state_machine.rs, service_binding_schema.rs, export_import.rs, authority_conflict.rs, async_loop.rs, finalizer.rs, and wrong_zone.rs cover authority/projection/Binding lifecycle, Service-only export, projection `spec.provider` rejection, semantic fingerprint stability under adapter identity changes plus separate descriptor authentication, D088 `status.resource`/`status.provider` placement, shared physical tuple collision before effects, encrypted fake streams, no physical projection effect, exclusivity, restart, and WrongZone degradation.", + "workItemId" : "ADR046-usbip-004" + }, + { + "currentSource" : "None — net-new Process resources; templates derive from the Provider package descriptor", + "dataMigration" : "Full d2b 3.0 reset; old per-env runners become Host/Network authorities and per-Device port-3240 workers are forbidden", + "dependencyOwner" : "ADR046-usbip-003; Process ResourceType schema; device-usbip process lifecycle owner", + "destination" : "packages/d2b-provider-device-usbip/src/reconcile.rs", + "detailedDesign" : "Create/adopt exactly one Host backend Process authority, exactly one D097 Network relay Process/Endpoint/firewall authority bound to TCP 3240 with a Core-derived Network/policy key, and one Binding-owned Guest proxy/private Endpoint per attached Binding. Restrict relay resolution to the Core adapter and exact active-lease Binding proxies; deliver their connected streams by LaunchTicket. Use canonical system-minijail specs, signed templates, bounded budgets/readiness/restart, no argv/path/address/fd fields; attach/detach remains a one-shot EffectPort operation, not a second Process. Primary reuse disposition: `adapt`. Preserved source-plan detail: adapt into singleton Host backend, per-Network relay, and per-Binding Guest proxy management.", + "integration" : "Service controller registers physical busids with shared backend/relay; Binding controller creates its Guest proxy/private Endpoint; Process controller launches workers; UsbipGuestEffectPort attaches to the private Endpoint.", + "removalProof" : "Old per-env usbipd autostart and ProcessRole::Usbip paths are removed by ADR046-usbip-009 after Process resource lifecycle tests pass.", + "reuseAction" : "adapt", + "reuseSource" : null, + "specId" : "ADR-046-provider-device-usbip", + "specPath" : "docs/specs/providers/ADR-046-provider-device-usbip.md", + "validation" : "Fast Process/Endpoint shape tests prove one backend per Host, one Core-derived multiplexed TCP 3240 Endpoint/firewall authority per Network, deterministic duplicate conflict, adapter/exact-Binding-only resolution, LaunchTicket-connected stream, no generic Network reader access, Binding ownership/private Guest policy, no per-Device listener, no raw address/argv/path/fd, and readiness before bind/attach.", + "workItemId" : "ADR046-usbip-005" + }, + { + "currentSource" : "packages/d2bd/src/usbip_reconcile_state.rs state fields", + "dataMigration" : "Full d2b 3.0 reset; current d2bd reconcile state is not imported", + "dependencyOwner" : "ADR046-usbip-003; Device provider details plus `UsbService` and `UsbBinding` base/provider status schema owner", + "destination" : "packages/d2b-provider-device-usbip/src/status.rs", + "detailedDesign" : "Define provider-neutral `UsbService` `status.resource` with whole-device availability/access counts, authority-only `physical-usb-backing` claim state, and projection-only common import state; define `UsbBinding.status.resource` with generic attachment phase/queue/generation/timestamps. Define separate strict `status.provider` USBIP details: Device probe, owner backend/relay, USBIP-owned `firewallState`/opaque digest/observed generation, imported-route observations, and Binding proxy/private Endpoint/subphase. No common access/import/attachment field appears directly under `status`; no USBIP module, Network, Endpoint, proxy, server/client, port, firewall, or busid field enters base status; no raw backing digest, busid, path, fd, address, session/transfer ID, remote identity, or payload appears anywhere. Primary reuse disposition: `adapt`. Preserved source-plan detail: adapt state fields to typed status.provider.details.", + "integration" : "Controller writes each extension atomically with its resource's common status; dependency/update propagation is Device/Export → Service/projection → Binding.", + "removalProof" : "Old d2bd USBIP reconcile-state structs are removed by ADR046-usbip-009 after status extension coverage passes.", + "reuseAction" : "adapt", + "reuseSource" : null, + "specId" : "ADR-046-provider-device-usbip", + "specPath" : "docs/specs/providers/ADR-046-provider-device-usbip.md", + "validation" : "Fast tests/status_serde.rs covers generic base status plus three strict USBIP detail schemas, exact `status.resource` placement for access/import/attachment/backing claim, exact `status.provider` placement for relay/firewall implementation observations, drift transition and ownership, rejection of semantic fields directly under `status`, mode-dependent omissions, bounded counts/refs, unknown-field denial, and a deny corpus proving USBIP-only fields are rejected from base and Network status.", + "workItemId" : "ADR046-usbip-006" + }, + { + "currentSource" : "packages/d2b-contract-tests/tests/usbip_policy_network_scoping.rs plus new integration scenarios", + "dataMigration" : "None — docs/tooling only; no runtime state", + "dependencyOwner" : "ADR046-usbip-004 and ADR046-usbip-005; device-usbip integration owner", + "destination" : "packages/d2b-provider-device-usbip/{src,tests,integration/README.md}; tests/host-integration/usbip-service.nix; tests/host-integration/hardware/usbip-service.sh", + "detailedDesign" : "Put provider-neutral Service/Binding base-schema separation, strict USBIP extensions, projection `spec.provider` rejection, D088 layered status, semantic factory-fingerprint independence from Provider/adapter identity, exact shared physical backing tuple/conflict, one Core-derived per-Network relay Endpoint/firewall authority, exact per-busid firewall ownership/drift/release, arbitration, same-type export/import, encrypted fake-stream, and least-privilege process/Endpoint shape coverage in fast Layer-1 Rust tests. Include a fake direct-local Provider proving the same base contract has no USBIP dependency. Reserve runNixOSTest for real Linux usbip_host/vhci_hcd, usbipd, namespaces/nftables, TCP 3240, zero network-local carve-out, and Guest checks; reserve the hardware script for an approved physical device. Use existing Make gates only. Primary reuse disposition: `adapt`. Preserved source-plan detail: adapt existing network-scoping assertion and add new scenarios.", + "integration" : "Layer-2 lanes exercise actual kernel/backend/relay/Guest/device paths and do not duplicate pure controller/schema cases. Cross-Zone protocol logic remains hermetic with fake peers; the runNixOSTest only proves its real-system integration.", + "removalProof" : "Old usbip_policy_network_scoping coverage is retired only after the fast wrong-Zone admission test and `tests/host-integration/usbip-service.nix` successor both pass and the migration ledger is updated.", + "reuseAction" : "adapt", + "reuseSource" : null, + "specId" : "ADR-046-provider-device-usbip", + "specPath" : "docs/specs/providers/ADR-046-provider-device-usbip.md", + "validation" : "`make test-host-integration` runs the non-hardware real-kernel case on a capable host; `make test-hardware` runs the explicit manual device case. No Layer-1 test opens a device, loads a module, creates a namespace, or listens on a socket.", + "workItemId" : "ADR046-usbip-007" + }, + { + "currentSource" : "nixos-modules/components/usbip.nix guest wiring and new Zone resource declarations", + "dataMigration" : "Full d2b 3.0 reset; operators reauthor old per-VM options as Device + authority/projection Service + per-Guest Binding", + "dependencyOwner" : "ADR046-usbip-003, ADR046-zone-control-024; ADR-046-nix-configuration; Nix integrator", + "destination" : "nixos-modules/components/usbip.nix, nixos-modules/options-zones.nix, nixos-modules/assertions.nix", + "detailedDesign" : "Add Provider config; remove the old per-VM option; emit provider-neutral authority `UsbService`, per-Guest `UsbBinding`, strict USBIP Provider envelopes, and optional ResourceExport/ResourceImport authoring shapes; imported same-type projection Services remain Core-created with no `spec.provider`. Assert same-Zone base/provider refs, projection ownerRef/forbidden implementation and physical fields, Service-only export target, one Host backend/Network relay, exact Core-derived Host-global `(Host, physical-usb-backing, opaqueKeyDigest)` exclusion shared by USB/security-key, Provider-private-class bypass rejection, and retain guest vhci_hcd/tools. Primary reuse disposition: `adapt`. Preserved source-plan detail: adapt guest module, remove host-side option surface, and extend eval assertions.", + "integration" : "Nix compiler emits Device plus provider-neutral `UsbService`/`UsbBinding` and explicit D096 resources consumed by core/Provider; guest runtime supplies USBIP proxy/attach tools; generated base and provider-extension schemas/fingerprints remain canonical.", + "removalProof" : "d2b.vms..usbip.yubikey and host-side USBIP module paths are removed at reset once Zone resource emitter coverage passes.", + "reuseAction" : "adapt", + "reuseSource" : null, + "specId" : "ADR-046-provider-device-usbip", + "specPath" : "docs/specs/providers/ADR-046-provider-device-usbip.md", + "validation" : "Fast tests/unit/nix/cases/usbip-*.nix cover schema shape, Core projection `spec.provider` rejection, D088 layered status fixtures including USBIP-owned firewall state, all reference/owner/export assertions, byte-identical USB/security-key tuple collision and private-class bypass rejection, one Core-derived 3240 Endpoint/firewall authority per Network, least-privilege consumer policy, absence of network-local 3240 rules, old-option removal, and guest module retention.", + "workItemId" : "ADR046-usbip-008" + }, + { + "currentSource" : "packages/d2bd/src/usbipd_perenv_autostart.rs, packages/d2bd/src/usbip_state_machine.rs, packages/d2bd/src/usbip_reconcile_state.rs, nixos-modules/network.nix USBIP firewall block, and ProcessRole::Usbip in packages/d2b-core/src/processes.rs", + "dataMigration" : "Full d2b 3.0 reset; no daemon-coupled USBIP runtime state import", + "dependencyOwner" : "ADR046-usbip-004 and ADR046-usbip-008; Provider fully wired and validated; daemon cleanup owner", + "destination" : "packages/d2bd/src/, nixos-modules/network.nix, packages/d2b-core/src/processes.rs", + "detailedDesign" : "Remove daemon-coupled USBIP after Provider tests and integration tests pass: delete per-env autostart, state machine, and reconcile state modules after migration; remove USBIP firewall block from network.nix; remove ProcessRole::Usbip; run Layer-1 gates and confirm no d2bd or network.nix references remain outside the adapter and contracts. Primary reuse disposition: `delete-after-cutover`. Preserved source-plan detail: delete after Provider replacement reaches parity.", + "integration" : "Provider/device-usbip, core D096/D097 adapters, Nix Device plus provider-neutral USB resource emitter, USBIP authority workers, and Binding-owned children are the sole USBIP lifecycle path after deletion.", + "removalProof" : "usbipd_perenv_autostart.rs, usbip_state_machine.rs, usbip_reconcile_state.rs, network.nix USBIP firewall block, and ProcessRole::Usbip are deleted after parity.", + "reuseAction" : "delete-after-cutover", + "reuseSource" : null, + "specId" : "ADR-046-provider-device-usbip", + "specPath" : "docs/specs/providers/ADR-046-provider-device-usbip.md", + "validation" : "make test-unit and make test-flake plus grep or contract checks for removed symbols and no residual d2bd/network.nix USBIP lifecycle references.", + "workItemId" : "ADR046-usbip-009" + }, + { + "currentSource" : "`d2b-core/src/storage.rs` (`StoragePathSpec`, `StoragePathKind`, policy enums); `d2b-core/src/sync.rs` (`SyncJson`, `LockSpec`)", + "dataMigration" : "Full v3 reset; no row-level import", + "dependencyOwner" : "ADR046-primitives-001; v3 contracts owner", + "destination" : "`d2b-contracts/src/v3/volume_layout.rs` (LayoutEntry, EntryType, all policy enums, AclGrant, Invariant, SensitivityClass); `d2b-contracts/src/v3/volume_spec.rs` (VolumeSpec, ViewSpec, Attachment, QuotaSpec, SourceKind, `SourcePolicyId` opaque newtype); `d2b-contracts/src/v3/effect_port.rs` (`VolumeEffectPort` trait, opaque ID newtypes `VolumeId`/`LayoutEntryId`/`UserId`/`ViewId` each with custom redacted Debug, and `VolumeMountToken` opaque handle with custom redacted Debug)", + "detailedDesign" : "All LayoutEntry fields as documented in this dossier; enum value names preserved from `StoragePathKind`/policy enums with renames where noted; `User/` ACL principal (no numeric UID); `sourcePolicyId` opaque newtype replaces raw `hostPath` in `SourceKind::LocalPath` and `SourceKind::BlockImage` Primary reuse disposition: `adapt`. Preserved source-plan detail: extract and adapt.", + "integration" : "Volume spec and status structs; Provider descriptor component stateNamespace; Nix resource compiler schema validation", + "removalProof" : "`d2b-core/src/storage.rs` StoragePathSpec/policy enums removed only after all Provider descriptor consumers are on v3 Volume spec", + "reuseAction" : "adapt", + "reuseSource" : null, + "specId" : "ADR-046-provider-volume-local", + "specPath" : "docs/specs/providers/ADR-046-provider-volume-local.md", + "validation" : "Schema golden vectors; round-trip serde; ACL principal validation rejects numeric forms; `sourcePolicyId` present; no `hostPath` field in any volume_spec contract", + "workItemId" : "ADR046-vl-001" + }, + { + "currentSource" : "`d2b-state/src/{atomic,path,lock}.rs` (main `6faa5256`); `d2b-priv-broker/src/ops/swtpm_dir.rs` (marker algorithm)", + "dataMigration" : "New marker written for each Volume at v3 first-boot", + "dependencyOwner" : "ADR046-vl-001; volume-local Provider owner", + "destination" : "Full `packages/d2b-provider-volume-local/` scaffold per §Crate layout: `src/`, `tests/`, `integration/`, `README.md`; crate `Cargo.toml` depends only on `d2b-contracts`, `d2b-provider`, `d2b-provider-toolkit`", + "detailedDesign" : "`AnchoredDir`, `AnchoredResource`, `LeafName`, `RelativePath`; adapted `AtomicFilesystem`/`StateEnvelope`; adapted `LockGuard`/`LockSet`/`OfdTransfer`; marker write/verify/check; `src/effect_port.rs` re-exports `VolumeEffectPort` trait from `d2b-contracts::v3::effect_port` and provides Provider-side opaque ID construction helpers (no adapter implementation; adapter lives in host runtime); `sourcePolicyId` validation against declared policy list; no `openat2`/`setfacl`/`fallocate`/numeric-UID call sites in Provider crate Primary reuse disposition: `adapt`. Preserved source-plan detail: copy-unchanged (`path.rs`); adapt (`atomic.rs`, `lock.rs`); adapt swtpm_dir marker algorithm.", + "integration" : "Controller binary receives `VolumeEffectPort` via ComponentSession injection; adapter calls `provision_marker` when a new Volume first appears in the `providerRef` reconcile queue (ProviderDeployment has already created the resource; volume-local provisions physical state) and `verify_marker` on restart relist", + "removalProof" : "`swtpm_dir.rs` marker implementation retired only after device-tpm Provider Volume is live and marker-check parity is confirmed", + "reuseAction" : "adapt", + "reuseSource" : null, + "specId" : "ADR-046-provider-volume-local", + "specPath" : "docs/specs/providers/ADR-046-provider-volume-local.md", + "validation" : "All `tests/marker.rs`, `tests/state.rs` scenarios; all `integration/provision.rs` scenarios; `cargo deny check` verifies no `d2b-priv-broker`/`d2bd` dependency", + "workItemId" : "ADR046-vl-002" + }, + { + "currentSource" : "`d2b-priv-broker/src/ops/{state_dir,storage_contract}.rs` (broker layout ops); `d2b-core/src/storage_lifecycle.rs` (lifecycle report)", + "dataMigration" : "None — full d2b 3.0 reset; no prior state to migrate", + "dependencyOwner" : "ADR046-vl-002; ADR046-reconcile-001; d2b-bus/ComponentSession owner", + "destination" : "`src/controller.rs`, `src/layout.rs`, `src/acl.rs`, `src/source.rs`", + "detailedDesign" : "Async reconcile loop; topological LayoutEntry evaluation; `VolumeEffectPort` semantic op dispatch (no direct broker connection; no `openat2`/`setfacl` call sites); ACL reconciliation cycle via effect port; drift detection; status write with expected revision; `sourcePolicyId` validation against declared `sourcePolicies`; controller watch remains responsive while per-resource effect calls run concurrently; **single watch scope** `providerRef: Provider/volume-local` — physical state reconciliation for all served Volumes (layout/ACL/quota/marker); ProviderDeployment creates/deletes Volume instances; volume-local does not issue create/delete API calls; Nix-preprovisioned `User/` layout principals; no cross-component Volume sharing; each component consumes only its declared view; empty-payload stateNamespace Volumes use `migrationPolicy: none` — no migration EphemeralProcess dispatched", + "integration" : "Controller binary instantiated by Zone runtime after Provider Ready; receives `VolumeEffectPort` implementation and d2b-bus `ResourceClient` via ComponentSession", + "removalProof" : "`d2b-priv-broker/src/ops/storage_contract.rs` `reconcile_storage_scope` retired only after Volume controller parity confirmed", + "reuseAction" : "adapt", + "reuseSource" : null, + "specId" : "ADR-046-provider-volume-local", + "specPath" : "docs/specs/providers/ADR-046-provider-volume-local.md", + "validation" : "`tests/layout_provision.rs`, `tests/layout_repair.rs`, `tests/layout_adopt.rs`, `tests/acl.rs`, `tests/view_rights.rs`, `tests/source.rs`, `integration/provision.rs`", + "workItemId" : "ADR046-vl-003" + }, + { + "currentSource" : "`d2b-host/src/hardlink_farm.rs`; `d2b-priv-broker/src/ops/{store_sync,store_view_posture,store_view_farm,store_sync_audit,store_sync_export}.rs`; `nixos-modules/store.nix`", + "dataMigration" : "None (format preserved; activation changed from Nix to Volume controller)", + "dependencyOwner" : "ADR046-vl-003; runtime-cloud-hypervisor Provider owner", + "destination" : "`src/store_view.rs`; `tests/store_view.rs`; `integration/store_view.rs`", + "detailedDesign" : "Store-view LayoutEntry matrix (see §Same-filesystem hardlink farm); private-NS sync via `run_store_sync` effect op; `StoreSyncComplete` semantic operation; OFD lock semantics; `gcroots/` and `state/` at store-view root; spec-correction enforcement", + "integration" : "`runtime-cloud-hypervisor` Provider declares store-view Volume in its ProviderStateSet; volume-local controller handles sync", + "removalProof" : "`nixos-modules/store.nix` activation and `d2b-priv-broker/src/ops/store_sync.rs` retired only after store-view Volume controller is live and passes all parity tests", + "reuseAction" : "adapt", + "reuseSource" : null, + "specId" : "ADR-046-provider-volume-local", + "specPath" : "docs/specs/providers/ADR-046-provider-volume-local.md", + "validation" : "`tests/store_view.rs` all invariants; `integration/store_view.rs` same-filesystem boundary; private-NS sync with concurrent reader", + "workItemId" : "ADR046-vl-004" + }, + { + "currentSource" : "`d2b-priv-broker/src/ops/swtpm_dir.rs`", + "dataMigration" : "None (full v3 reset; TPM NVRAM must be backed up by operator)", + "dependencyOwner" : "ADR046-vl-002; device-tpm Provider owner", + "destination" : "`src/swtpm_volume.rs`; `tests/swtpm_volume.rs`; `integration/swtpm_marker.rs`", + "detailedDesign" : "TPM LayoutEntry matrix; `create-if-never-provisioned` + fail-closed repair; broker-maintained provisioning marker; ancestor traverse ACL; `previously-provisioned-swtpm-state-missing` fail-closed detection; `secret-adjacent` sensitivity enforcement", + "integration" : "`device-tpm` Provider declares TPM Volume in ProviderStateSet; volume-local handles layout/marker lifecycle", + "removalProof" : "`d2b-priv-broker/src/ops/swtpm_dir.rs` retired only after device-tpm Provider TPM Volume is live and fail-closed tests pass", + "reuseAction" : "adapt", + "reuseSource" : null, + "specId" : "ADR-046-provider-volume-local", + "specPath" : "docs/specs/providers/ADR-046-provider-volume-local.md", + "validation" : "`tests/swtpm_volume.rs` all scenarios; `integration/swtpm_marker.rs` real broker-maintained marker", + "workItemId" : "ADR046-vl-005" + }, + { + "currentSource" : "No equivalent in baseline; new", + "dataMigration" : "None — full d2b 3.0 reset; no prior state to migrate", + "dependencyOwner" : "ADR046-vl-003", + "destination" : "`src/source.rs` (block-image and tmpfs branches); `tests/source.rs`; `integration/block_image.rs`", + "detailedDesign" : "`block-image`: image file create/verify via `provision_block_image` effect op; `fallocate` performed by adapter when `preallocate: true`; FD transfer to Guest runtime via LaunchTicket via `open_volume_mount_token` effect op; `tmpfs`: `mount_tmpfs`/`umount_tmpfs` effect ops; `size=` and `nr_inodes=` derived from quota fields; cleanup via `umount_tmpfs` op", + "integration" : "Guest runtime Provider (cloud-hypervisor) receives block-image FD from volume-local via LaunchTicket; no path crosses the boundary", + "removalProof" : "Not applicable (new)", + "reuseAction" : "create", + "reuseSource" : null, + "specId" : "ADR-046-provider-volume-local", + "specPath" : "docs/specs/providers/ADR-046-provider-volume-local.md", + "validation" : "`tests/source.rs` allowlist pass/fail; block-image/tmpfs eval constraints; `integration/block_image.rs` real image lifecycle", + "workItemId" : "ADR046-vl-006" + }, + { + "currentSource" : "`d2b-state/src/atomic.rs` (main); no existing migration/snapshot infrastructure in v3", + "dataMigration" : "None (new protocol)", + "dependencyOwner" : "ADR046-vl-002; ADR046-vl-003; ADR046-pstate-004 through ADR046-pstate-006", + "destination" : "`src/{migration,snapshot}.rs`; `tests/{migration_unit,snapshot_unit}.rs`; `integration/{migration,snapshot}.rs`", + "detailedDesign" : "Schema migration (see §Schema migration) via `volume-migration-worker` EphemeralProcess; Snapshot create/list/expire (see §Snapshots) via `volume-snapshot-worker` EphemeralProcess; controller reports `stateSchemaPhase` and `snapshots` in Volume status. Sealing lifecycle (`sealingStatus`, key-shred) is a core/framework concern dispatched through `VolumeEffectPort` semantic ops, not a Provider-owned EphemeralProcess worker.", + "integration" : "Controller's reconcile handler dispatches EphemeralProcess via d2b-bus `ResourceClient`; volume-local reports `stateSchemaPhase`, `snapshots` in Volume status", + "removalProof" : "Not applicable (new)", + "reuseAction" : "adapt", + "reuseSource" : null, + "specId" : "ADR-046-provider-volume-local", + "specPath" : "docs/specs/providers/ADR-046-provider-volume-local.md", + "validation" : "All `tests/migration_unit.rs`, `tests/snapshot_unit.rs`, `integration/migration.rs`, `integration/snapshot.rs` scenarios", + "workItemId" : "ADR046-vl-007" + }, + { + "currentSource" : "No equivalent; new", + "dataMigration" : "Not applicable", + "dependencyOwner" : "ADR046-vl-003; ADR046-vl-007", + "destination" : "`src/relocation.rs`; `tests/relocation_unit.rs`; `integration/relocation.rs`", + "detailedDesign" : "As documented in §Relocation, §Retention, §Incident hold, §Unclaimed Volume GC, §Destruction", + "integration" : "Controller adds `Relocating` finalizer; creates relocation EphemeralProcess; destruction sequence is ordered leaf-first with `fsync` after each step", + "removalProof" : "Not applicable (new)", + "reuseAction" : "create", + "reuseSource" : null, + "specId" : "ADR-046-provider-volume-local", + "specPath" : "docs/specs/providers/ADR-046-provider-volume-local.md", + "validation" : "All `tests/relocation_unit.rs`, `integration/relocation.rs` scenarios; destruction ordering under fault injection", + "workItemId" : "ADR046-vl-008" + }, + { + "currentSource" : "`d2b-state/src/audit.rs` (main `6faa5256`); OTEL cardinality model from `d2b-provider-observability-local/src/` (main `a1cc0b2d`)", + "dataMigration" : "None — full d2b 3.0 reset; no prior state to migrate", + "dependencyOwner" : "ADR046-vl-001; Zone audit stream owner; `observability-otel` Provider owner", + "destination" : "`src/audit.rs`; `src/otel.rs`; `src/error.rs`; `tests/audit_unit.rs`; `integration/audit.rs`", + "detailedDesign" : "Event types and Zone audit emission per §Audit events; OTEL metric definitions per §OTEL metrics; error catalog per §Error catalog; no-path invariant enforced in all outputs", + "integration" : "Every lifecycle transition calls `audit::emit_volume_event`; OTEL metrics exported via `observability-otel` Provider", + "removalProof" : "Not applicable", + "reuseAction" : "adapt", + "reuseSource" : null, + "specId" : "ADR-046-provider-volume-local", + "specPath" : "docs/specs/providers/ADR-046-provider-volume-local.md", + "validation" : "`tests/audit_unit.rs` golden records; `tests/error_messages.rs` bounded messages; OTEL label cardinality; `integration/audit.rs` live stream", + "workItemId" : "ADR046-vl-009" + }, + { + "currentSource" : "`nixos-modules/storage-json.nix`; `nixos-modules/store.nix`; `packages/xtask/src/main.rs` (`gen-schemas`)", + "dataMigration" : "`nixos-modules/storage-json.nix` path rows superseded by Volume resources; `nixos-modules/store.nix` activation superseded by store-view Volume", + "dependencyOwner" : "ADR046-vl-001; ADR046-pstate-010; NixOS module owner", + "destination" : "`nixos-modules/zone-resources.nix` (per §ADR046-pstate-010); `root-config.schema.json` in the Provider package", + "detailedDesign" : "`sourcePolicies` in Provider root config (opaque IDs; no raw host paths); path prefix injection by resource compiler into private bundle (never into ResourceSpec or operator-authored Nix); `controllerExecutionRef` in Provider config; all eval-time validation rules per §Nix configuration including `sourcePolicyId` validation; artifact catalog entry; Provider and Volume resource authoring shapes", + "integration" : "NixOS build emits `/etc/d2b/zones//resources.json`; Zone daemon activates bundle and creates Volume resources; volume-local controller reconciles", + "removalProof" : "`nixos-modules/storage-json.nix` and `nixos-modules/store.nix` per-VM rows retired only after Volume resources replace every path row and all consumers complete bundle-format migration", + "reuseAction" : "adapt", + "reuseSource" : null, + "specId" : "ADR-046-provider-volume-local", + "specPath" : "docs/specs/providers/ADR-046-provider-volume-local.md", + "validation" : "All Nix eval-time validation rules; `contentId` determinism; credential-ref guard; unknown Provider config key → build fail", + "workItemId" : "ADR046-vl-010" + }, + { + "currentSource" : "`packages/xtask/src/main.rs` (`gen-schemas`, workspace-policy checks); `tests/unit/gates/drift-check.sh`", + "dataMigration" : "Not applicable", + "dependencyOwner" : "ADR046-vl-002; ADR046-pstate-011; workspace policy owner", + "destination" : "`packages/xtask/src/provider_crate_policy.rs`; `tests/unit/gates/provider-crate-layout-check.sh`", + "detailedDesign" : "`cargo xtask check-provider-crate-layout` gate asserts `src/`, `tests/`, `integration/` (with at least one `.rs` file and a `README.md`), and `README.md` for every `packages/d2b-provider-*` workspace member; fails closed with typed `missing-provider-crate-path` error Primary reuse disposition: `adapt`. Preserved source-plan detail: extend (per ADR046-pstate-011).", + "integration" : "`make test-policy` runs the gate; GitHub CI runs `make test-policy` on every PR", + "removalProof" : "Not applicable (permanent gate)", + "reuseAction" : "adapt", + "reuseSource" : null, + "specId" : "ADR-046-provider-volume-local", + "specPath" : "docs/specs/providers/ADR-046-provider-volume-local.md", + "validation" : "Gate detects each missing path; idempotent across re-runs; existing non-provider `d2b-*` crates not flagged", + "workItemId" : "ADR046-vl-011" + }, + { + "currentSource" : "`d2b-priv-broker/src/ops/{state_dir,storage_contract,swtpm_dir,store_sync,store_view_posture}.rs`; `d2b-host/src/hardlink_farm.rs`", + "dataMigration" : "None (adapter replaces direct broker-op call sites)", + "dependencyOwner" : "ADR046-vl-001; ADR046-vl-002; Zone broker/core owner", + "destination" : "`packages/d2b-host/src/volume_effect_adapter.rs` (or the equivalent host-runtime crate designated by the Zone broker owner); implements the `VolumeEffectPort` trait defined in `d2b-contracts`", + "detailedDesign" : "Adapter holds trusted FD table keyed by `VolumeId`; resolves `SourcePolicyId` to host path prefix from private bundle; calls `openat2(RESOLVE_BENEATH)` anchored at retained FD for all FS ops; calls `setfacl`/`acl_set_fd`, `mount`/`umount`, `fallocate` from within adapter only; emits path-free audit records for each op (audit is never atomic with redb write); injected into controller via Zone runtime ComponentSession; blocking filesystem calls run in bounded blocking-thread pool Primary reuse disposition: `adapt`. Preserved source-plan detail: adapt into adapter.", + "integration" : "Zone runtime creates adapter with required FD table and bundle reference at provider startup; passes `Arc` to controller via ComponentSession bootstrap", + "removalProof" : "Baseline broker op handlers (`state_dir.rs`, `storage_contract.rs`, `swtpm_dir.rs`, `store_sync.rs`, `store_view_posture.rs`) retired only after Volume controller parity is confirmed and all callers are on the adapter", + "reuseAction" : "adapt", + "reuseSource" : null, + "specId" : "ADR-046-provider-volume-local", + "specPath" : "docs/specs/providers/ADR-046-provider-volume-local.md", + "validation" : "Adapter hermetic tests: each effect op called with mock FD table and bundle; no path in any output; anchored-path rejection for RESOLVE_BENEATH violations; `cargo deny check` verifies adapter does not expose raw paths to Provider crate; `integration/provision.rs` exercises full adapter path", + "workItemId" : "ADR046-vl-012" + }, + { + "currentSource" : "No equivalent; new", + "dataMigration" : "None; pre-existing baseline `StorageRoot` rows for the volume-local controller are superseded on v3 reset", + "dependencyOwner" : "ADR046-vl-001; ADR046-vl-012; Zone broker/core owner", + "destination" : "Zone core ProviderDeployment controller-start path (outside `d2b-provider-volume-local`)", + "detailedDesign" : "The volume-local controller declares no Provider state Volume, so there is no bootstrap Volume, no `BootstrapProviderStateVolume` broker op, no pre-provisioned controller Volume, and no bootstrap-storage exception (D086, superseded by D087). On first install and on every daemon restart, core ProviderDeployment starts the volume-local controller Process directly; the controller reaches `Ready` from its own resource `status`, the core Operation ledger, and a resource-store relist. Once Ready, it reconciles every Volume carrying `providerRef: Provider/volume-local` (operator-created Volumes and other Providers' declared state Volumes) as they appear in its `providerRef` watch, re-verifying identity markers against external reality, never creating them itself. A Guest bootstraps its own Guest-local volume-local instance from Guest-local primitives only.", + "integration" : "Core ProviderDeployment spawns the controller Process with no state-Volume prerequisite; the controller's startup relist reconciles served Volumes and re-verifies markers", + "removalProof" : "Not applicable (new)", + "reuseAction" : "create", + "reuseSource" : null, + "specId" : "ADR-046-provider-volume-local", + "specPath" : "docs/specs/providers/ADR-046-provider-volume-local.md", + "validation" : "`integration/provider_state.rs`: controller starts and reaches Ready with no state Volume; served Volumes reconciled and markers re-verified after restart; no bootstrap Volume and no bootstrap Provider Process in the resource list", + "workItemId" : "ADR046-vl-013" + }, + { + "currentSource" : "`packages/d2b-core/src/storage.rs`, `sync.rs`; `nixos-modules/storage-json.nix`", + "dataMigration" : "Full d2b 3.0 reset; storage.json rows migrated per table above", + "dependencyOwner" : "W0 shared contract root; `d2b-contracts`", + "destination" : "`packages/d2b-contracts/src/v3/volume.rs`, `volume_layout.rs`, `volume_attachment.rs`", + "detailedDesign" : "Complete Volume ResourceSpec, LayoutEntry, all policy enums (values preserved from baseline), AclGrant, ViewSpec, AttachmentSpec, quota placeholder, strict serde unknown-field rejection, canonicalization, bounds Primary reuse disposition: `adapt`. Preserved source-plan detail: extract and adapt.", + "integration" : "Provider dossiers/controller descriptors bind exact types; Nix resource compiler emits canonical JSON", + "removalProof" : "`d2b-core/src/storage.rs` and `sync.rs` removed only after all Volume-successor consumers are live", + "reuseAction" : "adapt", + "reuseSource" : null, + "specId" : "ADR-046-resources-volume", + "specPath" : "docs/specs/ADR-046-resources-volume.md", + "validation" : "Golden JSON spec vectors; serde unknown-field; path anchor/depth/traversal validators; ACL grant bounds; policy enum coverage", + "workItemId" : "ADR046-volume-001" + }, + { + "currentSource" : "`nixos-modules/storage-json.nix`, `nixos-modules/store.nix`, `packages/d2b-priv-broker/src/ops/swtpm_dir.rs`, `packages/d2b-priv-broker/src/ops/storage_contract.rs` (`reconcile_storage_scope`, `validate_lock_spec`), `packages/d2b-priv-broker/src/ops/store_sync.rs` (`run_store_sync`, `run_store_sync_repair`, `cleanup_store_view`, `prune_gcroots`), `packages/d2b-priv-broker/src/ops/store_sync_audit.rs`, `packages/d2b-priv-broker/src/ops/store_sync_export.rs`, `packages/d2b-priv-broker/src/ops/store_view_posture.rs` (`posture_store_view_matrix_paths`, `plant_live_marker_with_matrix_posture`), `packages/d2b-priv-broker/src/ops/state_dir.rs` (`PrepareStateDir`, `PrepareRuntimeDir`), `packages/d2b-host/src/hardlink_farm.rs` (`build_store_view`, `GenerationMarker`, `gcroots_dir`, `state_dir`), `packages/d2b-core/src/storage_lifecycle.rs` (`StorageLifecycleReport`, `StorageLifecycleIssue`), `packages/d2b-contract-tests/tests/storage_sync_contracts.rs`, `packages/d2bd/src/ownership_preflight.rs`", + "dataMigration" : "Per-VM `storage.json` rows (scoped `\"vm:\"` → `ownerRef: Guest/`) replaced by Volume resources generated by Nix resource compiler; broker continues to own path operations", + "dependencyOwner" : "ADR046-volume-001; volume-local Provider owner", + "destination" : "`packages/d2b-provider-volume-local/src/` (layout engine, store_view.rs, swtpm_volume.rs, broker op adapters); `packages/d2b-provider-volume-local/tests/` (hermetic layout/store-view/swtpm tests); `packages/d2b-provider-volume-local/integration/` (container Host-path and store-view FS fixtures); `packages/d2b-provider-volume-local/README.md`", + "detailedDesign" : "volume-local controller: layout engine (provision/repair/cleanup/adopt per policy), store-view mode (hardlink farm from `hardlink_farm.rs`, private-NS sync, zero-length marker, `gcroots/` and `state/` at store-view root, `sync.lock` OFD), swtpm volume hardening (provisionIfNeverProvisioned + marker + fail-closed repair as in `swtpm_dir.rs`), path-free broker audit ops, storage lifecycle report, opaque BundleOpId contract preserved from `storage_contract.rs` Primary reuse disposition: `adapt`. Preserved source-plan detail: extract and adapt.", + "integration" : "volume-local controller registered under Host/system-core; resource status written via ResourceClient", + "removalProof" : "`nixos-modules/storage-json.nix` removed only after Volume resources replace all path rows and all consumers verified", + "reuseAction" : "adapt", + "reuseSource" : null, + "specId" : "ADR-046-resources-volume", + "specPath" : "docs/specs/ADR-046-resources-volume.md", + "validation" : "`tests/unit/nix/cases/per-vm-state-ownership.nix` adapted to Volume LayoutEntry matrix; `tests/unit/smoke/smoke-eval-tpm.nix` migrated to TPM Volume invariant; `d2b-contract-tests/tests/storage_sync_contracts.rs` parity tests adapted; new: store-view same-filesystem, zero-length marker existence, sync.lock preserve-OFD, gcroots at store-view root (not meta/), state/ dir existence, swtpm fail-closed-on-missing-after-provision, anchored-path validators", + "workItemId" : "ADR046-volume-002" + }, + { + "currentSource" : "`packages/d2b-host/src/virtiofsd_argv.rs` (`VirtiofsdArgvInput`, `generate_virtiofsd_argv`), `nixos-modules/minijail-profiles.nix` (virtiofsdProfiles; principals `d2b--runner`, `d2b--gctlfs`), `nixos-modules/processes-json.nix` (virtiofsdRunner shape; `roStoreSharedDir` sentinel), `packages/d2b-core/src/processes.rs` (`ProcessRole::Virtiofsd`, `VmProcessDag`; the virtiofsd dag node is a `ProcessRole::Virtiofsd` entry in a WorkloadId-keyed `VmProcessDag`), `packages/d2b-priv-broker/src/ops/spawn_runner.rs` (`SpawnRunnerPlan` for virtiofsd; current `SpawnRunnerPlanInput` carries `adr_carve_out` for virtiofsd swtpm path), `packages/d2b-priv-broker/src/sys.rs` (clone3/user-NS pre-establishment), ADR 0021", + "dataMigration" : "Current `processes-json.nix` virtiofsd `VmProcessDag` nodes (keyed by `WorkloadId` = current VM name, role `ProcessRole::Virtiofsd`) replaced by virtiofsd Process resources owned by Volume", + "dependencyOwner" : "ADR046-volume-001; volume-virtiofs Provider owner", + "destination" : "`packages/d2b-provider-volume-virtiofs/src/` (controller, virtiofsd_argv.rs); `packages/d2b-provider-volume-virtiofs/tests/` (hermetic argv/lifecycle/ADR-0021 tests); `packages/d2b-provider-volume-virtiofs/integration/` (virtiofsd launch and guest-mount fixtures); `packages/d2b-provider-volume-virtiofs/README.md`", + "detailedDesign" : "volume-virtiofs controller: attachment lifecycle, owned virtiofsd Process create/update/delete, argv generation (reuse current 14 tests), ADR 0021 invariant (`capabilityClasses: []`, `startRoot: false`, `sandbox: chroot`, user-NS via `userNamespace.mappingClass: process-principal-root`), per-attachment export socket readiness check (`unix-socket-exists` readiness kind; current v2 socket path: `/run/d2b/vms//-virtiofs-.sock`; v3: stable hash-derived private path under Zone runtime directory, never exposed in spec/status/API), guest-mount status observation, finalizer drain Primary reuse disposition: `adapt`. Preserved source-plan detail: extract and adapt.", + "integration" : "volume-virtiofs registered under Host; virtiofsd Process resource owned by Volume; guest-control health integration for mount readiness", + "removalProof" : "`nixos-modules/processes-json.nix` virtiofsdRunner block removed only after virtiofsd Process resources pass parity; `packages/d2bd/src/supervisor/dag.rs` `ProcessRole::Virtiofsd` path removed after controller lifecycle covers all cases", + "reuseAction" : "adapt", + "reuseSource" : null, + "specId" : "ADR-046-resources-volume", + "specPath" : "docs/specs/ADR-046-resources-volume.md", + "validation" : "Migrated `virtiofsd_argv` unit tests (14 tests); `tests/tools/gen-migration-ledger.sh` virtiofsd-argv-shape gate adapted; `minijail-validator-virtiofsd` gate adapted to Process sandbox spec; new: attachment lifecycle (create/ready/delete), ADR 0021 invariant rejection test, multi-attachment isolation, readOnly flag per access mode, store-view shared-dir = store-view/live (never /nix/store)", + "workItemId" : "ADR046-volume-003" + }, + { + "currentSource" : "`nixos-modules/storage-json.nix`, `nixos-modules/store.nix`, `nixos-modules/options-vms.nix` (`d2b.vms..*` — current VM Nix option namespace; virtiofs shares and TPM enable are configured here), `nixos-modules/options-realms-workloads.nix` (`d2b.realms..stateDir` — current realm workload state root), `packages/d2b-realm-core/src/workload.rs` (`WorkloadProviderKind::LocalVm`/`QemuMedia`/`UnsafeLocal` — informs which WorkloadIds need store-view Volumes vs. no Volume)", + "dataMigration" : "`d2b.vms..shares` (virtiofs entries) → Volume attachments; `d2b.vms..tpm.enable` → swtpm Volume", + "dependencyOwner" : "ADR046-volume-001; Nix integrator", + "destination" : "`nixos-modules/resources-volume.nix`, `nixos-modules/options-volumes.nix`", + "detailedDesign" : "Nix resource compiler for Volume/LayoutEntry/View/Attachment from d2b.zones config; strict schema validation; emit canonical JSON per Volume; generate store-view Volume per Guest (from current `d2b.vms.` → future flat `d2b.zones..resources.` with `type = \"Guest\"`) with hardlink-farm layout (gcroots/, state/ at root per `hardlink_farm.rs`); generate swtpm Volume for TPM-enabled Guests; emit volume-virtiofs attachment spec per virtiofs share; migration: store-view stateDir root configuration", + "integration" : "`nixos-modules/default.nix` wires resources-volume.nix; Nix evaluation tests verify canonical output", + "removalProof" : "Old `storage-json.nix` and `store.nix` emitters removed only after rendered Volume JSON passes all drift-check gates", + "reuseAction" : "adapt", + "reuseSource" : null, + "specId" : "ADR-046-resources-volume", + "specPath" : "docs/specs/ADR-046-resources-volume.md", + "validation" : "nix-unit cases for store-view Volume output (gcroots at root), TPM Volume spec, virtiofs attachment spec, anchored-path rejection; render parity with current storage.json path rows; canonical JSON golden vector; Provider schema validation rejection; symlink target validation; bundle digest coverage", + "workItemId" : "ADR046-volume-004" + }, + { + "currentSource" : "N/A (no baseline evidence for block-image, quota enforcement, snapshots, or tmpfs Volume paths)", + "dataMigration" : "Per-sub-item; block-image and tmpfs are new capabilities with no legacy migration required", + "dependencyOwner" : "ADR046-volume-002, ADR046-volume-003; respective Provider owners", + "destination" : "`packages/d2b-provider-volume-local/src/` (block-image, quota, snapshots, tmpfs, ACL reconciliation); `packages/d2b-provider-volume-local/tests/` (hermetic quota/tmpfs/ACL/block-image/snapshot tests); `packages/d2b-provider-volume-local/integration/` (block-image virtio-blk, FS-without-quota fixture, tmpfs memory-budget); `packages/d2b-provider-volume-virtiofs/src/` (single-writer enforcement, shared-write, private socket path contract); `packages/d2b-provider-volume-virtiofs/tests/` (single-writer rejection, shared-write capability gate, socket-path invariant); `packages/d2b-provider-volume-virtiofs/integration/` (shared-write cross-Guest fixture)", + "detailedDesign" : "(1) **block-image SourceKind**: add `SourceKind::BlockImage` to volume-local; manage raw/qcow2 image file lifecycle; emit virtio-blk attachment spec consumed by Guest Provider; `quota.maxBytes` required; add store-overlay.img migration path for current `DiskInit` plan-op. (2) **Quota hard enforcement**: implement `enforcement: hard` capability check in volume-local at Volume creation time; query backing FS for quota/limits support; test with no-project-quota fixture; enforce `maxBytes`/`maxInodes` via xfs project quota or ext4 per-dir quota where available. (3) **Volume snapshots/migrations**: design and implement EphemeralProcess templates in volume-local catalog for snapshot (copy-on-write or rsync capture) and content migration (atomic rename + sync); no CLI-only path; all operations surface through resource API. (4) **Single-writer enforcement**: volume-virtiofs controller tracks active `read-write` attachment count; rejects a second `read-write` attachment while one is active (returns `ResourceConflict` error); `shared-write` mode accepted only if Provider declares `supportsSharedWrite: true`. (5) **tmpfs source**: implement tmpfs mount/unmount lifecycle in volume-local; `maxBytes` → `size=`, `maxInodes` → `nr_inodes=` mount options; charge memory against Host/Guest budget; cleanup unmounts on Volume deletion or restart. (6) **Bounds enforcement**: enforce max 1024 layout entries, 64 Views, 64 attachments at schema validation layer; add corresponding row to API request-size limit table in `ADR-046-resource-api-and-authorization`. (7) **File/symlink first-class lifecycle**: implement independent `createPolicy`/`repairPolicy`/`cleanupPolicy` for `file` and `symlink` entries; implement `target` field validation (relative, no `..`, must resolve within Volume root); `symlink` create writes the target link. (8) **ACL principal ResourceRef**: remove bare `{type,ref}` struct from AclGrant; implement `User/` ResourceRef resolution with User resource watch and re-reconcile on User revision change. (9) **Continuous ACL reconciliation**: implement `foreignChildPolicy: preserve|fail` in broker reconcile loop; re-apply `accessAcl`/`defaultAcl` to all existing entries and children on every repair cycle; emit `ForeignAclViolation` condition when `foreignChildPolicy: fail` and unexpected entries found. (10) **virtiofsd socket path contract**: implement stable hash-derived private socket path in volume-virtiofs (deterministic hash of Zone name + Volume name + attachment executionRef); assert path never appears in public status, spec, audit, or CLI output; validate with a dedicated security invariant test.", + "integration" : "Each sub-item produces a focused spec amendment; resolved decisions already reflected in spec revision 2", + "removalProof" : "None — net-new capabilities; no prior owner to remove", + "reuseAction" : "create", + "reuseSource" : null, + "specId" : "ADR-046-resources-volume", + "specPath" : "docs/specs/ADR-046-resources-volume.md", + "validation" : "(1) `VirtioblkArgvInput` unit tests; block-image integration fixture. (2) Quota-enforcement fixture with FS-without-quota; hard-enforcement failure test. (3) EphemeralProcess snapshot lifecycle test; content-migration parity test. (4) Single-writer rejection test; shared-write capability gate test. (5) tmpfs mount/unmount lifecycle test; memory-budget accounting assertion. (6) Schema bound rejection tests (1025 entries, 65 views, 65 attachments). (7) File/symlink independent lifecycle tests; target validation (absolute rejected, `..` rejected, escape rejected). (8) ACL principal ResourceRef validation; numeric form rejected; User revision trigger test. (9) foreignChildPolicy preserve/fail tests; continuous repair cycle test. (10) Socket path invariant test; no-status-leak assertion.", + "workItemId" : "ADR046-volume-005" + }, + { + "currentSource" : "`nixos-modules/storage-json.nix` (current Nix eval-time validation via `lib.asserts`; schema checked by `d2b-contract-tests/tests/storage_sync_contracts.rs`); `packages/d2bd/src/` (current config activation / host-prepare dispatch)", + "dataMigration" : "Full d2b 3.0 reset; no partial import of prior generation state", + "dependencyOwner" : "ADR046-volume-001, ADR046-volume-004; Nix integrator and core-controller config-publication owner", + "destination" : "`nixos-modules/resources-volume.nix` (Nix eval-time schema validation, canonical JSON emission, bundle digest); `packages/d2b-core-controller/src/configuration.rs` (config-publication handler cleanup logic); `packages/d2b-contracts/src/v3/zone_bundle.rs` (bundle index schema)", + "detailedDesign" : "**Nix eval/build validation**: implement all 15 validation steps in §Nix eval/build validation as Nix assertions; abort build on any failure with structured error (Volume name + field path + error class); provider-specific settings schema (`root-config.schema.json`, `attachment.schema.json`) read from the private artifact catalog entry for each Provider's `artifactId` by the resource compiler; validate against `lib.evalModules`-compatible schema; emit canonical sorted JSON with all defaults materialized; compute SHA-256 per resource; emit Zone resource bundle with `generationId` and `bundleDigest`. **Config-publication handler cleanup**: on new bundle activation, diff resources with `metadata.managedBy = \"configuration\"` between new and prior bundle; issue async Delete for resources absent from new bundle; mark deleted resources with `ConfigurationRemoved` condition; track pending-cleanup set in Zone status; Zone status is `Degraded/PendingCleanup` while prior-generation deletions are in progress; activation is immediate but Zone readiness reflects cleanup completion. **Config-owned vs controller-created distinction**: `metadata.managedBy = \"configuration\"` is the authoritative marker set by core at activation; the bundle index carries only digests; controller-created resources have `metadata.managedBy = \"controller\"` and are never touched by the configuration cleanup pass. **Prior generation retention**: retain `priorGenerationCount` prior generations (default 3, range 1..16); no time-based TTL; when count is exceeded prune oldest generation from the store with a tamper-evident audit record. **Generation reactivation**: re-activate any retained prior bundle via `ActivateGeneration` operation; cancel in-flight Deletes for resources being reinstated; issue Deletes for resources added by the aborted new generation.", + "integration" : "`nixos-modules/default.nix` wires resources-volume.nix; `d2b-core-controller` config-publication handler consumes the bundle; all Volume controllers observe `ConfigurationRemoved` condition and respond to finalizer triggers", + "removalProof" : "Old `storage-json.nix` schema assertions removed only after Nix eval-time Volume validation covers all prior `lib.asserts` paths; old config-activation code in `d2bd` removed after config-publication handler is live", + "reuseAction" : "create", + "reuseSource" : null, + "specId" : "ADR-046-resources-volume", + "specPath" : "docs/specs/ADR-046-resources-volume.md", + "validation" : "Tests per §Cleanup contract — Tests for removed-resource cleanup table (10 tests); nix-unit: `volume_canonical_json_golden_vector`, `volume_bundle_digest_covers_all_resources`, `provider_schema_validation_rejects_unknown_fields`, `symlink_target_escape_rejected_at_eval`, `tmpfs_without_quota_rejected_at_eval`, `layout_bounds_1025_entries_rejected`, `attachment_bounds_65_rejected`, `conflicting_host_paths_rejected`; integration: cleanup audit redaction, generation reactivation, prior-generation pruning", + "workItemId" : "ADR046-volume-006" + }, + { + "currentSource" : "Evidence class `test-only-or-preview`; baseline has no generic vsock transport Provider or opaque endpoint/binding ID trait.", + "dataMigration" : "Full d2b 3.0 reset; no v2 state/config import.", + "dependencyOwner" : "Title: Implement `VsockEffectPort` trait and `OpaqueEndpointId`/`OpaqueBindingId` newtypes; Phase 1; Priority P0; Depends on ADR046-session-003 (OwnedTransport in d2b-session); Owner crate `d2b-provider-transport-vsock`.", + "destination" : "`packages/d2b-provider-transport-vsock/src/effect_port.rs`; test fake in `tests/effect_port_mock.rs`; redaction checks in `tests/redaction.rs`.", + "detailedDesign" : "Implement `VsockEffectPort` trait and `OpaqueEndpointId`/`OpaqueBindingId` newtypes. Define `VsockEffectPort` async trait and opaque ID newtypes in `effect_port.rs`; implement `FakeVsockEffectPort` for tests; `redaction.rs` asserts no raw `u32` in any `Debug`/`Display` output of opaque types; no real vsock socket opened. Primary reuse disposition: `create`. Preserved source-plan detail: net-new trait/newtypes with redaction tests; no real vsock socket opened.", + "integration" : "Core ZoneLink/delegation controller calls the Provider service with opaque IDs; Provider calls injected `VsockEffectPort`; live AF_VSOCK resolution remains in core runtime, not the Provider crate.", + "removalProof" : "None — net-new; no prior owner to remove.", + "reuseAction" : "create", + "reuseSource" : null, + "specId" : "ADR-046-provider-transport-vsock", + "specPath" : "docs/specs/providers/ADR-046-provider-transport-vsock.md", + "validation" : "Proof type: hermetic unit + redaction test; `tests/effect_port_mock.rs` and `tests/redaction.rs`.", + "workItemId" : "ADR046-vsock-001" + }, + { + "currentSource" : "Evidence class `implemented-but-unwired`; main commit `a1cc0b2da4a08ca3240a770a972fe4da6f912bef` `packages/d2b-session-unix/src/vsock.rs` contains `FramedVsockTransport` framing behavior, not current v3 baseline behavior.", + "dataMigration" : "Full d2b 3.0 reset; no v2 state/config import.", + "dependencyOwner" : "Title: Implement framing utilities and bridge task in Provider crate; Phase 1; Priority P0; Depends on ADR046-vsock-001; Owner crate `d2b-provider-transport-vsock`.", + "destination" : "`packages/d2b-provider-transport-vsock/src/framing.rs` and `src/bridge.rs`; tests in `packages/d2b-provider-transport-vsock/tests/framing.rs`.", + "detailedDesign" : "Implement framing utilities and bridge task in Provider crate. Copy `FramedVsockTransport` framing-only code (length-prefix encode/decode, bounded allocation, EOF/reset) from main `a1cc0b2d` → `framing.rs`; implement bridge task pumping bytes between an opaque `AsyncRead+AsyncWrite` stream from `VsockEffectPort::open` and the named ComponentSession stream; hermetic tests using `FakeVsockEffectPort` (no real socket). Primary reuse disposition: `adapt`. Preserved source-plan detail: copy/adapt framing-only code; exclude raw AF_VSOCK socket calls and ADR 0045 endpoint-role assumptions.", + "integration" : "OpenTransport creates a framed opaque stream, bridge task pumps to a named ComponentSession stream, and d2b-bus consumes it as an `OwnedTransport` without FD attachment support.", + "removalProof" : "None for framing; raw socket portions from the source are deliberately not copied into the Provider crate.", + "reuseAction" : "adapt", + "reuseSource" : "Main `a1cc0b2d` `packages/d2b-session-unix/src/vsock.rs` framing-only code and `packages/d2b-session-unix/tests/unix_session.rs` vsock framing subset.", + "specId" : "ADR-046-provider-transport-vsock", + "specPath" : "docs/specs/providers/ADR-046-provider-transport-vsock.md", + "validation" : "Proof type: hermetic framing tests; `tests/framing.rs` covers partial/coalesced records, oversized frames, EOF/reset classification, and no real socket.", + "workItemId" : "ADR046-vsock-002" + }, + { + "currentSource" : "Evidence class `test-only-or-preview`; no current v3 generic `VsockTransportService` implementation exists.", + "dataMigration" : "Full d2b 3.0 reset; no v2 state/config import.", + "dependencyOwner" : "Title: Implement `VsockTransportService` (OpenTransport/CloseTransport/ObserveTransport); Phase 1; Priority P0; Depends on ADR046-vsock-002 and ADR046-session-003; Owner crate `d2b-provider-transport-vsock`.", + "destination" : "`packages/d2b-provider-transport-vsock/src/service.rs`; tests `tests/open_close.rs`, `tests/observe.rs`, and conformance kit.", + "detailedDesign" : "Implement `VsockTransportService` (OpenTransport/CloseTransport/ObserveTransport). Implement all three service methods in `service.rs`; `open_close.rs` and `observe.rs` test full service API against `FakeVsockEffectPort`; conformance kit passes. Primary reuse disposition: `adapt`. Preserved source-plan detail: net-new service implementation over ComponentSession and fake effect port tests.", + "integration" : "Core ZoneLink/delegation controller is the only authorized caller; service opens named stream handles for d2b-bus, releases them on close, and streams transport events for observe.", + "removalProof" : "None — net-new; no prior owner to remove.", + "reuseAction" : "adapt", + "reuseSource" : null, + "specId" : "ADR-046-provider-transport-vsock", + "specPath" : "docs/specs/providers/ADR-046-provider-transport-vsock.md", + "validation" : "Proof type: service round-trip test (mock); `tests/open_close.rs`, `tests/observe.rs`, and provider conformance tests.", + "workItemId" : "ADR046-vsock-003" + }, + { + "currentSource" : "Evidence class `ADR-only`; baseline has guest-control and relay vsock paths, but no allocator-backed `LiveVsockEffectPort` for ZoneLink transport.", + "dataMigration" : "Full d2b 3.0 reset; no v2 state/config import.", + "dependencyOwner" : "Title: Implement `LiveVsockEffectPort` in Zone runtime; Phase 2; Priority P0; Depends on ADR046-vsock-001 and the Zone allocator (`ADR-046-resources-zone-control`); Owner crate `d2b-core-controller`.", + "destination" : "`d2b-core-controller` Zone runtime `LiveVsockEffectPort`; Provider receives it by dependency injection at startup.", + "detailedDesign" : "Implement `LiveVsockEffectPort` in Zone runtime. Zone runtime provides `LiveVsockEffectPort` backed by core allocator state; resolves `OpaqueEndpointId` → CID and `OpaqueBindingId` → port; opens AF_VSOCK socket; injects into Provider service at startup; no raw CID/port exposed to Provider. Primary reuse disposition: `adapt`. Preserved source-plan detail: net-new core adapter; keep raw AF_VSOCK syscalls outside Provider crate.", + "integration" : "Zone allocator issues endpoint/binding IDs; core runtime resolves them, opens/accepts AF_VSOCK sockets, returns opaque streams to Provider service, and excludes reserved ports.", + "removalProof" : "None — net-new core adapter; no prior owner to remove.", + "reuseAction" : "adapt", + "reuseSource" : null, + "specId" : "ADR-046-provider-transport-vsock", + "specPath" : "docs/specs/providers/ADR-046-provider-transport-vsock.md", + "validation" : "Proof type: integration test; `integration/host_guest.rs` exercises live open/close byte round-trip with the injected effect.", + "workItemId" : "ADR046-vsock-004" + }, + { + "currentSource" : "Evidence class `test-only-or-preview`; no operator-authored v3 state Volume exists for transport-vsock in baseline.", + "dataMigration" : "Full d2b 3.0 reset; no v2 state/config import; state Volume is created fresh with `migrationPolicy: none`.", + "dependencyOwner" : "Title: Core ProviderDeployment creates/deletes service component state Volume; Phase 1; Priority P0; Depends on the volume-local Provider (`ADR-046-provider-volume-local`); Owner crate `d2b-provider-transport-vsock`.", + "destination" : "ProviderDeployment Volume creation/deletion path plus `packages/d2b-provider-transport-vsock/tests/state_volume.rs`.", + "detailedDesign" : "Core ProviderDeployment creates/deletes service component state Volume. Core ProviderDeployment creates `Volume/transport-vsock--service--empty-state--*` before the component Process and deletes it after the Process finalizer; transport-vsock Provider controller does not own Volume, does not add Volume to exported ResourceTypes, and does not create its prerequisite; Volume spec: empty schema, `kind: state`, `persistenceClass: persistent`, `migrationPolicy: none`, `User/d2b-transport-vsock` owner, minimal nonzero `quota.maxBytes`/`quota.maxInodes` with `enforcement: hard`, `private` sensitivity, `broker-maintained` identity marker; `state_volume.rs` test verifies Volume spec fields against canonical schema; integration test verifies marker written at install and removed at Provider deletion; no operator-authored Volume; component receives dirfd view only. Primary reuse disposition: `create`. Preserved source-plan detail: net-new ProviderDeployment/Volume integration and tests.", + "integration" : "Core ProviderDeployment creates Volume before Process, volume-local reconciles it, Provider process receives only a dirfd view, and Provider deletion removes the Process before deleting the Volume/identity marker.", + "removalProof" : "Remove the state Volume and its broker-maintained identity marker during Provider deletion; no operator-authored Volume remains.", + "reuseAction" : "create", + "reuseSource" : null, + "specId" : "ADR-046-provider-transport-vsock", + "specPath" : "docs/specs/providers/ADR-046-provider-transport-vsock.md", + "validation" : "Proof type: unit + integration test; `tests/state_volume.rs` and Provider install/remove integration tests verify schema, user refs, marker lifecycle, and no ComponentPrincipal.", + "workItemId" : "ADR046-vsock-005" + }, + { + "currentSource" : "Evidence class `test-only-or-preview`; existing guest-control compile proof and socat relay tests are not full ZoneLink transport coverage.", + "dataMigration" : "None — docs/tooling only; no runtime state.", + "dependencyOwner" : "Title: Integration test: real vsock socketpair + full ZoneLink open/close; Phase 2; Priority P1; Depends on ADR046-vsock-003 and ADR046-vsock-004; Owner crate `d2b-provider-transport-vsock`.", + "destination" : "`packages/d2b-provider-transport-vsock/integration/host_guest.rs` and `integration/no_fd_transfer.rs`.", + "detailedDesign" : "Integration test: real vsock socketpair + full ZoneLink open/close. `integration/host_guest.rs`: real vsock socketpair (Linux); `OpenTransport` + byte round-trip + `CloseTransport`; validates bridge throughput ≥ 512 MiB/s; `no_fd_transfer.rs`: structural rejection of attachment packets over vsock transport. Primary reuse disposition: `create`. Preserved source-plan detail: net-new integration coverage with no FD transfer over vsock.", + "integration" : "Test drives Provider service, LiveVsockEffectPort, d2b-bus `OwnedTransport`, byte bridge, close path, and attachment rejection across the integration lane.", + "removalProof" : "None — test coverage net-new; old duplicate vsock tests are retired only after successor assertions migrate.", + "reuseAction" : "create", + "reuseSource" : null, + "specId" : "ADR-046-provider-transport-vsock", + "specPath" : "docs/specs/providers/ADR-046-provider-transport-vsock.md", + "validation" : "Proof type: integration test; `make test-integration` runs `host_guest.rs` and `no_fd_transfer.rs`.", + "workItemId" : "ADR046-vsock-006" + }, + { + "currentSource" : "Evidence class `implemented-and-reachable`; legacy sources are `packages/d2b-host/src/vsock_relay_argv.rs` socat OTLP relay and `packages/d2bd/src/guest_control_vsock.rs` CONNECT-proxy guest-control path.", + "dataMigration" : "Full d2b 3.0 reset; no v2 relay or guest-control state/config import.", + "dependencyOwner" : "Title: Delete legacy socat OTLP relay and CONNECT-proxy guest-control vsock; Phase 3; Priority P2; Depends on the observability-otel Provider (`ADR-046-provider-observability-otel`) and the Guest resource lifecycle (`ADR-046-resources-host-guest-process-user`); Owner crates `d2b-host`, `d2bd`.", + "destination" : "Remove legacy paths from `d2b-host` and `d2bd`; replacement lives in `observability-otel` Provider native vsock relay and Guest resource lifecycle/bootstrap.", + "detailedDesign" : "Delete legacy socat OTLP relay and CONNECT-proxy guest-control vsock. Remove `vsock_relay_argv.rs` socat path after `observability-otel` Provider native vsock relay passes parity; remove `guest_control_vsock.rs` CONNECT-proxy after Guest resource lifecycle + guestd vsock bootstrap reach parity; no raw CID or socat vsock path remains. Primary reuse disposition: `delete-after-cutover`. Preserved source-plan detail: delete after replacement parity; preserve reserved guest-control/OTLP port exclusions until replacements own them.", + "integration" : "Observability-otel owns OTLP vsock relay replacement; Guest lifecycle owns guest-control bootstrap replacement; transport-vsock ZoneLink allocator excludes ports 14317, 14318, and 14319.", + "removalProof" : "Delete `packages/d2b-host/src/vsock_relay_argv.rs` socat path after observability parity, delete `packages/d2bd/src/guest_control_vsock.rs` CONNECT-proxy after Guest lifecycle parity, and prove no raw CID or socat vsock path remains.", + "reuseAction" : "delete-after-cutover", + "reuseSource" : null, + "specId" : "ADR-046-provider-transport-vsock", + "specPath" : "docs/specs/providers/ADR-046-provider-transport-vsock.md", + "validation" : "Proof type: deletion + parity test; parity tests for observability-otel and Guest lifecycle plus redaction checks that no raw CID/socat vsock path remains.", + "workItemId" : "ADR046-vsock-007" + }, + { + "currentSource" : "`packages/d2b-host/src/virtiofsd_argv.rs` (VirtiofsdArgvInput, generate_virtiofsd_argv, 14 unit tests, golden argv.txt); `packages/d2b-host/src/lib.rs` (module declaration)", + "dataMigration" : "v3.0 reset; socket path format changes", + "dependencyOwner" : "ADR046-volume-001 (Volume contract types); ADR046-vvfs-007 (Export type); W1; volume-virtiofs Provider owner", + "destination" : "`packages/d2b-provider-volume-virtiofs/src/virtiofsd_argv.rs`; `packages/d2b-provider-volume-virtiofs/tests/argv_golden.rs`", + "detailedDesign" : "Create crate skeleton with mandatory `src/`, `tests/`, `integration/`, `README.md`. Extract `VirtiofsdArgvInput` and `generate_virtiofsd_argv` with these changes: (1) replace `extra_args: Vec` with nothing (removed); (2) replace `socket_path: String` with `socket_path: SocketPath` newtype backed by `socket_path.rs`; (3) add `shared_dir_fd: i32` replacing `shared_dir: String` (FD-based); (4) replace `socket_group: Option` with `socket_group: Option`. Implement `socket_path.rs`: private path using SHA-256 of `\\x00\\x00`, truncated 8 hex chars, formatted as `/vms//vol-.vfd.sock`. Assert path length ≤ 108 bytes. Primary reuse disposition: `adapt`. Preserved source-plan detail: extract and adapt.", + "integration" : "volume-virtiofs controller `export.rs` calls virtiofsd_argv.rs at spawn time; LaunchTicket carries resolved socket path as opaque sealed field", + "removalProof" : "`packages/d2b-host/src/virtiofsd_argv.rs` removed only after parity confirmed by argv-shape gate", + "reuseAction" : "adapt", + "reuseSource" : null, + "specId" : "ADR-046-provider-volume-virtiofs", + "specPath" : "docs/specs/providers/ADR-046-provider-volume-virtiofs.md", + "validation" : "`tests/argv_golden.rs`: 14 migrated tests + `no_extra_args_ever_emitted`, `socket_path_is_not_in_args`, `shared_dir_is_fd_path`, `path_length_within_sunpath_limit`; `tests/socket_path_privacy.rs`: `socket_path_not_in_export_status`, `socket_path_not_in_volume_status`, `socket_path_not_in_audit_record`; `tests/schema_conformance.rs`: `process_spec_readiness_class_is_provider_defined`, `process_spec_readiness_has_no_kind_or_period_fields`, `process_spec_budget_cpu_request_limit_nested`, `process_spec_budget_memory_request_limit_nested`, `process_spec_budget_pids_limit_present`, `process_spec_budget_fds_limit_present`, `process_spec_sandbox_no_new_privileges_true`, `process_spec_sandbox_read_only_root_true`, `process_spec_no_host_uid_gid_in_spec`", + "workItemId" : "ADR046-vvfs-001" + }, + { + "currentSource" : "`packages/d2b-priv-broker/src/sys.rs` (`clone3_spawn_runner`, user-NS pre-establishment block); `packages/d2b-priv-broker/src/ops/spawn_runner.rs` (`SpawnRunnerPlanInput.user_namespace`, `RunnerIsolationSpec.user_namespace`); ADR 0021 implementation contract", + "dataMigration" : "v3.0 reset; current `adr_carve_out` field in `SpawnRunnerPlanInput` removed; ADR 0021 path is now the default", + "dependencyOwner" : "ADR046-vvfs-001; ADR046-volume-001; W1; broker/spawn owner", + "destination" : "`packages/d2b-provider-volume-virtiofs/src/user_ns.rs` (conformance kit); `packages/d2b-provider-volume-virtiofs/tests/adr021_invariant.rs`", + "detailedDesign" : "`user_ns.rs` contains only the conformance check and template descriptor assertion: verify that the virtiofsd-worker Process template declares `capabilityClasses: []`, `startRoot: false`, `noNewPrivileges: true`, `readOnlyRoot: true`, and `sandbox.userNamespace.mappingClass: process-principal-root`. `hostUid`/`hostGid` are NOT set by the controller — system-minijail resolves the mapping from the `User/vol--vfd` principal when building the LaunchTicket via the effect port. The conformance check rejects any template mutation that adds host capability classes, sets `startRoot: true`, disables `noNewPrivileges`, or disables `readOnlyRoot`. The user-NS pre-establishment code itself remains in `d2b-priv-broker/src/sys.rs` and is invoked via the system-minijail effect port. Primary reuse disposition: `extract`. Preserved source-plan detail: extract conformance kit only; pre-establishment code stays in broker.", + "integration" : "volume-virtiofs controller calls conformance check before emitting any Process Create; effect port adapter in system-minijail invokes broker spawn path", + "removalProof" : "`adr_carve_out` field and virtiofsd-specific branch in current `SpawnRunnerPlanInput` removed only after v3 LaunchTicket covers all virtiofsd spawn cases", + "reuseAction" : "extract", + "reuseSource" : null, + "specId" : "ADR-046-provider-volume-virtiofs", + "specPath" : "docs/specs/providers/ADR-046-provider-volume-virtiofs.md", + "validation" : "`tests/adr021_invariant.rs`: `virtiofsd_capability_classes_must_be_empty`, `virtiofsd_start_root_must_be_false`, `virtiofsd_no_new_privileges_must_be_true`, `virtiofsd_read_only_root_must_be_true`, `process_spec_has_no_host_uid_gid`, `sandbox_namespace_never_emitted`, `user_ns_single_entry_single_uid_mapping`, `uid_map_write_ordering_uid_setgroups_gid`, `child_setuid_in_ns_not_host_uid`, `clone_newns_not_in_clone3_flags`, `child_exits_user_ns_sync_on_pipe_eof`", + "workItemId" : "ADR046-vvfs-002" + }, + { + "currentSource" : "`packages/d2bd/src/supervisor/dag.rs` (ProcessRole::Virtiofsd dag node); `nixos-modules/processes-json.nix` (virtiofsdRunner block; attachment-to-Process mapping)", + "dataMigration" : "Current `ProcessRole::Virtiofsd` dag nodes replaced by Export → Process resource lifecycle", + "dependencyOwner" : "ADR046-vvfs-001, ADR046-vvfs-002, ADR046-vvfs-007; ADR046-volume-001; W2; volume-virtiofs controller owner", + "destination" : "`packages/d2b-provider-volume-virtiofs/src/controller.rs`; `packages/d2b-provider-volume-virtiofs/src/export.rs`", + "detailedDesign" : "Implement volume-virtiofs-controller reconcile loop using toolkit ResourceClient. Watch selector: `virtiofs.d2bus.org.Export` resources (all in zone), owned Process resources, owned User resources, Volume resources (read-only for view/vcpu resolution), Guest resources (read-only for vcpu count). On `spec-generation-changed` for an Export: (1) resolve View from Volume; (2) check store-view marker if applicable; (3) resolve threadPoolSize from Guest vcpus; (4) ensure User/vol--vfd; (5) diff against current Process; (6) emit Create/UpdateSpec. On `owned-resource-changed` for a Process: update Export status. On `deletionRequestedAt` for Export: two-phase teardown (§6.2). Primary reuse disposition: `adapt`. Preserved source-plan detail: extract and adapt.", + "integration" : "volume-virtiofs controller registered by core ProviderDeployment; receives owned-resource-changed trigger from Export; emits Process resources consumed by system-minijail", + "removalProof" : "`ProcessRole::Virtiofsd` branch in `d2bd/src/supervisor/dag.rs` removed only after v3 controller passes all lifecycle tests", + "reuseAction" : "adapt", + "reuseSource" : null, + "specId" : "ADR-046-provider-volume-virtiofs", + "specPath" : "docs/specs/providers/ADR-046-provider-volume-virtiofs.md", + "validation" : "`tests/export_lifecycle.rs`: `export_create_spawns_virtiofsd_process`, `export_ready_when_socket_present`, `export_delete_terminates_virtiofsd`, `export_delete_waits_for_guest_mount_absent`, `export_delete_with_guest_unreachable_holds_finalizer_degraded`, `export_proof_of_ns_death_clears_finalizer`; `tests/multi_attachment.rs`: `two_guests_get_separate_exports_and_processes`, `process_failure_does_not_affect_sibling_export`; `tests/schema_conformance.rs`: `provider_state_set_volume_created_on_install`, `provider_state_set_volume_owner_ref_is_provider`, `provider_state_set_volume_layout_principal_is_user_not_component_principal`, `provider_state_set_no_cross_component_volume_sharing`", + "workItemId" : "ADR046-vvfs-003" + }, + { + "currentSource" : "`packages/d2bd/src/vm_readiness.rs` (`ReadinessKind::UnixSocketExists`); guest-control vsock health protocol", + "dataMigration" : "Current `UnixSocketExists` readiness kind adapted to FD-based path resolution", + "dependencyOwner" : "ADR046-vvfs-003; guest-control integration owner; W2", + "destination" : "`packages/d2b-provider-volume-virtiofs/src/readiness.rs`; `packages/d2b-provider-volume-virtiofs/integration/guest_mount_readiness/`", + "detailedDesign" : "`unix-socket-exists` readiness: check file existence at the private socket path via a bounded blocking adapter (e.g., `tokio::task::spawn_blocking` wrapping `fstatat(2)` relative to the zone runtime `OwnedFd`, or an async-safe fd-relative equivalent); no blocking syscall on the async executor thread. Probe period 1 s; timeout 30 s. On socket present → set `Export.status.exportReady: true`. Guest-mount readiness: send `VirtioFsMountReady?` probe to guest-control health endpoint over vsock. Response `MountReady` sets `guestMountReady: true`. Response `MountAbsent` or timeout sets `guestMountReady: false`. The vsock health probe is async-native. If Guest is down, set Export `phase: Unknown`. All readiness probes (unix-socket-exists, guest-mount health) use bounded blocking adapters or async-safe fd-relative equivalents; no blocking I/O on the reconcile executor thread. Primary reuse disposition: `adapt`. Preserved source-plan detail: extract and adapt.", + "integration" : "`readiness.rs` called from `controller.rs` reconcile loop; uses toolkit health probe client", + "removalProof" : "Current `UnixSocketExists` readiness path in `d2bd` retired after volume-virtiofs readiness covers all cases", + "reuseAction" : "adapt", + "reuseSource" : null, + "specId" : "ADR-046-provider-volume-virtiofs", + "specPath" : "docs/specs/providers/ADR-046-provider-volume-virtiofs.md", + "validation" : "`tests/export_lifecycle.rs` (extended); `integration/guest_mount_readiness/`: virtiofsd launches, socket appears, guest-control probe returns MountReady, guestMountReady flips to true; probe returns MountAbsent on umount", + "workItemId" : "ADR046-vvfs-004" + }, + { + "currentSource" : "`packages/d2b-host/src/hardlink_farm.rs` (`live_dir()`, marker `live/.d2b-marker-`, zero-length); `nixos-modules/processes-json.nix` (`roStoreSharedDir` sentinel `share.source == \"/nix/store\"` → `store-view/live`); `nixos-modules/store.nix` (per-VM hardlink farm)", + "dataMigration" : "Current `roStoreSharedDir` redirect in `processes-json.nix` replaced by `ro-store` View definition in the store-view Volume resource", + "dependencyOwner" : "ADR046-vvfs-003, ADR046-vvfs-004; ADR046-volume-002 (store-view Volume); W3", + "destination" : "`packages/d2b-provider-volume-virtiofs/src/controller.rs` (pre-launch prerequisite check); `packages/d2b-provider-volume-virtiofs/integration/store_view_readonly/`", + "detailedDesign" : "Before issuing Process Create for a store-view virtiofs Export, check that `live/.d2b-marker-` exists (zero-length, correct mode) via a bounded blocking adapter (e.g., `tokio::task::spawn_blocking` wrapping `fstatat(2)` relative to the zone runtime directory, or an async-safe fd-relative equivalent); no blocking syscall on the async executor thread directly. If absent, requeue with exponential backoff. Assert `--shared-dir` resolves to `store-view/live` (the `ro-store` View path), never to `/nix/store`. Validate in `integration/store_view_readonly/` that virtiofsd serves only paths under `store-view/live`.", + "integration" : "Pre-launch check in controller.rs; store-view Export recognized by `view == \"ro-store\"` and `access == \"read-only\"`", + "removalProof" : "`nixos-modules/processes-json.nix` `virtiofsdRunner` block and `roStoreSharedDir` sentinel removed only after store-view virtiofs Export resources pass parity gate", + "reuseAction" : "adapt", + "reuseSource" : null, + "specId" : "ADR-046-provider-volume-virtiofs", + "specPath" : "docs/specs/providers/ADR-046-provider-volume-virtiofs.md", + "validation" : "`integration/store_view_readonly/`: mount from guest reads closure paths; no host-store path escapes; `tests/argv_golden.rs`: `store_view_shared_dir_is_live_not_nix_store`; `tests/export_lifecycle.rs`: `store_view_launch_waits_for_marker`", + "workItemId" : "ADR046-vvfs-005" + }, + { + "currentSource" : "`nixos-modules/processes-json.nix` (virtiofsdRunner block); `nixos-modules/minijail-profiles.nix` (virtiofsdProfiles); `nixos-modules/options-vms.nix` (`d2b.vms..shares.*`)", + "dataMigration" : "`d2b.vms..shares` virtiofs entries → Volume attachments; `d2b.vms.` store-view auto-emission replaces `nixos-modules/store.nix` virtiofsd portion", + "dependencyOwner" : "ADR046-vvfs-001, ADR046-volume-004; Nix integrator; W3", + "destination" : "`nixos-modules/resources-volume.nix` (store-view and user Volume attachment emission); `nixos-modules/options-volumes.nix` (optional user-facing volume/attachment options)", + "detailedDesign" : "Extend the Nix resource compiler to: (1) auto-emit a store-view Volume (with `ro-store` and `meta` Views, virtiofs ro-store attachment) per Guest that has a VM runtime Provider; (2) emit virtiofs attachment entries for explicitly configured user Volumes; (3) emit `User/vol--vfd` resources for each Volume with virtiofs attachments; (4) emit `Provider/volume-virtiofs` as a Provider resource when any virtiofs attachment is configured. volume-local creates Export resources at runtime (not in Nix bundle); no `virtiofs.d2bus.org.Export` resources appear in the Nix-emitted bundle. All eval validation steps (§16.5) apply.", + "integration" : "`nixos-modules/default.nix` wires resources-volume.nix; nix-unit tests verify canonical output", + "removalProof" : "`nixos-modules/processes-json.nix` virtiofsdRunner block, `nixos-modules/minijail-profiles.nix` virtiofsdProfiles removed only after Nix resource compiler produces parity output and all nix-unit cases pass", + "reuseAction" : "adapt", + "reuseSource" : null, + "specId" : "ADR-046-provider-volume-virtiofs", + "specPath" : "docs/specs/providers/ADR-046-provider-volume-virtiofs.md", + "validation" : "nix-unit: `store_view_volume_auto_emitted_per_guest`, `volume_virtiofs_attachment_canonical_json`, `virtiofs_provider_emitted_when_attachment_configured`, `vfd_user_emitted_per_volume`, `second_read_write_attachment_rejected_at_eval`, `transport_virtiofs_requires_provider_installed`; drift-check gate for `nixos-modules/processes-json.nix` virtiofsdRunner removal", + "workItemId" : "ADR046-vvfs-006" + }, + { + "currentSource" : "No analog; new ResourceType", + "dataMigration" : "None; new type", + "dependencyOwner" : "ADR046-volume-001; W1; volume-virtiofs Provider owner", + "destination" : "`packages/d2b-provider-volume-virtiofs/src/export.rs`; `packages/d2b-contracts/src/v3/virtiofs_export.rs`", + "detailedDesign" : "Declare `virtiofs.d2bus.org.Export` ResourceType in `d2b-contracts`. Base fields: `providerRef`, `volumeRef`, `executionRef`, `view`, `access`, `mountPath`; virtiofs tunables live under `spec.provider.settings` (as in §4.2). Status fields: top-level `phase`/`conditions`, `status.resource.exportReady`, `status.resource.guestMountReady`, and `status.provider.details.workerProcessRef`. Strict serde `deny_unknown_fields`. Implement the conformance test fixture that validates schema fingerprint stability. The Export spec JSON schema and provider status extension schema are signed and included in the Provider package.", + "integration" : "`d2b-contracts` exports the Export DTO; volume-virtiofs controller and volume-local both import it for ResourceClient typed operations", + "removalProof" : "N/A (new type)", + "reuseAction" : "create", + "reuseSource" : null, + "specId" : "ADR-046-provider-volume-virtiofs", + "specPath" : "docs/specs/providers/ADR-046-provider-volume-virtiofs.md", + "validation" : "`tests/schema_conformance.rs`: `export_schema_canonical_json_stable`, `export_spec_denied_unknown_fields`, `export_status_exportready_is_boolean_not_path`, `export_owner_must_be_volume`", + "workItemId" : "ADR046-vvfs-007" + }, + { + "currentSource" : "`packages/d2b-realm-core/src/ids.rs` (`RealmId`, `LABEL_PATTERN = \"^[a-z][a-z0-9-]*$\"`, `is_label()`, `MAX_ID_LEN = 128`, `IdError` — `implemented-and-reachable`, baseline `b5ddbed6`); `packages/d2b-realm-core/src/realm.rs` (`RealmPath`, `MAX_REALM_LABELS = 16`, `RealmControllerPlacement`, `EntrypointMode` — `implemented-and-reachable`, baseline `b5ddbed6`); Zone resource schema: `ADR-only`", + "dataMigration" : "Destructive d2b 3.0 reset; no v2/Realm Zone resource import", + "dependencyOwner" : "ADR046-object-001 (resource envelope); ADR046-store-001 (redb store); ADR046-identities-001 (types)", + "destination" : "`packages/d2b-contracts/src/v3/zone.rs`; `packages/d2b-core-controller/src/zone.rs`", + "detailedDesign" : "Zone ResourceType schema with `spec = {}`; self-resource enforcement; store_meta binding checks; phase/conditions; cardinality-1 enforcement; `zone-drain` finalizer; canonical JSON schema. Nix Zone options include compiler-only `parentZone`: required for every non-root Zone, forbidden on `local-root`, and never emitted into `Zone.spec` Primary reuse disposition: `adapt`. Preserved source-plan detail: extract and adapt (`RealmId`→`ResourceName` validators, `RealmPath` depth bound→ZoneLink depth, `RealmControllerPlacement`→Provider component placement); new (Zone resource schema/store table/handler).", + "integration" : "Zone runtime open/upgrade verifies `Zone/` self resource; configuration publication handler creates/reconciles Zone; resource API rejects cross-Zone refs", + "removalProof" : "`d2b-realm-core` Realm struct removed only after Zone resource integration is live in Zone runtime", + "reuseAction" : "adapt", + "reuseSource" : null, + "specId" : "ADR-046-resources-zone-control", + "specPath" : "docs/specs/ADR-046-resources-zone-control.md", + "validation" : "`zone-self-resource-enforced`, `zone-uid-mismatch-quarantine`, `zone-name-mismatch-rejected`, `zone-cardinality-one`, `zone-cross-zone-ref-rejected`, `zone-owner-rejected`, `zone-deletion-only-on-drain`", + "workItemId" : "ADR046-zone-control-001" + }, + { + "currentSource" : "`packages/d2b-realm-router/src/session.rs` (`PeerSession` — `implemented-but-unwired`, baseline `b5ddbed6`; no live call sites in d2bd or d2b — only reachable through `realm_stubs.rs` dead code in d2bd); `packages/d2b-realm-router/src/secure_session.rs` (`SecurePeerSession`, Noise-based — `implemented-but-unwired`); `packages/d2b-realm-router/src/mux_session.rs` (`MuxSession` — `implemented-but-unwired`); `packages/d2b-realm-router/src/session_lifecycle.rs` (`SessionLifecycle`, `SessionPhase` — `implemented-but-unwired`); `packages/d2b-realm-core/src/route_engine.rs` (`RouteTreeEngine`, `admit_advertisement()`, `decide_route()`, `RoutePruneReport` — `implemented-but-unwired`; exported from `d2b_realm_core::lib.rs:122` but zero call sites in live daemon or CLI code, tests only); `packages/d2b-realm-core/src/identity_store.rs` (`RealmIdentityStore`, `EnrollmentRecord`, `ChildKeyPin` — `implemented-and-reachable`); `packages/d2b-realm-core/src/realm.rs` (`RealmPath`, `MAX_REALM_LABELS = 16` — `implemented-and-reachable`); `packages/d2b-realm-core/src/access.rs` (`RealmTransportBinding::{LocalUnixSocket,RemoteRealmTransport,ProviderRealmTransport}` — `implemented-and-reachable` in CLI routing path); ZoneLink resource schema and cursor tables: `ADR-only`", + "dataMigration" : "Destructive reset; no v2 Realm peer migration", + "dependencyOwner" : "ADR046-zone-control-001; ADR046-zone-control-018 (ComponentSession)", + "destination" : "`packages/d2b-contracts/src/v3/zone_link.rs`; `packages/d2b-core-controller/src/zone_link.rs`", + "detailedDesign" : "Child-local ZoneLink schema with self-matching `spec.childZoneName` plus same-Zone `spec.transportProviderRef`, `spec.transportSettings`, and `spec.transportCredentials`; at most one uplink resource per non-root Zone and none in local root; child-store cursor/intent state; reconnect loop with exponential backoff; local intent queue (max 256 entries); local child UID change detection; drain finalizer; no reciprocal parent-store resource; compiler-only `parentZone` selects the one allocator owner, while that allocator owns privileged listener/placement/route allocation effects and exposes only a sealed bootstrap/allocation interface Primary reuse disposition: `adapt`. Preserved source-plan detail: extract and adapt (`SecurePeerSession` Noise model → ComponentSession Noise KK; `SessionLifecycle`/`SessionPhase` → ZoneLink session reconnect loop and connection detail fields; `Connecting`/`Established` current evidence phases drive `status.connected` and `status.phase` transitions to `Pending`/`Ready`, not direct phase values; `RouteTreeEngine.decide_route()` → cursor tracking; `RealmIdentityStore` enrollment → ZoneLink child key-pin).", + "integration" : "child core-controller zone_link handler; child redb `zone_link_cursors` table; Nix-compiled `parentZone` bootstrap topology; selected parent allocator and route engine through sealed allocation authority; d2b-bus transport resolver; ComponentSession lifecycle", + "removalProof" : "`realm_stubs.rs` (`ApiFrontend`, `PeerOperationRouter`, `TargetResolver`) removed after ComponentSession integration (ADR046-zone-control-018); `realm_access_resolver.rs` module removed after ZoneLink replaces entrypoint-table resolution; gateway `PeerSession`/`SecurePeerSession` session types remain as dead code in d2b-realm-router until Provider session migration wave", + "reuseAction" : "adapt", + "reuseSource" : "main `a1cc0b2d`: `packages/d2b-session/src/lifecycle.rs`, `d2b-session-unix/src/adapter.rs` for reconnect/transport precedents", + "specId" : "ADR-046-resources-zone-control", + "specPath" : "docs/specs/ADR-046-resources-zone-control.md", + "validation" : "`zonelink-reconnect-child-uid-change`, `zonelink-disconnect-unknown-phase`, `zonelink-intent-queue-limit`, `zonelink-disabled-no-reconnect`, `zonelink-child-auth-denied-failed`, `zonelink-drain-closes-session`, `zonelink-child-name-matches-store`, `zonelink-one-child-local-uplink`, `zonelink-parent-bootstrap-binding`, `zonelink-parent-has-no-reciprocal-row`", + "workItemId" : "ADR046-zone-control-002" + }, + { + "currentSource" : "`packages/d2b-realm-provider/src/provider.rs` (`HostSubstrateProvider`, `RuntimeProvider`, `WorkloadProvider`, `DurableExecutionProvider`, `InfrastructureProvider`, `NodeProvider` traits — `implemented-and-reachable`, baseline `b5ddbed6`); `packages/d2b-realm-provider/src/capabilities.rs` (`RuntimeCapabilitySet`, `WorkloadCapabilitySet`, `NodeCapabilitySet` — `implemented-and-reachable`); `packages/d2b-realm-provider/src/conformance.rs` (`workload_lists_and_advertises`, `display_fails_closed_when_unsupported` — `implemented-and-reachable`); `packages/d2b-realm-provider/src/error.rs` (`ProviderError`, `ErrorKind`, `RetryHint`, `ProviderDiagnostic` — `implemented-and-reachable`); `packages/d2b-realm-core/src/ids.rs` (`ProviderId`, label-shaped — `implemented-and-reachable`); `packages/d2b-core/src/processes.rs` (`ProcessRole::{CloudHypervisorRunner, Virtiofsd, Swtpm, GpuRenderNode, Audio, Video, QemuMediaRunner, VsockRelay, OtelHostBridge, Usbip}`, `VmProcessDag`, `ProcessNode` — `implemented-and-reachable`); Provider resource schema and API catalog: `ADR-only`", + "dataMigration" : "Full reset; Provider packages recompiled and re-registered per new schema", + "dependencyOwner" : "ADR046-zone-control-001; ADR046-api-001; ADR046-zone-control-017", + "destination" : "`packages/d2b-contracts/src/v3/provider.rs`; `packages/d2b-core-controller/src/provider_lifecycle.rs`; `packages/d2b-core-controller/src/api_catalog.rs`", + "detailedDesign" : "Provider resource schema with all spec fields from §4.3, including resolved component bounds (max 8 controllers, 8 services, 32 worker templates, 16 ResourceTypes per controller); trust/conformance/config validation; component descriptor validation; dependency alias resolution; API binding with permission intersection; lifecycle policies; Nix Provider installation options; Provider crate layout enforcement per §4.8 (see ADR046-zone-control-026) Primary reuse disposition: `adapt`. Preserved source-plan detail: extract and adapt (`workload_lists_and_advertises`/`display_fails_closed_when_unsupported` conformance behavior; `RuntimeCapabilitySet`/`WorkloadCapabilitySet`/`NodeCapabilitySet` → Provider component `supportedCapabilities`; `ProviderError`/`RetryHint` → Provider lifecycle error schema; `ProviderId` → Provider `metadata.name` validator; `ProcessRole` variants → Provider component type identifiers).", + "integration" : "Zone config publication installs Provider resources; API catalog handler binds exported ResourceTypes; Provider/system-core and Provider/system-minijail are bootstrap exceptions with pre-created records", + "removalProof" : "`d2b-realm-provider` trait crate removed per ADR046-zone-control-017 after Provider resource integration", + "reuseAction" : "adapt", + "reuseSource" : "main `a1cc0b2d`: any `d2b-provider-toolkit` registry/descriptor patterns named in ADR046-zone-control-017 sub-items", + "specId" : "ADR-046-resources-zone-control", + "specPath" : "docs/specs/ADR-046-resources-zone-control.md", + "validation" : "All §15.3 Provider tests including the resolved bounds checks", + "workItemId" : "ADR046-zone-control-003" + }, + { + "currentSource" : "`packages/d2bd/src/admission.rs` (`PeerRole::{Admin, Launcher, HostShutdown}`, `PeerIdentity`, `authorize_peer()`, `verb_requires_admin()`, `verb_allowed_for_host_shutdown()` — `implemented-and-reachable`, baseline `b5ddbed6`); `packages/d2b-daemon-access/src/lib.rs` (`LocalUnixAllowlistRole::{Admin, Launcher}`, `DaemonAccessPolicyRole::RealmAdmin`, `DaemonAccessDecision`, `MappedDaemonAccessPrincipal`, `map_local_unix_daemon_access()` — `implemented-and-reachable`); Role resource schema and RBAC index: `ADR-only`", + "dataMigration" : "Initial Roles generated from Nix config; no v2 Role resource import", + "dependencyOwner" : "ADR046-zone-control-001; ADR046-api-002", + "destination" : "`packages/d2b-contracts/src/v3/role.rs`; `packages/d2b-core-controller/src/authz.rs`", + "detailedDesign" : "Role resource schema with rule schema from §5.3 and resolved bounds (32 rules, 16 resourceTypes per rule, 64 resourceNames, 32 executionRefs); verb enum; explicit wildcard enforcement; index builder; phase/conditions; `role-binding-drain` finalizer; Nix Role options; audit/OTEL instrumentation Primary reuse disposition: `adapt`. Preserved source-plan detail: extract and adapt (`verb_requires_admin()` verb table → Role verb enum; `DaemonAccessDecision` error types → Role admission errors; `MappedDaemonAccessPrincipal` → subject identity model).", + "integration" : "Authorization evaluator (ADR046-api-002) reads Role index entries; core `authz` handler owns reconcile loop", + "removalProof" : "`daemon-access` capability enum removed after RBAC Role engine covers all access decisions", + "reuseAction" : "adapt", + "reuseSource" : null, + "specId" : "ADR-046-resources-zone-control", + "specPath" : "docs/specs/ADR-046-resources-zone-control.md", + "validation" : "All §15.4 Role tests including the resolved bounds checks", + "workItemId" : "ADR046-zone-control-004" + }, + { + "currentSource" : "`packages/d2b-daemon-access/src/lib.rs` (`DaemonAccessDecision::Authorized { role: DaemonAccessPolicyRole }`, `DaemonAccessAdmissionSource`, `map_remote_daemon_access()` — `implemented-and-reachable`, baseline `b5ddbed6`; these implement the current coarse binding decision per-connection); RoleBinding resource schema: `ADR-only`", + "dataMigration" : "Initial RoleBindings generated from Nix config", + "dependencyOwner" : "ADR046-zone-control-004", + "destination" : "`packages/d2b-contracts/src/v3/role_binding.rs`; `packages/d2b-core-controller/src/authz.rs` (shared with Role handler)", + "detailedDesign" : "RoleBinding resource schema with no expiry field and max 128 subjects; subject resolution and UID binding; external principal selector; scope narrowing intersection; revocation; immediate deletion with cache invalidation; Nix RoleBinding options Primary reuse disposition: `adapt`. Preserved source-plan detail: new (RoleBinding resource schema/store table/handler); adapt (`DaemonAccessAdmissionSource` identity fields → subject selector; `map_remote_daemon_access()` logic → subject UID-binding behavior).", + "integration" : "Subject resolution uses store owner index; authorization evaluator reads combined Role+narrowing entry; revocation/update/delete flow uses the normal resource lifecycle", + "removalProof" : "Not applicable; new type", + "reuseAction" : "adapt", + "reuseSource" : null, + "specId" : "ADR-046-resources-zone-control", + "specPath" : "docs/specs/ADR-046-resources-zone-control.md", + "validation" : "All §15.5 RoleBinding tests, including the 128-subject admission bound and no-expiry lifecycle model", + "workItemId" : "ADR046-zone-control-005" + }, + { + "currentSource" : "`packages/d2bd/src/admission.rs` (`authorize_peer()`, `verb_requires_admin()`, `verb_allowed_for_host_shutdown()`, `PeerRole::{Admin, Launcher, HostShutdown}` — `implemented-and-reachable`, baseline `b5ddbed6`); `packages/d2b-daemon-access/src/lib.rs` (`LocalUnixAllowlistRole`, `DaemonAccessDecision::Authorized/Denied`, `DaemonAccessAdmissionSource`, `MappedDaemonAccessPrincipal`, `map_local_unix_daemon_access()` — `implemented-and-reachable`); `packages/d2bd/src/lib.rs` (`LoadedRealmControllersConfig`, `LoadedRealmIdentityConfig` startup state — `implemented-and-reachable`); compiled bootstrap constant policy: `ADR-only`", + "dataMigration" : "Bootstrap is always freshly compiled; no migration", + "dependencyOwner" : "ADR046-zone-control-004, ADR046-zone-control-005; Zone runtime", + "destination" : "`packages/d2b-resource-api/src/bootstrap_authz.rs`; Zone runtime startup path", + "detailedDesign" : "Compiled bootstrap authorization as described in §9; exact subjects (system-core, system-minijail); closed verb table; non-configurable enforcement; atomic supersession after stored RBAC publishes; out-of-band reset path Primary reuse disposition: `adapt`. Preserved source-plan detail: extract and adapt (`authorize_peer()`/`verb_requires_admin()` verb table → bootstrap constant policy verb table; `PeerRole` two-variant model → system-core/system-minijail bootstrap subjects; `map_local_unix_daemon_access()` SO_PEERCRED mapping → bootstrap subject derivation; `LoadedRealmControllersConfig` startup path → Zone runtime startup bootstrap init sequence).", + "integration" : "Resource API authorization layer checks bootstrap policy before stored RBAC; supersession is triggered by first `IndexBuilt=True` event from authorization handler", + "removalProof" : "`daemon-access` bootstrap stub removed after Zone runtime bootstrap authz integrates", + "reuseAction" : "adapt", + "reuseSource" : null, + "specId" : "ADR-046-resources-zone-control", + "specPath" : "docs/specs/ADR-046-resources-zone-control.md", + "validation" : "All §15.6 bootstrap tests", + "workItemId" : "ADR046-zone-control-006" + }, + { + "currentSource" : "`nixos-modules/options-realms.nix` (`providerKind = \"^[a-z][a-z0-9-]*$\"` label regex matching `LABEL_PATTERN` in `ids.rs`; `providerType` submodule with `enable`/`kind`/`placement`/`freeformType` fields; `d2b.realms..providers.*` attrset — `generated-or-eval-contract`, baseline `b5ddbed6`); `nixos-modules/options-realms-workloads.nix` (`d2b.realms..workloads.*` submodule shape — `generated-or-eval-contract`); `d2b.zones.*` Nix options: `ADR-only`", + "dataMigration" : "Full reset; Nix realm options (`d2b.realms.*`) remain until purge wave", + "dependencyOwner" : "ADR046-identities-002; ADR046-zone-control-001; ADR046-zone-control-002; ADR046-zone-control-003; ADR046-zone-control-004; ADR046-zone-control-005; ADR046-zone-control-009; ADR046-zone-control-010", + "destination" : "`nixos-modules/options-zones.nix`; `nixos-modules/resources-zone-control.nix`; extended `nixos-modules/index.nix`", + "detailedDesign" : "`d2b.zones..*` Nix options for Zone/ZoneLink/Provider/Role/RoleBinding/Quota/EmergencyPolicy authoring; compiler-only scalar `parentZone` required on non-root Zones and forbidden on `local-root`; Nix eval-time validation of parent existence, one resolved parent, self/cycle rejection, 16-name ancestry depth, ResourceRefs, verb enums, digest format, subject types, child-local ZoneLink self-name/one-uplink/local transport-ref constraints, and the no-expiry RoleBinding model; canonical JSON serialization; generation-bound resource bundle output; the compiler seals the validated parent map into allocator bootstrap topology without emitting `parentZone` into `Zone.spec` or reciprocal resources Primary reuse disposition: `adapt`. Preserved source-plan detail: adapt (`providerType` submodule → Provider option submodule; label regex `^[a-z][a-z0-9-]*$` retained unchanged; `placement` option → Provider component placement; `d2b.realms.*` option namespace → `d2b.zones.*` option namespace).", + "integration" : "Nix compiler → sealed allocator topology for `parentZone`; resource compiler → configuration publication handler → Zone store; bootstrap Provider records auto-generated", + "removalProof" : "`nixos-modules/options-realms.nix` and related realm options removed only after Zone resource Nix integration is live", + "reuseAction" : "adapt", + "reuseSource" : null, + "specId" : "ADR-046-resources-zone-control", + "specPath" : "docs/specs/ADR-046-resources-zone-control.md", + "validation" : "nix-unit vectors for each Zone control type schema; cross-field constraint tests; rendered JSON contract tests (`make test-drift`)", + "workItemId" : "ADR046-zone-control-007" + }, + { + "currentSource" : "`d2b-realm-core/src/workload.rs:13,27,83` (`WorkloadProviderKind::UnsafeLocal`, `IsolationPosture::UnsafeLocal`, `WorkloadExecutionPosture` with canonical unsafe-local tuple at lines 206–211: `isolation=unsafe-local`, `environment=systemd-user-manager-ambient`, `executionIdentity=authenticated-requester-uid`, `sessionPersistence=user-manager-lifetime` — `implemented-and-reachable`, baseline `b5ddbed6`); `d2b-core/src/unsafe_local_workloads.rs:16,47,106,150` (`UnsafeLocalWorkloadsJson`, `UnsafeLocalWorkload`, `UnsafeLocalShellPolicy`, `MAX_UNSAFE_LOCAL_SHELL_SESSIONS=64`, `MAX_UNSAFE_LOCAL_WORKLOADS=256` — `implemented-and-reachable`); `d2bd/src/unsafe_local_helper.rs` (`HelperRegistry`, `bind_helper_socket`, `dispatch_launch` — `implemented-and-reachable`, live in `d2bd/src/lib.rs:1346–1468`); `nixos-modules/options-realms.nix:346` (`policy.allowUnsafeLocal` — `generated-or-eval-contract`); `nixos-modules/options-realms-workloads.nix:221,233` (`kind = \"unsafe-local\"`, doc \"no isolation boundary\" — `generated-or-eval-contract`)", + "dataMigration" : "Destructive reset; `unsafe-local-workloads.json` bundle artifact and `HelperRegistry` replaced by Host resource store entries + Process Provider launch", + "dependencyOwner" : "ADR046-zone-control-001 (Zone resource); ADR046-zone-control-003 (Provider/system-core installed)", + "destination" : "`packages/d2b-contracts/src/v3/host.rs` (Host resource schema, user-domain variant); `packages/d2b-core-controller/src/host_user.rs` (reconciler owned by Provider/system-core); Nix Host authoring via `d2b.zones..resources. = { type = \"Host\"; spec = { ... }; };` (validated per ResourceTypeSchema; no separate `options-zones-hosts.nix` submodule)", + "detailedDesign" : "Host ResourceType schema for user-domain variant: `spec.defaultDomain=user`, `spec.allowedDomains=[user]`, `spec.defaultUserRef=User/`, `spec.shellPolicy` (adapted from `UnsafeLocalShellPolicy`), `spec.launcherItems` (adapted from `UnsafeLocalLauncherItem`). No-isolation posture recorded in `status.observedPosture`. A dedicated Host admission gate blocks unsafe-local Host creation without opt-in. Mandatory no-isolation warning in Host `status.conditions[0].message` and CLI/UI output for all Host commands. No-isolation posture included in audit record body (`isolation=no-isolation`); it is never emitted as an OTEL metric label value or span attribute. Child processes use standard Process Providers — no Provider resource with name or kind `unsafe-local` is created. Cardinality bounds: max 256 user-domain Hosts per Zone, max 64 launcher items per Host, max 64 shell sessions per Host. Primary reuse disposition: `adapt`. Preserved source-plan detail: adapt (`WorkloadExecutionPosture` unsafe-local posture tuple → Host `status.observedPosture`; posture details in audit record body, not OTEL labels; `UnsafeLocalShellPolicy.{defaultName,maxSessions}` → Host `spec.shellPolicy`; `HelperRegistry.dispatch_launch` → Zone runtime Host process launch broker; `policy.allowUnsafeLocal` → dedicated Host admission gate); new (Host ResourceType user-domain schema with `defaultDomain`, `allowedDomains`, `defaultUserRef`, `shellPolicy`, cardinality bounds).", + "integration" : "Provider/system-core bootstrap exception creates Host resources from Zone config publication; Host controller reconciles `defaultUserRef` via User resource lookup; `HelperRegistry` in Zone runtime becomes per-uid launch broker for Host Process launch; Host admission layer enforces the `allowUnsafeLocal` gate before Host creation", + "removalProof" : "`d2bd/src/unsafe_local_helper.rs` `HelperRegistry` removed after Zone runtime Host/Process broker integration; `unsafe-local-workloads.json` bundle artifact removed after Host resource store replaces it; `kind = \"unsafe-local\"` Nix enum value removed in purge wave", + "reuseAction" : "adapt", + "reuseSource" : null, + "specId" : "ADR-046-resources-zone-control", + "specPath" : "docs/specs/ADR-046-resources-zone-control.md", + "validation" : "`host-user-domain-no-isolation-warning-required`, `host-user-only-disallows-system-domain`, `host-allowUnsafeLocal-gates-creation`, `host-defaultUserRef-user-type-required`, `host-shell-policy-max-sessions-bound`, `host-launcher-item-max-count-bound`, `host-audit-body-isolation-label-present`, `host-otel-no-posture-label`, `host-cli-no-isolation-warning-present`", + "workItemId" : "ADR046-zone-control-008" + }, + { + "currentSource" : "`packages/d2b-realm-core/src/ids.rs` (`LABEL_PATTERN`, `MAX_ID_LEN` — ResourceName validation for quotaRef); `packages/d2b-core/src/unsafe_local_workloads.rs:16–164` (`MAX_UNSAFE_LOCAL_WORKLOADS=256`, etc. — bound evidence for quota ceiling defaults); no current quota ResourceType exists (`ADR-only`)", + "dataMigration" : "Full reset; no prior Quota resources exist", + "dependencyOwner" : "ADR046-zone-control-001; Zone store (ADR046-store-001); Quota handler owner", + "destination" : "`packages/d2b-contracts/src/v3/quota.rs`; `packages/d2b-core-controller/src/quota.rs`; `packages/d2b-resource-api/src/quota_gate.rs`", + "detailedDesign" : "Quota resource schema with all spec/status fields from §7; ceiling bounds enforcement at admission (hard policy: reject over-quota with `quota-exceeded`; soft policy: warn); usage index built from resource scan with `quotaRef` field; per-ResourceType ceiling in `perTypeCeilings`; `core.quota-drain` finalizer that blocks Quota deletion until all dependent resources with `spec.quotaRef` pointing to this Quota are reassigned or deleted by authorized owners/operators — the controller never issues spec-updates to clear `quotaRef` on other resources; `dependentCount` status field updated from dependency index; audit event `quota-check` per admission; Nix Quota options per §7.7", + "integration" : "Resource API admission gate (`packages/d2b-resource-api/src/quota_gate.rs`) called for every `create` verb; Zone controller triggers quota reconcile on resource-created/deleted/quotaRef-changed events; quota handler registered in core-controller process", + "removalProof" : "Additive; no existing code removed", + "reuseAction" : "create", + "reuseSource" : null, + "specId" : "ADR-046-resources-zone-control", + "specPath" : "docs/specs/ADR-046-resources-zone-control.md", + "validation" : "`quota-ceiling-hard-reject`, `quota-ceiling-soft-warn`, `quota-ceiling-pertype`, `quota-drain-blocks-on-dependents`, `quota-over-quota-status`, `quota-nix-eval-bounds`, `quota-nix-build-pertype-unknown-type`", + "workItemId" : "ADR046-zone-control-009" + }, + { + "currentSource" : "`packages/d2bd/src/lib.rs` admission gate (`authorize_peer()` — `implemented-and-reachable`, baseline `b5ddbed6`); `packages/d2b-daemon-access/src/lib.rs` (`DaemonAccessDecision::Denied` — basis for admission rejection pattern); no EmergencyPolicy ResourceType exists (`ADR-only`)", + "dataMigration" : "Full reset; no prior EmergencyPolicy resources exist", + "dependencyOwner" : "ADR046-zone-control-001; Zone store (ADR046-store-001); EmergencyPolicy handler owner", + "destination" : "`packages/d2b-contracts/src/v3/emergency_policy.rs`; `packages/d2b-core-controller/src/emergency_policy.rs`; `packages/d2b-resource-api/src/emergency_gate.rs`", + "detailedDesign" : "EmergencyPolicy resource schema from §8; union semantics: multiple `enabled=true` policies allowed simultaneously; effective scope = OR of all enabled policies' scope flags; effective `drainDeadlineSeconds` = minimum across enabled policies; scope flag evaluation: `stopNewAdmissions` signal to API admission gate, `disconnectZoneLinks` graceful signal to ZoneLink handler, `stopProviderProcesses` suppresses Process launch and sends stop signal to running Provider component Processes (does NOT set `deletionRequestedAt` on Process resources; reconciliation resumes on deactivation), `drainOngoingOperations` deadline drain; `core.emergency-drain` finalizer for enabled policies; audit events `emergency-policy-activated` / `emergency-policy-deactivated`; `reason` field stored in spec and included in audit record body (never in OTEL metric label values, structured log labels, or status fields); Nix EmergencyPolicy options per §8.7", + "integration" : "API admission gate checks union of enabled EmergencyPolicies before every admitted request; ZoneLink handler subscribes to EmergencyPolicy watch triggers; Provider process lifecycle listens for effective `stopProviderProcesses` and resumes launch on deactivation", + "removalProof" : "Replaces the inline `emergencyDisable` field from the proposed Zone.spec option B; that option was not implemented", + "reuseAction" : "create", + "reuseSource" : null, + "specId" : "ADR-046-resources-zone-control", + "specPath" : "docs/specs/ADR-046-resources-zone-control.md", + "validation" : "`emergency-policy-activates-gate`, `emergency-policy-disconnects-zonelinks`, `emergency-policy-union-most-restrictive`, `emergency-policy-multi-enabled-combined-scope`, `emergency-policy-stop-processes-no-delete`, `emergency-policy-deactivation-restores-gate`, `emergency-policy-drain-finalizer`, `emergency-nix-eval-drain-deadline-bound-tightest`", + "workItemId" : "ADR046-zone-control-010" + }, + { + "currentSource" : "main `a1cc0b2d`: `packages/d2b-session/src/lifecycle.rs` (`SessionPhase::{Established,Disconnected,Reconnecting,Closing,Closed}`, `KeepaliveAction::{None,SendPing,Close}`, `SessionLifecycle::new/phase/on_activity/poll_keepalive/receive_pong/disconnect/begin_reconnect/reconnect_established/close` — lines 9–195); `packages/d2b-session/src/engine.rs` (`SessionEngine`, `SessionEvent`, `establish_initiator/responder`, `reconnect_initiator/responder`, `call/complete_call/cancel_call`, `send_ttrpc/receive`, `open/send/grant_credit/close/reset_named_stream`, `send_attachments`, `drive_keepalive`, `fail_closed`, encode/decode helpers for keepalive/cancel/close/stream-control/attachment — lines 33–995); `packages/d2b-session/src/driver.rs` (`ComponentSessionDriver` trait 18 methods, `SessionDriverHandle`, `DriverQueues` 4 event queues + named-send FIFO, `DRIVER_COMMAND_CAPACITY=128`, `DRIVER_EVENT_CAPACITY=128`, backpressure on enqueue and dequeue, test `cancelled_immediate_receiver_restores_queued_event` — lines 20–718); `packages/d2b-session/src/streams.rs` (`StreamId`, `StreamPhase::{Open,HalfClosedLocal,HalfClosedRemote,Closed,Reset}`, `StreamEvent::{Data,RemoteClosed,Reset}`, `NamedStreamMux::open/reserve_send/grant_send_credit/refund_send_credit/receive_data/close_local/receive_close/reset/remove_terminal/active` — lines 7–237); `packages/d2b-session/src/transport.rs` (`OwnedTransport` trait, `TransportDescriptor`, `TransportPacket`, `TransportError::{Disconnected,Truncated,LimitExceeded,InvalidAttachment,Other}` — lines 9–106); `packages/d2b-session/src/error.rs` (`SessionError`, all `From<>` mappings covering `ContractError`, `BinaryError`, `SequenceError`, `FragmentSequenceError`, `HandshakeRejectReason`, `TransportError` — lines 10–143); supporting modules: `attachment.rs`, `bootstrap.rs`, `cancellation.rs`, `deadline.rs`, `fragmentation.rs`, `metrics.rs`, `record.rs`, `scheduler.rs`, `server.rs`", + "dataMigration" : "Not applicable; new crate", + "dependencyOwner" : "ADR046-identities-001; ADR046-store-001", + "destination" : "`packages/d2b-bus/src/{lifecycle,engine,driver,streams,transport,error}.rs` (new crate `d2b-bus`)", + "detailedDesign" : "**Selected**: full `SessionLifecycle` FSM including keepalive ping-nonce/timeout close (`poll_keepalive` lines 81–124), nonce-exhaustion close (`NonceExhausted` remediation `ReplaceGeneration`), reconnect attempt counting + window expiry (`begin_reconnect` lines 147–174), generation increment on reconnect; `ComponentSessionDriver` 18-method trait as the d2b-bus driver contract; `SessionEngine` full frame encode/decode, keepalive, named-stream credit/half-close/reset; `NamedStreamMux` credit model with send/receive credit, phase transitions, terminal removal; `DriverQueues` backpressure (`QueueBackpressure`) on both deliver and receive paths; all `SessionError`/`From<>` mapping chains. **Excluded ADR45 assumptions**: `establish_initiator_with_generation_discovery()` (lines 102–123): ADR45 initiator-probes-server for current generation; in v3 the generation lives in the Zone resource store — use `establish_initiator()` directly. OTEL labels in `metrics.rs` reference ADR45 realm/workload dimensions — replace with Zone/ZoneLink labels per §13.2. Primary reuse disposition: `adapt`. Preserved source-plan detail: copy + adapt.", + "integration" : "`d2b-bus` crate is the sole session-transport dependency for ZoneLink (ADR046-zone-control-018), Provider component Processes (ADR046-zone-control-017), and the Zone resource API service layer (ADR046-api-001); `ComponentSessionDriver` is the interface ZoneLink uses to send/receive ttrpc frames and named streams", + "removalProof" : "`d2b-realm-router/src/{session.rs,secure_session.rs,mux_session.rs,session_lifecycle.rs}` removed after d2b-bus integration wave", + "reuseAction" : "adapt", + "reuseSource" : null, + "specId" : "ADR-046-resources-zone-control", + "specPath" : "docs/specs/ADR-046-resources-zone-control.md", + "validation" : "`session-lifecycle-reconnect-attempts-exhausted`, `session-lifecycle-keepalive-timeout-closes`, `session-lifecycle-nonce-exhausted-close-with-replace-generation-remediation`, `session-driver-cancelled-receiver-restores-event` (ported from test at line 707), `named-stream-credit-half-close-then-remote-close-transitions-closed`, `named-stream-reset-cancels-pending-send`", + "workItemId" : "ADR046-zone-control-011" + }, + { + "currentSource" : "main `a1cc0b2d`: `packages/d2b-session-unix/src/adapter.rs` (`UnixSeqpacketTransport::new`, `UnixStreamTransport::new`, `PeerIdentityPolicy::{Pathname{verifier},InheritedSocketpair{expected_peer}}`, `OwnedUnixAttachment::file`, `UnixAttachmentPayload`, `consume_peer_credentials()` credential strip+verify, `map_validation_error`/`map_transport_error`, tests `inherited_first_packet_requires_credentials`/`inherited_first_packet_rejects_wrong_credentials` — lines 23–852); `packages/d2b-session-unix/src/socket.rs` (`AncillaryCapacity::from_policy`, `OutboundPacket::new`, `SeqpacketSocket`, `prearmed_seqpacket_pair()`, `SendBurst`/`PacketBurst`/`SentPacket`, `recv_one` with `DONTWAIT|CMSG_CLOEXEC`, `send_one` with `DONTWAIT|NOSIGNAL`, SCM_RIGHTS+SCM_CREDENTIALS, fairness-budget burst loops, tests `raw_control_scanner_rejects_unknown_and_partial_headers`/`raw_control_scanner_accepts_exact_rights_shape` — lines 35–698); `packages/d2b-session-unix/src/pidfd.rs` (`PidfdEvidence::new`, `ProcPidfdIdentityVerifier::verify` with double-read race guard, `ProcSelfFdInfoSource::read_pid_from_fdinfo`, `DigestEvidenceCallback` — lines 8–159); `packages/d2b-session-unix/src/credit.rs` (`CreditPool::new/reserve`, `CreditScope::{Packet,Request,Operation,Session,Process,Host}`, `CreditScopeSet::reserve/reserve_ingress`, `CreditBundle::acquire_dispatch`, `ProcessCreditLimit::derive` from `RLIMIT_NOFILE` — lines 13–286); `packages/d2b-session-unix/src/descriptor.rs` (`ReceivedPacket::verify/verify_first_packet_credentials`, `DescriptorPolicy`, `ObjectIdentity`, `PeerCredentials`, `FirstPacketCredentials`, sealed memfd `F_SEAL_WRITE|GROW|SHRINK|SEAL` — lines 21–617); `packages/d2b-session-unix/src/error.rs` (`UnixSessionError` 21 variants, Display strings — lines 4–92); `packages/d2b-session-unix/src/systemd.rs` (`ActivatedSeqpacketListener`, `SystemdActivationError`)", + "dataMigration" : "Not applicable; new crate", + "dependencyOwner" : "ADR046-zone-control-011", + "destination" : "`packages/d2b-bus-unix/src/{adapter,socket,pidfd,credit,descriptor,error,systemd}.rs` (new crate `d2b-bus-unix`)", + "detailedDesign" : "**Selected**: `UnixSeqpacketTransport` seqpacket adapter implementing `OwnedTransport`; `PeerIdentityPolicy` SO_PEERCRED verification (pathname + inherited socketpair); `prearmed_seqpacket_pair()` for bootstrap inherited-socketpair path; `CreditPool`/`CreditScopeSet` six-scope credit model with per-packet/request/operation/session/process/host reservation; `ProcessCreditLimit::derive()` from `RLIMIT_NOFILE` minus `RESERVED_CONTROL_FDS`; `ProcPidfdIdentityVerifier` double-read race guard on `/proc/self/fdinfo/` with executable + cgroup digest callbacks; sealed memfd four-seal enforcement; `AncillaryCapacity` derived from `AttachmentPolicy`; `UnixSessionError` 21-variant Display strings preserved verbatim for audit log compatibility; `consume_peer_credentials()` strips-and-verifies SCM_CREDENTIALS exactly once on first packet. **Excluded ADR45 assumptions**: `vsock.rs` (`FramedVsockTransport`, `NativeVsockListener`, `NativeVsockTransport`): ADR45 guest-control vsock transport; v3 ZoneLink uses Unix sockets for host-local; vsock for Guest connections is separate work. Specific socket paths (`PUBLIC_SOCKET_PATH`, `BROKER_SOCKET_PATH`) are replaced by Zone-resource-managed paths. Primary reuse disposition: `adapt`. Preserved source-plan detail: copy + adapt.", + "integration" : "`d2b-bus-unix` provides `OwnedTransport` impl consumed by `d2b-bus` `SessionEngine`; Zone runtime passes accepted seqpacket socket to `UnixSeqpacketTransport::new`; Process pidfd identity verifier integrates with Host/Process broker launch", + "removalProof" : "`d2b-realm-router/src/secure_session.rs` `SecurePeerSession` seqpacket layer removed after d2b-bus-unix provides the replacement", + "reuseAction" : "adapt", + "reuseSource" : null, + "specId" : "ADR-046-resources-zone-control", + "specPath" : "docs/specs/ADR-046-resources-zone-control.md", + "validation" : "`seqpacket-inherited-missing-credentials-rejected` (ported from `inherited_first_packet_requires_credentials`), `seqpacket-inherited-wrong-credentials-rejected` (ported from `inherited_first_packet_rejects_wrong_credentials`), `pidfd-double-read-race-guard-detects-pid-reuse`, `sealed-memfd-partial-seal-rejected`, `credit-scope-six-levels-ordered`, `ancillary-capacity-from-disabled-policy-is-zero`, `raw-control-unknown-header-rejected` (ported from `raw_control_scanner_rejects_unknown_and_partial_headers`), `raw-control-valid-rights-parsed` (ported from `raw_control_scanner_accepts_exact_rights_shape`)", + "workItemId" : "ADR046-zone-control-012" + }, + { + "currentSource" : "main `a1cc0b2d`: `packages/d2b-contracts/src/v2_component_session.rs` (all protocol constants: `PREFACE_LEN=16`, `PREFACE_MAGIC=b\"D2BCS2\\r\\n\"`, `COMPONENT_SESSION_MAJOR=2`, `COMPONENT_SESSION_MINOR=0`, `MAX_HANDSHAKE_OFFER_BYTES=16384`, `HANDSHAKE_OFFER_CANONICAL_LEN=148`, `ENDPOINT_POLICY_IDENTITY_CANONICAL_LEN=140`, `MAX_PROTECTED_CIPHERTEXT_BYTES=65535`, `NOISE_TAG_BYTES=16`, `RECORD_LENGTH_BYTES=2`, `MAX_PROTECTED_PLAINTEXT_BYTES=65519`, `MAX_LOGICAL_MESSAGE_BYTES=1_048_576`, `MAX_ACTIVE_NAMED_STREAMS=128`, `MAX_PACKET_ATTACHMENTS=32`, `MAX_REQUEST_ATTACHMENTS=64`, `MAX_OPERATION_ATTACHMENTS=128`, `MAX_SESSION_ATTACHMENTS=256`, `MAX_PROCESS_ATTACHMENT_CREDITS=2048`, `MAX_HOST_ATTACHMENT_CREDITS=8192`, named-stream/control queue limits, credential size/flag constants; closed enums: `EndpointPurpose` 19 tags, `PurposeClass` 3 tags, `EndpointRole` 19 tags, `ServicePackage` 15 tags, `NoiseProfile` 3 tags, `IdentityEvidenceRequirement` 3 tags, `Locality` 4 tags, `TransportClass` 7 tags, `AttachmentPolicyKind`; structs: `ComponentSessionPreface::parse/encode`, `HandshakeOffer` 11 fields, `HandshakeAccept`, `HandshakeReject`, `HandshakeRejectReason` 21 tags, `LimitProfile` 15 fields with `local_default()`, `AttachmentPolicy::validate/disabled`, `BoundedVec` — throughout); `packages/d2b-session/src/handshake.rs` (`HandshakeRole::{Initiator,Responder}`, `HandshakeCredentials::{Nn,Kk,IkPsk2Initiator,IkPsk2Responder}`, `NegotiatedOffer`, `NoiseHandshake::new/write_next/read_next/finish`, `EstablishedHandshake::transcript_hash/generation`, `encode_offer/negotiate_offer`, generation-discovery magics `b\"D2BGD2Q\\n\"`/`b\"D2BGD2A\\n\"`, `encode_generation_discovery_request/accept/response/decode_generation_discovery_response` — lines 39–433)", + "dataMigration" : "Additive: new v3 tags appended to existing closed enums; ADR45 v2 tag values remain valid during coexistence", + "dependencyOwner" : "ADR046-identities-001", + "destination" : "`packages/d2b-contracts/src/v3/component_session.rs` (new v3 namespace in existing contracts crate)", + "detailedDesign" : "**Selected**: all numeric constants verbatim; all 21 `HandshakeRejectReason` tags; `ComponentSessionPreface::parse/encode` with exact magic/length layout; `HandshakeOffer` 11-field shape; `AttachmentPolicy::validate` transport constraints (packet-atomic requires seqpacket or inherited-socketpair); `LimitProfile` 15-field profile; `NoiseHandshake` for Nn/Kk/IkPsk2 profiles; `EstablishedHandshake::transcript_hash`; `BoundedVec`. **Excluded ADR45 assumptions**: `EndpointPurpose` 19 tags and `ServicePackage` 15 tags encode ADR45-specific service families — v3 will append new tags for Zone API endpoints without renumbering existing ones. `IdentityEvidenceRequirement` + `GuestSessionCredentialV1` / `GuestBootstrapPsk` / `GUEST_SESSION_CREDENTIAL_MAGIC`: ADR45 guest bootstrap credential formats, excluded until v3 Guest bootstrap work item. Generation-discovery protocol (`encode_generation_discovery_request` lines 138–149): ADR45 initiator probes server for current generation; v3 generation lives in Zone resource store — generation-discovery excluded from initial d2b-bus copy. Primary reuse disposition: `adapt`. Preserved source-plan detail: copy + adapt.", + "integration" : "All d2b-bus, d2b-bus-unix, Provider registry, and client packages import v3 protocol constants; v3 Zone API service layer uses `EndpointPurpose`/`ServicePackage` tags for Zone API endpoints", + "removalProof" : "`d2b-contracts/src/v2_component_session.rs` ADR45-specific tags retired after all v2 sessions decommissioned", + "reuseAction" : "adapt", + "reuseSource" : null, + "specId" : "ADR-046-resources-zone-control", + "specPath" : "docs/specs/ADR-046-resources-zone-control.md", + "validation" : "`preface-magic-exact-16-bytes`, `preface-offer-len-zero-rejected`, `preface-offer-len-over-max-rejected`, `preface-offer-len-canonical-accepted`, `handshake-21-reject-reasons-all-covered`, `attachment-policy-packet-atomic-requires-seqpacket-or-inherited`, `limit-profile-local-default-all-fields-positive`", + "workItemId" : "ADR046-zone-control-013" + }, + { + "currentSource" : "`nixos-modules/options-realms.nix` (option schema/assertion conventions); `nixos-modules/bundle-artifacts.nix` (bundle emit pattern); `nixos-modules/provider-registry-v2-json.nix` (provider-registry JSON emit); `nixos-modules/assertions.nix:730` (assertion style for unsafe-local); `packages/d2b-realm-core/src/ids.rs` (`LABEL_PATTERN`, `MAX_ID_LEN`, `is_label()` — eval-time name validation target)", + "dataMigration" : "Replace `nixos-modules/options-realms.nix`-derived option trees once Zone controller is live and has reached parity", + "dependencyOwner" : "ADR046-zone-control-001; Nix module owner", + "destination" : "`nixos-modules/options-zones.nix`, `nixos-modules/resource-type-validators.nix`", + "detailedDesign" : "Declare the unified `d2b.zones..resources.` option tree with two sub-options: `type` (string, required) and `spec` (attrset, required), plus the Zone-level compiler-only `parentZone` scalar (plain Zone name, no default). Require `parentZone` on every non-root Zone and forbid it on `local-root`; resolve it against declared Zone keys, reject self-parent/conflicting module definitions/cycles, and cap each ancestry path at 16 Zone names. Compile the validated map into sealed allocator bootstrap topology; never emit it into the resource bundle or `Zone.spec`. Declare the `d2b.artifacts.` catalog option tree with `package` (types.package, required) and `type` (closed string enum, required); `d2b.artifacts` is a global option, not zone-local. For each core ResourceType (Zone, ZoneLink, Provider, Role, RoleBinding, Credential), a `genSchemaOptions` helper reads the corresponding ResourceTypeSchema JSON and emits per-type `spec` option declarations with correct Nix types, defaults, and documentation comments. Provider `spec.artifactId` is a plain string referencing the `d2b.artifacts` catalog; the `package` derivation is NOT a `spec` field. Non-core (Provider-registered) ResourceTypes are deferred to Phase 2 schema validation; Phase 1 accepts any non-empty `spec` attrset for non-core types. Implement all Phase 1 eval-time assertions (§14.10 Phase 1 table) via Nix `assert` and `lib.mkAssert`; `credentialRef` helper function producing `{ \"$credentialRef\": \"Credential/\" }` literals; `resourceRef` and `closedEnum` helpers; bootstrap provider exclusion assertions; reject operator-authored `type = \"Zone\"` resources; child-local ZoneLink assertions require `childZoneName` to equal the enclosing Zone key, reject a second uplink resource even when disabled, and reject any local-root uplink. The runtime creates `Zone/`, `Provider/system-core`, and `Provider/system-minijail` directly with `managedBy=controller`; none is emitted or inferred from the configuration bundle. `allowUnsafeLocal` maps to a dedicated Host admission gate (current `options-realms.nix:346`). Provider manifest-derived fields (`spec.exports`, `spec.components`, `spec.dependencies`, `spec.permissionClaims`, `spec.upgradePolicy`, `spec.restartPolicy`) are declared as read-only/not-settable options; setting them is an eval assertion error", + "integration" : "Validated `parentZone` map feeds the allocator bootstrap sealer; unified `d2b.zones..resources.*` option tree is consumed by ADR046-zone-control-015 resource compiler; Zone controller (ADR046-zone-control-001) reads the resulting bundle; Provider package conventions come from ADR046-zone-control-003", + "removalProof" : "`nixos-modules/options-realms.nix`, `nixos-modules/realm-controller-config-json.nix`, `nixos-modules/realm-identity-config-json.nix` deleted after Zone controller and resource compiler reach full parity; `nixos-modules/assertions.nix` lines referencing `allowUnsafeLocal`/realm names removed after Host admission validation replaces them", + "reuseAction" : "create", + "reuseSource" : null, + "specId" : "ADR-046-resources-zone-control", + "specPath" : "docs/specs/ADR-046-resources-zone-control.md", + "validation" : "All Phase 1 eval tests in §15.8 (`nix-eval-name-regex-enforced`, `nix-eval-verb-closed-enum`, `nix-eval-roleref-format`, `nix-eval-subject-type-restricted`, `nix-eval-no-duplicate-subjects`, `nix-eval-bootstrap-provider-rejected`, `nix-eval-provider-missing-artifact-id`, `nix-eval-artifact-id-not-in-catalog`, `nix-eval-artifact-wrong-type`, `nix-eval-artifact-id-format`, `nix-eval-credentialref-declared`, `nix-eval-dollar-key-rejected`, all five `nix-eval-parent-zone-*` vectors, `nix-eval-zonelink-child-name-mismatch-rejected`, `nix-eval-zonelink-second-uplink-rejected`, `nix-eval-zonelink-limits-maxpendingintents-bound`)", + "workItemId" : "ADR046-zone-control-014" + }, + { + "currentSource" : "`nixos-modules/bundle-artifacts.nix` (artifact emit pattern); `nixos-modules/processes-json.nix` (canonical JSON serialization conventions); `packages/xtask/src/main.rs` `gen-schemas` subcommand (schema-from-derivation pattern); `packages/d2b-core/src/bundle.rs` (current bundle DTO shape for adaptation reference)", + "dataMigration" : "Full reset; no prior bundle state exists to carry forward", + "dependencyOwner" : "ADR046-zone-control-014; resource compiler owner", + "destination" : "`packages/d2b-resource-compiler/src/{main,bundle,schema,validator,digest,sort,secret_lint,generation}.rs`; exposed as `pkgs.d2b-resource-compiler`; called from `nixos-modules/resource-compiler.nix`", + "detailedDesign" : "Implement all Phase 2 build-time checks (§14.10 Phase 2 table): dispatch on `type` to look up ResourceTypeSchema; validate each resource's `spec` canonical JSON against the committed schema (build validation compares canonical rendered JSON against ResourceTypeSchema for each core type); compile the `d2b.artifacts.*` catalog: for each entry, build/include the derivation, verify `type` is a recognized value, compute `digest` over the derivation output, extract and hash manifest and config schema files, validate signature chain and conformance attestation; detect duplicate artifact IDs; for each Provider resource, look up `spec.artifactId` in the compiled catalog (build failure if absent or wrong type), verify `configSchemaDigest` matches schema SHA-256, validate operator `spec.config` against loaded JSON Schema using a pure-Rust validator bundled in the derivation, verify `manifestDigest` and signature chain, load manifest-derived fields (`exports`, `components`, `dependencies`, `permissionClaims`, `upgradePolicy`, `restartPolicy`) into the bundle envelope; emit private integrity-pinned artifact catalog (ID → type/digest/closure metadata) as a separate private file (never merged into the public resource bundle); check `spec.rules[*].resourceTypes` against installed Provider catalogs in the bundle (Role); verify `spec.roleRef` names an existing Role in the bundle (RoleBinding); verify `spec.subjects[*]` names resolve in bundle (RoleBinding); check ResourceType short-name collision across all Zone Providers; RFC 8785 canonical JSON serialization; per-resource `digest` computation; `bundleDigest` computation over sorted `resources` array; inline-secret heuristic lint (`--strict-secrets` flag); `generation` counter persistence in Nix module state; emit `zone-resources.json` bundle with all fields per §14.9", + "integration" : "Reads from `d2b.zones..resources.*` (ADR046-zone-control-014); emits bundle consumed by ADR046-zone-control-001 configuration publication handler; generation counter stored as Nix module derivation input hash (hermetic) or in a NixOS state file (impure) — exact mechanism is implementation decision", + "removalProof" : "No current equivalent; additive only; no prior code removed", + "reuseAction" : "create", + "reuseSource" : null, + "specId" : "ADR-046-resources-zone-control", + "specPath" : "docs/specs/ADR-046-resources-zone-control.md", + "validation" : "All Phase 2 build tests in §15.8 (`nix-build-artifact-id-missing-from-catalog`, `nix-build-artifact-wrong-type-rejected`, `nix-build-duplicate-artifact-id`, `nix-build-artifact-store-path-absent-from-bundle`, `nix-build-artifact-store-path-absent-from-config`, `nix-build-config-schema-failure`, `nix-build-schema-digest-mismatch`, `nix-build-manifest-digest-mismatch`, `nix-build-resourcetype-collision`, `nix-build-bundle-sorted`, `nix-build-bundle-digest-stable`, `nix-build-per-resource-digest-correct`, `nix-build-credential-ref-survives-build`, `nix-build-inline-secret-lint-warning`, `nix-build-inline-secret-strict-failure`)", + "workItemId" : "ADR046-zone-control-015" + }, + { + "currentSource" : "`packages/d2bd/src/lib.rs` lines 1408 and 16741 (`RealmControllersJson` load — live active generation load pattern); `nixos-modules/realm-controller-config-json.nix` (current config bundle emit to `/etc/d2b/`); `packages/d2b-realm-core/src/allocator_engine.rs` (generation/activation pattern); `packages/d2b-core/src/unsafe_local_workloads.rs:16–164` (`MAX_UNSAFE_LOCAL_WORKLOADS=256`, etc. — bounds reference for Credential/Host cleanup)", + "dataMigration" : "Full reset; no prior bundle activation state exists to carry forward", + "dependencyOwner" : "ADR046-zone-control-015; ADR046-zone-control-001; configuration publication handler owner", + "destination" : "`packages/d2b-core-controller/src/configuration.rs` (Phase 3 activation, diff, delete dispatch); `packages/d2b-core-controller/src/cleanup.rs` (pending tracking, status, stuck detection, rollback verb handler)", + "detailedDesign" : "Implement all Phase 3 runtime activation checks (§14.10 Phase 3 table); `bundleDigest` integrity verify; `zoneUid` consistency check; `generation` monotone check; per-resource `digest` re-verify; atomic generation advance in `store_meta`; diff computation (resources with `managedBy=configuration` absent from new bundle → async Delete; `managedBy=controller`/`managedBy=api` resources untouched); `managedBy` and `configurationGeneration` field maintenance on resources in redb store; `cleanupPendingCount` and `generationCleanupPending` maintenance; Zone.status.phase → Degraded while cleanup pending, reverts on completion; `GenerationCleanupPending`/`GenerationCleanupFailed` condition management; stuck-cleanup `GenerationCleanupFailed=True` at `cleanupStuckThreshold` (default 5 min) with exponential backoff retry; prior generation bundle retention and pruning up to configured `retainedPriorGenerationCount` (default 3, range 1..16); audit emission for all four cleanup audit kinds (§14.11); `zone.config-rollback` verb handler Primary reuse disposition: `adapt`. Preserved source-plan detail: extract exponential backoff from `begin_reconnect`.", + "integration" : "Zone store / redb (ADR046-store-001); core-controller watch/trigger bus (ADR046-zone-control-011); Zone status writer (ADR046-zone-control-001); audit emitter (§13.2); Credential revocation hook (triggered when `deletionRequestedAt` is set on a Credential and `core.credential-revoke` finalizer is present; revocation completes before finalizer clearance)", + "removalProof" : "`d2bd/src/lib.rs` config-load at lines 1408 and 16741 removed after Zone configuration publication handler reaches parity; `realm-controller-config-json.nix` and `realm-identity-config-json.nix` Nix bundle-emit removed after resource compiler reaches parity", + "reuseAction" : "adapt", + "reuseSource" : "main `a1cc0b2d`: `packages/d2b-session/src/lifecycle.rs` `begin_reconnect` exponential backoff logic (cleanup retry); `packages/d2b-state/` lock/lease types (ADR046-store-001 dependency for bundle file locking)", + "specId" : "ADR-046-resources-zone-control", + "specPath" : "docs/specs/ADR-046-resources-zone-control.md", + "validation" : "All Phase 3 runtime and cleanup tests in §15.8 (`nix-runtime-bundledigest-integrity`, `nix-runtime-generation-monotone`, `nix-runtime-zoneuid-mismatch-rejected`, `nix-runtime-zonename-mismatch-rejected`, `nix-runtime-activation-nonblocking`, `nix-runtime-provider-config-invalid-continues`, all `cleanup-*` and `rollback-*` tests)", + "workItemId" : "ADR046-zone-control-016" + }, + { + "currentSource" : "main `a1cc0b2d`: `packages/d2b-provider/src/registry.rs` (`RegistryLimits`, `AdmissionOptions`, `ProviderRegistryBuilder::new/limits/register_factory/register_instance/register_constructed/finish`, `ProviderRegistry::lifecycle/snapshot/instance/admit/shutdown`, `InFlightPermit`, `AdmittedProvider`, `ProviderRegistryManager::new/current/publish` — lines 33–568; tests `shutdown_closes_final_permit_notify_race`, `finish_drain_closes_final_permit_notify_race` — lines 683–691); `packages/d2b-provider/src/rpc.rs` (`SessionIdentity`, `ProviderClock`/`SystemProviderClock`, `RpcCall`, `RpcPayload`, `RpcResponse`, `AuthenticatedProviderRpc`, `RpcProviderProxy::new/preflight/call*`, `session_identity_matches_placement` — lines 27–895; test `provider_and_user_agent_session_identities_are_placement_exact` — lines 923–959); `packages/d2b-provider-toolkit/src/adapter.rs` (`ProviderAgentAdapter::new/invoke_session/invoke` — lines 18–194); `packages/d2b-provider-toolkit/src/conformance.rs` (`ConformanceError`, `check_descriptor_conformance`, `check_provider_conformance` — lines 8–126); `packages/d2b-provider-toolkit/src/fixture.rs` (`DeterministicClock`, `Fixture::new/from_descriptor/operation/request/call_context/session_identity`, `FakeProvider`, `sample_lease_request` — lines 39–262); `packages/d2b-provider-toolkit/src/registration.rs` (`ToolkitError`, `register_exact_instances` — lines 12–107); `packages/d2b-provider-toolkit/src/server.rs` (`GeneratedProviderServiceServer::from_session_handle/new/shutdown/generated_services` — lines 59–176; test `rpc_statuses_retain_closed_actionable_reasons`); `packages/d2b-provider-toolkit/src/values.rs` (`ProviderValues::new/descriptor/health/plan/handle_from_request/handle_from_plan/observation` — lines 18–192); `packages/d2b-provider-toolkit/src/redaction.rs` (`Redacted`, `Secret` — lines 3–37)", + "dataMigration" : "Provider descriptors re-registered on Zone store bootstrap; no v2 provider state migration", + "dependencyOwner" : "ADR046-zone-control-011, ADR046-zone-control-013; Provider registry/toolkit foundation owner", + "destination" : "`packages/d2b-provider/src/{registry,rpc}.rs` (new v3 Provider package); `packages/d2b-provider-toolkit/src/{adapter,conformance,fixture,registration,server,values,redaction}.rs` (new v3 toolkit)", + "detailedDesign" : "**Selected**: `ProviderRegistry` lifecycle `Accepting→Draining→Retired` with drain-waiter notify-race safety (ported from `shutdown_closes_final_permit_notify_race` / `finish_drain_closes_final_permit_notify_race` tests); `InFlightPermit` + global + per-provider in-flight quota; `ProviderRegistryManager::publish` validates snapshot before swap; `RpcProviderProxy::preflight` cancellation-check, deadline-check, method/capability match, session-identity/placement exactness; `ProviderAgentAdapter` rejects attachments with missing descriptors or non-increasing indexes (lines 79–99); `GeneratedProviderServiceServer::new` single-service-per-agent requirement + shutdown via atomic accept-flag + idle notify + timeout; `check_provider_conformance` health/inspection/observability check sequence; `Fixture`/`FakeProvider`/`DeterministicClock` as conformance harness; `Redacted`/`Secret` retained unchanged. **Excluded ADR45 assumptions**: `TrustedFirstPartyInProcess` as the only accepted placement (in `session_identity_matches_placement` lines 577–598): v3 Provider resources support multiple placements per `RealmControllerPlacement` mapping (§16.2); in-process placement is retained but not exclusive. Generated ttrpc stubs in `d2b-contracts/src/generated_v2_services/` are v2-service-specific; v3 Provider processes compile their own stubs from v3 proto files. ACA workload adapter (`d2b-gateway-runtime/src/aca_workload.rs`) excluded. Primary reuse disposition: `adapt`. Preserved source-plan detail: copy + adapt.", + "integration" : "Provider/system-core and Provider/system-minijail use `ProviderRegistryBuilder::register_constructed` as bootstrap exceptions; other Providers register instances from Process-spawned agents via `ProviderAgentAdapter`; Zone runtime hosts one `ProviderRegistry` per installed Provider component; `ProviderRegistryManager::publish` swaps registry on Provider update", + "removalProof" : "`d2b-realm-provider` trait crate removed after v3 Provider resource + registry integration (§16.5)", + "reuseAction" : "adapt", + "reuseSource" : null, + "specId" : "ADR-046-resources-zone-control", + "specPath" : "docs/specs/ADR-046-resources-zone-control.md", + "validation" : "`provider-registry-drain-waiter-race-safe` (ported from both notify-race tests), `provider-registry-publish-validates-snapshot-before-swap`, `provider-rpc-proxy-placement-exact` (ported from `provider_and_user_agent_session_identities_are_placement_exact`), `provider-agent-adapter-rejects-non-monotone-attachment-indexes`, `provider-server-shutdown-drains-in-flight-requests`, `provider-conformance-health-inspection-observability-sequence`", + "workItemId" : "ADR046-zone-control-017" + }, + { + "currentSource" : "main `a1cc0b2d`: `packages/d2b-session/src/lifecycle.rs` (`SessionPhase` 5-phase FSM, `begin_reconnect` window/attempt bounds — lines 9–195); `packages/d2b-realm-router/src/service_v2.rs` (`RealmSessionAuthority::local_controller/gateway_peer`, authority validation rejecting invalid combinations — lines 53–123; `RealmServiceServer::call` wire/request/generation/lifetime/attachment validation — lines 415–497; `RealmServiceLimits` 15 fields — lines 147–181; `RealmAuditEvent`/`RealmAuditOutcome` — lines 236–245); `packages/d2b-realm-router/src/session_lifecycle.rs` (`SessionPhase::{Allocating,TokenMinting,RelayConnecting,DisplayOpening,Running,Stopping,Stopped}`, `fail`/`stop`/`finish_stop`, tests `forward_sequence_reaches_running_then_refuses_to_advance`, `failure_mid_establishment_rolls_into_teardown_and_records_phase`, `stop_is_idempotent`, `finish_stop_without_stopping_is_a_no_op` — lines 31–220); `packages/d2b-client/src/session.rs` (`ComponentSessionConnector` trait — lines 79–86; `NamedStream` lifecycle/close/reset/send/receive — lines 426–576); `packages/d2b-daemon-access/src/component_session.rs` (`connect_component_session()` peer-UID verify + `HandshakeCredentials::Nn` + `TransportKind::LocalUnix` — lines 53–85)", + "dataMigration" : "Not applicable; new implementation", + "dependencyOwner" : "ADR046-zone-control-011, ADR046-zone-control-012, ADR046-zone-control-013; ZoneLink session/admission foundation owner", + "destination" : "`packages/d2b-core-controller/src/zone_link.rs` (ZoneLink handler); `packages/d2b-resource-api/src/admission.rs` (request admission)", + "detailedDesign" : "**Selected**: `SessionPhase` 5-phase FSM (from main `d2b-session`) drives ZoneLink session state; `Established` state → `status.connected=true` + ZoneLink `status.phase=Ready`; `Disconnected`/`Reconnecting` states → `status.connected=false` + ZoneLink `status.phase=Pending` (or `Degraded` if degraded capability); session-internal phases are not exposed as `ZoneLink.status.phase` values — only the common Resource phases (`Pending|Ready|Degraded|Failed|Unknown`) appear in `status.phase` (§3.5); `begin_reconnect` window/attempt logic → ZoneLink reconnect loop; `RealmSessionAuthority` local vs gateway pattern → ZoneLink authority types for host-local vs transport-bridge sessions; `RealmServiceServer::call` wire validation (generation, request lifetime, attachment) → Zone API request admission; `RealmServiceLimits` 15 fields → ZoneLink `spec.limits`; `connect_component_session()` Nn peer-UID path → Zone runtime bootstrap ComponentSession; `NamedStream` lifecycle → ZoneLink named-stream operations; session-lifecycle tests ported as ZoneLink phase regression tests. **Excluded ADR45 assumptions**: `RealmSessionAuthority::gateway_peer()` (lines 72–87): gateway custody and `Locality::Remote` + `CredentialCustody::GatewayGuest` are ADR45 realm-gateway patterns; v3 ZoneLink transport is bound by the resolved `spec.transportProviderRef`, `spec.transportSettings`, and `spec.transportCredentials` contract instead. Realm 7-phase `SessionLifecycle` (`Allocating→…→Running→Stopping→Stopped`) is the ADR45 realm-specific lifecycle; ZoneLink uses the 5-phase d2b-session model. `GuestBootstrapPsk`/`GuestSessionCredentialV1`: ADR45 guest bootstrap, excluded. `realm_stubs.rs` `ApiService`/`ApiFrontend` dead code excluded (§16.2).", + "integration" : "Zone runtime startup creates bootstrap ComponentSession using `HandshakeCredentials::Nn` + local domain socket + peer-UID verification (adapted from `connect_component_session`); the child-local ZoneLink handler opens its allocator-bound uplink session using d2b-bus `SessionEngine`, and the parent routes child calls over that session; resource API admission validates requests using the `RealmServiceServer::call` pattern", + "removalProof" : "`d2b-realm-router/src/service_v2.rs` `RealmServiceServer` removed after Zone API service layer replaces realm v2 service; `d2b-realm-router/src/session_lifecycle.rs` removed after d2b-bus lifecycle replaces realm-specific one", + "reuseAction" : "adapt", + "reuseSource" : null, + "specId" : "ADR-046-resources-zone-control", + "specPath" : "docs/specs/ADR-046-resources-zone-control.md", + "validation" : "`zonelink-reconnect-child-uid-change`, `zonelink-disconnect-unknown-phase`, `zonelink-intent-queue-limit`, `zone-session-phase-forward-sequence-refuses-repeat` (ported from `forward_sequence_reaches_running_then_refuses_to_advance`), `zone-session-failure-records-phase` (ported from `failure_mid_establishment_rolls_into_teardown_and_records_phase`), `zone-session-stop-is-idempotent` (ported), `zone-bootstrap-session-nn-peer-uid-verified`", + "workItemId" : "ADR046-zone-control-018" + }, + { + "currentSource" : "None — net-new ADR 0046 cross-Zone sharing model (D096); no pre-ADR45 baseline equivalent", + "dataMigration" : "None — full d2b 3.0 reset; no prior cross-Zone sharing state", + "dependencyOwner" : "ADR046-zone-control-001, ADR046-provider-004; ADR046-zonelink owner; `d2b-core-controller` + `d2b-contracts` owners", + "destination" : "`packages/d2b-contracts/src/v3/{resource_export,resource_import}.rs` (base schemas); `packages/d2b-core-controller/src/export_import.rs` (core ZoneLink export/import routing controller); shared adapter trait in `packages/d2b-provider/src/share_adapter.rs` (`ExportAdapter`/`ImportAdapter` signed-capability traits)", + "detailedDesign" : "Implement the `ResourceExport` and `ResourceImport` standard ResourceTypes per §8A plus signed Provider `ProjectionFactory` metadata binding qualified Service type, qualified Binding type, allowed owner-Service backing refs, allowed Binding target refs, projection schema/fingerprint, and aggregate factory fingerprint. Admission accepts only an owner Service as `ResourceExport.resourceRef`; matches export/import/local-factory type and fingerprints; and creates exactly one same-qualified-type projection Service (`ownerRef: ResourceImport/`). It never projects Device/Endpoint/Binding and never creates Binding. Binding spec is desired consumer intent only; observations belong only in status. No cross-Zone Ref, FD, secret, path, locator, or resource grant crosses a Zone; payload bytes use bounded encrypted named streams and high-churn sessions/streams remain internal. Export removal/ZoneLink loss revokes leases and degrades the projection Service; reconnect revalidates generation and both fingerprints. D091 currency propagates Service → export → import → projection Service → authored Binding → children. Primary reuse disposition: `adapt`. Preserved source-plan detail: net-new (extend ZoneLink controller).", + "integration" : "Zone store/redb (ADR046-store-001); shared D098 semantic base catalog (ADR046-provider-004); ZoneLink reconcile (§3); ComponentSession bounded encrypted named streams; signed projection factories/adapters for audio-pipewire, device-security-key, observability-otel, and policy-gated device-usbip; CLI graph rendering", + "removalProof" : "Not applicable (new surface)", + "reuseAction" : "adapt", + "reuseSource" : "ZoneLink reconcile/handler scaffolding (§3); `packages/d2b-session/src/streams.rs` `NamedStream` credit/backpressure (bounded encrypted stream carriage)", + "specId" : "ADR-046-resources-zone-control", + "specPath" : "docs/specs/ADR-046-resources-zone-control.md", + "validation" : "§8A.7: fast hermetic factory absent/mismatch/tamper, Service-only export target, exactly-one same-type projection Service, no Device/Endpoint/Binding projection, no auto-Binding, intent-only spec/status-only observations, backing/target allowlists, finalizer/update propagation, Provider classification, canonical Nix stability including compiler-only `d2b.zones.work.parentZone = \"local-root\"`, child-local `ZoneLink/work-uplink` in `d2b.zones.work.resources`, local `zoneLinkRef` resolution, quotas/reconnect/revoke, and no FD/secret/path tests; slower real encrypted-stream integration for audio/security-key/observability/policy-gated USBIP", + "workItemId" : "ADR046-zone-control-019" + }, + { + "currentSource" : "None — net-new ADR 0046 cross-Zone sharing model (D096)", + "dataMigration" : "None — full d2b 3.0 reset", + "dependencyOwner" : "ADR046-zone-control-019; `d2b-core-controller` owner", + "destination" : "`packages/d2b-core-controller/src/export_import_projection.rs` (local qualified Service projection lifecycle owned by `ResourceImport`)", + "detailedDesign" : "Core creates exactly one same-qualified-type projection Service per `ResourceImport` and keeps it synchronized with the remote Service lease. Operators/Nix separately author same-Zone matching Binding resources with `serviceRef` plus an allowed Guest/User/Zone target; Binding specs hold desired intent only and Binding controllers write observations only to status while owning Process/Endpoint children. On revoke, mark the projection draining/revoked and let Binding controllers stop children. On delete, wait for Bindings to be deleted/retargeted (`BindingReferencesRemain`), release the lease, delete only the projection Service/provider-owned children, then clear the import finalizer. Never create/delete Binding or project Device/Endpoint.", + "integration" : "ADR046-zone-control-019 controller; owner/dependency reconcile (§11, ADR046-reconcile-*); local semantic Provider import adapter", + "removalProof" : "Not applicable (new surface)", + "reuseAction" : "create", + "reuseSource" : null, + "specId" : "ADR-046-resources-zone-control", + "specPath" : "docs/specs/ADR-046-resources-zone-control.md", + "validation" : "Exactly one same-type Service projection owned by import; no Device/Endpoint/Binding projection; Binding never auto-created/deleted; Binding target allowlist; intent-only spec/status-only observations; owned Process/Endpoint child cleanup; pending finalizer while Binding refs remain; reconnect only after generation/factory/schema revalidation; hermetic fake-adapter + real-stream integration tiers", + "workItemId" : "ADR046-zone-control-020" + }, + { + "currentSource" : "`packages/d2bd/src/` process-global statics `USBIP_BACKGROUND_RECONCILE_ACTIVE`, `FORCE_SHUTDOWN_GENERATIONS`, and `activation_locks()`; current per-VM configuration staging symbols; ZoneLink cursor persistence in `zone_link_cursors`", + "dataMigration" : "Full d2b 3.0 reset; no process-global state persisted across the cutover", + "dependencyOwner" : "ADR046-zone-control-001, ADR046-zone-control-016; `d2b-core-controller` owner", + "destination" : "`packages/d2b-core-controller/src/{coordinator,configuration,zonelink}.rs`", + "detailedDesign" : "Per D097 core-audit findings (§8B.2): move the process-global `USBIP_BACKGROUND_RECONCILE_ACTIVE`, `FORCE_SHUTDOWN_GENERATIONS`, and `activation_locks()` state into **per-Zone** provider/resource status or a per-Zone coordinator keyed by the authority index (no process-global singletons that ignore Zone boundaries). Migrate the configuration publisher's per-VM staging symbols to **per-Zone** staging under the single configuration-publisher authority. Make ZoneLink cursor persistence and restart adoption an authority owned by the ZoneLink handler (`ownerProof`; ambiguity quarantines). All coordinated through the core authority index; no direct broker path, no process-global lock. Primary reuse disposition: `adapt`. Preserved source-plan detail: `adapt` (move process-global state to per-Zone status/coordinator).", + "integration" : "Core authority index (ADR046-zone-control-019); ZoneLink reconcile (§3); configuration publication handler (ADR046-zone-control-016)", + "removalProof" : "The process-global statics and per-VM staging symbols are deleted after the per-Zone coordinator reaches parity; confirmed by `cargo check` and a no-process-global lint", + "reuseAction" : "adapt", + "reuseSource" : "ZoneLink handler/cursor scaffolding (§3); core coordinator patterns", + "specId" : "ADR-046-resources-zone-control", + "specPath" : "docs/specs/ADR-046-resources-zone-control.md", + "validation" : "Two Zones on one host do not share `USBIP_BACKGROUND_RECONCILE_ACTIVE`/`FORCE_SHUTDOWN_GENERATIONS`/activation-lock state; per-Zone configuration staging isolation; ZoneLink cursor adoption by `ownerProof` and quarantine on ambiguity; hermetic with fakes", + "workItemId" : "ADR046-zone-control-021" + }, + { + "currentSource" : "None — net-new D097 admission (Provider cardinality)", + "dataMigration" : "None — full d2b 3.0 reset", + "dependencyOwner" : "ADR046-zone-control-019, ADR046-api-001; `d2b-core-controller` + resource API owners", + "destination" : "`packages/d2b-core-controller/src/authority.rs`; resource API admission hook", + "detailedDesign" : "Admission enforces **Provider controller cardinality** via the core authority index: most Providers are `exactly-one` per Zone; the observability Provider is `at-most-one` (zero-or-one). A `Create`/activation that would install a second controller for an `exactly-one` Provider (or a second observability Provider) is rejected with `duplicateConflict` naming the incumbent owner digest before any effect; config activation goes `Degraded`.", + "integration" : "Core authority index; resource API admission; configuration activation", + "removalProof" : "Not applicable (new surface)", + "reuseAction" : "adapt", + "reuseSource" : "Core authority index (ADR046-zone-control-019); resource API admission (`ADR-046-resource-api-and-authorization`)", + "specId" : "ADR-046-resources-zone-control", + "specPath" : "docs/specs/ADR-046-resources-zone-control.md", + "validation" : "Second Provider controller for an `exactly-one` Provider rejected with `duplicateConflict`; second observability Provider rejected; single controller admitted; `Degraded` config activation names the incumbent digest; hermetic", + "workItemId" : "ADR046-zone-control-022" + }, + { + "currentSource" : "None — `Quota` and `EmergencyPolicy` scope-uniqueness are specified (§7, §8) but not implemented/tested at baseline", + "dataMigration" : "None — full d2b 3.0 reset", + "dependencyOwner" : "ADR046-zone-control-019; `d2b-core-controller` + `d2b-contracts` owners", + "destination" : "`packages/d2b-core-controller/src/{quota,emergency_policy}.rs`; `packages/d2b-contracts/src/v3/{quota,emergency_policy}.rs`", + "detailedDesign" : "Implement `Quota` and `EmergencyPolicy` scope-uniqueness as `exactly-one`-per-scope authorities in the core authority index: a second `Quota`/`EmergencyPolicy` claiming the same scope is a `duplicateConflict`. Add the scope-uniqueness admission, status, and the test matrix (per D094 fast hermetic tests). Primary reuse disposition: `adapt`. Preserved source-plan detail: net-new (implementation + tests).", + "integration" : "Core authority index (ADR046-zone-control-019); resource API admission", + "removalProof" : "Not applicable (new implementation)", + "reuseAction" : "adapt", + "reuseSource" : "`Quota`/`EmergencyPolicy` schemas (§7, §8); core authority index", + "specId" : "ADR-046-resources-zone-control", + "specPath" : "docs/specs/ADR-046-resources-zone-control.md", + "validation" : "Duplicate-scope `Quota`/`EmergencyPolicy` rejected with `duplicateConflict`; single-scope admitted; union/individual scope flags honored; fast hermetic tests", + "workItemId" : "ADR046-zone-control-023" + }, + { + "currentSource" : "None — net-new D097 hardware-audit contract; today physical/kernel backings are guarded per-Zone or per-process, not Host-global", + "dataMigration" : "None — full d2b 3.0 reset", + "dependencyOwner" : "ADR046-zone-control-019, ADR046-zone-control-022; `d2b-core-controller` owner", + "destination" : "`packages/d2b-core-controller/src/authority.rs` (Host-global index scope + hardware admission)", + "detailedDesign" : "Extend the core authority index so `host`, `physical-device`, `seat`, and `external-service` authorities are keyed **Host-global** (`(Host, authorityClass, opaqueKeyDigest)`), admitting exactly one owner across all Zones on the host, while `zone`-scoped authorities stay Zone-local. Enforce the §8B.3 hardware rows: GPU full-device exclusive vs render-node shared; per-Guest swtpm and physical TPM exclusive (state never wiped); one Core-derived `physical-usb-backing/v1` identity digest claimed through the exact `(Host, physical-usb-backing, opaqueKeyDigest)` tuple by every USB or security-key implementation before effects, plus the separate host-global `usbip-host` module; macvtap/NIC `parentInterface` `passthru` globally exclusive across all Zones; host-shared `/dev/kvm` and `/dev/vhost-vsock` as `Provider/system-core` grants (no 28th Provider, no `kvm` busClass); globally-unique vsock CID; fixed listener ports as `Endpoint`s; host store + per-Guest store-view writer; Network TAP/bridge. A second Zone claiming the same physical backing receives `physical-usb-backing-conflict` before any open, bind, withhold, module, relay, or attachment effect; restart adopts by `ownerProof`; Guest-stop drains dependent leases. GPU-owned `udmabuf`/video and per-session `vhost-vsock` tokens stay authority subresources/DeviceGrants (not resources/Providers). Primary reuse disposition: `adapt`. Preserved source-plan detail: net-new (Host-global index scope for host/physical-device authorities).", + "integration" : "Core authority index (ADR046-zone-control-019); Provider cardinality admission (ADR046-zone-control-022); `Provider/system-core` KVM/vhost-vsock grant; `Provider/device-*` and `Network` authorities", + "removalProof" : "Not applicable (new surface)", + "reuseAction" : "adapt", + "reuseSource" : "Core authority index (ADR046-zone-control-019)", + "specId" : "ADR-046-resources-zone-control", + "specPath" : "docs/specs/ADR-046-resources-zone-control.md", + "validation" : "Two Zones on one host cannot both claim one GPU/TPM/USB/`/dev/kvm`/passthru NIC/vsock CID/fixed port — second is `duplicateConflict`; security-key and USB implementations resolving the same physical token submit byte-identical `physical-usb-backing` tuple keys and the loser receives `physical-usb-backing-conflict` before any effect; Provider-private authority classes/digests cannot bypass the collision; render-node shared admits bounded holders; per-Guest swtpm exclusive and marker never wiped; host-global adoption by `ownerProof`; hardware D096 exportability (GPU/KVM/TPM/store/macvtap non-exportable; semantic USB policy-gated); fast hermetic with fakes", + "workItemId" : "ADR046-zone-control-024" + }, + { + "currentSource" : "main `a1cc0b2d`: `packages/d2b-client/src/client.rs` (`WallClock`, `MetadataInput`, `RetryPolicy` 1..8 attempts, `CallOptions`, `CancellationToken`, `Client::new/with_clock/connect`, `ConnectedClient` methods incl. `session_generation/session_limits/service/invoke/invoke_with_attachments/named_stream/open_server_stream`, `prepare_typed_request/prepare_operation_context`, `can_retry/retryable_failure/validate_outbound_attachments/validate_reply/service_package/map_remote_kind/map_retry` — lines 35–921); `packages/d2b-client/src/session.rs` (`ComponentSessionConnector` trait, `NamedStream` lifecycle, `StreamDispatcher`, `SharedDriver`, aggregate-queue-bound test — lines 24–626); `packages/d2b-client/src/target.rs` (`ServiceOwner`, `TargetInput`, `TransportKind`, `RouteRecord`, `ResolvedTarget`, `TargetResolver`, `RouteTable` — lines 7–228); `packages/d2b-client/src/service.rs` (`ServiceKind`, `GeneratedClient`, `MethodHandle`, `ServiceHandle::new/kind/generated/proxy/method/invoke` — lines 21–184); `packages/d2b-client/src/daemon_service.rs` (`DaemonClient::new/session_generation/connected/resolve/inspect/lifecycle/open_terminal`, `DaemonTerminal`, `daemon_call_options`, `ensure_daemon_outcome`, `map_ttrpc_error`, test `redacted_terminal_debug_payload` — lines 29–689); `packages/d2b-client/src/host_socket.rs` (`HostSocketConnector::new/from_seqpacket_fd`, `local_daemon_endpoint_identity`, `ComponentSessionConnector::connect` — lines 252–383); `packages/d2b-client/src/error.rs` (`RemoteErrorKind`, `RetryClass`, `ClientError` — lines 5–128)", + "dataMigration" : "Not applicable; updated in place", + "dependencyOwner" : "ADR046-zone-control-011, ADR046-zone-control-012, ADR046-zone-control-013, ADR046-zone-control-018", + "destination" : "`packages/d2b-client/src/` (updated for v3 Zone API, replacing ADR45 daemon verbs with Zone resource operations)", + "detailedDesign" : "**Selected**: `Client::connect()` target-resolve → ComponentSession-open → `ConnectedClient` lifecycle; `RetryPolicy` 1..8 bound + `retryable_failure()` safe-only retry detection; `NamedStream::send/receive/close_local/reset` lifecycle; `ComponentSessionConnector` trait as connector abstraction; `HostSocketConnector::from_seqpacket_fd` + `local_daemon_endpoint_identity` for Zone runtime socket connector; `RouteTable` ambiguous-route rejection; `ServiceHandle`/`GeneratedClient`/`MethodHandle` typed service client pattern; `map_ttrpc_error`/`validate_reply`/`map_retry` error-handling chain; `ClientError`/`RemoteErrorKind`/`RetryClass` error taxonomy; `DaemonClient` call-options and outcome helpers (infrastructure only). **Excluded ADR45 assumptions**: `DaemonMethod` enum (lines 29–56 of daemon_service.rs): ADR45 daemon verbs (`vm_start`, `vm_stop`, `list_realms`, etc.) — replaced with Zone API verbs. `GuestClient`/`guest_service.rs`: ADR45 guest operations; excluded until v3 Guest transport work item. Hardcoded socket path `PUBLIC_SOCKET_PATH = \"/run/d2b/public.sock\"` in `host_socket.rs`: replaced by Zone-resource-managed path. `TransportKind::LocalUnix` restriction in daemon-access: v3 allows multiple transport kinds per ZoneLink binding. Primary reuse disposition: `adapt`. Preserved source-plan detail: copy + adapt.", + "integration" : "`d2b` CLI uses `d2b-client` to connect to Zone runtime via `HostSocketConnector`; a child Zone runtime uses `d2b-client` for its allocator-bound uplink while the parent route engine uses the established session for child calls; Provider toolkit conformance tests use `Fixture`/`FakeProvider` with `d2b-client` service handles", + "removalProof" : "`DaemonMethod` v2 verb enum retired after all v2 daemon operations migrated to Zone API", + "reuseAction" : "adapt", + "reuseSource" : null, + "specId" : "ADR-046-resources-zone-control", + "specPath" : "docs/specs/ADR-046-resources-zone-control.md", + "validation" : "`client-retry-policy-max-8-attempts-enforced`, `client-named-stream-close-local-then-remote-close-transitions-closed`, `client-route-table-ambiguous-route-rejected`, `client-host-socket-peer-uid-verified-on-connect`, `client-retryable-failure-only-safe-mutations`", + "workItemId" : "ADR046-zone-control-025" + }, + { + "currentSource" : "`packages/d2b-contract-tests/tests/policy_contracts.rs` lines 5–6 (D2B_FIXTURES gate / workspace-checks integration pattern — `implemented-and-reachable`, baseline `b5ddbed6`); `packages/d2b-contract-tests/tests/static_invariants.rs` (hermetic policy test structure — `implemented-and-reachable`); `tests/tools/rust-workspace-checks.sh` (D2B_FIXTURES step shell harness — `implemented-and-reachable`); AGENTS.md \"Naming conventions\" section (`-` workspace sort rules — `implemented-and-reachable`); `packages/d2b-realm-core/src/ids.rs` `LABEL_PATTERN` / `MAX_ID_LEN` (name regex reused for crate name token validation — `implemented-and-reachable`)", + "dataMigration" : "Additive; no existing `d2b-provider-*` crates in the pre-ADR45 baseline; first Provider crate created must comply from inception", + "dependencyOwner" : "ADR046-zone-control-003; workspace policy owner", + "destination" : "`packages/d2b-contract-tests/tests/policy_provider_crate_layout.rs` (new file; gated under D2B_FIXTURES in existing `tests/tools/rust-workspace-checks.sh`)", + "detailedDesign" : "Implement `policy_provider_crate_layout.rs` with the following test functions: (1) `every_provider_crate_has_src` — walk `packages/d2b-provider-*/` directories in the workspace, assert each contains `src/`; failure names crate and missing path; (2) `every_provider_crate_has_tests` — assert `tests/` present; (3) `every_provider_crate_has_integration` — assert `integration/` present; (4) `every_provider_crate_has_readme` — assert `README.md` present; (5) `every_provider_readme_has_required_sections` — read `README.md`, check for all nine section headings from §4.8.3 (case-insensitive, after stripping `#` and whitespace); failure names the missing heading(s); (6) `every_integration_file_has_target_declaration` — for each `integration/*.rs` file, scan first 20 lines for exactly one `//! integration-target: (container|host-integration)` declaration; failure names the file and the violation (missing/multiple/invalid value); (7) `non_provider_crates_exempt` — verify the check does not run on non-`d2b-provider-*` crates. All checks are filesystem-only (no compilation). Workspace member list is discovered by parsing `packages/Cargo.toml` `[workspace].members`. Gate: add the new test file to `tests/tools/rust-workspace-checks.sh` D2B_FIXTURES list alongside existing policy tests", + "integration" : "`make test-policy` and `make check` both fail if any provider crate violates §4.8; consistent with existing `no-bash-ast-walker` and workspace-sort gates; ADR046-zone-control-003 references §4.8 for Provider package conventions", + "removalProof" : "No existing code removed; additive policy test only", + "reuseAction" : "create", + "reuseSource" : null, + "specId" : "ADR-046-resources-zone-control", + "specPath" : "docs/specs/ADR-046-resources-zone-control.md", + "validation" : "§15.3 layout conformance tests: `provider-crate-layout-src-required`, `provider-crate-layout-tests-required`, `provider-crate-layout-integration-required`, `provider-crate-layout-readme-required`, `provider-readme-sections-all-present`, `provider-readme-sections-partial-missing`, `provider-integration-target-declared`, `provider-integration-target-unique`, `provider-integration-target-valid-values`, `provider-crate-naming-convention`, `provider-crate-layout-non-provider-exempt`", + "workItemId" : "ADR046-zone-control-026" + }, + { + "currentSource" : "main `a1cc0b2d`: `packages/d2b-gateway-runtime/src/provider_agent.rs` (`ProviderAgentError::{UnregisteredAdapter,RegistryNotAccepting,RegistrationRejected,InvalidAuditCapacity,SessionClosed,ProtocolViolation}`, `ProviderAgentAuditOutcome`, `ProviderAgentAuditEvent`, `ProviderAgentProcess::from_registry/from_registry_with/provider_type/service_names/audit_snapshot/serve`, `run_registered`, bounded in-memory audit ring, frame dispatch loop: semaphore in-flight limit, service/method routing, negative-timeout guard, `SessionClosed` termination, `ProtocolViolation` audit + terminate — lines 31–452; tests `standalone_entrypoint_fails_without_registration`, `audit_capacity_is_bounded` — lines 454–486); `packages/d2b-contracts/src/provider_registry_v2.rs` (`ProviderRegistryV2` wire contract, `ProviderRegistryEntryV2::validate` with provider-id derivation rule, schema fingerprint, scope-digest, generation exactness, `TrustedFirstPartyInProcess` placement requirement, `ProviderIntentId` label rules `max 128 bytes`, `MAX_PROVIDER_REGISTRY_ENTRIES`, `MAX_PROVIDER_MAPPING_IDS=64`, `ProviderBindingV2` non-exhaustive + `UnsupportedProviderBindingV2` fallback, `ProviderRegistryV2::validate` sort/unique/count checks — lines 23–566; tests `validates_closed_local_runtime_mapping`, `validates_closed_local_observability_mapping`, `serializes_declared_mapping_axes_as_closed_variants`, `rejects_duplicate_or_unbounded_mapping_ids`, `local_storage_binding_realm_must_match_descriptor_placement`, `rejects_generation_and_exact_identity_mismatches`, `contradictory_binding_realm_json_is_unrepresentable`, `unknown_binding_axis_remains_rejected_on_the_wire`, `identity_mismatch_messages_name_the_failed_contract`, `accepts_explicit_empty_registry` — lines 722–1044)", + "dataMigration" : "Not applicable; new implementation", + "dependencyOwner" : "ADR046-zone-control-011, ADR046-zone-control-017, ADR046-zone-control-018", + "destination" : "`packages/d2b-provider/src/agent.rs` (v3 provider agent dispatch); `packages/d2b-contracts/src/v3/provider_registry.rs` (v3 provider registry wire contract)", + "detailedDesign" : "**Selected**: `ProviderAgentProcess::serve` dispatch loop with semaphore in-flight limit; unsupported-service/method → ttrpc error; negative-timeout rejection; `SessionClosed` clean termination; `ProtocolViolation` audit-and-terminate path; bounded audit ring; `GeneratedProviderServiceServer` single-service-per-agent requirement; `ProviderRegistryV2` entry validation: provider-id derivation, schema fingerprint, scope digest, generation exactness; `MAX_PROVIDER_MAPPING_IDS=64` → Provider component mapping bound; `ProviderBindingV2` non-exhaustive + explicit `UnsupportedProviderBindingV2` fallback (never panics on unknown axis); `ProviderIntentId` `max 128 bytes` label rules → Provider component `spec.intentRef`; all 10 `provider_registry_v2.rs` tests ported. **Excluded ADR45 assumptions**: `aca_workload.rs` (`AcaGatewayWorkload`): ADR45 ACA external provider adapter, excluded entirely. `waypipe_display.rs` (`WaypipeDisplayProvider`): ADR45 display provider, excluded. `ProviderRegistryV2.registry_generation` / `configuration_fingerprint` bind to ADR45 bundle generation; v3 Provider resource version is tracked in redb store, not a JSON bundle. `run()` binary entrypoint uses the fixed `d2b-provider-agent` command; v3 provider processes use normal Zone runtime Process launch. `TrustedFirstPartyInProcess` is the only placement in v2; v3 Provider resources extend to `HostLocal`/`GatewayVm` etc. Primary reuse disposition: `adapt`. Preserved source-plan detail: copy + adapt.", + "integration" : "Zone runtime spawns each Provider component Process via normal Process launch; Process binary calls `ProviderAgentProcess::from_registry` then `serve()` on established ComponentSession; on `SessionClosed` the process exits and Zone runtime observes `status.phase` transition", + "removalProof" : "Not applicable; new implementation", + "reuseAction" : "adapt", + "reuseSource" : null, + "specId" : "ADR-046-resources-zone-control", + "specPath" : "docs/specs/ADR-046-resources-zone-control.md", + "validation" : "`provider-agent-dispatch-unsupported-service-returns-ttrpc-error`, `provider-agent-negative-timeout-rejected`, `provider-agent-session-closed-terminates-serve-loop`, `provider-agent-audit-ring-capacity-bounded` (ported from `audit_capacity_is_bounded`), `provider-registry-entry-fingerprint-generation-exact` (ported from `rejects_generation_and_exact_identity_mismatches`), `provider-registry-unknown-axis-fallback-non-exhaustive` (ported from `unknown_binding_axis_remains_rejected_on_the_wire`), `provider-registry-duplicate-ids-rejected` (ported from `rejects_duplicate_or_unbounded_mapping_ids`)", + "workItemId" : "ADR046-zone-control-027" + }, + { + "currentSource" : "main `a1cc0b2d`: `packages/d2b-contracts/src/v2_services.rs` (`MethodSpec{mutating,stream_kind,...}`, `ServiceSpec`, `SERVICE_INVENTORY` covering 20+ services and all provider services, `service_schema_fingerprint`, `public_daemon_schema_fingerprint`, `direct_guest_schema_fingerprint`, `StrictWireMessage`, `decode_strict`, `encode_strict`, `admit_metadata`, `TerminalStreamValidator`, `ServerStreamLease`, `RedactedTerminalFrame`, stream-name validators — lines 204–1004; tests `public_endpoint_fingerprint_binds_both_services_dependencies_and_order`, `public_endpoint_fingerprint_binds_daemon_and_guest_method_descriptors`, `direct_guest_fingerprint_binds_activation_and_remains_separate_from_public_endpoint` — lines 872–1030); `packages/d2b-contracts/src/v2_state.rs` (constants: `STATE_SCHEMA_VERSION=2`, `STATE_SCHEMA_GENERATION=1`, `MAX_JSON_DOCUMENT_BYTES=1_048_576`, `MAX_INVENTORY_ROWS=4096`, `MAX_LOCKS=1024`, `MAX_LOCK_DEPENDENCIES=32`, `MAX_DISCOVERY_OBSERVATIONS=4096`, `MAX_AUDIT_RECORD_BYTES=8192`, `MAX_AUDIT_RECORDS_PER_SEGMENT=16384`, `MAX_AUDIT_SEGMENT_BYTES=64*1024*1024`, `MAX_AUDIT_RETENTION_DAYS=14`, `MAX_LOCK_DEADLINE_MS=300_000`; types: `Digest`, `Generation`, `OwnershipEpoch`, `SafeJsonInteger`, `StorageCategory`, `StateEnvelope`, `CanonicalPayloadVerifier`, `AtomicWritePhase`, `RunnerEvidence`, audit types incl. `AuditRecord/Segment/Checkpoint/Gap`, `detect_audit_gap`, `AuditRetentionPolicy`, lock types: `LockClass`, `LockSpec`, `SyncInventory`, `LeaseRecord`); `packages/d2b-contracts/src/v2_identity.rs` (`IdentityError` 13 variants, canonical name rules `^[a-z][a-z0-9-]*$` max 63 bytes start-lowercase-letter, `RealmPath` label/separator rules, `ProviderType::ALL` 11 types + `as_str()` — lines 11–250); `packages/d2b-contracts/src/v2_provider.rs` (bounded opaque IDs `[a-z][a-z0-9-]{0,63}`: `ImplementationId`, `OperationId`, `IdempotencyKey`, `PlanId`, `HandleId`, `LeaseId`, `TransferId`, `PROVIDER_CONTRACT_FINGERPRINT`, `ProviderContractError` 34 variants, `Fingerprint` 64 lowercase-hex chars — lines 18–219); `packages/d2b-contracts/src/generated_v2_services/` (all 40+ generated ttrpc client/server stubs for v2 services)", + "dataMigration" : "v2 and v3 wire type namespaces coexist in the same contracts crate; no migration", + "dependencyOwner" : "ADR046-zone-control-013", + "destination" : "`packages/d2b-contracts/src/v3/{services,state,identity,provider}.rs` (new v3 namespace); generated stubs regenerated from v3 proto files in `packages/d2b-contracts/proto/v3/`", + "detailedDesign" : "**Selected**: `MethodSpec`/`ServiceSpec`/fingerprinting infrastructure → v3 Zone API service schema fingerprinting; `StrictWireMessage`/`decode_strict`/`encode_strict`/`admit_metadata` → v3 wire decode/admit for all resource API requests; `CanonicalPayloadVerifier` payload-digest binding → v3 resource store integrity checks; audit chain types `AuditRecord`/`AuditSegment`/`AuditCheckpoint`/`detect_audit_gap` + `MAX_AUDIT_RETENTION_DAYS=14`/`MAX_AUDIT_RECORD_BYTES=8192`/`MAX_AUDIT_SEGMENT_BYTES=64MiB` → v3 Zone audit (§13.3); lock types `LockSpec`/`SyncInventory`/`LeaseRecord` + `MAX_LOCKS=1024`/`MAX_LOCK_DEADLINE_MS=300_000` → v3 resource store lock layer; bounded opaque ID pattern `[a-z][a-z0-9-]{0,63}` → v3 `OperationId`/`HandleId`/`PlanId`/`LeaseId`; `Fingerprint` 64-hex-char → v3 Provider `spec.configFingerprint`; `ProviderContractError` 34 variants → v3 Provider operation error taxonomy; canonical name constraint `^[a-z][a-z0-9-]*$` max 63 bytes → same as `ids.rs::is_label()` shared validator (§16.2). **Excluded ADR45 assumptions**: `RealmLabel`/`WorkloadName`/`RealmPath` identity types: ADR45 workload/realm address format; replaced by `metadata.name` + Zone `ResourceRef`. `ProviderType::ALL` fixed 11-type closed enum: v3 Provider type is an open string field in the Provider resource spec. `STATE_SCHEMA_VERSION=2`/`STATE_SCHEMA_GENERATION=1`: v3 store schema uses redb table versioning, not a JSON schema version field. v2 service fingerprint tests reference ADR45-specific proto files; v3 fingerprints use different proto inputs but the same `service_schema_fingerprint` seeding mechanism. Generated stubs in `generated_v2_services/` are v2-specific and excluded; v3 uses regenerated stubs from `proto/v3/`.", + "integration" : "v3 Zone API service layer uses `MethodSpec.mutating` + `StrictWireMessage` for admission and fingerprinting; `v3/state.rs` `Digest`/`StateEnvelope` integrate with redb store writes; `CanonicalPayloadVerifier` validates resource payloads loaded from store; audit types feed §13.3 Zone audit segment; lock types wire into ADR 0034 lock lifecycle", + "removalProof" : "`d2b-contracts/src/v2_{services,state,identity,provider}.rs` and `v2_component_session.rs` deprecated in contracts crate after all v2 clients decommissioned", + "reuseAction" : "adapt", + "reuseSource" : null, + "specId" : "ADR-046-resources-zone-control", + "specPath" : "docs/specs/ADR-046-resources-zone-control.md", + "validation" : "`v3-canonical-name-matches-ids-is-label-regex`, `v3-service-fingerprint-changes-on-method-mutation` (behavior ported from `public_endpoint_fingerprint_binds_daemon_and_guest_method_descriptors`), `v3-audit-gap-detection-covers-missing-segment`, `v3-strict-wire-rejects-unknown-fields`, `v3-state-envelope-digest-mismatch-rejected`, `v3-canonical-payload-verifier-binding-holds-under-mutation`", + "workItemId" : "ADR046-zone-control-028" } - ] + ], + "schemaVersion" : 1, + "status" : "Proposed" } diff --git a/docs/specs/ADR-046-zone-routing.md b/docs/specs/ADR-046-zone-routing.md index e510853a1..5c4c8f554 100644 --- a/docs/specs/ADR-046-zone-routing.md +++ b/docs/specs/ADR-046-zone-routing.md @@ -2381,9 +2381,9 @@ The following transitions are NOT simple textual renames: | Dependency/owner | W0/W1a; zone routing contract owner | | Current source | `packages/d2b-realm-core/src/routing.rs`: `RouteAdvertisement`, `RouteNamespaceAllocation`, `RealmTreeEdge`, `DescendantRoute`, `TreeRoutePath`, `TreeRouteHop`, `RouteFailClosedReason`, `DirectShortcutAuthorizationRequest`, `DirectShortcutAuthorizationDecision`, `DirectShortcutTeardown`, `DirectShortcutTeardownReason`, `RouteAuditEventKind`, `RouteRealmClass`, `RoutePlacementClass`, `RouteAuditEventMetadata`, all route newtypes; `packages/d2b-realm-core/src/realm.rs`: `RealmPath`, `MAX_REALM_LABELS`, `MAX_REALM_PATH_BYTES`, `RealmControllerPlacement`, `EntrypointMode`; `packages/d2b-realm-core/src/ids.rs`: `RealmId`, `RouteId`, `ControllerGenerationId`, `WorkloadId`, `NodeId`, `ProviderId` (evidence: **A** for ids.rs — used in production; **B** for routing.rs — types exist with tests but no production daemon routing callers) | | Reuse source | Same v3 baseline commit `b5ddbed6` | -| Reuse action | extract and adapt | +| Reuse action | adapt | | Destination | `packages/d2b-contracts/src/v3/zone_routing.rs` | -| Detailed design | Rename RealmPath → ZonePath, RealmId → ZoneLabelId, RouteId → ZoneRouteId, ControllerGenerationId → ZoneLinkControllerGeneration; preserve all bounds/validation/serde; add ZoneLink-specific advertisement envelope fields (v3 schema version, capability ceiling field); preserve `RouteFailClosedReason` + add `zone-link-disconnected`, `hop-limit-exceeded`, `relay-denied`, `attachment-not-permitted-over-zone-link`; freeze v3 protobuf numbers separately from v2 | +| Detailed design | Rename RealmPath → ZonePath, RealmId → ZoneLabelId, RouteId → ZoneRouteId, ControllerGenerationId → ZoneLinkControllerGeneration; preserve all bounds/validation/serde; add ZoneLink-specific advertisement envelope fields (v3 schema version, capability ceiling field); preserve `RouteFailClosedReason` + add `zone-link-disconnected`, `hop-limit-exceeded`, `relay-denied`, `attachment-not-permitted-over-zone-link`; freeze v3 protobuf numbers separately from v2 Primary reuse disposition: `adapt`. Preserved source-plan detail: extract and adapt. | | Integration | d2b-bus zone route engine and ZoneLink controller consume these types | | Data migration | Full reset; no v2 Realm route compatibility | | Validation | Golden advertisement/path/failure vectors shared by Rust/Nix; property tests for NCA/loop/ceiling; replay-window tests; hop-count tests | @@ -2397,9 +2397,9 @@ The following transitions are NOT simple textual renames: | Dependency/owner | ADR046-routing-001; zone route engine owner | | Current source | `packages/d2b-realm-core/src/route_engine.rs`: full `RouteTreeEngine` struct and impl; `RouteInventoryEntry`, `RoutePruneReport`, `DirectShortcutAuthorizationRequest/Decision/Teardown`; all helper functions | | Reuse source | Same v3 baseline commit `b5ddbed6` | -| Reuse action | extract and adapt | +| Reuse action | adapt | | Destination | `packages/d2b-zone-routing/src/engine.rs` | -| Detailed design | Adapt RouteTreeEngine to ZoneRouteEngine using ZonePath/ZoneRouteId/ZoneLinkControllerGeneration from ADR046-routing-001; preserve all NCA/loop/multi-parent/capability/replay/capacity logic; add `decide_route` enforcement of `maxHops` parameter; add hop-counter decrement in relay path; expose `ZoneRoutePath` in v3 types | +| Detailed design | Adapt RouteTreeEngine to ZoneRouteEngine using ZonePath/ZoneRouteId/ZoneLinkControllerGeneration from ADR046-routing-001; preserve all NCA/loop/multi-parent/capability/replay/capacity logic; add `decide_route` enforcement of `maxHops` parameter; add hop-counter decrement in relay path; expose `ZoneRoutePath` in v3 types Primary reuse disposition: `adapt`. Preserved source-plan detail: extract and adapt. | | Integration | d2b-bus calls `ZoneRouteEngine::decide_route` for every cross-Zone ResourceClient call; ZoneLink controller calls `admit_advertisement`/`admit_withdrawal` | | Data migration | None (pure in-memory engine) | | Validation | Copy exact `route_engine.rs` test suite adapted to ZonePath; add relay/hop-count/RBAC-narrowing/shortcut integration tests | @@ -2413,9 +2413,9 @@ The following transitions are NOT simple textual renames: | Dependency/owner | ADR046-routing-001, ADR046-routing-002; ZoneLink resource owner | | Current source | `packages/d2b-realm-core/src/routing.rs`: `RouteNamespaceAllocation`; `packages/d2b-realm-core/src/access.rs`: `RealmAccessResolverRequest/Response/Error`, `RealmAccessBinding`, `RealmTransportBinding`, `RealmAccessClientContract`, `UnixSocketPath`, `AccessBindingRef`, all access types (evidence: **B** — complete implementation, no production callers); `packages/d2bd/src/realm_access_resolver.rs`: `resolve_local_root_realm_access()`, `local_root_realm_access_client_contract()` (evidence: **B** — `pub mod` at `d2bd/src/lib.rs:117`, no callers from running daemon); `packages/d2b-realm-router/src/target_resolver.rs`: `RealmEntrypointTable`, `DispatchTarget`, `RealmEntrypoint`, `ResolveError` (evidence: **A**); `packages/d2b/src/lib.rs:5240`: `load_realm_entrypoint_table()` (evidence: **A**); `packages/d2b/src/target_routing.rs`: `Route::Local`/`Route::GatewayBacked` dispatch (evidence: **A**); `packages/d2b-realm-core/src/realm.rs`: `EntrypointMode`, `RealmControllerPlacement` (evidence: **A** as types; routing use **B**); `packages/d2b-core/src/realm_controller_config.rs`: `RealmControllersJson` (evidence: **C**); `nixos-modules/realm-controller-config-json.nix` (evidence: **C**) | | Reuse source | Same v3 baseline commit `b5ddbed6` | -| Reuse action | extract and adapt | +| Reuse action | adapt | | Destination | `packages/d2b-contracts/src/v3/zone_link.rs` (ZoneLink spec/status types, intent types, namespace allocation); `packages/d2b-zone-routing/src/resolver.rs` (ZoneEntrypointResolver) | -| Detailed design | Child-local ZoneLink spec/status fields; self-name and one-uplink invariants; ZoneLinkIntent record stored in the child; ZoneLinkNamespaceAllocation issued by the exact parent allocator selected in sealed Nix-compiled `parentZone` topology; ZoneEntrypointResolver with longest-suffix match over ZonePath (adapted from `RealmEntrypointTable::resolve`); no reciprocal parent-store resource; fail-closed on unknown Zone | +| Detailed design | Child-local ZoneLink spec/status fields; self-name and one-uplink invariants; ZoneLinkIntent record stored in the child; ZoneLinkNamespaceAllocation issued by the exact parent allocator selected in sealed Nix-compiled `parentZone` topology; ZoneEntrypointResolver with longest-suffix match over ZonePath (adapted from `RealmEntrypointTable::resolve`); no reciprocal parent-store resource; fail-closed on unknown Zone Primary reuse disposition: `adapt`. Preserved source-plan detail: extract and adapt. | | Integration | Core-controller ZoneLink handler manages ZoneLink resources; ZoneEntrypointResolver in d2b-bus for per-call dispatch decision | | Data migration | None; ZoneLink resources created from Nix configuration at v3 reset | | Validation | Longest-suffix match vectors; ZoneLink spec validation tests; resolver fail-closed test | @@ -2430,9 +2430,9 @@ The following transitions are NOT simple textual renames: | Current source | `packages/d2b-realm-router/src/remote_node.rs`: `RemoteNodeRegistry`, `RemoteNodeEntry`, `RemoteNodeAvailability`, `RemoteNodeErrorKind`, `RemoteRetryAction`, `ensure_remote_execution_generation` (evidence: **B** — only in `d2bd/src/realm_stubs.rs` dead_code seam); `packages/d2b-realm-router/src/session_lifecycle.rs`: `SessionLifecycle`, `SessionPhase` (evidence: **B** — same seam); `packages/d2bd/src/realm_stubs.rs`: compile-only seam (`#[allow(dead_code)]`, declared at `d2bd/src/lib.rs:249`); `packages/d2b-realm-core/src/enrollment.rs`: `EnrollmentRecord`, `EnrollmentStatus`, `KeyRotationPlan`, `RevocationRecord`, `SessionTeardownDirective`, `RecoveryProcedure`, `IdentityAuditEventKind` (evidence: **B** — consumed by `RealmIdentityStore` which itself has no production callers); `packages/d2b-realm-core/src/identity_store.rs`: `RealmIdentityStore` (evidence: **B** — no production callers); `packages/d2b-realm-core/src/identity_config.rs`: `RealmIdentityConfigJson` (evidence: **A** — loaded at d2bd/priv-broker startup, routing inert); `nixos-modules/realm-identity-config-json.nix` (evidence: **C**); `packages/d2bd/src/workload_target_index.rs`: `WorkloadTargetIndex` (evidence: **A** — called at `d2bd/src/lib.rs:16745`; this is the live bridge from realm metadata to VM-name dispatch; retires with Guest/Host resource lookups) | | Main reuse source | `packages/d2b-session/src/lifecycle.rs` (`SessionLifecycle`, `SessionPhase`, `KeepaliveAction`, `poll_keepalive`, `disconnect`, `begin_reconnect`, `reconnect_established`, `close`; limits: `MAX_RECONNECT_ATTEMPTS=10`, `MAX_RECONNECT_WINDOW_MS=300000`; test: `lifecycle_keepalive_close_and_reconnect_change_generation`) — adapt as the ZoneLink session state machine inside the ZoneLink handler; generation-increment logic maps to `sessionGeneration` in ZoneLink status; reconnect policy comes from `spec.reconnectPolicy` instead of hard-coded LimitProfile | | Reuse source | Same v3 baseline commit `b5ddbed6` | -| Reuse action | extract and adapt | +| Reuse action | adapt | | Destination | `packages/d2b-core-controller/src/zone_links.rs` | -| Detailed design | Child-local ZoneLink handler in core-controller: manages local ResourceSpec→allocator-bound session→advertisement lifecycle; session state machine (Pending/Established/Disconnected/Reconnecting/Revoked); reconnect backoff per `reconnectPolicy`; advertisement issuance/renewal/withdrawal using enrolled KK ComponentSession; child-store route cursor and outbound intent queue; capability ceiling change handling; status writer; no copied parent/child resource content; Nix-compiled `parentZone` selects the parent allocator, which alone owns privileged listeners, placement, and route namespace and creates no reciprocal resource | +| Detailed design | Child-local ZoneLink handler in core-controller: manages local ResourceSpec→allocator-bound session→advertisement lifecycle; session state machine (Pending/Established/Disconnected/Reconnecting/Revoked); reconnect backoff per `reconnectPolicy`; advertisement issuance/renewal/withdrawal using enrolled KK ComponentSession; child-store route cursor and outbound intent queue; capability ceiling change handling; status writer; no copied parent/child resource content; Nix-compiled `parentZone` selects the parent allocator, which alone owns privileged listeners, placement, and route namespace and creates no reciprocal resource Primary reuse disposition: `adapt`. Preserved source-plan detail: extract and adapt. | | Integration | Child core-controller process → local transport Provider → sealed binding for the allocator selected by `parentZone` → d2b-bus ComponentSession; child ZoneLink handler exchanges advertisements while that parent ZoneRouteEngine admits/withdraws them | | Data migration | New ZoneLink resources from Nix configuration; no prior enrollment compatibility | | Validation | Session lifecycle tests; reconnect/revocation/ceiling-change; intent queue drain; cursor resync; advertisement renewal timing; fake-child tests | @@ -2447,9 +2447,9 @@ The following transitions are NOT simple textual renames: | Current source | `packages/d2b-realm-router/src/lib.rs`: `OperationRouter`, `RouteDecision`, `DEFAULT_RETENTION`, `DEFAULT_NO_REUSE_HORIZON`, `DEFAULT_MAX_DEDUP_RECORDS` (evidence: **B** — only in dead_code seam); `packages/d2b-realm-router/src/mux_session.rs`: `MuxSession` stream/operation forwarding (evidence: **B** for zone relay — stream-forwarding path for Zone relay does not exist yet; **D** within realm-router display-session tests); `packages/d2b-realm-core/src/frame.rs`: `ConstellationFrame`, `Handshake*`, `OperationRequest/Response`, `StreamOpen/Data/Flow/Close/Resume`, `OperationKind` (evidence: **B** from d2bd routing perspective; **A** within realm-router + `d2b-gateway-runtime` for individual types); `packages/d2b-realm-router/src/session.rs`: `PeerSession` (evidence: **B** from d2bd; **A** within realm-router tests); `packages/d2b-realm-router/src/secure_session.rs`: `SecurePeerSession`, `SecureSessionKey`, `NonceReplayGuard` (evidence: **B** from d2bd; reachable within realm-router) | | Main reuse source | `packages/d2b-session/src/cancellation.rs` (`Cancellation`, `RequestRegistry`, `cancel_generated`, `CancelResult` 5 variants; test: `cancellation_is_generation_bound_and_shared`) — copy for cross-Zone cancellation forwarding; generation-bound registry maps to per-hop session generation; `packages/d2b-session/src/streams.rs` (`NamedStreamMux`, `StreamId`, credit model; tests: `named_stream_state_and_scheduler_have_independent_credit_and_fairness`, `driver_fragments_one_mib_logical_stream_under_256_kib_credit`) — credit state machine for ZoneLink named-stream forwarding; `packages/d2b-realm-router/src/lib.rs` `OperationRouter`/`DEFAULT_RETENTION=15min`/`DEFAULT_NO_REUSE_HORIZON=60min`/`DEFAULT_MAX_DEDUP_RECORDS=65536` idempotency dedup constants — adapt full 6-tuple dedup namespace `(zone, principal, node, operation_kind, idempotency_key)` as `ZoneLinkIdempotencyKey` | | Reuse source | Same v3 baseline commit `b5ddbed6` | -| Reuse action | extract and adapt | +| Reuse action | adapt | | Destination | `packages/d2b-bus/src/zone_route.rs` (cross-Zone bus routing), `packages/d2b-bus/src/relay.rs` (per-hop relay handler) | -| Detailed design | Cross-Zone routing path in d2b-bus: ZoneEntrypointResolver → ZoneRouteEngine::decide_route → ZoneLink ComponentSession per hop; hop-counter decrement and enforcement; RBAC `relay` verb check at each intermediate hop; idempotency key namespace (full 6-tuple) in ZoneLinkIdempotencyKey; pinned reverse path tracking; cancellation forwarding; watch cursor forwarding and revision-expired handling; no-FD/credential structural rejection at serialization boundary | +| Detailed design | Cross-Zone routing path in d2b-bus: ZoneEntrypointResolver → ZoneRouteEngine::decide_route → ZoneLink ComponentSession per hop; hop-counter decrement and enforcement; RBAC `relay` verb check at each intermediate hop; idempotency key namespace (full 6-tuple) in ZoneLinkIdempotencyKey; pinned reverse path tracking; cancellation forwarding; watch cursor forwarding and revision-expired handling; no-FD/credential structural rejection at serialization boundary Primary reuse disposition: `adapt`. Preserved source-plan detail: extract and adapt. | | Integration | ResourceClient → d2b-bus → ZoneLink CS → intermediate zone → target zone; cancel/watch/stream all use the same routing path | | Data migration | None — full d2b 3.0 reset; no prior state to migrate | | Validation | End-to-end K0→K1→K2 resource call; relay-denied/hop-limit/FD-rejection tests; idempotency namespace collision tests; cancellation delivery tests; watch resync tests | @@ -2479,9 +2479,9 @@ The following transitions are NOT simple textual renames: | Dependency/owner | W0; d2b-bus owner | | Current source | None in v3 pre-ADR45 baseline. | | Main reuse source | `packages/d2b-session/` (commit `a1cc0b2d`): `SessionEngine`, `ComponentSessionDriver` (20 async methods), `SessionDriverHandle`, `NoiseHandshake` (Nn/Kk/IKpsk2), `EstablishedHandshake`, `NegotiatedOffer`, `SessionLifecycle`/`SessionPhase`, `NamedStreamMux`/`StreamId`/`StreamPhase`, `Cancellation`/`RequestRegistry`, `OwnedAttachment`/`AttachmentPayload`, `FairScheduler`/`QueueClass`/`OutboundFrame`, `RecordProtector`/`ProtectedRecord`, `BootstrapAdmission`, `OwnedTransport`/`TransportPacket`/`TransportDescriptor`, `MetricsSink`/`NoopMetrics`; `packages/d2b-contracts/src/v2_component_session.rs`: all protocol constants and wire types; `packages/d2b-session/tests/component_session.rs` (all 18+ test functions) and `tests/noise_vectors.rs` (canonical Noise vectors). | -| Reuse action | copy and adapt | +| Reuse action | adapt | | Destination | `packages/d2b-bus/src/session/` | -| Detailed design | Copy `d2b-session` crate wholesale into `d2b-bus/src/session/`; adapt `EndpointPurpose`/`EndpointRole`/`ServicePackage` closed-enum tags for v3 purposes; strip `GUEST_SESSION_CREDENTIAL_*` types; strip `serve_ttrpc_services` fixed-endpoint binding (replaced by allocator-issued FD bootstrap); adapt `SessionEngine` as ZoneLink session drive loop; keep all Noise profiles (Nn/Kk/IKpsk2), generation discovery, record/fragment/keepalive/credit/cancellation/attachment logic verbatim | +| Detailed design | Copy `d2b-session` crate wholesale into `d2b-bus/src/session/`; adapt `EndpointPurpose`/`EndpointRole`/`ServicePackage` closed-enum tags for v3 purposes; strip `GUEST_SESSION_CREDENTIAL_*` types; strip `serve_ttrpc_services` fixed-endpoint binding (replaced by allocator-issued FD bootstrap); adapt `SessionEngine` as ZoneLink session drive loop; keep all Noise profiles (Nn/Kk/IKpsk2), generation discovery, record/fragment/keepalive/credit/cancellation/attachment logic verbatim Primary reuse disposition: `adapt`. Preserved source-plan detail: copy and adapt. | | Integration | ZoneLink controller instantiates one `ComponentSessionDriver` per ZoneLink, typed as Kk for enrolled peers and Nn for initial bootstrap; d2b-bus routes ResourceClient calls through these drivers | | Data migration | None (new infrastructure) | | Validation | Port all `component_session.rs` tests; port `noise_vectors.rs`; add ZoneLink-specific KK enrollment test; add ZoneLink reconnect/revocation integration test | @@ -2495,9 +2495,9 @@ The following transitions are NOT simple textual renames: | Dependency/owner | ADR046-routing-007; transport-provider owner | | Current source | None in v3 pre-ADR45 baseline (vsock and seqpacket paths are provider-specific in v3). | | Main reuse source | `packages/d2b-session-unix/` (commit `a1cc0b2d`): `UnixSeqpacketTransport`, `UnixStreamTransport`, `PeerIdentityPolicy`, `UnixAttachmentPayload`, `OwnedUnixAttachment`; `CreditPool`/`CreditScopeSet`/`CreditBundle`/`ProcessCreditLimit`/`CreditScope` (6 scopes); `PeerCredentials`/`PidfdIdentityPolicy`/`DescriptorPolicy`/`VerifiedPacket`/`ObjectIdentity`; `SeqpacketSocket`/`StreamSocket`; `tests/unix_session.rs` (all 12+ test functions). | -| Reuse action | copy and adapt | +| Reuse action | adapt | | Destination | `packages/d2b-bus/src/transport/unix.rs`, `packages/d2b-bus/src/transport/credit.rs` | -| Detailed design | Copy `UnixSeqpacketTransport`/`UnixStreamTransport`/credit modules verbatim; adapt `PeerIdentityPolicy` for v3 Zone principal model; adapt `InheritedSocketTransport` to receive allocator-issued FD directly (not SD_LISTEN_FDS); vsock paths adapted as transport-Provider-specific implementations (not hardcoded); `PidfdIdentityPolicy` adapted for v3 Process resource pidfd model | +| Detailed design | Copy `UnixSeqpacketTransport`/`UnixStreamTransport`/credit modules verbatim; adapt `PeerIdentityPolicy` for v3 Zone principal model; adapt `InheritedSocketTransport` to receive allocator-issued FD directly (not SD_LISTEN_FDS); vsock paths adapted as transport-Provider-specific implementations (not hardcoded); `PidfdIdentityPolicy` adapted for v3 Process resource pidfd model Primary reuse disposition: `adapt`. Preserved source-plan detail: copy and adapt. | | Integration | Unix transport Provider instantiates `UnixSeqpacketTransport`; vsock transport Provider instantiates vsock transports; both implement `OwnedTransport` consumed by `SessionEngine` in ADR046-routing-007 | | Data migration | None — full d2b 3.0 reset; no prior state to migrate | | Validation | Port all `unix_session.rs` tests; add allocator-issued FD handoff test; add inherited-socket no-SD-listen test | @@ -2511,9 +2511,9 @@ The following transitions are NOT simple textual renames: | Dependency/owner | ADR046-routing-007; contracts owner | | Current source | None in v3 pre-ADR45 baseline. | | Main reuse source | `packages/d2b-contracts/src/v2_component_session.rs` (commit `a1cc0b2d`): all protocol constants (`PREFACE_LEN=16`, `MAX_ACTIVE_NAMED_STREAMS=128`, all limit constants), `ComponentSessionPreface`, `HandshakeOffer`/`EndpointPolicy`/`EndpointPolicyIdentity`, `NoiseProfile`, `LimitProfile`, `AttachmentPolicy`/`AttachmentDescriptor`/`AttachmentKind`/`AttachmentCreditClass`, `ChannelId`/`RecordHeader`/`FragmentHeader`, `SessionErrorCode`/`CloseReason`/`Remediation`, `BoundedVec`, `BinaryError`/`ContractError`; `closed_enum!` macro; `v2_component_session` test coverage via `noise_vectors.rs`. | -| Reuse action | copy and adapt | +| Reuse action | adapt | | Destination | `packages/d2b-contracts/src/v3/zone_session.rs` | -| Detailed design | Copy all protocol constants verbatim; copy `BoundedVec`, `BinaryError`, `ContractError`; copy `ComponentSessionPreface`, `LimitProfile`, `AttachmentDescriptor`/`AttachmentKind`/`AttachmentCreditClass`/`ChannelId`/`RecordHeader`/`FragmentHeader`/`SessionErrorCode`/`CloseReason`/`Remediation` verbatim; extend `ServicePackage` closed-enum with `ZoneV3`/`ResourceV3`/`ZoneLinkV3` variants at new tag values; extend `EndpointRole` with `ZoneController`/`ZoneRelay`/`ZoneBootstrap` variants; extend `EndpointPurpose`/`PurposeClass` with v3 Zone purposes; strip `GUEST_SESSION_CREDENTIAL_*` constants and types; re-freeze protobuf field numbers for v3 services independently from v2 assignments | +| Detailed design | Copy all protocol constants verbatim; copy `BoundedVec`, `BinaryError`, `ContractError`; copy `ComponentSessionPreface`, `LimitProfile`, `AttachmentDescriptor`/`AttachmentKind`/`AttachmentCreditClass`/`ChannelId`/`RecordHeader`/`FragmentHeader`/`SessionErrorCode`/`CloseReason`/`Remediation` verbatim; extend `ServicePackage` closed-enum with `ZoneV3`/`ResourceV3`/`ZoneLinkV3` variants at new tag values; extend `EndpointRole` with `ZoneController`/`ZoneRelay`/`ZoneBootstrap` variants; extend `EndpointPurpose`/`PurposeClass` with v3 Zone purposes; strip `GUEST_SESSION_CREDENTIAL_*` constants and types; re-freeze protobuf field numbers for v3 services independently from v2 assignments Primary reuse disposition: `adapt`. Preserved source-plan detail: copy and adapt. | | Integration | All d2b-bus session/transport code imports from `d2b-contracts::v3::zone_session` | | Data migration | None (new contract module) | | Validation | Updated `negotiate_offer`/`validate_exact` round-trip tests for v3 purposes; canonical encoding stability test; closed-enum exhaustiveness tests | @@ -2527,9 +2527,9 @@ The following transitions are NOT simple textual renames: | Dependency/owner | ADR046-routing-007, ADR046-routing-009; resource-client owner | | Current source | None in v3 pre-ADR45 baseline. | | Main reuse source | `packages/d2b-client/` (commit `a1cc0b2d`): `Client`, `ConnectedClient`, `CallOptions`/`CancellationToken`/`RetryPolicy`/`MetadataInput`, `Response`; `ServiceHandle`/`ServiceKind`/`GeneratedClient`/`MethodHandle`; `ConnectedSession`/`ComponentSessionConnector`/`SessionFailure`/`StreamDispatcher`; `ServiceOwner`/`TargetInput`/`TransportKind`/`ResolvedTarget`/`RouteRecord`/`RouteTable`/`TargetResolver`/`TransportSelection`; `DaemonClient`/`GuestClient`; `HostSocketConnector`/`local_daemon_endpoint_identity`; `ClientError`/`RemoteErrorKind`/`RetryClass`; `tests/client.rs` (all test functions). | -| Reuse action | copy and adapt | +| Reuse action | adapt | | Destination | `packages/d2b-resource-client/` | -| Detailed design | Copy `Client` generic structure; rename `ServiceOwner::Realm(RealmId)` → `ServiceOwner::Zone(ZonePath)`, `ServiceOwner::Workload{realm,workload}` → `ServiceOwner::Guest{zone,guest}`, `ServiceOwner::LocalRoot` → `ServiceOwner::ZoneLocal`; rename `TargetInput` variants to match; add `TargetInput::ZoneService(ZonePath, ZoneServiceKind)` for cross-Zone service targeting; replace `ServiceKind` (25 ADR45 variants) with v3 service inventory (`Resource`, `Zone`, `ZoneLink`, `Provider`, plus retained guest/daemon variants); adapt `RouteTable` to route by `ZonePath`; replace `HostSocketConnector` uid-based trust with allocator-issued FD + KK static key pinning; keep `SessionFailure`/retry/cancellation/`MetadataInput`/`RetryPolicy` logic verbatim | +| Detailed design | Copy `Client` generic structure; rename `ServiceOwner::Realm(RealmId)` → `ServiceOwner::Zone(ZonePath)`, `ServiceOwner::Workload{realm,workload}` → `ServiceOwner::Guest{zone,guest}`, `ServiceOwner::LocalRoot` → `ServiceOwner::ZoneLocal`; rename `TargetInput` variants to match; add `TargetInput::ZoneService(ZonePath, ZoneServiceKind)` for cross-Zone service targeting; replace `ServiceKind` (25 ADR45 variants) with v3 service inventory (`Resource`, `Zone`, `ZoneLink`, `Provider`, plus retained guest/daemon variants); adapt `RouteTable` to route by `ZonePath`; replace `HostSocketConnector` uid-based trust with allocator-issued FD + KK static key pinning; keep `SessionFailure`/retry/cancellation/`MetadataInput`/`RetryPolicy` logic verbatim Primary reuse disposition: `adapt`. Preserved source-plan detail: copy and adapt. | | Integration | Zone runtime uses `ResourceClient` for all cross-Zone ResourceType calls; d2b-bus wraps `ComponentSessionDriver` (ADR046-routing-007) as the underlying session | | Data migration | None — full d2b 3.0 reset; no prior state to migrate | | Validation | Port `client.rs` tests; add ZonePath routing test; add cross-Zone K0→K1 end-to-end test; add retry/cancellation forwarding test | @@ -2543,9 +2543,9 @@ The following transitions are NOT simple textual renames: | Dependency/owner | ADR046-routing-007; Provider resource owner | | Current source | None in v3 pre-ADR45 baseline (provider traits exist in `d2b-realm-provider` but are unregistered). | | Main reuse source | `packages/d2b-provider/src/` (commit `a1cc0b2d`): `ProviderRegistry`/`ProviderRegistryBuilder`/`ProviderRegistryManager`; `RegistryLimits`/`AdmissionOptions`/`InFlightPermit`/`AdmittedProvider`; `AuthenticatedProviderRpc` (trait)/`RpcProviderProxy`/`RpcCall`/`RpcResponse`/`RpcOperation`/`RpcPayload`/`SessionIdentity`; `ProviderInstance` (11 variants); `OwnedOperationContext`/`ProviderCallContext`; `ProviderResult`/`ProviderFailure`/`ProviderRuntimeError`; `packages/d2b-contracts/src/v2_provider.rs`: `ProviderDescriptor`/`ProviderCapabilitySet`/`ProviderHealth`/`ProviderAuthority`/`ProviderPlacement`/`AgentPlacementBinding`/`ProviderOperationContext`/`ProviderCallContext` etc.; inline registry tests (`prove_final_drop_between_check_and_await_completes`, `shutdown_closes_final_permit_notify_race`). | -| Reuse action | copy and adapt | +| Reuse action | adapt | | Destination | `packages/d2b-provider/src/` (adapted in place) | -| Detailed design | Retain `ProviderRegistry`/`ProviderRegistryBuilder`/`ProviderRegistryManager` lifecycle verbatim; adapt `SessionIdentity` to carry `ZonePath` instead of `RealmId`; adapt `AdmissionOptions::peer_role` to v3 Zone principal + RBAC binding; adapt `ProviderDescriptor` schema version to v3; `RegistryLimits` unchanged; `RpcProviderProxy` field adaptations to match v3 session identity; `ProviderInstance` variants retain all 11 types | +| Detailed design | Retain `ProviderRegistry`/`ProviderRegistryBuilder`/`ProviderRegistryManager` lifecycle verbatim; adapt `SessionIdentity` to carry `ZonePath` instead of `RealmId`; adapt `AdmissionOptions::peer_role` to v3 Zone principal + RBAC binding; adapt `ProviderDescriptor` schema version to v3; `RegistryLimits` unchanged; `RpcProviderProxy` field adaptations to match v3 session identity; `ProviderInstance` variants retain all 11 types Primary reuse disposition: `adapt`. Preserved source-plan detail: copy and adapt. | | Integration | Zone runtime `ProviderComposition` builds a `ProviderRegistry` per Zone; Provider resource controller admits calls through `ProviderRegistry::admit()` | | Data migration | None (pure runtime) | | Validation | Port inline registry lifecycle/drain/shutdown tests; add v3 ZonePath routing test; add RBAC relay-verb check at provider admission | @@ -2559,9 +2559,9 @@ The following transitions are NOT simple textual renames: | Dependency/owner | ADR046-routing-014; Provider agent process owner | | Current source | None in v3 pre-ADR45 baseline. | | Main reuse source | `packages/d2b-provider-toolkit/src/` (commit `a1cc0b2d`): `GeneratedProviderServiceServer`/`ProviderAgentProcess`; `ProviderAgentAdapter`; `check_descriptor_conformance`/`check_provider_conformance`/`ConformanceError`; `register_exact_instances`/`ToolkitError`; redaction helpers; `packages/d2b-gateway-runtime/src/provider_agent.rs`: `ProviderAgentProcess::from_registry`/`from_registry_with`, `MAX_DISPATCH_IN_FLIGHT=64`, `DEFAULT_AUDIT_CAPACITY=1024`, `ProviderAgentAuditEvent`/`ProviderAgentError`; `run_registered`/`run`; test `audit_capacity_is_closed_and_bounded`. | -| Reuse action | copy and adapt | +| Reuse action | adapt | | Destination | `packages/d2b-provider-toolkit/src/` (adapted in place) | -| Detailed design | Retain `GeneratedProviderServiceServer` ttrpc dispatch verbatim; adapt `ProviderAgentProcess::from_registry` to receive ComponentSession FD from Zone allocator bootstrap instead of SD_LISTEN_FDS; adapt audit event types for v3 Zone principal; `ProviderAgentAdapter` (client-side proxy) adapted for v3 ZoneLink session; conformance kit extended for v3 Provider resource conformance checks; redaction helpers unchanged | +| Detailed design | Retain `GeneratedProviderServiceServer` ttrpc dispatch verbatim; adapt `ProviderAgentProcess::from_registry` to receive ComponentSession FD from Zone allocator bootstrap instead of SD_LISTEN_FDS; adapt audit event types for v3 Zone principal; `ProviderAgentAdapter` (client-side proxy) adapted for v3 ZoneLink session; conformance kit extended for v3 Provider resource conformance checks; redaction helpers unchanged Primary reuse disposition: `adapt`. Preserved source-plan detail: copy and adapt. | | Integration | Provider Processes (EphemeralProcess or Process resources with `executionRef`) spawn the provider agent entrypoint; Zone bus instantiates `ProviderAgentAdapter` as the proxy inside the Zone runtime | | Data migration | None — full d2b 3.0 reset; no prior state to migrate | | Validation | Port `audit_capacity_is_closed_and_bounded`; add v3 bootstrap-via-allocator test; add conformance test for new Provider ResourceType schema | @@ -2575,9 +2575,9 @@ The following transitions are NOT simple textual renames: | Dependency/owner | ADR046-routing-007, ADR046-routing-001; Zone service owner | | Current source | `packages/d2b-realm-router/src/service_v2.rs` (v3 baseline `b5ddbed6`): `RealmServiceServer` (bootstrap/enroll/resolve_route/authorize_shortcut/revoke_shortcut/report_shortcut_close/inspect/cancel), `RealmServiceProcess`, `RealmSessionAuthority`, `CredentialCustody`, `RealmServiceLimits`, `RealmAuditEvent`/`RealmMethod`/`RealmAuditOutcome`, `BootstrapBinding`/`EnrollmentBinding`/`ShortcutBinding`/`MutationRecord`; constants `DEFAULT_MAX_REALM_BINDINGS=256`, `DEFAULT_MAX_SHORTCUTS=256`, `DEFAULT_MAX_MUTATION_RECORDS=1024`, `DEFAULT_AUDIT_CAPACITY=1024`, `MAX_CONFIGURED_BOUND=4096`, `MAX_DISPATCH_IN_FLIGHT=64`, `SHUTDOWN_TIMEOUT=5s` (evidence: v3 baseline, not main; see Baseline section — **B** from d2bd/CLI perspective, **A** within realm-router display-session use) | | Main reuse source | `packages/d2b-realm-router/src/service_v2.rs` (commit `a1cc0b2d`): same symbols, unchanged from v3 baseline in the main commit. All evidence class notes apply equally to main. | -| Reuse action | copy and adapt | +| Reuse action | adapt | | Destination | `packages/d2b-zone-routing/src/service.rs` | -| Detailed design | Rename `RealmServiceServer` → `ZoneServiceServer`; service wire name `d2b.realm.v2.RealmService` → `d2b.zone.v3.ZoneService`; rename methods (bootstrap→zone-bootstrap, enroll→zone-enroll, resolve_route→resolve-zone-route, authorize_shortcut→authorize-zone-shortcut, revoke_shortcut→revoke-zone-shortcut, report_shortcut_close→report-zone-shortcut-close, inspect→zone-inspect); replace `RealmSessionAuthority` with Zone principal + RBAC binding; replace `BootstrapBinding` with ZoneLink allocator-issued PSK binding; replace `EnrollmentBinding` with ZoneLink KK enrollment record; add `relay` verb RBAC check per hop; adapt shortcut model to ZonePath addressing; `RealmServiceLimits` defaults preserved; `MAX_DISPATCH_IN_FLIGHT=64`, `SHUTDOWN_TIMEOUT=5s` preserved; `CredentialCustody::GatewayGuest` excluded (all ZoneLink sessions are direct KK) | +| Detailed design | Rename `RealmServiceServer` → `ZoneServiceServer`; service wire name `d2b.realm.v2.RealmService` → `d2b.zone.v3.ZoneService`; rename methods (bootstrap→zone-bootstrap, enroll→zone-enroll, resolve_route→resolve-zone-route, authorize_shortcut→authorize-zone-shortcut, revoke_shortcut→revoke-zone-shortcut, report_shortcut_close→report-zone-shortcut-close, inspect→zone-inspect); replace `RealmSessionAuthority` with Zone principal + RBAC binding; replace `BootstrapBinding` with ZoneLink allocator-issued PSK binding; replace `EnrollmentBinding` with ZoneLink KK enrollment record; add `relay` verb RBAC check per hop; adapt shortcut model to ZonePath addressing; `RealmServiceLimits` defaults preserved; `MAX_DISPATCH_IN_FLIGHT=64`, `SHUTDOWN_TIMEOUT=5s` preserved; `CredentialCustody::GatewayGuest` excluded (all ZoneLink sessions are direct KK) Primary reuse disposition: `adapt`. Preserved source-plan detail: copy and adapt. | | Integration | Zone runtime instantiates one `ZoneServiceServer` per Zone; d2b-bus routes `d2b.zone.v3.ZoneService` calls to this server; CLI uses `ZoneServiceClient` (from ADR046-routing-010) for zone inspect/enroll/route-resolve | | Data migration | None; v3 Zone service is new; no v2 realm-service compatibility | | Validation | Bootstrap/enroll/resolve-route/shortcut integration tests against fake ZoneLink; relay-verb RBAC test; KK enrollment test; shortcut ZonePath addressing test; concurrent dispatch bound test (64 in-flight) | @@ -2591,9 +2591,9 @@ The following transitions are NOT simple textual renames: | Dependency/owner | ADR046-routing-001; Nix module owner | | Current source | `nixos-modules/options-realms-workloads.nix` (v3 baseline `b5ddbed6`): `d2b.realms..*` option declarations (evidence: **C** — eval contract; bespoke field names that do NOT mirror canonical ResourceSpec); `nixos-modules/assertions.nix`: realm-name regex, platform-gate, CIDR assertions (evidence: **A**); `nixos-modules/realm-controller-config-json.nix`: `realmControllersJson` emitter, bundle.nix:59 wiring (evidence: **C**) | | Reuse source | Same v3 baseline `b5ddbed6`; `assertions.nix` pattern reused for Zone assertions; `realm-controller-config-json.nix` is the structural template | -| Reuse action | new module following same pattern | +| Reuse action | adapt | | Destination | `nixos-modules/options-zones.nix` (new structural base), `nixos-modules/generated/options-zones-.nix` (generated per ResourceType by `xtask gen-zone-nix-options`), `nixos-modules/assertions.nix` (new Zone assertions) | -| Detailed design | Declare compiler-only scalar `d2b.zones..parentZone` plus structural `d2b.zones..resources. = { type = ...; spec = {}; }` as specified in the "Option schema" section above. `parentZone` has no default, is required for every non-root Zone, forbidden on `local-root`, resolves to one declared Zone, and never enters a ResourceSpec. Wire `options-zones.nix` and all `generated/options-zones-*.nix` files into `nixos-modules/default.nix`. Add a new `xtask gen-zone-nix-options` command that reads `docs/reference/schemas/v3/.schema.json` for each ResourceType and emits a generated submodule overlaying typed spec options (types, bounds, enum constraints, defaults, docs) onto `d2b.zones..resources..spec`. These generated modules are committed and kept in sync by `xtask gen-zone-nix-options && git diff --exit-code` wired into `make test-drift`. Because the generated options carry field-level type constraints, field-level eval errors (wrong enum, out-of-range int, non-hex fingerprint) are caught without explicit assertions. Explicit assertions in `nixos-modules/assertions.nix` cover cross-resource invariants only: zone/resource key name regex, reserved names, `parentZone` required/forbidden/existence/self/cycle/16-name-depth constraints, child-local `childZoneName == zone`, at most one uplink resource per non-root Zone and none in local root, ref resolution, count limits, and transportSettings secret-key exclusion (listed in the eval-time assertions table). Conflicting parent scalar definitions fail through standard Nix module merging. Each new assertion must have a matching case in `tests/unit/nix/cases/zone-assertions.nix` (nix-unit auto-discovered). `d2b.realms` option namespace is NOT removed in this work item. | +| Detailed design | Declare compiler-only scalar `d2b.zones..parentZone` plus structural `d2b.zones..resources. = { type = ...; spec = {}; }` as specified in the "Option schema" section above. `parentZone` has no default, is required for every non-root Zone, forbidden on `local-root`, resolves to one declared Zone, and never enters a ResourceSpec. Wire `options-zones.nix` and all `generated/options-zones-*.nix` files into `nixos-modules/default.nix`. Add a new `xtask gen-zone-nix-options` command that reads `docs/reference/schemas/v3/.schema.json` for each ResourceType and emits a generated submodule overlaying typed spec options (types, bounds, enum constraints, defaults, docs) onto `d2b.zones..resources..spec`. These generated modules are committed and kept in sync by `xtask gen-zone-nix-options && git diff --exit-code` wired into `make test-drift`. Because the generated options carry field-level type constraints, field-level eval errors (wrong enum, out-of-range int, non-hex fingerprint) are caught without explicit assertions. Explicit assertions in `nixos-modules/assertions.nix` cover cross-resource invariants only: zone/resource key name regex, reserved names, `parentZone` required/forbidden/existence/self/cycle/16-name-depth constraints, child-local `childZoneName == zone`, at most one uplink resource per non-root Zone and none in local root, ref resolution, count limits, and transportSettings secret-key exclusion (listed in the eval-time assertions table). Conflicting parent scalar definitions fail through standard Nix module merging. Each new assertion must have a matching case in `tests/unit/nix/cases/zone-assertions.nix` (nix-unit auto-discovered). `d2b.realms` option namespace is NOT removed in this work item. Primary reuse disposition: `adapt`. Preserved source-plan detail: new module following same pattern. | | Integration | ADR046-routing-012 consumes the validated `parentZone` map for private allocator bootstrap sealing and iterates `d2b.zones..resources.*` for resource-bundle emission | | Data migration | None; Zone options are new; Realm options retained until migration PR | | Validation | `nix-unit: zone-name-regex`, all five `nix-unit: zone-parent-*` vectors, `nix-unit: zone-link-fingerprint`, `nix-unit: zone-link-child-name`, `nix-unit: zone-link-one-uplink`, `nix-unit: capability-ceiling-unknown-verb`, `nix-unit: transport-settings-secret-key`; add `drift: zone-nix-options` (`xtask gen-zone-nix-options && git diff --exit-code`); run `make nix-unit-pin` after adding eval cases | @@ -2607,9 +2607,9 @@ The following transitions are NOT simple textual renames: | Dependency/owner | ADR046-routing-011, ADR046-routing-001; bundle emitter owner | | Current source | `nixos-modules/realm-controller-config-json.nix` (v3 baseline `b5ddbed6`): `builtins.toJSON` emitter for `realm-controllers.json` (bundle.nix:59); `nixos-modules/bundle-artifacts.nix`: install table (root:d2bd 0640); `nixos-modules/bundle.nix`: artifact wiring (evidence: **C**); `packages/xtask/src/main.rs` `gen-schemas` subcommand (evidence: **A** — wired into `make test-drift`) | | Reuse source | `realm-controller-config-json.nix` structural template; `xtask gen-schemas` extension point (main `a1cc0b2d` unchanged in this area) | -| Reuse action | extend and adapt | +| Reuse action | adapt | | Destination | `nixos-modules/zone-resources-json.nix` (new), private local-root allocator bootstrap compiler/sealer input (not a ResourceSpec or public bundle), `nixos-modules/bundle-artifacts.nix` (new row for per-Zone `resource-bundle.json`), `packages/xtask/src/main.rs` (`gen-zone-schemas` subcommand emitting `docs/reference/schemas/v3/.schema.json` for Zone and ZoneLink; `gen-zone-nix-options` subcommand emitting `nixos-modules/generated/options-zones-.nix`) | -| Detailed design | `zone-resources-json.nix` iterates `d2b.zones..resources.*` to produce the canonical sorted resource list: for each entry, render `{ apiVersion, type, metadata: { name, zone, ownerRef: , labels: , annotations: }, spec: }`. Separately canonicalize sorted `{ childZone, parentZone }` rows from the compiler-only topology and seal them into the private allocator bootstrap input; `parentZone` never enters a resource bundle or `Zone.spec`, and a topology digest change releases/reallocates affected edges independently of resource `generationId`. The bundle JSON omits `managedBy` and `configurationGeneration`; the configuration service/core sets those fields when activating the validated bundle. Sort all resources by `(type, zone, name)`. Compute `generationId` as SHA-256 (lower hex) of the UTF-8 bytes of the sorted `resources` array JSON. Compute `integrity` as SHA-256 (base64url, no padding) of the full bundle JSON with integrity field zeroed. Install at `/etc/d2b/zones//resource-bundle.json` root:d2bd 0640. Canonical form: all object keys sorted lexicographically; order-significant arrays preserved; schema-declared set-like arrays sorted lexicographically; all optional fields emitted with defaults; no field renaming or restructuring. Build-time validation runs in a Nix derivation: (1) validate the complete parent map (non-root required, local-root forbidden, declared target, one scalar parent, not self, acyclic, max 16 names); (2) validate each resource against the committed JSON Schema; (3) validate `transportSettings` for each child-local ZoneLink against its same-Zone Provider's `transportSettingsSchema` — `transportProviderRef` is always explicit, never inferred or defaulted; (4) verify capability ceilings are subsets of the sealed selected-parent allocator grants; (5) verify `childZoneName == metadata.zone`, at most one uplink resource per non-root Zone, and no local-root uplink; (6) check for duplicate `(type, zone, name)` tuples. Providers MUST commit their `transportSettingsSchema` before any ZoneLink can reference them. Drift gates: `xtask gen-zone-schemas && git diff --exit-code` and `xtask gen-zone-nix-options && git diff --exit-code` both wired into `make test-drift`. Add `checks.${system}.zone-schema-drift` to `flake.nix`. | +| Detailed design | `zone-resources-json.nix` iterates `d2b.zones..resources.*` to produce the canonical sorted resource list: for each entry, render `{ apiVersion, type, metadata: { name, zone, ownerRef: , labels: , annotations: }, spec: }`. Separately canonicalize sorted `{ childZone, parentZone }` rows from the compiler-only topology and seal them into the private allocator bootstrap input; `parentZone` never enters a resource bundle or `Zone.spec`, and a topology digest change releases/reallocates affected edges independently of resource `generationId`. The bundle JSON omits `managedBy` and `configurationGeneration`; the configuration service/core sets those fields when activating the validated bundle. Sort all resources by `(type, zone, name)`. Compute `generationId` as SHA-256 (lower hex) of the UTF-8 bytes of the sorted `resources` array JSON. Compute `integrity` as SHA-256 (base64url, no padding) of the full bundle JSON with integrity field zeroed. Install at `/etc/d2b/zones//resource-bundle.json` root:d2bd 0640. Canonical form: all object keys sorted lexicographically; order-significant arrays preserved; schema-declared set-like arrays sorted lexicographically; all optional fields emitted with defaults; no field renaming or restructuring. Build-time validation runs in a Nix derivation: (1) validate the complete parent map (non-root required, local-root forbidden, declared target, one scalar parent, not self, acyclic, max 16 names); (2) validate each resource against the committed JSON Schema; (3) validate `transportSettings` for each child-local ZoneLink against its same-Zone Provider's `transportSettingsSchema` — `transportProviderRef` is always explicit, never inferred or defaulted; (4) verify capability ceilings are subsets of the sealed selected-parent allocator grants; (5) verify `childZoneName == metadata.zone`, at most one uplink resource per non-root Zone, and no local-root uplink; (6) check for duplicate `(type, zone, name)` tuples. Providers MUST commit their `transportSettingsSchema` before any ZoneLink can reference them. Drift gates: `xtask gen-zone-schemas && git diff --exit-code` and `xtask gen-zone-nix-options && git diff --exit-code` both wired into `make test-drift`. Add `checks.${system}.zone-schema-drift` to `flake.nix`. Primary reuse disposition: `adapt`. Preserved source-plan detail: extend and adapt. | | Integration | The local-root allocator consumes sealed parent topology independently of resource bundles; `nixos-modules/bundle-artifacts.nix` installs each per-Zone `resource-bundle.json`; ADR046-routing-013 Zone runtime reads it on startup | | Data migration | None; new artifact file | | Validation | `drift: zone-resource-schema`, `drift: zone-nix-options`, `build: zone-bundle-deterministic`, `build: parent-topology-sealed`, `build: transport-settings-unknown-field`, `build: capability-ceiling-superset`, `build: missing-transport-provider`; run `make flake-matrix-pin` after adding flake checks | @@ -2623,9 +2623,9 @@ The following transitions are NOT simple textual renames: | Dependency/owner | ADR046-routing-012, ADR046-routing-003; `d2b-core-controller` owner (ADR-046-core-controllers) | | Current source | `packages/d2b-realm-core/src/realm_controller_config.rs`: `RealmControllersJson`, `RealmControllerRow`, `RealmControllerConfigError` (evidence: **C** — loaded but routing inert); `packages/d2bd/src/realm_access_resolver.rs`: `resolve_local_root_realm_access()`, `RealmAccessResolverRequest`, `RealmAccessBinding` (evidence: **B** — pub mod at lib.rs:117, no callers); `packages/d2b-state/src/` (both baselines): atomic state, OFD locks, lease primitives (evidence: **A** for locks/leases, **B** for realm-specific storage); `nixos-modules/host-daemon.nix:220–221`: bundle artifact install paths, daemon SIGHUP wiring (evidence: **A**) | | Main reuse source | `packages/d2b-state/src/` (main `a1cc0b2d`): atomic state, audit segment primitives adapted for generation tracking | -| Reuse action | extract and adapt | +| Reuse action | adapt | | Destination | `packages/d2b-core-controller/src/configuration.rs` (defined by ADR-046-core-controllers); shared bundle DTOs may live in `packages/d2b-core/` | -| Detailed design | Implement the configuration ownership and cleanup contract from the "Configuration ownership and cleanup contract" section. `configuration.rs` owns: (1) reading and integrity-verifying `/etc/d2b/zones//resource-bundle.json` on startup and SIGHUP; (2) diffing against active generation by `generationId` (no-op if unchanged); (3) queuing Create/UpdateSpec/Delete intents — core sets `configurationGeneration` and `managedBy` when applying Create/UpdateSpec; Delete targets only resources where BOTH `managedBy` equals the configuration service's value AND `configurationGeneration` matches the prior bundle — resources with `managedBy=controller` or `managedBy=api` are never seized; (4) setting `deletionRequestedAt` on pending-delete resources immediately and adding a Pending condition; (5) writing the prior bundle into the capped ring at `/var/lib/d2b/zones//configuration/prior/.json` (default retentionCount=3, range 1..16, no TTL; prune oldest when count would exceed limit); (6) enforcing boundary invariants (no diff-delete for absent `configurationGeneration`, `managedBy` collision guard, live controller-child teardown guard); (7) driving finalizer drain + controller-child cascade before completing a Delete; (8) on successful deletion: one store transaction writes the `Deleted` revision/change event and removes the resource row and all index entries; the authoritative audit record (`zone-resource-cleanup`) is appended from the committed revision with dedup/exactly-once recovery and is NOT part of the store transaction; (9) tracking `deletionRequestedAt`/`cleanupConfigGeneration`/`cleanupError`/`cleanupAttempt` per resource; (10) on rollback: clearing `deletionRequestedAt` and Pending condition for revived resources; (11) never pruning a prior bundle while a Delete intent from its `configurationGeneration` is in flight. OFD lock on the bundle file prevents concurrent activation races. Generation state persisted atomically at `/var/lib/d2b/zones//configuration/generation.json` (root:d2bd 0640). The `spec` object comparison for UpdateSpec detection uses the canonical JSON form so two identical specs always compare equal regardless of Nix rendering order. Resource phase transitions: Pending while Create/UpdateSpec in-flight; Degraded while cleanup pending; Ready when clean; Failed on permanent error. | +| Detailed design | Implement the configuration ownership and cleanup contract from the "Configuration ownership and cleanup contract" section. `configuration.rs` owns: (1) reading and integrity-verifying `/etc/d2b/zones//resource-bundle.json` on startup and SIGHUP; (2) diffing against active generation by `generationId` (no-op if unchanged); (3) queuing Create/UpdateSpec/Delete intents — core sets `configurationGeneration` and `managedBy` when applying Create/UpdateSpec; Delete targets only resources where BOTH `managedBy` equals the configuration service's value AND `configurationGeneration` matches the prior bundle — resources with `managedBy=controller` or `managedBy=api` are never seized; (4) setting `deletionRequestedAt` on pending-delete resources immediately and adding a Pending condition; (5) writing the prior bundle into the capped ring at `/var/lib/d2b/zones//configuration/prior/.json` (default retentionCount=3, range 1..16, no TTL; prune oldest when count would exceed limit); (6) enforcing boundary invariants (no diff-delete for absent `configurationGeneration`, `managedBy` collision guard, live controller-child teardown guard); (7) driving finalizer drain + controller-child cascade before completing a Delete; (8) on successful deletion: one store transaction writes the `Deleted` revision/change event and removes the resource row and all index entries; the authoritative audit record (`zone-resource-cleanup`) is appended from the committed revision with dedup/exactly-once recovery and is NOT part of the store transaction; (9) tracking `deletionRequestedAt`/`cleanupConfigGeneration`/`cleanupError`/`cleanupAttempt` per resource; (10) on rollback: clearing `deletionRequestedAt` and Pending condition for revived resources; (11) never pruning a prior bundle while a Delete intent from its `configurationGeneration` is in flight. OFD lock on the bundle file prevents concurrent activation races. Generation state persisted atomically at `/var/lib/d2b/zones//configuration/generation.json` (root:d2bd 0640). The `spec` object comparison for UpdateSpec detection uses the canonical JSON form so two identical specs always compare equal regardless of Nix rendering order. Resource phase transitions: Pending while Create/UpdateSpec in-flight; Degraded while cleanup pending; Ready when clean; Failed on permanent error. Primary reuse disposition: `adapt`. Preserved source-plan detail: extract and adapt. | | Integration | `d2b-core-controller` configuration service activates on bundle install and SIGHUP; zone-controller reconcile loops in `d2b-core-controller` consume the queued intents; d2b-bus resource API exposes `status.phase` and `pendingCleanup` via Get/Watch on the active generation resource | | Data migration | None; new runtime component | | Validation | `host-integration: cleanup-removed-zonelink`, `host-integration: rollback-restores-zonelink`, `host-integration: dynamic-child-not-deleted`, `host-integration: zonelink-no-reciprocal-row`; unit tests: deterministic generationId, no-op on same generationId, cross-ownership invariant enforcement, prior-bundle write/prune cycle, UpdateSpec canonical comparison, store-transaction-then-audit-append ordering, exactly-once audit dedup | diff --git a/docs/specs/README.md b/docs/specs/README.md index 7b570b07d..95e654497 100644 --- a/docs/specs/README.md +++ b/docs/specs/README.md @@ -89,14 +89,18 @@ resource, indexed with owned/exported ResourceTypes and component placement in ### Generated manifests -`ADR-046-spec-set.json` and `ADR-046-work-items.json` are deterministically -generated indexes, regenerated from the member Markdown and not themselves -members of the set. - -- `ADR-046-spec-set.json` (`artifactKind: d2b-adr-spec-set`, `schemaVersion` 1) +`ADR-046-spec-set.json` and `ADR-046-work-items.json` are deterministic indexes, +regenerated from the member Markdown and not themselves members of the set. +This Proposed documentation set does not yet contain the future checked-in +generator. The current candidate was produced with a disposable script that was +removed after regeneration; `ADR046-delivery-004` and +`ADR046-delivery-009` own the future generator and fail-closed policy tests. + +- `ADR-046-spec-set.json` (`artifactKind: d2b-adr-spec-set`, `schemaVersion` 2) binds the exact 55 member files: for each member, its `specId`, `path`, `status`, `version`, resolved `dependsOn` edges (the `ADR-046-provider-*` - dependency glob is expanded to every Provider dossier), `supersedes`, and the + dependency glob is expanded to every Provider dossier), `supersedes`, + registered `workItemPrefix` (or `null` for a member with no items), and the lowercase SHA-256 of the exact Markdown bytes. It records the parent path and the `v3` baseline commit and carries no timestamp or host path. - `ADR-046-work-items.json` (`artifactKind: d2b-adr-work-items`, `schemaVersion` @@ -291,10 +295,10 @@ Each spec contains an **Implementation work items** section. Every item has: | Field | Requirement | | --- | --- | -| Work item ID | Stable `ADR046--` ID | +| Work item ID | Declared by the heading as `ADR046--`; an optional table row must match it exactly | | Dependency/owner | Prerequisites, future wave, crate/component, shared owner | | Current source | Exact v3 paths, symbols, call sites, artifacts, and tests | -| Reuse source | Optional exact main commit/paths/symbols/tests used for copy/adaptation | +| Reuse source | Optional exact main commit/paths/symbols/tests used for copy/adaptation; explicit `None` serializes as `null` | | Reuse action | Exactly one canonical `reuseAction` value defined below | | Destination | Exact future crate/module/file and binary targets | | Detailed design | Types, APIs, algorithms, state, limits, errors, security | @@ -305,8 +309,68 @@ Each spec contains an **Implementation work items** section. Every item has: The exact work-item ID regex is `^ADR046-[a-z0-9]+(?:-[a-z0-9]+)*-(?:00[1-9]|0[1-9][0-9]|[1-9][0-9]{2})$`. -`` is the owning Spec ID with the literal `ADR-046-` prefix removed; -`` is a three-digit value from `001` through `999`. +`` is the owning member's registered stable prefix below; it is +not required to equal the full Spec ID suffix. `` is a three-digit +value from `001` through `999`. Prefixes are globally unique and never inferred +from a filename. + +| Normative member | Registered `workItemPrefix` | +| --- | --- | +| `ADR-046-cli-and-operations` | `cli` | +| `ADR-046-components-processes-and-sandbox` | `process` | +| `ADR-046-componentsession-and-bus` | `session` | +| `ADR-046-core-controllers` | `core` | +| `ADR-046-current-code-migration-map` | — | +| `ADR-046-decision-register` | `decisions` | +| `ADR-046-feasibility-and-spikes` | `feasibility` | +| `ADR-046-nix-configuration` | `nix` | +| `ADR-046-primitive-resource-composition` | `primitives` | +| `ADR-046-provider-activation-nixos` | `activation` | +| `ADR-046-provider-audio-pipewire` | `audio` | +| `ADR-046-provider-clipboard-wayland` | `clipboard` | +| `ADR-046-provider-credential-entra` | `cred-entra` | +| `ADR-046-provider-credential-managed-identity` | `cred-mi` | +| `ADR-046-provider-credential-secret-service` | `cred-ss` | +| `ADR-046-provider-device-gpu` | `gpu` | +| `ADR-046-provider-device-security-key` | `security-key` | +| `ADR-046-provider-device-tpm` | `device-tpm` | +| `ADR-046-provider-device-usbip` | `usbip` | +| `ADR-046-provider-display-wayland` | `display` | +| `ADR-046-provider-model-and-packaging` | `provider` | +| `ADR-046-provider-network-local` | `nl` | +| `ADR-046-provider-notification-desktop` | `notify` | +| `ADR-046-provider-observability-otel` | `otel` | +| `ADR-046-provider-runtime-azure-container-apps` | `aca` | +| `ADR-046-provider-runtime-azure-virtual-machine` | `azure-vm` | +| `ADR-046-provider-runtime-cloud-hypervisor` | `ch` | +| `ADR-046-provider-runtime-qemu-media` | `qemu-media` | +| `ADR-046-provider-shell-terminal` | `sterm` | +| `ADR-046-provider-state` | `pstate` | +| `ADR-046-provider-system-core` | `system-core` | +| `ADR-046-provider-system-minijail` | `minijail` | +| `ADR-046-provider-system-systemd` | `systemd` | +| `ADR-046-provider-transport-azure-relay` | `transport-relay` | +| `ADR-046-provider-transport-unix` | `transport-unix` | +| `ADR-046-provider-transport-vsock` | `vsock` | +| `ADR-046-provider-volume-local` | `vl` | +| `ADR-046-provider-volume-virtiofs` | `vvfs` | +| `ADR-046-reset-and-cutover` | `reset` | +| `ADR-046-resource-api-and-authorization` | `api` | +| `ADR-046-resource-object-model` | `object` | +| `ADR-046-resource-reconciliation` | `reconcile` | +| `ADR-046-resource-store-redb` | `store` | +| `ADR-046-resources-credential` | `credential` | +| `ADR-046-resources-device` | `device` | +| `ADR-046-resources-host-guest-process-user` | `exec` | +| `ADR-046-resources-network` | `network` | +| `ADR-046-resources-volume` | `volume` | +| `ADR-046-resources-zone-control` | `zone-control` | +| `ADR-046-security-and-threat-model` | `security` | +| `ADR-046-streamline` | `streamline` | +| `ADR-046-telemetry-audit-and-support` | `telem` | +| `ADR-046-terminology-and-identities` | `identities` | +| `ADR-046-validation-and-delivery` | `delivery` | +| `ADR-046-zone-routing` | `routing` | `reuseAction` is a closed scalar vocabulary: @@ -327,12 +391,18 @@ separate items. Generation and validation fail closed unless every normative member's Implementation work items section is complete: -- every work-item heading matches the ID regex, uses its owning spec slug, and - is unique across the set; -- every item has exactly one of each required table field above, no unknown or - duplicate fields, and its Work item ID field exactly matches its heading; -- every required value is nonempty except an explicitly absent Reuse source, - which serializes as `null`; `create` requires that null value; +- every work-item heading matches the ID regex, uses its owning registered + prefix, and is unique across the set; +- every item has exactly one nonempty `Dependency/owner`, `Current source`, + `Reuse action`, `Destination`, `Detailed design`, `Integration`, + `Data migration`, `Validation`, and `Removal proof` field, with no duplicate + fields; an optional `Work item ID` row exactly matches its heading and an + optional `Reuse source` is nonempty; +- every heading prefix exactly matches the owning member's registered + `workItemPrefix`, every registered prefix is globally unique, and a member + with no work items has a `null` prefix in the generated spec set; +- an absent or explicit-none Reuse source serializes as `null`; `create` + requires that null value; - every Markdown item appears exactly once in `ADR-046-work-items.json`, every manifest item resolves to exactly one Markdown item, and the bound `specId` and `specPath` match the owning member; and diff --git a/docs/specs/providers/ADR-046-provider-activation-nixos.md b/docs/specs/providers/ADR-046-provider-activation-nixos.md index 53fc912bc..c35766e44 100644 --- a/docs/specs/providers/ADR-046-provider-activation-nixos.md +++ b/docs/specs/providers/ADR-046-provider-activation-nixos.md @@ -1046,9 +1046,9 @@ Required adaptations (work item `ADR046-activation-001`): | --- | --- | | Dependency/owner | Provider/activation-nixos runner owner; reused helper owner in d2b-host | | Current source | packages/d2b-host/src/bin/d2b-activation-helper.rs | -| Reuse action | adapt in place | +| Reuse action | adapt | | Destination | packages/d2b-host/src/bin/d2b-activation-helper.rs | -| Detailed design | Replace the helper CLI flag interface with structured JSON input and JSON output, accept bounded systemArtifactId and activationMode, resolve store path internally, emit bounded outcome code, write no resource metadata, and preserve the no-bash-fallback invariant. | +| Detailed design | Replace the helper CLI flag interface with structured JSON input and JSON output, accept bounded systemArtifactId and activationMode, resolve store path internally, emit bounded outcome code, write no resource metadata, and preserve the no-bash-fallback invariant. Primary reuse disposition: `adapt`. Preserved source-plan detail: adapt in place. | | Integration | activation-runner invokes the helper through the pre-opened activation portal and system-manager effect resources, then reports structured outcome to activation-nixos status. | | Data migration | Full d2b 3.0 reset; no v2 activation-helper invocation compatibility | | Validation | Unit tests for JSON protocol, bounded outcomes, no resource metadata writes, and no Command::new bash fallback. | @@ -1066,9 +1066,9 @@ Required adaptations (work item `ADR046-activation-001`): | --- | --- | | Dependency/owner | ADR-046-resource-object-model and ADR-046-core-controllers; d2b-contracts activation-nixos owner | | Current source | None — net-new v3 ResourceType; no pre-ADR45 baseline equivalent | -| Reuse action | net-new schema and DTOs | +| Reuse action | create | | Destination | docs/reference/schemas/v3/activation-nixos.d2bus.org.NixosGeneration.json and packages/d2b-contracts/src/activation_nixos.rs | -| Detailed design | Define JSON schema and Rust DTOs for activation-nixos.d2bus.org.NixosGeneration, with systemArtifactId as the only artifact reference, no store path fields, activationDetail as a closed status.resource enum absent from spec, and phase constrained to the common framework enum values. | +| Detailed design | Define JSON schema and Rust DTOs for activation-nixos.d2bus.org.NixosGeneration, with systemArtifactId as the only artifact reference, no store path fields, activationDetail as a closed status.resource enum absent from spec, and phase constrained to the common framework enum values. Primary reuse disposition: `create`. Preserved source-plan detail: net-new schema and DTOs. | | Integration | Resource API, resource store, Nix compiler, activation-nixos controller, and CLI projections consume the schema and DTOs. | | Data migration | Full d2b 3.0 reset; no v2 generation resource import | | Validation | Schema golden vectors, serde unknown-field rejection, phase enum tests, activationDetail-not-in-spec test, and no-store-path-in-spec-or-status test. | @@ -1087,9 +1087,9 @@ Define JSON schema and Rust DTOs. Enforce: | --- | --- | | Dependency/owner | ADR046-activation-002; activation-nixos controller owner | | Current source | Current top-level activation behavior in packages/d2b/src/lib.rs and hardlink-farm/store ownership split described in this dossier | -| Reuse action | replace top-level imperative activation flow with resource controller logic | +| Reuse action | replace | | Destination | packages/d2b-provider-activation-nixos/src/controller/ | -| Detailed design | Implement the reconcile loop for activation-nixos.d2bus.org.NixosGeneration: validate executionRef, systemArtifactId, and priorGenerationRef; dispatch one activation-runner EphemeralProcess with canonical startRoot=true shape; observe runner status; mark superseded generations; prune by retainedGenerations through the finalizer protocol; never perform direct store-path operations, nix-collect-garbage, explicit VolumeGcRequest, raw argv composition, or store path writes to resources. | +| Detailed design | Implement the reconcile loop for activation-nixos.d2bus.org.NixosGeneration: validate executionRef, systemArtifactId, and priorGenerationRef; dispatch one activation-runner EphemeralProcess with canonical startRoot=true shape; observe runner status; mark superseded generations; prune by retainedGenerations through the finalizer protocol; never perform direct store-path operations, nix-collect-garbage, explicit VolumeGcRequest, raw argv composition, or store path writes to resources. Primary reuse disposition: `replace`. Preserved source-plan detail: replace top-level imperative activation flow with resource controller logic. | | Integration | Controller watches NixosGeneration resources through Zone resource API, creates activation-runner EphemeralProcesses, releases ownership references for Provider/volume-local, and writes bounded status. | | Data migration | Full d2b 3.0 reset; adopt mode records an existing active generation but does not import v2 controller state | | Validation | Controller tests for retention, finalizer sequence, no TTL retention, no direct store ops, no store path in status, deleted event-only removal, and runner shape. | @@ -1111,9 +1111,9 @@ Reconcile loop for `activation-nixos.d2bus.org.NixosGeneration`. Key invariants: | --- | --- | | Dependency/owner | ADR046-activation-003; activation-runner owner | | Current source | packages/d2b-host/src/bin/d2b-activation-helper.rs for helper invocation; runner process is net-new | -| Reuse action | net-new runner invoking adapted helper | +| Reuse action | adapt | | Destination | packages/d2b-provider-activation-nixos/src/runner/ | -| Detailed design | Implement target-local activation-runner worker that executes on NixosGeneration.spec.executionRef for Host and Guest targets using the same contract, reads private artifact-catalog.json through the integrity channel, resolves systemArtifactId to a store path in memory only, invokes d2b-activation-helper through structured JSON, executes target-local switch-to-configuration through typed helper dispatch with no raw exec or SSH, emits structured outcome JSON, and never outputs store paths. | +| Detailed design | Implement target-local activation-runner worker that executes on NixosGeneration.spec.executionRef for Host and Guest targets using the same contract, reads private artifact-catalog.json through the integrity channel, resolves systemArtifactId to a store path in memory only, invokes d2b-activation-helper through structured JSON, executes target-local switch-to-configuration through typed helper dispatch with no raw exec or SSH, emits structured outcome JSON, and never outputs store paths. Primary reuse disposition: `adapt`. Preserved source-plan detail: net-new runner invoking adapted helper. | | Integration | Controller-created EphemeralProcess runs under Provider/system-minijail on the target execution context and returns outcome through the activation portal for status update. | | Data migration | Full d2b 3.0 reset; no v2 runner state import | | Validation | Runner tests for artifact lookup, JSON helper invocation, Host and Guest target parity, no raw argv, no SSH, no store path in output, and terminal nonzero handling. | @@ -1138,9 +1138,9 @@ use the same contract with no bypass. | --- | --- | | Dependency/owner | ADR-046-cli-and-operations; activation CLI owner | | Current source | packages/d2b/src/lib.rs top-level cmd_switch, cmd_boot, cmd_test, cmd_rollback, cmd_build, cmd_generations, cmd_gc, and cmd_migrate | -| Reuse action | replace with grouped CLI projection | +| Reuse action | replace | | Destination | packages/d2b/src/activation.rs | -| Detailed design | Implement d2b activation build, switch, boot, test, rollback, adopt, generations, gc, and migrate subcommands, projecting ADR-046 CLI and Operations behavior and ensuring no store path, digest, or artifact catalog field appears in JSON or human output. | +| Detailed design | Implement d2b activation build, switch, boot, test, rollback, adopt, generations, gc, and migrate subcommands, projecting ADR-046 CLI and Operations behavior and ensuring no store path, digest, or artifact catalog field appears in JSON or human output. Primary reuse disposition: `replace`. Preserved source-plan detail: replace with grouped CLI projection. | | Integration | d2b CLI dispatcher calls resource API and activation-nixos controller by creating or listing NixosGeneration resources; legacy top-level verbs are removed by ADR046-activation-007 after integration tests pass. | | Data migration | Full d2b 3.0 reset; CLI command surface changes with no runtime state import | | Validation | CLI integration tests for subcommand parsing, authorization, resource creation/listing, rollback priorGenerationRef, gc ownership release, and output redaction. | @@ -1157,9 +1157,9 @@ only after this lands (work item ADR046-activation-007). | --- | --- | | Dependency/owner | ADR-046-nix-configuration; activation-nixos Nix owner | | Current source | Current VM Nix configuration emits activation inputs implicitly; this item creates the explicit Provider and NixosGeneration resource emitter | -| Reuse action | net-new resource emitter adapted from existing Nix activation inputs | +| Reuse action | adapt | | Destination | nixos-modules/providers/activation-nixos.nix | -| Detailed design | Emit Provider spec and activation-nixos.d2bus.org.NixosGeneration resources per target, flow retainedGenerations only through Provider.spec.config.retainedGenerations, reference systems by systemArtifactId only, omit store paths from all emitted resources, and avoid dedicated state-layout User or ComponentPrincipal because ProviderStateSet is empty. | +| Detailed design | Emit Provider spec and activation-nixos.d2bus.org.NixosGeneration resources per target, flow retainedGenerations only through Provider.spec.config.retainedGenerations, reference systems by systemArtifactId only, omit store paths from all emitted resources, and avoid dedicated state-layout User or ComponentPrincipal because ProviderStateSet is empty. Primary reuse disposition: `adapt`. Preserved source-plan detail: net-new resource emitter adapted from existing Nix activation inputs. | | Integration | Nix compiler emits Provider and NixosGeneration resources plus private artifact catalog entries consumed by core configuration publication and the activation-nixos controller. | | Data migration | Full d2b 3.0 reset; existing d2b.vms activation settings are reauthored as Zone resources rather than imported | | Validation | Nix eval tests for Provider config, NixosGeneration shape, retainedGenerations source, no systemStorePath in bundle, no state Volume or state-layout principal, and artifact ID resolution. | @@ -1179,9 +1179,9 @@ Provider's normal principal model. | --- | --- | | Dependency/owner | ADR046-activation-005; d2b CLI dispatcher owner | | Current source | packages/d2b/src/lib.rs cmd_switch, cmd_boot, cmd_test, cmd_rollback, cmd_build, cmd_generations, cmd_gc, cmd_migrate and dispatcher registrations | -| Reuse action | delete legacy top-level commands | +| Reuse action | delete-after-cutover | | Destination | packages/d2b/src/lib.rs | -| Detailed design | Remove the legacy top-level activation command functions and their dispatcher registrations after the grouped d2b activation namespace passes integration tests. | +| Detailed design | Remove the legacy top-level activation command functions and their dispatcher registrations after the grouped d2b activation namespace passes integration tests. Primary reuse disposition: `delete-after-cutover`. Preserved source-plan detail: delete legacy top-level commands. | | Integration | CLI dispatcher routes only d2b activation subcommands for activation operations; documentation and tests use the new namespace. | | Data migration | Full d2b 3.0 reset; no command alias compatibility window | | Validation | CLI integration matrix for d2b activation passes; grep or contract test confirms old cmd_* symbols and dispatcher registrations are absent. | diff --git a/docs/specs/providers/ADR-046-provider-audio-pipewire.md b/docs/specs/providers/ADR-046-provider-audio-pipewire.md index e696849d7..0734434a3 100644 --- a/docs/specs/providers/ADR-046-provider-audio-pipewire.md +++ b/docs/specs/providers/ADR-046-provider-audio-pipewire.md @@ -2750,9 +2750,9 @@ remote lease are released. | Dependency/owner | No prerequisites; `d2b-provider-audio-pipewire` crate | | Current source | `packages/d2b-core/src/audio_policy.rs` (all symbols); `packages/d2b-core/tests/audio_policy.rs` | | Reuse source | Same baseline paths | -| Reuse action | `copy-unchanged` (no daemon imports; pure DTO library) | +| Reuse action | copy-unchanged | | Destination | `packages/d2b-provider-audio-pipewire/src/audio_policy.rs`; re-exported from crate root | -| Detailed design | `LevelPercent`, `AudioGrant`, `AudioPolicyState`, `parse_audio_state`, `to_v2_bytes`, `AudioPolicyError` copy unchanged. `AudioPolicyState` is the canonical in-memory representation of `AudioBinding.spec.grants`. `parse_audio_state`/`to_v2_bytes` are used only once during first-activation migration from a prior v1/v2 on-disk file; there is no ongoing state file in v3. | +| Detailed design | `LevelPercent`, `AudioGrant`, `AudioPolicyState`, `parse_audio_state`, `to_v2_bytes`, `AudioPolicyError` copy unchanged. `AudioPolicyState` is the canonical in-memory representation of `AudioBinding.spec.grants`. `parse_audio_state`/`to_v2_bytes` are used only once during first-activation migration from a prior v1/v2 on-disk file; there is no ongoing state file in v3. Primary reuse disposition: `copy-unchanged`. Preserved source-plan detail: `copy-unchanged` (no daemon imports; pure DTO library). | | Integration | First-activation migration uses `parse_audio_state` and writes grants only into a per-Guest AudioBinding that also names the explicitly configured same-Zone owner AudioService. | | Data migration | Parse v1/v2 once; require exactly one configured owner Service; write grants plus `serviceRef`; fail closed on missing/ambiguous Service; remove prior file only after successful commit. | | Validation | `tests/audio_policy.rs`: all existing tests from `d2b-core/tests/audio_policy.rs` plus AudioBinding spec serialization tests | @@ -2766,9 +2766,9 @@ remote lease are released. | Dependency/owner | Depends on `ADR046-audio-001`; Process Provider template schema | | Current source | `packages/d2b-host/src/audio_argv.rs` (all symbols + tests); `tests/golden/runner-shape/audio-argv-minimal.txt` | | Reuse source | Same baseline paths | -| Reuse action | `adapt` — argv builder retained; becomes a signed component-template projection, not a live Process spec field | +| Reuse action | adapt | | Destination | `packages/d2b-provider-audio-pipewire/src/argv.rs` (component template renderer) | -| Detailed design | `generate_audio_argv` remains the canonical argv builder for the `vhost-user-sound-worker` component template. The resulting argv/env/executableRef are sealed into the LaunchTicket. The per-Guest binary copy path enforcement remains via the LaunchTicket verifier. The live Process resource spec contains no argv or executableRef. The `--socket` argument is removed; the vhost-user service identity is `Endpoint/corp-vm-audio-vhost-user`, while the backing locator is resolved into the LaunchTicket under authorization. | +| Detailed design | `generate_audio_argv` remains the canonical argv builder for the `vhost-user-sound-worker` component template. The resulting argv/env/executableRef are sealed into the LaunchTicket. The per-Guest binary copy path enforcement remains via the LaunchTicket verifier. The live Process resource spec contains no argv or executableRef. The `--socket` argument is removed; the vhost-user service identity is `Endpoint/corp-vm-audio-vhost-user`, while the backing locator is resolved into the LaunchTicket under authorization. Primary reuse disposition: `adapt`. Preserved source-plan detail: `adapt` — argv builder retained; becomes a signed component-template projection, not a live Process spec field. | | Integration | The component template for `vhost-user-sound-worker` embeds the output of `generate_audio_argv`; the Process Provider resolves arg0 from the artifact catalog. | | Data migration | No runtime state migration; argv template output is regenerated from the v3 component template, and live Process specs never store argv. | | Validation | `tests/argv.rs`: rejection matrix (Nix store path, symlink, cross-guest copy, empty name); no-socket-in-argv assertion; no-argv-in-process-spec assertion | @@ -2782,9 +2782,9 @@ remote lease are released. | Dependency/owner | Depends on `ADR046-audio-001`; ComponentSession service contract; libpipewire 1.x API | | Current source | `packages/d2bd/src/audio_host_controller.rs` `PipeWireHostController` enforcement logic; `QemuAudioController` | | Reuse source | Same baseline paths | -| Reuse action | `adapt` — enforcement logic becomes a libpipewire API implementation behind the `SetGrant`/`SetLevel` ComponentSession service | +| Reuse action | adapt | | Destination | `packages/d2b-provider-audio-pipewire/src/mediator/enforcement.rs` | -| Detailed design | Owner AudioService only: speaker `SetGrant` maps `"off"` to `pw_node_set_param(SPA_PARAM_Props, mute=true, target.object=-1)` on the worker's node and `"on"` to `mute=false`; `SetLevel` maps to a bounded volume. Microphone `"on"` is applied only after the owner authority grants its single capture slot; queued/blocked consumers remain muted, and release mutes/disconnects before handoff. `captureAlias` resolves privately through the registry. A projection AudioService routes the operation to the remote owner over its import stream and is denied any local mediator/PipeWire open. `FakeAudioMediator` is the hermetic test double. No state file, wpctl, EphemeralProcess, or node ID in any external surface. | +| Detailed design | Owner AudioService only: speaker `SetGrant` maps `"off"` to `pw_node_set_param(SPA_PARAM_Props, mute=true, target.object=-1)` on the worker's node and `"on"` to `mute=false`; `SetLevel` maps to a bounded volume. Microphone `"on"` is applied only after the owner authority grants its single capture slot; queued/blocked consumers remain muted, and release mutes/disconnects before handoff. `captureAlias` resolves privately through the registry. A projection AudioService routes the operation to the remote owner over its import stream and is denied any local mediator/PipeWire open. `FakeAudioMediator` is the hermetic test double. No state file, wpctl, EphemeralProcess, or node ID in any external surface. Primary reuse disposition: `adapt`. Preserved source-plan detail: `adapt` — enforcement logic becomes a libpipewire API implementation behind the `SetGrant`/`SetLevel` ComponentSession service. | | Integration | AudioBinding controller calls its resolved AudioService. Owner Service dispatches locally to AudioMediator; projection Service dispatches over the encrypted import route to the remote owner. | | Data migration | No state migration; mediator applies current AudioBinding grants and levels from resource state during reconcile, replacing host-controller direct writes. | | Validation | `tests/mediator.rs` and `tests/enforcement.rs`: owner-Service SetGrant/SetLevel round-trip; speaker mixing; microphone queued consumers remain muted; release/lease-expiry mute-before-handoff and no-overlap proof; projection routing with fake streams; projection-PipeWire-open denial; no-node-id-in-bus-message; ProviderSessionUnavailable; captureAlias registry resolution | @@ -2798,9 +2798,9 @@ remote lease are released. | Dependency/owner | ADR046-provider-004 common audio Service/Binding base; Core resource-api foundation; `d2b-provider-audio-pipewire` crate | | Current source | None (ADR-only); structured after `d2b-contracts/src/public_wire.rs` audio types | | Reuse source | `public_wire.rs` `AudioChannel`, `AudioEnforcementPosture`, `AudioErrorKind`, `AudioProviderKind`, `AudioSetApplied`; `AudioProviderKind` is removal evidence, not a base-status field | -| Reuse action | `adapt` provider-neutral closed enums; remove `AudioProviderKind` from base status; `ADR-only` for schema/admission | +| Reuse action | adapt | | Destination | `packages/d2b-provider-audio-pipewire/src/{resource_type,admission,provider_extension}.rs` (strict implementation extensions and binding only; common base lives under ADR046-provider-004) | -| Detailed design | Bind the shared D098 `audio.d2bus.org.AudioService` and `audio.d2bus.org.AudioBinding` base schema versions/fingerprints from ADR046-provider-004 and define only strict audio-pipewire Provider extensions/admission. AudioService validates immutable `serviceRole`, same-Zone local Endpoint refs, owner-only D097 AuthorityDescriptor, projection-only `ownerRef: ResourceImport/`, Core-only projection creation, and projection `spec.provider` rejection. Initial-v3 owner admission requires speaker `multiplexed` and microphone `exclusive` and rejects multiplexed capture plus every consent/approval/priority placeholder. Owner Service status carries only bounded aggregate microphone active/request/Zone counts; Binding status carries only its own arbitration state. AudioBinding validates Guest ownership, required immutable same-Zone `serviceRef`, grants/levels/users, and forbids authority/export/projection semantics. Semantic authority/import/attachment observations stay under `status.resource`; implementation observations stay under `status.provider`. PipeWire fields are rejected from base spec/status. Register no provider-qualified or AudioState identifier and no serde/schema alias. | +| Detailed design | Bind the shared D098 `audio.d2bus.org.AudioService` and `audio.d2bus.org.AudioBinding` base schema versions/fingerprints from ADR046-provider-004 and define only strict audio-pipewire Provider extensions/admission. AudioService validates immutable `serviceRole`, same-Zone local Endpoint refs, owner-only D097 AuthorityDescriptor, projection-only `ownerRef: ResourceImport/`, Core-only projection creation, and projection `spec.provider` rejection. Initial-v3 owner admission requires speaker `multiplexed` and microphone `exclusive` and rejects multiplexed capture plus every consent/approval/priority placeholder. Owner Service status carries only bounded aggregate microphone active/request/Zone counts; Binding status carries only its own arbitration state. AudioBinding validates Guest ownership, required immutable same-Zone `serviceRef`, grants/levels/users, and forbids authority/export/projection semantics. Semantic authority/import/attachment observations stay under `status.resource`; implementation observations stay under `status.provider`. PipeWire fields are rejected from base spec/status. Register no provider-qualified or AudioState identifier and no serde/schema alias. Primary reuse disposition: `adapt`. Preserved source-plan detail: `adapt` provider-neutral closed enums; remove `AudioProviderKind` from base status; `ADR-only` for schema/admission. | | Integration | `Provider/audio-pipewire` signs and publishes implementation support for both neutral qualified ResourceTypeSchemas and strict provider-envelope schemas. Core import controller may create/delete only projection AudioService; ordinary resource API admission handles owner Services and AudioBindings. | | Data migration | Full d2b 3.0 reset; owner Services and per-Guest AudioBindings are authored as new v3 resources; projection Services are core-generated from ResourceImport. | | Validation | `tests/resource_type.rs`: consume the ADR046-provider-004 common fixtures/fingerprints; canonical minimal base without `spec.provider`; neutral qualified-name registration; both schema/status round-trips including bounded aggregate owner queue status and per-Binding arbitration state; clean-break rejection of provider-qualified names, every AudioState spelling, and all aliases; fake alternate-provider base conformance; strict base/provider unknown-field matrices; projection `spec.provider` rejection; D088 `status.resource`/`status.provider` placement; PipeWire fields only in strict provider envelopes/config; Service role/AuthorityDescriptor/ownerRef/Endpoint-locality rules; initial-v3 exclusive-mic/mixed-speaker schema and consent/approval/priority/concurrent-capture rejection; Core-only projection admission; AudioBinding required same-Zone serviceRef and Guest owner; immutable refs; out-of-range levels/users; explicit tests that AudioBinding cannot be exported or projected | @@ -2814,9 +2814,9 @@ remote lease are released. | Dependency/owner | Depends on `ADR046-audio-001` through `ADR046-audio-005`; core ResourceImport controller; system Process Provider; AudioMediator (`ADR046-audio-007`); GuestAudioAgent (`ADR046-audio-011`); no Provider state Volume under D087 | | Current source | `packages/d2bd/src/audio_dispatch.rs` lines 250–end (dispatch ordering reference) | | Reuse source | None directly; reconcile flow is new async controller | -| Reuse action | `adapt` — dispatch logic is the reference for step ordering only | +| Reuse action | adapt | | Destination | `packages/d2b-provider-audio-pipewire/src/controller/audio_service.rs`; `src/controller/audio_binding.rs` | -| Detailed design | One controller binary registers deterministic handlers for the two neutral ResourceTypes, constrained to immutable `spec.providerRef: Provider/audio-pipewire`. Service handler watches AudioService, its ResourceImport owner and local Endpoints; owner semantics claim/revalidate D097, local mediator, and aggregate exclusive-mic queue state, while projection semantics bind only encrypted import streams and deny PipeWire. It cannot create/delete projection Service. Binding handler watches AudioBinding, same-Zone serviceRef, Guest/User, owned Process, and private Endpoints; creates the vhost-user worker, GuestAudioAgents, and private Endpoints, then calls the resolved Service and guest agents. It maps `Applied|Queued|MicQueueFull` into the closed conditions/status/phase, and release/delete/revocation cancels queue state before child teardown. A Service event enqueues serviceRef-indexed Bindings. A resource selecting another conforming Provider is ignored and cannot be status/finalizer-mutated. Neither handler uses broker/pidfd/EphemeralProcess/Volume/User operations or direct filesystem access. | +| Detailed design | One controller binary registers deterministic handlers for the two neutral ResourceTypes, constrained to immutable `spec.providerRef: Provider/audio-pipewire`. Service handler watches AudioService, its ResourceImport owner and local Endpoints; owner semantics claim/revalidate D097, local mediator, and aggregate exclusive-mic queue state, while projection semantics bind only encrypted import streams and deny PipeWire. It cannot create/delete projection Service. Binding handler watches AudioBinding, same-Zone serviceRef, Guest/User, owned Process, and private Endpoints; creates the vhost-user worker, GuestAudioAgents, and private Endpoints, then calls the resolved Service and guest agents. It maps `Applied|Queued|MicQueueFull` into the closed conditions/status/phase, and release/delete/revocation cancels queue state before child teardown. A Service event enqueues serviceRef-indexed Bindings. A resource selecting another conforming Provider is ignored and cannot be status/finalizer-mutated. Neither handler uses broker/pidfd/EphemeralProcess/Volume/User operations or direct filesystem access. Primary reuse disposition: `adapt`. Preserved source-plan detail: `adapt` — dispatch logic is the reference for step ordering only. | | Integration | Registered with Zone core as a controller under `Provider/audio-pipewire`. | | Data migration | v1/v2 audio policy file migration is handled by ADR046-audio-001 before reconcile; the controller keeps no Provider state Volume and imports no additional runtime state. | | Validation | Fast hermetic `tests/audio_service_controller.rs`: neutral type/provider selection, foreign-provider ignore/deny, owner authority, bounded aggregate microphone status, projection ownerRef/import chain, core-only create/delete, projection no-PipeWire-open, revocation queue cancellation, and D091 propagation. `tests/audio_binding_controller.rs`: neutral type/provider selection, required same-Zone serviceRef, owner/projection dispatch, child Process/private Endpoint state machine, `Applied|Queued|MicQueueFull` status mapping, off/delete/revocation cancellation, grant changes, absence/failures/deletion. Conformance asserts no AudioBinding export/projection, no broker/pidfd/EphemeralProcess/Volume/User ops. ProviderDeployment integration remains fake-only and validates empty ProviderStateSet. | @@ -2830,7 +2830,7 @@ remote lease are released. | Dependency/owner | Depends on `ADR046-audio-004`; ComponentSession service (ADR-046-componentsession-and-bus); libpipewire 1.x | | Current source | `packages/d2bd/src/audio_host_controller.rs` PipeWire session access patterns (reference only) | | Reuse source | None; new component | -| Reuse action | `ADR-only` | +| Reuse action | create | | Destination | `packages/d2b-provider-audio-pipewire/src/mediator/mod.rs`; `src/bin/audio_pipewire_mediator.rs` | | Detailed design | Owner AudioService implementation only. Long-lived user-session Process maintains per-AudioBinding nodes under the single owner backing, receives the pre-opened local PipeWire portal FD, and exposes `SetGrant`/`SetLevel` through `Endpoint/audio-pipewire-authority`. It enforces the authority arbiter's single microphone slot and mute-before-handoff result while speaker nodes remain mixed. Projection Services never start/call a local mediator and cannot receive its FD. No EphemeralProcess, wpctl, remote Ref, or node identity in external surfaces. | | Integration | Second binary in the `d2b-provider-audio-pipewire` package. Registered as a user-session service under `Provider/audio-pipewire`. | @@ -2846,7 +2846,7 @@ remote lease are released. | Dependency/owner | Depends on `ADR046-audio-005`; Nix resource compilation framework; `ADR-046-nix-configuration` | | Current source | `nixos-modules/components/audio/host.nix` and `guest.nix` | | Reuse source | Same | -| Reuse action | `replace` | +| Reuse action | replace | | Destination | `nixos-modules/components/audio/v3-resource.nix`; `nixos-modules/components/audio/host-config.nix`; `nixos-modules/components/audio/guest-config.nix` | | Detailed design | `v3-resource.nix` emits only `audio.d2bus.org.AudioService` and `audio.d2bus.org.AudioBinding`, selected by `Provider/audio-pipewire`, with required same-Zone serviceRef. For projection identity, ResourceExport emits `resourceRef` plus canonical `serviceType`, `projectionSchemaFingerprint`, and `factoryFingerprint`; its Service retains the Endpoint. ResourceImport emits the matching three `expected*` fields. Projection Services are never authored: core materializes them from ResourceImport. Eval rejects every obsolete Export/Import field in the explicit rejection matrix, provider-qualified ResourceTypes, every AudioState spelling, aliases, PipeWire fields in neutral base spec/status, AudioBinding export/projection, non-exclusive microphone or consent/approval/priority/concurrent-capture fields, Service role/ownerRef/authority mismatches, cross-Zone Endpoint/service refs, and duplicate owner authority. Existing captureAlias stays in Provider config; guestUsers/group injection, runtime-audio derivation, host stream rules, and guest stack remain. | | Integration | `nixos-modules/default.nix` imports all three modules. | @@ -2862,7 +2862,7 @@ remote lease are released. | Dependency/owner | Depends on `ADR046-audio-005`; `d2b-contract-tests` crate | | Current source | `packages/d2b-contract-tests/tests/minijail_audio_usbip.rs` audio section | | Reuse source | Same | -| Reuse action | `adapt` | +| Reuse action | adapt | | Destination | `packages/d2b-provider-audio-pipewire/tests/minijail_contract.rs` (provider-local); retain cross-bundle source greps in `d2b-contract-tests` | | Detailed design | Retain worker sandbox assertions and add role-sensitive attachment checks: an owner-Service worker may receive only the local AudioMediator attachment; a projection-Service worker receives only its same-Zone route Endpoint and can never receive a PipeWire FD. Binding-owned private Endpoint ownership/producerRef is explicit. All Service/Binding/Process/Endpoint serialized forms remain locator-free. | | Integration | Provider-local contract tests run in `d2b-provider-audio-pipewire`; retained cross-bundle greps in `d2b-contract-tests` ensure bundle-wide invariants still hold. | @@ -2878,7 +2878,7 @@ remote lease are released. | Dependency/owner | Depends on `ADR046-audio-006`, `ADR046-audio-007`, `ADR046-audio-011`; `d2b-telemetry` lightweight emitter | | Current source | `packages/d2bd/src/audio_dispatch.rs` audit call sites (redaction pattern reference) | | Reuse source | Same; adapt redaction pattern | -| Reuse action | `adapt` | +| Reuse action | adapt | | Destination | `packages/d2b-provider-audio-pipewire/src/telemetry.rs` | | Detailed design | Emit closed-label Service and Binding metrics plus post-commit audit. Service events distinguish only `owner\|projection` and closed outcomes; microphone arbitration emits closed transition/outcome plus bounded aggregate counts only. Metrics expose active count `0|1`, aggregate queue depth, and closed arbitration state without Zone/Binding/handle/position labels. Events omit authority keys, import/export keys, remote identity, stream/session ids, endpoints, client timestamps, and queue entries. Enforcement metrics cover owner-local and projection-routed calls without exposing route identity. ProcessEffect audit remains Process Provider-owned. | | Integration | Audio controller and mediator call telemetry/audit emitters after commit or enforcement; d2b-telemetry exporter and policy_observability consume the resulting records. | @@ -2894,9 +2894,9 @@ remote lease are released. | Dependency/owner | Depends on `ADR046-audio-004`; ComponentSession service contract; libpipewire 1.x; system-systemd Process Provider for guest domain | | Current source | `packages/d2b-guestd/src/audio_set.rs` (guestd wpctl dispatch — reference only) | | Reuse source | `packages/d2bd/src/audio_host_controller.rs` libpipewire enforcement patterns (reference only) | -| Reuse action | `ADR-only` (new component; supersedes guestd wpctl dispatch path) | +| Reuse action | adapt | | Destination | `packages/d2b-provider-audio-pipewire/src/guest_agent/mod.rs`; `src/guest_agent/enforcement.rs`; `src/bin/audio_pipewire_guest_agent.rs` | -| Detailed design | Long-lived user-domain Process running in the Guest under the guest workload user's UID. One Process resource per entry in `AudioBinding.spec.guestUsers`; each named by opaque UID digest (`ag-`) and carrying label `audio-pipewire.d2bus.org/role: guest-audio-agent`. `userRef` is the corresponding `User/` Zone resource. Opens a PipeWire connection in the Guest's compositor session (same-UID, natural access). Exposes a typed `AudioSet` ComponentSession service through an owned `Endpoint/ag--audio-set` (vsock transport, Guest→Zone d2b-bus). `AudioSet(mic, speaker, speakerLevel, micGain)` applies changes via libpipewire API (`pw_node_set_param` with `SPA_PARAM_Props`, `pw_stream_set_control`) on the guest virtio-snd PipeWire node. No wpctl binary, no command path, no EphemeralProcess. Controller calls ALL active GuestAudioAgent instances in parallel for each grant change and aggregates failures. `FakeGuestAudioAgent` is a test double behind `#[cfg(test)]`. | +| Detailed design | Long-lived user-domain Process running in the Guest under the guest workload user's UID. One Process resource per entry in `AudioBinding.spec.guestUsers`; each named by opaque UID digest (`ag-`) and carrying label `audio-pipewire.d2bus.org/role: guest-audio-agent`. `userRef` is the corresponding `User/` Zone resource. Opens a PipeWire connection in the Guest's compositor session (same-UID, natural access). Exposes a typed `AudioSet` ComponentSession service through an owned `Endpoint/ag--audio-set` (vsock transport, Guest→Zone d2b-bus). `AudioSet(mic, speaker, speakerLevel, micGain)` applies changes via libpipewire API (`pw_node_set_param` with `SPA_PARAM_Props`, `pw_stream_set_control`) on the guest virtio-snd PipeWire node. No wpctl binary, no command path, no EphemeralProcess. Controller calls ALL active GuestAudioAgent instances in parallel for each grant change and aggregates failures. `FakeGuestAudioAgent` is a test double behind `#[cfg(test)]`. Primary reuse disposition: `adapt`. Preserved source-plan detail: `ADR-only` (new component; supersedes guestd wpctl dispatch path). | | Integration | Third binary in the `d2b-provider-audio-pipewire` package. Declared as GuestAudioAgent Process resources by the audio-binding-controller (one per guestUser; template: `guest-audio-agent`). System Process Provider (`Provider/system-systemd`) launches each inside the Guest under the respective guest workload user's UID. | | Data migration | No guest runtime state migration; GuestAudioAgent reconnects to guest PipeWire and applies current AudioBinding grants and levels on reconcile, replacing guestd wpctl dispatch. | | Validation | `tests/guest_agent.rs`: AudioSet service call → libpipewire apply; mute/route/level; session-unavailable path; reconnect state restore; no wpctl binary; no command path; N-agent creation (one per guestUser); parallel call and aggregated failure | @@ -2910,9 +2910,9 @@ remote lease are released. | Dependency/owner | ADR046-zone-control-019, ADR046-zone-control-020; audio Provider owner | | Current source | None — net-new ADR 0046 cross-Zone sharing (D096) | | Reuse source | audio authority/mediator service (this dossier); `packages/d2b-provider/src/share_adapter.rs` `ExportAdapter`/`ImportAdapter` traits | -| Reuse action | net-new (implement the signed audio export/import adapter) | +| Reuse action | adapt | | Destination | `packages/d2b-provider-audio-pipewire/src/share_adapter.rs` | -| Detailed design | Implement signed `Provider/audio-pipewire` adapters only for canonical `serviceType: audio.d2bus.org.AudioService` when `spec.providerRef` selects this Provider. ResourceExport carries that `serviceType` plus the signed `projectionSchemaFingerprint` and semantic `factoryFingerprint`; ResourceImport carries the matching `expectedServiceType`, `expectedProjectionSchemaFingerprint`, and `expectedFactoryFingerprint`. Export adapter admits only the owner Service. Its local authority Endpoint remains Service-owned and is never an Export field. Core creates/deletes the projection AudioService with `ownerRef: ResourceImport/`, `providerRef`, and semantic base/import fields but no `spec.provider`; routing derives from the signed local descriptor and ResourceImport record. The semantic factory fingerprint binds factory metadata plus projection-protocol version, never Provider/adapter identity, which the signed descriptor authenticates separately. The import adapter reconciles its semantic route and never creates AudioBinding or opens PipeWire. Per-Guest AudioBindings are ordinary consumer resources with same-Zone serviceRef. No provider-qualified type alias, FD/path/socket/remote Ref crosses a Zone. | +| Detailed design | Implement signed `Provider/audio-pipewire` adapters only for canonical `serviceType: audio.d2bus.org.AudioService` when `spec.providerRef` selects this Provider. ResourceExport carries that `serviceType` plus the signed `projectionSchemaFingerprint` and semantic `factoryFingerprint`; ResourceImport carries the matching `expectedServiceType`, `expectedProjectionSchemaFingerprint`, and `expectedFactoryFingerprint`. Export adapter admits only the owner Service. Its local authority Endpoint remains Service-owned and is never an Export field. Core creates/deletes the projection AudioService with `ownerRef: ResourceImport/`, `providerRef`, and semantic base/import fields but no `spec.provider`; routing derives from the signed local descriptor and ResourceImport record. The semantic factory fingerprint binds factory metadata plus projection-protocol version, never Provider/adapter identity, which the signed descriptor authenticates separately. The import adapter reconciles its semantic route and never creates AudioBinding or opens PipeWire. Per-Guest AudioBindings are ordinary consumer resources with same-Zone serviceRef. No provider-qualified type alias, FD/path/socket/remote Ref crosses a Zone. Primary reuse disposition: `adapt`. Preserved source-plan detail: net-new (implement the signed audio export/import adapter). | | Integration | Core export/import controller (ADR046-zone-control-019); local projection lifecycle (ADR046-zone-control-020); ComponentSession bounded encrypted named streams | | Data migration | None — full d2b 3.0 reset | | Validation | Fast hermetic `tests/share_adapter.rs`: exact neutral AudioService `serviceType`; exact projection-schema/factory fingerprint match; explicit rejection of obsolete `endpointRef`, `exportedType`, `baseSchemaFingerprint`, `expectedType`, `expectedBaseSchemaFingerprint`, and `projectionType`; reject every Export Endpoint field, provider-qualified alias, and AudioBinding export/projection; accept owner AudioService export; Core-only projection creation/deletion; exact ResourceImport -> projection AudioService ownerRef chain with no `spec.provider`; semantic factory fingerprint unchanged by Provider/adapter identity mutation while signed identity authentication remains exact; projection never opens PipeWire; reconnect/revocation/D091 propagation with fake streams. Only `integration/real_stream.rs` exercises a real encrypted named stream. | @@ -2926,9 +2926,9 @@ remote lease are released. | Dependency/owner | Depends on `ADR046-audio-001`, `ADR046-audio-004`, `ADR046-zone-control-019`; audio Provider owner | | Current source | `packages/d2bd/src/audio_host_controller.rs` (`HostAudioController` trait, `PipeWireHostController::{from_audio_node,find_audio_node}`, `QemuAudioController`, `FakeHostController`); `packages/d2b-core/src/audio_policy.rs` (`LevelPercent`, `AudioGrant`, `AudioPolicyState`) | | Reuse source | Same baseline controller/policy symbols | -| Reuse action | `adapt` — the host controller becomes the single authority service; no daemon `Mutex`/state-file wrapping | +| Reuse action | adapt | | Destination | `packages/d2b-provider-audio-pipewire/src/authority.rs` (speaker mixer + mic arbiter); `AuthorityDescriptor` on owner `AudioService` | -| Detailed design | Exactly one owner AudioService holds the real PipeWire connection and D097 AuthorityDescriptor. Projection Services and AudioBindings cannot carry it. Speaker streams remain multiplexed/mixed. Microphone capture has exactly one slot across owner and importing Zones. The arbiter keys requests by authenticated Zone plus route-scoped opaque consumer handle, permits one pending entry per handle, bounds pending entries to 16 per Zone and 64 total, uses owner-sequenced FIFO per Zone and round-robin across non-empty Zones, and ignores client timestamps/priority. Its 30-second active lease renews only while no other Zone waits; contended expiry mutes/disconnects, dequeues the next Zone, and atomically requeues a still-requesting old holder at its Zone FIFO tail. Off/delete/revoke/disconnect cancel idempotently. Queue entries are memory-only and restart rebuild fails closed with capture muted. Multiplexed capture and consent/approval/priority/concurrent-capture surfaces are rejected; a future spec must define a concrete consent authorization ResourceType and resource-API verb before concurrent capture exists. Core rejects duplicate owner Services before open and adopts by ownerProof. No new ProcessRole, broker path, or state file. | +| Detailed design | Exactly one owner AudioService holds the real PipeWire connection and D097 AuthorityDescriptor. Projection Services and AudioBindings cannot carry it. Speaker streams remain multiplexed/mixed. Microphone capture has exactly one slot across owner and importing Zones. The arbiter keys requests by authenticated Zone plus route-scoped opaque consumer handle, permits one pending entry per handle, bounds pending entries to 16 per Zone and 64 total, uses owner-sequenced FIFO per Zone and round-robin across non-empty Zones, and ignores client timestamps/priority. Its 30-second active lease renews only while no other Zone waits; contended expiry mutes/disconnects, dequeues the next Zone, and atomically requeues a still-requesting old holder at its Zone FIFO tail. Off/delete/revoke/disconnect cancel idempotently. Queue entries are memory-only and restart rebuild fails closed with capture muted. Multiplexed capture and consent/approval/priority/concurrent-capture surfaces are rejected; a future spec must define a concrete consent authorization ResourceType and resource-API verb before concurrent capture exists. Core rejects duplicate owner Services before open and adopts by ownerProof. No new ProcessRole, broker path, or state file. Primary reuse disposition: `adapt`. Preserved source-plan detail: `adapt` — the host controller becomes the single authority service; no daemon `Mutex`/state-file wrapping. | | Integration | Owner AudioService references `Endpoint/audio-pipewire-authority`; same-Zone AudioBindings and remote projection Services call it. Core authority index admits exactly one owner Service. | | Data migration | None — full d2b 3.0 reset; grants are authoritative in `AudioBinding.spec` (no state file). | | Validation | Fast hermetic `tests/authority.rs`: AuthorityDescriptor accepted only on owner AudioService; Binding/projection rejection; duplicate conflict; multiplexed speaker mix/quota; one active mic across local/imported Zones; per-Zone FIFO and cross-Zone round-robin; one-entry-per-handle, per-Zone/total bounds and `MicQueueFull`; idempotent cancellation; contended 30-second lease; mute-before-handoff/no overlap; restart-muted rebuild; multiplexed-capture and consent/approval/priority/concurrent-verb rejection; ownerProof adoption; D091 drain/recycle with fake clock/FakeHostController | @@ -2942,9 +2942,9 @@ remote lease are released. | Dependency/owner | Depends on `ADR046-audio-013`, `ADR046-zone-control-019`; audio Provider owner | | Current source | `packages/d2b-realm-core/src/stream.rs` (`StreamKind::{AudioPlayback,AudioCapture}` → `Capability::{AudioPlayback,AudioCapture}`, `StreamAuthz`, `StreamChannel` split-direction); `packages/d2b-realm-core/src/mux.rs` (credit-based flow); `packages/d2bd/src/guest_control_bridge.rs` (`audio_set_authenticated`/`audio_status_authenticated`, `GuestAudioSetRequest`/`GuestAudioStatus`) | | Reuse source | Same baseline stream/mux/bridge symbols | -| Reuse action | `adapt` — audio frames ride the existing ComponentSession named-stream credit machinery | +| Reuse action | adapt | | Destination | `packages/d2b-provider-audio-pipewire/src/streams.rs` | -| Detailed design | Per-import audio frames flow only over bounded encrypted named streams: one stream with two `StreamChannel`s for playback/capture split direction and a single `StreamAuthz` (a consumer never opens two authz contexts to split direction), credit-based backpressure (a sender spends only receiver-granted credit), per-import session generation, cancel, and deadline. `StreamKind::AudioPlayback`/`AudioCapture` require `Capability::AudioPlayback`/`AudioCapture`. Playback streams may run concurrently; the owner activates capture frames for only its single granted opaque consumer handle, and revocation/disconnect cancels that Zone's active/pending requests before route teardown. No PipeWire FD/socket crosses a Zone; intermediaries see ciphertext. Guest audio calls (`audio_set`/`audio_status`) are issued to all active guests concurrently and results aggregated. Volume/gain (`LevelPercent`), queue identity/position, and node identity are redacted from audit/OTEL/logs. | +| Detailed design | Per-import audio frames flow only over bounded encrypted named streams: one stream with two `StreamChannel`s for playback/capture split direction and a single `StreamAuthz` (a consumer never opens two authz contexts to split direction), credit-based backpressure (a sender spends only receiver-granted credit), per-import session generation, cancel, and deadline. `StreamKind::AudioPlayback`/`AudioCapture` require `Capability::AudioPlayback`/`AudioCapture`. Playback streams may run concurrently; the owner activates capture frames for only its single granted opaque consumer handle, and revocation/disconnect cancels that Zone's active/pending requests before route teardown. No PipeWire FD/socket crosses a Zone; intermediaries see ciphertext. Guest audio calls (`audio_set`/`audio_status`) are issued to all active guests concurrently and results aggregated. Volume/gain (`LevelPercent`), queue identity/position, and node identity are redacted from audit/OTEL/logs. Primary reuse disposition: `adapt`. Preserved source-plan detail: `adapt` — audio frames ride the existing ComponentSession named-stream credit machinery. | | Integration | Owner and projection AudioService adapters allocate per-import streams over ComponentSession; AudioBinding only consumes the same-Zone Service Ref; core routes encrypted records only. | | Data migration | None — full d2b 3.0 reset | | Validation | Fast hermetic `tests/streams.rs`: projection-Service ownerRef/import binding, split-direction single-authz stream, credits, generation isolation, cancel/deadline, concurrent playback, one active capture across imports, route loss cancels that Zone's active/pending capture, ciphertext-only intermediary, redaction. Only `integration/real_stream.rs` runs the slower real encrypted stream. | diff --git a/docs/specs/providers/ADR-046-provider-clipboard-wayland.md b/docs/specs/providers/ADR-046-provider-clipboard-wayland.md index 89d8d7a02..db84f938f 100644 --- a/docs/specs/providers/ADR-046-provider-clipboard-wayland.md +++ b/docs/specs/providers/ADR-046-provider-clipboard-wayland.md @@ -1271,7 +1271,7 @@ violated by any implementation: | --- | --- | | Dependency/owner | ADR-046-provider-model-and-packaging; Provider/clipboard-wayland crate owner | | Current source | None — net-new v3 work; no pre-ADR45 baseline equivalent for the provider crate skeleton | -| Reuse action | net-new | +| Reuse action | create | | Destination | packages/d2b-provider-clipboard-wayland/ with src, tests, integration, README.md, and binaries clipboard-controller, clipd-host, picker-session | | Detailed design | Create the provider crate skeleton, required source layout, three binaries, and README covering purpose, component map, local build instructions, test commands, and display-wayland fake dependency for integration tests. | | Integration | Workspace package manifest and Provider artifact catalog consume the crate; provider packaging registers component templates for core ProviderDeployment. | @@ -1295,9 +1295,9 @@ test commands, dependency on display-wayland fake in integration tests. | --- | --- | | Dependency/owner | ADR046-clipboard-001; clipd-host service owner | | Current source | packages/d2b-clipd/ algorithms and types listed in the Reuse from baseline section | -| Reuse action | port and adapt algorithms; replace direct compositor, picker subprocess, Unix bridge, SO_PEERCRED, bridge directory, and group ACL paths | +| Reuse action | adapt | | Destination | packages/d2b-provider-clipboard-wayland/src/clipd_host/ service binary modules such as service, display_client, bridge, picker_coord, policy, fd, audit, history | -| Detailed design | Adapt d2b-clipd into clipd-host: call RequestPickerSession over d2b.clipboard.picker-coord.v3, consume d2b.display.host-clipboard.v3 instead of WAYLAND_DISPLAY or NIRI_SOCKET, serve d2b.clipboard.bridge.v3 on Endpoint/clipboard-bridge, remove filesystem bridge and peer group ACL logic, and preserve MIME allowlist, FD safety, fail-closed audit, loop suppression, and LRU history algorithms. | +| Detailed design | Adapt d2b-clipd into clipd-host: call RequestPickerSession over d2b.clipboard.picker-coord.v3, consume d2b.display.host-clipboard.v3 instead of WAYLAND_DISPLAY or NIRI_SOCKET, serve d2b.clipboard.bridge.v3 on Endpoint/clipboard-bridge, remove filesystem bridge and peer group ACL logic, and preserve MIME allowlist, FD safety, fail-closed audit, loop suppression, and LRU history algorithms. Primary reuse disposition: `adapt`. Preserved source-plan detail: port and adapt algorithms; replace direct compositor, picker subprocess, Unix bridge, SO_PEERCRED, bridge directory, and group ACL paths. | | Integration | Core creates Process/clipd-host; display-wayland wayland-proxy consumes Endpoint/clipboard-bridge; clipd-host consumes display-wayland host-clipboard service and controller picker coordination service. | | Data migration | Full d2b 3.0 reset; clipboard history is bounded process memory and no v2 clipboard runtime state is imported | | Validation | make test-rust -p d2b-provider-clipboard-wayland plus unit coverage for MIME policy, FD safety, audit fail-closed queue, history bounds, lifecycle purge and suspension, no filesystem bridge, and no bytes in status or audit. | @@ -1329,9 +1329,9 @@ Conformance gates: `make test-rust -p d2b-provider-clipboard-wayland`. | --- | --- | | Dependency/owner | ADR046-clipboard-001; clipboard-controller owner | | Current source | None — net-new v3 controller; no pre-ADR45 baseline equivalent | -| Reuse action | net-new controller using existing resource API and ComponentSession contracts | +| Reuse action | create | | Destination | packages/d2b-provider-clipboard-wayland/src/controller/ and clipboard-controller binary | -| Detailed design | Implement Process/clipboard-controller as a system-domain system-minijail Process serving d2b.clipboard.picker-coord.v3, creating picker EphemeralProcesses from the signed picker-session template, observing picker status, relaying Guest lifecycle messages to clipd-host, creating clipboard RBAC Roles and RoleBindings, writing only bounded redacted operational observations through the optimistic status writer, and never owning or mounting Provider state Volumes. | +| Detailed design | Implement Process/clipboard-controller as a system-domain system-minijail Process serving d2b.clipboard.picker-coord.v3, creating picker EphemeralProcesses from the signed picker-session template, observing picker status, relaying Guest lifecycle messages to clipd-host, creating clipboard RBAC Roles and RoleBindings, writing only bounded redacted operational observations through the optimistic status writer, and never owning or mounting Provider state Volumes. Primary reuse disposition: `create`. Preserved source-plan detail: net-new controller using existing resource API and ComponentSession contracts. | | Integration | Core ProviderDeployment creates the controller Process; controller uses Zone resource API for EphemeralProcess and RBAC resources and ComponentSession to clipd-host for picker and purge or suspend coordination. | | Data migration | Full d2b 3.0 reset; no controller durable state import because ProviderStateSet is empty | | Validation | Controller unit tests for picker request validation, EphemeralProcess spec shape, terminal status callback, GuestStopped and GuestLocked handling, RBAC resources, status bounds, and empty ProviderStateSet. | @@ -1365,9 +1365,9 @@ Process. Responsibilities: | --- | --- | | Dependency/owner | ADR046-clipboard-001 and ADR046-clipboard-003; picker worker owner | | Current source | packages/d2b-clipd/src/picker.rs subprocess flow is replacement context only; picker-session worker is net-new | -| Reuse action | rewrite as EphemeralProcess worker | +| Reuse action | adapt | | Destination | packages/d2b-provider-clipboard-wayland/src/picker_session/ and picker-session binary | -| Detailed design | Implement picker-session as a user-domain worker EphemeralProcess with minimal environment, metadata over inherited ComponentSession named stream, restricted WAYLAND_SOCKET FD from display-wayland presentation portal, GTK4 closure-contained runtime, one Select or Cancel response, no clipboard FDs, no clipboard bytes, no compositor credentials, no socket paths, and typed PickerStartFailed on install or start failure instead of bypass. | +| Detailed design | Implement picker-session as a user-domain worker EphemeralProcess with minimal environment, metadata over inherited ComponentSession named stream, restricted WAYLAND_SOCKET FD from display-wayland presentation portal, GTK4 closure-contained runtime, one Select or Cancel response, no clipboard FDs, no clipboard bytes, no compositor credentials, no socket paths, and typed PickerStartFailed on install or start failure instead of bypass. Primary reuse disposition: `adapt`. Preserved source-plan detail: rewrite as EphemeralProcess worker. | | Integration | clipboard-controller creates picker EphemeralProcess per paste request; ProviderSupervisor pre-opens restricted Wayland FD; picker returns result to controller which notifies clipd-host. | | Data migration | Full d2b 3.0 reset; picker state is per-operation EphemeralProcess status only | | Validation | Contract test that picker cannot bind zwlr_data_control_manager_v1 plus unit tests for processClass worker, no FDs or payload in picker config, TTL defaults, response framing, and requirePickerForPaste false bypass semantics in clipd-host. | @@ -1407,9 +1407,9 @@ Contract test: picker binary must fail to bind `zwlr_data_control_manager_v1` | --- | --- | | Dependency/owner | ADR046-clipboard-001; ADR-046-componentsession-and-bus; clipboard service contract owner | | Current source | None — net-new clipboard-wayland ComponentSession service definitions; display-wayland client stubs are consumed from the display-wayland contract | -| Reuse action | net-new generation of service stubs and named-stream types | +| Reuse action | create | | Destination | packages/d2b-provider-clipboard-wayland service descriptors and generated Rust async ttrpc bindings, plus any shared contracts crate selected by ADR-046-componentsession-and-bus | -| Detailed design | Generate stubs for d2b.clipboard.bridge.v3, d2b.clipboard.picker-coord.v3, and d2b.clipboard.v3, consume display-wayland d2b.display.host-clipboard.v3 client stubs, reject service-name collisions, and declare attachment classes clipboard-transfer-fd, host-selection-transfer-fd, and host-selection-supply-fd in the signed descriptor for ComponentSession handshake validation. | +| Detailed design | Generate stubs for d2b.clipboard.bridge.v3, d2b.clipboard.picker-coord.v3, and d2b.clipboard.v3, consume display-wayland d2b.display.host-clipboard.v3 client stubs, reject service-name collisions, and declare attachment classes clipboard-transfer-fd, host-selection-transfer-fd, and host-selection-supply-fd in the signed descriptor for ComponentSession handshake validation. Primary reuse disposition: `create`. Preserved source-plan detail: net-new generation of service stubs and named-stream types. | | Integration | Service registry, Zone ComponentSession enrollment, clipd-host, clipboard-controller, display-wayland wayland-proxy, and CLI/operator clients all consume the generated bindings. | | Data migration | None — docs/tooling only; no runtime state | | Validation | Contract tests for wire format, service-name collision rejection, attachment class matching, and descriptor handshake validation. | @@ -1439,9 +1439,9 @@ the signed service descriptor and validated at ComponentSession handshake. | --- | --- | | Dependency/owner | ADR-046-nix-configuration; Provider/clipboard-wayland Nix owner | | Current source | nixos-modules/clipboard.nix and current d2b.clipboard.* options | -| Reuse action | replace option surface with Provider resource Nix module | +| Reuse action | replace | | Destination | nixos-modules/providers/clipboard-wayland.nix and d2b.artifacts.clipboard-wayland catalog entry | -| Detailed design | Implement Nix module emitting d2b.zones..resources. Provider resources with spec.artifactId and spec.config, validate hostExecutionRef, hostUserRef, displayWaylandRef, and pickerArtifactId, forbid spec.componentPlacements, spec.settings, and spec.status, and remove nixos-modules/clipboard.nix in the same landing sequence as the new module. | +| Detailed design | Implement Nix module emitting d2b.zones..resources. Provider resources with spec.artifactId and spec.config, validate hostExecutionRef, hostUserRef, displayWaylandRef, and pickerArtifactId, forbid spec.componentPlacements, spec.settings, and spec.status, and remove nixos-modules/clipboard.nix in the same landing sequence as the new module. Primary reuse disposition: `replace`. Preserved source-plan detail: replace option surface with Provider resource Nix module. | | Integration | Nix resource compiler emits Provider resource and artifact catalog data consumed by core configuration publication and ProviderDeployment. | | Data migration | Full d2b 3.0 reset; operators translate old d2b.clipboard.* options using the dossier mapping table | | Validation | Nix eval tests for resource shape, reference validation, null displayWaylandRef host-only mode, artifact catalog lookup, and absence of deprecated spec fields. | @@ -1467,7 +1467,7 @@ Implement the d2b Nix module for `Provider/clipboard-wayland`: | --- | --- | | Dependency/owner | ADR046-clipboard-003; clipboard RBAC owner | | Current source | None — net-new Zone RBAC resources for clipboard-wayland; no pre-ADR45 baseline equivalent | -| Reuse action | net-new | +| Reuse action | create | | Destination | packages/d2b-provider-clipboard-wayland/src/controller/rbac.rs or equivalent controller reconcile module | | Detailed design | Controller creates Role/clipboard-admin, Role/clipboard-viewer, Role/clipboard-bridge-peer, Role/clipboard-picker-worker and RoleBindings display-wayland-bridge, host-admin-clipboard, picker-session-worker, all Zone-scoped, owned by Process/clipboard-controller, selector-bound for Process/picker-*, and cleaned up when Provider is deleted. | | Integration | Resource API stores RBAC resources; ComponentSession authorization checks consume Roles and RoleBindings for management, bridge, and picker worker services. | @@ -1494,9 +1494,9 @@ deleted. | --- | --- | | Dependency/owner | ADR046-clipboard-002; ADR-046-telemetry-audit-and-support; clipboard observability owner | | Current source | packages/d2b-clipd/src/audit.rs and policy types from packages/d2b-clipd/src/policy.rs | -| Reuse action | port and adapt audit plus metrics with zone names and redaction changes | +| Reuse action | adapt | | Destination | packages/d2b-provider-clipboard-wayland/src/service/audit.rs and packages/d2b-provider-clipboard-wayland/src/service/metrics.rs | -| Detailed design | Implement ClipboardAuditEvent and fail-closed Zone audit queue by porting baseline audit code, renaming realm fields to source_zone_id and dest_zone_id, making ReasonCode a closed enum with unknown protobuf fields rejected, replacing exact byte counts with SizeBucket, emitting to d2b.audit.v3, and adding closed-label OTEL metrics and spans from the dossier tables. | +| Detailed design | Implement ClipboardAuditEvent and fail-closed Zone audit queue by porting baseline audit code, renaming realm fields to source_zone_id and dest_zone_id, making ReasonCode a closed enum with unknown protobuf fields rejected, replacing exact byte counts with SizeBucket, emitting to d2b.audit.v3, and adding closed-label OTEL metrics and spans from the dossier tables. Primary reuse disposition: `adapt`. Preserved source-plan detail: port and adapt audit plus metrics with zone names and redaction changes. | | Integration | clipd-host emits audit events to the Zone audit sink and OTEL metrics/spans to the observability Provider pipeline during clipboard operations. | | Data migration | Full d2b 3.0 reset; audit stream is v3 Zone-local and no v2 audit records are imported | | Validation | Audit tests for no bytes in events, closed ReasonCode deserialization, fail-closed queue rejection, SizeBucket discretization, allowed metric labels, and excluded span attributes. | @@ -1522,9 +1522,9 @@ Implement `ClipboardAuditEvent` and fail-closed queue in `service/audit.rs`: | --- | --- | | Dependency/owner | ADR046-clipboard-001 through ADR046-clipboard-008; clipboard test owner | | Current source | packages/d2b-clipd/ behavior and this dossier invariants; no single baseline test path is declared for every assertion | -| Reuse action | extract semantic assertions into hermetic provider tests | +| Reuse action | extract | | Destination | packages/d2b-provider-clipboard-wayland/tests/ | -| Detailed design | Create hermetic unit and Cargo integration tests covering closed MIME policy, secret-hint suppression, FD validation and bounds, LRU and TTL history, fail-closed audit, lifecycle purge and suspend, picker EphemeralProcess invariants, no filesystem bridge, core-created Processes, empty ProviderStateSet, no state mounts or state-layout principals, status-first observation, and no clipboard bytes in status, audit, metrics, Operations, or Volumes. | +| Detailed design | Create hermetic unit and Cargo integration tests covering closed MIME policy, secret-hint suppression, FD validation and bounds, LRU and TTL history, fail-closed audit, lifecycle purge and suspend, picker EphemeralProcess invariants, no filesystem bridge, core-created Processes, empty ProviderStateSet, no state mounts or state-layout principals, status-first observation, and no clipboard bytes in status, audit, metrics, Operations, or Volumes. Primary reuse disposition: `extract`. Preserved source-plan detail: extract semantic assertions into hermetic provider tests. | | Integration | cargo test -p d2b-provider-clipboard-wayland --lib --tests consumes the provider crate and fake clocks/effect ports without live Wayland, systemd, broker, or Nix eval. | | Data migration | None — docs/tooling only; no runtime state | | Validation | All tests listed in the Required test coverage table must pass under make test-rust -p d2b-provider-clipboard-wayland. | @@ -1566,9 +1566,9 @@ Required test coverage (in `packages/d2b-provider-clipboard-wayland/tests/`): | --- | --- | | Dependency/owner | ADR046-clipboard-009; display-wayland fake and clipboard integration owner | | Current source | None — net-new v3 provider integration scenarios; no pre-ADR45 baseline equivalent | -| Reuse action | net-new integration suite with fake display-wayland and fake wayland-proxy | +| Reuse action | create | | Destination | packages/d2b-provider-clipboard-wayland/integration/ | -| Detailed design | Implement e2e paste, host capture, bridge backpressure, rate limiting, echo suppression, dependency absent host-only mode, GuestDestroyed purge, audit fail-closed, picker start timeout, and cross-zone denied scenarios using fake d2b.display.host-clipboard.v3 server and fake wayland-proxy bridge client without requiring a live compositor. | +| Detailed design | Implement e2e paste, host capture, bridge backpressure, rate limiting, echo suppression, dependency absent host-only mode, GuestDestroyed purge, audit fail-closed, picker start timeout, and cross-zone denied scenarios using fake d2b.display.host-clipboard.v3 server and fake wayland-proxy bridge client without requiring a live compositor. Primary reuse disposition: `create`. Preserved source-plan detail: net-new integration suite with fake display-wayland and fake wayland-proxy. | | Integration | Provider integration lane exercises clipd-host, clipboard-controller, generated ComponentSession services, fake display-wayland service, and fake bridge client end-to-end. | | Data migration | None — docs/tooling only; no runtime state | | Validation | All integration scenarios in the dossier table pass and assert no live Wayland compositor dependency. | @@ -1600,9 +1600,9 @@ fake wayland-proxy bridge client. They do not require a live Wayland compositor. | --- | --- | | Dependency/owner | ADR046-clipboard-005; packages/d2b-contract-tests owner | | Current source | packages/d2b-contract-tests/tests/policy_clipboard.rs | -| Reuse action | adapt contract tests and delete obsolete filesystem bridge assumptions | +| Reuse action | adapt | | Destination | packages/d2b-contract-tests/tests/policy_clipboard.rs | -| Detailed design | Add contract tests for d2b.clipboard.bridge.v3 and d2b.clipboard.picker-coord.v3 wire formats, ReasonCode numeric stability, and attachment class descriptor names while removing tests that assume shared filesystem bridge paths or SO_PEERCRED config. | +| Detailed design | Add contract tests for d2b.clipboard.bridge.v3 and d2b.clipboard.picker-coord.v3 wire formats, ReasonCode numeric stability, and attachment class descriptor names while removing tests that assume shared filesystem bridge paths or SO_PEERCRED config. Primary reuse disposition: `adapt`. Preserved source-plan detail: adapt contract tests and delete obsolete filesystem bridge assumptions. | | Integration | Contract test suite consumes generated service descriptors and guards downstream ComponentSession consumers. | | Data migration | None — docs/tooling only; no runtime state | | Validation | packages/d2b-contract-tests policy_clipboard.rs passes with v3 wire format and attachment descriptor assertions. | @@ -1625,9 +1625,9 @@ Update `packages/d2b-contract-tests/tests/policy_clipboard.rs`: | --- | --- | | Dependency/owner | ADR046-clipboard-006; Nix/module removal owner | | Current source | nixos-modules/clipboard.nix and its import in nixos-modules/default.nix | -| Reuse action | delete superseded module and update migration docs | +| Reuse action | delete-after-cutover | | Destination | nixos-modules/default.nix, docs/how-to/ migration guide, tests/static.sh example iteration expectations, CHANGELOG.md | -| Detailed design | After the new Provider Nix module validates, delete nixos-modules/clipboard.nix, remove its default.nix import, update migration guide with the option mapping table, ensure tests/static.sh examples no longer rely on removed paths, and add an Unreleased changelog entry. | +| Detailed design | After the new Provider Nix module validates, delete nixos-modules/clipboard.nix, remove its default.nix import, update migration guide with the option mapping table, ensure tests/static.sh examples no longer rely on removed paths, and add an Unreleased changelog entry. Primary reuse disposition: `delete-after-cutover`. Preserved source-plan detail: delete superseded module and update migration docs. | | Integration | Nix module aggregator, docs migration flow, static example iteration, and changelog all reflect Provider/clipboard-wayland as the only clipboard surface. | | Data migration | Full d2b 3.0 reset; no v2 clipboard runtime state or option import | | Validation | New module eval tests pass and tests/static.sh example iteration has no references to removed option paths. | diff --git a/docs/specs/providers/ADR-046-provider-credential-entra.md b/docs/specs/providers/ADR-046-provider-credential-entra.md index edc506ab4..7c31e676b 100644 --- a/docs/specs/providers/ADR-046-provider-credential-entra.md +++ b/docs/specs/providers/ADR-046-provider-credential-entra.md @@ -553,9 +553,9 @@ DBus names, tokens, refresh tokens, or cookies. | --- | --- | | Dependency/owner | `ADR046-credential-001` (Credential base fields and status), `ADR046-credential-002` (`d2b.credential.v3` service), D092 Endpoint, D093 Entrablau identity Guest decision, credential-entra owner | | Current source | `d2b-realm-provider/src/credential.rs:OpaqueAzureRef` remains a bounded opaque identifier reuse source only; no Host login/token implementation is retained | -| Reuse action | Adapt tests/types where non-secret; replace production token acquisition with Entrablau Endpoint client | +| Reuse action | adapt | | Destination | `packages/d2b-provider-credential-entra/src/{lib.rs,controller.rs,service.rs,controller_main.rs,agent_main.rs,audit.rs,telemetry.rs}` and corresponding tests/integration docs | -| Detailed design | Implement secret-free controller/helper; require Credential base `identityGuestRef` and `loginEndpointRef`; resolve dependency alias `entra-login-token`; validate same-Zone Guest placement and exact `consumerRef`; implement typed `EntraTokenLeaseClient` whose production implementation is the Entrablau Endpoint; implement `BeginLogin`/`ObserveLogin`/`CancelLogin` projection to Credential base interaction status; route access-token leases end-to-end from Entrablau service to exact consumer over Noise_KK; keep all refresh/login/TPM state inside the Entrablau Guest; declare no Provider state Volume; reject Host placement and all ambient fallback chains. | +| Detailed design | Implement secret-free controller/helper; require Credential base `identityGuestRef` and `loginEndpointRef`; resolve dependency alias `entra-login-token`; validate same-Zone Guest placement and exact `consumerRef`; implement typed `EntraTokenLeaseClient` whose production implementation is the Entrablau Endpoint; implement `BeginLogin`/`ObserveLogin`/`CancelLogin` projection to Credential base interaction status; route access-token leases end-to-end from Entrablau service to exact consumer over Noise_KK; keep all refresh/login/TPM state inside the Entrablau Guest; declare no Provider state Volume; reject Host placement and all ambient fallback chains. Primary reuse disposition: `adapt`. Preserved source-plan detail: Adapt tests/types where non-secret; replace production token acquisition with Entrablau Endpoint client. | | Integration | Consumer composes `inputs.entrablau.nixosModules.default` into the identity Guest; sibling package declares login/token Process and Endpoint; d2b Credential resource binds `identityGuestRef`, `loginEndpointRef`, and `consumerRef` | | Data migration | Full v3 reset of d2b Credential metadata; Entrablau Guest state is preserved unless explicitly destroyed by the sibling-owned reset flow | | Validation | See §13 | diff --git a/docs/specs/providers/ADR-046-provider-credential-managed-identity.md b/docs/specs/providers/ADR-046-provider-credential-managed-identity.md index 9b9f479e9..74b54ff3b 100644 --- a/docs/specs/providers/ADR-046-provider-credential-managed-identity.md +++ b/docs/specs/providers/ADR-046-provider-credential-managed-identity.md @@ -1764,15 +1764,15 @@ endpoint-role, and provider-factory code is explicitly excluded. ## Work items -### ADR046-mi-topology-001 (agent process split) +### ADR046-cred-mi-005 (agent process split) | Field | Value | | --- | --- | -| Dependency/owner | ADR046-credential-001 and ADR046-credential-002; ADR046-cred-mi-001; ADR046-cred-mi-002; owner: credential-managed-identity controller/agent topology | +| Dependency/owner | ADR046-credential-001 and ADR046-credential-002; owner: credential-managed-identity controller/agent topology. This topology contract is consumed by the later managed-identity controller work. | | Current source | `packages/d2b-realm-provider/src/provider.rs:CredentialProvider` status/enrollment-only trait; main `a1cc0b2d` managed-identity Provider implementation and tests listed in §Source reuse | -| Reuse action | copy and adapt the main managed-identity Provider; replace v2 provider registry/session assumptions with v3 controller/agent Process topology and `d2b.credential.v3` service | +| Reuse action | adapt | | Destination | packages/d2b-provider-credential-managed-identity/src/{controller.rs,agent.rs}; packages/d2b-provider-credential-managed-identity/{controller/main.rs,agent/main.rs}; packages/d2b-provider-credential-managed-identity/tests/topology.rs | -| Detailed design | Implement the controller/agent process split: separate `d2b-managed-identity-controller` binary with no IMDS client and no KK delivery, and `d2b-managed-identity-agent` binary with injected IMDS client via effect port and KK delivery. Controller manages Credential resources, spawns/monitors agent Processes, uses canonical Process templates, attaches LaunchTickets projecting `imdsEndpointAlias` and `credentialRef`, monitors agent Process health with bounded backoff, performs Deleted-phase cleanup without emitting Deleted closure audit, and applies D087 status-first state with no Provider state Volume. Agent validates `ExactSdkConsumer` via `AuthenticatedSubjectContext`, serves token-delivery methods, terminates Noise_KK delivery sessions, reports lease state, declares no direct IMDS egress, and keeps token bytes transient. | +| Detailed design | Implement the controller/agent process split: separate `d2b-managed-identity-controller` binary with no IMDS client and no KK delivery, and `d2b-managed-identity-agent` binary with injected IMDS client via effect port and KK delivery. Controller manages Credential resources, spawns/monitors agent Processes, uses canonical Process templates, attaches LaunchTickets projecting `imdsEndpointAlias` and `credentialRef`, monitors agent Process health with bounded backoff, performs Deleted-phase cleanup without emitting Deleted closure audit, and applies D087 status-first state with no Provider state Volume. Agent validates `ExactSdkConsumer` via `AuthenticatedSubjectContext`, serves token-delivery methods, terminates Noise_KK delivery sessions, reports lease state, declares no direct IMDS egress, and keeps token bytes transient. Primary reuse disposition: `adapt`. Preserved source-plan detail: copy and adapt the main managed-identity Provider; replace v2 provider registry/session assumptions with v3 controller/agent Process topology and `d2b.credential.v3` service. | | Integration | ProviderDeployment starts the controller Process; controller reconciles Credential resources and creates agent Process/Endpoint resources at the declared executionRef; d2b-bus routes `d2b.credential.v3` calls to the agent; co-located runtime Provider injects the IMDS client through the LaunchTicket/effect port; core aggregates Provider status and audit subsystem appends deletion records. | | Data migration | Full d2b 3.0 reset; no v2 managed-identity process/session state import | | Validation | `tests/topology.rs`; `integration/host-guest-placement.nix`; `make test-rust`; `make test-integration`; `make test-host-integration` | @@ -1783,12 +1783,12 @@ endpoint-role, and provider-factory code is explicitly excluded. | Field | Value | | --- | --- | | Work item ID | `ADR046-cred-mi-001` | -| Dependency/owner | `ADR046-credential-001` (contracts), `ADR046-credential-002` (service); `ADR046-reconcile-001`; `ADR046-mi-topology-001`; `credential-managed-identity` crate owner | +| Dependency/owner | `ADR046-credential-001` (contracts), `ADR046-credential-002` (service); `ADR046-reconcile-001`; `ADR046-cred-mi-005`; `credential-managed-identity` crate owner | | Current source | `packages/d2b-realm-provider/src/credential.rs:ManagedIdentityRef` (reachable); `packages/d2b-provider-aca/src/lib.rs:managed_identity_client_id` line 112 (reachable ACA config); `packages/d2bd/src/lib.rs:managed_identity_client_id` lines 3960, 4173 (reachable) | | Reuse source | main `a1cc0b2d`: `packages/d2b-provider-credential-managed-identity/src/lib.rs` (full implementation); `src/tests.rs` (full test suite) | -| Reuse action | copy and adapt | +| Reuse action | adapt | | Destination | `packages/d2b-provider-credential-managed-identity/src/{lib.rs, controller.rs, agent.rs, service.rs, audit.rs, telemetry.rs}`; `packages/d2b-provider-credential-managed-identity/{controller/main.rs, agent/main.rs}`; `packages/d2b-provider-credential-managed-identity/tests/{lifecycle.rs, conformance.rs, faults.rs, canary.rs, delivery.rs, placement.rs, topology.rs}`; `packages/d2b-provider-credential-managed-identity/integration/{container-service.sh, host-guest-placement.nix, aca-credential-ref.sh, cleanup-rollback.sh}`; `packages/d2b-provider-credential-managed-identity/README.md` | -| Detailed design | (1) Adapt `ManagedIdentityCredentialProvider` to `d2b.credential.v3` service interface; split controller and agent roles (see `ADR046-mi-topology-001`). (2) Enforce `ManagedIdentityCredentialOwner::ExactSdkConsumer` in agent via `AuthenticatedSubjectContext` from ComponentSession, independently of scope fields. (3) Reject `user-agent` placement: `scope.domainFilter=user` returns `credential-placement-mismatch` before agent spawn. (4) Validate `clientId` using `OpaqueAzureRef::parse` from v3 baseline; artifact IDs match `^[a-z][a-z0-9-]*$`. (5) Validate `imdsEndpointAlias` against closed enum `{azure-imds, azure-imds-aca}`; project into LaunchTicket at spawn time (never into Process spec config or env); co-located runtime Provider constructs `ManagedIdentityCredentialClient` from LaunchTicket projection and supplies via effect port; resolved URL never in any output surface. (6) Agent Process declares `networkUsage.allowEgress=false`; uses canonical Process template shape (see `ADR046-mi-topology-001` design item 6). (7) Reject `sign-challenge` with `credential-schema-invalid` immediately. (8) Map `ManagedIdentityClientState::Unavailable` to `credential-provider-unavailable`; no `InteractionRequired` state. (9) Implement `ManagedIdentityLeaseHandle` as opaque bounded newtype with redacted `Debug`. (10) All token bytes held by injected client in agent; delivered only via agent-terminated `Noise_KK` delivery session. (11) Integrate with Provider resource descriptor and controller toolkit. (12) Confirm `credential_canary` never appears in any service response, status field, delivery record outer header, or audit record. (13) Apply D087 status-first state: declare no Provider state Volume, keep ProviderStateSet empty, and write only bounded non-secret lease observation to `Credential.status` plus the Operation ledger. | +| Detailed design | (1) Adapt `ManagedIdentityCredentialProvider` to `d2b.credential.v3` service interface; split controller and agent roles (see `ADR046-cred-mi-005`). (2) Enforce `ManagedIdentityCredentialOwner::ExactSdkConsumer` in agent via `AuthenticatedSubjectContext` from ComponentSession, independently of scope fields. (3) Reject `user-agent` placement: `scope.domainFilter=user` returns `credential-placement-mismatch` before agent spawn. (4) Validate `clientId` using `OpaqueAzureRef::parse` from v3 baseline; artifact IDs match `^[a-z][a-z0-9-]*$`. (5) Validate `imdsEndpointAlias` against closed enum `{azure-imds, azure-imds-aca}`; project into LaunchTicket at spawn time (never into Process spec config or env); co-located runtime Provider constructs `ManagedIdentityCredentialClient` from LaunchTicket projection and supplies via effect port; resolved URL never in any output surface. (6) Agent Process declares `networkUsage.allowEgress=false`; uses canonical Process template shape (see `ADR046-cred-mi-005` design item 6). (7) Reject `sign-challenge` with `credential-schema-invalid` immediately. (8) Map `ManagedIdentityClientState::Unavailable` to `credential-provider-unavailable`; no `InteractionRequired` state. (9) Implement `ManagedIdentityLeaseHandle` as opaque bounded newtype with redacted `Debug`. (10) All token bytes held by injected client in agent; delivered only via agent-terminated `Noise_KK` delivery session. (11) Integrate with Provider resource descriptor and controller toolkit. (12) Confirm `credential_canary` never appears in any service response, status field, delivery record outer header, or audit record. (13) Apply D087 status-first state: declare no Provider state Volume, keep ProviderStateSet empty, and write only bounded non-secret lease observation to `Credential.status` plus the Operation ledger. Primary reuse disposition: `adapt`. Preserved source-plan detail: copy and adapt. | | Integration | Agent `Process` resource under `Host` or `Guest` executionRef; controller `Process` resource at Zone system host; d2b-bus routes `d2b.credential.v3` token-delivery calls to agent; Credential controller reconciles status; ACA `Provider/runtime-azure-container-apps` holds `credentialRef` pointing to a `credential-managed-identity`-backed Credential resource | | Data migration | Full v3 reset. `d2b-provider-aca:managed_identity_client_id` raw field migrated to a Credential resource reference in the v3 ACA Provider config; see removal precondition below. | | Validation | See §Test matrix | @@ -1798,11 +1798,11 @@ endpoint-role, and provider-factory code is explicitly excluded. | Field | Value | | --- | --- | -| Dependency/owner | ADR046-credential-001, ADR046-credential-002; ADR046-reconcile-001, ADR046-reconcile-002; ADR046-mi-topology-001; owner: Credential controller toolkit and managed-identity controller | +| Dependency/owner | ADR046-credential-001, ADR046-credential-002; ADR046-reconcile-001, ADR046-reconcile-002; ADR046-cred-mi-005; owner: Credential controller toolkit and managed-identity controller | | Current source | `packages/d2b-realm-provider/src/provider.rs:CredentialProvider` status/enrollment-only trait; main `a1cc0b2d` managed-identity controller/test behavior listed in §Source reuse | -| Reuse action | adapt shared Credential controller lifecycle to managed-identity controller/agent spawn and teardown | +| Reuse action | adapt | | Destination | packages/d2b-provider-credential-managed-identity/src/controller.rs; packages/d2b-contracts/src/v3/credential_controller.rs | -| Detailed design | Managed-identity-specific controller design: implement async reconcile and agent spawn/teardown from §Async reconcile; enforce system-only domain; spawn agent on Credential admission plus dependency-ready, not on `phase=Ready`; implement `observeInterval=30s` health-check RPC to the agent, which calls `InspectMetadata` on the injected client; controller never calls IMDS; derive idempotency key as `SHA-256(UID \|\| ":" \|\| rotationGeneration.to_le_bytes() \|\| ":" \|\| operation_class_byte)`; enforce `MAX_LOCAL_LEASES=256` in the resource store; implement Deleted-phase closure by clearing `provider-revoke` only after agent Process deletion and revocation confirmation while core/audit own Deleted revision and deletion record. | +| Detailed design | Managed-identity-specific controller design: implement async reconcile and agent spawn/teardown from §Async reconcile; enforce system-only domain; spawn agent on Credential admission plus dependency-ready, not on `phase=Ready`; implement `observeInterval=30s` health-check RPC to the agent, which calls `InspectMetadata` on the injected client; controller never calls IMDS; derive idempotency key as `SHA-256(UID \|\| ":" \|\| rotationGeneration.to_le_bytes() \|\| ":" \|\| operation_class_byte)`; enforce `MAX_LOCAL_LEASES=256` in the resource store; implement Deleted-phase closure by clearing `provider-revoke` only after agent Process deletion and revocation confirmation while core/audit own Deleted revision and deletion record. Primary reuse disposition: `adapt`. Preserved source-plan detail: adapt shared Credential controller lifecycle to managed-identity controller/agent spawn and teardown. | | Integration | Shared Credential controller contract produces reconcile events; managed-identity controller consumes them, manages agent Process resources, and writes Credential/agent status; generated controller contracts are consumed by all Credential Providers. | | Data migration | None — controller lifecycle code only; no runtime state import | | Validation | Managed-identity reconcile/controller tests; shared Credential reconciliation tests; topology tests validating agent spawn/teardown and Deleted-phase cleanup | @@ -1818,9 +1818,9 @@ item. | --- | --- | | Dependency/owner | Depends on ADR046-credential-001, ADR046-credential-002, and ADR046-cred-mi-001; owner: Nix resource compiler and activation cleanup | | Current source | `packages/d2b-provider-aca/src/lib.rs:managed_identity_client_id` and `packages/d2bd/src/lib.rs:managed_identity_client_id` are the current raw ACA managed-identity config surfaces; v3 Provider/Credential resource authoring is net-new | -| Reuse action | replace raw ACA managed identity client-id fields with Credential resource references and v3 Provider/Credential Nix emission | +| Reuse action | replace | | Destination | nixos-modules/options-resources.nix; nixos-modules/activation-nixos-cleanup.nix; integration/aca-credential-ref.sh | -| Detailed design | Shared Nix and cleanup: implement Nix eval-time assertions 1–12 from §Eval-time assertions, closed enum schema for `imdsEndpointAlias`, `clientId` validation via `OpaqueAzureRef` charset, generation cleanup contract, and artifact catalog validation for `credential-managed-identity-bin`. Integration fixture asserts that the migrated ACA Provider config carries `credentialRef: "Credential/aca-relay-mi"` and the raw `managed_identity_client_id` string field is absent from rendered Provider config JSON and ACA runtime bundle. | +| Detailed design | Shared Nix and cleanup: implement Nix eval-time assertions 1–12 from §Eval-time assertions, closed enum schema for `imdsEndpointAlias`, `clientId` validation via `OpaqueAzureRef` charset, generation cleanup contract, and artifact catalog validation for `credential-managed-identity-bin`. Integration fixture asserts that the migrated ACA Provider config carries `credentialRef: "Credential/aca-relay-mi"` and the raw `managed_identity_client_id` string field is absent from rendered Provider config JSON and ACA runtime bundle. Primary reuse disposition: `replace`. Preserved source-plan detail: replace raw ACA managed identity client-id fields with Credential resource references and v3 Provider/Credential Nix emission. | | Integration | Nix compiler emits Provider/Credential/ACA resource config; ResourceAPI admission validates it; activation cleanup deletes old generation resources through finalizers; ACA Provider consumes `credentialRef` to obtain tokens from the managed-identity Credential Provider. | | Data migration | Full d2b 3.0 reset; raw ACA `managed_identity_client_id` config is replaced by a newly authored Credential resource reference rather than imported in place | | Validation | Nix eval assertion tests; artifact catalog validation tests; `integration/aca-credential-ref.sh` | @@ -1830,11 +1830,11 @@ item. | Field | Value | | --- | --- | -| Dependency/owner | Depends on ADR046-cred-mi-001 and ADR046-mi-topology-001; owner: credential-managed-identity audit/telemetry implementation | +| Dependency/owner | Depends on ADR046-cred-mi-001 and ADR046-cred-mi-005; owner: credential-managed-identity audit/telemetry implementation | | Current source | `packages/d2b-realm-provider/src/error.rs:contains_sensitive_shape`; `packages/d2b-core/src/realm_workloads_launcher.rs:LauncherMetadataInvariants.no_secrets_or_credentials`; main `a1cc0b2d` managed-identity canary tests listed in §Source reuse | -| Reuse action | adapt existing sensitive-shape guard and canary pattern to v3 audit, OTEL, and metric surfaces | +| Reuse action | adapt | | Destination | packages/d2b-provider-credential-managed-identity/src/{audit.rs,telemetry.rs}; packages/d2b-contract-tests/tests/credential_audit.rs | -| Detailed design | Shared audit/OTEL: emit audit records for all methods and controller events per §Audit; emit OTEL spans and metrics per §OTEL and metrics; add `d2b_credential_imds_calls_total` counter with bounded `alias` label; enforce `contains_sensitive_shape` on all string fields in audit records and metric labels; add canary tests for `managed-identity-canary`, `credential_canary`, and `imds-endpoint-canary` in `canary.rs`. | +| Detailed design | Shared audit/OTEL: emit audit records for all methods and controller events per §Audit; emit OTEL spans and metrics per §OTEL and metrics; add `d2b_credential_imds_calls_total` counter with bounded `alias` label; enforce `contains_sensitive_shape` on all string fields in audit records and metric labels; add canary tests for `managed-identity-canary`, `credential_canary`, and `imds-endpoint-canary` in `canary.rs`. Primary reuse disposition: `adapt`. Preserved source-plan detail: adapt existing sensitive-shape guard and canary pattern to v3 audit, OTEL, and metric surfaces. | | Integration | Controller and agent service methods call audit/telemetry helpers; audit subsystem and OTEL exporters consume bounded redacted records; contract tests validate credential audit shape across providers. | | Data migration | None — audit/telemetry only; no runtime state import | | Validation | `packages/d2b-contract-tests/tests/credential_audit.rs`; managed-identity `canary.rs`; audit/OTEL unit tests for labels and sensitive-shape rejection | @@ -2091,7 +2091,7 @@ Old and new suites never run in parallel indefinitely. | Behavior retained | `OpaqueAzureRef` charset/length validation fail-closed on `clientId`; injected-client pattern keeps token material in the client process; zero-secret-bytes invariant at config/audit/log boundary; `ExactSdkConsumer` ownership model; `contains_sensitive_shape` guard | | Required delta | Full `ManagedIdentityCredentialProvider` implementation; split `managed-identity-controller` (no IMDS) + `managed-identity-agent` (IMDS client, KK delivery); `d2b.credential.v3` service dispatch split by role; agent spawn/teardown lifecycle in controller reconcile; `Noise_KK` delivery channel terminated by agent; Provider resource/descriptor; agent Process template; `imdsEndpointAlias` closed-alias config; `ExactSdkConsumer` via `AuthenticatedSubjectContext`; D087 status-first state model with no Provider state Volume, empty ProviderStateSet, status/Operation-ledger lease observation, and transient-only token bytes; Nix eval assertions; controller finalizer-release-only deletion path; Core-written event-only Deleted revision; audit subsystem deletion record with exactly-once dedup; audit/OTEL; `aca-credential-ref` migration | | Feasibility proof | Main `a1cc0b2d` proves the `ManagedIdentityCredentialProvider` implementation; its `FakeClient` test suite covers acquire/refresh/revoke/inspect, idempotency, unavailable state, canary enforcement, `ExactSdkConsumer` enforcement, and lease cardinality | -| Future owner | `ADR046-mi-topology-001` (process split), `ADR046-cred-mi-001` (primary), `ADR046-cred-mi-002`, `ADR046-cred-mi-003`, `ADR046-cred-mi-004` | +| Future owner | `ADR046-cred-mi-005` (process split), `ADR046-cred-mi-001` (primary), `ADR046-cred-mi-002`, `ADR046-cred-mi-003`, `ADR046-cred-mi-004` | --- diff --git a/docs/specs/providers/ADR-046-provider-credential-secret-service.md b/docs/specs/providers/ADR-046-provider-credential-secret-service.md index d1c6626ad..8cb9b92c6 100644 --- a/docs/specs/providers/ADR-046-provider-credential-secret-service.md +++ b/docs/specs/providers/ADR-046-provider-credential-secret-service.md @@ -1206,9 +1206,9 @@ All controller handlers are async. The reconcile loop follows | Dependency/owner | `ADR046-cred-ss-001` (contract types); `ADR046-cred-ss-002` (service proto); `ADR046-reconcile-001`; credential-secret-service owner | | Current source | `packages/d2b-realm-provider/src/provider.rs:CredentialProvider` (minimal v3 baseline) | | Reuse source | main `a1cc0b2d`: `packages/d2b-provider-credential-secret-service/src/lib.rs` (full implementation); `src/tests.rs` (full test suite including `FakeOo7Port`, lease lifecycle, locked state, canary enforcement, cardinality limits) | -| Reuse action | copy and adapt | +| Reuse action | adapt | | Destination | `packages/d2b-provider-credential-secret-service/src/{lib.rs, controller.rs, service.rs, main.rs}`; `packages/d2b-provider-credential-secret-service/tests/{lifecycle.rs, conformance.rs, faults.rs, canary.rs, delivery.rs, placement.rs}`; `packages/d2b-provider-credential-secret-service/integration/{container-service.sh, host-placement.nix, guest-placement.nix, cleanup-rollback.sh}`; `packages/d2b-provider-credential-secret-service/README.md` | -| Detailed design | Adapt `SecretServiceCredentialProvider` and `SecretServiceCredentialProviderFactory` to v3 `d2b.credential.v3` service; replace v2 `CredentialProvider` trait with v3 controller/service handler; retain `Oo7SecretServicePort` trait methods unchanged; ensure `SecretServiceOwner::Userd` placement guard rejects system-domain and guest-agent construction; validate `collectionAlias` against provider-internal charset (not `OpaqueAzureRef`; collection aliases may include spaces); integrate with Provider resource descriptor and controller toolkit; test that `credential_canary` never appears in any service response; create a Process resource per `(Zone, User, executionRef)` triple with `template = "secret-service-controller"` (plain string), canonical `sandbox` fields (`namespaceClasses`, `capabilityClasses`, `seccompClass`, `noNewPrivileges`, `startRoot`, `environmentClass`, `readOnlyRoot`), `budget` with nested `cpu`/`memory`/`pids`/`fds` sub-fields, `networkUsage: null`, no inline endpoint fields, an owned credential-service `Endpoint` resource, and `readiness.class = "provider-defined"`; component descriptor declares the required authenticated `dbus-session` FD attachment carried privately by the LaunchTicket; D087 status-first state model: no Provider state Volume is declared, ProviderStateSet is optional/query-time and empty, no Volume mount or layout principal is required, and the storage-need test is not met; bounded non-secret lease/acquisition/retry observation lives in `Credential.status` plus the core Operation ledger; any opaque status handle is non-secret, non-authorizing, bounded, safe for authorized status readers, and independently revalidated; no token/object-path/lease bytes persist anywhere; finalize() emits revoke outcome audit but MUST NOT emit the resource-deleted closure audit (audit subsystem only); controller writes only scoped Credential/Process health (core aggregates Provider status) | +| Detailed design | Adapt `SecretServiceCredentialProvider` and `SecretServiceCredentialProviderFactory` to v3 `d2b.credential.v3` service; replace v2 `CredentialProvider` trait with v3 controller/service handler; retain `Oo7SecretServicePort` trait methods unchanged; ensure `SecretServiceOwner::Userd` placement guard rejects system-domain and guest-agent construction; validate `collectionAlias` against provider-internal charset (not `OpaqueAzureRef`; collection aliases may include spaces); integrate with Provider resource descriptor and controller toolkit; test that `credential_canary` never appears in any service response; create a Process resource per `(Zone, User, executionRef)` triple with `template = "secret-service-controller"` (plain string), canonical `sandbox` fields (`namespaceClasses`, `capabilityClasses`, `seccompClass`, `noNewPrivileges`, `startRoot`, `environmentClass`, `readOnlyRoot`), `budget` with nested `cpu`/`memory`/`pids`/`fds` sub-fields, `networkUsage: null`, no inline endpoint fields, an owned credential-service `Endpoint` resource, and `readiness.class = "provider-defined"`; component descriptor declares the required authenticated `dbus-session` FD attachment carried privately by the LaunchTicket; D087 status-first state model: no Provider state Volume is declared, ProviderStateSet is optional/query-time and empty, no Volume mount or layout principal is required, and the storage-need test is not met; bounded non-secret lease/acquisition/retry observation lives in `Credential.status` plus the core Operation ledger; any opaque status handle is non-secret, non-authorizing, bounded, safe for authorized status readers, and independently revalidated; no token/object-path/lease bytes persist anywhere; finalize() emits revoke outcome audit but MUST NOT emit the resource-deleted closure audit (audit subsystem only); controller writes only scoped Credential/Process health (core aggregates Provider status) Primary reuse disposition: `adapt`. Preserved source-plan detail: copy and adapt. | | Integration | Target: user-domain `Process` resource under `Host` or `Guest` (ADR-only ResourceType); d2b-bus routes `d2b.credential.v3` calls to this process; Credential controller reconciles status. Current v3 has no user-credential host process: v3 `d2b-userd` is a guest exec stub (exits 78 in service mode; no credential functionality; `test-only-or-preview`). This integration path is fully new (ADR-only) work. | | Data migration | Full reset; no migration from old `CredentialProvider` trait | | Validation | See §16 | @@ -1220,9 +1220,9 @@ All controller handlers are async. The reconcile loop follows | --- | --- | | Dependency/owner | Dependency for ADR046-cred-ss-003; owner: `packages/d2b-contracts` Credential ResourceType contract | | Current source | `packages/d2b-realm-provider/src/provider.rs:CredentialProvider`; `packages/d2b-realm-provider/src/credential.rs` opaque credential refs and `OpaqueAzureRef` helpers | -| Reuse action | adapt current credential/status concepts into v3 ResourceType DTOs; reuse `OpaqueAzureRef` directly where applicable | +| Reuse action | adapt | | Destination | packages/d2b-contracts/src/v3/credential.rs | -| Detailed design | Contract types: define `CredentialSpec`, `CredentialStatus`, `CredentialLeaseHandle`, `OperationClass`, `PlacementBinding`, `CredentialConditionType`, and serde/validation/redaction helpers. Reuse `OpaqueAzureRef` from the v3 baseline. Full detail remains in `ADR-046-resources-credential` §Implementation work items. | +| Detailed design | Contract types: define `CredentialSpec`, `CredentialStatus`, `CredentialLeaseHandle`, `OperationClass`, `PlacementBinding`, `CredentialConditionType`, and serde/validation/redaction helpers. Reuse `OpaqueAzureRef` from the v3 baseline. Full detail remains in `ADR-046-resources-credential` §Implementation work items. Primary reuse disposition: `adapt`. Preserved source-plan detail: adapt current credential/status concepts into v3 ResourceType DTOs; reuse `OpaqueAzureRef` directly where applicable. | | Integration | Nix compiler emits these DTOs; ResourceAPI stores them; credential-secret-service controller/service consumes them; CLI and conformance tests validate base Credential spec/status behavior. | | Data migration | Full d2b 3.0 reset; no v2 CredentialProvider status/config import | | Validation | Credential ResourceType schema/serde/redaction tests from `ADR-046-resources-credential`; credential-secret-service conformance consumes the shared types | @@ -1241,9 +1241,9 @@ directly from v3 `d2b-realm-provider/src/credential.rs`. Full detail in | --- | --- | | Dependency/owner | Dependency for ADR046-cred-ss-003; owner: credential service contract/codegen | | Current source | None — net-new v3 `d2b.credential.v3` service; no pre-ADR45 baseline service proto equivalent | -| Reuse action | net-new service contract replacing the v2 in-process `CredentialProvider` trait | +| Reuse action | create | | Destination | packages/d2b-contracts/proto/v3/credential.proto; packages/d2b-credential-service/ | -| Detailed design | Service proto: define the `d2b.credential.v3` protobuf/ttrpc service and generate typed client/server code. Full detail remains in `ADR-046-resources-credential` §Implementation work items. | +| Detailed design | Service proto: define the `d2b.credential.v3` protobuf/ttrpc service and generate typed client/server code. Full detail remains in `ADR-046-resources-credential` §Implementation work items. Primary reuse disposition: `create`. Preserved source-plan detail: net-new service contract replacing the v2 in-process `CredentialProvider` trait. | | Integration | d2b-bus routes Credential service calls to credential-secret-service Process instances; generated client/server types bind the controller/service implementation to ComponentSession delivery. | | Data migration | Full d2b 3.0 reset; no v2 service state import | | Validation | Generated-code compile tests and credential service contract tests from `ADR-046-resources-credential`; credential-secret-service lifecycle/delivery tests consume the generated service | @@ -1260,9 +1260,9 @@ client/server in `packages/d2b-credential-service/`. Full detail in | --- | --- | | Dependency/owner | Dependency for ADR046-cred-ss-003; owner: common Credential controller/reconciliation toolkit | | Current source | ADR-only controller pattern from `ADR-046-resource-reconciliation`; no concrete secret-service baseline controller to import | -| Reuse action | net-new shared controller handler pattern specialized by each Credential Provider | +| Reuse action | create | | Destination | packages/d2b-provider-credential-/src/controller.rs | -| Detailed design | Controller toolkit: implement the common Credential controller handler conforming to the `ADR-046-resource-reconciliation` async loop. Secret-service-specific controller code plugs into this handler while keeping provider bytes out of status/store/audit. | +| Detailed design | Controller toolkit: implement the common Credential controller handler conforming to the `ADR-046-resource-reconciliation` async loop. Secret-service-specific controller code plugs into this handler while keeping provider bytes out of status/store/audit. Primary reuse disposition: `create`. Preserved source-plan detail: net-new shared controller handler pattern specialized by each Credential Provider. | | Integration | Resource watches and Operation ledger drive the controller loop; credential-secret-service handler uses the toolkit to reconcile Credential status, finalizers, Process health, and service lifecycle. | | Data migration | None — controller toolkit code only; no runtime state migration | | Validation | Shared reconciliation tests from `ADR-046-resources-credential`; credential-secret-service lifecycle/fault tests verify the handler integration | @@ -1279,9 +1279,9 @@ Implements the common Credential controller handler conforming to the | --- | --- | | Dependency/owner | Dependency for ADR046-cred-ss-003; owner: Nix resource compiler and activation cleanup | | Current source | None — net-new v3 `d2b.zones..resources.` Credential/Provider authoring surface; no pre-ADR45 baseline equivalent | -| Reuse action | net-new Nix resource emission and cleanup contract | +| Reuse action | create | | Destination | nixos-modules/options-resources.nix; nixos-modules/activation-nixos-cleanup.nix | -| Detailed design | Nix compiler: implement `d2b.zones..resources.` authoring, eval-time assertions, canonical JSON emission, artifact catalog, bundle digest, and generation cleanup contract. Full detail remains in `ADR-046-resources-credential` §Implementation work items. | +| Detailed design | Nix compiler: implement `d2b.zones..resources.` authoring, eval-time assertions, canonical JSON emission, artifact catalog, bundle digest, and generation cleanup contract. Full detail remains in `ADR-046-resources-credential` §Implementation work items. Primary reuse disposition: `create`. Preserved source-plan detail: net-new Nix resource emission and cleanup contract. | | Integration | Nix emits Provider/Credential resource JSON and artifact catalog entries; ResourceAPI admission and credential-secret-service controller consume the rendered resources; activation cleanup issues async Delete/finalizer flow on generation removal. | | Data migration | Full d2b 3.0 reset; no old credential config is imported into v3 resources | | Validation | Nix eval/assertion/golden tests from `ADR-046-resources-credential`; credential-secret-service cleanup rollback integration fixture | @@ -1299,9 +1299,9 @@ generation cleanup contract in `nixos-modules/options-resources.nix` and | --- | --- | | Dependency/owner | Dependency for ADR046-cred-ss-003; owner: credential-secret-service audit and telemetry implementation | | Current source | `packages/d2b-core/src/realm_workloads_launcher.rs:LauncherMetadataInvariants.no_secrets_or_credentials`; secret-service main reuse canary tests listed in §14 | -| Reuse action | adapt zero-secret invariant and canary test pattern to credential-secret-service audit/OTEL surfaces | +| Reuse action | adapt | | Destination | packages/d2b-provider-credential-secret-service/src/{audit.rs,telemetry.rs} | -| Detailed design | Audit/OTEL: emit audit records and OTEL spans/metrics for all credential service methods and controller events, with canary enforcement and no token/object-path/lease bytes in status, delivery outer headers, audit, metrics, spans, or logs. Full detail remains in `ADR-046-resources-credential` §Implementation work items. | +| Detailed design | Audit/OTEL: emit audit records and OTEL spans/metrics for all credential service methods and controller events, with canary enforcement and no token/object-path/lease bytes in status, delivery outer headers, audit, metrics, spans, or logs. Full detail remains in `ADR-046-resources-credential` §Implementation work items. Primary reuse disposition: `adapt`. Preserved source-plan detail: adapt zero-secret invariant and canary test pattern to credential-secret-service audit/OTEL surfaces. | | Integration | Controller and service methods call audit/telemetry helpers; audit subsystem and OTEL exporters consume bounded event/span/metric records; canary tests verify every public observable surface stays secret-free. | | Data migration | None — audit/telemetry only; no runtime state migration | | Validation | Credential audit/OTEL tests from `ADR-046-resources-credential`; `tests/canary.rs` and `tests/delivery.rs` for credential-secret-service | diff --git a/docs/specs/providers/ADR-046-provider-device-gpu.md b/docs/specs/providers/ADR-046-provider-device-gpu.md index d5714f822..e9ce2be1c 100644 --- a/docs/specs/providers/ADR-046-provider-device-gpu.md +++ b/docs/specs/providers/ADR-046-provider-device-gpu.md @@ -1625,47 +1625,47 @@ disposition contract test passes. ## Implementation work items -### ADR046-provider-device-gpu-01: Create `d2b-provider-device-gpu` crate scaffold +### ADR046-gpu-001: Create `d2b-provider-device-gpu` crate scaffold | Field | Value | | --- | --- | -| Work item ID | `ADR046-provider-device-gpu-01` | +| Work item ID | `ADR046-gpu-001` | | Dependency/owner | `ADR046-resources-device` accepted; `ADR046-provider-model-and-packaging` accepted; workspace root must add crate | | Current source | No v3 source; `packages/d2b-host/src/gpu_argv.rs` and `packages/d2b-host/src/video_argv.rs` (implemented-and-reachable) provide argv generators | | Reuse source | `packages/d2b-host/src/gpu_argv.rs` (baseline `b5ddbed`), `packages/d2b-host/src/video_argv.rs` (baseline `b5ddbed`) | -| Reuse action | `extract` both argv files into `d2b-provider-device-gpu/src/argv.rs` as re-exports; do not copy logic | +| Reuse action | extract | | Destination | `packages/d2b-provider-device-gpu/` with `src/`, `tests/`, `integration/`, `README.md`; add to workspace `Cargo.toml` members list (alphanumerically sorted) | -| Detailed design | Crate scaffold: `Cargo.toml` with `d2b-host`, `d2b-contracts`, `d2b-provider-toolkit`, `d2b-core` dependencies; `lib.rs` exporting controller binary entry points; `error.rs` with `DeviceGpuError` closed-set enum; placeholder `controller.rs` | +| Detailed design | Crate scaffold: `Cargo.toml` with `d2b-host`, `d2b-contracts`, `d2b-provider-toolkit`, `d2b-core` dependencies; `lib.rs` exporting controller binary entry points; `error.rs` with `DeviceGpuError` closed-set enum; placeholder `controller.rs` Primary reuse disposition: `extract`. Preserved source-plan detail: `extract` both argv files into `d2b-provider-device-gpu/src/argv.rs` as re-exports; do not copy logic. | | Integration | Workspace policy test must pass; crate must build; `src/`, `tests/`, `integration/`, `README.md` must exist | | Data migration | None — full d2b 3.0 reset; no prior state to migrate | | Validation | `cargo build -p d2b-provider-device-gpu`; workspace policy crate-layout check passes | | Removal proof | N/A (new crate) | -### ADR046-provider-device-gpu-02: Implement async reconcile controller +### ADR046-gpu-002: Implement async reconcile controller | Field | Value | | --- | --- | -| Work item ID | `ADR046-provider-device-gpu-02` | -| Dependency/owner | ADR046-provider-device-gpu-01; `ADR-046-resource-reconciliation` implementation present; Provider toolkit `ResourceClient` available | +| Work item ID | `ADR046-gpu-002` | +| Dependency/owner | ADR046-gpu-001; `ADR-046-resource-reconciliation` implementation present; Provider toolkit `ResourceClient` available | | Current source | `packages/d2bd/src/usbip_state_machine.rs` (implemented-and-reachable) as reconcile loop pattern reference. GPU/video reconcile state is `ADR-only`. | | Reuse source | Pattern only: `packages/d2bd/src/usbip_state_machine.rs` (baseline). No code copy. | -| Reuse action | `adapt` — implement the five-trigger reconcile loop using Provider toolkit async reconciler | +| Reuse action | adapt | | Destination | `packages/d2b-provider-device-gpu/src/controller.rs` | -| Detailed design | Five triggers: `spec-generation-changed`, `deletion-requested`, `dependency-changed`, `scheduled-observe`, `owned-resource-changed`. Each trigger handler writes optimistic `ResourceMutationBatch`. Status writer in `status.rs`. Async watch task + per-resource reconcile tasks. Independent resources in parallel. | +| Detailed design | Five triggers: `spec-generation-changed`, `deletion-requested`, `dependency-changed`, `scheduled-observe`, `owned-resource-changed`. Each trigger handler writes optimistic `ResourceMutationBatch`. Status writer in `status.rs`. Async watch task + per-resource reconcile tasks. Independent resources in parallel. Primary reuse disposition: `adapt`. Preserved source-plan detail: `adapt` — implement the five-trigger reconcile loop using Provider toolkit async reconciler. | | Integration | Resource API (ADR046 store) must be present; fake ResourceClient available from Provider toolkit; `tests/combined_reconcile.rs` validates trigger dispatch | | Data migration | None — full d2b 3.0 reset; no prior state to migrate | | Validation | `cargo test -p d2b-provider-device-gpu --test combined_reconcile`; all five trigger handlers must reach their expected output state | | Removal proof | Current ProcessRole::Gpu/Video/GpuRenderNode retained until this test passes; see ProcessRole disposition table | -### ADR046-provider-device-gpu-03: DRM sysfs probe and observe scheduler +### ADR046-gpu-003: DRM sysfs probe and observe scheduler | Field | Value | | --- | --- | -| Work item ID | `ADR046-provider-device-gpu-03` | -| Dependency/owner | ADR046-provider-device-gpu-02 | +| Work item ID | `ADR046-gpu-003` | +| Dependency/owner | ADR046-gpu-002 | | Current source | `nixos-modules/assertions.nix` x86_64-linux guard; `packages/d2b-core/src/processes.rs` ProcessRole::Gpu/GpuRenderNode; no existing sysfs probe module | | Reuse source | None; probe is `ADR-only` | -| Reuse action | `new` | +| Reuse action | create | | Destination | `packages/d2b-provider-device-gpu/src/probe.rs` | | Detailed design | Call `GpuEffectPort::probe_drm_device(selector)` on each `scheduled-observe` trigger; the effect port resolves device presence against the trusted device table and returns a presence/health result without exposing raw sysfs or device paths to the controller. Three-strike failure counter; `observe_interval_secs` (10–60, default 30); emit `DevicePresent` condition and update `lastProbedAt`. | | Integration | `scheduled-observe` trigger from reconcile loop calls `probe::check_drm_device` | @@ -1673,15 +1673,15 @@ disposition contract test passes. | Validation | `tests/conformance.rs` contains probe-mock path; `cargo test` passes | | Removal proof | N/A (new module) | -### ADR046-provider-device-gpu-04: Exclusive/shared arbitration enforcement +### ADR046-gpu-004: Exclusive/shared arbitration enforcement | Field | Value | | --- | --- | -| Work item ID | `ADR046-provider-device-gpu-04` | -| Dependency/owner | ADR046-provider-device-gpu-02 | +| Work item ID | `ADR046-gpu-004` | +| Dependency/owner | ADR046-gpu-002 | | Current source | `packages/d2b-core/src/bundle_resolver.rs` `validate_graphics_vm_invariants` (assertion guard) — `ADR-only` for resource-level arbitration | | Reuse source | None | -| Reuse action | `new` | +| Reuse action | create | | Destination | `packages/d2b-provider-device-gpu/src/arbitration.rs` | | Detailed design | On `spec-generation-changed` and each new claim: check `arbitration` vs `maxConcurrentClaims` vs current `holderRefs` length. Exclusive: reject any second claim with `ClaimConflict` condition, set requesting Device phase `Degraded`. Shared render-node: accept up to `maxConcurrentClaims`. Admission: `shared + renderNodeOnly=false` fails with `shared-arbitration-requires-render-node-only`. | | Integration | Tested by `tests/arbitration_conflict.rs`; integration fixture `render_node_shared/` | @@ -1689,80 +1689,80 @@ disposition contract test passes. | Validation | `cargo test -p d2b-provider-device-gpu --test arbitration_conflict`; `cargo test -p d2b-provider-device-gpu --test render_node_enforcement` | | Removal proof | N/A (new module) | -### ADR046-provider-device-gpu-05: GPU and render-node worker Process management +### ADR046-gpu-005: GPU and render-node worker Process management | Field | Value | | --- | --- | -| Work item ID | `ADR046-provider-device-gpu-05` | -| Dependency/owner | ADR046-provider-device-gpu-02; `ADR046-components-processes-and-sandbox` (Provider/system-minijail present and able to handle Process resources with `gpu-worker`/`render-node-worker` templates) | +| Work item ID | `ADR046-gpu-005` | +| Dependency/owner | ADR046-gpu-002; `ADR046-components-processes-and-sandbox` (Provider/system-minijail present and able to handle Process resources with `gpu-worker`/`render-node-worker` templates) | | Current source | `packages/d2b-host/src/gpu_argv.rs` (implemented-and-reachable); `packages/d2b-core/src/bundle_resolver.rs` lines 1888–1894 (device token set); `packages/d2b-core/src/processes.rs` `ProcessRole::Gpu`, `ProcessRole::GpuRenderNode` (implemented-and-reachable); `nixos-modules/minijail-profiles.nix` gpu/gpu-render-node profiles (implemented-and-reachable) | | Reuse source | `packages/d2b-host/src/gpu_argv.rs` (baseline `b5ddbed`): `GpuArgvInput`, `GpuParams`, `GpuContextType`, `GpuDisplayConfig`; `packages/d2b-core/src/bundle_resolver.rs` device token constant comment | -| Reuse action | `extract` argv builder logic into `argv.rs` as re-export from `d2b-host` (used by Provider/system-minijail at LaunchTicket resolution time; the signed component descriptor is static and is not rewritten per Device); `adapt` device allowlist token set from `bundle_resolver.rs` into `worker_gpu.rs` `GPU_DEVICE_ALLOWLIST` constant for `deviceUsage` population | +| Reuse action | adapt | | Destination | `packages/d2b-provider-device-gpu/src/worker_gpu.rs` | -| Detailed design | Build and commit `Process` resource record with `template: gpu-worker` or `template: render-node-worker`; set `sandbox.seccompClass` (`w1-gpu` or `w1-gpu-render-node`), `sandbox.userNamespace: {mappingClass: process-principal-root}` (uid/gid resolved privately by core from signed worker template — controller does NOT write numeric values), `sandbox.namespaceClasses`, `sandbox.capabilityClasses=[]`, `sandbox.startRoot=false`; set `deviceUsage[{deviceRef,access,purpose}]`, `networkUsage: null`, `endpoints[{name,transport,purpose}]`, `budget` (including `pids` and `fds` bounded limits), `readiness` (with `class`, `initialDelay`, `timeout`, `failureThreshold`, `successThreshold`), and `restartPolicy` (with `class`, `backoffBase`, `backoffMax`, `backoffMultiplier`, `maxRestarts`, `resetAfter`). Provider/system-minijail validates and resolves the LaunchTicket and sends effect requests via `MinijailProcessEffectPort`; the core EffectPort adapter routes them to the **privileged broker** which performs `SpawnRunner`, `OpenDevice`, `clone3`, `uid_map`/`gid_map` writes, and fd transfer — the device-gpu controller does not have execution authority or fd access. `crossDomainTrusted` gating: the signed descriptor is static; `crossDomainTrusted` is projected from the Device setting into the LaunchTicket by Provider/system-minijail, which omits `GpuContextType::CrossDomain` from runtime argv when false. | +| Detailed design | Build and commit `Process` resource record with `template: gpu-worker` or `template: render-node-worker`; set `sandbox.seccompClass` (`w1-gpu` or `w1-gpu-render-node`), `sandbox.userNamespace: {mappingClass: process-principal-root}` (uid/gid resolved privately by core from signed worker template — controller does NOT write numeric values), `sandbox.namespaceClasses`, `sandbox.capabilityClasses=[]`, `sandbox.startRoot=false`; set `deviceUsage[{deviceRef,access,purpose}]`, `networkUsage: null`, `endpoints[{name,transport,purpose}]`, `budget` (including `pids` and `fds` bounded limits), `readiness` (with `class`, `initialDelay`, `timeout`, `failureThreshold`, `successThreshold`), and `restartPolicy` (with `class`, `backoffBase`, `backoffMax`, `backoffMultiplier`, `maxRestarts`, `resetAfter`). Provider/system-minijail validates and resolves the LaunchTicket and sends effect requests via `MinijailProcessEffectPort`; the core EffectPort adapter routes them to the **privileged broker** which performs `SpawnRunner`, `OpenDevice`, `clone3`, `uid_map`/`gid_map` writes, and fd transfer — the device-gpu controller does not have execution authority or fd access. `crossDomainTrusted` gating: the signed descriptor is static; `crossDomainTrusted` is projected from the Device setting into the LaunchTicket by Provider/system-minijail, which omits `GpuContextType::CrossDomain` from runtime argv when false. Primary reuse disposition: `adapt`. Preserved source-plan detail: `extract` argv builder logic into `argv.rs` as re-export from `d2b-host` (used by Provider/system-minijail at LaunchTicket resolution time; the signed component descriptor is static and is not rewritten per Device); `adapt` device allowlist token set from `bundle_resolver.rs` into `worker_gpu.rs` `GPU_DEVICE_ALLOWLIST` constant for `deviceUsage` population. | | Integration | `integration/gpu_worker_start/`; `integration/render_node_shared/`; `packages/d2b-contract-tests/tests/minijail_gpu.rs` (reused existing test) | | Data migration | None — full d2b 3.0 reset; no prior state to migrate | | Validation | `cargo test -p d2b-provider-device-gpu`; `cargo test -p d2b-contract-tests --test minijail_gpu` continues to pass | | Removal proof | `ProcessRole::Gpu` and `ProcessRole::GpuRenderNode` removed from `processes.rs` only after both integration tests pass and the ProcessRole disposition contract test confirms zero remaining references | -### ADR046-provider-device-gpu-06: Video decoder Process management and wire-contract check +### ADR046-gpu-006: Video decoder Process management and wire-contract check | Field | Value | | --- | --- | -| Work item ID | `ADR046-provider-device-gpu-06` | -| Dependency/owner | ADR046-provider-device-gpu-05 (video depends on GPU Process being Ready) | +| Work item ID | `ADR046-gpu-006` | +| Dependency/owner | ADR046-gpu-005 (video depends on GPU Process being Ready) | | Current source | `packages/d2b-host/src/video_argv.rs` (implemented-and-reachable): `VideoArgvInput`, `VideoBackend`, `wire_contract_snapshot()`; `packages/d2b-contract-tests/tests/video_binary_contract.rs` (implemented-and-reachable); `packages/d2b-contract-tests/tests/minijail_swtpm_video.rs` video section (implemented-and-reachable); `nixos-modules/minijail-profiles.nix` video profile (implemented-and-reachable) | | Reuse source | `packages/d2b-host/src/video_argv.rs` (baseline `b5ddbed`): argv generator, wire-contract constants, `wire_contract_snapshot()` | -| Reuse action | `extract` argv generator (re-export from `argv.rs`); `copy-unchanged` wire-contract constants into `tests/wire_constant_snapshot.rs` golden comparison | +| Reuse action | adapt | | Destination | `packages/d2b-provider-device-gpu/src/worker_video.rs`, `tests/wire_constant_snapshot.rs` | -| Detailed design | Controller creates `Process/device--video` only after `GpuWorkerReady=True`. `worker_video.rs` builds `VideoArgvInput` from resolved device spec and signed descriptor binary path. Validates `wire_contract_snapshot()` matches committed golden at startup; fails closed if mismatch (error `device-wire-contract-mismatch`). NVIDIA device gating: include `nvidia-ctl`, `nvidia-device`, `nvidia-uvm` tokens in `deviceUsage[]` entries only when `videoNvidiaDecode=true`; the **privileged broker** opens the fds when executing the effect request from the core EffectPort adapter. Distinct allocator-assigned principal enforced by LaunchTicket (internal invariant; not expressed in the resource spec); `template: video-worker` descriptor declares no Wayland/audio endpoint capability. `sandbox.seccompClass: w1-video`; `sandbox.namespaceClasses` includes `pid`; `userNamespace: null` (explicit, tested invariant). | +| Detailed design | Controller creates `Process/device--video` only after `GpuWorkerReady=True`. `worker_video.rs` builds `VideoArgvInput` from resolved device spec and signed descriptor binary path. Validates `wire_contract_snapshot()` matches committed golden at startup; fails closed if mismatch (error `device-wire-contract-mismatch`). NVIDIA device gating: include `nvidia-ctl`, `nvidia-device`, `nvidia-uvm` tokens in `deviceUsage[]` entries only when `videoNvidiaDecode=true`; the **privileged broker** opens the fds when executing the effect request from the core EffectPort adapter. Distinct allocator-assigned principal enforced by LaunchTicket (internal invariant; not expressed in the resource spec); `template: video-worker` descriptor declares no Wayland/audio endpoint capability. `sandbox.seccompClass: w1-video`; `sandbox.namespaceClasses` includes `pid`; `userNamespace: null` (explicit, tested invariant). Primary reuse disposition: `adapt`. Preserved source-plan detail: `extract` argv generator (re-export from `argv.rs`); `copy-unchanged` wire-contract constants into `tests/wire_constant_snapshot.rs` golden comparison. | | Integration | `integration/video_dependency/`; `packages/d2b-contract-tests/tests/video_binary_contract.rs` (reused); `packages/d2b-contract-tests/tests/minijail_swtpm_video.rs` video section (reused) | | Data migration | None — full d2b 3.0 reset; no prior state to migrate | | Validation | `cargo test -p d2b-provider-device-gpu --test video_dependency`; `cargo test -p d2b-provider-device-gpu --test wire_constant_snapshot`; `cargo test -p d2b-contract-tests --test video_binary_contract` continues to pass | | Removal proof | `ProcessRole::Video` removed from `processes.rs` only after `integration/video_dependency/` passes and the video Process reaches `Ready` in a live Zone | -### ADR046-provider-device-gpu-07: Nix option migration and eval validation +### ADR046-gpu-007: Nix option migration and eval validation | Field | Value | | --- | --- | -| Work item ID | `ADR046-provider-device-gpu-07` | -| Dependency/owner | ADR046-provider-device-gpu-01; ADR 0046 Zone Nix emitter wired; `ADR-046-nix-configuration` Nix emitter present | +| Work item ID | `ADR046-gpu-007` | +| Dependency/owner | ADR046-gpu-001; ADR 0046 Zone Nix emitter wired; `ADR-046-nix-configuration` Nix emitter present | | Current source | `nixos-modules/options-realms-workloads.nix` `d2b.vms..graphics.*` options (generated-or-eval-contract); `nixos-modules/assertions.nix` graphics assertions; `nixos-modules/components/graphics.nix` (host-side); `nixos-modules/components/video/guest.nix` (guest-side) | | Reuse source | Settings schema field names/defaults/bounds from `nixos-modules/options-realms-workloads.nix` options documentation | -| Reuse action | `adapt` — map old `d2b.vms..graphics.*` fields to `d2b.zones..resources.` Device spec settings fields; add eval assertions | +| Reuse action | adapt | | Destination | `nixos-modules/assertions.nix` (new GPU Device eval assertions); `tests/unit/nix/cases/device-gpu-eval.nix` (new Nix eval case); committed settings schema `docs/reference/schemas/v3/providers/device-gpu.settings.json` | -| Detailed design | Eval assertions as documented in § Nix configuration / Eval-time assertions. Canonical JSON golden as documented. Settings schema drift gate via `make test-drift`. `d2b.vms..graphics.*` options are deprecated (emit deprecation warning) until a transition generation removes them; they are not removed in the same commit that adds the Device spec option. | +| Detailed design | Eval assertions as documented in § Nix configuration / Eval-time assertions. Canonical JSON golden as documented. Settings schema drift gate via `make test-drift`. `d2b.vms..graphics.*` options are deprecated (emit deprecation warning) until a transition generation removes them; they are not removed in the same commit that adds the Device spec option. Primary reuse disposition: `adapt`. Preserved source-plan detail: `adapt` — map old `d2b.vms..graphics.*` fields to `d2b.zones..resources.` Device spec settings fields; add eval assertions. | | Integration | `nix flake check`; `tests/unit/nix/cases/device-gpu-eval.nix`; `make test-drift` | | Data migration | Consumer config migration guide: replace `d2b.vms..graphics.enable = true` with a Device resource declaration. Old options emit deprecation warnings, not hard failures, during the transition window. | | Validation | `nix-unit tests/unit/nix/cases/device-gpu-eval.nix`; `make test-drift`; `make test-flake` | | Removal proof | `d2b.vms..graphics.*` options removed only after migration guide ships and the deprecation warning has been live for one minor release | -### ADR046-provider-device-gpu-08: Assert status-first Provider state +### ADR046-gpu-008: Assert status-first Provider state | Field | Value | | --- | --- | -| Work item ID | `ADR046-provider-device-gpu-08` | -| Dependency/owner | ADR046-provider-device-gpu-01; D087 status-first state model present in the foundational ADR-046 specs | +| Work item ID | `ADR046-gpu-008` | +| Dependency/owner | ADR046-gpu-001; D087 status-first state model present in the foundational ADR-046 specs | | Current source | None — net-new v3 work; no pre-ADR45 baseline equivalent | | Reuse source | None | -| Reuse action | `new` — status-first state assertions in the component descriptor and controller tests | +| Reuse action | create | | Destination | `packages/d2b-provider-device-gpu/` component descriptor; controller/status tests | -| Detailed design | Do **not** declare a controller Provider state Volume. The device-gpu component descriptor declares an empty ProviderStateSet; controller and worker Process templates contain no `/state` mount. Bounded non-secret operational state is published to Device/Provider status and the core Operation ledger. GPU has no Device-payload Volume; render-node access remains a Device attachment resolved by LaunchTicket and broker policy. | +| Detailed design | Do **not** declare a controller Provider state Volume. The device-gpu component descriptor declares an empty ProviderStateSet; controller and worker Process templates contain no `/state` mount. Bounded non-secret operational state is published to Device/Provider status and the core Operation ledger. GPU has no Device-payload Volume; render-node access remains a Device attachment resolved by LaunchTicket and broker policy. Primary reuse disposition: `create`. Preserved source-plan detail: `new` — status-first state assertions in the component descriptor and controller tests. | | Integration | `tests/status_state.rs`; `integration/gpu_worker_start/` verifies controller startup is gated by resource dependencies and status writer authority, not by a Provider state Volume | | Data migration | None — no Provider state Volume exists to migrate. | | Validation | `cargo test -p d2b-provider-device-gpu --test status_state`; component descriptor golden has no Provider state Volume declaration; controller Process template has no `/state` mount; ProviderStateSet query is empty; status/core-ledger fields carry bounded operational observations | | Removal proof | `StorageRoot`/`StoragePathSpec` lifecycle tracking entries for GPU/video roles in `d2b-core/src/storage.rs` removed after Device/Process status-first lifecycle and restart-adoption integration tests pass in a live Zone | -### ADR046-provider-device-gpu-09: Provider `README.md` +### ADR046-gpu-009: Provider `README.md` | Field | Value | | --- | --- | -| Work item ID | `ADR046-provider-device-gpu-09` | -| Dependency/owner | ADR046-provider-device-gpu-01 | +| Work item ID | `ADR046-gpu-009` | +| Dependency/owner | ADR046-gpu-001 | | Current source | None; new file | | Reuse source | None | -| Reuse action | `new` | +| Reuse action | create | | Destination | `packages/d2b-provider-device-gpu/README.md` | | Detailed design | Must include: Provider identity, supported ResourceTypes, controller/service/worker binary descriptions, placement (Host, system domain), dependencies (system-minijail, volume-local, observability-otel), RBAC roles, security model summary, state/telemetry contract, build command (`cargo build -p d2b-provider-device-gpu`), test commands (`cargo test -p d2b-provider-device-gpu`), integration command (`make test-integration`), hardware test note (see `integration/README.md`), standalone-repository consumption stub. | | Integration | Workspace policy checks for `README.md` presence | @@ -1777,8 +1777,8 @@ disposition contract test passes. | Current anchor | **GPU/video process roles**: `packages/d2b-core/src/processes.rs` `ProcessRole::Gpu`, `ProcessRole::GpuRenderNode`, `ProcessRole::Video` (`implemented-and-reachable`). **GPU argv**: `packages/d2b-host/src/gpu_argv.rs` `GpuArgvInput`, `GpuParams`, `GpuContextType`, `GpuDisplayConfig` (`implemented-and-reachable`). **Video argv + wire constants**: `packages/d2b-host/src/video_argv.rs` `VideoArgvInput`, `VideoBackend`, `wire_contract_snapshot()`, all `VHOST_USER_MEDIA_*` constants (`implemented-and-reachable`). **GPU device token set**: `packages/d2b-core/src/bundle_resolver.rs` lines 1882–1894, ProcessRole::Gpu/GpuRenderNode arm (`implemented-and-reachable`). **Minijail profiles**: `nixos-modules/minijail-profiles.nix` gpu, video, gpu-render-node profiles with device binds, seccomp refs, user NS config (`implemented-and-reachable`). **Broker ops**: `packages/d2b-contracts/src/broker_wire.rs` `RunnerRole::Gpu`, `RunnerRole::Video` (`implemented-and-reachable`). **Nix host graphics**: `nixos-modules/components/graphics.nix` (crosvm wrapper, virglVideo patch, CH rev guard, crossDomainTrusted enforcement) (`implemented-and-reachable`). **Nix guest video**: `nixos-modules/components/video/guest.nix` (`virtio_media` module, CH `--vhost-user-media` arg) (`generated-or-eval-contract`). **Contract tests**: `packages/d2b-contract-tests/tests/minijail_gpu.rs`, `minijail_swtpm_video.rs`, `video_binary_contract.rs` (`implemented-and-reachable`). **Provider crate**: `packages/d2b-provider-device-gpu/` (`ADR-only`). | | Evidence class | GPU/video process role enum and argv generators: `implemented-and-reachable`. GPU device token set and minijail profiles: `implemented-and-reachable`. Broker RunnerRole::Gpu/Video: `implemented-and-reachable`. CH/crosvm version guard: `implemented-and-reachable`. Video wire-contract constants: `implemented-and-reachable`. Device ResourceType schema: `ADR-only`. Provider crate and reconcile loop: `ADR-only`. | | Behavior retained | GPU device allowlist token set (kvm/dri/udmabuf/nvidia*); video wire-contract constants frozen; distinct allocator-assigned video vs GPU worker principal (LaunchTicket invariant; private broker state); render-node fd pre-opened by the **privileged broker** and inherited via private fd-inheritance protocol; user-namespace zero-host-caps (ADR 0021); no Wayland/audio sockets for video role; EndpointRef-based cross-domain trust projected from Device setting into LaunchTicket at resolution time; argv builder omits CrossDomain from runtime args when false; NVIDIA opt-in gating for video; CH/crosvm rev compatibility guard; `videoSidecar` + `videoNvidiaDecode` mutual independence; `virglVideo` + `videoSidecar` mutual exclusion. | -| Required delta | `d2b-provider-device-gpu` crate, async reconcile controller, Device ResourceType schema for GPU settings, Provider resource registration, process-name templates from Device UID, wire-contract check at startup, shared render-node arbitration enforcement, generation-based lifecycle via Zone resource plane; D087 status-first state assertion in the component descriptor — no Provider state Volume, empty ProviderStateSet, no controller `/state` mount, bounded operational state in status and Operation rows (ADR046-provider-device-gpu-08). | +| Required delta | `d2b-provider-device-gpu` crate, async reconcile controller, Device ResourceType schema for GPU settings, Provider resource registration, process-name templates from Device UID, wire-contract check at startup, shared render-node arbitration enforcement, generation-based lifecycle via Zone resource plane; D087 status-first state assertion in the component descriptor — no Provider state Volume, empty ProviderStateSet, no controller `/state` mount, bounded operational state in status and Operation rows (ADR046-gpu-008). | | Reuse path | Re-export `gpu_argv.rs` and `video_argv.rs` from `d2b-host` unmodified. Adapt device token set constant from `bundle_resolver.rs` into `worker_gpu.rs` `GPU_DEVICE_ALLOWLIST` for `deviceUsage` population. Adapt minijail profile field names to `Process` resource spec fields. uid/gid mapping is resolved privately by core from the signed worker template — the device-gpu controller does not write hostUid/hostGid into any resource spec field. | | Replacement/deletion | `ProcessRole::Gpu`, `ProcessRole::GpuRenderNode`, `ProcessRole::Video` in `processes.rs` retained until Provider integration parity. `d2b.vms..graphics.*` Nix options deprecated (with warning) until consumer migration window closes. Nix `components/graphics.nix` host-side worker-spawn logic removed after `worker_gpu.rs` is live; CH arg injection and crosvm patches stay in Guest runtime Nix module. `StorageRoot`/`StoragePathSpec` entries for GPU/video roles in `d2b-core/src/storage.rs` removed after status-first Device/Process lifecycle integration passes. | | Feasibility proof | GPU worker process broker token set: `packages/d2b-contract-tests/tests/minijail_gpu.rs` (existing, reachable). Video wire-contract constant snapshot: `packages/d2b-host/src/video_argv.rs` `wire_contract_snapshot()` + `tests/video_binary_contract.rs` (existing, reachable). Render-node user-NS propagation: `packages/d2b-core/src/bundle_resolver.rs` test `gpu_render_node_user_namespace_propagates_to_resolved_intent` (existing, reachable). | -| Future owner | `packages/d2b-provider-device-gpu/` crate; work items ADR046-provider-device-gpu-01 through ADR046-provider-device-gpu-09 | +| Future owner | `packages/d2b-provider-device-gpu/` crate; work items ADR046-gpu-001 through ADR046-gpu-009 | diff --git a/docs/specs/providers/ADR-046-provider-device-security-key.md b/docs/specs/providers/ADR-046-provider-device-security-key.md index 41c9ba28e..402aa2ae5 100644 --- a/docs/specs/providers/ADR-046-provider-device-security-key.md +++ b/docs/specs/providers/ADR-046-provider-device-security-key.md @@ -831,7 +831,7 @@ The frontend Process: **Current implementation note:** Baseline `d2b-sk-frontend.service` is an untracked Guest systemd unit. The v3 target removes that unit when the Process -resource is live (see W-N13). +resource is live (see ADR046-security-key-020). ## ComponentSession: relay server endpoint @@ -880,7 +880,7 @@ the session: The baseline `vsock.sock_14320` (port 14320) and `packages/d2b-sk-frontend/src/framing.rs` raw-frame protocol are obsolete under v3. The frontend `packages/d2b-sk-frontend/src/vsock.rs` is replaced by the ComponentSession -vsock client from `d2b-session-unix/src/vsock.rs` (see W-R03). +vsock client from `d2b-session-unix/src/vsock.rs` (see ADR046-security-key-003). ### Manifest-declared relay ↔ controller service @@ -1872,53 +1872,53 @@ class. ### Reuse from baseline -### W-R01 +### ADR046-security-key-001 | Field | Value | | --- | --- | -| Dependency/owner | ADR-046 provider-device-security-key session/relay owner; depends on W-N01 and the ComponentSession/Process contracts. | +| Dependency/owner | ADR-046 provider-device-security-key session/relay owner; depends on ADR046-security-key-008 and the ComponentSession/Process contracts. | | Current source | `packages/d2bd/src/security_key.rs` — baseline internal `SecurityKeyState` (renamed `RelaySessionTable` in v3 so state terminology remains reserved for Resource status), `LeaseState`, `LeaseId`, `CidTranslator`, `try_acquire_lease`, `release_lease`, `CEREMONY_TIMEOUT`, `QUEUE_WAIT_TIMEOUT` (implemented-and-reachable) | -| Reuse action | extract and adapt | +| Reuse action | adapt | | Destination | Move to `packages/d2b-provider-device-security-key/src/session.rs` and `cid.rs`; adapt to Provider Process model (remove daemon Mutex wrapping, add async relay protocol) | -| Detailed design | Extract the baseline lease/session constants and CID mapping into provider-local modules. Preserve `LeaseId` stale-release protection, cancel-all-active-CIDs, `CEREMONY_TIMEOUT`, `QUEUE_WAIT_TIMEOUT`, and bounded fair queue semantics; remove daemon-global `Mutex` ownership; keep the authority relay's DeviceGrant/OFD lease for its lifetime. Ceremony rows remain high-churn session records, never Resources. | -| Integration | W-N03 owns the relay loop; W-N04/W-N05 consume this extracted foundation; W-N02 consumes lifecycle events and writes bounded Service/Binding observations; ComponentSession/encrypted named streams carry CTAPHID bytes. | +| Detailed design | Extract the baseline lease/session constants and CID mapping into provider-local modules. Preserve `LeaseId` stale-release protection, cancel-all-active-CIDs, `CEREMONY_TIMEOUT`, `QUEUE_WAIT_TIMEOUT`, and bounded fair queue semantics; remove daemon-global `Mutex` ownership; keep the authority relay's DeviceGrant/OFD lease for its lifetime. Ceremony rows remain high-churn session records, never Resources. Primary reuse disposition: `adapt`. Preserved source-plan detail: extract and adapt. | +| Integration | ADR046-security-key-010 owns the relay loop; ADR046-security-key-011/ADR046-security-key-012 consume this extracted foundation; ADR046-security-key-009 consumes lifecycle events and writes bounded Service/Binding observations; ComponentSession/encrypted named streams carry CTAPHID bytes. | | Data migration | Full d2b 3.0 reset; no v2 state/config import | | Validation | `session_state_machine.rs`, `session_ring.rs`, `cancel_propagation.rs`, `session_timeout.rs`, `fair_queue.rs`, and `cid_isolation.rs` verify queue/active/completed/timeout transitions, ring eviction, LeaseId stale-release denial, cancel-all-CIDs, fair timeout, and per-session CID isolation with no daemon-global lease state or ceremony Resource. | -| Removal proof | W-X01 deletes the superseded daemon-internal `packages/d2bd/src/security_key.rs` `SecurityKeyState`, `LeaseState`, `SkRegistry`, and accept-loop ownership after the provider relay/session tests pass. | +| Removal proof | ADR046-security-key-030 deletes the superseded daemon-internal `packages/d2bd/src/security_key.rs` `SecurityKeyState`, `LeaseState`, `SkRegistry`, and accept-loop ownership after the provider relay/session tests pass. | -### W-R02 +### ADR046-security-key-002 | Field | Value | | --- | --- | -| Dependency/owner | ADR-046 provider-device-security-key relay extraction owner; depends on W-N01, W-R01, and the frozen ComponentSession/Endpoint contracts. | +| Dependency/owner | ADR-046 provider-device-security-key relay extraction owner; depends on ADR046-security-key-008, ADR046-security-key-001, and the frozen ComponentSession/Endpoint contracts. | | Current source | `packages/d2bd/src/security_key.rs` — CTAPHID relay loop, `SkAcceptHandle`, `relay_one_ceremony` (implemented-and-reachable) | -| Reuse action | extract and adapt | +| Reuse action | adapt | | Destination | Move to `packages/d2b-provider-device-security-key/src/relay.rs`; replace daemon-internal Unix socket proxy with ComponentSession over the owned Service Endpoint | -| Detailed design | Extract the CTAPHID ceremony relay behavior into the provider relay binary. Preserve one-ceremony-at-a-time proxy semantics and CTAPHID cancel handling, but replace daemon-internal Unix socket proxying with the `d2b.security-key.v3` ComponentSession over the owned CTAPHID Endpoint and named `ctaphid` stream. | +| Detailed design | Extract the CTAPHID ceremony relay behavior into the provider relay binary. Preserve one-ceremony-at-a-time proxy semantics and CTAPHID cancel handling, but replace daemon-internal Unix socket proxying with the `d2b.security-key.v3` ComponentSession over the owned CTAPHID Endpoint and named `ctaphid` stream. Primary reuse disposition: `adapt`. Preserved source-plan detail: extract and adapt. | | Integration | Core launches the relay Process with a LaunchTicket DeviceGrant and Endpoint attachment; transport-vsock resolves `Endpoint/-ctaphid-relay`; frontend Process connects as ComponentSession initiator; controller receives session events over the manifest-declared internal channel. | | Data migration | Full d2b 3.0 reset; no v2 state/config import | | Validation | `host_relay_guest_frontend/` integration fixture, `device_grant_no_path.rs`, `descriptor_validation.rs`, and `cancel_propagation.rs` prove relay fd injection, ComponentSession transport, cancel propagation, and absence of daemon-internal socket proxying. | -| Removal proof | W-X01 and W-X02 remove `start_sk_accept_loop`, `SkAcceptHandle`, `relay_one_ceremony`, and the daemon-internal Unix socket proxy bind from `packages/d2bd/src/security_key.rs` and `packages/d2bd/src/lib.rs`. | +| Removal proof | ADR046-security-key-030 and ADR046-security-key-031 remove `start_sk_accept_loop`, `SkAcceptHandle`, `relay_one_ceremony`, and the daemon-internal Unix socket proxy bind from `packages/d2bd/src/security_key.rs` and `packages/d2bd/src/lib.rs`. | -### W-R03 +### ADR046-security-key-003 | Field | Value | | --- | --- | -| Dependency/owner | ADR-046 provider-device-security-key frontend extraction owner; depends on W-N01 and frozen Process/ComponentSession contracts. | +| Dependency/owner | ADR-046 provider-device-security-key frontend extraction owner; depends on ADR046-security-key-008 and frozen Process/ComponentSession contracts. | | Current source | `packages/d2b-sk-frontend/src/` — `main.rs`, `uhid.rs` (implemented-and-reachable); `framing.rs` and `vsock.rs` are obsolete under v3 (replaced by ComponentSession transport) | -| Reuse action | extract and adapt | +| Reuse action | adapt | | Destination | Adopt `main.rs` and `uhid.rs` as the v3 Process binary entry point; replace `framing.rs`/`vsock.rs` with ComponentSession client from `d2b-session-unix/src/vsock.rs`; wire as Process service in Provider crate | -| Detailed design | Retain UHID creation and frontend entry behavior, but run it as a Binding-owned v3 user-domain Process receiving a pre-opened `/dev/uhid` fd from the `Provider/system-core` Guest-substrate DeviceGrant. Delete raw frame/vsock protocol and use the ComponentSession client/named `ctaphid` stream. No virtual/projected Device exists. | -| Integration | W-N19 defines Service/Binding ownership; W-N13/N17 wire the Binding-owned frontend Process/private Endpoint and same-Zone Service resolution; Core injects UHID from the Guest substrate. | +| Detailed design | Retain UHID creation and frontend entry behavior, but run it as a Binding-owned v3 user-domain Process receiving a pre-opened `/dev/uhid` fd from the `Provider/system-core` Guest-substrate DeviceGrant. Delete raw frame/vsock protocol and use the ComponentSession client/named `ctaphid` stream. No virtual/projected Device exists. Primary reuse disposition: `adapt`. Preserved source-plan detail: extract and adapt. | +| Integration | ADR046-security-key-026 defines Service/Binding ownership; ADR046-security-key-020/N17 wire the Binding-owned frontend Process/private Endpoint and same-Zone Service resolution; Core injects UHID from the Guest substrate. | | Data migration | Full d2b 3.0 reset; no frontend session state import | | Validation | `host_relay_guest_frontend/`, `device_grant_no_path.rs`, `descriptor_validation.rs`, and guest Nix migration tests prove UHID fd injection, no `/dev/uhid` path, ComponentSession client use, and no raw `framing.rs`/`vsock.rs` protocol. | -| Removal proof | W-X03 removes the legacy `d2b-sk-frontend.service` unit declaration, and the v3 frontend excludes the obsolete `packages/d2b-sk-frontend/src/framing.rs` and `vsock.rs` raw transport behavior. | +| Removal proof | ADR046-security-key-032 removes the legacy `d2b-sk-frontend.service` unit declaration, and the v3 frontend excludes the obsolete `packages/d2b-sk-frontend/src/framing.rs` and `vsock.rs` raw transport behavior. | -### W-R04 +### ADR046-security-key-004 | Field | Value | | --- | --- | -| Dependency/owner | Core LaunchTicket/privileged broker reuse owner; depends on ADR-046-resources-device and W-N06 probe/device-token population. | +| Dependency/owner | Core LaunchTicket/privileged broker reuse owner; depends on ADR-046-resources-device and ADR046-security-key-013 probe/device-token population. | | Current source | `packages/d2b-priv-broker/src/ops/security_key.rs` — `live_open_hidraw_security_key`, FIDO usage page revalidation, group validation, `ALLOWED_GROUPS` (implemented-and-reachable) | | Reuse action | adapt | | Destination | Preserve revalidation logic; update `SecurityKeyOpenDevice` to use bundle device table `device_token` as sole open target (no iterative sysfs scan); add zone-field handling; remove sysfs fallback. **Core's LaunchTicket calls this internally; the Provider does not call it.** | @@ -1928,71 +1928,71 @@ class. | Validation | `packages/d2b-priv-broker/tests/security_key_broker.rs` updates for bundle table lookup and zone-field round trip; `device_grant_no_path.rs` proves Provider code does not call the broker and sees no device path; audit tests prove path-free grant records. | | Removal proof | The superseded iterative sysfs scan/fallback behavior in `packages/d2b-priv-broker/src/ops/security_key.rs` is removed once bundle-token lookup and revalidation tests pass. | -### W-R05 +### ADR046-security-key-005 | Field | Value | | --- | --- | -| Dependency/owner | `d2b-contracts` security-key ceremony/effect DTO owner; depends on ADR046-provider-004, ADR-046-resource-object-model, ADR-046-resources-device, and W-N01. | +| Dependency/owner | `d2b-contracts` security-key ceremony/effect DTO owner; depends on ADR046-provider-004, ADR-046-resource-object-model, ADR-046-resources-device, and ADR046-security-key-008. | | Current source | `packages/d2b-contracts/src/security_key.rs` — `SecurityKeySessionId`, `SecurityKeyDeviceLabel`, `SecurityKeySession`, `SecurityKeySessionResult`, `SecurityKeyStatusResponse`, `SecurityKeySessionsResponse`, `SecurityKeyOpenDeviceRequest`, `SecurityKeyEvent` (implemented-and-reachable) | | Reuse action | adapt | -| Destination | Adapt to v3 Zone/ResourceRef identifiers; preserve serde shapes for zero downstream breakage where possible; remove `SecurityKeyApplyUdevRulesRequest` (W-X06) | +| Destination | Adapt to v3 Zone/ResourceRef identifiers; preserve serde shapes for zero downstream breakage where possible; remove `SecurityKeyApplyUdevRulesRequest` (ADR046-security-key-035) | | Detailed design | Rebase wire DTOs onto v3 Zone/ResourceRef identifiers; consume the shared ADR046-provider-004 `security-key.d2bus.org` Service/Binding bases and define only strict `device-security-key.d2bus.org` Provider-extension DTOs; reject `spec.provider` on Core projections; place authority/import/attachment semantic observations only under `status.resource` and implementation observations only under `status.provider`; preserve opaque bounded ceremony records as non-Resource DTOs; add `zone` to `SecurityKeyOpenDeviceRequest`; drop the udev-rules request because UHID comes from the Guest-substrate DeviceGrant. No provider-named ResourceType alias is admitted. | | Integration | Core LaunchTicket, broker open op, Provider controller Service/Binding status/audit, CLI session readers, and provider tests consume the v3 DTOs. | | Data migration | Full d2b 3.0 reset; no v2 DTO compatibility migration beyond serde-shape preservation where possible | | Validation | DTO serde round trips, exact provider-neutral ResourceType identity, provider-named alias rejection, canonical minimal base acceptance, Core projection `spec.provider` rejection, D088 `status.resource`/`status.provider` layering, base/Provider-extension field separation, unknown-field denial, zone-field round trip, path-redaction tests, and updated `usb_sk_contract.rs` assertions in the provider crate. | -| Removal proof | W-X06 removes `SecurityKeyApplyUdevRulesRequest`, the `SecurityKeyApplyUdevRules` broker op, and related broker code after UHID DeviceGrant coverage is live. | +| Removal proof | ADR046-security-key-035 removes `SecurityKeyApplyUdevRulesRequest`, the `SecurityKeyApplyUdevRules` broker op, and related broker code after UHID DeviceGrant coverage is live. | -### W-R06 +### ADR046-security-key-006 | Field | Value | | --- | --- | -| Dependency/owner | Provider crate test owner; depends on W-R05 v3 DTOs and W-N01 provider crate layout. | +| Dependency/owner | Provider crate test owner; depends on ADR046-security-key-005 v3 DTOs and ADR046-security-key-008 provider crate layout. | | Current source | `packages/d2b-contract-tests/tests/usb_sk_contract.rs` — DTO serde round-trips, unknown-field denial, broker capability set (implemented-and-reachable) | -| Reuse action | move and adapt | +| Reuse action | adapt | | Destination | Move to `packages/d2b-provider-device-security-key/tests/`; update imports and v3 type names | -| Detailed design | Move the reusable semantic assertions for security-key DTO serde, unknown-field denial, and broker capability shape into the provider crate's hermetic `tests/` suite, updating imports and names to the v3 contract modules without weakening assertions. | +| Detailed design | Move the reusable semantic assertions for security-key DTO serde, unknown-field denial, and broker capability shape into the provider crate's hermetic `tests/` suite, updating imports and names to the v3 contract modules without weakening assertions. Primary reuse disposition: `adapt`. Preserved source-plan detail: move and adapt. | | Integration | `cargo test -p d2b-provider-device-security-key --lib --tests` runs the moved contract tests with the provider's DTO/controller test matrix; old contract-test manifests point to the successor coverage before deletion. | | Data migration | None — test-only move; no runtime state | | Validation | Moved tests pass under the provider crate; contract assertions are retained; D094 disposition records moved/adapted coverage before old duplicate tests are deleted. | -| Removal proof | W-X04 deletes `packages/d2b-contract-tests/tests/usb_sk_contract.rs` only after the provider-crate successor test covers all prior assertions. | +| Removal proof | ADR046-security-key-033 deletes `packages/d2b-contract-tests/tests/usb_sk_contract.rs` only after the provider-crate successor test covers all prior assertions. | -### W-R07 +### ADR046-security-key-007 | Field | Value | | --- | --- | -| Dependency/owner | Provider crate test/minijail adaptation owner; depends on W-N01 and the frozen Process sandbox contract. | +| Dependency/owner | Provider crate test/minijail adaptation owner; depends on ADR046-security-key-008 and the frozen Process sandbox contract. | | Current source | `packages/d2b-contract-tests/tests/minijail_sk_frontend.rs` — minijail profile shape, `ProcessRole::SecurityKeyFrontend` (implemented-and-reachable) | -| Reuse action | move and adapt | +| Reuse action | adapt | | Destination | Move to `packages/d2b-provider-device-security-key/tests/`; update for v3 Process resource minijail profile; retain zero-capabilities assertion | -| Detailed design | Move the reusable minijail/sandbox assertions into the provider crate and retarget them from `ProcessRole::SecurityKeyFrontend` to the v3 Process resource templates and relay/controller minijail profiles. Preserve zero-capabilities and seccomp-class assertions while recognizing the frontend uses `Provider/system-systemd` hardening rather than a minijail profile. | +| Detailed design | Move the reusable minijail/sandbox assertions into the provider crate and retarget them from `ProcessRole::SecurityKeyFrontend` to the v3 Process resource templates and relay/controller minijail profiles. Preserve zero-capabilities and seccomp-class assertions while recognizing the frontend uses `Provider/system-systemd` hardening rather than a minijail profile. Primary reuse disposition: `adapt`. Preserved source-plan detail: move and adapt. | | Integration | Provider tests validate Nix minijail profile entries, Process resource sandbox templates, and system-minijail/system-systemd conformance expectations before old contract tests are retired. | | Data migration | None — test-only move; no runtime state | | Validation | Provider-crate tests retain zero-capability and seccomp assertions for relay/controller and assert no minijail profile is used for the frontend Process. | -| Removal proof | W-X04 deletes `packages/d2b-contract-tests/tests/minijail_sk_frontend.rs` only after the provider-crate successor test covers all prior assertions. | +| Removal proof | ADR046-security-key-033 deletes `packages/d2b-contract-tests/tests/minijail_sk_frontend.rs` only after the provider-crate successor test covers all prior assertions. | ### New items -### W-N01 +### ADR046-security-key-008 | Field | Value | | --- | --- | | Dependency/owner | ADR-046 provider-device-security-key crate owner; depends on provider-model/package workspace policy. | | Current source | None — net-new v3 work; no pre-ADR45 baseline equivalent | -| Reuse action | net-new | +| Reuse action | create | | Destination | New crate `packages/d2b-provider-device-security-key/` with `src/`, `tests/`, `integration/`, `README.md` (workspace policy requires all four) | | Detailed design | New crate `packages/d2b-provider-device-security-key/` with `src/`, `tests/`, `integration/`, `README.md` (workspace policy requires all four) | -| Integration | Workspace/package descriptor expose the crate to Core; W-N02 through W-N22 add controllers, resource contracts, relay/frontend, adapters, tests, and docs. | +| Integration | Workspace/package descriptor expose the crate to Core; ADR046-security-key-009 through ADR046-security-key-029 add controllers, resource contracts, relay/frontend, adapters, tests, and docs. | | Data migration | Full d2b 3.0 reset; no v2 state/config import | | Validation | Workspace package-policy check rejects missing `src/`, `tests/`, `integration/`, or `README.md`; `cargo test -p d2b-provider-device-security-key --lib --tests` discovers the hermetic suite; README acceptance criteria from the provider crate standard layout are satisfied. | | Removal proof | None — net-new; no prior owner to remove | -### W-N02 +### ADR046-security-key-009 | Field | Value | | --- | --- | -| Dependency/owner | Provider controller owner; depends on W-N06 probe, W-N09 templates, W-N18 effect port, W-N19 Service/Binding contracts, W-N20 status contract, and ADR-046-resource-reconciliation. | +| Dependency/owner | Provider controller owner; depends on ADR046-security-key-013 probe, ADR046-security-key-016 templates, ADR046-security-key-025 effect port, ADR046-security-key-026 Service/Binding contracts, ADR046-security-key-027 status contract, and ADR-046-resource-reconciliation. | | Current source | None — net-new v3 work; no pre-ADR45 baseline equivalent | -| Reuse action | net-new | +| Reuse action | create | | Destination | `packages/d2b-provider-device-security-key/src/controller.rs` | | Detailed design | One controller implements standard reconcile for local physical Devices, authority/projection SecurityKeyServices, and SecurityKeyBindings. It observes Devices; realizes an authority Service as relay Process/Service-owned Endpoint; accepts projection Services only from Core/import after signed-factory admission; realizes each Binding as frontend Process/private Endpoint; enforces child-first finalizers and never creates an import or Device projection. Export/Import routing never treats an Endpoint as exported identity. | | Integration | Watches Device and both provider-neutral semantic types filtered by `providerRef=Provider/device-security-key`, plus Process, Endpoint, Guest/User, ResourceExport/Import, and Service/export/import-owner dependency indexes; writes semantic base plus signed Provider-extension status/finalizers; drives relay-control messages. | @@ -2000,41 +2000,41 @@ class. | Validation | `controller_reconcile.rs`, `service_binding_projection.rs`, `mutual_exclusion.rs`, `status_binding.rs`, and deletion/finalizer tests cover authority/projection/Binding branches, signed-factory admission before projection reconcile, Service-owned Endpoint isolation from Export/Import identity, no Device projection, and no Volume API calls. | | Removal proof | None — net-new; no prior owner to remove | -### W-N03 +### ADR046-security-key-010 | Field | Value | | --- | --- | -| Dependency/owner | Authority Service relay owner; depends on W-R01, W-R02, W-N04, W-N05, W-N07, W-N09, and W-N11. | +| Dependency/owner | Authority Service relay owner; depends on ADR046-security-key-001, ADR046-security-key-002, ADR046-security-key-011, ADR046-security-key-012, ADR046-security-key-014, ADR046-security-key-016, and ADR046-security-key-018. | | Current source | None — net-new v3 work; no pre-ADR45 baseline equivalent | -| Reuse action | net-new | +| Reuse action | create | | Destination | `packages/d2b-provider-device-security-key/src/relay.rs` | | Detailed design | Authority Service-owned relay entry point: bounded authenticated Binding connections, one LeaseId-guarded fair ceremony queue, CID translation/cancel-all-CIDs, hidraw fd from Core DeviceGrant, CTAPHID named stream, and internal relay-control channel. | | Integration | Core injects the Service's physical DeviceGrant, relay Endpoint, and controller channel; Bindings that reference authority or projection Services connect through same-Zone Service Endpoints; Core releases grant on relay exit. | | Data migration | Full d2b 3.0 reset; no relay session state import | | Validation | `host_relay_guest_frontend/`, `fair_queue.rs`, `device_grant_no_path.rs`, `descriptor_validation.rs`, `cancel_propagation.rs`, and `cid_isolation.rs` prove one authority open, multi-Binding fair serialization, fd-only access, LeaseId cancel, and CID isolation. | -| Removal proof | Supersedes daemon-internal relay behavior removed by W-X01/W-X02 after relay Process tests pass. | +| Removal proof | Supersedes daemon-internal relay behavior removed by ADR046-security-key-030/ADR046-security-key-031 after relay Process tests pass. | -### W-N04 +### ADR046-security-key-011 | Field | Value | | --- | --- | -| Dependency/owner | Relay ceremony-session foundation owner; depends on W-R01 and W-N01. | +| Dependency/owner | Relay ceremony-session foundation owner; depends on ADR046-security-key-001 and ADR046-security-key-008. | | Current source | None — net-new v3 work; no pre-ADR45 baseline equivalent | -| Reuse action | net-new | +| Reuse action | create | | Destination | `packages/d2b-provider-device-security-key/src/session.rs` | | Detailed design | `SessionStateMachine` with Idle/Queued/Active/Completed/TimedOut, bounded FIFO queue, monotonic LeaseId stale-release guard, per-Binding session ring, timeout/cancel, and ring eviction. Ceremony rows are non-Resource records; DeviceGrant remains held for relay lifetime. | -| Integration | W-N03 consumes it; controller receives lifecycle messages; Service/Binding status receives aggregates only; session query/audit consumes bounded non-secret rows. | +| Integration | ADR046-security-key-010 consumes it; controller receives lifecycle messages; Service/Binding status receives aggregates only; session query/audit consumes bounded non-secret rows. | | Data migration | Full d2b 3.0 reset; no session ring import | | Validation | `session_state_machine.rs`, `session_ring.rs`, `fair_queue.rs`, `session_timeout.rs`, and `cancel_propagation.rs` cover queue fairness, eviction, stale LeaseId rejection, timeout, and cancel. | | Removal proof | None — net-new; no prior owner to remove | -### W-N05 +### ADR046-security-key-012 | Field | Value | | --- | --- | -| Dependency/owner | Relay CID-translation foundation owner; depends on W-R01 and W-N01. | +| Dependency/owner | Relay CID-translation foundation owner; depends on ADR046-security-key-001 and ADR046-security-key-008. | | Current source | None — net-new v3 work; no pre-ADR45 baseline equivalent | -| Reuse action | net-new | +| Reuse action | create | | Destination | `packages/d2b-provider-device-security-key/src/cid.rs` | | Detailed design | CID translator: per-active-ceremony u32→u64 host-CID allocation, bimap, cancel-all-active-CIDs, and eviction on ceremony end | | Integration | Relay rewrites frontend CTAPHID CIDs before sending to hidraw fd and reverses responses before writing the ComponentSession named stream; session teardown drops the map. | @@ -2042,27 +2042,27 @@ class. | Validation | `cid_isolation.rs` verifies per-session allocation, round trip, no sharing across relays, and eviction on session end. | | Removal proof | None — net-new; no prior owner to remove | -### W-N06 +### ADR046-security-key-013 | Field | Value | | --- | --- | -| Dependency/owner | Probe/effect-port and activation owner; depends on W-N18 effect port and Core private bundle device table support. | +| Dependency/owner | Probe/effect-port and activation owner; depends on ADR046-security-key-025 effect port and Core private bundle device table support. | | Current source | None — net-new v3 work; no pre-ADR45 baseline equivalent | -| Reuse action | net-new | +| Reuse action | create | | Destination | `packages/d2b-provider-device-security-key/src/probe.rs`; Provider activation/Core private bundle device table population for label → `device_token` | | Detailed design | hidraw probe: `probe.rs` — calls `SecurityKeyEffectPort::observe_inventory(&device_id, &policy_id)` with opaque types injected by Core; interprets `InventoryObservation`; never reads `/sys/class/hidraw/` directly; bundle device table population at activation time (Provider activation resolves label → `device_token` via Core; stored in private bundle) | | Integration | Controller scheduled-observe invokes `probe.rs`; Core adapter implements `SecurityKeyEffectPort`; Nix activation emits private label-to-token bundle entries; Device status receives `DevicePresent` and phase updates. | | Data migration | Full d2b 3.0 reset; no v2 probe state import | | Validation | `controller_reconcile.rs` scheduled-observe tests, `descriptor_validation.rs` Debug-redaction capture, and path-safety tests prove Provider never reads sysfs and receives only opaque observations. | -| Removal proof | Supersedes provider-side or broker fallback sysfs scanning; W-R04/W-N11 removal proof verifies only bundle `device_token` lookup remains. | +| Removal proof | Supersedes provider-side or broker fallback sysfs scanning; ADR046-security-key-004/ADR046-security-key-018 removal proof verifies only bundle `device_token` lookup remains. | -### W-N07 +### ADR046-security-key-014 | Field | Value | | --- | --- | -| Dependency/owner | ComponentSession/security descriptor contract owner; depends on W-N01, W-R05, and ADR-046-componentsession-and-bus. | +| Dependency/owner | ComponentSession/security descriptor contract owner; depends on ADR046-security-key-008, ADR046-security-key-005, and ADR-046-componentsession-and-bus. | | Current source | None — net-new v3 work; no pre-ADR45 baseline equivalent | -| Reuse action | net-new | +| Reuse action | create | | Destination | `packages/d2b-provider-device-security-key/src/descriptor.rs` | | Detailed design | Declare relay↔controller service and relay↔Binding-frontend `d2b.security-key.v3` fingerprints, Noise profiles, canonical Service/Binding subject pairing, bounded encrypted-stream records, and descriptor validation; no ambient path or raw vsock CID. | | Integration | Provider descriptor declares services and fingerprints; LaunchTicket injects internal channel and Endpoint transport; relay/controller/frontend validate descriptors and peer authority before exchanging messages. | @@ -2070,41 +2070,41 @@ class. | Validation | `descriptor_validation.rs` covers wrong service, wrong descriptor digest, wrong SO_PEERCRED uid, unenrolled key, oversized records, no ambient path, and redacted opaque IDs. | | Removal proof | None — net-new; no prior owner to remove | -### W-N08 +### ADR046-security-key-015 | Field | Value | | --- | --- | -| Dependency/owner | Sandbox/minijail foundation owner; depends on W-N01, W-R07, and ADR-046-components-processes-and-sandbox. | +| Dependency/owner | Sandbox/minijail foundation owner; depends on ADR046-security-key-008, ADR046-security-key-007, and ADR-046-components-processes-and-sandbox. | | Current source | None — net-new v3 work; no pre-ADR45 baseline equivalent | -| Reuse action | net-new | +| Reuse action | create | | Destination | `nixos-modules/minijail-profiles.nix` entries for relay and controller; provider descriptor sandbox templates for relay/controller/frontend | | Detailed design | Minijail profiles for relay and controller only; frontend uses `Provider/system-systemd` hardening directives compiled from `SandboxSpec` (no minijail profile for frontend). Add relay and controller entries to `nixos-modules/minijail-profiles.nix`; `capabilityClasses: []`; `seccompClass: sk-relay` and `seccompClass: sk-controller` | | Integration | Nix minijail profiles feed system-minijail Process launches for controller/relay; frontend Process template feeds system-systemd hardening; provider tests assert the split. | | Data migration | Full d2b 3.0 reset; no sandbox state import | | Validation | `minijail_sk_frontend` successor tests, sandbox template tests, and zero-capability/seccomp assertions cover relay/controller minijail profiles and no frontend minijail profile. | -| Removal proof | Supersedes `ProcessRole::SecurityKeyFrontend`-centric minijail test ownership removed by W-X04/W-X05 after Process-resource coverage passes. | +| Removal proof | Supersedes `ProcessRole::SecurityKeyFrontend`-centric minijail test ownership removed by ADR046-security-key-033/ADR046-security-key-034 after Process-resource coverage passes. | -### W-N09 +### ADR046-security-key-016 | Field | Value | | --- | --- | -| Dependency/owner | Provider process/Endpoint-template owner; depends on W-N01, W-N08, and W-N19 Service/Binding ownership contracts. | +| Dependency/owner | Provider process/Endpoint-template owner; depends on ADR046-security-key-008, ADR046-security-key-015, and ADR046-security-key-026 Service/Binding ownership contracts. | | Current source | None — net-new v3 work; no pre-ADR45 baseline equivalent | -| Reuse action | net-new | +| Reuse action | create | | Destination | Provider descriptor Process templates and owned CTAPHID `Endpoint` template for `Provider/device-security-key` | | Detailed design | Templates: Provider controller; authority Service-owned relay Process/relay Endpoint; Binding-owned frontend Process/private Endpoint; projection Service local Endpoint. Frontend requires Guest/User and the system-core UHID DeviceGrant; no virtual Device template exists. | | Integration | Core creates controller; Provider controller realizes authority Services and Bindings plus each projection Service's ordinary local import-route Endpoint; Process Providers launch children and preserve ownerRef boundaries. | | Data migration | Full d2b 3.0 reset; no v2 processes.json import | | Validation | `controller_reconcile.rs`, Process template golden tests, Endpoint resource tests, and frontend `userRef` admission tests prove templates and Endpoint shape. | -| Removal proof | Supersedes the legacy readiness-only `ProcessRole::SecurityKeyFrontend` tracking node removed by W-X05 after v3 Process resources are live. | +| Removal proof | Supersedes the legacy readiness-only `ProcessRole::SecurityKeyFrontend` tracking node removed by ADR046-security-key-034 after v3 Process resources are live. | -### W-N10 +### ADR046-security-key-017 | Field | Value | | --- | --- | -| Dependency/owner | Provider package descriptor owner; depends on W-N01, W-R05, W-N07, W-N09, W-N19, W-N20, and ADR-046-provider-model-and-packaging. | +| Dependency/owner | Provider package descriptor owner; depends on ADR046-security-key-008, ADR046-security-key-005, ADR046-security-key-014, ADR046-security-key-016, ADR046-security-key-026, ADR046-security-key-027, and ADR-046-provider-model-and-packaging. | | Current source | None — net-new v3 work; no pre-ADR45 baseline equivalent | -| Reuse action | net-new | +| Reuse action | create | | Destination | Signed Provider descriptor JSON for `Provider/device-security-key` in the provider package | | Detailed design | Signed descriptor: config; physical Device integration; implementation claim for the provider-neutral `security-key.d2bus.org` Service/Binding base schemas/fingerprints; strict `device-security-key.d2bus.org` spec/status extensions; authority/projection union and D097 descriptor; a D096 projection factory with exact `serviceType`, `projectionSchemaFingerprint`, and semantic `factoryFingerprint`; controller/relay/frontend/Endpoint templates; export/import adapter capability; ComponentSession services; empty ProviderStateSet; permission claims. Provider/adapter identity is signed separately and Service-owned Endpoints are not factory or Export fields. | | Integration | Core ProviderDeployment verifies the signed descriptor, installs ResourceApiBinding and component descriptors, exposes service fingerprints to ComponentSession validation, and supplies permission claims/RBAC bindings. | @@ -2112,13 +2112,13 @@ class. | Validation | Descriptor schema validation, semantic-base versus Provider-extension fingerprints, exact projection-schema/factory fingerprint derivation and stability under Provider/adapter identity changes, exact type/no-alias tests, service inventory tests, permission claim tests, empty ProviderStateSet tests, and README/provider package conformance checks. | | Removal proof | None — net-new; no prior owner to remove | -### W-N11 +### ADR046-security-key-018 | Field | Value | | --- | --- | -| Dependency/owner | Core LaunchTicket/broker owner; depends on W-R04, W-R05, W-N06, and ADR-046-resources-device. | +| Dependency/owner | Core LaunchTicket/broker owner; depends on ADR046-security-key-004, ADR046-security-key-005, ADR046-security-key-013, and ADR-046-resources-device. | | Current source | None — net-new v3 work; no pre-ADR45 baseline equivalent | -| Reuse action | net-new | +| Reuse action | create | | Destination | v3 `SecurityKeyOpenDevice` broker op and Core LaunchTicket DeviceGrant resolution path | | Detailed design | v3 `SecurityKeyOpenDevice` broker op update: add `zone` field; implement bundle device table `device_token` lookup as sole open path; remove iterative sysfs scan from broker; add post-open revalidation steps (fstat, HIDIOCGRAWINFO, HIDIOCGRDESC). This is an internal Core operation called by LaunchTicket; the Provider controller does not call it. | | Integration | Authority Service controller derives relay `deviceUsage` from `spec.provider.settings.deviceRef`; Core admits authority then resolves DeviceGrant through the private bundle table; broker returns an fd to Core; projection Services never enter this path. | @@ -2126,27 +2126,27 @@ class. | Validation | Broker unit tests for zone field and token lookup, path-rejection tests, post-open revalidation tests, and provider tests proving no Provider broker call or sysfs path. | | Removal proof | Superseded broker iterative sysfs scan behavior is removed; tests prove only bundle `device_token` lookup is accepted for `SecurityKeyOpenDevice`. | -### W-N20 +### ADR046-security-key-027 | Field | Value | | --- | --- | -| Dependency/owner | Provider state/status contract owner; depends on W-N01, W-N19, and ADR-046-provider-state. | +| Dependency/owner | Provider state/status contract owner; depends on ADR046-security-key-008, ADR046-security-key-026, and ADR-046-provider-state. | | Current source | None — net-new v3 work; no pre-ADR45 baseline equivalent | -| Reuse action | net-new | +| Reuse action | create | | Destination | Provider descriptor state declaration, controller/status logic, Process templates, and Nix principal provisioning for `Provider/device-security-key` | | Detailed design | Empty ProviderStateSet and strict bounded status schemas: physical presence in Device `status.resource`; semantic authority/import aggregates in Service `status.resource`; attachment aggregates in Binding `status.resource`; initial physical-backing claim, relay, Endpoint, queue, and ceremony observations only in `status.provider`. No semantic field appears directly under `status`, and Core projections contain no `spec.provider`. Ceremony rows remain high-churn non-Resource session records; CTAP/fd/LeaseId/CID data stays transient. No Process has `/state`. | -| Integration | W-N10 signs schemas; W-N02 writes resource-local status; Core Operation/session/audit surfaces own bounded records; Volume controllers see no request. | +| Integration | ADR046-security-key-017 signs schemas; ADR046-security-key-009 writes resource-local status; Core Operation/session/audit surfaces own bounded records; Volume controllers see no request. | | Data migration | Full d2b 3.0 reset; no v2 state/config import | | Validation | `status_binding.rs` proves empty ProviderStateSet, no `/state` mounts, no Volume API calls, authority/import/attachment fields only under `status.resource`, implementation fields only under `status.provider`, no projection `spec.provider`, and no CTAP/fd/session secrets in status/log/audit/metrics. | | Removal proof | None — net-new; no prior owner to remove | -### W-N12 +### ADR046-security-key-019 | Field | Value | | --- | --- | -| Dependency/owner | Nix resource compiler owner; depends on W-N10, W-N19, ADR046-zone-control-024, and ADR-046-nix-configuration. | +| Dependency/owner | Nix resource compiler owner; depends on ADR046-security-key-017, ADR046-security-key-026, ADR046-zone-control-024, and ADR-046-nix-configuration. | | Current source | None — net-new v3 work; no pre-ADR45 baseline equivalent | -| Reuse action | net-new | +| Reuse action | create | | Destination | `nixos-modules/` resource compiler/eval assertions for physical Device, authority Service, ResourceExport/Import, and consumer Binding | | Detailed design | Compile the owner Device→Service→export and consumer import→projection-Service→Binding shape. Emit Export `resourceRef`, `serviceType`, `projectionSchemaFingerprint`, and `factoryFingerprint`, and matching Import `expectedServiceType`, `expectedProjectionSchemaFingerprint`, and `expectedFactoryFingerprint`; the Import `exportKey` identifies the ResourceExport. Reject Export Endpoint/custom-key fields, authored projections, projection `spec.provider`, Device export/projection, cross-Zone refs, duplicate authorities/Bindings, paths, and any security-key/USB configuration that does not collide through the exact Core-derived `(Host, physical-usb-backing, opaqueKeyDigest)` tuple after trusted identity resolution. | | Integration | Nix emits Device/authority Service/export/import/Binding with canonical D096 fields; Core alone creates projection Service; the Service controller alone owns relay/import-route Endpoints; bundle feeds Provider and authority-index admission. | @@ -2154,27 +2154,27 @@ class. | Validation | Nix eval tests for label resolution, `busClass=hidraw`, exclusive arbitration, exact canonical Export/Import field emission and fingerprint matching, rejection of obsolete Export `endpointRef`/`exportedType`/`baseSchemaFingerprint`/`exportKey` and Import `expectedType`/`expectedBaseSchemaFingerprint`/`projectionType`, Core-only projection without `spec.provider`, byte-identical USB/security-key physical backing tuple collision, Provider-private-class bypass rejection, prohibited fields, and providerRef resolution. | | Removal proof | Supersedes current option shape only after v3 Zone resource option parity; legacy security-key/USBIP mutual-exclusion assertion is replaced by v3 resource assertion coverage. | -### W-N13 +### ADR046-security-key-020 | Field | Value | | --- | --- | -| Dependency/owner | Guest Nix migration owner; depends on W-R03, W-N09, and W-N19 Binding-owned frontend contract. | +| Dependency/owner | Guest Nix migration owner; depends on ADR046-security-key-003, ADR046-security-key-016, and ADR046-security-key-026 Binding-owned frontend contract. | | Current source | None — net-new v3 work; no pre-ADR45 baseline equivalent | -| Reuse action | net-new | +| Reuse action | create | | Destination | `nixos-modules/components/security-key-guest.nix` migration gate `d2b.securityKey._legacySystemdUnit` | | Detailed design | Guest Nix module migration gate: `d2b.securityKey._legacySystemdUnit` option, defaulting to false when Provider is installed; remove `d2b-sk-frontend.service` unit | | Integration | Guest Nix keeps `uhid` and the static frontend binary; Binding controller owns frontend lifecycle and system-core supplies the UHID DeviceGrant; no Device row or udev rule is emitted. | | Data migration | Full d2b 3.0 reset; no legacy frontend unit state import | | Validation | Nix eval tests show the legacy unit is absent by default with Provider installed, can be gated only during transition if required, and `uhid` module/binary wiring remains present. | -| Removal proof | W-X03 deletes the superseded `nixos-modules/components/security-key-guest.nix` `d2b-sk-frontend.service` declaration after the gate defaults to false. | +| Removal proof | ADR046-security-key-032 deletes the superseded `nixos-modules/components/security-key-guest.nix` `d2b-sk-frontend.service` declaration after the gate defaults to false. | -### W-N14 +### ADR046-security-key-021 | Field | Value | | --- | --- | -| Dependency/owner | Audit owner for Core device-grant and Service/Binding lifecycle; depends on W-N02, W-N11, and ADR-046-telemetry-audit-and-support. | +| Dependency/owner | Audit owner for Core device-grant and Service/Binding lifecycle; depends on ADR046-security-key-009, ADR046-security-key-018, and ADR-046-telemetry-audit-and-support. | | Current source | None — net-new v3 work; no pre-ADR45 baseline equivalent | -| Reuse action | net-new | +| Reuse action | create | | Destination | Core `device-grant` audit and Provider controller Service/Binding ceremony lifecycle audit | | Detailed design | Path-free authority-grant records from Core and bounded Service/Binding/session digests/outcomes from controller; no path, raw target identity, LeaseId, session content, or CTAP bytes. | | Integration | Core emits grant audit; controller emits Service/Binding lifecycle audit; Zone stream stores bounded records; CLI/support consumes digests/outcomes. | @@ -2182,13 +2182,13 @@ class. | Validation | Audit tests assert path-free fields, bounded digests, no guest name/session content/CTAP bytes, grant emitted by Core not Provider controller, and lifecycle emitted by controller. | | Removal proof | None — net-new; no prior owner to remove | -### W-N15 +### ADR046-security-key-022 | Field | Value | | --- | --- | -| Dependency/owner | Observability owner; depends on W-N03 relay, W-N02 controller, and ADR-046-telemetry-audit-and-support. | +| Dependency/owner | Observability owner; depends on ADR046-security-key-010 relay, ADR046-security-key-009 controller, and ADR-046-telemetry-audit-and-support. | | Current source | None — net-new v3 work; no pre-ADR45 baseline equivalent | -| Reuse action | net-new | +| Reuse action | create | | Destination | Provider/controller bounded telemetry emitter and observability-otel handoff for security-key metrics | | Detailed design | OTEL metrics: `d2b_device_sk_session_total`, `d2b_device_sk_ceremony_duration_seconds`, `d2b_device_sk_relay_restarts_total` via bounded emitter ring | | Integration | Relay/controller write metric events to the bounded ring; observability-otel Provider drains and exports; dashboards/CLI consume closed labels and bounded histograms. | @@ -2196,13 +2196,13 @@ class. | Validation | Metrics tests assert closed label sets, no device/session/guest/path labels, bounded ring behavior, and correct session/ceremony/restart counters. | | Removal proof | None — net-new; no prior owner to remove | -### W-N16 +### ADR046-security-key-023 | Field | Value | | --- | --- | -| Dependency/owner | Provider documentation owner; depends on W-N01 through W-N15 and W-N17 through W-N22 for complete behavior. | +| Dependency/owner | Provider documentation owner; depends on ADR046-security-key-008 through ADR046-security-key-022 and ADR046-security-key-024 through ADR046-security-key-029 for complete behavior. | | Current source | None — net-new v3 work; no pre-ADR45 baseline equivalent | -| Reuse action | net-new | +| Reuse action | create | | Destination | `packages/d2b-provider-device-security-key/README.md` | | Detailed design | README: initial Provider identity, provider-neutral Service/Binding catalog, strict Provider-extension fields, physical Device, owner/export/import/projection/Binding chain, process ownership, RBAC, invariants, status/telemetry, no-alias rule, and commands | | Integration | Workspace/package policy and provider crate acceptance use the README as the human entry point; docs link to it for provider-local build/test/integration commands. | @@ -2210,13 +2210,13 @@ class. | Validation | README presence check from provider crate standard layout; documentation review verifies every listed section and command is present and matches the crate/package behavior. | | Removal proof | None — net-new; no prior owner to remove | -### W-N17 +### ADR046-security-key-024 | Field | Value | | --- | --- | -| Dependency/owner | Endpoint/ComponentSession integration owner; depends on W-R03, W-N03, W-N07, W-N09, W-N19, and ADR-046-componentsession-and-bus. | +| Dependency/owner | Endpoint/ComponentSession integration owner; depends on ADR046-security-key-003, ADR046-security-key-010, ADR046-security-key-014, ADR046-security-key-016, ADR046-security-key-026, and ADR-046-componentsession-and-bus. | | Current source | None — net-new v3 work; no pre-ADR45 baseline equivalent | -| Reuse action | net-new | +| Reuse action | create | | Destination | Authority/projection Service Endpoint and Binding private Endpoint resolution, including transport-vsock and ZoneLink encrypted streams | | Detailed design | Resolve each Binding only through its same-Zone Service Endpoint; enroll Noise KK for Service/Binding frontend; authority uses transport-vsock locally, projection uses per-import bounded encrypted stream with credits/backpressure/generation/deadline/cancel. | | Integration | Service/Binding-owned Endpoints produce opaque LaunchTicket attachments; the import adapter binds the projection Service's ordinary local import-route Endpoint; no remote Ref, FD, or raw locator is exposed. | @@ -2224,13 +2224,13 @@ class. | Validation | `host_relay_guest_frontend/` and `descriptor_validation.rs` verify Endpoint resolution, Noise KK enrollment, attachment opacity, and no raw vsock CID/port in status/spec. | | Removal proof | Supersedes baseline `vsock.sock_14320` raw port usage; tests prove no `vsockPort` or raw AF_VSOCK framing remains for security-key transport. | -### W-N18 +### ADR046-security-key-025 | Field | Value | | --- | --- | -| Dependency/owner | `d2b-contracts` neutral effect-port foundation owner; depends on W-N01 and ADR-046-resources-device. | +| Dependency/owner | `d2b-contracts` neutral effect-port foundation owner; depends on ADR046-security-key-008 and ADR-046-resources-device. | | Current source | None — net-new v3 work; no pre-ADR45 baseline equivalent | -| Reuse action | net-new | +| Reuse action | create | | Destination | `d2b-contracts` neutral `SecurityKeyEffectPort` trait/types; `packages/d2b-provider-device-security-key/src/effect_port.rs` re-export; Core adapter implementation in `d2b-provider` or `d2b-provider-toolkit` | | Detailed design | Define/re-export the opaque redacting `SecurityKeyEffectPort` types in the neutral contract crate and implement the Core adapter; inject per physical Device into the Provider controller; relay and projection Service do not receive the port. | | Integration | Core resolves Zone/label to opaque IDs and injects the port into the controller; controller scheduled-observe calls the trait; Provider crate depends only on the neutral contract/re-export; relay path is unaffected. | @@ -2238,134 +2238,134 @@ class. | Validation | Unit tests assert Debug redaction, controller calls `observe_inventory` with injected IDs, relay has no port dependency, and fake Core adapter returns bounded `InventoryObservation`. | | Removal proof | None — net-new; no prior owner to remove | -### W-N19 +### ADR046-security-key-026 | Field | Value | | --- | --- | -| Dependency/owner | Device-security-key Service/Binding implementation owner; depends on ADR046-provider-004, W-N01, W-R05, resource object/Device/D096/D097 contracts. | +| Dependency/owner | Device-security-key Service/Binding implementation owner; depends on ADR046-provider-004, ADR046-security-key-008, ADR046-security-key-005, resource object/Device/D096/D097 contracts. | | Current source | None — net-new v3 work; no pre-ADR45 baseline equivalent | -| Reuse action | net-new | +| Reuse action | create | | Destination | `packages/d2b-provider-device-security-key/src/{resource_type,provider_extension,admission}.rs`; controller contracts; system-core Guest UHID authority-subresource DeviceGrant (common base lives under ADR046-provider-004) | | Detailed design | Bind the shared semantic authority/projection Service and Binding base versions/fingerprints from ADR046-provider-004, then define only the initial strict Provider extension and admission. The owner/Binding extension references the local physical Device/relay Endpoint and owns CTAPHID/fairness/frontend settings and observations. Projection is Core-owned by ResourceImport with `providerRef` plus semantic base/import fields, no `spec.provider`, and no Device/open; routing derives from the signed local descriptor, `providerRef`, and import record. Export admission binds the authority Service's `resourceRef` and `serviceType` to the signed projection-schema and factory fingerprints, never to its Endpoint. Binding is operator intent and the initial extension realizes its frontend Process/private Endpoint. Standard Device remains physical only; provider-named ResourceType aliases are rejected. | | Integration | ResourceExport targets Service with canonical type/fingerprint fields; ResourceImport supplies matching expected fields and creates projection Service; Binding references same-Zone Service; Service controllers retain Endpoint ownership; Core injects Guest UHID without a Device row. | | Data migration | Full d2b 3.0 reset; no legacy Device/claim projection import | | Validation | Fast schema/lifecycle conformance consumes the ADR046-provider-004 fixtures, accepts canonical minimal base without `spec.provider`, includes a fake alternate security-key Provider, and proves Device→provider-neutral Service→export→import→projection Service→provider-neutral Binding→frontend, exact canonical Export/Import fields, no Endpoint export, projection `spec.provider` rejection, D088 status layering, strict base/Provider-extension separation, exact types with no aliases, strict ownership/finalizers, no Device projection, and no local hidraw open in consumer Zone. | -| Removal proof | Supersedes legacy frontend/import Device modeling; W-X06 removes udev mutation and W-X03 removes the legacy unit once Binding-owned realization is live. | +| Removal proof | Supersedes legacy frontend/import Device modeling; ADR046-security-key-035 removes udev mutation and ADR046-security-key-032 removes the legacy unit once Binding-owned realization is live. | ### Removal items -### W-X01 +### ADR046-security-key-030 | Field | Value | | --- | --- | -| Dependency/owner | Provider-device-security-key removal owner; depends on W-R01, W-R02, W-N03, W-N04, and W-N05 successor relay/session coverage. | +| Dependency/owner | Provider-device-security-key removal owner; depends on ADR046-security-key-001, ADR046-security-key-002, ADR046-security-key-010, ADR046-security-key-011, and ADR046-security-key-012 successor relay/session coverage. | | Current source | `packages/d2bd/src/security_key.rs` — `start_sk_accept_loop`, `SecurityKeyState`, `LeaseState`, `SkRegistry` | -| Reuse action | delete | +| Reuse action | delete-after-cutover | | Destination | Removed from daemon; successor behavior lives in `packages/d2b-provider-device-security-key/src/relay.rs`, `session.rs`, and `cid.rs` | -| Detailed design | Remove target `packages/d2bd/src/security_key.rs` — `start_sk_accept_loop`, `SecurityKeyState`, `LeaseState`, `SkRegistry` after v3 relay Process is live and stable; keep behind feature gate only if needed during transition. | +| Detailed design | Remove target `packages/d2bd/src/security_key.rs` — `start_sk_accept_loop`, `SecurityKeyState`, `LeaseState`, `SkRegistry` after v3 relay Process is live and stable; keep behind feature gate only if needed during transition. Primary reuse disposition: `delete-after-cutover`. Preserved source-plan detail: delete. | | Integration | d2bd no longer owns security-key accept/session state; Provider controller, authority Service relay, and Binding frontends own lifecycle; Core LaunchTicket owns hidraw/UHID grants. | | Data migration | Full d2b 3.0 reset; no daemon session state migration | | Validation | Provider relay/session tests pass; daemon build has no references to removed symbols; no legacy security-key accept loop starts under d2bd. | | Removal proof | Concrete removed path/behavior: `packages/d2bd/src/security_key.rs` `start_sk_accept_loop`, `SecurityKeyState`, `LeaseState`, and `SkRegistry` daemon-internal accept/session ownership are absent. | -### W-X02 +### ADR046-security-key-031 | Field | Value | | --- | --- | -| Dependency/owner | d2bd integration removal owner; depends on W-X01. | +| Dependency/owner | d2bd integration removal owner; depends on ADR046-security-key-030. | | Current source | `packages/d2bd/src/lib.rs` — `start_sk_accept_loop` call site and daemon-internal Unix socket proxy bind | -| Reuse action | delete | +| Reuse action | delete-after-cutover | | Destination | Removed from daemon startup; successor launch path is ProviderDeployment/controller-created relay Process plus Endpoint/ComponentSession transport | -| Detailed design | Remove target `packages/d2bd/src/lib.rs` — `start_sk_accept_loop` call site and daemon-internal Unix socket proxy bind after W-X01. | +| Detailed design | Remove target `packages/d2bd/src/lib.rs` — `start_sk_accept_loop` call site and daemon-internal Unix socket proxy bind after ADR046-security-key-030. Primary reuse disposition: `delete-after-cutover`. Preserved source-plan detail: delete. | | Integration | d2bd startup no longer binds a security-key Unix socket proxy; Core/ProviderDeployment starts provider controller and relay Process resources; transport-vsock Endpoint supplies frontend connectivity. | | Data migration | Full d2b 3.0 reset; no daemon socket state migration | | Validation | d2bd startup tests/build prove no `start_sk_accept_loop` call or security-key proxy bind remains; provider integration test proves CTAPHID flow through Endpoint/ComponentSession. | | Removal proof | Concrete removed path/behavior: `packages/d2bd/src/lib.rs` no longer calls `start_sk_accept_loop` and no longer binds the daemon-internal security-key Unix socket proxy. | -### W-X03 +### ADR046-security-key-032 | Field | Value | | --- | --- | -| Dependency/owner | Guest Nix module removal owner; depends on W-N13, W-R03, and W-N19 Binding frontend/UHID contract. | +| Dependency/owner | Guest Nix module removal owner; depends on ADR046-security-key-020, ADR046-security-key-003, and ADR046-security-key-026 Binding frontend/UHID contract. | | Current source | `nixos-modules/components/security-key-guest.nix` — `d2b-sk-frontend.service` systemd unit declaration | -| Reuse action | delete | +| Reuse action | delete-after-cutover | | Destination | Removed from guest Nix module; successor is Binding-owned `Process/binding--sk-frontend` | -| Detailed design | Remove target `nixos-modules/components/security-key-guest.nix` — `d2b-sk-frontend.service` systemd unit declaration after W-N13 migration gate defaults to false. | +| Detailed design | Remove target `nixos-modules/components/security-key-guest.nix` — `d2b-sk-frontend.service` systemd unit declaration after ADR046-security-key-020 migration gate defaults to false. Primary reuse disposition: `delete-after-cutover`. Preserved source-plan detail: delete. | | Integration | Guest Nix keeps `uhid` and frontend binary only; Provider controller creates the Binding-owned frontend; system-systemd manages it. | | Data migration | Full d2b 3.0 reset; no legacy unit state migration | | Validation | Nix eval tests prove no static `d2b-sk-frontend.service` is emitted with Provider installed; frontend Process integration proves replacement lifecycle. | | Removal proof | Concrete removed path/behavior: `nixos-modules/components/security-key-guest.nix` no longer declares the static `d2b-sk-frontend.service` unit. | -### W-X04 +### ADR046-security-key-033 | Field | Value | | --- | --- | -| Dependency/owner | Test-suite migration/removal owner; depends on W-R06 and W-R07 provider-crate successor tests. | +| Dependency/owner | Test-suite migration/removal owner; depends on ADR046-security-key-006 and ADR046-security-key-007 provider-crate successor tests. | | Current source | `packages/d2b-contract-tests/tests/minijail_sk_frontend.rs` and `packages/d2b-contract-tests/tests/usb_sk_contract.rs` | -| Reuse action | delete after move/adapt | +| Reuse action | delete-after-cutover | | Destination | Removed from `packages/d2b-contract-tests/tests/`; successor tests live in `packages/d2b-provider-device-security-key/tests/` | -| Detailed design | Remove target `packages/d2b-contract-tests/tests/minijail_sk_frontend.rs` and `packages/d2b-contract-tests/tests/usb_sk_contract.rs` after W-R06/W-R07 tests are in Provider crate and cover all prior assertions. | +| Detailed design | Remove target `packages/d2b-contract-tests/tests/minijail_sk_frontend.rs` and `packages/d2b-contract-tests/tests/usb_sk_contract.rs` after ADR046-security-key-006/ADR046-security-key-007 tests are in Provider crate and cover all prior assertions. Primary reuse disposition: `delete-after-cutover`. Preserved source-plan detail: delete after move/adapt. | | Integration | D094 disposition updates closed gate manifests, layer1 jobs, pins, ledgers, and CI shards so only the provider-crate successor suite remains. | | Data migration | None — test-only move/delete; no runtime state | | Validation | Provider-crate tests pass with retained assertions; old contract-test paths are absent from manifests/CI; no duplicate old/new suite runs indefinitely. | | Removal proof | Concrete removed paths: `packages/d2b-contract-tests/tests/minijail_sk_frontend.rs` and `packages/d2b-contract-tests/tests/usb_sk_contract.rs` are deleted after provider-crate successor coverage passes. | -### W-X05 +### ADR046-security-key-034 | Field | Value | | --- | --- | -| Dependency/owner | Core ProcessRole removal owner; depends on W-N09 Process resources, W-N08 sandbox templates, and system-minijail/system-systemd conformance. | +| Dependency/owner | Core ProcessRole removal owner; depends on ADR046-security-key-016 Process resources, ADR046-security-key-015 sandbox templates, and system-minijail/system-systemd conformance. | | Current source | `ProcessRole::SecurityKeyFrontend` in `d2b-core/src/processes.rs` | -| Reuse action | delete | +| Reuse action | delete-after-cutover | | Destination | Removed from `d2b-core/src/processes.rs`; successor frontend is a v3 Process resource owned by `Provider/device-security-key` | -| Detailed design | Remove target `ProcessRole::SecurityKeyFrontend` in `d2b-core/src/processes.rs` after relay and frontend are v3 Process resources; no other code reference expected. | +| Detailed design | Remove target `ProcessRole::SecurityKeyFrontend` in `d2b-core/src/processes.rs` after relay and frontend are v3 Process resources; no other code reference expected. Primary reuse disposition: `delete-after-cutover`. Preserved source-plan detail: delete. | | Integration | ProcessRole disposition table confirms all security-key frontend lifecycle, sandbox, readiness, and DeviceGrant semantics are represented by Resource Process templates and Process Providers before enum removal. | | Data migration | Full d2b 3.0 reset; no processes.json role migration | | Validation | Workspace build proves no `ProcessRole::SecurityKeyFrontend` references; provider Process template tests prove the v3 replacement; process conformance passes. | | Removal proof | Concrete removed path/behavior: `d2b-core/src/processes.rs` no longer contains `ProcessRole::SecurityKeyFrontend` or a security-key frontend role in the legacy ProcessRole/VmProcessDag model. | -### W-X06 +### ADR046-security-key-035 | Field | Value | | --- | --- | -| Dependency/owner | Broker/contracts/Nix removal owner; depends on W-R05, W-N11, W-N13, and W-N19 Guest-substrate UHID replacement. | +| Dependency/owner | Broker/contracts/Nix removal owner; depends on ADR046-security-key-005, ADR046-security-key-018, ADR046-security-key-020, and ADR046-security-key-026 Guest-substrate UHID replacement. | | Current source | `SecurityKeyApplyUdevRules` broker op, `SecurityKeyApplyUdevRulesRequest` DTO in `packages/d2b-contracts/src/security_key.rs`, and all related broker code | -| Reuse action | delete | +| Reuse action | delete-after-cutover | | Destination | Removed from contracts and broker; successor access is static guest Nix `uhid` module plus Core pre-opened `/dev/uhid` DeviceGrant for the frontend Process | -| Detailed design | Remove `SecurityKeyApplyUdevRules`, its DTO, and related broker code after the Binding-owned frontend and system-core UHID DeviceGrant are live. Guest Nix loads `uhid` but emits no security-key udev rule. | +| Detailed design | Remove `SecurityKeyApplyUdevRules`, its DTO, and related broker code after the Binding-owned frontend and system-core UHID DeviceGrant are live. Guest Nix loads `uhid` but emits no security-key udev rule. Primary reuse disposition: `delete-after-cutover`. Preserved source-plan detail: delete. | | Integration | Guest Nix/Process DeviceGrant path provides UHID access; contracts no longer expose the op/request; broker capability set drops the udev mutation; provider/contract tests assert absence. | | Data migration | Full d2b 3.0 reset; no udev rule state migration | | Validation | DTO unknown-field/capability tests prove `SecurityKeyApplyUdevRulesRequest` and op are absent; `device_grant_no_path.rs` proves frontend has UHID fd without udev/plugdev; broker build has no related code. | | Removal proof | Concrete removed path/behavior: `SecurityKeyApplyUdevRules` broker operation, `SecurityKeyApplyUdevRulesRequest` in `packages/d2b-contracts/src/security_key.rs`, and related broker code are absent. | -### W-N21 +### ADR046-security-key-028 | Field | Value | | --- | --- | -| Dependency/owner | Cross-Zone adapter owner; depends on W-N17, W-N19, W-N22, ADR046-zone-control-019, and ADR046-zone-control-020. | +| Dependency/owner | Cross-Zone adapter owner; depends on ADR046-security-key-024, ADR046-security-key-026, ADR046-security-key-029, ADR046-zone-control-019, and ADR046-zone-control-020. | | Current source | None — net-new ADR 0046 cross-Zone sharing (D096) | -| Reuse action | net-new (implement the signed security-key export/import adapter) | +| Reuse action | adapt | | Destination | `packages/d2b-provider-device-security-key/src/share_adapter.rs` | -| Detailed design | Signed adapters admit ResourceExport only when `resourceRef` names an authority SecurityKeyService, `serviceType` is `security-key.d2bus.org.SecurityKeyService`, and `projectionSchemaFingerprint` plus `factoryFingerprint` match the signed semantic factory. ResourceImport must supply the corresponding `expectedServiceType`, `expectedProjectionSchemaFingerprint`, and `expectedFactoryFingerprint`; its `exportKey` identifies the ResourceExport. The Service's relay Endpoint stays a Service-owned implementation child and is never an Export field. Core invokes the factory to create one projection SecurityKeyService with `ownerRef: ResourceImport/`, `providerRef`, semantic base/import fields, and no `spec.provider`; route selection comes from the signed local descriptor and ResourceImport record. The semantic factory fingerprint binds factory metadata plus projection-protocol version only, while adapter identity is authenticated separately by the signed Provider descriptor. They never project Device or auto-create Binding. Route Binding ceremonies over bounded encrypted named streams to the single authority fair queue; no FD/USBIP/hidraw/ref crosses Zones. | -| Integration | Core export/import routing/projection lifecycle; W-N22 authority; W-N17 Endpoint streams; Nix/operator-authored Binding consumes the same-Zone projection. | +| Detailed design | Signed adapters admit ResourceExport only when `resourceRef` names an authority SecurityKeyService, `serviceType` is `security-key.d2bus.org.SecurityKeyService`, and `projectionSchemaFingerprint` plus `factoryFingerprint` match the signed semantic factory. ResourceImport must supply the corresponding `expectedServiceType`, `expectedProjectionSchemaFingerprint`, and `expectedFactoryFingerprint`; its `exportKey` identifies the ResourceExport. The Service's relay Endpoint stays a Service-owned implementation child and is never an Export field. Core invokes the factory to create one projection SecurityKeyService with `ownerRef: ResourceImport/`, `providerRef`, semantic base/import fields, and no `spec.provider`; route selection comes from the signed local descriptor and ResourceImport record. The semantic factory fingerprint binds factory metadata plus projection-protocol version only, while adapter identity is authenticated separately by the signed Provider descriptor. They never project Device or auto-create Binding. Route Binding ceremonies over bounded encrypted named streams to the single authority fair queue; no FD/USBIP/hidraw/ref crosses Zones. Primary reuse disposition: `adapt`. Preserved source-plan detail: net-new (implement the signed security-key export/import adapter). | +| Integration | Core export/import routing/projection lifecycle; ADR046-security-key-029 authority; ADR046-security-key-024 Endpoint streams; Nix/operator-authored Binding consumes the same-Zone projection. | | Data migration | Full d2b 3.0 reset; no cross-Zone sharing state | | Validation | Fast fake-stream conformance proves owner Service→export→import→projection Service→Binding→frontend; exact canonical Export/Import type and fingerprint fields; rejection of Export `endpointRef`, `exportedType`, `baseSchemaFingerprint`, and `exportKey` plus Import `expectedType`, `expectedBaseSchemaFingerprint`, and `projectionType`; rejection of projection `spec.provider`; semantic factory-fingerprint stability when signed adapter identity changes; separate signed-descriptor identity authentication; one fair LeaseId-guarded ceremony; ciphertext to intermediaries; no Device projection/local hidraw/FD/USBIP; revocation degradation; and audit metadata only. | | Removal proof | Not applicable (new surface) | -### W-N22 +### ADR046-security-key-029 | Field | Value | | --- | --- | -| Dependency/owner | D097 authority foundation owner; depends on W-R01, W-R02, W-R03, W-R04, W-N11, W-N19, ADR046-zone-control-024, and the D097 authority contract. | +| Dependency/owner | D097 authority foundation owner; depends on ADR046-security-key-001, ADR046-security-key-002, ADR046-security-key-003, ADR046-security-key-004, ADR046-security-key-018, ADR046-security-key-026, ADR046-zone-control-024, and the D097 authority contract. | | Current source | `packages/d2bd/src/security_key.rs` (`CidTranslator`, `SecurityKeyState`, `LeaseId`/`LeaseState`, `CEREMONY_TIMEOUT` 120 s, `QUEUE_WAIT_TIMEOUT` 15 s, `parse_ctaphid_report`/`build_cancel_packet`); `packages/d2b-priv-broker/src/ops/security_key.rs` (`live_open_hidraw_security_key`, double `fstat` + FIDO usage-page 0xF1D0 + HID raw-info revalidation, `O_RDWR\|O_NONBLOCK\|O_NOFOLLOW`); `packages/d2b-sk-frontend/src/{main,uhid,vsock,framing}.rs` (UHID FIDO2 CTAPHID frontend, 64-byte report relay) | | Reuse source | Same baseline daemon/broker/frontend symbols | -| Reuse action | `adapt` — relay becomes the D097 hidraw authority; transport moves to Endpoint/named-stream | +| Reuse action | adapt | | Destination | `packages/d2b-provider-device-security-key/src/{authority,relay,streams}.rs`; D097 `AuthorityDescriptor` on authority SecurityKeyService | -| Detailed design | The provider-neutral authority Service, not Device/Endpoint/Process, is the stable D097 owner and carries the semantic opaque Host-scoped zero-or-one descriptor. The initial Provider extension references the local physical Device and relay Endpoint and supplies service-specific physical-key derivation, Service+relay ownerProof, and bounded-fairness details. After trusted USB identity resolution, Core additionally derives `physical-usb-backing/v1` and atomically claims the exact `(Host, physical-usb-backing, opaqueKeyDigest)` tuple used by every USB Provider before any open, withhold, bind, module, relay, or attachment effect; Provider-private claims cannot replace it. Preserve sole Core open with double-fstat/FIDO/HID validation, async fd I/O, per-session CidTranslator, LeaseId stale-release guard, cancel-all-CIDs, one ceremony, bounded FIFO wait, and Binding-owned UHID frontend. Ceremony rows are not Resources. | -| Integration | Authority Service owns relay/Endpoint; Core index admits it and LaunchTicket supplies physical DeviceGrant; W-N21 exports/imports Service; Binding owns frontend/private Endpoint; USBIP conflict remains Host-wide. | +| Detailed design | The provider-neutral authority Service, not Device/Endpoint/Process, is the stable D097 owner and carries the semantic opaque Host-scoped zero-or-one descriptor. The initial Provider extension references the local physical Device and relay Endpoint and supplies service-specific physical-key derivation, Service+relay ownerProof, and bounded-fairness details. After trusted USB identity resolution, Core additionally derives `physical-usb-backing/v1` and atomically claims the exact `(Host, physical-usb-backing, opaqueKeyDigest)` tuple used by every USB Provider before any open, withhold, bind, module, relay, or attachment effect; Provider-private claims cannot replace it. Preserve sole Core open with double-fstat/FIDO/HID validation, async fd I/O, per-session CidTranslator, LeaseId stale-release guard, cancel-all-CIDs, one ceremony, bounded FIFO wait, and Binding-owned UHID frontend. Ceremony rows are not Resources. Primary reuse disposition: `adapt`. Preserved source-plan detail: `adapt` — relay becomes the D097 hidraw authority; transport moves to Endpoint/named-stream. | +| Integration | Authority Service owns relay/Endpoint; Core index admits it and LaunchTicket supplies physical DeviceGrant; ADR046-security-key-028 exports/imports Service; Binding owns frontend/private Endpoint; USBIP conflict remains Host-wide. | | Data migration | Full d2b 3.0 reset; no per-session/lease state persisted | | Validation | Fast hermetic tests adapt the existing `CidTranslator`/lease/cancel/UHID/broker-revalidation suites: CID alloc/translate/release, `LeaseId` stale-release, cancel-all-CIDs on disconnect, one-ceremony + 120 s timeout, 15 s fair-wait `ERR_CHANNEL_BUSY`, UHID frame round-trip, broker double-`fstat`+FIDO+HID revalidation, byte-identical USB/security-key backing tuple derivation for one fake token, and `physical-usb-backing-conflict` before effects under alternate labels/private authority classes — all with fakes/`FakeEffectPort`, no real hidraw. Integration proves cross-Zone CTAP ceremony **serialization** over the encrypted named stream and the shared physical USB collision. | -| Removal proof | The legacy daemon accept loop, raw CTAPHID framing, fixed `SK_VSOCK_PORT`, and broker sysfs `/sys/class/hidraw/` scan fallback are deleted only after the relay `Endpoint`/named-stream successor and the `device_token`-only broker open are green (coordinated with W-X05 `ProcessRole` removal and the W-R broker-op revalidation item). | +| Removal proof | The legacy daemon accept loop, raw CTAPHID framing, fixed `SK_VSOCK_PORT`, and broker sysfs `/sys/class/hidraw/` scan fallback are deleted only after the relay `Endpoint`/named-stream successor and the `device_token`-only broker open are green (coordinated with ADR046-security-key-034 `ProcessRole` removal and ADR046-security-key-004 broker-op revalidation). | Per D094, each replaced current-code test is retired with an explicit keep/adapt/move/delete disposition and a removal gate: the minimum reusable @@ -2428,8 +2428,8 @@ per-test budget. | Existing test | Action | | --- | --- | -| `packages/d2b-contract-tests/tests/usb_sk_contract.rs` | Move to `packages/d2b-provider-device-security-key/tests/` as part of W-R06; update v3 type imports; retain all existing assertions | -| `packages/d2b-contract-tests/tests/minijail_sk_frontend.rs` | Move to `packages/d2b-provider-device-security-key/tests/` as part of W-R07; update for the v3 Process resource sandbox; retain zero-`capabilityClasses` and `seccompClass` assertions | +| `packages/d2b-contract-tests/tests/usb_sk_contract.rs` | Move to `packages/d2b-provider-device-security-key/tests/` as part of ADR046-security-key-006; update v3 type imports; retain all existing assertions | +| `packages/d2b-contract-tests/tests/minijail_sk_frontend.rs` | Move to `packages/d2b-provider-device-security-key/tests/` as part of ADR046-security-key-007; update for the v3 Process resource sandbox; retain zero-`capabilityClasses` and `seccompClass` assertions | | `packages/d2b-priv-broker/tests/security_key_broker.rs` | Retain in broker crate; update for v3 bundle table lookup path; add zone-field round-trip test | ## Nix option migration diff --git a/docs/specs/providers/ADR-046-provider-device-tpm.md b/docs/specs/providers/ADR-046-provider-device-tpm.md index e75565a70..64581692e 100644 --- a/docs/specs/providers/ADR-046-provider-device-tpm.md +++ b/docs/specs/providers/ADR-046-provider-device-tpm.md @@ -1254,9 +1254,9 @@ v3: the Nix Device declaration in §17.1 replaces this option. Migration steps: | --- | --- | | Dependency/owner | P0; unblocked; owner: `d2b-provider-device-tpm` crate | | Current source | No existing provider crate; baseline TPM behavior is in `nixos-modules/components/tpm.nix`, `packages/d2b-host/src/swtpm_argv.rs`, and broker/daemon swtpm paths listed in §18 | -| Reuse action | net-new crate scaffold; later items adapt baseline TPM behavior into the new Provider boundary | +| Reuse action | adapt | | Destination | packages/d2b-provider-device-tpm/{src/,tests/,integration/README.md,README.md}; Cargo workspace membership | -| Detailed design | Crate scaffold: create `packages/d2b-provider-device-tpm/` with `src/`, `tests/`, `integration/README.md`, and `README.md`; add it to the Cargo workspace; workspace policy test must pass. | +| Detailed design | Crate scaffold: create `packages/d2b-provider-device-tpm/` with `src/`, `tests/`, `integration/README.md`, and `README.md`; add it to the Cargo workspace; workspace policy test must pass. Primary reuse disposition: `adapt`. Preserved source-plan detail: net-new crate scaffold; later items adapt baseline TPM behavior into the new Provider boundary. | | Integration | Workspace policy and Cargo consume the new crate; all controller, effect-port, resource-builder, status, Nix, and integration work lands under this scaffold. | | Data migration | None — scaffold only; TPM state migration is covered by later Volume/Nix work per §17.3 | | Validation | Workspace policy test for required crate paths and Cargo workspace membership | @@ -1272,9 +1272,9 @@ policy test must pass. | --- | --- | | Dependency/owner | P0; blocked by ADR046-device-tpm-001; owner: device-tpm effect boundary | | Current source | `PrepareSwtpmDir` in `packages/d2b-priv-broker/src/ops/swtpm_dir.rs` and `SpawnRunner { role: Swtpm }` in `packages/d2b-priv-broker/src/ops/spawn_runner.rs` remain privileged executors, but the controller must not import broker crates | -| Reuse action | wrap privileged effects behind an injected async `TpmEffectPort`; keep broker operations only behind `volume-local` and `system-minijail` | +| Reuse action | wrap | | Destination | packages/d2b-provider-device-tpm/src/{effect_port.rs,effect_impl.rs}; packages/d2b-provider-device-tpm/tests/effect_fake.rs | -| Detailed design | TpmEffectPort and FakeTpmEffectPort: implement the effect trait, typed TPM EndpointRef handoff, and fake test port. Prove non-test files contain no `use d2b_priv_broker::` and the controller sees only opaque resource IDs and EndpointRefs. | +| Detailed design | TpmEffectPort and FakeTpmEffectPort: implement the effect trait, typed TPM EndpointRef handoff, and fake test port. Prove non-test files contain no `use d2b_priv_broker::` and the controller sees only opaque resource IDs and EndpointRefs. Primary reuse disposition: `wrap`. Preserved source-plan detail: wrap privileged effects behind an injected async `TpmEffectPort`; keep broker operations only behind `volume-local` and `system-minijail`. | | Integration | Device controller calls `TpmEffectPort`; ResourceClient-backed implementation talks to ResourceAPI/ComponentSession; `volume-local` and `system-minijail` translate resource operations into broker effects. | | Data migration | Existing TPM state migration follows §17.3: the old `/var/lib/d2b/vms//swtpm/` directory moves to the controller-created Volume path with the provisioning marker preserved and re-keyed; this item must not silently recreate missing state. | | Validation | `tests/effect_fake.rs`; static proof that non-test files do not import `d2b_priv_broker` | @@ -1289,9 +1289,9 @@ Implement `TpmEffectPort` trait, typed TPM EndpointRef handoff, and | --- | --- | | Dependency/owner | P0; blocked by ADR046-device-tpm-002; owner: device-tpm controller FSM | | Current source | Current direct daemon/broker swtpm lifecycle call sites in `packages/d2bd/src/*` are superseded; controller algorithm is specified in §11.1 | -| Reuse action | replace direct daemon lifecycle with Provider reconcile against `FakeTpmEffectPort` and resource status | +| Reuse action | replace | | Destination | packages/d2b-provider-device-tpm/src/controller.rs; packages/d2b-provider-device-tpm/tests/controller_fsm.rs | -| Detailed design | Controller reconcile state machine: implement the Device reconcile algorithm from §11.1 against `FakeTpmEffectPort`, covering happy path, Volume not-ready, marker fail-closed, flush failure, swtpm maxRestarts, and finalizer behavior where Process is deleted and Volume retained. | +| Detailed design | Controller reconcile state machine: implement the Device reconcile algorithm from §11.1 against `FakeTpmEffectPort`, covering happy path, Volume not-ready, marker fail-closed, flush failure, swtpm maxRestarts, and finalizer behavior where Process is deleted and Volume retained. Primary reuse disposition: `replace`. Preserved source-plan detail: replace direct daemon lifecycle with Provider reconcile against `FakeTpmEffectPort` and resource status. | | Integration | Resource watches drive the controller; controller creates/observes Volume, Process, EphemeralProcess, and Endpoint resources through `TpmEffectPort`; Device status/finalizers expose outcomes to the ResourceAPI. | | Data migration | Existing TPM state migration follows §17.3: the old `/var/lib/d2b/vms//swtpm/` directory moves to the controller-created Volume path with the provisioning marker preserved and re-keyed; this item must not silently recreate missing state. | | Validation | `tests/controller_fsm.rs` covering happy path, Volume not-ready, marker fail-closed, flush failed, swtpm maxRestarts, and finalizer behavior | @@ -1307,9 +1307,9 @@ swtpm maxRestarts, finalizer (Process deleted; Volume retained). | --- | --- | | Dependency/owner | P0; blocked by ADR046-device-tpm-001; owner: device-tpm resource builders | | Current source | `nixos-modules/components/tpm.nix` declares TPM enablement today; §17.3 defines migration from the existing swtpm directory and marker | -| Reuse action | replace VM-level TPM option/state path with controller-created Device-owned Volume spec | +| Reuse action | replace | | Destination | packages/d2b-provider-device-tpm/src/resources.rs; packages/d2b-provider-device-tpm/tests/volume_create.rs | -| Detailed design | Controller-created Volume spec: implement `build_tpm_state_volume_spec` with `cleanupPolicy: never`, `repairPolicy: fail-closed`, `adoptionPolicy: quarantine-on-ambiguity`, `sensitivity: secret-adjacent`, required invariants, `source.sourceId`, no `hostPath`, no top-level identityMarker/persistenceClass/quotaBytes/stateSchema, `ownerRef: Device/`, `managedBy: controller`, empty attachments, and `quota: null`. | +| Detailed design | Controller-created Volume spec: implement `build_tpm_state_volume_spec` with `cleanupPolicy: never`, `repairPolicy: fail-closed`, `adoptionPolicy: quarantine-on-ambiguity`, `sensitivity: secret-adjacent`, required invariants, `source.sourceId`, no `hostPath`, no top-level identityMarker/persistenceClass/quotaBytes/stateSchema, `ownerRef: Device/`, `managedBy: controller`, empty attachments, and `quota: null`. Primary reuse disposition: `replace`. Preserved source-plan detail: replace VM-level TPM option/state path with controller-created Device-owned Volume spec. | | Integration | Device controller creates the TPM data Volume; `volume-local` materializes/protects state and marker; swtpm Process mounts the Volume; Guest runtime receives only EndpointRefs. | | Data migration | Existing TPM state migration follows §17.3: the old `/var/lib/d2b/vms//swtpm/` directory moves to the controller-created Volume path with the provisioning marker preserved and re-keyed; this item must not silently recreate missing state. | | Validation | `tests/volume_create.rs` proving every canonical Volume field and forbidden field listed in this item | @@ -1331,9 +1331,9 @@ Implement `build_tpm_state_volume_spec` in `resources.rs`. Tests prove: | --- | --- | | Dependency/owner | P0; blocked by ADR046-device-tpm-004; owner: device-tpm Process resource builder | | Current source | `SwtpmArgvInput` in `packages/d2b-host/src/swtpm_argv.rs`; `ProcessRole::Swtpm` in `packages/d2b-core/src/processes.rs`; `minijail_swtpm_video.rs` contract tests | -| Reuse action | extract swtpm argv/sandbox intent into canonical Process resources; remove caller-supplied binary path, UID, GID, and socket path fields | +| Reuse action | adapt | | Destination | packages/d2b-provider-device-tpm/src/resources.rs; Process spec tests under packages/d2b-provider-device-tpm/tests/ | -| Detailed design | Canonical swtpm Process spec: implement `build_swtpm_process_spec` with `readOnlyRoot: true`, `userNamespace.mappingClass: process-principal-root`, namespace classes `[pid, mount, user]`, empty capability classes, `seccompClass: w1-swtpm`, two Device-owned Endpoint resources (`tpm` and `ctrl`), `mounts[0].required: true`, and no socket path, binary path, UID integer, or GID integer in any spec field. | +| Detailed design | Canonical swtpm Process spec: implement `build_swtpm_process_spec` with `readOnlyRoot: true`, `userNamespace.mappingClass: process-principal-root`, namespace classes `[pid, mount, user]`, empty capability classes, `seccompClass: w1-swtpm`, two Device-owned Endpoint resources (`tpm` and `ctrl`), `mounts[0].required: true`, and no socket path, binary path, UID integer, or GID integer in any spec field. Primary reuse disposition: `adapt`. Preserved source-plan detail: extract swtpm argv/sandbox intent into canonical Process resources; remove caller-supplied binary path, UID, GID, and socket path fields. | | Integration | Controller emits the Process spec; `system-minijail` consumes it and invokes broker `SpawnRunner`; Endpoint resources publish TPM and control sockets for downstream consumers. | | Data migration | Existing TPM state migration follows §17.3: the old `/var/lib/d2b/vms//swtpm/` directory moves to the controller-created Volume path with the provisioning marker preserved and re-keyed; this item must not silently recreate missing state. | | Validation | Process spec golden tests proving all required and forbidden fields; preserved `minijail_swtpm_video.rs` contract tests | @@ -1354,9 +1354,9 @@ Implement `build_swtpm_process_spec` in `resources.rs`. Tests prove: | --- | --- | | Dependency/owner | P0; blocked by ADR046-device-tpm-003; owner: device-tpm EphemeralProcess resource builder | | Current source | `SwtpmIoctlFlushInput` in `packages/d2b-host/src/swtpm_argv.rs`; `ProcessRole::SwtpmPreStartFlush` in `packages/d2b-core/src/processes.rs` | -| Reuse action | extract flush intent into mandatory EphemeralProcess resource; remove configurable `startupClear` path and caller-supplied binary path fields | +| Reuse action | adapt | | Destination | packages/d2b-provider-device-tpm/src/resources.rs; packages/d2b-provider-device-tpm/tests/flush_mandatory.rs | -| Detailed design | Mandatory flush EphemeralProcess spec: implement `build_flush_ephemeral_process_spec`; no `startupClear` field exists; flush is always created before swtpm Process start with no skip path; TTLs are `successfulTtl: "1h"` and `failedTtl: "24h"`; no userNamespace on flush Process; deadlines are `startDeadline: "30s"` and `runtimeDeadline: "60s"`. | +| Detailed design | Mandatory flush EphemeralProcess spec: implement `build_flush_ephemeral_process_spec`; no `startupClear` field exists; flush is always created before swtpm Process start with no skip path; TTLs are `successfulTtl: "1h"` and `failedTtl: "24h"`; no userNamespace on flush Process; deadlines are `startDeadline: "30s"` and `runtimeDeadline: "60s"`. Primary reuse disposition: `adapt`. Preserved source-plan detail: extract flush intent into mandatory EphemeralProcess resource; remove configurable `startupClear` path and caller-supplied binary path fields. | | Integration | Controller inserts the EphemeralProcess before every swtpm activation cycle; Process provider runs the flush against the control Endpoint fd before the long-lived swtpm Process becomes Ready. | | Data migration | Existing TPM state migration follows §17.3: the old `/var/lib/d2b/vms//swtpm/` directory moves to the controller-created Volume path with the provisioning marker preserved and re-keyed; this item must not silently recreate missing state. | | Validation | `tests/flush_mandatory.rs` plus contract proof that user-NS is long-lived only | @@ -1375,9 +1375,9 @@ Implement `build_flush_ephemeral_process_spec` in `resources.rs`. Tests prove: | --- | --- | | Dependency/owner | P1; blocked by ADR046-device-tpm-003; owner: device-tpm status builder | | Current source | Existing status/path observations from direct daemon TPM paths are superseded; status fields are defined in §10 | -| Reuse action | net-new bounded Device status projection; do not reuse path/socket/UID/GID/PID observations | +| Reuse action | create | | Destination | packages/d2b-provider-device-tpm/src/status.rs; packages/d2b-provider-device-tpm/tests/{endpoint_ref.rs,redaction.rs} | -| Detailed design | Device status builder: implement `build_device_status`; `tpmEndpointRef` is an `Endpoint/` ResourceRef with no opaque endpoint ID compatibility alias and never a filesystem path; `stateVolumeRef` and `swtpmProcessRef` are canonical ResourceRef strings; no path, socket name, UID, GID, PID, or pidfd appears in status; `markerStatus` is one of `verified`, `missing`, `replaced`, or `unknown`. | +| Detailed design | Device status builder: implement `build_device_status`; `tpmEndpointRef` is an `Endpoint/` ResourceRef with no opaque endpoint ID compatibility alias and never a filesystem path; `stateVolumeRef` and `swtpmProcessRef` are canonical ResourceRef strings; no path, socket name, UID, GID, PID, or pidfd appears in status; `markerStatus` is one of `verified`, `missing`, `replaced`, or `unknown`. Primary reuse disposition: `create`. Preserved source-plan detail: net-new bounded Device status projection; do not reuse path/socket/UID/GID/PID observations. | | Integration | Controller writes Device status; Guest runtime Provider reads EndpointRef from Device status; CLI/support tooling reads bounded non-secret status. | | Data migration | None — status is re-derived during v3 reconcile; TPM state migration remains the Volume/marker migration in §17.3 | | Validation | `tests/endpoint_ref.rs`; `tests/redaction.rs`; status builder tests for allowed `markerStatus` values | @@ -1396,9 +1396,9 @@ Implement `build_device_status` in `status.rs`. Tests prove: | --- | --- | | Dependency/owner | P1; blocked by ADR046-device-tpm-007; owner: device-tpm endpoint handoff integration | | Current source | Baseline guest wiring consumed socket paths from TPM sidecar state; v3 handoff is the Endpoint resource contract in §8.4 and §10.5 | -| Reuse action | replace path handoff with EndpointRef and Zone runtime endpoint resolver fd acquisition | +| Reuse action | replace | | Destination | packages/d2b-provider-device-tpm/src/{effect_port.rs,status.rs}; packages/d2b-provider-device-tpm/integration/guest_endpoint.rs | -| Detailed design | EndpointRef handoff: hermetic tests prove `tpmEndpointRef` is an EndpointRef and never a path; integration proves Guest runtime Provider reads `tpmEndpointRef` and obtains the socket fd from the Zone runtime endpoint resolver with no path string in Guest spec or LaunchTicket API surface. | +| Detailed design | EndpointRef handoff: hermetic tests prove `tpmEndpointRef` is an EndpointRef and never a path; integration proves Guest runtime Provider reads `tpmEndpointRef` and obtains the socket fd from the Zone runtime endpoint resolver with no path string in Guest spec or LaunchTicket API surface. Primary reuse disposition: `replace`. Preserved source-plan detail: replace path handoff with EndpointRef and Zone runtime endpoint resolver fd acquisition. | | Integration | Device status publishes EndpointRef; Guest runtime Provider resolves the Endpoint through the Zone endpoint resolver; LaunchTicket receives an fd, not a socket path. | | Data migration | None — endpoint handoff has no state migration; TPM data migration remains §17.3 | | Validation | `tests/endpoint_ref.rs`; `integration/guest_endpoint.rs` | @@ -1415,9 +1415,9 @@ surface. | --- | --- | | Dependency/owner | P0; blocked by ADR046-device-tpm-004; owner: device-tpm marker/fail-closed tests | | Current source | Existing provisioning marker behavior in `/var/lib/d2b/swtpm-markers/` is preserved and re-keyed by volume-local per §17.3 | -| Reuse action | preserve fail-closed marker semantics while moving ownership to Volume/Device resources | +| Reuse action | adapt | | Destination | packages/d2b-provider-device-tpm/tests/marker_fail_closed.rs; packages/d2b-provider-device-tpm/integration/marker_tamper.rs | -| Detailed design | Marker fail-closed test: FakeTpmEffectPort returning `markerStatus: replaced` makes Device Failed, prevents a second `ensure_state_volume` call, and prevents swtpm Process creation. Integration physically replaces `swtpm/`; volume-local sets Volume Failed; Device fails with no auto-recovery. | +| Detailed design | Marker fail-closed test: FakeTpmEffectPort returning `markerStatus: replaced` makes Device Failed, prevents a second `ensure_state_volume` call, and prevents swtpm Process creation. Integration physically replaces `swtpm/`; volume-local sets Volume Failed; Device fails with no auto-recovery. Primary reuse disposition: `adapt`. Preserved source-plan detail: preserve fail-closed marker semantics while moving ownership to Volume/Device resources. | | Integration | volume-local observes marker state and reports Volume/marker status; controller maps that to Device failure and blocks Process creation; integration exercises the actual filesystem marker path through volume-local. | | Data migration | Existing TPM state migration follows §17.3: the old `/var/lib/d2b/vms//swtpm/` directory moves to the controller-created Volume path with the provisioning marker preserved and re-keyed; this item must not silently recreate missing state. | | Validation | `tests/marker_fail_closed.rs`; `integration/marker_tamper.rs` | @@ -1435,9 +1435,9 @@ Device Failed; no auto-recovery. | --- | --- | | Dependency/owner | P1; blocked by ADR046-device-tpm-001; owner: device-tpm controller Process descriptor | | Current source | None — net-new v3 work; no pre-ADR45 baseline equivalent | -| Reuse action | net-new status-first controller Process spec; no Provider state Volume is reused or created | +| Reuse action | create | | Destination | packages/d2b-provider-device-tpm/src/resources.rs; packages/d2b-provider-device-tpm/tests/controller_process.rs | -| Detailed design | Controller Process (status-first; no Provider state Volume): implement the controller Process spec from §4.1 with `processClass: controller`, `readOnlyRoot: true`, empty mounts, no controller-scratch namespace, no scratch mount, no `User/device-tpm-controller-system` state-layout principal, no permission to create Provider-owned Volumes, and restart re-derivation from resource store plus external marker/process observations while treating status as observation. | +| Detailed design | Controller Process (status-first; no Provider state Volume): implement the controller Process spec from §4.1 with `processClass: controller`, `readOnlyRoot: true`, empty mounts, no controller-scratch namespace, no scratch mount, no `User/device-tpm-controller-system` state-layout principal, no permission to create Provider-owned Volumes, and restart re-derivation from resource store plus external marker/process observations while treating status as observation. Primary reuse disposition: `create`. Preserved source-plan detail: net-new status-first controller Process spec; no Provider state Volume is reused or created. | | Integration | ProviderDeployment creates the controller Process from the descriptor; controller status and Operation ledger carry bounded non-secret observations; Device-owned TPM data Volume remains separate from ProviderStateSet. | | Data migration | None — controller has no Provider state Volume to migrate; Device data migration remains §17.3 | | Validation | Controller Process spec tests proving the bullets in this item | @@ -1464,9 +1464,9 @@ Implement controller Process spec (§4.1). Tests prove: | --- | --- | | Dependency/owner | P1; blocked by ADR046-device-tpm-001; owner: Nix Resource compiler for Device declarations | | Current source | `nixos-modules/components/tpm.nix` current `d2b.vms..tpm.enable` option is replaced by the Device declaration in §17.1 | -| Reuse action | replace VM-level TPM enable option with v3 Device resource emission and assertions | +| Reuse action | replace | | Destination | nixos-modules/options-resources.nix and Nix eval/golden tests for §17.1 Device JSON | -| Detailed design | Nix roundtrip test: Device Nix spec from §17.1 round-trips through the Nix emitter to expected resource JSON; emitted bundle contains no Volume, Process, or EphemeralProcess resources because controller-managed resources are not in the Nix bundle. | +| Detailed design | Nix roundtrip test: Device Nix spec from §17.1 round-trips through the Nix emitter to expected resource JSON; emitted bundle contains no Volume, Process, or EphemeralProcess resources because controller-managed resources are not in the Nix bundle. Primary reuse disposition: `replace`. Preserved source-plan detail: replace VM-level TPM enable option with v3 Device resource emission and assertions. | | Integration | Nix authoring emits only the Device and Provider resources; ResourceAPI admission hands the Device to the controller; controller creates managed resources at runtime. | | Data migration | Existing TPM state migration follows §17.3: the old `/var/lib/d2b/vms//swtpm/` directory moves to the controller-created Volume path with the provisioning marker preserved and re-keyed; this item must not silently recreate missing state. | | Validation | Nix roundtrip/golden test for §17.1 and emitted-bundle absence of controller-managed resources | @@ -1482,9 +1482,9 @@ resources (`managedBy: controller` resources are not in the Nix bundle). | --- | --- | | Dependency/owner | P0; blocked by ADR046-device-tpm-003; owner: device-tpm finalizer lifecycle | | Current source | Current TPM state retention behavior is tied to the swtpm directory and marker; v3 retention is `cleanupPolicy: never` on the Device-owned Volume | -| Reuse action | preserve TPM identity retention while moving deletion sequencing to Resource finalizers | +| Reuse action | adapt | | Destination | packages/d2b-provider-device-tpm/src/controller.rs; packages/d2b-provider-device-tpm/tests/finalizer.rs | -| Detailed design | Finalizer: Volume retained on Device deletion. Hermetic tests cover Device deletion finalizer leading to swtpm Process deletion, TPM state Volume not deleted because `cleanupPolicy: never`, Volume persists, Core emits `phase=Deleted` for Device after finalizer clears, and audit carries no path/UID. | +| Detailed design | Finalizer: Volume retained on Device deletion. Hermetic tests cover Device deletion finalizer leading to swtpm Process deletion, TPM state Volume not deleted because `cleanupPolicy: never`, Volume persists, Core emits `phase=Deleted` for Device after finalizer clears, and audit carries no path/UID. Primary reuse disposition: `adapt`. Preserved source-plan detail: preserve TPM identity retention while moving deletion sequencing to Resource finalizers. | | Integration | Resource deletion sets finalizer; controller deletes Process and retains Volume; core completes Deleted revision after finalizer clears; audit subsystem records redacted deletion outcome. | | Data migration | Existing TPM state migration follows §17.3: the old `/var/lib/d2b/vms//swtpm/` directory moves to the controller-created Volume path with the provisioning marker preserved and re-keyed; this item must not silently recreate missing state. | | Validation | `tests/finalizer.rs` proving Process deletion, retained Volume, core Deleted phase, and redacted audit | @@ -1500,9 +1500,9 @@ Volume NOT deleted (`cleanupPolicy: never`) → Volume persists. Core emits | --- | --- | | Dependency/owner | P0; blocked by ADR046-device-tpm-002; owner: device-tpm migration/removal cleanup | | Current source | `packages/d2bd/src/*` direct broker/swtpm call sites; `packages/d2b-core/src/processes.rs` `ProcessRole::Swtpm` and `ProcessRole::SwtpmPreStartFlush`; `packages/d2b-host/src/swtpm_argv.rs` argv builders | -| Reuse action | remove direct broker references from daemon and move argv builders into the Provider with binary path fields removed; retain broker ops only behind resource providers | +| Reuse action | delete-after-cutover | | Destination | packages/d2bd/src/*; packages/d2b-core/src/processes.rs; packages/d2b-provider-device-tpm/src/; packages/d2b-host/src/swtpm_argv.rs | -| Detailed design | Remove direct broker references: remove pre-ADR-0046 daemon swtpm broker call sites, retire `ProcessRole::Swtpm` and `ProcessRole::SwtpmPreStartFlush`, move argv builders from `d2b-host/src/swtpm_argv.rs` to `d2b-provider-device-tpm/src/` with binary path fields removed, while retaining `d2b-priv-broker/src/ops/swtpm_dir.rs` for `volume-local` and `spawn_runner.rs` for `system-minijail`. | +| Detailed design | Remove direct broker references: remove pre-ADR-0046 daemon swtpm broker call sites, retire `ProcessRole::Swtpm` and `ProcessRole::SwtpmPreStartFlush`, move argv builders from `d2b-host/src/swtpm_argv.rs` to `d2b-provider-device-tpm/src/` with binary path fields removed, while retaining `d2b-priv-broker/src/ops/swtpm_dir.rs` for `volume-local` and `spawn_runner.rs` for `system-minijail`. Primary reuse disposition: `delete-after-cutover`. Preserved source-plan detail: remove direct broker references from daemon and move argv builders into the Provider with binary path fields removed; retain broker ops only behind resource providers. | | Integration | After controller/effect-port parity, daemon no longer calls TPM broker ops; Resource providers invoke broker effects from Volume and Process reconciliation; contract tests ensure swtpm sandbox/readiness still hold. | | Data migration | Existing TPM state migration follows §17.3: the old `/var/lib/d2b/vms//swtpm/` directory moves to the controller-created Volume path with the provisioning marker preserved and re-keyed; this item must not silently recreate missing state. | | Validation | Static search/proof for no direct broker swtpm references in daemon/controller plus preserved swtpm contract tests | diff --git a/docs/specs/providers/ADR-046-provider-device-usbip.md b/docs/specs/providers/ADR-046-provider-device-usbip.md index dc5da2be6..d788e5fb2 100644 --- a/docs/specs/providers/ADR-046-provider-device-usbip.md +++ b/docs/specs/providers/ADR-046-provider-device-usbip.md @@ -1623,9 +1623,9 @@ assert resolve(export.resourceRef).spec.mode == "authority"; | --- | --- | | Dependency/owner | d2b-contracts crate shape stabilised by shared root contract; d2b-contracts owner | | Current source | None — net-new v3 work; no pre-ADR45 baseline equivalent | -| Reuse action | net-new trait definition | +| Reuse action | create | | Destination | packages/d2b-contracts/src/usbip_effect_port.rs | -| Detailed design | Define UsbipEffectPort and UsbipGuestEffectPort in d2b-contracts with DeviceUid, NetworkUid, UsbBindingUid, LeaseToken, FirewallToken, FirewallObservation, KernelModuleClass, DeviceProbeResult, and UsbipEffectError; export traits/types only with no implementation; keep firewall apply/observe/release Network/busid-scoped, attach/detach Binding-addressed, and all fd/path/busid values private. `TransientDetail` derives `Clone, PartialEq, Eq` while retaining manual redacted Debug/Display so `UsbipEffectError`'s derives compile without disclosure. | +| Detailed design | Define UsbipEffectPort and UsbipGuestEffectPort in d2b-contracts with DeviceUid, NetworkUid, UsbBindingUid, LeaseToken, FirewallToken, FirewallObservation, KernelModuleClass, DeviceProbeResult, and UsbipEffectError; export traits/types only with no implementation; keep firewall apply/observe/release Network/busid-scoped, attach/detach Binding-addressed, and all fd/path/busid values private. `TransientDetail` derives `Clone, PartialEq, Eq` while retaining manual redacted Debug/Display so `UsbipEffectError`'s derives compile without disclosure. Primary reuse disposition: `create`. Preserved source-plan detail: net-new trait definition. | | Integration | Provider/device-usbip controller depends on this trait for injected semantic effects; the framework core adapter implements it in ADR046-usbip-002. | | Data migration | None — docs/tooling only; no runtime state | | Validation | d2b-contracts tests for trait object safety, firewall apply/observe/release signatures, `UsbipEffectError: Clone + PartialEq + Eq`, `TransientDetail` clone/equality, redacted Debug/Display behavior, and no implementation leakage. | @@ -1645,9 +1645,9 @@ in `d2b-contracts`; trait only. Add conformance tests in `d2b-contracts/tests/us | --- | --- | | Dependency/owner | ADR046-usbip-001, ADR046-zone-control-024; UsbipBindFirewallRule broker op; d2b-host usbip argv support; framework core adapter owner | | Current source | packages/d2bd/src/usbip_state_machine.rs, packages/d2bd/src/usbip_reconcile_state.rs, packages/d2b-host/src/usbip_argv.rs, packages/d2b-priv-broker/src/ops/usbip_firewall.rs, usbip_host.rs, and usbip_lock.rs | -| Reuse action | extract and adapt into framework-internal adapter | +| Reuse action | adapt | | Destination | packages/d2b-core/src/device_usbip_adapter.rs | -| Detailed design | Implement UsbipEffectPort in the core adapter: signed-bundle busid lookup, same-Zone validation, trusted physical-USB identity resolution, mandatory Core-derived `physical-usb-backing/v1` digest and exact `(Host, physical-usb-backing, opaqueKeyDigest)` claim shared with every security-key/USB Provider, exclusive OFD claim, sole ownership of all USBIP TCP/3240 and exact per-Network/per-busid `UsbipBindFirewallRule` effects plus ownership-scoped observe/release, anti-spoof probe, one shared Host module/backend authority, one Core-derived D097 relay Endpoint/firewall authority per Network, D097 authority-index preflight/adoption, and post-effect audit; complete the shared claim before any open, withhold, bind, module, relay, firewall, or attachment effect; never expose raw busid, identity digest, path, fd, bind address, nftables body, audit structs, or broker wire types. | +| Detailed design | Implement UsbipEffectPort in the core adapter: signed-bundle busid lookup, same-Zone validation, trusted physical-USB identity resolution, mandatory Core-derived `physical-usb-backing/v1` digest and exact `(Host, physical-usb-backing, opaqueKeyDigest)` claim shared with every security-key/USB Provider, exclusive OFD claim, sole ownership of all USBIP TCP/3240 and exact per-Network/per-busid `UsbipBindFirewallRule` effects plus ownership-scoped observe/release, anti-spoof probe, one shared Host module/backend authority, one Core-derived D097 relay Endpoint/firewall authority per Network, D097 authority-index preflight/adoption, and post-effect audit; complete the shared claim before any open, withhold, bind, module, relay, firewall, or attachment effect; never expose raw busid, identity digest, path, fd, bind address, nftables body, audit structs, or broker wire types. Primary reuse disposition: `adapt`. Preserved source-plan detail: extract and adapt into framework-internal adapter. | | Integration | Reconcile framework injects the adapter into Provider/device-usbip; D097 authority index gates effects; adapter calls privileged broker and d2b-host argv helpers behind the semantic trait. | | Data migration | Full d2b 3.0 reset; adapter resumes from Service/Binding status and authority owner proofs rather than daemon-coupled snapshots | | Validation | Fast packages/d2b-core/tests/device_usbip_adapter.rs covers same-Zone gate, exact shared physical backing tuple derivation, byte-identical keys for USB/security-key views of one fake token, Provider-private-class/digest bypass rejection, `physical-usb-backing-conflict` before effects, separate USBIP module/relay authorities, one-module/one-relay reuse, exact Network/busid firewall scoping, foreign-marker failure, ownership-scoped drift/status, independent per-busid release, no network-local dependency, anti-spoof, redaction, broker mapping, and no digest/busid/path/fd exposure. | @@ -1667,9 +1667,9 @@ type to the trait caller. Add unit tests for same-Zone gate and anti-spoof logic | --- | --- | | Dependency/owner | ADR046-usbip-001, ADR046-provider-004; Provider model crate structure; device-usbip provider owner | | Current source | None — net-new Provider crate; no pre-ADR45 baseline equivalent | -| Reuse action | net-new crate skeleton with contract reuse | +| Reuse action | create | | Destination | packages/d2b-provider-device-usbip/ | -| Detailed design | Create the required crate layout; bind the shared D098 `UsbService`/`UsbBinding` base versions/fingerprints from ADR046-provider-004 and implement only strict USBIP Provider extensions; sign/register extension schemas and advertise explicit export only for authority `UsbService` resources implemented by this Provider; implement validation.rs and compile-checked EffectPort injection. Declare the controller user/User resource in Nix activation. | +| Detailed design | Create the required crate layout; bind the shared D098 `UsbService`/`UsbBinding` base versions/fingerprints from ADR046-provider-004 and implement only strict USBIP Provider extensions; sign/register extension schemas and advertise explicit export only for authority `UsbService` resources implemented by this Provider; implement validation.rs and compile-checked EffectPort injection. Declare the controller user/User resource in Nix activation. Primary reuse disposition: `create`. Preserved source-plan detail: net-new crate skeleton with contract reuse. | | Integration | Workspace manifests, Provider artifact catalog, Nix module, and ProviderDeployment consume the crate and component descriptor. | | Data migration | None — docs/tooling only; no runtime state | | Validation | make test-policy passes; Cargo.toml has no d2b-priv-broker dependency; fast schema/manifest tests consume the common fixtures, accept canonical minimal base without `spec.provider`, prove a fake direct-local Provider can implement the same base, and cover Service-only exportability, Binding non-exportability, Core projection ownerRef/base fields with explicit `spec.provider` rejection, D088 status layering, semantic factory-fingerprint stability across Provider/adapter identity changes, strict refs, and trait injection. | @@ -1695,9 +1695,9 @@ of this Provider. | --- | --- | | Dependency/owner | ADR046-usbip-001, ADR046-usbip-002, ADR046-usbip-003, and ADR046-zone-control-024; device-usbip controller owner | | Current source | packages/d2bd/src/usbip_state_machine.rs and packages/d2bd/src/usbip_reconcile_state.rs | -| Reuse action | extract and adapt step machine into Provider reconcile loop | +| Reuse action | adapt | | Destination | packages/d2b-provider-device-usbip/src/{controller,reconcile,export_import}.rs | -| Detailed design | Reconcile provider-neutral authority/projection `UsbService` and per-Guest `UsbBinding` resources through strict USBIP Provider extensions, consuming UsbipEffectPort, the exact shared Host-global physical USB tuple, USBIP-private D097 authorities, and signed D096 ExportAdapter/ImportAdapter. Enforce same-Zone base and provider refs; ResourceExport authority-Service-only target; same-type ResourceImport-owned projection with `providerRef`, semantic base/import fields, no `spec.provider`, and no physical fields/effects; route selection from the signed local descriptor/import record; semantic factory fingerprint independent of separately authenticated adapter identity; exclusive fair Binding admission; encrypted bounded named-stream control/data; D088 layered status; Service/Binding finalizers; restart adoption; declared/explicit modes; no session/transfer resources. | +| Detailed design | Reconcile provider-neutral authority/projection `UsbService` and per-Guest `UsbBinding` resources through strict USBIP Provider extensions, consuming UsbipEffectPort, the exact shared Host-global physical USB tuple, USBIP-private D097 authorities, and signed D096 ExportAdapter/ImportAdapter. Enforce same-Zone base and provider refs; ResourceExport authority-Service-only target; same-type ResourceImport-owned projection with `providerRef`, semantic base/import fields, no `spec.provider`, and no physical fields/effects; route selection from the signed local descriptor/import record; semantic factory fingerprint independent of separately authenticated adapter identity; exclusive fair Binding admission; encrypted bounded named-stream control/data; D088 layered status; Service/Binding finalizers; restart adoption; declared/explicit modes; no session/transfer resources. Primary reuse disposition: `adapt`. Preserved source-plan detail: extract and adapt step machine into Provider reconcile loop. | | Integration | Controller watches Device/Host/Network/Guest/Endpoint/Export/Import dependencies, calls injected EffectPorts, commits ResourceMutationBatch updates, coordinates children from ADR046-usbip-005, and delegates only semantic export/import admission to the Provider adapter while core owns D096 routing/lifecycle. | | Data migration | Full d2b 3.0 reset; no direct import of d2bd usbip_reconcile_state snapshots | | Validation | Fast tests/controller_state_machine.rs, service_binding_schema.rs, export_import.rs, authority_conflict.rs, async_loop.rs, finalizer.rs, and wrong_zone.rs cover authority/projection/Binding lifecycle, Service-only export, projection `spec.provider` rejection, semantic fingerprint stability under adapter identity changes plus separate descriptor authentication, D088 `status.resource`/`status.provider` placement, shared physical tuple collision before effects, encrypted fake streams, no physical projection effect, exclusivity, restart, and WrongZone degradation. | @@ -1723,9 +1723,9 @@ Tests required: | --- | --- | | Dependency/owner | ADR046-usbip-003; Process ResourceType schema; device-usbip process lifecycle owner | | Current source | None — net-new Process resources; templates derive from the Provider package descriptor | -| Reuse action | adapt into singleton Host backend, per-Network relay, and per-Binding Guest proxy management | +| Reuse action | adapt | | Destination | packages/d2b-provider-device-usbip/src/reconcile.rs | -| Detailed design | Create/adopt exactly one Host backend Process authority, exactly one D097 Network relay Process/Endpoint/firewall authority bound to TCP 3240 with a Core-derived Network/policy key, and one Binding-owned Guest proxy/private Endpoint per attached Binding. Restrict relay resolution to the Core adapter and exact active-lease Binding proxies; deliver their connected streams by LaunchTicket. Use canonical system-minijail specs, signed templates, bounded budgets/readiness/restart, no argv/path/address/fd fields; attach/detach remains a one-shot EffectPort operation, not a second Process. | +| Detailed design | Create/adopt exactly one Host backend Process authority, exactly one D097 Network relay Process/Endpoint/firewall authority bound to TCP 3240 with a Core-derived Network/policy key, and one Binding-owned Guest proxy/private Endpoint per attached Binding. Restrict relay resolution to the Core adapter and exact active-lease Binding proxies; deliver their connected streams by LaunchTicket. Use canonical system-minijail specs, signed templates, bounded budgets/readiness/restart, no argv/path/address/fd fields; attach/detach remains a one-shot EffectPort operation, not a second Process. Primary reuse disposition: `adapt`. Preserved source-plan detail: adapt into singleton Host backend, per-Network relay, and per-Binding Guest proxy management. | | Integration | Service controller registers physical busids with shared backend/relay; Binding controller creates its Guest proxy/private Endpoint; Process controller launches workers; UsbipGuestEffectPort attaches to the private Endpoint. | | Data migration | Full d2b 3.0 reset; old per-env runners become Host/Network authorities and per-Device port-3240 workers are forbidden | | Validation | Fast Process/Endpoint shape tests prove one backend per Host, one Core-derived multiplexed TCP 3240 Endpoint/firewall authority per Network, deterministic duplicate conflict, adapter/exact-Binding-only resolution, LaunchTicket-connected stream, no generic Network reader access, Binding ownership/private Guest policy, no per-Device listener, no raw address/argv/path/fd, and readiness before bind/attach. | @@ -1744,9 +1744,9 @@ private proxies/Endpoints. Attach/detach remains an EffectPort call. | --- | --- | | Dependency/owner | ADR046-usbip-003; Device provider details plus `UsbService` and `UsbBinding` base/provider status schema owner | | Current source | packages/d2bd/src/usbip_reconcile_state.rs state fields | -| Reuse action | adapt state fields to typed status.provider.details | +| Reuse action | adapt | | Destination | packages/d2b-provider-device-usbip/src/status.rs | -| Detailed design | Define provider-neutral `UsbService` `status.resource` with whole-device availability/access counts, authority-only `physical-usb-backing` claim state, and projection-only common import state; define `UsbBinding.status.resource` with generic attachment phase/queue/generation/timestamps. Define separate strict `status.provider` USBIP details: Device probe, owner backend/relay, USBIP-owned `firewallState`/opaque digest/observed generation, imported-route observations, and Binding proxy/private Endpoint/subphase. No common access/import/attachment field appears directly under `status`; no USBIP module, Network, Endpoint, proxy, server/client, port, firewall, or busid field enters base status; no raw backing digest, busid, path, fd, address, session/transfer ID, remote identity, or payload appears anywhere. | +| Detailed design | Define provider-neutral `UsbService` `status.resource` with whole-device availability/access counts, authority-only `physical-usb-backing` claim state, and projection-only common import state; define `UsbBinding.status.resource` with generic attachment phase/queue/generation/timestamps. Define separate strict `status.provider` USBIP details: Device probe, owner backend/relay, USBIP-owned `firewallState`/opaque digest/observed generation, imported-route observations, and Binding proxy/private Endpoint/subphase. No common access/import/attachment field appears directly under `status`; no USBIP module, Network, Endpoint, proxy, server/client, port, firewall, or busid field enters base status; no raw backing digest, busid, path, fd, address, session/transfer ID, remote identity, or payload appears anywhere. Primary reuse disposition: `adapt`. Preserved source-plan detail: adapt state fields to typed status.provider.details. | | Integration | Controller writes each extension atomically with its resource's common status; dependency/update propagation is Device/Export → Service/projection → Binding. | | Data migration | Full d2b 3.0 reset; current d2bd reconcile state is not imported | | Validation | Fast tests/status_serde.rs covers generic base status plus three strict USBIP detail schemas, exact `status.resource` placement for access/import/attachment/backing claim, exact `status.provider` placement for relay/firewall implementation observations, drift transition and ownership, rejection of semantic fields directly under `status`, mode-dependent omissions, bounded counts/refs, unknown-field denial, and a deny corpus proving USBIP-only fields are rejected from base and Network status. | @@ -1766,9 +1766,9 @@ not ResourceTypes or compatibility aliases. | --- | --- | | Dependency/owner | ADR046-usbip-004 and ADR046-usbip-005; device-usbip integration owner | | Current source | packages/d2b-contract-tests/tests/usbip_policy_network_scoping.rs plus new integration scenarios | -| Reuse action | adapt existing network-scoping assertion and add new scenarios | +| Reuse action | adapt | | Destination | packages/d2b-provider-device-usbip/{src,tests,integration/README.md}; tests/host-integration/usbip-service.nix; tests/host-integration/hardware/usbip-service.sh | -| Detailed design | Put provider-neutral Service/Binding base-schema separation, strict USBIP extensions, projection `spec.provider` rejection, D088 layered status, semantic factory-fingerprint independence from Provider/adapter identity, exact shared physical backing tuple/conflict, one Core-derived per-Network relay Endpoint/firewall authority, exact per-busid firewall ownership/drift/release, arbitration, same-type export/import, encrypted fake-stream, and least-privilege process/Endpoint shape coverage in fast Layer-1 Rust tests. Include a fake direct-local Provider proving the same base contract has no USBIP dependency. Reserve runNixOSTest for real Linux usbip_host/vhci_hcd, usbipd, namespaces/nftables, TCP 3240, zero network-local carve-out, and Guest checks; reserve the hardware script for an approved physical device. Use existing Make gates only. | +| Detailed design | Put provider-neutral Service/Binding base-schema separation, strict USBIP extensions, projection `spec.provider` rejection, D088 layered status, semantic factory-fingerprint independence from Provider/adapter identity, exact shared physical backing tuple/conflict, one Core-derived per-Network relay Endpoint/firewall authority, exact per-busid firewall ownership/drift/release, arbitration, same-type export/import, encrypted fake-stream, and least-privilege process/Endpoint shape coverage in fast Layer-1 Rust tests. Include a fake direct-local Provider proving the same base contract has no USBIP dependency. Reserve runNixOSTest for real Linux usbip_host/vhci_hcd, usbipd, namespaces/nftables, TCP 3240, zero network-local carve-out, and Guest checks; reserve the hardware script for an approved physical device. Use existing Make gates only. Primary reuse disposition: `adapt`. Preserved source-plan detail: adapt existing network-scoping assertion and add new scenarios. | | Integration | Layer-2 lanes exercise actual kernel/backend/relay/Guest/device paths and do not duplicate pure controller/schema cases. Cross-Zone protocol logic remains hermetic with fake peers; the runNixOSTest only proves its real-system integration. | | Data migration | None — docs/tooling only; no runtime state | | Validation | `make test-host-integration` runs the non-hardware real-kernel case on a capable host; `make test-hardware` runs the explicit manual device case. No Layer-1 test opens a device, loads a module, creates a namespace, or listens on a socket. | @@ -1796,9 +1796,9 @@ Required tests: | --- | --- | | Dependency/owner | ADR046-usbip-003, ADR046-zone-control-024; ADR-046-nix-configuration; Nix integrator | | Current source | nixos-modules/components/usbip.nix guest wiring and new Zone resource declarations | -| Reuse action | adapt guest module, remove host-side option surface, and extend eval assertions | +| Reuse action | adapt | | Destination | nixos-modules/components/usbip.nix, nixos-modules/options-zones.nix, nixos-modules/assertions.nix | -| Detailed design | Add Provider config; remove the old per-VM option; emit provider-neutral authority `UsbService`, per-Guest `UsbBinding`, strict USBIP Provider envelopes, and optional ResourceExport/ResourceImport authoring shapes; imported same-type projection Services remain Core-created with no `spec.provider`. Assert same-Zone base/provider refs, projection ownerRef/forbidden implementation and physical fields, Service-only export target, one Host backend/Network relay, exact Core-derived Host-global `(Host, physical-usb-backing, opaqueKeyDigest)` exclusion shared by USB/security-key, Provider-private-class bypass rejection, and retain guest vhci_hcd/tools. | +| Detailed design | Add Provider config; remove the old per-VM option; emit provider-neutral authority `UsbService`, per-Guest `UsbBinding`, strict USBIP Provider envelopes, and optional ResourceExport/ResourceImport authoring shapes; imported same-type projection Services remain Core-created with no `spec.provider`. Assert same-Zone base/provider refs, projection ownerRef/forbidden implementation and physical fields, Service-only export target, one Host backend/Network relay, exact Core-derived Host-global `(Host, physical-usb-backing, opaqueKeyDigest)` exclusion shared by USB/security-key, Provider-private-class bypass rejection, and retain guest vhci_hcd/tools. Primary reuse disposition: `adapt`. Preserved source-plan detail: adapt guest module, remove host-side option surface, and extend eval assertions. | | Integration | Nix compiler emits Device plus provider-neutral `UsbService`/`UsbBinding` and explicit D096 resources consumed by core/Provider; guest runtime supplies USBIP proxy/attach tools; generated base and provider-extension schemas/fingerprints remain canonical. | | Data migration | Full d2b 3.0 reset; operators reauthor old per-VM options as Device + authority/projection Service + per-Guest Binding | | Validation | Fast tests/unit/nix/cases/usbip-*.nix cover schema shape, Core projection `spec.provider` rejection, D088 layered status fixtures including USBIP-owned firewall state, all reference/owner/export assertions, byte-identical USB/security-key tuple collision and private-class bypass rejection, one Core-derived 3240 Endpoint/firewall authority per Network, least-privilege consumer policy, absence of network-local 3240 rules, old-option removal, and guest module retention. | @@ -1823,9 +1823,9 @@ Required tests: | --- | --- | | Dependency/owner | ADR046-usbip-004 and ADR046-usbip-008; Provider fully wired and validated; daemon cleanup owner | | Current source | packages/d2bd/src/usbipd_perenv_autostart.rs, packages/d2bd/src/usbip_state_machine.rs, packages/d2bd/src/usbip_reconcile_state.rs, nixos-modules/network.nix USBIP firewall block, and ProcessRole::Usbip in packages/d2b-core/src/processes.rs | -| Reuse action | delete after Provider replacement reaches parity | +| Reuse action | delete-after-cutover | | Destination | packages/d2bd/src/, nixos-modules/network.nix, packages/d2b-core/src/processes.rs | -| Detailed design | Remove daemon-coupled USBIP after Provider tests and integration tests pass: delete per-env autostart, state machine, and reconcile state modules after migration; remove USBIP firewall block from network.nix; remove ProcessRole::Usbip; run Layer-1 gates and confirm no d2bd or network.nix references remain outside the adapter and contracts. | +| Detailed design | Remove daemon-coupled USBIP after Provider tests and integration tests pass: delete per-env autostart, state machine, and reconcile state modules after migration; remove USBIP firewall block from network.nix; remove ProcessRole::Usbip; run Layer-1 gates and confirm no d2bd or network.nix references remain outside the adapter and contracts. Primary reuse disposition: `delete-after-cutover`. Preserved source-plan detail: delete after Provider replacement reaches parity. | | Integration | Provider/device-usbip, core D096/D097 adapters, Nix Device plus provider-neutral USB resource emitter, USBIP authority workers, and Binding-owned children are the sole USBIP lifecycle path after deletion. | | Data migration | Full d2b 3.0 reset; no daemon-coupled USBIP runtime state import | | Validation | make test-unit and make test-flake plus grep or contract checks for removed symbols and no residual d2bd/network.nix USBIP lifecycle references. | diff --git a/docs/specs/providers/ADR-046-provider-display-wayland.md b/docs/specs/providers/ADR-046-provider-display-wayland.md index fb283140b..ecb4e493f 100644 --- a/docs/specs/providers/ADR-046-provider-display-wayland.md +++ b/docs/specs/providers/ADR-046-provider-display-wayland.md @@ -1741,9 +1741,9 @@ produces the guest binary (see §19 removal table). | --- | --- | | Dependency/owner | `ADR046-provider-001`, `ADR046-process-001`; display Provider owner | | Current source | `packages/d2b-wayland-proxy/`, `packages/d2b-host/src/wayland_proxy_argv.rs`, `packages/d2b-host-providers/src/lib.rs`, `packages/d2b-realm-provider/src/{conformance,mock}.rs` | -| Reuse action | extract and adapt | +| Reuse action | adapt | | Destination | `packages/d2b-provider-display-wayland/src/` | -| Detailed design | Create Provider crate layout (`src/`, `tests/`, `integration/`, `README.md`); extract `FilterPolicy`, `PolicyInput`, `DecorationManager`, `BridgeConfig`, `ProxyReadinessEvent`, `ProxyIdentity`, `ClipboardGlobalDisposition` from `d2b-wayland-proxy`; implement single `display-controller` using toolkit `ResourceClient`/`Reconciler` to manage both `WaylandSession` and `WaylandPolicy` resources; implement `display-user-portal` as a separately sandboxed user-domain service that receives pre-opened compositor connections from the fixed user session supervisor (never reads `WAYLAND_DISPLAY`), validates same-user via `SO_PEERCRED`, and issues bounded per-session compositor connection attachment grants to `ProviderSupervisor`; implement LaunchTicket composition with opaque attachment grant handles (compositor, GPU endpoint) so no fd transits through the controller; implement pool-slot acquisition using opaque hash-derived account names (`d2b-wlp-` for bundle sessions, `d2b-wlp-p` for pool) that fails closed with `NoPrincipalAvailable` when all pool slots are occupied; implement `wl-cross-domain-proxy` guest frontend binary at `src/bin/wl-cross-domain-proxy.rs` within the Provider package; implement provider-neutral `display_fails_closed_when_unsupported` conformance; assert D087 status-first state: ProviderStateSet is empty and bounded operational state is in resource status plus the core Operation ledger | +| Detailed design | Create Provider crate layout (`src/`, `tests/`, `integration/`, `README.md`); extract `FilterPolicy`, `PolicyInput`, `DecorationManager`, `BridgeConfig`, `ProxyReadinessEvent`, `ProxyIdentity`, `ClipboardGlobalDisposition` from `d2b-wayland-proxy`; implement single `display-controller` using toolkit `ResourceClient`/`Reconciler` to manage both `WaylandSession` and `WaylandPolicy` resources; implement `display-user-portal` as a separately sandboxed user-domain service that receives pre-opened compositor connections from the fixed user session supervisor (never reads `WAYLAND_DISPLAY`), validates same-user via `SO_PEERCRED`, and issues bounded per-session compositor connection attachment grants to `ProviderSupervisor`; implement LaunchTicket composition with opaque attachment grant handles (compositor, GPU endpoint) so no fd transits through the controller; implement pool-slot acquisition using opaque hash-derived account names (`d2b-wlp-` for bundle sessions, `d2b-wlp-p` for pool) that fails closed with `NoPrincipalAvailable` when all pool slots are occupied; implement `wl-cross-domain-proxy` guest frontend binary at `src/bin/wl-cross-domain-proxy.rs` within the Provider package; implement provider-neutral `display_fails_closed_when_unsupported` conformance; assert D087 status-first state: ProviderStateSet is empty and bounded operational state is in resource status plus the core Operation ledger Primary reuse disposition: `adapt`. Preserved source-plan detail: extract and adapt. | | Integration | Zone Provider resource/catalog → `WaylandSession` controller (in `display-controller`) → Process resources → supervisor ticket; framework enrollment creates no Provider state Volume for display components | | Data migration | Full reset; no v2 session compatibility | | Validation | conformance vectors, fake-bus tests, filter policy golden tests (migrate from `packages/d2b-wayland-proxy/`), redaction/audit contract tests, no-fallback test, `controller_unknown_interface_fails_closed`, `controller_finalizer_ambiguous_retained`, `user_portal_unavailable_blocks_pending`, `provider_state_set_empty_status_first` | @@ -1769,9 +1769,9 @@ produces the guest binary (see §19 removal table). | --- | --- | | Dependency/owner | `ADR046-display-001`; telemetry/audit owner | | Current source | `packages/d2b-wayland-proxy/src/diag.rs` (rate-limited bounded diagnostics) | -| Reuse action | extract and adapt | +| Reuse action | adapt | | Destination | `packages/d2b-provider-display-wayland/src/audit.rs`, `packages/d2b-provider-display-wayland/src/metrics.rs` | -| Detailed design | Implement audit record types for all events in §14.1; implement OTEL metric counters/gauges in §14.2; adapt `DiagRateLimiter` to use closed label sets; validate that no socket path, user identity, window title, or app-id appears in any log/audit/metric surface | +| Detailed design | Implement audit record types for all events in §14.1; implement OTEL metric counters/gauges in §14.2; adapt `DiagRateLimiter` to use closed label sets; validate that no socket path, user identity, window title, or app-id appears in any log/audit/metric surface Primary reuse disposition: `adapt`. Preserved source-plan detail: extract and adapt. | | Integration | Providers emit via Zone telemetry emitter; audit records committed before operation completion | | Data migration | None — full d2b 3.0 reset; no prior state to migrate | | Validation | Redaction contract tests (`policy_observability.rs` pattern), audit record schema tests, label-cardinality tests | @@ -1783,7 +1783,7 @@ produces the guest binary (see §19 removal table). | --- | --- | | Dependency/owner | `ADR046-display-001`; integration test owner | | Current source | `tests/integration/` test orchestration structure | -| Reuse action | new | +| Reuse action | create | | Destination | `packages/d2b-provider-display-wayland/integration/` | | Detailed design | Container/Host/Guest/cross-process integration fixtures for: (a) end-to-end WaylandSession create → proxy Process ready → guest frontend ready; (b) GPU endpoint unavailable → Pending; (c) proxy crash → Failed backoff; (d) policy policy warning production; (e) clipboard boundary denial; (f) crossDomainTrusted=false admission rejection. Follows `ADR-046-provider-model-and-packaging` integration/ convention. | | Integration | Invoked by existing repository test orchestration (`make test-integration` / container lane) | diff --git a/docs/specs/providers/ADR-046-provider-network-local.md b/docs/specs/providers/ADR-046-provider-network-local.md index 2b00115da..a0c720f91 100644 --- a/docs/specs/providers/ADR-046-provider-network-local.md +++ b/docs/specs/providers/ADR-046-provider-network-local.md @@ -2295,7 +2295,7 @@ On controller binary upgrade: | --- | --- | | Dependency/owner | Core; owns `NetworkEffectPort` contract/versioning in `d2b-contracts` and adapter implementation in `d2b-core`. | | Current source | None — net-new v3 work; no pre-ADR45 baseline equivalent for a provider-neutral `NetworkEffectPort` core adapter. | -| Reuse action | net-new | +| Reuse action | create | | Destination | `d2b-contracts` trait plus `d2b-core` core adapter; maps to broker wire operations and audit emission. | | Detailed design | Implement `NetworkEffectPort` core adapter in `d2b-core`; map to broker wire ops; emit audit records. Versioning: minor releases may add methods with default impls; major releases require Provider upgrade. The trait lives in `d2b-contracts`; the adapter in `d2b-core`. | | Integration | `Provider/network-local` reconcile calls injected `NetworkEffectPort`; the core adapter resolves opaque Network intents to closed broker wire ops and emits broker-level audit records. | @@ -2308,9 +2308,9 @@ On controller binary upgrade: | --- | --- | | Dependency/owner | Core; broker/core contract work consumed by ADR046-nl-001. | | Current source | Existing broker wire has related ApplyNftables, ApplyRoute, ApplySysctl, ApplyNmUnmanaged, UpdateHostsFile, and SeedDnsmasqLease operations, but no `CreateBridge`, `DeleteBridge`, `ReadNftablesDigest`, `ReadSysctlState`, or `ReadBridgePortFlags` v3 ops. | -| Reuse action | extend broker wire with net-new operations and reuse existing closed broker-operation dispatch shape | +| Reuse action | adapt | | Destination | Broker wire contract and broker/core adapter operation table for `CreateBridge`, `DeleteBridge`, `ReadNftablesDigest`, `ReadSysctlState`, and `ReadBridgePortFlags`. | -| Detailed design | Add `CreateBridge`, `DeleteBridge`, `ReadNftablesDigest`, `ReadSysctlState`, `ReadBridgePortFlags` broker ops. | +| Detailed design | Add `CreateBridge`, `DeleteBridge`, `ReadNftablesDigest`, `ReadSysctlState`, `ReadBridgePortFlags` broker ops. Primary reuse disposition: `adapt`. Preserved source-plan detail: extend broker wire with net-new operations and reuse existing closed broker-operation dispatch shape. | | Integration | `NetworkEffectPort` core adapter invokes these broker ops for bridge lifecycle and observe/drift checks; `Provider/network-local` receives only typed results and opaque digests/handles. | | Data migration | Full d2b 3.0 reset; no v2 state/config import. | | Validation | `integration/host_fabric.rs` covers bridge create/delete, nftables apply/digest, IPv6 suppression, NetworkManager unmanaged handling, and real `NetworkEffectPort` implementation. | @@ -2321,7 +2321,7 @@ On controller binary upgrade: | --- | --- | | Dependency/owner | Core; handle DTOs are owned by `d2b-contracts` and consumed by `d2b-core` plus `Provider/network-local`. | | Current source | None — net-new v3 work; no public pre-ADR45 baseline equivalent for opaque `AttachmentHandle` or `FabricHandle`. | -| Reuse action | net-new | +| Reuse action | create | | Destination | `d2b-contracts` opaque byte-array newtypes; core-held HMAC key and provider-facing redacted handle types. | | Detailed design | Implement `AttachmentHandle` and `FabricHandle` as opaque byte-array newtypes (32 bytes of HMAC-SHA-256 over internal identity material; key held by core). Each handle is single-use; revocation is implicit when the owning Network is deleted. These types are declared in `d2b-contracts`, not in the provider crate. | | Integration | Core creates handles from Network and attachment identity, stores them only in internal state/status-resource attachment realization, and supplies resolved tap FDs through LaunchTicket without exposing IfNames or MACs. | @@ -2334,9 +2334,9 @@ On controller binary upgrade: | --- | --- | | Dependency/owner | Core; depends on ADR046-nl-003 handles and runtime-cloud-hypervisor LaunchTicket consumption. | | Current source | Existing v1 runtime tap handling is broker/runtime-specific; no v3 LaunchTicket owner-graph FD resolution surface exists. | -| Reuse action | net-new LaunchTicket integration; reuse existing LaunchTicket mechanism without adding provider/runtime API surface | +| Reuse action | create | | Destination | Core LaunchTicket builder and dependency resolver that walks `Guest.ownerRef: Network/` to resolved tap FDs. | -| Detailed design | Implement LaunchTicket FD resolution: when core builds the LaunchTicket for a Guest with `ownerRef: Network/`, it walks the owner graph, locates the Network, reads its internally-held `AttachmentHandle` set, and includes the corresponding tap FDs in the ticket. No API surface for the provider or runtime is required beyond the existing LaunchTicket mechanism. | +| Detailed design | Implement LaunchTicket FD resolution: when core builds the LaunchTicket for a Guest with `ownerRef: Network/`, it walks the owner graph, locates the Network, reads its internally-held `AttachmentHandle` set, and includes the corresponding tap FDs in the ticket. No API surface for the provider or runtime is required beyond the existing LaunchTicket mechanism. Primary reuse disposition: `create`. Preserved source-plan detail: net-new LaunchTicket integration; reuse existing LaunchTicket mechanism without adding provider/runtime API surface. | | Integration | Runtime-cloud-hypervisor starts Guests using tap FDs supplied in LaunchTickets; `Provider/network-local` declares attachments and core resolves handles privately. | | Data migration | Full d2b 3.0 reset; no v2 state/config import. | | Validation | `integration/guest_lifecycle.rs` validates net-VM/workload Guest lifecycle, opaque attachment handle resolution, and `systemArtifactId` binding. | @@ -2348,9 +2348,9 @@ On controller binary upgrade: | Dependency/owner | Provider plus Core; provider validates through `d2b_host::ifname::derive_ifname`, while core adapter consumes host networking modules. | | Current source | Reuse modules listed in §25.1: `packages/d2b-host/src/ifname.rs:derive_ifname`, `packages/d2b-host/src/nftables.rs`, `packages/d2b-host/src/bridge_port.rs`, `packages/d2b-host/src/routes.rs`, and `packages/d2b-host/src/netlink.rs`. | | Reuse source | `d2b-host` IfName, nftables, bridge-port, route preflight, and sysctl/netlink modules. | -| Reuse action | reuse directly in core adapter; provider re-exports only `derive_ifname` for validation | +| Reuse action | adapt | | Destination | Core adapter imports `d2b-host` modules; `packages/d2b-provider-network-local/src/ifname.rs` re-exports `d2b_host::ifname::derive_ifname` only. | -| Detailed design | The `d2b-host` IfName/nftables/bridge/route modules are consumed directly by the core adapter (not by the provider crate). The provider crate re-exports only `d2b_host::ifname::derive_ifname` for validation purposes. No additional extraction work is required beyond confirming the `d2b-host` API surface is stable. | +| Detailed design | The `d2b-host` IfName/nftables/bridge/route modules are consumed directly by the core adapter (not by the provider crate). The provider crate re-exports only `d2b_host::ifname::derive_ifname` for validation purposes. No additional extraction work is required beyond confirming the `d2b-host` API surface is stable. Primary reuse disposition: `adapt`. Preserved source-plan detail: reuse directly in core adapter; provider re-exports only `derive_ifname` for validation. | | Integration | Provider validateSpec uses deterministic IfName derivation for collision checks; core adapter applies bridge, nftables, route, and sysctl effects through reused `d2b-host` helpers. | | Data migration | Full d2b 3.0 reset; no v2 state/config import. | | Validation | `tests/ifname_derive.rs`, `tests/fault_injection.rs`, and `integration/host_fabric.rs` prove derivation, adapter reuse, and real host-fabric behavior. | @@ -2361,9 +2361,9 @@ On controller binary upgrade: | --- | --- | | Dependency/owner | Provider; depends on ADR046-nl-001 through ADR046-nl-005 and owns the Network reconcile/observe/finalize handlers. | | Current source | None — net-new v3 provider controller; v1 behavior lived in `nixos-modules/network.nix` and `nixos-modules/net.nix` static NixOS module logic. | -| Reuse action | port semantics into provider reconcile state machine; do not reuse static per-env systemd/Nix ownership | +| Reuse action | adapt | | Destination | `packages/d2b-provider-network-local/src/controller.rs`. | -| Detailed design | Implement `controller.rs` reconcile/observe/finalize handlers with `NetworkEffectPort` injection. | +| Detailed design | Implement `controller.rs` reconcile/observe/finalize handlers with `NetworkEffectPort` injection. Primary reuse disposition: `adapt`. Preserved source-plan detail: port semantics into provider reconcile state machine; do not reuse static per-env systemd/Nix ownership. | | Integration | Controller watches Network, Guest, Volume, Process, User, Host, and Zone resources; creates child resources, writes status, invokes `NetworkEffectPort`, and drives finalizers. | | Data migration | Full d2b 3.0 reset; no v2 state/config import. | | Validation | `tests/controller_state.rs` covers normal reconcile, errors, finalizer ordering, adoption on restart, and observe/drift cycles with deterministic clock. | @@ -2374,9 +2374,9 @@ On controller binary upgrade: | --- | --- | | Dependency/owner | Provider; owns net-agent ComponentSession service and depends on ComponentSession/bus and net-VM Process lifecycle. | | Current source | None — net-new v3 NetworkAgentService; v1 net-VM behavior was encoded in NixOS services and scripts under `nixos-modules/net.nix`. | -| Reuse action | net-new service; preserve semantic nftables/routes reload behavior from v1 net VM configuration | +| Reuse action | create | | Destination | `packages/d2b-provider-network-local/src/process_specs.rs` agent template plus agent service implementation in the net-VM artifact. | -| Detailed design | Implement `NetworkAgentService` Noise-KK vsock ComponentSession (Reload + ReadinessQuery methods). Agent reconnect policy: if the controller cannot reach the agent vsock (Guest restart in progress), it retries with exponential backoff up to `drainTimeout` of the agent Process; after timeout it deletes and re-creates the agent Process resource. | +| Detailed design | Implement `NetworkAgentService` Noise-KK vsock ComponentSession (Reload + ReadinessQuery methods). Agent reconnect policy: if the controller cannot reach the agent vsock (Guest restart in progress), it retries with exponential backoff up to `drainTimeout` of the agent Process; after timeout it deletes and re-creates the agent Process resource. Primary reuse disposition: `create`. Preserved source-plan detail: net-new service; preserve semantic nftables/routes reload behavior from v1 net VM configuration. | | Integration | Controller writes config Volume content, resolves `Endpoint/net--agent-service`, calls `Reload(config_digest)`, and uses readiness predicates to set Network conditions. | | Data migration | Full d2b 3.0 reset; no v2 state/config import. | | Validation | `integration/agent_reload.rs` validates Reload, `nft_applied` and `routes_applied` predicates, reconnect behavior, and config digest matching. | @@ -2388,87 +2388,87 @@ On controller binary upgrade: | Dependency/owner | Provider; config rendering owned by `Provider/network-local`, storage reconciliation owned by `Provider/volume-local`. | | Current source | Reuse semantics from `nixos-modules/net.nix` lines 168–296 for nftables and lines 302–441 for dnsmasq; runtime volume model is net-new. | | Reuse source | `nixos-modules/net.nix` dnsmasq, nftables, routing, and attachment configuration semantics. | -| Reuse action | port and render into bounded config Volume files | +| Reuse action | adapt | | Destination | `packages/d2b-provider-network-local/src/config_volume.rs`. | -| Detailed design | Implement config Volume content rendering (dnsmasq.conf, nftables.rules, routing.conf, attachments.json). | +| Detailed design | Implement config Volume content rendering (dnsmasq.conf, nftables.rules, routing.conf, attachments.json). Primary reuse disposition: `adapt`. Preserved source-plan detail: port and render into bounded config Volume files. | | Integration | Controller creates `Volume/net--config`, writes four files through the Volume service, attaches the read-only view to the net VM, and triggers agent reload plus dnsmasq restart. | | Data migration | Full d2b 3.0 reset; config Volume is runtime tmpfs content regenerated from Network spec. | | Validation | `tests/controller_state.rs`, `integration/agent_reload.rs`, and `integration/delete_sequence.rs` validate rendering, write flow, reload, and cleanup ordering. | | Removal proof | Supersedes static per-env config generation in `nixos-modules/net.nix`; successor coverage retires duplicate old Nix/service assertions. | -### ADR046-network-009 +### ADR046-nl-009 | Field | Value | | --- | --- | | Dependency/owner | Provider; Process resource builders owned by `d2b-provider-network-local`. | | Current source | v1 dnsmasq and mDNS process shape came from `nixos-modules/net.nix` and static NixOS services; no v3 Process builder exists. | -| Reuse action | port service semantics into canonical Process resource specs | +| Reuse action | adapt | | Destination | `packages/d2b-provider-network-local/src/process_specs.rs`. | -| Detailed design | Implement canonical Process spec builders for agent, dnsmasq, mdns-reflector, mdns-dnsbridge. | +| Detailed design | Implement canonical Process spec builders for agent, dnsmasq, mdns-reflector, mdns-dnsbridge. Primary reuse disposition: `adapt`. Preserved source-plan detail: port service semantics into canonical Process resource specs. | | Integration | Controller creates agent service, dnsmasq worker, and optional mDNS workers as owned Process resources on the net VM; Process Provider reports readiness and lifecycle status. | | Data migration | Full d2b 3.0 reset; no v2 state/config import. | | Validation | `tests/controller_state.rs`, `integration/mdns_reflector.rs`, and eval case `process-sandbox-netns.nix` validate Process shape, optional mDNS, and guest-netns capability isolation. | | Removal proof | Supersedes static per-env systemd services; old duplicate service tests are retired after successor Process coverage passes. | -### ADR046-network-010 +### ADR046-nl-010 | Field | Value | | --- | --- | | Dependency/owner | net-vm artifact; owns generic `net-vm-base` nixos-system artifact and shared net-local-controller UID/GID reservation. | | Current source | Reuse lib.mkForce NIC bootstrap from `nixos-modules/base.nix` `10-eth-dhcp` override and account reservation documented in `nixos-modules/host-users.nix`; v1 per-Network config does not carry forward. | | Reuse source | `nixos-modules/base.nix` `10-eth-dhcp` lib.mkForce override and host-users reservation table. | -| Reuse action | preserve generic boot/safety invariants; exclude per-Network static config | +| Reuse action | adapt | | Destination | `net-vm-base` nixos-system artifact and artifact catalog entry `d2b.artifacts.net-vm-base`. | -| Detailed design | Build generic `net-vm-base` nixos-system artifact with net-agent binary, agent-service endpoint, guest-agent binary, standard NIC bootstrap, lib.mkForce override; bake `net-local-controller` account with the UID/GID allocated from the host-users reservation table (documented in `nixos-modules/host-users.nix`). | +| Detailed design | Build generic `net-vm-base` nixos-system artifact with net-agent binary, agent-service endpoint, guest-agent binary, standard NIC bootstrap, lib.mkForce override; bake `net-local-controller` account with the UID/GID allocated from the host-users reservation table (documented in `nixos-modules/host-users.nix`). Primary reuse disposition: `adapt`. Preserved source-plan detail: preserve generic boot/safety invariants; exclude per-Network static config. | | Integration | Network resource `spec.netVmSystemArtifactId` points to `net-vm-base`; runtime-cloud-hypervisor consumes the artifact ID, and the config Volume provides all per-Network DHCP/firewall/routing content. | | Data migration | Full d2b 3.0 reset; no per-Network v2 net-VM config import. | | Validation | Eval cases `net-vm-artifact-id-eval.nix` and `network-spec-eval.nix`, plus `tests/net-vm-network-eval.sh` for the lib.mkForce invariant. | | Removal proof | Supersedes implicit microvm/Nix path coupling; remove `net-vm-base` artifact catalog entry only after all Network resources and provider references are gone. | -### ADR046-network-011 +### ADR046-nl-011 | Field | Value | | --- | --- | | Dependency/owner | Nix; owns resource declaration, User declaration, host account provisioning, and artifact catalog wiring. | | Current source | Existing NixOS modules `nixos-modules/network.nix`, `nixos-modules/net.nix`, and `nixos-modules/host-users.nix` provide v1 static declarations and user/account patterns. | -| Reuse action | adapt Nix option/resource emission and account provisioning to v3 resources | +| Reuse action | adapt | | Destination | Nix module resource emission for `Provider/network-local`, `User/net-local-controller`, host OS account, `provider-network-local`, and `net-vm-base` artifacts. | -| Detailed design | Nix module for `Provider/network-local` resource declaration; `User/net-local-controller` declaration; OS account provisioning; artifact catalog entries. | +| Detailed design | Nix module for `Provider/network-local` resource declaration; `User/net-local-controller` declaration; OS account provisioning; artifact catalog entries. Primary reuse disposition: `adapt`. Preserved source-plan detail: adapt Nix option/resource emission and account provisioning to v3 resources. | | Integration | Nix compiler emits Provider/User/Network resources and host prerequisites; core ProviderDeployment starts controller Process; system-core reconciles the User resource. | | Data migration | Full d2b 3.0 reset; no v2 state/config import. | | Validation | Eval cases `network-spec-eval.nix`, `user-no-managed-by-eval.nix`, `net-vm-artifact-id-eval.nix`, and `make test-policy` for artifact/package paths. | | Removal proof | Supersedes v1 NixOS module declarations; removal proof is deletion of old resource emission and account/artifact entries when provider is retired. | -### ADR046-network-012 +### ADR046-nl-012 | Field | Value | | --- | --- | | Dependency/owner | Nix; depends on Network resource schema and CIDR validation rules. | | Current source | `nixos-modules/lib.nix:cidrOverlaps` lines 429–462 provides CIDR overlap logic in the v1 module layer. | | Reuse source | `nixos-modules/lib.nix:cidrOverlaps`. | -| Reuse action | port/reuse overlap semantics in v3 eval checks and provider validation | +| Reuse action | adapt | | Destination | Nix flake/resource schema checks for declared Networks and provider `validate.rs` parity. | -| Detailed design | Build-time CIDR overlap check for declared Networks in flake check. | +| Detailed design | Build-time CIDR overlap check for declared Networks in flake check. Primary reuse disposition: `adapt`. Preserved source-plan detail: port/reuse overlap semantics in v3 eval checks and provider validation. | | Integration | Nix compiler rejects overlapping declared Network CIDRs before resource publication; runtime `validateSpec` re-checks full overlap matrix. | | Data migration | None — docs/tooling only; no runtime state. | | Validation | Eval case `network-cidr-overlap-eval.nix` and `tests/cidr_overlap.rs` cover same-Network, cross-Network, external CIDR, and adjacency cases. | | Removal proof | None — validation net-new in v3 resource compiler; no prior owner to remove. | -### ADR046-network-013 +### ADR046-nl-013 | Field | Value | | --- | --- | | Dependency/owner | Tests; owned by `d2b-provider-network-local` hermetic test suite. | | Current source | Reusable semantic assertions come from §25.1 IfName/CIDR reuse inventory and Network schema defined in this spec. | -| Reuse action | adapt existing IfName/CIDR assertions into provider conformance tests | +| Reuse action | adapt | | Destination | `packages/d2b-provider-network-local/tests/schema_roundtrip.rs`, `tests/ifname_derive.rs`, and `tests/cidr_overlap.rs`. | -| Detailed design | Conformance suite: NetworkSpec round-trip, IfName derivation, CIDR validation matrix. | +| Detailed design | Conformance suite: NetworkSpec round-trip, IfName derivation, CIDR validation matrix. Primary reuse disposition: `adapt`. Preserved source-plan detail: adapt existing IfName/CIDR assertions into provider conformance tests. | | Integration | Test suite runs under `cargo test -p d2b-provider-network-local --lib --tests` and validates provider schema before integration gates. | | Data migration | None — docs/tooling only; no runtime state. | | Validation | The listed conformance tests themselves are the validation, with workspace policy ensuring `tests/` exists. | | Removal proof | Retire replaced current-code tests only after successor hermetic tests cover the minimum reusable assertions and gate manifests/pins are updated. | -### ADR046-network-014 +### ADR046-nl-014 | Field | Value | | --- | --- | | Dependency/owner | Tests; depends on ADR046-nl-006 controller and fake `NetworkEffectPort` from `d2b-contracts`. | | Current source | None — net-new v3 controller state-machine test; v1 shell/Nix gates are not a controller-reconcile equivalent. | -| Reuse action | net-new | +| Reuse action | create | | Destination | `packages/d2b-provider-network-local/tests/controller_state.rs`. | | Detailed design | Controller state-machine unit tests with fake `NetworkEffectPort` (from d2b-contracts mock) and deterministic clock. | | Integration | Hermetic fake effect port drives reconcile, observe, finalizer, and adoption transitions without real broker, systemd, container, or network dependencies. | @@ -2476,79 +2476,79 @@ On controller binary upgrade: | Validation | `tests/controller_state.rs` covers normal path, CIDR conflict, User not Ready, Volume error, Guest timeout, agent reload failure, finalizer sequence, adoption, and drift. | | Removal proof | None — net-new; no prior owner to remove. | -### ADR046-network-015 +### ADR046-nl-015 | Field | Value | | --- | --- | | Dependency/owner | Tests; integration coverage for the complete Network lifecycle. | | Current source | Existing Layer-1 eval and shell gates cover fragments; no v3 provider lifecycle integration test exists. | -| Reuse action | adapt reusable semantic assertions into v3 integration coverage | +| Reuse action | adapt | | Destination | `packages/d2b-provider-network-local/integration/host_fabric.rs`, `guest_lifecycle.rs`, `agent_reload.rs`, and `delete_sequence.rs`. | -| Detailed design | Integration tests: full Network lifecycle (create, config update, agent Reload, delete sequence) in container environment. | +| Detailed design | Integration tests: full Network lifecycle (create, config update, agent Reload, delete sequence) in container environment. Primary reuse disposition: `adapt`. Preserved source-plan detail: adapt reusable semantic assertions into v3 integration coverage. | | Integration | Integration tests exercise resource publication, host fabric effects, config Volume updates, ComponentSession reload, Process lifecycle, and finalizer cleanup through the provider stack. | | Data migration | None — docs/tooling only; no runtime state. | | Validation | `make test-integration` for container tests and `make test-host-integration` where guest lifecycle requires host/KVM coverage. | | Removal proof | Old duplicate tests, shell gates, fixtures, static artifacts, CI jobs, manifests, and pins are deleted once successor coverage and removal proof pass. | -### ADR046-network-016 +### ADR046-nl-016 | Field | Value | | --- | --- | | Dependency/owner | Security; depends on Process sandbox schema and `Provider/system-minijail` guest namespace inheritance. | | Current source | The invariant is specified as INV-NET-008 and has eval coverage named `tests/unit/nix/cases/process-sandbox-netns.nix`. | -| Reuse action | preserve and verify existing guest-netns isolation invariant in v3 Process specs | +| Reuse action | adapt | | Destination | Process templates for agent and dnsmasq plus sandbox/eval tests. | -| Detailed design | Verify INV-NET-008 (Guest-network-admin isolation): Process Provider correctly inherits Guest netns for agent/dnsmasq. | +| Detailed design | Verify INV-NET-008 (Guest-network-admin isolation): Process Provider correctly inherits Guest netns for agent/dnsmasq. Primary reuse disposition: `adapt`. Preserved source-plan detail: preserve and verify existing guest-netns isolation invariant in v3 Process specs. | | Integration | `Provider/network-local` emits `namespaceClasses: []` and guest-only capability classes; Process Provider starts agent/dnsmasq inside the net VM network namespace only. | | Data migration | None — docs/tooling only; no runtime state. | | Validation | `tests/unit/nix/cases/process-sandbox-netns.nix` and provider Process-template tests assert no host capability or host network namespace grant. | | Removal proof | None — security invariant preserved; no prior owner to remove. | -### ADR046-network-017 +### ADR046-nl-017 | Field | Value | | --- | --- | | Dependency/owner | Docs; owned by `d2b-provider-network-local` package documentation. | | Current source | None — provider crate README is net-new for v3 packaging; this dossier supplies the required topics. | -| Reuse action | net-new documentation | +| Reuse action | create | | Destination | `packages/d2b-provider-network-local/README.md`. | -| Detailed design | `packages/d2b-provider-network-local/README.md` covering all 7 required topics. | +| Detailed design | `packages/d2b-provider-network-local/README.md` covering all 7 required topics. Primary reuse disposition: `create`. Preserved source-plan detail: net-new documentation. | | Integration | Workspace policy requires the README alongside `src/`, `tests/`, and `integration/`; operators and contributors use it for provider identity, build, test, integration, state, RBAC, and standalone-repo path. | | Data migration | None — docs/tooling only; no runtime state. | | Validation | `make test-policy` / `xtask workspace-policy` verifies required provider crate paths and README presence. | | Removal proof | None — net-new documentation; no prior owner to remove. | -### ADR046-network-018 +### ADR046-nl-018 | Field | Value | | --- | --- | | Dependency/owner | Broker plus device provider boundary; `UsbipBindFirewallRule` remains owned by `Provider/device-usbip`. | | Current source | Existing `UsbipBindFirewallRule` owns per-busid `inet d2b` exposure, while legacy `network.nix` and `net.nix` add broader TCP/3240 allows. | -| Reuse action | preserve the typed closed broker op; remove both generic network-local USBIP allows | +| Reuse action | adapt | | Destination | Device-usbip EffectPort/adapter owns USBIP rules, drift, and strict provider status; network-local host/net-VM renderers and status cover only Network-owned policy. | -| Detailed design | `UsbipBindFirewallRule` remains the sole broker mutation path for exact per-Network/per-busid TCP/3240 exposure. Network-local emits no TCP/3240 match, excludes device-usbip ownership markers from its digest, and never reports USBIP drift. | +| Detailed design | `UsbipBindFirewallRule` remains the sole broker mutation path for exact per-Network/per-busid TCP/3240 exposure. Network-local emits no TCP/3240 match, excludes device-usbip ownership markers from its digest, and never reports USBIP drift. Primary reuse disposition: `adapt`. Preserved source-plan detail: preserve the typed closed broker op; remove both generic network-local USBIP allows. | | Integration | `Provider/device-usbip` watches only Network identity/readiness/generation; Core privately resolves the Network attachment for the one relay Endpoint authority and firewall op. Binding proxies receive authorized connected streams through LaunchTickets. | | Data migration | Full d2b 3.0 reset; no v2 state/config import. | | Validation | Network-local host and net-VM firewall intent tests assert no TCP/3240/USBIP rule; USBIP rule churn leaves Network digest/`FirewallReady` unchanged; device-usbip tests own exact scoping, drift, status, and release. | | Removal proof | Legacy generic `network.nix` and `net.nix` USBIP allow fragments and any golden expectation for them are removed after device-usbip host integration passes. | -### ADR046-network-019 +### ADR046-nl-019 | Field | Value | | --- | --- | | Dependency/owner | Provider; depends on D087 ProviderStateSet and status-first storage rules. | | Current source | None — net-new v3 ProviderStateSet/status contract; v1 network module did not expose Provider state Volumes. | -| Reuse action | net-new status-first provider-state conformance | +| Reuse action | create | | Destination | Provider descriptor, controller-main deployment, `tests/state_schema_roundtrip.rs`, and eval case `provider-state-volume-eval.nix`. | -| Detailed design | Confirm `controller-main` declares no stateNamespace and core ProviderDeployment creates no Provider state Volume or state mount; validate ProviderStateSet query returns empty for `Provider/network-local`; validate bounded operational state is written to revisioned/redacted status and the core Operation ledger with `status-oversize` conformance; confirm per-Network config Volumes remain `ownerRef: Network/` runtime/config operational Volumes outside the ProviderStateSet and `Volume` is not in `ResourceTypes implemented`. | +| Detailed design | Confirm `controller-main` declares no stateNamespace and core ProviderDeployment creates no Provider state Volume or state mount; validate ProviderStateSet query returns empty for `Provider/network-local`; validate bounded operational state is written to revisioned/redacted status and the core Operation ledger with `status-oversize` conformance; confirm per-Network config Volumes remain `ownerRef: Network/` runtime/config operational Volumes outside the ProviderStateSet and `Volume` is not in `ResourceTypes implemented`. Primary reuse disposition: `create`. Preserved source-plan detail: net-new status-first provider-state conformance. | | Integration | Core ProviderDeployment starts controller without `/state`; controller uses Network status and Operation ledger for bounded observations; ProviderStateSet query excludes per-Network config Volumes. | | Data migration | Full d2b 3.0 reset; no v2 state/config import. | | Validation | `tests/state_schema_roundtrip.rs` and `tests/unit/nix/cases/provider-state-volume-eval.nix` validate empty ProviderStateSet, status bounds/redaction, and config Volume exclusion. | | Removal proof | None — net-new; no Provider state Volume or prior owner to remove. | -### ADR046-network-020 +### ADR046-nl-020 | Field | Value | | --- | --- | | Dependency/owner | D097 Core authority index, Network contracts, runtime-cloud-hypervisor private attachment path | | Current source | Current macvtap spawn resolves a raw parent interface but has no Host-global duplicate admission, sharing policy, authority status, or owner-proof lifecycle. | -| Reuse action | adapt the existing broker-internal macvtap-FD creation path behind mandatory Core authority admission | +| Reuse action | adapt | | Destination | Network schema/Provider descriptor, Core authority index, Network reconcile/update/finalizer, runtime LaunchTicket resolver, and authority tests | -| Detailed design | Register the external physical-NIC `AuthorityDescriptor`: Host-global `external-physical-nic/v1` Core-derived identity, `zero-or-one` authority, exclusive `passthru`/`private`/`vepa`, exclusive-by-default `bridge`, explicitly compatible bounded multiplexing only for bridge, `external-physical-nic-conflict`, exact owner proof, drain-release-reacquire update, forbidden export, and bounded FIFO holder policy. | +| Detailed design | Register the external physical-NIC `AuthorityDescriptor`: Host-global `external-physical-nic/v1` Core-derived identity, `zero-or-one` authority, exclusive `passthru`/`private`/`vepa`, exclusive-by-default `bridge`, explicitly compatible bounded multiplexing only for bridge, `external-physical-nic-conflict`, exact owner proof, drain-release-reacquire update, forbidden export, and bounded FIFO holder policy. Primary reuse disposition: `adapt`. Preserved source-plan detail: adapt the existing broker-internal macvtap-FD creation path behind mandatory Core authority admission. | | Integration | Core preflight gates every runtime LaunchTicket/`SpawnRunner`; status reports bounded authority state and conditions; D091 update and finalizer close macvtap ownership before release. | | Data migration | Full d2b 3.0 reset; no legacy authority import. | | Validation | `external_nic_authority.rs` covers Core-derived identity, same-/cross-Zone conflicts, explicit bridge multiplexing, incompatible policy, non-bridge multiplex denial, no-effect rejection, adoption ambiguity, owner transfer, disruptive update, release ordering, and redaction; Nix eval and host integration cover declared configuration and lifecycle. | diff --git a/docs/specs/providers/ADR-046-provider-notification-desktop.md b/docs/specs/providers/ADR-046-provider-notification-desktop.md index 8d7ac3b2d..a2479922a 100644 --- a/docs/specs/providers/ADR-046-provider-notification-desktop.md +++ b/docs/specs/providers/ADR-046-provider-notification-desktop.md @@ -1333,9 +1333,9 @@ The v2 `d2b.notify.v2.NotifyService` ttrpc contract is superseded by | --- | --- | | Dependency/owner | W0 shared contract root; session/bus owner | | Current source | `packages/d2b-notify/src/{events,state,notifications,nonce}.rs` | -| Reuse action | copy/adapt | +| Reuse action | adapt | | Destination | `packages/d2b-provider-notification-desktop/src/{types,redact,action_nonce}.rs` | -| Detailed design | `NotificationRequest`/`NotificationResult` DTOs and stream record types; bounded fields; closed category set; icon catalog contract; `ActionNonce`/`ActionNonceStore` adapted from main; no ResourceType DTO | +| Detailed design | `NotificationRequest`/`NotificationResult` DTOs and stream record types; bounded fields; closed category set; icon catalog contract; `ActionNonce`/`ActionNonceStore` adapted from main; no ResourceType DTO Primary reuse disposition: `adapt`. Preserved source-plan detail: copy/adapt. | | Integration | Zone bus service; host-sink stream consumer; guest-source stream producer | | Data migration | No v2 compatibility; reset | | Validation | `tests/stream_record.rs` — DTO schema vectors; `tests/action_nonce.rs` — single-use/TTL/capacity/replay | @@ -1345,11 +1345,11 @@ The v2 `d2b.notify.v2.NotifyService` ttrpc contract is superseded by | Field | Value | | --- | --- | -| Dependency/owner | ADR046-session-001, ADR046-bus-001; session/bus wiring | +| Dependency/owner | ADR046-session-001, ADR046-session-003; session/bus wiring | | Current source | `packages/d2b-notify/src/services/` | -| Reuse action | copy/adapt | +| Reuse action | adapt | | Destination | `packages/d2b-provider-notification-desktop/src/stream_admission.rs` | -| Detailed design | Session admission checks, Noise profile enforcement, transport class validation | +| Detailed design | Session admission checks, Noise profile enforcement, transport class validation Primary reuse disposition: `adapt`. Preserved source-plan detail: copy/adapt. | | Integration | ComponentSession/d2b-bus | | Data migration | None — full d2b 3.0 reset; no prior state to migrate | | Validation | `tests/stream_admission.rs` — all rejection vectors | @@ -1361,7 +1361,7 @@ The v2 `d2b.notify.v2.NotifyService` ttrpc contract is superseded by | --- | --- | | Dependency/owner | ADR046-notify-001, ADR046-reconcile-001; controller owner | | Current source | `packages/d2b-notify/src/services/observer.rs` | -| Reuse action | new | +| Reuse action | create | | Destination | `packages/d2b-provider-notification-desktop/src/controller.rs` | | Detailed design | Async Process placement controller; watches `guestSources` Guest refs; creates/drains/deletes guest-source Processes; creates/stops host-sink Process on display-wayland readiness change; declares no Provider state Volume and does not own/add/create/delete Volumes; bounded non-secret operational state lives in `status`/the core Operation ledger (D087); notification delivery state (in-memory projection, action nonce store) is host-sink process memory only; no ResourceType reconcile loop | | Integration | Zone resource store (Process API); d2b-bus; display-wayland dependency watch | @@ -1389,7 +1389,7 @@ The v2 `d2b.notify.v2.NotifyService` ttrpc contract is superseded by | --- | --- | | Dependency/owner | ADR046-notify-002; guest-source owner | | Current source | v3 security-key guest vsock path (conceptual similarity only; not copied directly) | -| Reuse action | new | +| Reuse action | create | | Destination | `packages/d2b-provider-notification-desktop/src/guest_source.rs` | | Detailed design | Guest-side vsock ComponentSession; `NotificationRequest` record validation and field bounding; category filter; `DesktopNotificationSink` stream forwarding; `NotificationResult` handling; no host-side resource creation | | Integration | Guest process vsock → host ComponentSession | diff --git a/docs/specs/providers/ADR-046-provider-observability-otel.md b/docs/specs/providers/ADR-046-provider-observability-otel.md index abcfb9d2c..6be334446 100644 --- a/docs/specs/providers/ADR-046-provider-observability-otel.md +++ b/docs/specs/providers/ADR-046-provider-observability-otel.md @@ -2118,9 +2118,9 @@ Old and new suites never run in parallel indefinitely. | Work item ID | `ADR046-otel-001` | | Dependency/owner | ADR046-telem-001 (BoundedEmitter crate); W0/W2; telemetry/observability owner | | Current source | `packages/d2b-host/src/otel_host_bridge_argv.rs` (`OtelHostBridgeArgvInputs`, `otel_host_bridge_argv`, `OtelHostBridgeArgvError`); `packages/d2bd/src/otel_host_bridge_readiness.rs` (`OtelHostBridgeReadiness`, `otel_host_bridge_read`); `packages/d2b-contracts/src/broker_wire.rs::RunnerRole::OtelHostBridge`; `packages/d2b-core/src/processes.rs::ProcessRole::OtelHostBridge` | -| Reuse action | adapt (`OtelHostBridgeArgvInputs` vsock logic → native Rust OTLP relay); adapt (`OtelHostBridgeReadiness` → `TelemetryBinding` conditions); delete-after-cutover (`RunnerRole::OtelHostBridge`, `ProcessRole::OtelHostBridge`) | +| Reuse action | adapt | | Destination | `packages/d2b-provider-observability-otel/src/{forwarder_bin,controller,binding}.rs` | -| Detailed design | Binding-owned forwarder: accept OTLP only from the exact Guest producer, relay through the Binding-private Endpoint/Volume to its edge collector, enforce bounded frames/quota/session timeout, and use no OTEL SDK. Map forwarder readiness to `status.provider`, then derive provider-neutral Binding ingest readiness; Process Provider owns launch/pidfd. | +| Detailed design | Binding-owned forwarder: accept OTLP only from the exact Guest producer, relay through the Binding-private Endpoint/Volume to its edge collector, enforce bounded frames/quota/session timeout, and use no OTEL SDK. Map forwarder readiness to `status.provider`, then derive provider-neutral Binding ingest readiness; Process Provider owns launch/pidfd. Primary reuse disposition: `adapt`. Preserved source-plan detail: adapt (`OtelHostBridgeArgvInputs` vsock logic → native Rust OTLP relay); adapt (`OtelHostBridgeReadiness` → `TelemetryBinding` conditions); delete-after-cutover (`RunnerRole::OtelHostBridge`, `ProcessRole::OtelHostBridge`). | | Integration | Controller creates vsock-forwarder long-lived `Process` → ProviderSupervisor → system-minijail/systemd launch → vsock socket bind → Guest side connects | | Data migration | Full reset; existing socat bridge retired after cutover | | Validation | `integration/scenario_obs_zone_forwarding.rs`; adapted `minijail_relay_otel.rs` shape test for Provider-managed runner; assert `RunnerRole::OtelHostBridge` is absent from `d2b-contracts` after removal | @@ -2133,9 +2133,9 @@ Old and new suites never run in parallel indefinitely. | Work item ID | `ADR046-otel-002` | | Dependency/owner | ADR046-otel-001 + ADR046-telem-001 + ADR046-provider-001 (Provider toolkit) + ADR046-provider-004 (common telemetry Service/Binding base) + resource/Endpoint/Volume contracts; W2; observability owner | | Current source | `nixos-modules/components/observability/host.nix` (`otelRuntimeDir`, `hostEgressSocket`, `setfacl` ACL pattern, `scrapeJournal` option, `identityName`); `nixos-modules/components/observability/stack.nix` (`ingressSources`, `vmName`, `receiverGrpcPort`, loopback binding, `signoz.listenPort`) | -| Reuse action | adapt Nix pipeline shape (replace per-VM `vmName` with per-Zone name; replace socat runner with vsock-forwarder long-lived Process; adapt `ingressSources` per-Zone entry) | +| Reuse action | adapt | | Destination | `packages/d2b-provider-observability-otel/src/{collector_bin,emitter_socket,exporter,controller,service,binding}.rs`; updated Nix observability modules | -| Detailed design | Register the initial implementation of both provider-neutral qualified ResourceTypes by binding the exact ADR046-provider-004 base versions/fingerprints, then define only strict observability-otel Service/Binding spec and status extensions. Reconcile each Binding into an edge collector, private Endpoints, runtime Volume, and optional forwarder. Collector links the full OTEL SDK, resolves `serviceRef`, stamps trusted producer identity, and enforces common signals/quota/policy plus strict batching extension. Write generic Service/Binding observations to `status.resource` and SigNoz/OTLP/OTEL observations only to `status.provider`; no state file or Provider state Volume. Provider root config remains installation-only. | +| Detailed design | Register the initial implementation of both provider-neutral qualified ResourceTypes by binding the exact ADR046-provider-004 base versions/fingerprints, then define only strict observability-otel Service/Binding spec and status extensions. Reconcile each Binding into an edge collector, private Endpoints, runtime Volume, and optional forwarder. Collector links the full OTEL SDK, resolves `serviceRef`, stamps trusted producer identity, and enforces common signals/quota/policy plus strict batching extension. Write generic Service/Binding observations to `status.resource` and SigNoz/OTLP/OTEL observations only to `status.provider`; no state file or Provider state Volume. Provider root config remains installation-only. Primary reuse disposition: `adapt`. Preserved source-plan detail: adapt Nix pipeline shape (replace per-VM `vmName` with per-Zone name; replace socat runner with vsock-forwarder long-lived Process; adapt `ingressSources` per-Zone entry). | | Integration | `BoundedEmitter` → Binding-private Endpoint → edge collector/OTEL SDK → same-Zone authority or projected Service → SigNoz | | Data migration | Existing SigNoz data not migrated; v3 starts fresh per Zone | | Validation | Common-fixture/fingerprint and canonical-minimal-base conformance (including a fake alternate telemetry Provider); `tests/emitter_socket_receive.rs`; `tests/exporter_outage.rs`; `tests/exporter_backpressure.rs`; `integration/scenario_full_pipeline.rs`; adapted `policy_observability.rs` (retain all existing assertions; add new `d2b.zone`, `d2b.provider` allowlist entries) | @@ -2148,9 +2148,9 @@ Old and new suites never run in parallel indefinitely. | Work item ID | `ADR046-otel-003` | | Dependency/owner | ADR046-otel-002 + ADR046-telem-001 + ADR046-volume-001; Nix/observability owner | | Current source | `nixos-modules/components/observability/host.nix::journaldStorageDir`, `scrapeJournal` option; journald cgroup-path filtering pattern | -| Reuse action | adapt journald receiver config for per-Zone cgroup filter | +| Reuse action | adapt | | Destination | `packages/d2b-provider-observability-otel/src/nix/journald.nix`; `packages/d2b-provider-observability-otel/src/journald.rs` | -| Detailed design | Per `ADR-046-telemetry-audit-and-support` §journald stdout/stderr ingestion: cgroup filter derived from trusted `producerRef`; redaction drops credential/secret/path fields, `_CMDLINE`, `_EXE`, and `INVOCATION_ID`; the strict `TelemetryBinding.spec.provider.settings.journald.enable` defaults false. | +| Detailed design | Per `ADR-046-telemetry-audit-and-support` §journald stdout/stderr ingestion: cgroup filter derived from trusted `producerRef`; redaction drops credential/secret/path fields, `_CMDLINE`, `_EXE`, and `INVOCATION_ID`; the strict `TelemetryBinding.spec.provider.settings.journald.enable` defaults false. Primary reuse disposition: `adapt`. Preserved source-plan detail: adapt journald receiver config for per-Zone cgroup filter. | | Integration | Collector binary journald receiver config path → cgroup filter expression → OTel Collector journald receiver → redaction filter → OTLP export | | Data migration | None — full d2b 3.0 reset; no prior state to migrate | | Validation | Nix eval test: filter expression set when enabled; assert `_CMDLINE` and `INVOCATION_ID` in drop list; `tests/redaction.rs` for journald field redaction | @@ -2163,9 +2163,9 @@ Old and new suites never run in parallel indefinitely. | Work item ID | `ADR046-otel-004` | | Dependency/owner | ADR046-otel-002; policy/contract-tests owner | | Current source | `packages/d2b-contract-tests/tests/policy_observability.rs` (`loki_native_otel_resource_attributes` allowlist; `tempo_stack_signoz_backend_and_collector`; `startup_tracing_avoids_host_path_fields`); `packages/d2b-contract-tests/tests/policy_metrics.rs` (`EXPECTED_METRICS` table); `packages/d2b-contract-tests/tests/minijail_relay_otel.rs` | -| Reuse action | adapt and extend existing tests; keep existing test assertions | +| Reuse action | adapt | | Destination | `packages/d2b-contract-tests/tests/policy_observability.rs` (updated); `packages/d2b-contract-tests/tests/policy_telemetry_redaction.rs` (new, per ADR046-telem-008); `packages/d2b-provider-observability-otel/tests/no_vm_label_in_metrics.rs` | -| Detailed design | (1) Extend `loki_native_otel_resource_attributes` allowlist with `d2b.zone`, `d2b.provider`, `d2b.component`, `service.version`. (2) Add gate: `no_isolation` must not appear in any Provider `MetricDescriptor` label or span attribute catalog. (3) Adapt `minijail_relay_otel.rs` shape test for Provider-managed runner (no broker `RunnerRole::OtelHostBridge`). (4) Add metric inventory gates for `d2b_otel_*` instruments from this spec. (5) Retain: `startup_tracing_avoids_host_path_fields`; SigNoz-only backend assertion; `tempo_guest_collector_shape`; `config_source = "realm-controllers"` absence gate. | +| Detailed design | (1) Extend `loki_native_otel_resource_attributes` allowlist with `d2b.zone`, `d2b.provider`, `d2b.component`, `service.version`. (2) Add gate: `no_isolation` must not appear in any Provider `MetricDescriptor` label or span attribute catalog. (3) Adapt `minijail_relay_otel.rs` shape test for Provider-managed runner (no broker `RunnerRole::OtelHostBridge`). (4) Add metric inventory gates for `d2b_otel_*` instruments from this spec. (5) Retain: `startup_tracing_avoids_host_path_fields`; SigNoz-only backend assertion; `tempo_guest_collector_shape`; `config_source = "realm-controllers"` absence gate. Primary reuse disposition: `adapt`. Preserved source-plan detail: adapt and extend existing tests; keep existing test assertions. | | Integration | Contract-tests run in workspace `make test-drift` and `make test-lint` | | Data migration | None — full d2b 3.0 reset; no prior state to migrate | | Validation | All contract-tests pass after update; existing allowlist test does not regress | @@ -2179,9 +2179,9 @@ Old and new suites never run in parallel indefinitely. | Dependency/owner | ADR046-zone-control-019, ADR046-zone-control-020; observability Provider owner | | Current source | None — net-new ADR 0046 cross-Zone sharing (D096) | | Reuse source | SigNoz ingest authority (this dossier); `packages/d2b-provider/src/share_adapter.rs` `ExportAdapter`/`ImportAdapter` traits | -| Reuse action | net-new (implement the signed observability export/import adapter) | +| Reuse action | adapt | | Destination | `packages/d2b-provider-observability-otel/src/share_adapter.rs` | -| Detailed design | Implement the signed adapter: `sys-obs` exports only the authority `TelemetryService`; its local ingest Endpoint remains Service-owned implementation transport and is never an Export field. ResourceExport uses only canonical `serviceType`, `projectionSchemaFingerprint`, and `factoryFingerprint`; ResourceImport uses the corresponding `expectedServiceType`, `expectedProjectionSchemaFingerprint`, and `expectedFactoryFingerprint`. Every producer import causes Core to create one local `TelemetryService` projection with ResourceImport ownership, `providerRef`, semantic base/import fields, and no `spec.provider`. Routing derives from the signed local descriptor and ResourceImport record. The semantic factory fingerprint binds factory metadata plus projection-protocol version, never Provider/adapter identity, which the signed descriptor authenticates separately. Binding `serviceRef` targets that projection. Enforce many-to-one quota/credit/backpressure/schema/source-stamp/redaction/cardinality over bounded encrypted streams; no FD/socket crosses a Zone and audit authority stays local. | +| Detailed design | Implement the signed adapter: `sys-obs` exports only the authority `TelemetryService`; its local ingest Endpoint remains Service-owned implementation transport and is never an Export field. ResourceExport uses only canonical `serviceType`, `projectionSchemaFingerprint`, and `factoryFingerprint`; ResourceImport uses the corresponding `expectedServiceType`, `expectedProjectionSchemaFingerprint`, and `expectedFactoryFingerprint`. Every producer import causes Core to create one local `TelemetryService` projection with ResourceImport ownership, `providerRef`, semantic base/import fields, and no `spec.provider`. Routing derives from the signed local descriptor and ResourceImport record. The semantic factory fingerprint binds factory metadata plus projection-protocol version, never Provider/adapter identity, which the signed descriptor authenticates separately. Binding `serviceRef` targets that projection. Enforce many-to-one quota/credit/backpressure/schema/source-stamp/redaction/cardinality over bounded encrypted streams; no FD/socket crosses a Zone and audit authority stays local. Primary reuse disposition: `adapt`. Preserved source-plan detail: net-new (implement the signed observability export/import adapter). | | Integration | Core export/import controller (ADR046-zone-control-019); local projection lifecycle (ADR046-zone-control-020); ComponentSession bounded encrypted named streams | | Data migration | None — full d2b 3.0 reset | | Validation | Fast `projection_chain.rs` proves Service projection semantics with a fake stream, exact canonical Export/Import type and fingerprint fields, rejection of obsolete `endpointRef`/`exportedType`/`baseSchemaFingerprint`/`expectedType`/`expectedBaseSchemaFingerprint`/`projectionType`, rejection of projection `spec.provider`, and semantic-fingerprint stability under Provider/adapter identity mutation while signed descriptor authentication remains exact; integration alone runs real encrypted streams and SigNoz; revocation/reconnect, quotas, source stamp, redaction/cardinality, no FD crossing, and audit locality | @@ -2195,9 +2195,9 @@ Old and new suites never run in parallel indefinitely. | Dependency/owner | ADR046-otel-005, ADR046-zone-control-019, ADR046-zone-control-020; observability Provider owner | | Current source | `nixos-modules/components/observability/{stack,host,guest}.nix` (SigNoz/ClickHouse/Keeper loopback authority, per-source vsock ingress, source-identity `upsert`, `sending_queue`/`retry_on_failure`, allow-list projection); `nixos-modules/{index,observability-vm}.nix`; `packages/d2b-host/src/{otel_host_bridge_argv,vsock_relay_argv}.rs` (pre-opened vsock fds; broker rejects source VM ≠ obs VM); `packages/d2bd/src/otel_host_bridge_readiness.rs` | | Reuse source | Same baseline stack/bridge/readiness symbols; `packages/d2b-provider/src/share_adapter.rs` traits | -| Reuse action | net-new universal provider-neutral `telemetry.d2bus.org.TelemetryService`/`telemetry.d2bus.org.TelemetryBinding` pair; adapt existing authority/edge behavior as the initial observability-otel implementation | +| Reuse action | adapt | | Destination | `packages/d2b-provider-observability-otel/src/{authority,service,binding,projection}.rs`; `AuthorityDescriptor` on the `sys-obs` `TelemetryService` | -| Detailed design | Implement one provider-neutral D097 authority Service with generic telemetry-ingest Endpoint refs and common service/signals/quota/policy fields. Its strict observability-otel `spec.provider` alone selects the loopback SigNoz stack, backend Endpoints, and OTLP. Core rejects duplicates and adopts by owner proof. Implement core-owned imported Service projections with no `spec.provider` or backend ownership. Implement per-producer Bindings with common service/producer/signals/quota/policy fields and strict implementation extension; Bindings own/cause edge collector/forwarder/private Endpoints/runtime Volume. Keep generic observations in `status.resource` and SigNoz/OTLP/OTEL observations in `status.provider`. Preserve trusted source upsert, retry/queue, bounded cardinality/redaction, audit non-transfer, status-first state, and no OTEL SDK in core. Endpoint is transport only. | +| Detailed design | Implement one provider-neutral D097 authority Service with generic telemetry-ingest Endpoint refs and common service/signals/quota/policy fields. Its strict observability-otel `spec.provider` alone selects the loopback SigNoz stack, backend Endpoints, and OTLP. Core rejects duplicates and adopts by owner proof. Implement core-owned imported Service projections with no `spec.provider` or backend ownership. Implement per-producer Bindings with common service/producer/signals/quota/policy fields and strict implementation extension; Bindings own/cause edge collector/forwarder/private Endpoints/runtime Volume. Keep generic observations in `status.resource` and SigNoz/OTLP/OTEL observations in `status.provider`. Preserve trusted source upsert, retry/queue, bounded cardinality/redaction, audit non-transfer, status-first state, and no OTEL SDK in core. Endpoint is transport only. Primary reuse disposition: `adapt`. Preserved source-plan detail: net-new universal provider-neutral `telemetry.d2bus.org.TelemetryService`/`telemetry.d2bus.org.TelemetryBinding` pair; adapt existing authority/edge behavior as the initial observability-otel implementation. | | Integration | Ingest authority + export (ADR046-otel-005); core export/import controller and projection lifecycle (ADR046-zone-control-019/020); ComponentSession per-import encrypted streams; d2b-telemetry closed-label metrics | | Data migration | None — full d2b 3.0 reset | | Validation | Fast `resource_service_binding.rs` and `projection_chain.rs` plus reused nix-unit/policy tests prove provider-neutral names, base/Provider field separation, projection `spec.provider` rejection, exact D088 placement of semantic observations under `status.resource` and implementation observations under `status.provider`, ownership, schemas, stamping, quotas, redaction, and projection chain. Real SigNoz and real stream scenarios are integration-only. | diff --git a/docs/specs/providers/ADR-046-provider-runtime-azure-container-apps.md b/docs/specs/providers/ADR-046-provider-runtime-azure-container-apps.md index 277b5fd4b..8b01376be 100644 --- a/docs/specs/providers/ADR-046-provider-runtime-azure-container-apps.md +++ b/docs/specs/providers/ADR-046-provider-runtime-azure-container-apps.md @@ -1276,9 +1276,9 @@ All sources in this section are from main commit `a1cc0b2da4a08ca3240a770a972fe4 | --- | --- | | Dependency/owner | ADR046-provider-001; runtime-aca owner | | Current source | `packages/d2b-provider-aca/src/lib.rs`: `AcaWorkloadProvider`, 2841 lines production-reachable; `packages/d2b-provider-runtime-azure-container-apps/src/provider.rs`: `AzureContainerAppsRuntimeProvider`, 2796 lines (test-only at v3 baseline) | -| Reuse action | REPLACE (old) + ADAPT (main types/traits) | +| Reuse action | replace | | Destination | `packages/d2b-provider-runtime-azure-container-apps/src/controller.rs` | -| Detailed design | Async `Guest` reconcile loop: `describe` → `validateSpec` → `plan` → `reconcile` → `observe` → `finalize`. Adoption before first `RuntimeEnsure`. Operation ledger persisted to Volume inside gateway Guest. Credential lease acquire/revoke per call. Noise KK enrollment via ZoneLink (from gateway Guest to managed ACA sandbox). `providerPhase` and `guestIdentityDigest` in `status.provider.details`; no raw endpoint/path in any status field. **ProviderDeployment creates both static Processes; ACA controller never instantiates its own Processes and never writes Provider status directly. All Processes run inside the gateway Guest. No Host Process, no Host Credential, no Host Azure HTTP socket. Long-running cloud ops return `progressing`/`requeue-at` immediately; never block watch loop.** | +| Detailed design | Async `Guest` reconcile loop: `describe` → `validateSpec` → `plan` → `reconcile` → `observe` → `finalize`. Adoption before first `RuntimeEnsure`. Operation ledger persisted to Volume inside gateway Guest. Credential lease acquire/revoke per call. Noise KK enrollment via ZoneLink (from gateway Guest to managed ACA sandbox). `providerPhase` and `guestIdentityDigest` in `status.provider.details`; no raw endpoint/path in any status field. **ProviderDeployment creates both static Processes; ACA controller never instantiates its own Processes and never writes Provider status directly. All Processes run inside the gateway Guest. No Host Process, no Host Credential, no Host Azure HTTP socket. Long-running cloud ops return `progressing`/`requeue-at` immediately; never block watch loop.** Primary reuse disposition: `replace`. Preserved source-plan detail: REPLACE (old) + ADAPT (main types/traits). | | Integration | Zone ResourceClient → ProviderDeployment → Process launch inside gateway Guest → d2b-bus → deployment service | | Data migration | Full d2b 3.0 reset; no v2 provider state compatibility | | Validation | Controller conformance suite; adoption/ambiguity tests; deadline/cancellation matrix; redaction coverage; **gateway Guest placement validation: assert no Process has `executionRef: Host/*`**; Process spec field schema tests (`spec.template`, canonical `sandbox`/`budget`/`networkUsage`/`endpoints`/`readiness`/`restartPolicy` fields, `mounts` with `required: true`, `providerRef: Provider/system-minijail`); ProviderDeployment creates both Processes (controller never self-spawns); no raw endpoint/path in Guest status | @@ -1290,7 +1290,7 @@ All sources in this section are from main commit `a1cc0b2da4a08ca3240a770a972fe4 | --- | --- | | Dependency/owner | ADR046-aca-001; Nix/gateway wiring owner | | Current source | n/a — new requirement (gateway Guest placement) | -| Reuse action | NEW | +| Reuse action | create | | Destination | `nixos-modules/` (gateway Guest declaration, Process template wiring, Credential scope assertion); eval-time validation module | | Detailed design | Nix eval-time assertions for: (a) `gatewayExecutionRef` resolves to a `Guest` resource, not `Host/*`; (b) Credential `scope.executionRef` matches `gatewayExecutionRef`; (c) all Process templates emitted for this Provider have `executionRef` equal to `gatewayExecutionRef`. No `User` resource or `users.users.*` declarations required — component principals are framework-assigned and not OS accounts. Gateway Guest NixOS closure includes only the two ACA component binaries (§15.6). Assertion error messages name the offending resource and the required `gatewayExecutionRef`. | | Integration | Nix eval gate; `d2b.zones.*.resources` validation pass; consumer flake usage example | @@ -1304,9 +1304,9 @@ All sources in this section are from main commit `a1cc0b2da4a08ca3240a770a972fe4 | --- | --- | | Dependency/owner | ADR046-aca-001; deployment service owner | | Current source | `packages/d2b-gateway-runtime/src/provider_agent.rs` (main) | -| Reuse action | COPY/ADAPT (partial); exclude `aca_workload.rs` | +| Reuse action | adapt | | Destination | `packages/d2b-provider-runtime-azure-container-apps/src/deployment_service.rs` | -| Detailed design | `ProviderAgentProcess`-shaped binary; bounded dispatch (64 in-flight); bounded audit ring (1024 capacity); shutdown within 5 s; serves `d2b.aca.v3.deployment` service schema including `GuestHealth` (health probing folded in from former health worker). All ACA API calls go through the injected `AcaControl` port — no ambient network call, no SDK default chain. Long-running ops return `progressing`/`requeue-at` to the caller; no blocking on Azure API completion. | +| Detailed design | `ProviderAgentProcess`-shaped binary; bounded dispatch (64 in-flight); bounded audit ring (1024 capacity); shutdown within 5 s; serves `d2b.aca.v3.deployment` service schema including `GuestHealth` (health probing folded in from former health worker). All ACA API calls go through the injected `AcaControl` port — no ambient network call, no SDK default chain. Long-running ops return `progressing`/`requeue-at` to the caller; no blocking on Azure API completion. Primary reuse disposition: `adapt`. Preserved source-plan detail: COPY/ADAPT (partial); exclude `aca_workload.rs`. | | Integration | ProviderDeployment spawns service; d2b-bus routes GuestProvision/Start/Stop/Destroy/Adopt/Inspect/Health methods | | Data migration | None — full d2b 3.0 reset; no prior state to migrate | | Validation | Service dispatch matrix; RBAC refusal tests; redaction tests; shutdown deadline tests | @@ -1318,9 +1318,9 @@ All sources in this section are from main commit `a1cc0b2da4a08ca3240a770a972fe4 | --- | --- | | Dependency/owner | ADR046-aca-001; credential integration owner | | Current source | `packages/d2b-provider-runtime-azure-container-apps/src/control.rs` (main): `AcaCredentialLeaseClient`, `AcaCredentialLease`, `AcaCredentialLeaseRequest`, `AcaCredentialPurpose` | -| Reuse action | RETAIN+ADAPT | +| Reuse action | adapt | | Destination | `packages/d2b-contracts/src/provider_effects/aca.rs` (shared `d2b-contracts` provider-effects module; no new crate; provider crate remains one package) | -| Detailed design | `AcaCredentialLeaseClient`, `AcaCredentialLease`, `AcaCredentialLeaseRequest`, and `AcaCredentialPurpose` live in the shared `d2b-contracts` provider-effects module. Adapt `CredentialLease` to v3 Credential resource opaque lease handle. `AcaCredentialPurpose` maps to `allowedOperations` check against `Credential.spec`. Lease expiry capped at call deadline. Cleanup job pattern retained verbatim. | +| Detailed design | `AcaCredentialLeaseClient`, `AcaCredentialLease`, `AcaCredentialLeaseRequest`, and `AcaCredentialPurpose` live in the shared `d2b-contracts` provider-effects module. Adapt `CredentialLease` to v3 Credential resource opaque lease handle. `AcaCredentialPurpose` maps to `allowedOperations` check against `Credential.spec`. Lease expiry capped at call deadline. Cleanup job pattern retained verbatim. Primary reuse disposition: `adapt`. Preserved source-plan detail: RETAIN+ADAPT. | | Integration | Controller acquires lease per reconcile step via injected `AcaCredentialLeaseClient`; raw token delivered only via Noise KK E2E channel through `d2b.credential.v3.AcquireToken` method | | Data migration | None — full d2b 3.0 reset; no prior state to migrate | | Validation | Mock credential client tests; lease cleanup timeout tests; token non-exposure assertion | @@ -1332,9 +1332,9 @@ All sources in this section are from main commit `a1cc0b2da4a08ca3240a770a972fe4 | --- | --- | | Dependency/owner | ADR046-session-001; ZoneLink transport owner | | Current source | `packages/d2b-provider-relay/src/lib.rs`: `AzureRelayTransportProvider`; `packages/d2b-provider-aca/src/lib.rs`: `AcaRelayTransportConfig` | -| Reuse action | REPLACE (both); ADAPT config fields to ZoneLink transport settings | +| Reuse action | replace | | Destination | ZoneLink resource `spec.transportSettings` (§15.4); `Provider/transport-azure-relay` dossier (separate) | -| Detailed design | `AcaRelayTransportConfig` fields mapped to `transportSettings.relayNamespace`, `.relayHybridConnection`, `.relayCredentialRef`. KK enrollment replaces ZoneLink transport. | +| Detailed design | `AcaRelayTransportConfig` fields mapped to `transportSettings.relayNamespace`, `.relayHybridConnection`, `.relayCredentialRef`. KK enrollment replaces ZoneLink transport. Primary reuse disposition: `replace`. Preserved source-plan detail: REPLACE (both); ADAPT config fields to ZoneLink transport settings. | | Integration | ZoneLink controller resolves transport Provider; ACA controller establishes enrolled KK ComponentSession over relay transport after `GuestProvision` | | Data migration | No relay session compatibility; re-enroll on first `RuntimeAdopt` | | Validation | ZoneLink enrollment tests; relay unavailability tests; KK re-enrollment after sandbox restart | @@ -1346,9 +1346,9 @@ All sources in this section are from main commit `a1cc0b2da4a08ca3240a770a972fe4 | --- | --- | | Dependency/owner | ADR046-aca-001; state/migration owner | | Current source | `packages/d2b-provider-runtime-azure-container-apps/src/types.rs` (main): `AcaRuntimeConfig`, `AcaSandboxProfile`, `AcaResourceBinding`, `AcaWorkloadQuery` — test-only at v3 baseline | -| Reuse action | RETAIN+ADAPT | +| Reuse action | adapt | | Destination | `packages/d2b-provider-runtime-azure-container-apps/src/types.rs` | -| Detailed design | Replace `RealmId`/`WorkloadId` with v3 `Zone`/`Guest` UID types. `AcaResourceBinding` keys the adoption query. The provider declares **no** Provider state Volume: bounded, non-secret sandbox binding/adoption metadata lives in `Guest.status` (latest bounded observed handle digests) and in-flight operation/requeue truth lives in the core Operation ledger (D087). Neither Process mounts a state Volume; there is no `sandbox-state`/`service-state` Volume, no `User/d2b-aca-controller`/`User/d2b-aca-deployment-service` state-layout principal, and no empty identity-only Volume. On restart the controller re-derives observed binding from `Guest.status`, the core Operation ledger, and an external `find_sandboxes` query, treating status as observation, never authority. Host never holds cloud binding, admission, PSK, or operation state. | +| Detailed design | Replace `RealmId`/`WorkloadId` with v3 `Zone`/`Guest` UID types. `AcaResourceBinding` keys the adoption query. The provider declares **no** Provider state Volume: bounded, non-secret sandbox binding/adoption metadata lives in `Guest.status` (latest bounded observed handle digests) and in-flight operation/requeue truth lives in the core Operation ledger (D087). Neither Process mounts a state Volume; there is no `sandbox-state`/`service-state` Volume, no `User/d2b-aca-controller`/`User/d2b-aca-deployment-service` state-layout principal, and no empty identity-only Volume. On restart the controller re-derives observed binding from `Guest.status`, the core Operation ledger, and an external `find_sandboxes` query, treating status as observation, never authority. Host never holds cloud binding, admission, PSK, or operation state. Primary reuse disposition: `adapt`. Preserved source-plan detail: RETAIN+ADAPT. | | Integration | No Provider state Volume is created before Processes start; the controller writes its bounded observed binding/adoption metadata to `Guest.status` on material change and reads in-flight operation state from the core Operation ledger adapter | | Data migration | None — no state Volume at v3 `1.0` | | Validation | Controller/service declare empty `stateNamespaces`; no `sandbox-state`/`service-state` Volume created; neither Process mounts a state Volume; `Guest.status` binding/adoption fields are bounded, non-secret, and carry no credential/endpoint/poll-URL bytes; restart re-derivation from status/core ledger/external `find_sandboxes` without a Volume; core Operation ledger adapter integration test | @@ -1360,9 +1360,9 @@ All sources in this section are from main commit `a1cc0b2da4a08ca3240a770a972fe4 | --- | --- | | Dependency/owner | ADR046-aca-001; Nix/telemetry owner | | Current source | `nixos-modules/options-realms-workloads.nix`: `kind = "ProviderManaged"` → ACA; `packages/d2b-provider-aca/src/lib.rs`: tracing fields | -| Reuse action | REPLACE (Nix emitter) + ADAPT (metric/audit shapes) | +| Reuse action | replace | | Destination | `nixos-modules/` (generated Guest resource options); `packages/d2b-provider-runtime-azure-container-apps/src/{audit,metrics}.rs` | -| Detailed design | Eval-time assertions for ACA-specific invariants (§15.6). Closed OTEL label set (§13.4). Audit event schema (§13.3). Tracing target constant `d2b_provider_runtime_azure_container_apps::credential_lease_cleanup` retained. | +| Detailed design | Eval-time assertions for ACA-specific invariants (§15.6). Closed OTEL label set (§13.4). Audit event schema (§13.3). Tracing target constant `d2b_provider_runtime_azure_container_apps::credential_lease_cleanup` retained. Primary reuse disposition: `replace`. Preserved source-plan detail: REPLACE (Nix emitter) + ADAPT (metric/audit shapes). | | Integration | Nix eval gate; `observability-otel` Provider OTEL pipeline | | Data migration | None — full d2b 3.0 reset; no prior state to migrate | | Validation | Label cardinality policy test; audit commit-before-complete test; Nix assertion eval tests | diff --git a/docs/specs/providers/ADR-046-provider-runtime-azure-virtual-machine.md b/docs/specs/providers/ADR-046-provider-runtime-azure-virtual-machine.md index 383a7b1c1..dd09660e7 100644 --- a/docs/specs/providers/ADR-046-provider-runtime-azure-virtual-machine.md +++ b/docs/specs/providers/ADR-046-provider-runtime-azure-virtual-machine.md @@ -1355,9 +1355,9 @@ d2b.zones.dev.resources.corp-vm = { | --- | --- | | Dependency/owner | Provider contract owner | | Current source | `d2bd/src/provider_registry.rs`: `AzureVmForbidden`, `AZURE_VM_IMPLEMENTATION_ID`; `d2b-realm-provider/src/provider.rs`: `InfrastructureProvider` (dead-reachable) | -| Reuse action | Extract and adapt; DELETE `InfrastructureProvider` after this Provider is operational | +| Reuse action | adapt | | Destination | `src/{lib.rs,config.rs,schema.rs,error.rs,effect/mod.rs}` | -| Detailed design | Provider descriptor/manifest; `spec.config` schema; Guest spec.provider.settings schema; `AzureEffectPort` trait + `AzureOperationHandle`; `AzureVmError` enum; `SandboxSpec` with semantic classes; `BudgetSpec` with SI suffix memory fields; `restartPolicy` class/backoffBase/backoffMax; `networkUsage.allowEgress=false`; Endpoint ResourceType templates with name/transport/purpose | +| Detailed design | Provider descriptor/manifest; `spec.config` schema; Guest spec.provider.settings schema; `AzureEffectPort` trait + `AzureOperationHandle`; `AzureVmError` enum; `SandboxSpec` with semantic classes; `BudgetSpec` with SI suffix memory fields; `restartPolicy` class/backoffBase/backoffMax; `networkUsage.allowEgress=false`; Endpoint ResourceType templates with name/transport/purpose Primary reuse disposition: `adapt`. Preserved source-plan detail: Extract and adapt; DELETE `InfrastructureProvider` after this Provider is operational. | | Integration | ProviderDeployment loads the descriptor/catalog and ResourceType schemas; Nix and Guest specs reference the provider settings; controller and EffectPort modules consume the shared config/error types. | | Data migration | Full d2b 3.0 reset; no v2 state/config import. Existing registry sentinels are deleted only after the Provider resource model replaces them. | | Validation | Provider catalog; descriptor fingerprint; schema/conformance tests | @@ -1369,9 +1369,9 @@ d2b.zones.dev.resources.corp-vm = { | --- | --- | | Dependency/owner | ADR046-azure-vm-001 | | Current source | `d2b-realm-provider/src/rate_limit.rs` (implemented-and-reachable) | -| Reuse action | Copy and adapt | +| Reuse action | adapt | | Destination | `src/effect/{mod.rs,real.rs,fake.rs,rate_limit.rs}` | -| Detailed design | `AzureEffectPort` async trait; opaque `AzureOperationHandle` (bounded bytes, no poll URL); real `azure_core`/`azure_mgmt_compute` impl; `FakeAzureEffectPort` for hermetic tests; ARM 429/503/409 handling | +| Detailed design | `AzureEffectPort` async trait; opaque `AzureOperationHandle` (bounded bytes, no poll URL); real `azure_core`/`azure_mgmt_compute` impl; `FakeAzureEffectPort` for hermetic tests; ARM 429/503/409 handling Primary reuse disposition: `adapt`. Preserved source-plan detail: Copy and adapt. | | Integration | Azure VM controller lifecycle/idempotency code calls `AzureEffectPort`; the real implementation talks to ARM in production and `FakeAzureEffectPort` drives hermetic lifecycle tests. | | Data migration | No persistent data migration; in-flight ARM operation handles are new v3 status/core-ledger records and are re-derived or adopted on reconcile when absent. | | Validation | `tests/lifecycle_hermetic.rs`; all ARM paths via `FakeAzureEffectPort`; no ARM URL in test assertions | @@ -1383,9 +1383,9 @@ d2b.zones.dev.resources.corp-vm = { | --- | --- | | Dependency/owner | ADR046-azure-vm-001; ADR046-azure-vm-002; Guest ResourceType controller contract | | Current source | `d2b-realm-provider/src/conformance.rs`; main `a1cc0b2d`: `d2b-provider-toolkit/src/reconciler_loop.rs` | -| Reuse action | Copy/adapt main toolkit; adapt conformance shape | +| Reuse action | adapt | | Destination | `src/controller/{mod.rs,lifecycle.rs,idempotency.rs}` | -| Detailed design | Non-blocking reconcile: `start_*(...)` → persist `AzureOperationHandle` → `requeue-at`; `poll_lro` on subsequent ticks; controller as authorized `update-status` writer for Guest resources; finalizer held until ARM delete confirmed; top-level `phase`, `status.resource`, and Azure `status.provider.details.providerPhase` written atomically | +| Detailed design | Non-blocking reconcile: `start_*(...)` → persist `AzureOperationHandle` → `requeue-at`; `poll_lro` on subsequent ticks; controller as authorized `update-status` writer for Guest resources; finalizer held until ARM delete confirmed; top-level `phase`, `status.resource`, and Azure `status.provider.details.providerPhase` written atomically Primary reuse disposition: `adapt`. Preserved source-plan detail: Copy/adapt main toolkit; adapt conformance shape. | | Integration | Zone core dispatches Guest resource events to the Azure VM controller; ResourceClient updates status/finalizers; `AzureEffectPort` starts, polls, and deletes ARM LROs. | | Data migration | Full d2b 3.0 reset; old WorkloadProvider lifecycle state is not imported. Existing ARM resources may be adopted by tag/idempotency checks during reconcile. | | Validation | `tests/lifecycle_hermetic.rs`; `tests/conformance.rs` | @@ -1397,9 +1397,9 @@ d2b.zones.dev.resources.corp-vm = { | --- | --- | | Dependency/owner | ADR046-azure-vm-001; ComponentSession IKpsk2 | | Current source | `d2b-realm-provider/src/types.rs`: `ProviderGuestdBootstrapContract` (implemented-and-reachable); main `a1cc0b2d`: `d2b-session/src/bootstrap.rs` | -| Reuse action | Copy/adapt main `BootstrapPsk`/`BootstrapAdmission` | +| Reuse action | adapt | | Destination | `src/controller/bootstrap.rs`; `src/bootstrap_svc/{mod.rs,admission.rs,enrollment.rs}` | -| Detailed design | PSK generation; sealed PSK/admission/enrollment recovery material (ciphertext) in the controller's single guest-local sealed recovery Volume; `GrantBootstrapAdmission` typed bus call; IKpsk2 in bootstrap-svc; enrollment record; enrolled KK; the bootstrap-svc declares **no** state Volume (session state in process memory; obtains sealed PSK/admission from the controller only); the controller's sealed recovery Volume is an ordinary Volume resource created by core ProviderDeployment (before component Process start) from the controller's single `stateNamespaces` declaration with a Nix-preprovisioned `User/azure-vm-controller` layout principal; ARM operation/idempotency records live in the core Operation ledger and non-secret observed cloud phase lives in `Guest.status` (D087); controller does not own, create, or add Volume to exported ResourceTypes; it consumes its view dirfd only | +| Detailed design | PSK generation; sealed PSK/admission/enrollment recovery material (ciphertext) in the controller's single guest-local sealed recovery Volume; `GrantBootstrapAdmission` typed bus call; IKpsk2 in bootstrap-svc; enrollment record; enrolled KK; the bootstrap-svc declares **no** state Volume (session state in process memory; obtains sealed PSK/admission from the controller only); the controller's sealed recovery Volume is an ordinary Volume resource created by core ProviderDeployment (before component Process start) from the controller's single `stateNamespaces` declaration with a Nix-preprovisioned `User/azure-vm-controller` layout principal; ARM operation/idempotency records live in the core Operation ledger and non-secret observed cloud phase lives in `Guest.status` (D087); controller does not own, create, or add Volume to exported ResourceTypes; it consumes its view dirfd only Primary reuse disposition: `adapt`. Preserved source-plan detail: Copy/adapt main `BootstrapPsk`/`BootstrapAdmission`. | | Integration | Controller creates and seals recovery material in its state Volume, grants bootstrap admission over the bus, and bootstrap-svc performs IKpsk2 enrollment for Guest sessions. | | Data migration | No v2 bootstrap state import; the new sealed recovery Volume is initialized on first v3 activation, and old vsock bootstrap material is retired at cutover. | | Validation | `tests/bootstrap_hermetic.rs`; `tests/error_redaction.rs` | @@ -1411,9 +1411,9 @@ d2b.zones.dev.resources.corp-vm = { | --- | --- | | Dependency/owner | ADR046-azure-vm-003; Credential ResourceType; D055/D056 | | Current source | `d2b-realm-provider/src/credential.rs`: `AzureControlPlaneRef`, `OpaqueAzureRef`, `ManagedIdentityRef` (implemented-and-reachable) | -| Reuse action | Retain `OpaqueAzureRef` directly; adapt credential acquisition to enrolled KK | +| Reuse action | adapt | | Destination | `src/credential.rs` | -| Detailed design | ARM credential via enrolled KK `AcquireToken`; zeroizing token handling; no ambient credential fallback; `credential-managed-identity` guest-agent placement | +| Detailed design | ARM credential via enrolled KK `AcquireToken`; zeroizing token handling; no ambient credential fallback; `credential-managed-identity` guest-agent placement Primary reuse disposition: `adapt`. Preserved source-plan detail: Retain `OpaqueAzureRef` directly; adapt credential acquisition to enrolled KK. | | Integration | Controller obtains ARM credentials through enrolled KK and the Credential ResourceType before EffectPort operations; the credential-managed-identity guest agent provides the token source. | | Data migration | No ambient credential migration; v3 requires ResourceType Credential/ManagedIdentityRef plus enrolled KK, and the old direct IMDS fallback is removed. | | Validation | `tests/credential_hermetic.rs`; `tests/error_redaction.rs` | @@ -1425,9 +1425,9 @@ d2b.zones.dev.resources.corp-vm = { | --- | --- | | Dependency/owner | ADR046-azure-vm-003 | | Current source | `d2bd/src/provider_registry.rs`: `NEXT_LIFECYCLE_OPERATION_ID: AtomicU64` (production-reachable) | -| Reuse action | Adapt to deterministic per-Guest keys | +| Reuse action | adapt | | Destination | `src/controller/idempotency.rs` | -| Detailed design | Deterministic ARM request ID derivation; `AzureOperationHandle` opaque persistence (no poll URL in state); ARM 409 adoption; finalizer held through async deletion | +| Detailed design | Deterministic ARM request ID derivation; `AzureOperationHandle` opaque persistence (no poll URL in state); ARM 409 adoption; finalizer held through async deletion Primary reuse disposition: `adapt`. Preserved source-plan detail: Adapt to deterministic per-Guest keys. | | Integration | Lifecycle controller stores deterministic request IDs and opaque handles in the core Operation ledger/status; restart recovery reads them before polling or adopting ARM operations. | | Data migration | Old `AtomicU64` operation IDs are not imported; v3 operations use deterministic keys, while missing handles are re-derived or adopted from ARM. | | Validation | `tests/idempotency.rs`; restart-recovery scenario | @@ -1439,7 +1439,7 @@ d2b.zones.dev.resources.corp-vm = { | --- | --- | | Dependency/owner | ADR046-azure-vm-001; ADR-046-provider-state; ADR-046-nix-configuration | | Current source | `nixos-modules/options-realms-workloads.nix`: `WorkloadProviderKind::ProviderManaged` | -| Reuse action | Adapt | +| Reuse action | adapt | | Destination | `nixos-modules/` (Provider/Guest resource emitters); crate Nix build | | Detailed design | Nix `spec.config` shape; `controllerExecutionRef`/`networkRef` eval-time assertions; no Volume refs for data disks; `systemArtifactId=null` enforcement; the single controller sealed recovery Volume is an ordinary Volume resource created by core ProviderDeployment (not in Zone bundle; not operator-authored); the bootstrap-svc declares no state Volume; guest-local placement — reconciled by the Guest-local volume-local instance and expressed by `source.executionRef` = config gateway Guest; host MUST NOT hold ARM binding, admission, PSK, or operation state; ARM operation/idempotency records live in the core Operation ledger and non-secret observed cloud phase in `Guest.status` (D087); no virtiofs or host-to-guest attachment; manifest freezes guest-local with no fallback; controller does not create, own, or list Volume in exported ResourceTypes; `Provider/volume-local` is the sole Volume reconciler; controller consumes required view dirfd only; **the recovery Volume is `kind: state`, `persistenceClass: persistent`, `storageNeed: secret`, sealed via `sealingCredentialRef`, with nonzero `quotaBytes`, `quota.maxBytes`, `quota.maxInodes`, and `source.settings.sourcePolicyId`; `persistenceClass: ephemeral` and zero quotas are rejected**; it survives component/Provider restart and participates in upgrade/destroy/reset; full canonical Volume spec including `stateSchema`, `source`, `layout` with a Nix-preprovisioned `User/` principal (not ComponentPrincipal), `views`, `identityMarker`, `snapshotPolicy: null`, `retentionPolicy: null`; `sensitivityClass: private` and `volume-domain-mismatch` isolation enforced; canonical `SandboxSpec` fields with `namespaceClasses`/`capabilityClasses`/`seccompClass`/`noNewPrivileges`/`startRoot`/`environmentClass`/`readOnlyRoot`; `BudgetSpec` with SI suffix; `restartPolicy` class/backoffBase/backoffMax; Endpoint ResourceType templates with name/transport/purpose | | Integration | Nix emitters produce Provider, Guest, Volume, and Endpoint resource specs consumed by ProviderDeployment, `Provider/volume-local`, the Process Provider, and the Azure VM controller. | @@ -1453,9 +1453,9 @@ d2b.zones.dev.resources.corp-vm = { | --- | --- | | Dependency/owner | ADR046-azure-vm-003; ADR-046-telemetry-audit-and-support | | Current source | `d2bd/src/metrics.rs` (production-reachable) | -| Reuse action | Adapt audit shape; replace Prometheus with d2b-telemetry emitter | +| Reuse action | adapt | | Destination | `src/{telemetry.rs,audit.rs}` | -| Detailed design | Closed metric labels; OTEL span attributes; audit durability classes; `azure-vm-deleted` appended post-commit; no ARM URI, ARM resource ID, or cloud endpoint in any telemetry surface | +| Detailed design | Closed metric labels; OTEL span attributes; audit durability classes; `azure-vm-deleted` appended post-commit; no ARM URI, ARM resource ID, or cloud endpoint in any telemetry surface Primary reuse disposition: `adapt`. Preserved source-plan detail: Adapt audit shape; replace Prometheus with d2b-telemetry emitter. | | Integration | Controller/error paths call telemetry and audit emitters after status commits; d2b-telemetry consumes the metrics/spans and policy_observability enforces redaction. | | Data migration | No metrics/audit data migration; new OTEL/audit surfaces start at v3 cutover and the old Prometheus registry is retired. | | Validation | `tests/error_redaction.rs`; `d2b-contract-tests/tests/policy_observability.rs` updated | @@ -1467,9 +1467,9 @@ d2b.zones.dev.resources.corp-vm = { | --- | --- | | Dependency/owner | All ADR046-azure-vm-* | | Current source | No existing Azure VM tests at baseline; fake/hermetic patterns from main `a1cc0b2d` | -| Reuse action | Copy/adapt fake toolkit; write new tests | +| Reuse action | adapt | | Destination | `tests/`; `integration/` | -| Detailed design | See §Test requirements | +| Detailed design | See §Test requirements Primary reuse disposition: `adapt`. Preserved source-plan detail: Copy/adapt fake toolkit; write new tests. | | Integration | Provider crate tests, fake toolkit, and integration harness run under cargo/Layer-1 and validate all ADR046-azure-vm-* outputs together. | | Data migration | None — test-only work; no runtime state. Old mock tests are removed only after parity. | | Validation | All tests pass | diff --git a/docs/specs/providers/ADR-046-provider-runtime-cloud-hypervisor.md b/docs/specs/providers/ADR-046-provider-runtime-cloud-hypervisor.md index 49eba9f1d..ecd0aac4d 100644 --- a/docs/specs/providers/ADR-046-provider-runtime-cloud-hypervisor.md +++ b/docs/specs/providers/ADR-046-provider-runtime-cloud-hypervisor.md @@ -1652,9 +1652,9 @@ per-test budget. | --- | --- | | Dependency/owner | Provider toolkit / system-minijail; W1 spike owner | | Current source | `d2b-host/src/runtime_provider.rs`; `d2b-host/src/ch_argv.rs`; `d2bd/src/supervisor/dag.rs` | -| Reuse action | Extract and adapt | +| Reuse action | adapt | | Destination | `packages/d2b-provider-runtime-cloud-hypervisor/src/controller.rs` | -| Detailed design | End-to-end: single Guest reconcile → synchronous dependency-readiness check via ResourceClient → VMM Process creation → guest-control health check in observe handler → status write. Uses fake bus/store/supervisor stubs from toolkit. Proves fast-path latency gates (≤5 ms hint, ≤20 ms VMM Process creation when all deps ready). No EphemeralProcess resources at any step. | +| Detailed design | End-to-end: single Guest reconcile → synchronous dependency-readiness check via ResourceClient → VMM Process creation → guest-control health check in observe handler → status write. Uses fake bus/store/supervisor stubs from toolkit. Proves fast-path latency gates (≤5 ms hint, ≤20 ms VMM Process creation when all deps ready). No EphemeralProcess resources at any step. Primary reuse disposition: `adapt`. Preserved source-plan detail: Extract and adapt. | | Integration | Zone ResourceClient + system-minijail Process Provider + fake broker effect | | Data migration | None (spike) | | Validation | Unit: reconcile state machine, fast-path latency, adoption/ambiguity, finalize ordering. Integration: end-to-end VMM boot with real KVM and guest-control session (requires `make test-host-integration`) | @@ -1666,9 +1666,9 @@ per-test budget. | --- | --- | | Dependency/owner | ADR046-ch-001; Volume and Device foundation | | Current source | `d2b-core/src/processes.rs`; `nixos-modules/processes-json.nix`; `d2b-priv-broker/src/ops/swtpm_dir.rs`; `d2b-host/src/swtpm_argv.rs` | -| Reuse action | EXTRACT and REPLACE | +| Reuse action | replace | | Destination | `packages/d2b-provider-runtime-cloud-hypervisor/src/bootstrap_graph.rs` | -| Detailed design | Single owned VMM Process resource; synchronous ResourceClient dependency check (Device/kvm + all declared Devices, Networks, virtiofs Volumes); immediate Process creation when all deps ready; no EphemeralProcess resources; conditional net-VM Guest creation; per-dependency readiness tracking in reconcile loop | +| Detailed design | Single owned VMM Process resource; synchronous ResourceClient dependency check (Device/kvm + all declared Devices, Networks, virtiofs Volumes); immediate Process creation when all deps ready; no EphemeralProcess resources; conditional net-VM Guest creation; per-dependency readiness tracking in reconcile loop Primary reuse disposition: `replace`. Preserved source-plan detail: EXTRACT and REPLACE. | | Integration | Depends on `Provider/volume-virtiofs`, `Provider/device-tpm`, `Provider/device-kvm`, `Provider/network-local` ResourceType readiness | | Data migration | v3 reset; no v2 process graph migration | | Validation | Golden VMM Process spec vectors; dependency-ordering tests; parallel Guest tests (8 concurrent); net-VM creation tests; Device/kvm explicit-ref enforcement | @@ -1680,9 +1680,9 @@ per-test budget. | --- | --- | | Dependency/owner | ADR046-ch-002; artifact catalog foundation | | Current source | `d2b-host/src/ch_argv.rs::ChArgvInput`, `generate_ch_argv`; `tests/golden/runner-shape/cloud-hypervisor-argv-*.txt` | -| Reuse action | COPY/ADAPT | +| Reuse action | adapt | | Destination | `packages/d2b-provider-runtime-cloud-hypervisor/src/vmm_argv.rs`; `tests/vmm_argv_golden_test.rs` | -| Detailed design | `VmmArgvInput` derived from validated `GuestSpec.spec.provider.settings`; kernel/initrd/rootfs paths resolved privately from artifact catalog at dispatch time; no path in spec/status; golden tests for headless/q35/microvm/gpu/video/macvtap variants | +| Detailed design | `VmmArgvInput` derived from validated `GuestSpec.spec.provider.settings`; kernel/initrd/rootfs paths resolved privately from artifact catalog at dispatch time; no path in spec/status; golden tests for headless/q35/microvm/gpu/video/macvtap variants Primary reuse disposition: `adapt`. Preserved source-plan detail: COPY/ADAPT. | | Integration | ProviderSupervisor LaunchTicket resolution | | Data migration | None — full d2b 3.0 reset; no prior state to migrate | | Validation | Golden argv vectors matching `cloud-hypervisor-argv-*.txt` shapes with v3 adaptations; redaction test (no store path in Debug output) | @@ -1694,9 +1694,9 @@ per-test budget. | --- | --- | | Dependency/owner | ADR046-ch-002; nix-configuration foundation (`ADR046-identities-002`) | | Current source | `nixos-modules/options-realms-workloads.nix`; `nixos-modules/options-vms.nix`; `nixos-modules/processes-json.nix`; `nixos-modules/store.nix` | -| Reuse action | ADAPT and REPLACE | +| Reuse action | adapt | | Destination | `packages/d2b-provider-runtime-cloud-hypervisor/nix/` (Nix emitter); `nixos-modules/` option extension for `runtime-cloud-hypervisor` Guest schema | -| Detailed design | `d2b.zones..resources.` with `type = "Guest"` and `spec.provider.settings` validated against signed Provider schema; `spec.systemArtifactId` top-level field; artifact catalog `type = "nixos-system"` enforced by rule 17; Guest-control `Endpoint` resource emitted without raw locator; `make test-drift` gate for schema/Nix drift | +| Detailed design | `d2b.zones..resources.` with `type = "Guest"` and `spec.provider.settings` validated against signed Provider schema; `spec.systemArtifactId` top-level field; artifact catalog `type = "nixos-system"` enforced by rule 17; Guest-control `Endpoint` resource emitted without raw locator; `make test-drift` gate for schema/Nix drift Primary reuse disposition: `adapt`. Preserved source-plan detail: ADAPT and REPLACE. | | Integration | Zone resource bundle emission; private artifact catalog; `xtask gen-resource-nix-options` for auto-generated Nix option types | | Data migration | `d2b.vms.` → `d2b.zones..resources.` documented in migration guide | | Validation | nix-unit eval tests: rule CH-1 through CH-4 + rules 1–17; golden resource bundle JSON (no store path); type-mismatch eval errors; raw locator rejection; `spec.systemArtifactId` at top-level in JSON (not in `spec.provider.settings`) | @@ -1708,7 +1708,7 @@ per-test budget. | --- | --- | | Dependency/owner | ADR046-ch-001; ComponentSession/d2b-bus (`ADR046-session-001`) | | Current source | `packages/d2bd/src/provider_shutdown.rs::GracefulVmShutdown`; `packages/d2b-host/src/runtime_provider.rs::RuntimeProvider::plan_guest_update` | -| Reuse action | ADAPT | +| Reuse action | adapt | | Destination | `packages/d2b-provider-runtime-cloud-hypervisor/src/health.rs`; `src/adoption.rs` | | Detailed design | Authenticated KK ComponentSession health check over vsock; adoption verification (pid/cgroup/executable/generation) within `adoptionWindow`; ambiguity → Unknown/Degraded, never broad kill; graceful shutdown via guest-control session before SIGTERM | | Integration | ComponentSession enrolled KK; guest bootstrap credential from `d2b-gctl` virtiofs share; `GuestReachable` condition write | @@ -1722,7 +1722,7 @@ per-test budget. | --- | --- | | Dependency/owner | ADR046-ch-001; telemetry foundation (`ADR046-telem-001`) | | Current source | `packages/d2bd/src/metrics.rs` (`d2b_daemon_vm_*`); `packages/d2b-contract-tests/tests/policy_observability.rs` | -| Reuse action | REPLACE | +| Reuse action | replace | | Destination | `packages/d2b-provider-runtime-cloud-hypervisor/src/metrics.rs`; `src/audit.rs` | | Detailed design | `d2b_runtime_ch_*` metrics from §18.3; bounded durable audit records from §17.3; no `vm=` metric label; no path/argv/socket in any field; closed OTEL attribute allowlist extended per §18.4 | | Integration | Zone lightweight bounded emitter; `Provider/observability-otel` forwarding | @@ -1736,17 +1736,13 @@ per-test budget. | --- | --- | | Dependency/owner | ADR046-ch-001; `ADR046-pstate-001` (common status types) | | Current source | `packages/d2b-core/src/storage.rs` (`StoragePathSpec`, `SensitivityClass`) — to be retired | -| Reuse action | REPLACE (storage.rs) | +| Reuse action | replace | | Destination | `packages/d2b-provider-runtime-cloud-hypervisor/src/state.rs`; `packages/d2b-provider-runtime-cloud-hypervisor/tests/state_status_test.rs` | -| Detailed design | `state.rs` owns the controller's bounded non-secret operational-state projection into the owning resource's `status` subresource (reconcile stage, per-Guest launch/adoption observations, bounded counters, closed-enum error detail) — the controller declares no Provider state Volume and mounts no `/state`; on restart it re-derives observed state from the Zone resource store, the core Operation ledger, and external observation (running VMM/virtiofsd re-adopted from cgroup leaves + fresh pidfds), treating `status` as observation, never authority (D087); status writes occur only on material change and stay within the status bounds | +| Detailed design | `state.rs` owns the controller's bounded non-secret operational-state projection into the owning resource's `status` subresource (reconcile stage, per-Guest launch/adoption observations, bounded counters, closed-enum error detail) — the controller declares no Provider state Volume and mounts no `/state`; on restart it re-derives observed state from the Zone resource store, the core Operation ledger, and external observation (running VMM/virtiofsd re-adopted from cgroup leaves + fresh pidfds), treating `status` as observation, never authority (D087); status writes occur only on material change and stay within the status bounds. The superseded state-Volume integration, migration, validation, and removal rows are rejected: this Provider has no state Volume, state mount, or `StateEnvelope` startup path. Primary reuse disposition: `replace`. Preserved source-plan detail: REPLACE (storage.rs). | | Integration | The controller reads Volume/Device/Network dependency status through its ComponentSession/ResourceClient and writes its own bounded `status`; no Provider state Volume is provisioned or mounted | | Data migration | v3 reset; no v2 state storage migration | | Validation | `state_status_test.rs` (hermetic): status projection round-trip and bound enforcement; restart re-derivation from store/ledger/external observation without a state Volume; no secret/path/argv/PID in status | | Removal proof | `d2b-core/src/storage.rs` `StoragePathSpec` / `SensitivityClass` retired only after all Provider state consumers migrate to v3 status/optional-Volume helpers | -| Integration | Volume is provisioned and mounted by ProviderDeployment before controller Process launch (`required: true` mount); controller observes Volume status only through its ComponentSession, never through direct ResourceClient Volume verbs | -| Data migration | v3 reset; no v2 state storage migration | -| Validation | `state_volume_test.rs` (hermetic): StateEnvelope round-trip, view helper correctness, startup-check behavior when Volume phase is not current | -| Removal proof | `d2b-core/src/storage.rs` `StoragePathSpec` / `SensitivityClass` retired only after all Provider state consumers migrate to v3 Volume state helpers | Per `ADR-046-provider-model-and-packaging` and `ADR-046-nix-configuration`, the workspace policy gate rejects the crate unless all four paths exist: diff --git a/docs/specs/providers/ADR-046-provider-runtime-qemu-media.md b/docs/specs/providers/ADR-046-provider-runtime-qemu-media.md index 73a6456ed..acf4b5dd1 100644 --- a/docs/specs/providers/ADR-046-provider-runtime-qemu-media.md +++ b/docs/specs/providers/ADR-046-provider-runtime-qemu-media.md @@ -1527,13 +1527,13 @@ destination in `packages/d2b-provider-runtime-qemu-media/`. --- -### WI-001 Crate scaffold and layout gate +### ADR046-qemu-media-001 Crate scaffold and layout gate | Field | Value | | --- | --- | | Dependency/owner | P0; blocks all other runtime-qemu-media work items; owner: `runtime-qemu-media` Provider crate | | Current source | None — net-new v3 work; no pre-ADR45 baseline equivalent | -| Reuse action | net-new | +| Reuse action | create | | Destination | packages/d2b-provider-runtime-qemu-media/{src/lib.rs,tests/provider_layout.rs,integration/mod.rs,README.md} | | Detailed design | Crate scaffold and layout gate: create the crate with the four required paths, commit a README.md stub meeting §1 requirements, and wire the workspace policy gate so the crate cannot land without `src/`, `tests/`, `integration/`, and `README.md`. | | Integration | Workspace/Cargo policy consumes the new crate layout; later Guest schema, controller, QMP, Nix, and integration work items build inside this crate. | @@ -1543,15 +1543,15 @@ destination in `packages/d2b-provider-runtime-qemu-media/`. --- -### WI-002 Guest ResourceType schema and serde +### ADR046-qemu-media-002 Guest ResourceType schema and serde | Field | Value | | --- | --- | -| Dependency/owner | P0; depends on WI-001; owner: runtime-qemu-media type/schema implementation | +| Dependency/owner | P0; depends on ADR046-qemu-media-001; owner: runtime-qemu-media type/schema implementation | | Current source | `packages/d2b-core/src/host.rs` — `HostQemuMedia`, `QemuMediaSourceIntent` field names/types only; raw path/credential fields are discarded | -| Reuse action | adapt selected baseline field concepts; discard raw paths, argv, and credential-carrying fields | +| Reuse action | adapt | | Destination | packages/d2b-provider-runtime-qemu-media/src/types/guest.rs | -| Detailed design | Guest ResourceType schema and serde: define `GuestSpec`, `GuestStatus`, and `GuestProviderSpecSettings` with serde and `schemars` JSON Schema. Fields must match §4, §5, and §16 exactly. Enforce `bootMediaRef` as a `Volume/` ResourceRef, `removableVolumeRefs` max 4 entries, `providerPhase` max 64 chars with the closed value set, and no argv/path/credential bytes in any serialized type. | +| Detailed design | Guest ResourceType schema and serde: define `GuestSpec`, `GuestStatus`, and `GuestProviderSpecSettings` with serde and `schemars` JSON Schema. Fields must match §4, §5, and §16 exactly. Enforce `bootMediaRef` as a `Volume/` ResourceRef, `removableVolumeRefs` max 4 entries, `providerPhase` max 64 chars with the closed value set, and no argv/path/credential bytes in any serialized type. Primary reuse disposition: `adapt`. Preserved source-plan detail: adapt selected baseline field concepts; discard raw paths, argv, and credential-carrying fields. | | Integration | Nix-rendered Guest resources and ResourceAPI admission use these types; the controller consumes the validated spec and writes matching status; conformance and schema tests consume the generated schema. | | Data migration | Full d2b 3.0 reset; media guests are reauthored as `Guest`/`Volume`/`Device` resources rather than importing v2 host media config | | Validation | `tests/guest_schema_roundtrip.rs`; `tests/guest_provider_settings_bounds.rs` | @@ -1559,15 +1559,15 @@ destination in `packages/d2b-provider-runtime-qemu-media/`. --- -### WI-003 Provider config schema and projection +### ADR046-qemu-media-003 Provider config schema and projection | Field | Value | | --- | --- | -| Dependency/owner | P0; depends on WI-001; owner: runtime-qemu-media Provider config/schema implementation | +| Dependency/owner | P0; depends on ADR046-qemu-media-001; owner: runtime-qemu-media Provider config/schema implementation | | Current source | `packages/d2b-core/src/runtime.rs` — timeout/quota concepts only | -| Reuse action | adapt bounded timeout/quota concepts into v3 Provider config; project only to the controller component | +| Reuse action | adapt | | Destination | packages/d2b-provider-runtime-qemu-media/src/config.rs | -| Detailed design | Provider config schema and projection: define `ProviderConfig`, derive JSON Schema, require `controllerExecutionRef`, validate bounds, and project config only to the controller component. Worker processes receive no root config, no ResourceAPI authority, and no d2b-bus authority. | +| Detailed design | Provider config schema and projection: define `ProviderConfig`, derive JSON Schema, require `controllerExecutionRef`, validate bounds, and project config only to the controller component. Worker processes receive no root config, no ResourceAPI authority, and no d2b-bus authority. Primary reuse disposition: `adapt`. Preserved source-plan detail: adapt bounded timeout/quota concepts into v3 Provider config; project only to the controller component. | | Integration | Provider ResourceSpec admission validates this schema; ProviderDeployment injects the projected config into the controller; controller uses the provider refs and quotas when reconciling Guest, Volume, Network, Device, Endpoint, and Process resources. | | Data migration | Full d2b 3.0 reset; no v2 state/config import | | Validation | `tests/config_schema_projection.rs` | @@ -1575,13 +1575,13 @@ destination in `packages/d2b-provider-runtime-qemu-media/`. --- -### WI-003B Controller status-first operational state (no state Volume) +### ADR046-qemu-media-004 Controller status-first operational state (no state Volume) | Field | Value | | --- | --- | -| Dependency/owner | P0; depends on WI-001 and WI-003; owner: runtime-qemu-media controller descriptor/state implementation | +| Dependency/owner | P0; depends on ADR046-qemu-media-001 and ADR046-qemu-media-003; owner: runtime-qemu-media controller descriptor/state implementation | | Current source | None — net-new v3 work; no pre-ADR45 baseline equivalent | -| Reuse action | net-new | +| Reuse action | create | | Destination | packages/d2b-provider-runtime-qemu-media/src/{descriptor.rs,state.rs}; no Volume management code for Provider state | | Detailed design | Controller status-first operational state (no state Volume): controller component descriptor declares an empty `stateNamespaces` list; ProviderDeployment creates no controller state Volume; controller writes reconcile stage, per-Guest launch/adoption observations, bounded counters, and closed-enum error detail to `status` on material change without secrets, paths, argv, PIDs, or unit names; restart re-derives observed state from the Zone resource store, core Operation ledger, and independent external observation with fresh pidfds. Worker Processes and the controller receive no state-Volume mount. | | Integration | ProviderDeployment reads the descriptor; the controller projects bounded observations to Guest status and the Operation ledger; restart/adoption logic consumes resource-store, ledger, and external runner observations rather than private state storage. | @@ -1591,15 +1591,15 @@ destination in `packages/d2b-provider-runtime-qemu-media/`. --- -### WI-004 Runtime tmpfs Volume resource +### ADR046-qemu-media-005 Runtime tmpfs Volume resource | Field | Value | | --- | --- | -| Dependency/owner | P1; depends on WI-001 and WI-003; owner: runtime-qemu-media controller Volume reconciliation | +| Dependency/owner | P1; depends on ADR046-qemu-media-001 and ADR046-qemu-media-003; owner: runtime-qemu-media controller Volume reconciliation | | Current source | `packages/d2b-host/src/qemu_media_argv.rs` — `run_dir` and socket naming pattern only; raw path construction is discarded | -| Reuse action | adapt naming intent into controller-created Volume resources; replace raw runtime directory paths with `Volume` specs | +| Reuse action | adapt | | Destination | packages/d2b-provider-runtime-qemu-media/src/controller/volume.rs | -| Detailed design | Runtime tmpfs Volume resource: controller creates the per-Guest runtime tmpfs Volume specified in §6.1. The emitted spec must exactly match the canonical YAML, including all layout entries, views, quota, and `cleanupPolicy: vm-stop-with-proof`. | +| Detailed design | Runtime tmpfs Volume resource: controller creates the per-Guest runtime tmpfs Volume specified in §6.1. The emitted spec must exactly match the canonical YAML, including all layout entries, views, quota, and `cleanupPolicy: vm-stop-with-proof`. Primary reuse disposition: `adapt`. Preserved source-plan detail: adapt naming intent into controller-created Volume resources; replace raw runtime directory paths with `Volume` specs. | | Integration | Guest reconcile creates/updates this Volume through the ResourceAPI; `volume-local` materializes the tmpfs and returns attachments to the Process launch flow; finalize proves cleanup before Guest finalization. | | Data migration | Full d2b 3.0 reset; runtime tmpfs state is ephemeral and not imported from v2 run directories | | Validation | `tests/runtime_volume_spec.rs`; `tests/volume_cleanup_policy.rs` | @@ -1607,15 +1607,15 @@ destination in `packages/d2b-provider-runtime-qemu-media/`. --- -### WI-005 Media Volume watch and virtio-blk attachment validation +### ADR046-qemu-media-006 Media Volume watch and virtio-blk attachment validation | Field | Value | | --- | --- | -| Dependency/owner | P1; depends on WI-002 and WI-004; owner: runtime-qemu-media media dependency controller | +| Dependency/owner | P1; depends on ADR046-qemu-media-002 and ADR046-qemu-media-005; owner: runtime-qemu-media media dependency controller | | Current source | `packages/d2b-core/src/host.rs` `QemuMediaSourceKind` — media kind enumeration only | -| Reuse action | adapt media kind concepts to Volume source-kind assertions and ResourceRef watches | +| Reuse action | adapt | | Destination | packages/d2b-provider-runtime-qemu-media/src/controller/media_watch.rs | -| Detailed design | Media Volume watch and virtio-blk attachment validation: controller watches `bootMediaRef` and `removableVolumeRefs` Volumes for `Ready` status and validates that each has a `virtio-blk` attachment for the owning Guest. It performs no path inspection. | +| Detailed design | Media Volume watch and virtio-blk attachment validation: controller watches `bootMediaRef` and `removableVolumeRefs` Volumes for `Ready` status and validates that each has a `virtio-blk` attachment for the owning Guest. It performs no path inspection. Primary reuse disposition: `adapt`. Preserved source-plan detail: adapt media kind concepts to Volume source-kind assertions and ResourceRef watches. | | Integration | Guest reconcile gates Process launch on watched Volume readiness; Volume attachment status feeds LaunchTicket media fd assembly and Guest conditions. | | Data migration | Full d2b 3.0 reset; operator-authored media is declared as Volume resources rather than imported from raw qemu-media source paths | | Validation | `tests/media_volume_watch.rs`; `tests/media_attachment_validation.rs` | @@ -1623,13 +1623,13 @@ destination in `packages/d2b-provider-runtime-qemu-media/`. --- -### WI-006 KVM Device watch +### ADR046-qemu-media-007 KVM Device watch | Field | Value | | --- | --- | -| Dependency/owner | P1; depends on WI-002; owner: runtime-qemu-media Device dependency controller | +| Dependency/owner | P1; depends on ADR046-qemu-media-002; owner: runtime-qemu-media Device dependency controller | | Current source | None — net-new v3 work; no pre-ADR45 baseline equivalent | -| Reuse action | net-new | +| Reuse action | create | | Destination | packages/d2b-provider-runtime-qemu-media/src/controller/device_watch.rs | | Detailed design | KVM Device watch: controller watches `Device/host-kvm` from `spec.deviceAttachments` for `Ready` status and gates runner launch on it, propagating Pending/Ready/Failed transitions to Guest conditions. | | Integration | Device resource status drives Guest reconcile dependency gating; a Ready KVM Device contributes the sealed kvm fd slot to the LaunchTicket through the Process provider chain. | @@ -1639,15 +1639,15 @@ destination in `packages/d2b-provider-runtime-qemu-media/`. --- -### WI-007 WaylandSession resource management +### ADR046-qemu-media-008 WaylandSession resource management | Field | Value | | --- | --- | -| Dependency/owner | P1; depends on WI-002 and the `display-wayland` Provider dossier; owner: runtime-qemu-media display integration | +| Dependency/owner | P1; depends on ADR046-qemu-media-002 and the `display-wayland` Provider dossier; owner: runtime-qemu-media display integration | | Current source | None — net-new v3 work; no pre-ADR45 baseline equivalent | -| Reuse action | net-new against the display-wayland Resource contract | +| Reuse action | create | | Destination | packages/d2b-provider-runtime-qemu-media/src/controller/display.rs | -| Detailed design | WaylandSession resource management: when `spec.provider.settings.displayWindow = true`, controller creates, updates, deletes, and watches a `display-wayland.d2bus.org.WaylandSession` resource using the exact ResourceSpec from the display-wayland dossier. It reads the EndpointRef attachment from status using only display-wayland-defined field names. | +| Detailed design | WaylandSession resource management: when `spec.provider.settings.displayWindow = true`, controller creates, updates, deletes, and watches a `display-wayland.d2bus.org.WaylandSession` resource using the exact ResourceSpec from the display-wayland dossier. It reads the EndpointRef attachment from status using only display-wayland-defined field names. Primary reuse disposition: `create`. Preserved source-plan detail: net-new against the display-wayland Resource contract. | | Integration | Guest reconcile produces WaylandSession resources; display-wayland publishes Endpoint attachments; LaunchTicket assembly consumes the display fd only when the session is Ready; finalize deletes the session. | | Data migration | Full d2b 3.0 reset; no v2 state/config import | | Validation | `tests/wayland_session_create.rs`; `tests/wayland_session_attachment_read.rs`; `tests/wayland_session_missing_provider.rs` | @@ -1655,15 +1655,15 @@ destination in `packages/d2b-provider-runtime-qemu-media/`. --- -### WI-008 Process spec builder and LaunchTicket assembly +### ADR046-qemu-media-009 Process spec builder and LaunchTicket assembly | Field | Value | | --- | --- | -| Dependency/owner | P1; depends on WI-002, WI-004, WI-005, WI-006, WI-007, and WI-011; owner: runtime-qemu-media Process launch builder | +| Dependency/owner | P1; depends on ADR046-qemu-media-002, ADR046-qemu-media-005, ADR046-qemu-media-006, ADR046-qemu-media-007, ADR046-qemu-media-008, and ADR046-qemu-media-012; owner: runtime-qemu-media Process launch builder | | Current source | `packages/d2b-host/src/qemu_media_argv.rs` fd-index arg shape; `packages/d2b-core/src/processes.rs` `ProcessRole::QemuMediaRunner` sandbox/budget baseline | -| Reuse action | adapt fd-slot and sandbox/budget concepts to canonical Process resources and sealed LaunchTickets; do not copy raw argv strings or path construction | +| Reuse action | adapt | | Destination | packages/d2b-provider-runtime-qemu-media/src/controller/process_builder.rs | -| Detailed design | Process spec builder and LaunchTicket assembly: build the canonical `qemu-media-runner` Process ResourceSpec from §10.1 and assemble the LaunchTicket with sealed fd slots for kvm, tap, media, and optional display fds. No raw path, argv, executable path, or principal appears in any public field. | +| Detailed design | Process spec builder and LaunchTicket assembly: build the canonical `qemu-media-runner` Process ResourceSpec from §10.1 and assemble the LaunchTicket with sealed fd slots for kvm, tap, media, and optional display fds. No raw path, argv, executable path, or principal appears in any public field. Primary reuse disposition: `adapt`. Preserved source-plan detail: adapt fd-slot and sandbox/budget concepts to canonical Process resources and sealed LaunchTickets; do not copy raw argv strings or path construction. | | Integration | Controller emits Process resources; system-minijail/Process Provider consumes the spec and LaunchTicket; QEMU runner receives only sealed fds; Endpoint resources represent QMP/serial connections. | | Data migration | Full d2b 3.0 reset; existing QEMU runner process state is not imported and launch state is rebuilt from resources | | Validation | `tests/process_spec_golden.rs`; `tests/launch_ticket_fd_slots.rs`; `tests/no_raw_argv_in_spec.rs` | @@ -1671,15 +1671,15 @@ destination in `packages/d2b-provider-runtime-qemu-media/`. --- -### WI-009 QMP endpoint attachment handling +### ADR046-qemu-media-010 QMP endpoint attachment handling | Field | Value | | --- | --- | -| Dependency/owner | P1; depends on WI-008; owner: runtime-qemu-media QMP client implementation | +| Dependency/owner | P1; depends on ADR046-qemu-media-009; owner: runtime-qemu-media QMP client implementation | | Current source | `packages/d2b-host/src/media.rs` QMP command set; `packages/d2b-contracts/src/broker_wire.rs` `QemuMedia*` command payload shapes only | -| Reuse action | adapt QMP command payloads to internal DTOs; discard broker wire ops and all socket path/fd-open code | +| Reuse action | adapt | | Destination | packages/d2b-provider-runtime-qemu-media/src/qmp/ | -| Detailed design | QMP endpoint attachment handling: consume `qmp` and `serial` Endpoint connection attachments delivered by the ProviderSupervisor ComponentSession channel; implement QMP capability negotiation, command dispatch, and health check using only the delivered fd, never direct socket path access. | +| Detailed design | QMP endpoint attachment handling: consume `qmp` and `serial` Endpoint connection attachments delivered by the ProviderSupervisor ComponentSession channel; implement QMP capability negotiation, command dispatch, and health check using only the delivered fd, never direct socket path access. Primary reuse disposition: `adapt`. Preserved source-plan detail: adapt QMP command payloads to internal DTOs; discard broker wire ops and all socket path/fd-open code. | | Integration | Process Provider publishes Endpoint attachments; the controller QMP client consumes those fds through ComponentSession; Guest status and health checks reflect QMP outcomes. | | Data migration | Full d2b 3.0 reset; no v2 QMP socket path/session state is imported | | Validation | `tests/qmp_capability_negotiation.rs`; `tests/qmp_command_dispatch.rs`; `tests/qmp_greeting_timeout.rs`; `tests/qmp_health_check.rs` | @@ -1687,15 +1687,15 @@ destination in `packages/d2b-provider-runtime-qemu-media/`. --- -### WI-010 Hotplug attach/detach protocol +### ADR046-qemu-media-011 Hotplug attach/detach protocol | Field | Value | | --- | --- | -| Dependency/owner | P2; depends on WI-005 and WI-009; owner: runtime-qemu-media hotplug controller | +| Dependency/owner | P2; depends on ADR046-qemu-media-006 and ADR046-qemu-media-010; owner: runtime-qemu-media hotplug controller | | Current source | `packages/d2b-contracts/src/broker_wire.rs` `QemuMediaAttach` and `QemuMediaDetach` command bodies only | -| Reuse action | adapt QMP hotplug command bodies; delete broker op wiring | +| Reuse action | adapt | | Destination | packages/d2b-provider-runtime-qemu-media/src/controller/hotplug.rs | -| Detailed design | Hotplug attach/detach protocol: on `removableVolumeRefs` update, request a Volume fd from the `volume-local` ComponentSession service and issue `blockdev-add`/`device_add` QMP commands; reverse the sequence for detach; QMP failures set Degraded with `hotplug-media-failed`. | +| Detailed design | Hotplug attach/detach protocol: on `removableVolumeRefs` update, request a Volume fd from the `volume-local` ComponentSession service and issue `blockdev-add`/`device_add` QMP commands; reverse the sequence for detach; QMP failures set Degraded with `hotplug-media-failed`. Primary reuse disposition: `adapt`. Preserved source-plan detail: adapt QMP hotplug command bodies; delete broker op wiring. | | Integration | Guest spec updates trigger controller reconcile; volume-local supplies media fds; QMP client executes attach/detach; Guest status records hotplug outcomes. | | Data migration | Full d2b 3.0 reset; removable media hotplug state is reconciled from Guest spec and Volume status, not imported from broker op history | | Validation | `tests/hotplug_attach_sequence.rs`; `tests/hotplug_detach_sequence.rs`; `tests/hotplug_qmp_failure.rs` | @@ -1703,15 +1703,15 @@ destination in `packages/d2b-provider-runtime-qemu-media/`. --- -### WI-011 Network tap fd acquisition +### ADR046-qemu-media-012 Network tap fd acquisition | Field | Value | | --- | --- | -| Dependency/owner | P1; depends on WI-002 and Provider config `networkProviderRef`; owner: runtime-qemu-media network dependency integration | +| Dependency/owner | P1; depends on ADR046-qemu-media-002 and Provider config `networkProviderRef`; owner: runtime-qemu-media network dependency integration | | Current source | None — net-new v3 work; no pre-ADR45 baseline equivalent | -| Reuse action | net-new against the `network-local` ComponentSession contract | +| Reuse action | create | | Destination | packages/d2b-provider-runtime-qemu-media/src/controller/network.rs | -| Detailed design | Network tap fd acquisition: call the `network-local` ComponentSession service to request a tap fd for a Guest MAC/bridge assignment and include the fd in the LaunchTicket. No bridge name or interface name appears in any public field. | +| Detailed design | Network tap fd acquisition: call the `network-local` ComponentSession service to request a tap fd for a Guest MAC/bridge assignment and include the fd in the LaunchTicket. No bridge name or interface name appears in any public field. Primary reuse disposition: `create`. Preserved source-plan detail: net-new against the `network-local` ComponentSession contract. | | Integration | Guest networkAttachments drive requests to network-local; network-local returns a sealed tap fd; Process LaunchTicket carries the fd to the QEMU runner; Guest conditions report unavailable taps. | | Data migration | Full d2b 3.0 reset; no v2 state/config import | | Validation | `tests/tap_fd_acquisition.rs`; `tests/tap_fd_unavailable.rs` | @@ -1719,15 +1719,15 @@ destination in `packages/d2b-provider-runtime-qemu-media/`. --- -### WI-012 Reconcile loop and finalize +### ADR046-qemu-media-013 Reconcile loop and finalize | Field | Value | | --- | --- | -| Dependency/owner | P1; depends on WI-004 through WI-011; owner: runtime-qemu-media controller | +| Dependency/owner | P1; depends on ADR046-qemu-media-005 through ADR046-qemu-media-012; owner: runtime-qemu-media controller | | Current source | None — net-new v3 work; no pre-ADR45 baseline equivalent | -| Reuse action | net-new reconcile/finalize implementation using the v3 Resource API | +| Reuse action | create | | Destination | packages/d2b-provider-runtime-qemu-media/src/controller/reconcile.rs | -| Detailed design | Reconcile loop and finalize: implement the full async reconcile loop from §11.3 and finalize sequence from §11.4, including dependency gating, providerPhase transitions, condition management, runner exit handling, and WaylandSession cleanup. | +| Detailed design | Reconcile loop and finalize: implement the full async reconcile loop from §11.3 and finalize sequence from §11.4, including dependency gating, providerPhase transitions, condition management, runner exit handling, and WaylandSession cleanup. Primary reuse disposition: `create`. Preserved source-plan detail: net-new reconcile/finalize implementation using the v3 Resource API. | | Integration | Resource watches feed the controller; the controller creates/updates/deletes Volume, WaylandSession, Endpoint, and Process resources; Guest status and finalizers expose lifecycle outcomes to core and CLI. | | Data migration | Full d2b 3.0 reset; lifecycle state is re-derived from Resource specs/status and Operation ledger rather than imported from v2 daemon state | | Validation | `tests/reconcile_dependency_gating.rs`; `tests/reconcile_runner_exit_handling.rs`; `tests/finalize_sequence.rs`; `tests/finalize_wayland_session_cleanup.rs` | @@ -1735,15 +1735,15 @@ destination in `packages/d2b-provider-runtime-qemu-media/`. --- -### WI-013 Status, conditions, and error reporting +### ADR046-qemu-media-014 Status, conditions, and error reporting | Field | Value | | --- | --- | -| Dependency/owner | P1; depends on WI-012; owner: runtime-qemu-media status/error implementation | +| Dependency/owner | P1; depends on ADR046-qemu-media-013; owner: runtime-qemu-media status/error implementation | | Current source | None — net-new v3 work; no pre-ADR45 baseline equivalent | -| Reuse action | net-new status/error projection for the v3 Guest ResourceType | +| Reuse action | create | | Destination | packages/d2b-provider-runtime-qemu-media/src/controller/status.rs | -| Detailed design | Status, conditions, and error reporting: implement all phase transitions from §16.1, providerPhase values from §16.2, condition types from §16.3, error codes from §16.4, and bounds enforcement on `providerPhase`. | +| Detailed design | Status, conditions, and error reporting: implement all phase transitions from §16.1, providerPhase values from §16.2, condition types from §16.3, error codes from §16.4, and bounds enforcement on `providerPhase`. Primary reuse disposition: `create`. Preserved source-plan detail: net-new status/error projection for the v3 Guest ResourceType. | | Integration | Controller reconcile writes Guest status; ResourceAPI stores bounded status; CLI/support tooling reads status without paths, argv, fds, socket paths, VM names as labels, or secret material. | | Data migration | None — status schema is new v3 observation state; no v2 status import | | Validation | `tests/status_phase_transitions.rs`; `tests/condition_reason_codes.rs` | @@ -1751,15 +1751,15 @@ destination in `packages/d2b-provider-runtime-qemu-media/`. --- -### WI-014 Audit event emission +### ADR046-qemu-media-015 Audit event emission | Field | Value | | --- | --- | -| Dependency/owner | P2; depends on WI-012 and WI-013; owner: runtime-qemu-media audit integration | +| Dependency/owner | P2; depends on ADR046-qemu-media-013 and ADR046-qemu-media-014; owner: runtime-qemu-media audit integration | | Current source | None — net-new v3 work; no pre-ADR45 baseline equivalent | -| Reuse action | net-new audit emission for the Provider events in §17 | +| Reuse action | create | | Destination | packages/d2b-provider-runtime-qemu-media/src/audit.rs | -| Detailed design | Audit event emission: emit all audit events in §17 and verify that no sensitive fields such as paths, argv, fds, or socket paths appear in any payload. | +| Detailed design | Audit event emission: emit all audit events in §17 and verify that no sensitive fields such as paths, argv, fds, or socket paths appear in any payload. Primary reuse disposition: `create`. Preserved source-plan detail: net-new audit emission for the Provider events in §17. | | Integration | Controller lifecycle and QMP/hotplug operations call audit helpers; the audit subsystem records bounded event kinds and outcomes; support tooling consumes redacted payloads. | | Data migration | None — audit-only work; no runtime state import | | Validation | `tests/audit_event_shapes.rs`; `tests/audit_no_sensitive_fields.rs` | @@ -1767,15 +1767,15 @@ destination in `packages/d2b-provider-runtime-qemu-media/`. --- -### WI-015 Metrics and OTEL spans +### ADR046-qemu-media-016 Metrics and OTEL spans | Field | Value | | --- | --- | -| Dependency/owner | P2; depends on WI-012 and WI-013; owner: runtime-qemu-media telemetry integration | +| Dependency/owner | P2; depends on ADR046-qemu-media-013 and ADR046-qemu-media-014; owner: runtime-qemu-media telemetry integration | | Current source | None — net-new v3 work; no pre-ADR45 baseline equivalent | -| Reuse action | net-new telemetry emission for the Provider metrics and spans in §18 | +| Reuse action | create | | Destination | packages/d2b-provider-runtime-qemu-media/src/telemetry.rs | -| Detailed design | Metrics and OTEL spans: implement all metrics from §18 and OTEL trace spans with label cardinality enforcement and no VM name, user identity, path, or other sensitive value in any label or attribute. | +| Detailed design | Metrics and OTEL spans: implement all metrics from §18 and OTEL trace spans with label cardinality enforcement and no VM name, user identity, path, or other sensitive value in any label or attribute. Primary reuse disposition: `create`. Preserved source-plan detail: net-new telemetry emission for the Provider metrics and spans in §18. | | Integration | Controller, QMP, hotplug, and dependency-watch paths call telemetry helpers; OTEL/metrics exporters consume only closed, bounded labels for support dashboards. | | Data migration | None — telemetry-only work; no runtime state import | | Validation | `tests/metrics_label_cardinality.rs`; `tests/otel_span_attributes.rs` | @@ -1783,15 +1783,15 @@ destination in `packages/d2b-provider-runtime-qemu-media/`. --- -### WI-016 Nix module and assertions +### ADR046-qemu-media-017 Nix module and assertions | Field | Value | | --- | --- | -| Dependency/owner | P1; depends on WI-002 and WI-003; owner: Nix resource compiler and runtime-qemu-media options | +| Dependency/owner | P1; depends on ADR046-qemu-media-002 and ADR046-qemu-media-003; owner: Nix resource compiler and runtime-qemu-media options | | Current source | `nixos-modules/components/qemu-media.nix` option names only; `nixos-modules/assertions.nix` assertion framework | -| Reuse action | adapt option names into v3 Guest/Provider spec fields; remove raw path options; extend existing assertion predicates | +| Reuse action | adapt | | Destination | nixos-modules/options-guest-qemu-media.nix; nixos-modules/assertions.nix | -| Detailed design | Nix module and assertions: implement the Guest resource declaration from §19 and eval-time assertions from §19.8. Rewrite qemu-media options as v3 spec fields and reject raw path options. | +| Detailed design | Nix module and assertions: implement the Guest resource declaration from §19 and eval-time assertions from §19.8. Rewrite qemu-media options as v3 spec fields and reject raw path options. Primary reuse disposition: `adapt`. Preserved source-plan detail: adapt option names into v3 Guest/Provider spec fields; remove raw path options; extend existing assertion predicates. | | Integration | Nix authoring emits Provider, Guest, Volume, and Device resource JSON; assertions fail invalid configs before build; emitted resources feed ResourceAPI admission and controller reconcile. | | Data migration | Full d2b 3.0 reset; users reauthor qemu-media configuration as v3 resources and raw path options are not imported | | Validation | `tests/unit/nix/cases/guest-qemu-media-spec.nix`; `tests/assertions-eval.sh` new assertion cases | @@ -1799,15 +1799,15 @@ destination in `packages/d2b-provider-runtime-qemu-media/`. --- -### WI-017 d2b-provider-toolkit conformance +### ADR046-qemu-media-018 d2b-provider-toolkit conformance | Field | Value | | --- | --- | -| Dependency/owner | P2; depends on WI-012 through WI-015; owner: runtime-qemu-media conformance tests | +| Dependency/owner | P2; depends on ADR046-qemu-media-013 through ADR046-qemu-media-016; owner: runtime-qemu-media conformance tests | | Current source | d2b-provider-toolkit conformance kit | -| Reuse action | reuse conformance harness; add runtime-qemu-media Guest ResourceType coverage | +| Reuse action | adapt | | Destination | packages/d2b-provider-runtime-qemu-media/tests/conformance_guest.rs | -| Detailed design | d2b-provider-toolkit conformance: pass the Provider conformance kit for the Guest ResourceType axis, including reconcile/finalize contract, phase machine, condition typing, audit shape, and telemetry cardinality. | +| Detailed design | d2b-provider-toolkit conformance: pass the Provider conformance kit for the Guest ResourceType axis, including reconcile/finalize contract, phase machine, condition typing, audit shape, and telemetry cardinality. Primary reuse disposition: `adapt`. Preserved source-plan detail: reuse conformance harness; add runtime-qemu-media Guest ResourceType coverage. | | Integration | Conformance tests instantiate the Provider against fake ResourceAPI/ComponentSession dependencies and verify the public Provider contract consumed by core CI. | | Data migration | None — test-only work; no runtime state import | | Validation | `make test-rust` (runs conformance suite) | @@ -1815,15 +1815,15 @@ destination in `packages/d2b-provider-runtime-qemu-media/`. --- -### WI-018 Integration tests +### ADR046-qemu-media-019 Integration tests | Field | Value | | --- | --- | -| Dependency/owner | P2; depends on WI-004 through WI-017; owner: runtime-qemu-media integration fixtures | +| Dependency/owner | P2; depends on ADR046-qemu-media-005 through ADR046-qemu-media-018; owner: runtime-qemu-media integration fixtures | | Current source | None — net-new v3 work; no pre-ADR45 baseline equivalent | -| Reuse action | net-new integration fixtures | +| Reuse action | create | | Destination | packages/d2b-provider-runtime-qemu-media/integration/ | -| Detailed design | Integration tests: implement container/fake-Host scenarios for full reconcile from Created to Ready with fake dependencies, finalize sequence, hotplug attach/detach, and restart recovery. | +| Detailed design | Integration tests: implement container/fake-Host scenarios for full reconcile from Created to Ready with fake dependencies, finalize sequence, hotplug attach/detach, and restart recovery. Primary reuse disposition: `create`. Preserved source-plan detail: net-new integration fixtures. | | Integration | Integration fixtures launch the Provider with fake or containerized Host/Guest/Volume/Network/Device dependencies; CI `make test-integration` consumes the fixtures as the cross-process proof lane. | | Data migration | None — test-only work; no runtime state import | | Validation | `make test-integration` | @@ -1954,7 +1954,7 @@ reference to `QemuMediaRunner` in any non-migration source file. | `nixos-modules/components/qemu-media.nix` raw path options | Replaced by `nixos-modules/options-guest-qemu-media.nix` | The migration guide `docs/how-to/migrate-qemu-media-v2-to-v3.md` (authored -in WI-016) explains the option mapping for operator configurations. +in ADR046-qemu-media-017) explains the option mapping for operator configurations. ### 23.4 `docs/adr/0036-qemu-media-runtime.md` supersession @@ -1966,7 +1966,7 @@ check) verifies the superseded field is present. ### 23.5 CHANGELOG entry The CHANGELOG.md `## [Unreleased]` block receives an `Added` entry at the -WI-001 commit and a `Changed` entry noting the removal of `QemuMedia*` broker +ADR046-qemu-media-001 commit and a `Changed` entry noting the removal of `QemuMedia*` broker ops at the removal-gate commit. No process markers appear in the CHANGELOG text. diff --git a/docs/specs/providers/ADR-046-provider-shell-terminal.md b/docs/specs/providers/ADR-046-provider-shell-terminal.md index e2c5b5ea1..d1def521e 100644 --- a/docs/specs/providers/ADR-046-provider-shell-terminal.md +++ b/docs/specs/providers/ADR-046-provider-shell-terminal.md @@ -1413,7 +1413,7 @@ per-test budget. | --- | --- | | Dependency/owner | Resource schemas area; owned by `d2b-provider-shell-terminal` resource modules. | | Current source | None — net-new v3 qualified `ShellPool` and `ShellSession` resource schemas; superseded draft and legacy shell code do not define these canonical resources. | -| Reuse action | net-new | +| Reuse action | create | | Destination | `packages/d2b-provider-shell-terminal/src/resources/{pool,session}.rs` | | Detailed design | Implement `shell-terminal.d2bus.org.ShellPool` and `shell-terminal.d2bus.org.ShellSession` schemas with qualified names, common phases, and typed detail fields. | | Integration | Nix resource compiler, resource API admission, controller reconcile, status writers, and d2b-bus routing all consume the qualified pool/session schemas. Integration path: `packages/d2b-provider-shell-terminal/integration/resource-shape/`. | @@ -1426,9 +1426,9 @@ per-test budget. | --- | --- | | Dependency/owner | Controller binary area; owned by `d2b-provider-shell-terminal` controller and core Operation ledger integration. | | Current source | None — net-new v3 controller; legacy guestd and unsafe-local helper shell paths are not the controller/state authority. | -| Reuse action | net-new controller; preserve status-first ProviderStateSet-empty rule | +| Reuse action | create | | Destination | `packages/d2b-provider-shell-terminal/src/bin/d2b-shell-terminal-controller.rs` | -| Detailed design | Implement `d2b-shell-terminal-controller` with pool/session reconcile loops; assert ProviderStateSet is empty; publish bounded non-secret operational state to resource status and the core Operation ledger; no controller Provider state Volume or `/state` mount exists. | +| Detailed design | Implement `d2b-shell-terminal-controller` with pool/session reconcile loops; assert ProviderStateSet is empty; publish bounded non-secret operational state to resource status and the core Operation ledger; no controller Provider state Volume or `/state` mount exists. Primary reuse disposition: `create`. Preserved source-plan detail: net-new controller; preserve status-first ProviderStateSet-empty rule. | | Integration | Core ProviderDeployment starts the controller Process; controller reconciles ShellPool/ShellSession resources, writes status, registers routes, and records operations without a Provider state Volume. Integration path: `packages/d2b-provider-shell-terminal/integration/controller-restart/`. | | Data migration | Full d2b 3.0 reset; no v2 state/config import. | | Validation | `packages/d2b-provider-shell-terminal/tests/controller_reconcile.rs` | @@ -1440,9 +1440,9 @@ per-test budget. | Dependency/owner | Supervisor binary area; owned by `d2b-provider-shell-terminal` session supervisor runtime. | | Current source | Reuse narrow ring/runtime ideas from `packages/d2b-guestd/src/shell.rs` and adoption-shape ideas from `packages/d2b-unsafe-local-helper/src/runtime.rs`; both legacy authorities are superseded. | | Reuse source | `packages/d2b-guestd/src/shell.rs`; `packages/d2b-unsafe-local-helper/src/runtime.rs`. | -| Reuse action | reuse narrow mechanics only; move PTY authority into per-session supervisor and exclude legacy protocols/identities/state storage | +| Reuse action | adapt | | Destination | `packages/d2b-provider-shell-terminal/src/bin/d2b-shell-session-supervisor.rs` | -| Detailed design | Implement `d2b-shell-session-supervisor` as the sole PTY owner for Host and Guest pools. | +| Detailed design | Implement `d2b-shell-session-supervisor` as the sole PTY owner for Host and Guest pools. Primary reuse disposition: `adapt`. Preserved source-plan detail: reuse narrow mechanics only; move PTY authority into per-session supervisor and exclude legacy protocols/identities/state storage. | | Integration | Controller creates one user-domain supervisor Process per ShellSession; supervisor owns PTY, login shell, ring, attach bookkeeping, and private ComponentSession service. Integration path: `packages/d2b-provider-shell-terminal/integration/supervisor-host-guest/`. | | Data migration | Full d2b 3.0 reset; no v2 shell state import; PTY/ring state is live process memory only. | | Validation | `packages/d2b-provider-shell-terminal/tests/supervisor_runtime.rs` | @@ -1453,9 +1453,9 @@ per-test budget. | --- | --- | | Dependency/owner | Process templates area; owned by Nix compiler plus shell-terminal controller. | | Current source | Superseded draft templates included pool-wide/system-domain or management-worker concepts; canonical v3 templates are defined in this spec. | -| Reuse action | replace incorrect draft templates with canonical controller and user-domain supervisor Process templates | +| Reuse action | replace | | Destination | `packages/d2b-provider-shell-terminal/src/process_templates.rs` | -| Detailed design | Teach the Nix compiler and controller to emit the canonical controller and user-domain supervisor `Process` templates. | +| Detailed design | Teach the Nix compiler and controller to emit the canonical controller and user-domain supervisor `Process` templates. Primary reuse disposition: `replace`. Preserved source-plan detail: replace incorrect draft templates with canonical controller and user-domain supervisor Process templates. | | Integration | Nix compiler emits controller Process/Endpoint resources; controller emits per-session user-domain supervisor Processes; Provider/system-systemd realizes them. Integration path: `packages/d2b-provider-shell-terminal/integration/process-placement/`. | | Data migration | Full d2b 3.0 reset; no v2 state/config import. | | Validation | `packages/d2b-provider-shell-terminal/tests/process_templates.rs` | @@ -1466,7 +1466,7 @@ per-test budget. | --- | --- | | Dependency/owner | OpenSession lifecycle area; owned by controller service implementation. | | Current source | None — net-new v3 `OpenSession` lifecycle; legacy shell protocols do not create ShellSession resources with inherited-field freeze. | -| Reuse action | net-new | +| Reuse action | create | | Destination | `packages/d2b-provider-shell-terminal/src/service/open_session.rs` | | Detailed design | Create sessions from pools, freeze inherited fields, and return `supervisorGeneration` to callers. | | Integration | `shell-terminal.v3.OpenSession` validates pool capacity and policy, creates ShellSession and supervisor Process, registers route data, and returns session/supervisor references to clients. Integration path: `packages/d2b-provider-shell-terminal/integration/open-session/`. | @@ -1480,9 +1480,9 @@ per-test budget. | Dependency/owner | PTY and ring area; owned by per-session supervisor runtime. | | Current source | Ring buffer mechanics may reuse ideas from `packages/d2b-guestd/src/shell.rs`; management workers and sealed output Volumes from prior draft are removed. | | Reuse source | `packages/d2b-guestd/src/shell.rs` bounded ring ideas only. | -| Reuse action | reuse ring semantics; keep bytes in supervisor memory and remove management worker/EphemeralProcess model | +| Reuse action | adapt | | Destination | `packages/d2b-provider-shell-terminal/src/session/{pty,ring}.rs` | -| Detailed design | Implement the in-memory PTY owner model, bounded ring buffer, replay, and eviction counters; do not create any management worker or `EphemeralProcess`. | +| Detailed design | Implement the in-memory PTY owner model, bounded ring buffer, replay, and eviction counters; do not create any management worker or `EphemeralProcess`. Primary reuse disposition: `adapt`. Preserved source-plan detail: reuse ring semantics; keep bytes in supervisor memory and remove management worker/EphemeralProcess model. | | Integration | Supervisor named terminal stream replays bounded ring tail then streams live PTY I/O; status publishes only ring byte counters and attach count. Integration path: `packages/d2b-provider-shell-terminal/integration/ring-overflow/`. | | Data migration | Full d2b 3.0 reset; no terminal byte or ring-state import. | | Validation | `packages/d2b-provider-shell-terminal/tests/ring_buffer.rs` | @@ -1494,9 +1494,9 @@ per-test budget. | Dependency/owner | Adoption and routing area; owned by controller session adoption/routing module. | | Current source | Reuse verification/adoption shape only from `packages/d2b-unsafe-local-helper/src/runtime.rs` `ScopeRuntime` and `PersistedScope`; exclude helper protocol and state storage. | | Reuse source | `packages/d2b-unsafe-local-helper/src/runtime.rs` `ScopeRuntime` and `PersistedScope` adoption pattern. | -| Reuse action | adapt identity-verification shape; do not reuse helper protocol, identities, or state storage assumptions | +| Reuse action | adapt | | Destination | `packages/d2b-provider-shell-terminal/src/session/adopt.rs` | -| Detailed design | Implement restart adoption, InvocationID plus cgroup verification, route registration, and stale-generation invalidation. | +| Detailed design | Implement restart adoption, InvocationID plus cgroup verification, route registration, and stale-generation invalidation. Primary reuse disposition: `adapt`. Preserved source-plan detail: adapt identity-verification shape; do not reuse helper protocol, identities, or state storage assumptions. | | Integration | Controller restart scans supervisor Processes, verifies owner/session/generation identity, re-registers exact d2b-bus routes, and rejects stale or ambiguous handles. Integration path: `packages/d2b-provider-shell-terminal/integration/adopt-after-restart/`. | | Data migration | Full d2b 3.0 reset; no v2 state/config import. | | Validation | `packages/d2b-provider-shell-terminal/tests/adoption.rs` | @@ -1507,9 +1507,9 @@ per-test budget. | --- | --- | | Dependency/owner | Host rules area; owned by shell-terminal Host policy module. | | Current source | Supersedes unsafe-local Host shell path from `packages/d2b-unsafe-local-helper/src/services/shell/` while preserving explicit non-isolation warning semantics. | -| Reuse action | replace unsafe-local helper shell policy with resource-backed Host pool warnings and same-UID checks | +| Reuse action | replace | | Destination | `packages/d2b-provider-shell-terminal/src/host_rules.rs` | -| Detailed design | Emit Host `isolationPosture=none` warnings, same-UID verification, and relay denial for Host user-domain pools. | +| Detailed design | Emit Host `isolationPosture=none` warnings, same-UID verification, and relay denial for Host user-domain pools. Primary reuse disposition: `replace`. Preserved source-plan detail: replace unsafe-local helper shell policy with resource-backed Host pool warnings and same-UID checks. | | Integration | Pool reconcile reads Host posture and User identity, writes warning status/audit, and admission rejects relay-authenticated Host user-domain access. Integration path: `packages/d2b-provider-shell-terminal/integration/host-warning/`. | | Data migration | Full d2b 3.0 reset; no unsafe-local shell state/config import. | | Validation | `packages/d2b-provider-shell-terminal/tests/host_rules.rs` | @@ -1520,9 +1520,9 @@ per-test budget. | --- | --- | | Dependency/owner | Guest rules area; owned by shell-terminal Guest policy module. | | Current source | Supersedes guest persistent-shell runtime in `packages/d2b-guestd/src/shell.rs`; Guest user-domain placement moves to Provider/system-systemd. | -| Reuse action | replace guestd shell authority with Guest resource user-domain admission and supervisor placement | +| Reuse action | replace | | Destination | `packages/d2b-provider-shell-terminal/src/guest_rules.rs` | -| Detailed design | Require Guest `allowedDomains` to include `user`, require `defaultUserRef`, and place supervisors through the Guest user manager. | +| Detailed design | Require Guest `allowedDomains` to include `user`, require `defaultUserRef`, and place supervisors through the Guest user manager. Primary reuse disposition: `replace`. Preserved source-plan detail: replace guestd shell authority with Guest resource user-domain admission and supervisor placement. | | Integration | Pool admission validates Guest capabilities and default user; controller creates user-domain supervisor Processes through the Guest user manager exposed by Provider/system-systemd. Integration path: `packages/d2b-provider-shell-terminal/integration/guest-user-domain/`. | | Data migration | Full d2b 3.0 reset; no guestd shell runtime state/config import. | | Validation | `packages/d2b-provider-shell-terminal/tests/guest_rules.rs` | @@ -1533,9 +1533,9 @@ per-test budget. | --- | --- | | Dependency/owner | RBAC and relay denial area; owned by shell-terminal authorization module. | | Current source | Existing public-wire `ShellOp`/unsafe-local surfaces are superseded; v3 requires ComponentSession service authorization. | -| Reuse action | replace legacy shell operation authorization with Role/shell-admin or Zone-admin service gates | +| Reuse action | replace | | Destination | `packages/d2b-provider-shell-terminal/src/authz.rs` | -| Detailed design | Gate all verbs on `Role/shell-admin` or Zone-admin superset and fail closed for relay-authenticated Host user-domain callers. | +| Detailed design | Gate all verbs on `Role/shell-admin` or Zone-admin superset and fail closed for relay-authenticated Host user-domain callers. Primary reuse disposition: `replace`. Preserved source-plan detail: replace legacy shell operation authorization with Role/shell-admin or Zone-admin service gates. | | Integration | Controller and supervisor ComponentSession methods authorize before capacity or route lookup, preserving stale-handle non-disclosure and Host relay denial. Integration path: `packages/d2b-provider-shell-terminal/integration/authz/`. | | Data migration | Full d2b 3.0 reset; no v2 state/config import. | | Validation | `packages/d2b-provider-shell-terminal/tests/authz.rs` | @@ -1546,9 +1546,9 @@ per-test budget. | --- | --- | | Dependency/owner | Audit and telemetry area; owned by shell-terminal audit/telemetry modules. | | Current source | None — net-new v3 closed-label/redacted observability for shell-terminal; legacy shell paths must not leak names, paths, PIDs, or terminal bytes. | -| Reuse action | net-new redacted observability | +| Reuse action | create | | Destination | `packages/d2b-provider-shell-terminal/src/{audit,telemetry}.rs` | -| Detailed design | Implement closed-label metrics, redacted spans, and audit events with no usernames, session names, paths, or terminal bytes. | +| Detailed design | Implement closed-label metrics, redacted spans, and audit events with no usernames, session names, paths, or terminal bytes. Primary reuse disposition: `create`. Preserved source-plan detail: net-new redacted observability. | | Integration | Reconcile, OpenSession, Attach, Detach, Kill, terminal exit, degradation, and Host posture warnings emit only digest/enum surfaces consumed by audit and OTEL collectors. Integration path: `packages/d2b-provider-shell-terminal/integration/support-redaction/`. | | Data migration | Full d2b 3.0 reset; no v2 audit/telemetry state import. | | Validation | `packages/d2b-provider-shell-terminal/tests/redaction.rs` | @@ -1559,9 +1559,9 @@ per-test budget. | --- | --- | | Dependency/owner | Baseline removal area; owned by migration/removal implementation for shell-terminal. | | Current source | Superseded sources: `packages/d2b-guestd/src/shell.rs`, `packages/d2b-unsafe-local-helper/src/services/shell/`, and `packages/d2b-contracts/src/public_wire.rs` `ShellOp`/`ShellOpResponse`. | -| Reuse action | delete superseded runtime/protocol surfaces after successor parity | +| Reuse action | delete-after-cutover | | Destination | `packages/d2b-provider-shell-terminal/src/migration.rs` | -| Detailed design | Delete superseded guestd shell runtime, unsafe-local helper shell supervisor, and public-wire `ShellOp` or `ShellOpResponse` shell protocol. | +| Detailed design | Delete superseded guestd shell runtime, unsafe-local helper shell supervisor, and public-wire `ShellOp` or `ShellOpResponse` shell protocol. Primary reuse disposition: `delete-after-cutover`. Preserved source-plan detail: delete superseded runtime/protocol surfaces after successor parity. | | Integration | Removal runs after shell-terminal resource, supervisor, service, RBAC, and integration coverage prove parity; workspace manifests, CI shards, and pins are updated so old and new suites do not run indefinitely. Integration path: `packages/d2b-provider-shell-terminal/integration/migration-baseline/`. | | Data migration | Full d2b 3.0 reset; no v2 shell state/config import. | | Validation | `packages/d2b-provider-shell-terminal/tests/migration.rs` | @@ -1573,9 +1573,9 @@ per-test budget. | Dependency/owner | Supervisor service area; owned by shell-terminal controller/supervisor service modules and ComponentSession contracts. | | Current source | Reuse service-shape ideas only from main commit `a1cc0b2da4a08ca3240a770a972fe4da6f912bef` generated v2 shell services; exclude ADR 0045 session, realm, and constellation assumptions. | | Reuse source | `a1cc0b2da4a08ca3240a770a972fe4da6f912bef` `packages/d2b-contracts/src/generated_v2_services/shell.rs` and `shell_ttrpc.rs`. | -| Reuse action | adapt service-shape ideas into v3 ComponentSession services and named terminal stream | +| Reuse action | adapt | | Destination | `packages/d2b-provider-shell-terminal/src/service/{controller,supervisor}.rs` | -| Detailed design | Define and implement `shell-terminal.v3` and `shell-session-supervisor.v1` ComponentSession services and the named `terminal` stream contract. | +| Detailed design | Define and implement `shell-terminal.v3` and `shell-session-supervisor.v1` ComponentSession services and the named `terminal` stream contract. Primary reuse disposition: `adapt`. Preserved source-plan detail: adapt service-shape ideas into v3 ComponentSession services and named terminal stream. | | Integration | Controller service handles OpenSession/ListSessions/PoolStatus; supervisor service handles Attach/Detach/DetachAll/Kill/SupervisorStatus and terminal named stream routed by d2b-bus generation identity. Integration path: `packages/d2b-provider-shell-terminal/integration/service-contract/`. | | Data migration | Full d2b 3.0 reset; no v2 service/session state import. | | Validation | `packages/d2b-provider-shell-terminal/tests/service_contract.rs` | diff --git a/docs/specs/providers/ADR-046-provider-system-core.md b/docs/specs/providers/ADR-046-provider-system-core.md index 7ce5e620f..17789b3a0 100644 --- a/docs/specs/providers/ADR-046-provider-system-core.md +++ b/docs/specs/providers/ADR-046-provider-system-core.md @@ -1336,10 +1336,10 @@ items do not acquire co-ownership. | --- | --- | --- | --- | | `SC-001` | `ADR046-exec-001` | Owns the Host/User DTOs, schemas, bounds, admission vectors, and shared execution-policy extraction. `ADR046-core-002` is the coordination parent only; it owns no duplicate contract destination. | `ADR046-exec-001` owns removal of the Host/User/ExecutionPolicy portions of `HostJson`, `VmRuntimeRow`, and `WorkloadExecutionPosture` after all consuming resource slices reach parity. | | `SC-002` | `ADR046-exec-003` (Host), `ADR046-exec-004` (User), `ADR046-exec-005` (bootstrap ordering), and `ADR046-system-core-001` (Provider-specific manifest and audit boundary) | `ADR046-core-001` owns only the fixed core-controller process frame. Host and User handlers remain library code in their `ADR046-exec-*` destinations; the parent frame does not reimplement them. | `ADR046-exec-003` and `ADR046-exec-004` own host/group and UID/NSS parity; `ADR046-exec-005` owns retirement of the old daemon initialization sequence. | -| `SC-003` | `ADR046-exec-009` (user-only Host migration and status posture), `ADR046-exec-006`/`ADR046-exec-007` (Process Provider `ProcessEffect` emission), `ADR046-host-posture-001` (CLI/doctor warning), and `ADR046-telem-008` (OTEL absence gate) | system-core owns `Host.status.isolationPosture` and `NoIsolation`, but never emits `ProcessEffect`. The Process Providers query that status and own launch/stop/adopt/quarantine records. The telemetry gate owns no runtime emitter. | `ADR046-exec-009` owns helper binary/wire/dispatch retirement; `ADR046-nix-010` owns unsafe-local-specific Nix removal. | +| `SC-003` | `ADR046-exec-009` (user-only Host migration and status posture), `ADR046-exec-006`/`ADR046-exec-007` (Process Provider `ProcessEffect` emission), `ADR046-telem-018` (CLI/doctor warning), and `ADR046-telem-008` (OTEL absence gate) | system-core owns `Host.status.isolationPosture` and `NoIsolation`, but never emits `ProcessEffect`. The Process Providers query that status and own launch/stop/adopt/quarantine records. The telemetry gate owns no runtime emitter. | `ADR046-exec-009` owns helper binary/wire/dispatch retirement; `ADR046-nix-010` owns unsafe-local-specific Nix removal. | | `SC-004` | `ADR046-exec-012` (Nix resource authoring and eval rules) and `ADR046-exec-014` (schema-generated option modules and Zone bundle emission) | These items own the Nix destinations. system-core consumes the emitted Host/User resources and does not maintain a second emitter or schema vocabulary. | `ADR046-exec-012` owns Realm/Workload option removal; `ADR046-nix-010` owns the unsafe-local-specific Nix migration gate. | | `SC-005` | `ADR046-provider-002` (Provider package shape), `ADR046-exec-003`/`ADR046-exec-004` (system-core crate tests and conformance invocation), `ADR046-exec-020` (shared conformance toolkit), and `ADR046-pstate-011` (workspace layout gate) | The toolkit and policy gate are shared infrastructure. They are not reimplemented in `d2b-provider-system-core`; this crate only supplies its Host/User fixtures and required package paths. | `ADR046-pstate-011` owns the permanent layout gate; there is no system-core removal destination. | -| `SC-006` | `ADR046-telem-004` (core-controller instruments), `ADR046-telem-008` (allowlist/cardinality/redaction policy), `ADR046-audit-001` (shared audit sink and record machinery), and `ADR046-system-core-001` (Host/User `ResourceReconciled` adapter) | system-core contributes only the two closed handler values and its reconcile events. It does not own shared telemetry machinery, policy tests, or `ProcessEffect`. | `ADR046-telem-002` and `ADR046-telem-005` own replacement of their legacy metric families; `ADR046-telem-008` owns the final no-`vm`/no-`unsafe-local` label proof. | +| `SC-006` | `ADR046-telem-004` (core-controller instruments), `ADR046-telem-008` (allowlist/cardinality/redaction policy), `ADR046-telem-012` (shared audit sink and record machinery), and `ADR046-system-core-001` (Host/User `ResourceReconciled` adapter) | system-core contributes only the two closed handler values and its reconcile events. It does not own shared telemetry machinery, policy tests, or `ProcessEffect`. | `ADR046-telem-002` and `ADR046-telem-005` own replacement of their legacy metric families; `ADR046-telem-008` owns the final no-`vm`/no-`unsafe-local` label proof. | Test ownership is enumerated per test in §16. The §14.3 table and the final column above are the complete removal-proof assignment for this dossier. @@ -1349,11 +1349,11 @@ column above are the complete removal-proof assignment for this dossier. | Field | Value | | --- | --- | | Work item ID | `ADR046-system-core-001` | -| Dependency/owner | `ADR046-provider-001`, `ADR046-exec-003`, `ADR046-exec-004`, `ADR046-exec-005`, `ADR046-pstate-012`, `ADR046-telem-001`, and `ADR046-audit-001`; `Provider/system-core` owner | +| Dependency/owner | `ADR046-provider-001`, `ADR046-exec-003`, `ADR046-exec-004`, `ADR046-exec-005`, `ADR046-pstate-012`, `ADR046-telem-001`, and `ADR046-telem-012`; `Provider/system-core` owner | | Current source | No canonical v3 equivalent. Adapt the Provider descriptor pattern from `packages/d2b-realm-provider/src/provider.rs` and the bounded audit-envelope pattern from `packages/d2bd/src/daemon_audit.rs`; do not carry forward daemon topology or unsafe-local helper protocol types. | -| Reuse action | adapt descriptor/audit patterns; implement the v3 Provider-specific boundary | +| Reuse action | adapt | | Destination | `packages/d2b-provider-system-core/src/manifest.rs`, `packages/d2b-provider-system-core/src/audit.rs`, and `packages/d2b-provider-system-core/tests/provider_boundary.rs` | -| Detailed design | Compile the system-core Provider manifest, empty closed config schema, Host/User component descriptors, and empty state-namespace declaration. The manifest binds both library handlers to the fixed `d2b-core-controller` bootstrap process without declaring either handler as a Process resource. The audit adapter emits one bounded, redacted `ResourceReconciled` record after each Host/User reconcile. The boundary rejects Provider config fields and proves that handler call paths neither write `Provider.status` nor emit `ProcessEffect`; core-controller infrastructure owns the former and `ADR046-exec-006`/`ADR046-exec-007` own the latter. `ADR046-pstate-012` remains the owner of generic optional-state admission; this item only declares system-core's empty state set. | +| Detailed design | Compile the system-core Provider manifest, empty closed config schema, Host/User component descriptors, and empty state-namespace declaration. The manifest binds both library handlers to the fixed `d2b-core-controller` bootstrap process without declaring either handler as a Process resource. The audit adapter emits one bounded, redacted `ResourceReconciled` record after each Host/User reconcile. The boundary rejects Provider config fields and proves that handler call paths neither write `Provider.status` nor emit `ProcessEffect`; core-controller infrastructure owns the former and `ADR046-exec-006`/`ADR046-exec-007` own the latter. `ADR046-pstate-012` remains the owner of generic optional-state admission; this item only declares system-core's empty state set. Primary reuse disposition: `adapt`. Preserved source-plan detail: adapt descriptor/audit patterns; implement the v3 Provider-specific boundary. | | Integration | `ADR046-exec-003` and `ADR046-exec-004` call the audit adapter after reconcile; `ADR046-exec-005` and core-controller infrastructure load the manifest and derive the runtime-owned `Provider/system-core` resource/status. | | Data migration | Full d2b 3.0 reset; no Provider config, handler checkpoint, or audit state is imported. | | Validation | `config_schema_empty_only`, `provider_status_not_written_by_handlers`, `provider_state_set_empty`, `host_no_process_effect_emitted`, `host_resource_reconciled_audit`, and `user_resource_reconciled_audit`; manifest golden vector proves no Process descriptor and no state namespace for either handler. | @@ -1430,7 +1430,7 @@ per-test budget. | `host_reconcile_real_zone` | Provider/system-core controller reconciles a real Host resource in a container Zone runtime; phase reaches Ready | `ADR046-exec-003` | | `host_capability_probes_real_host` | Real `kvm`, `pidfd`, `cgroup-v2` capability probes succeed on a KVM-capable test host | `ADR046-exec-003` | | `user_reconcile_real_nss` | Real NSS getpwnam lookup for a declared test user; User reaches Ready | `ADR046-exec-004` | -| `unsafe_local_host_warning_cli` | `d2b zone inspect` renders the no-isolation warning for a user-only Host; warning absent for system-domain Host | `ADR046-host-posture-001` | +| `unsafe_local_host_warning_cli` | `d2b zone inspect` renders the no-isolation warning for a user-only Host; warning absent for system-domain Host | `ADR046-telem-018` | | `user_only_host_isolation_posture_stable` | Under a real Zone runtime, a user-only Host consistently reports `isolationPosture="none"` in status after restart and reconcile cycles | `ADR046-exec-009` | | `provider_system_core_bootstrap_failure_blocks_readiness` | If core cannot create or verify the runtime-owned `Provider/system-core` bootstrap resource, Zone reports Failed with a mandatory-provider condition; no Nix bundle declaration is expected | `ADR046-exec-005` | | `generation_cleanup_host_deleted` | Removing Host from Nix config triggers async Delete; ResourceDeletionRequested audit event present; store transaction removes row/index and writes Deleted revision; ResourceDeleted audit event appended with exactly-once recovery | `ADR046-exec-015` | diff --git a/docs/specs/providers/ADR-046-provider-system-minijail.md b/docs/specs/providers/ADR-046-provider-system-minijail.md index 5f1acba00..90025e84d 100644 --- a/docs/specs/providers/ADR-046-provider-system-minijail.md +++ b/docs/specs/providers/ADR-046-provider-system-minijail.md @@ -1583,9 +1583,9 @@ delivery assumptions are not copied. | --- | --- | | Dependency/owner | `ADR046-process-001` (common spec/status types); `ADR046-provider-001` (toolkit/contracts); system-minijail Provider owner | | Current source | `d2b-core/src/minijail_profile.rs`; `d2b-core/src/processes.rs` (NamespaceSet, MountPolicy, CgroupPlacement); `d2b-priv-broker/src/ops/spawn_runner.rs` | -| Reuse action | EXTRACT/ADAPT | +| Reuse action | adapt | | Destination | `packages/d2b-provider-system-minijail/src/sandbox_compiler.rs` | -| Detailed design | Accept `SandboxSpec` from common contracts; compile NamespaceClass/CapabilityClass/SeccompClass/UserNamespaceSpec/mount/environment/rlimit/umask into a versioned `CompiledSandboxPlan`; compute `sandboxRevisionDigest`; all rejection conditions from §12.1; no raw bitmask/BPF/argv/path in any output type; golden round-trip test vectors | +| Detailed design | Accept `SandboxSpec` from common contracts; compile NamespaceClass/CapabilityClass/SeccompClass/UserNamespaceSpec/mount/environment/rlimit/umask into a versioned `CompiledSandboxPlan`; compute `sandboxRevisionDigest`; all rejection conditions from §12.1; no raw bitmask/BPF/argv/path in any output type; golden round-trip test vectors Primary reuse disposition: `adapt`. Preserved source-plan detail: EXTRACT/ADAPT. | | Integration | LaunchTicket builder (ADR046-minijail-002); effect port integration (ADR046-minijail-003) | | Data migration | Full reset; current `MinijailProfile` not import-compatible with v3 SandboxSpec | | Validation | `tests/sandbox_compilation.rs`; `tests/schema.rs`; golden vectors | @@ -1597,7 +1597,7 @@ delivery assumptions are not copied. | --- | --- | | Dependency/owner | ADR046-minijail-001; common `LaunchTicket` contract | | Current source | `d2b-core/src/process_builder.rs`; `d2bd/src/supervisor/*.rs` (ticket generation) | -| Reuse action | ADAPT | +| Reuse action | adapt | | Destination | `packages/d2b-provider-system-minijail/src/launch.rs` | | Detailed design | LaunchTicket construction with compiled sandbox/budget/mount digests; ticket verification on ProviderSupervisor receipt; `d2b.supervisor.v3/IssueLaunchTicket` service call; expired/revoked/malformed ticket rejection | | Integration | `ProviderSupervisor` local adapter; minijail controller (ADR046-minijail-005) | @@ -1611,7 +1611,7 @@ delivery assumptions are not copied. | --- | --- | | Dependency/owner | ADR046-minijail-001; broker integration owner | | Current source | `d2b-priv-broker/src/ops/spawn_runner.rs`; `d2b-priv-broker/src/sys.rs` (`clone3_spawn_runner`, user namespace setup) | -| Reuse action | ADAPT | +| Reuse action | adapt | | Destination | Broker-side: `d2b-priv-broker` retains `SpawnRunner` op, invoked by the `MinijailProcessEffectPort` implementation owned by core/ProviderSupervisor; Provider-side: `packages/d2b-provider-system-minijail/src/launch.rs` calls `MinijailProcessEffectPort` with opaque Process/LaunchTicket/profile IDs; `user_ns.rs` implements the user namespace pre-establishment protocol | | Detailed design | Linux ≥5.14 and delegated-leaf `cgroup.kill` platform gate; `clone3(CLONE_PIDFD | CLONE_INTO_CGROUP)` with pre-declared cgroup leaf FD; broker retained as child parent and sole `waitid(P_PIDFD)`/reap/exit-status owner; verified duplicate returned privately to ProviderSupervisor for poll/readiness and exact-main `pidfd_send_signal`; anchored `cgroup.kill` write for unambiguous intentional teardown; user namespace pre-establishment sequence (§7.7) when `userNamespace` set; host UID 0 rejection; parent name-to-inode re-validation; zero-host-capability invariant (ADR 0021); `MinijailProcessEffectPort` privately maps opaque IDs to SpawnRunner/OpenDevice/clone3/uid-map/FD effects; Provider crate imports no broker service/client/DTO | | Integration | ADR046-minijail-002 (LaunchTicket); real cgroup/broker fixture in `integration/clone3_pidfd/` and `integration/user_namespace/` | @@ -1625,21 +1625,21 @@ delivery assumptions are not copied. | --- | --- | | Dependency/owner | ADR046-minijail-003; wait/pidfd owner | | Current source | `d2bd/src/supervisor/pidfd_table.rs` (PidfdTable, WaitTermination, BrokerReapLog) | -| Reuse action | EXTRACT/ADAPT | +| Reuse action | adapt | | Destination | Broker-side parent wait/reap and typed terminal relay in `packages/d2b-priv-broker/src/`; non-parent observation/status consumption in `packages/d2b-provider-system-minijail/src/{pidfd,wait}.rs` | -| Detailed design | Broker that called `clone3` alone calls `waitid(P_PIDFD)`, collects exit status, and reaps exactly once; ProviderSupervisor `AsyncFd` readability is a hint only and never a wait/status source; controller consumes the identity-bound broker relay and holds no raw pidfd; ProviderSupervisor duplicate reacquisition is dispatched through a bounded blocking adapter with explicit timeout; pidfd never serialized; verified broker/ProviderSupervisor holder retains exact-main `pidfd_send_signal`; no PID/PGID fallback; graceful deadline followed by mandatory anchored leaf `cgroup.kill`; empty-leaf proof before rmdir; exit class classification (clean-exit/crash/signal/timeout/unknown) | +| Detailed design | Broker that called `clone3` alone calls `waitid(P_PIDFD)`, collects exit status, and reaps exactly once; ProviderSupervisor `AsyncFd` readability is a hint only and never a wait/status source; controller consumes the identity-bound broker relay and holds no raw pidfd; ProviderSupervisor duplicate reacquisition is dispatched through a bounded blocking adapter with explicit timeout; pidfd never serialized; verified broker/ProviderSupervisor holder retains exact-main `pidfd_send_signal`; no PID/PGID fallback; graceful deadline followed by mandatory anchored leaf `cgroup.kill`; empty-leaf proof before rmdir; exit class classification (clean-exit/crash/signal/timeout/unknown) Primary reuse disposition: `adapt`. Preserved source-plan detail: EXTRACT/ADAPT. | | Integration | Controller restart → adoption (ADR046-minijail-005); finalize (§8.6) | | Data migration | None — full d2b 3.0 reset; no prior state to migrate | | Validation | `tests/lifecycle.rs`; `tests/broker_wait_contract.rs` (only clone3 parent calls waitid/reaps; poll readability cannot supply status); `tests/cgroup_kill_finalize.rs` (setsid descendant and PGID reuse); `tests/redaction.rs` (PID never in log/status/audit); `tests/blocking_adapter.rs` (duplicate/status relay via adapter; timeout → error) | | Removal proof | Old `PidfdTable` in `d2bd` supervisor removed after Provider integration | -### ADR046-minijail-005 (Dependency: ADR046-minijail-002, ADR046-minijail-004, ADR046-session-001, ADR046-bus-001) +### ADR046-minijail-005 (Dependency: ADR046-minijail-002, ADR046-minijail-004, ADR046-session-001, ADR046-session-003) | Field | Value | | --- | --- | -| Dependency/owner | All of ADR046-minijail-001 through ADR046-minijail-004; ComponentSession/d2b-bus (ADR046-session-001, ADR046-bus-001); bootstrap authz | +| Dependency/owner | All of ADR046-minijail-001 through ADR046-minijail-004; ComponentSession/d2b-bus (ADR046-session-001, ADR046-session-003); bootstrap authz | | Current source | `d2bd/src/supervisor/*.rs` (DagExecutor, NodeOutcome); `d2bd/src/supervisor/pidfd_table.rs`; `d2b-realm-core/src/allocator_engine.rs` (adoption/identity concepts) | -| Reuse action | ADAPT | +| Reuse action | adapt | | Destination | `packages/d2b-provider-system-minijail/src/` — controller binary entry point; reconcile loop; adoption; quarantine; bootstrap authz; health/status; restart; finalize | | Detailed design | Full Process/EphemeralProcess reconcile algorithm (§8); fast path ≤5/≤20 ms gates; spawn via `MinijailProcessEffectPort` (opaque IDs; no broker DTO imported); adoption algorithm (§8.5) with `/proc` reads, cgroup enumeration, and original-broker-parent verification via bounded blocking adapters; quarantine on ambiguity; quarantine reuse blocked until externally established process-absence proof or full Zone reset; no signal or cgroup.kill write to quarantined/ambiguous identity; restart/backoff driven only by broker-relayed terminal status; finalize (§8.6) with exact-main SIGTERM, bounded grace, mandatory cgroup.kill, broker wait/reap, empty-leaf proof, and no PGID ownership; EphemeralProcess continuation recovery (§9); bootstrap authz scope (§3); post-bootstrap RBAC; metric label closed-set enforcement (no `zone` label); controller writes status only on Process/EphemeralProcess resources; Provider resource status aggregated by core; the controller declares no Provider state Volume and mounts none — its bounded non-secret operational state lives in `status`/the core Operation ledger (§5.1, D087) and running units are re-adopted from cgroup leaves + fresh pidfds on restart | | Integration | Zone runtime startup (bootstrap); all v3 ResourceClient/bus/session paths | @@ -1653,7 +1653,7 @@ delivery assumptions are not copied. | --- | --- | | Dependency/owner | ADR046-minijail-005; Nix integrator; test infrastructure owner | | Current source | `nixos-modules/processes-json.nix`; `nixos-modules/minijail-profiles.nix`; `packages/d2b-contract-tests/tests/policy_observability.rs` | -| Reuse action | ADAPT | +| Reuse action | adapt | | Destination | `nixos-modules/` — v3 Nix `Process`/`EphemeralProcess` resource authoring; Provider catalog entry; `docs/reference/schemas/v3/Process.json`; `docs/reference/schemas/v3/EphemeralProcess.json`; `make test-drift` schema drift gate | | Detailed design | Nix module accepts `d2b.zones..resources.` with `type = "Process"` or `"EphemeralProcess"`; eval-time validation rules (§16.4); build-time JSON validation (§16.5); artifact catalog integration; cleanup contract tests (§16.5) | | Integration | `d2b.artifacts` catalog; Zone bundle emission; `make test-drift` | diff --git a/docs/specs/providers/ADR-046-provider-system-systemd.md b/docs/specs/providers/ADR-046-provider-system-systemd.md index debc6e1b7..3d27afe0a 100644 --- a/docs/specs/providers/ADR-046-provider-system-systemd.md +++ b/docs/specs/providers/ADR-046-provider-system-systemd.md @@ -1321,9 +1321,9 @@ assumptions. Copied behavior is independently re-tested against v3 | Dependency/owner | `ADR046-process-002`; Process contracts/supervisor owner; effect port interface owner | | Current source | `packages/d2b-unsafe-local-helper/src/systemd.rs` — `SystemdUserScopeManager`, `VerifiedScope`; `packages/d2bd/src/supervisor/` — pidfd adoption, restart backoff | | Reuse source | Main `a1cc0b2d`: `d2b-session/src/engine.rs`, `d2b-session-unix/src/adapter.rs` (effect port test double session/transport) | -| Reuse action | extract and adapt | +| Reuse action | adapt | | Destination | `packages/d2b-provider-system-systemd/src/controller.rs` (async reconcile loop), `src/launch.rs` (launch algorithm via effect port), `src/effect_port.rs` (`SystemdProcessEffectPort` trait + test double), `src/adoption.rs` (adoption algorithm), `src/sandbox.rs` (SandboxSpec → unit property compiler) | -| Detailed design | Full §6 launch algorithm (effect port integration); §7 EphemeralProcess; §8 restart/adoption (effect port `locate_by_identity`); §9 drain (effect port `stop`/`kill`); §10 sandbox compilation; §11 bus services; ProviderSupervisor LaunchTicket integration | +| Detailed design | Full §6 launch algorithm (effect port integration); §7 EphemeralProcess; §8 restart/adoption (effect port `locate_by_identity`); §9 drain (effect port `stop`/`kill`); §10 sandbox compilation; §11 bus services; ProviderSupervisor LaunchTicket integration Primary reuse disposition: `adapt`. Preserved source-plan detail: extract and adapt. | | Integration | Core ProviderDeployment creates controller Process via Provider/system-minijail and creates/deletes one private Volume per component per execution target before/after component Processes (naming: `system-systemd--controller--main-state--`; ownerRef: Provider/system-systemd; kind=state; persistenceClass=persistent; minimal nonzero quota; identity marker; migrationPolicy=none; layout principal: Nix-preprovisioned `User/` or bounded principal pool; no ComponentPrincipal ResourceRef); Provider/volume-local is sole Volume reconciler; system-systemd controller does NOT create, own, or reconcile its Volume; controller consumes only the `controller-rw` view dirfd via its Process mounts; controller watches Process/EphemeralProcess; ProviderSupervisor calls LaunchProcess; effect port implementation injected by core supervisor spec | | Data migration | No state migration; controller relists and adopts on restart | | Validation | `tests/conformance.rs` (shared conformance kit); `tests/identity_binding.rs` (InvocationID/cgroup/MainPID/start-time golden vectors via mock effect port); `tests/adoption.rs` (quarantine/identity-mismatch cases); `tests/restart.rs` (backoff/maxRestarts); latency assertions (p95 ≤5 ms hint→handler, ≤20 ms commit→effect port `start` call) | @@ -1351,9 +1351,9 @@ assumptions. Copied behavior is independently re-tested against v3 | Work item ID | `ADR046-systemd-003` | | Dependency/owner | `ADR046-systemd-001`; conformance kit / test infrastructure | | Current source | `packages/d2bd/src/supervisor/` (existing process lifecycle tests); `packages/d2b-unsafe-local-helper/src/systemd.rs` (existing scope tests) | -| Reuse action | copy/adapt | +| Reuse action | adapt | | Destination | `packages/d2b-provider-system-systemd/tests/conformance.rs`, `tests/fault.rs`, `tests/ephemeral.rs`, `tests/sandbox_compile.rs`; `integration/host_scenario.rs`, `integration/guest_scenario.rs` | -| Detailed design | Full §19 test/integration requirements | +| Detailed design | Full §19 test/integration requirements Primary reuse disposition: `adapt`. Preserved source-plan detail: copy/adapt. | | Integration | `cargo test -p d2b-provider-system-systemd`; `make test-integration -- provider-system-systemd`; `make test-host-integration -- provider-system-systemd` | | Data migration | None — full d2b 3.0 reset; no prior state to migrate | | Validation | All conformance vectors pass; all fault injection scenarios reach expected phase/condition; all §19 Host and Guest test scenarios pass | diff --git a/docs/specs/providers/ADR-046-provider-transport-azure-relay.md b/docs/specs/providers/ADR-046-provider-transport-azure-relay.md index be284aac6..6f0efead0 100644 --- a/docs/specs/providers/ADR-046-provider-transport-azure-relay.md +++ b/docs/specs/providers/ADR-046-provider-transport-azure-relay.md @@ -1312,9 +1312,9 @@ download, or PATH scan. | --- | --- | | Dependency/owner | W0 shared contract root; ComponentSession transport adapter owner | | Current source | `packages/d2b-provider-relay/src/lib.rs` (`RelayEndpoint`, `RelayCredential`, `RelayRole`, `RelayStream`, `connect()`, `listen()`, `mint_sas()`) | -| Reuse action | extract and adapt | +| Reuse action | adapt | | Destination | `packages/d2b-provider-transport-azure-relay/src/relay_transport.rs` | -| Detailed design | Adapt `RelayStream` as relay transport service process; expose named opaque byte stream on the `transport-service` Unix endpoint; add 2-byte length-prefixed framing; preserve credential redaction; TLS/WebSocket state stays in-process — only Noise record bytes traverse the named stream; register named stream with d2b-bus as `TransportHandle`; transport descriptor: `attachment_support: false`, `locality: Remote`, `atomic: false`; expose `OpenTransport`/`CloseTransport`/`ObserveTransport` interface to core; long-lived service process multiplexes sessions internally | +| Detailed design | Adapt `RelayStream` as relay transport service process; expose named opaque byte stream on the `transport-service` Unix endpoint; add 2-byte length-prefixed framing; preserve credential redaction; TLS/WebSocket state stays in-process — only Noise record bytes traverse the named stream; register named stream with d2b-bus as `TransportHandle`; transport descriptor: `attachment_support: false`, `locality: Remote`, `atomic: false`; expose `OpenTransport`/`CloseTransport`/`ObserveTransport` interface to core; long-lived service process multiplexes sessions internally Primary reuse disposition: `adapt`. Preserved source-plan detail: extract and adapt. | | Integration | Core ZoneLink controller calls `OpenTransport(spec.provider.settings)` → receives named byte stream handle; relay service cannot interpret plaintext bytes; one carriage per call; WebSocket loss closes the named stream | | Data migration | No compatibility with current relay sessions; v3 sessions are independent | | Validation | `tests/fake_relay_transport.rs`: connect/accept, framing, credential redaction, named stream roundtrip; `tests/listener_sender_conformance.rs`: named stream contract; Noise KK binding; relay identity exclusion | @@ -1326,7 +1326,7 @@ download, or PATH scan. | --- | --- | | Dependency/owner | ADR046-transport-relay-001; Credential KK session; ComponentSession/d2b-bus owner | | Current source | None (new) | -| Reuse action | new | +| Reuse action | create | | Destination | `packages/d2b-provider-transport-azure-relay/src/credential_client.rs` | | Detailed design | Async Credential KK session client for service components (service processes have d2b-bus access, enabling Credential KK; workers do not); acquire listener credential via KK inside the gateway Guest using `config.credentialBindings[listenerCredentialAlias]`; acquire sender credential independently inside the child's gateway Guest via the same KK model using `config.credentialBindings[senderCredentialAlias]`; raw credential bytes held in zeroizing memory inside the gateway Guest, presented to Azure Relay, then immediately zeroized; no credential bytes cross process, network, or Guest boundary; redacted Debug; no credential bytes in logs/audit/OTEL; core ProviderDeployment creates a private persistent Volume (per ADR-046-provider-state) for each component before its Process starts — the transport Provider does not own or create these Volumes; `Provider/volume-local` reconciles them; `migrationPolicy: none` means no migration worker is ever spawned; no relay auth token, WebSocket handle, session key, or credential byte is written to that Volume; all relay session state remains transient in-process memory | | Integration | Listener service invokes before each relay connect attempt inside gateway Guest; child Zone's sender service acquires its own credential independently inside its own gateway Guest | @@ -1340,7 +1340,7 @@ download, or PATH scan. | --- | --- | | Dependency/owner | ADR046-transport-relay-001; reconnect contract; ZoneLink handler | | Current source | None (new; core drives reconnect, not the transport Provider) | -| Reuse action | new | +| Reuse action | create | | Destination | `packages/d2b-provider-transport-azure-relay/src/reconnect.rs` | | Detailed design | Relay service responds to `CloseTransport`+`OpenTransport` cycle from core; core owns reconnect policy and backoff scheduling; relay service tears down the current WebSocket when core calls `CloseTransport` and establishes a new WebSocket connection when core calls `OpenTransport`; relay service does not maintain a backoff state machine or independently retry — it starts a new WebSocket on demand and emits the connect result via `ObserveTransport`; listener and sender are long-lived service processes that do not re-spawn on reconnect | | Integration | `ObserveTransport` delivers `TransportObservation::Disconnected` to core; core drives reconnect via `CloseTransport` then `OpenTransport` after applying its own backoff | @@ -1354,7 +1354,7 @@ download, or PATH scan. | --- | --- | | Dependency/owner | ADR046-transport-relay-001; transport settings schema; Nix configuration owner | | Current source | `docs/specs/ADR-046-zone-routing.md` transport settings Nix example | -| Reuse action | new | +| Reuse action | create | | Destination | `packages/d2b-provider-transport-azure-relay/src/transport_settings.rs`; `docs/reference/schemas/v3/providers/transport-azure-relay.transport-settings.json` | | Detailed design | `AzureRelayTransportSettings` Rust struct with serde; validation against committed JSON Schema; reject `secret`-annotated fields; enforce `^[a-z][a-z0-9-]*$` pattern for `listenerCredentialAlias`/`senderCredentialAlias` alias ID fields (never `Credential/` refs); xtask `gen-provider-transport-schemas` integration | | Integration | `make test-drift` gate: `xtask gen-provider-transport-schemas && git diff --exit-code` | @@ -1382,7 +1382,7 @@ download, or PATH scan. | --- | --- | | Dependency/owner | ADR046-transport-relay-001 through ADR046-transport-relay-005; telemetry/audit owner | | Current source | `packages/d2bd/src/metrics.rs` (hand-rolled Prometheus; baseline) | -| Reuse action | new | +| Reuse action | create | | Destination | `packages/d2b-provider-transport-azure-relay/src/{metrics.rs, audit.rs}` | | Detailed design | Emit all OTEL metrics and audit records listed in §OTEL and §Audit; closed label sets; never label secret bytes; provider audit covers **carriage authentication and health observations only** — Azure auth events, WebSocket lifecycle, credential acquisition outcomes — and is **separate from resource audit** (resource lifecycle events are owned by core); audit records appended through the Zone runtime audit log interface (no atomicity guarantee with Zone resource state in redb; best-effort delivery per the Zone's audit provider configuration); OTEL via lightweight emitter ring (no direct OTEL SDK dependency in Provider) | | Integration | `Provider/observability-otel` receives emitter ring frames; audit log via Zone runtime `d2b.audit.transport` category | @@ -1396,7 +1396,7 @@ download, or PATH scan. | --- | --- | | Dependency/owner | Provider crate owner; integration test owner | | Current source | None — net-new v3 work; no pre-ADR45 baseline equivalent | -| Reuse action | new | +| Reuse action | create | | Destination | `packages/d2b-provider-transport-azure-relay/src/tests/integration/README` | | Detailed design | Required content: fake relay server setup and teardown using the injected fake Relay effect port; how to run hermetic integration tests without a live Azure service; how to configure the injected fake Credential effect port for credential delivery tests; how to run with a real Azure namespace (requires a `Credential` resource declared in `spec.config.credentialBindings`, not environment-variable credential paths); integration test scenarios and expected outcomes; CI/local execution instructions | | Integration | `make test-integration` invokes `tests/integration/containers/` scenarios which inject the fake relay and credential port implementations from `src/tests/integration/fake_relay_server.rs` | diff --git a/docs/specs/providers/ADR-046-provider-transport-unix.md b/docs/specs/providers/ADR-046-provider-transport-unix.md index 96a6fa628..7a7317530 100644 --- a/docs/specs/providers/ADR-046-provider-transport-unix.md +++ b/docs/specs/providers/ADR-046-provider-transport-unix.md @@ -1404,9 +1404,9 @@ Old and new suites never run in parallel indefinitely. | Dependency/owner | ADR046-session-001 (v3 ComponentSession contracts); W0 shared contract root | | Current source | `packages/d2b-contracts/src/v2_component_session.rs` (protocol constants, credit-class constants) at main `a1cc0b2d` | | Reuse source | Same file; constants copied into `packages/d2b-contracts/src/v3/zone_session.rs` by ADR046-session-001 | -| Reuse action | Dependency on ADR046-session-001 output | +| Reuse action | adapt | | Destination | `packages/d2b-provider-transport-unix/src/credit.rs` (imports `MAX_PACKET_ATTACHMENTS=32`, `RESERVED_CONTROL_FDS=64`, credit-class constants from v3 contract); `src/portal.rs` (imports `MAX_PACKET_ATTACHMENTS` for portal validation) | -| Detailed design | Import credit scope capacities and headroom from `v3_zone_session.rs`; add `MAX_OPEN_TRANSPORTS: usize = 256` local constant for handle table bound | +| Detailed design | Import credit scope capacities and headroom from `v3_zone_session.rs`; add `MAX_OPEN_TRANSPORTS: usize = 256` local constant for handle table bound. Primary reuse disposition: `adapt`. Preserved source-plan detail: Dependency on ADR046-session-001 output. | | Integration | `CreditScopeSet` constructed from imported constants at session setup | | Data migration | None; v3 constants freeze independently | | Validation | `tests/credit.rs::ancillary_capacity_is_derived_from_closed_hard_bounds` passes against v3 constants | @@ -1419,12 +1419,12 @@ Old and new suites never run in parallel indefinitely. | Field | Value | | --- | --- | | Work item ID | `ADR046-transport-unix-002` | -| Dependency/owner | ADR046-transport-unix-001; d2b-bus transport layer (ADR046-bus-001) | +| Dependency/owner | ADR046-transport-unix-001; d2b-bus transport layer (ADR046-session-003) | | Current source | `packages/d2b-session-unix/src/{adapter,socket,descriptor}.rs`, `tests/unix_session.rs` at main `a1cc0b2d` | | Reuse source | Same; `UnixSeqpacketTransport`, `PeerIdentityPolicy`, `UnixAttachmentPayload`, `OwnedUnixAttachment`, `SeqpacketSocket`, `PeerCredentials`, `ObjectIdentity`, `AcceptedAttachment`, `VerifiedPacket` | -| Reuse action | copy and adapt | +| Reuse action | adapt | | Destination | `packages/d2b-provider-transport-unix/src/{seqpacket,identity,socket}.rs` | -| Detailed design | Copy transport structs verbatim; adapt `PeerIdentityPolicy` to report `PeerCredentials` upward to ComponentSession for subject mapping (not for direct resource lookup — that is core's responsibility); maintain `SO_PASSCRED` setup and first-packet credential extraction as documented; CLOEXEC enforcement uses `rustix` syscall wrappers over `libc` where available | +| Detailed design | Copy transport structs verbatim; adapt `PeerIdentityPolicy` to report `PeerCredentials` upward to ComponentSession for subject mapping (not for direct resource lookup — that is core's responsibility); maintain `SO_PASSCRED` setup and first-packet credential extraction as documented; CLOEXEC enforcement uses `rustix` syscall wrappers over `libc` where available Primary reuse disposition: `adapt`. Preserved source-plan detail: copy and adapt. | | Integration | `portal.rs::open_transport` calls `SeqpacketSocket::getsockopt(SO_TYPE)` and `setsockopt(SO_PASSCRED)`, constructs `UnixSeqpacketTransport`, hands OwnedTransport FD back to caller | | Data migration | None — full d2b 3.0 reset; no prior state to migrate | | Validation | Copy all 12 test functions; add `peercred_reported_to_componentsession_not_resolved_to_subject_here` | @@ -1440,9 +1440,9 @@ Old and new suites never run in parallel indefinitely. | Dependency/owner | ADR046-transport-unix-001 | | Current source | `packages/d2b-session-unix/src/adapter.rs` `UnixStreamTransport`, `src/socket.rs` `StreamSocket` at main `a1cc0b2d` | | Reuse source | Same | -| Reuse action | copy unchanged | +| Reuse action | adapt | | Destination | `packages/d2b-provider-transport-unix/src/{stream,socket}.rs` | -| Detailed design | Copy verbatim; add `attachment_support: false` in `TransportDescriptor` (stream never carries SCM_RIGHTS regardless of route class); `admission.rs::validate_route_class` rejects `attachments_enabled=true` for stream | +| Detailed design | Copy verbatim; add `attachment_support: false` in `TransportDescriptor` (stream never carries SCM_RIGHTS regardless of route class); `admission.rs::validate_route_class` rejects `attachments_enabled=true` for stream Primary reuse disposition: `adapt`. Preserved source-plan detail: copy unchanged. | | Integration | Same path as seqpacket but without SCM_RIGHTS paths | | Data migration | None — full d2b 3.0 reset; no prior state to migrate | | Validation | `tests/portal.rs::stream_open_transport_forces_no_attachments`; `tests/identity.rs::stream_transport_reassembles_partial_and_coalesced_records` | @@ -1458,9 +1458,9 @@ Old and new suites never run in parallel indefinitely. | Dependency/owner | ADR046-transport-unix-001 | | Current source | `packages/d2b-session-unix/src/credit.rs` at main `a1cc0b2d` | | Reuse source | Same; `CreditPool`, `CreditScopeSet`, `CreditBundle`, `ProcessCreditLimit`, `CreditScope` | -| Reuse action | copy unchanged | +| Reuse action | adapt | | Destination | `packages/d2b-provider-transport-unix/src/credit.rs` | -| Detailed design | Copy all five types verbatim; import scope-capacity constants from v3 contract; add `#[derive(Debug)]` with redacted Display (no raw counts in Debug output) | +| Detailed design | Copy all five types verbatim; import scope-capacity constants from v3 contract; add `#[derive(Debug)]` with redacted Display (no raw counts in Debug output) Primary reuse disposition: `adapt`. Preserved source-plan detail: copy unchanged. | | Integration | `CreditScopeSet` created per active ComponentSession; `CreditBundle` per packet receive; credits released in `UnixAttachmentPayload::close()` | | Data migration | None — full d2b 3.0 reset; no prior state to migrate | | Validation | Copy all 4 credit test functions; add `credit_released_on_attachment_close` and `emergency_headroom_constant_across_fd_counts` | @@ -1476,9 +1476,9 @@ Old and new suites never run in parallel indefinitely. | Dependency/owner | ADR046-transport-unix-002 | | Current source | `packages/d2b-session-unix/src/descriptor.rs` `PidfdIdentityPolicy`, `DescriptorPolicy` at main `a1cc0b2d` | | Reuse source | Same | -| Reuse action | copy and adapt | +| Reuse action | adapt | | Destination | `packages/d2b-provider-transport-unix/src/descriptor.rs` | -| Detailed design | Copy verbatim; adapt `DescriptorPolicy::verify` to produce `AcceptedAttachment` carrying `ObjectIdentity` binding for v3 ComponentSession attachment descriptor model; `pid` not stored beyond liveness check | +| Detailed design | Copy verbatim; adapt `DescriptorPolicy::verify` to produce `AcceptedAttachment` carrying `ObjectIdentity` binding for v3 ComponentSession attachment descriptor model; `pid` not stored beyond liveness check Primary reuse disposition: `adapt`. Preserved source-plan detail: copy and adapt. | | Integration | Called by seqpacket transport after decrypting attachment descriptor | | Data migration | None — full d2b 3.0 reset; no prior state to migrate | | Validation | Copy `pidfd_identity_requires_live_launch_evidence_and_rejects_unrelated_process` and `duplicate_kernel_objects_are_rejected_and_cleaned_up` | @@ -1494,7 +1494,7 @@ Old and new suites never run in parallel indefinitely. | Dependency/owner | ADR046-transport-unix-002 | | Current source | No existing v3 socket-kind admission module | | Reuse source | `getsockopt(SO_TYPE)` pattern widely used; no specific main reuse source | -| Reuse action | new | +| Reuse action | adapt | | Destination | `packages/d2b-provider-transport-unix/src/admission.rs` | | Detailed design | `validate_route_class(route_class, socket_kind, attachments_enabled, received_fd)` calls `getsockopt(SO_TYPE)` (blocking adapter) on `received_fd`: `SOCK_SEQPACKET` must match `"seqpacket"`, `SOCK_STREAM` must match `"stream"`, any other type fails `invalid-socket-fd`; if `route_class == RouteClass::ZoneLink && attachments_enabled == true` fail `attachment-policy-conflict` with detail `cross-zone-attachments-forbidden`; if `socket_kind == "stream" && attachments_enabled == true` fail `attachment-policy-conflict`; no Noise profile enforcement (that is ComponentSession's responsibility); returns `Ok(RouteAdmission { route_class, socket_kind, attachments_enabled })` | | Integration | Called by `portal.rs::open_transport` before the monitoring dup and handle allocation | @@ -1509,12 +1509,12 @@ Old and new suites never run in parallel indefinitely. | Field | Value | | --- | --- | | Work item ID | `ADR046-transport-unix-007` | -| Dependency/owner | ADR046-transport-unix-002 through 006; ADR046-bus-001 (d2b-bus ComponentSession method dispatch); ADR046-session-001 (named-stream protocol) | +| Dependency/owner | ADR046-transport-unix-002 through 006; ADR046-session-003 (d2b-bus ComponentSession method dispatch); ADR046-session-001 (named-stream protocol) | | Current source | No portal service in v3 baseline; `d2b-provider-toolkit/src/server.rs` `GeneratedProviderServiceServer` dispatch pattern (main `a1cc0b2d`) for service entry pattern | | Reuse source | main `a1cc0b2d` `d2b-provider-toolkit/src/server.rs` service dispatch pattern | -| Reuse action | adapt dispatch pattern; implement portal methods as new | +| Reuse action | adapt | | Destination | `packages/d2b-provider-transport-unix/src/{portal,service}.rs` | -| Detailed design | `portal.rs`: `PortalHandler` struct owns a bounded `HashMap` (capacity `MAX_OPEN_TRANSPORTS=256`); `open_transport(req, attachment_fd)` validates via `admission.rs`, dups FD, allocates handle, stores `MonitorState { dup_fd, observation_senders: Vec }`; `close_transport(handle)` closes dup FD, half-closes all observation senders, removes entry; `observe_transport(handle)` registers a new `NamedStreamSender` and spawns an async epoll-watcher task on the dup FD; `TransportHandle` is a `[u8; 16]` random token; redacted in all Debug impls; `service.rs` is the binary entry: accepts the allocator-issued portal endpoint FD at launch, runs `GeneratedTransportServiceServer` over it, dispatches to `PortalHandler` | +| Detailed design | `portal.rs`: `PortalHandler` struct owns a bounded `HashMap` (capacity `MAX_OPEN_TRANSPORTS=256`); `open_transport(req, attachment_fd)` validates via `admission.rs`, dups FD, allocates handle, stores `MonitorState { dup_fd, observation_senders: Vec }`; `close_transport(handle)` closes dup FD, half-closes all observation senders, removes entry; `observe_transport(handle)` registers a new `NamedStreamSender` and spawns an async epoll-watcher task on the dup FD; `TransportHandle` is a `[u8; 16]` random token; redacted in all Debug impls; `service.rs` is the binary entry: accepts the allocator-issued portal endpoint FD at launch, runs `GeneratedTransportServiceServer` over it, dispatches to `PortalHandler` Primary reuse disposition: `adapt`. Preserved source-plan detail: adapt dispatch pattern; implement portal methods as new. | | Integration | Core ZoneLink controller calls the three methods via d2b-bus; portal endpoint FD is supplied by Zone runtime/allocator at Process spawn, not SD_LISTEN_FDS | | Data migration | None — full d2b 3.0 reset; no prior state to migrate | | Validation | `tests/portal.rs::open_transport_zone_link_validates_and_returns_ownedtransport`; `open_transport_local_portal_seqpacket_with_attachments_accepted`; `open_transport_zone_link_attachments_enabled_rejected`; `close_transport_is_idempotent_after_handle_removed`; `observe_transport_delivers_pollhup_as_peer_disconnected`; `handle_table_rejects_at_max_capacity`; `restart_clears_all_handles` | @@ -1530,9 +1530,9 @@ Old and new suites never run in parallel indefinitely. | Dependency/owner | ADR046-transport-unix-007; ADR046-provider-003 (system Provider framework); Provider/system-minijail (ADR046-provider-003) | | Current source | `packages/d2b-priv-broker/src/` minijail spawn patterns (v3 baseline); `packages/d2b-host/src/` process arg patterns; current package derivations in `flake.nix` | | Reuse source | Minijail sandbox semantic class patterns from current v3 broker; Process resource schema from ADR-046-resources-host-guest-process-user | -| Reuse action | adapt; no direct symbol copy | +| Reuse action | adapt | | Destination | `packages/d2b-provider-transport-unix/` crate Cargo.toml binary target `d2b-transport-unix-service`; Provider component descriptor JSON committed at `packages/d2b-provider-transport-unix/descriptor/unix-transport-service.json`; Nix package derivation at `packages/d2b-provider-transport-unix/` | -| Detailed design | Component descriptor declares: `processClass=service`, `template=unix-transport-service`, `stateNamespaces=[]` (no Provider state Volume; bounded non-secret operational state in status/core ledger, D087), `sandbox.capabilityClasses=[]`, `sandbox.namespaceClasses=[mount]`, `sandbox.seccompClass=strict`, `budget.memory.limit="16Mi"`, `budget.cpu.limit="200m"`, `budget.fds.limit=512`, `endpoints=[{name:portal,transport:unix,purpose:transport-unix-portal}]`, `readiness={class:provider-defined,initialDelay:"0s",timeout:"5s",failureThreshold:1,successThreshold:1}`, `restartPolicy={class:always,backoffBase:"2s",backoffMax:"60s",backoffMultiplier:2.0,maxRestarts:10,resetAfter:"1h"}`; Provider package bundles descriptor digest; core ProviderDeployment creates the Process with empty `mounts` when `Provider/transport-unix` is installed | +| Detailed design | Component descriptor declares: `processClass=service`, `template=unix-transport-service`, `stateNamespaces=[]` (no Provider state Volume; bounded non-secret operational state in status/core ledger, D087), `sandbox.capabilityClasses=[]`, `sandbox.namespaceClasses=[mount]`, `sandbox.seccompClass=strict`, `budget.memory.limit="16Mi"`, `budget.cpu.limit="200m"`, `budget.fds.limit=512`, `endpoints=[{name:portal,transport:unix,purpose:transport-unix-portal}]`, `readiness={class:provider-defined,initialDelay:"0s",timeout:"5s",failureThreshold:1,successThreshold:1}`, `restartPolicy={class:always,backoffBase:"2s",backoffMax:"60s",backoffMultiplier:2.0,maxRestarts:10,resetAfter:"1h"}`; Provider package bundles descriptor digest; core ProviderDeployment creates the Process with empty `mounts` when `Provider/transport-unix` is installed Primary reuse disposition: `adapt`. Preserved source-plan detail: adapt; no direct symbol copy. | | Integration | Provider resource installed → core ProviderDeployment reads component descriptor → creates child `Process/transport-unix-service` (no state-Volume prerequisite) → ProviderSupervisor spawns binary with portal FD in inherited FD table. On delete: Process terminal first → ProviderDeployment finalizer cleared last; the service `status` disappears with the resource row | | Data migration | None (fresh Provider resource) | | Validation | `tests/conformance.rs::process_resource_matches_component_descriptor`; `tests/conformance.rs::provider_state_set_is_empty`; `tests/conformance.rs::no_state_volume_mount`; sandbox policy tests against minijail conformance kit | @@ -1548,7 +1548,7 @@ Old and new suites never run in parallel indefinitely. | Dependency/owner | ADR046-transport-unix-001; Nix/build integrator | | Current source | `nixos-modules/options-realms.nix` realm options (v3 baseline); `nixos-modules/assertions.nix` | | Reuse source | None; new schema file | -| Reuse action | new | +| Reuse action | create | | Destination | `docs/reference/schemas/v3/providers/transport-unix.transport-binding.json`; `nixos-modules/assertions.nix` (assertion additions); generated `nixos-modules/generated/options-zones-ZoneLink.nix` provider-settings submodule | | Detailed design | Commit the JSON Schema; run `xtask gen-zone-schemas` and `xtask gen-zone-nix-options` to regenerate committed files; add two assertions to `assertions.nix` (stream+attachments conflict; sensitive key names); `xtask gen-zone-resources` adds provider-specific settings validation step | | Integration | Build emitter validates `spec.provider.settings` against schema before computing `generationId`; drift gate enforces sync | @@ -1566,7 +1566,7 @@ Old and new suites never run in parallel indefinitely. | Dependency/owner | ADR046-transport-unix-007; ADR-046-telemetry-audit-and-support | | Current source | v3 baseline `tracing` crate patterns; v3 `d2b-realm-router/src/service_v2.rs` audit field shapes | | Reuse source | None; new per v3 telemetry separation invariant | -| Reuse action | new | +| Reuse action | create | | Destination | `packages/d2b-provider-transport-unix/src/{audit,metrics}.rs` | | Detailed design | `AuditRecordKind` enum with 6 event kinds from Audit section; `AuditRecord` carries only the fields listed (no uid/gid/pid/path/handle/ZoneLink name); emit via Zone runtime `emit_audit_record()` interface; `MetricCounter`/`MetricHistogram` with closed label types per Metrics section; emit via bounded in-process ring to OTEL Provider datagram socket; `tracing::instrument` spans on `PortalHandler` methods with the 3 permitted span attributes only | | Integration | `portal.rs` calls `audit.rs::emit_*` before returning from each portal method; `seqpacket.rs` calls `metrics.rs::record_*` on every accept/packet/attachment | @@ -1584,7 +1584,7 @@ Old and new suites never run in parallel indefinitely. | Dependency/owner | ADR046-transport-unix-007 through 010; test orchestration owner | | Current source | No existing integration tests for Unix portal scenarios | | Reuse source | Test scenario shapes from `d2b-session-unix/tests/unix_session.rs` end-to-end test (main `a1cc0b2d`) | -| Reuse action | new | +| Reuse action | adapt | | Destination | `packages/d2b-provider-transport-unix/integration/` and `integration/README.md` | | Detailed design | Four scenarios: `transport_open.rs` (fake Zone portal, allocator-socketpair FD attachment in → OwnedTransport attachment out → verify socket kind, CLOEXEC, SO_PASSCRED enabled; p95 latency assertion ≤2 ms); `fd_transfer.rs` (seqpacket `SCM_RIGHTS` transfer through opened transport, credit accounting, scavenge on error injection); `reconnect.rs` (CloseTransport + re-OpenTransport with fresh socketpair, verify previous handle is unknown, verify monitoring dup closed); `observation_stream.rs` (ObserveTransport stream receives `PEER_DISCONNECTED` event when peer closes socketpair end within 5 ms p95). `integration/README.md` documents prerequisites (no KVM required; all scenarios use in-process socketpairs and fake Zone API endpoint stub), invocation (`cargo test -p d2b-provider-transport-unix --test integration`), environment variables, and expected output | | Integration | Invoked by `make test-integration`; no host mutation; each scenario creates its own socketpairs | diff --git a/docs/specs/providers/ADR-046-provider-transport-vsock.md b/docs/specs/providers/ADR-046-provider-transport-vsock.md index f77a2aa80..8728e70b5 100644 --- a/docs/specs/providers/ADR-046-provider-transport-vsock.md +++ b/docs/specs/providers/ADR-046-provider-transport-vsock.md @@ -104,7 +104,7 @@ is a protocol error. The `SessionEngine` checks `descriptor().attachment_support before dispatching and returns `attachment-not-permitted-over-vsock` without contacting the remote end. -**V3 destination**: Imported from `packages/d2b-bus/src/session/` (ADR046-bus-001). +**V3 destination**: Imported from `packages/d2b-bus/src/session/` (ADR046-session-003). The `OwnedTransport` trait is not re-implemented in the transport Provider; it is consumed from `d2b-session`. @@ -827,7 +827,7 @@ expression. | Aspect | Anchor | Evidence class | Retained / Delta / Replacement | | --- | --- | --- | --- | | 2-byte framing utilities | `d2b-session-unix/src/vsock.rs` (main `a1cc0b2d`) | `implemented-but-unwired` | Retained: `FramedVsockTransport` framing (length-prefix encode/decode, bounded allocation, EOF/reset classification) → `framing.rs` in Provider crate. `NativeVsockTransport` / `NativeVsockListener` (raw AF_VSOCK socket calls) → core `LiveVsockEffectPort` in `d2b-core-controller`; NOT in Provider crate | -| `OwnedTransport` trait | `d2b-session-unix/src/adapter.rs` (main `a1cc0b2d`) | `implemented-but-unwired` | Retained verbatim; destination `d2b-session` crate (ADR046-bus-001) | +| `OwnedTransport` trait | `d2b-session-unix/src/adapter.rs` (main `a1cc0b2d`) | `implemented-but-unwired` | Retained verbatim; destination `d2b-session` crate (ADR046-session-003) | | vsock error variants (12) | `d2b-session-unix/src/vsock.rs` (main `a1cc0b2d`) | `implemented-but-unwired` | Retained verbatim in `errors.rs` | | CONNECT-proxy guest-control | `d2bd/src/guest_control_vsock.rs` | `implemented-and-reachable` | Replacement: superseded by `LiveVsockEffectPort` allocator path; guest-control port 14318 excluded from ZoneLink allocation | | socat OTLP relay | `d2b-host/src/vsock_relay_argv.rs` | `implemented-and-reachable` | Replacement: superseded by `observability-otel` Provider native vsock relay | @@ -846,11 +846,11 @@ expression. ### ADR046-vsock-001 | Field | Value | | --- | --- | -| Dependency/owner | Title: Implement `VsockEffectPort` trait and `OpaqueEndpointId`/`OpaqueBindingId` newtypes; Phase 1; Priority P0; Depends on ADR046-bus-001 (OwnedTransport in d2b-session); Owner crate `d2b-provider-transport-vsock`. | +| Dependency/owner | Title: Implement `VsockEffectPort` trait and `OpaqueEndpointId`/`OpaqueBindingId` newtypes; Phase 1; Priority P0; Depends on ADR046-session-003 (OwnedTransport in d2b-session); Owner crate `d2b-provider-transport-vsock`. | | Current source | Evidence class `test-only-or-preview`; baseline has no generic vsock transport Provider or opaque endpoint/binding ID trait. | -| Reuse action | net-new trait/newtypes with redaction tests; no real vsock socket opened | +| Reuse action | create | | Destination | `packages/d2b-provider-transport-vsock/src/effect_port.rs`; test fake in `tests/effect_port_mock.rs`; redaction checks in `tests/redaction.rs`. | -| Detailed design | Implement `VsockEffectPort` trait and `OpaqueEndpointId`/`OpaqueBindingId` newtypes. Define `VsockEffectPort` async trait and opaque ID newtypes in `effect_port.rs`; implement `FakeVsockEffectPort` for tests; `redaction.rs` asserts no raw `u32` in any `Debug`/`Display` output of opaque types; no real vsock socket opened. | +| Detailed design | Implement `VsockEffectPort` trait and `OpaqueEndpointId`/`OpaqueBindingId` newtypes. Define `VsockEffectPort` async trait and opaque ID newtypes in `effect_port.rs`; implement `FakeVsockEffectPort` for tests; `redaction.rs` asserts no raw `u32` in any `Debug`/`Display` output of opaque types; no real vsock socket opened. Primary reuse disposition: `create`. Preserved source-plan detail: net-new trait/newtypes with redaction tests; no real vsock socket opened. | | Integration | Core ZoneLink/delegation controller calls the Provider service with opaque IDs; Provider calls injected `VsockEffectPort`; live AF_VSOCK resolution remains in core runtime, not the Provider crate. | | Data migration | Full d2b 3.0 reset; no v2 state/config import. | | Validation | Proof type: hermetic unit + redaction test; `tests/effect_port_mock.rs` and `tests/redaction.rs`. | @@ -862,9 +862,9 @@ expression. | Dependency/owner | Title: Implement framing utilities and bridge task in Provider crate; Phase 1; Priority P0; Depends on ADR046-vsock-001; Owner crate `d2b-provider-transport-vsock`. | | Current source | Evidence class `implemented-but-unwired`; main commit `a1cc0b2da4a08ca3240a770a972fe4da6f912bef` `packages/d2b-session-unix/src/vsock.rs` contains `FramedVsockTransport` framing behavior, not current v3 baseline behavior. | | Reuse source | Main `a1cc0b2d` `packages/d2b-session-unix/src/vsock.rs` framing-only code and `packages/d2b-session-unix/tests/unix_session.rs` vsock framing subset. | -| Reuse action | copy/adapt framing-only code; exclude raw AF_VSOCK socket calls and ADR 0045 endpoint-role assumptions | +| Reuse action | adapt | | Destination | `packages/d2b-provider-transport-vsock/src/framing.rs` and `src/bridge.rs`; tests in `packages/d2b-provider-transport-vsock/tests/framing.rs`. | -| Detailed design | Implement framing utilities and bridge task in Provider crate. Copy `FramedVsockTransport` framing-only code (length-prefix encode/decode, bounded allocation, EOF/reset) from main `a1cc0b2d` → `framing.rs`; implement bridge task pumping bytes between an opaque `AsyncRead+AsyncWrite` stream from `VsockEffectPort::open` and the named ComponentSession stream; hermetic tests using `FakeVsockEffectPort` (no real socket). | +| Detailed design | Implement framing utilities and bridge task in Provider crate. Copy `FramedVsockTransport` framing-only code (length-prefix encode/decode, bounded allocation, EOF/reset) from main `a1cc0b2d` → `framing.rs`; implement bridge task pumping bytes between an opaque `AsyncRead+AsyncWrite` stream from `VsockEffectPort::open` and the named ComponentSession stream; hermetic tests using `FakeVsockEffectPort` (no real socket). Primary reuse disposition: `adapt`. Preserved source-plan detail: copy/adapt framing-only code; exclude raw AF_VSOCK socket calls and ADR 0045 endpoint-role assumptions. | | Integration | OpenTransport creates a framed opaque stream, bridge task pumps to a named ComponentSession stream, and d2b-bus consumes it as an `OwnedTransport` without FD attachment support. | | Data migration | Full d2b 3.0 reset; no v2 state/config import. | | Validation | Proof type: hermetic framing tests; `tests/framing.rs` covers partial/coalesced records, oversized frames, EOF/reset classification, and no real socket. | @@ -873,11 +873,11 @@ expression. ### ADR046-vsock-003 | Field | Value | | --- | --- | -| Dependency/owner | Title: Implement `VsockTransportService` (OpenTransport/CloseTransport/ObserveTransport); Phase 1; Priority P0; Depends on ADR046-vsock-002 and ADR046-bus-001; Owner crate `d2b-provider-transport-vsock`. | +| Dependency/owner | Title: Implement `VsockTransportService` (OpenTransport/CloseTransport/ObserveTransport); Phase 1; Priority P0; Depends on ADR046-vsock-002 and ADR046-session-003; Owner crate `d2b-provider-transport-vsock`. | | Current source | Evidence class `test-only-or-preview`; no current v3 generic `VsockTransportService` implementation exists. | -| Reuse action | net-new service implementation over ComponentSession and fake effect port tests | +| Reuse action | adapt | | Destination | `packages/d2b-provider-transport-vsock/src/service.rs`; tests `tests/open_close.rs`, `tests/observe.rs`, and conformance kit. | -| Detailed design | Implement `VsockTransportService` (OpenTransport/CloseTransport/ObserveTransport). Implement all three service methods in `service.rs`; `open_close.rs` and `observe.rs` test full service API against `FakeVsockEffectPort`; conformance kit passes. | +| Detailed design | Implement `VsockTransportService` (OpenTransport/CloseTransport/ObserveTransport). Implement all three service methods in `service.rs`; `open_close.rs` and `observe.rs` test full service API against `FakeVsockEffectPort`; conformance kit passes. Primary reuse disposition: `adapt`. Preserved source-plan detail: net-new service implementation over ComponentSession and fake effect port tests. | | Integration | Core ZoneLink/delegation controller is the only authorized caller; service opens named stream handles for d2b-bus, releases them on close, and streams transport events for observe. | | Data migration | Full d2b 3.0 reset; no v2 state/config import. | | Validation | Proof type: service round-trip test (mock); `tests/open_close.rs`, `tests/observe.rs`, and provider conformance tests. | @@ -888,9 +888,9 @@ expression. | --- | --- | | Dependency/owner | Title: Implement `LiveVsockEffectPort` in Zone runtime; Phase 2; Priority P0; Depends on ADR046-vsock-001 and the Zone allocator (`ADR-046-resources-zone-control`); Owner crate `d2b-core-controller`. | | Current source | Evidence class `ADR-only`; baseline has guest-control and relay vsock paths, but no allocator-backed `LiveVsockEffectPort` for ZoneLink transport. | -| Reuse action | net-new core adapter; keep raw AF_VSOCK syscalls outside Provider crate | +| Reuse action | adapt | | Destination | `d2b-core-controller` Zone runtime `LiveVsockEffectPort`; Provider receives it by dependency injection at startup. | -| Detailed design | Implement `LiveVsockEffectPort` in Zone runtime. Zone runtime provides `LiveVsockEffectPort` backed by core allocator state; resolves `OpaqueEndpointId` → CID and `OpaqueBindingId` → port; opens AF_VSOCK socket; injects into Provider service at startup; no raw CID/port exposed to Provider. | +| Detailed design | Implement `LiveVsockEffectPort` in Zone runtime. Zone runtime provides `LiveVsockEffectPort` backed by core allocator state; resolves `OpaqueEndpointId` → CID and `OpaqueBindingId` → port; opens AF_VSOCK socket; injects into Provider service at startup; no raw CID/port exposed to Provider. Primary reuse disposition: `adapt`. Preserved source-plan detail: net-new core adapter; keep raw AF_VSOCK syscalls outside Provider crate. | | Integration | Zone allocator issues endpoint/binding IDs; core runtime resolves them, opens/accepts AF_VSOCK sockets, returns opaque streams to Provider service, and excludes reserved ports. | | Data migration | Full d2b 3.0 reset; no v2 state/config import. | | Validation | Proof type: integration test; `integration/host_guest.rs` exercises live open/close byte round-trip with the injected effect. | @@ -901,9 +901,9 @@ expression. | --- | --- | | Dependency/owner | Title: Core ProviderDeployment creates/deletes service component state Volume; Phase 1; Priority P0; Depends on the volume-local Provider (`ADR-046-provider-volume-local`); Owner crate `d2b-provider-transport-vsock`. | | Current source | Evidence class `test-only-or-preview`; no operator-authored v3 state Volume exists for transport-vsock in baseline. | -| Reuse action | net-new ProviderDeployment/Volume integration and tests | +| Reuse action | create | | Destination | ProviderDeployment Volume creation/deletion path plus `packages/d2b-provider-transport-vsock/tests/state_volume.rs`. | -| Detailed design | Core ProviderDeployment creates/deletes service component state Volume. Core ProviderDeployment creates `Volume/transport-vsock--service--empty-state--*` before the component Process and deletes it after the Process finalizer; transport-vsock Provider controller does not own Volume, does not add Volume to exported ResourceTypes, and does not create its prerequisite; Volume spec: empty schema, `kind: state`, `persistenceClass: persistent`, `migrationPolicy: none`, `User/d2b-transport-vsock` owner, minimal nonzero `quota.maxBytes`/`quota.maxInodes` with `enforcement: hard`, `private` sensitivity, `broker-maintained` identity marker; `state_volume.rs` test verifies Volume spec fields against canonical schema; integration test verifies marker written at install and removed at Provider deletion; no operator-authored Volume; component receives dirfd view only. | +| Detailed design | Core ProviderDeployment creates/deletes service component state Volume. Core ProviderDeployment creates `Volume/transport-vsock--service--empty-state--*` before the component Process and deletes it after the Process finalizer; transport-vsock Provider controller does not own Volume, does not add Volume to exported ResourceTypes, and does not create its prerequisite; Volume spec: empty schema, `kind: state`, `persistenceClass: persistent`, `migrationPolicy: none`, `User/d2b-transport-vsock` owner, minimal nonzero `quota.maxBytes`/`quota.maxInodes` with `enforcement: hard`, `private` sensitivity, `broker-maintained` identity marker; `state_volume.rs` test verifies Volume spec fields against canonical schema; integration test verifies marker written at install and removed at Provider deletion; no operator-authored Volume; component receives dirfd view only. Primary reuse disposition: `create`. Preserved source-plan detail: net-new ProviderDeployment/Volume integration and tests. | | Integration | Core ProviderDeployment creates Volume before Process, volume-local reconciles it, Provider process receives only a dirfd view, and Provider deletion removes the Process before deleting the Volume/identity marker. | | Data migration | Full d2b 3.0 reset; no v2 state/config import; state Volume is created fresh with `migrationPolicy: none`. | | Validation | Proof type: unit + integration test; `tests/state_volume.rs` and Provider install/remove integration tests verify schema, user refs, marker lifecycle, and no ComponentPrincipal. | @@ -914,9 +914,9 @@ expression. | --- | --- | | Dependency/owner | Title: Integration test: real vsock socketpair + full ZoneLink open/close; Phase 2; Priority P1; Depends on ADR046-vsock-003 and ADR046-vsock-004; Owner crate `d2b-provider-transport-vsock`. | | Current source | Evidence class `test-only-or-preview`; existing guest-control compile proof and socat relay tests are not full ZoneLink transport coverage. | -| Reuse action | net-new integration coverage with no FD transfer over vsock | +| Reuse action | create | | Destination | `packages/d2b-provider-transport-vsock/integration/host_guest.rs` and `integration/no_fd_transfer.rs`. | -| Detailed design | Integration test: real vsock socketpair + full ZoneLink open/close. `integration/host_guest.rs`: real vsock socketpair (Linux); `OpenTransport` + byte round-trip + `CloseTransport`; validates bridge throughput ≥ 512 MiB/s; `no_fd_transfer.rs`: structural rejection of attachment packets over vsock transport. | +| Detailed design | Integration test: real vsock socketpair + full ZoneLink open/close. `integration/host_guest.rs`: real vsock socketpair (Linux); `OpenTransport` + byte round-trip + `CloseTransport`; validates bridge throughput ≥ 512 MiB/s; `no_fd_transfer.rs`: structural rejection of attachment packets over vsock transport. Primary reuse disposition: `create`. Preserved source-plan detail: net-new integration coverage with no FD transfer over vsock. | | Integration | Test drives Provider service, LiveVsockEffectPort, d2b-bus `OwnedTransport`, byte bridge, close path, and attachment rejection across the integration lane. | | Data migration | None — docs/tooling only; no runtime state. | | Validation | Proof type: integration test; `make test-integration` runs `host_guest.rs` and `no_fd_transfer.rs`. | @@ -927,9 +927,9 @@ expression. | --- | --- | | Dependency/owner | Title: Delete legacy socat OTLP relay and CONNECT-proxy guest-control vsock; Phase 3; Priority P2; Depends on the observability-otel Provider (`ADR-046-provider-observability-otel`) and the Guest resource lifecycle (`ADR-046-resources-host-guest-process-user`); Owner crates `d2b-host`, `d2bd`. | | Current source | Evidence class `implemented-and-reachable`; legacy sources are `packages/d2b-host/src/vsock_relay_argv.rs` socat OTLP relay and `packages/d2bd/src/guest_control_vsock.rs` CONNECT-proxy guest-control path. | -| Reuse action | delete after replacement parity; preserve reserved guest-control/OTLP port exclusions until replacements own them | +| Reuse action | delete-after-cutover | | Destination | Remove legacy paths from `d2b-host` and `d2bd`; replacement lives in `observability-otel` Provider native vsock relay and Guest resource lifecycle/bootstrap. | -| Detailed design | Delete legacy socat OTLP relay and CONNECT-proxy guest-control vsock. Remove `vsock_relay_argv.rs` socat path after `observability-otel` Provider native vsock relay passes parity; remove `guest_control_vsock.rs` CONNECT-proxy after Guest resource lifecycle + guestd vsock bootstrap reach parity; no raw CID or socat vsock path remains. | +| Detailed design | Delete legacy socat OTLP relay and CONNECT-proxy guest-control vsock. Remove `vsock_relay_argv.rs` socat path after `observability-otel` Provider native vsock relay passes parity; remove `guest_control_vsock.rs` CONNECT-proxy after Guest resource lifecycle + guestd vsock bootstrap reach parity; no raw CID or socat vsock path remains. Primary reuse disposition: `delete-after-cutover`. Preserved source-plan detail: delete after replacement parity; preserve reserved guest-control/OTLP port exclusions until replacements own them. | | Integration | Observability-otel owns OTLP vsock relay replacement; Guest lifecycle owns guest-control bootstrap replacement; transport-vsock ZoneLink allocator excludes ports 14317, 14318, and 14319. | | Data migration | Full d2b 3.0 reset; no v2 relay or guest-control state/config import. | | Validation | Proof type: deletion + parity test; parity tests for observability-otel and Guest lifecycle plus redaction checks that no raw CID/socat vsock path remains. | diff --git a/docs/specs/providers/ADR-046-provider-volume-local.md b/docs/specs/providers/ADR-046-provider-volume-local.md index 678a5f702..dadfa074e 100644 --- a/docs/specs/providers/ADR-046-provider-volume-local.md +++ b/docs/specs/providers/ADR-046-provider-volume-local.md @@ -2288,9 +2288,9 @@ Documents: | Dependency/owner | ADR046-primitives-001; v3 contracts owner | | Depends on | `ADR046-pstate-001` (VolumeStateSchema/PersistenceClass/SensitivityClass/StateEnvelope in `d2b-contracts/src/v3/volume_state.rs`) | | Current source | `d2b-core/src/storage.rs` (`StoragePathSpec`, `StoragePathKind`, policy enums); `d2b-core/src/sync.rs` (`SyncJson`, `LockSpec`) | -| Reuse action | extract and adapt | +| Reuse action | adapt | | Destination | `d2b-contracts/src/v3/volume_layout.rs` (LayoutEntry, EntryType, all policy enums, AclGrant, Invariant, SensitivityClass); `d2b-contracts/src/v3/volume_spec.rs` (VolumeSpec, ViewSpec, Attachment, QuotaSpec, SourceKind, `SourcePolicyId` opaque newtype); `d2b-contracts/src/v3/effect_port.rs` (`VolumeEffectPort` trait, opaque ID newtypes `VolumeId`/`LayoutEntryId`/`UserId`/`ViewId` each with custom redacted Debug, and `VolumeMountToken` opaque handle with custom redacted Debug) | -| Detailed design | All LayoutEntry fields as documented in this dossier; enum value names preserved from `StoragePathKind`/policy enums with renames where noted; `User/` ACL principal (no numeric UID); `sourcePolicyId` opaque newtype replaces raw `hostPath` in `SourceKind::LocalPath` and `SourceKind::BlockImage` | +| Detailed design | All LayoutEntry fields as documented in this dossier; enum value names preserved from `StoragePathKind`/policy enums with renames where noted; `User/` ACL principal (no numeric UID); `sourcePolicyId` opaque newtype replaces raw `hostPath` in `SourceKind::LocalPath` and `SourceKind::BlockImage` Primary reuse disposition: `adapt`. Preserved source-plan detail: extract and adapt. | | Integration | Volume spec and status structs; Provider descriptor component stateNamespace; Nix resource compiler schema validation | | Data migration | Full v3 reset; no row-level import | | Validation | Schema golden vectors; round-trip serde; ACL principal validation rejects numeric forms; `sourcePolicyId` present; no `hostPath` field in any volume_spec contract | @@ -2304,9 +2304,9 @@ Documents: | Dependency/owner | ADR046-vl-001; volume-local Provider owner | | Depends on | `ADR046-pstate-003` | | Current source | `d2b-state/src/{atomic,path,lock}.rs` (main `6faa5256`); `d2b-priv-broker/src/ops/swtpm_dir.rs` (marker algorithm) | -| Reuse action | copy-unchanged (`path.rs`); adapt (`atomic.rs`, `lock.rs`); adapt swtpm_dir marker algorithm | +| Reuse action | adapt | | Destination | Full `packages/d2b-provider-volume-local/` scaffold per §Crate layout: `src/`, `tests/`, `integration/`, `README.md`; crate `Cargo.toml` depends only on `d2b-contracts`, `d2b-provider`, `d2b-provider-toolkit` | -| Detailed design | `AnchoredDir`, `AnchoredResource`, `LeafName`, `RelativePath`; adapted `AtomicFilesystem`/`StateEnvelope`; adapted `LockGuard`/`LockSet`/`OfdTransfer`; marker write/verify/check; `src/effect_port.rs` re-exports `VolumeEffectPort` trait from `d2b-contracts::v3::effect_port` and provides Provider-side opaque ID construction helpers (no adapter implementation; adapter lives in host runtime); `sourcePolicyId` validation against declared policy list; no `openat2`/`setfacl`/`fallocate`/numeric-UID call sites in Provider crate | +| Detailed design | `AnchoredDir`, `AnchoredResource`, `LeafName`, `RelativePath`; adapted `AtomicFilesystem`/`StateEnvelope`; adapted `LockGuard`/`LockSet`/`OfdTransfer`; marker write/verify/check; `src/effect_port.rs` re-exports `VolumeEffectPort` trait from `d2b-contracts::v3::effect_port` and provides Provider-side opaque ID construction helpers (no adapter implementation; adapter lives in host runtime); `sourcePolicyId` validation against declared policy list; no `openat2`/`setfacl`/`fallocate`/numeric-UID call sites in Provider crate Primary reuse disposition: `adapt`. Preserved source-plan detail: copy-unchanged (`path.rs`); adapt (`atomic.rs`, `lock.rs`); adapt swtpm_dir marker algorithm. | | Integration | Controller binary receives `VolumeEffectPort` via ComponentSession injection; adapter calls `provision_marker` when a new Volume first appears in the `providerRef` reconcile queue (ProviderDeployment has already created the resource; volume-local provisions physical state) and `verify_marker` on restart relist | | Data migration | New marker written for each Volume at v3 first-boot | | Validation | All `tests/marker.rs`, `tests/state.rs` scenarios; all `integration/provision.rs` scenarios; `cargo deny check` verifies no `d2b-priv-broker`/`d2bd` dependency | @@ -2364,7 +2364,7 @@ Documents: | Work item ID | `ADR046-vl-006` | | Dependency/owner | ADR046-vl-003 | | Current source | No equivalent in baseline; new | -| Reuse action | new | +| Reuse action | create | | Destination | `src/source.rs` (block-image and tmpfs branches); `tests/source.rs`; `integration/block_image.rs` | | Detailed design | `block-image`: image file create/verify via `provision_block_image` effect op; `fallocate` performed by adapter when `preallocate: true`; FD transfer to Guest runtime via LaunchTicket via `open_volume_mount_token` effect op; `tmpfs`: `mount_tmpfs`/`umount_tmpfs` effect ops; `size=` and `nr_inodes=` derived from quota fields; cleanup via `umount_tmpfs` op | | Integration | Guest runtime Provider (cloud-hypervisor) receives block-image FD from volume-local via LaunchTicket; no path crosses the boundary | @@ -2394,7 +2394,7 @@ Documents: | Work item ID | `ADR046-vl-008` | | Dependency/owner | ADR046-vl-003; ADR046-vl-007 | | Current source | No equivalent; new | -| Reuse action | new | +| Reuse action | create | | Destination | `src/relocation.rs`; `tests/relocation_unit.rs`; `integration/relocation.rs` | | Detailed design | As documented in §Relocation, §Retention, §Incident hold, §Unclaimed Volume GC, §Destruction | | Integration | Controller adds `Relocating` finalizer; creates relocation EphemeralProcess; destruction sequence is ordered leaf-first with `fsync` after each step | @@ -2439,9 +2439,9 @@ Documents: | Work item ID | `ADR046-vl-011` | | Dependency/owner | ADR046-vl-002; ADR046-pstate-011; workspace policy owner | | Current source | `packages/xtask/src/main.rs` (`gen-schemas`, workspace-policy checks); `tests/unit/gates/drift-check.sh` | -| Reuse action | extend (per ADR046-pstate-011) | +| Reuse action | adapt | | Destination | `packages/xtask/src/provider_crate_policy.rs`; `tests/unit/gates/provider-crate-layout-check.sh` | -| Detailed design | `cargo xtask check-provider-crate-layout` gate asserts `src/`, `tests/`, `integration/` (with at least one `.rs` file and a `README.md`), and `README.md` for every `packages/d2b-provider-*` workspace member; fails closed with typed `missing-provider-crate-path` error | +| Detailed design | `cargo xtask check-provider-crate-layout` gate asserts `src/`, `tests/`, `integration/` (with at least one `.rs` file and a `README.md`), and `README.md` for every `packages/d2b-provider-*` workspace member; fails closed with typed `missing-provider-crate-path` error Primary reuse disposition: `adapt`. Preserved source-plan detail: extend (per ADR046-pstate-011). | | Integration | `make test-policy` runs the gate; GitHub CI runs `make test-policy` on every PR | | Data migration | Not applicable | | Validation | Gate detects each missing path; idempotent across re-runs; existing non-provider `d2b-*` crates not flagged | @@ -2455,9 +2455,9 @@ Documents: | Dependency/owner | ADR046-vl-001; ADR046-vl-002; Zone broker/core owner | | Depends on | `ADR046-pstate-003`; `ADR-046-provider-model-and-packaging` (generic effect-port injection contract) | | Current source | `d2b-priv-broker/src/ops/{state_dir,storage_contract,swtpm_dir,store_sync,store_view_posture}.rs`; `d2b-host/src/hardlink_farm.rs` | -| Reuse action | adapt into adapter | +| Reuse action | adapt | | Destination | `packages/d2b-host/src/volume_effect_adapter.rs` (or the equivalent host-runtime crate designated by the Zone broker owner); implements the `VolumeEffectPort` trait defined in `d2b-contracts` | -| Detailed design | Adapter holds trusted FD table keyed by `VolumeId`; resolves `SourcePolicyId` to host path prefix from private bundle; calls `openat2(RESOLVE_BENEATH)` anchored at retained FD for all FS ops; calls `setfacl`/`acl_set_fd`, `mount`/`umount`, `fallocate` from within adapter only; emits path-free audit records for each op (audit is never atomic with redb write); injected into controller via Zone runtime ComponentSession; blocking filesystem calls run in bounded blocking-thread pool | +| Detailed design | Adapter holds trusted FD table keyed by `VolumeId`; resolves `SourcePolicyId` to host path prefix from private bundle; calls `openat2(RESOLVE_BENEATH)` anchored at retained FD for all FS ops; calls `setfacl`/`acl_set_fd`, `mount`/`umount`, `fallocate` from within adapter only; emits path-free audit records for each op (audit is never atomic with redb write); injected into controller via Zone runtime ComponentSession; blocking filesystem calls run in bounded blocking-thread pool Primary reuse disposition: `adapt`. Preserved source-plan detail: adapt into adapter. | | Integration | Zone runtime creates adapter with required FD table and bundle reference at provider startup; passes `Arc` to controller via ComponentSession bootstrap | | Data migration | None (adapter replaces direct broker-op call sites) | | Validation | Adapter hermetic tests: each effect op called with mock FD table and bundle; no path in any output; anchored-path rejection for RESOLVE_BENEATH violations; `cargo deny check` verifies adapter does not expose raw paths to Provider crate; `integration/provision.rs` exercises full adapter path | @@ -2471,7 +2471,7 @@ Documents: | Dependency/owner | ADR046-vl-001; ADR046-vl-012; Zone broker/core owner | | Depends on | `ADR-046-provider-model-and-packaging` (Provider install sequencing) | | Current source | No equivalent; new | -| Reuse action | new | +| Reuse action | create | | Destination | Zone core ProviderDeployment controller-start path (outside `d2b-provider-volume-local`) | | Detailed design | The volume-local controller declares no Provider state Volume, so there is no bootstrap Volume, no `BootstrapProviderStateVolume` broker op, no pre-provisioned controller Volume, and no bootstrap-storage exception (D086, superseded by D087). On first install and on every daemon restart, core ProviderDeployment starts the volume-local controller Process directly; the controller reaches `Ready` from its own resource `status`, the core Operation ledger, and a resource-store relist. Once Ready, it reconciles every Volume carrying `providerRef: Provider/volume-local` (operator-created Volumes and other Providers' declared state Volumes) as they appear in its `providerRef` watch, re-verifying identity markers against external reality, never creating them itself. A Guest bootstraps its own Guest-local volume-local instance from Guest-local primitives only. | | Integration | Core ProviderDeployment spawns the controller Process with no state-Volume prerequisite; the controller's startup relist reconciles served Volumes and re-verifies markers | diff --git a/docs/specs/providers/ADR-046-provider-volume-virtiofs.md b/docs/specs/providers/ADR-046-provider-volume-virtiofs.md index ab5fa0406..4622d2dcd 100644 --- a/docs/specs/providers/ADR-046-provider-volume-virtiofs.md +++ b/docs/specs/providers/ADR-046-provider-volume-virtiofs.md @@ -1352,23 +1352,23 @@ it does not import session implementation internals directly. | Field | Value | | --- | --- | -| Dependency/owner | ADR046-volume-001 (Volume contract types); ADR046-vvfs-export-001 (Export type); W1; volume-virtiofs Provider owner | +| Dependency/owner | ADR046-volume-001 (Volume contract types); ADR046-vvfs-007 (Export type); W1; volume-virtiofs Provider owner | | Current source | `packages/d2b-host/src/virtiofsd_argv.rs` (VirtiofsdArgvInput, generate_virtiofsd_argv, 14 unit tests, golden argv.txt); `packages/d2b-host/src/lib.rs` (module declaration) | -| Reuse action | extract and adapt | +| Reuse action | adapt | | Destination | `packages/d2b-provider-volume-virtiofs/src/virtiofsd_argv.rs`; `packages/d2b-provider-volume-virtiofs/tests/argv_golden.rs` | -| Detailed design | Create crate skeleton with mandatory `src/`, `tests/`, `integration/`, `README.md`. Extract `VirtiofsdArgvInput` and `generate_virtiofsd_argv` with these changes: (1) replace `extra_args: Vec` with nothing (removed); (2) replace `socket_path: String` with `socket_path: SocketPath` newtype backed by `socket_path.rs`; (3) add `shared_dir_fd: i32` replacing `shared_dir: String` (FD-based); (4) replace `socket_group: Option` with `socket_group: Option`. Implement `socket_path.rs`: private path using SHA-256 of `\x00\x00`, truncated 8 hex chars, formatted as `/vms//vol-.vfd.sock`. Assert path length ≤ 108 bytes. | +| Detailed design | Create crate skeleton with mandatory `src/`, `tests/`, `integration/`, `README.md`. Extract `VirtiofsdArgvInput` and `generate_virtiofsd_argv` with these changes: (1) replace `extra_args: Vec` with nothing (removed); (2) replace `socket_path: String` with `socket_path: SocketPath` newtype backed by `socket_path.rs`; (3) add `shared_dir_fd: i32` replacing `shared_dir: String` (FD-based); (4) replace `socket_group: Option` with `socket_group: Option`. Implement `socket_path.rs`: private path using SHA-256 of `\x00\x00`, truncated 8 hex chars, formatted as `/vms//vol-.vfd.sock`. Assert path length ≤ 108 bytes. Primary reuse disposition: `adapt`. Preserved source-plan detail: extract and adapt. | | Integration | volume-virtiofs controller `export.rs` calls virtiofsd_argv.rs at spawn time; LaunchTicket carries resolved socket path as opaque sealed field | | Data migration | v3.0 reset; socket path format changes | | Validation | `tests/argv_golden.rs`: 14 migrated tests + `no_extra_args_ever_emitted`, `socket_path_is_not_in_args`, `shared_dir_is_fd_path`, `path_length_within_sunpath_limit`; `tests/socket_path_privacy.rs`: `socket_path_not_in_export_status`, `socket_path_not_in_volume_status`, `socket_path_not_in_audit_record`; `tests/schema_conformance.rs`: `process_spec_readiness_class_is_provider_defined`, `process_spec_readiness_has_no_kind_or_period_fields`, `process_spec_budget_cpu_request_limit_nested`, `process_spec_budget_memory_request_limit_nested`, `process_spec_budget_pids_limit_present`, `process_spec_budget_fds_limit_present`, `process_spec_sandbox_no_new_privileges_true`, `process_spec_sandbox_read_only_root_true`, `process_spec_no_host_uid_gid_in_spec` | | Removal proof | `packages/d2b-host/src/virtiofsd_argv.rs` removed only after parity confirmed by argv-shape gate | -### ADR046-vvfs-export-001 — Export ResourceType declaration +### ADR046-vvfs-007 — Export ResourceType declaration | Field | Value | | --- | --- | | Dependency/owner | ADR046-volume-001; W1; volume-virtiofs Provider owner | | Current source | No analog; new ResourceType | -| Reuse action | new | +| Reuse action | create | | Destination | `packages/d2b-provider-volume-virtiofs/src/export.rs`; `packages/d2b-contracts/src/v3/virtiofs_export.rs` | | Detailed design | Declare `virtiofs.d2bus.org.Export` ResourceType in `d2b-contracts`. Base fields: `providerRef`, `volumeRef`, `executionRef`, `view`, `access`, `mountPath`; virtiofs tunables live under `spec.provider.settings` (as in §4.2). Status fields: top-level `phase`/`conditions`, `status.resource.exportReady`, `status.resource.guestMountReady`, and `status.provider.details.workerProcessRef`. Strict serde `deny_unknown_fields`. Implement the conformance test fixture that validates schema fingerprint stability. The Export spec JSON schema and provider status extension schema are signed and included in the Provider package. | | Integration | `d2b-contracts` exports the Export DTO; volume-virtiofs controller and volume-local both import it for ResourceClient typed operations | @@ -1382,9 +1382,9 @@ it does not import session implementation internals directly. | --- | --- | | Dependency/owner | ADR046-vvfs-001; ADR046-volume-001; W1; broker/spawn owner | | Current source | `packages/d2b-priv-broker/src/sys.rs` (`clone3_spawn_runner`, user-NS pre-establishment block); `packages/d2b-priv-broker/src/ops/spawn_runner.rs` (`SpawnRunnerPlanInput.user_namespace`, `RunnerIsolationSpec.user_namespace`); ADR 0021 implementation contract | -| Reuse action | extract conformance kit only; pre-establishment code stays in broker | +| Reuse action | extract | | Destination | `packages/d2b-provider-volume-virtiofs/src/user_ns.rs` (conformance kit); `packages/d2b-provider-volume-virtiofs/tests/adr021_invariant.rs` | -| Detailed design | `user_ns.rs` contains only the conformance check and template descriptor assertion: verify that the virtiofsd-worker Process template declares `capabilityClasses: []`, `startRoot: false`, `noNewPrivileges: true`, `readOnlyRoot: true`, and `sandbox.userNamespace.mappingClass: process-principal-root`. `hostUid`/`hostGid` are NOT set by the controller — system-minijail resolves the mapping from the `User/vol--vfd` principal when building the LaunchTicket via the effect port. The conformance check rejects any template mutation that adds host capability classes, sets `startRoot: true`, disables `noNewPrivileges`, or disables `readOnlyRoot`. The user-NS pre-establishment code itself remains in `d2b-priv-broker/src/sys.rs` and is invoked via the system-minijail effect port. | +| Detailed design | `user_ns.rs` contains only the conformance check and template descriptor assertion: verify that the virtiofsd-worker Process template declares `capabilityClasses: []`, `startRoot: false`, `noNewPrivileges: true`, `readOnlyRoot: true`, and `sandbox.userNamespace.mappingClass: process-principal-root`. `hostUid`/`hostGid` are NOT set by the controller — system-minijail resolves the mapping from the `User/vol--vfd` principal when building the LaunchTicket via the effect port. The conformance check rejects any template mutation that adds host capability classes, sets `startRoot: true`, disables `noNewPrivileges`, or disables `readOnlyRoot`. The user-NS pre-establishment code itself remains in `d2b-priv-broker/src/sys.rs` and is invoked via the system-minijail effect port. Primary reuse disposition: `extract`. Preserved source-plan detail: extract conformance kit only; pre-establishment code stays in broker. | | Integration | volume-virtiofs controller calls conformance check before emitting any Process Create; effect port adapter in system-minijail invokes broker spawn path | | Data migration | v3.0 reset; current `adr_carve_out` field in `SpawnRunnerPlanInput` removed; ADR 0021 path is now the default | | Validation | `tests/adr021_invariant.rs`: `virtiofsd_capability_classes_must_be_empty`, `virtiofsd_start_root_must_be_false`, `virtiofsd_no_new_privileges_must_be_true`, `virtiofsd_read_only_root_must_be_true`, `process_spec_has_no_host_uid_gid`, `sandbox_namespace_never_emitted`, `user_ns_single_entry_single_uid_mapping`, `uid_map_write_ordering_uid_setgroups_gid`, `child_setuid_in_ns_not_host_uid`, `clone_newns_not_in_clone3_flags`, `child_exits_user_ns_sync_on_pipe_eof` | @@ -1394,11 +1394,11 @@ it does not import session implementation internals directly. | Field | Value | | --- | --- | -| Dependency/owner | ADR046-vvfs-001, ADR046-vvfs-002, ADR046-vvfs-export-001; ADR046-volume-001; W2; volume-virtiofs controller owner | +| Dependency/owner | ADR046-vvfs-001, ADR046-vvfs-002, ADR046-vvfs-007; ADR046-volume-001; W2; volume-virtiofs controller owner | | Current source | `packages/d2bd/src/supervisor/dag.rs` (ProcessRole::Virtiofsd dag node); `nixos-modules/processes-json.nix` (virtiofsdRunner block; attachment-to-Process mapping) | -| Reuse action | extract and adapt | +| Reuse action | adapt | | Destination | `packages/d2b-provider-volume-virtiofs/src/controller.rs`; `packages/d2b-provider-volume-virtiofs/src/export.rs` | -| Detailed design | Implement volume-virtiofs-controller reconcile loop using toolkit ResourceClient. Watch selector: `virtiofs.d2bus.org.Export` resources (all in zone), owned Process resources, owned User resources, Volume resources (read-only for view/vcpu resolution), Guest resources (read-only for vcpu count). On `spec-generation-changed` for an Export: (1) resolve View from Volume; (2) check store-view marker if applicable; (3) resolve threadPoolSize from Guest vcpus; (4) ensure User/vol--vfd; (5) diff against current Process; (6) emit Create/UpdateSpec. On `owned-resource-changed` for a Process: update Export status. On `deletionRequestedAt` for Export: two-phase teardown (§6.2). | +| Detailed design | Implement volume-virtiofs-controller reconcile loop using toolkit ResourceClient. Watch selector: `virtiofs.d2bus.org.Export` resources (all in zone), owned Process resources, owned User resources, Volume resources (read-only for view/vcpu resolution), Guest resources (read-only for vcpu count). On `spec-generation-changed` for an Export: (1) resolve View from Volume; (2) check store-view marker if applicable; (3) resolve threadPoolSize from Guest vcpus; (4) ensure User/vol--vfd; (5) diff against current Process; (6) emit Create/UpdateSpec. On `owned-resource-changed` for a Process: update Export status. On `deletionRequestedAt` for Export: two-phase teardown (§6.2). Primary reuse disposition: `adapt`. Preserved source-plan detail: extract and adapt. | | Integration | volume-virtiofs controller registered by core ProviderDeployment; receives owned-resource-changed trigger from Export; emits Process resources consumed by system-minijail | | Data migration | Current `ProcessRole::Virtiofsd` dag nodes replaced by Export → Process resource lifecycle | | Validation | `tests/export_lifecycle.rs`: `export_create_spawns_virtiofsd_process`, `export_ready_when_socket_present`, `export_delete_terminates_virtiofsd`, `export_delete_waits_for_guest_mount_absent`, `export_delete_with_guest_unreachable_holds_finalizer_degraded`, `export_proof_of_ns_death_clears_finalizer`; `tests/multi_attachment.rs`: `two_guests_get_separate_exports_and_processes`, `process_failure_does_not_affect_sibling_export`; `tests/schema_conformance.rs`: `provider_state_set_volume_created_on_install`, `provider_state_set_volume_owner_ref_is_provider`, `provider_state_set_volume_layout_principal_is_user_not_component_principal`, `provider_state_set_no_cross_component_volume_sharing` | @@ -1410,9 +1410,9 @@ it does not import session implementation internals directly. | --- | --- | | Dependency/owner | ADR046-vvfs-003; guest-control integration owner; W2 | | Current source | `packages/d2bd/src/vm_readiness.rs` (`ReadinessKind::UnixSocketExists`); guest-control vsock health protocol | -| Reuse action | extract and adapt | +| Reuse action | adapt | | Destination | `packages/d2b-provider-volume-virtiofs/src/readiness.rs`; `packages/d2b-provider-volume-virtiofs/integration/guest_mount_readiness/` | -| Detailed design | `unix-socket-exists` readiness: check file existence at the private socket path via a bounded blocking adapter (e.g., `tokio::task::spawn_blocking` wrapping `fstatat(2)` relative to the zone runtime `OwnedFd`, or an async-safe fd-relative equivalent); no blocking syscall on the async executor thread. Probe period 1 s; timeout 30 s. On socket present → set `Export.status.exportReady: true`. Guest-mount readiness: send `VirtioFsMountReady?` probe to guest-control health endpoint over vsock. Response `MountReady` sets `guestMountReady: true`. Response `MountAbsent` or timeout sets `guestMountReady: false`. The vsock health probe is async-native. If Guest is down, set Export `phase: Unknown`. All readiness probes (unix-socket-exists, guest-mount health) use bounded blocking adapters or async-safe fd-relative equivalents; no blocking I/O on the reconcile executor thread. | +| Detailed design | `unix-socket-exists` readiness: check file existence at the private socket path via a bounded blocking adapter (e.g., `tokio::task::spawn_blocking` wrapping `fstatat(2)` relative to the zone runtime `OwnedFd`, or an async-safe fd-relative equivalent); no blocking syscall on the async executor thread. Probe period 1 s; timeout 30 s. On socket present → set `Export.status.exportReady: true`. Guest-mount readiness: send `VirtioFsMountReady?` probe to guest-control health endpoint over vsock. Response `MountReady` sets `guestMountReady: true`. Response `MountAbsent` or timeout sets `guestMountReady: false`. The vsock health probe is async-native. If Guest is down, set Export `phase: Unknown`. All readiness probes (unix-socket-exists, guest-mount health) use bounded blocking adapters or async-safe fd-relative equivalents; no blocking I/O on the reconcile executor thread. Primary reuse disposition: `adapt`. Preserved source-plan detail: extract and adapt. | | Integration | `readiness.rs` called from `controller.rs` reconcile loop; uses toolkit health probe client | | Data migration | Current `UnixSocketExists` readiness kind adapted to FD-based path resolution | | Validation | `tests/export_lifecycle.rs` (extended); `integration/guest_mount_readiness/`: virtiofsd launches, socket appears, guest-control probe returns MountReady, guestMountReady flips to true; probe returns MountAbsent on umount | From 985348f7c0d6dcbb484ff765b894bae4e05b0922 Mon Sep 17 00:00:00 2001 From: John Vicondoa Date: Fri, 24 Jul 2026 03:36:31 -0700 Subject: [PATCH 084/115] docs: register ADR 0046 multi-prefix ownership Replace the singular inferred prefix contract with sorted per-member registries and restore accepted multi-prefix IDs. Keep only malformed IDs and cross-member bus/network conflicts normalized, then regenerate the manifest and dependency graph with fail-closed ownership checks. --- .../specs/ADR-046-componentsession-and-bus.md | 2 +- docs/specs/ADR-046-feasibility-and-spikes.md | 6 +- docs/specs/ADR-046-implementation-graph.json | 1857 +++++++++-------- docs/specs/ADR-046-implementation-graph.md | 6 +- docs/specs/ADR-046-reset-and-cutover.md | 2 +- docs/specs/ADR-046-resources-credential.md | 2 +- ...R-046-resources-host-guest-process-user.md | 4 +- docs/specs/ADR-046-resources-zone-control.md | 20 +- .../ADR-046-security-and-threat-model.md | 16 +- docs/specs/ADR-046-spec-set.json | 264 ++- .../ADR-046-telemetry-audit-and-support.md | 96 +- docs/specs/ADR-046-validation-and-delivery.md | 6 +- docs/specs/ADR-046-work-items.json | 820 ++++---- docs/specs/README.md | 51 +- ...46-provider-credential-managed-identity.md | 12 +- .../ADR-046-provider-notification-desktop.md | 2 +- .../providers/ADR-046-provider-system-core.md | 8 +- .../ADR-046-provider-system-minijail.md | 4 +- .../ADR-046-provider-transport-unix.md | 4 +- .../ADR-046-provider-transport-vsock.md | 8 +- .../ADR-046-provider-volume-virtiofs.md | 6 +- 21 files changed, 1668 insertions(+), 1528 deletions(-) diff --git a/docs/specs/ADR-046-componentsession-and-bus.md b/docs/specs/ADR-046-componentsession-and-bus.md index 73cf888fb..de79028c4 100644 --- a/docs/specs/ADR-046-componentsession-and-bus.md +++ b/docs/specs/ADR-046-componentsession-and-bus.md @@ -390,7 +390,7 @@ transcript/session generation digest, route, and fixed outcome. | Validation | Copied fd/peer/credit tests plus Host/Guest subject mapping | | Removal proof | Ad hoc guest/public/helper transport removed only per service cutover | -### ADR046-session-003 +### ADR046-bus-001 | Field | Value | | --- | --- | diff --git a/docs/specs/ADR-046-feasibility-and-spikes.md b/docs/specs/ADR-046-feasibility-and-spikes.md index 6bfeed5ac..f4de829d9 100644 --- a/docs/specs/ADR-046-feasibility-and-spikes.md +++ b/docs/specs/ADR-046-feasibility-and-spikes.md @@ -381,9 +381,9 @@ performed by this documentation-only spec. | Metrics | (1) every one of the 500 calls is delivered to exactly the recipient named by its route key, verified by a per-recipient received-call counter; (2) each recipient's session transcript hash and record sequence counter are independent (no shared state, verified by asserting the three sessions' internal sequence counters never reference each other's session object); (3) the deliberate cross-wiring case is rejected before any record is exchanged. | | Pass/fail threshold | 500/500 correct routing, 0 cross-recipient session-state leakage, 1/1 cross-wiring case rejected with a stable typed error (not a panic, not a silent no-op). | | Expected resource budget | ≤2 minutes wall time; ≤64 MiB RSS (three lightweight Noise sessions plus the fake router). | -| Failure interpretation | A misrouted call or shared-session-state finding blocks `ADR046-session-003` from starting until the route-key resolution or per-session isolation in `ADR-046-componentsession-and-bus` §"d2b-bus" is corrected; per anti-claim rule 3, the fix is never "widen the route key to fail open." | -| Affected decisions/work items | D011, D039, D040, D054; `ADR046-session-001`, `ADR046-session-002`, `ADR046-session-003`. | -| Cleanup | Deleted once `packages/d2b-bus/src/router.rs` (the real `ADR046-session-003` destination) carries an in-tree message-isolation/route-authorization/no-direct-store-path conformance test with equal or stricter coverage, per that work item's own Validation column. | +| Failure interpretation | A misrouted call or shared-session-state finding blocks `ADR046-bus-001` from starting until the route-key resolution or per-session isolation in `ADR-046-componentsession-and-bus` §"d2b-bus" is corrected; per anti-claim rule 3, the fix is never "widen the route key to fail open." | +| Affected decisions/work items | D011, D039, D040, D054; `ADR046-session-001`, `ADR046-session-002`, `ADR046-bus-001`. | +| Cleanup | Deleted once `packages/d2b-bus/src/router.rs` (the real `ADR046-bus-001` destination) carries an in-tree message-isolation/route-authorization/no-direct-store-path conformance test with equal or stricter coverage, per that work item's own Validation column. | | Status | Specified — not yet executed. | ### SPIKE-07 — Unix/vsock/Azure-Relay transports carry only opaque Noise record bytes diff --git a/docs/specs/ADR-046-implementation-graph.json b/docs/specs/ADR-046-implementation-graph.json index b4d8a7636..8ed6987a9 100644 --- a/docs/specs/ADR-046-implementation-graph.json +++ b/docs/specs/ADR-046-implementation-graph.json @@ -222,6 +222,26 @@ "to" : "ADR-046-provider-audio-pipewire", "type" : "implements-spec" }, + { + "from" : "ADR046-audit-001", + "to" : "ADR-046-telemetry-audit-and-support", + "type" : "implements-spec" + }, + { + "from" : "ADR046-audit-002", + "to" : "ADR-046-telemetry-audit-and-support", + "type" : "implements-spec" + }, + { + "from" : "ADR046-audit-003", + "to" : "ADR-046-telemetry-audit-and-support", + "type" : "implements-spec" + }, + { + "from" : "ADR046-audit-004", + "to" : "ADR-046-telemetry-audit-and-support", + "type" : "implements-spec" + }, { "from" : "ADR046-azure-vm-001", "to" : "ADR-046-provider-runtime-azure-virtual-machine", @@ -267,6 +287,11 @@ "to" : "ADR-046-provider-runtime-azure-virtual-machine", "type" : "implements-spec" }, + { + "from" : "ADR046-bus-001", + "to" : "ADR-046-componentsession-and-bus", + "type" : "implements-spec" + }, { "from" : "ADR046-ch-001", "to" : "ADR-046-provider-runtime-cloud-hypervisor", @@ -367,6 +392,11 @@ "to" : "ADR-046-cli-and-operations", "type" : "implements-spec" }, + { + "from" : "ADR046-client-001", + "to" : "ADR-046-resources-zone-control", + "type" : "implements-spec" + }, { "from" : "ADR046-clipboard-001", "to" : "ADR-046-provider-clipboard-wayland", @@ -462,11 +492,6 @@ "to" : "ADR-046-provider-credential-managed-identity", "type" : "implements-spec" }, - { - "from" : "ADR046-cred-mi-005", - "to" : "ADR-046-provider-credential-managed-identity", - "type" : "implements-spec" - }, { "from" : "ADR046-cred-ss-001", "to" : "ADR-046-provider-credential-secret-service", @@ -712,6 +737,16 @@ "to" : "ADR-046-provider-display-wayland", "type" : "implements-spec" }, + { + "from" : "ADR046-doctor-001", + "to" : "ADR-046-telemetry-audit-and-support", + "type" : "implements-spec" + }, + { + "from" : "ADR046-doctor-002", + "to" : "ADR-046-telemetry-audit-and-support", + "type" : "implements-spec" + }, { "from" : "ADR046-exec-001", "to" : "ADR-046-resources-host-guest-process-user", @@ -827,11 +862,6 @@ "to" : "ADR-046-resources-host-guest-process-user", "type" : "implements-spec" }, - { - "from" : "ADR046-exec-024", - "to" : "ADR-046-resources-host-guest-process-user", - "type" : "implements-spec" - }, { "from" : "ADR046-feasibility-001", "to" : "ADR-046-feasibility-and-spikes", @@ -932,6 +962,11 @@ "to" : "ADR-046-provider-device-gpu", "type" : "implements-spec" }, + { + "from" : "ADR046-host-posture-001", + "to" : "ADR-046-telemetry-audit-and-support", + "type" : "implements-spec" + }, { "from" : "ADR046-identities-001", "to" : "ADR-046-terminology-and-identities", @@ -942,6 +977,11 @@ "to" : "ADR-046-terminology-and-identities", "type" : "implements-spec" }, + { + "from" : "ADR046-mi-topology-001", + "to" : "ADR-046-provider-credential-managed-identity", + "type" : "implements-spec" + }, { "from" : "ADR046-minijail-001", "to" : "ADR-046-provider-system-minijail", @@ -1362,6 +1402,11 @@ "to" : "ADR-046-provider-observability-otel", "type" : "implements-spec" }, + { + "from" : "ADR046-pkg-001", + "to" : "ADR-046-resources-zone-control", + "type" : "implements-spec" + }, { "from" : "ADR046-primitives-001", "to" : "ADR-046-primitive-resource-composition", @@ -1407,6 +1452,11 @@ "to" : "ADR-046-provider-model-and-packaging", "type" : "implements-spec" }, + { + "from" : "ADR046-provider-agent-001", + "to" : "ADR-046-resources-zone-control", + "type" : "implements-spec" + }, { "from" : "ADR046-pstate-001", "to" : "ADR-046-provider-state", @@ -1632,6 +1682,51 @@ "to" : "ADR-046-reset-and-cutover", "type" : "implements-spec" }, + { + "from" : "ADR046-reuse-001", + "to" : "ADR-046-telemetry-audit-and-support", + "type" : "implements-spec" + }, + { + "from" : "ADR046-reuse-002", + "to" : "ADR-046-telemetry-audit-and-support", + "type" : "implements-spec" + }, + { + "from" : "ADR046-reuse-003", + "to" : "ADR-046-telemetry-audit-and-support", + "type" : "implements-spec" + }, + { + "from" : "ADR046-reuse-004", + "to" : "ADR-046-telemetry-audit-and-support", + "type" : "implements-spec" + }, + { + "from" : "ADR046-reuse-005", + "to" : "ADR-046-telemetry-audit-and-support", + "type" : "implements-spec" + }, + { + "from" : "ADR046-reuse-006", + "to" : "ADR-046-telemetry-audit-and-support", + "type" : "implements-spec" + }, + { + "from" : "ADR046-reuse-007", + "to" : "ADR-046-telemetry-audit-and-support", + "type" : "implements-spec" + }, + { + "from" : "ADR046-reuse-008", + "to" : "ADR-046-telemetry-audit-and-support", + "type" : "implements-spec" + }, + { + "from" : "ADR046-reuse-009", + "to" : "ADR-046-telemetry-audit-and-support", + "type" : "implements-spec" + }, { "from" : "ADR046-routing-001", "to" : "ADR-046-zone-routing", @@ -1992,11 +2087,6 @@ "to" : "ADR-046-componentsession-and-bus", "type" : "implements-spec" }, - { - "from" : "ADR046-session-003", - "to" : "ADR-046-componentsession-and-bus", - "type" : "implements-spec" - }, { "from" : "ADR046-sterm-001", "to" : "ADR-046-provider-shell-terminal", @@ -2272,86 +2362,6 @@ "to" : "ADR-046-telemetry-audit-and-support", "type" : "implements-spec" }, - { - "from" : "ADR046-telem-012", - "to" : "ADR-046-telemetry-audit-and-support", - "type" : "implements-spec" - }, - { - "from" : "ADR046-telem-013", - "to" : "ADR-046-telemetry-audit-and-support", - "type" : "implements-spec" - }, - { - "from" : "ADR046-telem-014", - "to" : "ADR-046-telemetry-audit-and-support", - "type" : "implements-spec" - }, - { - "from" : "ADR046-telem-015", - "to" : "ADR-046-telemetry-audit-and-support", - "type" : "implements-spec" - }, - { - "from" : "ADR046-telem-016", - "to" : "ADR-046-telemetry-audit-and-support", - "type" : "implements-spec" - }, - { - "from" : "ADR046-telem-017", - "to" : "ADR-046-telemetry-audit-and-support", - "type" : "implements-spec" - }, - { - "from" : "ADR046-telem-018", - "to" : "ADR-046-telemetry-audit-and-support", - "type" : "implements-spec" - }, - { - "from" : "ADR046-telem-019", - "to" : "ADR-046-telemetry-audit-and-support", - "type" : "implements-spec" - }, - { - "from" : "ADR046-telem-020", - "to" : "ADR-046-telemetry-audit-and-support", - "type" : "implements-spec" - }, - { - "from" : "ADR046-telem-021", - "to" : "ADR-046-telemetry-audit-and-support", - "type" : "implements-spec" - }, - { - "from" : "ADR046-telem-022", - "to" : "ADR-046-telemetry-audit-and-support", - "type" : "implements-spec" - }, - { - "from" : "ADR046-telem-023", - "to" : "ADR-046-telemetry-audit-and-support", - "type" : "implements-spec" - }, - { - "from" : "ADR046-telem-024", - "to" : "ADR-046-telemetry-audit-and-support", - "type" : "implements-spec" - }, - { - "from" : "ADR046-telem-025", - "to" : "ADR-046-telemetry-audit-and-support", - "type" : "implements-spec" - }, - { - "from" : "ADR046-telem-026", - "to" : "ADR-046-telemetry-audit-and-support", - "type" : "implements-spec" - }, - { - "from" : "ADR046-telem-027", - "to" : "ADR-046-telemetry-audit-and-support", - "type" : "implements-spec" - }, { "from" : "ADR046-transport-relay-001", "to" : "ADR-046-provider-transport-azure-relay", @@ -2487,6 +2497,11 @@ "to" : "ADR-046-provider-device-usbip", "type" : "implements-spec" }, + { + "from" : "ADR046-user-session-001", + "to" : "ADR-046-resources-host-guest-process-user", + "type" : "implements-spec" + }, { "from" : "ADR046-vl-001", "to" : "ADR-046-provider-volume-local", @@ -2648,10 +2663,15 @@ "type" : "implements-spec" }, { - "from" : "ADR046-vvfs-007", + "from" : "ADR046-vvfs-export-001", "to" : "ADR-046-provider-volume-virtiofs", "type" : "implements-spec" }, + { + "from" : "ADR046-wire-001", + "to" : "ADR-046-resources-zone-control", + "type" : "implements-spec" + }, { "from" : "ADR046-zone-control-001", "to" : "ADR-046-resources-zone-control", @@ -2772,26 +2792,6 @@ "to" : "ADR-046-resources-zone-control", "type" : "implements-spec" }, - { - "from" : "ADR046-zone-control-025", - "to" : "ADR-046-resources-zone-control", - "type" : "implements-spec" - }, - { - "from" : "ADR046-zone-control-026", - "to" : "ADR-046-resources-zone-control", - "type" : "implements-spec" - }, - { - "from" : "ADR046-zone-control-027", - "to" : "ADR-046-resources-zone-control", - "type" : "implements-spec" - }, - { - "from" : "ADR046-zone-control-028", - "to" : "ADR-046-resources-zone-control", - "type" : "implements-spec" - }, { "from" : "ADR-046-resource-api-and-authorization", "to" : "ADR-046-resource-object-model", @@ -5427,6 +5427,36 @@ "to" : "ADR046-zone-control-019", "type" : "work-item-depends-on" }, + { + "from" : "ADR046-audit-002", + "to" : "ADR046-audit-001", + "type" : "work-item-depends-on" + }, + { + "from" : "ADR046-audit-002", + "to" : "ADR046-store-001", + "type" : "work-item-depends-on" + }, + { + "from" : "ADR046-audit-003", + "to" : "ADR046-audit-001", + "type" : "work-item-depends-on" + }, + { + "from" : "ADR046-audit-003", + "to" : "ADR046-bus-001", + "type" : "work-item-depends-on" + }, + { + "from" : "ADR046-audit-003", + "to" : "ADR046-session-001", + "type" : "work-item-depends-on" + }, + { + "from" : "ADR046-audit-004", + "to" : "ADR046-audit-001", + "type" : "work-item-depends-on" + }, { "from" : "ADR046-azure-vm-002", "to" : "ADR046-azure-vm-001", @@ -5627,6 +5657,26 @@ "to" : "ADR046-zone-control-019", "type" : "work-item-depends-on" }, + { + "from" : "ADR046-client-001", + "to" : "ADR046-zone-control-011", + "type" : "work-item-depends-on" + }, + { + "from" : "ADR046-client-001", + "to" : "ADR046-zone-control-012", + "type" : "work-item-depends-on" + }, + { + "from" : "ADR046-client-001", + "to" : "ADR046-zone-control-013", + "type" : "work-item-depends-on" + }, + { + "from" : "ADR046-client-001", + "to" : "ADR046-zone-control-018", + "type" : "work-item-depends-on" + }, { "from" : "ADR046-clipboard-002", "to" : "ADR046-clipboard-001", @@ -5734,17 +5784,17 @@ }, { "from" : "ADR046-cred-mi-001", - "to" : "ADR046-cred-mi-005", + "to" : "ADR046-credential-001", "type" : "work-item-depends-on" }, { "from" : "ADR046-cred-mi-001", - "to" : "ADR046-credential-001", + "to" : "ADR046-credential-002", "type" : "work-item-depends-on" }, { "from" : "ADR046-cred-mi-001", - "to" : "ADR046-credential-002", + "to" : "ADR046-mi-topology-001", "type" : "work-item-depends-on" }, { @@ -5754,17 +5804,17 @@ }, { "from" : "ADR046-cred-mi-002", - "to" : "ADR046-cred-mi-005", + "to" : "ADR046-credential-001", "type" : "work-item-depends-on" }, { "from" : "ADR046-cred-mi-002", - "to" : "ADR046-credential-001", + "to" : "ADR046-credential-002", "type" : "work-item-depends-on" }, { "from" : "ADR046-cred-mi-002", - "to" : "ADR046-credential-002", + "to" : "ADR046-mi-topology-001", "type" : "work-item-depends-on" }, { @@ -5799,17 +5849,7 @@ }, { "from" : "ADR046-cred-mi-004", - "to" : "ADR046-cred-mi-005", - "type" : "work-item-depends-on" - }, - { - "from" : "ADR046-cred-mi-005", - "to" : "ADR046-credential-001", - "type" : "work-item-depends-on" - }, - { - "from" : "ADR046-cred-mi-005", - "to" : "ADR046-credential-002", + "to" : "ADR046-mi-topology-001", "type" : "work-item-depends-on" }, { @@ -5844,12 +5884,12 @@ }, { "from" : "ADR046-credential-002", - "to" : "ADR046-credential-001", + "to" : "ADR046-bus-001", "type" : "work-item-depends-on" }, { "from" : "ADR046-credential-002", - "to" : "ADR046-session-003", + "to" : "ADR046-credential-001", "type" : "work-item-depends-on" }, { @@ -6152,6 +6192,21 @@ "to" : "ADR046-display-001", "type" : "work-item-depends-on" }, + { + "from" : "ADR046-doctor-001", + "to" : "ADR046-audit-001", + "type" : "work-item-depends-on" + }, + { + "from" : "ADR046-doctor-001", + "to" : "ADR046-core-001", + "type" : "work-item-depends-on" + }, + { + "from" : "ADR046-doctor-002", + "to" : "ADR046-doctor-001", + "type" : "work-item-depends-on" + }, { "from" : "ADR046-exec-002", "to" : "ADR046-exec-001", @@ -6327,11 +6382,6 @@ "to" : "ADR046-exec-021", "type" : "work-item-depends-on" }, - { - "from" : "ADR046-exec-024", - "to" : "ADR046-zone-control-019", - "type" : "work-item-depends-on" - }, { "from" : "ADR046-feasibility-002", "to" : "ADR046-feasibility-001", @@ -6393,13 +6443,33 @@ "type" : "work-item-depends-on" }, { - "from" : "ADR046-identities-002", - "to" : "ADR046-identities-001", + "from" : "ADR046-host-posture-001", + "to" : "ADR046-audit-001", "type" : "work-item-depends-on" }, { - "from" : "ADR046-minijail-001", - "to" : "ADR046-process-001", + "from" : "ADR046-host-posture-001", + "to" : "ADR046-core-001", + "type" : "work-item-depends-on" + }, + { + "from" : "ADR046-identities-002", + "to" : "ADR046-identities-001", + "type" : "work-item-depends-on" + }, + { + "from" : "ADR046-mi-topology-001", + "to" : "ADR046-credential-001", + "type" : "work-item-depends-on" + }, + { + "from" : "ADR046-mi-topology-001", + "to" : "ADR046-credential-002", + "type" : "work-item-depends-on" + }, + { + "from" : "ADR046-minijail-001", + "to" : "ADR046-process-001", "type" : "work-item-depends-on" }, { @@ -6422,6 +6492,11 @@ "to" : "ADR046-minijail-003", "type" : "work-item-depends-on" }, + { + "from" : "ADR046-minijail-005", + "to" : "ADR046-bus-001", + "type" : "work-item-depends-on" + }, { "from" : "ADR046-minijail-005", "to" : "ADR046-minijail-001", @@ -6447,11 +6522,6 @@ "to" : "ADR046-session-001", "type" : "work-item-depends-on" }, - { - "from" : "ADR046-minijail-005", - "to" : "ADR046-session-003", - "type" : "work-item-depends-on" - }, { "from" : "ADR046-minijail-006", "to" : "ADR046-minijail-005", @@ -6844,12 +6914,12 @@ }, { "from" : "ADR046-notify-002", - "to" : "ADR046-session-001", + "to" : "ADR046-bus-001", "type" : "work-item-depends-on" }, { "from" : "ADR046-notify-002", - "to" : "ADR046-session-003", + "to" : "ADR046-session-001", "type" : "work-item-depends-on" }, { @@ -6957,6 +7027,11 @@ "to" : "ADR046-zone-control-020", "type" : "work-item-depends-on" }, + { + "from" : "ADR046-pkg-001", + "to" : "ADR046-zone-control-003", + "type" : "work-item-depends-on" + }, { "from" : "ADR046-process-002", "to" : "ADR046-process-001", @@ -6967,6 +7042,21 @@ "to" : "ADR046-provider-001", "type" : "work-item-depends-on" }, + { + "from" : "ADR046-provider-agent-001", + "to" : "ADR046-zone-control-011", + "type" : "work-item-depends-on" + }, + { + "from" : "ADR046-provider-agent-001", + "to" : "ADR046-zone-control-017", + "type" : "work-item-depends-on" + }, + { + "from" : "ADR046-provider-agent-001", + "to" : "ADR046-zone-control-018", + "type" : "work-item-depends-on" + }, { "from" : "ADR046-pstate-001", "to" : "ADR046-primitives-001", @@ -7482,6 +7572,81 @@ "to" : "ADR046-reset-010", "type" : "work-item-depends-on" }, + { + "from" : "ADR046-reuse-001", + "to" : "ADR046-telem-001", + "type" : "work-item-depends-on" + }, + { + "from" : "ADR046-reuse-002", + "to" : "ADR046-reuse-001", + "type" : "work-item-depends-on" + }, + { + "from" : "ADR046-reuse-003", + "to" : "ADR046-reuse-001", + "type" : "work-item-depends-on" + }, + { + "from" : "ADR046-reuse-004", + "to" : "ADR046-reuse-001", + "type" : "work-item-depends-on" + }, + { + "from" : "ADR046-reuse-004", + "to" : "ADR046-reuse-003", + "type" : "work-item-depends-on" + }, + { + "from" : "ADR046-reuse-005", + "to" : "ADR046-audit-003", + "type" : "work-item-depends-on" + }, + { + "from" : "ADR046-reuse-005", + "to" : "ADR046-reuse-004", + "type" : "work-item-depends-on" + }, + { + "from" : "ADR046-reuse-006", + "to" : "ADR046-bus-001", + "type" : "work-item-depends-on" + }, + { + "from" : "ADR046-reuse-006", + "to" : "ADR046-reuse-001", + "type" : "work-item-depends-on" + }, + { + "from" : "ADR046-reuse-007", + "to" : "ADR046-reuse-004", + "type" : "work-item-depends-on" + }, + { + "from" : "ADR046-reuse-007", + "to" : "ADR046-reuse-006", + "type" : "work-item-depends-on" + }, + { + "from" : "ADR046-reuse-008", + "to" : "ADR046-reuse-001", + "type" : "work-item-depends-on" + }, + { + "from" : "ADR046-reuse-009", + "to" : "ADR046-reuse-001", + "type" : "work-item-depends-on" + }, + { + "from" : "ADR046-reuse-009", + "to" : "ADR046-telem-001", + "type" : "work-item-depends-on" + }, + { + "from" : "ADR046-reuse-009", + "to" : "ADR046-telem-003", + "type" : "work-item-depends-on" + }, { "from" : "ADR046-routing-002", "to" : "ADR046-routing-001", @@ -7589,12 +7754,12 @@ }, { "from" : "ADR046-security-002", - "to" : "ADR046-session-001", + "to" : "ADR046-bus-001", "type" : "work-item-depends-on" }, { "from" : "ADR046-security-002", - "to" : "ADR046-session-003", + "to" : "ADR046-session-001", "type" : "work-item-depends-on" }, { @@ -7609,12 +7774,12 @@ }, { "from" : "ADR046-security-004", - "to" : "ADR046-routing-005", + "to" : "ADR046-bus-001", "type" : "work-item-depends-on" }, { "from" : "ADR046-security-004", - "to" : "ADR046-session-003", + "to" : "ADR046-routing-005", "type" : "work-item-depends-on" }, { @@ -7674,7 +7839,7 @@ }, { "from" : "ADR046-security-012", - "to" : "ADR046-telem-013", + "to" : "ADR046-audit-002", "type" : "work-item-depends-on" }, { @@ -7694,17 +7859,17 @@ }, { "from" : "ADR046-security-014", - "to" : "ADR046-telem-016", + "to" : "ADR046-doctor-001", "type" : "work-item-depends-on" }, { "from" : "ADR046-security-014", - "to" : "ADR046-telem-017", + "to" : "ADR046-doctor-002", "type" : "work-item-depends-on" }, { "from" : "ADR046-security-015", - "to" : "ADR046-telem-012", + "to" : "ADR046-audit-001", "type" : "work-item-depends-on" }, { @@ -8452,6 +8617,11 @@ "to" : "ADR046-streamline-001", "type" : "work-item-depends-on" }, + { + "from" : "ADR046-system-core-001", + "to" : "ADR046-audit-001", + "type" : "work-item-depends-on" + }, { "from" : "ADR046-system-core-001", "to" : "ADR046-exec-003", @@ -8482,11 +8652,6 @@ "to" : "ADR046-telem-001", "type" : "work-item-depends-on" }, - { - "from" : "ADR046-system-core-001", - "to" : "ADR046-telem-012", - "type" : "work-item-depends-on" - }, { "from" : "ADR046-systemd-001", "to" : "ADR046-process-002", @@ -8514,12 +8679,12 @@ }, { "from" : "ADR046-telem-003", - "to" : "ADR046-session-001", + "to" : "ADR046-bus-001", "type" : "work-item-depends-on" }, { "from" : "ADR046-telem-003", - "to" : "ADR046-session-003", + "to" : "ADR046-session-001", "type" : "work-item-depends-on" }, { @@ -8584,152 +8749,22 @@ }, { "from" : "ADR046-telem-011", - "to" : "ADR046-store-001", + "to" : "ADR046-audit-001", "type" : "work-item-depends-on" }, { "from" : "ADR046-telem-011", - "to" : "ADR046-telem-009", + "to" : "ADR046-store-001", "type" : "work-item-depends-on" }, { "from" : "ADR046-telem-011", - "to" : "ADR046-telem-010", + "to" : "ADR046-telem-009", "type" : "work-item-depends-on" }, { "from" : "ADR046-telem-011", - "to" : "ADR046-telem-012", - "type" : "work-item-depends-on" - }, - { - "from" : "ADR046-telem-013", - "to" : "ADR046-store-001", - "type" : "work-item-depends-on" - }, - { - "from" : "ADR046-telem-013", - "to" : "ADR046-telem-012", - "type" : "work-item-depends-on" - }, - { - "from" : "ADR046-telem-014", - "to" : "ADR046-session-001", - "type" : "work-item-depends-on" - }, - { - "from" : "ADR046-telem-014", - "to" : "ADR046-session-003", - "type" : "work-item-depends-on" - }, - { - "from" : "ADR046-telem-014", - "to" : "ADR046-telem-012", - "type" : "work-item-depends-on" - }, - { - "from" : "ADR046-telem-015", - "to" : "ADR046-telem-012", - "type" : "work-item-depends-on" - }, - { - "from" : "ADR046-telem-016", - "to" : "ADR046-core-001", - "type" : "work-item-depends-on" - }, - { - "from" : "ADR046-telem-016", - "to" : "ADR046-telem-012", - "type" : "work-item-depends-on" - }, - { - "from" : "ADR046-telem-017", - "to" : "ADR046-telem-016", - "type" : "work-item-depends-on" - }, - { - "from" : "ADR046-telem-018", - "to" : "ADR046-core-001", - "type" : "work-item-depends-on" - }, - { - "from" : "ADR046-telem-018", - "to" : "ADR046-telem-012", - "type" : "work-item-depends-on" - }, - { - "from" : "ADR046-telem-019", - "to" : "ADR046-telem-001", - "type" : "work-item-depends-on" - }, - { - "from" : "ADR046-telem-020", - "to" : "ADR046-telem-019", - "type" : "work-item-depends-on" - }, - { - "from" : "ADR046-telem-021", - "to" : "ADR046-telem-019", - "type" : "work-item-depends-on" - }, - { - "from" : "ADR046-telem-022", - "to" : "ADR046-telem-019", - "type" : "work-item-depends-on" - }, - { - "from" : "ADR046-telem-022", - "to" : "ADR046-telem-021", - "type" : "work-item-depends-on" - }, - { - "from" : "ADR046-telem-023", - "to" : "ADR046-telem-014", - "type" : "work-item-depends-on" - }, - { - "from" : "ADR046-telem-023", - "to" : "ADR046-telem-022", - "type" : "work-item-depends-on" - }, - { - "from" : "ADR046-telem-024", - "to" : "ADR046-session-003", - "type" : "work-item-depends-on" - }, - { - "from" : "ADR046-telem-024", - "to" : "ADR046-telem-019", - "type" : "work-item-depends-on" - }, - { - "from" : "ADR046-telem-025", - "to" : "ADR046-telem-022", - "type" : "work-item-depends-on" - }, - { - "from" : "ADR046-telem-025", - "to" : "ADR046-telem-024", - "type" : "work-item-depends-on" - }, - { - "from" : "ADR046-telem-026", - "to" : "ADR046-telem-019", - "type" : "work-item-depends-on" - }, - { - "from" : "ADR046-telem-027", - "to" : "ADR046-telem-001", - "type" : "work-item-depends-on" - }, - { - "from" : "ADR046-telem-027", - "to" : "ADR046-telem-003", - "type" : "work-item-depends-on" - }, - { - "from" : "ADR046-telem-027", - "to" : "ADR046-telem-019", + "to" : "ADR046-telem-010", "type" : "work-item-depends-on" }, { @@ -8784,7 +8819,7 @@ }, { "from" : "ADR046-transport-unix-002", - "to" : "ADR046-session-003", + "to" : "ADR046-bus-001", "type" : "work-item-depends-on" }, { @@ -8814,12 +8849,12 @@ }, { "from" : "ADR046-transport-unix-007", - "to" : "ADR046-session-001", + "to" : "ADR046-bus-001", "type" : "work-item-depends-on" }, { "from" : "ADR046-transport-unix-007", - "to" : "ADR046-session-003", + "to" : "ADR046-session-001", "type" : "work-item-depends-on" }, { @@ -8967,6 +9002,11 @@ "to" : "ADR046-usbip-008", "type" : "work-item-depends-on" }, + { + "from" : "ADR046-user-session-001", + "to" : "ADR046-zone-control-019", + "type" : "work-item-depends-on" + }, { "from" : "ADR046-vl-001", "to" : "ADR046-primitives-001", @@ -9119,7 +9159,7 @@ }, { "from" : "ADR046-vsock-001", - "to" : "ADR046-session-003", + "to" : "ADR046-bus-001", "type" : "work-item-depends-on" }, { @@ -9129,7 +9169,7 @@ }, { "from" : "ADR046-vsock-003", - "to" : "ADR046-session-003", + "to" : "ADR046-bus-001", "type" : "work-item-depends-on" }, { @@ -9159,7 +9199,7 @@ }, { "from" : "ADR046-vvfs-001", - "to" : "ADR046-vvfs-007", + "to" : "ADR046-vvfs-export-001", "type" : "work-item-depends-on" }, { @@ -9189,7 +9229,7 @@ }, { "from" : "ADR046-vvfs-003", - "to" : "ADR046-vvfs-007", + "to" : "ADR046-vvfs-export-001", "type" : "work-item-depends-on" }, { @@ -9223,10 +9263,15 @@ "type" : "work-item-depends-on" }, { - "from" : "ADR046-vvfs-007", + "from" : "ADR046-vvfs-export-001", "to" : "ADR046-volume-001", "type" : "work-item-depends-on" }, + { + "from" : "ADR046-wire-001", + "to" : "ADR046-zone-control-013", + "type" : "work-item-depends-on" + }, { "from" : "ADR046-zone-control-001", "to" : "ADR046-identities-001", @@ -9476,51 +9521,6 @@ "from" : "ADR046-zone-control-024", "to" : "ADR046-zone-control-022", "type" : "work-item-depends-on" - }, - { - "from" : "ADR046-zone-control-025", - "to" : "ADR046-zone-control-011", - "type" : "work-item-depends-on" - }, - { - "from" : "ADR046-zone-control-025", - "to" : "ADR046-zone-control-012", - "type" : "work-item-depends-on" - }, - { - "from" : "ADR046-zone-control-025", - "to" : "ADR046-zone-control-013", - "type" : "work-item-depends-on" - }, - { - "from" : "ADR046-zone-control-025", - "to" : "ADR046-zone-control-018", - "type" : "work-item-depends-on" - }, - { - "from" : "ADR046-zone-control-026", - "to" : "ADR046-zone-control-003", - "type" : "work-item-depends-on" - }, - { - "from" : "ADR046-zone-control-027", - "to" : "ADR046-zone-control-011", - "type" : "work-item-depends-on" - }, - { - "from" : "ADR046-zone-control-027", - "to" : "ADR046-zone-control-017", - "type" : "work-item-depends-on" - }, - { - "from" : "ADR046-zone-control-027", - "to" : "ADR046-zone-control-018", - "type" : "work-item-depends-on" - }, - { - "from" : "ADR046-zone-control-028", - "to" : "ADR046-zone-control-013", - "type" : "work-item-depends-on" } ], "generatedFrom" : { @@ -12094,18 +12094,104 @@ { "blockers" : [], "destinations" : [ - "`src/{lib.rs,config.rs,schema.rs,error.rs,effect/mod.rs}`" + "`packages/d2b-audit/src/{hash_chain.rs,segment.rs,rate_limit.rs,record_types.rs,sink.rs,export.rs}`" ], "entryContracts" : [ - "ADR-046-provider-runtime-azure-virtual-machine" + "ADR-046-telemetry-audit-and-support" ], - "exitGate" : "ADR046-W6 exit criteria (ADR-046-validation-and-delivery §4): every spec/work item in this wave Merged with clean destinations, all validators green, and the ten-role panel seal recorded", - "id" : "ADR046-azure-vm-001", + "exitGate" : "ADR046-W5 exit criteria (ADR-046-validation-and-delivery §4): every spec/work item in this wave Merged with clean destinations, all validators green, and the ten-role panel seal recorded", + "id" : "ADR046-audit-001", "kind" : "work-item", - "owner" : "Provider contract owner", - "parallelGroup" : "wi:ADR-046-provider-runtime-azure-virtual-machine", + "owner" : "W0/W1a; audit crate owner", + "parallelGroup" : "wi:ADR-046-telemetry-audit-and-support", "prerequisites" : [ - "ADR-046-provider-runtime-azure-virtual-machine" + "ADR-046-telemetry-audit-and-support" + ], + "specId" : "ADR-046-telemetry-audit-and-support", + "topologicalRank" : 10, + "wave" : "W5" + }, + { + "blockers" : [], + "destinations" : [ + "`packages/d2b-resource-store-redb/src/audit.rs`, `packages/d2b-core-controller/src/authz_audit.rs`" + ], + "entryContracts" : [ + "ADR-046-telemetry-audit-and-support" + ], + "exitGate" : "ADR046-W5 exit criteria (ADR-046-validation-and-delivery §4): every spec/work item in this wave Merged with clean destinations, all validators green, and the ten-role panel seal recorded", + "id" : "ADR046-audit-002", + "kind" : "work-item", + "owner" : "ADR046-audit-001 + ADR046-store-001; store/authz owner", + "parallelGroup" : "wi:ADR-046-telemetry-audit-and-support", + "prerequisites" : [ + "ADR-046-telemetry-audit-and-support", + "ADR046-audit-001", + "ADR046-store-001" + ], + "specId" : "ADR-046-telemetry-audit-and-support", + "topologicalRank" : 11, + "wave" : "W5" + }, + { + "blockers" : [], + "destinations" : [ + "`packages/d2b-session/src/audit.rs`, `packages/d2b-bus/src/audit.rs`" + ], + "entryContracts" : [ + "ADR-046-telemetry-audit-and-support" + ], + "exitGate" : "ADR046-W5 exit criteria (ADR-046-validation-and-delivery §4): every spec/work item in this wave Merged with clean destinations, all validators green, and the ten-role panel seal recorded", + "id" : "ADR046-audit-003", + "kind" : "work-item", + "owner" : "ADR046-audit-001 + ADR046-session-001 + ADR046-bus-001; session/bus owner", + "parallelGroup" : "wi:ADR-046-telemetry-audit-and-support", + "prerequisites" : [ + "ADR-046-telemetry-audit-and-support", + "ADR046-audit-001", + "ADR046-bus-001", + "ADR046-session-001" + ], + "specId" : "ADR-046-telemetry-audit-and-support", + "topologicalRank" : 11, + "wave" : "W5" + }, + { + "blockers" : [], + "destinations" : [ + "`packages/d2b/src/zone_audit.rs` (new `d2b zone audit export` subcommand); `packages/d2b/tests/zone_audit_contract.rs`" + ], + "entryContracts" : [ + "ADR-046-telemetry-audit-and-support" + ], + "exitGate" : "ADR046-W5 exit criteria (ADR-046-validation-and-delivery §4): every spec/work item in this wave Merged with clean destinations, all validators green, and the ten-role panel seal recorded", + "id" : "ADR046-audit-004", + "kind" : "work-item", + "owner" : "ADR046-audit-001; CLI owner", + "parallelGroup" : "wi:ADR-046-telemetry-audit-and-support", + "prerequisites" : [ + "ADR-046-telemetry-audit-and-support", + "ADR046-audit-001" + ], + "specId" : "ADR-046-telemetry-audit-and-support", + "topologicalRank" : 11, + "wave" : "W5" + }, + { + "blockers" : [], + "destinations" : [ + "`src/{lib.rs,config.rs,schema.rs,error.rs,effect/mod.rs}`" + ], + "entryContracts" : [ + "ADR-046-provider-runtime-azure-virtual-machine" + ], + "exitGate" : "ADR046-W6 exit criteria (ADR-046-validation-and-delivery §4): every spec/work item in this wave Merged with clean destinations, all validators green, and the ten-role panel seal recorded", + "id" : "ADR046-azure-vm-001", + "kind" : "work-item", + "owner" : "Provider contract owner", + "parallelGroup" : "wi:ADR-046-provider-runtime-azure-virtual-machine", + "prerequisites" : [ + "ADR-046-provider-runtime-azure-virtual-machine" ], "specId" : "ADR-046-provider-runtime-azure-virtual-machine", "topologicalRank" : 11, @@ -12279,6 +12365,26 @@ "topologicalRank" : 11, "wave" : "W6" }, + { + "blockers" : [], + "destinations" : [ + "`packages/d2b-bus/src/{router,registry,authorization,streams,operations}.rs`" + ], + "entryContracts" : [ + "ADR-046-componentsession-and-bus" + ], + "exitGate" : "ADR046-W1 exit criteria (ADR-046-validation-and-delivery §4): every spec/work item in this wave Merged with clean destinations, all validators green, and the ten-role panel seal recorded", + "id" : "ADR046-bus-001", + "kind" : "work-item", + "owner" : "Sessions + resource API; bus owner", + "parallelGroup" : "wi:ADR-046-componentsession-and-bus", + "prerequisites" : [ + "ADR-046-componentsession-and-bus" + ], + "specId" : "ADR-046-componentsession-and-bus", + "topologicalRank" : 6, + "wave" : "W1" + }, { "blockers" : [], "destinations" : [ @@ -12711,6 +12817,30 @@ "topologicalRank" : 12, "wave" : "W5" }, + { + "blockers" : [], + "destinations" : [ + "`packages/d2b-client/src/` (updated for v3 Zone API, replacing ADR45 daemon verbs with Zone resource operations)" + ], + "entryContracts" : [ + "ADR-046-resources-zone-control" + ], + "exitGate" : "ADR046-W5 exit criteria (ADR-046-validation-and-delivery §4): every spec/work item in this wave Merged with clean destinations, all validators green, and the ten-role panel seal recorded", + "id" : "ADR046-client-001", + "kind" : "work-item", + "owner" : "ADR046-zone-control-011, ADR046-zone-control-012, ADR046-zone-control-013, ADR046-zone-control-018", + "parallelGroup" : "wi:ADR-046-resources-zone-control", + "prerequisites" : [ + "ADR-046-resources-zone-control", + "ADR046-zone-control-011", + "ADR046-zone-control-012", + "ADR046-zone-control-013", + "ADR046-zone-control-018" + ], + "specId" : "ADR-046-resources-zone-control", + "topologicalRank" : 13, + "wave" : "W5" + }, { "blockers" : [], "destinations" : [ @@ -13043,13 +13173,13 @@ "exitGate" : "ADR046-W6 exit criteria (ADR-046-validation-and-delivery §4): every spec/work item in this wave Merged with clean destinations, all validators green, and the ten-role panel seal recorded", "id" : "ADR046-cred-mi-001", "kind" : "work-item", - "owner" : "`ADR046-credential-001` (contracts), `ADR046-credential-002` (service); `ADR046-reconcile-001`; `ADR046-cred-mi-005`; `credential-managed-identity` crate owner", + "owner" : "`ADR046-credential-001` (contracts), `ADR046-credential-002` (service); `ADR046-reconcile-001`; `ADR046-mi-topology-001`; `credential-managed-identity` crate owner", "parallelGroup" : "wi:ADR-046-provider-credential-managed-identity", "prerequisites" : [ "ADR-046-provider-credential-managed-identity", - "ADR046-cred-mi-005", "ADR046-credential-001", "ADR046-credential-002", + "ADR046-mi-topology-001", "ADR046-reconcile-001" ], "specId" : "ADR-046-provider-credential-managed-identity", @@ -13067,13 +13197,13 @@ "exitGate" : "ADR046-W6 exit criteria (ADR-046-validation-and-delivery §4): every spec/work item in this wave Merged with clean destinations, all validators green, and the ten-role panel seal recorded", "id" : "ADR046-cred-mi-002", "kind" : "work-item", - "owner" : "ADR046-credential-001, ADR046-credential-002; ADR046-reconcile-001, ADR046-reconcile-002; ADR046-cred-mi-005; owner: Credential controller toolkit and managed-identity controller", + "owner" : "ADR046-credential-001, ADR046-credential-002; ADR046-reconcile-001, ADR046-reconcile-002; ADR046-mi-topology-001; owner: Credential controller toolkit and managed-identity controller", "parallelGroup" : "wi:ADR-046-provider-credential-managed-identity", "prerequisites" : [ "ADR-046-provider-credential-managed-identity", - "ADR046-cred-mi-005", "ADR046-credential-001", "ADR046-credential-002", + "ADR046-mi-topology-001", "ADR046-reconcile-001", "ADR046-reconcile-002" ], @@ -13115,39 +13245,17 @@ "exitGate" : "ADR046-W6 exit criteria (ADR-046-validation-and-delivery §4): every spec/work item in this wave Merged with clean destinations, all validators green, and the ten-role panel seal recorded", "id" : "ADR046-cred-mi-004", "kind" : "work-item", - "owner" : "Depends on ADR046-cred-mi-001 and ADR046-cred-mi-005; owner: credential-managed-identity audit/telemetry implementation", + "owner" : "Depends on ADR046-cred-mi-001 and ADR046-mi-topology-001; owner: credential-managed-identity audit/telemetry implementation", "parallelGroup" : "wi:ADR-046-provider-credential-managed-identity", "prerequisites" : [ "ADR-046-provider-credential-managed-identity", "ADR046-cred-mi-001", - "ADR046-cred-mi-005" + "ADR046-mi-topology-001" ], "specId" : "ADR-046-provider-credential-managed-identity", "topologicalRank" : 13, "wave" : "W6" }, - { - "blockers" : [], - "destinations" : [ - "packages/d2b-provider-credential-managed-identity/src/{controller.rs,agent.rs}; packages/d2b-provider-credential-managed-identity/{controller/main.rs,agent/main.rs}; packages/d2b-provider-credential-managed-identity/tests/topology.rs" - ], - "entryContracts" : [ - "ADR-046-provider-credential-managed-identity" - ], - "exitGate" : "ADR046-W6 exit criteria (ADR-046-validation-and-delivery §4): every spec/work item in this wave Merged with clean destinations, all validators green, and the ten-role panel seal recorded", - "id" : "ADR046-cred-mi-005", - "kind" : "work-item", - "owner" : "ADR046-credential-001 and ADR046-credential-002; owner: credential-managed-identity controller/agent topology. This topology contract is consumed by the later managed-identity controller work.", - "parallelGroup" : "wi:ADR-046-provider-credential-managed-identity", - "prerequisites" : [ - "ADR-046-provider-credential-managed-identity", - "ADR046-credential-001", - "ADR046-credential-002" - ], - "specId" : "ADR-046-provider-credential-managed-identity", - "topologicalRank" : 11, - "wave" : "W6" - }, { "blockers" : [], "destinations" : [ @@ -13304,13 +13412,13 @@ "exitGate" : "ADR046-W4 exit criteria (ADR-046-validation-and-delivery §4): every spec/work item in this wave Merged with clean destinations, all validators green, and the ten-role panel seal recorded", "id" : "ADR046-credential-002", "kind" : "work-item", - "owner" : "`ADR046-credential-001`; `ADR046-api-001` (resource API); `ADR046-session-003` (d2b-bus); Credential service owner", + "owner" : "`ADR046-credential-001`; `ADR046-api-001` (resource API); `ADR046-bus-001` (d2b-bus); Credential service owner", "parallelGroup" : "wi:ADR-046-resources-credential", "prerequisites" : [ "ADR-046-resources-credential", "ADR046-api-001", - "ADR046-credential-001", - "ADR046-session-003" + "ADR046-bus-001", + "ADR046-credential-001" ], "specId" : "ADR-046-resources-credential", "topologicalRank" : 10, @@ -13809,6 +13917,7 @@ "parallelGroup" : "wi:core-config-hub:w5", "prerequisites" : [ "ADR-046-resources-device", + "ADR046-core-001", "ADR046-device-006" ], "specId" : "ADR-046-resources-device", @@ -14196,6 +14305,49 @@ "topologicalRank" : 12, "wave" : "W6" }, + { + "blockers" : [], + "destinations" : [ + "`packages/d2b/src/zone_doctor.rs`, `packages/d2b/tests/zone_doctor_contract.rs`" + ], + "entryContracts" : [ + "ADR-046-telemetry-audit-and-support" + ], + "exitGate" : "ADR046-W5 exit criteria (ADR-046-validation-and-delivery §4): every spec/work item in this wave Merged with clean destinations, all validators green, and the ten-role panel seal recorded", + "id" : "ADR046-doctor-001", + "kind" : "work-item", + "owner" : "ADR046-core-001 + ADR046-audit-001; CLI/doctor owner", + "parallelGroup" : "wi:ADR-046-telemetry-audit-and-support", + "prerequisites" : [ + "ADR-046-telemetry-audit-and-support", + "ADR046-audit-001", + "ADR046-core-001" + ], + "specId" : "ADR-046-telemetry-audit-and-support", + "topologicalRank" : 11, + "wave" : "W5" + }, + { + "blockers" : [], + "destinations" : [ + "`packages/d2b/src/zone_support_bundle.rs`, `packages/d2b/tests/zone_support_bundle_contract.rs`" + ], + "entryContracts" : [ + "ADR-046-telemetry-audit-and-support" + ], + "exitGate" : "ADR046-W5 exit criteria (ADR-046-validation-and-delivery §4): every spec/work item in this wave Merged with clean destinations, all validators green, and the ten-role panel seal recorded", + "id" : "ADR046-doctor-002", + "kind" : "work-item", + "owner" : "ADR046-doctor-001; CLI/doctor owner", + "parallelGroup" : "wi:ADR-046-telemetry-audit-and-support", + "prerequisites" : [ + "ADR-046-telemetry-audit-and-support", + "ADR046-doctor-001" + ], + "specId" : "ADR-046-telemetry-audit-and-support", + "topologicalRank" : 12, + "wave" : "W5" + }, { "blockers" : [], "destinations" : [ @@ -14468,6 +14620,7 @@ "parallelGroup" : "wi:core-config-hub:w5", "prerequisites" : [ "ADR-046-resources-host-guest-process-user", + "ADR046-core-001", "ADR046-exec-008" ], "specId" : "ADR-046-resources-host-guest-process-user", @@ -14511,6 +14664,7 @@ "parallelGroup" : "wi:core-config-hub:w5", "prerequisites" : [ "ADR-046-resources-host-guest-process-user", + "ADR046-core-001", "ADR046-exec-001", "ADR046-exec-014", "ADR046-exec-022" @@ -14691,27 +14845,6 @@ "topologicalRank" : 14, "wave" : "W5" }, - { - "blockers" : [], - "destinations" : [ - "`packages/d2b-core-controller/src/user_session_authority.rs` (or a core/user-agent per-session agent Process under `Provider/system-systemd`); `AuthorityDescriptor` on the session authority" - ], - "entryContracts" : [ - "ADR-046-resources-host-guest-process-user" - ], - "exitGate" : "ADR046-W5 exit criteria (ADR-046-validation-and-delivery §4): every spec/work item in this wave Merged with clean destinations, all validators green, and the ten-role panel seal recorded", - "id" : "ADR046-exec-024", - "kind" : "work-item", - "owner" : "ADR046-zone-control-019 (authority index); `Provider/system-systemd` (user manager) + core/user-agent owner", - "parallelGroup" : "wi:ADR-046-resources-host-guest-process-user", - "prerequisites" : [ - "ADR-046-resources-host-guest-process-user", - "ADR046-zone-control-019" - ], - "specId" : "ADR-046-resources-host-guest-process-user", - "topologicalRank" : 12, - "wave" : "W5" - }, { "blockers" : [], "destinations" : [ @@ -15124,6 +15257,28 @@ "topologicalRank" : 12, "wave" : "W6" }, + { + "blockers" : [], + "destinations" : [ + "`packages/d2b-provider-system-core/src/{host_reconciler.rs,host_status.rs,host_process_audit.rs}`; adapted `nixos-modules/unsafe-local-workloads-json.nix`; `packages/d2b-provider-system-core/tests/host_posture_contract.rs`" + ], + "entryContracts" : [ + "ADR-046-telemetry-audit-and-support" + ], + "exitGate" : "ADR046-W5 exit criteria (ADR-046-validation-and-delivery §4): every spec/work item in this wave Merged with clean destinations, all validators green, and the ten-role panel seal recorded", + "id" : "ADR046-host-posture-001", + "kind" : "work-item", + "owner" : "ADR046-audit-001 + ADR046-core-001; `Provider/system-core` owner", + "parallelGroup" : "wi:ADR-046-telemetry-audit-and-support", + "prerequisites" : [ + "ADR-046-telemetry-audit-and-support", + "ADR046-audit-001", + "ADR046-core-001" + ], + "specId" : "ADR-046-telemetry-audit-and-support", + "topologicalRank" : 11, + "wave" : "W5" + }, { "blockers" : [], "destinations" : [ @@ -15165,6 +15320,28 @@ "topologicalRank" : 3, "wave" : "W0" }, + { + "blockers" : [], + "destinations" : [ + "packages/d2b-provider-credential-managed-identity/src/{controller.rs,agent.rs}; packages/d2b-provider-credential-managed-identity/{controller/main.rs,agent/main.rs}; packages/d2b-provider-credential-managed-identity/tests/topology.rs" + ], + "entryContracts" : [ + "ADR-046-provider-credential-managed-identity" + ], + "exitGate" : "ADR046-W6 exit criteria (ADR-046-validation-and-delivery §4): every spec/work item in this wave Merged with clean destinations, all validators green, and the ten-role panel seal recorded", + "id" : "ADR046-mi-topology-001", + "kind" : "work-item", + "owner" : "ADR046-credential-001 and ADR046-credential-002; owner: credential-managed-identity controller/agent topology. This topology contract is consumed by the later managed-identity controller work.", + "parallelGroup" : "wi:ADR-046-provider-credential-managed-identity", + "prerequisites" : [ + "ADR-046-provider-credential-managed-identity", + "ADR046-credential-001", + "ADR046-credential-002" + ], + "specId" : "ADR-046-provider-credential-managed-identity", + "topologicalRank" : 11, + "wave" : "W6" + }, { "blockers" : [], "destinations" : [ @@ -15261,16 +15438,16 @@ "exitGate" : "ADR046-W6 exit criteria (ADR-046-validation-and-delivery §4): every spec/work item in this wave Merged with clean destinations, all validators green, and the ten-role panel seal recorded", "id" : "ADR046-minijail-005", "kind" : "work-item", - "owner" : "All of ADR046-minijail-001 through ADR046-minijail-004; ComponentSession/d2b-bus (ADR046-session-001, ADR046-session-003); bootstrap authz", + "owner" : "All of ADR046-minijail-001 through ADR046-minijail-004; ComponentSession/d2b-bus (ADR046-session-001, ADR046-bus-001); bootstrap authz", "parallelGroup" : "wi:ADR-046-provider-system-minijail", "prerequisites" : [ "ADR-046-provider-system-minijail", + "ADR046-bus-001", "ADR046-minijail-001", "ADR046-minijail-002", "ADR046-minijail-003", "ADR046-minijail-004", - "ADR046-session-001", - "ADR046-session-003" + "ADR046-session-001" ], "specId" : "ADR-046-provider-system-minijail", "topologicalRank" : 14, @@ -15462,6 +15639,7 @@ "parallelGroup" : "wi:core-config-hub:w4", "prerequisites" : [ "ADR-046-resources-network", + "ADR046-core-001", "ADR046-network-004", "ADR046-network-005" ], @@ -16685,12 +16863,12 @@ "exitGate" : "ADR046-W6 exit criteria (ADR-046-validation-and-delivery §4): every spec/work item in this wave Merged with clean destinations, all validators green, and the ten-role panel seal recorded", "id" : "ADR046-notify-002", "kind" : "work-item", - "owner" : "ADR046-session-001, ADR046-session-003; session/bus wiring", + "owner" : "ADR046-session-001, ADR046-bus-001; session/bus wiring", "parallelGroup" : "wi:ADR-046-provider-notification-desktop", "prerequisites" : [ "ADR-046-provider-notification-desktop", - "ADR046-session-001", - "ADR046-session-003" + "ADR046-bus-001", + "ADR046-session-001" ], "specId" : "ADR-046-provider-notification-desktop", "topologicalRank" : 11, @@ -16960,17 +17138,38 @@ { "blockers" : [], "destinations" : [ - "`packages/d2b-contracts/src/v3/host.rs`, `guest.rs`, `execution_policy.rs`, `process.rs`, `volume.rs`, `user.rs`, `network.rs`, `device.rs`, `credential.rs`" + "`packages/d2b-contract-tests/tests/policy_provider_crate_layout.rs` (new file; gated under D2B_FIXTURES in existing `tests/tools/rust-workspace-checks.sh`)" ], "entryContracts" : [ - "ADR-046-primitive-resource-composition" + "ADR-046-resources-zone-control" ], - "exitGate" : "ADR046-W2 exit criteria (ADR-046-validation-and-delivery §4): every spec/work item in this wave Merged with clean destinations, all validators green, and the ten-role panel seal recorded", - "id" : "ADR046-primitives-001", + "exitGate" : "ADR046-W5 exit criteria (ADR-046-validation-and-delivery §4): every spec/work item in this wave Merged with clean destinations, all validators green, and the ten-role panel seal recorded", + "id" : "ADR046-pkg-001", "kind" : "work-item", - "owner" : "W0; resource contracts", - "parallelGroup" : "wi:ADR-046-primitive-resource-composition", - "prerequisites" : [ + "owner" : "ADR046-zone-control-003; workspace policy owner", + "parallelGroup" : "wi:ADR-046-resources-zone-control", + "prerequisites" : [ + "ADR-046-resources-zone-control", + "ADR046-zone-control-003" + ], + "specId" : "ADR-046-resources-zone-control", + "topologicalRank" : 13, + "wave" : "W5" + }, + { + "blockers" : [], + "destinations" : [ + "`packages/d2b-contracts/src/v3/host.rs`, `guest.rs`, `execution_policy.rs`, `process.rs`, `volume.rs`, `user.rs`, `network.rs`, `device.rs`, `credential.rs`" + ], + "entryContracts" : [ + "ADR-046-primitive-resource-composition" + ], + "exitGate" : "ADR046-W2 exit criteria (ADR-046-validation-and-delivery §4): every spec/work item in this wave Merged with clean destinations, all validators green, and the ten-role panel seal recorded", + "id" : "ADR046-primitives-001", + "kind" : "work-item", + "owner" : "W0; resource contracts", + "parallelGroup" : "wi:ADR-046-primitive-resource-composition", + "prerequisites" : [ "ADR-046-primitive-resource-composition" ], "specId" : "ADR-046-primitive-resource-composition", @@ -17139,6 +17338,29 @@ "topologicalRank" : 9, "wave" : "W3" }, + { + "blockers" : [], + "destinations" : [ + "`packages/d2b-provider/src/agent.rs` (v3 provider agent dispatch); `packages/d2b-contracts/src/v3/provider_registry.rs` (v3 provider registry wire contract)" + ], + "entryContracts" : [ + "ADR-046-resources-zone-control" + ], + "exitGate" : "ADR046-W5 exit criteria (ADR-046-validation-and-delivery §4): every spec/work item in this wave Merged with clean destinations, all validators green, and the ten-role panel seal recorded", + "id" : "ADR046-provider-agent-001", + "kind" : "work-item", + "owner" : "ADR046-zone-control-011, ADR046-zone-control-017, ADR046-zone-control-018", + "parallelGroup" : "wi:ADR-046-resources-zone-control", + "prerequisites" : [ + "ADR-046-resources-zone-control", + "ADR046-zone-control-011", + "ADR046-zone-control-017", + "ADR046-zone-control-018" + ], + "specId" : "ADR-046-resources-zone-control", + "topologicalRank" : 13, + "wave" : "W5" + }, { "blockers" : [], "destinations" : [ @@ -18142,6 +18364,201 @@ "topologicalRank" : 19, "wave" : "W7" }, + { + "blockers" : [], + "destinations" : [ + "`packages/d2b-session/` copied verbatim; v3 contract extension renames endpoint purpose values while preserving backward wire compatibility during transition." + ], + "entryContracts" : [ + "ADR-046-telemetry-audit-and-support" + ], + "exitGate" : "ADR046-W5 exit criteria (ADR-046-validation-and-delivery §4): every spec/work item in this wave Merged with clean destinations, all validators green, and the ten-role panel seal recorded", + "id" : "ADR046-reuse-001", + "kind" : "work-item", + "owner" : "ADR046-telem-001 (d2b-telemetry must exist first for MetricsSink injection); session owner", + "parallelGroup" : "wi:ADR-046-telemetry-audit-and-support", + "prerequisites" : [ + "ADR-046-telemetry-audit-and-support", + "ADR046-telem-001" + ], + "specId" : "ADR-046-telemetry-audit-and-support", + "topologicalRank" : 11, + "wave" : "W5" + }, + { + "blockers" : [], + "destinations" : [ + "`packages/d2b-session-unix/` copied verbatim." + ], + "entryContracts" : [ + "ADR-046-telemetry-audit-and-support" + ], + "exitGate" : "ADR046-W5 exit criteria (ADR-046-validation-and-delivery §4): every spec/work item in this wave Merged with clean destinations, all validators green, and the ten-role panel seal recorded", + "id" : "ADR046-reuse-002", + "kind" : "work-item", + "owner" : "ADR046-reuse-001; Unix transport owner", + "parallelGroup" : "wi:ADR-046-telemetry-audit-and-support", + "prerequisites" : [ + "ADR-046-telemetry-audit-and-support", + "ADR046-reuse-001" + ], + "specId" : "ADR-046-telemetry-audit-and-support", + "topologicalRank" : 12, + "wave" : "W5" + }, + { + "blockers" : [], + "destinations" : [ + "`packages/d2b-client/` copied; DaemonClient/GuestClient adapted to v3 service packages; `MetadataInput::with_trace` drives `TraceContext` propagation." + ], + "entryContracts" : [ + "ADR-046-telemetry-audit-and-support" + ], + "exitGate" : "ADR046-W5 exit criteria (ADR-046-validation-and-delivery §4): every spec/work item in this wave Merged with clean destinations, all validators green, and the ten-role panel seal recorded", + "id" : "ADR046-reuse-003", + "kind" : "work-item", + "owner" : "ADR046-reuse-001; client owner", + "parallelGroup" : "wi:ADR-046-telemetry-audit-and-support", + "prerequisites" : [ + "ADR-046-telemetry-audit-and-support", + "ADR046-reuse-001" + ], + "specId" : "ADR-046-telemetry-audit-and-support", + "topologicalRank" : 12, + "wave" : "W5" + }, + { + "blockers" : [], + "destinations" : [ + "`packages/d2b-provider/` and `packages/d2b-provider-toolkit/` copied with v3 session admission and bus routing adaptations." + ], + "entryContracts" : [ + "ADR-046-telemetry-audit-and-support" + ], + "exitGate" : "ADR046-W5 exit criteria (ADR-046-validation-and-delivery §4): every spec/work item in this wave Merged with clean destinations, all validators green, and the ten-role panel seal recorded", + "id" : "ADR046-reuse-004", + "kind" : "work-item", + "owner" : "ADR046-reuse-001 + ADR046-reuse-003; Provider owner", + "parallelGroup" : "wi:ADR-046-telemetry-audit-and-support", + "prerequisites" : [ + "ADR-046-telemetry-audit-and-support", + "ADR046-reuse-001", + "ADR046-reuse-003" + ], + "specId" : "ADR-046-telemetry-audit-and-support", + "topologicalRank" : 13, + "wave" : "W5" + }, + { + "blockers" : [], + "destinations" : [ + "`packages/d2b-provider-observability-otel/src/agent.rs` adapted; ComponentSessionDriver mock becomes shared Provider session fixture." + ], + "entryContracts" : [ + "ADR-046-telemetry-audit-and-support" + ], + "exitGate" : "ADR046-W5 exit criteria (ADR-046-validation-and-delivery §4): every spec/work item in this wave Merged with clean destinations, all validators green, and the ten-role panel seal recorded", + "id" : "ADR046-reuse-005", + "kind" : "work-item", + "owner" : "ADR046-reuse-004 + ADR046-audit-003; Provider agent / observability-otel owner", + "parallelGroup" : "wi:ADR-046-telemetry-audit-and-support", + "prerequisites" : [ + "ADR-046-telemetry-audit-and-support", + "ADR046-audit-003", + "ADR046-reuse-004" + ], + "specId" : "ADR-046-telemetry-audit-and-support", + "topologicalRank" : 14, + "wave" : "W5" + }, + { + "blockers" : [], + "destinations" : [ + "`packages/d2b-bus/src/routing.rs` adapted from `service_v2.rs`; RemoteNodeErrorKind stable code pattern becomes v3 BusErrorKind." + ], + "entryContracts" : [ + "ADR-046-telemetry-audit-and-support" + ], + "exitGate" : "ADR046-W5 exit criteria (ADR-046-validation-and-delivery §4): every spec/work item in this wave Merged with clean destinations, all validators green, and the ten-role panel seal recorded", + "id" : "ADR046-reuse-006", + "kind" : "work-item", + "owner" : "ADR046-reuse-001 + ADR046-bus-001; bus routing owner", + "parallelGroup" : "wi:ADR-046-telemetry-audit-and-support", + "prerequisites" : [ + "ADR-046-telemetry-audit-and-support", + "ADR046-bus-001", + "ADR046-reuse-001" + ], + "specId" : "ADR-046-telemetry-audit-and-support", + "topologicalRank" : 12, + "wave" : "W5" + }, + { + "blockers" : [], + "destinations" : [ + "`packages/d2b-bus/src/service_router.rs` and `packages/d2b-core-controller/src/provider_effects.rs`." + ], + "entryContracts" : [ + "ADR-046-telemetry-audit-and-support" + ], + "exitGate" : "ADR046-W5 exit criteria (ADR-046-validation-and-delivery §4): every spec/work item in this wave Merged with clean destinations, all validators green, and the ten-role panel seal recorded", + "id" : "ADR046-reuse-007", + "kind" : "work-item", + "owner" : "ADR046-reuse-004 + ADR046-reuse-006; core-controller routing owner", + "parallelGroup" : "wi:ADR-046-telemetry-audit-and-support", + "prerequisites" : [ + "ADR-046-telemetry-audit-and-support", + "ADR046-reuse-004", + "ADR046-reuse-006" + ], + "specId" : "ADR-046-telemetry-audit-and-support", + "topologicalRank" : 14, + "wave" : "W5" + }, + { + "blockers" : [], + "destinations" : [ + "`packages/d2b-contract-tests/tests/component_session_v2_vectors.rs` and `tests/noise_vectors.rs` copied verbatim." + ], + "entryContracts" : [ + "ADR-046-telemetry-audit-and-support" + ], + "exitGate" : "ADR046-W5 exit criteria (ADR-046-validation-and-delivery §4): every spec/work item in this wave Merged with clean destinations, all validators green, and the ten-role panel seal recorded", + "id" : "ADR046-reuse-008", + "kind" : "work-item", + "owner" : "ADR046-reuse-001; contract-tests owner", + "parallelGroup" : "wi:ADR-046-telemetry-audit-and-support", + "prerequisites" : [ + "ADR-046-telemetry-audit-and-support", + "ADR046-reuse-001" + ], + "specId" : "ADR-046-telemetry-audit-and-support", + "topologicalRank" : 12, + "wave" : "W5" + }, + { + "blockers" : [], + "destinations" : [ + "`packages/d2b-telemetry/src/session_metrics_sink.rs`." + ], + "entryContracts" : [ + "ADR-046-telemetry-audit-and-support" + ], + "exitGate" : "ADR046-W5 exit criteria (ADR-046-validation-and-delivery §4): every spec/work item in this wave Merged with clean destinations, all validators green, and the ten-role panel seal recorded", + "id" : "ADR046-reuse-009", + "kind" : "work-item", + "owner" : "ADR046-reuse-001 + ADR046-telem-001 + ADR046-telem-003; telemetry/session owner", + "parallelGroup" : "wi:ADR-046-telemetry-audit-and-support", + "prerequisites" : [ + "ADR-046-telemetry-audit-and-support", + "ADR046-reuse-001", + "ADR046-telem-001", + "ADR046-telem-003" + ], + "specId" : "ADR-046-telemetry-audit-and-support", + "topologicalRank" : 12, + "wave" : "W5" + }, { "blockers" : [], "destinations" : [ @@ -18514,12 +18931,12 @@ "exitGate" : "ADR046-W7 exit criteria (ADR-046-validation-and-delivery §4): every spec/work item in this wave Merged with clean destinations, all validators green, and the ten-role panel seal recorded", "id" : "ADR046-security-002", "kind" : "work-item", - "owner" : "`ADR046-session-001`/`ADR046-session-003` (ComponentSession/d2b-bus implementation)", + "owner" : "`ADR046-session-001`/`ADR046-bus-001` (ComponentSession/d2b-bus implementation)", "parallelGroup" : "wi:ADR-046-security-and-threat-model", "prerequisites" : [ "ADR-046-security-and-threat-model", - "ADR046-session-001", - "ADR046-session-003" + "ADR046-bus-001", + "ADR046-session-001" ], "specId" : "ADR-046-security-and-threat-model", "topologicalRank" : 12, @@ -18558,12 +18975,12 @@ "exitGate" : "ADR046-W7 exit criteria (ADR-046-validation-and-delivery §4): every spec/work item in this wave Merged with clean destinations, all validators green, and the ten-role panel seal recorded", "id" : "ADR046-security-004", "kind" : "work-item", - "owner" : "`ADR046-routing-005`/`ADR046-session-003` (ZoneLink/d2b-bus relay implementation)", + "owner" : "`ADR046-routing-005`/`ADR046-bus-001` (ZoneLink/d2b-bus relay implementation)", "parallelGroup" : "wi:ADR-046-security-and-threat-model", "prerequisites" : [ "ADR-046-security-and-threat-model", - "ADR046-routing-005", - "ADR046-session-003" + "ADR046-bus-001", + "ADR046-routing-005" ], "specId" : "ADR-046-security-and-threat-model", "topologicalRank" : 12, @@ -18731,11 +19148,11 @@ "exitGate" : "ADR046-W7 exit criteria (ADR-046-validation-and-delivery §4): every spec/work item in this wave Merged with clean destinations, all validators green, and the ten-role panel seal recorded", "id" : "ADR046-security-012", "kind" : "work-item", - "owner" : "`ADR046-telem-013` (privileged audit durability)", + "owner" : "`ADR046-audit-002` (privileged audit durability)", "parallelGroup" : "wi:ADR-046-security-and-threat-model", "prerequisites" : [ "ADR-046-security-and-threat-model", - "ADR046-telem-013" + "ADR046-audit-002" ], "specId" : "ADR-046-security-and-threat-model", "topologicalRank" : 12, @@ -18775,12 +19192,12 @@ "exitGate" : "ADR046-W7 exit criteria (ADR-046-validation-and-delivery §4): every spec/work item in this wave Merged with clean destinations, all validators green, and the ten-role panel seal recorded", "id" : "ADR046-security-014", "kind" : "work-item", - "owner" : "`ADR046-telem-016`/`ADR046-telem-017` (doctor/support-bundle commands)", + "owner" : "`ADR046-doctor-001`/`ADR046-doctor-002` (doctor/support-bundle commands)", "parallelGroup" : "wi:ADR-046-security-and-threat-model", "prerequisites" : [ "ADR-046-security-and-threat-model", - "ADR046-telem-016", - "ADR046-telem-017" + "ADR046-doctor-001", + "ADR046-doctor-002" ], "specId" : "ADR-046-security-and-threat-model", "topologicalRank" : 13, @@ -18797,11 +19214,11 @@ "exitGate" : "ADR046-W7 exit criteria (ADR-046-validation-and-delivery §4): every spec/work item in this wave Merged with clean destinations, all validators green, and the ten-role panel seal recorded", "id" : "ADR046-security-015", "kind" : "work-item", - "owner" : "`ADR046-telem-012` (`StateReset` record), Volume/Credential lifecycle work items", + "owner" : "`ADR046-audit-001` (`StateReset` record), Volume/Credential lifecycle work items", "parallelGroup" : "wi:ADR-046-security-and-threat-model", "prerequisites" : [ "ADR-046-security-and-threat-model", - "ADR046-telem-012" + "ADR046-audit-001" ], "specId" : "ADR-046-security-and-threat-model", "topologicalRank" : 12, @@ -19747,26 +20164,6 @@ "topologicalRank" : 7, "wave" : "W1" }, - { - "blockers" : [], - "destinations" : [ - "`packages/d2b-bus/src/{router,registry,authorization,streams,operations}.rs`" - ], - "entryContracts" : [ - "ADR-046-componentsession-and-bus" - ], - "exitGate" : "ADR046-W1 exit criteria (ADR-046-validation-and-delivery §4): every spec/work item in this wave Merged with clean destinations, all validators green, and the ten-role panel seal recorded", - "id" : "ADR046-session-003", - "kind" : "work-item", - "owner" : "Sessions + resource API; bus owner", - "parallelGroup" : "wi:ADR-046-componentsession-and-bus", - "prerequisites" : [ - "ADR-046-componentsession-and-bus" - ], - "specId" : "ADR-046-componentsession-and-bus", - "topologicalRank" : 6, - "wave" : "W1" - }, { "blockers" : [], "destinations" : [ @@ -20607,17 +21004,17 @@ "exitGate" : "ADR046-W6 exit criteria (ADR-046-validation-and-delivery §4): every spec/work item in this wave Merged with clean destinations, all validators green, and the ten-role panel seal recorded", "id" : "ADR046-system-core-001", "kind" : "work-item", - "owner" : "`ADR046-provider-001`, `ADR046-exec-003`, `ADR046-exec-004`, `ADR046-exec-005`, `ADR046-pstate-012`, `ADR046-telem-001`, and `ADR046-telem-012`; `Provider/system-core` owner", + "owner" : "`ADR046-provider-001`, `ADR046-exec-003`, `ADR046-exec-004`, `ADR046-exec-005`, `ADR046-pstate-012`, `ADR046-telem-001`, and `ADR046-audit-001`; `Provider/system-core` owner", "parallelGroup" : "wi:ADR-046-provider-system-core", "prerequisites" : [ "ADR-046-provider-system-core", + "ADR046-audit-001", "ADR046-exec-003", "ADR046-exec-004", "ADR046-exec-005", "ADR046-provider-001", "ADR046-pstate-012", - "ADR046-telem-001", - "ADR046-telem-012" + "ADR046-telem-001" ], "specId" : "ADR-046-provider-system-core", "topologicalRank" : 13, @@ -20739,12 +21136,12 @@ "exitGate" : "ADR046-W5 exit criteria (ADR-046-validation-and-delivery §4): every spec/work item in this wave Merged with clean destinations, all validators green, and the ten-role panel seal recorded", "id" : "ADR046-telem-003", "kind" : "work-item", - "owner" : "ADR046-telem-001 + ADR046-session-001 + ADR046-session-003; session/bus owner", + "owner" : "ADR046-telem-001 + ADR046-session-001 + ADR046-bus-001; session/bus owner", "parallelGroup" : "wi:ADR-046-telemetry-audit-and-support", "prerequisites" : [ "ADR-046-telemetry-audit-and-support", + "ADR046-bus-001", "ADR046-session-001", - "ADR046-session-003", "ADR046-telem-001" ], "specId" : "ADR-046-telemetry-audit-and-support", @@ -20913,14 +21310,15 @@ "exitGate" : "ADR046-W5 exit criteria (ADR-046-validation-and-delivery §4): every spec/work item in this wave Merged with clean destinations, all validators green, and the ten-role panel seal recorded", "id" : "ADR046-telem-011", "kind" : "work-item", - "owner" : "ADR046-telem-009 + ADR046-telem-010 + ADR046-telem-012 + ADR046-store-001; core-controller owner", + "owner" : "ADR046-telem-009 + ADR046-telem-010 + ADR046-audit-001 + ADR046-store-001; core-controller owner", "parallelGroup" : "wi:core-config-hub:w5", "prerequisites" : [ "ADR-046-telemetry-audit-and-support", + "ADR046-audit-001", + "ADR046-core-001", "ADR046-store-001", "ADR046-telem-009", - "ADR046-telem-010", - "ADR046-telem-012" + "ADR046-telem-010" ], "specId" : "ADR-046-telemetry-audit-and-support", "topologicalRank" : 12, @@ -20929,373 +21327,27 @@ { "blockers" : [], "destinations" : [ - "`packages/d2b-audit/src/{hash_chain.rs,segment.rs,rate_limit.rs,record_types.rs,sink.rs,export.rs}`" + "`packages/d2b-provider-transport-azure-relay/src/relay_transport.rs`" ], "entryContracts" : [ - "ADR-046-telemetry-audit-and-support" + "ADR-046-provider-transport-azure-relay" ], - "exitGate" : "ADR046-W5 exit criteria (ADR-046-validation-and-delivery §4): every spec/work item in this wave Merged with clean destinations, all validators green, and the ten-role panel seal recorded", - "id" : "ADR046-telem-012", + "exitGate" : "ADR046-W6 exit criteria (ADR-046-validation-and-delivery §4): every spec/work item in this wave Merged with clean destinations, all validators green, and the ten-role panel seal recorded", + "id" : "ADR046-transport-relay-001", "kind" : "work-item", - "owner" : "W0/W1a; audit crate owner", - "parallelGroup" : "wi:ADR-046-telemetry-audit-and-support", + "owner" : "W0 shared contract root; ComponentSession transport adapter owner", + "parallelGroup" : "wi:ADR-046-provider-transport-azure-relay", "prerequisites" : [ - "ADR-046-telemetry-audit-and-support" + "ADR-046-provider-transport-azure-relay" ], - "specId" : "ADR-046-telemetry-audit-and-support", - "topologicalRank" : 10, - "wave" : "W5" + "specId" : "ADR-046-provider-transport-azure-relay", + "topologicalRank" : 11, + "wave" : "W6" }, { "blockers" : [], "destinations" : [ - "`packages/d2b-resource-store-redb/src/audit.rs`, `packages/d2b-core-controller/src/authz_audit.rs`" - ], - "entryContracts" : [ - "ADR-046-telemetry-audit-and-support" - ], - "exitGate" : "ADR046-W5 exit criteria (ADR-046-validation-and-delivery §4): every spec/work item in this wave Merged with clean destinations, all validators green, and the ten-role panel seal recorded", - "id" : "ADR046-telem-013", - "kind" : "work-item", - "owner" : "ADR046-telem-012 + ADR046-store-001; store/authz owner", - "parallelGroup" : "wi:ADR-046-telemetry-audit-and-support", - "prerequisites" : [ - "ADR-046-telemetry-audit-and-support", - "ADR046-store-001", - "ADR046-telem-012" - ], - "specId" : "ADR-046-telemetry-audit-and-support", - "topologicalRank" : 11, - "wave" : "W5" - }, - { - "blockers" : [], - "destinations" : [ - "`packages/d2b-session/src/audit.rs`, `packages/d2b-bus/src/audit.rs`" - ], - "entryContracts" : [ - "ADR-046-telemetry-audit-and-support" - ], - "exitGate" : "ADR046-W5 exit criteria (ADR-046-validation-and-delivery §4): every spec/work item in this wave Merged with clean destinations, all validators green, and the ten-role panel seal recorded", - "id" : "ADR046-telem-014", - "kind" : "work-item", - "owner" : "ADR046-telem-012 + ADR046-session-001 + ADR046-session-003; session/bus owner", - "parallelGroup" : "wi:ADR-046-telemetry-audit-and-support", - "prerequisites" : [ - "ADR-046-telemetry-audit-and-support", - "ADR046-session-001", - "ADR046-session-003", - "ADR046-telem-012" - ], - "specId" : "ADR-046-telemetry-audit-and-support", - "topologicalRank" : 11, - "wave" : "W5" - }, - { - "blockers" : [], - "destinations" : [ - "`packages/d2b/src/zone_audit.rs` (new `d2b zone audit export` subcommand); `packages/d2b/tests/zone_audit_contract.rs`" - ], - "entryContracts" : [ - "ADR-046-telemetry-audit-and-support" - ], - "exitGate" : "ADR046-W5 exit criteria (ADR-046-validation-and-delivery §4): every spec/work item in this wave Merged with clean destinations, all validators green, and the ten-role panel seal recorded", - "id" : "ADR046-telem-015", - "kind" : "work-item", - "owner" : "ADR046-telem-012; CLI owner", - "parallelGroup" : "wi:ADR-046-telemetry-audit-and-support", - "prerequisites" : [ - "ADR-046-telemetry-audit-and-support", - "ADR046-telem-012" - ], - "specId" : "ADR-046-telemetry-audit-and-support", - "topologicalRank" : 11, - "wave" : "W5" - }, - { - "blockers" : [], - "destinations" : [ - "`packages/d2b/src/zone_doctor.rs`, `packages/d2b/tests/zone_doctor_contract.rs`" - ], - "entryContracts" : [ - "ADR-046-telemetry-audit-and-support" - ], - "exitGate" : "ADR046-W5 exit criteria (ADR-046-validation-and-delivery §4): every spec/work item in this wave Merged with clean destinations, all validators green, and the ten-role panel seal recorded", - "id" : "ADR046-telem-016", - "kind" : "work-item", - "owner" : "ADR046-core-001 + ADR046-telem-012; CLI/doctor owner", - "parallelGroup" : "wi:ADR-046-telemetry-audit-and-support", - "prerequisites" : [ - "ADR-046-telemetry-audit-and-support", - "ADR046-core-001", - "ADR046-telem-012" - ], - "specId" : "ADR-046-telemetry-audit-and-support", - "topologicalRank" : 11, - "wave" : "W5" - }, - { - "blockers" : [], - "destinations" : [ - "`packages/d2b/src/zone_support_bundle.rs`, `packages/d2b/tests/zone_support_bundle_contract.rs`" - ], - "entryContracts" : [ - "ADR-046-telemetry-audit-and-support" - ], - "exitGate" : "ADR046-W5 exit criteria (ADR-046-validation-and-delivery §4): every spec/work item in this wave Merged with clean destinations, all validators green, and the ten-role panel seal recorded", - "id" : "ADR046-telem-017", - "kind" : "work-item", - "owner" : "ADR046-telem-016; CLI/doctor owner", - "parallelGroup" : "wi:ADR-046-telemetry-audit-and-support", - "prerequisites" : [ - "ADR-046-telemetry-audit-and-support", - "ADR046-telem-016" - ], - "specId" : "ADR-046-telemetry-audit-and-support", - "topologicalRank" : 12, - "wave" : "W5" - }, - { - "blockers" : [], - "destinations" : [ - "`packages/d2b-provider-system-core/src/{host_reconciler.rs,host_status.rs,host_process_audit.rs}`; adapted `nixos-modules/unsafe-local-workloads-json.nix`; `packages/d2b-provider-system-core/tests/host_posture_contract.rs`" - ], - "entryContracts" : [ - "ADR-046-telemetry-audit-and-support" - ], - "exitGate" : "ADR046-W5 exit criteria (ADR-046-validation-and-delivery §4): every spec/work item in this wave Merged with clean destinations, all validators green, and the ten-role panel seal recorded", - "id" : "ADR046-telem-018", - "kind" : "work-item", - "owner" : "ADR046-telem-012 + ADR046-core-001; `Provider/system-core` owner", - "parallelGroup" : "wi:ADR-046-telemetry-audit-and-support", - "prerequisites" : [ - "ADR-046-telemetry-audit-and-support", - "ADR046-core-001", - "ADR046-telem-012" - ], - "specId" : "ADR-046-telemetry-audit-and-support", - "topologicalRank" : 11, - "wave" : "W5" - }, - { - "blockers" : [], - "destinations" : [ - "`packages/d2b-session/` copied verbatim; v3 contract extension renames endpoint purpose values while preserving backward wire compatibility during transition." - ], - "entryContracts" : [ - "ADR-046-telemetry-audit-and-support" - ], - "exitGate" : "ADR046-W5 exit criteria (ADR-046-validation-and-delivery §4): every spec/work item in this wave Merged with clean destinations, all validators green, and the ten-role panel seal recorded", - "id" : "ADR046-telem-019", - "kind" : "work-item", - "owner" : "ADR046-telem-001 (d2b-telemetry must exist first for MetricsSink injection); session owner", - "parallelGroup" : "wi:ADR-046-telemetry-audit-and-support", - "prerequisites" : [ - "ADR-046-telemetry-audit-and-support", - "ADR046-telem-001" - ], - "specId" : "ADR-046-telemetry-audit-and-support", - "topologicalRank" : 11, - "wave" : "W5" - }, - { - "blockers" : [], - "destinations" : [ - "`packages/d2b-session-unix/` copied verbatim." - ], - "entryContracts" : [ - "ADR-046-telemetry-audit-and-support" - ], - "exitGate" : "ADR046-W5 exit criteria (ADR-046-validation-and-delivery §4): every spec/work item in this wave Merged with clean destinations, all validators green, and the ten-role panel seal recorded", - "id" : "ADR046-telem-020", - "kind" : "work-item", - "owner" : "ADR046-telem-019; Unix transport owner", - "parallelGroup" : "wi:ADR-046-telemetry-audit-and-support", - "prerequisites" : [ - "ADR-046-telemetry-audit-and-support", - "ADR046-telem-019" - ], - "specId" : "ADR-046-telemetry-audit-and-support", - "topologicalRank" : 12, - "wave" : "W5" - }, - { - "blockers" : [], - "destinations" : [ - "`packages/d2b-client/` copied; DaemonClient/GuestClient adapted to v3 service packages; `MetadataInput::with_trace` drives `TraceContext` propagation." - ], - "entryContracts" : [ - "ADR-046-telemetry-audit-and-support" - ], - "exitGate" : "ADR046-W5 exit criteria (ADR-046-validation-and-delivery §4): every spec/work item in this wave Merged with clean destinations, all validators green, and the ten-role panel seal recorded", - "id" : "ADR046-telem-021", - "kind" : "work-item", - "owner" : "ADR046-telem-019; client owner", - "parallelGroup" : "wi:ADR-046-telemetry-audit-and-support", - "prerequisites" : [ - "ADR-046-telemetry-audit-and-support", - "ADR046-telem-019" - ], - "specId" : "ADR-046-telemetry-audit-and-support", - "topologicalRank" : 12, - "wave" : "W5" - }, - { - "blockers" : [], - "destinations" : [ - "`packages/d2b-provider/` and `packages/d2b-provider-toolkit/` copied with v3 session admission and bus routing adaptations." - ], - "entryContracts" : [ - "ADR-046-telemetry-audit-and-support" - ], - "exitGate" : "ADR046-W5 exit criteria (ADR-046-validation-and-delivery §4): every spec/work item in this wave Merged with clean destinations, all validators green, and the ten-role panel seal recorded", - "id" : "ADR046-telem-022", - "kind" : "work-item", - "owner" : "ADR046-telem-019 + ADR046-telem-021; Provider owner", - "parallelGroup" : "wi:ADR-046-telemetry-audit-and-support", - "prerequisites" : [ - "ADR-046-telemetry-audit-and-support", - "ADR046-telem-019", - "ADR046-telem-021" - ], - "specId" : "ADR-046-telemetry-audit-and-support", - "topologicalRank" : 13, - "wave" : "W5" - }, - { - "blockers" : [], - "destinations" : [ - "`packages/d2b-provider-observability-otel/src/agent.rs` adapted; ComponentSessionDriver mock becomes shared Provider session fixture." - ], - "entryContracts" : [ - "ADR-046-telemetry-audit-and-support" - ], - "exitGate" : "ADR046-W5 exit criteria (ADR-046-validation-and-delivery §4): every spec/work item in this wave Merged with clean destinations, all validators green, and the ten-role panel seal recorded", - "id" : "ADR046-telem-023", - "kind" : "work-item", - "owner" : "ADR046-telem-022 + ADR046-telem-014; Provider agent / observability-otel owner", - "parallelGroup" : "wi:ADR-046-telemetry-audit-and-support", - "prerequisites" : [ - "ADR-046-telemetry-audit-and-support", - "ADR046-telem-014", - "ADR046-telem-022" - ], - "specId" : "ADR-046-telemetry-audit-and-support", - "topologicalRank" : 14, - "wave" : "W5" - }, - { - "blockers" : [], - "destinations" : [ - "`packages/d2b-bus/src/routing.rs` adapted from `service_v2.rs`; RemoteNodeErrorKind stable code pattern becomes v3 BusErrorKind." - ], - "entryContracts" : [ - "ADR-046-telemetry-audit-and-support" - ], - "exitGate" : "ADR046-W5 exit criteria (ADR-046-validation-and-delivery §4): every spec/work item in this wave Merged with clean destinations, all validators green, and the ten-role panel seal recorded", - "id" : "ADR046-telem-024", - "kind" : "work-item", - "owner" : "ADR046-telem-019 + ADR046-session-003; bus routing owner", - "parallelGroup" : "wi:ADR-046-telemetry-audit-and-support", - "prerequisites" : [ - "ADR-046-telemetry-audit-and-support", - "ADR046-session-003", - "ADR046-telem-019" - ], - "specId" : "ADR-046-telemetry-audit-and-support", - "topologicalRank" : 12, - "wave" : "W5" - }, - { - "blockers" : [], - "destinations" : [ - "`packages/d2b-bus/src/service_router.rs` and `packages/d2b-core-controller/src/provider_effects.rs`." - ], - "entryContracts" : [ - "ADR-046-telemetry-audit-and-support" - ], - "exitGate" : "ADR046-W5 exit criteria (ADR-046-validation-and-delivery §4): every spec/work item in this wave Merged with clean destinations, all validators green, and the ten-role panel seal recorded", - "id" : "ADR046-telem-025", - "kind" : "work-item", - "owner" : "ADR046-telem-022 + ADR046-telem-024; core-controller routing owner", - "parallelGroup" : "wi:ADR-046-telemetry-audit-and-support", - "prerequisites" : [ - "ADR-046-telemetry-audit-and-support", - "ADR046-telem-022", - "ADR046-telem-024" - ], - "specId" : "ADR-046-telemetry-audit-and-support", - "topologicalRank" : 14, - "wave" : "W5" - }, - { - "blockers" : [], - "destinations" : [ - "`packages/d2b-contract-tests/tests/component_session_v2_vectors.rs` and `tests/noise_vectors.rs` copied verbatim." - ], - "entryContracts" : [ - "ADR-046-telemetry-audit-and-support" - ], - "exitGate" : "ADR046-W5 exit criteria (ADR-046-validation-and-delivery §4): every spec/work item in this wave Merged with clean destinations, all validators green, and the ten-role panel seal recorded", - "id" : "ADR046-telem-026", - "kind" : "work-item", - "owner" : "ADR046-telem-019; contract-tests owner", - "parallelGroup" : "wi:ADR-046-telemetry-audit-and-support", - "prerequisites" : [ - "ADR-046-telemetry-audit-and-support", - "ADR046-telem-019" - ], - "specId" : "ADR-046-telemetry-audit-and-support", - "topologicalRank" : 12, - "wave" : "W5" - }, - { - "blockers" : [], - "destinations" : [ - "`packages/d2b-telemetry/src/session_metrics_sink.rs`." - ], - "entryContracts" : [ - "ADR-046-telemetry-audit-and-support" - ], - "exitGate" : "ADR046-W5 exit criteria (ADR-046-validation-and-delivery §4): every spec/work item in this wave Merged with clean destinations, all validators green, and the ten-role panel seal recorded", - "id" : "ADR046-telem-027", - "kind" : "work-item", - "owner" : "ADR046-telem-019 + ADR046-telem-001 + ADR046-telem-003; telemetry/session owner", - "parallelGroup" : "wi:ADR-046-telemetry-audit-and-support", - "prerequisites" : [ - "ADR-046-telemetry-audit-and-support", - "ADR046-telem-001", - "ADR046-telem-003", - "ADR046-telem-019" - ], - "specId" : "ADR-046-telemetry-audit-and-support", - "topologicalRank" : 12, - "wave" : "W5" - }, - { - "blockers" : [], - "destinations" : [ - "`packages/d2b-provider-transport-azure-relay/src/relay_transport.rs`" - ], - "entryContracts" : [ - "ADR-046-provider-transport-azure-relay" - ], - "exitGate" : "ADR046-W6 exit criteria (ADR-046-validation-and-delivery §4): every spec/work item in this wave Merged with clean destinations, all validators green, and the ten-role panel seal recorded", - "id" : "ADR046-transport-relay-001", - "kind" : "work-item", - "owner" : "W0 shared contract root; ComponentSession transport adapter owner", - "parallelGroup" : "wi:ADR-046-provider-transport-azure-relay", - "prerequisites" : [ - "ADR-046-provider-transport-azure-relay" - ], - "specId" : "ADR-046-provider-transport-azure-relay", - "topologicalRank" : 11, - "wave" : "W6" - }, - { - "blockers" : [], - "destinations" : [ - "`packages/d2b-provider-transport-azure-relay/src/credential_client.rs`" + "`packages/d2b-provider-transport-azure-relay/src/credential_client.rs`" ], "entryContracts" : [ "ADR-046-provider-transport-azure-relay" @@ -21453,11 +21505,11 @@ "exitGate" : "ADR046-W6 exit criteria (ADR-046-validation-and-delivery §4): every spec/work item in this wave Merged with clean destinations, all validators green, and the ten-role panel seal recorded", "id" : "ADR046-transport-unix-002", "kind" : "work-item", - "owner" : "ADR046-transport-unix-001; d2b-bus transport layer (ADR046-session-003)", + "owner" : "ADR046-transport-unix-001; d2b-bus transport layer (ADR046-bus-001)", "parallelGroup" : "wi:ADR-046-provider-transport-unix", "prerequisites" : [ "ADR-046-provider-transport-unix", - "ADR046-session-003", + "ADR046-bus-001", "ADR046-transport-unix-001" ], "specId" : "ADR-046-provider-transport-unix", @@ -21559,12 +21611,12 @@ "exitGate" : "ADR046-W6 exit criteria (ADR-046-validation-and-delivery §4): every spec/work item in this wave Merged with clean destinations, all validators green, and the ten-role panel seal recorded", "id" : "ADR046-transport-unix-007", "kind" : "work-item", - "owner" : "ADR046-transport-unix-002 through 006; ADR046-session-003 (d2b-bus ComponentSession method dispatch); ADR046-session-001 (named-stream protocol)", + "owner" : "ADR046-transport-unix-002 through 006; ADR046-bus-001 (d2b-bus ComponentSession method dispatch); ADR046-session-001 (named-stream protocol)", "parallelGroup" : "wi:ADR-046-provider-transport-unix", "prerequisites" : [ "ADR-046-provider-transport-unix", + "ADR046-bus-001", "ADR046-session-001", - "ADR046-session-003", "ADR046-transport-unix-002", "ADR046-transport-unix-003", "ADR046-transport-unix-004", @@ -21859,6 +21911,27 @@ "topologicalRank" : 16, "wave" : "W6" }, + { + "blockers" : [], + "destinations" : [ + "`packages/d2b-core-controller/src/user_session_authority.rs` (or a core/user-agent per-session agent Process under `Provider/system-systemd`); `AuthorityDescriptor` on the session authority" + ], + "entryContracts" : [ + "ADR-046-resources-host-guest-process-user" + ], + "exitGate" : "ADR046-W5 exit criteria (ADR-046-validation-and-delivery §4): every spec/work item in this wave Merged with clean destinations, all validators green, and the ten-role panel seal recorded", + "id" : "ADR046-user-session-001", + "kind" : "work-item", + "owner" : "ADR046-zone-control-019 (authority index); `Provider/system-systemd` (user manager) + core/user-agent owner", + "parallelGroup" : "wi:ADR-046-resources-host-guest-process-user", + "prerequisites" : [ + "ADR-046-resources-host-guest-process-user", + "ADR046-zone-control-019" + ], + "specId" : "ADR-046-resources-host-guest-process-user", + "topologicalRank" : 12, + "wave" : "W5" + }, { "blockers" : [], "destinations" : [ @@ -22280,11 +22353,11 @@ "exitGate" : "ADR046-W6 exit criteria (ADR-046-validation-and-delivery §4): every spec/work item in this wave Merged with clean destinations, all validators green, and the ten-role panel seal recorded", "id" : "ADR046-vsock-001", "kind" : "work-item", - "owner" : "Title: Implement `VsockEffectPort` trait and `OpaqueEndpointId`/`OpaqueBindingId` newtypes; Phase 1; Priority P0; Depends on ADR046-session-003 (OwnedTransport in d2b-session); Owner crate `d2b-provider-transport-vsock`.", + "owner" : "Title: Implement `VsockEffectPort` trait and `OpaqueEndpointId`/`OpaqueBindingId` newtypes; Phase 1; Priority P0; Depends on ADR046-bus-001 (OwnedTransport in d2b-session); Owner crate `d2b-provider-transport-vsock`.", "parallelGroup" : "wi:ADR-046-provider-transport-vsock", "prerequisites" : [ "ADR-046-provider-transport-vsock", - "ADR046-session-003" + "ADR046-bus-001" ], "specId" : "ADR-046-provider-transport-vsock", "topologicalRank" : 11, @@ -22322,11 +22395,11 @@ "exitGate" : "ADR046-W6 exit criteria (ADR-046-validation-and-delivery §4): every spec/work item in this wave Merged with clean destinations, all validators green, and the ten-role panel seal recorded", "id" : "ADR046-vsock-003", "kind" : "work-item", - "owner" : "Title: Implement `VsockTransportService` (OpenTransport/CloseTransport/ObserveTransport); Phase 1; Priority P0; Depends on ADR046-vsock-002 and ADR046-session-003; Owner crate `d2b-provider-transport-vsock`.", + "owner" : "Title: Implement `VsockTransportService` (OpenTransport/CloseTransport/ObserveTransport); Phase 1; Priority P0; Depends on ADR046-vsock-002 and ADR046-bus-001; Owner crate `d2b-provider-transport-vsock`.", "parallelGroup" : "wi:ADR-046-provider-transport-vsock", "prerequisites" : [ "ADR-046-provider-transport-vsock", - "ADR046-session-003", + "ADR046-bus-001", "ADR046-vsock-002" ], "specId" : "ADR-046-provider-transport-vsock", @@ -22427,12 +22500,12 @@ "exitGate" : "ADR046-W6 exit criteria (ADR-046-validation-and-delivery §4): every spec/work item in this wave Merged with clean destinations, all validators green, and the ten-role panel seal recorded", "id" : "ADR046-vvfs-001", "kind" : "work-item", - "owner" : "ADR046-volume-001 (Volume contract types); ADR046-vvfs-007 (Export type); W1; volume-virtiofs Provider owner", + "owner" : "ADR046-volume-001 (Volume contract types); ADR046-vvfs-export-001 (Export type); W1; volume-virtiofs Provider owner", "parallelGroup" : "wi:ADR-046-provider-volume-virtiofs", "prerequisites" : [ "ADR-046-provider-volume-virtiofs", "ADR046-volume-001", - "ADR046-vvfs-007" + "ADR046-vvfs-export-001" ], "specId" : "ADR-046-provider-volume-virtiofs", "topologicalRank" : 12, @@ -22471,14 +22544,14 @@ "exitGate" : "ADR046-W6 exit criteria (ADR-046-validation-and-delivery §4): every spec/work item in this wave Merged with clean destinations, all validators green, and the ten-role panel seal recorded", "id" : "ADR046-vvfs-003", "kind" : "work-item", - "owner" : "ADR046-vvfs-001, ADR046-vvfs-002, ADR046-vvfs-007; ADR046-volume-001; W2; volume-virtiofs controller owner", + "owner" : "ADR046-vvfs-001, ADR046-vvfs-002, ADR046-vvfs-export-001; ADR046-volume-001; W2; volume-virtiofs controller owner", "parallelGroup" : "wi:ADR-046-provider-volume-virtiofs", "prerequisites" : [ "ADR-046-provider-volume-virtiofs", "ADR046-volume-001", "ADR046-vvfs-001", "ADR046-vvfs-002", - "ADR046-vvfs-007" + "ADR046-vvfs-export-001" ], "specId" : "ADR-046-provider-volume-virtiofs", "topologicalRank" : 14, @@ -22559,7 +22632,7 @@ "ADR-046-provider-volume-virtiofs" ], "exitGate" : "ADR046-W6 exit criteria (ADR-046-validation-and-delivery §4): every spec/work item in this wave Merged with clean destinations, all validators green, and the ten-role panel seal recorded", - "id" : "ADR046-vvfs-007", + "id" : "ADR046-vvfs-export-001", "kind" : "work-item", "owner" : "ADR046-volume-001; W1; volume-virtiofs Provider owner", "parallelGroup" : "wi:ADR-046-provider-volume-virtiofs", @@ -22571,6 +22644,27 @@ "topologicalRank" : 11, "wave" : "W6" }, + { + "blockers" : [], + "destinations" : [ + "`packages/d2b-contracts/src/v3/{services,state,identity,provider}.rs` (new v3 namespace); generated stubs regenerated from v3 proto files in `packages/d2b-contracts/proto/v3/`" + ], + "entryContracts" : [ + "ADR-046-resources-zone-control" + ], + "exitGate" : "ADR046-W5 exit criteria (ADR-046-validation-and-delivery §4): every spec/work item in this wave Merged with clean destinations, all validators green, and the ten-role panel seal recorded", + "id" : "ADR046-wire-001", + "kind" : "work-item", + "owner" : "ADR046-zone-control-013", + "parallelGroup" : "wi:ADR-046-resources-zone-control", + "prerequisites" : [ + "ADR-046-resources-zone-control", + "ADR046-zone-control-013" + ], + "specId" : "ADR-046-resources-zone-control", + "topologicalRank" : 11, + "wave" : "W5" + }, { "blockers" : [], "destinations" : [ @@ -22919,6 +23013,7 @@ "parallelGroup" : "wi:core-config-hub:w5", "prerequisites" : [ "ADR-046-resources-zone-control", + "ADR046-core-001", "ADR046-zone-control-001", "ADR046-zone-control-015" ], @@ -23029,6 +23124,7 @@ "parallelGroup" : "wi:core-config-hub:w5", "prerequisites" : [ "ADR-046-resources-zone-control", + "ADR046-core-001", "ADR046-zone-control-001", "ADR046-zone-control-016" ], @@ -23100,95 +23196,6 @@ "specId" : "ADR-046-resources-zone-control", "topologicalRank" : 13, "wave" : "W5" - }, - { - "blockers" : [], - "destinations" : [ - "`packages/d2b-client/src/` (updated for v3 Zone API, replacing ADR45 daemon verbs with Zone resource operations)" - ], - "entryContracts" : [ - "ADR-046-resources-zone-control" - ], - "exitGate" : "ADR046-W5 exit criteria (ADR-046-validation-and-delivery §4): every spec/work item in this wave Merged with clean destinations, all validators green, and the ten-role panel seal recorded", - "id" : "ADR046-zone-control-025", - "kind" : "work-item", - "owner" : "ADR046-zone-control-011, ADR046-zone-control-012, ADR046-zone-control-013, ADR046-zone-control-018", - "parallelGroup" : "wi:ADR-046-resources-zone-control", - "prerequisites" : [ - "ADR-046-resources-zone-control", - "ADR046-zone-control-011", - "ADR046-zone-control-012", - "ADR046-zone-control-013", - "ADR046-zone-control-018" - ], - "specId" : "ADR-046-resources-zone-control", - "topologicalRank" : 13, - "wave" : "W5" - }, - { - "blockers" : [], - "destinations" : [ - "`packages/d2b-contract-tests/tests/policy_provider_crate_layout.rs` (new file; gated under D2B_FIXTURES in existing `tests/tools/rust-workspace-checks.sh`)" - ], - "entryContracts" : [ - "ADR-046-resources-zone-control" - ], - "exitGate" : "ADR046-W5 exit criteria (ADR-046-validation-and-delivery §4): every spec/work item in this wave Merged with clean destinations, all validators green, and the ten-role panel seal recorded", - "id" : "ADR046-zone-control-026", - "kind" : "work-item", - "owner" : "ADR046-zone-control-003; workspace policy owner", - "parallelGroup" : "wi:ADR-046-resources-zone-control", - "prerequisites" : [ - "ADR-046-resources-zone-control", - "ADR046-zone-control-003" - ], - "specId" : "ADR-046-resources-zone-control", - "topologicalRank" : 13, - "wave" : "W5" - }, - { - "blockers" : [], - "destinations" : [ - "`packages/d2b-provider/src/agent.rs` (v3 provider agent dispatch); `packages/d2b-contracts/src/v3/provider_registry.rs` (v3 provider registry wire contract)" - ], - "entryContracts" : [ - "ADR-046-resources-zone-control" - ], - "exitGate" : "ADR046-W5 exit criteria (ADR-046-validation-and-delivery §4): every spec/work item in this wave Merged with clean destinations, all validators green, and the ten-role panel seal recorded", - "id" : "ADR046-zone-control-027", - "kind" : "work-item", - "owner" : "ADR046-zone-control-011, ADR046-zone-control-017, ADR046-zone-control-018", - "parallelGroup" : "wi:ADR-046-resources-zone-control", - "prerequisites" : [ - "ADR-046-resources-zone-control", - "ADR046-zone-control-011", - "ADR046-zone-control-017", - "ADR046-zone-control-018" - ], - "specId" : "ADR-046-resources-zone-control", - "topologicalRank" : 13, - "wave" : "W5" - }, - { - "blockers" : [], - "destinations" : [ - "`packages/d2b-contracts/src/v3/{services,state,identity,provider}.rs` (new v3 namespace); generated stubs regenerated from v3 proto files in `packages/d2b-contracts/proto/v3/`" - ], - "entryContracts" : [ - "ADR-046-resources-zone-control" - ], - "exitGate" : "ADR046-W5 exit criteria (ADR-046-validation-and-delivery §4): every spec/work item in this wave Merged with clean destinations, all validators green, and the ten-role panel seal recorded", - "id" : "ADR046-zone-control-028", - "kind" : "work-item", - "owner" : "ADR046-zone-control-013", - "parallelGroup" : "wi:ADR-046-resources-zone-control", - "prerequisites" : [ - "ADR-046-resources-zone-control", - "ADR046-zone-control-013" - ], - "specId" : "ADR-046-resources-zone-control", - "topologicalRank" : 11, - "wave" : "W5" } ], "schemaVersion" : 1, diff --git a/docs/specs/ADR-046-implementation-graph.md b/docs/specs/ADR-046-implementation-graph.md index 5907276c3..45786f5ee 100644 --- a/docs/specs/ADR-046-implementation-graph.md +++ b/docs/specs/ADR-046-implementation-graph.md @@ -271,7 +271,11 @@ single-wave while the exact seven ordering edges above remain unchanged. - `ADR046-provider-004` owns the common D098 Service/Binding base DTOs and schemas; the four implementation Providers own only strict extensions and controllers. - `ADR046-zone-control-024` owns the shared Core-derived `physical-usb-backing` tuple; both the security-key and USB effect DAGs depend on it. - Every `ADR046-security-key-*` dependency in `Dependency/owner` is encoded. The dependency subgraph is acyclic and uses no generator tie-break. -- File-overlap barriers cover only the shared core configuration/cleanup files. Soft cross-Provider integration order remains file-disjoint and concurrent. +- Seven file-overlap barriers cover only the shared core + configuration/cleanup files. Each appears both as a + `file-overlap-order` edge and in the dependent node's `prerequisites`, so the + ready-wave query enforces it. Soft cross-Provider integration order remains + file-disjoint and concurrent. - No repository generator exists at this Proposed stage. `ADR046-streamline-001`/`024` and `ADR046-delivery-004`/`009` own the future canonical implementation and policy gate. ## Ready-wave algorithm diff --git a/docs/specs/ADR-046-reset-and-cutover.md b/docs/specs/ADR-046-reset-and-cutover.md index 4408f2776..4e2030974 100644 --- a/docs/specs/ADR-046-reset-and-cutover.md +++ b/docs/specs/ADR-046-reset-and-cutover.md @@ -1487,7 +1487,7 @@ owns the destination. | `d2b-priv-broker.service`, `d2b-priv-broker.socket` | §7, `production-reachable` | Preserve until Phase 10 gate clears, then Destroy | Zone-local privileged broker (`ADR046-provider-003`) | `ADR046-provider-003` | | `/run/d2b/d2bd.sock`, `/run/d2b/broker.sock` | §7 | Destroy (Phase 3, boot-scoped) | `/run/d2b/z-/...` fresh sockets | `ADR046-core-controllers` "Process model" | | `/run/d2b/allocator.sock` | §7, config-ref/schema-only, engine not live | Destroy (Phase 3; never adopted — no live allocator process to quiesce) | No successor socket; provisioning integrates into fixed core controllers | `ADR046-core-001` | -| `d2b-realm-router` PeerSession/MuxSession/`WorkloadOp`/`RealmMethod` wire | multiple rows, `dead-reachable`/`production-reachable` | Preserve (compiled into binary; Destroy only when the binary itself is retired at Phase 10) | ComponentSession/d2b-bus/`ResourceOp` | `ADR046-session-001`, `ADR046-session-003`, `ADR046-api-001` | +| `d2b-realm-router` PeerSession/MuxSession/`WorkloadOp`/`RealmMethod` wire | multiple rows, `dead-reachable`/`production-reachable` | Preserve (compiled into binary; Destroy only when the binary itself is retired at Phase 10) | ComponentSession/d2b-bus/`ResourceOp` | `ADR046-session-001`, `ADR046-bus-001`, `ADR046-api-001` | | `d2b-unsafe-local-helper` binary, `DaemonToUnsafeLocalHelper` protocol | §7, `production-reachable` | Preserve until Process Provider supervisor ticket migration lands, then Destroy | User-only `Host` Process supervisor | `ADR046-primitives-003` | | `d2b-guest-shell-runner` | `production-reachable` | Preserve until user-only Host shell Process parity, then Destroy | `Process` child of user-only Host | `ADR046-primitives-003` | | `~/.local/state/d2b/unsafe-local-scopes.json` | §7 evidence, per-user scope ledger | Adopt into a declared user-only Host state Volume — this ledger is real private per-user content that passes the storage-need test, not derivable from status/core ledger — via a per-user migration `EphemeralProcess` | User-only Host declared component state Volume | `ADR046-primitives-003` | diff --git a/docs/specs/ADR-046-resources-credential.md b/docs/specs/ADR-046-resources-credential.md index 69eb8b206..438ac223c 100644 --- a/docs/specs/ADR-046-resources-credential.md +++ b/docs/specs/ADR-046-resources-credential.md @@ -1898,7 +1898,7 @@ config formalizes this as an `OpaqueAzureRef` with the same charset restriction. | Field | Value | | --- | --- | | Work item ID | `ADR046-credential-002` | -| Dependency/owner | `ADR046-credential-001`; `ADR046-api-001` (resource API); `ADR046-session-003` (d2b-bus); Credential service owner | +| Dependency/owner | `ADR046-credential-001`; `ADR046-api-001` (resource API); `ADR046-bus-001` (d2b-bus); Credential service owner | | Current source | `packages/d2b-realm-provider/src/provider.rs:CredentialProvider` (status-only trait); `d2b-contracts/proto/v2/provider_credential.proto` (main: Health, Capabilities, Status, AcquireLease, RefreshLease, RevokeLease) | | Reuse source | main `a1cc0b2d`: `packages/d2b-contracts/proto/v2/provider_credential.proto` method names | | Reuse action | adapt | diff --git a/docs/specs/ADR-046-resources-host-guest-process-user.md b/docs/specs/ADR-046-resources-host-guest-process-user.md index 1639dd26d..add4fe016 100644 --- a/docs/specs/ADR-046-resources-host-guest-process-user.md +++ b/docs/specs/ADR-046-resources-host-guest-process-user.md @@ -2565,11 +2565,11 @@ A work item whose `Destination` row introduces a new `d2b-provider-*` crate must | Validation | Runtime/integration tests 19–23 from the "Tests for Nix configuration and ResourceType-specific lifecycle" section in this spec; additionally: `GenerationDiff` hermetic unit tests (new/changed/unchanged/removed classification); `bundleSha256` integrity failure aborts and emits correct audit event; `catalogSha256` mismatch aborts bundle; UpdateSpec optimistic lock conflict retried correctly; Watch `Deleted` revision events consumed (not polling GET) to track cleanup completion; Zone `phase=Pending` while intents outstanding; Zone `phase=Degraded` immediately when any cleanup outstanding (no grace window); Zone `phase=Ready` when complete; activation returns after durable queue commit, not after reconcile; same-name `managedBy=controller` OR `managedBy=api` collision emits per-item `config-collision` error without seizing resource, other intents continue; unchanged spec refreshes `configurationGeneration` without triggering controller reconcile; final deletion: atomic tx commits `Deleted` revision event + row/index removal only; audit append follows committed revision via dedup/exactly-once recovery (NOT part of atomic tx); recovery retry produces no duplicate audit record; prior bundle record released after cleanup-complete and retention count exceeded; activation with zero diff and identical bundleSha256 is a no-op | | Removal proof | None — net-new controller; no prior owner to remove | -### ADR046-exec-024 +### ADR046-user-session-001 | Field | Value | | --- | --- | -| Work item ID | `ADR046-exec-024` | +| Work item ID | `ADR046-user-session-001` | | Dependency/owner | ADR046-zone-control-019 (authority index); `Provider/system-systemd` (user manager) + core/user-agent owner | | Current source | None — the fixed user-session authority is today ambient prose across the display/audio/clipboard/notification/secret-service dossiers; no named owner exists | | Reuse source | `Provider/system-systemd` user-manager scope; D077 EffectPort/LaunchTicket FD handoff | diff --git a/docs/specs/ADR-046-resources-zone-control.md b/docs/specs/ADR-046-resources-zone-control.md index f9d7b2e50..e7d568e8f 100644 --- a/docs/specs/ADR-046-resources-zone-control.md +++ b/docs/specs/ADR-046-resources-zone-control.md @@ -918,7 +918,7 @@ paths or the workspace/package policy check fails the build: #### 4.8.2 Policy enforcement The workspace policy check (`packages/d2b-contract-tests/tests/policy_provider_crate_layout.rs`, -implemented in work item ADR046-zone-control-026) walks every `packages/d2b-provider-*` +implemented in work item ADR046-pkg-001) walks every `packages/d2b-provider-*` crate directory in the workspace and asserts all four paths exist. A missing `src/`, `tests/`, `integration/`, or `README.md` in any Provider crate is a **hard policy failure** that blocks `make test-policy` (and therefore @@ -4179,7 +4179,7 @@ None of the following exist in baseline: | Reuse source | main `a1cc0b2d`: any `d2b-provider-toolkit` registry/descriptor patterns named in ADR046-zone-control-017 sub-items | | Reuse action | adapt | | Destination | `packages/d2b-contracts/src/v3/provider.rs`; `packages/d2b-core-controller/src/provider_lifecycle.rs`; `packages/d2b-core-controller/src/api_catalog.rs` | -| Detailed design | Provider resource schema with all spec fields from §4.3, including resolved component bounds (max 8 controllers, 8 services, 32 worker templates, 16 ResourceTypes per controller); trust/conformance/config validation; component descriptor validation; dependency alias resolution; API binding with permission intersection; lifecycle policies; Nix Provider installation options; Provider crate layout enforcement per §4.8 (see ADR046-zone-control-026) Primary reuse disposition: `adapt`. Preserved source-plan detail: extract and adapt (`workload_lists_and_advertises`/`display_fails_closed_when_unsupported` conformance behavior; `RuntimeCapabilitySet`/`WorkloadCapabilitySet`/`NodeCapabilitySet` → Provider component `supportedCapabilities`; `ProviderError`/`RetryHint` → Provider lifecycle error schema; `ProviderId` → Provider `metadata.name` validator; `ProcessRole` variants → Provider component type identifiers). | +| Detailed design | Provider resource schema with all spec fields from §4.3, including resolved component bounds (max 8 controllers, 8 services, 32 worker templates, 16 ResourceTypes per controller); trust/conformance/config validation; component descriptor validation; dependency alias resolution; API binding with permission intersection; lifecycle policies; Nix Provider installation options; Provider crate layout enforcement per §4.8 (see ADR046-pkg-001) Primary reuse disposition: `adapt`. Preserved source-plan detail: extract and adapt (`workload_lists_and_advertises`/`display_fails_closed_when_unsupported` conformance behavior; `RuntimeCapabilitySet`/`WorkloadCapabilitySet`/`NodeCapabilitySet` → Provider component `supportedCapabilities`; `ProviderError`/`RetryHint` → Provider lifecycle error schema; `ProviderId` → Provider `metadata.name` validator; `ProcessRole` variants → Provider component type identifiers). | | Integration | Zone config publication installs Provider resources; API catalog handler binds exported ResourceTypes; Provider/system-core and Provider/system-minijail are bootstrap exceptions with pre-created records | | Data migration | Full reset; Provider packages recompiled and re-registered per new schema | | Validation | All §15.3 Provider tests including the resolved bounds checks | @@ -4375,11 +4375,11 @@ Evidence class for all: `main-reuse-source`. | Validation | `provider-registry-drain-waiter-race-safe` (ported from both notify-race tests), `provider-registry-publish-validates-snapshot-before-swap`, `provider-rpc-proxy-placement-exact` (ported from `provider_and_user_agent_session_identities_are_placement_exact`), `provider-agent-adapter-rejects-non-monotone-attachment-indexes`, `provider-server-shutdown-drains-in-flight-requests`, `provider-conformance-health-inspection-observability-sequence` | | Removal proof | `d2b-realm-provider` trait crate removed after v3 Provider resource + registry integration (§16.5) | -### ADR046-zone-control-027 +### ADR046-provider-agent-001 | Field | Value | | --- | --- | -| Work item ID | `ADR046-zone-control-027` | +| Work item ID | `ADR046-provider-agent-001` | | Dependency/owner | ADR046-zone-control-011, ADR046-zone-control-017, ADR046-zone-control-018 | | Current source | main `a1cc0b2d`: `packages/d2b-gateway-runtime/src/provider_agent.rs` (`ProviderAgentError::{UnregisteredAdapter,RegistryNotAccepting,RegistrationRejected,InvalidAuditCapacity,SessionClosed,ProtocolViolation}`, `ProviderAgentAuditOutcome`, `ProviderAgentAuditEvent`, `ProviderAgentProcess::from_registry/from_registry_with/provider_type/service_names/audit_snapshot/serve`, `run_registered`, bounded in-memory audit ring, frame dispatch loop: semaphore in-flight limit, service/method routing, negative-timeout guard, `SessionClosed` termination, `ProtocolViolation` audit + terminate — lines 31–452; tests `standalone_entrypoint_fails_without_registration`, `audit_capacity_is_bounded` — lines 454–486); `packages/d2b-contracts/src/provider_registry_v2.rs` (`ProviderRegistryV2` wire contract, `ProviderRegistryEntryV2::validate` with provider-id derivation rule, schema fingerprint, scope-digest, generation exactness, `TrustedFirstPartyInProcess` placement requirement, `ProviderIntentId` label rules `max 128 bytes`, `MAX_PROVIDER_REGISTRY_ENTRIES`, `MAX_PROVIDER_MAPPING_IDS=64`, `ProviderBindingV2` non-exhaustive + `UnsupportedProviderBindingV2` fallback, `ProviderRegistryV2::validate` sort/unique/count checks — lines 23–566; tests `validates_closed_local_runtime_mapping`, `validates_closed_local_observability_mapping`, `serializes_declared_mapping_axes_as_closed_variants`, `rejects_duplicate_or_unbounded_mapping_ids`, `local_storage_binding_realm_must_match_descriptor_placement`, `rejects_generation_and_exact_identity_mismatches`, `contradictory_binding_realm_json_is_unrepresentable`, `unknown_binding_axis_remains_rejected_on_the_wire`, `identity_mismatch_messages_name_the_failed_contract`, `accepts_explicit_empty_registry` — lines 722–1044) | | Reuse action | adapt | @@ -4390,11 +4390,11 @@ Evidence class for all: `main-reuse-source`. | Validation | `provider-agent-dispatch-unsupported-service-returns-ttrpc-error`, `provider-agent-negative-timeout-rejected`, `provider-agent-session-closed-terminates-serve-loop`, `provider-agent-audit-ring-capacity-bounded` (ported from `audit_capacity_is_bounded`), `provider-registry-entry-fingerprint-generation-exact` (ported from `rejects_generation_and_exact_identity_mismatches`), `provider-registry-unknown-axis-fallback-non-exhaustive` (ported from `unknown_binding_axis_remains_rejected_on_the_wire`), `provider-registry-duplicate-ids-rejected` (ported from `rejects_duplicate_or_unbounded_mapping_ids`) | | Removal proof | Not applicable; new implementation | -### ADR046-zone-control-025 +### ADR046-client-001 | Field | Value | | --- | --- | -| Work item ID | `ADR046-zone-control-025` | +| Work item ID | `ADR046-client-001` | | Dependency/owner | ADR046-zone-control-011, ADR046-zone-control-012, ADR046-zone-control-013, ADR046-zone-control-018 | | Current source | main `a1cc0b2d`: `packages/d2b-client/src/client.rs` (`WallClock`, `MetadataInput`, `RetryPolicy` 1..8 attempts, `CallOptions`, `CancellationToken`, `Client::new/with_clock/connect`, `ConnectedClient` methods incl. `session_generation/session_limits/service/invoke/invoke_with_attachments/named_stream/open_server_stream`, `prepare_typed_request/prepare_operation_context`, `can_retry/retryable_failure/validate_outbound_attachments/validate_reply/service_package/map_remote_kind/map_retry` — lines 35–921); `packages/d2b-client/src/session.rs` (`ComponentSessionConnector` trait, `NamedStream` lifecycle, `StreamDispatcher`, `SharedDriver`, aggregate-queue-bound test — lines 24–626); `packages/d2b-client/src/target.rs` (`ServiceOwner`, `TargetInput`, `TransportKind`, `RouteRecord`, `ResolvedTarget`, `TargetResolver`, `RouteTable` — lines 7–228); `packages/d2b-client/src/service.rs` (`ServiceKind`, `GeneratedClient`, `MethodHandle`, `ServiceHandle::new/kind/generated/proxy/method/invoke` — lines 21–184); `packages/d2b-client/src/daemon_service.rs` (`DaemonClient::new/session_generation/connected/resolve/inspect/lifecycle/open_terminal`, `DaemonTerminal`, `daemon_call_options`, `ensure_daemon_outcome`, `map_ttrpc_error`, test `redacted_terminal_debug_payload` — lines 29–689); `packages/d2b-client/src/host_socket.rs` (`HostSocketConnector::new/from_seqpacket_fd`, `local_daemon_endpoint_identity`, `ComponentSessionConnector::connect` — lines 252–383); `packages/d2b-client/src/error.rs` (`RemoteErrorKind`, `RetryClass`, `ClientError` — lines 5–128) | | Reuse action | adapt | @@ -4405,11 +4405,11 @@ Evidence class for all: `main-reuse-source`. | Validation | `client-retry-policy-max-8-attempts-enforced`, `client-named-stream-close-local-then-remote-close-transitions-closed`, `client-route-table-ambiguous-route-rejected`, `client-host-socket-peer-uid-verified-on-connect`, `client-retryable-failure-only-safe-mutations` | | Removal proof | `DaemonMethod` v2 verb enum retired after all v2 daemon operations migrated to Zone API | -### ADR046-zone-control-028 +### ADR046-wire-001 | Field | Value | | --- | --- | -| Work item ID | `ADR046-zone-control-028` | +| Work item ID | `ADR046-wire-001` | | Dependency/owner | ADR046-zone-control-013 | | Current source | main `a1cc0b2d`: `packages/d2b-contracts/src/v2_services.rs` (`MethodSpec{mutating,stream_kind,...}`, `ServiceSpec`, `SERVICE_INVENTORY` covering 20+ services and all provider services, `service_schema_fingerprint`, `public_daemon_schema_fingerprint`, `direct_guest_schema_fingerprint`, `StrictWireMessage`, `decode_strict`, `encode_strict`, `admit_metadata`, `TerminalStreamValidator`, `ServerStreamLease`, `RedactedTerminalFrame`, stream-name validators — lines 204–1004; tests `public_endpoint_fingerprint_binds_both_services_dependencies_and_order`, `public_endpoint_fingerprint_binds_daemon_and_guest_method_descriptors`, `direct_guest_fingerprint_binds_activation_and_remains_separate_from_public_endpoint` — lines 872–1030); `packages/d2b-contracts/src/v2_state.rs` (constants: `STATE_SCHEMA_VERSION=2`, `STATE_SCHEMA_GENERATION=1`, `MAX_JSON_DOCUMENT_BYTES=1_048_576`, `MAX_INVENTORY_ROWS=4096`, `MAX_LOCKS=1024`, `MAX_LOCK_DEPENDENCIES=32`, `MAX_DISCOVERY_OBSERVATIONS=4096`, `MAX_AUDIT_RECORD_BYTES=8192`, `MAX_AUDIT_RECORDS_PER_SEGMENT=16384`, `MAX_AUDIT_SEGMENT_BYTES=64*1024*1024`, `MAX_AUDIT_RETENTION_DAYS=14`, `MAX_LOCK_DEADLINE_MS=300_000`; types: `Digest`, `Generation`, `OwnershipEpoch`, `SafeJsonInteger`, `StorageCategory`, `StateEnvelope`, `CanonicalPayloadVerifier`, `AtomicWritePhase`, `RunnerEvidence`, audit types incl. `AuditRecord/Segment/Checkpoint/Gap`, `detect_audit_gap`, `AuditRetentionPolicy`, lock types: `LockClass`, `LockSpec`, `SyncInventory`, `LeaseRecord`); `packages/d2b-contracts/src/v2_identity.rs` (`IdentityError` 13 variants, canonical name rules `^[a-z][a-z0-9-]*$` max 63 bytes start-lowercase-letter, `RealmPath` label/separator rules, `ProviderType::ALL` 11 types + `as_str()` — lines 11–250); `packages/d2b-contracts/src/v2_provider.rs` (bounded opaque IDs `[a-z][a-z0-9-]{0,63}`: `ImplementationId`, `OperationId`, `IdempotencyKey`, `PlanId`, `HandleId`, `LeaseId`, `TransferId`, `PROVIDER_CONTRACT_FINGERPRINT`, `ProviderContractError` 34 variants, `Fingerprint` 64 lowercase-hex chars — lines 18–219); `packages/d2b-contracts/src/generated_v2_services/` (all 40+ generated ttrpc client/server stubs for v2 services) | | Reuse action | adapt | @@ -4470,11 +4470,11 @@ Evidence class for all: `main-reuse-source`. | Validation | All Phase 3 runtime and cleanup tests in §15.8 (`nix-runtime-bundledigest-integrity`, `nix-runtime-generation-monotone`, `nix-runtime-zoneuid-mismatch-rejected`, `nix-runtime-zonename-mismatch-rejected`, `nix-runtime-activation-nonblocking`, `nix-runtime-provider-config-invalid-continues`, all `cleanup-*` and `rollback-*` tests) | | Removal proof | `d2bd/src/lib.rs` config-load at lines 1408 and 16741 removed after Zone configuration publication handler reaches parity; `realm-controller-config-json.nix` and `realm-identity-config-json.nix` Nix bundle-emit removed after resource compiler reaches parity | -### ADR046-zone-control-026 +### ADR046-pkg-001 | Field | Value | | --- | --- | -| Work item ID | `ADR046-zone-control-026` | +| Work item ID | `ADR046-pkg-001` | | Dependency/owner | ADR046-zone-control-003; workspace policy owner | | Current source | `packages/d2b-contract-tests/tests/policy_contracts.rs` lines 5–6 (D2B_FIXTURES gate / workspace-checks integration pattern — `implemented-and-reachable`, baseline `b5ddbed6`); `packages/d2b-contract-tests/tests/static_invariants.rs` (hermetic policy test structure — `implemented-and-reachable`); `tests/tools/rust-workspace-checks.sh` (D2B_FIXTURES step shell harness — `implemented-and-reachable`); AGENTS.md "Naming conventions" section (`-` workspace sort rules — `implemented-and-reachable`); `packages/d2b-realm-core/src/ids.rs` `LABEL_PATTERN` / `MAX_ID_LEN` (name regex reused for crate name token validation — `implemented-and-reachable`) | | Reuse source | None from main; workspace policy tests are repo-specific | diff --git a/docs/specs/ADR-046-security-and-threat-model.md b/docs/specs/ADR-046-security-and-threat-model.md index 714c999a4..07e35a484 100644 --- a/docs/specs/ADR-046-security-and-threat-model.md +++ b/docs/specs/ADR-046-security-and-threat-model.md @@ -1548,12 +1548,12 @@ targeted. **`d2b zone doctor`.** Resource status reads only; no resource names, paths, `argv`, or PIDs in output; includes an audit hash-chain integrity check -(`ADR-046-telemetry-audit-and-support`, `ADR046-telem-016`). +(`ADR-046-telemetry-audit-and-support`, `ADR046-doctor-001`). **`d2b zone support-bundle`.** No spec bytes and no `metadata.name` in the bundle; metadata and status only. When a Provider is quarantined, the bundle reports `bundle_completeness: "partial"` rather than silently omitting the -gap or blocking entirely (`ADR046-telem-017`). This is the concrete tool an +gap or blocking entirely (`ADR046-doctor-002`). This is the concrete tool an operator or a coordinated-disclosure responder uses to gather evidence without themselves becoming a redaction bypass — it is bound by exactly the same audit/status redaction rules as every other read path in @@ -1562,7 +1562,7 @@ same audit/status redaction rules as every other read path in **`d2b zone audit export`.** Admin-only (`audit-export` verb); hash-chain breaks are reported inline in the export stream rather than silently truncating history; output carries no old field names (`realm`/`node`/ -`workload_id`) and no path/`argv` content (`ADR046-telem-015`). +`workload_id`) and no path/`argv` content (`ADR046-audit-004`). **Coordination with disclosure policy.** `SECURITY.md`'s GitHub Security Advisory channel, response-time targets (7-day acknowledgment, 30-day @@ -1927,7 +1927,7 @@ close. Each maps to the attacker class it is scoped against. | Field | Value | | --- | --- | | Work item ID | `ADR046-security-002` | -| Dependency/owner | `ADR046-session-001`/`ADR046-session-003` (ComponentSession/d2b-bus implementation) | +| Dependency/owner | `ADR046-session-001`/`ADR046-bus-001` (ComponentSession/d2b-bus implementation) | | Current source | main `a1cc0b2d`: `d2b-session/tests/noise_vectors.rs`, `d2b-session/tests/component_session.rs`, `d2b-session-unix/tests/unix_session.rs` | | Reuse source | Same main commit/paths | | Reuse action | adapt | @@ -1959,7 +1959,7 @@ close. Each maps to the attacker class it is scoped against. | Field | Value | | --- | --- | | Work item ID | `ADR046-security-004` | -| Dependency/owner | `ADR046-routing-005`/`ADR046-session-003` (ZoneLink/d2b-bus relay implementation) | +| Dependency/owner | `ADR046-routing-005`/`ADR046-bus-001` (ZoneLink/d2b-bus relay implementation) | | Current source | None (v3 ZoneLink relay is `ADR-only`) | | Reuse source | None | | Reuse action | adapt | @@ -2087,7 +2087,7 @@ close. Each maps to the attacker class it is scoped against. | Field | Value | | --- | --- | | Work item ID | `ADR046-security-012` | -| Dependency/owner | `ADR046-telem-013` (privileged audit durability) | +| Dependency/owner | `ADR046-audit-002` (privileged audit durability) | | Current source | `packages/d2bd/src/daemon_audit.rs` (existing audit-write path, adapted target) | | Reuse source | Same v3 path | | Reuse action | adapt | @@ -2119,7 +2119,7 @@ close. Each maps to the attacker class it is scoped against. | Field | Value | | --- | --- | | Work item ID | `ADR046-security-014` | -| Dependency/owner | `ADR046-telem-016`/`ADR046-telem-017` (doctor/support-bundle commands) | +| Dependency/owner | `ADR046-doctor-001`/`ADR046-doctor-002` (doctor/support-bundle commands) | | Current source | None (both commands are `ADR-only`) | | Reuse source | None | | Reuse action | adapt | @@ -2135,7 +2135,7 @@ close. Each maps to the attacker class it is scoped against. | Field | Value | | --- | --- | | Work item ID | `ADR046-security-015` | -| Dependency/owner | `ADR046-telem-012` (`StateReset` record), Volume/Credential lifecycle work items | +| Dependency/owner | `ADR046-audit-001` (`StateReset` record), Volume/Credential lifecycle work items | | Current source | Historical main ADR 0045 factory-reset design (`a1cc0b2d^:docs/adr/0045-provider-and-transport-framework.md`, reset process overview and apply-command verification steps) — reused only as a design precedent for atomicity/fail-closed sequencing, not as v3 architecture (see [Reset boundary](#reset-boundary) for the explicitly excluded assumptions) | | Reuse source | Same historical commit, sequencing pattern only (no code reuse; historical implementation was bash/systemd-generation-based and does not exist in any Rust crate) | | Reuse action | adapt | diff --git a/docs/specs/ADR-046-spec-set.json b/docs/specs/ADR-046-spec-set.json index 95ff5543f..13ea2e5cb 100644 --- a/docs/specs/ADR-046-spec-set.json +++ b/docs/specs/ADR-046-spec-set.json @@ -18,7 +18,9 @@ "status" : "Proposed", "supersedes" : "Current v3 `d2b` CLI contract (`packages/d2b/src/lib.rs` at baseline)", "version" : 1, - "workItemPrefix" : "cli" + "workItemPrefixes" : [ + "cli" + ] }, { "dependsOn" : [ @@ -32,7 +34,9 @@ "status" : "Proposed", "supersedes" : "Current ProcessRole/VmProcessDag as the public process model", "version" : 1, - "workItemPrefix" : "process" + "workItemPrefixes" : [ + "process" + ] }, { "dependsOn" : [ @@ -40,12 +44,15 @@ "ADR-046-terminology-and-identities" ], "path" : "docs/specs/ADR-046-componentsession-and-bus.md", - "sha256" : "dedab5286a5c324b9c1d3765d9c1230e3ad684a65a6fa15e8c48cc4d2b5f2164", + "sha256" : "2eca9b7d988406889a0fbd5062abb35e5c56c67c6df4f4385dab3d3e89d21904", "specId" : "ADR-046-componentsession-and-bus", "status" : "Proposed", "supersedes" : "Current v3 Realm PeerSession and ad hoc guest/user/public IPC", "version" : 1, - "workItemPrefix" : "session" + "workItemPrefixes" : [ + "bus", + "session" + ] }, { "dependsOn" : [ @@ -60,7 +67,9 @@ "status" : "Proposed", "supersedes" : "Current daemon-global hard-coded orchestration ownership", "version" : 1, - "workItemPrefix" : "core" + "workItemPrefixes" : [ + "core" + ] }, { "dependsOn" : [], @@ -70,7 +79,7 @@ "status" : "Proposed", "supersedes" : null, "version" : 1, - "workItemPrefix" : null + "workItemPrefixes" : [] }, { "dependsOn" : [], @@ -80,7 +89,9 @@ "status" : "Proposed", "supersedes" : null, "version" : 1, - "workItemPrefix" : "decisions" + "workItemPrefixes" : [ + "decisions" + ] }, { "dependsOn" : [ @@ -101,12 +112,14 @@ "ADR-046-zone-routing" ], "path" : "docs/specs/ADR-046-feasibility-and-spikes.md", - "sha256" : "4ecfb8e3e90ac932f019424e5003897c3809647e3ffd0c9c72c9faab476b147f", + "sha256" : "c6f82e63c4b7b76865510fcf151813e7feb66c2c72e85717c0e8d41b2b6ebf4e", "specId" : "ADR-046-feasibility-and-spikes", "status" : "Proposed", "supersedes" : null, "version" : 1, - "workItemPrefix" : "feasibility" + "workItemPrefixes" : [ + "feasibility" + ] }, { "dependsOn" : [ @@ -122,7 +135,9 @@ "status" : "Proposed", "supersedes" : "Current `nixos-modules/options-realms*.nix`, `options-envs.nix`, `options-vms.nix`, `index.nix`, `bundle*.nix`, `*-json.nix`, and generated `/etc/d2b/*.json`", "version" : 2, - "workItemPrefix" : "nix" + "workItemPrefixes" : [ + "nix" + ] }, { "dependsOn" : [ @@ -135,7 +150,9 @@ "status" : "Proposed", "supersedes" : "Current implementation-shaped ProcessRole composition", "version" : 1, - "workItemPrefix" : "primitives" + "workItemPrefixes" : [ + "primitives" + ] }, { "dependsOn" : [ @@ -157,7 +174,9 @@ "status" : "Proposed", "supersedes" : "Current `d2b switch`/`boot`/`test`/`rollback`/`build`/`generations`/`gc`/`migrate` top-level verbs in `packages/d2b/src/lib.rs` and hardlink-farm activation in `packages/d2b-host/src/hardlink_farm.rs`", "version" : 3, - "workItemPrefix" : "activation" + "workItemPrefixes" : [ + "activation" + ] }, { "dependsOn" : [ @@ -180,7 +199,9 @@ "status" : "Proposed", "supersedes" : "`nixos-modules/components/audio/host.nix`, `nixos-modules/components/audio/guest.nix`, `packages/d2b-core/src/audio_policy.rs`, `packages/d2bd/src/audio_dispatch.rs`, `packages/d2bd/src/audio_host_controller.rs`, `packages/d2b-host/src/audio_argv.rs`", "version" : 10, - "workItemPrefix" : "audio" + "workItemPrefixes" : [ + "audio" + ] }, { "dependsOn" : [ @@ -199,7 +220,9 @@ "status" : "Proposed", "supersedes" : "`nixos-modules/clipboard.nix` (v3 migration), ADR-046-provider-clipboard-wayland v1", "version" : 2, - "workItemPrefix" : "clipboard" + "workItemPrefixes" : [ + "clipboard" + ] }, { "dependsOn" : [ @@ -217,7 +240,9 @@ "status" : "Proposed", "supersedes" : "No Host login/token chains; no direct `EntraCredentialClient` production egress; no `DefaultAzureCredential`, environment, DBus, browser, or path discovery", "version" : 2, - "workItemPrefix" : "cred-entra" + "workItemPrefixes" : [ + "cred-entra" + ] }, { "dependsOn" : [ @@ -229,12 +254,15 @@ "ADR-046-telemetry-audit-and-support" ], "path" : "docs/specs/providers/ADR-046-provider-credential-managed-identity.md", - "sha256" : "192348ac96f06c90fadd73be9d437c81c4e742cd04415809c3b70cf4cdc30940", + "sha256" : "8e5a211b4b74a29096605a23b9e90f85ee44363b1c6b11a7b6c25f82728cf821", "specId" : "ADR-046-provider-credential-managed-identity", "status" : "Proposed", "supersedes" : "Current v3 `ManagedIdentityRef`, `managed_identity_client_id` ACA config field, `CredentialProvider` trait (status/enrollment-only) in `d2b-realm-provider/src/credential.rs` and `provider.rs`", "version" : 1, - "workItemPrefix" : "cred-mi" + "workItemPrefixes" : [ + "cred-mi", + "mi-topology" + ] }, { "dependsOn" : [ @@ -253,7 +281,9 @@ "status" : "Proposed", "supersedes" : "v2 `SecretServiceCredentialProvider` / `SecretServiceCredentialProviderFactory` in `d2b-realm-provider`; v2 `CredentialProvider` trait", "version" : 1, - "workItemPrefix" : "cred-ss" + "workItemPrefixes" : [ + "cred-ss" + ] }, { "dependsOn" : [ @@ -272,7 +302,9 @@ "status" : "Proposed", "supersedes" : "`ProcessRole::Gpu`, `ProcessRole::GpuRenderNode`, `ProcessRole::Video` in `packages/d2b-core/src/processes.rs`; Nix `nixos-modules/components/graphics.nix`; Nix `nixos-modules/components/video/guest.nix`; `d2b.vms..graphics.*` options", "version" : 8, - "workItemPrefix" : "gpu" + "workItemPrefixes" : [ + "gpu" + ] }, { "dependsOn" : [ @@ -294,7 +326,9 @@ "status" : "Proposed", "supersedes" : "`ProcessRole::SecurityKeyFrontend` daemon-internal accept loop (`packages/d2bd/src/security_key.rs`), `nixos-modules/components/security-key-guest.nix` untracked guest `d2b-sk-frontend.service` unit", "version" : 6, - "workItemPrefix" : "security-key" + "workItemPrefixes" : [ + "security-key" + ] }, { "dependsOn" : [ @@ -313,7 +347,9 @@ "status" : "Proposed", "supersedes" : "Current `nixos-modules/components/tpm.nix` swtpm provisioning and `d2b-priv-broker` swtpm-dir hardening path", "version" : 3, - "workItemPrefix" : "device-tpm" + "workItemPrefixes" : [ + "device-tpm" + ] }, { "dependsOn" : [ @@ -336,7 +372,9 @@ "status" : "Proposed", "supersedes" : "`nixos-modules/components/usbip.nix` (host-side), per-env usbipd systemd units in `nixos-modules/network.nix`, `ProcessRole::Usbip` / `RunnerRole::Usbip` in current v3 baseline", "version" : 7, - "workItemPrefix" : "usbip" + "workItemPrefixes" : [ + "usbip" + ] }, { "dependsOn" : [ @@ -357,7 +395,9 @@ "status" : "Proposed", "supersedes" : "`ProcessRole::WaylandProxy` in `packages/d2b-core/src/processes.rs`; `LocalCrossDomainWaylandProvider` in `packages/d2b-host-providers/src/lib.rs`; `generate_wayland_proxy_argv` in `packages/d2b-host/src/wayland_proxy_argv.rs`; `nixos-modules/components/graphics.nix` `graphics.waylandProxy.*` options; `nixos-modules/ui-colors.nix` VM border color resolution; current `pkgs/wl-cross-domain-proxy` guest binary", "version" : 2, - "workItemPrefix" : "display" + "workItemPrefixes" : [ + "display" + ] }, { "dependsOn" : [ @@ -372,7 +412,9 @@ "status" : "Proposed", "supersedes" : "Current direct Provider construction/composition", "version" : 1, - "workItemPrefix" : "provider" + "workItemPrefixes" : [ + "provider" + ] }, { "dependsOn" : [ @@ -392,7 +434,9 @@ "status" : "Proposed", "supersedes" : "`nixos-modules/network.nix`, `nixos-modules/net.nix`", "version" : 3, - "workItemPrefix" : "nl" + "workItemPrefixes" : [ + "nl" + ] }, { "dependsOn" : [ @@ -405,12 +449,14 @@ "ADR-046-telemetry-audit-and-support" ], "path" : "docs/specs/providers/ADR-046-provider-notification-desktop.md", - "sha256" : "7388e079a6b30eb235b04686237eaf7611b0b8721628cd14275cf78b14f10b01", + "sha256" : "9e886c51a12d25ab771019e2001c7e134c277be3f18576ecbdcedf54c1a4db1a", "specId" : "ADR-046-provider-notification-desktop", "status" : "Proposed", "supersedes" : null, "version" : 2, - "workItemPrefix" : "notify" + "workItemPrefixes" : [ + "notify" + ] }, { "dependsOn" : [ @@ -433,7 +479,9 @@ "status" : "Proposed", "supersedes" : "`ProcessRole::OtelHostBridge` / `RunnerRole::OtelHostBridge`; socat-based vsock forwarder in `packages/d2b-host/src/otel_host_bridge_argv.rs`; `packages/d2bd/src/otel_host_bridge_readiness.rs`; hand-rolled per-VM `nixos-modules/components/observability/` pipeline (adapted to per-Zone)", "version" : 3, - "workItemPrefix" : "otel" + "workItemPrefixes" : [ + "otel" + ] }, { "dependsOn" : [ @@ -452,7 +500,9 @@ "status" : "Proposed", "supersedes" : "`packages/d2b-provider-aca/` (`AcaWorkloadProvider`, `GuestControlEndpointProvider`), `AcaRelayTransportConfig`, direct vsock guest-control path", "version" : 1, - "workItemPrefix" : "aca" + "workItemPrefixes" : [ + "aca" + ] }, { "dependsOn" : [ @@ -478,7 +528,9 @@ "status" : "Proposed", "supersedes" : "Current `InfrastructureProvider` trait (`d2b-realm-provider/src/provider.rs`); `AzureVmForbidden` explicit rejection in `provider_registry.rs`; `AZURE_VM_IMPLEMENTATION_ID` constant; `WorkloadProviderKind::ProviderManaged` paths for Azure VM workloads", "version" : 3, - "workItemPrefix" : "azure-vm" + "workItemPrefixes" : [ + "azure-vm" + ] }, { "dependsOn" : [ @@ -502,7 +554,9 @@ "status" : "Proposed", "supersedes" : "`packages/d2b-host/src/runtime_provider.rs` `CloudHypervisorRuntimeProvider`; `packages/d2bd/src/` VM lifecycle paths; `d2b-host-providers` adapter; `ProcessRole::CloudHypervisor`, `ProcessRole::Swtpm`, `ProcessRole::NetVm`; systemd unit `d2b--vm.service`; `SwtpmDir` broker op", "version" : 1, - "workItemPrefix" : "ch" + "workItemPrefixes" : [ + "ch" + ] }, { "dependsOn" : [ @@ -524,7 +578,9 @@ "status" : "Proposed", "supersedes" : "`docs/adr/0036-qemu-media-runtime.md`", "version" : 2, - "workItemPrefix" : "qemu-media" + "workItemPrefixes" : [ + "qemu-media" + ] }, { "dependsOn" : [ @@ -551,7 +607,9 @@ "status" : "Proposed", "supersedes" : "v1 of this spec; `guestd/src/shell.rs` guest persistent-shell runtime (ADR 0038); `d2b-unsafe-local-helper` shell supervisor and wire protocol v2 (ADR 0044); `ShellOp`/`ShellOpResponse` seqpacket protocol (`d2b-contracts/src/public_wire.rs:1319,1394,1527`)", "version" : 2, - "workItemPrefix" : "sterm" + "workItemPrefixes" : [ + "sterm" + ] }, { "dependsOn" : [ @@ -568,7 +626,9 @@ "status" : "Proposed", "supersedes" : "`d2b-core/src/storage.rs` generated contract, ADR 0034 storage/sync/adoption model for v3", "version" : 1, - "workItemPrefix" : "pstate" + "workItemPrefixes" : [ + "pstate" + ] }, { "dependsOn" : [ @@ -582,12 +642,14 @@ "ADR-046-telemetry-audit-and-support" ], "path" : "docs/specs/providers/ADR-046-provider-system-core.md", - "sha256" : "88b56dc8b5ce5535317ab311931b3498488fb304a4456d84fa33d30f557df80f", + "sha256" : "a858d857fee753b704d43a9c6b70c36fdc3093afe9b1cbb01820cb8e6cfcf172", "specId" : "ADR-046-provider-system-core", "status" : "Proposed", "supersedes" : "Current `WorkloadProviderKind::UnsafeLocal`, host/User grouping helpers, `HostJson`/`VmRuntimeRow` host-management paths in `d2bd` and `d2b-realm-core`", "version" : 1, - "workItemPrefix" : "system-core" + "workItemPrefixes" : [ + "system-core" + ] }, { "dependsOn" : [ @@ -605,12 +667,14 @@ "ADR-046-terminology-and-identities" ], "path" : "docs/specs/providers/ADR-046-provider-system-minijail.md", - "sha256" : "ccc2116577ba8aedfd59f2810a7af0bd3814227b9872c731424170b90cddaee5", + "sha256" : "a7685f42c424d14f7da3a190ea40a24313c2a85200d949e688e0ab439318b9e4", "specId" : "ADR-046-provider-system-minijail", "status" : "Proposed", "supersedes" : "Current `d2b-priv-broker` SpawnRunner, `d2b-core` minijail profile, and `d2bd` supervisor pidfd/wait paths for minijail-spawned processes", "version" : 1, - "workItemPrefix" : "minijail" + "workItemPrefixes" : [ + "minijail" + ] }, { "dependsOn" : [ @@ -628,7 +692,9 @@ "status" : "Proposed", "supersedes" : "Current `d2b-unsafe-local-helper` systemd scope runtime; current `d2bd` `supervisor/` VM process management; broker `SpawnRunner` (systemd-owned roles)", "version" : 1, - "workItemPrefix" : "systemd" + "workItemPrefixes" : [ + "systemd" + ] }, { "dependsOn" : [ @@ -646,7 +712,9 @@ "status" : "Proposed", "supersedes" : "`d2b-provider-relay` gateway-display relay path (`AcaWorkloadProvider` + `RelayProvider` traits in `d2b-realm-provider`); `d2b-gateway-runtime/src/bin/d2b-gateway-relay.rs`; `packages/d2b-provider-relay/src/lib.rs` as a first-party transport surface", "version" : 1, - "workItemPrefix" : "transport-relay" + "workItemPrefixes" : [ + "transport-relay" + ] }, { "dependsOn" : [ @@ -663,12 +731,14 @@ "ADR-046-zone-routing" ], "path" : "docs/specs/providers/ADR-046-provider-transport-unix.md", - "sha256" : "6c5200e6b424c002ece340839684c7dfa7141c09de0e58e4e7b2aeed4bd12725", + "sha256" : "57f3e2b23777b7a595edb6444ff8243a1874e6f4723186466ea1e1fa09f0af62", "specId" : "ADR-046-provider-transport-unix", "status" : "Proposed", "supersedes" : "v1 of this dossier (ADR-046-provider-transport-unix v1; incorrect ownership model); `InheritedSocketTransport` SD_LISTEN_FDS path (`packages/d2b-session-unix/src/systemd.rs`)", "version" : 3, - "workItemPrefix" : "transport-unix" + "workItemPrefixes" : [ + "transport-unix" + ] }, { "dependsOn" : [ @@ -682,12 +752,14 @@ "ADR-046-zone-routing" ], "path" : "docs/specs/providers/ADR-046-provider-transport-vsock.md", - "sha256" : "5a73239f90903b689ec05a0ddbbbe24b7a78ccedbb6de377cc105f0afe43d202", + "sha256" : "824bc597f1c5ee34d6ebd3063a5a75d5b8b6a9ee8f0419357cd87fca0a779df7", "specId" : "ADR-046-provider-transport-vsock", "status" : "Proposed", "supersedes" : "`packages/d2bd/src/guest_control_vsock.rs` transport probe (→ allocator-issued endpoint via VsockEffectPort); `packages/d2b-host/src/vsock_relay_argv.rs` socat relay (→ native FramedVsockTransport in Provider service); `NativeVsock`/`CloudHypervisorVsock` in `d2b-session-unix/src/vsock.rs` (→ FramedVsockTransport implementing OwnedTransport adapted as service-Provider transport bridge)", "version" : 2, - "workItemPrefix" : "vsock" + "workItemPrefixes" : [ + "vsock" + ] }, { "dependsOn" : [ @@ -706,7 +778,9 @@ "status" : "Proposed", "supersedes" : "`d2b-priv-broker/src/ops/{state_dir,store_sync,store_sync_audit,store_sync_export,store_view_farm,store_view_posture,swtpm_dir}.rs`; `d2b-core/src/{storage,sync,storage_lifecycle}.rs` StorageJson/SyncJson contract; `nixos-modules/store.nix` per-VM hardlink farm activation", "version" : 2, - "workItemPrefix" : "vl" + "workItemPrefixes" : [ + "vl" + ] }, { "dependsOn" : [ @@ -722,12 +796,15 @@ "ADR-046-telemetry-audit-and-support" ], "path" : "docs/specs/providers/ADR-046-provider-volume-virtiofs.md", - "sha256" : "41883d577282a85d1771286c603f7e029356e59dae13eaa58c2103975f04e73b", + "sha256" : "b80201a129196e56150dfa0c4520ecd0ecc2d8eccfba2860c9962efeee284725", "specId" : "ADR-046-provider-volume-virtiofs", "status" : "Proposed", "supersedes" : "`nixos-modules/processes-json.nix` virtiofsdRunner block; `nixos-modules/minijail-profiles.nix` virtiofsdProfiles; `packages/d2b-host/src/virtiofsd_argv.rs`; `ProcessRole::Virtiofsd` dag nodes in `packages/d2bd/src/supervisor/dag.rs`", "version" : 2, - "workItemPrefix" : "vvfs" + "workItemPrefixes" : [ + "vvfs", + "vvfs-export" + ] }, { "dependsOn" : [ @@ -756,12 +833,14 @@ "ADR-046-zone-routing" ], "path" : "docs/specs/ADR-046-reset-and-cutover.md", - "sha256" : "8bacde7ecc5384f57d35d348fb95926cf6da0930133bda0a9d96446a84a8a11d", + "sha256" : "22beccd8ef43db1474b3b8ceb7564e072d5ab7f1eaf1bcc093eb87655e7a4439", "specId" : "ADR-046-reset-and-cutover", "status" : "Proposed", "supersedes" : "[ADR 0034](../adr/0034-storage-lifecycle-restart-and-synchronization.md) \"Migration decision\" section for the d2b 3.0 cutover; the current `d2b host migrate-storage` verb (retired per `ADR-046-current-code-migration-map` §6 and `ADR-046-cli-and-operations` \"v2 command surface removed at 3.0 clean break\": it served the one-time v1→v2 storage layout cutover only and has no v3 successor)", "version" : 1, - "workItemPrefix" : "reset" + "workItemPrefixes" : [ + "reset" + ] }, { "dependsOn" : [ @@ -775,7 +854,9 @@ "status" : "Proposed", "supersedes" : null, "version" : 1, - "workItemPrefix" : "api" + "workItemPrefixes" : [ + "api" + ] }, { "dependsOn" : [ @@ -788,7 +869,9 @@ "status" : "Proposed", "supersedes" : null, "version" : 1, - "workItemPrefix" : "object" + "workItemPrefixes" : [ + "object" + ] }, { "dependsOn" : [ @@ -802,7 +885,9 @@ "status" : "Proposed", "supersedes" : null, "version" : 1, - "workItemPrefix" : "reconcile" + "workItemPrefixes" : [ + "reconcile" + ] }, { "dependsOn" : [ @@ -815,7 +900,9 @@ "status" : "Proposed", "supersedes" : null, "version" : 1, - "workItemPrefix" : "store" + "workItemPrefixes" : [ + "store" + ] }, { "dependsOn" : [ @@ -828,12 +915,14 @@ "ADR-046-terminology-and-identities" ], "path" : "docs/specs/ADR-046-resources-credential.md", - "sha256" : "09dbcec4e1eccb67022b38103e649d1fe4ebb86707a6c92e67725b2cd5a54363", + "sha256" : "e2f0f583c0b5c8bfbda370960343648f141efd5376cc4e2f25f0b1a346b005a0", "specId" : "ADR-046-resources-credential", "status" : "Proposed", "supersedes" : "Current v3 `CredentialProvider` trait, `CredentialStatus`, `CredentialPlane` model in `d2b-realm-provider/src/credential.rs` and `provider.rs`", "version" : 1, - "workItemPrefix" : "credential" + "workItemPrefixes" : [ + "credential" + ] }, { "dependsOn" : [ @@ -850,7 +939,9 @@ "status" : "Proposed", "supersedes" : "Current ProcessRole device sidecars (Swtpm, SwtpmPreStartFlush, Usbip, SecurityKeyFrontend, Gpu, GpuRenderNode, Video) and their Nix components", "version" : 1, - "workItemPrefix" : "device" + "workItemPrefixes" : [ + "device" + ] }, { "dependsOn" : [ @@ -865,12 +956,15 @@ "ADR-046-terminology-and-identities" ], "path" : "docs/specs/ADR-046-resources-host-guest-process-user.md", - "sha256" : "b0be4f30c866dd27c06389c9ddc332c66ed1e5c72da99d6ce9a48fc26583feeb", + "sha256" : "8d6b786fd2a04cfa00fc8beff51bca3c62044adca265969dbf3899eb44847642", "specId" : "ADR-046-resources-host-guest-process-user", "status" : "Proposed", "supersedes" : "Current `ProcessRole`/`VmProcessDag` as the public process model; current Realm workload/unsafe-local/execution DTOs; `WorkloadProviderKind`/`IsolationPosture` posture enums", "version" : 1, - "workItemPrefix" : "exec" + "workItemPrefixes" : [ + "exec", + "user-session" + ] }, { "dependsOn" : [ @@ -886,7 +980,9 @@ "status" : "Proposed", "supersedes" : "`d2b.envs.` and `d2b.realms..network` Nix surfaces (v3 reset)", "version" : 2, - "workItemPrefix" : "network" + "workItemPrefixes" : [ + "network" + ] }, { "dependsOn" : [ @@ -902,7 +998,9 @@ "status" : "Proposed", "supersedes" : "`storage.json` v2, `sync.json` v1, per-VM hardlink-farm and store-view emitters in `nixos-modules/storage-json.nix`, `nixos-modules/store.nix`", "version" : 1, - "workItemPrefix" : "volume" + "workItemPrefixes" : [ + "volume" + ] }, { "dependsOn" : [ @@ -916,12 +1014,18 @@ "ADR-046-terminology-and-identities" ], "path" : "docs/specs/ADR-046-resources-zone-control.md", - "sha256" : "73de2de2eba753bd17528d397ffbc7dad68a41fa9007148c2bfc67cab77da479", + "sha256" : "c622e9286f85d882acd7fcada9920f5f3ab447d645810abce37c67dbea3e5988", "specId" : "ADR-046-resources-zone-control", "status" : "Proposed", "supersedes" : null, "version" : 1, - "workItemPrefix" : "zone-control" + "workItemPrefixes" : [ + "client", + "pkg", + "provider-agent", + "wire", + "zone-control" + ] }, { "dependsOn" : [ @@ -977,12 +1081,14 @@ "ADR-046-zone-routing" ], "path" : "docs/specs/ADR-046-security-and-threat-model.md", - "sha256" : "fd302854317e87b86f9ba1d349fcbcd00f729e9ec512ada409978308507212ee", + "sha256" : "6f1c04852e3e7276d2744d20abd01afb47517b9fe2e6a67d458ff3bf6b59bccf", "specId" : "ADR-046-security-and-threat-model", "status" : "Proposed", "supersedes" : null, "version" : 1, - "workItemPrefix" : "security" + "workItemPrefixes" : [ + "security" + ] }, { "dependsOn" : [ @@ -1002,7 +1108,9 @@ "status" : "Proposed", "supersedes" : null, "version" : 1, - "workItemPrefix" : "streamline" + "workItemPrefixes" : [ + "streamline" + ] }, { "dependsOn" : [ @@ -1015,12 +1123,18 @@ "ADR-046-terminology-and-identities" ], "path" : "docs/specs/ADR-046-telemetry-audit-and-support.md", - "sha256" : "0a47bc9532152e443a36ab61619a1fd3f3310fd858cb125e58e8b646d3cd1317", + "sha256" : "9acf45aed9f496d72b30a9d093c00cff474beb272a09110f7c3cc4887b922720", "specId" : "ADR-046-telemetry-audit-and-support", "status" : "Proposed", "supersedes" : "Current `d2bd` hand-rolled Prometheus registry; current daemon/broker/gateway JSONL audit paths", "version" : 2, - "workItemPrefix" : "telem" + "workItemPrefixes" : [ + "audit", + "doctor", + "host-posture", + "reuse", + "telem" + ] }, { "dependsOn" : [ @@ -1032,7 +1146,9 @@ "status" : "Proposed", "supersedes" : "Public Realm terminology selected by ADR 0043 for d2b v3", "version" : 1, - "workItemPrefix" : "identities" + "workItemPrefixes" : [ + "identities" + ] }, { "dependsOn" : [ @@ -1065,12 +1181,14 @@ "ADR-046-zone-routing" ], "path" : "docs/specs/ADR-046-validation-and-delivery.md", - "sha256" : "ee3ea5d1e335388053e8a29ae228cefb073b30e89c275b882d53abb74601bbfe", + "sha256" : "ef2412e7d995a880d3a23e79e16d3f22f898f5da8eeacda93fa966ccb51677c8", "specId" : "ADR-046-validation-and-delivery", "status" : "Proposed", "supersedes" : "This repository's current `AGENTS.md` \"Panel review\" phase-gate as the *sole* review mechanism for ADR 0046 work (extended, not replaced, per §12); ad hoc per-agent validation ordering for ADR 0046 implementation", "version" : 1, - "workItemPrefix" : "delivery" + "workItemPrefixes" : [ + "delivery" + ] }, { "dependsOn" : [ @@ -1086,10 +1204,12 @@ "status" : "Proposed", "supersedes" : "`RealmEntrypointTable`/`EntrypointMode`/`RealmControllerPlacement` enum (→ compiler-only `parentZone` topology + ZoneLink transport + private Zone runtime bootstrap placement); `RouteTreeEngine`/`routing.rs` route contracts (adapted, not deleted); `RemoteNodeRegistry` (→ ZoneLink controller); `OperationRouter` (→ ZoneLinkIdempotencyKey in d2b-bus); `realm_access_resolver` module (→ ZoneEntrypointResolver); `realm-controllers.json`/`realm-identity.json` bundle artifacts (→ Nix-authored Zone settings/ZoneLink resources); `d2b.realms.*` Nix option namespace (→ `d2b.zones.*`); `WorkloadId` routing targets (→ typed Guest/Host resource refs); `CapabilitySet`-only routing authz (→ per-hop RBAC narrowing + capability ceiling)", "version" : 1, - "workItemPrefix" : "routing" + "workItemPrefixes" : [ + "routing" + ] } ], "parent" : "docs/adr/0046-d2b-3-provider-control-plane.md", - "schemaVersion" : 2, + "schemaVersion" : 3, "status" : "Proposed" } diff --git a/docs/specs/ADR-046-telemetry-audit-and-support.md b/docs/specs/ADR-046-telemetry-audit-and-support.md index 2e7c2ca70..505082a35 100644 --- a/docs/specs/ADR-046-telemetry-audit-and-support.md +++ b/docs/specs/ADR-046-telemetry-audit-and-support.md @@ -1871,7 +1871,7 @@ New `packages/d2b-core-controller/tests/config_cleanup.rs`: | Field | Value | | --- | --- | | Work item ID | `ADR046-telem-003` | -| Dependency/owner | ADR046-telem-001 + ADR046-session-001 + ADR046-session-003; session/bus owner | +| Dependency/owner | ADR046-telem-001 + ADR046-session-001 + ADR046-bus-001; session/bus owner | | Current source | `packages/d2b-realm-core/src/ids.rs` (`OperationId`, `CorrelationId`); `packages/d2b-realm-codec-protobuf/src/lib.rs` (`encode_trace_context`, `decode_trace_context` for v3 codec adaptation); `packages/d2b-realm-router/src/mux_session.rs`, `route_engine.rs` (current implemented-but-unwired generic routing) | | Reuse action | adapt | | Destination | `packages/d2b-resource-api/src/metrics.rs`, `packages/d2b-session/src/metrics.rs`, `packages/d2b-bus/src/metrics.rs` | @@ -1926,11 +1926,11 @@ New `packages/d2b-core-controller/tests/config_cleanup.rs`: | Validation | `emitter_socket_receive`, `emitter_ring_drains_on_socket_available`, `emitter_ring_drop_on_overflow`, `no_vm_label_in_metrics`, `zone_startup_proceeds_without_provider` tests; adapted `policy_observability.rs` tests (retain `loki_native_otel_resource_attributes` and SigNoz-only backend assertions); adapted `minijail_relay_otel.rs` shape test for Provider-managed runner | | Removal proof | `otel_host_bridge_argv.rs` socat runner and `otel_host_bridge_readiness.rs` retired after `observability-otel` Provider delivers native OTLP/vsock and passes conformance; `ProcessRole::OtelHostBridge` and `RunnerRole::OtelHostBridge` retired from `d2b-core/src/processes.rs` and `d2b-contracts/src/broker_wire.rs` after Provider migration | -### ADR046-telem-012 +### ADR046-audit-001 | Field | Value | | --- | --- | -| Work item ID | `ADR046-telem-012` | +| Work item ID | `ADR046-audit-001` | | Dependency/owner | W0/W1a; audit crate owner | | Current source | `packages/d2b-realm-core/src/audit.rs` (`AuditHash::parse`, `AuditChainLink::new`/`verify`, `AuditChainRecord{stream: AuditStreamKind, realm: RealmPath, node: NodeId}`, `AuditStreamKind::{Gateway,RemoteNode,Daemon}`, `AuditSinkHealth`, `AuditRetentionFloorStatus`); `packages/d2bd/src/daemon_audit.rs` (hash-chain append algorithm, `prev_hash`/`record_hash` SHA-256 pattern, daily segment files `daemon-events-YYYY-MM-DD.jsonl`, `DaemonEvent` additive contract); `packages/d2b-priv-broker/src/audit.rs` (`AuditWriteClass::{Privileged,Unprivileged}`, `AuditDropSummary`, `DEFAULT_AUDIT_WRITES_PER_SECOND = 4096`, O_APPEND CLOEXEC file open, `AuditDropWarningState`); `packages/d2b-gateway-runtime/src/audit_jsonl.rs` (`JsonlGatewayAudit`, `DEFAULT_GATEWAY_AUDIT_RETENTION_DAYS = 14`, `prune_old` rotation algorithm); `packages/d2b-priv-broker/src/ops/audit_op.rs` (`OpAuditRecord`, `SwtpmDirAudit`, `SwtpmDirResult`, `SwtpmMarkerResult`); `packages/d2b-realm-core/src/ids.rs` (`OperationId`, `CorrelationId`, `PrincipalId` — `PrincipalId` becomes `subject_digest`); `packages/d2b/tests/audit_contract.rs`; `packages/d2b-priv-broker/tests/broker_export_audit.rs` | | Reuse action | adapt | @@ -1941,12 +1941,12 @@ New `packages/d2b-core-controller/tests/config_cleanup.rs`: | Validation | `audit_record_hash_chain`, `audit_record_schema` (no `realm`/`node` fields), `audit_segment_rotation`, `audit_rate_limit_privileged_never_dropped`, `audit_unavailable_blocks_privileged` | | Removal proof | `daemon_audit.rs`, broker `audit.rs`, `JsonlGatewayAudit` retired per-component after `d2b-audit` sink achieves parity | -### ADR046-telem-013 +### ADR046-audit-002 | Field | Value | | --- | --- | -| Work item ID | `ADR046-telem-013` | -| Dependency/owner | ADR046-telem-012 + ADR046-store-001; store/authz owner | +| Work item ID | `ADR046-audit-002` | +| Dependency/owner | ADR046-audit-001 + ADR046-store-001; store/authz owner | | Current source | `packages/d2b-priv-broker/src/ops/audit_op.rs` (`OpAuditRecord` structural pattern — operation, peer_uid, decision, result fields); `packages/d2b-realm-core/src/audit.rs::AuditEnvelope{principal: PrincipalId, scope: AuthorizationScope, decision: AuthzDecision}` (principal → v3 `subject_digest`) | | Reuse action | adapt | | Destination | `packages/d2b-resource-store-redb/src/audit.rs`, `packages/d2b-core-controller/src/authz_audit.rs` | @@ -1956,12 +1956,12 @@ New `packages/d2b-core-controller/tests/config_cleanup.rs`: | Validation | Integration test: 100 mutations → verify hash-chained audit records with `zone` field, no `realm` field | | Removal proof | None — net-new; no prior owner to remove | -### ADR046-telem-014 +### ADR046-audit-003 | Field | Value | | --- | --- | -| Work item ID | `ADR046-telem-014` | -| Dependency/owner | ADR046-telem-012 + ADR046-session-001 + ADR046-session-003; session/bus owner | +| Work item ID | `ADR046-audit-003` | +| Dependency/owner | ADR046-audit-001 + ADR046-session-001 + ADR046-bus-001; session/bus owner | | Current source | `packages/d2b-gateway/src/audit.rs` (`GatewayAuditEvent`, `GatewayAuditKind::{DisplaySessionOpenAdmitted,DisplaySessionOpenDenied,DisplaySessionRunning,DisplaySessionClosed}`, `GatewayAudit` trait, `NoopGatewayAudit`); `packages/d2b-realm-core/src/audit.rs::AuditEnvelope{realm, principal, scope, decision, trace}` (fields adapted to v3 `SessionConnect` record class) | | Reuse action | adapt | | Destination | `packages/d2b-session/src/audit.rs`, `packages/d2b-bus/src/audit.rs` | @@ -1971,12 +1971,12 @@ New `packages/d2b-core-controller/tests/config_cleanup.rs`: | Validation | Session connect/close/auth-failure audit tests; `GatewayAuditKind` → `SessionConnect` mapping test | | Removal proof | `NoopGatewayAudit` and gateway JSONL sink retired after gateway is on v3 resource API | -### ADR046-telem-015 +### ADR046-audit-004 | Field | Value | | --- | --- | -| Work item ID | `ADR046-telem-015` | -| Dependency/owner | ADR046-telem-012; CLI owner | +| Work item ID | `ADR046-audit-004` | +| Dependency/owner | ADR046-audit-001; CLI owner | | Current source | `packages/d2b/tests/audit_contract.rs` (`d2b audit --strict` returns 78; `auditResponse` relay; `authz-audit-requires-admin` denial; daemon-down exit 1 without bash fallback); `packages/d2b-priv-broker/tests/broker_export_audit.rs` (`export_audit_requires_admin_and_exports_op_audit_records`: admin-only, path-free, NDJSON `ExportBrokerAuditOk` shape, `peer_uid` field, `ApplyNftables` operation name in records) | | Reuse action | adapt | | Destination | `packages/d2b/src/zone_audit.rs` (new `d2b zone audit export` subcommand); `packages/d2b/tests/zone_audit_contract.rs` | @@ -1986,12 +1986,12 @@ New `packages/d2b-core-controller/tests/config_cleanup.rs`: | Validation | `export_audit.rs`: admin-only, hash break inline, no old field names (`realm`/`node`/`workload_id`), no path/argv in output, exit 0 on clean chain | | Removal proof | `d2b audit` legacy command retained until `d2b zone audit export` covers all record classes | -### ADR046-telem-016 +### ADR046-doctor-001 | Field | Value | | --- | --- | -| Work item ID | `ADR046-telem-016` | -| Dependency/owner | ADR046-core-001 + ADR046-telem-012; CLI/doctor owner | +| Work item ID | `ADR046-doctor-001` | +| Dependency/owner | ADR046-core-001 + ADR046-audit-001; CLI/doctor owner | | Current source | `packages/d2b/tests/host_doctor_contract.rs` (env-redirect sandbox: `D2B_BROKER_SOCKET`, `D2B_PUBLIC_SOCKET`, `D2B_DAEMON_STATE_DIR`, `D2B_METRICS_URL`, `D2B_MANIFEST_PATH`; `doctor::render_summary` JSON envelope fields: `command`, `mode`, `broker_ready`, per-check `status`+`data`, `summary`, `exitCode`); `packages/d2bd/src/audit_check.rs` (`defects` array audit-chain validation pattern); `packages/d2bd/src/lib.rs` (doctor read-only path: `host doctor --read-only` reads from `D2B_DAEMON_STATE_DIR`, pidfd_table file, kernel-module check file, metrics URL) | | Reuse action | adapt | | Destination | `packages/d2b/src/zone_doctor.rs`, `packages/d2b/tests/zone_doctor_contract.rs` | @@ -2001,12 +2001,12 @@ New `packages/d2b-core-controller/tests/config_cleanup.rs`: | Validation | `zone_doctor_contract.rs`: all-ready/degraded/quarantine/otel-absent/audit-absent fixtures; no resource names/paths/argv/PIDs; `zone_phase` field present; no legacy `broker_ready` field | | Removal proof | `d2b host doctor` retained until `d2b zone doctor` covers all check parity | -### ADR046-telem-017 +### ADR046-doctor-002 | Field | Value | | --- | --- | -| Work item ID | `ADR046-telem-017` | -| Dependency/owner | ADR046-telem-016; CLI/doctor owner | +| Work item ID | `ADR046-doctor-002` | +| Dependency/owner | ADR046-doctor-001; CLI/doctor owner | | Current source | `packages/d2b/tests/host_doctor_contract.rs` (env-redirect sandbox scaffold — no current `support-bundle` equivalent exists) | | Reuse action | adapt | | Destination | `packages/d2b/src/zone_support_bundle.rs`, `packages/d2b/tests/zone_support_bundle_contract.rs` | @@ -2046,12 +2046,12 @@ New `packages/d2b-core-controller/tests/config_cleanup.rs`: | Validation | These tests are their own validation artifact | | Removal proof | None — net-new; no prior owner to remove | -### ADR046-telem-018 +### ADR046-host-posture-001 | Field | Value | | --- | --- | -| Work item ID | `ADR046-telem-018` | -| Dependency/owner | ADR046-telem-012 + ADR046-core-001; `Provider/system-core` owner | +| Work item ID | `ADR046-host-posture-001` | +| Dependency/owner | ADR046-audit-001 + ADR046-core-001; `Provider/system-core` owner | | Current source | `packages/d2b-core/src/unsafe_local_workloads.rs` (`UnsafeLocalWorkloadsJson`, `UnsafeLocalWorkload`, `UnsafeLocalLauncherItem`, `UNSAFE_LOCAL_WORKLOADS_SCHEMA_VERSION = "v2"`, `MAX_UNSAFE_LOCAL_WORKLOADS = 256`); `packages/d2b-contracts/src/unsafe_local_wire.rs` (`HelperHello.uid: u32`, `HelperLaunchRequest`, `HelperShellRequest`, `HelperScopeKind::{Exec,Shell}`, `DaemonToUnsafeLocalHelper`, `UnsafeLocalHelperToDaemon`); `packages/d2bd/src/unsafe_local_helper.rs` (`HelperRegistry::new(daemon_uid, allowed_uids)`, `dispatch_launch`, `bind_helper_socket`); `packages/d2b-unsafe-local-helper/src/{main,protocol,runtime,systemd}.rs` (`HelperClient`, `ScopeRuntime`, `run_scope_supervisor`, `SystemdUserScopeManager`); `nixos-modules/options-realms-workloads.nix` (lines 221, 233–235 `kind = "unsafe-local"` description; lines 264–275 null `stateDir`/`runDir`); `nixos-modules/unsafe-local-workloads-json.nix` (`runtimeKind = "unsafe-local"`, `providerId = "unsafe-local"`); `nixos-modules/unsafe-local-helper.nix` (service unit) | | Reuse action | adapt | | Destination | `packages/d2b-provider-system-core/src/{host_reconciler.rs,host_status.rs,host_process_audit.rs}`; adapted `nixos-modules/unsafe-local-workloads-json.nix`; `packages/d2b-provider-system-core/tests/host_posture_contract.rs` | @@ -2071,11 +2071,11 @@ evidence. Each item below records: exact main commit file/symbol, selected behav exact v3 destination/integration, and the ADR45-specific assumptions that must be excluded or adapted. -### ADR046-telem-019 — ComponentSession v2 runtime +### ADR046-reuse-001 — ComponentSession v2 runtime | Field | Value | | --- | --- | -| Work item ID | `ADR046-telem-019` | +| Work item ID | `ADR046-reuse-001` | | Dependency/owner | ADR046-telem-001 (d2b-telemetry must exist first for MetricsSink injection); session owner | | Current source | Main commit source row below: `packages/d2b-session/` and `packages/d2b-contracts/src/v2_component_session.rs` at `a1cc0b2d`. | | Reuse action | adapt | @@ -2090,12 +2090,12 @@ excluded or adapted. | Integration | `d2b-bus` route handler calls `serve_ttrpc_services`; `d2b-session-unix` provides `OwnedTransport` impl; `d2b-telemetry` `MetricsSink` impl feeds `d2b_session_*` metrics inventory from this spec. | | Validation | Adopt `tests/component_session.rs` and `tests/noise_vectors.rs` unchanged; extend with v3 `EndpointPurpose` enum gate test; add `d2b-contract-tests/tests/component_session_v2_vectors.rs` (existing at `a1cc0b2d`) as-is. | -### ADR046-telem-020 — Unix transport substrate +### ADR046-reuse-002 — Unix transport substrate | Field | Value | | --- | --- | -| Work item ID | `ADR046-telem-020` | -| Dependency/owner | ADR046-telem-019; Unix transport owner | +| Work item ID | `ADR046-reuse-002` | +| Dependency/owner | ADR046-reuse-001; Unix transport owner | | Current source | Main commit source row below: `packages/d2b-session-unix/` at `a1cc0b2d`. | | Reuse action | adapt | | Destination | `packages/d2b-session-unix/` copied verbatim. | @@ -2109,12 +2109,12 @@ excluded or adapted. | Integration | `d2b-bus` Zone-local listeners use `UnixSeqpacketTransport`; Provider agent connections use vsock transport from this crate; `CreditPool`/`CreditScopeSet` enforce per-Zone attachment FD budgets. | | Validation | Adopt all `unix_session.rs` tests unchanged. | -### ADR046-telem-021 — Async client and retry layer +### ADR046-reuse-003 — Async client and retry layer | Field | Value | | --- | --- | -| Work item ID | `ADR046-telem-021` | -| Dependency/owner | ADR046-telem-019; client owner | +| Work item ID | `ADR046-reuse-003` | +| Dependency/owner | ADR046-reuse-001; client owner | | Current source | Main commit source row below: `packages/d2b-client/` at `a1cc0b2d`. | | Reuse action | adapt | | Destination | `packages/d2b-client/` copied; DaemonClient/GuestClient adapted to v3 service packages; `MetadataInput::with_trace` drives `TraceContext` propagation. | @@ -2128,12 +2128,12 @@ excluded or adapted. | Integration | Every controller/service that makes outbound calls uses `Client`; `MetadataInput::with_trace` feeds `d2b_api_request_duration_seconds` trace-id into `d2b.bus.route` span. | | Validation | Adopt typed-route, proxy-reuse, and cancel tests unchanged. Add v3 service-package name gate test. | -### ADR046-telem-022 — Provider registry, RPC proxy, and conformance toolkit +### ADR046-reuse-004 — Provider registry, RPC proxy, and conformance toolkit | Field | Value | | --- | --- | -| Work item ID | `ADR046-telem-022` | -| Dependency/owner | ADR046-telem-019 + ADR046-telem-021; Provider owner | +| Work item ID | `ADR046-reuse-004` | +| Dependency/owner | ADR046-reuse-001 + ADR046-reuse-003; Provider owner | | Current source | Main commit source row below: `packages/d2b-provider/` and `packages/d2b-provider-toolkit/` at `a1cc0b2d`. | | Reuse action | adapt | | Destination | `packages/d2b-provider/` and `packages/d2b-provider-toolkit/` copied with v3 session admission and bus routing adaptations. | @@ -2147,12 +2147,12 @@ excluded or adapted. | Integration | Each v3 Provider process embeds `ProviderRegistry` + `GeneratedProviderServiceServer`; `check_provider_conformance` runs in Provider install-time conformance check (feeds `d2b_provider_reconcile_total{outcome="error"}` on failure). | | Validation | Adopt all `conformance.rs` and `runtime.rs` tests unchanged. Add v3 `SessionIdentity` zone-name gate. Add conformance-failure → `d2b_provider_reconcile_total` metric integration test. | -### ADR046-telem-023 — Provider agent process and gateway-runtime audit bridge +### ADR046-reuse-005 — Provider agent process and gateway-runtime audit bridge | Field | Value | | --- | --- | -| Work item ID | `ADR046-telem-023` | -| Dependency/owner | ADR046-telem-022 + ADR046-telem-014; Provider agent / observability-otel owner | +| Work item ID | `ADR046-reuse-005` | +| Dependency/owner | ADR046-reuse-004 + ADR046-audit-003; Provider agent / observability-otel owner | | Current source | Main commit source row below: `packages/d2b-gateway-runtime/src/provider_agent.rs` at `a1cc0b2d` plus `tests/provider_agent_v2.rs`. | | Reuse action | adapt | | Destination | `packages/d2b-provider-observability-otel/src/agent.rs` adapted; ComponentSessionDriver mock becomes shared Provider session fixture. | @@ -2166,12 +2166,12 @@ excluded or adapted. | Integration | `observability-otel` Provider embeds a `ProviderAgentProcess`; session connect/disconnect emits `SessionConnect` audit records via `d2b-audit`; `ProviderAgentAuditEvent` ring feeds `d2b_provider_reconcile_total` metric on session error. | | Validation | Adopt `provider_agent_v2.rs` mock harness unchanged as shared v3 Provider session fixture. Add v3 audit-bridge test: provider-agent session → `SessionConnect{transport_class="zone_link"}` record emitted. | -### ADR046-telem-024 — Realm service v2 routing and remote-node routing state +### ADR046-reuse-006 — Realm service v2 routing and remote-node routing state | Field | Value | | --- | --- | -| Work item ID | `ADR046-telem-024` | -| Dependency/owner | ADR046-telem-019 + ADR046-session-003; bus routing owner | +| Work item ID | `ADR046-reuse-006` | +| Dependency/owner | ADR046-reuse-001 + ADR046-bus-001; bus routing owner | | Current source | Main commit source row below: `packages/d2b-realm-router/` at `a1cc0b2d`. | | Reuse action | adapt | | Destination | `packages/d2b-bus/src/routing.rs` adapted from `service_v2.rs`; RemoteNodeErrorKind stable code pattern becomes v3 BusErrorKind. | @@ -2185,12 +2185,12 @@ excluded or adapted. | Integration | `d2b-bus` route handler adapts `RealmServiceServer` dispatch table; `RemoteNodeErrorKind::code()` values feed `d2b_bus_route_total{outcome}` metric labels; `CredentialCustody::Host` maps to `purpose_class=local` in `d2b_session_connect_total`. | | Validation | Adopt `authority_keeps_remote_credentials_in_gateway_guests` test renamed to `authority_keeps_remote_credentials_in_zone_link_sessions`; adapt `RealmId` → Zone name; adopt `authenticated_bootstrap_enrollment_route_and_shortcut_lifecycle` renamed with zone terminology. | -### ADR046-telem-025 — d2bd service routing, provider effects, and daemon session tests +### ADR046-reuse-007 — d2bd service routing, provider effects, and daemon session tests | Field | Value | | --- | --- | -| Work item ID | `ADR046-telem-025` | -| Dependency/owner | ADR046-telem-022 + ADR046-telem-024; core-controller routing owner | +| Work item ID | `ADR046-reuse-007` | +| Dependency/owner | ADR046-reuse-004 + ADR046-reuse-006; core-controller routing owner | | Current source | Main commit source row below: `packages/d2bd/` routing/provider effects files and tests at `a1cc0b2d`. | | Reuse action | adapt | | Destination | `packages/d2b-bus/src/service_router.rs` and `packages/d2b-core-controller/src/provider_effects.rs`. | @@ -2204,12 +2204,12 @@ excluded or adapted. | Integration | Bus service router uses `service.package` closed-set matching from route-gate pattern; `ProviderLifecycleDispatch` feeds `d2b_provider_component_phase` metric. | | Validation | Port `local_daemon_policy_is_fixed_and_has_no_negotiation_or_fd_surface` invariant to v3 bus local policy test; port `every_generated_daemon_method_has_one_typed_adapter` to v3 bus method adapter completeness test. | -### ADR046-telem-026 — ComponentSession v2 vector tests and contract conformance +### ADR046-reuse-008 — ComponentSession v2 vector tests and contract conformance | Field | Value | | --- | --- | -| Work item ID | `ADR046-telem-026` | -| Dependency/owner | ADR046-telem-019; contract-tests owner | +| Work item ID | `ADR046-reuse-008` | +| Dependency/owner | ADR046-reuse-001; contract-tests owner | | Current source | Main commit source row below: ComponentSession vector/conformance tests at `a1cc0b2d`. | | Reuse action | adapt | | Destination | `packages/d2b-contract-tests/tests/component_session_v2_vectors.rs` and `tests/noise_vectors.rs` copied verbatim. | @@ -2223,12 +2223,12 @@ excluded or adapted. | Integration | These tests run in `make test-rust` / `cargo test -p d2b-contract-tests` and `cargo test -p d2b-session`. They are gating for any Noise library update. | | Validation | These tests are self-validating. Add one gate: assert `COMPONENT_SESSION_MAJOR = 2` and `COMPONENT_SESSION_MINOR = 0` constants are unchanged in v3 contract. | -### ADR046-telem-027 — Session MetricsSink → d2b-telemetry bridge +### ADR046-reuse-009 — Session MetricsSink → d2b-telemetry bridge | Field | Value | | --- | --- | -| Work item ID | `ADR046-telem-027` | -| Dependency/owner | ADR046-telem-019 + ADR046-telem-001 + ADR046-telem-003; telemetry/session owner | +| Work item ID | `ADR046-reuse-009` | +| Dependency/owner | ADR046-reuse-001 + ADR046-telem-001 + ADR046-telem-003; telemetry/session owner | | Current source | Main commit source row below: `packages/d2b-session/src/metrics.rs` and `MetricLabels` in `packages/d2b-contracts/src/v2_component_session.rs` at `a1cc0b2d`. | | Reuse action | adapt | | Destination | `packages/d2b-telemetry/src/session_metrics_sink.rs`. | @@ -2277,7 +2277,7 @@ excluded or adapted. | Field | Value | | --- | --- | | Work item ID | `ADR046-telem-011` | -| Dependency/owner | ADR046-telem-009 + ADR046-telem-010 + ADR046-telem-012 + ADR046-store-001; core-controller owner | +| Dependency/owner | ADR046-telem-009 + ADR046-telem-010 + ADR046-audit-001 + ADR046-store-001; core-controller owner | | Current source | `packages/d2bd/src/daemon_audit.rs` (hash-chain `ResourceMutation`-like append pattern — adapt for cleanup audit records); `packages/d2b-priv-broker/src/audit.rs` (`AuditWriteClass::{Standard,Unprivileged}` — cleanup audit records use `Standard` durability); `packages/d2b-realm-core/src/audit.rs::AuditChainLink::new` (hash-chain append for cleanup audit records); `nixos-modules/manifest.nix` (prior-generation retention pattern in the current bundle contract) | | Reuse action | adapt | | Destination | `packages/d2b-core-controller/src/{configuration.rs, ownership.rs}` | diff --git a/docs/specs/ADR-046-validation-and-delivery.md b/docs/specs/ADR-046-validation-and-delivery.md index d1308d191..03e8439f1 100644 --- a/docs/specs/ADR-046-validation-and-delivery.md +++ b/docs/specs/ADR-046-validation-and-delivery.md @@ -1317,7 +1317,7 @@ binaries. | Reuse source | none required — this generator is specific to the `docs/specs/ADR-046-*` manifest shape | | Reuse action | adapt | | Destination | `packages/xtask/src/gen_spec_set.rs`; `docs/specs/ADR-046-spec-set.json`, `docs/specs/ADR-046-work-items.json` | -| Detailed design | Enumerates every `docs/specs/ADR-046-*.md` and `docs/specs/providers/ADR-046-provider-*.md` file, its metadata table, registered globally unique `workItemPrefix`, content digest, and every `### ADR046--` work item, per §8. Primary reuse disposition: `adapt`. Preserved source-plan detail: adapt (new generator, following the existing `xtask gen-schemas`/`gen-nix-options` pattern already used for other generated artifacts). | +| Detailed design | Enumerates every `docs/specs/ADR-046-*.md` and `docs/specs/providers/ADR-046-provider-*.md` file, its metadata table, bytewise-sorted `workItemPrefixes` registry, content digest, and every `### ADR046--` work item, per §8. Each prefix belongs globally to exactly one member, and generation resolves ownership only through the registry rather than by splitting IDs or filenames. Primary reuse disposition: `adapt`. Preserved source-plan detail: adapt (new generator, following the existing `xtask gen-schemas`/`gen-nix-options` pattern already used for other generated artifacts). | | Integration | `make test-drift` gains a row running this generator and `git diff --exit-code`; every wave's exit criteria (§4) require it committed as the wave's last commit | | Data migration | None — full d2b 3.0 reset; no prior state to migrate | | Validation | Golden-fixture test against a small synthetic spec directory; drift test against the real `docs/specs/` tree | @@ -1397,8 +1397,8 @@ binaries. | Reuse source | `ADR046-delivery-004` generator shape and the existing `d2b-contract-tests` fixture-driven policy-test pattern | | Reuse action | adapt | | Destination | `packages/xtask/src/gen_spec_set.rs`; `packages/d2b-contract-tests/tests/policy_adr046_work_items.rs`; generated spec-set, work-item, and implementation-graph drift checks | -| Detailed design | Parse every normative member's declared work-item headings and tables. Require an exact Markdown/manifest bijection; exact `specId`, `specPath`, and registered prefix; globally unique prefixes and IDs; three-digit nonzero ordinals; every mandatory field exactly once and nonempty; one closed scalar `reuseAction`; and `reuseSource: null` for `create`. Reject dropped, extra, malformed, duplicate, ambiguous, or unconsumed items before writing any artifact. Validate all dependency endpoints, DAG acyclicity, wave monotonicity, and single-wave parallel groups before atomically publishing all generated files. | +| Detailed design | Parse every normative member's declared work-item headings and tables. Require an exact Markdown/manifest bijection; exact `specId` and `specPath`; a bytewise-sorted, nonempty `workItemPrefixes` list for each item-owning member; global one-member ownership for every registered prefix; registry-based ID ownership; three-digit nonzero ordinals; every mandatory field exactly once and nonempty; one closed scalar `reuseAction`; and `reuseSource: null` for `create`. Reject dropped, extra, malformed, duplicate, ambiguous, heuristic-split, unregistered-prefix, or unconsumed items before writing any artifact. Validate all dependency endpoints, DAG acyclicity, wave monotonicity, and single-wave parallel groups before atomically publishing all generated files. | | Integration | `make test-policy` runs negative fixtures; `make test-drift` regenerates all ADR 0046 artifacts and requires a clean diff; `ADR046-delivery-008` consumes only a manifest that passed this policy | | Data migration | None — documentation/build-policy contract only | -| Validation | Fixtures fail for a dropped heading, extra manifest row, duplicate ID/prefix, wrong owner/path/prefix, two-digit/zero ordinal, missing/duplicate mandatory field, free-form/compound action, `create` with a reuse source, dangling dependency, cyclic DAG, backward-wave dependency, and cross-wave parallel group; the exact 55-spec real tree passes with every item once | +| Validation | Fixtures fail for a dropped heading, extra manifest row, duplicate ID, duplicate cross-member prefix, unsorted/empty required prefix registry, wrong owner/path/prefix, heuristic-only prefix match, two-digit/zero ordinal, missing/duplicate mandatory field, free-form/compound action, `create` with a reuse source, dangling dependency, cyclic DAG, backward-wave dependency, and cross-wave parallel group; the exact 55-spec real tree passes with every item once | | Removal proof | Not applicable; the policy remains the permanent generated-artifact closure gate | diff --git a/docs/specs/ADR-046-work-items.json b/docs/specs/ADR-046-work-items.json index 8bf73cc07..4d6ad9fc5 100644 --- a/docs/specs/ADR-046-work-items.json +++ b/docs/specs/ADR-046-work-items.json @@ -437,6 +437,66 @@ "validation" : "Fast hermetic `tests/streams.rs`: projection-Service ownerRef/import binding, split-direction single-authz stream, credits, generation isolation, cancel/deadline, concurrent playback, one active capture across imports, route loss cancels that Zone's active/pending capture, ciphertext-only intermediary, redaction. Only `integration/real_stream.rs` runs the slower real encrypted stream.", "workItemId" : "ADR046-audio-014" }, + { + "currentSource" : "`packages/d2b-realm-core/src/audit.rs` (`AuditHash::parse`, `AuditChainLink::new`/`verify`, `AuditChainRecord{stream: AuditStreamKind, realm: RealmPath, node: NodeId}`, `AuditStreamKind::{Gateway,RemoteNode,Daemon}`, `AuditSinkHealth`, `AuditRetentionFloorStatus`); `packages/d2bd/src/daemon_audit.rs` (hash-chain append algorithm, `prev_hash`/`record_hash` SHA-256 pattern, daily segment files `daemon-events-YYYY-MM-DD.jsonl`, `DaemonEvent` additive contract); `packages/d2b-priv-broker/src/audit.rs` (`AuditWriteClass::{Privileged,Unprivileged}`, `AuditDropSummary`, `DEFAULT_AUDIT_WRITES_PER_SECOND = 4096`, O_APPEND CLOEXEC file open, `AuditDropWarningState`); `packages/d2b-gateway-runtime/src/audit_jsonl.rs` (`JsonlGatewayAudit`, `DEFAULT_GATEWAY_AUDIT_RETENTION_DAYS = 14`, `prune_old` rotation algorithm); `packages/d2b-priv-broker/src/ops/audit_op.rs` (`OpAuditRecord`, `SwtpmDirAudit`, `SwtpmDirResult`, `SwtpmMarkerResult`); `packages/d2b-realm-core/src/ids.rs` (`OperationId`, `CorrelationId`, `PrincipalId` — `PrincipalId` becomes `subject_digest`); `packages/d2b/tests/audit_contract.rs`; `packages/d2b-priv-broker/tests/broker_export_audit.rs`", + "dataMigration" : "v3 bootstrap; existing daemon/broker JSONL files not migrated", + "dependencyOwner" : "W0/W1a; audit crate owner", + "destination" : "`packages/d2b-audit/src/{hash_chain.rs,segment.rs,rate_limit.rs,record_types.rs,sink.rs,export.rs}`", + "detailedDesign" : "`d2b-audit` provides: typed record structs per class; canonical serialization with `zone` replacing `realm: RealmPath`; SHA-256 hash chain (extracted from `daemon_audit.rs`); segment writer (O_APPEND CLOEXEC, 64 MiB / UTC-midnight rotation); 30-day compaction (adapts `prune_old` from `JsonlGatewayAudit`); `AuditWriteClass::{Privileged,Standard,BestEffort}` (extends current `{Privileged,Unprivileged}`); rate-limit with privileged-never-dropped invariant; export iterator with inline hash-break reporting. `AuditStreamKind` re-versioned: `Daemon→Zone`, `Gateway→ZoneLink`, `RemoteNode→RemoteZone`. `AuditChainRecord` re-versioned: `{zone: String}` replaces `{realm: RealmPath, node: NodeId}`. Primary reuse disposition: `adapt`. Preserved source-plan detail: extract unchanged: `AuditHash`, `AuditChainLink` from `d2b-realm-core/src/audit.rs`; copy hash-chain append algorithm from `daemon_audit.rs`; copy `AuditWriteClass`/rate-limit/rotation/prune from broker `audit.rs`; adapt `JsonlGatewayAudit` segment writer; adapt `OpAuditRecord` to `BrokerEffect` record class.", + "integration" : "Zone runtime, core-controller, Process Providers, broker effect bridge → `d2b-audit` sink; `d2b zone audit export` → export iterator", + "removalProof" : "`daemon_audit.rs`, broker `audit.rs`, `JsonlGatewayAudit` retired per-component after `d2b-audit` sink achieves parity", + "reuseAction" : "adapt", + "reuseSource" : null, + "specId" : "ADR-046-telemetry-audit-and-support", + "specPath" : "docs/specs/ADR-046-telemetry-audit-and-support.md", + "validation" : "`audit_record_hash_chain`, `audit_record_schema` (no `realm`/`node` fields), `audit_segment_rotation`, `audit_rate_limit_privileged_never_dropped`, `audit_unavailable_blocks_privileged`", + "workItemId" : "ADR046-audit-001" + }, + { + "currentSource" : "`packages/d2b-priv-broker/src/ops/audit_op.rs` (`OpAuditRecord` structural pattern — operation, peer_uid, decision, result fields); `packages/d2b-realm-core/src/audit.rs::AuditEnvelope{principal: PrincipalId, scope: AuthorizationScope, decision: AuthzDecision}` (principal → v3 `subject_digest`)", + "dataMigration" : "Full d2b 3.0 reset; no v2 state/config import", + "dependencyOwner" : "ADR046-audit-001 + ADR046-store-001; store/authz owner", + "destination" : "`packages/d2b-resource-store-redb/src/audit.rs`, `packages/d2b-core-controller/src/authz_audit.rs`", + "detailedDesign" : "`ResourceMutation` records emitted by the store actor inside the write transaction before commit returns. The audit sink must durably fsync the audit record before returning the commit success (privileged durability class). `RBACChange` emitted by the authz handler in the same write transaction. `subject_digest` = SHA-256 of normalized canonical subject string from v3 `AuthenticatedSubjectContext` (ADR-046-componentsession-and-bus). Primary reuse disposition: `adapt`. Preserved source-plan detail: adapt `OpAuditRecord` structural pattern for `ResourceMutation` / `RBACChange` record classes; adapt `PrincipalId` → `subject_digest` derivation.", + "integration" : "Store write transaction → `d2b-audit` sink → fsync → commit result", + "removalProof" : "None — net-new; no prior owner to remove", + "reuseAction" : "adapt", + "reuseSource" : null, + "specId" : "ADR-046-telemetry-audit-and-support", + "specPath" : "docs/specs/ADR-046-telemetry-audit-and-support.md", + "validation" : "Integration test: 100 mutations → verify hash-chained audit records with `zone` field, no `realm` field", + "workItemId" : "ADR046-audit-002" + }, + { + "currentSource" : "`packages/d2b-gateway/src/audit.rs` (`GatewayAuditEvent`, `GatewayAuditKind::{DisplaySessionOpenAdmitted,DisplaySessionOpenDenied,DisplaySessionRunning,DisplaySessionClosed}`, `GatewayAudit` trait, `NoopGatewayAudit`); `packages/d2b-realm-core/src/audit.rs::AuditEnvelope{realm, principal, scope, decision, trace}` (fields adapted to v3 `SessionConnect` record class)", + "dataMigration" : "Full d2b 3.0 reset; no v2 state/config import", + "dependencyOwner" : "ADR046-audit-001 + ADR046-session-001 + ADR046-bus-001; session/bus owner", + "destination" : "`packages/d2b-session/src/audit.rs`, `packages/d2b-bus/src/audit.rs`", + "detailedDesign" : "`SessionConnect` records emitted at handshake completion. `GatewayAuditKind::DisplaySessionOpenAdmitted/Denied` → `event=\"connect\"`, `authz_decision=\"allowed/denied\"`. `GatewayAuditKind::DisplaySessionRunning` → informational `ProcessEffect`. `GatewayAuditKind::DisplaySessionClosed` → `event=\"close\"`. `transport_class=zone_link` covers what the current `AuditStreamKind::Gateway` stream recorded for gateway-backed realm sessions. `RouteAdmission` records emitted at bus route resolution for denied routes. Primary reuse disposition: `adapt`. Preserved source-plan detail: adapt `GatewayAudit` trait pattern for `SessionConnect` and `RouteAdmission` record classes; `NoopGatewayAudit` pattern reused for test sinks.", + "integration" : "Session engine and bus router → `d2b-audit` sink", + "removalProof" : "`NoopGatewayAudit` and gateway JSONL sink retired after gateway is on v3 resource API", + "reuseAction" : "adapt", + "reuseSource" : null, + "specId" : "ADR-046-telemetry-audit-and-support", + "specPath" : "docs/specs/ADR-046-telemetry-audit-and-support.md", + "validation" : "Session connect/close/auth-failure audit tests; `GatewayAuditKind` → `SessionConnect` mapping test", + "workItemId" : "ADR046-audit-003" + }, + { + "currentSource" : "`packages/d2b/tests/audit_contract.rs` (`d2b audit --strict` returns 78; `auditResponse` relay; `authz-audit-requires-admin` denial; daemon-down exit 1 without bash fallback); `packages/d2b-priv-broker/tests/broker_export_audit.rs` (`export_audit_requires_admin_and_exports_op_audit_records`: admin-only, path-free, NDJSON `ExportBrokerAuditOk` shape, `peer_uid` field, `ApplyNftables` operation name in records)", + "dataMigration" : "Full d2b 3.0 reset; no v2 state/config import", + "dependencyOwner" : "ADR046-audit-001; CLI owner", + "destination" : "`packages/d2b/src/zone_audit.rs` (new `d2b zone audit export` subcommand); `packages/d2b/tests/zone_audit_contract.rs`", + "detailedDesign" : "`d2b zone audit export` opens segments read-only (shared flock), streams NDJSON to stdout, validates hash chain inline, reports breaks as inline error records, enforces `audit-export` verb via resource API (admin-only, same `SO_PEERCRED`/Role check as current `ExportBrokerAuditOk`). Assert no `realm`, `node`, `workload_id` fields in exported records. Primary reuse disposition: `adapt`. Preserved source-plan detail: adapt audit CLI contract test (daemon-down/exit behavior); adapt broker export test to new `zone` field and v3 record schema.", + "integration" : "`d2b` CLI → resource API `audit-export` verb → `d2b-audit` export iterator → stdout", + "removalProof" : "`d2b audit` legacy command retained until `d2b zone audit export` covers all record classes", + "reuseAction" : "adapt", + "reuseSource" : null, + "specId" : "ADR-046-telemetry-audit-and-support", + "specPath" : "docs/specs/ADR-046-telemetry-audit-and-support.md", + "validation" : "`export_audit.rs`: admin-only, hash break inline, no old field names (`realm`/`node`/`workload_id`), no path/argv in output, exit 0 on clean chain", + "workItemId" : "ADR046-audit-004" + }, { "currentSource" : "`d2bd/src/provider_registry.rs`: `AzureVmForbidden`, `AZURE_VM_IMPLEMENTATION_ID`; `d2b-realm-provider/src/provider.rs`: `InfrastructureProvider` (dead-reachable)", "dataMigration" : "Full d2b 3.0 reset; no v2 state/config import. Existing registry sentinels are deleted only after the Provider resource model replaces them.", @@ -572,6 +632,21 @@ "validation" : "All tests pass", "workItemId" : "ADR046-azure-vm-009" }, + { + "currentSource" : "v3 `d2b-realm-router`, target resolver, CLI routing, operation router", + "dataMigration" : "None — full d2b 3.0 reset; no prior state to migrate", + "dependencyOwner" : "Sessions + resource API; bus owner", + "destination" : "`packages/d2b-bus/src/{router,registry,authorization,streams,operations}.rs`", + "detailedDesign" : "Exact service/resource routes, RBAC, pinned reverse route, cancellation, named stream bridge, no wildcard pub/sub Primary reuse disposition: `adapt`. Preserved source-plan detail: extract/adapt.", + "integration" : "Every ResourceClient/controller/Provider/CLI service", + "removalProof" : "Old direct dispatch branches removed only after route parity", + "reuseAction" : "adapt", + "reuseSource" : "Any useful main d2b-client/provider/session routing symbols named by implementation sub-items", + "specId" : "ADR-046-componentsession-and-bus", + "specPath" : "docs/specs/ADR-046-componentsession-and-bus.md", + "validation" : "Message isolation, route/auth revocation, fairness, reconnect, no direct-store path", + "workItemId" : "ADR046-bus-001" + }, { "currentSource" : "`d2b-host/src/runtime_provider.rs`; `d2b-host/src/ch_argv.rs`; `d2bd/src/supervisor/dag.rs`", "dataMigration" : "None (spike)", @@ -872,6 +947,21 @@ "validation" : "Both standard types parse locally; qualified semantic Service/Binding resolve dynamically; verbs route; Service-only export and factory/type/fingerprint/policy/capability rejections; projection preserves semantic Service type across local implementation selection; canonical minimal base works without `spec.provider`; graph shape exact and bounded; Binding never auto-created/deleted; Binding spec intent-only/status observations; no implementation detail/backing/remote-ref/session/stream/FD/secret/path/locator/bytes leakage", "workItemId" : "ADR046-cli-013" }, + { + "currentSource" : "main `a1cc0b2d`: `packages/d2b-client/src/client.rs` (`WallClock`, `MetadataInput`, `RetryPolicy` 1..8 attempts, `CallOptions`, `CancellationToken`, `Client::new/with_clock/connect`, `ConnectedClient` methods incl. `session_generation/session_limits/service/invoke/invoke_with_attachments/named_stream/open_server_stream`, `prepare_typed_request/prepare_operation_context`, `can_retry/retryable_failure/validate_outbound_attachments/validate_reply/service_package/map_remote_kind/map_retry` — lines 35–921); `packages/d2b-client/src/session.rs` (`ComponentSessionConnector` trait, `NamedStream` lifecycle, `StreamDispatcher`, `SharedDriver`, aggregate-queue-bound test — lines 24–626); `packages/d2b-client/src/target.rs` (`ServiceOwner`, `TargetInput`, `TransportKind`, `RouteRecord`, `ResolvedTarget`, `TargetResolver`, `RouteTable` — lines 7–228); `packages/d2b-client/src/service.rs` (`ServiceKind`, `GeneratedClient`, `MethodHandle`, `ServiceHandle::new/kind/generated/proxy/method/invoke` — lines 21–184); `packages/d2b-client/src/daemon_service.rs` (`DaemonClient::new/session_generation/connected/resolve/inspect/lifecycle/open_terminal`, `DaemonTerminal`, `daemon_call_options`, `ensure_daemon_outcome`, `map_ttrpc_error`, test `redacted_terminal_debug_payload` — lines 29–689); `packages/d2b-client/src/host_socket.rs` (`HostSocketConnector::new/from_seqpacket_fd`, `local_daemon_endpoint_identity`, `ComponentSessionConnector::connect` — lines 252–383); `packages/d2b-client/src/error.rs` (`RemoteErrorKind`, `RetryClass`, `ClientError` — lines 5–128)", + "dataMigration" : "Not applicable; updated in place", + "dependencyOwner" : "ADR046-zone-control-011, ADR046-zone-control-012, ADR046-zone-control-013, ADR046-zone-control-018", + "destination" : "`packages/d2b-client/src/` (updated for v3 Zone API, replacing ADR45 daemon verbs with Zone resource operations)", + "detailedDesign" : "**Selected**: `Client::connect()` target-resolve → ComponentSession-open → `ConnectedClient` lifecycle; `RetryPolicy` 1..8 bound + `retryable_failure()` safe-only retry detection; `NamedStream::send/receive/close_local/reset` lifecycle; `ComponentSessionConnector` trait as connector abstraction; `HostSocketConnector::from_seqpacket_fd` + `local_daemon_endpoint_identity` for Zone runtime socket connector; `RouteTable` ambiguous-route rejection; `ServiceHandle`/`GeneratedClient`/`MethodHandle` typed service client pattern; `map_ttrpc_error`/`validate_reply`/`map_retry` error-handling chain; `ClientError`/`RemoteErrorKind`/`RetryClass` error taxonomy; `DaemonClient` call-options and outcome helpers (infrastructure only). **Excluded ADR45 assumptions**: `DaemonMethod` enum (lines 29–56 of daemon_service.rs): ADR45 daemon verbs (`vm_start`, `vm_stop`, `list_realms`, etc.) — replaced with Zone API verbs. `GuestClient`/`guest_service.rs`: ADR45 guest operations; excluded until v3 Guest transport work item. Hardcoded socket path `PUBLIC_SOCKET_PATH = \"/run/d2b/public.sock\"` in `host_socket.rs`: replaced by Zone-resource-managed path. `TransportKind::LocalUnix` restriction in daemon-access: v3 allows multiple transport kinds per ZoneLink binding. Primary reuse disposition: `adapt`. Preserved source-plan detail: copy + adapt.", + "integration" : "`d2b` CLI uses `d2b-client` to connect to Zone runtime via `HostSocketConnector`; a child Zone runtime uses `d2b-client` for its allocator-bound uplink while the parent route engine uses the established session for child calls; Provider toolkit conformance tests use `Fixture`/`FakeProvider` with `d2b-client` service handles", + "removalProof" : "`DaemonMethod` v2 verb enum retired after all v2 daemon operations migrated to Zone API", + "reuseAction" : "adapt", + "reuseSource" : null, + "specId" : "ADR-046-resources-zone-control", + "specPath" : "docs/specs/ADR-046-resources-zone-control.md", + "validation" : "`client-retry-policy-max-8-attempts-enforced`, `client-named-stream-close-local-then-remote-close-transitions-closed`, `client-route-table-ambiguous-route-rejected`, `client-host-socket-peer-uid-verified-on-connect`, `client-retryable-failure-only-safe-mutations`", + "workItemId" : "ADR046-client-001" + }, { "currentSource" : "None — net-new v3 work; no pre-ADR45 baseline equivalent for the provider crate skeleton", "dataMigration" : "None — docs/tooling only; no runtime state", @@ -1100,9 +1190,9 @@ { "currentSource" : "`packages/d2b-realm-provider/src/credential.rs:ManagedIdentityRef` (reachable); `packages/d2b-provider-aca/src/lib.rs:managed_identity_client_id` line 112 (reachable ACA config); `packages/d2bd/src/lib.rs:managed_identity_client_id` lines 3960, 4173 (reachable)", "dataMigration" : "Full v3 reset. `d2b-provider-aca:managed_identity_client_id` raw field migrated to a Credential resource reference in the v3 ACA Provider config; see removal precondition below.", - "dependencyOwner" : "`ADR046-credential-001` (contracts), `ADR046-credential-002` (service); `ADR046-reconcile-001`; `ADR046-cred-mi-005`; `credential-managed-identity` crate owner", + "dependencyOwner" : "`ADR046-credential-001` (contracts), `ADR046-credential-002` (service); `ADR046-reconcile-001`; `ADR046-mi-topology-001`; `credential-managed-identity` crate owner", "destination" : "`packages/d2b-provider-credential-managed-identity/src/{lib.rs, controller.rs, agent.rs, service.rs, audit.rs, telemetry.rs}`; `packages/d2b-provider-credential-managed-identity/{controller/main.rs, agent/main.rs}`; `packages/d2b-provider-credential-managed-identity/tests/{lifecycle.rs, conformance.rs, faults.rs, canary.rs, delivery.rs, placement.rs, topology.rs}`; `packages/d2b-provider-credential-managed-identity/integration/{container-service.sh, host-guest-placement.nix, aca-credential-ref.sh, cleanup-rollback.sh}`; `packages/d2b-provider-credential-managed-identity/README.md`", - "detailedDesign" : "(1) Adapt `ManagedIdentityCredentialProvider` to `d2b.credential.v3` service interface; split controller and agent roles (see `ADR046-cred-mi-005`). (2) Enforce `ManagedIdentityCredentialOwner::ExactSdkConsumer` in agent via `AuthenticatedSubjectContext` from ComponentSession, independently of scope fields. (3) Reject `user-agent` placement: `scope.domainFilter=user` returns `credential-placement-mismatch` before agent spawn. (4) Validate `clientId` using `OpaqueAzureRef::parse` from v3 baseline; artifact IDs match `^[a-z][a-z0-9-]*$`. (5) Validate `imdsEndpointAlias` against closed enum `{azure-imds, azure-imds-aca}`; project into LaunchTicket at spawn time (never into Process spec config or env); co-located runtime Provider constructs `ManagedIdentityCredentialClient` from LaunchTicket projection and supplies via effect port; resolved URL never in any output surface. (6) Agent Process declares `networkUsage.allowEgress=false`; uses canonical Process template shape (see `ADR046-cred-mi-005` design item 6). (7) Reject `sign-challenge` with `credential-schema-invalid` immediately. (8) Map `ManagedIdentityClientState::Unavailable` to `credential-provider-unavailable`; no `InteractionRequired` state. (9) Implement `ManagedIdentityLeaseHandle` as opaque bounded newtype with redacted `Debug`. (10) All token bytes held by injected client in agent; delivered only via agent-terminated `Noise_KK` delivery session. (11) Integrate with Provider resource descriptor and controller toolkit. (12) Confirm `credential_canary` never appears in any service response, status field, delivery record outer header, or audit record. (13) Apply D087 status-first state: declare no Provider state Volume, keep ProviderStateSet empty, and write only bounded non-secret lease observation to `Credential.status` plus the Operation ledger. Primary reuse disposition: `adapt`. Preserved source-plan detail: copy and adapt.", + "detailedDesign" : "(1) Adapt `ManagedIdentityCredentialProvider` to `d2b.credential.v3` service interface; split controller and agent roles (see `ADR046-mi-topology-001`). (2) Enforce `ManagedIdentityCredentialOwner::ExactSdkConsumer` in agent via `AuthenticatedSubjectContext` from ComponentSession, independently of scope fields. (3) Reject `user-agent` placement: `scope.domainFilter=user` returns `credential-placement-mismatch` before agent spawn. (4) Validate `clientId` using `OpaqueAzureRef::parse` from v3 baseline; artifact IDs match `^[a-z][a-z0-9-]*$`. (5) Validate `imdsEndpointAlias` against closed enum `{azure-imds, azure-imds-aca}`; project into LaunchTicket at spawn time (never into Process spec config or env); co-located runtime Provider constructs `ManagedIdentityCredentialClient` from LaunchTicket projection and supplies via effect port; resolved URL never in any output surface. (6) Agent Process declares `networkUsage.allowEgress=false`; uses canonical Process template shape (see `ADR046-mi-topology-001` design item 6). (7) Reject `sign-challenge` with `credential-schema-invalid` immediately. (8) Map `ManagedIdentityClientState::Unavailable` to `credential-provider-unavailable`; no `InteractionRequired` state. (9) Implement `ManagedIdentityLeaseHandle` as opaque bounded newtype with redacted `Debug`. (10) All token bytes held by injected client in agent; delivered only via agent-terminated `Noise_KK` delivery session. (11) Integrate with Provider resource descriptor and controller toolkit. (12) Confirm `credential_canary` never appears in any service response, status field, delivery record outer header, or audit record. (13) Apply D087 status-first state: declare no Provider state Volume, keep ProviderStateSet empty, and write only bounded non-secret lease observation to `Credential.status` plus the Operation ledger. Primary reuse disposition: `adapt`. Preserved source-plan detail: copy and adapt.", "integration" : "Agent `Process` resource under `Host` or `Guest` executionRef; controller `Process` resource at Zone system host; d2b-bus routes `d2b.credential.v3` token-delivery calls to agent; Credential controller reconciles status; ACA `Provider/runtime-azure-container-apps` holds `credentialRef` pointing to a `credential-managed-identity`-backed Credential resource", "removalProof" : "`d2b-provider-aca:managed_identity_client_id` raw field removed only after the `credential-managed-identity` controller and agent are integrated and the ACA Provider config uses `credentialRef` exclusively; `ProviderWorkloadIdentity::ManagedIdentity` bootstrap path superseded only after the ACA Provider controller uses the Credential resource for token acquisition", "reuseAction" : "adapt", @@ -1115,7 +1205,7 @@ { "currentSource" : "`packages/d2b-realm-provider/src/provider.rs:CredentialProvider` status/enrollment-only trait; main `a1cc0b2d` managed-identity controller/test behavior listed in §Source reuse", "dataMigration" : "None — controller lifecycle code only; no runtime state import", - "dependencyOwner" : "ADR046-credential-001, ADR046-credential-002; ADR046-reconcile-001, ADR046-reconcile-002; ADR046-cred-mi-005; owner: Credential controller toolkit and managed-identity controller", + "dependencyOwner" : "ADR046-credential-001, ADR046-credential-002; ADR046-reconcile-001, ADR046-reconcile-002; ADR046-mi-topology-001; owner: Credential controller toolkit and managed-identity controller", "destination" : "packages/d2b-provider-credential-managed-identity/src/controller.rs; packages/d2b-contracts/src/v3/credential_controller.rs", "detailedDesign" : "Managed-identity-specific controller design: implement async reconcile and agent spawn/teardown from §Async reconcile; enforce system-only domain; spawn agent on Credential admission plus dependency-ready, not on `phase=Ready`; implement `observeInterval=30s` health-check RPC to the agent, which calls `InspectMetadata` on the injected client; controller never calls IMDS; derive idempotency key as `SHA-256(UID \\|\\| \":\" \\|\\| rotationGeneration.to_le_bytes() \\|\\| \":\" \\|\\| operation_class_byte)`; enforce `MAX_LOCAL_LEASES=256` in the resource store; implement Deleted-phase closure by clearing `provider-revoke` only after agent Process deletion and revocation confirmation while core/audit own Deleted revision and deletion record. Primary reuse disposition: `adapt`. Preserved source-plan detail: adapt shared Credential controller lifecycle to managed-identity controller/agent spawn and teardown.", "integration" : "Shared Credential controller contract produces reconcile events; managed-identity controller consumes them, manages agent Process resources, and writes Credential/agent status; generated controller contracts are consumed by all Credential Providers.", @@ -1145,7 +1235,7 @@ { "currentSource" : "`packages/d2b-realm-provider/src/error.rs:contains_sensitive_shape`; `packages/d2b-core/src/realm_workloads_launcher.rs:LauncherMetadataInvariants.no_secrets_or_credentials`; main `a1cc0b2d` managed-identity canary tests listed in §Source reuse", "dataMigration" : "None — audit/telemetry only; no runtime state import", - "dependencyOwner" : "Depends on ADR046-cred-mi-001 and ADR046-cred-mi-005; owner: credential-managed-identity audit/telemetry implementation", + "dependencyOwner" : "Depends on ADR046-cred-mi-001 and ADR046-mi-topology-001; owner: credential-managed-identity audit/telemetry implementation", "destination" : "packages/d2b-provider-credential-managed-identity/src/{audit.rs,telemetry.rs}; packages/d2b-contract-tests/tests/credential_audit.rs", "detailedDesign" : "Shared audit/OTEL: emit audit records for all methods and controller events per §Audit; emit OTEL spans and metrics per §OTEL and metrics; add `d2b_credential_imds_calls_total` counter with bounded `alias` label; enforce `contains_sensitive_shape` on all string fields in audit records and metric labels; add canary tests for `managed-identity-canary`, `credential_canary`, and `imds-endpoint-canary` in `canary.rs`. Primary reuse disposition: `adapt`. Preserved source-plan detail: adapt existing sensitive-shape guard and canary pattern to v3 audit, OTEL, and metric surfaces.", "integration" : "Controller and agent service methods call audit/telemetry helpers; audit subsystem and OTEL exporters consume bounded redacted records; contract tests validate credential audit shape across providers.", @@ -1157,21 +1247,6 @@ "validation" : "`packages/d2b-contract-tests/tests/credential_audit.rs`; managed-identity `canary.rs`; audit/OTEL unit tests for labels and sensitive-shape rejection", "workItemId" : "ADR046-cred-mi-004" }, - { - "currentSource" : "`packages/d2b-realm-provider/src/provider.rs:CredentialProvider` status/enrollment-only trait; main `a1cc0b2d` managed-identity Provider implementation and tests listed in §Source reuse", - "dataMigration" : "Full d2b 3.0 reset; no v2 managed-identity process/session state import", - "dependencyOwner" : "ADR046-credential-001 and ADR046-credential-002; owner: credential-managed-identity controller/agent topology. This topology contract is consumed by the later managed-identity controller work.", - "destination" : "packages/d2b-provider-credential-managed-identity/src/{controller.rs,agent.rs}; packages/d2b-provider-credential-managed-identity/{controller/main.rs,agent/main.rs}; packages/d2b-provider-credential-managed-identity/tests/topology.rs", - "detailedDesign" : "Implement the controller/agent process split: separate `d2b-managed-identity-controller` binary with no IMDS client and no KK delivery, and `d2b-managed-identity-agent` binary with injected IMDS client via effect port and KK delivery. Controller manages Credential resources, spawns/monitors agent Processes, uses canonical Process templates, attaches LaunchTickets projecting `imdsEndpointAlias` and `credentialRef`, monitors agent Process health with bounded backoff, performs Deleted-phase cleanup without emitting Deleted closure audit, and applies D087 status-first state with no Provider state Volume. Agent validates `ExactSdkConsumer` via `AuthenticatedSubjectContext`, serves token-delivery methods, terminates Noise_KK delivery sessions, reports lease state, declares no direct IMDS egress, and keeps token bytes transient. Primary reuse disposition: `adapt`. Preserved source-plan detail: copy and adapt the main managed-identity Provider; replace v2 provider registry/session assumptions with v3 controller/agent Process topology and `d2b.credential.v3` service.", - "integration" : "ProviderDeployment starts the controller Process; controller reconciles Credential resources and creates agent Process/Endpoint resources at the declared executionRef; d2b-bus routes `d2b.credential.v3` calls to the agent; co-located runtime Provider injects the IMDS client through the LaunchTicket/effect port; core aggregates Provider status and audit subsystem appends deletion records.", - "removalProof" : "V2 single-process/trait topology is superseded once controller and agent Process split is integrated and all token delivery terminates in the agent", - "reuseAction" : "adapt", - "reuseSource" : null, - "specId" : "ADR-046-provider-credential-managed-identity", - "specPath" : "docs/specs/providers/ADR-046-provider-credential-managed-identity.md", - "validation" : "`tests/topology.rs`; `integration/host-guest-placement.nix`; `make test-rust`; `make test-integration`; `make test-host-integration`", - "workItemId" : "ADR046-cred-mi-005" - }, { "currentSource" : "`packages/d2b-realm-provider/src/provider.rs:CredentialProvider`; `packages/d2b-realm-provider/src/credential.rs` opaque credential refs and `OpaqueAzureRef` helpers", "dataMigration" : "Full d2b 3.0 reset; no v2 CredentialProvider status/config import", @@ -1280,7 +1355,7 @@ { "currentSource" : "`packages/d2b-realm-provider/src/provider.rs:CredentialProvider` (status-only trait); `d2b-contracts/proto/v2/provider_credential.proto` (main: Health, Capabilities, Status, AcquireLease, RefreshLease, RevokeLease)", "dataMigration" : "None — full d2b 3.0 reset; no prior state to migrate", - "dependencyOwner" : "`ADR046-credential-001`; `ADR046-api-001` (resource API); `ADR046-session-003` (d2b-bus); Credential service owner", + "dependencyOwner" : "`ADR046-credential-001`; `ADR046-api-001` (resource API); `ADR046-bus-001` (d2b-bus); Credential service owner", "destination" : "`packages/d2b-contracts/proto/v3/credential.proto`; `packages/d2b-credential-service/src/{service.rs, client.rs, server.rs}`", "detailedDesign" : "Define `d2b.credential.v3` protobuf service with methods: `Status`, `AcquireToken`, `RefreshToken`, `RevokeToken`, `SignChallenge`, `InspectMetadata`; each request carries `credential_ref`, `operation_class`, `operation_id`, `idempotency_key`, `requested_expiry_unix_ms`, `deadline_unix_ms`; `Status`, `RevokeToken`, and `InspectMetadata` responses carry only non-secret metadata (leaseHandle digest, rotationGeneration, sourceVersion, expiresAtUnixMs, state, outcome code); `AcquireToken`, `RefreshToken`, and `SignChallenge` responses additionally include a `delivery_session_params` field carrying the binding contract fields required to establish the end-to-end credential-delivery ComponentSession (see §Credential-delivery endpoint contract); the token bytes themselves travel in the separate Noise-encrypted delivery session, never in the outer DTO; strict unknown-field rejection; bounded message sizes; all record wrappers for delivery sessions must be zeroizing types", "integration" : "d2b-bus routes `d2b.credential.v3` service to the exact credential provider Process identified by `Credential.spec.providerRef`; RBAC checks `use-credential` verb before dispatch; for `AcquireToken`/`RefreshToken`/`SignChallenge`, bus additionally authorizes the credential-delivery endpoint route and forwards opaque Noise-encrypted delivery records without terminating or buffering them; bus never stores or inspects delivery record plaintext", @@ -1447,7 +1522,7 @@ "dataMigration" : "None — full d2b 3.0 reset; no prior state to migrate", "dependencyOwner" : "`ADR046-delivery-002`; spec-set integrator", "destination" : "`packages/xtask/src/gen_spec_set.rs`; `docs/specs/ADR-046-spec-set.json`, `docs/specs/ADR-046-work-items.json`", - "detailedDesign" : "Enumerates every `docs/specs/ADR-046-*.md` and `docs/specs/providers/ADR-046-provider-*.md` file, its metadata table, registered globally unique `workItemPrefix`, content digest, and every `### ADR046--` work item, per §8. Primary reuse disposition: `adapt`. Preserved source-plan detail: adapt (new generator, following the existing `xtask gen-schemas`/`gen-nix-options` pattern already used for other generated artifacts).", + "detailedDesign" : "Enumerates every `docs/specs/ADR-046-*.md` and `docs/specs/providers/ADR-046-provider-*.md` file, its metadata table, bytewise-sorted `workItemPrefixes` registry, content digest, and every `### ADR046--` work item, per §8. Each prefix belongs globally to exactly one member, and generation resolves ownership only through the registry rather than by splitting IDs or filenames. Primary reuse disposition: `adapt`. Preserved source-plan detail: adapt (new generator, following the existing `xtask gen-schemas`/`gen-nix-options` pattern already used for other generated artifacts).", "integration" : "`make test-drift` gains a row running this generator and `git diff --exit-code`; every wave's exit criteria (§4) require it committed as the wave's last commit", "removalProof" : "Not applicable", "reuseAction" : "adapt", @@ -1522,14 +1597,14 @@ "dataMigration" : "None — documentation/build-policy contract only", "dependencyOwner" : "`ADR046-delivery-004`, `ADR046-delivery-008`; spec-set policy-test owner", "destination" : "`packages/xtask/src/gen_spec_set.rs`; `packages/d2b-contract-tests/tests/policy_adr046_work_items.rs`; generated spec-set, work-item, and implementation-graph drift checks", - "detailedDesign" : "Parse every normative member's declared work-item headings and tables. Require an exact Markdown/manifest bijection; exact `specId`, `specPath`, and registered prefix; globally unique prefixes and IDs; three-digit nonzero ordinals; every mandatory field exactly once and nonempty; one closed scalar `reuseAction`; and `reuseSource: null` for `create`. Reject dropped, extra, malformed, duplicate, ambiguous, or unconsumed items before writing any artifact. Validate all dependency endpoints, DAG acyclicity, wave monotonicity, and single-wave parallel groups before atomically publishing all generated files.", + "detailedDesign" : "Parse every normative member's declared work-item headings and tables. Require an exact Markdown/manifest bijection; exact `specId` and `specPath`; a bytewise-sorted, nonempty `workItemPrefixes` list for each item-owning member; global one-member ownership for every registered prefix; registry-based ID ownership; three-digit nonzero ordinals; every mandatory field exactly once and nonempty; one closed scalar `reuseAction`; and `reuseSource: null` for `create`. Reject dropped, extra, malformed, duplicate, ambiguous, heuristic-split, unregistered-prefix, or unconsumed items before writing any artifact. Validate all dependency endpoints, DAG acyclicity, wave monotonicity, and single-wave parallel groups before atomically publishing all generated files.", "integration" : "`make test-policy` runs negative fixtures; `make test-drift` regenerates all ADR 0046 artifacts and requires a clean diff; `ADR046-delivery-008` consumes only a manifest that passed this policy", "removalProof" : "Not applicable; the policy remains the permanent generated-artifact closure gate", "reuseAction" : "adapt", "reuseSource" : "`ADR046-delivery-004` generator shape and the existing `d2b-contract-tests` fixture-driven policy-test pattern", "specId" : "ADR-046-validation-and-delivery", "specPath" : "docs/specs/ADR-046-validation-and-delivery.md", - "validation" : "Fixtures fail for a dropped heading, extra manifest row, duplicate ID/prefix, wrong owner/path/prefix, two-digit/zero ordinal, missing/duplicate mandatory field, free-form/compound action, `create` with a reuse source, dangling dependency, cyclic DAG, backward-wave dependency, and cross-wave parallel group; the exact 55-spec real tree passes with every item once", + "validation" : "Fixtures fail for a dropped heading, extra manifest row, duplicate ID, duplicate cross-member prefix, unsorted/empty required prefix registry, wrong owner/path/prefix, heuristic-only prefix match, two-digit/zero ordinal, missing/duplicate mandatory field, free-form/compound action, `create` with a reuse source, dangling dependency, cyclic DAG, backward-wave dependency, and cross-wave parallel group; the exact 55-spec real tree passes with every item once", "workItemId" : "ADR046-delivery-009" }, { @@ -1907,6 +1982,36 @@ "validation" : "All six scenarios above pass; no socket paths in test output", "workItemId" : "ADR046-display-004" }, + { + "currentSource" : "`packages/d2b/tests/host_doctor_contract.rs` (env-redirect sandbox: `D2B_BROKER_SOCKET`, `D2B_PUBLIC_SOCKET`, `D2B_DAEMON_STATE_DIR`, `D2B_METRICS_URL`, `D2B_MANIFEST_PATH`; `doctor::render_summary` JSON envelope fields: `command`, `mode`, `broker_ready`, per-check `status`+`data`, `summary`, `exitCode`); `packages/d2bd/src/audit_check.rs` (`defects` array audit-chain validation pattern); `packages/d2bd/src/lib.rs` (doctor read-only path: `host doctor --read-only` reads from `D2B_DAEMON_STATE_DIR`, pidfd_table file, kernel-module check file, metrics URL)", + "dataMigration" : "Full d2b 3.0 reset; no v2 state/config import", + "dependencyOwner" : "ADR046-core-001 + ADR046-audit-001; CLI/doctor owner", + "destination" : "`packages/d2b/src/zone_doctor.rs`, `packages/d2b/tests/zone_doctor_contract.rs`", + "detailedDesign" : "`d2b zone doctor [--zone ] [--json]` reads resource status from Zone API (read-only verb), OTEL self-metrics from `observability-otel` Provider endpoint (optional), and audit segment inventory from `d2b-audit` segment reader. Named check set from this spec. Exit 0 on all-ready; 1 on any warn/error. Env-redirect sandbox for all test fixtures. Current `MANIFEST_JSON` fixture pattern adapted: `\"_observability\": {\"enabled\": false}` test ensures OTEL probe short-circuits cleanly. Primary reuse disposition: `adapt`. Preserved source-plan detail: adapt env-redirect sandbox test harness; adapt `defects` array pattern for `audit-hash-chain-clean` check; adapt `broker_ready` → `zone_phase` field.", + "integration" : "`d2b` CLI → Zone resource API status reads + audit segment reader", + "removalProof" : "`d2b host doctor` retained until `d2b zone doctor` covers all check parity", + "reuseAction" : "adapt", + "reuseSource" : null, + "specId" : "ADR-046-telemetry-audit-and-support", + "specPath" : "docs/specs/ADR-046-telemetry-audit-and-support.md", + "validation" : "`zone_doctor_contract.rs`: all-ready/degraded/quarantine/otel-absent/audit-absent fixtures; no resource names/paths/argv/PIDs; `zone_phase` field present; no legacy `broker_ready` field", + "workItemId" : "ADR046-doctor-001" + }, + { + "currentSource" : "`packages/d2b/tests/host_doctor_contract.rs` (env-redirect sandbox scaffold — no current `support-bundle` equivalent exists)", + "dataMigration" : "Full d2b 3.0 reset; no v2 state/config import", + "dependencyOwner" : "ADR046-doctor-001; CLI/doctor owner", + "destination" : "`packages/d2b/src/zone_support_bundle.rs`, `packages/d2b/tests/zone_support_bundle_contract.rs`", + "detailedDesign" : "`d2b zone support-bundle [--zone ]` requires `support-bundle` verb. Reads bounded resource status snapshots (32 per type, 512 total; metadata + status only; no spec bytes; no `metadata.name`), controller queue depths, schema catalog (names+versions only), audit segment inventory, OTEL collector metrics summary, bounded structured log ring (2000 entries). NDJSON output. On quarantine: `bundle_completeness: \"partial\"`, exit 1. Primary reuse disposition: `adapt`. Preserved source-plan detail: reuse env-redirect sandbox scaffold.", + "integration" : "`d2b` CLI → Zone resource API list (status subresource only) + controller introspection + audit segment reader + OTEL self-metrics", + "removalProof" : "None — net-new; no prior owner to remove", + "reuseAction" : "adapt", + "reuseSource" : null, + "specId" : "ADR-046-telemetry-audit-and-support", + "specPath" : "docs/specs/ADR-046-telemetry-audit-and-support.md", + "validation" : "`zone_support_bundle_contract.rs`: complete/partial bundles; no spec/name/path/argv; field completeness", + "workItemId" : "ADR046-doctor-002" + }, { "currentSource" : "`packages/d2b-core/src/processes.rs`: `ProcessRole` (18 variants), `ProcessNode`, `RoleProfile`, `NamespaceSet`, `MountPolicy`, `CgroupPlacement`, `ReadinessPredicate`; `packages/d2b-core/src/minijail_profile.rs`: `MinijailProfile`, `UserNamespaceProfile`, `NamespaceSet`, `MountPolicy`, `BindMount`, `CgroupPlacement`; `packages/d2b-core/src/storage.rs`: `StoragePathSpec`, `AclGrant`, `CleanupPolicy`, `RepairPolicy`; `packages/d2b-realm-core/src/ids.rs`: `RealmId`, `WorkloadId` (→ GuestRef), `NodeId` (→ HostRef), `ProviderId` (→ Provider ResourceRef), `ExecutionId` (→ EphemeralProcess exec identity), `PrincipalId` (→ User ResourceRef), `AllocatorLeaseId`, `ControllerGenerationId`; `packages/d2b-realm-core/src/workload.rs`: `WorkloadProviderKind` (`LocalVm`→runtime-cloud-hypervisor Provider, `QemuMedia`→runtime-qemu-media Provider, `ProviderManaged`→ACA/relay Providers, `UnsafeLocal`→user-only Host `isolationPosture=\"none\"`), `IsolationPosture` (`VirtualMachine`→Guest, `ProviderManaged`→Guest, `UnsafeLocal`→Host `isolationPosture=\"none\"`), `WorkloadExecutionPosture`, `WorkloadSummary`, `WorkloadState`; `packages/d2b-realm-core/src/target.rs`: `RealmTarget`, `TargetName`, `RealmTargetParser` (current analog for `/` ResourceRef parsing); `packages/d2b-realm-core/src/realm.rs`: `RealmPath`, `RealmControllerPlacement`, `EntrypointMode` (current Zone hierarchy analog); `packages/d2b-core/src/workload_identity.rs`: `WorkloadIdentity`, `WorkloadTarget` (= `RealmTarget`), `WorkloadBackend`, `WorkloadRuntimeIntent` (identity/backend separation reuse model for Host/Guest ResourceType split)", "dataMigration" : "Full reset; no v2 resource import", @@ -2252,21 +2357,6 @@ "validation" : "Idempotency replay returns original result; conflict returns error; expired tombstone fails closed; `MAX_DISPATCH_IN_FLIGHT` semaphore back-pressure; principal-binding enforcement (mismatched principal returns auth-denied); `DurableExecTable` capacity limit; v3 5-tuple dedup key golden vector test", "workItemId" : "ADR046-exec-023" }, - { - "currentSource" : "None — the fixed user-session authority is today ambient prose across the display/audio/clipboard/notification/secret-service dossiers; no named owner exists", - "dataMigration" : "None — full d2b 3.0 reset", - "dependencyOwner" : "ADR046-zone-control-019 (authority index); `Provider/system-systemd` (user manager) + core/user-agent owner", - "destination" : "`packages/d2b-core-controller/src/user_session_authority.rs` (or a core/user-agent per-session agent Process under `Provider/system-systemd`); `AuthorityDescriptor` on the session authority", - "detailedDesign" : "Name and implement the **fixed user-session authority** (D097 desktop/session): `authorityScope: seat` bound to `(Host, User, login-session/seat)`, opaque `authorityKey` (never a raw socket path/XDG_RUNTIME_DIR/DISPLAY/seat name), `cardinality: exactly-one` per `(Host, User, login-session)`, `arbitration: exclusive`, owner = a core/user-agent per-user-session agent Process (NOT a new Provider), adoption by `ownerProof` (agent Process identity + login-session id), `exportability: forbidden`. It is the sole opener of the compositor/PipeWire/session-bus FDs and hands them to desktop Providers only via the EffectPort/LaunchTicket. Core's authority index rejects a duplicate session authority (or a duplicate same-user display portal, clipboard host, notification sink, audio mediator, systemd user manager, Secret Service, or seat-input claimant) with `duplicateConflict` before any FD open; multi-user/seat is admitted only up to the declared per-Host limit. Guest-stop invalidates every session authority/lease bound to that Guest across display/audio/notification/credential/shell in one dependency-aware cascade (D091), with no stale FD surviving. Host input (`wl_seat`/pointer constraints) is an `at-most-one`-per-seat authority under this session authority; pointer-constraint enforcement is a declared boundary until an interaction Provider implements it. Primary reuse disposition: `adapt`. Preserved source-plan detail: net-new (name and implement the shared user-session authority).", - "integration" : "Core authority index (ADR046-zone-control-019); `Provider/system-systemd` user manager; display/audio/clipboard/notification/secret-service/shell Provider services bind to this single authority for their FDs; D091 Guest-stop cascade", - "removalProof" : "Not applicable (net-new named authority; replaces ambient prose)", - "reuseAction" : "adapt", - "reuseSource" : "`Provider/system-systemd` user-manager scope; D077 EffectPort/LaunchTicket FD handoff", - "specId" : "ADR-046-resources-host-guest-process-user", - "specPath" : "docs/specs/ADR-046-resources-host-guest-process-user.md", - "validation" : "Single session authority per `(Host, User, session)`; duplicate same-user session authority / desktop service rejected with `duplicateConflict`; multi-seat declared-limit enforcement; Guest-stop invalidates all bound desktop/audio/notification/credential/shell authorities and leases (no stale compositor/PipeWire/session-bus FD); seat-input second claimant rejected; adoption by `ownerProof` and quarantine on ambiguity; hermetic with fakes", - "workItemId" : "ADR046-exec-024" - }, { "currentSource" : "`packages/d2b-core/src/{storage,sync}.rs` (atomic/idempotency reference shape, E4); no redb usage exists anywhere in this repository at any inspected commit", "dataMigration" : "None (disposable fixture data only)", @@ -2567,6 +2657,21 @@ "validation" : "`make test-policy` (workspace crate layout policy check)", "workItemId" : "ADR046-gpu-009" }, + { + "currentSource" : "`packages/d2b-core/src/unsafe_local_workloads.rs` (`UnsafeLocalWorkloadsJson`, `UnsafeLocalWorkload`, `UnsafeLocalLauncherItem`, `UNSAFE_LOCAL_WORKLOADS_SCHEMA_VERSION = \"v2\"`, `MAX_UNSAFE_LOCAL_WORKLOADS = 256`); `packages/d2b-contracts/src/unsafe_local_wire.rs` (`HelperHello.uid: u32`, `HelperLaunchRequest`, `HelperShellRequest`, `HelperScopeKind::{Exec,Shell}`, `DaemonToUnsafeLocalHelper`, `UnsafeLocalHelperToDaemon`); `packages/d2bd/src/unsafe_local_helper.rs` (`HelperRegistry::new(daemon_uid, allowed_uids)`, `dispatch_launch`, `bind_helper_socket`); `packages/d2b-unsafe-local-helper/src/{main,protocol,runtime,systemd}.rs` (`HelperClient`, `ScopeRuntime`, `run_scope_supervisor`, `SystemdUserScopeManager`); `nixos-modules/options-realms-workloads.nix` (lines 221, 233–235 `kind = \"unsafe-local\"` description; lines 264–275 null `stateDir`/`runDir`); `nixos-modules/unsafe-local-workloads-json.nix` (`runtimeKind = \"unsafe-local\"`, `providerId = \"unsafe-local\"`); `nixos-modules/unsafe-local-helper.nix` (service unit)", + "dataMigration" : "Full d2b 3.0 reset; no v2 state/config import", + "dependencyOwner" : "ADR046-audit-001 + ADR046-core-001; `Provider/system-core` owner", + "destination" : "`packages/d2b-provider-system-core/src/{host_reconciler.rs,host_status.rs,host_process_audit.rs}`; adapted `nixos-modules/unsafe-local-workloads-json.nix`; `packages/d2b-provider-system-core/tests/host_posture_contract.rs`", + "detailedDesign" : "`Provider/system-core` reconciler: (1) On user-only `Host` resource creation (`defaultDomain=user`, `allowedDomains=[user]`), set `status.isolationPosture = \"none\"` and `status.isolationPostureMessage = \"...\"` unconditionally; reject any operator-supplied value for these fields. Host resources with other execution policies do not receive `isolationPosture`. (2) On every user-only Host process launch: emit `ProcessEffect{event:\"launch\", provider:\"system-core-user\", domain:\"user\", no_isolation:true, ...}` audit record. (3) On every user-only Host process stop: emit `ProcessEffect{event:\"stop\", ...}`. (4) `d2b zone list`/`inspect` CLI renders `⚠ no isolation boundary (user domain)` annotation only for `Host` resources with `isolationPosture: \"none\"`; annotation is not suppressible. (5) `isolation-posture-declared` doctor check: passes when user-only `Host` resource status has `isolationPosture: \"none\"`; omitted when Zone has no user-only `Host` resources. (6) `no_isolation=true` is emitted in `ProcessEffect` records only; it does not appear in any OTEL span attribute, log field, or metric label. Primary reuse disposition: `adapt`. Preserved source-plan detail: adapt `UnsafeLocalWorkload` private-bundle contract for the `Host` resource spec payload; adapt `HelperRegistry::allowed_uids` constraint as `defaultUserRef=User/` validation; adapt Nix `unsafe-local-workloads-json.nix` emitter for the new Host resource shape; gap-fill: add `ProcessEffect{no_isolation:true}` at `dispatch_launch` / stop call sites.", + "integration" : "`Provider/system-core` reconciler → `d2b-audit` sink; `d2b zone doctor` → resource status check; `d2b zone list`/`inspect` → CLI output renderer", + "removalProof" : "`d2b-unsafe-local-helper` binary and `DaemonToUnsafeLocalHelper`/`UnsafeLocalHelperToDaemon` wire types retired after `Provider/system-core` Process Provider supervisor ticket migration; `nixos-modules/unsafe-local-helper.nix` Nix unit retired after migration; `nixos-modules/unsafe-local-workloads-json.nix` adapted (not deleted) to emit Host resource spec format", + "reuseAction" : "adapt", + "reuseSource" : null, + "specId" : "ADR-046-telemetry-audit-and-support", + "specPath" : "docs/specs/ADR-046-telemetry-audit-and-support.md", + "validation" : "`host_posture_contract.rs` tests from the Host posture tests section of this spec; `d2b-contract-tests/tests/policy_telemetry_redaction.rs` asserts `no_isolation` key absent from all span/metric/log surfaces", + "workItemId" : "ADR046-host-posture-001" + }, { "currentSource" : "`packages/d2b-realm-core/src/ids.rs`, `realm.rs`, `target.rs`, `workload.rs`", "dataMigration" : "Destructive d2b 3.0 reset; no RealmRef parser compatibility", @@ -2597,6 +2702,21 @@ "validation" : "nix-unit vectors and rendered contract tests", "workItemId" : "ADR046-identities-002" }, + { + "currentSource" : "`packages/d2b-realm-provider/src/provider.rs:CredentialProvider` status/enrollment-only trait; main `a1cc0b2d` managed-identity Provider implementation and tests listed in §Source reuse", + "dataMigration" : "Full d2b 3.0 reset; no v2 managed-identity process/session state import", + "dependencyOwner" : "ADR046-credential-001 and ADR046-credential-002; owner: credential-managed-identity controller/agent topology. This topology contract is consumed by the later managed-identity controller work.", + "destination" : "packages/d2b-provider-credential-managed-identity/src/{controller.rs,agent.rs}; packages/d2b-provider-credential-managed-identity/{controller/main.rs,agent/main.rs}; packages/d2b-provider-credential-managed-identity/tests/topology.rs", + "detailedDesign" : "Implement the controller/agent process split: separate `d2b-managed-identity-controller` binary with no IMDS client and no KK delivery, and `d2b-managed-identity-agent` binary with injected IMDS client via effect port and KK delivery. Controller manages Credential resources, spawns/monitors agent Processes, uses canonical Process templates, attaches LaunchTickets projecting `imdsEndpointAlias` and `credentialRef`, monitors agent Process health with bounded backoff, performs Deleted-phase cleanup without emitting Deleted closure audit, and applies D087 status-first state with no Provider state Volume. Agent validates `ExactSdkConsumer` via `AuthenticatedSubjectContext`, serves token-delivery methods, terminates Noise_KK delivery sessions, reports lease state, declares no direct IMDS egress, and keeps token bytes transient. Primary reuse disposition: `adapt`. Preserved source-plan detail: copy and adapt the main managed-identity Provider; replace v2 provider registry/session assumptions with v3 controller/agent Process topology and `d2b.credential.v3` service.", + "integration" : "ProviderDeployment starts the controller Process; controller reconciles Credential resources and creates agent Process/Endpoint resources at the declared executionRef; d2b-bus routes `d2b.credential.v3` calls to the agent; co-located runtime Provider injects the IMDS client through the LaunchTicket/effect port; core aggregates Provider status and audit subsystem appends deletion records.", + "removalProof" : "V2 single-process/trait topology is superseded once controller and agent Process split is integrated and all token delivery terminates in the agent", + "reuseAction" : "adapt", + "reuseSource" : null, + "specId" : "ADR-046-provider-credential-managed-identity", + "specPath" : "docs/specs/providers/ADR-046-provider-credential-managed-identity.md", + "validation" : "`tests/topology.rs`; `integration/host-guest-placement.nix`; `make test-rust`; `make test-integration`; `make test-host-integration`", + "workItemId" : "ADR046-mi-topology-001" + }, { "currentSource" : "`d2b-core/src/minijail_profile.rs`; `d2b-core/src/processes.rs` (NamespaceSet, MountPolicy, CgroupPlacement); `d2b-priv-broker/src/ops/spawn_runner.rs`", "dataMigration" : "Full reset; current `MinijailProfile` not import-compatible with v3 SandboxSpec", @@ -2660,7 +2780,7 @@ { "currentSource" : "`d2bd/src/supervisor/*.rs` (DagExecutor, NodeOutcome); `d2bd/src/supervisor/pidfd_table.rs`; `d2b-realm-core/src/allocator_engine.rs` (adoption/identity concepts)", "dataMigration" : "Full reset; current DAG/role snapshot import not required", - "dependencyOwner" : "All of ADR046-minijail-001 through ADR046-minijail-004; ComponentSession/d2b-bus (ADR046-session-001, ADR046-session-003); bootstrap authz", + "dependencyOwner" : "All of ADR046-minijail-001 through ADR046-minijail-004; ComponentSession/d2b-bus (ADR046-session-001, ADR046-bus-001); bootstrap authz", "destination" : "`packages/d2b-provider-system-minijail/src/` — controller binary entry point; reconcile loop; adoption; quarantine; bootstrap authz; health/status; restart; finalize", "detailedDesign" : "Full Process/EphemeralProcess reconcile algorithm (§8); fast path ≤5/≤20 ms gates; spawn via `MinijailProcessEffectPort` (opaque IDs; no broker DTO imported); adoption algorithm (§8.5) with `/proc` reads, cgroup enumeration, and original-broker-parent verification via bounded blocking adapters; quarantine on ambiguity; quarantine reuse blocked until externally established process-absence proof or full Zone reset; no signal or cgroup.kill write to quarantined/ambiguous identity; restart/backoff driven only by broker-relayed terminal status; finalize (§8.6) with exact-main SIGTERM, bounded grace, mandatory cgroup.kill, broker wait/reap, empty-leaf proof, and no PGID ownership; EphemeralProcess continuation recovery (§9); bootstrap authz scope (§3); post-bootstrap RBAC; metric label closed-set enforcement (no `zone` label); controller writes status only on Process/EphemeralProcess resources; Provider resource status aggregated by core; the controller declares no Provider state Volume and mounts none — its bounded non-secret operational state lives in `status`/the core Operation ledger (§5.1, D087) and running units are re-adopted from cgroup leaves + fresh pidfds on restart", "integration" : "Zone runtime startup (bootstrap); all v3 ResourceClient/bus/session paths", @@ -3665,7 +3785,7 @@ { "currentSource" : "`packages/d2b-notify/src/services/`", "dataMigration" : "None — full d2b 3.0 reset; no prior state to migrate", - "dependencyOwner" : "ADR046-session-001, ADR046-session-003; session/bus wiring", + "dependencyOwner" : "ADR046-session-001, ADR046-bus-001; session/bus wiring", "destination" : "`packages/d2b-provider-notification-desktop/src/stream_admission.rs`", "detailedDesign" : "Session admission checks, Noise profile enforcement, transport class validation Primary reuse disposition: `adapt`. Preserved source-plan detail: copy/adapt.", "integration" : "ComponentSession/d2b-bus", @@ -3857,6 +3977,21 @@ "validation" : "Fast `resource_service_binding.rs` and `projection_chain.rs` plus reused nix-unit/policy tests prove provider-neutral names, base/Provider field separation, projection `spec.provider` rejection, exact D088 placement of semantic observations under `status.resource` and implementation observations under `status.provider`, ownership, schemas, stamping, quotas, redaction, and projection chain. Real SigNoz and real stream scenarios are integration-only.", "workItemId" : "ADR046-otel-006" }, + { + "currentSource" : "`packages/d2b-contract-tests/tests/policy_contracts.rs` lines 5–6 (D2B_FIXTURES gate / workspace-checks integration pattern — `implemented-and-reachable`, baseline `b5ddbed6`); `packages/d2b-contract-tests/tests/static_invariants.rs` (hermetic policy test structure — `implemented-and-reachable`); `tests/tools/rust-workspace-checks.sh` (D2B_FIXTURES step shell harness — `implemented-and-reachable`); AGENTS.md \"Naming conventions\" section (`-` workspace sort rules — `implemented-and-reachable`); `packages/d2b-realm-core/src/ids.rs` `LABEL_PATTERN` / `MAX_ID_LEN` (name regex reused for crate name token validation — `implemented-and-reachable`)", + "dataMigration" : "Additive; no existing `d2b-provider-*` crates in the pre-ADR45 baseline; first Provider crate created must comply from inception", + "dependencyOwner" : "ADR046-zone-control-003; workspace policy owner", + "destination" : "`packages/d2b-contract-tests/tests/policy_provider_crate_layout.rs` (new file; gated under D2B_FIXTURES in existing `tests/tools/rust-workspace-checks.sh`)", + "detailedDesign" : "Implement `policy_provider_crate_layout.rs` with the following test functions: (1) `every_provider_crate_has_src` — walk `packages/d2b-provider-*/` directories in the workspace, assert each contains `src/`; failure names crate and missing path; (2) `every_provider_crate_has_tests` — assert `tests/` present; (3) `every_provider_crate_has_integration` — assert `integration/` present; (4) `every_provider_crate_has_readme` — assert `README.md` present; (5) `every_provider_readme_has_required_sections` — read `README.md`, check for all nine section headings from §4.8.3 (case-insensitive, after stripping `#` and whitespace); failure names the missing heading(s); (6) `every_integration_file_has_target_declaration` — for each `integration/*.rs` file, scan first 20 lines for exactly one `//! integration-target: (container|host-integration)` declaration; failure names the file and the violation (missing/multiple/invalid value); (7) `non_provider_crates_exempt` — verify the check does not run on non-`d2b-provider-*` crates. All checks are filesystem-only (no compilation). Workspace member list is discovered by parsing `packages/Cargo.toml` `[workspace].members`. Gate: add the new test file to `tests/tools/rust-workspace-checks.sh` D2B_FIXTURES list alongside existing policy tests", + "integration" : "`make test-policy` and `make check` both fail if any provider crate violates §4.8; consistent with existing `no-bash-ast-walker` and workspace-sort gates; ADR046-zone-control-003 references §4.8 for Provider package conventions", + "removalProof" : "No existing code removed; additive policy test only", + "reuseAction" : "create", + "reuseSource" : null, + "specId" : "ADR-046-resources-zone-control", + "specPath" : "docs/specs/ADR-046-resources-zone-control.md", + "validation" : "§15.3 layout conformance tests: `provider-crate-layout-src-required`, `provider-crate-layout-tests-required`, `provider-crate-layout-integration-required`, `provider-crate-layout-readme-required`, `provider-readme-sections-all-present`, `provider-readme-sections-partial-missing`, `provider-integration-target-declared`, `provider-integration-target-unique`, `provider-integration-target-valid-values`, `provider-crate-naming-convention`, `provider-crate-layout-non-provider-exempt`", + "workItemId" : "ADR046-pkg-001" + }, { "currentSource" : "`packages/d2b-core/src/processes.rs`, `minijail_profile.rs`, `storage.rs`; `d2b-contracts/src/broker_wire.rs`", "dataMigration" : "Full reset", @@ -3992,6 +4127,21 @@ "validation" : "Shared contract tests cover exact names, strict serde/schema round trips, common base discoverability without any Provider package, canonical minimal base acceptance without `spec.provider`, same-Zone refs/targets, owner/projection discrimination, Core projection rejection of `spec.provider`, common fields only under `status.resource`, implementation observation only under `status.provider`, status-only observations, no Device/Endpoint/Binding projection, implementation-detail rejection, semantic factory-fingerprint stability under Provider/adapter identity changes, and rejection of every implementation-qualified/former `*State` alias. Each initial and fake alternate Provider must pass the identical base conformance fixture.", "workItemId" : "ADR046-provider-004" }, + { + "currentSource" : "main `a1cc0b2d`: `packages/d2b-gateway-runtime/src/provider_agent.rs` (`ProviderAgentError::{UnregisteredAdapter,RegistryNotAccepting,RegistrationRejected,InvalidAuditCapacity,SessionClosed,ProtocolViolation}`, `ProviderAgentAuditOutcome`, `ProviderAgentAuditEvent`, `ProviderAgentProcess::from_registry/from_registry_with/provider_type/service_names/audit_snapshot/serve`, `run_registered`, bounded in-memory audit ring, frame dispatch loop: semaphore in-flight limit, service/method routing, negative-timeout guard, `SessionClosed` termination, `ProtocolViolation` audit + terminate — lines 31–452; tests `standalone_entrypoint_fails_without_registration`, `audit_capacity_is_bounded` — lines 454–486); `packages/d2b-contracts/src/provider_registry_v2.rs` (`ProviderRegistryV2` wire contract, `ProviderRegistryEntryV2::validate` with provider-id derivation rule, schema fingerprint, scope-digest, generation exactness, `TrustedFirstPartyInProcess` placement requirement, `ProviderIntentId` label rules `max 128 bytes`, `MAX_PROVIDER_REGISTRY_ENTRIES`, `MAX_PROVIDER_MAPPING_IDS=64`, `ProviderBindingV2` non-exhaustive + `UnsupportedProviderBindingV2` fallback, `ProviderRegistryV2::validate` sort/unique/count checks — lines 23–566; tests `validates_closed_local_runtime_mapping`, `validates_closed_local_observability_mapping`, `serializes_declared_mapping_axes_as_closed_variants`, `rejects_duplicate_or_unbounded_mapping_ids`, `local_storage_binding_realm_must_match_descriptor_placement`, `rejects_generation_and_exact_identity_mismatches`, `contradictory_binding_realm_json_is_unrepresentable`, `unknown_binding_axis_remains_rejected_on_the_wire`, `identity_mismatch_messages_name_the_failed_contract`, `accepts_explicit_empty_registry` — lines 722–1044)", + "dataMigration" : "Not applicable; new implementation", + "dependencyOwner" : "ADR046-zone-control-011, ADR046-zone-control-017, ADR046-zone-control-018", + "destination" : "`packages/d2b-provider/src/agent.rs` (v3 provider agent dispatch); `packages/d2b-contracts/src/v3/provider_registry.rs` (v3 provider registry wire contract)", + "detailedDesign" : "**Selected**: `ProviderAgentProcess::serve` dispatch loop with semaphore in-flight limit; unsupported-service/method → ttrpc error; negative-timeout rejection; `SessionClosed` clean termination; `ProtocolViolation` audit-and-terminate path; bounded audit ring; `GeneratedProviderServiceServer` single-service-per-agent requirement; `ProviderRegistryV2` entry validation: provider-id derivation, schema fingerprint, scope digest, generation exactness; `MAX_PROVIDER_MAPPING_IDS=64` → Provider component mapping bound; `ProviderBindingV2` non-exhaustive + explicit `UnsupportedProviderBindingV2` fallback (never panics on unknown axis); `ProviderIntentId` `max 128 bytes` label rules → Provider component `spec.intentRef`; all 10 `provider_registry_v2.rs` tests ported. **Excluded ADR45 assumptions**: `aca_workload.rs` (`AcaGatewayWorkload`): ADR45 ACA external provider adapter, excluded entirely. `waypipe_display.rs` (`WaypipeDisplayProvider`): ADR45 display provider, excluded. `ProviderRegistryV2.registry_generation` / `configuration_fingerprint` bind to ADR45 bundle generation; v3 Provider resource version is tracked in redb store, not a JSON bundle. `run()` binary entrypoint uses the fixed `d2b-provider-agent` command; v3 provider processes use normal Zone runtime Process launch. `TrustedFirstPartyInProcess` is the only placement in v2; v3 Provider resources extend to `HostLocal`/`GatewayVm` etc. Primary reuse disposition: `adapt`. Preserved source-plan detail: copy + adapt.", + "integration" : "Zone runtime spawns each Provider component Process via normal Process launch; Process binary calls `ProviderAgentProcess::from_registry` then `serve()` on established ComponentSession; on `SessionClosed` the process exits and Zone runtime observes `status.phase` transition", + "removalProof" : "Not applicable; new implementation", + "reuseAction" : "adapt", + "reuseSource" : null, + "specId" : "ADR-046-resources-zone-control", + "specPath" : "docs/specs/ADR-046-resources-zone-control.md", + "validation" : "`provider-agent-dispatch-unsupported-service-returns-ttrpc-error`, `provider-agent-negative-timeout-rejected`, `provider-agent-session-closed-terminates-serve-loop`, `provider-agent-audit-ring-capacity-bounded` (ported from `audit_capacity_is_bounded`), `provider-registry-entry-fingerprint-generation-exact` (ported from `rejects_generation_and_exact_identity_mismatches`), `provider-registry-unknown-axis-fallback-non-exhaustive` (ported from `unknown_binding_axis_remains_rejected_on_the_wire`), `provider-registry-duplicate-ids-rejected` (ported from `rejects_duplicate_or_unbounded_mapping_ids`)", + "workItemId" : "ADR046-provider-agent-001" + }, { "currentSource" : "`packages/d2b-core/src/storage.rs` (`StoragePathSpec` with `scope: ContractId` currently keyed by `ProcessRole`/Workload; `SensitivityClass`; `StorageLifecycle`; `StorageRootClass`); `packages/d2b-state/src/atomic.rs` (main, 6faa5256; absent from v3 baseline)", "dataMigration" : "Full v3 reset; no v2 state schema import", @@ -4668,52 +4818,187 @@ "workItemId" : "ADR046-reset-011" }, { - "currentSource" : "`packages/d2b-realm-core/src/routing.rs`: `RouteAdvertisement`, `RouteNamespaceAllocation`, `RealmTreeEdge`, `DescendantRoute`, `TreeRoutePath`, `TreeRouteHop`, `RouteFailClosedReason`, `DirectShortcutAuthorizationRequest`, `DirectShortcutAuthorizationDecision`, `DirectShortcutTeardown`, `DirectShortcutTeardownReason`, `RouteAuditEventKind`, `RouteRealmClass`, `RoutePlacementClass`, `RouteAuditEventMetadata`, all route newtypes; `packages/d2b-realm-core/src/realm.rs`: `RealmPath`, `MAX_REALM_LABELS`, `MAX_REALM_PATH_BYTES`, `RealmControllerPlacement`, `EntrypointMode`; `packages/d2b-realm-core/src/ids.rs`: `RealmId`, `RouteId`, `ControllerGenerationId`, `WorkloadId`, `NodeId`, `ProviderId` (evidence: **A** for ids.rs — used in production; **B** for routing.rs — types exist with tests but no production daemon routing callers)", - "dataMigration" : "Full reset; no v2 Realm route compatibility", - "dependencyOwner" : "W0/W1a; zone routing contract owner", - "destination" : "`packages/d2b-contracts/src/v3/zone_routing.rs`", - "detailedDesign" : "Rename RealmPath → ZonePath, RealmId → ZoneLabelId, RouteId → ZoneRouteId, ControllerGenerationId → ZoneLinkControllerGeneration; preserve all bounds/validation/serde; add ZoneLink-specific advertisement envelope fields (v3 schema version, capability ceiling field); preserve `RouteFailClosedReason` + add `zone-link-disconnected`, `hop-limit-exceeded`, `relay-denied`, `attachment-not-permitted-over-zone-link`; freeze v3 protobuf numbers separately from v2 Primary reuse disposition: `adapt`. Preserved source-plan detail: extract and adapt.", - "integration" : "d2b-bus zone route engine and ZoneLink controller consume these types", - "removalProof" : "v3 old `RealmPath` route types retired after zone-routing engine is live and all callers switched", + "currentSource" : "Main commit source row below: `packages/d2b-session/` and `packages/d2b-contracts/src/v2_component_session.rs` at `a1cc0b2d`.", + "dataMigration" : "Full d2b 3.0 reset; no v2 session runtime state/config import", + "dependencyOwner" : "ADR046-telem-001 (d2b-telemetry must exist first for MetricsSink injection); session owner", + "destination" : "`packages/d2b-session/` copied verbatim; v3 contract extension renames endpoint purpose values while preserving backward wire compatibility during transition.", + "detailedDesign" : "Noise handshake, record protection, scheduler, named streams, cancellation, attachments, deadlines, metrics injection, ttrpc serving, and generation-discovery behavior from the selected source. Primary reuse disposition: `adapt`. Preserved source-plan detail: Copy/adopt the core ComponentSession runtime; adapt EndpointPurpose/ServicePackage/RealmSessionAuthority names listed in Excluded ADR45 assumptions and inject a `d2b-telemetry` MetricsSink..", + "integration" : "`d2b-bus` route handler calls `serve_ttrpc_services`; `d2b-session-unix` provides `OwnedTransport` impl; `d2b-telemetry` `MetricsSink` impl feeds `d2b_session_*` metrics inventory from this spec.", + "removalProof" : "ADR45 topology enum names listed in Excluded ADR45 assumptions are adapted; no prior v3 session runtime owner to remove.", "reuseAction" : "adapt", - "reuseSource" : "Same v3 baseline commit `b5ddbed6`", - "specId" : "ADR-046-zone-routing", - "specPath" : "docs/specs/ADR-046-zone-routing.md", - "validation" : "Golden advertisement/path/failure vectors shared by Rust/Nix; property tests for NCA/loop/ceiling; replay-window tests; hop-count tests", - "workItemId" : "ADR046-routing-001" + "reuseSource" : null, + "specId" : "ADR-046-telemetry-audit-and-support", + "specPath" : "docs/specs/ADR-046-telemetry-audit-and-support.md", + "validation" : "Adopt `tests/component_session.rs` and `tests/noise_vectors.rs` unchanged; extend with v3 `EndpointPurpose` enum gate test; add `d2b-contract-tests/tests/component_session_v2_vectors.rs` (existing at `a1cc0b2d`) as-is.", + "workItemId" : "ADR046-reuse-001" }, { - "currentSource" : "`packages/d2b-realm-core/src/route_engine.rs`: full `RouteTreeEngine` struct and impl; `RouteInventoryEntry`, `RoutePruneReport`, `DirectShortcutAuthorizationRequest/Decision/Teardown`; all helper functions", - "dataMigration" : "None (pure in-memory engine)", - "dependencyOwner" : "ADR046-routing-001; zone route engine owner", - "destination" : "`packages/d2b-zone-routing/src/engine.rs`", - "detailedDesign" : "Adapt RouteTreeEngine to ZoneRouteEngine using ZonePath/ZoneRouteId/ZoneLinkControllerGeneration from ADR046-routing-001; preserve all NCA/loop/multi-parent/capability/replay/capacity logic; add `decide_route` enforcement of `maxHops` parameter; add hop-counter decrement in relay path; expose `ZoneRoutePath` in v3 types Primary reuse disposition: `adapt`. Preserved source-plan detail: extract and adapt.", - "integration" : "d2b-bus calls `ZoneRouteEngine::decide_route` for every cross-Zone ResourceClient call; ZoneLink controller calls `admit_advertisement`/`admit_withdrawal`", - "removalProof" : "`RouteTreeEngine` on v3 RealmPath types retired after ZoneRouteEngine is exercised in all bus routing paths", + "currentSource" : "Main commit source row below: `packages/d2b-session-unix/` at `a1cc0b2d`.", + "dataMigration" : "Full d2b 3.0 reset; no v2 transport runtime state/config import", + "dependencyOwner" : "ADR046-reuse-001; Unix transport owner", + "destination" : "`packages/d2b-session-unix/` copied verbatim.", + "detailedDesign" : "SO_PASSCRED verification, pidfd identity verification, multi-scope credit reservation, emergency headroom, seqpacket atomic CLOEXEC transfer, stream reassembly, vsock transport, and descriptor policy enforcement. Primary reuse disposition: `adapt`. Preserved source-plan detail: Copy verbatim; retain `host-socket`/`native-vsock` feature flags; no ADR45 topology adaptation required..", + "integration" : "`d2b-bus` Zone-local listeners use `UnixSeqpacketTransport`; Provider agent connections use vsock transport from this crate; `CreditPool`/`CreditScopeSet` enforce per-Zone attachment FD budgets.", + "removalProof" : "None — net-new; no prior owner to remove", "reuseAction" : "adapt", - "reuseSource" : "Same v3 baseline commit `b5ddbed6`", - "specId" : "ADR-046-zone-routing", - "specPath" : "docs/specs/ADR-046-zone-routing.md", - "validation" : "Copy exact `route_engine.rs` test suite adapted to ZonePath; add relay/hop-count/RBAC-narrowing/shortcut integration tests", - "workItemId" : "ADR046-routing-002" + "reuseSource" : null, + "specId" : "ADR-046-telemetry-audit-and-support", + "specPath" : "docs/specs/ADR-046-telemetry-audit-and-support.md", + "validation" : "Adopt all `unix_session.rs` tests unchanged.", + "workItemId" : "ADR046-reuse-002" }, { - "currentSource" : "`packages/d2b-realm-core/src/routing.rs`: `RouteNamespaceAllocation`; `packages/d2b-realm-core/src/access.rs`: `RealmAccessResolverRequest/Response/Error`, `RealmAccessBinding`, `RealmTransportBinding`, `RealmAccessClientContract`, `UnixSocketPath`, `AccessBindingRef`, all access types (evidence: **B** — complete implementation, no production callers); `packages/d2bd/src/realm_access_resolver.rs`: `resolve_local_root_realm_access()`, `local_root_realm_access_client_contract()` (evidence: **B** — `pub mod` at `d2bd/src/lib.rs:117`, no callers from running daemon); `packages/d2b-realm-router/src/target_resolver.rs`: `RealmEntrypointTable`, `DispatchTarget`, `RealmEntrypoint`, `ResolveError` (evidence: **A**); `packages/d2b/src/lib.rs:5240`: `load_realm_entrypoint_table()` (evidence: **A**); `packages/d2b/src/target_routing.rs`: `Route::Local`/`Route::GatewayBacked` dispatch (evidence: **A**); `packages/d2b-realm-core/src/realm.rs`: `EntrypointMode`, `RealmControllerPlacement` (evidence: **A** as types; routing use **B**); `packages/d2b-core/src/realm_controller_config.rs`: `RealmControllersJson` (evidence: **C**); `nixos-modules/realm-controller-config-json.nix` (evidence: **C**)", - "dataMigration" : "None; ZoneLink resources created from Nix configuration at v3 reset", - "dependencyOwner" : "ADR046-routing-001, ADR046-routing-002; ZoneLink resource owner", - "destination" : "`packages/d2b-contracts/src/v3/zone_link.rs` (ZoneLink spec/status types, intent types, namespace allocation); `packages/d2b-zone-routing/src/resolver.rs` (ZoneEntrypointResolver)", - "detailedDesign" : "Child-local ZoneLink spec/status fields; self-name and one-uplink invariants; ZoneLinkIntent record stored in the child; ZoneLinkNamespaceAllocation issued by the exact parent allocator selected in sealed Nix-compiled `parentZone` topology; ZoneEntrypointResolver with longest-suffix match over ZonePath (adapted from `RealmEntrypointTable::resolve`); no reciprocal parent-store resource; fail-closed on unknown Zone Primary reuse disposition: `adapt`. Preserved source-plan detail: extract and adapt.", - "integration" : "Core-controller ZoneLink handler manages ZoneLink resources; ZoneEntrypointResolver in d2b-bus for per-call dispatch decision", - "removalProof" : "`RealmEntrypointTable` retired after all host-daemon routing paths use ZoneEntrypointResolver", + "currentSource" : "Main commit source row below: `packages/d2b-client/` at `a1cc0b2d`.", + "dataMigration" : "Full d2b 3.0 reset; no v2 client state/config import", + "dependencyOwner" : "ADR046-reuse-001; client owner", + "destination" : "`packages/d2b-client/` copied; DaemonClient/GuestClient adapted to v3 service packages; `MetadataInput::with_trace` drives `TraceContext` propagation.", + "detailedDesign" : "MetadataInput trace/correlation/idempotency, retry policy, cancellation token, generic client over resolver/connector/clock, typed service proxies, stream terminal, and host-socket verifier. Primary reuse disposition: `adapt`. Preserved source-plan detail: Copy client layer; adapt service package/method sets, local-root Zone identity, and TraceContext propagation for v3..", + "integration" : "Every controller/service that makes outbound calls uses `Client`; `MetadataInput::with_trace` feeds `d2b_api_request_duration_seconds` trace-id into `d2b.bus.route` span.", + "removalProof" : "ADR45 Daemon/Guest wrappers and RealmPath identity assumptions are adapted to v3 service packages/Zone name; no prior v3 client owner to remove.", "reuseAction" : "adapt", - "reuseSource" : "Same v3 baseline commit `b5ddbed6`", - "specId" : "ADR-046-zone-routing", - "specPath" : "docs/specs/ADR-046-zone-routing.md", - "validation" : "Longest-suffix match vectors; ZoneLink spec validation tests; resolver fail-closed test", - "workItemId" : "ADR046-routing-003" - }, - { - "currentSource" : "`packages/d2b-realm-router/src/remote_node.rs`: `RemoteNodeRegistry`, `RemoteNodeEntry`, `RemoteNodeAvailability`, `RemoteNodeErrorKind`, `RemoteRetryAction`, `ensure_remote_execution_generation` (evidence: **B** — only in `d2bd/src/realm_stubs.rs` dead_code seam); `packages/d2b-realm-router/src/session_lifecycle.rs`: `SessionLifecycle`, `SessionPhase` (evidence: **B** — same seam); `packages/d2bd/src/realm_stubs.rs`: compile-only seam (`#[allow(dead_code)]`, declared at `d2bd/src/lib.rs:249`); `packages/d2b-realm-core/src/enrollment.rs`: `EnrollmentRecord`, `EnrollmentStatus`, `KeyRotationPlan`, `RevocationRecord`, `SessionTeardownDirective`, `RecoveryProcedure`, `IdentityAuditEventKind` (evidence: **B** — consumed by `RealmIdentityStore` which itself has no production callers); `packages/d2b-realm-core/src/identity_store.rs`: `RealmIdentityStore` (evidence: **B** — no production callers); `packages/d2b-realm-core/src/identity_config.rs`: `RealmIdentityConfigJson` (evidence: **A** — loaded at d2bd/priv-broker startup, routing inert); `nixos-modules/realm-identity-config-json.nix` (evidence: **C**); `packages/d2bd/src/workload_target_index.rs`: `WorkloadTargetIndex` (evidence: **A** — called at `d2bd/src/lib.rs:16745`; this is the live bridge from realm metadata to VM-name dispatch; retires with Guest/Host resource lookups)", + "reuseSource" : null, + "specId" : "ADR-046-telemetry-audit-and-support", + "specPath" : "docs/specs/ADR-046-telemetry-audit-and-support.md", + "validation" : "Adopt typed-route, proxy-reuse, and cancel tests unchanged. Add v3 service-package name gate test.", + "workItemId" : "ADR046-reuse-003" + }, + { + "currentSource" : "Main commit source row below: `packages/d2b-provider/` and `packages/d2b-provider-toolkit/` at `a1cc0b2d`.", + "dataMigration" : "Full d2b 3.0 reset; no v2 ProviderRegistry runtime state import", + "dependencyOwner" : "ADR046-reuse-001 + ADR046-reuse-003; Provider owner", + "destination" : "`packages/d2b-provider/` and `packages/d2b-provider-toolkit/` copied with v3 session admission and bus routing adaptations.", + "detailedDesign" : "ProviderRegistry factory registration, bounded admission, RPC proxy fail-closed behavior, generated provider service admission/dispatch, redaction wrappers, and conformance gates. Primary reuse disposition: `adapt`. Preserved source-plan detail: Copy provider runtime/toolkit; adapt SessionIdentity Zone name and route through `d2b-bus` instead of daemon embedding..", + "integration" : "Each v3 Provider process embeds `ProviderRegistry` + `GeneratedProviderServiceServer`; `check_provider_conformance` runs in Provider install-time conformance check (feeds `d2b_provider_reconcile_total{outcome=\"error\"}` on failure).", + "removalProof" : "Daemon-embedded ADR45 ProviderRegistry wiring is excluded; v3 provider-process registry replaces it without importing state.", + "reuseAction" : "adapt", + "reuseSource" : null, + "specId" : "ADR-046-telemetry-audit-and-support", + "specPath" : "docs/specs/ADR-046-telemetry-audit-and-support.md", + "validation" : "Adopt all `conformance.rs` and `runtime.rs` tests unchanged. Add v3 `SessionIdentity` zone-name gate. Add conformance-failure → `d2b_provider_reconcile_total` metric integration test.", + "workItemId" : "ADR046-reuse-004" + }, + { + "currentSource" : "Main commit source row below: `packages/d2b-gateway-runtime/src/provider_agent.rs` at `a1cc0b2d` plus `tests/provider_agent_v2.rs`.", + "dataMigration" : "Full d2b 3.0 reset; no v2 provider-agent runtime state import", + "dependencyOwner" : "ADR046-reuse-004 + ADR046-audit-003; Provider agent / observability-otel owner", + "destination" : "`packages/d2b-provider-observability-otel/src/agent.rs` adapted; ComponentSessionDriver mock becomes shared Provider session fixture.", + "detailedDesign" : "Session-bound ProviderAgentProcess construction, bounded audit snapshot, closed-set ProviderAgentError paths, and full ComponentSessionDriver mock for hermetic Provider tests. Primary reuse disposition: `adapt`. Preserved source-plan detail: Adapt provider-agent session wrapper and audit ring; replace gateway-runtime audit envelope with v3 `d2b-audit` records..", + "integration" : "`observability-otel` Provider embeds a `ProviderAgentProcess`; session connect/disconnect emits `SessionConnect` audit records via `d2b-audit`; `ProviderAgentAuditEvent` ring feeds `d2b_provider_reconcile_total` metric on session error.", + "removalProof" : "ADR45 gateway-runtime AuditEnvelope/realm custody and launch path are excluded; v3 provider agent is supervised by Provider/system-core or the owning Provider supervisor.", + "reuseAction" : "adapt", + "reuseSource" : null, + "specId" : "ADR-046-telemetry-audit-and-support", + "specPath" : "docs/specs/ADR-046-telemetry-audit-and-support.md", + "validation" : "Adopt `provider_agent_v2.rs` mock harness unchanged as shared v3 Provider session fixture. Add v3 audit-bridge test: provider-agent session → `SessionConnect{transport_class=\"zone_link\"}` record emitted.", + "workItemId" : "ADR046-reuse-005" + }, + { + "currentSource" : "Main commit source row below: `packages/d2b-realm-router/` at `a1cc0b2d`.", + "dataMigration" : "Full d2b 3.0 reset; no v2 routing state import", + "dependencyOwner" : "ADR046-reuse-001 + ADR046-bus-001; bus routing owner", + "destination" : "`packages/d2b-bus/src/routing.rs` adapted from `service_v2.rs`; RemoteNodeErrorKind stable code pattern becomes v3 BusErrorKind.", + "detailedDesign" : "Realm/Zone service ttrpc handler table, session authority constructors, host/gateway credential custody split, stable low-cardinality error codes, and binding/shortcut bounds. Primary reuse disposition: `adapt`. Preserved source-plan detail: Adapt realm service routing to Zone service routing; preserve credential-custody, bounded routing, and stable error-code behavior..", + "integration" : "`d2b-bus` route handler adapts `RealmServiceServer` dispatch table; `RemoteNodeErrorKind::code()` values feed `d2b_bus_route_total{outcome}` metric labels; `CredentialCustody::Host` maps to `purpose_class=local` in `d2b_session_connect_total`.", + "removalProof" : "ADR45 RealmId and RealmService names are adapted to Zone/resource UID routing; no prior v3 bus routing owner to remove.", + "reuseAction" : "adapt", + "reuseSource" : null, + "specId" : "ADR-046-telemetry-audit-and-support", + "specPath" : "docs/specs/ADR-046-telemetry-audit-and-support.md", + "validation" : "Adopt `authority_keeps_remote_credentials_in_gateway_guests` test renamed to `authority_keeps_remote_credentials_in_zone_link_sessions`; adapt `RealmId` → Zone name; adopt `authenticated_bootstrap_enrollment_route_and_shortcut_lifecycle` renamed with zone terminology.", + "workItemId" : "ADR046-reuse-006" + }, + { + "currentSource" : "Main commit source row below: `packages/d2bd/` routing/provider effects files and tests at `a1cc0b2d`.", + "dataMigration" : "Full d2b 3.0 reset; no v2 daemon routing/provider-effects state import", + "dependencyOwner" : "ADR046-reuse-004 + ADR046-reuse-006; core-controller routing owner", + "destination" : "`packages/d2b-bus/src/service_router.rs` and `packages/d2b-core-controller/src/provider_effects.rs`.", + "detailedDesign" : "Closed-set service.package route gates, provider lifecycle effect-adapter composition, closed-set error mapping, fixed local policy invariants, and bootstrap/enrolled credential binding shape. Primary reuse disposition: `adapt`. Preserved source-plan detail: Adapt route-gate and ProviderLifecycleDispatch patterns; exclude daemon-embedded effect composition and route through v3 bus/provider processes..", + "integration" : "Bus service router uses `service.package` closed-set matching from route-gate pattern; `ProviderLifecycleDispatch` feeds `d2b_provider_component_phase` metric.", + "removalProof" : "ADR45 daemon-embedded effect adapters and realm child supervisor assumptions are excluded; v3 bus/provider effects own the adapted behavior.", + "reuseAction" : "adapt", + "reuseSource" : null, + "specId" : "ADR-046-telemetry-audit-and-support", + "specPath" : "docs/specs/ADR-046-telemetry-audit-and-support.md", + "validation" : "Port `local_daemon_policy_is_fixed_and_has_no_negotiation_or_fd_surface` invariant to v3 bus local policy test; port `every_generated_daemon_method_has_one_typed_adapter` to v3 bus method adapter completeness test.", + "workItemId" : "ADR046-reuse-007" + }, + { + "currentSource" : "Main commit source row below: ComponentSession vector/conformance tests at `a1cc0b2d`.", + "dataMigration" : "None — docs/tooling only; no runtime state", + "dependencyOwner" : "ADR046-reuse-001; contract-tests owner", + "destination" : "`packages/d2b-contract-tests/tests/component_session_v2_vectors.rs` and `tests/noise_vectors.rs` copied verbatim.", + "detailedDesign" : "Pinned Noise KAT vectors, transcript/PSK mutation rejection, typed bootstrap admission fixture, and public-key corruption rejection remain the session wire security ground truth. Primary reuse disposition: `adapt`. Preserved source-plan detail: Copy the vector and conformance tests verbatim; add v3 constant gate for ComponentSession major/minor version..", + "integration" : "These tests run in `make test-rust` / `cargo test -p d2b-contract-tests` and `cargo test -p d2b-session`. They are gating for any Noise library update.", + "removalProof" : "None — net-new; no prior owner to remove", + "reuseAction" : "adapt", + "reuseSource" : null, + "specId" : "ADR-046-telemetry-audit-and-support", + "specPath" : "docs/specs/ADR-046-telemetry-audit-and-support.md", + "validation" : "These tests are self-validating. Add one gate: assert `COMPONENT_SESSION_MAJOR = 2` and `COMPONENT_SESSION_MINOR = 0` constants are unchanged in v3 contract.", + "workItemId" : "ADR046-reuse-008" + }, + { + "currentSource" : "Main commit source row below: `packages/d2b-session/src/metrics.rs` and `MetricLabels` in `packages/d2b-contracts/src/v2_component_session.rs` at `a1cc0b2d`.", + "dataMigration" : "Full d2b 3.0 reset; no v2 telemetry metric state import", + "dependencyOwner" : "ADR046-reuse-001 + ADR046-telem-001 + ADR046-telem-003; telemetry/session owner", + "destination" : "`packages/d2b-telemetry/src/session_metrics_sink.rs`.", + "detailedDesign" : "Map every MetricEvent variant to the `d2b_session_*` inventory; convert MetricLabels closed sets to allowed v3 label strings; keep NoopMetrics for hermetic tests. Primary reuse disposition: `adapt`. Preserved source-plan detail: Adapt MetricsSink bridge to v3 OTEL instruments and rename ADR45 endpoint-purpose label values to v3 closed-set labels..", + "integration" : "`serve_ttrpc_services` receives a `Box` from `d2b-telemetry`; all session endpoints call through this bridge.", + "removalProof" : "ADR45 EndpointPurpose metric label strings are replaced by v3 purpose names; no prior v3 MetricsSink bridge owner to remove.", + "reuseAction" : "adapt", + "reuseSource" : null, + "specId" : "ADR-046-telemetry-audit-and-support", + "specPath" : "docs/specs/ADR-046-telemetry-audit-and-support.md", + "validation" : "New test `packages/d2b-telemetry/tests/session_sink_bridge.rs`: drive `MetricEvent` variants through the sink; assert OTEL counter/gauge values; assert `MetricLabels` closed-set values map only to allowed label strings (no `DaemonLocal` string in v3 metric output).", + "workItemId" : "ADR046-reuse-009" + }, + { + "currentSource" : "`packages/d2b-realm-core/src/routing.rs`: `RouteAdvertisement`, `RouteNamespaceAllocation`, `RealmTreeEdge`, `DescendantRoute`, `TreeRoutePath`, `TreeRouteHop`, `RouteFailClosedReason`, `DirectShortcutAuthorizationRequest`, `DirectShortcutAuthorizationDecision`, `DirectShortcutTeardown`, `DirectShortcutTeardownReason`, `RouteAuditEventKind`, `RouteRealmClass`, `RoutePlacementClass`, `RouteAuditEventMetadata`, all route newtypes; `packages/d2b-realm-core/src/realm.rs`: `RealmPath`, `MAX_REALM_LABELS`, `MAX_REALM_PATH_BYTES`, `RealmControllerPlacement`, `EntrypointMode`; `packages/d2b-realm-core/src/ids.rs`: `RealmId`, `RouteId`, `ControllerGenerationId`, `WorkloadId`, `NodeId`, `ProviderId` (evidence: **A** for ids.rs — used in production; **B** for routing.rs — types exist with tests but no production daemon routing callers)", + "dataMigration" : "Full reset; no v2 Realm route compatibility", + "dependencyOwner" : "W0/W1a; zone routing contract owner", + "destination" : "`packages/d2b-contracts/src/v3/zone_routing.rs`", + "detailedDesign" : "Rename RealmPath → ZonePath, RealmId → ZoneLabelId, RouteId → ZoneRouteId, ControllerGenerationId → ZoneLinkControllerGeneration; preserve all bounds/validation/serde; add ZoneLink-specific advertisement envelope fields (v3 schema version, capability ceiling field); preserve `RouteFailClosedReason` + add `zone-link-disconnected`, `hop-limit-exceeded`, `relay-denied`, `attachment-not-permitted-over-zone-link`; freeze v3 protobuf numbers separately from v2 Primary reuse disposition: `adapt`. Preserved source-plan detail: extract and adapt.", + "integration" : "d2b-bus zone route engine and ZoneLink controller consume these types", + "removalProof" : "v3 old `RealmPath` route types retired after zone-routing engine is live and all callers switched", + "reuseAction" : "adapt", + "reuseSource" : "Same v3 baseline commit `b5ddbed6`", + "specId" : "ADR-046-zone-routing", + "specPath" : "docs/specs/ADR-046-zone-routing.md", + "validation" : "Golden advertisement/path/failure vectors shared by Rust/Nix; property tests for NCA/loop/ceiling; replay-window tests; hop-count tests", + "workItemId" : "ADR046-routing-001" + }, + { + "currentSource" : "`packages/d2b-realm-core/src/route_engine.rs`: full `RouteTreeEngine` struct and impl; `RouteInventoryEntry`, `RoutePruneReport`, `DirectShortcutAuthorizationRequest/Decision/Teardown`; all helper functions", + "dataMigration" : "None (pure in-memory engine)", + "dependencyOwner" : "ADR046-routing-001; zone route engine owner", + "destination" : "`packages/d2b-zone-routing/src/engine.rs`", + "detailedDesign" : "Adapt RouteTreeEngine to ZoneRouteEngine using ZonePath/ZoneRouteId/ZoneLinkControllerGeneration from ADR046-routing-001; preserve all NCA/loop/multi-parent/capability/replay/capacity logic; add `decide_route` enforcement of `maxHops` parameter; add hop-counter decrement in relay path; expose `ZoneRoutePath` in v3 types Primary reuse disposition: `adapt`. Preserved source-plan detail: extract and adapt.", + "integration" : "d2b-bus calls `ZoneRouteEngine::decide_route` for every cross-Zone ResourceClient call; ZoneLink controller calls `admit_advertisement`/`admit_withdrawal`", + "removalProof" : "`RouteTreeEngine` on v3 RealmPath types retired after ZoneRouteEngine is exercised in all bus routing paths", + "reuseAction" : "adapt", + "reuseSource" : "Same v3 baseline commit `b5ddbed6`", + "specId" : "ADR-046-zone-routing", + "specPath" : "docs/specs/ADR-046-zone-routing.md", + "validation" : "Copy exact `route_engine.rs` test suite adapted to ZonePath; add relay/hop-count/RBAC-narrowing/shortcut integration tests", + "workItemId" : "ADR046-routing-002" + }, + { + "currentSource" : "`packages/d2b-realm-core/src/routing.rs`: `RouteNamespaceAllocation`; `packages/d2b-realm-core/src/access.rs`: `RealmAccessResolverRequest/Response/Error`, `RealmAccessBinding`, `RealmTransportBinding`, `RealmAccessClientContract`, `UnixSocketPath`, `AccessBindingRef`, all access types (evidence: **B** — complete implementation, no production callers); `packages/d2bd/src/realm_access_resolver.rs`: `resolve_local_root_realm_access()`, `local_root_realm_access_client_contract()` (evidence: **B** — `pub mod` at `d2bd/src/lib.rs:117`, no callers from running daemon); `packages/d2b-realm-router/src/target_resolver.rs`: `RealmEntrypointTable`, `DispatchTarget`, `RealmEntrypoint`, `ResolveError` (evidence: **A**); `packages/d2b/src/lib.rs:5240`: `load_realm_entrypoint_table()` (evidence: **A**); `packages/d2b/src/target_routing.rs`: `Route::Local`/`Route::GatewayBacked` dispatch (evidence: **A**); `packages/d2b-realm-core/src/realm.rs`: `EntrypointMode`, `RealmControllerPlacement` (evidence: **A** as types; routing use **B**); `packages/d2b-core/src/realm_controller_config.rs`: `RealmControllersJson` (evidence: **C**); `nixos-modules/realm-controller-config-json.nix` (evidence: **C**)", + "dataMigration" : "None; ZoneLink resources created from Nix configuration at v3 reset", + "dependencyOwner" : "ADR046-routing-001, ADR046-routing-002; ZoneLink resource owner", + "destination" : "`packages/d2b-contracts/src/v3/zone_link.rs` (ZoneLink spec/status types, intent types, namespace allocation); `packages/d2b-zone-routing/src/resolver.rs` (ZoneEntrypointResolver)", + "detailedDesign" : "Child-local ZoneLink spec/status fields; self-name and one-uplink invariants; ZoneLinkIntent record stored in the child; ZoneLinkNamespaceAllocation issued by the exact parent allocator selected in sealed Nix-compiled `parentZone` topology; ZoneEntrypointResolver with longest-suffix match over ZonePath (adapted from `RealmEntrypointTable::resolve`); no reciprocal parent-store resource; fail-closed on unknown Zone Primary reuse disposition: `adapt`. Preserved source-plan detail: extract and adapt.", + "integration" : "Core-controller ZoneLink handler manages ZoneLink resources; ZoneEntrypointResolver in d2b-bus for per-call dispatch decision", + "removalProof" : "`RealmEntrypointTable` retired after all host-daemon routing paths use ZoneEntrypointResolver", + "reuseAction" : "adapt", + "reuseSource" : "Same v3 baseline commit `b5ddbed6`", + "specId" : "ADR-046-zone-routing", + "specPath" : "docs/specs/ADR-046-zone-routing.md", + "validation" : "Longest-suffix match vectors; ZoneLink spec validation tests; resolver fail-closed test", + "workItemId" : "ADR046-routing-003" + }, + { + "currentSource" : "`packages/d2b-realm-router/src/remote_node.rs`: `RemoteNodeRegistry`, `RemoteNodeEntry`, `RemoteNodeAvailability`, `RemoteNodeErrorKind`, `RemoteRetryAction`, `ensure_remote_execution_generation` (evidence: **B** — only in `d2bd/src/realm_stubs.rs` dead_code seam); `packages/d2b-realm-router/src/session_lifecycle.rs`: `SessionLifecycle`, `SessionPhase` (evidence: **B** — same seam); `packages/d2bd/src/realm_stubs.rs`: compile-only seam (`#[allow(dead_code)]`, declared at `d2bd/src/lib.rs:249`); `packages/d2b-realm-core/src/enrollment.rs`: `EnrollmentRecord`, `EnrollmentStatus`, `KeyRotationPlan`, `RevocationRecord`, `SessionTeardownDirective`, `RecoveryProcedure`, `IdentityAuditEventKind` (evidence: **B** — consumed by `RealmIdentityStore` which itself has no production callers); `packages/d2b-realm-core/src/identity_store.rs`: `RealmIdentityStore` (evidence: **B** — no production callers); `packages/d2b-realm-core/src/identity_config.rs`: `RealmIdentityConfigJson` (evidence: **A** — loaded at d2bd/priv-broker startup, routing inert); `nixos-modules/realm-identity-config-json.nix` (evidence: **C**); `packages/d2bd/src/workload_target_index.rs`: `WorkloadTargetIndex` (evidence: **A** — called at `d2bd/src/lib.rs:16745`; this is the live bridge from realm metadata to VM-name dispatch; retires with Guest/Host resource lookups)", "dataMigration" : "New ZoneLink resources from Nix configuration; no prior enrollment compatibility", "dependencyOwner" : "ADR046-routing-003; core-controller ZoneLink handler owner", "destination" : "`packages/d2b-core-controller/src/zone_links.rs`", @@ -4925,7 +5210,7 @@ { "currentSource" : "main `a1cc0b2d`: `d2b-session/tests/noise_vectors.rs`, `d2b-session/tests/component_session.rs`, `d2b-session-unix/tests/unix_session.rs`", "dataMigration" : "None — full d2b 3.0 reset; no prior state to migrate", - "dependencyOwner" : "`ADR046-session-001`/`ADR046-session-003` (ComponentSession/d2b-bus implementation)", + "dependencyOwner" : "`ADR046-session-001`/`ADR046-bus-001` (ComponentSession/d2b-bus implementation)", "destination" : "`packages/d2b-session/tests/noise_conformance.rs`, `packages/d2b-session/fuzz/fuzz_targets/{handshake_offer,record_frame}.rs`", "detailedDesign" : "Property/fuzz test suite over the three Noise profiles (§7): exact NN/KK/IKpsk2 vectors and rejection mutations (copied), plus new `cargo-fuzz` targets mutating the canonical handshake offer, preface, and encrypted record frame to assert no panic/UB and that every malformed input is a typed rejection (never a partial accept) Primary reuse disposition: `adapt`. Preserved source-plan detail: copy and adapt.", "integration" : "Wired into `make test-rust` (vectors) and a separate `make test-fuzz` target (new; time-boxed nightly run, not part of the PR-blocking gate)", @@ -4955,7 +5240,7 @@ { "currentSource" : "None (v3 ZoneLink relay is `ADR-only`)", "dataMigration" : "None — full d2b 3.0 reset; no prior state to migrate", - "dependencyOwner" : "`ADR046-routing-005`/`ADR046-session-003` (ZoneLink/d2b-bus relay implementation)", + "dependencyOwner" : "`ADR046-routing-005`/`ADR046-bus-001` (ZoneLink/d2b-bus relay implementation)", "destination" : "`packages/d2b-bus/fuzz/fuzz_targets/zonelink_frame.rs`, `packages/d2b-bus/tests/zonelink_structural_rejection.rs`", "detailedDesign" : "Fuzz + property suite asserting that no mutation of a ZoneLink-bound frame (attachment count, credential-shaped byte runs, path-shaped strings, PID-shaped integers) is ever forwarded — every such mutation is rejected at serialization with `attachment-not-permitted-over-zone-link` or the transport-specific equivalent, never silently dropped or partially forwarded Primary reuse disposition: `adapt`. Preserved source-plan detail: extract and adapt (design copied from `ADR-046-zone-routing.md` structural-rejection sections).", "integration" : "`make test-fuzz`; a companion container test (`tests/integration/containers/zonelink-cross-zone.rs`) runs two real Zone runtime containers connected by a real ZoneLink and asserts the same property end to end over the wire, not just in the frame-serialization unit", @@ -5075,7 +5360,7 @@ { "currentSource" : "`packages/d2bd/src/daemon_audit.rs` (existing audit-write path, adapted target)", "dataMigration" : "None — full d2b 3.0 reset; no prior state to migrate", - "dependencyOwner" : "`ADR046-telem-013` (privileged audit durability)", + "dependencyOwner" : "`ADR046-audit-002` (privileged audit durability)", "destination" : "`packages/d2b-audit/tests/privileged_fail_closed.rs`", "detailedDesign" : "Fault-injection test that makes the audit sink's fsync fail (simulated ENOSPC/EIO) during a privileged `ResourceMutation`/`RBACChange`/`StateReset` write, and asserts the originating operation itself fails with `audit-unavailable` rather than completing with a lost audit record; a companion test floods `Standard`/`Best-effort` records past `DEFAULT_AUDIT_WRITES_PER_SECOND` and asserts privileged records are never dropped or delayed by the resulting backpressure", "integration" : "`make test-rust`", @@ -5105,7 +5390,7 @@ { "currentSource" : "None (both commands are `ADR-only`)", "dataMigration" : "None — full d2b 3.0 reset; no prior state to migrate", - "dependencyOwner" : "`ADR046-telem-016`/`ADR046-telem-017` (doctor/support-bundle commands)", + "dependencyOwner" : "`ADR046-doctor-001`/`ADR046-doctor-002` (doctor/support-bundle commands)", "destination" : "`packages/d2b/src/commands/{doctor,support_bundle}.rs`", "detailedDesign" : "`d2b zone doctor` performs read-only status/audit-hash-chain checks with the redaction rules from §21 enforced on every field it prints; `d2b zone support-bundle` assembles a bounded archive of metadata+status (never spec bytes or `metadata.name`) and sets `bundle_completeness: \"partial\"` when any Provider in scope is quarantined, rather than omitting the gap silently Primary reuse disposition: `adapt`. Preserved source-plan detail: extract and adapt (design from `ADR-046-telemetry-audit-and-support.md`).", "integration" : "`make test-rust` (CLI integration tests); a container test (`tests/integration/containers/support-bundle-quarantined.rs`) runs a real Zone with one quarantined Provider and asserts the bundle correctly reports `partial`", @@ -5120,7 +5405,7 @@ { "currentSource" : "Historical main ADR 0045 factory-reset design (`a1cc0b2d^:docs/adr/0045-provider-and-transport-framework.md`, reset process overview and apply-command verification steps) — reused only as a design precedent for atomicity/fail-closed sequencing, not as v3 architecture (see [Reset boundary](#reset-boundary) for the explicitly excluded assumptions)", "dataMigration" : "None (v3-native; no v1/v2 reset-generation state to migrate)", - "dependencyOwner" : "`ADR046-telem-012` (`StateReset` record), Volume/Credential lifecycle work items", + "dependencyOwner" : "`ADR046-audit-001` (`StateReset` record), Volume/Credential lifecycle work items", "destination" : "`packages/d2b-core-controller/src/reset.rs`, `packages/d2b-core-controller/tests/reset_atomicity.rs`", "detailedDesign" : "Implements the `scope` (`zone`, `provider`, `host`, or `guest`) `StateReset` flow from §25: quiesce via `EmergencyPolicy`, revoke open Credential leases in scope, destroy Volumes in scope (key-shred first), commit the `StateReset` audit record durably, and only then report the reset complete. A crash-recovery path re-derives \"was this reset already committed?\" solely from the durable `StateReset` record, never from partial filesystem state Primary reuse disposition: `adapt`. Preserved source-plan detail: adapt (pattern only).", "integration" : "`make test-rust` (unit-level state machine); a host/KVM integration test (`tests/host-integration/reset-atomicity.nix`) kills the process mid-reset at each of the four phases (quiesce, credential revoke, Volume destroy, audit commit) and asserts recovery never double-destroys, never silently completes without the audit record, and never leaves an orphaned sealed-Volume-without-key state", @@ -5747,21 +6032,6 @@ "validation" : "Copied fd/peer/credit tests plus Host/Guest subject mapping", "workItemId" : "ADR046-session-002" }, - { - "currentSource" : "v3 `d2b-realm-router`, target resolver, CLI routing, operation router", - "dataMigration" : "None — full d2b 3.0 reset; no prior state to migrate", - "dependencyOwner" : "Sessions + resource API; bus owner", - "destination" : "`packages/d2b-bus/src/{router,registry,authorization,streams,operations}.rs`", - "detailedDesign" : "Exact service/resource routes, RBAC, pinned reverse route, cancellation, named stream bridge, no wildcard pub/sub Primary reuse disposition: `adapt`. Preserved source-plan detail: extract/adapt.", - "integration" : "Every ResourceClient/controller/Provider/CLI service", - "removalProof" : "Old direct dispatch branches removed only after route parity", - "reuseAction" : "adapt", - "reuseSource" : "Any useful main d2b-client/provider/session routing symbols named by implementation sub-items", - "specId" : "ADR-046-componentsession-and-bus", - "specPath" : "docs/specs/ADR-046-componentsession-and-bus.md", - "validation" : "Message isolation, route/auth revocation, fairness, reconnect, no direct-store path", - "workItemId" : "ADR046-session-003" - }, { "currentSource" : "None — net-new v3 qualified `ShellPool` and `ShellSession` resource schemas; superseded draft and legacy shell code do not define these canonical resources.", "dataMigration" : "Full d2b 3.0 reset; no v2 state/config import.", @@ -6365,7 +6635,7 @@ { "currentSource" : "No canonical v3 equivalent. Adapt the Provider descriptor pattern from `packages/d2b-realm-provider/src/provider.rs` and the bounded audit-envelope pattern from `packages/d2bd/src/daemon_audit.rs`; do not carry forward daemon topology or unsafe-local helper protocol types.", "dataMigration" : "Full d2b 3.0 reset; no Provider config, handler checkpoint, or audit state is imported.", - "dependencyOwner" : "`ADR046-provider-001`, `ADR046-exec-003`, `ADR046-exec-004`, `ADR046-exec-005`, `ADR046-pstate-012`, `ADR046-telem-001`, and `ADR046-telem-012`; `Provider/system-core` owner", + "dependencyOwner" : "`ADR046-provider-001`, `ADR046-exec-003`, `ADR046-exec-004`, `ADR046-exec-005`, `ADR046-pstate-012`, `ADR046-telem-001`, and `ADR046-audit-001`; `Provider/system-core` owner", "destination" : "`packages/d2b-provider-system-core/src/manifest.rs`, `packages/d2b-provider-system-core/src/audit.rs`, and `packages/d2b-provider-system-core/tests/provider_boundary.rs`", "detailedDesign" : "Compile the system-core Provider manifest, empty closed config schema, Host/User component descriptors, and empty state-namespace declaration. The manifest binds both library handlers to the fixed `d2b-core-controller` bootstrap process without declaring either handler as a Process resource. The audit adapter emits one bounded, redacted `ResourceReconciled` record after each Host/User reconcile. The boundary rejects Provider config fields and proves that handler call paths neither write `Provider.status` nor emit `ProcessEffect`; core-controller infrastructure owns the former and `ADR046-exec-006`/`ADR046-exec-007` own the latter. `ADR046-pstate-012` remains the owner of generic optional-state admission; this item only declares system-core's empty state set. Primary reuse disposition: `adapt`. Preserved source-plan detail: adapt descriptor/audit patterns; implement the v3 Provider-specific boundary.", "integration" : "`ADR046-exec-003` and `ADR046-exec-004` call the audit adapter after reconcile; `ADR046-exec-005` and core-controller infrastructure load the manifest and derive the runtime-owned `Provider/system-core` resource/status.", @@ -6455,7 +6725,7 @@ { "currentSource" : "`packages/d2b-realm-core/src/ids.rs` (`OperationId`, `CorrelationId`); `packages/d2b-realm-codec-protobuf/src/lib.rs` (`encode_trace_context`, `decode_trace_context` for v3 codec adaptation); `packages/d2b-realm-router/src/mux_session.rs`, `route_engine.rs` (current implemented-but-unwired generic routing)", "dataMigration" : "Full d2b 3.0 reset; no v2 state/config import", - "dependencyOwner" : "ADR046-telem-001 + ADR046-session-001 + ADR046-session-003; session/bus owner", + "dependencyOwner" : "ADR046-telem-001 + ADR046-session-001 + ADR046-bus-001; session/bus owner", "destination" : "`packages/d2b-resource-api/src/metrics.rs`, `packages/d2b-session/src/metrics.rs`, `packages/d2b-bus/src/metrics.rs`", "detailedDesign" : "Instrument resource API verb dispatch, watch delivery, bus route resolution, and session handshake/reconnect per the metric/span catalog in this spec. Propagate `TraceContext` from incoming bus request to store write transaction span as child context. Primary reuse disposition: `adapt`. Preserved source-plan detail: adapt `TraceContext` protobuf codec for v3 resource API framing; adapt routing metrics patterns.", "integration" : "ResourceClient → bus → API → store span chain via `TraceContext`", @@ -6575,7 +6845,7 @@ { "currentSource" : "`packages/d2bd/src/daemon_audit.rs` (hash-chain `ResourceMutation`-like append pattern — adapt for cleanup audit records); `packages/d2b-priv-broker/src/audit.rs` (`AuditWriteClass::{Standard,Unprivileged}` — cleanup audit records use `Standard` durability); `packages/d2b-realm-core/src/audit.rs::AuditChainLink::new` (hash-chain append for cleanup audit records); `nixos-modules/manifest.nix` (prior-generation retention pattern in the current bundle contract)", "dataMigration" : "None — the `managedBy`/`configurationGeneration`/`deletionRequestedAt` fields are new; existing resources gain them on first v3 activation", - "dependencyOwner" : "ADR046-telem-009 + ADR046-telem-010 + ADR046-telem-012 + ADR046-store-001; core-controller owner", + "dependencyOwner" : "ADR046-telem-009 + ADR046-telem-010 + ADR046-audit-001 + ADR046-store-001; core-controller owner", "destination" : "`packages/d2b-core-controller/src/{configuration.rs, ownership.rs}`", "detailedDesign" : "(1) On new generation activation, every stored `managedBy=configuration` resource absent from the new configured set receives `deletionRequestedAt` plus `deletion-pending`; controller/API-managed resources are untouched. (2) Activation returns after durable intent queueing and does not wait for cleanup. (3) The ownership handler drives child-before-parent finalizers. (4) When finalizers clear, one atomic store transaction writes the `Deleted` revision/change event and removes the row and indexes. After commit, the audit subsystem appends `ResourceMutation{event=\"deleted\", trigger=\"config-cleanup\"}` from that revision using a dedup/exactly-once recovery key; audit append is not part of the store transaction. (5) Stall detection sets `cleanup-stalled` without force-removing finalizers. (6) Prior generations use count retention, default 3 and range 1..16, with no TTL. (7) Core sets `managedBy`/`configurationGeneration` in persisted resources; input bundles omit both. Primary reuse disposition: `adapt`. Preserved source-plan detail: Adapt hash-chain append from `daemon_audit.rs` for `ResourceMutation{trigger=\"config-cleanup\"}` records; adapt prior-generation retention window from `manifest.nix` pattern.", "integration" : "`d2b-core-controller::configuration.rs` (generation activation); `d2b-core-controller::ownership.rs` (cleanup ordering and atomic final deletion); `d2b-audit` sink (cleanup audit records)", @@ -6587,246 +6857,6 @@ "validation" : "All tests in \"Configuration-owned cleanup contract tests\" subsection; additionally: `managedby_configuration_set_on_activated_resources`, `controller_created_resources_have_managedby_controller`, `absent_resource_receives_delete_on_new_generation`, `deletion_sets_deletionrequestedat_not_phase`, `final_deletion_is_atomic`, `cleanup_does_not_touch_controller_children`, `pending_cleanup_condition_set_on_zone`, `zone_is_degraded_not_failed_during_cleanup`, `pending_cleanup_cleared_after_deletion_completes`, `prior_generation_retained_count_based`, `rollback_schedules_delete_for_new_generation_resources`, `audit_segments_preserved_on_provider_delete`, `cleanup_stall_condition_set`, `generation_rejected_emits_audit_record`", "workItemId" : "ADR046-telem-011" }, - { - "currentSource" : "`packages/d2b-realm-core/src/audit.rs` (`AuditHash::parse`, `AuditChainLink::new`/`verify`, `AuditChainRecord{stream: AuditStreamKind, realm: RealmPath, node: NodeId}`, `AuditStreamKind::{Gateway,RemoteNode,Daemon}`, `AuditSinkHealth`, `AuditRetentionFloorStatus`); `packages/d2bd/src/daemon_audit.rs` (hash-chain append algorithm, `prev_hash`/`record_hash` SHA-256 pattern, daily segment files `daemon-events-YYYY-MM-DD.jsonl`, `DaemonEvent` additive contract); `packages/d2b-priv-broker/src/audit.rs` (`AuditWriteClass::{Privileged,Unprivileged}`, `AuditDropSummary`, `DEFAULT_AUDIT_WRITES_PER_SECOND = 4096`, O_APPEND CLOEXEC file open, `AuditDropWarningState`); `packages/d2b-gateway-runtime/src/audit_jsonl.rs` (`JsonlGatewayAudit`, `DEFAULT_GATEWAY_AUDIT_RETENTION_DAYS = 14`, `prune_old` rotation algorithm); `packages/d2b-priv-broker/src/ops/audit_op.rs` (`OpAuditRecord`, `SwtpmDirAudit`, `SwtpmDirResult`, `SwtpmMarkerResult`); `packages/d2b-realm-core/src/ids.rs` (`OperationId`, `CorrelationId`, `PrincipalId` — `PrincipalId` becomes `subject_digest`); `packages/d2b/tests/audit_contract.rs`; `packages/d2b-priv-broker/tests/broker_export_audit.rs`", - "dataMigration" : "v3 bootstrap; existing daemon/broker JSONL files not migrated", - "dependencyOwner" : "W0/W1a; audit crate owner", - "destination" : "`packages/d2b-audit/src/{hash_chain.rs,segment.rs,rate_limit.rs,record_types.rs,sink.rs,export.rs}`", - "detailedDesign" : "`d2b-audit` provides: typed record structs per class; canonical serialization with `zone` replacing `realm: RealmPath`; SHA-256 hash chain (extracted from `daemon_audit.rs`); segment writer (O_APPEND CLOEXEC, 64 MiB / UTC-midnight rotation); 30-day compaction (adapts `prune_old` from `JsonlGatewayAudit`); `AuditWriteClass::{Privileged,Standard,BestEffort}` (extends current `{Privileged,Unprivileged}`); rate-limit with privileged-never-dropped invariant; export iterator with inline hash-break reporting. `AuditStreamKind` re-versioned: `Daemon→Zone`, `Gateway→ZoneLink`, `RemoteNode→RemoteZone`. `AuditChainRecord` re-versioned: `{zone: String}` replaces `{realm: RealmPath, node: NodeId}`. Primary reuse disposition: `adapt`. Preserved source-plan detail: extract unchanged: `AuditHash`, `AuditChainLink` from `d2b-realm-core/src/audit.rs`; copy hash-chain append algorithm from `daemon_audit.rs`; copy `AuditWriteClass`/rate-limit/rotation/prune from broker `audit.rs`; adapt `JsonlGatewayAudit` segment writer; adapt `OpAuditRecord` to `BrokerEffect` record class.", - "integration" : "Zone runtime, core-controller, Process Providers, broker effect bridge → `d2b-audit` sink; `d2b zone audit export` → export iterator", - "removalProof" : "`daemon_audit.rs`, broker `audit.rs`, `JsonlGatewayAudit` retired per-component after `d2b-audit` sink achieves parity", - "reuseAction" : "adapt", - "reuseSource" : null, - "specId" : "ADR-046-telemetry-audit-and-support", - "specPath" : "docs/specs/ADR-046-telemetry-audit-and-support.md", - "validation" : "`audit_record_hash_chain`, `audit_record_schema` (no `realm`/`node` fields), `audit_segment_rotation`, `audit_rate_limit_privileged_never_dropped`, `audit_unavailable_blocks_privileged`", - "workItemId" : "ADR046-telem-012" - }, - { - "currentSource" : "`packages/d2b-priv-broker/src/ops/audit_op.rs` (`OpAuditRecord` structural pattern — operation, peer_uid, decision, result fields); `packages/d2b-realm-core/src/audit.rs::AuditEnvelope{principal: PrincipalId, scope: AuthorizationScope, decision: AuthzDecision}` (principal → v3 `subject_digest`)", - "dataMigration" : "Full d2b 3.0 reset; no v2 state/config import", - "dependencyOwner" : "ADR046-telem-012 + ADR046-store-001; store/authz owner", - "destination" : "`packages/d2b-resource-store-redb/src/audit.rs`, `packages/d2b-core-controller/src/authz_audit.rs`", - "detailedDesign" : "`ResourceMutation` records emitted by the store actor inside the write transaction before commit returns. The audit sink must durably fsync the audit record before returning the commit success (privileged durability class). `RBACChange` emitted by the authz handler in the same write transaction. `subject_digest` = SHA-256 of normalized canonical subject string from v3 `AuthenticatedSubjectContext` (ADR-046-componentsession-and-bus). Primary reuse disposition: `adapt`. Preserved source-plan detail: adapt `OpAuditRecord` structural pattern for `ResourceMutation` / `RBACChange` record classes; adapt `PrincipalId` → `subject_digest` derivation.", - "integration" : "Store write transaction → `d2b-audit` sink → fsync → commit result", - "removalProof" : "None — net-new; no prior owner to remove", - "reuseAction" : "adapt", - "reuseSource" : null, - "specId" : "ADR-046-telemetry-audit-and-support", - "specPath" : "docs/specs/ADR-046-telemetry-audit-and-support.md", - "validation" : "Integration test: 100 mutations → verify hash-chained audit records with `zone` field, no `realm` field", - "workItemId" : "ADR046-telem-013" - }, - { - "currentSource" : "`packages/d2b-gateway/src/audit.rs` (`GatewayAuditEvent`, `GatewayAuditKind::{DisplaySessionOpenAdmitted,DisplaySessionOpenDenied,DisplaySessionRunning,DisplaySessionClosed}`, `GatewayAudit` trait, `NoopGatewayAudit`); `packages/d2b-realm-core/src/audit.rs::AuditEnvelope{realm, principal, scope, decision, trace}` (fields adapted to v3 `SessionConnect` record class)", - "dataMigration" : "Full d2b 3.0 reset; no v2 state/config import", - "dependencyOwner" : "ADR046-telem-012 + ADR046-session-001 + ADR046-session-003; session/bus owner", - "destination" : "`packages/d2b-session/src/audit.rs`, `packages/d2b-bus/src/audit.rs`", - "detailedDesign" : "`SessionConnect` records emitted at handshake completion. `GatewayAuditKind::DisplaySessionOpenAdmitted/Denied` → `event=\"connect\"`, `authz_decision=\"allowed/denied\"`. `GatewayAuditKind::DisplaySessionRunning` → informational `ProcessEffect`. `GatewayAuditKind::DisplaySessionClosed` → `event=\"close\"`. `transport_class=zone_link` covers what the current `AuditStreamKind::Gateway` stream recorded for gateway-backed realm sessions. `RouteAdmission` records emitted at bus route resolution for denied routes. Primary reuse disposition: `adapt`. Preserved source-plan detail: adapt `GatewayAudit` trait pattern for `SessionConnect` and `RouteAdmission` record classes; `NoopGatewayAudit` pattern reused for test sinks.", - "integration" : "Session engine and bus router → `d2b-audit` sink", - "removalProof" : "`NoopGatewayAudit` and gateway JSONL sink retired after gateway is on v3 resource API", - "reuseAction" : "adapt", - "reuseSource" : null, - "specId" : "ADR-046-telemetry-audit-and-support", - "specPath" : "docs/specs/ADR-046-telemetry-audit-and-support.md", - "validation" : "Session connect/close/auth-failure audit tests; `GatewayAuditKind` → `SessionConnect` mapping test", - "workItemId" : "ADR046-telem-014" - }, - { - "currentSource" : "`packages/d2b/tests/audit_contract.rs` (`d2b audit --strict` returns 78; `auditResponse` relay; `authz-audit-requires-admin` denial; daemon-down exit 1 without bash fallback); `packages/d2b-priv-broker/tests/broker_export_audit.rs` (`export_audit_requires_admin_and_exports_op_audit_records`: admin-only, path-free, NDJSON `ExportBrokerAuditOk` shape, `peer_uid` field, `ApplyNftables` operation name in records)", - "dataMigration" : "Full d2b 3.0 reset; no v2 state/config import", - "dependencyOwner" : "ADR046-telem-012; CLI owner", - "destination" : "`packages/d2b/src/zone_audit.rs` (new `d2b zone audit export` subcommand); `packages/d2b/tests/zone_audit_contract.rs`", - "detailedDesign" : "`d2b zone audit export` opens segments read-only (shared flock), streams NDJSON to stdout, validates hash chain inline, reports breaks as inline error records, enforces `audit-export` verb via resource API (admin-only, same `SO_PEERCRED`/Role check as current `ExportBrokerAuditOk`). Assert no `realm`, `node`, `workload_id` fields in exported records. Primary reuse disposition: `adapt`. Preserved source-plan detail: adapt audit CLI contract test (daemon-down/exit behavior); adapt broker export test to new `zone` field and v3 record schema.", - "integration" : "`d2b` CLI → resource API `audit-export` verb → `d2b-audit` export iterator → stdout", - "removalProof" : "`d2b audit` legacy command retained until `d2b zone audit export` covers all record classes", - "reuseAction" : "adapt", - "reuseSource" : null, - "specId" : "ADR-046-telemetry-audit-and-support", - "specPath" : "docs/specs/ADR-046-telemetry-audit-and-support.md", - "validation" : "`export_audit.rs`: admin-only, hash break inline, no old field names (`realm`/`node`/`workload_id`), no path/argv in output, exit 0 on clean chain", - "workItemId" : "ADR046-telem-015" - }, - { - "currentSource" : "`packages/d2b/tests/host_doctor_contract.rs` (env-redirect sandbox: `D2B_BROKER_SOCKET`, `D2B_PUBLIC_SOCKET`, `D2B_DAEMON_STATE_DIR`, `D2B_METRICS_URL`, `D2B_MANIFEST_PATH`; `doctor::render_summary` JSON envelope fields: `command`, `mode`, `broker_ready`, per-check `status`+`data`, `summary`, `exitCode`); `packages/d2bd/src/audit_check.rs` (`defects` array audit-chain validation pattern); `packages/d2bd/src/lib.rs` (doctor read-only path: `host doctor --read-only` reads from `D2B_DAEMON_STATE_DIR`, pidfd_table file, kernel-module check file, metrics URL)", - "dataMigration" : "Full d2b 3.0 reset; no v2 state/config import", - "dependencyOwner" : "ADR046-core-001 + ADR046-telem-012; CLI/doctor owner", - "destination" : "`packages/d2b/src/zone_doctor.rs`, `packages/d2b/tests/zone_doctor_contract.rs`", - "detailedDesign" : "`d2b zone doctor [--zone ] [--json]` reads resource status from Zone API (read-only verb), OTEL self-metrics from `observability-otel` Provider endpoint (optional), and audit segment inventory from `d2b-audit` segment reader. Named check set from this spec. Exit 0 on all-ready; 1 on any warn/error. Env-redirect sandbox for all test fixtures. Current `MANIFEST_JSON` fixture pattern adapted: `\"_observability\": {\"enabled\": false}` test ensures OTEL probe short-circuits cleanly. Primary reuse disposition: `adapt`. Preserved source-plan detail: adapt env-redirect sandbox test harness; adapt `defects` array pattern for `audit-hash-chain-clean` check; adapt `broker_ready` → `zone_phase` field.", - "integration" : "`d2b` CLI → Zone resource API status reads + audit segment reader", - "removalProof" : "`d2b host doctor` retained until `d2b zone doctor` covers all check parity", - "reuseAction" : "adapt", - "reuseSource" : null, - "specId" : "ADR-046-telemetry-audit-and-support", - "specPath" : "docs/specs/ADR-046-telemetry-audit-and-support.md", - "validation" : "`zone_doctor_contract.rs`: all-ready/degraded/quarantine/otel-absent/audit-absent fixtures; no resource names/paths/argv/PIDs; `zone_phase` field present; no legacy `broker_ready` field", - "workItemId" : "ADR046-telem-016" - }, - { - "currentSource" : "`packages/d2b/tests/host_doctor_contract.rs` (env-redirect sandbox scaffold — no current `support-bundle` equivalent exists)", - "dataMigration" : "Full d2b 3.0 reset; no v2 state/config import", - "dependencyOwner" : "ADR046-telem-016; CLI/doctor owner", - "destination" : "`packages/d2b/src/zone_support_bundle.rs`, `packages/d2b/tests/zone_support_bundle_contract.rs`", - "detailedDesign" : "`d2b zone support-bundle [--zone ]` requires `support-bundle` verb. Reads bounded resource status snapshots (32 per type, 512 total; metadata + status only; no spec bytes; no `metadata.name`), controller queue depths, schema catalog (names+versions only), audit segment inventory, OTEL collector metrics summary, bounded structured log ring (2000 entries). NDJSON output. On quarantine: `bundle_completeness: \"partial\"`, exit 1. Primary reuse disposition: `adapt`. Preserved source-plan detail: reuse env-redirect sandbox scaffold.", - "integration" : "`d2b` CLI → Zone resource API list (status subresource only) + controller introspection + audit segment reader + OTEL self-metrics", - "removalProof" : "None — net-new; no prior owner to remove", - "reuseAction" : "adapt", - "reuseSource" : null, - "specId" : "ADR-046-telemetry-audit-and-support", - "specPath" : "docs/specs/ADR-046-telemetry-audit-and-support.md", - "validation" : "`zone_support_bundle_contract.rs`: complete/partial bundles; no spec/name/path/argv; field completeness", - "workItemId" : "ADR046-telem-017" - }, - { - "currentSource" : "`packages/d2b-core/src/unsafe_local_workloads.rs` (`UnsafeLocalWorkloadsJson`, `UnsafeLocalWorkload`, `UnsafeLocalLauncherItem`, `UNSAFE_LOCAL_WORKLOADS_SCHEMA_VERSION = \"v2\"`, `MAX_UNSAFE_LOCAL_WORKLOADS = 256`); `packages/d2b-contracts/src/unsafe_local_wire.rs` (`HelperHello.uid: u32`, `HelperLaunchRequest`, `HelperShellRequest`, `HelperScopeKind::{Exec,Shell}`, `DaemonToUnsafeLocalHelper`, `UnsafeLocalHelperToDaemon`); `packages/d2bd/src/unsafe_local_helper.rs` (`HelperRegistry::new(daemon_uid, allowed_uids)`, `dispatch_launch`, `bind_helper_socket`); `packages/d2b-unsafe-local-helper/src/{main,protocol,runtime,systemd}.rs` (`HelperClient`, `ScopeRuntime`, `run_scope_supervisor`, `SystemdUserScopeManager`); `nixos-modules/options-realms-workloads.nix` (lines 221, 233–235 `kind = \"unsafe-local\"` description; lines 264–275 null `stateDir`/`runDir`); `nixos-modules/unsafe-local-workloads-json.nix` (`runtimeKind = \"unsafe-local\"`, `providerId = \"unsafe-local\"`); `nixos-modules/unsafe-local-helper.nix` (service unit)", - "dataMigration" : "Full d2b 3.0 reset; no v2 state/config import", - "dependencyOwner" : "ADR046-telem-012 + ADR046-core-001; `Provider/system-core` owner", - "destination" : "`packages/d2b-provider-system-core/src/{host_reconciler.rs,host_status.rs,host_process_audit.rs}`; adapted `nixos-modules/unsafe-local-workloads-json.nix`; `packages/d2b-provider-system-core/tests/host_posture_contract.rs`", - "detailedDesign" : "`Provider/system-core` reconciler: (1) On user-only `Host` resource creation (`defaultDomain=user`, `allowedDomains=[user]`), set `status.isolationPosture = \"none\"` and `status.isolationPostureMessage = \"...\"` unconditionally; reject any operator-supplied value for these fields. Host resources with other execution policies do not receive `isolationPosture`. (2) On every user-only Host process launch: emit `ProcessEffect{event:\"launch\", provider:\"system-core-user\", domain:\"user\", no_isolation:true, ...}` audit record. (3) On every user-only Host process stop: emit `ProcessEffect{event:\"stop\", ...}`. (4) `d2b zone list`/`inspect` CLI renders `⚠ no isolation boundary (user domain)` annotation only for `Host` resources with `isolationPosture: \"none\"`; annotation is not suppressible. (5) `isolation-posture-declared` doctor check: passes when user-only `Host` resource status has `isolationPosture: \"none\"`; omitted when Zone has no user-only `Host` resources. (6) `no_isolation=true` is emitted in `ProcessEffect` records only; it does not appear in any OTEL span attribute, log field, or metric label. Primary reuse disposition: `adapt`. Preserved source-plan detail: adapt `UnsafeLocalWorkload` private-bundle contract for the `Host` resource spec payload; adapt `HelperRegistry::allowed_uids` constraint as `defaultUserRef=User/` validation; adapt Nix `unsafe-local-workloads-json.nix` emitter for the new Host resource shape; gap-fill: add `ProcessEffect{no_isolation:true}` at `dispatch_launch` / stop call sites.", - "integration" : "`Provider/system-core` reconciler → `d2b-audit` sink; `d2b zone doctor` → resource status check; `d2b zone list`/`inspect` → CLI output renderer", - "removalProof" : "`d2b-unsafe-local-helper` binary and `DaemonToUnsafeLocalHelper`/`UnsafeLocalHelperToDaemon` wire types retired after `Provider/system-core` Process Provider supervisor ticket migration; `nixos-modules/unsafe-local-helper.nix` Nix unit retired after migration; `nixos-modules/unsafe-local-workloads-json.nix` adapted (not deleted) to emit Host resource spec format", - "reuseAction" : "adapt", - "reuseSource" : null, - "specId" : "ADR-046-telemetry-audit-and-support", - "specPath" : "docs/specs/ADR-046-telemetry-audit-and-support.md", - "validation" : "`host_posture_contract.rs` tests from the Host posture tests section of this spec; `d2b-contract-tests/tests/policy_telemetry_redaction.rs` asserts `no_isolation` key absent from all span/metric/log surfaces", - "workItemId" : "ADR046-telem-018" - }, - { - "currentSource" : "Main commit source row below: `packages/d2b-session/` and `packages/d2b-contracts/src/v2_component_session.rs` at `a1cc0b2d`.", - "dataMigration" : "Full d2b 3.0 reset; no v2 session runtime state/config import", - "dependencyOwner" : "ADR046-telem-001 (d2b-telemetry must exist first for MetricsSink injection); session owner", - "destination" : "`packages/d2b-session/` copied verbatim; v3 contract extension renames endpoint purpose values while preserving backward wire compatibility during transition.", - "detailedDesign" : "Noise handshake, record protection, scheduler, named streams, cancellation, attachments, deadlines, metrics injection, ttrpc serving, and generation-discovery behavior from the selected source. Primary reuse disposition: `adapt`. Preserved source-plan detail: Copy/adopt the core ComponentSession runtime; adapt EndpointPurpose/ServicePackage/RealmSessionAuthority names listed in Excluded ADR45 assumptions and inject a `d2b-telemetry` MetricsSink..", - "integration" : "`d2b-bus` route handler calls `serve_ttrpc_services`; `d2b-session-unix` provides `OwnedTransport` impl; `d2b-telemetry` `MetricsSink` impl feeds `d2b_session_*` metrics inventory from this spec.", - "removalProof" : "ADR45 topology enum names listed in Excluded ADR45 assumptions are adapted; no prior v3 session runtime owner to remove.", - "reuseAction" : "adapt", - "reuseSource" : null, - "specId" : "ADR-046-telemetry-audit-and-support", - "specPath" : "docs/specs/ADR-046-telemetry-audit-and-support.md", - "validation" : "Adopt `tests/component_session.rs` and `tests/noise_vectors.rs` unchanged; extend with v3 `EndpointPurpose` enum gate test; add `d2b-contract-tests/tests/component_session_v2_vectors.rs` (existing at `a1cc0b2d`) as-is.", - "workItemId" : "ADR046-telem-019" - }, - { - "currentSource" : "Main commit source row below: `packages/d2b-session-unix/` at `a1cc0b2d`.", - "dataMigration" : "Full d2b 3.0 reset; no v2 transport runtime state/config import", - "dependencyOwner" : "ADR046-telem-019; Unix transport owner", - "destination" : "`packages/d2b-session-unix/` copied verbatim.", - "detailedDesign" : "SO_PASSCRED verification, pidfd identity verification, multi-scope credit reservation, emergency headroom, seqpacket atomic CLOEXEC transfer, stream reassembly, vsock transport, and descriptor policy enforcement. Primary reuse disposition: `adapt`. Preserved source-plan detail: Copy verbatim; retain `host-socket`/`native-vsock` feature flags; no ADR45 topology adaptation required..", - "integration" : "`d2b-bus` Zone-local listeners use `UnixSeqpacketTransport`; Provider agent connections use vsock transport from this crate; `CreditPool`/`CreditScopeSet` enforce per-Zone attachment FD budgets.", - "removalProof" : "None — net-new; no prior owner to remove", - "reuseAction" : "adapt", - "reuseSource" : null, - "specId" : "ADR-046-telemetry-audit-and-support", - "specPath" : "docs/specs/ADR-046-telemetry-audit-and-support.md", - "validation" : "Adopt all `unix_session.rs` tests unchanged.", - "workItemId" : "ADR046-telem-020" - }, - { - "currentSource" : "Main commit source row below: `packages/d2b-client/` at `a1cc0b2d`.", - "dataMigration" : "Full d2b 3.0 reset; no v2 client state/config import", - "dependencyOwner" : "ADR046-telem-019; client owner", - "destination" : "`packages/d2b-client/` copied; DaemonClient/GuestClient adapted to v3 service packages; `MetadataInput::with_trace` drives `TraceContext` propagation.", - "detailedDesign" : "MetadataInput trace/correlation/idempotency, retry policy, cancellation token, generic client over resolver/connector/clock, typed service proxies, stream terminal, and host-socket verifier. Primary reuse disposition: `adapt`. Preserved source-plan detail: Copy client layer; adapt service package/method sets, local-root Zone identity, and TraceContext propagation for v3..", - "integration" : "Every controller/service that makes outbound calls uses `Client`; `MetadataInput::with_trace` feeds `d2b_api_request_duration_seconds` trace-id into `d2b.bus.route` span.", - "removalProof" : "ADR45 Daemon/Guest wrappers and RealmPath identity assumptions are adapted to v3 service packages/Zone name; no prior v3 client owner to remove.", - "reuseAction" : "adapt", - "reuseSource" : null, - "specId" : "ADR-046-telemetry-audit-and-support", - "specPath" : "docs/specs/ADR-046-telemetry-audit-and-support.md", - "validation" : "Adopt typed-route, proxy-reuse, and cancel tests unchanged. Add v3 service-package name gate test.", - "workItemId" : "ADR046-telem-021" - }, - { - "currentSource" : "Main commit source row below: `packages/d2b-provider/` and `packages/d2b-provider-toolkit/` at `a1cc0b2d`.", - "dataMigration" : "Full d2b 3.0 reset; no v2 ProviderRegistry runtime state import", - "dependencyOwner" : "ADR046-telem-019 + ADR046-telem-021; Provider owner", - "destination" : "`packages/d2b-provider/` and `packages/d2b-provider-toolkit/` copied with v3 session admission and bus routing adaptations.", - "detailedDesign" : "ProviderRegistry factory registration, bounded admission, RPC proxy fail-closed behavior, generated provider service admission/dispatch, redaction wrappers, and conformance gates. Primary reuse disposition: `adapt`. Preserved source-plan detail: Copy provider runtime/toolkit; adapt SessionIdentity Zone name and route through `d2b-bus` instead of daemon embedding..", - "integration" : "Each v3 Provider process embeds `ProviderRegistry` + `GeneratedProviderServiceServer`; `check_provider_conformance` runs in Provider install-time conformance check (feeds `d2b_provider_reconcile_total{outcome=\"error\"}` on failure).", - "removalProof" : "Daemon-embedded ADR45 ProviderRegistry wiring is excluded; v3 provider-process registry replaces it without importing state.", - "reuseAction" : "adapt", - "reuseSource" : null, - "specId" : "ADR-046-telemetry-audit-and-support", - "specPath" : "docs/specs/ADR-046-telemetry-audit-and-support.md", - "validation" : "Adopt all `conformance.rs` and `runtime.rs` tests unchanged. Add v3 `SessionIdentity` zone-name gate. Add conformance-failure → `d2b_provider_reconcile_total` metric integration test.", - "workItemId" : "ADR046-telem-022" - }, - { - "currentSource" : "Main commit source row below: `packages/d2b-gateway-runtime/src/provider_agent.rs` at `a1cc0b2d` plus `tests/provider_agent_v2.rs`.", - "dataMigration" : "Full d2b 3.0 reset; no v2 provider-agent runtime state import", - "dependencyOwner" : "ADR046-telem-022 + ADR046-telem-014; Provider agent / observability-otel owner", - "destination" : "`packages/d2b-provider-observability-otel/src/agent.rs` adapted; ComponentSessionDriver mock becomes shared Provider session fixture.", - "detailedDesign" : "Session-bound ProviderAgentProcess construction, bounded audit snapshot, closed-set ProviderAgentError paths, and full ComponentSessionDriver mock for hermetic Provider tests. Primary reuse disposition: `adapt`. Preserved source-plan detail: Adapt provider-agent session wrapper and audit ring; replace gateway-runtime audit envelope with v3 `d2b-audit` records..", - "integration" : "`observability-otel` Provider embeds a `ProviderAgentProcess`; session connect/disconnect emits `SessionConnect` audit records via `d2b-audit`; `ProviderAgentAuditEvent` ring feeds `d2b_provider_reconcile_total` metric on session error.", - "removalProof" : "ADR45 gateway-runtime AuditEnvelope/realm custody and launch path are excluded; v3 provider agent is supervised by Provider/system-core or the owning Provider supervisor.", - "reuseAction" : "adapt", - "reuseSource" : null, - "specId" : "ADR-046-telemetry-audit-and-support", - "specPath" : "docs/specs/ADR-046-telemetry-audit-and-support.md", - "validation" : "Adopt `provider_agent_v2.rs` mock harness unchanged as shared v3 Provider session fixture. Add v3 audit-bridge test: provider-agent session → `SessionConnect{transport_class=\"zone_link\"}` record emitted.", - "workItemId" : "ADR046-telem-023" - }, - { - "currentSource" : "Main commit source row below: `packages/d2b-realm-router/` at `a1cc0b2d`.", - "dataMigration" : "Full d2b 3.0 reset; no v2 routing state import", - "dependencyOwner" : "ADR046-telem-019 + ADR046-session-003; bus routing owner", - "destination" : "`packages/d2b-bus/src/routing.rs` adapted from `service_v2.rs`; RemoteNodeErrorKind stable code pattern becomes v3 BusErrorKind.", - "detailedDesign" : "Realm/Zone service ttrpc handler table, session authority constructors, host/gateway credential custody split, stable low-cardinality error codes, and binding/shortcut bounds. Primary reuse disposition: `adapt`. Preserved source-plan detail: Adapt realm service routing to Zone service routing; preserve credential-custody, bounded routing, and stable error-code behavior..", - "integration" : "`d2b-bus` route handler adapts `RealmServiceServer` dispatch table; `RemoteNodeErrorKind::code()` values feed `d2b_bus_route_total{outcome}` metric labels; `CredentialCustody::Host` maps to `purpose_class=local` in `d2b_session_connect_total`.", - "removalProof" : "ADR45 RealmId and RealmService names are adapted to Zone/resource UID routing; no prior v3 bus routing owner to remove.", - "reuseAction" : "adapt", - "reuseSource" : null, - "specId" : "ADR-046-telemetry-audit-and-support", - "specPath" : "docs/specs/ADR-046-telemetry-audit-and-support.md", - "validation" : "Adopt `authority_keeps_remote_credentials_in_gateway_guests` test renamed to `authority_keeps_remote_credentials_in_zone_link_sessions`; adapt `RealmId` → Zone name; adopt `authenticated_bootstrap_enrollment_route_and_shortcut_lifecycle` renamed with zone terminology.", - "workItemId" : "ADR046-telem-024" - }, - { - "currentSource" : "Main commit source row below: `packages/d2bd/` routing/provider effects files and tests at `a1cc0b2d`.", - "dataMigration" : "Full d2b 3.0 reset; no v2 daemon routing/provider-effects state import", - "dependencyOwner" : "ADR046-telem-022 + ADR046-telem-024; core-controller routing owner", - "destination" : "`packages/d2b-bus/src/service_router.rs` and `packages/d2b-core-controller/src/provider_effects.rs`.", - "detailedDesign" : "Closed-set service.package route gates, provider lifecycle effect-adapter composition, closed-set error mapping, fixed local policy invariants, and bootstrap/enrolled credential binding shape. Primary reuse disposition: `adapt`. Preserved source-plan detail: Adapt route-gate and ProviderLifecycleDispatch patterns; exclude daemon-embedded effect composition and route through v3 bus/provider processes..", - "integration" : "Bus service router uses `service.package` closed-set matching from route-gate pattern; `ProviderLifecycleDispatch` feeds `d2b_provider_component_phase` metric.", - "removalProof" : "ADR45 daemon-embedded effect adapters and realm child supervisor assumptions are excluded; v3 bus/provider effects own the adapted behavior.", - "reuseAction" : "adapt", - "reuseSource" : null, - "specId" : "ADR-046-telemetry-audit-and-support", - "specPath" : "docs/specs/ADR-046-telemetry-audit-and-support.md", - "validation" : "Port `local_daemon_policy_is_fixed_and_has_no_negotiation_or_fd_surface` invariant to v3 bus local policy test; port `every_generated_daemon_method_has_one_typed_adapter` to v3 bus method adapter completeness test.", - "workItemId" : "ADR046-telem-025" - }, - { - "currentSource" : "Main commit source row below: ComponentSession vector/conformance tests at `a1cc0b2d`.", - "dataMigration" : "None — docs/tooling only; no runtime state", - "dependencyOwner" : "ADR046-telem-019; contract-tests owner", - "destination" : "`packages/d2b-contract-tests/tests/component_session_v2_vectors.rs` and `tests/noise_vectors.rs` copied verbatim.", - "detailedDesign" : "Pinned Noise KAT vectors, transcript/PSK mutation rejection, typed bootstrap admission fixture, and public-key corruption rejection remain the session wire security ground truth. Primary reuse disposition: `adapt`. Preserved source-plan detail: Copy the vector and conformance tests verbatim; add v3 constant gate for ComponentSession major/minor version..", - "integration" : "These tests run in `make test-rust` / `cargo test -p d2b-contract-tests` and `cargo test -p d2b-session`. They are gating for any Noise library update.", - "removalProof" : "None — net-new; no prior owner to remove", - "reuseAction" : "adapt", - "reuseSource" : null, - "specId" : "ADR-046-telemetry-audit-and-support", - "specPath" : "docs/specs/ADR-046-telemetry-audit-and-support.md", - "validation" : "These tests are self-validating. Add one gate: assert `COMPONENT_SESSION_MAJOR = 2` and `COMPONENT_SESSION_MINOR = 0` constants are unchanged in v3 contract.", - "workItemId" : "ADR046-telem-026" - }, - { - "currentSource" : "Main commit source row below: `packages/d2b-session/src/metrics.rs` and `MetricLabels` in `packages/d2b-contracts/src/v2_component_session.rs` at `a1cc0b2d`.", - "dataMigration" : "Full d2b 3.0 reset; no v2 telemetry metric state import", - "dependencyOwner" : "ADR046-telem-019 + ADR046-telem-001 + ADR046-telem-003; telemetry/session owner", - "destination" : "`packages/d2b-telemetry/src/session_metrics_sink.rs`.", - "detailedDesign" : "Map every MetricEvent variant to the `d2b_session_*` inventory; convert MetricLabels closed sets to allowed v3 label strings; keep NoopMetrics for hermetic tests. Primary reuse disposition: `adapt`. Preserved source-plan detail: Adapt MetricsSink bridge to v3 OTEL instruments and rename ADR45 endpoint-purpose label values to v3 closed-set labels..", - "integration" : "`serve_ttrpc_services` receives a `Box` from `d2b-telemetry`; all session endpoints call through this bridge.", - "removalProof" : "ADR45 EndpointPurpose metric label strings are replaced by v3 purpose names; no prior v3 MetricsSink bridge owner to remove.", - "reuseAction" : "adapt", - "reuseSource" : null, - "specId" : "ADR-046-telemetry-audit-and-support", - "specPath" : "docs/specs/ADR-046-telemetry-audit-and-support.md", - "validation" : "New test `packages/d2b-telemetry/tests/session_sink_bridge.rs`: drive `MetricEvent` variants through the sink; assert OTEL counter/gauge values; assert `MetricLabels` closed-set values map only to allowed label strings (no `DaemonLocal` string in v3 metric output).", - "workItemId" : "ADR046-telem-027" - }, { "currentSource" : "`packages/d2b-provider-relay/src/lib.rs` (`RelayEndpoint`, `RelayCredential`, `RelayRole`, `RelayStream`, `connect()`, `listen()`, `mint_sas()`)", "dataMigration" : "No compatibility with current relay sessions; v3 sessions are independent", @@ -6950,7 +6980,7 @@ { "currentSource" : "`packages/d2b-session-unix/src/{adapter,socket,descriptor}.rs`, `tests/unix_session.rs` at main `a1cc0b2d`", "dataMigration" : "None — full d2b 3.0 reset; no prior state to migrate", - "dependencyOwner" : "ADR046-transport-unix-001; d2b-bus transport layer (ADR046-session-003)", + "dependencyOwner" : "ADR046-transport-unix-001; d2b-bus transport layer (ADR046-bus-001)", "destination" : "`packages/d2b-provider-transport-unix/src/{seqpacket,identity,socket}.rs`", "detailedDesign" : "Copy transport structs verbatim; adapt `PeerIdentityPolicy` to report `PeerCredentials` upward to ComponentSession for subject mapping (not for direct resource lookup — that is core's responsibility); maintain `SO_PASSCRED` setup and first-packet credential extraction as documented; CLOEXEC enforcement uses `rustix` syscall wrappers over `libc` where available Primary reuse disposition: `adapt`. Preserved source-plan detail: copy and adapt.", "integration" : "`portal.rs::open_transport` calls `SeqpacketSocket::getsockopt(SO_TYPE)` and `setsockopt(SO_PASSCRED)`, constructs `UnixSeqpacketTransport`, hands OwnedTransport FD back to caller", @@ -7025,7 +7055,7 @@ { "currentSource" : "No portal service in v3 baseline; `d2b-provider-toolkit/src/server.rs` `GeneratedProviderServiceServer` dispatch pattern (main `a1cc0b2d`) for service entry pattern", "dataMigration" : "None — full d2b 3.0 reset; no prior state to migrate", - "dependencyOwner" : "ADR046-transport-unix-002 through 006; ADR046-session-003 (d2b-bus ComponentSession method dispatch); ADR046-session-001 (named-stream protocol)", + "dependencyOwner" : "ADR046-transport-unix-002 through 006; ADR046-bus-001 (d2b-bus ComponentSession method dispatch); ADR046-session-001 (named-stream protocol)", "destination" : "`packages/d2b-provider-transport-unix/src/{portal,service}.rs`", "detailedDesign" : "`portal.rs`: `PortalHandler` struct owns a bounded `HashMap` (capacity `MAX_OPEN_TRANSPORTS=256`); `open_transport(req, attachment_fd)` validates via `admission.rs`, dups FD, allocates handle, stores `MonitorState { dup_fd, observation_senders: Vec }`; `close_transport(handle)` closes dup FD, half-closes all observation senders, removes entry; `observe_transport(handle)` registers a new `NamedStreamSender` and spawns an async epoll-watcher task on the dup FD; `TransportHandle` is a `[u8; 16]` random token; redacted in all Debug impls; `service.rs` is the binary entry: accepts the allocator-issued portal endpoint FD at launch, runs `GeneratedTransportServiceServer` over it, dispatches to `PortalHandler` Primary reuse disposition: `adapt`. Preserved source-plan detail: adapt dispatch pattern; implement portal methods as new.", "integration" : "Core ZoneLink controller calls the three methods via d2b-bus; portal endpoint FD is supplied by Zone runtime/allocator at Process spawn, not SD_LISTEN_FDS", @@ -7232,6 +7262,21 @@ "validation" : "make test-unit and make test-flake plus grep or contract checks for removed symbols and no residual d2bd/network.nix USBIP lifecycle references.", "workItemId" : "ADR046-usbip-009" }, + { + "currentSource" : "None — the fixed user-session authority is today ambient prose across the display/audio/clipboard/notification/secret-service dossiers; no named owner exists", + "dataMigration" : "None — full d2b 3.0 reset", + "dependencyOwner" : "ADR046-zone-control-019 (authority index); `Provider/system-systemd` (user manager) + core/user-agent owner", + "destination" : "`packages/d2b-core-controller/src/user_session_authority.rs` (or a core/user-agent per-session agent Process under `Provider/system-systemd`); `AuthorityDescriptor` on the session authority", + "detailedDesign" : "Name and implement the **fixed user-session authority** (D097 desktop/session): `authorityScope: seat` bound to `(Host, User, login-session/seat)`, opaque `authorityKey` (never a raw socket path/XDG_RUNTIME_DIR/DISPLAY/seat name), `cardinality: exactly-one` per `(Host, User, login-session)`, `arbitration: exclusive`, owner = a core/user-agent per-user-session agent Process (NOT a new Provider), adoption by `ownerProof` (agent Process identity + login-session id), `exportability: forbidden`. It is the sole opener of the compositor/PipeWire/session-bus FDs and hands them to desktop Providers only via the EffectPort/LaunchTicket. Core's authority index rejects a duplicate session authority (or a duplicate same-user display portal, clipboard host, notification sink, audio mediator, systemd user manager, Secret Service, or seat-input claimant) with `duplicateConflict` before any FD open; multi-user/seat is admitted only up to the declared per-Host limit. Guest-stop invalidates every session authority/lease bound to that Guest across display/audio/notification/credential/shell in one dependency-aware cascade (D091), with no stale FD surviving. Host input (`wl_seat`/pointer constraints) is an `at-most-one`-per-seat authority under this session authority; pointer-constraint enforcement is a declared boundary until an interaction Provider implements it. Primary reuse disposition: `adapt`. Preserved source-plan detail: net-new (name and implement the shared user-session authority).", + "integration" : "Core authority index (ADR046-zone-control-019); `Provider/system-systemd` user manager; display/audio/clipboard/notification/secret-service/shell Provider services bind to this single authority for their FDs; D091 Guest-stop cascade", + "removalProof" : "Not applicable (net-new named authority; replaces ambient prose)", + "reuseAction" : "adapt", + "reuseSource" : "`Provider/system-systemd` user-manager scope; D077 EffectPort/LaunchTicket FD handoff", + "specId" : "ADR-046-resources-host-guest-process-user", + "specPath" : "docs/specs/ADR-046-resources-host-guest-process-user.md", + "validation" : "Single session authority per `(Host, User, session)`; duplicate same-user session authority / desktop service rejected with `duplicateConflict`; multi-seat declared-limit enforcement; Guest-stop invalidates all bound desktop/audio/notification/credential/shell authorities and leases (no stale compositor/PipeWire/session-bus FD); seat-input second claimant rejected; adoption by `ownerProof` and quarantine on ambiguity; hermetic with fakes", + "workItemId" : "ADR046-user-session-001" + }, { "currentSource" : "`d2b-core/src/storage.rs` (`StoragePathSpec`, `StoragePathKind`, policy enums); `d2b-core/src/sync.rs` (`SyncJson`, `LockSpec`)", "dataMigration" : "Full v3 reset; no row-level import", @@ -7520,7 +7565,7 @@ { "currentSource" : "Evidence class `test-only-or-preview`; baseline has no generic vsock transport Provider or opaque endpoint/binding ID trait.", "dataMigration" : "Full d2b 3.0 reset; no v2 state/config import.", - "dependencyOwner" : "Title: Implement `VsockEffectPort` trait and `OpaqueEndpointId`/`OpaqueBindingId` newtypes; Phase 1; Priority P0; Depends on ADR046-session-003 (OwnedTransport in d2b-session); Owner crate `d2b-provider-transport-vsock`.", + "dependencyOwner" : "Title: Implement `VsockEffectPort` trait and `OpaqueEndpointId`/`OpaqueBindingId` newtypes; Phase 1; Priority P0; Depends on ADR046-bus-001 (OwnedTransport in d2b-session); Owner crate `d2b-provider-transport-vsock`.", "destination" : "`packages/d2b-provider-transport-vsock/src/effect_port.rs`; test fake in `tests/effect_port_mock.rs`; redaction checks in `tests/redaction.rs`.", "detailedDesign" : "Implement `VsockEffectPort` trait and `OpaqueEndpointId`/`OpaqueBindingId` newtypes. Define `VsockEffectPort` async trait and opaque ID newtypes in `effect_port.rs`; implement `FakeVsockEffectPort` for tests; `redaction.rs` asserts no raw `u32` in any `Debug`/`Display` output of opaque types; no real vsock socket opened. Primary reuse disposition: `create`. Preserved source-plan detail: net-new trait/newtypes with redaction tests; no real vsock socket opened.", "integration" : "Core ZoneLink/delegation controller calls the Provider service with opaque IDs; Provider calls injected `VsockEffectPort`; live AF_VSOCK resolution remains in core runtime, not the Provider crate.", @@ -7550,7 +7595,7 @@ { "currentSource" : "Evidence class `test-only-or-preview`; no current v3 generic `VsockTransportService` implementation exists.", "dataMigration" : "Full d2b 3.0 reset; no v2 state/config import.", - "dependencyOwner" : "Title: Implement `VsockTransportService` (OpenTransport/CloseTransport/ObserveTransport); Phase 1; Priority P0; Depends on ADR046-vsock-002 and ADR046-session-003; Owner crate `d2b-provider-transport-vsock`.", + "dependencyOwner" : "Title: Implement `VsockTransportService` (OpenTransport/CloseTransport/ObserveTransport); Phase 1; Priority P0; Depends on ADR046-vsock-002 and ADR046-bus-001; Owner crate `d2b-provider-transport-vsock`.", "destination" : "`packages/d2b-provider-transport-vsock/src/service.rs`; tests `tests/open_close.rs`, `tests/observe.rs`, and conformance kit.", "detailedDesign" : "Implement `VsockTransportService` (OpenTransport/CloseTransport/ObserveTransport). Implement all three service methods in `service.rs`; `open_close.rs` and `observe.rs` test full service API against `FakeVsockEffectPort`; conformance kit passes. Primary reuse disposition: `adapt`. Preserved source-plan detail: net-new service implementation over ComponentSession and fake effect port tests.", "integration" : "Core ZoneLink/delegation controller is the only authorized caller; service opens named stream handles for d2b-bus, releases them on close, and streams transport events for observe.", @@ -7625,7 +7670,7 @@ { "currentSource" : "`packages/d2b-host/src/virtiofsd_argv.rs` (VirtiofsdArgvInput, generate_virtiofsd_argv, 14 unit tests, golden argv.txt); `packages/d2b-host/src/lib.rs` (module declaration)", "dataMigration" : "v3.0 reset; socket path format changes", - "dependencyOwner" : "ADR046-volume-001 (Volume contract types); ADR046-vvfs-007 (Export type); W1; volume-virtiofs Provider owner", + "dependencyOwner" : "ADR046-volume-001 (Volume contract types); ADR046-vvfs-export-001 (Export type); W1; volume-virtiofs Provider owner", "destination" : "`packages/d2b-provider-volume-virtiofs/src/virtiofsd_argv.rs`; `packages/d2b-provider-volume-virtiofs/tests/argv_golden.rs`", "detailedDesign" : "Create crate skeleton with mandatory `src/`, `tests/`, `integration/`, `README.md`. Extract `VirtiofsdArgvInput` and `generate_virtiofsd_argv` with these changes: (1) replace `extra_args: Vec` with nothing (removed); (2) replace `socket_path: String` with `socket_path: SocketPath` newtype backed by `socket_path.rs`; (3) add `shared_dir_fd: i32` replacing `shared_dir: String` (FD-based); (4) replace `socket_group: Option` with `socket_group: Option`. Implement `socket_path.rs`: private path using SHA-256 of `\\x00\\x00`, truncated 8 hex chars, formatted as `/vms//vol-.vfd.sock`. Assert path length ≤ 108 bytes. Primary reuse disposition: `adapt`. Preserved source-plan detail: extract and adapt.", "integration" : "volume-virtiofs controller `export.rs` calls virtiofsd_argv.rs at spawn time; LaunchTicket carries resolved socket path as opaque sealed field", @@ -7655,7 +7700,7 @@ { "currentSource" : "`packages/d2bd/src/supervisor/dag.rs` (ProcessRole::Virtiofsd dag node); `nixos-modules/processes-json.nix` (virtiofsdRunner block; attachment-to-Process mapping)", "dataMigration" : "Current `ProcessRole::Virtiofsd` dag nodes replaced by Export → Process resource lifecycle", - "dependencyOwner" : "ADR046-vvfs-001, ADR046-vvfs-002, ADR046-vvfs-007; ADR046-volume-001; W2; volume-virtiofs controller owner", + "dependencyOwner" : "ADR046-vvfs-001, ADR046-vvfs-002, ADR046-vvfs-export-001; ADR046-volume-001; W2; volume-virtiofs controller owner", "destination" : "`packages/d2b-provider-volume-virtiofs/src/controller.rs`; `packages/d2b-provider-volume-virtiofs/src/export.rs`", "detailedDesign" : "Implement volume-virtiofs-controller reconcile loop using toolkit ResourceClient. Watch selector: `virtiofs.d2bus.org.Export` resources (all in zone), owned Process resources, owned User resources, Volume resources (read-only for view/vcpu resolution), Guest resources (read-only for vcpu count). On `spec-generation-changed` for an Export: (1) resolve View from Volume; (2) check store-view marker if applicable; (3) resolve threadPoolSize from Guest vcpus; (4) ensure User/vol--vfd; (5) diff against current Process; (6) emit Create/UpdateSpec. On `owned-resource-changed` for a Process: update Export status. On `deletionRequestedAt` for Export: two-phase teardown (§6.2). Primary reuse disposition: `adapt`. Preserved source-plan detail: extract and adapt.", "integration" : "volume-virtiofs controller registered by core ProviderDeployment; receives owned-resource-changed trigger from Export; emits Process resources consumed by system-minijail", @@ -7725,7 +7770,22 @@ "specId" : "ADR-046-provider-volume-virtiofs", "specPath" : "docs/specs/providers/ADR-046-provider-volume-virtiofs.md", "validation" : "`tests/schema_conformance.rs`: `export_schema_canonical_json_stable`, `export_spec_denied_unknown_fields`, `export_status_exportready_is_boolean_not_path`, `export_owner_must_be_volume`", - "workItemId" : "ADR046-vvfs-007" + "workItemId" : "ADR046-vvfs-export-001" + }, + { + "currentSource" : "main `a1cc0b2d`: `packages/d2b-contracts/src/v2_services.rs` (`MethodSpec{mutating,stream_kind,...}`, `ServiceSpec`, `SERVICE_INVENTORY` covering 20+ services and all provider services, `service_schema_fingerprint`, `public_daemon_schema_fingerprint`, `direct_guest_schema_fingerprint`, `StrictWireMessage`, `decode_strict`, `encode_strict`, `admit_metadata`, `TerminalStreamValidator`, `ServerStreamLease`, `RedactedTerminalFrame`, stream-name validators — lines 204–1004; tests `public_endpoint_fingerprint_binds_both_services_dependencies_and_order`, `public_endpoint_fingerprint_binds_daemon_and_guest_method_descriptors`, `direct_guest_fingerprint_binds_activation_and_remains_separate_from_public_endpoint` — lines 872–1030); `packages/d2b-contracts/src/v2_state.rs` (constants: `STATE_SCHEMA_VERSION=2`, `STATE_SCHEMA_GENERATION=1`, `MAX_JSON_DOCUMENT_BYTES=1_048_576`, `MAX_INVENTORY_ROWS=4096`, `MAX_LOCKS=1024`, `MAX_LOCK_DEPENDENCIES=32`, `MAX_DISCOVERY_OBSERVATIONS=4096`, `MAX_AUDIT_RECORD_BYTES=8192`, `MAX_AUDIT_RECORDS_PER_SEGMENT=16384`, `MAX_AUDIT_SEGMENT_BYTES=64*1024*1024`, `MAX_AUDIT_RETENTION_DAYS=14`, `MAX_LOCK_DEADLINE_MS=300_000`; types: `Digest`, `Generation`, `OwnershipEpoch`, `SafeJsonInteger`, `StorageCategory`, `StateEnvelope`, `CanonicalPayloadVerifier`, `AtomicWritePhase`, `RunnerEvidence`, audit types incl. `AuditRecord/Segment/Checkpoint/Gap`, `detect_audit_gap`, `AuditRetentionPolicy`, lock types: `LockClass`, `LockSpec`, `SyncInventory`, `LeaseRecord`); `packages/d2b-contracts/src/v2_identity.rs` (`IdentityError` 13 variants, canonical name rules `^[a-z][a-z0-9-]*$` max 63 bytes start-lowercase-letter, `RealmPath` label/separator rules, `ProviderType::ALL` 11 types + `as_str()` — lines 11–250); `packages/d2b-contracts/src/v2_provider.rs` (bounded opaque IDs `[a-z][a-z0-9-]{0,63}`: `ImplementationId`, `OperationId`, `IdempotencyKey`, `PlanId`, `HandleId`, `LeaseId`, `TransferId`, `PROVIDER_CONTRACT_FINGERPRINT`, `ProviderContractError` 34 variants, `Fingerprint` 64 lowercase-hex chars — lines 18–219); `packages/d2b-contracts/src/generated_v2_services/` (all 40+ generated ttrpc client/server stubs for v2 services)", + "dataMigration" : "v2 and v3 wire type namespaces coexist in the same contracts crate; no migration", + "dependencyOwner" : "ADR046-zone-control-013", + "destination" : "`packages/d2b-contracts/src/v3/{services,state,identity,provider}.rs` (new v3 namespace); generated stubs regenerated from v3 proto files in `packages/d2b-contracts/proto/v3/`", + "detailedDesign" : "**Selected**: `MethodSpec`/`ServiceSpec`/fingerprinting infrastructure → v3 Zone API service schema fingerprinting; `StrictWireMessage`/`decode_strict`/`encode_strict`/`admit_metadata` → v3 wire decode/admit for all resource API requests; `CanonicalPayloadVerifier` payload-digest binding → v3 resource store integrity checks; audit chain types `AuditRecord`/`AuditSegment`/`AuditCheckpoint`/`detect_audit_gap` + `MAX_AUDIT_RETENTION_DAYS=14`/`MAX_AUDIT_RECORD_BYTES=8192`/`MAX_AUDIT_SEGMENT_BYTES=64MiB` → v3 Zone audit (§13.3); lock types `LockSpec`/`SyncInventory`/`LeaseRecord` + `MAX_LOCKS=1024`/`MAX_LOCK_DEADLINE_MS=300_000` → v3 resource store lock layer; bounded opaque ID pattern `[a-z][a-z0-9-]{0,63}` → v3 `OperationId`/`HandleId`/`PlanId`/`LeaseId`; `Fingerprint` 64-hex-char → v3 Provider `spec.configFingerprint`; `ProviderContractError` 34 variants → v3 Provider operation error taxonomy; canonical name constraint `^[a-z][a-z0-9-]*$` max 63 bytes → same as `ids.rs::is_label()` shared validator (§16.2). **Excluded ADR45 assumptions**: `RealmLabel`/`WorkloadName`/`RealmPath` identity types: ADR45 workload/realm address format; replaced by `metadata.name` + Zone `ResourceRef`. `ProviderType::ALL` fixed 11-type closed enum: v3 Provider type is an open string field in the Provider resource spec. `STATE_SCHEMA_VERSION=2`/`STATE_SCHEMA_GENERATION=1`: v3 store schema uses redb table versioning, not a JSON schema version field. v2 service fingerprint tests reference ADR45-specific proto files; v3 fingerprints use different proto inputs but the same `service_schema_fingerprint` seeding mechanism. Generated stubs in `generated_v2_services/` are v2-specific and excluded; v3 uses regenerated stubs from `proto/v3/`.", + "integration" : "v3 Zone API service layer uses `MethodSpec.mutating` + `StrictWireMessage` for admission and fingerprinting; `v3/state.rs` `Digest`/`StateEnvelope` integrate with redb store writes; `CanonicalPayloadVerifier` validates resource payloads loaded from store; audit types feed §13.3 Zone audit segment; lock types wire into ADR 0034 lock lifecycle", + "removalProof" : "`d2b-contracts/src/v2_{services,state,identity,provider}.rs` and `v2_component_session.rs` deprecated in contracts crate after all v2 clients decommissioned", + "reuseAction" : "adapt", + "reuseSource" : null, + "specId" : "ADR-046-resources-zone-control", + "specPath" : "docs/specs/ADR-046-resources-zone-control.md", + "validation" : "`v3-canonical-name-matches-ids-is-label-regex`, `v3-service-fingerprint-changes-on-method-mutation` (behavior ported from `public_endpoint_fingerprint_binds_daemon_and_guest_method_descriptors`), `v3-audit-gap-detection-covers-missing-segment`, `v3-strict-wire-rejects-unknown-fields`, `v3-state-envelope-digest-mismatch-rejected`, `v3-canonical-payload-verifier-binding-holds-under-mutation`", + "workItemId" : "ADR046-wire-001" }, { "currentSource" : "`packages/d2b-realm-core/src/ids.rs` (`RealmId`, `LABEL_PATTERN = \"^[a-z][a-z0-9-]*$\"`, `is_label()`, `MAX_ID_LEN = 128`, `IdError` — `implemented-and-reachable`, baseline `b5ddbed6`); `packages/d2b-realm-core/src/realm.rs` (`RealmPath`, `MAX_REALM_LABELS = 16`, `RealmControllerPlacement`, `EntrypointMode` — `implemented-and-reachable`, baseline `b5ddbed6`); Zone resource schema: `ADR-only`", @@ -7762,7 +7822,7 @@ "dataMigration" : "Full reset; Provider packages recompiled and re-registered per new schema", "dependencyOwner" : "ADR046-zone-control-001; ADR046-api-001; ADR046-zone-control-017", "destination" : "`packages/d2b-contracts/src/v3/provider.rs`; `packages/d2b-core-controller/src/provider_lifecycle.rs`; `packages/d2b-core-controller/src/api_catalog.rs`", - "detailedDesign" : "Provider resource schema with all spec fields from §4.3, including resolved component bounds (max 8 controllers, 8 services, 32 worker templates, 16 ResourceTypes per controller); trust/conformance/config validation; component descriptor validation; dependency alias resolution; API binding with permission intersection; lifecycle policies; Nix Provider installation options; Provider crate layout enforcement per §4.8 (see ADR046-zone-control-026) Primary reuse disposition: `adapt`. Preserved source-plan detail: extract and adapt (`workload_lists_and_advertises`/`display_fails_closed_when_unsupported` conformance behavior; `RuntimeCapabilitySet`/`WorkloadCapabilitySet`/`NodeCapabilitySet` → Provider component `supportedCapabilities`; `ProviderError`/`RetryHint` → Provider lifecycle error schema; `ProviderId` → Provider `metadata.name` validator; `ProcessRole` variants → Provider component type identifiers).", + "detailedDesign" : "Provider resource schema with all spec fields from §4.3, including resolved component bounds (max 8 controllers, 8 services, 32 worker templates, 16 ResourceTypes per controller); trust/conformance/config validation; component descriptor validation; dependency alias resolution; API binding with permission intersection; lifecycle policies; Nix Provider installation options; Provider crate layout enforcement per §4.8 (see ADR046-pkg-001) Primary reuse disposition: `adapt`. Preserved source-plan detail: extract and adapt (`workload_lists_and_advertises`/`display_fails_closed_when_unsupported` conformance behavior; `RuntimeCapabilitySet`/`WorkloadCapabilitySet`/`NodeCapabilitySet` → Provider component `supportedCapabilities`; `ProviderError`/`RetryHint` → Provider lifecycle error schema; `ProviderId` → Provider `metadata.name` validator; `ProcessRole` variants → Provider component type identifiers).", "integration" : "Zone config publication installs Provider resources; API catalog handler binds exported ResourceTypes; Provider/system-core and Provider/system-minijail are bootstrap exceptions with pre-created records", "removalProof" : "`d2b-realm-provider` trait crate removed per ADR046-zone-control-017 after Provider resource integration", "reuseAction" : "adapt", @@ -8086,66 +8146,6 @@ "specPath" : "docs/specs/ADR-046-resources-zone-control.md", "validation" : "Two Zones on one host cannot both claim one GPU/TPM/USB/`/dev/kvm`/passthru NIC/vsock CID/fixed port — second is `duplicateConflict`; security-key and USB implementations resolving the same physical token submit byte-identical `physical-usb-backing` tuple keys and the loser receives `physical-usb-backing-conflict` before any effect; Provider-private authority classes/digests cannot bypass the collision; render-node shared admits bounded holders; per-Guest swtpm exclusive and marker never wiped; host-global adoption by `ownerProof`; hardware D096 exportability (GPU/KVM/TPM/store/macvtap non-exportable; semantic USB policy-gated); fast hermetic with fakes", "workItemId" : "ADR046-zone-control-024" - }, - { - "currentSource" : "main `a1cc0b2d`: `packages/d2b-client/src/client.rs` (`WallClock`, `MetadataInput`, `RetryPolicy` 1..8 attempts, `CallOptions`, `CancellationToken`, `Client::new/with_clock/connect`, `ConnectedClient` methods incl. `session_generation/session_limits/service/invoke/invoke_with_attachments/named_stream/open_server_stream`, `prepare_typed_request/prepare_operation_context`, `can_retry/retryable_failure/validate_outbound_attachments/validate_reply/service_package/map_remote_kind/map_retry` — lines 35–921); `packages/d2b-client/src/session.rs` (`ComponentSessionConnector` trait, `NamedStream` lifecycle, `StreamDispatcher`, `SharedDriver`, aggregate-queue-bound test — lines 24–626); `packages/d2b-client/src/target.rs` (`ServiceOwner`, `TargetInput`, `TransportKind`, `RouteRecord`, `ResolvedTarget`, `TargetResolver`, `RouteTable` — lines 7–228); `packages/d2b-client/src/service.rs` (`ServiceKind`, `GeneratedClient`, `MethodHandle`, `ServiceHandle::new/kind/generated/proxy/method/invoke` — lines 21–184); `packages/d2b-client/src/daemon_service.rs` (`DaemonClient::new/session_generation/connected/resolve/inspect/lifecycle/open_terminal`, `DaemonTerminal`, `daemon_call_options`, `ensure_daemon_outcome`, `map_ttrpc_error`, test `redacted_terminal_debug_payload` — lines 29–689); `packages/d2b-client/src/host_socket.rs` (`HostSocketConnector::new/from_seqpacket_fd`, `local_daemon_endpoint_identity`, `ComponentSessionConnector::connect` — lines 252–383); `packages/d2b-client/src/error.rs` (`RemoteErrorKind`, `RetryClass`, `ClientError` — lines 5–128)", - "dataMigration" : "Not applicable; updated in place", - "dependencyOwner" : "ADR046-zone-control-011, ADR046-zone-control-012, ADR046-zone-control-013, ADR046-zone-control-018", - "destination" : "`packages/d2b-client/src/` (updated for v3 Zone API, replacing ADR45 daemon verbs with Zone resource operations)", - "detailedDesign" : "**Selected**: `Client::connect()` target-resolve → ComponentSession-open → `ConnectedClient` lifecycle; `RetryPolicy` 1..8 bound + `retryable_failure()` safe-only retry detection; `NamedStream::send/receive/close_local/reset` lifecycle; `ComponentSessionConnector` trait as connector abstraction; `HostSocketConnector::from_seqpacket_fd` + `local_daemon_endpoint_identity` for Zone runtime socket connector; `RouteTable` ambiguous-route rejection; `ServiceHandle`/`GeneratedClient`/`MethodHandle` typed service client pattern; `map_ttrpc_error`/`validate_reply`/`map_retry` error-handling chain; `ClientError`/`RemoteErrorKind`/`RetryClass` error taxonomy; `DaemonClient` call-options and outcome helpers (infrastructure only). **Excluded ADR45 assumptions**: `DaemonMethod` enum (lines 29–56 of daemon_service.rs): ADR45 daemon verbs (`vm_start`, `vm_stop`, `list_realms`, etc.) — replaced with Zone API verbs. `GuestClient`/`guest_service.rs`: ADR45 guest operations; excluded until v3 Guest transport work item. Hardcoded socket path `PUBLIC_SOCKET_PATH = \"/run/d2b/public.sock\"` in `host_socket.rs`: replaced by Zone-resource-managed path. `TransportKind::LocalUnix` restriction in daemon-access: v3 allows multiple transport kinds per ZoneLink binding. Primary reuse disposition: `adapt`. Preserved source-plan detail: copy + adapt.", - "integration" : "`d2b` CLI uses `d2b-client` to connect to Zone runtime via `HostSocketConnector`; a child Zone runtime uses `d2b-client` for its allocator-bound uplink while the parent route engine uses the established session for child calls; Provider toolkit conformance tests use `Fixture`/`FakeProvider` with `d2b-client` service handles", - "removalProof" : "`DaemonMethod` v2 verb enum retired after all v2 daemon operations migrated to Zone API", - "reuseAction" : "adapt", - "reuseSource" : null, - "specId" : "ADR-046-resources-zone-control", - "specPath" : "docs/specs/ADR-046-resources-zone-control.md", - "validation" : "`client-retry-policy-max-8-attempts-enforced`, `client-named-stream-close-local-then-remote-close-transitions-closed`, `client-route-table-ambiguous-route-rejected`, `client-host-socket-peer-uid-verified-on-connect`, `client-retryable-failure-only-safe-mutations`", - "workItemId" : "ADR046-zone-control-025" - }, - { - "currentSource" : "`packages/d2b-contract-tests/tests/policy_contracts.rs` lines 5–6 (D2B_FIXTURES gate / workspace-checks integration pattern — `implemented-and-reachable`, baseline `b5ddbed6`); `packages/d2b-contract-tests/tests/static_invariants.rs` (hermetic policy test structure — `implemented-and-reachable`); `tests/tools/rust-workspace-checks.sh` (D2B_FIXTURES step shell harness — `implemented-and-reachable`); AGENTS.md \"Naming conventions\" section (`-` workspace sort rules — `implemented-and-reachable`); `packages/d2b-realm-core/src/ids.rs` `LABEL_PATTERN` / `MAX_ID_LEN` (name regex reused for crate name token validation — `implemented-and-reachable`)", - "dataMigration" : "Additive; no existing `d2b-provider-*` crates in the pre-ADR45 baseline; first Provider crate created must comply from inception", - "dependencyOwner" : "ADR046-zone-control-003; workspace policy owner", - "destination" : "`packages/d2b-contract-tests/tests/policy_provider_crate_layout.rs` (new file; gated under D2B_FIXTURES in existing `tests/tools/rust-workspace-checks.sh`)", - "detailedDesign" : "Implement `policy_provider_crate_layout.rs` with the following test functions: (1) `every_provider_crate_has_src` — walk `packages/d2b-provider-*/` directories in the workspace, assert each contains `src/`; failure names crate and missing path; (2) `every_provider_crate_has_tests` — assert `tests/` present; (3) `every_provider_crate_has_integration` — assert `integration/` present; (4) `every_provider_crate_has_readme` — assert `README.md` present; (5) `every_provider_readme_has_required_sections` — read `README.md`, check for all nine section headings from §4.8.3 (case-insensitive, after stripping `#` and whitespace); failure names the missing heading(s); (6) `every_integration_file_has_target_declaration` — for each `integration/*.rs` file, scan first 20 lines for exactly one `//! integration-target: (container|host-integration)` declaration; failure names the file and the violation (missing/multiple/invalid value); (7) `non_provider_crates_exempt` — verify the check does not run on non-`d2b-provider-*` crates. All checks are filesystem-only (no compilation). Workspace member list is discovered by parsing `packages/Cargo.toml` `[workspace].members`. Gate: add the new test file to `tests/tools/rust-workspace-checks.sh` D2B_FIXTURES list alongside existing policy tests", - "integration" : "`make test-policy` and `make check` both fail if any provider crate violates §4.8; consistent with existing `no-bash-ast-walker` and workspace-sort gates; ADR046-zone-control-003 references §4.8 for Provider package conventions", - "removalProof" : "No existing code removed; additive policy test only", - "reuseAction" : "create", - "reuseSource" : null, - "specId" : "ADR-046-resources-zone-control", - "specPath" : "docs/specs/ADR-046-resources-zone-control.md", - "validation" : "§15.3 layout conformance tests: `provider-crate-layout-src-required`, `provider-crate-layout-tests-required`, `provider-crate-layout-integration-required`, `provider-crate-layout-readme-required`, `provider-readme-sections-all-present`, `provider-readme-sections-partial-missing`, `provider-integration-target-declared`, `provider-integration-target-unique`, `provider-integration-target-valid-values`, `provider-crate-naming-convention`, `provider-crate-layout-non-provider-exempt`", - "workItemId" : "ADR046-zone-control-026" - }, - { - "currentSource" : "main `a1cc0b2d`: `packages/d2b-gateway-runtime/src/provider_agent.rs` (`ProviderAgentError::{UnregisteredAdapter,RegistryNotAccepting,RegistrationRejected,InvalidAuditCapacity,SessionClosed,ProtocolViolation}`, `ProviderAgentAuditOutcome`, `ProviderAgentAuditEvent`, `ProviderAgentProcess::from_registry/from_registry_with/provider_type/service_names/audit_snapshot/serve`, `run_registered`, bounded in-memory audit ring, frame dispatch loop: semaphore in-flight limit, service/method routing, negative-timeout guard, `SessionClosed` termination, `ProtocolViolation` audit + terminate — lines 31–452; tests `standalone_entrypoint_fails_without_registration`, `audit_capacity_is_bounded` — lines 454–486); `packages/d2b-contracts/src/provider_registry_v2.rs` (`ProviderRegistryV2` wire contract, `ProviderRegistryEntryV2::validate` with provider-id derivation rule, schema fingerprint, scope-digest, generation exactness, `TrustedFirstPartyInProcess` placement requirement, `ProviderIntentId` label rules `max 128 bytes`, `MAX_PROVIDER_REGISTRY_ENTRIES`, `MAX_PROVIDER_MAPPING_IDS=64`, `ProviderBindingV2` non-exhaustive + `UnsupportedProviderBindingV2` fallback, `ProviderRegistryV2::validate` sort/unique/count checks — lines 23–566; tests `validates_closed_local_runtime_mapping`, `validates_closed_local_observability_mapping`, `serializes_declared_mapping_axes_as_closed_variants`, `rejects_duplicate_or_unbounded_mapping_ids`, `local_storage_binding_realm_must_match_descriptor_placement`, `rejects_generation_and_exact_identity_mismatches`, `contradictory_binding_realm_json_is_unrepresentable`, `unknown_binding_axis_remains_rejected_on_the_wire`, `identity_mismatch_messages_name_the_failed_contract`, `accepts_explicit_empty_registry` — lines 722–1044)", - "dataMigration" : "Not applicable; new implementation", - "dependencyOwner" : "ADR046-zone-control-011, ADR046-zone-control-017, ADR046-zone-control-018", - "destination" : "`packages/d2b-provider/src/agent.rs` (v3 provider agent dispatch); `packages/d2b-contracts/src/v3/provider_registry.rs` (v3 provider registry wire contract)", - "detailedDesign" : "**Selected**: `ProviderAgentProcess::serve` dispatch loop with semaphore in-flight limit; unsupported-service/method → ttrpc error; negative-timeout rejection; `SessionClosed` clean termination; `ProtocolViolation` audit-and-terminate path; bounded audit ring; `GeneratedProviderServiceServer` single-service-per-agent requirement; `ProviderRegistryV2` entry validation: provider-id derivation, schema fingerprint, scope digest, generation exactness; `MAX_PROVIDER_MAPPING_IDS=64` → Provider component mapping bound; `ProviderBindingV2` non-exhaustive + explicit `UnsupportedProviderBindingV2` fallback (never panics on unknown axis); `ProviderIntentId` `max 128 bytes` label rules → Provider component `spec.intentRef`; all 10 `provider_registry_v2.rs` tests ported. **Excluded ADR45 assumptions**: `aca_workload.rs` (`AcaGatewayWorkload`): ADR45 ACA external provider adapter, excluded entirely. `waypipe_display.rs` (`WaypipeDisplayProvider`): ADR45 display provider, excluded. `ProviderRegistryV2.registry_generation` / `configuration_fingerprint` bind to ADR45 bundle generation; v3 Provider resource version is tracked in redb store, not a JSON bundle. `run()` binary entrypoint uses the fixed `d2b-provider-agent` command; v3 provider processes use normal Zone runtime Process launch. `TrustedFirstPartyInProcess` is the only placement in v2; v3 Provider resources extend to `HostLocal`/`GatewayVm` etc. Primary reuse disposition: `adapt`. Preserved source-plan detail: copy + adapt.", - "integration" : "Zone runtime spawns each Provider component Process via normal Process launch; Process binary calls `ProviderAgentProcess::from_registry` then `serve()` on established ComponentSession; on `SessionClosed` the process exits and Zone runtime observes `status.phase` transition", - "removalProof" : "Not applicable; new implementation", - "reuseAction" : "adapt", - "reuseSource" : null, - "specId" : "ADR-046-resources-zone-control", - "specPath" : "docs/specs/ADR-046-resources-zone-control.md", - "validation" : "`provider-agent-dispatch-unsupported-service-returns-ttrpc-error`, `provider-agent-negative-timeout-rejected`, `provider-agent-session-closed-terminates-serve-loop`, `provider-agent-audit-ring-capacity-bounded` (ported from `audit_capacity_is_bounded`), `provider-registry-entry-fingerprint-generation-exact` (ported from `rejects_generation_and_exact_identity_mismatches`), `provider-registry-unknown-axis-fallback-non-exhaustive` (ported from `unknown_binding_axis_remains_rejected_on_the_wire`), `provider-registry-duplicate-ids-rejected` (ported from `rejects_duplicate_or_unbounded_mapping_ids`)", - "workItemId" : "ADR046-zone-control-027" - }, - { - "currentSource" : "main `a1cc0b2d`: `packages/d2b-contracts/src/v2_services.rs` (`MethodSpec{mutating,stream_kind,...}`, `ServiceSpec`, `SERVICE_INVENTORY` covering 20+ services and all provider services, `service_schema_fingerprint`, `public_daemon_schema_fingerprint`, `direct_guest_schema_fingerprint`, `StrictWireMessage`, `decode_strict`, `encode_strict`, `admit_metadata`, `TerminalStreamValidator`, `ServerStreamLease`, `RedactedTerminalFrame`, stream-name validators — lines 204–1004; tests `public_endpoint_fingerprint_binds_both_services_dependencies_and_order`, `public_endpoint_fingerprint_binds_daemon_and_guest_method_descriptors`, `direct_guest_fingerprint_binds_activation_and_remains_separate_from_public_endpoint` — lines 872–1030); `packages/d2b-contracts/src/v2_state.rs` (constants: `STATE_SCHEMA_VERSION=2`, `STATE_SCHEMA_GENERATION=1`, `MAX_JSON_DOCUMENT_BYTES=1_048_576`, `MAX_INVENTORY_ROWS=4096`, `MAX_LOCKS=1024`, `MAX_LOCK_DEPENDENCIES=32`, `MAX_DISCOVERY_OBSERVATIONS=4096`, `MAX_AUDIT_RECORD_BYTES=8192`, `MAX_AUDIT_RECORDS_PER_SEGMENT=16384`, `MAX_AUDIT_SEGMENT_BYTES=64*1024*1024`, `MAX_AUDIT_RETENTION_DAYS=14`, `MAX_LOCK_DEADLINE_MS=300_000`; types: `Digest`, `Generation`, `OwnershipEpoch`, `SafeJsonInteger`, `StorageCategory`, `StateEnvelope`, `CanonicalPayloadVerifier`, `AtomicWritePhase`, `RunnerEvidence`, audit types incl. `AuditRecord/Segment/Checkpoint/Gap`, `detect_audit_gap`, `AuditRetentionPolicy`, lock types: `LockClass`, `LockSpec`, `SyncInventory`, `LeaseRecord`); `packages/d2b-contracts/src/v2_identity.rs` (`IdentityError` 13 variants, canonical name rules `^[a-z][a-z0-9-]*$` max 63 bytes start-lowercase-letter, `RealmPath` label/separator rules, `ProviderType::ALL` 11 types + `as_str()` — lines 11–250); `packages/d2b-contracts/src/v2_provider.rs` (bounded opaque IDs `[a-z][a-z0-9-]{0,63}`: `ImplementationId`, `OperationId`, `IdempotencyKey`, `PlanId`, `HandleId`, `LeaseId`, `TransferId`, `PROVIDER_CONTRACT_FINGERPRINT`, `ProviderContractError` 34 variants, `Fingerprint` 64 lowercase-hex chars — lines 18–219); `packages/d2b-contracts/src/generated_v2_services/` (all 40+ generated ttrpc client/server stubs for v2 services)", - "dataMigration" : "v2 and v3 wire type namespaces coexist in the same contracts crate; no migration", - "dependencyOwner" : "ADR046-zone-control-013", - "destination" : "`packages/d2b-contracts/src/v3/{services,state,identity,provider}.rs` (new v3 namespace); generated stubs regenerated from v3 proto files in `packages/d2b-contracts/proto/v3/`", - "detailedDesign" : "**Selected**: `MethodSpec`/`ServiceSpec`/fingerprinting infrastructure → v3 Zone API service schema fingerprinting; `StrictWireMessage`/`decode_strict`/`encode_strict`/`admit_metadata` → v3 wire decode/admit for all resource API requests; `CanonicalPayloadVerifier` payload-digest binding → v3 resource store integrity checks; audit chain types `AuditRecord`/`AuditSegment`/`AuditCheckpoint`/`detect_audit_gap` + `MAX_AUDIT_RETENTION_DAYS=14`/`MAX_AUDIT_RECORD_BYTES=8192`/`MAX_AUDIT_SEGMENT_BYTES=64MiB` → v3 Zone audit (§13.3); lock types `LockSpec`/`SyncInventory`/`LeaseRecord` + `MAX_LOCKS=1024`/`MAX_LOCK_DEADLINE_MS=300_000` → v3 resource store lock layer; bounded opaque ID pattern `[a-z][a-z0-9-]{0,63}` → v3 `OperationId`/`HandleId`/`PlanId`/`LeaseId`; `Fingerprint` 64-hex-char → v3 Provider `spec.configFingerprint`; `ProviderContractError` 34 variants → v3 Provider operation error taxonomy; canonical name constraint `^[a-z][a-z0-9-]*$` max 63 bytes → same as `ids.rs::is_label()` shared validator (§16.2). **Excluded ADR45 assumptions**: `RealmLabel`/`WorkloadName`/`RealmPath` identity types: ADR45 workload/realm address format; replaced by `metadata.name` + Zone `ResourceRef`. `ProviderType::ALL` fixed 11-type closed enum: v3 Provider type is an open string field in the Provider resource spec. `STATE_SCHEMA_VERSION=2`/`STATE_SCHEMA_GENERATION=1`: v3 store schema uses redb table versioning, not a JSON schema version field. v2 service fingerprint tests reference ADR45-specific proto files; v3 fingerprints use different proto inputs but the same `service_schema_fingerprint` seeding mechanism. Generated stubs in `generated_v2_services/` are v2-specific and excluded; v3 uses regenerated stubs from `proto/v3/`.", - "integration" : "v3 Zone API service layer uses `MethodSpec.mutating` + `StrictWireMessage` for admission and fingerprinting; `v3/state.rs` `Digest`/`StateEnvelope` integrate with redb store writes; `CanonicalPayloadVerifier` validates resource payloads loaded from store; audit types feed §13.3 Zone audit segment; lock types wire into ADR 0034 lock lifecycle", - "removalProof" : "`d2b-contracts/src/v2_{services,state,identity,provider}.rs` and `v2_component_session.rs` deprecated in contracts crate after all v2 clients decommissioned", - "reuseAction" : "adapt", - "reuseSource" : null, - "specId" : "ADR-046-resources-zone-control", - "specPath" : "docs/specs/ADR-046-resources-zone-control.md", - "validation" : "`v3-canonical-name-matches-ids-is-label-regex`, `v3-service-fingerprint-changes-on-method-mutation` (behavior ported from `public_endpoint_fingerprint_binds_daemon_and_guest_method_descriptors`), `v3-audit-gap-detection-covers-missing-segment`, `v3-strict-wire-rejects-unknown-fields`, `v3-state-envelope-digest-mismatch-rejected`, `v3-canonical-payload-verifier-binding-holds-under-mutation`", - "workItemId" : "ADR046-zone-control-028" } ], "schemaVersion" : 1, diff --git a/docs/specs/README.md b/docs/specs/README.md index 95e654497..a0c4a282f 100644 --- a/docs/specs/README.md +++ b/docs/specs/README.md @@ -96,11 +96,12 @@ generator. The current candidate was produced with a disposable script that was removed after regeneration; `ADR046-delivery-004` and `ADR046-delivery-009` own the future generator and fail-closed policy tests. -- `ADR-046-spec-set.json` (`artifactKind: d2b-adr-spec-set`, `schemaVersion` 2) +- `ADR-046-spec-set.json` (`artifactKind: d2b-adr-spec-set`, `schemaVersion` 3) binds the exact 55 member files: for each member, its `specId`, `path`, `status`, `version`, resolved `dependsOn` edges (the `ADR-046-provider-*` dependency glob is expanded to every Provider dossier), `supersedes`, - registered `workItemPrefix` (or `null` for a member with no items), and the + sorted `workItemPrefixes` registry (an empty array for a member with no + items), and the lowercase SHA-256 of the exact Markdown bytes. It records the parent path and the `v3` baseline commit and carries no timestamp or host path. - `ADR-046-work-items.json` (`artifactKind: d2b-adr-work-items`, `schemaVersion` @@ -295,7 +296,7 @@ Each spec contains an **Implementation work items** section. Every item has: | Field | Requirement | | --- | --- | -| Work item ID | Declared by the heading as `ADR046--`; an optional table row must match it exactly | +| Work item ID | Declared by the heading as `ADR046--`; an optional table row must match it exactly | | Dependency/owner | Prerequisites, future wave, crate/component, shared owner | | Current source | Exact v3 paths, symbols, call sites, artifacts, and tests | | Reuse source | Optional exact main commit/paths/symbols/tests used for copy/adaptation; explicit `None` serializes as `null` | @@ -309,18 +310,20 @@ Each spec contains an **Implementation work items** section. Every item has: The exact work-item ID regex is `^ADR046-[a-z0-9]+(?:-[a-z0-9]+)*-(?:00[1-9]|0[1-9][0-9]|[1-9][0-9]{2})$`. -`` is the owning member's registered stable prefix below; it is -not required to equal the full Spec ID suffix. `` is a three-digit -value from `001` through `999`. Prefixes are globally unique and never inferred -from a filename. - -| Normative member | Registered `workItemPrefix` | +`` is one entry in the owning member's +`workItemPrefixes` registry below; it is not required to equal the full Spec ID +suffix. `` is a three-digit value from `001` through `999`. A member +that owns work items registers a nonempty bytewise-sorted prefix list. Every +prefix is globally unique to exactly one member and is never inferred by +splitting an ID, Spec ID, or filename. + +| Normative member | Registered `workItemPrefixes` | | --- | --- | | `ADR-046-cli-and-operations` | `cli` | | `ADR-046-components-processes-and-sandbox` | `process` | -| `ADR-046-componentsession-and-bus` | `session` | +| `ADR-046-componentsession-and-bus` | `bus`, `session` | | `ADR-046-core-controllers` | `core` | -| `ADR-046-current-code-migration-map` | — | +| `ADR-046-current-code-migration-map` | `[]` | | `ADR-046-decision-register` | `decisions` | | `ADR-046-feasibility-and-spikes` | `feasibility` | | `ADR-046-nix-configuration` | `nix` | @@ -329,7 +332,7 @@ from a filename. | `ADR-046-provider-audio-pipewire` | `audio` | | `ADR-046-provider-clipboard-wayland` | `clipboard` | | `ADR-046-provider-credential-entra` | `cred-entra` | -| `ADR-046-provider-credential-managed-identity` | `cred-mi` | +| `ADR-046-provider-credential-managed-identity` | `cred-mi`, `mi-topology` | | `ADR-046-provider-credential-secret-service` | `cred-ss` | | `ADR-046-provider-device-gpu` | `gpu` | | `ADR-046-provider-device-security-key` | `security-key` | @@ -353,7 +356,7 @@ from a filename. | `ADR-046-provider-transport-unix` | `transport-unix` | | `ADR-046-provider-transport-vsock` | `vsock` | | `ADR-046-provider-volume-local` | `vl` | -| `ADR-046-provider-volume-virtiofs` | `vvfs` | +| `ADR-046-provider-volume-virtiofs` | `vvfs`, `vvfs-export` | | `ADR-046-reset-and-cutover` | `reset` | | `ADR-046-resource-api-and-authorization` | `api` | | `ADR-046-resource-object-model` | `object` | @@ -361,17 +364,22 @@ from a filename. | `ADR-046-resource-store-redb` | `store` | | `ADR-046-resources-credential` | `credential` | | `ADR-046-resources-device` | `device` | -| `ADR-046-resources-host-guest-process-user` | `exec` | +| `ADR-046-resources-host-guest-process-user` | `exec`, `user-session` | | `ADR-046-resources-network` | `network` | | `ADR-046-resources-volume` | `volume` | -| `ADR-046-resources-zone-control` | `zone-control` | +| `ADR-046-resources-zone-control` | `client`, `pkg`, `provider-agent`, `wire`, `zone-control` | | `ADR-046-security-and-threat-model` | `security` | | `ADR-046-streamline` | `streamline` | -| `ADR-046-telemetry-audit-and-support` | `telem` | +| `ADR-046-telemetry-audit-and-support` | `audit`, `doctor`, `host-posture`, `reuse`, `telem` | | `ADR-046-terminology-and-identities` | `identities` | | `ADR-046-validation-and-delivery` | `delivery` | | `ADR-046-zone-routing` | `routing` | +The registry also resolves the two formerly shared prefixes: `bus` belongs +only to `ADR-046-componentsession-and-bus`, so Nix integration items use +`nix`; `network` belongs only to `ADR-046-resources-network`, so +`ADR-046-provider-network-local` items use `nl`. + `reuseAction` is a closed scalar vocabulary: | Value | Disposition | @@ -391,16 +399,17 @@ separate items. Generation and validation fail closed unless every normative member's Implementation work items section is complete: -- every work-item heading matches the ID regex, uses its owning registered - prefix, and is unique across the set; +- every work-item heading matches the ID regex, uses one prefix registered by + its owning member, and is unique across the set; - every item has exactly one nonempty `Dependency/owner`, `Current source`, `Reuse action`, `Destination`, `Detailed design`, `Integration`, `Data migration`, `Validation`, and `Removal proof` field, with no duplicate fields; an optional `Work item ID` row exactly matches its heading and an optional `Reuse source` is nonempty; -- every heading prefix exactly matches the owning member's registered - `workItemPrefix`, every registered prefix is globally unique, and a member - with no work items has a `null` prefix in the generated spec set; +- every heading prefix appears in the owning member's bytewise-sorted + `workItemPrefixes`; every registered prefix belongs globally to exactly one + member, and a member with no work items has an empty array in the generated + spec set; - an absent or explicit-none Reuse source serializes as `null`; `create` requires that null value; - every Markdown item appears exactly once in `ADR-046-work-items.json`, every diff --git a/docs/specs/providers/ADR-046-provider-credential-managed-identity.md b/docs/specs/providers/ADR-046-provider-credential-managed-identity.md index 74b54ff3b..421b8f7ee 100644 --- a/docs/specs/providers/ADR-046-provider-credential-managed-identity.md +++ b/docs/specs/providers/ADR-046-provider-credential-managed-identity.md @@ -1764,7 +1764,7 @@ endpoint-role, and provider-factory code is explicitly excluded. ## Work items -### ADR046-cred-mi-005 (agent process split) +### ADR046-mi-topology-001 (agent process split) | Field | Value | | --- | --- | @@ -1783,12 +1783,12 @@ endpoint-role, and provider-factory code is explicitly excluded. | Field | Value | | --- | --- | | Work item ID | `ADR046-cred-mi-001` | -| Dependency/owner | `ADR046-credential-001` (contracts), `ADR046-credential-002` (service); `ADR046-reconcile-001`; `ADR046-cred-mi-005`; `credential-managed-identity` crate owner | +| Dependency/owner | `ADR046-credential-001` (contracts), `ADR046-credential-002` (service); `ADR046-reconcile-001`; `ADR046-mi-topology-001`; `credential-managed-identity` crate owner | | Current source | `packages/d2b-realm-provider/src/credential.rs:ManagedIdentityRef` (reachable); `packages/d2b-provider-aca/src/lib.rs:managed_identity_client_id` line 112 (reachable ACA config); `packages/d2bd/src/lib.rs:managed_identity_client_id` lines 3960, 4173 (reachable) | | Reuse source | main `a1cc0b2d`: `packages/d2b-provider-credential-managed-identity/src/lib.rs` (full implementation); `src/tests.rs` (full test suite) | | Reuse action | adapt | | Destination | `packages/d2b-provider-credential-managed-identity/src/{lib.rs, controller.rs, agent.rs, service.rs, audit.rs, telemetry.rs}`; `packages/d2b-provider-credential-managed-identity/{controller/main.rs, agent/main.rs}`; `packages/d2b-provider-credential-managed-identity/tests/{lifecycle.rs, conformance.rs, faults.rs, canary.rs, delivery.rs, placement.rs, topology.rs}`; `packages/d2b-provider-credential-managed-identity/integration/{container-service.sh, host-guest-placement.nix, aca-credential-ref.sh, cleanup-rollback.sh}`; `packages/d2b-provider-credential-managed-identity/README.md` | -| Detailed design | (1) Adapt `ManagedIdentityCredentialProvider` to `d2b.credential.v3` service interface; split controller and agent roles (see `ADR046-cred-mi-005`). (2) Enforce `ManagedIdentityCredentialOwner::ExactSdkConsumer` in agent via `AuthenticatedSubjectContext` from ComponentSession, independently of scope fields. (3) Reject `user-agent` placement: `scope.domainFilter=user` returns `credential-placement-mismatch` before agent spawn. (4) Validate `clientId` using `OpaqueAzureRef::parse` from v3 baseline; artifact IDs match `^[a-z][a-z0-9-]*$`. (5) Validate `imdsEndpointAlias` against closed enum `{azure-imds, azure-imds-aca}`; project into LaunchTicket at spawn time (never into Process spec config or env); co-located runtime Provider constructs `ManagedIdentityCredentialClient` from LaunchTicket projection and supplies via effect port; resolved URL never in any output surface. (6) Agent Process declares `networkUsage.allowEgress=false`; uses canonical Process template shape (see `ADR046-cred-mi-005` design item 6). (7) Reject `sign-challenge` with `credential-schema-invalid` immediately. (8) Map `ManagedIdentityClientState::Unavailable` to `credential-provider-unavailable`; no `InteractionRequired` state. (9) Implement `ManagedIdentityLeaseHandle` as opaque bounded newtype with redacted `Debug`. (10) All token bytes held by injected client in agent; delivered only via agent-terminated `Noise_KK` delivery session. (11) Integrate with Provider resource descriptor and controller toolkit. (12) Confirm `credential_canary` never appears in any service response, status field, delivery record outer header, or audit record. (13) Apply D087 status-first state: declare no Provider state Volume, keep ProviderStateSet empty, and write only bounded non-secret lease observation to `Credential.status` plus the Operation ledger. Primary reuse disposition: `adapt`. Preserved source-plan detail: copy and adapt. | +| Detailed design | (1) Adapt `ManagedIdentityCredentialProvider` to `d2b.credential.v3` service interface; split controller and agent roles (see `ADR046-mi-topology-001`). (2) Enforce `ManagedIdentityCredentialOwner::ExactSdkConsumer` in agent via `AuthenticatedSubjectContext` from ComponentSession, independently of scope fields. (3) Reject `user-agent` placement: `scope.domainFilter=user` returns `credential-placement-mismatch` before agent spawn. (4) Validate `clientId` using `OpaqueAzureRef::parse` from v3 baseline; artifact IDs match `^[a-z][a-z0-9-]*$`. (5) Validate `imdsEndpointAlias` against closed enum `{azure-imds, azure-imds-aca}`; project into LaunchTicket at spawn time (never into Process spec config or env); co-located runtime Provider constructs `ManagedIdentityCredentialClient` from LaunchTicket projection and supplies via effect port; resolved URL never in any output surface. (6) Agent Process declares `networkUsage.allowEgress=false`; uses canonical Process template shape (see `ADR046-mi-topology-001` design item 6). (7) Reject `sign-challenge` with `credential-schema-invalid` immediately. (8) Map `ManagedIdentityClientState::Unavailable` to `credential-provider-unavailable`; no `InteractionRequired` state. (9) Implement `ManagedIdentityLeaseHandle` as opaque bounded newtype with redacted `Debug`. (10) All token bytes held by injected client in agent; delivered only via agent-terminated `Noise_KK` delivery session. (11) Integrate with Provider resource descriptor and controller toolkit. (12) Confirm `credential_canary` never appears in any service response, status field, delivery record outer header, or audit record. (13) Apply D087 status-first state: declare no Provider state Volume, keep ProviderStateSet empty, and write only bounded non-secret lease observation to `Credential.status` plus the Operation ledger. Primary reuse disposition: `adapt`. Preserved source-plan detail: copy and adapt. | | Integration | Agent `Process` resource under `Host` or `Guest` executionRef; controller `Process` resource at Zone system host; d2b-bus routes `d2b.credential.v3` token-delivery calls to agent; Credential controller reconciles status; ACA `Provider/runtime-azure-container-apps` holds `credentialRef` pointing to a `credential-managed-identity`-backed Credential resource | | Data migration | Full v3 reset. `d2b-provider-aca:managed_identity_client_id` raw field migrated to a Credential resource reference in the v3 ACA Provider config; see removal precondition below. | | Validation | See §Test matrix | @@ -1798,7 +1798,7 @@ endpoint-role, and provider-factory code is explicitly excluded. | Field | Value | | --- | --- | -| Dependency/owner | ADR046-credential-001, ADR046-credential-002; ADR046-reconcile-001, ADR046-reconcile-002; ADR046-cred-mi-005; owner: Credential controller toolkit and managed-identity controller | +| Dependency/owner | ADR046-credential-001, ADR046-credential-002; ADR046-reconcile-001, ADR046-reconcile-002; ADR046-mi-topology-001; owner: Credential controller toolkit and managed-identity controller | | Current source | `packages/d2b-realm-provider/src/provider.rs:CredentialProvider` status/enrollment-only trait; main `a1cc0b2d` managed-identity controller/test behavior listed in §Source reuse | | Reuse action | adapt | | Destination | packages/d2b-provider-credential-managed-identity/src/controller.rs; packages/d2b-contracts/src/v3/credential_controller.rs | @@ -1830,7 +1830,7 @@ item. | Field | Value | | --- | --- | -| Dependency/owner | Depends on ADR046-cred-mi-001 and ADR046-cred-mi-005; owner: credential-managed-identity audit/telemetry implementation | +| Dependency/owner | Depends on ADR046-cred-mi-001 and ADR046-mi-topology-001; owner: credential-managed-identity audit/telemetry implementation | | Current source | `packages/d2b-realm-provider/src/error.rs:contains_sensitive_shape`; `packages/d2b-core/src/realm_workloads_launcher.rs:LauncherMetadataInvariants.no_secrets_or_credentials`; main `a1cc0b2d` managed-identity canary tests listed in §Source reuse | | Reuse action | adapt | | Destination | packages/d2b-provider-credential-managed-identity/src/{audit.rs,telemetry.rs}; packages/d2b-contract-tests/tests/credential_audit.rs | @@ -2091,7 +2091,7 @@ Old and new suites never run in parallel indefinitely. | Behavior retained | `OpaqueAzureRef` charset/length validation fail-closed on `clientId`; injected-client pattern keeps token material in the client process; zero-secret-bytes invariant at config/audit/log boundary; `ExactSdkConsumer` ownership model; `contains_sensitive_shape` guard | | Required delta | Full `ManagedIdentityCredentialProvider` implementation; split `managed-identity-controller` (no IMDS) + `managed-identity-agent` (IMDS client, KK delivery); `d2b.credential.v3` service dispatch split by role; agent spawn/teardown lifecycle in controller reconcile; `Noise_KK` delivery channel terminated by agent; Provider resource/descriptor; agent Process template; `imdsEndpointAlias` closed-alias config; `ExactSdkConsumer` via `AuthenticatedSubjectContext`; D087 status-first state model with no Provider state Volume, empty ProviderStateSet, status/Operation-ledger lease observation, and transient-only token bytes; Nix eval assertions; controller finalizer-release-only deletion path; Core-written event-only Deleted revision; audit subsystem deletion record with exactly-once dedup; audit/OTEL; `aca-credential-ref` migration | | Feasibility proof | Main `a1cc0b2d` proves the `ManagedIdentityCredentialProvider` implementation; its `FakeClient` test suite covers acquire/refresh/revoke/inspect, idempotency, unavailable state, canary enforcement, `ExactSdkConsumer` enforcement, and lease cardinality | -| Future owner | `ADR046-cred-mi-005` (process split), `ADR046-cred-mi-001` (primary), `ADR046-cred-mi-002`, `ADR046-cred-mi-003`, `ADR046-cred-mi-004` | +| Future owner | `ADR046-mi-topology-001` (process split), `ADR046-cred-mi-001` (primary), `ADR046-cred-mi-002`, `ADR046-cred-mi-003`, `ADR046-cred-mi-004` | --- diff --git a/docs/specs/providers/ADR-046-provider-notification-desktop.md b/docs/specs/providers/ADR-046-provider-notification-desktop.md index a2479922a..2f7ed6377 100644 --- a/docs/specs/providers/ADR-046-provider-notification-desktop.md +++ b/docs/specs/providers/ADR-046-provider-notification-desktop.md @@ -1345,7 +1345,7 @@ The v2 `d2b.notify.v2.NotifyService` ttrpc contract is superseded by | Field | Value | | --- | --- | -| Dependency/owner | ADR046-session-001, ADR046-session-003; session/bus wiring | +| Dependency/owner | ADR046-session-001, ADR046-bus-001; session/bus wiring | | Current source | `packages/d2b-notify/src/services/` | | Reuse action | adapt | | Destination | `packages/d2b-provider-notification-desktop/src/stream_admission.rs` | diff --git a/docs/specs/providers/ADR-046-provider-system-core.md b/docs/specs/providers/ADR-046-provider-system-core.md index 17789b3a0..6c802bbc8 100644 --- a/docs/specs/providers/ADR-046-provider-system-core.md +++ b/docs/specs/providers/ADR-046-provider-system-core.md @@ -1336,10 +1336,10 @@ items do not acquire co-ownership. | --- | --- | --- | --- | | `SC-001` | `ADR046-exec-001` | Owns the Host/User DTOs, schemas, bounds, admission vectors, and shared execution-policy extraction. `ADR046-core-002` is the coordination parent only; it owns no duplicate contract destination. | `ADR046-exec-001` owns removal of the Host/User/ExecutionPolicy portions of `HostJson`, `VmRuntimeRow`, and `WorkloadExecutionPosture` after all consuming resource slices reach parity. | | `SC-002` | `ADR046-exec-003` (Host), `ADR046-exec-004` (User), `ADR046-exec-005` (bootstrap ordering), and `ADR046-system-core-001` (Provider-specific manifest and audit boundary) | `ADR046-core-001` owns only the fixed core-controller process frame. Host and User handlers remain library code in their `ADR046-exec-*` destinations; the parent frame does not reimplement them. | `ADR046-exec-003` and `ADR046-exec-004` own host/group and UID/NSS parity; `ADR046-exec-005` owns retirement of the old daemon initialization sequence. | -| `SC-003` | `ADR046-exec-009` (user-only Host migration and status posture), `ADR046-exec-006`/`ADR046-exec-007` (Process Provider `ProcessEffect` emission), `ADR046-telem-018` (CLI/doctor warning), and `ADR046-telem-008` (OTEL absence gate) | system-core owns `Host.status.isolationPosture` and `NoIsolation`, but never emits `ProcessEffect`. The Process Providers query that status and own launch/stop/adopt/quarantine records. The telemetry gate owns no runtime emitter. | `ADR046-exec-009` owns helper binary/wire/dispatch retirement; `ADR046-nix-010` owns unsafe-local-specific Nix removal. | +| `SC-003` | `ADR046-exec-009` (user-only Host migration and status posture), `ADR046-exec-006`/`ADR046-exec-007` (Process Provider `ProcessEffect` emission), `ADR046-host-posture-001` (CLI/doctor warning), and `ADR046-telem-008` (OTEL absence gate) | system-core owns `Host.status.isolationPosture` and `NoIsolation`, but never emits `ProcessEffect`. The Process Providers query that status and own launch/stop/adopt/quarantine records. The telemetry gate owns no runtime emitter. | `ADR046-exec-009` owns helper binary/wire/dispatch retirement; `ADR046-nix-010` owns unsafe-local-specific Nix removal. | | `SC-004` | `ADR046-exec-012` (Nix resource authoring and eval rules) and `ADR046-exec-014` (schema-generated option modules and Zone bundle emission) | These items own the Nix destinations. system-core consumes the emitted Host/User resources and does not maintain a second emitter or schema vocabulary. | `ADR046-exec-012` owns Realm/Workload option removal; `ADR046-nix-010` owns the unsafe-local-specific Nix migration gate. | | `SC-005` | `ADR046-provider-002` (Provider package shape), `ADR046-exec-003`/`ADR046-exec-004` (system-core crate tests and conformance invocation), `ADR046-exec-020` (shared conformance toolkit), and `ADR046-pstate-011` (workspace layout gate) | The toolkit and policy gate are shared infrastructure. They are not reimplemented in `d2b-provider-system-core`; this crate only supplies its Host/User fixtures and required package paths. | `ADR046-pstate-011` owns the permanent layout gate; there is no system-core removal destination. | -| `SC-006` | `ADR046-telem-004` (core-controller instruments), `ADR046-telem-008` (allowlist/cardinality/redaction policy), `ADR046-telem-012` (shared audit sink and record machinery), and `ADR046-system-core-001` (Host/User `ResourceReconciled` adapter) | system-core contributes only the two closed handler values and its reconcile events. It does not own shared telemetry machinery, policy tests, or `ProcessEffect`. | `ADR046-telem-002` and `ADR046-telem-005` own replacement of their legacy metric families; `ADR046-telem-008` owns the final no-`vm`/no-`unsafe-local` label proof. | +| `SC-006` | `ADR046-telem-004` (core-controller instruments), `ADR046-telem-008` (allowlist/cardinality/redaction policy), `ADR046-audit-001` (shared audit sink and record machinery), and `ADR046-system-core-001` (Host/User `ResourceReconciled` adapter) | system-core contributes only the two closed handler values and its reconcile events. It does not own shared telemetry machinery, policy tests, or `ProcessEffect`. | `ADR046-telem-002` and `ADR046-telem-005` own replacement of their legacy metric families; `ADR046-telem-008` owns the final no-`vm`/no-`unsafe-local` label proof. | Test ownership is enumerated per test in §16. The §14.3 table and the final column above are the complete removal-proof assignment for this dossier. @@ -1349,7 +1349,7 @@ column above are the complete removal-proof assignment for this dossier. | Field | Value | | --- | --- | | Work item ID | `ADR046-system-core-001` | -| Dependency/owner | `ADR046-provider-001`, `ADR046-exec-003`, `ADR046-exec-004`, `ADR046-exec-005`, `ADR046-pstate-012`, `ADR046-telem-001`, and `ADR046-telem-012`; `Provider/system-core` owner | +| Dependency/owner | `ADR046-provider-001`, `ADR046-exec-003`, `ADR046-exec-004`, `ADR046-exec-005`, `ADR046-pstate-012`, `ADR046-telem-001`, and `ADR046-audit-001`; `Provider/system-core` owner | | Current source | No canonical v3 equivalent. Adapt the Provider descriptor pattern from `packages/d2b-realm-provider/src/provider.rs` and the bounded audit-envelope pattern from `packages/d2bd/src/daemon_audit.rs`; do not carry forward daemon topology or unsafe-local helper protocol types. | | Reuse action | adapt | | Destination | `packages/d2b-provider-system-core/src/manifest.rs`, `packages/d2b-provider-system-core/src/audit.rs`, and `packages/d2b-provider-system-core/tests/provider_boundary.rs` | @@ -1430,7 +1430,7 @@ per-test budget. | `host_reconcile_real_zone` | Provider/system-core controller reconciles a real Host resource in a container Zone runtime; phase reaches Ready | `ADR046-exec-003` | | `host_capability_probes_real_host` | Real `kvm`, `pidfd`, `cgroup-v2` capability probes succeed on a KVM-capable test host | `ADR046-exec-003` | | `user_reconcile_real_nss` | Real NSS getpwnam lookup for a declared test user; User reaches Ready | `ADR046-exec-004` | -| `unsafe_local_host_warning_cli` | `d2b zone inspect` renders the no-isolation warning for a user-only Host; warning absent for system-domain Host | `ADR046-telem-018` | +| `unsafe_local_host_warning_cli` | `d2b zone inspect` renders the no-isolation warning for a user-only Host; warning absent for system-domain Host | `ADR046-host-posture-001` | | `user_only_host_isolation_posture_stable` | Under a real Zone runtime, a user-only Host consistently reports `isolationPosture="none"` in status after restart and reconcile cycles | `ADR046-exec-009` | | `provider_system_core_bootstrap_failure_blocks_readiness` | If core cannot create or verify the runtime-owned `Provider/system-core` bootstrap resource, Zone reports Failed with a mandatory-provider condition; no Nix bundle declaration is expected | `ADR046-exec-005` | | `generation_cleanup_host_deleted` | Removing Host from Nix config triggers async Delete; ResourceDeletionRequested audit event present; store transaction removes row/index and writes Deleted revision; ResourceDeleted audit event appended with exactly-once recovery | `ADR046-exec-015` | diff --git a/docs/specs/providers/ADR-046-provider-system-minijail.md b/docs/specs/providers/ADR-046-provider-system-minijail.md index 90025e84d..0dcb4faab 100644 --- a/docs/specs/providers/ADR-046-provider-system-minijail.md +++ b/docs/specs/providers/ADR-046-provider-system-minijail.md @@ -1633,11 +1633,11 @@ delivery assumptions are not copied. | Validation | `tests/lifecycle.rs`; `tests/broker_wait_contract.rs` (only clone3 parent calls waitid/reaps; poll readability cannot supply status); `tests/cgroup_kill_finalize.rs` (setsid descendant and PGID reuse); `tests/redaction.rs` (PID never in log/status/audit); `tests/blocking_adapter.rs` (duplicate/status relay via adapter; timeout → error) | | Removal proof | Old `PidfdTable` in `d2bd` supervisor removed after Provider integration | -### ADR046-minijail-005 (Dependency: ADR046-minijail-002, ADR046-minijail-004, ADR046-session-001, ADR046-session-003) +### ADR046-minijail-005 (Dependency: ADR046-minijail-002, ADR046-minijail-004, ADR046-session-001, ADR046-bus-001) | Field | Value | | --- | --- | -| Dependency/owner | All of ADR046-minijail-001 through ADR046-minijail-004; ComponentSession/d2b-bus (ADR046-session-001, ADR046-session-003); bootstrap authz | +| Dependency/owner | All of ADR046-minijail-001 through ADR046-minijail-004; ComponentSession/d2b-bus (ADR046-session-001, ADR046-bus-001); bootstrap authz | | Current source | `d2bd/src/supervisor/*.rs` (DagExecutor, NodeOutcome); `d2bd/src/supervisor/pidfd_table.rs`; `d2b-realm-core/src/allocator_engine.rs` (adoption/identity concepts) | | Reuse action | adapt | | Destination | `packages/d2b-provider-system-minijail/src/` — controller binary entry point; reconcile loop; adoption; quarantine; bootstrap authz; health/status; restart; finalize | diff --git a/docs/specs/providers/ADR-046-provider-transport-unix.md b/docs/specs/providers/ADR-046-provider-transport-unix.md index 7a7317530..7ab0f1a61 100644 --- a/docs/specs/providers/ADR-046-provider-transport-unix.md +++ b/docs/specs/providers/ADR-046-provider-transport-unix.md @@ -1419,7 +1419,7 @@ Old and new suites never run in parallel indefinitely. | Field | Value | | --- | --- | | Work item ID | `ADR046-transport-unix-002` | -| Dependency/owner | ADR046-transport-unix-001; d2b-bus transport layer (ADR046-session-003) | +| Dependency/owner | ADR046-transport-unix-001; d2b-bus transport layer (ADR046-bus-001) | | Current source | `packages/d2b-session-unix/src/{adapter,socket,descriptor}.rs`, `tests/unix_session.rs` at main `a1cc0b2d` | | Reuse source | Same; `UnixSeqpacketTransport`, `PeerIdentityPolicy`, `UnixAttachmentPayload`, `OwnedUnixAttachment`, `SeqpacketSocket`, `PeerCredentials`, `ObjectIdentity`, `AcceptedAttachment`, `VerifiedPacket` | | Reuse action | adapt | @@ -1509,7 +1509,7 @@ Old and new suites never run in parallel indefinitely. | Field | Value | | --- | --- | | Work item ID | `ADR046-transport-unix-007` | -| Dependency/owner | ADR046-transport-unix-002 through 006; ADR046-session-003 (d2b-bus ComponentSession method dispatch); ADR046-session-001 (named-stream protocol) | +| Dependency/owner | ADR046-transport-unix-002 through 006; ADR046-bus-001 (d2b-bus ComponentSession method dispatch); ADR046-session-001 (named-stream protocol) | | Current source | No portal service in v3 baseline; `d2b-provider-toolkit/src/server.rs` `GeneratedProviderServiceServer` dispatch pattern (main `a1cc0b2d`) for service entry pattern | | Reuse source | main `a1cc0b2d` `d2b-provider-toolkit/src/server.rs` service dispatch pattern | | Reuse action | adapt | diff --git a/docs/specs/providers/ADR-046-provider-transport-vsock.md b/docs/specs/providers/ADR-046-provider-transport-vsock.md index 8728e70b5..6a626ca2c 100644 --- a/docs/specs/providers/ADR-046-provider-transport-vsock.md +++ b/docs/specs/providers/ADR-046-provider-transport-vsock.md @@ -104,7 +104,7 @@ is a protocol error. The `SessionEngine` checks `descriptor().attachment_support before dispatching and returns `attachment-not-permitted-over-vsock` without contacting the remote end. -**V3 destination**: Imported from `packages/d2b-bus/src/session/` (ADR046-session-003). +**V3 destination**: Imported from `packages/d2b-bus/src/session/` (ADR046-bus-001). The `OwnedTransport` trait is not re-implemented in the transport Provider; it is consumed from `d2b-session`. @@ -827,7 +827,7 @@ expression. | Aspect | Anchor | Evidence class | Retained / Delta / Replacement | | --- | --- | --- | --- | | 2-byte framing utilities | `d2b-session-unix/src/vsock.rs` (main `a1cc0b2d`) | `implemented-but-unwired` | Retained: `FramedVsockTransport` framing (length-prefix encode/decode, bounded allocation, EOF/reset classification) → `framing.rs` in Provider crate. `NativeVsockTransport` / `NativeVsockListener` (raw AF_VSOCK socket calls) → core `LiveVsockEffectPort` in `d2b-core-controller`; NOT in Provider crate | -| `OwnedTransport` trait | `d2b-session-unix/src/adapter.rs` (main `a1cc0b2d`) | `implemented-but-unwired` | Retained verbatim; destination `d2b-session` crate (ADR046-session-003) | +| `OwnedTransport` trait | `d2b-session-unix/src/adapter.rs` (main `a1cc0b2d`) | `implemented-but-unwired` | Retained verbatim; destination `d2b-session` crate (ADR046-bus-001) | | vsock error variants (12) | `d2b-session-unix/src/vsock.rs` (main `a1cc0b2d`) | `implemented-but-unwired` | Retained verbatim in `errors.rs` | | CONNECT-proxy guest-control | `d2bd/src/guest_control_vsock.rs` | `implemented-and-reachable` | Replacement: superseded by `LiveVsockEffectPort` allocator path; guest-control port 14318 excluded from ZoneLink allocation | | socat OTLP relay | `d2b-host/src/vsock_relay_argv.rs` | `implemented-and-reachable` | Replacement: superseded by `observability-otel` Provider native vsock relay | @@ -846,7 +846,7 @@ expression. ### ADR046-vsock-001 | Field | Value | | --- | --- | -| Dependency/owner | Title: Implement `VsockEffectPort` trait and `OpaqueEndpointId`/`OpaqueBindingId` newtypes; Phase 1; Priority P0; Depends on ADR046-session-003 (OwnedTransport in d2b-session); Owner crate `d2b-provider-transport-vsock`. | +| Dependency/owner | Title: Implement `VsockEffectPort` trait and `OpaqueEndpointId`/`OpaqueBindingId` newtypes; Phase 1; Priority P0; Depends on ADR046-bus-001 (OwnedTransport in d2b-session); Owner crate `d2b-provider-transport-vsock`. | | Current source | Evidence class `test-only-or-preview`; baseline has no generic vsock transport Provider or opaque endpoint/binding ID trait. | | Reuse action | create | | Destination | `packages/d2b-provider-transport-vsock/src/effect_port.rs`; test fake in `tests/effect_port_mock.rs`; redaction checks in `tests/redaction.rs`. | @@ -873,7 +873,7 @@ expression. ### ADR046-vsock-003 | Field | Value | | --- | --- | -| Dependency/owner | Title: Implement `VsockTransportService` (OpenTransport/CloseTransport/ObserveTransport); Phase 1; Priority P0; Depends on ADR046-vsock-002 and ADR046-session-003; Owner crate `d2b-provider-transport-vsock`. | +| Dependency/owner | Title: Implement `VsockTransportService` (OpenTransport/CloseTransport/ObserveTransport); Phase 1; Priority P0; Depends on ADR046-vsock-002 and ADR046-bus-001; Owner crate `d2b-provider-transport-vsock`. | | Current source | Evidence class `test-only-or-preview`; no current v3 generic `VsockTransportService` implementation exists. | | Reuse action | adapt | | Destination | `packages/d2b-provider-transport-vsock/src/service.rs`; tests `tests/open_close.rs`, `tests/observe.rs`, and conformance kit. | diff --git a/docs/specs/providers/ADR-046-provider-volume-virtiofs.md b/docs/specs/providers/ADR-046-provider-volume-virtiofs.md index 4622d2dcd..1a06b99b9 100644 --- a/docs/specs/providers/ADR-046-provider-volume-virtiofs.md +++ b/docs/specs/providers/ADR-046-provider-volume-virtiofs.md @@ -1352,7 +1352,7 @@ it does not import session implementation internals directly. | Field | Value | | --- | --- | -| Dependency/owner | ADR046-volume-001 (Volume contract types); ADR046-vvfs-007 (Export type); W1; volume-virtiofs Provider owner | +| Dependency/owner | ADR046-volume-001 (Volume contract types); ADR046-vvfs-export-001 (Export type); W1; volume-virtiofs Provider owner | | Current source | `packages/d2b-host/src/virtiofsd_argv.rs` (VirtiofsdArgvInput, generate_virtiofsd_argv, 14 unit tests, golden argv.txt); `packages/d2b-host/src/lib.rs` (module declaration) | | Reuse action | adapt | | Destination | `packages/d2b-provider-volume-virtiofs/src/virtiofsd_argv.rs`; `packages/d2b-provider-volume-virtiofs/tests/argv_golden.rs` | @@ -1362,7 +1362,7 @@ it does not import session implementation internals directly. | Validation | `tests/argv_golden.rs`: 14 migrated tests + `no_extra_args_ever_emitted`, `socket_path_is_not_in_args`, `shared_dir_is_fd_path`, `path_length_within_sunpath_limit`; `tests/socket_path_privacy.rs`: `socket_path_not_in_export_status`, `socket_path_not_in_volume_status`, `socket_path_not_in_audit_record`; `tests/schema_conformance.rs`: `process_spec_readiness_class_is_provider_defined`, `process_spec_readiness_has_no_kind_or_period_fields`, `process_spec_budget_cpu_request_limit_nested`, `process_spec_budget_memory_request_limit_nested`, `process_spec_budget_pids_limit_present`, `process_spec_budget_fds_limit_present`, `process_spec_sandbox_no_new_privileges_true`, `process_spec_sandbox_read_only_root_true`, `process_spec_no_host_uid_gid_in_spec` | | Removal proof | `packages/d2b-host/src/virtiofsd_argv.rs` removed only after parity confirmed by argv-shape gate | -### ADR046-vvfs-007 — Export ResourceType declaration +### ADR046-vvfs-export-001 — Export ResourceType declaration | Field | Value | | --- | --- | @@ -1394,7 +1394,7 @@ it does not import session implementation internals directly. | Field | Value | | --- | --- | -| Dependency/owner | ADR046-vvfs-001, ADR046-vvfs-002, ADR046-vvfs-007; ADR046-volume-001; W2; volume-virtiofs controller owner | +| Dependency/owner | ADR046-vvfs-001, ADR046-vvfs-002, ADR046-vvfs-export-001; ADR046-volume-001; W2; volume-virtiofs controller owner | | Current source | `packages/d2bd/src/supervisor/dag.rs` (ProcessRole::Virtiofsd dag node); `nixos-modules/processes-json.nix` (virtiofsdRunner block; attachment-to-Process mapping) | | Reuse action | adapt | | Destination | `packages/d2b-provider-volume-virtiofs/src/controller.rs`; `packages/d2b-provider-volume-virtiofs/src/export.rs` | From afe2a5906eab13cea6fe0fd2fa1a9505a28c1ad3 Mon Sep 17 00:00:00 2001 From: John Vicondoa Date: Fri, 24 Jul 2026 04:27:37 -0700 Subject: [PATCH 085/115] docs: close ADR 0046 closure review gaps --- docs/specs/ADR-046-cli-and-operations.md | 38 +++++----- docs/specs/ADR-046-core-controllers.md | 16 ++--- docs/specs/ADR-046-feasibility-and-spikes.md | 4 +- docs/specs/ADR-046-implementation-graph.json | 69 ++++++++++++++----- docs/specs/ADR-046-implementation-graph.md | 20 ++++-- docs/specs/ADR-046-nix-configuration.md | 2 +- docs/specs/ADR-046-reset-and-cutover.md | 2 - docs/specs/ADR-046-spec-set.json | 20 +++--- docs/specs/ADR-046-validation-and-delivery.md | 4 +- docs/specs/ADR-046-work-items.json | 32 ++++----- docs/specs/ADR-046-zone-routing.md | 25 ++++--- docs/specs/README.md | 9 +-- .../ADR-046-provider-device-security-key.md | 6 +- .../ADR-046-provider-device-usbip.md | 2 +- .../ADR-046-provider-notification-desktop.md | 12 ++-- 15 files changed, 159 insertions(+), 102 deletions(-) diff --git a/docs/specs/ADR-046-cli-and-operations.md b/docs/specs/ADR-046-cli-and-operations.md index 494f438c6..eacfd6e99 100644 --- a/docs/specs/ADR-046-cli-and-operations.md +++ b/docs/specs/ADR-046-cli-and-operations.md @@ -235,16 +235,19 @@ a matching schema is installed. # Derivations are declared in d2b.artifacts; ResourceSpecs reference them by id only. d2b.artifacts."cloud-hv-pkg" = { package = pkgs.d2b-provider-cloud-hv; type = "provider"; }; -d2b.zones.main.resources = { - "main" = { type = "Zone"; spec = {}; }; # Zone.spec is empty - "work-vm" = { type = "Guest"; spec = { providerRef = "Provider/cloud-hv"; executionPolicy = { cores = 4; memoryMiB = 8192; }; networkRefs = [ "Network/default" ]; }; }; - "local-user" = { type = "Host"; spec.defaultUserRef = "User/alice"; }; - "default" = { type = "Network"; spec = { cidr = "10.100.0.0/24"; gatewayAddress = "10.100.0.1"; }; }; - "cloud-hv" = { type = "Provider"; spec.artifactId = "cloud-hv-pkg"; }; # plain id, no derivation in spec - "ssh-host-key" = { type = "Credential"; spec = { credentialType = "ssh-ed25519-host-key"; credentialSource = "systemd-credential:d2b-ssh-key"; }; }; - "alice" = { type = "User"; spec = { uid = 1001; homeDir = "/home/alice"; }; }; - "default" = { type = "Quota"; spec = { /* quota fields per Quota schema */ }; }; - "lockdown" = { type = "EmergencyPolicy"; spec = { /* policy fields per EmergencyPolicy schema */ }; }; +d2b.zones.local-root = {}; +d2b.zones.main = { + parentZone = "local-root"; + resources = { + "work-vm" = { type = "Guest"; spec = { providerRef = "Provider/cloud-hv"; executionPolicy = { cores = 4; memoryMiB = 8192; }; networkRefs = [ "Network/default-network" ]; }; }; + "local-user" = { type = "Host"; spec.defaultUserRef = "User/alice"; }; + "default-network" = { type = "Network"; spec = { cidr = "10.100.0.0/24"; gatewayAddress = "10.100.0.1"; }; }; + "cloud-hv" = { type = "Provider"; spec.artifactId = "cloud-hv-pkg"; }; # plain id, no derivation in spec + "ssh-host-key" = { type = "Credential"; spec = { credentialType = "ssh-ed25519-host-key"; credentialSource = "systemd-credential:d2b-ssh-key"; }; }; + "alice" = { type = "User"; spec = { uid = 1001; homeDir = "/home/alice"; }; }; + "default-quota" = { type = "Quota"; spec = { /* quota fields per Quota schema */ }; }; + "lockdown" = { type = "EmergencyPolicy"; spec = { /* policy fields per EmergencyPolicy schema */ }; }; + }; }; ``` @@ -281,7 +284,7 @@ exact shape (all fields always present; null where empty): "spec": { "providerRef": "Provider/cloud-hv", "executionPolicy": { "cores": 4, "memoryMiB": 8192 }, - "networkRefs": ["Network/default"], + "networkRefs": ["Network/default-network"], "provider": { "schemaId": "runtime-cloud-hypervisor.d2bus.org/Guest/spec", "schemaVersion": "1.0", "settings": {} } }, "status": { @@ -1569,7 +1572,7 @@ callsite keeps a separate reserved-name list. The registry is: `reconcile`, `host`, `guest`, `process`, `exec`, `shell`, `volume`, `network`, `device`, `endpoint`, `export`, `import`, `resource`, `user`, `credential`, `provider`, `zone`, `quota`, `emergency-policy`, `activation`, `audit`, `op`, -`auth`, `complete`, and `migrate-check`. +`auth`, and `complete`. Adding or removing a built-in command updates this registry; projection binding therefore inherits the change without a second reserved-name edit. @@ -1910,10 +1913,9 @@ class: `ADR-only`. ## v2 command surface removed at 3.0 clean break All v2 aliases and predecessor commands are deleted at the d2b 3.0 clean break. -There are no executable aliases in 3.0. The `d2b migrate-check` diagnostic -command explains replacements, but it does not dispatch to v2 behavior. -The table below records each removed command and its v3 successor for -documentation and test-removal tracking only. +There are no executable aliases or migration-diagnostic command in 3.0. +Migration guidance is documentation-only. The table below records each removed +command and its v3 successor for documentation and test-removal tracking only. | Removed v2 command | v3 successor | | --- | --- | @@ -2341,7 +2343,7 @@ baseline (only a deprecation notice remains at `lib.rs:2424`). | Reuse source | main `a1cc0b2d` — copy/adapt: (1) `packages/d2b-client/src/daemon_service.rs` `DaemonClient::lifecycle()` (line 210), `DaemonClient::list_workloads()` (line 148), `DaemonClient::inspect()` (line 179); `DaemonMethod::Apply/Start/Stop/Restart/ListWorkloads` variants (lines 31-46) — adapt: replace `WorkloadLifecycleRequest`/`WorkloadName` with `Guest/` ResourceRef; replace `TargetInput::Workload`-scoped calls with zone-root resource API calls; (2) `packages/d2b-contracts/src/generated_v2_services/daemon.rs` `WorkloadLifecycleProjection`, `DeploymentProjection`, `RuntimeProjection` — adapt field mapping to Guest resource spec/status; (3) `packages/d2b/src/lib.rs` `cmd_launch` (`LaunchArgs`) — adapt: the typed ComponentSession target resolution pattern applies but realm/workload-model types (`RealmPath`, `WorkloadName`) are excluded; behavior selected: idempotent apply with dry-run/apply precondition | | Reuse action | adapt | | Destination | `packages/d2b/src/guest.rs` (`d2b guest start/stop/restart/list/status`); unsafe-local workloads go to `packages/d2b/src/host.rs` (`d2b host list/status/get`), NOT guest.rs | -| Detailed design | Route Guest lifecycle (WorkloadProviderKind: LocalVm/QemuMedia/ProviderManaged) through `d2b.resource.v3` Get/UpdateSpec/Watch; map dry-run/apply to resource API precondition; `--no-wait-ready` exits on accepted; with-wait uses `d2b status --watch` loop. WorkloadProviderKind::UnsafeLocal entries MUST route to `d2b host` commands only; any code path that would return an unsafe-local entry from `d2b guest list` is a correctness violation. v2 commands (`d2b up/down/restart/list/status`, `d2b vm start/stop/restart/list/status`) are deleted at 3.0; `d2b migrate-check` explains replacements. | +| Detailed design | Route Guest lifecycle (WorkloadProviderKind: LocalVm/QemuMedia/ProviderManaged) through `d2b.resource.v3` Get/UpdateSpec/Watch; map dry-run/apply to resource API precondition; `--no-wait-ready` exits on accepted; with-wait uses `d2b status --watch` loop. WorkloadProviderKind::UnsafeLocal entries MUST route to `d2b host` commands only; any code path that would return an unsafe-local entry from `d2b guest list` is a correctness violation. v2 commands (`d2b up/down/restart/list/status`, `d2b vm start/stop/restart/list/status`) are deleted at 3.0; replacement guidance remains documentation-only, with no migration-diagnostic command. | | Integration | ZoneContext → resource API client → Guest resource; status watch uses Watch stream | | Data migration | None — full d2b 3.0 reset; no prior state to migrate | | Validation | Dry-run/apply/wait/no-wait-ready tests; zone-unavailable degraded path; JSON output schema tests; confirm v2 command paths are absent (compilation failure if any cmd_vm_start/stop alias re-introduced) | @@ -2480,7 +2482,7 @@ baseline (only a deprecation notice remains at `lib.rs:2424`). | Field | Value | | --- | --- | | Work item ID | `ADR046-cli-011` | -| Dependency/owner | ADR046-identities-002, ADR046-cli-001, ADR046-cli-002, ADR046-cli-007; Nix module owner + Zone runtime owner | +| Dependency/owner | ADR046-identities-002, ADR046-cli-001, ADR046-cli-002, ADR046-cli-007, ADR046-nix-014; Nix module owner + Zone runtime owner; `nixos-modules/assertions.nix` base migration precedes this item's unified-resource update | | Current source | Nix emitters: `nixos-modules/options-realms-workloads.nix` (current `d2b.envs..vms..*`), `nixos-modules/options-realms.nix` (`d2b.realms.*`), `nixos-modules/unsafe-local-workloads-json.nix` (unsafe-local source), `nixos-modules/bundle-artifacts.nix`, `nixos-modules/manifest.nix`, `nixos-modules/assertions.nix`; JSON output: `/etc/d2b/processes.json` (old bundle), `/etc/d2b/realm-entrypoints.json` (static realm index); Zone runtime apply path: `packages/d2bd/src/` (activation apply handler — pre-ADR 0046 path through `cmd_host_prepare`/broker; no live resource bundle apply); cleanup: no current resource-deletion-on-bundle-apply path at baseline | | Reuse source | None (new implementation; no main `a1cc0b2d` reuse — this is the Nix/Zone side, not the CLI client side) | | Reuse action | replace | diff --git a/docs/specs/ADR-046-core-controllers.md b/docs/specs/ADR-046-core-controllers.md index 1329bcf78..41d929ca9 100644 --- a/docs/specs/ADR-046-core-controllers.md +++ b/docs/specs/ADR-046-core-controllers.md @@ -351,13 +351,13 @@ Keeping handlers in one process does not union arbitrary Provider privilege: | Field | Value | | --- | --- | -| Dependency/owner | ADR046-core-001; system-core Provider owner | -| Current source | Nix host/Realm options/index; host check/provider code; user/group lookup and unsafe-local eligibility | -| Reuse source | Any main local-host/user-provider code selected by exact sub-items | +| Dependency/owner | ADR046-core-001, ADR046-exec-003, ADR046-exec-004, ADR046-exec-005, ADR046-system-core-001; coordination-only owner | +| Current source | Canonical Host/User resource, bootstrap-order, and Provider-boundary work owned by ADR046-exec-003, ADR046-exec-004, ADR046-exec-005, and ADR046-system-core-001 | +| Reuse source | The canonical owner work items named above; this item reuses their public contracts but owns no Host/User implementation | | Reuse action | adapt | -| Destination | `packages/d2b-provider-system-core/src/{host,user}.rs` linked into fixed core controller | -| Detailed design | Host and User schemas/reconcile/status/capabilities Primary reuse disposition: `adapt`. Preserved source-plan detail: extract/adapt. | -| Integration | Bootstrap Provider/system-core; other controllers target Host/Guest/User refs | +| Destination | `packages/d2b-core-controller/tests/system_core_coordination.rs` | +| Detailed design | Coordination-only acceptance: prove the fixed core controller loads the manifest/audit boundary from ADR046-system-core-001 and routes Host/User reconciliation to the canonical ADR046-exec-003/004 handlers after ADR046-exec-005 bootstrap ordering. This item defines no schema, handler, status, capability, or `packages/d2b-provider-system-core/src/{host,user}.rs` destination. Primary reuse disposition: `adapt`. Preserved source-plan detail: adapt only the integration acceptance. | +| Integration | After all four canonical owner items are complete, exercise bootstrap Provider/system-core and prove other controllers resolve Host/Guest/User refs without a duplicate core-controller handler | | Data migration | New v3 resources from Nix | -| Validation | Multiple Hosts, system/user restrictions, UID/session drift | -| Removal proof | Host grouping/user helper policy removed only after resource parity | +| Validation | Coordination test asserts one Host handler owner, one User handler owner, manifest load after bootstrap ordering, and no duplicate Host/User destination or reconcile path in core-controller | +| Removal proof | No independent implementation removal; canonical exec/system-core work items own legacy Host/User removal proofs | diff --git a/docs/specs/ADR-046-feasibility-and-spikes.md b/docs/specs/ADR-046-feasibility-and-spikes.md index f4de829d9..3da287a36 100644 --- a/docs/specs/ADR-046-feasibility-and-spikes.md +++ b/docs/specs/ADR-046-feasibility-and-spikes.md @@ -502,11 +502,11 @@ performed by this documentation-only spec. | Field | Value | | --- | --- | -| Hypothesis | A v3 CLI binary built from this spec's authoring baseline contains zero executable dispatch paths for any of the removed v2 commands listed in `ADR-046-cli-and-operations` §"v2 command surface removed at 3.0 clean break" (only an optional `d2b migrate-check` diagnostic that explains replacements without dispatching), and a fresh Zone can bootstrap from an empty state directory through Nix generation activation to a Ready `Zone/` self-resource with no v2/Realm state import of any kind. | +| Hypothesis | A v3 CLI binary built from this spec's authoring baseline contains zero executable dispatch paths for any of the removed v2 commands listed in `ADR-046-cli-and-operations` §"v2 command surface removed at 3.0 clean break", exposes no migration-diagnostic command, and can bootstrap a fresh Zone from an empty state directory through Nix generation activation to a Ready `Zone/` self-resource with no v2/Realm state import of any kind. | | Minimal disposable artifact | `proofs/clean-cutover-spike/` — a static-analysis check (`cargo metadata` + a symbol-table scan of the built CLI binary, e.g., via `nm`/`strings`-shaped inspection or, more robustly, a source-level `grep`-based assertion against the CLI crate's command-table source) confirming none of the 27 removed-command strings from that table dispatch to a handler function, plus a minimal "fresh Zone bootstrap" harness driving the fixed bootstrap sequence in `ADR-046-components-processes-and-sandbox` §"Bootstrap boundary" against fakes for the Zone runtime, broker, and fixed controllers (no real redb/broker — this spike proves *sequencing and state-freshness*, not the redb/process spikes already covered by SPIKE-01/SPIKE-03/SPIKE-11). | | Inputs | (a) the 27-row removed-command table from `ADR-046-cli-and-operations`; (b) an empty state directory (no `/var/lib/d2b` content of any kind, no Realm artifacts); (c) one deliberately injected legacy Realm-shaped file dropped into the fresh state directory before bootstrap, which must be ignored (never imported, never migrated) by the v3 bootstrap sequence. | | Command/harness | A source-scan test: `cargo test --manifest-path proofs/clean-cutover-spike/Cargo.toml -- --test-threads=1 no_v2_alias_dispatch` asserting, for every one of the 27 removed-command strings, that the CLI crate's command-table source (a copy of the relevant `packages/d2b/src/lib.rs`-shaped command enum used by this spike's fixture, not the real crate) contains no executable arm; a second test, `fresh_zone_bootstrap_ignores_legacy_state`, drives the fake bootstrap sequence against inputs (b) and (c). | -| Metrics | (1) 27/27 removed commands have zero executable dispatch arms (only, optionally, a `migrate-check` explanatory branch); (2) fresh bootstrap from an empty directory reaches a Ready `Zone/` self-resource through the fixed sequence (Zone runtime → broker → core-controller → minimum Host/Guest supervisor → user supervisor → system-minijail) with no step skipped or reordered; (3) the injected legacy Realm-shaped file is never read, referenced, or copied by any step of the bootstrap sequence (verified by a file-access-tracing fake filesystem that fails the test on any open() of that path). | +| Metrics | (1) 27/27 removed commands have zero executable dispatch arms and no migration-diagnostic branch exists; (2) fresh bootstrap from an empty directory reaches a Ready `Zone/` self-resource through the fixed sequence (Zone runtime → broker → core-controller → minimum Host/Guest supervisor → user supervisor → system-minijail) with no step skipped or reordered; (3) the injected legacy Realm-shaped file is never read, referenced, or copied by any step of the bootstrap sequence (verified by a file-access-tracing fake filesystem that fails the test on any open() of that path). | | Pass/fail threshold | All three metrics binary pass; metric (3) is zero-tolerance — a single stat()/open() of the legacy file is an automatic fail, since ADR 0046 defines the cutover as destructive with the pre-ADR45 v3 tree as ancestry, not main, and with no v2 data-import path of any kind. | | Expected resource budget | ≤2 minutes wall time; ≤32 MiB RSS. | | Failure interpretation | Any surviving v2 dispatch arm or any legacy-file access blocks the entire CLI/cutover implementation wave; per D001/D041, the fix is deletion of the offending path, never a compatibility shim. | diff --git a/docs/specs/ADR-046-implementation-graph.json b/docs/specs/ADR-046-implementation-graph.json index 8ed6987a9..e4f5980e5 100644 --- a/docs/specs/ADR-046-implementation-graph.json +++ b/docs/specs/ADR-046-implementation-graph.json @@ -2,7 +2,7 @@ "adr" : "0046", "artifactKind" : "d2b-adr-implementation-graph", "counts" : { - "edges" : 1896, + "edges" : 1902, "maxTopologicalRank" : 22, "nodes" : 598, "specNodes" : 55, @@ -42,6 +42,11 @@ "work-item-depends-on" ], "edges" : [ + { + "from" : "ADR046-cli-011", + "to" : "ADR046-nix-014", + "type" : "file-overlap-order" + }, { "from" : "ADR046-device-007", "to" : "ADR046-core-001", @@ -62,6 +67,11 @@ "to" : "ADR046-core-001", "type" : "file-overlap-order" }, + { + "from" : "ADR046-nix-019", + "to" : "ADR046-cli-011", + "type" : "file-overlap-order" + }, { "from" : "ADR046-telem-011", "to" : "ADR046-core-001", @@ -5772,6 +5782,26 @@ "to" : "ADR046-core-001", "type" : "work-item-depends-on" }, + { + "from" : "ADR046-core-002", + "to" : "ADR046-exec-003", + "type" : "work-item-depends-on" + }, + { + "from" : "ADR046-core-002", + "to" : "ADR046-exec-004", + "type" : "work-item-depends-on" + }, + { + "from" : "ADR046-core-002", + "to" : "ADR046-exec-005", + "type" : "work-item-depends-on" + }, + { + "from" : "ADR046-core-002", + "to" : "ADR046-system-core-001", + "type" : "work-item-depends-on" + }, { "from" : "ADR046-cred-entra-001", "to" : "ADR046-credential-001", @@ -12759,17 +12789,18 @@ "exitGate" : "ADR046-W5 exit criteria (ADR-046-validation-and-delivery §4): every spec/work item in this wave Merged with clean destinations, all validators green, and the ten-role panel seal recorded", "id" : "ADR046-cli-011", "kind" : "work-item", - "owner" : "ADR046-identities-002, ADR046-cli-001, ADR046-cli-002, ADR046-cli-007; Nix module owner + Zone runtime owner", + "owner" : "ADR046-identities-002, ADR046-cli-001, ADR046-cli-002, ADR046-cli-007, ADR046-nix-014; Nix module owner + Zone runtime owner; `nixos-modules/assertions.nix` base migration precedes this item's unified-resource update", "parallelGroup" : "wi:ADR-046-cli-and-operations", "prerequisites" : [ "ADR-046-cli-and-operations", "ADR046-cli-001", "ADR046-cli-002", "ADR046-cli-007", - "ADR046-identities-002" + "ADR046-identities-002", + "ADR046-nix-014" ], "specId" : "ADR-046-cli-and-operations", - "topologicalRank" : 12, + "topologicalRank" : 13, "wave" : "W5" }, { @@ -13122,23 +13153,27 @@ { "blockers" : [], "destinations" : [ - "`packages/d2b-provider-system-core/src/{host,user}.rs` linked into fixed core controller" + "`packages/d2b-core-controller/tests/system_core_coordination.rs`" ], "entryContracts" : [ "ADR-046-core-controllers" ], - "exitGate" : "ADR046-W4 exit criteria (ADR-046-validation-and-delivery §4): every spec/work item in this wave Merged with clean destinations, all validators green, and the ten-role panel seal recorded", + "exitGate" : "ADR046-W6 exit criteria (ADR-046-validation-and-delivery §4): every spec/work item in this wave Merged with clean destinations, all validators green, and the ten-role panel seal recorded", "id" : "ADR046-core-002", "kind" : "work-item", - "owner" : "ADR046-core-001; system-core Provider owner", - "parallelGroup" : "wi:ADR-046-core-controllers", + "owner" : "ADR046-core-001, ADR046-exec-003, ADR046-exec-004, ADR046-exec-005, ADR046-system-core-001; coordination-only owner", + "parallelGroup" : "wi:core-controller-coordination:w6", "prerequisites" : [ "ADR-046-core-controllers", - "ADR046-core-001" + "ADR046-core-001", + "ADR046-exec-003", + "ADR046-exec-004", + "ADR046-exec-005", + "ADR046-system-core-001" ], "specId" : "ADR-046-core-controllers", - "topologicalRank" : 10, - "wave" : "W4" + "topologicalRank" : 14, + "wave" : "W6" }, { "blockers" : [], @@ -16063,10 +16098,11 @@ "exitGate" : "ADR046-W5 exit criteria (ADR-046-validation-and-delivery §4): every spec/work item in this wave Merged with clean destinations, all validators green, and the ten-role panel seal recorded", "id" : "ADR046-nix-019", "kind" : "work-item", - "owner" : "ADR046-nix-005; ADR046-nix-001; `d2b-contracts` schema generation (ADR046-nix-027)", + "owner" : "ADR046-nix-005; ADR046-nix-001; ADR046-cli-011 assertions/resource-option shape; `d2b-contracts` schema generation (ADR046-nix-027)", "parallelGroup" : "wi:ADR-046-nix-configuration", "prerequisites" : [ "ADR-046-nix-configuration", + "ADR046-cli-011", "ADR046-nix-001", "ADR046-nix-005", "ADR046-nix-027" @@ -18773,7 +18809,7 @@ { "blockers" : [], "destinations" : [ - "`nixos-modules/options-zones.nix` (new structural base), `nixos-modules/generated/options-zones-.nix` (generated per ResourceType by `xtask gen-zone-nix-options`), `nixos-modules/assertions.nix` (new Zone assertions)" + "`nixos-modules/options-zones.nix` (new structural base), `nixos-modules/generated/resource-types.nix` (generated registry), `nixos-modules/generated/options-zones-.nix` (generated per ResourceType by `xtask gen-zone-nix-options`), `nixos-modules/assertions.nix` (new Zone assertions)" ], "entryContracts" : [ "ADR-046-zone-routing" @@ -23256,7 +23292,7 @@ ], "specCount" : 5, "wave" : "W4", - "workItemCount" : 33 + "workItemCount" : 32 }, { "parallelGroups" : [ @@ -23307,11 +23343,12 @@ "wi:ADR-046-provider-transport-unix", "wi:ADR-046-provider-transport-vsock", "wi:ADR-046-provider-volume-local", - "wi:ADR-046-provider-volume-virtiofs" + "wi:ADR-046-provider-volume-virtiofs", + "wi:core-controller-coordination:w6" ], "specCount" : 27, "wave" : "W6", - "workItemCount" : 256 + "workItemCount" : 257 }, { "parallelGroups" : [ diff --git a/docs/specs/ADR-046-implementation-graph.md b/docs/specs/ADR-046-implementation-graph.md index 45786f5ee..7216613a1 100644 --- a/docs/specs/ADR-046-implementation-graph.md +++ b/docs/specs/ADR-046-implementation-graph.md @@ -23,7 +23,7 @@ tie-break or omitted dependency is used. | Spec nodes | 55 | | Work-item nodes | 543 | | Total nodes | 598 | -| Edges | 1896 | +| Edges | 1902 | | Max topological rank | 22 | ## Waves (W0–W7) @@ -34,9 +34,9 @@ tie-break or omitted dependency is used. | W1 | componentsession-and-bus, resource-reconciliation | 2 | 6 | W1-reconcile-and-bus | | W2 | primitive-resource-composition, zone-routing | 2 | 19 | W2-composition-and-routing | | W3 | provider-model-and-packaging | 1 | 4 | W3-provider-contract | -| W4 | components-processes-and-sandbox, core-controllers, provider-state, resources-credential, resources-network | 5 | 33 | W4-parallel-specs | +| W4 | components-processes-and-sandbox, core-controllers, provider-state, resources-credential, resources-network | 5 | 32 | W4-parallel-specs | | W5 | cli-and-operations, nix-configuration, resources-device, resources-host-guest-process-user, resources-volume, resources-zone-control, telemetry-audit-and-support | 7 | 141 | W5-parallel-specs | -| W6 | provider-activation-nixos, provider-audio-pipewire, provider-clipboard-wayland, provider-credential-entra, provider-credential-managed-identity, provider-credential-secret-service, provider-device-gpu, provider-device-security-key, provider-device-tpm, provider-device-usbip, provider-display-wayland, provider-network-local, provider-notification-desktop, provider-observability-otel, provider-runtime-azure-container-apps, provider-runtime-azure-virtual-machine, provider-runtime-cloud-hypervisor, provider-runtime-qemu-media, provider-shell-terminal, provider-system-core, provider-system-minijail, provider-system-systemd, provider-transport-azure-relay, provider-transport-unix, provider-transport-vsock, provider-volume-local, provider-volume-virtiofs | 27 | 256 | W6-credentials, W6-interaction, W6-storage-network-device, W6-system-host-guest, W6-transport-observability-activation | +| W6 | provider-activation-nixos, provider-audio-pipewire, provider-clipboard-wayland, provider-credential-entra, provider-credential-managed-identity, provider-credential-secret-service, provider-device-gpu, provider-device-security-key, provider-device-tpm, provider-device-usbip, provider-display-wayland, provider-network-local, provider-notification-desktop, provider-observability-otel, provider-runtime-azure-container-apps, provider-runtime-azure-virtual-machine, provider-runtime-cloud-hypervisor, provider-runtime-qemu-media, provider-shell-terminal, provider-system-core, provider-system-minijail, provider-system-systemd, provider-transport-azure-relay, provider-transport-unix, provider-transport-vsock, provider-volume-local, provider-volume-virtiofs | 27 | 257 | W6-credentials, W6-interaction, W6-storage-network-device, W6-system-host-guest, W6-transport-observability-activation | | W7 | feasibility-and-spikes, reset-and-cutover, security-and-threat-model, streamline, validation-and-delivery | 5 | 74 | W7-closing | ## Dependency DAG (waves and prep barriers) @@ -138,10 +138,12 @@ fully represented in the JSON. | Prerequisite | Consumer | Type | | --- | --- | --- | +| `ADR046-nix-014` | `ADR046-cli-011` | file-overlap-order | | `ADR046-core-001` | `ADR046-device-007` | file-overlap-order | | `ADR046-core-001` | `ADR046-exec-013` | file-overlap-order | | `ADR046-core-001` | `ADR046-exec-015` | file-overlap-order | | `ADR046-core-001` | `ADR046-network-008` | file-overlap-order | +| `ADR046-cli-011` | `ADR046-nix-019` | file-overlap-order | | `ADR046-core-001` | `ADR046-telem-011` | file-overlap-order | | `ADR046-core-001` | `ADR046-zone-control-016` | file-overlap-order | | `ADR046-core-001` | `ADR046-zone-control-021` | file-overlap-order | @@ -163,7 +165,9 @@ Only the listed `file-overlap-order` edges constrain shared files. Provider integration ordering that touches disjoint crate trees is not represented as file overlap. The former `wi:core-config-hub` is split into `wi:core-config-hub:w4` and `wi:core-config-hub:w5`; each parallel group is -single-wave while the exact seven ordering edges above remain unchanged. +single-wave. The two `assertions.nix` edges order only +`ADR046-nix-014` → `ADR046-cli-011` → `ADR046-nix-019`; all other files in +those work items retain their existing parallelism. ## Parallel groups @@ -185,7 +189,7 @@ single-wave while the exact seven ordering edges above remain unchanged. | `wi:ADR-046-cli-and-operations` | W5 | 13 | | `wi:ADR-046-components-processes-and-sandbox` | W4 | 2 | | `wi:ADR-046-componentsession-and-bus` | W1 | 3 | -| `wi:ADR-046-core-controllers` | W4 | 2 | +| `wi:ADR-046-core-controllers` | W4 | 1 | | `wi:ADR-046-decision-register` | W0 | 1 | | `wi:ADR-046-feasibility-and-spikes` | W7 | 11 | | `wi:ADR-046-nix-configuration` | W5 | 35 | @@ -238,6 +242,7 @@ single-wave while the exact seven ordering edges above remain unchanged. | `wi:ADR-046-zone-routing` | W2 | 16 | | `wi:core-config-hub:w4` | W4 | 1 | | `wi:core-config-hub:w5` | W5 | 6 | +| `wi:core-controller-coordination:w6` | W6 | 1 | ## Critical path (longest dependency chain) @@ -271,8 +276,9 @@ single-wave while the exact seven ordering edges above remain unchanged. - `ADR046-provider-004` owns the common D098 Service/Binding base DTOs and schemas; the four implementation Providers own only strict extensions and controllers. - `ADR046-zone-control-024` owns the shared Core-derived `physical-usb-backing` tuple; both the security-key and USB effect DAGs depend on it. - Every `ADR046-security-key-*` dependency in `Dependency/owner` is encoded. The dependency subgraph is acyclic and uses no generator tie-break. -- Seven file-overlap barriers cover only the shared core - configuration/cleanup files. Each appears both as a +- Nine file-overlap barriers cover only the shared core + configuration/cleanup files and `nixos-modules/assertions.nix`. Each appears + both as a `file-overlap-order` edge and in the dependent node's `prerequisites`, so the ready-wave query enforces it. Soft cross-Provider integration order remains file-disjoint and concurrent. diff --git a/docs/specs/ADR-046-nix-configuration.md b/docs/specs/ADR-046-nix-configuration.md index 502b53903..35927ea49 100644 --- a/docs/specs/ADR-046-nix-configuration.md +++ b/docs/specs/ADR-046-nix-configuration.md @@ -2619,7 +2619,7 @@ contract work item (ADR046-nix-034/ADR046-nix-035). Cross-reference: | Field | Value | | --- | --- | -| Dependency/owner | ADR046-nix-005; ADR046-nix-001; `d2b-contracts` schema generation (ADR046-nix-027) | +| Dependency/owner | ADR046-nix-005; ADR046-nix-001; ADR046-cli-011 assertions/resource-option shape; `d2b-contracts` schema generation (ADR046-nix-027) | | Current source | `nixos-modules/bundle-artifacts.nix` (`artifactModule` submodule, mode/ownership); `nixos-modules/bundle.nix` (digest chain, SHA256SUMS); `packages/xtask/src/main.rs` (`gen-schemas`); no current per-ResourceType JSON Schema under `docs/reference/schemas/v3/` | | Reuse action | adapt | | Destination | `docs/reference/schemas/v3/.json` for each ResourceType; `nixos-modules/resource-schema-validation.nix` (validates emitted spec against committed JSON Schema at build time); `nixos-modules/provider-settings-validation.nix` (validates `spec.provider.settings` where declared in schema, and Provider `config`, against Provider-embedded schema at build time); `nixos-modules/assertions.nix` (Credential ref enforcement, secret-pattern rejection) | diff --git a/docs/specs/ADR-046-reset-and-cutover.md b/docs/specs/ADR-046-reset-and-cutover.md index 4e2030974..9bdd5294f 100644 --- a/docs/specs/ADR-046-reset-and-cutover.md +++ b/docs/specs/ADR-046-reset-and-cutover.md @@ -1552,8 +1552,6 @@ owns the destination. | --- | --- | --- | --- | --- | | `d2b vm *`, `d2b realm *`, `d2b up/down/restart/list/status`, `d2b usb *`, `d2b keys *`, `d2b build/switch/boot/test/rollback/gc/migrate/config *` | `ADR-046-cli-and-operations` "v2 command surface removed at 3.0 clean break" | Preserve (compiled dispatch) until v3 successor is wired, then Destroy at Phase 10 (compile-time removal, verified by policy lint) | `d2b guest/zone/device/exec/shell/activation *` | `ADR-046-cli-and-operations` per-verb work items | | `d2b host migrate-storage` | `ADR-046-cli-and-operations` "Removal notes" | Destroy at Phase 10 with **no v3 successor** — the layout cutover it served (v1→v2) is unrelated to this cutover and is not re-implemented | None (retired) | N/A | -| `d2b migrate-check` | `ADR-046-cli-and-operations` diagnostic | Preserve (retained diagnostic explaining v2→v3 verb replacements) | Same | N/A | - Any current-baseline path, unit, or artifact not named in any table above is, per [Authoritative inventories](#authoritative-inventories), classified **Preserve by default** and surfaced in the `plan` output's `unclassified` diff --git a/docs/specs/ADR-046-spec-set.json b/docs/specs/ADR-046-spec-set.json index 13ea2e5cb..a2a222919 100644 --- a/docs/specs/ADR-046-spec-set.json +++ b/docs/specs/ADR-046-spec-set.json @@ -13,7 +13,7 @@ "ADR-046-terminology-and-identities" ], "path" : "docs/specs/ADR-046-cli-and-operations.md", - "sha256" : "90ed48fa2062e1580eb5c063f4c6c6fbbdf2dc6bc9db422013a3a57efbbc0e41", + "sha256" : "c5b81aa9ce9638020f6991d4a1d9793f9f51659d1ecd16f720beacb33cb7ea6a", "specId" : "ADR-046-cli-and-operations", "status" : "Proposed", "supersedes" : "Current v3 `d2b` CLI contract (`packages/d2b/src/lib.rs` at baseline)", @@ -62,7 +62,7 @@ "ADR-046-resource-store-redb" ], "path" : "docs/specs/ADR-046-core-controllers.md", - "sha256" : "66cd76546c32adf160515b194f8a55a2df6243950f59a86f18060c380e7be0b8", + "sha256" : "f118cb0b4f1852ab54cbee3a85ebf257acc6d38bf69161a259428a772078ca62", "specId" : "ADR-046-core-controllers", "status" : "Proposed", "supersedes" : "Current daemon-global hard-coded orchestration ownership", @@ -112,7 +112,7 @@ "ADR-046-zone-routing" ], "path" : "docs/specs/ADR-046-feasibility-and-spikes.md", - "sha256" : "c6f82e63c4b7b76865510fcf151813e7feb66c2c72e85717c0e8d41b2b6ebf4e", + "sha256" : "a06ee4f34b96459d014db53f218e6d0dc9e45cf65a63ae32050bc9d6f6df21da", "specId" : "ADR-046-feasibility-and-spikes", "status" : "Proposed", "supersedes" : null, @@ -130,7 +130,7 @@ "ADR-046-terminology-and-identities" ], "path" : "docs/specs/ADR-046-nix-configuration.md", - "sha256" : "47ff8244dec4c6579af3b2740122ccb18e374e844f37f41dd7bd353bbdd6ab43", + "sha256" : "049cfb964da600de8aed072425a870cdec84e749c14f177cc473b1b61d5e611c", "specId" : "ADR-046-nix-configuration", "status" : "Proposed", "supersedes" : "Current `nixos-modules/options-realms*.nix`, `options-envs.nix`, `options-vms.nix`, `index.nix`, `bundle*.nix`, `*-json.nix`, and generated `/etc/d2b/*.json`", @@ -321,7 +321,7 @@ "ADR-046-telemetry-audit-and-support" ], "path" : "docs/specs/providers/ADR-046-provider-device-security-key.md", - "sha256" : "40dcc4733e13ef47905a36759f97e8ac9ec22e0b9c9c378443f8e551e3f792d1", + "sha256" : "81f67a57285dfce25ce55eb3163025d2071cdc0ed6f7c18422b6b39216968fdc", "specId" : "ADR-046-provider-device-security-key", "status" : "Proposed", "supersedes" : "`ProcessRole::SecurityKeyFrontend` daemon-internal accept loop (`packages/d2bd/src/security_key.rs`), `nixos-modules/components/security-key-guest.nix` untracked guest `d2b-sk-frontend.service` unit", @@ -367,7 +367,7 @@ "ADR-046-zone-routing" ], "path" : "docs/specs/providers/ADR-046-provider-device-usbip.md", - "sha256" : "a88ee0635abc5feb93a028bb428fceb52430e6d5a6323962f1e62769a1fa03a9", + "sha256" : "7b01ffa56a0132639a19452656bfe45d97552e68ae4e8a970b12fe9e1acf6750", "specId" : "ADR-046-provider-device-usbip", "status" : "Proposed", "supersedes" : "`nixos-modules/components/usbip.nix` (host-side), per-env usbipd systemd units in `nixos-modules/network.nix`, `ProcessRole::Usbip` / `RunnerRole::Usbip` in current v3 baseline", @@ -449,7 +449,7 @@ "ADR-046-telemetry-audit-and-support" ], "path" : "docs/specs/providers/ADR-046-provider-notification-desktop.md", - "sha256" : "9e886c51a12d25ab771019e2001c7e134c277be3f18576ecbdcedf54c1a4db1a", + "sha256" : "e6c7e56fd23920a53a5f1308cd750a79d283ef2415420d0ed2871d3e1bdd7333", "specId" : "ADR-046-provider-notification-desktop", "status" : "Proposed", "supersedes" : null, @@ -833,7 +833,7 @@ "ADR-046-zone-routing" ], "path" : "docs/specs/ADR-046-reset-and-cutover.md", - "sha256" : "22beccd8ef43db1474b3b8ceb7564e072d5ab7f1eaf1bcc093eb87655e7a4439", + "sha256" : "8b9968dd5479cf7d26183beb8e146b91ee5f10b6ab36b0048a8578b4c08803cf", "specId" : "ADR-046-reset-and-cutover", "status" : "Proposed", "supersedes" : "[ADR 0034](../adr/0034-storage-lifecycle-restart-and-synchronization.md) \"Migration decision\" section for the d2b 3.0 cutover; the current `d2b host migrate-storage` verb (retired per `ADR-046-current-code-migration-map` §6 and `ADR-046-cli-and-operations` \"v2 command surface removed at 3.0 clean break\": it served the one-time v1→v2 storage layout cutover only and has no v3 successor)", @@ -1181,7 +1181,7 @@ "ADR-046-zone-routing" ], "path" : "docs/specs/ADR-046-validation-and-delivery.md", - "sha256" : "ef2412e7d995a880d3a23e79e16d3f22f898f5da8eeacda93fa966ccb51677c8", + "sha256" : "671aae07fa95d66ea3b7e7588868cec460aad9008a433a7499b4e59c952fb174", "specId" : "ADR-046-validation-and-delivery", "status" : "Proposed", "supersedes" : "This repository's current `AGENTS.md` \"Panel review\" phase-gate as the *sole* review mechanism for ADR 0046 work (extended, not replaced, per §12); ad hoc per-agent validation ordering for ADR 0046 implementation", @@ -1199,7 +1199,7 @@ "ADR-046-terminology-and-identities" ], "path" : "docs/specs/ADR-046-zone-routing.md", - "sha256" : "8059eb4ef018db992cca53e07b0b29afd70d132a8bb13531b439dc5755683ef4", + "sha256" : "06d3ebfaae8849a009e74b9652925ed4fa8b42bddf68248d2365eb95c6885455", "specId" : "ADR-046-zone-routing", "status" : "Proposed", "supersedes" : "`RealmEntrypointTable`/`EntrypointMode`/`RealmControllerPlacement` enum (→ compiler-only `parentZone` topology + ZoneLink transport + private Zone runtime bootstrap placement); `RouteTreeEngine`/`routing.rs` route contracts (adapted, not deleted); `RemoteNodeRegistry` (→ ZoneLink controller); `OperationRouter` (→ ZoneLinkIdempotencyKey in d2b-bus); `realm_access_resolver` module (→ ZoneEntrypointResolver); `realm-controllers.json`/`realm-identity.json` bundle artifacts (→ Nix-authored Zone settings/ZoneLink resources); `d2b.realms.*` Nix option namespace (→ `d2b.zones.*`); `WorkloadId` routing targets (→ typed Guest/Host resource refs); `CapabilitySet`-only routing authz (→ per-hop RBAC narrowing + capability ceiling)", diff --git a/docs/specs/ADR-046-validation-and-delivery.md b/docs/specs/ADR-046-validation-and-delivery.md index 03e8439f1..69a838e1b 100644 --- a/docs/specs/ADR-046-validation-and-delivery.md +++ b/docs/specs/ADR-046-validation-and-delivery.md @@ -1397,8 +1397,8 @@ binaries. | Reuse source | `ADR046-delivery-004` generator shape and the existing `d2b-contract-tests` fixture-driven policy-test pattern | | Reuse action | adapt | | Destination | `packages/xtask/src/gen_spec_set.rs`; `packages/d2b-contract-tests/tests/policy_adr046_work_items.rs`; generated spec-set, work-item, and implementation-graph drift checks | -| Detailed design | Parse every normative member's declared work-item headings and tables. Require an exact Markdown/manifest bijection; exact `specId` and `specPath`; a bytewise-sorted, nonempty `workItemPrefixes` list for each item-owning member; global one-member ownership for every registered prefix; registry-based ID ownership; three-digit nonzero ordinals; every mandatory field exactly once and nonempty; one closed scalar `reuseAction`; and `reuseSource: null` for `create`. Reject dropped, extra, malformed, duplicate, ambiguous, heuristic-split, unregistered-prefix, or unconsumed items before writing any artifact. Validate all dependency endpoints, DAG acyclicity, wave monotonicity, and single-wave parallel groups before atomically publishing all generated files. | +| Detailed design | Parse every normative member's exact level-three `### ADR046--` headings and tables; `##` or `####` item declarations are invalid. Require an exact Markdown/manifest bijection; exact `specId` and `specPath`; a bytewise-sorted, nonempty `workItemPrefixes` list for each item-owning member; global one-member ownership for every registered prefix; registry-based ID ownership; three-digit nonzero ordinals; every mandatory field exactly once and nonempty; one closed scalar `reuseAction`; and `reuseSource: null` for `create`. Reject dropped, extra, malformed, wrong-level, duplicate, ambiguous, heuristic-split, unregistered-prefix, or unconsumed items before writing any artifact. Validate all dependency endpoints, DAG acyclicity, wave monotonicity, and single-wave parallel groups before atomically publishing all generated files. | | Integration | `make test-policy` runs negative fixtures; `make test-drift` regenerates all ADR 0046 artifacts and requires a clean diff; `ADR046-delivery-008` consumes only a manifest that passed this policy | | Data migration | None — documentation/build-policy contract only | -| Validation | Fixtures fail for a dropped heading, extra manifest row, duplicate ID, duplicate cross-member prefix, unsorted/empty required prefix registry, wrong owner/path/prefix, heuristic-only prefix match, two-digit/zero ordinal, missing/duplicate mandatory field, free-form/compound action, `create` with a reuse source, dangling dependency, cyclic DAG, backward-wave dependency, and cross-wave parallel group; the exact 55-spec real tree passes with every item once | +| Validation | Fixtures fail for a dropped heading, `##`/`####` item heading, extra manifest row, duplicate ID, duplicate cross-member prefix, unsorted/empty required prefix registry, wrong owner/path/prefix, heuristic-only prefix match, two-digit/zero ordinal, missing/duplicate mandatory field, free-form/compound action, `create` with a reuse source, dangling dependency, cyclic DAG, backward-wave dependency, and cross-wave parallel group; the exact 55-spec real tree passes with every item once | | Removal proof | Not applicable; the policy remains the permanent generated-artifact closure gate | diff --git a/docs/specs/ADR-046-work-items.json b/docs/specs/ADR-046-work-items.json index 4d6ad9fc5..8e63f2ecb 100644 --- a/docs/specs/ADR-046-work-items.json +++ b/docs/specs/ADR-046-work-items.json @@ -772,7 +772,7 @@ "dataMigration" : "None — full d2b 3.0 reset; no prior state to migrate", "dependencyOwner" : "ADR046-cli-001, ADR046-api-001; CLI crate owner", "destination" : "`packages/d2b/src/guest.rs` (`d2b guest start/stop/restart/list/status`); unsafe-local workloads go to `packages/d2b/src/host.rs` (`d2b host list/status/get`), NOT guest.rs", - "detailedDesign" : "Route Guest lifecycle (WorkloadProviderKind: LocalVm/QemuMedia/ProviderManaged) through `d2b.resource.v3` Get/UpdateSpec/Watch; map dry-run/apply to resource API precondition; `--no-wait-ready` exits on accepted; with-wait uses `d2b status --watch` loop. WorkloadProviderKind::UnsafeLocal entries MUST route to `d2b host` commands only; any code path that would return an unsafe-local entry from `d2b guest list` is a correctness violation. v2 commands (`d2b up/down/restart/list/status`, `d2b vm start/stop/restart/list/status`) are deleted at 3.0; `d2b migrate-check` explains replacements.", + "detailedDesign" : "Route Guest lifecycle (WorkloadProviderKind: LocalVm/QemuMedia/ProviderManaged) through `d2b.resource.v3` Get/UpdateSpec/Watch; map dry-run/apply to resource API precondition; `--no-wait-ready` exits on accepted; with-wait uses `d2b status --watch` loop. WorkloadProviderKind::UnsafeLocal entries MUST route to `d2b host` commands only; any code path that would return an unsafe-local entry from `d2b guest list` is a correctness violation. v2 commands (`d2b up/down/restart/list/status`, `d2b vm start/stop/restart/list/status`) are deleted at 3.0; replacement guidance remains documentation-only, with no migration-diagnostic command.", "integration" : "ZoneContext → resource API client → Guest resource; status watch uses Watch stream", "removalProof" : "Old `cmd_vm_start/stop/restart` seqpacket paths removed after Guest resource API paths are live with full test coverage", "reuseAction" : "adapt", @@ -905,7 +905,7 @@ { "currentSource" : "Nix emitters: `nixos-modules/options-realms-workloads.nix` (current `d2b.envs..vms..*`), `nixos-modules/options-realms.nix` (`d2b.realms.*`), `nixos-modules/unsafe-local-workloads-json.nix` (unsafe-local source), `nixos-modules/bundle-artifacts.nix`, `nixos-modules/manifest.nix`, `nixos-modules/assertions.nix`; JSON output: `/etc/d2b/processes.json` (old bundle), `/etc/d2b/realm-entrypoints.json` (static realm index); Zone runtime apply path: `packages/d2bd/src/` (activation apply handler — pre-ADR 0046 path through `cmd_host_prepare`/broker; no live resource bundle apply); cleanup: no current resource-deletion-on-bundle-apply path at baseline", "dataMigration" : "Full reset from current manifest/processes/realm-entrypoints JSON format; prior Nix-generated artifacts (`/etc/d2b/processes.json`, `/etc/d2b/realm-entrypoints.json`) deleted after Zone resource bundle activates", - "dependencyOwner" : "ADR046-identities-002, ADR046-cli-001, ADR046-cli-002, ADR046-cli-007; Nix module owner + Zone runtime owner", + "dependencyOwner" : "ADR046-identities-002, ADR046-cli-001, ADR046-cli-002, ADR046-cli-007, ADR046-nix-014; Nix module owner + Zone runtime owner; `nixos-modules/assertions.nix` base migration precedes this item's unified-resource update", "destination" : "Nix: `nixos-modules/options-zones.nix` (unified `d2b.zones..resources` attrset; per-type `spec` sub-options generated from ResourceTypeSchema/Provider schema), `nixos-modules/bundle-emit.nix` (canonical JSON emit + SHA256 pin), `nixos-modules/assertions.nix` (updated); core controller: `packages/d2b-core-controller/src/configuration.rs`, `packages/d2b-core-controller/src/cleanup.rs`; Contracts: `packages/d2b-contracts/src/zone_bundle.rs` (new)", "detailedDesign" : "**Nix shape:** `d2b.zones..resources` is `attrsOf (submodule { type; optional metadata { ownerRef; labels; annotations }; spec })`. `spec` sub-options per `type` are generated from ResourceTypeSchema and signed Provider schemas; field names remain identical. `metadata.name`/`metadata.zone`/`apiVersion` are derived; status and all core metadata are rejected in input. Vendor-qualified types are admitted only when their schema is installed. **Nix emit:** `bundle-emit.nix` emits `/etc/d2b/zones//resource-bundle.json` plus its integrity pin with canonical resource ordering and schema digests. **Core-controller apply:** `configuration.rs` verifies bundle/catalog integrity, applies Create/Update/no-op intents with bounded async concurrency, refreshes `configurationGeneration` for unchanged configuration-managed resources without waking their controller, handles controller/API name collisions per-item without seizing them, and asynchronously deletes only persisted `managedBy=configuration` resources absent from the new configured set. `cleanup.rs` consumes `Deleted` revision watches and maintains `PendingCleanup`; it never force-removes finalizers. **Prior generation retention:** `d2b.zones..retainedGenerations`, default 3 and range 1–16, is a compiler setting outside `Zone.spec`; no TTL. Rollback reapplies a retained bundle as a new higher generation.", "integration" : "Nix build → per-Zone `resource-bundle.json` + global private artifact catalog → `d2b activation switch` → `d2b-core-controller` configuration service → resource API Create/Update/Delete → owner controllers → finalizer cascade → cleanup watcher → Zone status update", @@ -1158,18 +1158,18 @@ "workItemId" : "ADR046-core-001" }, { - "currentSource" : "Nix host/Realm options/index; host check/provider code; user/group lookup and unsafe-local eligibility", + "currentSource" : "Canonical Host/User resource, bootstrap-order, and Provider-boundary work owned by ADR046-exec-003, ADR046-exec-004, ADR046-exec-005, and ADR046-system-core-001", "dataMigration" : "New v3 resources from Nix", - "dependencyOwner" : "ADR046-core-001; system-core Provider owner", - "destination" : "`packages/d2b-provider-system-core/src/{host,user}.rs` linked into fixed core controller", - "detailedDesign" : "Host and User schemas/reconcile/status/capabilities Primary reuse disposition: `adapt`. Preserved source-plan detail: extract/adapt.", - "integration" : "Bootstrap Provider/system-core; other controllers target Host/Guest/User refs", - "removalProof" : "Host grouping/user helper policy removed only after resource parity", + "dependencyOwner" : "ADR046-core-001, ADR046-exec-003, ADR046-exec-004, ADR046-exec-005, ADR046-system-core-001; coordination-only owner", + "destination" : "`packages/d2b-core-controller/tests/system_core_coordination.rs`", + "detailedDesign" : "Coordination-only acceptance: prove the fixed core controller loads the manifest/audit boundary from ADR046-system-core-001 and routes Host/User reconciliation to the canonical ADR046-exec-003/004 handlers after ADR046-exec-005 bootstrap ordering. This item defines no schema, handler, status, capability, or `packages/d2b-provider-system-core/src/{host,user}.rs` destination. Primary reuse disposition: `adapt`. Preserved source-plan detail: adapt only the integration acceptance.", + "integration" : "After all four canonical owner items are complete, exercise bootstrap Provider/system-core and prove other controllers resolve Host/Guest/User refs without a duplicate core-controller handler", + "removalProof" : "No independent implementation removal; canonical exec/system-core work items own legacy Host/User removal proofs", "reuseAction" : "adapt", - "reuseSource" : "Any main local-host/user-provider code selected by exact sub-items", + "reuseSource" : "The canonical owner work items named above; this item reuses their public contracts but owns no Host/User implementation", "specId" : "ADR-046-core-controllers", "specPath" : "docs/specs/ADR-046-core-controllers.md", - "validation" : "Multiple Hosts, system/user restrictions, UID/session drift", + "validation" : "Coordination test asserts one Host handler owner, one User handler owner, manifest load after bootstrap ordering, and no duplicate Host/User destination or reconcile path in core-controller", "workItemId" : "ADR046-core-002" }, { @@ -1597,14 +1597,14 @@ "dataMigration" : "None — documentation/build-policy contract only", "dependencyOwner" : "`ADR046-delivery-004`, `ADR046-delivery-008`; spec-set policy-test owner", "destination" : "`packages/xtask/src/gen_spec_set.rs`; `packages/d2b-contract-tests/tests/policy_adr046_work_items.rs`; generated spec-set, work-item, and implementation-graph drift checks", - "detailedDesign" : "Parse every normative member's declared work-item headings and tables. Require an exact Markdown/manifest bijection; exact `specId` and `specPath`; a bytewise-sorted, nonempty `workItemPrefixes` list for each item-owning member; global one-member ownership for every registered prefix; registry-based ID ownership; three-digit nonzero ordinals; every mandatory field exactly once and nonempty; one closed scalar `reuseAction`; and `reuseSource: null` for `create`. Reject dropped, extra, malformed, duplicate, ambiguous, heuristic-split, unregistered-prefix, or unconsumed items before writing any artifact. Validate all dependency endpoints, DAG acyclicity, wave monotonicity, and single-wave parallel groups before atomically publishing all generated files.", + "detailedDesign" : "Parse every normative member's exact level-three `### ADR046--` headings and tables; `##` or `####` item declarations are invalid. Require an exact Markdown/manifest bijection; exact `specId` and `specPath`; a bytewise-sorted, nonempty `workItemPrefixes` list for each item-owning member; global one-member ownership for every registered prefix; registry-based ID ownership; three-digit nonzero ordinals; every mandatory field exactly once and nonempty; one closed scalar `reuseAction`; and `reuseSource: null` for `create`. Reject dropped, extra, malformed, wrong-level, duplicate, ambiguous, heuristic-split, unregistered-prefix, or unconsumed items before writing any artifact. Validate all dependency endpoints, DAG acyclicity, wave monotonicity, and single-wave parallel groups before atomically publishing all generated files.", "integration" : "`make test-policy` runs negative fixtures; `make test-drift` regenerates all ADR 0046 artifacts and requires a clean diff; `ADR046-delivery-008` consumes only a manifest that passed this policy", "removalProof" : "Not applicable; the policy remains the permanent generated-artifact closure gate", "reuseAction" : "adapt", "reuseSource" : "`ADR046-delivery-004` generator shape and the existing `d2b-contract-tests` fixture-driven policy-test pattern", "specId" : "ADR-046-validation-and-delivery", "specPath" : "docs/specs/ADR-046-validation-and-delivery.md", - "validation" : "Fixtures fail for a dropped heading, extra manifest row, duplicate ID, duplicate cross-member prefix, unsorted/empty required prefix registry, wrong owner/path/prefix, heuristic-only prefix match, two-digit/zero ordinal, missing/duplicate mandatory field, free-form/compound action, `create` with a reuse source, dangling dependency, cyclic DAG, backward-wave dependency, and cross-wave parallel group; the exact 55-spec real tree passes with every item once", + "validation" : "Fixtures fail for a dropped heading, `##`/`####` item heading, extra manifest row, duplicate ID, duplicate cross-member prefix, unsorted/empty required prefix registry, wrong owner/path/prefix, heuristic-only prefix match, two-digit/zero ordinal, missing/duplicate mandatory field, free-form/compound action, `create` with a reuse source, dangling dependency, cyclic DAG, backward-wave dependency, and cross-wave parallel group; the exact 55-spec real tree passes with every item once", "workItemId" : "ADR046-delivery-009" }, { @@ -3215,7 +3215,7 @@ { "currentSource" : "`nixos-modules/bundle-artifacts.nix` (`artifactModule` submodule, mode/ownership); `nixos-modules/bundle.nix` (digest chain, SHA256SUMS); `packages/xtask/src/main.rs` (`gen-schemas`); no current per-ResourceType JSON Schema under `docs/reference/schemas/v3/`", "dataMigration" : "None — docs/tooling only; no runtime state", - "dependencyOwner" : "ADR046-nix-005; ADR046-nix-001; `d2b-contracts` schema generation (ADR046-nix-027)", + "dependencyOwner" : "ADR046-nix-005; ADR046-nix-001; ADR046-cli-011 assertions/resource-option shape; `d2b-contracts` schema generation (ADR046-nix-027)", "destination" : "`docs/reference/schemas/v3/.json` for each ResourceType; `nixos-modules/resource-schema-validation.nix` (validates emitted spec against committed JSON Schema at build time); `nixos-modules/provider-settings-validation.nix` (validates `spec.provider.settings` where declared in schema, and Provider `config`, against Provider-embedded schema at build time); `nixos-modules/assertions.nix` (Credential ref enforcement, secret-pattern rejection)", "detailedDesign" : "`cargo xtask gen-schemas` emits one JSON Schema per ResourceType under `docs/reference/schemas/v3/`; Nix derivation reads these schemas from `pkgs.d2b-resource-schemas` and validates every emitted `spec` JSON before producing the Zone bundle; Provider-settings validation reads `settingsSchemaDigest` from `provider-catalog.json` and resolves the schema from the Provider package closure; Credential ref enforcement: eval assertion rejects any `spec` string field matching `-----BEGIN`, `eyJ`, or a hex string ≥ 32 bytes in a secret-typed field; `managedBy` in any input spec rejected at eval (core-set runtime field, never in Nix input); bundle integrity: `candidateId`/`contentId` computed over canonical sorted output Primary reuse disposition: `adapt`. Preserved source-plan detail: extend xtask schema generation; new Nix eval/build validation hooks.", "integration" : "Validation hooks wired into `bundle-zones.nix` derivation; `d2b-activation-helper` re-verifies digest chain at staging", @@ -5106,15 +5106,15 @@ "currentSource" : "`nixos-modules/options-realms-workloads.nix` (v3 baseline `b5ddbed6`): `d2b.realms..*` option declarations (evidence: **C** — eval contract; bespoke field names that do NOT mirror canonical ResourceSpec); `nixos-modules/assertions.nix`: realm-name regex, platform-gate, CIDR assertions (evidence: **A**); `nixos-modules/realm-controller-config-json.nix`: `realmControllersJson` emitter, bundle.nix:59 wiring (evidence: **C**)", "dataMigration" : "None; Zone options are new; Realm options retained until migration PR", "dependencyOwner" : "ADR046-routing-001; Nix module owner", - "destination" : "`nixos-modules/options-zones.nix` (new structural base), `nixos-modules/generated/options-zones-.nix` (generated per ResourceType by `xtask gen-zone-nix-options`), `nixos-modules/assertions.nix` (new Zone assertions)", - "detailedDesign" : "Declare compiler-only scalar `d2b.zones..parentZone` plus structural `d2b.zones..resources. = { type = ...; spec = {}; }` as specified in the \"Option schema\" section above. `parentZone` has no default, is required for every non-root Zone, forbidden on `local-root`, resolves to one declared Zone, and never enters a ResourceSpec. Wire `options-zones.nix` and all `generated/options-zones-*.nix` files into `nixos-modules/default.nix`. Add a new `xtask gen-zone-nix-options` command that reads `docs/reference/schemas/v3/.schema.json` for each ResourceType and emits a generated submodule overlaying typed spec options (types, bounds, enum constraints, defaults, docs) onto `d2b.zones..resources..spec`. These generated modules are committed and kept in sync by `xtask gen-zone-nix-options && git diff --exit-code` wired into `make test-drift`. Because the generated options carry field-level type constraints, field-level eval errors (wrong enum, out-of-range int, non-hex fingerprint) are caught without explicit assertions. Explicit assertions in `nixos-modules/assertions.nix` cover cross-resource invariants only: zone/resource key name regex, reserved names, `parentZone` required/forbidden/existence/self/cycle/16-name-depth constraints, child-local `childZoneName == zone`, at most one uplink resource per non-root Zone and none in local root, ref resolution, count limits, and transportSettings secret-key exclusion (listed in the eval-time assertions table). Conflicting parent scalar definitions fail through standard Nix module merging. Each new assertion must have a matching case in `tests/unit/nix/cases/zone-assertions.nix` (nix-unit auto-discovered). `d2b.realms` option namespace is NOT removed in this work item. Primary reuse disposition: `adapt`. Preserved source-plan detail: new module following same pattern.", + "destination" : "`nixos-modules/options-zones.nix` (new structural base), `nixos-modules/generated/resource-types.nix` (generated registry), `nixos-modules/generated/options-zones-.nix` (generated per ResourceType by `xtask gen-zone-nix-options`), `nixos-modules/assertions.nix` (new Zone assertions)", + "detailedDesign" : "Declare compiler-only scalar `d2b.zones..parentZone` plus structural `d2b.zones..resources. = { type = ...; spec = {}; }` as specified in the \"Option schema\" section above. `parentZone` has no default, is required for every non-root Zone, forbidden on `local-root`, resolves to one declared Zone, and never enters a ResourceSpec. Wire `options-zones.nix` and all `generated/options-zones-*.nix` files into `nixos-modules/default.nix`. Add a new `xtask gen-zone-nix-options` command that reads `docs/reference/schemas/v3/.schema.json` for each ResourceType, derives `generated/resource-types.nix`, and emits a generated submodule overlaying typed spec options (types, bounds, enum constraints, defaults, docs) onto `d2b.zones..resources..spec`. The generated registry's standard subset must equal the canonical 19-type registry from `ADR-046-resource-object-model` exactly; installed signed Provider schemas may append qualified types. These generated modules are committed and kept in sync by `xtask gen-zone-nix-options && git diff --exit-code` wired into `make test-drift`. Because the generated options carry field-level type constraints, field-level eval errors (wrong enum, out-of-range int, non-hex fingerprint) are caught without explicit assertions. Explicit assertions in `nixos-modules/assertions.nix` cover cross-resource invariants only: zone/resource key name regex, reserved names, `parentZone` required/forbidden/existence/self/cycle/16-name-depth constraints, child-local `childZoneName == zone`, at most one uplink resource per non-root Zone and none in local root, ref resolution, count limits, and transportSettings secret-key exclusion (listed in the eval-time assertions table). Conflicting parent scalar definitions fail through standard Nix module merging. Each new assertion must have a matching case in `tests/unit/nix/cases/zone-assertions.nix` (nix-unit auto-discovered). `d2b.realms` option namespace is NOT removed in this work item. Primary reuse disposition: `adapt`. Preserved source-plan detail: new module following same pattern.", "integration" : "ADR046-routing-012 consumes the validated `parentZone` map for private allocator bootstrap sealing and iterates `d2b.zones..resources.*` for resource-bundle emission", "removalProof" : "`nixos-modules/options-realms-workloads.nix` `d2b.realms` namespace retires after all hosts migrate to `d2b.zones`", "reuseAction" : "adapt", "reuseSource" : "Same v3 baseline `b5ddbed6`; `assertions.nix` pattern reused for Zone assertions; `realm-controller-config-json.nix` is the structural template", "specId" : "ADR-046-zone-routing", "specPath" : "docs/specs/ADR-046-zone-routing.md", - "validation" : "`nix-unit: zone-name-regex`, all five `nix-unit: zone-parent-*` vectors, `nix-unit: zone-link-fingerprint`, `nix-unit: zone-link-child-name`, `nix-unit: zone-link-one-uplink`, `nix-unit: capability-ceiling-unknown-verb`, `nix-unit: transport-settings-secret-key`; add `drift: zone-nix-options` (`xtask gen-zone-nix-options && git diff --exit-code`); run `make nix-unit-pin` after adding eval cases", + "validation" : "`nix-unit: zone-name-regex`, all five `nix-unit: zone-parent-*` vectors, `nix-unit: zone-link-fingerprint`, `nix-unit: zone-link-child-name`, `nix-unit: zone-link-one-uplink`, `nix-unit: capability-ceiling-unknown-verb`, `nix-unit: transport-settings-secret-key`; add `drift: standard-resource-type-registry` asserting the generated standard subset is exactly all 19 canonical types with no omission/addition/duplicate/reordering, plus `drift: zone-nix-options` (`xtask gen-zone-nix-options && git diff --exit-code`); run `make nix-unit-pin` after adding eval cases", "workItemId" : "ADR046-routing-011" }, { diff --git a/docs/specs/ADR-046-zone-routing.md b/docs/specs/ADR-046-zone-routing.md index 5c4c8f554..b372eab04 100644 --- a/docs/specs/ADR-046-zone-routing.md +++ b/docs/specs/ADR-046-zone-routing.md @@ -808,7 +808,12 @@ The base Nix option type for every resource is structural: ```nix # nixos-modules/options-zones.nix (structural base; type-specific options are # generated by xtask gen-zone-nix-options from ResourceTypeSchema JSON) -{ +let + # Generated from ADR-046-resource-object-model's canonical 19-type registry + # plus installed qualified Provider schemas. The drift test asserts that the + # standard subset is exactly the canonical registry. + registeredResourceTypes = import ./generated/resource-types.nix; +in { d2b.zones..parentZone = mkOption { # No default. Required for non-root Zones and forbidden on local-root. type = types.strMatching "^[a-z][a-z0-9-]*$"; @@ -820,10 +825,7 @@ The base Nix option type for every resource is structural: d2b.zones..resources. = { type = mkOption { - type = types.enum [ - "Zone" "ZoneLink" "Provider" "Process" "EphemeralProcess" - "Volume" "Network" "Credential" "Host" "Guest" - ]; + type = types.enum registeredResourceTypes; description = "ResourceType for this resource."; }; spec = mkOption { @@ -865,6 +867,13 @@ The base Nix option type for every resource is structural: } ``` +`xtask gen-zone-nix-options` derives `generated/resource-types.nix` from the +same committed ResourceTypeSchema catalog used by API and bundle validation. +Its drift test compares the standard subset byte-for-byte with the canonical +19-type registry and rejects an omission, addition, duplicate, or reordered +entry; qualified Provider types are appended only from installed signed +schemas. + `xtask gen-zone-nix-options` reads `docs/reference/schemas/v3/.schema.json` and emits a generated `nixos-modules/generated/options-zones-.nix` for each ResourceType, overlaying typed submodule options onto `spec`. These generated files @@ -2592,11 +2601,11 @@ The following transitions are NOT simple textual renames: | Current source | `nixos-modules/options-realms-workloads.nix` (v3 baseline `b5ddbed6`): `d2b.realms..*` option declarations (evidence: **C** — eval contract; bespoke field names that do NOT mirror canonical ResourceSpec); `nixos-modules/assertions.nix`: realm-name regex, platform-gate, CIDR assertions (evidence: **A**); `nixos-modules/realm-controller-config-json.nix`: `realmControllersJson` emitter, bundle.nix:59 wiring (evidence: **C**) | | Reuse source | Same v3 baseline `b5ddbed6`; `assertions.nix` pattern reused for Zone assertions; `realm-controller-config-json.nix` is the structural template | | Reuse action | adapt | -| Destination | `nixos-modules/options-zones.nix` (new structural base), `nixos-modules/generated/options-zones-.nix` (generated per ResourceType by `xtask gen-zone-nix-options`), `nixos-modules/assertions.nix` (new Zone assertions) | -| Detailed design | Declare compiler-only scalar `d2b.zones..parentZone` plus structural `d2b.zones..resources. = { type = ...; spec = {}; }` as specified in the "Option schema" section above. `parentZone` has no default, is required for every non-root Zone, forbidden on `local-root`, resolves to one declared Zone, and never enters a ResourceSpec. Wire `options-zones.nix` and all `generated/options-zones-*.nix` files into `nixos-modules/default.nix`. Add a new `xtask gen-zone-nix-options` command that reads `docs/reference/schemas/v3/.schema.json` for each ResourceType and emits a generated submodule overlaying typed spec options (types, bounds, enum constraints, defaults, docs) onto `d2b.zones..resources..spec`. These generated modules are committed and kept in sync by `xtask gen-zone-nix-options && git diff --exit-code` wired into `make test-drift`. Because the generated options carry field-level type constraints, field-level eval errors (wrong enum, out-of-range int, non-hex fingerprint) are caught without explicit assertions. Explicit assertions in `nixos-modules/assertions.nix` cover cross-resource invariants only: zone/resource key name regex, reserved names, `parentZone` required/forbidden/existence/self/cycle/16-name-depth constraints, child-local `childZoneName == zone`, at most one uplink resource per non-root Zone and none in local root, ref resolution, count limits, and transportSettings secret-key exclusion (listed in the eval-time assertions table). Conflicting parent scalar definitions fail through standard Nix module merging. Each new assertion must have a matching case in `tests/unit/nix/cases/zone-assertions.nix` (nix-unit auto-discovered). `d2b.realms` option namespace is NOT removed in this work item. Primary reuse disposition: `adapt`. Preserved source-plan detail: new module following same pattern. | +| Destination | `nixos-modules/options-zones.nix` (new structural base), `nixos-modules/generated/resource-types.nix` (generated registry), `nixos-modules/generated/options-zones-.nix` (generated per ResourceType by `xtask gen-zone-nix-options`), `nixos-modules/assertions.nix` (new Zone assertions) | +| Detailed design | Declare compiler-only scalar `d2b.zones..parentZone` plus structural `d2b.zones..resources. = { type = ...; spec = {}; }` as specified in the "Option schema" section above. `parentZone` has no default, is required for every non-root Zone, forbidden on `local-root`, resolves to one declared Zone, and never enters a ResourceSpec. Wire `options-zones.nix` and all `generated/options-zones-*.nix` files into `nixos-modules/default.nix`. Add a new `xtask gen-zone-nix-options` command that reads `docs/reference/schemas/v3/.schema.json` for each ResourceType, derives `generated/resource-types.nix`, and emits a generated submodule overlaying typed spec options (types, bounds, enum constraints, defaults, docs) onto `d2b.zones..resources..spec`. The generated registry's standard subset must equal the canonical 19-type registry from `ADR-046-resource-object-model` exactly; installed signed Provider schemas may append qualified types. These generated modules are committed and kept in sync by `xtask gen-zone-nix-options && git diff --exit-code` wired into `make test-drift`. Because the generated options carry field-level type constraints, field-level eval errors (wrong enum, out-of-range int, non-hex fingerprint) are caught without explicit assertions. Explicit assertions in `nixos-modules/assertions.nix` cover cross-resource invariants only: zone/resource key name regex, reserved names, `parentZone` required/forbidden/existence/self/cycle/16-name-depth constraints, child-local `childZoneName == zone`, at most one uplink resource per non-root Zone and none in local root, ref resolution, count limits, and transportSettings secret-key exclusion (listed in the eval-time assertions table). Conflicting parent scalar definitions fail through standard Nix module merging. Each new assertion must have a matching case in `tests/unit/nix/cases/zone-assertions.nix` (nix-unit auto-discovered). `d2b.realms` option namespace is NOT removed in this work item. Primary reuse disposition: `adapt`. Preserved source-plan detail: new module following same pattern. | | Integration | ADR046-routing-012 consumes the validated `parentZone` map for private allocator bootstrap sealing and iterates `d2b.zones..resources.*` for resource-bundle emission | | Data migration | None; Zone options are new; Realm options retained until migration PR | -| Validation | `nix-unit: zone-name-regex`, all five `nix-unit: zone-parent-*` vectors, `nix-unit: zone-link-fingerprint`, `nix-unit: zone-link-child-name`, `nix-unit: zone-link-one-uplink`, `nix-unit: capability-ceiling-unknown-verb`, `nix-unit: transport-settings-secret-key`; add `drift: zone-nix-options` (`xtask gen-zone-nix-options && git diff --exit-code`); run `make nix-unit-pin` after adding eval cases | +| Validation | `nix-unit: zone-name-regex`, all five `nix-unit: zone-parent-*` vectors, `nix-unit: zone-link-fingerprint`, `nix-unit: zone-link-child-name`, `nix-unit: zone-link-one-uplink`, `nix-unit: capability-ceiling-unknown-verb`, `nix-unit: transport-settings-secret-key`; add `drift: standard-resource-type-registry` asserting the generated standard subset is exactly all 19 canonical types with no omission/addition/duplicate/reordering, plus `drift: zone-nix-options` (`xtask gen-zone-nix-options && git diff --exit-code`); run `make nix-unit-pin` after adding eval cases | | Removal proof | `nixos-modules/options-realms-workloads.nix` `d2b.realms` namespace retires after all hosts migrate to `d2b.zones` | ### ADR046-routing-012 diff --git a/docs/specs/README.md b/docs/specs/README.md index a0c4a282f..81ac1aab1 100644 --- a/docs/specs/README.md +++ b/docs/specs/README.md @@ -55,7 +55,7 @@ manifests, and the generated implementation-graph artifacts **Resource catalog (6)** — the 19 standard ResourceTypes (`Zone`, `ZoneLink`, `Provider`, `Role`, `RoleBinding`, `Quota`, `EmergencyPolicy`, `Host`, `Guest`, -`Process`, `EphemeralProcess`, `User`, `Volume`, `Network`, `Device`, +`Process`, `EphemeralProcess`, `Volume`, `Network`, `Device`, `User`, `Credential`, `Endpoint`, `ResourceExport`, `ResourceImport`) have the following exclusive ResourceType owners. Foundation specs define shared contracts but do not co-own these types: @@ -296,7 +296,7 @@ Each spec contains an **Implementation work items** section. Every item has: | Field | Requirement | | --- | --- | -| Work item ID | Declared by the heading as `ADR046--`; an optional table row must match it exactly | +| Work item ID | Declared by an exact level-three heading `### ADR046--`; an optional table row must match it exactly | | Dependency/owner | Prerequisites, future wave, crate/component, shared owner | | Current source | Exact v3 paths, symbols, call sites, artifacts, and tests | | Reuse source | Optional exact main commit/paths/symbols/tests used for copy/adaptation; explicit `None` serializes as `null` | @@ -399,8 +399,9 @@ separate items. Generation and validation fail closed unless every normative member's Implementation work items section is complete: -- every work-item heading matches the ID regex, uses one prefix registered by - its owning member, and is unique across the set; +- every work-item heading is exactly level three (`###`), matches the ID regex, + uses one prefix registered by its owning member, and is unique across the + set; `##` and `####` item declarations fail closed; - every item has exactly one nonempty `Dependency/owner`, `Current source`, `Reuse action`, `Destination`, `Detailed design`, `Integration`, `Data migration`, `Validation`, and `Removal proof` field, with no duplicate diff --git a/docs/specs/providers/ADR-046-provider-device-security-key.md b/docs/specs/providers/ADR-046-provider-device-security-key.md index 402aa2ae5..5bc824ea3 100644 --- a/docs/specs/providers/ADR-046-provider-device-security-key.md +++ b/docs/specs/providers/ADR-046-provider-device-security-key.md @@ -1751,7 +1751,11 @@ d2b.zones.devices.resources."yubikey-primary-export" = { factoryFingerprint = "sha256:"; arbitration = "exclusive"; operations = [ "security-key-ceremony" ]; - consumerZonePolicy = { zones = [ "work" ]; }; + consumerZonePolicy = { + zones = [ "Zone/work" ]; + capabilityCeiling = [ "security-key-ceremony" ]; + }; + visibility = "named-zones"; }; }; diff --git a/docs/specs/providers/ADR-046-provider-device-usbip.md b/docs/specs/providers/ADR-046-provider-device-usbip.md index d788e5fb2..b8a0e1690 100644 --- a/docs/specs/providers/ADR-046-provider-device-usbip.md +++ b/docs/specs/providers/ADR-046-provider-device-usbip.md @@ -1518,7 +1518,7 @@ d2b.zones.dev.resources.work-token-import = { projectionName = "work-token"; requestedCapabilities = [ "usbip-control" "usbip-data" ]; requestedQuota = { maxConsumers = 1; }; - disconnectPolicy = { behavior = "degrade"; }; + disconnectPolicy = { mode = "degrade"; }; }; }; diff --git a/docs/specs/providers/ADR-046-provider-notification-desktop.md b/docs/specs/providers/ADR-046-provider-notification-desktop.md index 2f7ed6377..59d1e47c8 100644 --- a/docs/specs/providers/ADR-046-provider-notification-desktop.md +++ b/docs/specs/providers/ADR-046-provider-notification-desktop.md @@ -1327,7 +1327,7 @@ The v2 `d2b.notify.v2.NotifyService` ttrpc contract is superseded by ### 17.2 Work items -#### ADR046-notify-001 +### ADR046-notify-001 | Field | Value | | --- | --- | @@ -1341,7 +1341,7 @@ The v2 `d2b.notify.v2.NotifyService` ttrpc contract is superseded by | Validation | `tests/stream_record.rs` — DTO schema vectors; `tests/action_nonce.rs` — single-use/TTL/capacity/replay | | Removal proof | v2 `d2b.notify.v2` generated stubs removed after v3 service established | -#### ADR046-notify-002 +### ADR046-notify-002 | Field | Value | | --- | --- | @@ -1355,7 +1355,7 @@ The v2 `d2b.notify.v2.NotifyService` ttrpc contract is superseded by | Validation | `tests/stream_admission.rs` — all rejection vectors | | Removal proof | Old `DesktopServices` session admitted under v2 contract removed when v3 session established | -#### ADR046-notify-003 +### ADR046-notify-003 | Field | Value | | --- | --- | @@ -1369,7 +1369,7 @@ The v2 `d2b.notify.v2.NotifyService` ttrpc contract is superseded by | Validation | Unit tests for placement FSM in `tests/stream_record.rs`; Volume creation/deletion lifecycle in `tests/volume_lifecycle.rs`; see also `integration/cross_zone_source.rs` end-to-end | | Removal proof | Not applicable (new controller) | -#### ADR046-notify-004 +### ADR046-notify-004 | Field | Value | | --- | --- | @@ -1383,7 +1383,7 @@ The v2 `d2b.notify.v2.NotifyService` ttrpc contract is superseded by | Validation | `integration/dbus_sink.rs`, `integration/observer_client.rs`, `integration/action_invoke.rs` | | Removal proof | `nixos-modules/notifications.nix` state-dir tmpfiles rule retired; all notification state is in-memory per-session with no Volume replacement | -#### ADR046-notify-005 +### ADR046-notify-005 | Field | Value | | --- | --- | @@ -1397,7 +1397,7 @@ The v2 `d2b.notify.v2.NotifyService` ttrpc contract is superseded by | Validation | `integration/cross_zone_source.rs` | | Removal proof | v3 baseline security-key notification path in `d2b-notify` is superseded; clipd direct `notify_rust` call superseded | -#### ADR046-notify-006 +### ADR046-notify-006 | Field | Value | | --- | --- | From 0013c35a123ffaebb91d76792fb54403d6a1c459 Mon Sep 17 00:00:00 2001 From: John Vicondoa Date: Fri, 24 Jul 2026 05:08:35 -0700 Subject: [PATCH 086/115] docs: close ADR 0046 schema and graph gaps --- docs/specs/ADR-046-cli-and-operations.md | 114 +++++++++++++++---- docs/specs/ADR-046-implementation-graph.json | 58 ++++++++-- docs/specs/ADR-046-implementation-graph.md | 18 ++- docs/specs/ADR-046-resources-zone-control.md | 10 +- docs/specs/ADR-046-spec-set.json | 4 +- docs/specs/ADR-046-work-items.json | 12 +- 6 files changed, 165 insertions(+), 51 deletions(-) diff --git a/docs/specs/ADR-046-cli-and-operations.md b/docs/specs/ADR-046-cli-and-operations.md index eacfd6e99..c6bb373f1 100644 --- a/docs/specs/ADR-046-cli-and-operations.md +++ b/docs/specs/ADR-046-cli-and-operations.md @@ -229,28 +229,69 @@ Field names in `spec` are identical to the schema; there is no second vocabulary and no extra nesting. Vendor-qualified types (containing `.`) are admitted when a matching schema is installed. -**Minimal multi-resource example:** +**Minimal schema-valid example:** ```nix -# Derivations are declared in d2b.artifacts; ResourceSpecs reference them by id only. -d2b.artifacts."cloud-hv-pkg" = { package = pkgs.d2b-provider-cloud-hv; type = "provider"; }; +# Derivations are declared globally; ResourceSpecs carry only catalog IDs. +d2b.artifacts = { + provider-runtime-cloud-hypervisor = { + package = inputs.d2b-provider-runtime-cloud-hypervisor.packages.${system}.default; + type = "provider"; + }; + work-vm-system = { + package = inputs.nixpkgs.lib.nixosSystem { + inherit system; + modules = [ ./guests/work-vm.nix ]; + }.config.system.build.toplevel; + type = "nixos-system"; + }; +}; d2b.zones.local-root = {}; d2b.zones.main = { parentZone = "local-root"; resources = { - "work-vm" = { type = "Guest"; spec = { providerRef = "Provider/cloud-hv"; executionPolicy = { cores = 4; memoryMiB = 8192; }; networkRefs = [ "Network/default-network" ]; }; }; - "local-user" = { type = "Host"; spec.defaultUserRef = "User/alice"; }; - "default-network" = { type = "Network"; spec = { cidr = "10.100.0.0/24"; gatewayAddress = "10.100.0.1"; }; }; - "cloud-hv" = { type = "Provider"; spec.artifactId = "cloud-hv-pkg"; }; # plain id, no derivation in spec - "ssh-host-key" = { type = "Credential"; spec = { credentialType = "ssh-ed25519-host-key"; credentialSource = "systemd-credential:d2b-ssh-key"; }; }; - "alice" = { type = "User"; spec = { uid = 1001; homeDir = "/home/alice"; }; }; - "default-quota" = { type = "Quota"; spec = { /* quota fields per Quota schema */ }; }; - "lockdown" = { type = "EmergencyPolicy"; spec = { /* policy fields per EmergencyPolicy schema */ }; }; + main-host = { + type = "Host"; + spec.providerRef = "Provider/system-core"; + }; + runtime-cloud-hypervisor = { + type = "Provider"; + spec = { + artifactId = "provider-runtime-cloud-hypervisor"; + config.controllerExecutionRef = "Host/main-host"; + }; + }; + work-vm = { + type = "Guest"; + spec = { + allowedDomains = [ "system" ]; + budget = {}; + defaultDomain = "system"; + defaultUserRef = null; + deviceAttachments = []; + networkAttachments = []; + providerRef = "Provider/runtime-cloud-hypervisor"; + provider = { + schemaId = "runtime-cloud-hypervisor.d2bus.org/Guest/spec"; + schemaVersion = "1.0.0"; + settings = {}; + }; + systemArtifactId = "work-vm-system"; + volumeAttachmentDefaults = []; + }; + }; }; }; ``` +`Zone/main`, `Provider/system-core`, and `Provider/system-minijail` are +runtime-created; none is authored in the resource map. Every authored reference +resolves: the system Provider owns `Host/main-host`, the installed runtime +Provider names that Host as its controller execution target, and +`Guest/work-vm` names both that Provider and a `nixos-system` artifact. The +compact example omits unrelated types rather than using placeholder fields. + **Evidence class:** `ADR-only` — v3 Nix modules do not exist at baseline `b5ddbed6`. Current source: `nixos-modules/options-realms-workloads.nix`, `nixos-modules/options-realms.nix`, `nixos-modules/unsafe-local-workloads-json.nix` @@ -258,9 +299,11 @@ d2b.zones.main = { ### Canonical ResourceSpec JSON shape -Every `d2b get`, `d2b list` element, and `d2b watch` event carries the -canonical ResourceSpec envelope inside the outer `--json` response. The -exact shape (all fields always present; null where empty): +Every `d2b get`, `d2b list` element, and `d2b watch` event carries a canonical +resource envelope inside the outer `--json` response. The Guest `spec` below is +a field-for-field mirror of the authored Nix after deterministic defaults; +the runtime adds store-generated metadata and status. Optional presentation +metadata and the optional Provider status extension are omitted. ```json { @@ -278,14 +321,23 @@ exact shape (all fields always present; null where empty): "createdAt": "2026-07-22T00:00:00Z", "updatedAt": "2026-07-22T10:30:00Z", "managedBy": "configuration", - "configurationGeneration": 7, - "labels": {} + "configurationGeneration": 7 }, "spec": { - "providerRef": "Provider/cloud-hv", - "executionPolicy": { "cores": 4, "memoryMiB": 8192 }, - "networkRefs": ["Network/default-network"], - "provider": { "schemaId": "runtime-cloud-hypervisor.d2bus.org/Guest/spec", "schemaVersion": "1.0", "settings": {} } + "allowedDomains": ["system"], + "budget": {}, + "defaultDomain": "system", + "defaultUserRef": null, + "deviceAttachments": [], + "networkAttachments": [], + "providerRef": "Provider/runtime-cloud-hypervisor", + "provider": { + "schemaId": "runtime-cloud-hypervisor.d2bus.org/Guest/spec", + "schemaVersion": "1.0.0", + "settings": {} + }, + "systemArtifactId": "work-vm-system", + "volumeAttachmentDefaults": [] }, "status": { "observedGeneration": 3, @@ -294,7 +346,25 @@ exact shape (all fields always present; null where empty): "lastReconciledAt": "2026-07-22T10:30:05Z", "startedAt": "2026-07-22T10:30:05Z", "completedAt": null, - "outcome": null + "outcome": null, + "update": { + "state": "Current", + "reasons": [], + "observedGeneration": 3, + "targetGeneration": 3, + "disruption": "None", + "preserveState": true, + "operationId": null, + "lastAssessedAt": "2026-07-22T10:30:05Z", + "owned": { "count": 1, "refs": ["Process/work-vm-vmm"] }, + "dependencies": { "count": 1, "refs": ["Provider/runtime-cloud-hypervisor"] } + }, + "resource": { + "providerPhase": "running", + "bootstrapReady": true, + "guestIdentityDigest": "8d5f7b1a", + "activeProcessCount": 1 + } } } ``` @@ -2490,7 +2560,7 @@ baseline (only a deprecation notice remains at `lib.rs:2424`). | Detailed design | **Nix shape:** `d2b.zones..resources` is `attrsOf (submodule { type; optional metadata { ownerRef; labels; annotations }; spec })`. `spec` sub-options per `type` are generated from ResourceTypeSchema and signed Provider schemas; field names remain identical. `metadata.name`/`metadata.zone`/`apiVersion` are derived; status and all core metadata are rejected in input. Vendor-qualified types are admitted only when their schema is installed. **Nix emit:** `bundle-emit.nix` emits `/etc/d2b/zones//resource-bundle.json` plus its integrity pin with canonical resource ordering and schema digests. **Core-controller apply:** `configuration.rs` verifies bundle/catalog integrity, applies Create/Update/no-op intents with bounded async concurrency, refreshes `configurationGeneration` for unchanged configuration-managed resources without waking their controller, handles controller/API name collisions per-item without seizing them, and asynchronously deletes only persisted `managedBy=configuration` resources absent from the new configured set. `cleanup.rs` consumes `Deleted` revision watches and maintains `PendingCleanup`; it never force-removes finalizers. **Prior generation retention:** `d2b.zones..retainedGenerations`, default 3 and range 1–16, is a compiler setting outside `Zone.spec`; no TTL. Rollback reapplies a retained bundle as a new higher generation. | | Integration | Nix build → per-Zone `resource-bundle.json` + global private artifact catalog → `d2b activation switch` → `d2b-core-controller` configuration service → resource API Create/Update/Delete → owner controllers → finalizer cascade → cleanup watcher → Zone status update | | Data migration | Full reset from current manifest/processes/realm-entrypoints JSON format; prior Nix-generated artifacts (`/etc/d2b/processes.json`, `/etc/d2b/realm-entrypoints.json`) deleted after Zone resource bundle activates | -| Validation | Runtime integration: all CLI-visible cleanup/status/rollback/gc/audit tests (§CLI-visible tests for activation and cleanup), including no force-finalizer path; Nix unit and build tests owned by ADR-046-nix-configuration spec | +| Validation | Runtime integration: all CLI-visible cleanup/status/rollback/gc/audit tests (§CLI-visible tests for activation and cleanup), including no force-finalizer path; Nix unit and build tests owned by ADR-046-nix-configuration spec; canonical-example fixture validates every authored resource against the authoritative Host, Guest, and Provider schemas, resolves every artifact/resource reference, rejects unsupported fields, and proves the compiled Guest `spec` is field-for-field identical to the adjacent JSON | | Removal proof | Old `nixos-modules/manifest.nix`, `nixos-modules/bundle-artifacts.nix` emitters removed only after `bundle-emit.nix` produces equivalent-or-superseding output and all downstream consumers of the old bundle format are migrated | ### ADR046-cli-012 diff --git a/docs/specs/ADR-046-implementation-graph.json b/docs/specs/ADR-046-implementation-graph.json index e4f5980e5..f467e38c4 100644 --- a/docs/specs/ADR-046-implementation-graph.json +++ b/docs/specs/ADR-046-implementation-graph.json @@ -2,7 +2,7 @@ "adr" : "0046", "artifactKind" : "d2b-adr-implementation-graph", "counts" : { - "edges" : 1902, + "edges" : 1908, "maxTopologicalRank" : 22, "nodes" : 598, "specNodes" : 55, @@ -67,16 +67,41 @@ "to" : "ADR046-core-001", "type" : "file-overlap-order" }, + { + "from" : "ADR046-nix-014", + "to" : "ADR046-device-006", + "type" : "file-overlap-order" + }, { "from" : "ADR046-nix-019", "to" : "ADR046-cli-011", "type" : "file-overlap-order" }, + { + "from" : "ADR046-nix-031", + "to" : "ADR046-nix-019", + "type" : "file-overlap-order" + }, + { + "from" : "ADR046-qemu-media-017", + "to" : "ADR046-transport-unix-009", + "type" : "file-overlap-order" + }, { "from" : "ADR046-telem-011", "to" : "ADR046-core-001", "type" : "file-overlap-order" }, + { + "from" : "ADR046-transport-unix-009", + "to" : "ADR046-gpu-007", + "type" : "file-overlap-order" + }, + { + "from" : "ADR046-usbip-008", + "to" : "ADR046-qemu-media-017", + "type" : "file-overlap-order" + }, { "from" : "ADR046-zone-control-016", "to" : "ADR046-core-001", @@ -9457,6 +9482,11 @@ "to" : "ADR046-identities-001", "type" : "work-item-depends-on" }, + { + "from" : "ADR046-zone-control-014", + "to" : "ADR046-routing-011", + "type" : "work-item-depends-on" + }, { "from" : "ADR046-zone-control-014", "to" : "ADR046-zone-control-001", @@ -12800,7 +12830,7 @@ "ADR046-nix-014" ], "specId" : "ADR-046-cli-and-operations", - "topologicalRank" : 13, + "topologicalRank" : 14, "wave" : "W5" }, { @@ -15996,11 +16026,12 @@ "parallelGroup" : "wi:ADR-046-nix-configuration", "prerequisites" : [ "ADR-046-nix-configuration", + "ADR046-device-006", "ADR046-nix-001", "ADR046-nix-002" ], "specId" : "ADR-046-nix-configuration", - "topologicalRank" : 12, + "topologicalRank" : 13, "wave" : "W5" }, { @@ -16108,7 +16139,7 @@ "ADR046-nix-027" ], "specId" : "ADR-046-nix-configuration", - "topologicalRank" : 14, + "topologicalRank" : 15, "wave" : "W5" }, { @@ -16362,13 +16393,14 @@ "parallelGroup" : "wi:ADR-046-nix-configuration", "prerequisites" : [ "ADR-046-nix-configuration", + "ADR046-nix-019", "ADR046-provider-004", "ADR046-zone-control-019", "ADR046-zone-control-020", "ADR046-zone-control-024" ], "specId" : "ADR-046-nix-configuration", - "topologicalRank" : 14, + "topologicalRank" : 16, "wave" : "W5" }, { @@ -18028,10 +18060,11 @@ "prerequisites" : [ "ADR-046-provider-runtime-qemu-media", "ADR046-qemu-media-002", - "ADR046-qemu-media-003" + "ADR046-qemu-media-003", + "ADR046-transport-unix-009" ], "specId" : "ADR-046-provider-runtime-qemu-media", - "topologicalRank" : 13, + "topologicalRank" : 14, "wave" : "W6" }, { @@ -21700,10 +21733,11 @@ "parallelGroup" : "wi:ADR-046-provider-transport-unix", "prerequisites" : [ "ADR-046-provider-transport-unix", + "ADR046-gpu-007", "ADR046-transport-unix-001" ], "specId" : "ADR-046-provider-transport-unix", - "topologicalRank" : 12, + "topologicalRank" : 13, "wave" : "W6" }, { @@ -21918,11 +21952,12 @@ "parallelGroup" : "wi:ADR-046-provider-device-usbip", "prerequisites" : [ "ADR-046-provider-device-usbip", + "ADR046-qemu-media-017", "ADR046-usbip-003", "ADR046-zone-control-024" ], "specId" : "ADR-046-provider-device-usbip", - "topologicalRank" : 14, + "topologicalRank" : 15, "wave" : "W6" }, { @@ -22995,7 +23030,7 @@ { "blockers" : [], "destinations" : [ - "`nixos-modules/options-zones.nix`, `nixos-modules/resource-type-validators.nix`" + "`nixos-modules/options-zones.nix`, `nixos-modules/generated/resource-types.nix`, `nixos-modules/generated/options-zones-.nix`, `nixos-modules/resource-type-validators.nix`" ], "entryContracts" : [ "ADR-046-resources-zone-control" @@ -23003,10 +23038,11 @@ "exitGate" : "ADR046-W5 exit criteria (ADR-046-validation-and-delivery §4): every spec/work item in this wave Merged with clean destinations, all validators green, and the ten-role panel seal recorded", "id" : "ADR046-zone-control-014", "kind" : "work-item", - "owner" : "ADR046-zone-control-001; Nix module owner", + "owner" : "ADR046-zone-control-001, ADR046-routing-011; Nix module owner", "parallelGroup" : "wi:ADR-046-resources-zone-control", "prerequisites" : [ "ADR-046-resources-zone-control", + "ADR046-routing-011", "ADR046-zone-control-001" ], "specId" : "ADR-046-resources-zone-control", diff --git a/docs/specs/ADR-046-implementation-graph.md b/docs/specs/ADR-046-implementation-graph.md index 7216613a1..ddab5ac63 100644 --- a/docs/specs/ADR-046-implementation-graph.md +++ b/docs/specs/ADR-046-implementation-graph.md @@ -23,7 +23,7 @@ tie-break or omitted dependency is used. | Spec nodes | 55 | | Work-item nodes | 543 | | Total nodes | 598 | -| Edges | 1902 | +| Edges | 1908 | | Max topological rank | 22 | ## Waves (W0–W7) @@ -143,8 +143,13 @@ fully represented in the JSON. | `ADR046-core-001` | `ADR046-exec-013` | file-overlap-order | | `ADR046-core-001` | `ADR046-exec-015` | file-overlap-order | | `ADR046-core-001` | `ADR046-network-008` | file-overlap-order | +| `ADR046-device-006` | `ADR046-nix-014` | file-overlap-order | | `ADR046-cli-011` | `ADR046-nix-019` | file-overlap-order | +| `ADR046-nix-019` | `ADR046-nix-031` | file-overlap-order | +| `ADR046-transport-unix-009` | `ADR046-qemu-media-017` | file-overlap-order | | `ADR046-core-001` | `ADR046-telem-011` | file-overlap-order | +| `ADR046-gpu-007` | `ADR046-transport-unix-009` | file-overlap-order | +| `ADR046-qemu-media-017` | `ADR046-usbip-008` | file-overlap-order | | `ADR046-core-001` | `ADR046-zone-control-016` | file-overlap-order | | `ADR046-core-001` | `ADR046-zone-control-021` | file-overlap-order | | `ADR-046-resource-object-model` | `ADR-046-resource-api-and-authorization` | shared-contract | @@ -165,9 +170,12 @@ Only the listed `file-overlap-order` edges constrain shared files. Provider integration ordering that touches disjoint crate trees is not represented as file overlap. The former `wi:core-config-hub` is split into `wi:core-config-hub:w4` and `wi:core-config-hub:w5`; each parallel group is -single-wave. The two `assertions.nix` edges order only -`ADR046-nix-014` → `ADR046-cli-011` → `ADR046-nix-019`; all other files in -those work items retain their existing parallelism. +single-wave. The seven `assertions.nix` edges form the minimal per-wave chains +`ADR046-device-006` → `ADR046-nix-014` → `ADR046-cli-011` → +`ADR046-nix-019` → `ADR046-nix-031` in W5 and `ADR046-gpu-007` → +`ADR046-transport-unix-009` → `ADR046-qemu-media-017` → +`ADR046-usbip-008` in W6. W2 has one writer. These edges order only the shared +file; all other destinations retain their existing parallelism. ## Parallel groups @@ -276,7 +284,7 @@ those work items retain their existing parallelism. - `ADR046-provider-004` owns the common D098 Service/Binding base DTOs and schemas; the four implementation Providers own only strict extensions and controllers. - `ADR046-zone-control-024` owns the shared Core-derived `physical-usb-backing` tuple; both the security-key and USB effect DAGs depend on it. - Every `ADR046-security-key-*` dependency in `Dependency/owner` is encoded. The dependency subgraph is acyclic and uses no generator tie-break. -- Nine file-overlap barriers cover only the shared core +- Fourteen file-overlap barriers cover only the shared core configuration/cleanup files and `nixos-modules/assertions.nix`. Each appears both as a `file-overlap-order` edge and in the dependent node's `prerequisites`, so the diff --git a/docs/specs/ADR-046-resources-zone-control.md b/docs/specs/ADR-046-resources-zone-control.md index e7d568e8f..4e64053e1 100644 --- a/docs/specs/ADR-046-resources-zone-control.md +++ b/docs/specs/ADR-046-resources-zone-control.md @@ -4427,15 +4427,15 @@ Evidence class for all: `main-reuse-source`. | Field | Value | | --- | --- | | Work item ID | `ADR046-zone-control-014` | -| Dependency/owner | ADR046-zone-control-001; Nix module owner | +| Dependency/owner | ADR046-zone-control-001, ADR046-routing-011; Nix module owner | | Current source | `nixos-modules/options-realms.nix` (option schema/assertion conventions); `nixos-modules/bundle-artifacts.nix` (bundle emit pattern); `nixos-modules/provider-registry-v2-json.nix` (provider-registry JSON emit); `nixos-modules/assertions.nix:730` (assertion style for unsafe-local); `packages/d2b-realm-core/src/ids.rs` (`LABEL_PATTERN`, `MAX_ID_LEN`, `is_label()` — eval-time name validation target) | | Reuse source | None from main; eval-time validation is Nix-native | | Reuse action | create | -| Destination | `nixos-modules/options-zones.nix`, `nixos-modules/resource-type-validators.nix` | -| Detailed design | Declare the unified `d2b.zones..resources.` option tree with two sub-options: `type` (string, required) and `spec` (attrset, required), plus the Zone-level compiler-only `parentZone` scalar (plain Zone name, no default). Require `parentZone` on every non-root Zone and forbid it on `local-root`; resolve it against declared Zone keys, reject self-parent/conflicting module definitions/cycles, and cap each ancestry path at 16 Zone names. Compile the validated map into sealed allocator bootstrap topology; never emit it into the resource bundle or `Zone.spec`. Declare the `d2b.artifacts.` catalog option tree with `package` (types.package, required) and `type` (closed string enum, required); `d2b.artifacts` is a global option, not zone-local. For each core ResourceType (Zone, ZoneLink, Provider, Role, RoleBinding, Credential), a `genSchemaOptions` helper reads the corresponding ResourceTypeSchema JSON and emits per-type `spec` option declarations with correct Nix types, defaults, and documentation comments. Provider `spec.artifactId` is a plain string referencing the `d2b.artifacts` catalog; the `package` derivation is NOT a `spec` field. Non-core (Provider-registered) ResourceTypes are deferred to Phase 2 schema validation; Phase 1 accepts any non-empty `spec` attrset for non-core types. Implement all Phase 1 eval-time assertions (§14.10 Phase 1 table) via Nix `assert` and `lib.mkAssert`; `credentialRef` helper function producing `{ "$credentialRef": "Credential/" }` literals; `resourceRef` and `closedEnum` helpers; bootstrap provider exclusion assertions; reject operator-authored `type = "Zone"` resources; child-local ZoneLink assertions require `childZoneName` to equal the enclosing Zone key, reject a second uplink resource even when disabled, and reject any local-root uplink. The runtime creates `Zone/`, `Provider/system-core`, and `Provider/system-minijail` directly with `managedBy=controller`; none is emitted or inferred from the configuration bundle. `allowUnsafeLocal` maps to a dedicated Host admission gate (current `options-realms.nix:346`). Provider manifest-derived fields (`spec.exports`, `spec.components`, `spec.dependencies`, `spec.permissionClaims`, `spec.upgradePolicy`, `spec.restartPolicy`) are declared as read-only/not-settable options; setting them is an eval assertion error | -| Integration | Validated `parentZone` map feeds the allocator bootstrap sealer; unified `d2b.zones..resources.*` option tree is consumed by ADR046-zone-control-015 resource compiler; Zone controller (ADR046-zone-control-001) reads the resulting bundle; Provider package conventions come from ADR046-zone-control-003 | +| Destination | `nixos-modules/options-zones.nix`, `nixos-modules/generated/resource-types.nix`, `nixos-modules/generated/options-zones-.nix`, `nixos-modules/resource-type-validators.nix` | +| Detailed design | Consume the generator and registry established by ADR046-routing-011 to declare the unified `d2b.zones..resources.` option tree plus the Zone-level compiler-only `parentZone` scalar. The generated standard registry is exactly the canonical 19 types (`Zone`, `ZoneLink`, `Provider`, `Host`, `Guest`, `Process`, `EphemeralProcess`, `Network`, `Volume`, `Credential`, `Device`, `Endpoint`, `User`, `Role`, `RoleBinding`, `Quota`, `EmergencyPolicy`, `ResourceExport`, `ResourceImport`), and every standard type has a strict generated `spec` submodule carrying the schema's Nix types, defaults, bounds, and documentation. Installed Provider artifacts may append only signed qualified ResourceTypes whose strict schema has been verified and generated into the evaluated option set. `type` is selected from that closed combined registry: an unknown standard string, an unqualified extension, or a qualified type without an installed verified schema fails evaluation; there is no unrestricted string or free-form `spec` fallback. Require `parentZone` on every non-root Zone and forbid it on `local-root`; resolve it against declared Zone keys, reject self-parent/conflicting module definitions/cycles, and cap each ancestry path at 16 Zone names. Compile the validated map into sealed allocator bootstrap topology; never emit it into the resource bundle or `Zone.spec`. Declare the global `d2b.artifacts.` catalog with `package` (types.package, required) and `type` (closed enum, required). Provider `spec.artifactId` is a plain catalog ID; the derivation is not a `spec` field. Implement the Phase 1 cross-resource assertions (§14.10 Phase 1 table); retain `credentialRef`, `resourceRef`, and `closedEnum` helpers; reject operator-authored `Zone`; and enforce child-local ZoneLink topology. The runtime creates `Zone/`, `Provider/system-core`, and `Provider/system-minijail` with `managedBy=controller`; none is emitted or inferred from the configuration bundle. `allowUnsafeLocal` maps to the dedicated Host admission gate. Provider manifest-derived fields (`spec.exports`, `spec.components`, `spec.dependencies`, `spec.permissionClaims`, `spec.upgradePolicy`, `spec.restartPolicy`) are read-only and setting one is an eval error. | +| Integration | ADR046-routing-011 supplies the one canonical 19-type registry and generated option family; validated `parentZone` feeds the allocator bootstrap sealer; the closed `d2b.zones..resources.*` tree is consumed by ADR046-zone-control-015; the Zone controller (ADR046-zone-control-001) reads the resulting bundle; Provider package conventions come from ADR046-zone-control-003 | | Data migration | Replace `nixos-modules/options-realms.nix`-derived option trees once Zone controller is live and has reached parity | -| Validation | All Phase 1 eval tests in §15.8 (`nix-eval-name-regex-enforced`, `nix-eval-verb-closed-enum`, `nix-eval-roleref-format`, `nix-eval-subject-type-restricted`, `nix-eval-no-duplicate-subjects`, `nix-eval-bootstrap-provider-rejected`, `nix-eval-provider-missing-artifact-id`, `nix-eval-artifact-id-not-in-catalog`, `nix-eval-artifact-wrong-type`, `nix-eval-artifact-id-format`, `nix-eval-credentialref-declared`, `nix-eval-dollar-key-rejected`, all five `nix-eval-parent-zone-*` vectors, `nix-eval-zonelink-child-name-mismatch-rejected`, `nix-eval-zonelink-second-uplink-rejected`, `nix-eval-zonelink-limits-maxpendingintents-bound`) | +| Validation | All Phase 1 eval tests in §15.8 (`nix-eval-name-regex-enforced`, `nix-eval-verb-closed-enum`, `nix-eval-roleref-format`, `nix-eval-subject-type-restricted`, `nix-eval-no-duplicate-subjects`, `nix-eval-bootstrap-provider-rejected`, `nix-eval-provider-missing-artifact-id`, `nix-eval-artifact-id-not-in-catalog`, `nix-eval-artifact-wrong-type`, `nix-eval-artifact-id-format`, `nix-eval-credentialref-declared`, `nix-eval-dollar-key-rejected`, all five `nix-eval-parent-zone-*` vectors, `nix-eval-zonelink-child-name-mismatch-rejected`, `nix-eval-zonelink-second-uplink-rejected`, `nix-eval-zonelink-limits-maxpendingintents-bound`); drift test asserts the standard registry and generated option modules cover exactly all 19 canonical types; negative evals reject unknown strings, unqualified extensions, unsigned or uninstalled qualified types, and unknown `spec` fields; a positive fixture admits an installed signed qualified type and validates its strict generated schema | | Removal proof | `nixos-modules/options-realms.nix`, `nixos-modules/realm-controller-config-json.nix`, `nixos-modules/realm-identity-config-json.nix` deleted after Zone controller and resource compiler reach full parity; `nixos-modules/assertions.nix` lines referencing `allowUnsafeLocal`/realm names removed after Host admission validation replaces them | ### ADR046-zone-control-015 diff --git a/docs/specs/ADR-046-spec-set.json b/docs/specs/ADR-046-spec-set.json index a2a222919..c467c9bbf 100644 --- a/docs/specs/ADR-046-spec-set.json +++ b/docs/specs/ADR-046-spec-set.json @@ -13,7 +13,7 @@ "ADR-046-terminology-and-identities" ], "path" : "docs/specs/ADR-046-cli-and-operations.md", - "sha256" : "c5b81aa9ce9638020f6991d4a1d9793f9f51659d1ecd16f720beacb33cb7ea6a", + "sha256" : "54cb3d03cf39c5c40475e176f5a9acd306dc550709427ed180cfa7863d5eb615", "specId" : "ADR-046-cli-and-operations", "status" : "Proposed", "supersedes" : "Current v3 `d2b` CLI contract (`packages/d2b/src/lib.rs` at baseline)", @@ -1014,7 +1014,7 @@ "ADR-046-terminology-and-identities" ], "path" : "docs/specs/ADR-046-resources-zone-control.md", - "sha256" : "c622e9286f85d882acd7fcada9920f5f3ab447d645810abce37c67dbea3e5988", + "sha256" : "6116228dd5f004891aa918bd76015fd202d0fc953e428d09026a883d5834b5bf", "specId" : "ADR-046-resources-zone-control", "status" : "Proposed", "supersedes" : null, diff --git a/docs/specs/ADR-046-work-items.json b/docs/specs/ADR-046-work-items.json index 8e63f2ecb..202f7094c 100644 --- a/docs/specs/ADR-046-work-items.json +++ b/docs/specs/ADR-046-work-items.json @@ -914,7 +914,7 @@ "reuseSource" : null, "specId" : "ADR-046-cli-and-operations", "specPath" : "docs/specs/ADR-046-cli-and-operations.md", - "validation" : "Runtime integration: all CLI-visible cleanup/status/rollback/gc/audit tests (§CLI-visible tests for activation and cleanup), including no force-finalizer path; Nix unit and build tests owned by ADR-046-nix-configuration spec", + "validation" : "Runtime integration: all CLI-visible cleanup/status/rollback/gc/audit tests (§CLI-visible tests for activation and cleanup), including no force-finalizer path; Nix unit and build tests owned by ADR-046-nix-configuration spec; canonical-example fixture validates every authored resource against the authoritative Host, Guest, and Provider schemas, resolves every artifact/resource reference, rejects unsupported fields, and proves the compiled Guest `spec` is field-for-field identical to the adjacent JSON", "workItemId" : "ADR046-cli-011" }, { @@ -7985,16 +7985,16 @@ { "currentSource" : "`nixos-modules/options-realms.nix` (option schema/assertion conventions); `nixos-modules/bundle-artifacts.nix` (bundle emit pattern); `nixos-modules/provider-registry-v2-json.nix` (provider-registry JSON emit); `nixos-modules/assertions.nix:730` (assertion style for unsafe-local); `packages/d2b-realm-core/src/ids.rs` (`LABEL_PATTERN`, `MAX_ID_LEN`, `is_label()` — eval-time name validation target)", "dataMigration" : "Replace `nixos-modules/options-realms.nix`-derived option trees once Zone controller is live and has reached parity", - "dependencyOwner" : "ADR046-zone-control-001; Nix module owner", - "destination" : "`nixos-modules/options-zones.nix`, `nixos-modules/resource-type-validators.nix`", - "detailedDesign" : "Declare the unified `d2b.zones..resources.` option tree with two sub-options: `type` (string, required) and `spec` (attrset, required), plus the Zone-level compiler-only `parentZone` scalar (plain Zone name, no default). Require `parentZone` on every non-root Zone and forbid it on `local-root`; resolve it against declared Zone keys, reject self-parent/conflicting module definitions/cycles, and cap each ancestry path at 16 Zone names. Compile the validated map into sealed allocator bootstrap topology; never emit it into the resource bundle or `Zone.spec`. Declare the `d2b.artifacts.` catalog option tree with `package` (types.package, required) and `type` (closed string enum, required); `d2b.artifacts` is a global option, not zone-local. For each core ResourceType (Zone, ZoneLink, Provider, Role, RoleBinding, Credential), a `genSchemaOptions` helper reads the corresponding ResourceTypeSchema JSON and emits per-type `spec` option declarations with correct Nix types, defaults, and documentation comments. Provider `spec.artifactId` is a plain string referencing the `d2b.artifacts` catalog; the `package` derivation is NOT a `spec` field. Non-core (Provider-registered) ResourceTypes are deferred to Phase 2 schema validation; Phase 1 accepts any non-empty `spec` attrset for non-core types. Implement all Phase 1 eval-time assertions (§14.10 Phase 1 table) via Nix `assert` and `lib.mkAssert`; `credentialRef` helper function producing `{ \"$credentialRef\": \"Credential/\" }` literals; `resourceRef` and `closedEnum` helpers; bootstrap provider exclusion assertions; reject operator-authored `type = \"Zone\"` resources; child-local ZoneLink assertions require `childZoneName` to equal the enclosing Zone key, reject a second uplink resource even when disabled, and reject any local-root uplink. The runtime creates `Zone/`, `Provider/system-core`, and `Provider/system-minijail` directly with `managedBy=controller`; none is emitted or inferred from the configuration bundle. `allowUnsafeLocal` maps to a dedicated Host admission gate (current `options-realms.nix:346`). Provider manifest-derived fields (`spec.exports`, `spec.components`, `spec.dependencies`, `spec.permissionClaims`, `spec.upgradePolicy`, `spec.restartPolicy`) are declared as read-only/not-settable options; setting them is an eval assertion error", - "integration" : "Validated `parentZone` map feeds the allocator bootstrap sealer; unified `d2b.zones..resources.*` option tree is consumed by ADR046-zone-control-015 resource compiler; Zone controller (ADR046-zone-control-001) reads the resulting bundle; Provider package conventions come from ADR046-zone-control-003", + "dependencyOwner" : "ADR046-zone-control-001, ADR046-routing-011; Nix module owner", + "destination" : "`nixos-modules/options-zones.nix`, `nixos-modules/generated/resource-types.nix`, `nixos-modules/generated/options-zones-.nix`, `nixos-modules/resource-type-validators.nix`", + "detailedDesign" : "Consume the generator and registry established by ADR046-routing-011 to declare the unified `d2b.zones..resources.` option tree plus the Zone-level compiler-only `parentZone` scalar. The generated standard registry is exactly the canonical 19 types (`Zone`, `ZoneLink`, `Provider`, `Host`, `Guest`, `Process`, `EphemeralProcess`, `Network`, `Volume`, `Credential`, `Device`, `Endpoint`, `User`, `Role`, `RoleBinding`, `Quota`, `EmergencyPolicy`, `ResourceExport`, `ResourceImport`), and every standard type has a strict generated `spec` submodule carrying the schema's Nix types, defaults, bounds, and documentation. Installed Provider artifacts may append only signed qualified ResourceTypes whose strict schema has been verified and generated into the evaluated option set. `type` is selected from that closed combined registry: an unknown standard string, an unqualified extension, or a qualified type without an installed verified schema fails evaluation; there is no unrestricted string or free-form `spec` fallback. Require `parentZone` on every non-root Zone and forbid it on `local-root`; resolve it against declared Zone keys, reject self-parent/conflicting module definitions/cycles, and cap each ancestry path at 16 Zone names. Compile the validated map into sealed allocator bootstrap topology; never emit it into the resource bundle or `Zone.spec`. Declare the global `d2b.artifacts.` catalog with `package` (types.package, required) and `type` (closed enum, required). Provider `spec.artifactId` is a plain catalog ID; the derivation is not a `spec` field. Implement the Phase 1 cross-resource assertions (§14.10 Phase 1 table); retain `credentialRef`, `resourceRef`, and `closedEnum` helpers; reject operator-authored `Zone`; and enforce child-local ZoneLink topology. The runtime creates `Zone/`, `Provider/system-core`, and `Provider/system-minijail` with `managedBy=controller`; none is emitted or inferred from the configuration bundle. `allowUnsafeLocal` maps to the dedicated Host admission gate. Provider manifest-derived fields (`spec.exports`, `spec.components`, `spec.dependencies`, `spec.permissionClaims`, `spec.upgradePolicy`, `spec.restartPolicy`) are read-only and setting one is an eval error.", + "integration" : "ADR046-routing-011 supplies the one canonical 19-type registry and generated option family; validated `parentZone` feeds the allocator bootstrap sealer; the closed `d2b.zones..resources.*` tree is consumed by ADR046-zone-control-015; the Zone controller (ADR046-zone-control-001) reads the resulting bundle; Provider package conventions come from ADR046-zone-control-003", "removalProof" : "`nixos-modules/options-realms.nix`, `nixos-modules/realm-controller-config-json.nix`, `nixos-modules/realm-identity-config-json.nix` deleted after Zone controller and resource compiler reach full parity; `nixos-modules/assertions.nix` lines referencing `allowUnsafeLocal`/realm names removed after Host admission validation replaces them", "reuseAction" : "create", "reuseSource" : null, "specId" : "ADR-046-resources-zone-control", "specPath" : "docs/specs/ADR-046-resources-zone-control.md", - "validation" : "All Phase 1 eval tests in §15.8 (`nix-eval-name-regex-enforced`, `nix-eval-verb-closed-enum`, `nix-eval-roleref-format`, `nix-eval-subject-type-restricted`, `nix-eval-no-duplicate-subjects`, `nix-eval-bootstrap-provider-rejected`, `nix-eval-provider-missing-artifact-id`, `nix-eval-artifact-id-not-in-catalog`, `nix-eval-artifact-wrong-type`, `nix-eval-artifact-id-format`, `nix-eval-credentialref-declared`, `nix-eval-dollar-key-rejected`, all five `nix-eval-parent-zone-*` vectors, `nix-eval-zonelink-child-name-mismatch-rejected`, `nix-eval-zonelink-second-uplink-rejected`, `nix-eval-zonelink-limits-maxpendingintents-bound`)", + "validation" : "All Phase 1 eval tests in §15.8 (`nix-eval-name-regex-enforced`, `nix-eval-verb-closed-enum`, `nix-eval-roleref-format`, `nix-eval-subject-type-restricted`, `nix-eval-no-duplicate-subjects`, `nix-eval-bootstrap-provider-rejected`, `nix-eval-provider-missing-artifact-id`, `nix-eval-artifact-id-not-in-catalog`, `nix-eval-artifact-wrong-type`, `nix-eval-artifact-id-format`, `nix-eval-credentialref-declared`, `nix-eval-dollar-key-rejected`, all five `nix-eval-parent-zone-*` vectors, `nix-eval-zonelink-child-name-mismatch-rejected`, `nix-eval-zonelink-second-uplink-rejected`, `nix-eval-zonelink-limits-maxpendingintents-bound`); drift test asserts the standard registry and generated option modules cover exactly all 19 canonical types; negative evals reject unknown strings, unqualified extensions, unsigned or uninstalled qualified types, and unknown `spec` fields; a positive fixture admits an installed signed qualified type and validates its strict generated schema", "workItemId" : "ADR046-zone-control-014" }, { From 4c067328ba356d787a25a3988a028e2acbd0f6ad Mon Sep 17 00:00:00 2001 From: John Vicondoa Date: Fri, 24 Jul 2026 05:45:52 -0700 Subject: [PATCH 087/115] docs: correct ADR 0046 count and anchors --- docs/specs/ADR-046-cli-and-operations.md | 2 +- docs/specs/ADR-046-streamline.md | 6 +++--- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/docs/specs/ADR-046-cli-and-operations.md b/docs/specs/ADR-046-cli-and-operations.md index c6bb373f1..1be4e3d41 100644 --- a/docs/specs/ADR-046-cli-and-operations.md +++ b/docs/specs/ADR-046-cli-and-operations.md @@ -2573,7 +2573,7 @@ baseline (only a deprecation notice remains at `lib.rs:2424`). | Reuse source | main `a1cc0b2d` — copy/adapt: (1) `packages/d2b-client/src/client.rs` `ConnectedClient::invoke()` and `ConnectedClient::open_server_stream()` — copy unchanged; used for Endpoint Get/List/Status and Watch; (2) `packages/d2b-client/src/session.rs` `NamedStream` — copy unchanged; Watch stream output; (3) the ADR046-cli-010 generic resource-verb dispatch — reuse for `d2b endpoint get/list/watch/status`; the `d2b endpoint resolve` projection verb is net-new. No raw transport/FD handle type is imported into the CLI; resolution to a private transport/FD remains inside Core/ProviderSupervisor via EffectPort/LaunchTicket | | Reuse action | adapt | | Destination | `packages/d2b/src/endpoint.rs` (`d2b endpoint get/list/watch/status/resolve`) | -| Detailed design | Add `Endpoint` to the frozen standard ResourceType set (17 types; local ResourceRef parser accepts `Endpoint/` with no Zone round-trip). Implement `d2b endpoint get/list/watch/status` over the ADR046-cli-010 generic verbs and `d2b endpoint resolve ` as a provider-neutral resolution projection printing `producerRef`, `endpointClass`, transport class, readiness, and capability/locality observations. Endpoint output is the base envelope only (base spec + base status per D092); any `status.provider` projection is bounded, redacted, and deny-unknown. **No raw locator** (path/address/CID/port/fd/credential) appears in any CLI field or the `resolve` projection. `status.update` currency and `d2b upgrade Endpoint/` visibility follow the standard verbs and D091. `create`/`update-spec`/`delete` are rejected on the operator surface except for statically Nix/API-authored Endpoints whose schema permits it. | +| Detailed design | Add `Endpoint` to the frozen standard ResourceType set (19 types; local ResourceRef parser accepts `Endpoint/` with no Zone round-trip). Implement `d2b endpoint get/list/watch/status` over the ADR046-cli-010 generic verbs and `d2b endpoint resolve ` as a provider-neutral resolution projection printing `producerRef`, `endpointClass`, transport class, readiness, and capability/locality observations. Endpoint output is the base envelope only (base spec + base status per D092); any `status.provider` projection is bounded, redacted, and deny-unknown. **No raw locator** (path/address/CID/port/fd/credential) appears in any CLI field or the `resolve` projection. `status.update` currency and `d2b upgrade Endpoint/` visibility follow the standard verbs and D091. `create`/`update-spec`/`delete` are rejected on the operator surface except for statically Nix/API-authored Endpoints whose schema permits it. | | Integration | ZoneContext → `ConnectedClient` → resource API for `Endpoint`; consumers reference endpoints only through `Endpoint/` ResourceRefs | | Data migration | None — full d2b 3.0 reset; no prior state to migrate | | Validation | `Endpoint` accepted as a frozen standard type; `Endpoint/` parses locally without a Zone round-trip; `d2b endpoint get/list/watch/status/resolve` parse and route correctly; `get`/`resolve` output carries no raw path/address/CID/port/fd/credential; provider status projection is bounded/redacted; `d2b list Endpoint --updates` and `d2b upgrade Endpoint/` expose `status.update` currency and disruption; operator `create`/`update-spec`/`delete` rejected for controller-owned Endpoints | diff --git a/docs/specs/ADR-046-streamline.md b/docs/specs/ADR-046-streamline.md index 056cc9d92..9f2d65228 100644 --- a/docs/specs/ADR-046-streamline.md +++ b/docs/specs/ADR-046-streamline.md @@ -285,7 +285,7 @@ already-diverged branch rather than through a rebase onto a reconciled foundation commit, later Nix-authoring-alignment sweeps (`827e874f`/`6bcb60b3` "align Nix authoring contracts") had to re-touch files the corrective commits had already changed, which is exactly the shape of an avoidable cherry-pick/ -rebase conflict this spec's [ADR046-streamline-010](#adr046-streamline-010--stale-baseparent-reconcile-helper) +rebase conflict this spec's [ADR046-streamline-010](#adr046-streamline-010--stale-basecurrent-parent-reconcile-helper) targets. ### F12 — Manual global `rg`-shaped scans instead of generated lints @@ -368,11 +368,11 @@ computed mechanically, not estimated. | --- | --- | --- | --- | | Correction rounds | Count of commits matching `fix\|correct\|realign\|harmonize` (case-insensitive) reachable from any `adr0046-*` branch, divided by total commits in the same set | `git log --all --oneline --grep=... -i \| wc -l` over `git rev-list --all --count` restricted to `adr0046-*` refs | 768 / 2,478 ≈ 31% (see F11) | | Schema violations | Count of fenced-YAML/JSON blocks under `docs/specs/**` failing [ADR046-streamline-003](#adr046-streamline-003--markdown-fenced-yamljson-extractor-and-schema-validator) validation, per commit | `xtask spec-schema-check --format json` violation count | Not measured before this tool exists (F14); target 0 at every commit once adopted | -| Conflict count | Count of `git rebase`/`git town sync` conflict hunks reported by [ADR046-streamline-010](#adr046-streamline-010--stale-baseparent-reconcile-helper) per dossier restack | Reconcile-helper JSON report `conflicts[]` length | Not measured before this tool exists; F1/F11 show at least 5 dossier branches diverged from a foundation point superseded by 6+ later foundation commits | +| Conflict count | Count of `git rebase`/`git town sync` conflict hunks reported by [ADR046-streamline-010](#adr046-streamline-010--stale-basecurrent-parent-reconcile-helper) per dossier restack | Reconcile-helper JSON report `conflicts[]` length | Not measured before this tool exists; F1/F11 show at least 5 dossier branches diverged from a foundation point superseded by 6+ later foundation commits | | Handoff completeness | Fraction of integrator-merged commits whose commit trailer references a [ADR046-streamline-011](#adr046-streamline-011--agent-handoff-manifest) manifest with non-empty `assigned_files`, `commit_sha`, `test_result`, and `base_sha` | Task-DB↔git import report | 0% before adoption (F10: the two identical-diff commit pairs carry no such trailer) | | Ready/launched ratio | Count of todo-tracked scopes with all dependencies `done` ("ready"), divided by count of scopes with an open worktree/branch ("launched"), per [ADR046-streamline-013](#adr046-streamline-013--anti-serialization-readylaunchedblocker-report) | Anti-serialization report | Not measured before this tool exists | | Time-to-green | Wall-clock time from a dossier branch's first commit to its `xtask spec-schema-check` + cross-spec lint passing with zero violations | Task-DB timestamps + lint pass timestamp | Not measured before this tool exists; F1/F11 imply multiple hours-to-days of re-derivation per affected dossier | -| Disk usage | Aggregate size of `packages/target/` and stale worktrees reported by [ADR046-streamline-018](#adr046-streamline-018--worktreediskclosetarget-cleanup-reporting), never auto-deleted | `du -sh` per reported path, summed | Not measured before this tool exists | +| Disk usage | Aggregate size of `packages/target/` and stale worktrees reported by [ADR046-streamline-018](#adr046-streamline-018--worktreedisktarget-cleanup-reporting), never auto-deleted | `du -sh` per reported path, summed | Not measured before this tool exists | ## Permanent methodology after Accepted From ef824b26bea762a771411d66631ddb46b037d321 Mon Sep 17 00:00:00 2001 From: John Vicondoa Date: Fri, 24 Jul 2026 05:48:04 -0700 Subject: [PATCH 088/115] docs: correct ADR 046 networking authority Close networking rerun findings N3 and N4 by assigning the USBIP relay to its host-global provider Endpoint and using the actual network broker operation names. Point pidfd adoption evidence at its implementation work item. --- docs/specs/ADR-046-resources-zone-control.md | 6 +++--- docs/specs/ADR-046-security-and-threat-model.md | 4 ++-- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/docs/specs/ADR-046-resources-zone-control.md b/docs/specs/ADR-046-resources-zone-control.md index 4e64053e1..e36465619 100644 --- a/docs/specs/ADR-046-resources-zone-control.md +++ b/docs/specs/ADR-046-resources-zone-control.md @@ -2238,7 +2238,7 @@ so the index admits exactly one owner across all Zones on the host. | Physical TPM | physical-device (Host-global) | exactly-one (host singleton) | `Provider/device-tpm` | exclusive | forbidden | | Physical USB backing | physical-device (Host-global) | zero-or-one per Core-derived trusted identity digest | Any authority Service implemented by a USB or security-key Provider, initially `Provider/device-usbip` and `Provider/device-security-key` | exclusive through the identical `(Host, physical-usb-backing, opaqueKeyDigest)` tuple | forbidden directly; a policy-gated semantic USB Service or security-key Service may mediate it | | `usbip-host` kernel module | host (Host-global) | exactly-one host-global | `Provider/device-usbip` | exclusive | forbidden | -| Per-Network USBIP listener + firewall | zone | exactly-one per `Network` | `Network` authority | exclusive | forbidden (fixed port becomes an `Endpoint`) | +| Per-Network USBIP listener + firewall | host (Host-global) | exactly-one per Core-derived Network UID/signed-policy-port digest | `Provider/device-usbip` relay `Endpoint` (never `Network` authority) | multiplexed; conflict `usbip-network-relay-authority-conflict` | forbidden | | External NIC / macvtap `parentInterface` | physical-device (Host-global) | zero-or-one per interface | `Network`/`Provider/network-local` | `passthru` **globally exclusive across all Zones**; `bridge`/`private`/`vepa` per explicit policy | forbidden | | Host-shared KVM (`/dev/kvm`) | host (Host-global) | shared grant, one grant authority | **`Provider/system-core`** (host substrate/effect authority) | shared | forbidden | | Host-shared vhost-vsock (`/dev/vhost-vsock`) | host (Host-global) | shared grant, one grant authority | **`Provider/system-core`** | shared | forbidden | @@ -4576,10 +4576,10 @@ Evidence class for all: `main-reuse-source`. | Reuse source | Core authority index (ADR046-zone-control-019) | | Reuse action | adapt | | Destination | `packages/d2b-core-controller/src/authority.rs` (Host-global index scope + hardware admission) | -| Detailed design | Extend the core authority index so `host`, `physical-device`, `seat`, and `external-service` authorities are keyed **Host-global** (`(Host, authorityClass, opaqueKeyDigest)`), admitting exactly one owner across all Zones on the host, while `zone`-scoped authorities stay Zone-local. Enforce the §8B.3 hardware rows: GPU full-device exclusive vs render-node shared; per-Guest swtpm and physical TPM exclusive (state never wiped); one Core-derived `physical-usb-backing/v1` identity digest claimed through the exact `(Host, physical-usb-backing, opaqueKeyDigest)` tuple by every USB or security-key implementation before effects, plus the separate host-global `usbip-host` module; macvtap/NIC `parentInterface` `passthru` globally exclusive across all Zones; host-shared `/dev/kvm` and `/dev/vhost-vsock` as `Provider/system-core` grants (no 28th Provider, no `kvm` busClass); globally-unique vsock CID; fixed listener ports as `Endpoint`s; host store + per-Guest store-view writer; Network TAP/bridge. A second Zone claiming the same physical backing receives `physical-usb-backing-conflict` before any open, bind, withhold, module, relay, or attachment effect; restart adopts by `ownerProof`; Guest-stop drains dependent leases. GPU-owned `udmabuf`/video and per-session `vhost-vsock` tokens stay authority subresources/DeviceGrants (not resources/Providers). Primary reuse disposition: `adapt`. Preserved source-plan detail: net-new (Host-global index scope for host/physical-device authorities). | +| Detailed design | Extend the core authority index so `host`, `physical-device`, `seat`, and `external-service` authorities are keyed **Host-global** (`(Host, authorityClass, opaqueKeyDigest)`), admitting exactly one owner across all Zones on the host, while `zone`-scoped authorities stay Zone-local. Enforce the §8B.3 hardware rows: GPU full-device exclusive vs render-node shared; per-Guest swtpm and physical TPM exclusive (state never wiped); one Core-derived `physical-usb-backing/v1` identity digest claimed through the exact `(Host, physical-usb-backing, opaqueKeyDigest)` tuple by every USB or security-key implementation before effects, plus the separate host-global `usbip-host` module and the Host-global `Provider/device-usbip` relay `Endpoint`, exactly one per Core-derived Network UID/signed-policy-port digest with multiplexed arbitration and `usbip-network-relay-authority-conflict` on a second owner; macvtap/NIC `parentInterface` `passthru` globally exclusive across all Zones; host-shared `/dev/kvm` and `/dev/vhost-vsock` as `Provider/system-core` grants (no 28th Provider, no `kvm` busClass); globally-unique vsock CID; other fixed listener ports as `Endpoint`s; host store + per-Guest store-view writer; Network TAP/bridge. A second Zone claiming the same physical backing receives `physical-usb-backing-conflict` before any open, bind, withhold, module, relay, or attachment effect; restart adopts by `ownerProof`; Guest-stop drains dependent leases. GPU-owned `udmabuf`/video and per-session `vhost-vsock` tokens stay authority subresources/DeviceGrants (not resources/Providers). Primary reuse disposition: `adapt`. Preserved source-plan detail: net-new (Host-global index scope for host/physical-device authorities). | | Integration | Core authority index (ADR046-zone-control-019); Provider cardinality admission (ADR046-zone-control-022); `Provider/system-core` KVM/vhost-vsock grant; `Provider/device-*` and `Network` authorities | | Data migration | None — full d2b 3.0 reset | -| Validation | Two Zones on one host cannot both claim one GPU/TPM/USB/`/dev/kvm`/passthru NIC/vsock CID/fixed port — second is `duplicateConflict`; security-key and USB implementations resolving the same physical token submit byte-identical `physical-usb-backing` tuple keys and the loser receives `physical-usb-backing-conflict` before any effect; Provider-private authority classes/digests cannot bypass the collision; render-node shared admits bounded holders; per-Guest swtpm exclusive and marker never wiped; host-global adoption by `ownerProof`; hardware D096 exportability (GPU/KVM/TPM/store/macvtap non-exportable; semantic USB policy-gated); fast hermetic with fakes | +| Validation | Two Zones on one host cannot both claim one GPU/TPM/USB/`/dev/kvm`/passthru NIC/vsock CID/fixed port — second is `duplicateConflict`; security-key and USB implementations resolving the same physical token submit byte-identical `physical-usb-backing` tuple keys and the loser receives `physical-usb-backing-conflict` before any effect; a second USBIP relay `Endpoint` for one Core-derived Network UID/signed-policy-port digest receives `usbip-network-relay-authority-conflict`, while multiple admitted Services share the multiplexed owner and no `Network` authority owns the listener/firewall; Provider-private authority classes/digests cannot bypass the collision; render-node shared admits bounded holders; per-Guest swtpm exclusive and marker never wiped; host-global adoption by `ownerProof`; hardware D096 exportability (GPU/KVM/TPM/store/macvtap non-exportable; semantic USB policy-gated); fast hermetic with fakes | | Removal proof | Not applicable (new surface) | --- diff --git a/docs/specs/ADR-046-security-and-threat-model.md b/docs/specs/ADR-046-security-and-threat-model.md index 07e35a484..49a82a2da 100644 --- a/docs/specs/ADR-046-security-and-threat-model.md +++ b/docs/specs/ADR-046-security-and-threat-model.md @@ -759,7 +759,7 @@ Network/Device Providers themselves. | Process | `MinijailProcessEffectPort` | `Provider/system-minijail` controller + `d2b-priv-broker` `SpawnRunner` | Compile-time dependency audit: the Provider crate imports no `d2b.broker.v3` service/client/DTO (`ADR-046-provider-system-minijail.md` lines 1621-1628) | | Process (systemd) | `SystemdProcessEffectPort` | `Provider/system-systemd` controller via D-Bus transient unit API | Controller never connects to the systemd D-Bus socket directly and never calls `systemctl` as a subprocess | | Volume | `VolumeEffectPort` | `Provider/volume-local` controller + broker `ProvisionLayoutEntry`/`RepairLayoutEntry`/`CleanupLayoutEntry`/`RotateSealingKey`/`PrepareSwtpmDir` | "The controller process holds no claim that grants access to raw host paths" (`ADR-046-provider-volume-local.md` lines 1739-1776) | -| Network | `NetworkEffectPort` | `Provider/network-local` controller + broker `CreateBridge`/`DeclareTap`/`ApplyNftables`/`ApplySysctls` | "The controller holds no broker role and no `network-admin` capability" (`ADR-046-provider-network-local.md` lines 1680-1682) | +| Network | `NetworkEffectPort` | `Provider/network-local` controller + broker `CreateBridge`/`CreatePersistentTap`/`SetBridgePortFlags`/`ApplyNftables`/`ApplySysctl` | "The controller holds no broker role and no `network-admin` capability" (`ADR-046-provider-network-local.md` lines 1680-1682) | | Device (vsock) | `VsockEffectPort` | Zone runtime `LiveVsockEffectPort` | `tokio-vsock` is not a dependency of `transport-vsock` (INV-VSOCK-004) | | Cloud (ARM/ACA) | `AzureEffectPort` | The cloud runtime Provider's own controller, confined to the gateway Guest | All calls non-blocking; `AzureOperationHandle` is opaque, max 256 bytes | @@ -916,7 +916,7 @@ privileged executor (`ADR-046-provider-system-minijail.md` §7.4, - Pidfd identity for adoption additionally requires live launch evidence via `/proc//fdinfo`, a same-kernel-object check, and a double-read race guard on `/proc/self/fdinfo/` (`pidfd-double-read-race-guard-detects-pid-reuse`, - `ADR-046-resources-zone-control.md` lines 3553-3559). + [ADR046-zone-control-012](ADR-046-resources-zone-control.md#adr046-zone-control-012)). **Credit/quota.** `LaunchTicket` issuance is bounded per Zone (max 64 concurrent in-flight `LaunchTicket`s for `system-minijail`, From 744b06a01299d45c99c6600c248818f835fc9fef Mon Sep 17 00:00:00 2001 From: John Vicondoa Date: Fri, 24 Jul 2026 05:52:05 -0700 Subject: [PATCH 089/115] docs: correct ZoneLink reset ownership --- docs/specs/ADR-046-reset-and-cutover.md | 115 ++++++++++++++---------- 1 file changed, 67 insertions(+), 48 deletions(-) diff --git a/docs/specs/ADR-046-reset-and-cutover.md b/docs/specs/ADR-046-reset-and-cutover.md index 9bdd5294f..c1c5d1409 100644 --- a/docs/specs/ADR-046-reset-and-cutover.md +++ b/docs/specs/ADR-046-reset-and-cutover.md @@ -88,7 +88,7 @@ without an open decision: | **Guest reset** | Deletion and re-creation of exactly one `Guest/` resource and its owned children (its runtime Processes, its store-view/TPM Volumes unless explicitly retained). Does not touch the Zone, Providers, or other Guests. | | **Cutover checkpoint** | One phase-boundary durable record in the [cutover journal](#crashpower-lossretryidempotency-journals), written with the ADR 0034 atomic-persistence sequence (temp file, `fsync`, rename, parent `fsync`). | | **Incident hold (cutover-wide)** | An operator-set hold that blocks every destructive disposition step and the entire [old artifact/unit/schema removal gate](#old-artifactunitschema-removal-gates) sequence, built on the same `IncidentHold` condition semantics `ADR-046-provider-state` defines for a Volume, extended here to apply Zone-wide during a cutover window. | -| **Gateway Guest** | A Guest whose runtime Provider hosts a nested child Zone (a "gateway guest" in ADR 0032 terms) reachable only through a parent `ZoneLink`. The parent Zone never holds the gateway Guest's Credential, audit, or ZoneLink-internal session state — see [Gateway Guest credential/audit custody](#gateway-guest-credentialaudit-custody). | +| **Gateway Guest** | A Guest whose runtime Provider hosts a nested child Zone (a "gateway guest" in ADR 0032 terms). The child Zone authors and stores its one local uplink `ZoneLink`, whose `spec.childZoneName` matches that child Zone's self-name; the compiler-only `parentZone` setting selects the provisioning allocator. The parent keeps only sealed allocator/route state, with no reciprocal parent-store `ZoneLink` or parent-side `ZoneLink` handler, and never holds the gateway Guest's Credential or audit — see [Gateway Guest credential/audit custody](#gateway-guest-credentialaudit-custody). | Every term above composes with, and does not redefine, the shared vocabulary in `ADR-046-terminology-and-identities` (Zone, ResourceRef, Provider, Host, @@ -498,15 +498,19 @@ node registry, provider configuration, and audit trail live **only** inside its dedicated gateway guest VM; the host never holds them (`CredentialCustody::None` for host-local vs. `CredentialCustody::GatewayGuest` for relay-backed, per -`d2b-realm-router/src/service_v2.rs`, and `ADR-046-zone-routing` §3's -`CredentialCustody` evidence). This cutover preserves that boundary exactly, -translated into ZoneLink terms: +`d2b-realm-router/src/service_v2.rs`, and `ADR-046-zone-routing` +"d2b-realm-router/src/service_v2.rs — RealmServiceServer" evidence). This +cutover preserves that boundary exactly, translated into ZoneLink terms: 1. A pre-cutover gateway-backed realm becomes, post-cutover, a Guest whose - runtime Provider hosts a **nested child Zone**, reachable from the parent - only through a `ZoneLink/` resource in the parent Zone (per - `ADR-046-resources-zone-control` §3 and `ADR-046-terminology-and-identities` - "Zone"). + runtime Provider hosts a **nested child Zone**. That child authors and stores + its one local uplink `ZoneLink/`; `spec.childZoneName` must equal the + child Zone's self-name. The enclosing child Zone's compiler-only + `parentZone` setting selects the provisioning parent allocator, which keeps + only sealed allocator/route state and creates no reciprocal parent-store + resource or parent-side `ZoneLink` handler (per + `ADR-046-resources-zone-control` §3.1/§10.3 and + `ADR-046-nix-configuration` "Zone declaration"/"ZoneLink"). 2. The cutover's [inventories](#authoritative-inventories) and [migration/ disposition matrix](#migrationdisposition-matrix) **never** enumerate the gateway guest's internal relay credentials, remote node registry, or @@ -514,16 +518,18 @@ translated into ZoneLink terms: the parent host's perspective — those live inside the gateway Guest's own filesystem and are the gateway Guest's own (nested) cutover's responsibility when that Guest itself boots a v3 Zone runtime internally. - The parent host's cutover only Adopts the parent-visible `ZoneLink` - configuration (transport provider selection, `childZoneName`) as ordinary - Nix-authored resource configuration — never as migrated credential bytes. -3. The parent Zone's ZoneLink handler never receives, stores, or logs the - child Zone's Credential resources, matching + The parent host's cutover Adopts no `ZoneLink` row. It regenerates only the + compiler-only `parentZone` topology as sealed allocator/route state; the + child Zone's separate cutover authors and reconciles its local `ZoneLink` + from ordinary Nix configuration. Neither side treats that configuration as + migrated credential bytes. +3. The parent allocator/route engine owns no `ZoneLink` resource or handler and + never receives, stores, or logs the child Zone's Credential resources or raw + token bytes, matching `ADR-046-componentsession-and-bus` "Sensitive credential delivery": a Credential Provider may deliver raw token bytes only over a dedicated KK - ComponentSession to an authorized consumer, and intermediaries (including - a parent Zone's ZoneLink handler) forward opaque protected records without - decrypting them. + ComponentSession to an authorized consumer, and d2b-bus/Zone/relay + intermediaries forward opaque protected records without decrypting them. 4. If the gateway Guest itself is being cut over from a pre-ADR-0046 `d2b-gateway-runtime` process to a nested v3 Zone runtime, that nested cutover is a **separate, independent invocation** of this same procedure, @@ -531,10 +537,13 @@ translated into ZoneLink terms: snapshot, checkpoint id, and consent — it is never folded into the parent host's single `checkpoint_id` and never shares a cutover journal with the parent. -5. Audit: the parent Zone's audit stream never records a gateway Guest's - internal realm audit events; the parent only records its own ZoneLink - connect/reconnect/disconnect/route events per - `ADR-046-componentsession-and-bus` "Errors and telemetry". +5. Audit authority remains Zone-local: the child Zone's audit stream records + its child-local `ZoneLink` resource/session lifecycle events, while the + parent records only allocator/route-engine decisions and effects that the + parent authoritatively owns. The parent never records a gateway Guest's + internal realm audit events or claims a child `ZoneLink` resource transition + (per `ADR-046-zone-routing` "Audit records" and + `ADR-046-resources-zone-control` §3.1). ## ProviderStateSet and state schema migration policy @@ -745,25 +754,34 @@ complete, reproducible install sequence before `apply` runs. invocation; a multi-Zone host runs one independent cutover invocation per Zone (this spec's `checkpoint_id` is always Zone-scoped, never host-wide when a host is declared to run more than one Zone). -2. Any pre-cutover realm that used `EntrypointMode::GatewayBacked` becomes a - `ZoneLink/` resource in the parent Zone, created during Phase 6/7 as - ordinary Nix-authored configuration (per [Gateway Guest credential/audit +2. Any pre-cutover realm that used `EntrypointMode::GatewayBacked` is + represented after cutover by one local uplink `ZoneLink/` authored and + stored in the nested child Zone during Phase 6/7 of that child's independent + invocation (per [Gateway Guest credential/audit custody](#gateway-guest-credentialaudit-custody)) — never as migrated - session or credential state. The `ZoneLink`'s `transportProviderRef` must - name an already-`Ready` Stage C transport Provider (per [Provider - install/topological start](#provider-installtopological-start)); a - `ZoneLink` whose transport Provider is not yet Ready enters `Degraded` - with `ConfigurationCurrent: False` until that Provider reconciles, exactly - as `ADR-046-nix-configuration` "Cross-Zone generation ordering" already - specifies — Zone A's own activation is never blocked on Zone B/ZoneLink - readiness. + session or credential state. Its `spec.childZoneName` must equal the child + Zone's self-name, and its `transportProviderRef` must name an + already-`Ready` Stage C transport Provider in that same child Zone (per + [Provider install/topological start](#provider-installtopological-start)). + The child Zone's compiler-only `parentZone` selects the provisioning + allocator and is sealed into allocator bootstrap state; it is not emitted + into `Zone.spec` or a resource bundle. The parent stores only the resulting + sealed allocator/route state, never a reciprocal `ZoneLink` row or + parent-side handler (per `ADR-046-resources-zone-control` §3.1/§10.3 and + `ADR-046-nix-configuration` "Zone declaration"/"ZoneLink"). A child-local + `ZoneLink` whose transport Provider is not yet Ready enters `Degraded` with + `ConfigurationCurrent: False` until that Provider reconciles, exactly as + `ADR-046-nix-configuration` "Cross-Zone generation ordering" specifies; the + child Zone's activation is never blocked on the parent Zone's generation or + remote-link readiness. 3. Any pre-cutover realm that used `EntrypointMode::HostResident` becomes an ordinary `Guest` (VM/sandbox) or a user-only `Host` (unsafe-local, per D042) in the parent Zone — never a `ZoneLink`, because a host-resident realm never had a separate resource store to link to. -4. ZoneLink activation completes when the core `zone link/delegation` handler - (`ADR-046-core-controllers` "Zone link/delegation") reports the link - `Ready` — verified in [Post-cutover verification](#post-cutover-verification). +4. ZoneLink activation completes when the **child Zone's** core + `zone link/delegation` handler (`ADR-046-resources-zone-control` §11.2) + reports its local uplink `Ready` — verified in + [Post-cutover verification](#post-cutover-verification). ## Guest/runtime/network/store view activation @@ -816,7 +834,7 @@ checks, in order, refusing to report success unless every check passes: | 3 | Every declared `Guest` reports `status.phase == Ready` (or the operator-accepted subset, if `--allow-degraded-guests ` was explicitly passed to `apply`) | Any Guest not `Ready` outside the accepted subset | | 4 | Every adopted TPM marker's content digest matches the cutover snapshot's recorded digest | Digest mismatch on any TPM Volume | | 5 | Every adopted durable Volume's (disk image, store-view) content digest matches the cutover snapshot's recorded digest | Digest mismatch on any durable Volume | -| 6 | Every declared `ZoneLink` reports `Ready` or an accepted `Degraded/waiting-on-remote` (per Cross-Zone generation ordering) | `ZoneLink` in any other Degraded/Failed condition | +| 6 | Every declared child-local `ZoneLink` reports `Ready` or an accepted `Degraded/waiting-on-remote` (per Cross-Zone generation ordering), and no parent Zone store contains a reciprocal row | A child-local `ZoneLink` is in any other Degraded/Failed condition, or a reciprocal parent-store row exists | | 7 | Every declared `Network` reports `Ready` | Any Network Degraded/Failed | | 8 | No orphaned pre-cutover process remains under any `d2b--*`/`d2b.slice` cgroup leaf outside the current Zone's own cgroup partition | Any orphan found | | 9 | The new audit chain's first record verifies against the closure record of the old chain (per [Audit chain closure and opening](#audit-chain-closure-and-opening)) | Hash-chain break or missing closure record | @@ -950,7 +968,7 @@ is no bulk Destroy: | Candidate | Gate that must clear before Destroy | Removal proof | | --- | --- | --- | | `d2bd.service`, `d2bd.socket`, `d2b-priv-broker.service`, `d2b-priv-broker.socket` unit files | New fixed Zone runtime units installed and `verify` check 1 passed at least once since the units were stopped | `tests/host-integration/cutover-unit-retirement.nix` boots with only new units present and passes `d2b host cutover verify` | -| `/etc/d2b/realm-controllers.json`, `/etc/d2b/realm-identity.json` | Zone self-resource and every `ZoneLink` derived from them report `Ready`/accepted-`Degraded` per `verify` check 6 | `ADR046-nix-008`/`ADR046-nix-009` parity tests pass against the live Zone | +| `/etc/d2b/realm-controllers.json`, `/etc/d2b/realm-identity.json` | Every Zone self-resource exists, the compiler-only `parentZone` topology is present only in sealed allocator bootstrap state, and every declared child-local `ZoneLink` reports `Ready`/accepted-`Degraded` with no reciprocal parent-store row per `verify` check 6 | `ADR046-nix-008`/`ADR046-nix-009` parity tests pass against the live Zone | | `nixos-modules/options-realms*.nix`, `nixos-modules/options-vms.nix` | Every VM/realm declaration has an equivalent `d2b.zones..resources.*` declaration that produced a `Ready` Guest/Host in `verify` | `tests/unit/nix/cases/realm-to-zone-parity.nix` | | `/var/lib/d2b/vms//swtpm/`, `/var/lib/d2b/swtpm-markers/` (source side of an Adopt) | `verify` check 4 (TPM digest match) passed **and** the destination TPM Volume has survived at least one full Guest restart cycle post-cutover, proving the adopted marker is load-bearing in practice, not merely digest-equal | `integration/swtpm_marker.rs` adapted; `tests/host-integration/tpm-adopt-retirement.nix` | | `/var/lib/d2b/vms//store/` (source side of an Adopt) | `verify` check 5 (store-view digest match) **and** a live Guest boot successfully mounts the new store-view Volume path | `tests/store_view.rs`; `integration/store_view.rs` | @@ -1125,8 +1143,8 @@ evidence corrections](#cross-reference-and-evidence-corrections)): order under normal finalizer protocol (Guests/Processes first, then their owning Providers, then Volumes, then Networks/Devices/Credentials, with authored qualified Bindings removed/retargeted before their import-owned - projection Services, then ResourceImport/ResourceExport rows, ZoneLinks, and - finally Role/RoleBinding/Quota/EmergencyPolicy). + projection Services, then ResourceImport/ResourceExport rows, the Zone's + child-local ZoneLink, and finally Role/RoleBinding/Quota/EmergencyPolicy). 4. After every other resource is deleted, `core.zone-drain` is cleared and a final transaction emits the Zone's own `phase=Deleted` event and closes the store. @@ -1219,9 +1237,10 @@ All reset scopes tear down cross-Zone sharing child-first: deletes only the core-owned projection Service (`ownerRef: ResourceImport/`) and remaining provider-owned children, then deletes the import row. -5. ZoneLink loss during reset is treated as revoke/degrade, not as retained - authority. Reconnect after a reset must revalidate generation and schema - plus factory fingerprint before any new lease is admitted. +5. Loss of the Zone's child-local ZoneLink during reset is treated as + revoke/degrade, not as retained authority. Reconnect after a reset must + revalidate generation and schema plus factory fingerprint before any new + lease is admitted. 6. Full factory reset removes export/import rows, authored Bindings in scope, projection Services, internal leases/sessions/streams, and advertisements. No cross-Zone authority, capability grant, stream credit, or import session @@ -1534,7 +1553,7 @@ owns the destination. | --- | --- | --- | --- | --- | | `/_ed25519{,.pub}` | `nixos-modules/host-keys.nix` | Preserve (framework SSH keys are never regenerated by cutover) | Unchanged; continues to be consumed the same way by Guest boot | N/A — out of ADR 0046 Provider scope per activation-nixos dossier §1.2 ("SSH key lifecycle... belongs to a separate identity Provider") | | `/vms//host-keys/{host.pub,user-authorized-keys}` | `nixos-modules/host-keys.nix` | Preserve | Unchanged | Same as above | -| `realm-controllers.json`, `realm-identity.json` | migration-map §"Current-code fit" rows, `implemented-and-reachable`/live | Preserve until every `ZoneLink`/Credential successor is `Ready` (Phase 10 gate), then Destroy | `Zone` self resource + `ZoneLink` bootstrap; Credential `scope`/`audience`/`allowedOperations` fields | `ADR046-nix-008`, `ADR046-nix-009` | +| `realm-controllers.json`, `realm-identity.json` | migration-map §"Current-code fit" rows, `implemented-and-reachable`/live | Preserve until the compiler-only `parentZone` topology is sealed, every child-local `ZoneLink`/Credential successor is `Ready`, and the no-reciprocal-parent-row check passes (Phase 10 gate), then Destroy | Runtime-created `Zone` self resource + sealed allocator topology + child-local `ZoneLink` transport/route state; Credential `scope`/`audience`/`allowedOperations` fields | `ADR046-nix-008`, `ADR046-nix-009` | | Gateway guest realm relay credentials/audit (ADR 0032 `CredentialCustody::GatewayGuest`) | ADR 0032 evidence | **Never enumerated by the parent host's inventory** — see [Gateway Guest credential/audit custody](#gateway-guest-credentialaudit-custody) | Nested child Zone's own Credential resources | N/A — parent host cutover has no authority here | ### Audit and telemetry @@ -1568,7 +1587,7 @@ Preserve default. | Current anchor | ADR 0034 "Migration decision" (planned-downtime storage cutover, preserve list, checkpoint/rollback UX); `packages/d2b/src/lib.rs` `cmd_host_migrate_storage`/`build_storage_migration_plan`/`storage_migration_checkpoint_id` (retired verb, reused shape); `cmd_host_destroy`/`require_explicit_mutation_flag` (dry-run/apply precondition pattern); `packages/d2bd/src/storage_lifecycle.rs`/`ownership_preflight.rs` (bundle-versioned contract checks, legacy-recovery-artifact optionality); `ADR-046-resources-zone-control` §2.6/§9.4 (destructive reset primitive); `ADR-046-provider-state` (migration/incident-hold/unclaimed-GC machinery) | | Evidence class | The CLI dry-run planning precedent (`host migrate-storage`, `host destroy`) is `implemented-and-reachable`; the ADR 0034 preserve-list/checkpoint contract is `implemented-and-reachable` as design but its `--apply`/`--rollback` are themselves `test-only-or-preview` (fail closed in the current build); the Zone/Provider/Volume destinations this spec adopts into are `ADR-only` | | Behavior retained | Dry-run-before-apply with a printed checkpoint id and exact rollback command; preserve-list-first design (swtpm NVRAM/markers, SSH keys, store-view state/gcroots, disk images, audit/degraded history never silently destroyed); fail-closed hazards enumerated explicitly rather than left implicit; broker-mediated path-safe mutation (no manual chmod/chown/setfacl); atomic persistence sequence (temp file, fsync, rename, parent fsync) for every durable record this spec's own journal/snapshot writes | -| Required delta | The entire Zone/Provider/redb resource-store bootstrap this spec's Phase 5-8 execute has no current-baseline equivalent at all — ADR 0034's cutover only ever moved a storage *layout*, never replaced the daemon/wire protocol/resource model sitting on top of it; the Full/Provider/Guest reset scopes, the cutover-wide incident hold, and the gateway-custody-aware ZoneLink translation are new | +| Required delta | The entire Zone/Provider/redb resource-store bootstrap this spec's Phase 5-8 execute has no current-baseline equivalent at all — ADR 0034's cutover only ever moved a storage *layout*, never replaced the daemon/wire protocol/resource model sitting on top of it; the Full/Provider/Guest reset scopes, the cutover-wide incident hold, and the gateway-custody-aware child-local ZoneLink translation are new | | Reuse path | Copy the exact `StorageMigrationPlan` JSON shape (renamed fields) for the new `plan`/`apply` JSON envelopes; copy `require_explicit_mutation_flag`'s precondition gate for every new mutating verb; copy `storage_migration_checkpoint_id`'s digest-of-sorted-names pattern extended over every inventory class; copy the ADR 0034 preserve list verbatim as the seed of the [migration/disposition matrix](#migrationdisposition-matrix)'s TPM/keys/disk-image/audit rows; copy `ADR-046-provider-state`'s prepare/stage/commit/rollback/roll-forward algorithm unmodified as every Adopt step's mechanism | | Replacement/deletion | `d2b host migrate-storage` is retired with no successor (it served an unrelated v1→v2 cutover); every other current-baseline artifact this spec's matrix names is Preserved until its own [Old artifact/unit/schema removal gate](#old-artifactunitschema-removal-gates) clears — nothing is removed by this spec's own authoring, only by its future implementation work items after their gates clear | | Feasibility proof | A disposable end-to-end cutover rehearsal fixture (single-Guest, single-TPM, single-store-view host) proving: preflight snapshot digest reproducibility; drain quiescence detection; Adopt idempotency across an injected crash at every step boundary; Phase 5 redb bootstrap against the rehearsal fixture's adopted Volumes; Provider install topological order determinism; `verify` catching an injected digest mismatch; rollback within the boundary and refusal past it; Full/Provider/Guest reset scope isolation (resetting one does not affect siblings) | @@ -1739,20 +1758,20 @@ applies here exactly as everywhere else in the repository). | Validation | Provider install topological-order determinism test; cycle-rejection test; store-identity mismatch fail-closed test | | Removal proof | Not applicable | -### ADR046-reset-006 — Zone/ZoneLink/Guest activation and gateway custody boundary +### ADR046-reset-006 — Child-local ZoneLink/Guest activation and gateway custody boundary | Field | Value | | --- | --- | | Work item ID | `ADR046-reset-006` | | Dependency/owner | ADR046-reset-005; `ADR-046-zone-routing` owner; `ADR-046-resources-zone-control` owner | -| Current source | ADR 0032 gateway guest custody evidence (`d2b-realm-router/src/service_v2.rs` `CredentialCustody`); `ADR-046-zone-routing` §3 evidence | +| Current source | ADR 0032 gateway guest custody evidence (`d2b-realm-router/src/service_v2.rs` `CredentialCustody`); frozen target model in `ADR-046-resources-zone-control` §3.1/§10.3 and `ADR-046-nix-configuration` "Zone declaration"/"ZoneLink" | | Reuse source | None from main | | Reuse action | adapt | | Destination | `packages/d2b-cutover/src/{zonelink_cutover,guest_activation}.rs` | -| Detailed design | Phase 7 ZoneLink translation from `EntrypointMode::GatewayBacked`/`HostResident` per [Zone/ZoneLink cutover](#zonezonelink-cutover); Phase 8 Network→Volume→Device→Guest ordering per [Guest/runtime/network/store view activation](#guestruntimenetworkstore-view-activation); enforcement that the parent inventory never enumerates gateway-guest-internal credential/audit state | +| Detailed design | Phase 7 translation from `EntrypointMode::GatewayBacked`/`HostResident` per [Zone/ZoneLink cutover](#zonezonelink-cutover): the child authors/stores one local uplink with self-matching `childZoneName`; compiler-only `parentZone` selects the allocator; the parent retains only sealed allocator/route state and owns no reciprocal row or ZoneLink handler. Phase 8 follows Network→Volume→Device→Guest ordering per [Guest/runtime/network/store view activation](#guestruntimenetworkstore-view-activation). The parent inventory never enumerates gateway-guest-internal credential/audit state | | Integration | Consumes Providers installed by ADR046-reset-005; hands off to ADR046-reset-007 (verification) | -| Data migration | None (ZoneLink resources are ordinary Nix-authored configuration, not migrated credential bytes) | -| Validation | Gateway-custody-boundary test asserting the parent inventory never contains a gateway-guest-internal path; ZoneLink `Degraded/waiting-on-remote` non-blocking test | +| Data migration | None (the child-local ZoneLink is ordinary Nix-authored configuration, `parentZone` is recompiled into sealed allocator state, and neither is migrated credential material) | +| Validation | Gateway-custody-boundary test asserting the parent inventory never contains a gateway-guest-internal path; child-local ZoneLink test asserting one uplink, self-matching `childZoneName`, and child-store ownership; compiler test asserting `parentZone` selects the allocator but appears only in sealed bootstrap state; no-reciprocal-parent-row/no-parent-handler test; child-local ZoneLink `Degraded/waiting-on-remote` non-blocking test | | Removal proof | Not applicable | ### ADR046-reset-007 — Verification, doctor, and degraded-ledger integration From f3d34ca297aad9767c476cc23e87ac974609a281 Mon Sep 17 00:00:00 2001 From: John Vicondoa Date: Fri, 24 Jul 2026 06:10:17 -0700 Subject: [PATCH 090/115] docs: prohibit identity metric labels Keep metric dimensions bounded to closed semantic domains. Carry Zone and resource identity in the permitted OTEL resource and audit surfaces instead. --- docs/specs/ADR-046-provider-state.md | 31 +++++++----- docs/specs/ADR-046-resources-credential.md | 14 +++--- docs/specs/ADR-046-resources-device.md | 18 +++---- docs/specs/ADR-046-resources-network.md | 32 +++++++------ .../ADR-046-security-and-threat-model.md | 17 +++---- .../ADR-046-telemetry-audit-and-support.md | 48 +++++++++++++++---- docs/specs/ADR-046-zone-routing.md | 17 ++++--- .../ADR-046-provider-activation-nixos.md | 15 +++--- .../ADR-046-provider-clipboard-wayland.md | 4 +- ...46-provider-credential-managed-identity.md | 14 +++--- ...-046-provider-credential-secret-service.md | 14 +++--- .../providers/ADR-046-provider-device-gpu.md | 22 ++++----- .../ADR-046-provider-device-security-key.md | 16 +++---- .../providers/ADR-046-provider-device-tpm.md | 18 +++---- .../ADR-046-provider-display-wayland.md | 24 +++++----- .../ADR-046-provider-network-local.md | 37 +++++++------- .../ADR-046-provider-notification-desktop.md | 37 +++++++------- ...R-046-provider-runtime-cloud-hypervisor.md | 24 +++++----- .../ADR-046-provider-runtime-qemu-media.md | 32 +++++++------ .../ADR-046-provider-transport-azure-relay.md | 41 ++++++++-------- .../ADR-046-provider-volume-local.md | 46 +++++++++--------- 21 files changed, 293 insertions(+), 228 deletions(-) diff --git a/docs/specs/ADR-046-provider-state.md b/docs/specs/ADR-046-provider-state.md index 70d13d62a..1451b1284 100644 --- a/docs/specs/ADR-046-provider-state.md +++ b/docs/specs/ADR-046-provider-state.md @@ -674,16 +674,19 @@ No bytes of Volume content, credential material, migration data, raw paths, or p ### OTEL metrics -Cardinality-bounded metric labels are: `zone`, `provider`, `schema_id`, `schema_version`, `persistence_class`, `operation`, `outcome`. +Cardinality-bounded metric labels are: `provider`, `schema_id`, +`schema_version`, `persistence_class`, `operation`, `trigger`, and `outcome`. +Zone identity is carried only by the bounded `d2b.zone` OTEL resource +attribute, never by a metric label. | Metric | Unit | Labels | | --- | --- | --- | -| `d2b_volume_state_size_bytes` | Gauge, bytes | zone, provider, schema_id | -| `d2b_volume_state_migration_total` | Counter | zone, provider, schema_id, outcome | -| `d2b_volume_state_migration_duration_ms` | Histogram | zone, provider, schema_id | -| `d2b_volume_state_snapshot_total` | Counter | zone, provider, schema_id, trigger | -| `d2b_volume_state_marker_check_total` | Counter | zone, provider, outcome | -| `d2b_volume_state_quota_exceeded_total` | Counter | zone, provider | +| `d2b_volume_state_size_bytes` | Gauge, bytes | provider, schema_id | +| `d2b_volume_state_migration_total` | Counter | provider, schema_id, outcome | +| `d2b_volume_state_migration_duration_ms` | Histogram | provider, schema_id | +| `d2b_volume_state_snapshot_total` | Counter | provider, schema_id, trigger | +| `d2b_volume_state_marker_check_total` | Counter | provider, outcome | +| `d2b_volume_state_quota_exceeded_total` | Counter | provider | No schema content, raw path, instance ID, process arguments, or credential identifier enters any metric label. @@ -915,9 +918,9 @@ The following metrics extend the OTEL metric table in [Status, audit, and OTEL]( | Metric | Unit | Labels | | --- | --- | --- | -| `d2b_zone_generation_activation_total` | Counter | zone, outcome (`applied` \| `integrity-failure`) | -| `d2b_zone_generation_cleanup_pending_resources` | Gauge | zone | -| `d2b_zone_generation_cleanup_duration_ms` | Histogram | zone | +| `d2b_zone_generation_activation_total` | Counter | outcome (`applied` \| `integrity-failure`) | +| `d2b_zone_generation_cleanup_pending_resources` | Gauge | (none) | +| `d2b_zone_generation_cleanup_duration_ms` | Histogram | (none) | ### Required tests for removed-resource cleanup @@ -931,6 +934,10 @@ Required by ADR046-pstate-010: 6. **Finalizer timeout**: Provider controller does not remove finalizer within `maxFinalizerDurationSeconds` → `Degraded/finalizer-timeout` condition set; `provider-finalizer-timeout` audit event. Timeout is stall detection only; no finalizer is force-cleared; operator intervention resolves the stall. 7. **Eval credential-ref guard**: `config` field with `credentialRef: true` receives a raw string → NixOS eval fails with `credential-value-must-be-ref`; no bundle is emitted; build derivation is never entered. 8. **Name conflict**: new bundle contains a resource `(type, name)` that collides with an existing `managedBy: controller` or `managedBy: api` resource → that activation item is `Degraded/name-conflict`; the existing resource and its `managedBy` are unchanged; a `configuration-name-conflict` audit event is emitted; all non-conflicting resources in the same generation activate normally. +9. **Metric identity absence**: every Volume-state and generation metric + descriptor rejects `vm`, `zone`, `zone_id`, `zone_uid`, and every + resource-name-derived label key; a Zone/resource-name canary is absent from + emitted label values while `d2b.zone` remains in OTEL resource attributes. ## Current-code fit @@ -1134,10 +1141,10 @@ Every `packages/d2b-provider--/` crate created by this or | Reuse action | adapt | | Destination | `packages/d2b-provider-volume-local/src/audit.rs`; `packages/d2b-provider-volume-local/src/otel.rs`; `packages/d2b-provider-volume-local/tests/audit_unit.rs` (hermetic audit golden records, OTEL cardinality label tests) | | Crate layout | See [Provider crate layout](#provider-crate-layout). Hermetic golden-record and cardinality tests → `tests/audit_unit.rs`; live Zone audit stream emission and OTEL export against a running observability Provider → `integration/audit.rs` (added by ADR046-pstate-009) | -| Detailed design | Volume-state audit event types and Zone audit emission; OTEL metric definitions with closed cardinality label sets | +| Detailed design | Volume-state audit event types and Zone audit emission; OTEL metric definitions with closed semantic label sets and no Zone or resource-name-derived label keys; Zone identity remains in the `d2b.zone` OTEL resource attribute | | Integration | Every state lifecycle transition calls `audit::emit_volume_event`; OTEL metrics exported via `observability-otel` Provider | | Data migration | None — full d2b 3.0 reset; no prior state to migrate | -| Validation | Audit event golden records; no content/path/credential in audit payload; OTEL cardinality label tests | +| Validation | Audit event golden records; no content/path/credential in audit payload; structural OTEL label-policy tests assert exact absence of `vm`, `zone`, `zone_id`, `zone_uid`, and resource-name-derived keys and preserve `d2b.zone` as a resource attribute | | Removal proof | Not applicable | ### ADR046-pstate-009 diff --git a/docs/specs/ADR-046-resources-credential.md b/docs/specs/ADR-046-resources-credential.md index 438ac223c..1c4d85325 100644 --- a/docs/specs/ADR-046-resources-credential.md +++ b/docs/specs/ADR-046-resources-credential.md @@ -882,14 +882,16 @@ correlation IDs that embed secret shapes. | Metric | Type | Labels | | --- | --- | --- | | `d2b_credential_operations_total` | Counter | `provider`, `operation_class`, `placement_binding`, `outcome` | -| `d2b_credential_lease_expiry_seconds` | Gauge | `provider`, `credential_name`, `placement_binding` | +| `d2b_credential_lease_expiry_seconds` | Gauge | `provider`, `placement_binding` | | `d2b_credential_rotation_total` | Counter | `provider`, `policy`, `outcome` | | `d2b_credential_provider_health` | Gauge (0/1) | `provider` | | `d2b_credential_active_leases` | Gauge | `provider`, `placement_binding` | -Label cardinality is bounded. `credential_name` is used only in expiry gauges -where per-resource precision is required; it is omitted from high-cardinality -counters. +Label cardinality is bounded and semantic. The expiry gauge reports the +minimum seconds remaining across active leases in each provider/placement +aggregate (0 when none); it carries no Credential resource name. Zone and +Credential identity remain available in bounded OTEL attributes and permitted +audit fields, never metric labels. ## Nix configuration @@ -1997,8 +1999,8 @@ config formalizes this as an `OpaqueAzureRef` with the same charset restriction. | Current source | `packages/d2b-core/src/privileges.rs:SecretAccess` (implemented-and-reachable); `d2b-realm-provider/src/error.rs:ProviderDiagnostic`/`contains_sensitive_shape` (implemented-and-reachable); `packages/d2b-contract-tests/tests/policy_observability.rs` (reachable audit policy tests) | | Reuse action | adapt | | Destination | `packages/d2b-provider-credential-/src/audit.rs`, `telemetry.rs`; `packages/d2b-contract-tests/tests/credential_audit.rs` | -| Detailed design | Implement audit record emission for all credential service methods and controller events using the field set defined in §Audit; implement OTEL span/metric emission using the closed label set in §OTEL and metrics; implement `contains_sensitive_shape` check in all string fields of audit records and metric label values (adapted from `d2b-realm-provider/src/error.rs:contains_sensitive_shape`); add canary-enforcement tests that verify `"secret-canary"`, `"entra-token-canary"`, and `"managed-identity-canary"` values never appear in any audit record, metric label, span attribute, log line, or status field across all Provider test suites | +| Detailed design | Implement audit record emission for all credential service methods and controller events using the field set defined in §Audit; implement OTEL span/metric emission using the closed semantic label set in §OTEL and metrics, with expiry reported as a provider/placement aggregate and no Credential/Zone/resource-name-derived label; implement `contains_sensitive_shape` check in all string fields of audit records and metric label values (adapted from `d2b-realm-provider/src/error.rs:contains_sensitive_shape`); add canary-enforcement tests that verify `"secret-canary"`, `"entra-token-canary"`, `"managed-identity-canary"`, Credential `metadata.name`, and Zone name values never appear in any audit record, metric label, span attribute, log line, or status field except identity in allowed OTEL/audit fields | | Integration | Credential controller and service handlers emit audit records and telemetry through Zone audit/OTEL paths | | Data migration | None — full d2b 3.0 reset; no prior state to migrate | -| Validation | Canary tests across all three Provider crates; audit record field-presence tests; metric label cardinality tests; span attribute absence tests for forbidden fields | +| Validation | Canary tests across all three Provider crates; audit record field-presence tests; structural metric descriptor tests assert exact absence of `vm`, `zone`, `zone_id`, `zone_uid`, `credential_name`, and every resource-name-derived key plus Credential/Zone-name canary absence; span attribute tests preserve allowed `d2b.zone`/`d2b.credential.name` identity and reject forbidden fields | | Removal proof | Not applicable | diff --git a/docs/specs/ADR-046-resources-device.md b/docs/specs/ADR-046-resources-device.md index ca9507fce..0c252fb08 100644 --- a/docs/specs/ADR-046-resources-device.md +++ b/docs/specs/ADR-046-resources-device.md @@ -1257,13 +1257,13 @@ and any credential material. ### OTEL spans Device reconcile telemetry attribute placement — including span vs resource -attribute classification, `d2b.device.zone` cardinality, `d2b.device.provider` -label level, and full label set boundaries — is specified in -`ADR-046-telemetry-audit-and-support`. This spec does not define competing -telemetry decisions. Device Provider implementations must cross-reference that -spec for all OTEL label and span attribute constraints. No device path, busid, -serial, vendor/product string, session content, or process PID may appear in -any OTEL attribute or metric label. +attribute classification and full label-set boundaries — is specified in +`ADR-046-telemetry-audit-and-support`. `d2b.zone` and `d2b.provider` are +bounded OTEL resource attributes, never metric labels. This spec does not +define competing telemetry decisions. Device Provider implementations must +cross-reference that spec for all OTEL label and span attribute constraints. +No Zone/resource name or UID, device path, busid, serial, vendor/product +string, session content, or process PID may appear in a metric label. ## Errors @@ -2041,10 +2041,10 @@ error listing the missing paths. There is no opt-out mechanism. | Current source | `packages/d2b-contracts/src/security_key.rs` (SecurityKeyStatusResponse, SecurityKeySession, SecurityKeyLeaseState, SecurityKeyVmSessionState DTOs; implemented-and-reachable), `usbip.rs`, `broker_wire.rs`; `packages/d2b-core/src/privileges_w3.rs` (W3BrokerOperation: SecurityKeyOpenDevice, SecurityKeyApplyUdevRules, UsbipBindFirewallRule — implemented-and-reachable); `packages/d2b-core/src/manifest_v04.rs` VmEntry device fields (tpm, usbip_yubikey, security_key, graphics — old Workload manifest, generated-or-eval-contract) | | Reuse action | adapt | | Destination | `packages/d2b-contracts/src/v3/device.rs` | -| Detailed design | Device ResourceType schema (spec/status/conditions/claims/inventory); closed-set error codes; Device RBAC verbs; broker operation effect-limit constants Primary reuse disposition: `adapt`. Preserved source-plan detail: extract and adapt. | +| Detailed design | Device ResourceType schema (spec/status/conditions/claims/inventory); closed-set error codes; Device RBAC verbs; broker operation effect-limit constants; shared Device telemetry contract requires fixed semantic metric labels with no Zone/resource-name-derived identity and retains `d2b.zone`/`d2b.provider` only as OTEL resource attributes. Primary reuse disposition: `adapt`. Preserved source-plan detail: extract and adapt. | | Integration | Provider dossiers, resource API/store, CLI status surfaces | | Data migration | Full reset; no v2 device object import | -| Validation | Schema golden vectors; unknown-field denial; exclusive/shared conflict rejection; arbitration/maxClaims invariant | +| Validation | Schema golden vectors; unknown-field denial; exclusive/shared conflict rejection; arbitration/maxClaims invariant; cross-Provider structural descriptor test asserts exact absence of `vm`, `zone`, `zone_id`, `zone_uid`, and resource-name-derived keys plus Device/Zone-name canary absence while preserving `d2b.zone` resource attributes | | Removal proof | Old ProcessRole/DTO branches retained until Provider integrations are live | ### ADR046-device-002 diff --git a/docs/specs/ADR-046-resources-network.md b/docs/specs/ADR-046-resources-network.md index 120dc3f22..fa76fb2d4 100644 --- a/docs/specs/ADR-046-resources-network.md +++ b/docs/specs/ADR-046-resources-network.md @@ -1459,24 +1459,26 @@ d2b.network.agent.sync d2b.network.mdns.sync ``` -Labels use closed cardinality. No workload IP, MAC, hostname, nftables rule -text, or DHCP lease data appears in any span attribute, metric label, or log -field. The `network.name` attribute is a plain ResourceName, not a hostname -or FQDN. +Metric labels use closed semantic cardinality and contain no Zone or Network +identity. No workload IP, MAC, hostname, nftables rule text, or DHCP lease data +appears in any span attribute, metric label, or log field. Zone identity +remains in the `d2b.zone` OTEL resource attribute. The `network.name` trace +attribute is a plain ResourceName, not a hostname or FQDN, and is never copied +into a metric label. Metrics: | Metric | Labels | | --- | --- | -| `d2b_network_reconcile_total` | `zone`, `outcome` | -| `d2b_network_phase` | `zone`, `phase` | -| `d2b_network_attachment_count` | `zone`, `network` | -| `d2b_nftables_apply_total` | `zone`, `outcome` | -| `d2b_nftables_drift_total` | `zone` | -| `d2b_bridge_create_total` | `zone`, `outcome` | -| `d2b_bridge_delete_total` | `zone`, `outcome` | -| `d2b_network_volume_sync_total` | `zone`, `outcome` | -| `d2b_network_agent_restart_total` | `zone`, `outcome` | +| `d2b_network_reconcile_total` | `outcome` | +| `d2b_network_phase` | `phase` | +| `d2b_network_attachment_count` | (none) | +| `d2b_nftables_apply_total` | `outcome` | +| `d2b_nftables_drift_total` | (none) | +| `d2b_bridge_create_total` | `outcome` | +| `d2b_bridge_delete_total` | `outcome` | +| `d2b_network_volume_sync_total` | `outcome` | +| `d2b_network_agent_restart_total` | `outcome` | ## Security invariants @@ -2438,10 +2440,10 @@ bridges at reconcile time). | Current source | `nixos-modules/network.nix` (tap/sysctl sections); `packages/d2b-host/src/{bridge_port,nftables,netlink,routes}.rs`; broker ops in `packages/d2b-contracts/src/broker_wire.rs`: **real runtime ops** `ApplyNftables`, `ApplyNmUnmanaged`, `ApplyRoute`, `ApplySysctl`, `SetBridgePortFlags`, `UpdateHostsFile`, `SeedDnsmasqLease` (all `implemented-and-reachable`); **new ops to author**: `CreateBridge`, `DeleteBridge` (do not exist in v3 baseline; must be added to `broker_wire.rs` and implemented as `RealBrokerRequest` handlers in `packages/d2b-priv-broker/src/runtime.rs`); **NOT current ops**: `CreateMacvtap` does not exist — macvtap is created inside broker's `SpawnRunner` dispatch (`packages/d2b-priv-broker/src/runtime.rs` line 5097 `live_create_macvtap_fd`) | | Reuse action | adapt | | Destination | `packages/d2b-provider-network-local/src/controller.rs`: async NetworkReconciler; `packages/d2b-provider-network-local/src/plan.rs`: ReconcilePlan computation; `packages/d2b-provider-network-local/src/observe.rs`: drift-detection observe loop. Full crate layout required (see [Package and crate boundary](#package-and-crate-boundary)): `src/` (controller/plan/observe + colocated unit tests), `tests/` (hermetic conformance and state-machine tests), `integration/` (provider-system reconcile fixtures), `README.md` (Network ResourceType, controller binary, placement, RBAC, security invariants, build/test/integration commands). | -| Detailed design | Implements full async reconcile interface from `ADR-046-resource-reconciliation`. `plan()` computes desired vs. actual bridge-presence, sysctl, host-side nftables, hosts-file, NM-unmanaged, config Volume content, Guest, guest-agent Process, and mDNS-Process states. `reconcile()` dispatches in order: `CreateBridge` for each bridge not present (broker applies IPv6 sysctls atomically; `CreateBridge` failure sets `FabricReady=False/bridge-create-error` and aborts) → `ApplySysctl` (defense-in-depth IPv6) → `ApplyNftables` (host-side `inet d2b` table) → `ApplyNmUnmanaged` → `ApplyRoute` → `UpdateHostsFile` → `SeedDnsmasqLease` for new reservations → **Volume upsert** (two-phase): Phase 1 — create `Volume/net--config` with `kind: ephemeral`, `source.executionRef: Host/`, `source.settings.kind: tmpfs`, `quota: {maxBytes: 4194304, maxInodes: 128, enforcement: hard}` (tmpfs quota charged to Host memory budget), `layout` entries (root directory with `type: directory` and four config files each with `type: file`, `ownerRef: User/net-local-controller`, `groupRef: User/net-local-controller`, `mode: "0640"`, `accessAcl: []`, `defaultAcl: []`, `noFollow: true`, conservative create/repair/cleanup policies), `views: {guest-readonly: {path: "", rights: [read, traverse]}}`, `attachments: []` (no Guest attachment); abort on terminal error with `ConfigVolumeReady=False/config-volume-error`. Wait for Volume backing to reach `Ready`; requeue on `Degraded`/`Failed` with `ConfigVolumeReady=False/volume-not-ready`. Write rendered config content through Volume write service (no raw host paths). Phase 2 — create Guest upsert with `systemArtifactId` from REQUIRED `Network.spec.netVmSystemArtifactId`. Wait for Guest `Ready`. Then update Volume with Guest attachment: `attachments: [{executionRef: Guest/, transport: virtiofs, view: guest-readonly, access: read-only, mountPath: "/run/d2b/net-config", settings: {posixAcl: false, xattr: false, cache: auto, inodeFileHandles: never, threadPoolSize: null, socketGroup: null}}]`; wait for attachment `Ready`; requeue on `Degraded` with `ConfigVolumeReady=False/attachment-not-ready`. Create or update guest-agent Process `Process/net--agent` with `processClass: worker`, `sandbox: {namespaceClasses: [], capabilityClasses: [network-admin, network-bind, network-raw]}` (inherits Guest network namespace; `network-admin`→`CAP_NET_ADMIN`, `network-bind`→`CAP_NET_BIND_SERVICE`, `network-raw`→`CAP_NET_RAW`, all effective in Guest network namespace only; INV-NET-009), `mounts: [{volumeRef: Volume/net--config, view: guest-readonly, mountPath: "/run/d2b/net-config", access: read-only, required: true}]`. mDNS Process upsert when `spec.mdns.enable = true` (D-NETWORK-001) → `SetBridgePortFlags` per tap. Each broker op returns typed audit evidence. `observe()` re-reads `firewallDigest` (host-side), bridge isolation flags, IPv6 sysctls, and guest-agent Process status (`dnsmasq-bound`, `firewall-applied` predicates); queues reconcile on drift. **Finalizer** (strictly child-first): `NetworkDraining` → delete guest-agent Process and mDNS Processes; wait for each Deleted watch event → update Volume to remove Guest attachment (`attachments: []`); wait for attachment removal confirmed → delete `Guest/`; wait for Deleted watch event → delete `Volume/net--config`; wait for Deleted watch event → `ApplyNftables` (empty) → `ApplyNmUnmanaged` (empty) → `UpdateHostsFile` (empty) → `DeleteBridge` for each bridge (idempotent) → clear finalizer. No USBIP rules installed by Network; `UsbipBindFirewallRule` issued by device-usbip directly (D-NETWORK-002). | +| Detailed design | Implements full async reconcile interface from `ADR-046-resource-reconciliation`. `plan()` computes desired vs. actual bridge-presence, sysctl, host-side nftables, hosts-file, NM-unmanaged, config Volume content, Guest, guest-agent Process, and mDNS-Process states. `reconcile()` dispatches in order: `CreateBridge` for each bridge not present (broker applies IPv6 sysctls atomically; `CreateBridge` failure sets `FabricReady=False/bridge-create-error` and aborts) → `ApplySysctl` (defense-in-depth IPv6) → `ApplyNftables` (host-side `inet d2b` table) → `ApplyNmUnmanaged` → `ApplyRoute` → `UpdateHostsFile` → `SeedDnsmasqLease` for new reservations → **Volume upsert** (two-phase): Phase 1 — create `Volume/net--config` with `kind: ephemeral`, `source.executionRef: Host/`, `source.settings.kind: tmpfs`, `quota: {maxBytes: 4194304, maxInodes: 128, enforcement: hard}` (tmpfs quota charged to Host memory budget), `layout` entries (root directory with `type: directory` and four config files each with `type: file`, `ownerRef: User/net-local-controller`, `groupRef: User/net-local-controller`, `mode: "0640"`, `accessAcl: []`, `defaultAcl: []`, `noFollow: true`, conservative create/repair/cleanup policies), `views: {guest-readonly: {path: "", rights: [read, traverse]}}`, `attachments: []` (no Guest attachment); abort on terminal error with `ConfigVolumeReady=False/config-volume-error`. Wait for Volume backing to reach `Ready`; requeue on `Degraded`/`Failed` with `ConfigVolumeReady=False/volume-not-ready`. Write rendered config content through Volume write service (no raw host paths). Phase 2 — create Guest upsert with `systemArtifactId` from REQUIRED `Network.spec.netVmSystemArtifactId`. Wait for Guest `Ready`. Then update Volume with Guest attachment: `attachments: [{executionRef: Guest/, transport: virtiofs, view: guest-readonly, access: read-only, mountPath: "/run/d2b/net-config", settings: {posixAcl: false, xattr: false, cache: auto, inodeFileHandles: never, threadPoolSize: null, socketGroup: null}}]`; wait for attachment `Ready`; requeue on `Degraded` with `ConfigVolumeReady=False/attachment-not-ready`. Create or update guest-agent Process `Process/net--agent` with `processClass: worker`, `sandbox: {namespaceClasses: [], capabilityClasses: [network-admin, network-bind, network-raw]}` (inherits Guest network namespace; `network-admin`→`CAP_NET_ADMIN`, `network-bind`→`CAP_NET_BIND_SERVICE`, `network-raw`→`CAP_NET_RAW`, all effective in Guest network namespace only; INV-NET-009), `mounts: [{volumeRef: Volume/net--config, view: guest-readonly, mountPath: "/run/d2b/net-config", access: read-only, required: true}]`. mDNS Process upsert when `spec.mdns.enable = true` (D-NETWORK-001) → `SetBridgePortFlags` per tap. Each broker op returns typed audit evidence. `observe()` re-reads `firewallDigest` (host-side), bridge isolation flags, IPv6 sysctls, and guest-agent Process status (`dnsmasq-bound`, `firewall-applied` predicates); queues reconcile on drift. Metrics use only the fixed semantic labels in §OTEL spans and metrics; Zone/Network identity remains in OTEL resource/trace attributes and never enters metric labels. **Finalizer** (strictly child-first): `NetworkDraining` → delete guest-agent Process and mDNS Processes; wait for each Deleted watch event → update Volume to remove Guest attachment (`attachments: []`); wait for attachment removal confirmed → delete `Guest/`; wait for Deleted watch event → delete `Volume/net--config`; wait for Deleted watch event → `ApplyNftables` (empty) → `ApplyNmUnmanaged` (empty) → `UpdateHostsFile` (empty) → `DeleteBridge` for each bridge (idempotent) → clear finalizer. No USBIP rules installed by Network; `UsbipBindFirewallRule` issued by device-usbip directly (D-NETWORK-002). | | Integration | Controller process registers descriptor, watches `Network` resources via d2b-bus/ComponentSession/ResourceClient. Owned Guest and Process mutations trigger owner reconciliation. Device-usbip watches only Network identity/readiness/generation; its Core adapter privately resolves relay/firewall effects (D-NETWORK-002). | | Data migration | None after full reset | -| Validation | `ADR046-reconcile-001` toolkit conformance; latency gates (p95 ≤5 ms hint-to-handler); Network-specific: CIDR conflict blocks reconcile, `CreateBridge` failure sets `FabricReady=False`, Volume creation failure sets `ConfigVolumeReady=False/config-volume-error`, `User/net-local-controller` not Ready aborts with `ConfigVolumeReady=False/user-not-ready`, Volume schema round-trip (kind=ephemeral, source.settings.kind=tmpfs, quota.enforcement=hard, layout type=file entries, views.guest-readonly.rights=[read,traverse]), tmpfs quota charged to Host memory budget (test Volume creation fails when Host memory budget exceeded), Guest not created before Volume backing `Ready`, Guest attachment not added before Guest `Ready`, guest-agent Process created after attachment `Ready` (`processClass: worker`, `namespaceClasses: []`, `capabilityClasses: [network-admin, network-bind, network-raw]`, `access: read-only`, `required: true`), host-capability leakage test: no host-netns process gains `CAP_NET_ADMIN`/`CAP_NET_BIND_SERVICE`/`CAP_NET_RAW` as result of guest-agent launch (INV-NET-009; `tests/host-integration/guest-agent-cap-confinement.nix`), `DeleteBridge` called on finalizer, Volume attachment removed before Guest deletion in finalizer (test order: agent Deleted → attachment removed → Guest Deleted → Volume Deleted → bridges), east-west invariant (INV-NET-003), hostBlocklist enforcement (INV-NET-004), macvtap attachment status (delegated to runtime-ch), mDNS Process created/deleted with `spec.mdns.enable` toggle, broker INV-NET-002 tests, config-only spec change updates Volume content and triggers agent reload without Guest restart (INV-NET-008); golden tests updated for v3 IfNames | +| Validation | `ADR046-reconcile-001` toolkit conformance; latency gates (p95 ≤5 ms hint-to-handler); Network-specific: CIDR conflict blocks reconcile, `CreateBridge` failure sets `FabricReady=False`, Volume creation failure sets `ConfigVolumeReady=False/config-volume-error`, `User/net-local-controller` not Ready aborts with `ConfigVolumeReady=False/user-not-ready`, Volume schema round-trip (kind=ephemeral, source.settings.kind=tmpfs, quota.enforcement=hard, layout type=file entries, views.guest-readonly.rights=[read,traverse]), tmpfs quota charged to Host memory budget (test Volume creation fails when Host memory budget exceeded), Guest not created before Volume backing `Ready`, Guest attachment not added before Guest `Ready`, guest-agent Process created after attachment `Ready` (`processClass: worker`, `namespaceClasses: []`, `capabilityClasses: [network-admin, network-bind, network-raw]`, `access: read-only`, `required: true`), host-capability leakage test: no host-netns process gains `CAP_NET_ADMIN`/`CAP_NET_BIND_SERVICE`/`CAP_NET_RAW` as result of guest-agent launch (INV-NET-009; `tests/host-integration/guest-agent-cap-confinement.nix`), `DeleteBridge` called on finalizer, Volume attachment removed before Guest deletion in finalizer (test order: agent Deleted → attachment removed → Guest Deleted → Volume Deleted → bridges), east-west invariant (INV-NET-003), hostBlocklist enforcement (INV-NET-004), macvtap attachment status (delegated to runtime-ch), mDNS Process created/deleted with `spec.mdns.enable` toggle, broker INV-NET-002 tests, config-only spec change updates Volume content and triggers agent reload without Guest restart (INV-NET-008); golden tests updated for v3 IfNames; structural metric descriptor test asserts exact absence of `vm`, `zone`, `zone_id`, `zone_uid`, `network`, and every resource-name-derived label and verifies a Network-name canary is absent from emitted label values | | Removal proof | Daemon-orchestrated network/bridge lifecycle removed only after controller passes conformance and parity tests | ### ADR046-network-006 diff --git a/docs/specs/ADR-046-security-and-threat-model.md b/docs/specs/ADR-046-security-and-threat-model.md index 49a82a2da..af957521e 100644 --- a/docs/specs/ADR-046-security-and-threat-model.md +++ b/docs/specs/ADR-046-security-and-threat-model.md @@ -1374,14 +1374,15 @@ environment variables. in `ProcessEffect` records, forbidden in any metric label, span attribute, or log field. -**OTEL forbidden metric-label values** (lines 283-303): VM/Zone/Provider/ -resource names; user or resource UIDs; Host/Guest/User/Volume/Network/ -Device names; filesystem paths, `argv`, environment variables; status detail -messages; subject names; PIDs, pidfds, cgroup paths; operation IDs, endpoint -addresses. `d2b.zone` is allowed in resource attributes but never in a -metric label value. Cardinality is enforced by policy tests -(`policy_observability.rs`, and the new cross-spec -`policy_telemetry_redaction.rs` under `ADR046-telem-008`). +**OTEL forbidden metric dimensions** (telemetry spec §Cardinality rules): +the exact keys `vm`, `zone`, `zone_id`, and `zone_uid`; every +resource-name-derived key; VM/Zone/Provider-resource/resource names and UIDs; +filesystem paths, `argv`, environment variables; status detail messages; +subject names; PIDs, pidfds, cgroup paths; operation IDs; and endpoint +addresses. `d2b.zone` is allowed in resource attributes but never in a metric +label. Structural closed-label policy tests parse every descriptor and enforce +both key and value-domain rules in `policy_telemetry_redaction.rs` under +`ADR046-telem-008`. **Audit record types** (lines 914-1170): `ResourceMutation`, `RBACChange`, `SessionConnect`, `RouteAdmission`, `BrokerEffect`, `ProcessEffect`, diff --git a/docs/specs/ADR-046-telemetry-audit-and-support.md b/docs/specs/ADR-046-telemetry-audit-and-support.md index 505082a35..bb5d1851f 100644 --- a/docs/specs/ADR-046-telemetry-audit-and-support.md +++ b/docs/specs/ADR-046-telemetry-audit-and-support.md @@ -281,11 +281,28 @@ span attributes for Host processes. ### Cardinality rules -All metric label values must come from a **closed set enumerated in this spec**. -The following are unconditionally forbidden as metric label values: - -- VM names, Zone names, Provider names, resource names (`metadata.name` values) - — these appear only in OTEL resource attributes +Every metric descriptor MUST use only fixed semantic label keys from the +closed `METRIC_LABEL_POLICY` registry, and every value domain MUST be a closed +enum or an explicitly bounded semantic classifier from this spec. Free-form +resource identity is never a metric dimension. The structural policy lint +rejects descriptor keys that are absent from the registry before inspecting +any emitted value. + +The exact label keys `vm`, `zone`, `zone_id`, and `zone_uid` are +unconditionally forbidden. So are bare resource-kind identity keys such as +`network`, `credential`, `guest`, `host`, `user`, `volume`, `device`, and +`process`, plus every resource-name-derived key (`credential_name`, +`resource_name`, `network_name`, `link_name_hash`, any `*_name`, +`*_name_hash`, `*_name_digest`, or `*_uid`). Fixed semantic keys such as +`resource_type`, `provider`, `handler`, `operation`, `phase`, and `outcome` +remain allowed only with their closed value domains; for example, `provider` +identifies a fixed implementation class, never `Provider.metadata.name`. + +The following values are also unconditionally forbidden in metric labels: + +- VM names, Zone names, Provider resource names, and all resource names + (`metadata.name` values) — these appear only in bounded OTEL resource + attributes - Zone/Provider/Process UIDs - Host/Guest/User/Volume/Network/Device names - Filesystem paths, socket paths, executable paths @@ -298,6 +315,10 @@ The following are unconditionally forbidden as metric label values: - ResourceExport `exportKey` values, raw stream bytes, device serials, token values, and any path/device/socket locator +Zone identity remains available as the bounded `d2b.zone` OTEL resource +attribute. Removing identity labels MUST NOT remove that resource attribute or +the Zone/resource identity fields permitted by the audit contract. + Note: the current `d2b_daemon_vm_*` metrics in `packages/d2bd/src/metrics.rs` use `vm` labels with VM name values (e.g. labels `["vm", "state"]`, `["vm", "outcome"]`, `["vm", "vmm", "outcome"]`, `["vm", "reason"]`). @@ -1590,8 +1611,17 @@ Adapt `packages/d2b-contract-tests/tests/policy_metrics.rs`: - Assert every metric declared in this spec is present in the closed `METRIC_INVENTORY` table in its owning crate. -- Assert no metric label value from the forbidden list (`vm`, name-shaped - strings) appears in any `MetricDescriptor` label set. +- Parse every v3 `MetricDescriptor` and assert every label key/value domain is + present in the closed `METRIC_LABEL_POLICY` registry. +- Assert the exact keys `vm`, `zone`, `zone_id`, and `zone_uid` are absent from + every v3 descriptor. +- Assert resource-name-derived keys are absent, including exact regression + cases `credential_name`, `network`, `network_name`, and `link_name_hash`, and + reject the structural suffixes `*_name`, `*_name_hash`, `*_name_digest`, and + `*_uid`. +- Feed a canary `metadata.name` value through every metric emitter fixture and + assert it is absent from every emitted label value while `d2b.zone` remains + present in the OTEL resource-attribute fixture. - Assert `d2b_controller_hint_to_handler_seconds` bucket list includes a 5 ms bucket (0.005). - Assert `d2b_process_launch_duration_seconds` bucket list includes a 20 ms @@ -1616,6 +1646,8 @@ New test `packages/d2b-contract-tests/tests/policy_telemetry_redaction.rs`: - Assert the OTEL resource attribute initialization uses only the allowlist from this spec (extends `loki_native_otel_resource_attributes` test from `packages/d2b-contract-tests/tests/policy_observability.rs`). +- Parse the metric descriptor registry structurally and fail any label key not + declared by `METRIC_LABEL_POLICY`; grep-only checks are insufficient. - Assert `config_source = "realm-controllers"` string literal does not appear in any v3 component source; only `config_source = "zone-config"` is allowed. @@ -2040,7 +2072,7 @@ New `packages/d2b-core-controller/tests/config_cleanup.rs`: | Current source | `packages/d2b-contract-tests/tests/policy_observability.rs` (`loki_native_otel_resource_attributes` allowlist: `["deployment.environment","host.name","service.name","service.namespace","source","vm.env","vm.name","vm.role"]`; `tempo_stack_signoz_backend_and_collector` SigNoz-only assertion; `startup_tracing_avoids_host_path_fields` forbidden fields); `packages/d2b-contract-tests/tests/policy_metrics.rs` (`EXPECTED_METRICS` table parity with `docs/reference/daemon-metrics.md`) | | Reuse action | adapt | | Destination | `packages/d2b-contract-tests/tests/policy_telemetry_redaction.rs` (new); updated `policy_observability.rs`; updated `policy_metrics.rs` | -| Detailed design | (1) Extend `loki_native_otel_resource_attributes` allowlist to include `d2b.zone`, `d2b.provider`, `d2b.component`, `service.version`. (2) Add redaction lint: scan all v3 instrumentation call sites for `realm`, `workload_id`, `node_id`, `vm` (as label key), `path`, `socket`, `argv`, `pid`, `exe`. (3) Add metric label gate: assert no v3 `MetricDescriptor` carries a `vm` label. (4) Add bucket boundary gates for 5 ms and 20 ms. (5) Retain: `startup_tracing_avoids_host_path_fields`; SigNoz-only backend assertion; `tempo_guest_collector_shape`; `config_source = "realm-controllers"` absence gate. Primary reuse disposition: `adapt`. Preserved source-plan detail: adapt and extend; keep existing tests; add new policy gates. | +| Detailed design | (1) Extend `loki_native_otel_resource_attributes` allowlist to include `d2b.zone`, `d2b.provider`, `d2b.component`, `service.version`. (2) Add redaction lint: scan all v3 instrumentation call sites for `realm`, `workload_id`, `node_id`, `vm` (as label key), `path`, `socket`, `argv`, `pid`, `exe`. (3) Add structural metric-label policy lint: parse every v3 `MetricDescriptor`, require each label key and value domain to exist in the closed `METRIC_LABEL_POLICY`, reject exact keys `vm`, `zone`, `zone_id`, `zone_uid`, `credential_name`, `network`, `network_name`, and `link_name_hash`, reject resource-name-derived key suffixes `*_name`, `*_name_hash`, `*_name_digest`, and `*_uid`, and prove a `metadata.name` canary never enters label values. Fixed semantic labels remain allowed only with closed domains. (4) Assert the `d2b.zone` resource attribute remains present. (5) Add bucket boundary gates for 5 ms and 20 ms. (6) Retain: `startup_tracing_avoids_host_path_fields`; SigNoz-only backend assertion; `tempo_guest_collector_shape`; `config_source = "realm-controllers"` absence gate. Primary reuse disposition: `adapt`. Preserved source-plan detail: adapt and extend; keep existing tests; add new policy gates. | | Integration | Contract-tests run in workspace check and `make test-drift` | | Data migration | Full d2b 3.0 reset; no v2 state/config import | | Validation | These tests are their own validation artifact | diff --git a/docs/specs/ADR-046-zone-routing.md b/docs/specs/ADR-046-zone-routing.md index b372eab04..de6e3461b 100644 --- a/docs/specs/ADR-046-zone-routing.md +++ b/docs/specs/ADR-046-zone-routing.md @@ -2193,14 +2193,17 @@ Metric labels use closed low-cardinality sets: | --- | --- | | `d2b.zone_route.decision.total` | `outcome` (allowed/denied), `operation_kind`, `hop_count_bucket`, `reason_code` | | `d2b.zone_route.advertisement.total` | `outcome` (accepted/denied/withdrawn/replayed), `reason_code` | -| `d2b.zone_link.session.state` | `link_name_hash` (truncated sha256), `phase` (pending/ready/degraded/failed/unknown) | -| `d2b.zone_link.reconnect.total` | `link_name_hash`, `reason` | -| `d2b.zone_link.intent.queued` | `link_name_hash` | +| `d2b.zone_link.session.state` | `phase` (pending/ready/degraded/failed/unknown) | +| `d2b.zone_link.reconnect.total` | `reason` | +| `d2b.zone_link.intent.queued` | (none) | | `d2b.zone_link.relay.total` | `outcome`, `operation_kind` | | `d2b.zone_route.shortcut.total` | `outcome` (authorized/denied/torn-down), `teardown_reason` | -Prohibited labels: Zone tree path strings, resource names, subject refs, -provider diagnostics, host paths, session keys, or advertisement payload. +Prohibited labels: Zone names/UIDs, ZoneLink names or hashes/digests derived +from them, resource names, subject refs, provider diagnostics, host paths, +session keys, or advertisement payload. Zone identity remains the `d2b.zone` +OTEL resource attribute; ZoneLink identity remains available in authorized +audit records, never metric labels. ### OTEL spans @@ -2441,10 +2444,10 @@ The following transitions are NOT simple textual renames: | Reuse source | Same v3 baseline commit `b5ddbed6` | | Reuse action | adapt | | Destination | `packages/d2b-core-controller/src/zone_links.rs` | -| Detailed design | Child-local ZoneLink handler in core-controller: manages local ResourceSpec→allocator-bound session→advertisement lifecycle; session state machine (Pending/Established/Disconnected/Reconnecting/Revoked); reconnect backoff per `reconnectPolicy`; advertisement issuance/renewal/withdrawal using enrolled KK ComponentSession; child-store route cursor and outbound intent queue; capability ceiling change handling; status writer; no copied parent/child resource content; Nix-compiled `parentZone` selects the parent allocator, which alone owns privileged listeners, placement, and route namespace and creates no reciprocal resource Primary reuse disposition: `adapt`. Preserved source-plan detail: extract and adapt. | +| Detailed design | Child-local ZoneLink handler in core-controller: manages local ResourceSpec→allocator-bound session→advertisement lifecycle; session state machine (Pending/Established/Disconnected/Reconnecting/Revoked); reconnect backoff per `reconnectPolicy`; advertisement issuance/renewal/withdrawal using enrolled KK ComponentSession; child-store route cursor and outbound intent queue; capability ceiling change handling; status writer; aggregate metrics use only closed semantic phase/reason/outcome labels and never `link_name_hash` or another ZoneLink/Zone/resource identity label; Nix-compiled `parentZone` selects the parent allocator, which alone owns privileged listeners, placement, and route namespace and creates no reciprocal resource. Primary reuse disposition: `adapt`. Preserved source-plan detail: extract and adapt. | | Integration | Child core-controller process → local transport Provider → sealed binding for the allocator selected by `parentZone` → d2b-bus ComponentSession; child ZoneLink handler exchanges advertisements while that parent ZoneRouteEngine admits/withdraws them | | Data migration | New ZoneLink resources from Nix configuration; no prior enrollment compatibility | -| Validation | Session lifecycle tests; reconnect/revocation/ceiling-change; intent queue drain; cursor resync; advertisement renewal timing; fake-child tests | +| Validation | Session lifecycle tests; reconnect/revocation/ceiling-change; intent queue drain; cursor resync; advertisement renewal timing; fake-child tests; structural metric descriptor test asserts `vm`, `zone`, `zone_id`, `zone_uid`, and `link_name_hash` are absent and a ZoneLink-name canary never enters label values | | Removal proof | `RemoteNodeRegistry` retired after all enrolled peer routing moves to ZoneLink handler | ### ADR046-routing-005 diff --git a/docs/specs/providers/ADR-046-provider-activation-nixos.md b/docs/specs/providers/ADR-046-provider-activation-nixos.md index c35766e44..7ab5e1f63 100644 --- a/docs/specs/providers/ADR-046-provider-activation-nixos.md +++ b/docs/specs/providers/ADR-046-provider-activation-nixos.md @@ -983,11 +983,13 @@ material appears in any audit record. | Metric | Type | Labels (closed enumerations only) | | --- | --- | --- | -| `d2b_activation_nixos_generations_total` | Counter | zone, mode, outcome | -| `d2b_activation_nixos_generation_duration_seconds` | Histogram | zone, mode | -| `d2b_activation_nixos_runner_total` | Counter | zone, mode, outcome | +| `d2b_activation_nixos_generations_total` | Counter | mode, outcome | +| `d2b_activation_nixos_generation_duration_seconds` | Histogram | mode | +| `d2b_activation_nixos_runner_total` | Counter | mode, outcome | -No guest name, artifact ID, store path, or file path appears as a metric label. +No Zone/guest/resource name, artifact ID, store path, or file path appears as +a metric label. Zone identity remains in the bounded `d2b.zone` OTEL resource +attribute. --- @@ -1089,10 +1091,10 @@ Define JSON schema and Rust DTOs. Enforce: | Current source | Current top-level activation behavior in packages/d2b/src/lib.rs and hardlink-farm/store ownership split described in this dossier | | Reuse action | replace | | Destination | packages/d2b-provider-activation-nixos/src/controller/ | -| Detailed design | Implement the reconcile loop for activation-nixos.d2bus.org.NixosGeneration: validate executionRef, systemArtifactId, and priorGenerationRef; dispatch one activation-runner EphemeralProcess with canonical startRoot=true shape; observe runner status; mark superseded generations; prune by retainedGenerations through the finalizer protocol; never perform direct store-path operations, nix-collect-garbage, explicit VolumeGcRequest, raw argv composition, or store path writes to resources. Primary reuse disposition: `replace`. Preserved source-plan detail: replace top-level imperative activation flow with resource controller logic. | +| Detailed design | Implement the reconcile loop for activation-nixos.d2bus.org.NixosGeneration: validate executionRef, systemArtifactId, and priorGenerationRef; dispatch one activation-runner EphemeralProcess with canonical startRoot=true shape; observe runner status; mark superseded generations; prune by retainedGenerations through the finalizer protocol; emit §12.3 metrics with fixed `mode`/`outcome` semantics and no Zone or resource-name-derived labels; never perform direct store-path operations, nix-collect-garbage, explicit VolumeGcRequest, raw argv composition, or store path writes to resources. Primary reuse disposition: `replace`. Preserved source-plan detail: replace top-level imperative activation flow with resource controller logic. | | Integration | Controller watches NixosGeneration resources through Zone resource API, creates activation-runner EphemeralProcesses, releases ownership references for Provider/volume-local, and writes bounded status. | | Data migration | Full d2b 3.0 reset; adopt mode records an existing active generation but does not import v2 controller state | -| Validation | Controller tests for retention, finalizer sequence, no TTL retention, no direct store ops, no store path in status, deleted event-only removal, and runner shape. | +| Validation | Controller tests for retention, finalizer sequence, no TTL retention, no direct store ops, no store path in status, deleted event-only removal, runner shape, and a structural metric descriptor assertion that `vm`, `zone`, `zone_id`, `zone_uid`, and resource-name-derived keys are absent and a generation/Zone-name canary never enters labels. | | Removal proof | Direct hardlink-farm and garbage-collection calls from activation-nixos reachable paths are absent after controller and runner tests pass. | **Scope:** `packages/d2b-provider-activation-nixos/src/controller/` @@ -1214,6 +1216,7 @@ Gated on ADR046-activation-005 passing integration tests. | `state::test_provider_state_set_empty` | Provider declares no Provider state Volume; ProviderStateSet query returns empty for `Provider/activation-nixos` | | `state::test_no_state_layout_principal` | No dedicated state-layout `User/` or ComponentPrincipal reference is emitted for controller state | | `state::test_status_first_operational_state` | Bounded non-secret controller operational observations are stored in revisioned status/core Operation ledger and re-verified after restart | +| `metrics::test_identity_labels_absent` | Metric descriptors use only closed `mode`/`outcome` semantics; exact absence of `vm`, `zone`, `zone_id`, `zone_uid`, and resource-name-derived keys; Zone/generation-name canaries absent from values; `d2b.zone` resource attribute retained | ### 16.2 Integration tests (`integration/`) diff --git a/docs/specs/providers/ADR-046-provider-clipboard-wayland.md b/docs/specs/providers/ADR-046-provider-clipboard-wayland.md index db84f938f..7ed56d8a5 100644 --- a/docs/specs/providers/ADR-046-provider-clipboard-wayland.md +++ b/docs/specs/providers/ADR-046-provider-clipboard-wayland.md @@ -1496,10 +1496,10 @@ deleted. | Current source | packages/d2b-clipd/src/audit.rs and policy types from packages/d2b-clipd/src/policy.rs | | Reuse action | adapt | | Destination | packages/d2b-provider-clipboard-wayland/src/service/audit.rs and packages/d2b-provider-clipboard-wayland/src/service/metrics.rs | -| Detailed design | Implement ClipboardAuditEvent and fail-closed Zone audit queue by porting baseline audit code, renaming realm fields to source_zone_id and dest_zone_id, making ReasonCode a closed enum with unknown protobuf fields rejected, replacing exact byte counts with SizeBucket, emitting to d2b.audit.v3, and adding closed-label OTEL metrics and spans from the dossier tables. Primary reuse disposition: `adapt`. Preserved source-plan detail: port and adapt audit plus metrics with zone names and redaction changes. | +| Detailed design | Implement ClipboardAuditEvent and fail-closed Zone audit queue by porting baseline audit code, renaming realm fields to source_zone_id and dest_zone_id, making ReasonCode a closed enum with unknown protobuf fields rejected, replacing exact byte counts with SizeBucket, emitting to d2b.audit.v3, and adding closed-semantic-label OTEL metrics and spans from the dossier tables. Metric descriptors carry no Zone/resource-name-derived identity; `d2b.zone` remains a resource attribute. Primary reuse disposition: `adapt`. Preserved source-plan detail: port and adapt audit plus resource-name-free metrics and redaction changes. | | Integration | clipd-host emits audit events to the Zone audit sink and OTEL metrics/spans to the observability Provider pipeline during clipboard operations. | | Data migration | Full d2b 3.0 reset; audit stream is v3 Zone-local and no v2 audit records are imported | -| Validation | Audit tests for no bytes in events, closed ReasonCode deserialization, fail-closed queue rejection, SizeBucket discretization, allowed metric labels, and excluded span attributes. | +| Validation | Audit tests for no bytes in events, closed ReasonCode deserialization, fail-closed queue rejection, SizeBucket discretization, excluded span attributes, and structural metric descriptor assertions for exact absence of `vm`, `zone`, `zone_id`, `zone_uid`, and resource-name-derived keys plus clipboard/Zone-name canary absence while preserving `d2b.zone` resource attributes. | | Removal proof | Old audit shape with realm field names and exact byte counts is absent after ported tests assert the v3 ClipboardAuditEvent schema. | **Type:** implementation diff --git a/docs/specs/providers/ADR-046-provider-credential-managed-identity.md b/docs/specs/providers/ADR-046-provider-credential-managed-identity.md index 421b8f7ee..047471e15 100644 --- a/docs/specs/providers/ADR-046-provider-credential-managed-identity.md +++ b/docs/specs/providers/ADR-046-provider-credential-managed-identity.md @@ -1356,7 +1356,7 @@ embed secret shapes. | Metric | Type | Labels | | --- | --- | --- | | `d2b_credential_operations_total` | Counter | `provider="credential-managed-identity"`, `operation_class`, `placement_binding`, `outcome` | -| `d2b_credential_lease_expiry_seconds` | Gauge | `provider`, `credential_name`, `placement_binding` | +| `d2b_credential_lease_expiry_seconds` | Gauge | `provider`, `placement_binding` | | `d2b_credential_rotation_total` | Counter | `provider`, `policy`, `outcome` | | `d2b_credential_provider_health` | Gauge (0/1) | `provider`, `placement_binding` | | `d2b_credential_active_leases` | Gauge | `provider`, `placement_binding` | @@ -1364,9 +1364,10 @@ embed secret shapes. `alias` in `d2b_credential_imds_calls_total` uses only the closed alias string (`azure-imds`, `azure-imds-aca`); no resolved endpoint URL appears in this label. -Label cardinality is bounded. `credential_name` appears only in expiry gauges -where per-resource precision is required; it is omitted from high-cardinality -counters. +Label cardinality is bounded and semantic. The expiry gauge reports the +minimum seconds remaining across active leases in each provider/placement +aggregate (0 when none). Credential and Zone identity remain in the bounded +OTEL attributes above and permitted audit fields, never metric labels. --- @@ -1834,10 +1835,10 @@ item. | Current source | `packages/d2b-realm-provider/src/error.rs:contains_sensitive_shape`; `packages/d2b-core/src/realm_workloads_launcher.rs:LauncherMetadataInvariants.no_secrets_or_credentials`; main `a1cc0b2d` managed-identity canary tests listed in §Source reuse | | Reuse action | adapt | | Destination | packages/d2b-provider-credential-managed-identity/src/{audit.rs,telemetry.rs}; packages/d2b-contract-tests/tests/credential_audit.rs | -| Detailed design | Shared audit/OTEL: emit audit records for all methods and controller events per §Audit; emit OTEL spans and metrics per §OTEL and metrics; add `d2b_credential_imds_calls_total` counter with bounded `alias` label; enforce `contains_sensitive_shape` on all string fields in audit records and metric labels; add canary tests for `managed-identity-canary`, `credential_canary`, and `imds-endpoint-canary` in `canary.rs`. Primary reuse disposition: `adapt`. Preserved source-plan detail: adapt existing sensitive-shape guard and canary pattern to v3 audit, OTEL, and metric surfaces. | +| Detailed design | Shared audit/OTEL: emit audit records for all methods and controller events per §Audit; emit OTEL spans and metrics per §OTEL and metrics with no Zone/Credential/resource-name-derived label; report expiry as the minimum for each provider/placement aggregate; add `d2b_credential_imds_calls_total` counter with bounded `alias` label; enforce `contains_sensitive_shape` on all string fields in audit records and metric labels; add canary tests for `managed-identity-canary`, `credential_canary`, `imds-endpoint-canary`, Credential `metadata.name`, and Zone name in `canary.rs`. Primary reuse disposition: `adapt`. Preserved source-plan detail: adapt existing sensitive-shape guard and canary pattern to v3 audit, OTEL, and metric surfaces. | | Integration | Controller and agent service methods call audit/telemetry helpers; audit subsystem and OTEL exporters consume bounded redacted records; contract tests validate credential audit shape across providers. | | Data migration | None — audit/telemetry only; no runtime state import | -| Validation | `packages/d2b-contract-tests/tests/credential_audit.rs`; managed-identity `canary.rs`; audit/OTEL unit tests for labels and sensitive-shape rejection | +| Validation | `packages/d2b-contract-tests/tests/credential_audit.rs`; managed-identity `canary.rs`; audit/OTEL unit tests structurally assert exact absence of `vm`, `zone`, `zone_id`, `zone_uid`, `credential_name`, and every resource-name-derived label key, reject Credential/Zone-name label canaries, preserve allowed OTEL identity attributes, and reject sensitive shapes | | Removal proof | None — audit/telemetry helpers are additive; no prior owner to remove | --- @@ -1939,6 +1940,7 @@ audit record field set conformance, delivery session binding contract, RBAC | `clientId` value absent from audit records, metric labels, OTEL spans, error messages, and log lines | Config field exclusion | | `imdsEndpointAlias` value absent from OTEL span attributes, audit records, and error messages | Alias exclusion | | IMDS response-shaped string absent from status, audit, OTEL, and logs | IMDS response content exclusion | +| Metric descriptors contain no `vm`, `zone`, `zone_id`, `zone_uid`, `credential_name`, or resource-name-derived key; Credential/Zone-name canaries are absent from emitted label values while allowed OTEL identity attributes remain | Structural metric identity-label exclusion | #### `delivery.rs` diff --git a/docs/specs/providers/ADR-046-provider-credential-secret-service.md b/docs/specs/providers/ADR-046-provider-credential-secret-service.md index 8cb9b92c6..b2328507d 100644 --- a/docs/specs/providers/ADR-046-provider-credential-secret-service.md +++ b/docs/specs/providers/ADR-046-provider-credential-secret-service.md @@ -1032,14 +1032,15 @@ embedding secret shapes. | Metric | Type | Labels | | --- | --- | --- | | `d2b_credential_operations_total` | Counter | `provider=credential-secret-service`, `operation_class`, `placement_binding=user-agent`, `outcome` | -| `d2b_credential_lease_expiry_seconds` | Gauge | `provider=credential-secret-service`, `credential_name`, `placement_binding=user-agent` | +| `d2b_credential_lease_expiry_seconds` | Gauge | `provider=credential-secret-service`, `placement_binding=user-agent` | | `d2b_credential_rotation_total` | Counter | `provider=credential-secret-service`, `policy`, `outcome` | | `d2b_credential_provider_health` | Gauge (0/1) | `provider=credential-secret-service` | | `d2b_credential_active_leases` | Gauge | `provider=credential-secret-service`, `placement_binding=user-agent` | -`credential_name` appears only in the expiry gauge; it is omitted from -high-cardinality counters. Label cardinality is bounded; secret-shape -assertions run on all label values. +The expiry gauge reports the minimum seconds remaining across active +user-agent leases (0 when none). Label cardinality is bounded and semantic; +Credential and Zone identity remain in bounded OTEL attributes and permitted +audit fields, never labels. Secret-shape assertions run on all label values. --- @@ -1301,10 +1302,10 @@ generation cleanup contract in `nixos-modules/options-resources.nix` and | Current source | `packages/d2b-core/src/realm_workloads_launcher.rs:LauncherMetadataInvariants.no_secrets_or_credentials`; secret-service main reuse canary tests listed in §14 | | Reuse action | adapt | | Destination | packages/d2b-provider-credential-secret-service/src/{audit.rs,telemetry.rs} | -| Detailed design | Audit/OTEL: emit audit records and OTEL spans/metrics for all credential service methods and controller events, with canary enforcement and no token/object-path/lease bytes in status, delivery outer headers, audit, metrics, spans, or logs. Full detail remains in `ADR-046-resources-credential` §Implementation work items. Primary reuse disposition: `adapt`. Preserved source-plan detail: adapt zero-secret invariant and canary test pattern to credential-secret-service audit/OTEL surfaces. | +| Detailed design | Audit/OTEL: emit audit records and OTEL spans/metrics for all credential service methods and controller events, with canary enforcement, expiry aggregated across user-agent leases, no Zone/Credential/resource-name-derived metric label, and no token/object-path/lease bytes in status, delivery outer headers, audit, metrics, spans, or logs. Full detail remains in `ADR-046-resources-credential` §Implementation work items. Primary reuse disposition: `adapt`. Preserved source-plan detail: adapt zero-secret invariant and canary test pattern to credential-secret-service audit/OTEL surfaces. | | Integration | Controller and service methods call audit/telemetry helpers; audit subsystem and OTEL exporters consume bounded event/span/metric records; canary tests verify every public observable surface stays secret-free. | | Data migration | None — audit/telemetry only; no runtime state migration | -| Validation | Credential audit/OTEL tests from `ADR-046-resources-credential`; `tests/canary.rs` and `tests/delivery.rs` for credential-secret-service | +| Validation | Credential audit/OTEL tests from `ADR-046-resources-credential`; `tests/canary.rs` structurally asserts exact absence of `vm`, `zone`, `zone_id`, `zone_uid`, `credential_name`, and every resource-name-derived metric key plus Credential/Zone-name label canary absence while preserving allowed OTEL identity attributes; `tests/delivery.rs` for credential-secret-service | | Removal proof | None — audit/telemetry helpers are new; no prior owner to remove | Implements audit record and OTEL span/metric emission for all credential @@ -1377,6 +1378,7 @@ All `check_provider_conformance` arms pass for `d2b-provider-credential-secret-s | `object_path_absent_all_responses` | `object_path_canary` absent from all response DTOs | | `canary_absent_audit_records` | `credential_canary` and `object_path_canary` absent from all audit record JSON | | `canary_absent_span_attributes` | Neither canary present in any OTEL span attribute captured by test subscriber | +| `metric_identity_labels_absent` | No descriptor key is `vm`, `zone`, `zone_id`, `zone_uid`, `credential_name`, or resource-name-derived; Credential/Zone-name canaries are absent from values; allowed OTEL identity attributes remain | | `canary_absent_delivery_binding` | Neither canary present in delivery session binding parameters | #### `tests/delivery.rs` diff --git a/docs/specs/providers/ADR-046-provider-device-gpu.md b/docs/specs/providers/ADR-046-provider-device-gpu.md index e9ce2be1c..ac63e0243 100644 --- a/docs/specs/providers/ADR-046-provider-device-gpu.md +++ b/docs/specs/providers/ADR-046-provider-device-gpu.md @@ -1158,20 +1158,20 @@ video socket paths, and any credential material. ## OTEL telemetry -All telemetry placement — span vs resource attribute classification, -`d2b.device.zone` cardinality, `d2b.device.provider` label level, and full -label set boundaries — is defined in `ADR-046-telemetry-audit-and-support`. -This dossier does not compete with those constraints. +All telemetry placement — span vs resource attribute classification and full +label-set boundaries — is defined in +`ADR-046-telemetry-audit-and-support`. `d2b.zone` and +`d2b.provider="device-gpu"` are bounded OTEL resource attributes, never metric +labels. This dossier does not compete with those constraints. GPU-specific label rules: - No device path, DRM card node, render node path, PCI slot, GPU socket path, video socket path, or process PID may appear in any OTEL span attribute or metric label. -- `d2b.device.provider = device-gpu` is a fixed string label. -- `d2b.gpu.mode` ∈ `{full, render-node}` is a closed-set label (low cardinality). -- `d2b.gpu.video_sidecar` ∈ `{enabled, disabled}` is a closed-set label. -- `d2b.gpu.arbitration` ∈ `{exclusive, shared}` is a closed-set label. +- `mode` ∈ `{full, render-node}` is a closed semantic metric label. +- `video_sidecar` ∈ `{enabled, disabled}` is a closed semantic metric label. +- `arbitration` ∈ `{exclusive, shared}` is a closed semantic metric label. - No `vm_name`, `guest_name`, or human-readable VM identifier appears in metric labels; VM identity is carried only in OTEL resource attributes and trace context. @@ -1650,11 +1650,11 @@ disposition contract test passes. | Current source | `packages/d2bd/src/usbip_state_machine.rs` (implemented-and-reachable) as reconcile loop pattern reference. GPU/video reconcile state is `ADR-only`. | | Reuse source | Pattern only: `packages/d2bd/src/usbip_state_machine.rs` (baseline). No code copy. | | Reuse action | adapt | -| Destination | `packages/d2b-provider-device-gpu/src/controller.rs` | -| Detailed design | Five triggers: `spec-generation-changed`, `deletion-requested`, `dependency-changed`, `scheduled-observe`, `owned-resource-changed`. Each trigger handler writes optimistic `ResourceMutationBatch`. Status writer in `status.rs`. Async watch task + per-resource reconcile tasks. Independent resources in parallel. Primary reuse disposition: `adapt`. Preserved source-plan detail: `adapt` — implement the five-trigger reconcile loop using Provider toolkit async reconciler. | +| Destination | `packages/d2b-provider-device-gpu/src/{controller.rs,telemetry.rs}` | +| Detailed design | Five triggers: `spec-generation-changed`, `deletion-requested`, `dependency-changed`, `scheduled-observe`, `owned-resource-changed`. Each trigger handler writes optimistic `ResourceMutationBatch`. Status writer in `status.rs`. Async watch task + per-resource reconcile tasks. Independent resources in parallel. Telemetry uses only closed `mode`/`video_sidecar`/`arbitration` semantics; `d2b.zone` and `d2b.provider` remain resource attributes and no Zone/resource-name-derived key is a metric label. Primary reuse disposition: `adapt`. Preserved source-plan detail: `adapt` — implement the five-trigger reconcile loop using Provider toolkit async reconciler. | | Integration | Resource API (ADR046 store) must be present; fake ResourceClient available from Provider toolkit; `tests/combined_reconcile.rs` validates trigger dispatch | | Data migration | None — full d2b 3.0 reset; no prior state to migrate | -| Validation | `cargo test -p d2b-provider-device-gpu --test combined_reconcile`; all five trigger handlers must reach their expected output state | +| Validation | `cargo test -p d2b-provider-device-gpu --test combined_reconcile`; all five trigger handlers must reach their expected output state; structural metric descriptor test asserts exact absence of `vm`, `zone`, `zone_id`, `zone_uid`, and resource-name-derived keys plus GPU Device/Zone-name canary absence while preserving `d2b.zone` resource attributes | | Removal proof | Current ProcessRole::Gpu/Video/GpuRenderNode retained until this test passes; see ProcessRole disposition table | ### ADR046-gpu-003: DRM sysfs probe and observe scheduler diff --git a/docs/specs/providers/ADR-046-provider-device-security-key.md b/docs/specs/providers/ADR-046-provider-device-security-key.md index 5bc824ea3..bee6a6dd5 100644 --- a/docs/specs/providers/ADR-046-provider-device-security-key.md +++ b/docs/specs/providers/ADR-046-provider-device-security-key.md @@ -1546,12 +1546,12 @@ controller, not Core; uses the Zone runtime audit stream): OTEL span attributes and metric labels follow `ADR-046-telemetry-audit-and-support`. Constraints specific to this Provider: -- **`d2b.device.provider`** label value: `"device-security-key"` (closed literal). -- **`d2b.device.zone`** label: Zone name. Cardinality ≤ number of Zones. -- **`d2b.device.phase`** label: `"Ready"` | `"Pending"` | `"Degraded"` | `"Failed"` | `"Unknown"`. -- Metric `d2b_device_sk_session_total{zone, outcome}`: counter; `outcome` ∈ `{success, timeout, cancelled, busy, conflict, error}`. -- Metric `d2b_device_sk_ceremony_duration_seconds{zone}`: histogram; bucketed 0–120 s. -- Metric `d2b_device_sk_relay_restarts_total{zone}`: counter. +- OTEL resource attributes include `d2b.provider="device-security-key"` and + `d2b.zone=`; neither is copied into metric labels. +- **`phase`** metric label: `"Ready"` | `"Pending"` | `"Degraded"` | `"Failed"` | `"Unknown"`. +- Metric `d2b_device_sk_session_total{outcome}`: counter; `outcome` ∈ `{success, timeout, cancelled, busy, conflict, error}`. +- Metric `d2b_device_sk_ceremony_duration_seconds`: histogram; bucketed 0–120 s. +- Metric `d2b_device_sk_relay_restarts_total`: counter. - No metric or span attribute carries device name (only `resource_name_digest`), session ID, guest name, hidraw path, or serial. - OTEL emitter: lightweight bounded ring (no OTEL SDK in the Provider process; @@ -2194,10 +2194,10 @@ class. | Current source | None — net-new v3 work; no pre-ADR45 baseline equivalent | | Reuse action | create | | Destination | Provider/controller bounded telemetry emitter and observability-otel handoff for security-key metrics | -| Detailed design | OTEL metrics: `d2b_device_sk_session_total`, `d2b_device_sk_ceremony_duration_seconds`, `d2b_device_sk_relay_restarts_total` via bounded emitter ring | +| Detailed design | OTEL metrics: `d2b_device_sk_session_total`, `d2b_device_sk_ceremony_duration_seconds`, `d2b_device_sk_relay_restarts_total` via bounded emitter ring; descriptors use only closed semantic labels and never Zone/resource-name-derived identity, while `d2b.zone` and `d2b.provider` remain OTEL resource attributes | | Integration | Relay/controller write metric events to the bounded ring; observability-otel Provider drains and exports; dashboards/CLI consume closed labels and bounded histograms. | | Data migration | Full d2b 3.0 reset; no v2 telemetry import | -| Validation | Metrics tests assert closed label sets, no device/session/guest/path labels, bounded ring behavior, and correct session/ceremony/restart counters. | +| Validation | Metrics tests structurally assert closed label sets, exact absence of `vm`, `zone`, `zone_id`, `zone_uid`, and resource-name-derived keys, Device/Zone-name canary absence, retained `d2b.zone` resource attributes, bounded ring behavior, and correct session/ceremony/restart counters. | | Removal proof | None — net-new; no prior owner to remove | ### ADR046-security-key-023 diff --git a/docs/specs/providers/ADR-046-provider-device-tpm.md b/docs/specs/providers/ADR-046-provider-device-tpm.md index 64581692e..19a0bbeac 100644 --- a/docs/specs/providers/ADR-046-provider-device-tpm.md +++ b/docs/specs/providers/ADR-046-provider-device-tpm.md @@ -1040,13 +1040,14 @@ store-assigned UIDs — not human names, filesystem paths, or socket addresses. | Metric | Type | Labels | Description | | --- | --- | --- | --- | -| `d2b_device_tpm_phase` | gauge | `zone`, `phase` | Device count per phase | -| `d2b_device_tpm_flush_duration_seconds` | histogram | `zone`, `outcome` | Pre-start flush wall-clock duration | -| `d2b_device_tpm_swtpm_restart_count` | counter | `zone` | swtpm restart events | -| `d2b_device_tpm_marker_status` | gauge | `zone`, `status` | TPM state Volume marker status | +| `d2b_device_tpm_phase` | gauge | `phase` | Device count per phase | +| `d2b_device_tpm_flush_duration_seconds` | histogram | `outcome` | Pre-start flush wall-clock duration | +| `d2b_device_tpm_swtpm_restart_count` | counter | (none) | swtpm restart events | +| `d2b_device_tpm_marker_status` | gauge | `status` | TPM state Volume marker status | -No label carries a device name, VM name, path, process name, UID, GID, -PID, or socket address. +No label carries a Zone name/UID, device name, VM name, path, process name, +UID, GID, PID, or socket address. Zone identity remains in the bounded +`d2b.zone` OTEL resource attribute. ### 14.2 Traces @@ -1291,10 +1292,10 @@ Implement `TpmEffectPort` trait, typed TPM EndpointRef handoff, and | Current source | Current direct daemon/broker swtpm lifecycle call sites in `packages/d2bd/src/*` are superseded; controller algorithm is specified in §11.1 | | Reuse action | replace | | Destination | packages/d2b-provider-device-tpm/src/controller.rs; packages/d2b-provider-device-tpm/tests/controller_fsm.rs | -| Detailed design | Controller reconcile state machine: implement the Device reconcile algorithm from §11.1 against `FakeTpmEffectPort`, covering happy path, Volume not-ready, marker fail-closed, flush failure, swtpm maxRestarts, and finalizer behavior where Process is deleted and Volume retained. Primary reuse disposition: `replace`. Preserved source-plan detail: replace direct daemon lifecycle with Provider reconcile against `FakeTpmEffectPort` and resource status. | +| Detailed design | Controller reconcile state machine: implement the Device reconcile algorithm from §11.1 against `FakeTpmEffectPort`, covering happy path, Volume not-ready, marker fail-closed, flush failure, swtpm maxRestarts, and finalizer behavior where Process is deleted and Volume retained; emit §14 metrics with only closed `phase`/`outcome`/`status` semantics and no Zone/resource-name-derived label. Primary reuse disposition: `replace`. Preserved source-plan detail: replace direct daemon lifecycle with Provider reconcile against `FakeTpmEffectPort` and resource status. | | Integration | Resource watches drive the controller; controller creates/observes Volume, Process, EphemeralProcess, and Endpoint resources through `TpmEffectPort`; Device status/finalizers expose outcomes to the ResourceAPI. | | Data migration | Existing TPM state migration follows §17.3: the old `/var/lib/d2b/vms//swtpm/` directory moves to the controller-created Volume path with the provisioning marker preserved and re-keyed; this item must not silently recreate missing state. | -| Validation | `tests/controller_fsm.rs` covering happy path, Volume not-ready, marker fail-closed, flush failed, swtpm maxRestarts, and finalizer behavior | +| Validation | `tests/controller_fsm.rs` covering happy path, Volume not-ready, marker fail-closed, flush failed, swtpm maxRestarts, and finalizer behavior; `tests/metrics_labels.rs` structurally asserts exact absence of `vm`, `zone`, `zone_id`, `zone_uid`, and resource-name-derived keys and Device/Zone-name canary absence | | Removal proof | Direct daemon swtpm lifecycle logic is removable after this Provider reconcile FSM reaches parity and ADR046-device-tpm-013 removes the old call sites | Implement Device reconcile algorithm (§11.1) against `FakeTpmEffectPort`. @@ -1545,6 +1546,7 @@ budget. | `marker_fail_closed.rs` | Marker replaced/missing → Device Failed; no auto-recovery; no second `ensure_state_volume` | | `finalizer.rs` | Process deleted; Volume retained; Deleted emitted by core | | `redaction.rs` | No path/UID/socket/pidfd in status, audit span attrs, or log records | +| `metrics_labels.rs` | Closed semantic metric descriptors; exact identity-key absence; Device/Zone-name canaries absent from label values; `d2b.zone` resource attribute retained | | `schema.rs` | Device spec admission round-trip through JSON schema | | `nix_roundtrip.rs` | Nix form emits no Volume/Process/EphemeralProcess resources | diff --git a/docs/specs/providers/ADR-046-provider-display-wayland.md b/docs/specs/providers/ADR-046-provider-display-wayland.md index ecb4e493f..679fe44d5 100644 --- a/docs/specs/providers/ADR-046-provider-display-wayland.md +++ b/docs/specs/providers/ADR-046-provider-display-wayland.md @@ -1288,16 +1288,16 @@ a guest name, Zone name, socket path, interface name, or window title. | Metric name | Type | Labels | Description | | --- | --- | --- | --- | -| `d2b_display_wayland_session_total` | counter | `zone_id`, `outcome` | Total WaylandSession create/delete events | -| `d2b_display_wayland_session_ready` | gauge | `zone_id` | Current Ready session count | -| `d2b_display_wayland_proxy_start_total` | counter | `zone_id`, `outcome` | Proxy process start events | -| `d2b_display_wayland_proxy_exit_total` | counter | `zone_id`, `exit_class` | Proxy process exit events | -| `d2b_display_wayland_policy_warning_total` | counter | `zone_id`, `warning_code` | Policy advisory events | -| `d2b_display_wayland_policy_compile_total` | counter | `zone_id`, `outcome` | Policy compile events | +| `d2b_display_wayland_session_total` | counter | `outcome` | Total WaylandSession create/delete events | +| `d2b_display_wayland_session_ready` | gauge | (none) | Current Ready session count | +| `d2b_display_wayland_proxy_start_total` | counter | `outcome` | Proxy process start events | +| `d2b_display_wayland_proxy_exit_total` | counter | `exit_class` | Proxy process exit events | +| `d2b_display_wayland_policy_warning_total` | counter | `warning_code` | Policy advisory events | +| `d2b_display_wayland_policy_compile_total` | counter | `outcome` | Policy compile events | -`zone_id` is a stable opaque short ID (not the Zone name string) to avoid -metric cardinality explosion. `outcome` and `exit_class` are closed bounded -enums. +Zone identity is carried only in the bounded `d2b.zone` OTEL resource +attribute. `outcome`, `exit_class`, and `warning_code` are closed bounded +enums; no Zone UID or resource name is a metric dimension. ### 14.3 OTEL spans @@ -1771,10 +1771,10 @@ produces the guest binary (see §19 removal table). | Current source | `packages/d2b-wayland-proxy/src/diag.rs` (rate-limited bounded diagnostics) | | Reuse action | adapt | | Destination | `packages/d2b-provider-display-wayland/src/audit.rs`, `packages/d2b-provider-display-wayland/src/metrics.rs` | -| Detailed design | Implement audit record types for all events in §14.1; implement OTEL metric counters/gauges in §14.2; adapt `DiagRateLimiter` to use closed label sets; validate that no socket path, user identity, window title, or app-id appears in any log/audit/metric surface Primary reuse disposition: `adapt`. Preserved source-plan detail: extract and adapt. | +| Detailed design | Implement audit record types for all events in §14.1; implement OTEL metric counters/gauges in §14.2; adapt `DiagRateLimiter` to use closed semantic label sets with no Zone UID/name or resource-name-derived key; retain Zone identity in `d2b.zone` resource attributes; validate that no socket path, user identity, window title, or app-id appears in any log/audit/metric surface. Primary reuse disposition: `adapt`. Preserved source-plan detail: extract and adapt. | | Integration | Providers emit via Zone telemetry emitter; audit records committed before operation completion | | Data migration | None — full d2b 3.0 reset; no prior state to migrate | -| Validation | Redaction contract tests (`policy_observability.rs` pattern), audit record schema tests, label-cardinality tests | +| Validation | Redaction contract tests (`policy_observability.rs` pattern), audit record schema tests, structural label-policy tests asserting exact absence of `vm`, `zone`, `zone_id`, `zone_uid`, and resource-name-derived keys plus WaylandSession/Zone-name canary absence | | Removal proof | N/A (new code) | ### ADR046-display-004 @@ -1857,7 +1857,7 @@ per-test budget. | `virgl_video_unsupported_condition` | `virglVideo: true` with GPU Device not advertising video decode sets `VirglVideoUnsupported` condition | | `conformance_display_fails_closed_when_unsupported` | Provider without `window-forwarding` capability returns `CapabilityDenied`; no fallback | | `audit_record_no_paths` | All audit record types contain no socket paths, user identities, or window titles | -| `metric_labels_closed` | All metric label values are members of the closed pre-declared label sets | +| `metric_labels_closed` | All descriptors use closed semantic labels; exact identity-key absence; WaylandSession/Zone-name canaries absent from values; `d2b.zone` resource attribute retained | | `readiness_event_bounded` | `ProxyReadinessEvent` serialization contains no socket paths | | `principal_pool_exhausted_fails_closed` | All pool slots occupied → new dynamic session transitions to `Failed` with `NoPrincipalAvailable`; no OS user creation attempted | | `opaque_principal_name_derived` | `principalFor(zone, sessionName)` produces `d2b-wlp-` with correct SHA-256 derivation; two sessions with different names produce different accounts; pool slots produce `d2b-wlp-p` | diff --git a/docs/specs/providers/ADR-046-provider-network-local.md b/docs/specs/providers/ADR-046-provider-network-local.md index a0c720f91..4023b4944 100644 --- a/docs/specs/providers/ADR-046-provider-network-local.md +++ b/docs/specs/providers/ADR-046-provider-network-local.md @@ -1959,25 +1959,27 @@ d2b.network.dnsmasq.restart d2b.network.observe.drift_check ``` -Labels use closed cardinality. `network.name` is a ResourceName (bounded -`^[a-z][a-z0-9-]*$`), never a hostname or FQDN. +Metric labels use closed semantic cardinality and carry no Zone or Network +identity. Zone identity remains in the `d2b.zone` OTEL resource attribute. +`network.name` is a ResourceName (bounded `^[a-z][a-z0-9-]*$`), never a +hostname or FQDN, and is never copied into a metric label. Metrics: | Metric | Labels | | --- | --- | -| `d2b_network_reconcile_total` | `zone`, `outcome` | -| `d2b_network_phase` | `zone`, `phase` | -| `d2b_network_attachment_count` | `zone`, `network` | -| `d2b_nftables_apply_total` | `zone`, `outcome` | -| `d2b_nftables_drift_total` | `zone` | -| `d2b_bridge_create_total` | `zone`, `outcome` | -| `d2b_bridge_delete_total` | `zone`, `outcome` | -| `d2b_network_volume_sync_total` | `zone`, `outcome` | -| `d2b_network_agent_reload_total` | `zone`, `outcome` | -| `d2b_network_agent_restart_total` | `zone`, `outcome` | -| `d2b_network_dnsmasq_restart_total` | `zone`, `outcome` | -| `d2b_network_observe_drift_total` | `zone`, `surface` | +| `d2b_network_reconcile_total` | `outcome` | +| `d2b_network_phase` | `phase` | +| `d2b_network_attachment_count` | (none) | +| `d2b_nftables_apply_total` | `outcome` | +| `d2b_nftables_drift_total` | (none) | +| `d2b_bridge_create_total` | `outcome` | +| `d2b_bridge_delete_total` | `outcome` | +| `d2b_network_volume_sync_total` | `outcome` | +| `d2b_network_agent_reload_total` | `outcome` | +| `d2b_network_agent_restart_total` | `outcome` | +| `d2b_network_dnsmasq_restart_total` | `outcome` | +| `d2b_network_observe_drift_total` | `surface` | --- @@ -2362,11 +2364,11 @@ On controller binary upgrade: | Dependency/owner | Provider; depends on ADR046-nl-001 through ADR046-nl-005 and owns the Network reconcile/observe/finalize handlers. | | Current source | None — net-new v3 provider controller; v1 behavior lived in `nixos-modules/network.nix` and `nixos-modules/net.nix` static NixOS module logic. | | Reuse action | adapt | -| Destination | `packages/d2b-provider-network-local/src/controller.rs`. | -| Detailed design | Implement `controller.rs` reconcile/observe/finalize handlers with `NetworkEffectPort` injection. Primary reuse disposition: `adapt`. Preserved source-plan detail: port semantics into provider reconcile state machine; do not reuse static per-env systemd/Nix ownership. | +| Destination | `packages/d2b-provider-network-local/src/{controller.rs,metrics.rs}`. | +| Detailed design | Implement `controller.rs` reconcile/observe/finalize handlers with `NetworkEffectPort` injection and the §21 metric descriptors with closed semantic labels. No descriptor may carry `vm`, `zone`, `zone_id`, `zone_uid`, `network`, or another resource-name-derived key; Network/Zone identity stays in trace/resource attributes. Primary reuse disposition: `adapt`. Preserved source-plan detail: port semantics into provider reconcile state machine; do not reuse static per-env systemd/Nix ownership. | | Integration | Controller watches Network, Guest, Volume, Process, User, Host, and Zone resources; creates child resources, writes status, invokes `NetworkEffectPort`, and drives finalizers. | | Data migration | Full d2b 3.0 reset; no v2 state/config import. | -| Validation | `tests/controller_state.rs` covers normal reconcile, errors, finalizer ordering, adoption on restart, and observe/drift cycles with deterministic clock. | +| Validation | `tests/controller_state.rs` covers normal reconcile, errors, finalizer ordering, adoption on restart, and observe/drift cycles with deterministic clock; `tests/metrics_labels.rs` structurally asserts exact identity-key absence and that a Network-name canary never enters metric label values. | | Removal proof | Supersedes static per-env lifecycle in `nixos-modules/network.nix` and `nixos-modules/net.nix`; removal proof is successor controller coverage plus deletion of duplicate old gates when this provider lands. | ### ADR046-nl-007 @@ -2603,6 +2605,7 @@ budget. | `firewall_ownership.rs` | Host and net-VM intents contain no TCP/3240/USBIP rule; device-usbip marker/rule churn does not alter Network digest or `FirewallReady` | | `conformance.rs` | Provider toolkit black-box conformance suite; descriptor validation; ResourceType schema fingerprint | | `fault_injection.rs` | `NetworkEffectPort` returns each `EffectError` variant; each step fails independently; retry/requeue classification; reconcile context has no broker socket; provider crate has no broker import | +| `metrics_labels.rs` | Every metric descriptor uses only closed semantic labels; exact absence of `vm`, `zone`, `zone_id`, `zone_uid`, `network`, and resource-name-derived keys; Network-name canary absent from emitted labels; `d2b.zone` retained as an OTEL resource attribute | ### 26.3 Integration tests (`integration/`) diff --git a/docs/specs/providers/ADR-046-provider-notification-desktop.md b/docs/specs/providers/ADR-046-provider-notification-desktop.md index 59d1e47c8..a41588cdb 100644 --- a/docs/specs/providers/ADR-046-provider-notification-desktop.md +++ b/docs/specs/providers/ADR-046-provider-notification-desktop.md @@ -1056,27 +1056,27 @@ content. ### 13.1 Separation invariant Telemetry (OTEL) and authoritative audit are distinct subsystems per -`ADR-046-telemetry-audit-and-support` §"Separation invariant". No OTEL field -ever carries notification content, action text, or source identity beyond -opaque resource UID digests. +`ADR-046-telemetry-audit-and-support` §"Separation invariant". No OTEL field +ever carries notification content or action text. Metric labels carry no +source identity; bounded OTEL resource attributes retain `d2b.zone`. ### 13.2 OTEL metrics (closed labels only) | Metric | Type | Labels | Notes | | --- | --- | --- | --- | -| `d2b_notification_created_total` | counter | `zone`, `category`, `urgency` | Count by category/urgency only | -| `d2b_notification_delivered_total` | counter | `zone`, `category`, `urgency`, `sink_result` | Terminal delivery outcome | -| `d2b_notification_action_invoked_total` | counter | `zone`, `category` | Action invoked; no action ID label | -| `d2b_notification_drop_total` | counter | `zone`, `reason` | `reason`: `capacity` \| `observer-backpressure` \| `sink-unavailable` | -| `d2b_notification_dbus_duration_seconds` | histogram | `zone`, `outcome` | D-Bus call latency | -| `d2b_notification_action_nonce_issued_total` | counter | `zone` | — | -| `d2b_notification_action_nonce_expired_total` | counter | `zone` | — | -| `d2b_notification_stream_sessions_active` | gauge | `zone`, `stream_kind` | Active named stream sessions | - -**Labels that are explicitly excluded:** summary, body, action label, action -ID, icon ref, source VM name, notification ResourceRef name, correlationId, -idempotency key, D-Bus notification ID, and any string derived from -notification content. +| `d2b_notification_created_total` | counter | `category`, `urgency` | Count by category/urgency only | +| `d2b_notification_delivered_total` | counter | `category`, `urgency`, `sink_result` | Terminal delivery outcome | +| `d2b_notification_action_invoked_total` | counter | `category` | Action invoked; no action ID label | +| `d2b_notification_drop_total` | counter | `reason` | `reason`: `capacity` \| `observer-backpressure` \| `sink-unavailable` | +| `d2b_notification_dbus_duration_seconds` | histogram | `outcome` | D-Bus call latency | +| `d2b_notification_action_nonce_issued_total` | counter | (none) | — | +| `d2b_notification_action_nonce_expired_total` | counter | (none) | — | +| `d2b_notification_stream_sessions_active` | gauge | `stream_kind` | Active named stream sessions | + +**Labels that are explicitly excluded:** Zone name/UID, summary, body, action +label, action ID, icon ref, source VM name, notification ResourceRef name, +correlationId, idempotency key, D-Bus notification ID, and any string derived +from notification content. ### 13.3 OTEL traces @@ -1405,10 +1405,10 @@ The v2 `d2b.notify.v2.NotifyService` ttrpc contract is superseded by | Current source | `nixos-modules/notifications.nix`; `packages/d2bd/src/metrics.rs` | | Reuse action | adapt | | Destination | Nix: Zone resource authoring in `nixos-modules/`; metrics: `packages/d2b-provider-notification-desktop/src/` | -| Detailed design | Zone Provider resource and RoleBinding Nix compiler output; `spec.config.guestSources` authoring and eval-time assertions; OTEL metric emitters with closed labels; audit record emitters | +| Detailed design | Zone Provider resource and RoleBinding Nix compiler output; `spec.config.guestSources` authoring and eval-time assertions; OTEL metric emitters with closed semantic labels and no Zone/resource-name-derived dimensions while retaining `d2b.zone` as a resource attribute; audit record emitters | | Integration | Nix configuration compiler; OTEL emitter ring; authoritative audit | | Data migration | `d2b.notifications.*` Nix options retired; `d2b.zones..resources.notification-desktop` with `spec.config.guestSources` replaces | -| Validation | Eval tests for category enforcement, displayWaylandRef assertion, guestRef resolution; `tests/stream_redaction.rs` for content-free telemetry | +| Validation | Eval tests for category enforcement, displayWaylandRef assertion, guestRef resolution; `tests/stream_redaction.rs` for content-free telemetry; `tests/metrics_labels.rs` for structural exact absence of `vm`, `zone`, `zone_id`, `zone_uid`, and resource-name-derived keys plus notification/Zone-name canary absence | | Removal proof | `nixos-modules/notifications.nix` removed after Zone resource equivalence confirmed by eval test | ### 17.3 Removal items @@ -1461,6 +1461,7 @@ per-test budget. | `tests/status_lifecycle.rs` | The Provider declares no state Volume; `ProviderStateSet(zone, "notification-desktop")` is empty; no component Process mounts a state Volume; bounded non-secret operational state (component readiness, reconcile stage, closed-enum error/health detail) is written to `status`/the core Operation ledger within the status bounds; no notification summary/body/action-label/icon/nonce byte is ever persisted; on restart the controller re-derives component readiness from live `status` observation and reverifies against running processes, treating status as observation, never authority | | `tests/stream_record.rs` | `NotificationRequest`/`NotificationResult` DTO schema validation (all fields, closed category values, out-of-bound values, unknown fields rejected); category filter enforcement; `NotificationResult` `actionNonces` present only when actions declared; no content in error messages | | `tests/stream_redaction.rs` | Inject requests with distinguishable content bytes; assert zero occurrences in collected tracing events, OTEL span attributes, metric label values, audit record fields, error messages, and Debug output | +| `tests/metrics_labels.rs` | Every descriptor uses only fixed category/urgency/outcome/reason/stream semantics; exact identity-key absence; notification and Zone-name canaries absent from values; `d2b.zone` resource attribute retained | | `tests/action_nonce.rs` | Single-use consumption; expired TTL consumed-and-cleared; capacity at `MAX_STORE_SIZE`; replay after consume; nonce opaque in Debug; notification action key round trip | | `tests/stream_admission.rs` | All six rejection cases from §6.5; exact stable error codes | | `tests/observer_projection.rs` | In-memory projection state machine: request admitted → delivered → evicted on close; TTL-based eviction; bounded projection size; no content in projection status codes | diff --git a/docs/specs/providers/ADR-046-provider-runtime-cloud-hypervisor.md b/docs/specs/providers/ADR-046-provider-runtime-cloud-hypervisor.md index ecd0aac4d..6f94c2db8 100644 --- a/docs/specs/providers/ADR-046-provider-runtime-cloud-hypervisor.md +++ b/docs/specs/providers/ADR-046-provider-runtime-cloud-hypervisor.md @@ -1465,17 +1465,17 @@ emitter (no OTEL SDK; frames forwarded by `Provider/observability-otel`): | Metric name | Type | Labels | Description | | --- | --- | --- | --- | -| `d2b_runtime_ch_guest_total` | Counter | `zone`, `outcome: provisioned|failed|deleted` | Guest lifecycle events | -| `d2b_runtime_ch_guest_phase` | Gauge | `zone`, `phase` | Current Guests by phase | -| `d2b_runtime_ch_vmm_restarts_total` | Counter | `zone` | VMM Process restart count | -| `d2b_runtime_ch_reconcile_duration_seconds` | Histogram | `zone`, `result: converged|pending|failed` | Per-reconcile duration | -| `d2b_runtime_ch_adoption_total` | Counter | `zone`, `outcome: adopted|failed|ambiguous` | Controller restart adoption events | -| `d2b_runtime_ch_health_check_duration_seconds` | Histogram | `zone`, `result: passed|failed|timeout` | Guest health check duration | +| `d2b_runtime_ch_guest_total` | Counter | `outcome: provisioned|failed|deleted` | Guest lifecycle events | +| `d2b_runtime_ch_guest_phase` | Gauge | `phase` | Current Guests by phase | +| `d2b_runtime_ch_vmm_restarts_total` | Counter | (none) | VMM Process restart count | +| `d2b_runtime_ch_reconcile_duration_seconds` | Histogram | `result: converged|pending|failed` | Per-reconcile duration | +| `d2b_runtime_ch_adoption_total` | Counter | `outcome: adopted|failed|ambiguous` | Controller restart adoption events | +| `d2b_runtime_ch_health_check_duration_seconds` | Histogram | `result: passed|failed|timeout` | Guest health check duration | Cardinality rules: -- `zone` is allowed in metric labels (bounded by Zone count per host). -- Guest name (`vm.name`), guest-specific IDs, and endpoint locators are **not** metric - label values; they may appear only in OTEL resource attributes (advisory). +- Zone name/UID, Guest name (`vm.name`), guest-specific IDs, and endpoint + locators are **not** metric label keys or values; identity appears only in + bounded OTEL resource attributes (advisory). - No path, socket name, CID, PID, or runtime detail appears in any metric label. ### 18.4 OTEL resource attributes @@ -1724,10 +1724,10 @@ per-test budget. | Current source | `packages/d2bd/src/metrics.rs` (`d2b_daemon_vm_*`); `packages/d2b-contract-tests/tests/policy_observability.rs` | | Reuse action | replace | | Destination | `packages/d2b-provider-runtime-cloud-hypervisor/src/metrics.rs`; `src/audit.rs` | -| Detailed design | `d2b_runtime_ch_*` metrics from §18.3; bounded durable audit records from §17.3; no `vm=` metric label; no path/argv/socket in any field; closed OTEL attribute allowlist extended per §18.4 | +| Detailed design | `d2b_runtime_ch_*` metrics from §18.3 with closed semantic labels and no `vm`, `zone`, `zone_id`, `zone_uid`, or resource-name-derived key; bounded durable audit records from §17.3; no path/argv/socket in any field; identity retained in the closed OTEL resource-attribute allowlist extended per §18.4 | | Integration | Zone lightweight bounded emitter; `Provider/observability-otel` forwarding | | Data migration | `d2b_daemon_vm_*` metrics retired; consumers must update dashboards | -| Validation | `policy_observability.rs` updated with v3 allowlist; cardinality tests; bounded message/field tests; audit record schema golden vectors | +| Validation | `policy_observability.rs` updated with v3 allowlist; structural descriptor tests assert exact absence of `vm`, `zone`, `zone_id`, `zone_uid`, and resource-name-derived keys plus Guest/Zone-name canary absence; bounded message/field tests; audit record schema golden vectors | | Removal proof | Hand-rolled Prometheus registry (`d2bd/src/metrics.rs` `d2b_daemon_vm_*` section) deleted after migration | ### ADR046-ch-007 (controller status-first operational state) @@ -1771,7 +1771,7 @@ packages/d2b-provider-runtime-cloud-hypervisor/ adoption_property_test.rs # pidfd adoption: gone/ambiguous/stale-pid property tests health_check_test.rs # fake guest-control server; timeout/failure/retry (observe handler) finalize_ordering_test.rs # finalizer algorithm, single VMM Process teardown, ambiguity - metrics_cardinality_test.rs # no vm= label; bounded audit fields; no path/argv in output + metrics_cardinality_test.rs # no VM/Zone/resource-name labels; exact forbidden-key and canary absence schema_golden_test.rs # spec.provider.settings JSON Schema golden vector (no cmdlineExtra/seccompOverride) redaction_test.rs # no store path in Debug, status, or audit output state_status_test.rs # status projection round-trip; bound enforcement; diff --git a/docs/specs/providers/ADR-046-provider-runtime-qemu-media.md b/docs/specs/providers/ADR-046-provider-runtime-qemu-media.md index acf4b5dd1..8f7dd6c29 100644 --- a/docs/specs/providers/ADR-046-provider-runtime-qemu-media.md +++ b/docs/specs/providers/ADR-046-provider-runtime-qemu-media.md @@ -1251,21 +1251,23 @@ addresses, or argv. ## 18 Telemetry and metrics -All metric labels use closed sets. Cardinality is bounded; no label value -may contain VM name, user identity, executable path, or VM memory content. +All metric labels use closed semantic sets. Cardinality is bounded; no label +key or value may contain Zone/VM/resource identity, user identity, executable +path, or VM memory content. Zone identity remains in the bounded `d2b.zone` +OTEL resource attribute. | Metric | Type | Labels | Notes | | --- | --- | --- | --- | -| `d2b_guest_reconcile_total` | counter | zone, provider, outcome (success/failure) | Reconcile loop outcomes | -| `d2b_guest_reconcile_duration_seconds` | histogram | zone, provider | Reconcile latency | -| `d2b_guest_phase_transitions_total` | counter | zone, provider, from_phase, to_phase | Phase machine | -| `d2b_guest_runner_launches_total` | counter | zone, provider, outcome | Launch attempts | -| `d2b_guest_qmp_ready_seconds` | histogram | zone, provider | Time from runner spawn to QMP greeting | -| `d2b_guest_qmp_operations_total` | counter | zone, provider, operation, outcome | Per-operation QMP results | -| `d2b_guest_media_hotplug_total` | counter | zone, provider, operation (attach/detach), outcome | | -| `d2b_guest_dependency_wait_seconds` | histogram | zone, provider, dep_type | Time waiting for dependency | -| `d2b_guest_active` | gauge | zone, provider, phase | Active Guest count per phase | -| `d2b_guest_runner_restart_total` | counter | zone, provider | Runner exits (controller re-creates) | +| `d2b_guest_reconcile_total` | counter | provider, outcome (success/failure) | Reconcile loop outcomes | +| `d2b_guest_reconcile_duration_seconds` | histogram | provider | Reconcile latency | +| `d2b_guest_phase_transitions_total` | counter | provider, from_phase, to_phase | Phase machine | +| `d2b_guest_runner_launches_total` | counter | provider, outcome | Launch attempts | +| `d2b_guest_qmp_ready_seconds` | histogram | provider | Time from runner spawn to QMP greeting | +| `d2b_guest_qmp_operations_total` | counter | provider, operation, outcome | Per-operation QMP results | +| `d2b_guest_media_hotplug_total` | counter | provider, operation (attach/detach), outcome | | +| `d2b_guest_dependency_wait_seconds` | histogram | provider, dep_type | Time waiting for dependency | +| `d2b_guest_active` | gauge | provider, phase | Active Guest count per phase | +| `d2b_guest_runner_restart_total` | counter | provider | Runner exits (controller re-creates) | OTEL trace spans: @@ -1775,10 +1777,10 @@ destination in `packages/d2b-provider-runtime-qemu-media/`. | Current source | None — net-new v3 work; no pre-ADR45 baseline equivalent | | Reuse action | create | | Destination | packages/d2b-provider-runtime-qemu-media/src/telemetry.rs | -| Detailed design | Metrics and OTEL spans: implement all metrics from §18 and OTEL trace spans with label cardinality enforcement and no VM name, user identity, path, or other sensitive value in any label or attribute. Primary reuse disposition: `create`. Preserved source-plan detail: net-new telemetry emission for the Provider metrics and spans in §18. | +| Detailed design | Metrics and OTEL spans: implement all metrics from §18 and OTEL trace spans with structural closed-label enforcement and no Zone/VM/resource name, user identity, path, or other sensitive value in any metric label; retain Zone/resource identity only in bounded OTEL attributes and permitted audit fields. Primary reuse disposition: `create`. Preserved source-plan detail: net-new telemetry emission for the Provider metrics and spans in §18. | | Integration | Controller, QMP, hotplug, and dependency-watch paths call telemetry helpers; OTEL/metrics exporters consume only closed, bounded labels for support dashboards. | | Data migration | None — telemetry-only work; no runtime state import | -| Validation | `tests/metrics_label_cardinality.rs`; `tests/otel_span_attributes.rs` | +| Validation | `tests/metrics_label_cardinality.rs` asserts exact absence of `vm`, `zone`, `zone_id`, `zone_uid`, and resource-name-derived keys plus Guest/Zone-name canary absence; `tests/otel_span_attributes.rs` preserves allowed identity attributes | | Removal proof | None — telemetry helpers are new for this Provider; no prior owner to remove | --- @@ -1887,7 +1889,7 @@ per-test budget. | `condition_reason_codes.rs` | All reason codes in §16.3 | | `audit_event_shapes.rs` | Golden shape for every event in §17 | | `audit_no_sensitive_fields.rs` | Property test: no path/argv/fd/socket-path in payload | -| `metrics_label_cardinality.rs` | All metric label values; no VM name or path | +| `metrics_label_cardinality.rs` | Structural closed-label policy; exact identity-key absence; no Guest/Zone/resource-name canary or path in values; `d2b.zone` resource attribute retained | | `otel_span_attributes.rs` | No sensitive attribute in any span | | `provider_layout.rs` | Workspace layout conformance invocation | | `conformance_guest.rs` | d2b-provider-toolkit conformance suite | diff --git a/docs/specs/providers/ADR-046-provider-transport-azure-relay.md b/docs/specs/providers/ADR-046-provider-transport-azure-relay.md index 6f0efead0..874f7fa82 100644 --- a/docs/specs/providers/ADR-046-provider-transport-azure-relay.md +++ b/docs/specs/providers/ADR-046-provider-transport-azure-relay.md @@ -1166,27 +1166,27 @@ Rules: ### Metrics -All metric labels are closed sets. No label carries credential bytes, relay -token shapes, private key fragments, connection-string substrings, store paths, -or internal provider diagnostics. +All metric labels are closed semantic sets. No label carries Zone/resource +identity, credential bytes, relay token shapes, private key fragments, +connection-string substrings, store paths, or internal provider diagnostics. +Zone identity remains in the bounded `d2b.zone` OTEL resource attribute. | Metric name | Type | Labels | Description | | --- | --- | --- | --- | -| `d2b_relay_transport_connect_total` | Counter | `zone`, `outcome` (`success`/`failed`), `error_code` | Relay WebSocket connect attempts | -| `d2b_relay_transport_disconnect_total` | Counter | `zone`, `reason` (stable bounded code) | Relay WebSocket disconnects | -| `d2b_relay_transport_reconnect_total` | Counter | `zone`, `outcome` | Reconnect attempts | -| `d2b_relay_transport_session_seconds` | Histogram | `zone` | Duration of relay WebSocket sessions | -| `d2b_relay_transport_bytes_sent_total` | Counter | `zone` | Bytes sent over relay (post-encryption; opaque payload size) | -| `d2b_relay_transport_bytes_received_total` | Counter | `zone` | Bytes received over relay | -| `d2b_relay_transport_frames_sent_total` | Counter | `zone` | Frames sent | -| `d2b_relay_transport_frames_received_total` | Counter | `zone` | Frames received | -| `d2b_relay_transport_send_queue_bytes` | Gauge | `zone` | Current outbound frame queue depth (bytes) | -| `d2b_relay_transport_credential_expiry_seconds` | Gauge | `zone` | Seconds until listener credential expiry; 0 when no active lease | -| `d2b_relay_transport_backpressure_events_total` | Counter | `zone` | Times outbound send blocked on WebSocket write backpressure | - -Permitted label keys: `zone` (Zone name; max 63 chars, same regex as Zone name), -`outcome`, `reason`, `error_code`. The `zone` label carries the Zone name, not a -workload name, IP, or store path. +| `d2b_relay_transport_connect_total` | Counter | `outcome` (`success`/`failed`), `error_code` | Relay WebSocket connect attempts | +| `d2b_relay_transport_disconnect_total` | Counter | `reason` (stable bounded code) | Relay WebSocket disconnects | +| `d2b_relay_transport_reconnect_total` | Counter | `outcome` | Reconnect attempts | +| `d2b_relay_transport_session_seconds` | Histogram | (none) | Duration of relay WebSocket sessions | +| `d2b_relay_transport_bytes_sent_total` | Counter | (none) | Bytes sent over relay (post-encryption; opaque payload size) | +| `d2b_relay_transport_bytes_received_total` | Counter | (none) | Bytes received over relay | +| `d2b_relay_transport_frames_sent_total` | Counter | (none) | Frames sent | +| `d2b_relay_transport_frames_received_total` | Counter | (none) | Frames received | +| `d2b_relay_transport_send_queue_bytes` | Gauge | (none) | Current outbound frame queue depth (bytes) | +| `d2b_relay_transport_credential_expiry_seconds` | Gauge | (none) | Seconds until listener credential expiry; 0 when no active lease | +| `d2b_relay_transport_backpressure_events_total` | Counter | (none) | Times outbound send blocked on WebSocket write backpressure | + +Permitted label keys: `outcome`, `reason`, `error_code`. Their values are +closed semantic codes. Forbidden label keys: namespace FQDN, entity name, relay region, credential ref, token shape, connection string, or any credential material. @@ -1384,10 +1384,10 @@ download, or PATH scan. | Current source | `packages/d2bd/src/metrics.rs` (hand-rolled Prometheus; baseline) | | Reuse action | create | | Destination | `packages/d2b-provider-transport-azure-relay/src/{metrics.rs, audit.rs}` | -| Detailed design | Emit all OTEL metrics and audit records listed in §OTEL and §Audit; closed label sets; never label secret bytes; provider audit covers **carriage authentication and health observations only** — Azure auth events, WebSocket lifecycle, credential acquisition outcomes — and is **separate from resource audit** (resource lifecycle events are owned by core); audit records appended through the Zone runtime audit log interface (no atomicity guarantee with Zone resource state in redb; best-effort delivery per the Zone's audit provider configuration); OTEL via lightweight emitter ring (no direct OTEL SDK dependency in Provider) | +| Detailed design | Emit all OTEL metrics and audit records listed in §OTEL and §Audit; closed semantic label sets with no Zone/resource-name-derived keys; retain Zone identity only in the `d2b.zone` OTEL resource attribute; never label secret bytes; provider audit covers **carriage authentication and health observations only** — Azure auth events, WebSocket lifecycle, credential acquisition outcomes — and is **separate from resource audit** (resource lifecycle events are owned by core); audit records appended through the Zone runtime audit log interface (no atomicity guarantee with Zone resource state in redb; best-effort delivery per the Zone's audit provider configuration); OTEL via lightweight emitter ring (no direct OTEL SDK dependency in Provider) | | Integration | `Provider/observability-otel` receives emitter ring frames; audit log via Zone runtime `d2b.audit.transport` category | | Data migration | None — full d2b 3.0 reset; no prior state to migrate | -| Validation | `tests/credential_redaction.rs` extended to cover audit/OTEL paths; `tests/fake_relay_transport.rs` asserts audit record fields against schema | +| Validation | `tests/credential_redaction.rs` extended to cover audit/OTEL paths; `tests/metric_labels.rs` structurally asserts exact absence of `vm`, `zone`, `zone_id`, `zone_uid`, and resource-name-derived keys and that a Zone-name canary never enters label values; `tests/fake_relay_transport.rs` asserts audit record fields against schema | | Removal proof | N/A; new module | ### ADR046-transport-relay-007 (integration README) @@ -1420,6 +1420,7 @@ wave: | `fake_relay_transport.rs` | `listener_accepts_sender_connection`, `framing_is_length_prefixed`, `send_receive_roundtrip_over_fake_relay`, `attachment_support_is_false`, `locality_is_remote`, `transport_descriptor_contract` | Named byte-stream contract; framing; transport descriptor | | `reconnect_open_transport.rs` | `websocket_starts_on_open_transport`, `websocket_closes_on_close_transport`, `observe_transport_reports_connect_result`, `reconnect_clears_generation`, `reconnect_triggers_new_kk` | Relay responds to CloseTransport/OpenTransport cycle; named stream closes on WebSocket loss | | `backpressure_credit.rs` | `slow_relay_stalls_credit`, `aggregate_queue_bounded`, `source_never_buffers_beyond_limit`, `backpressure_event_counter_increments` | Backpressure and credit invariants | +| `metric_labels.rs` | `identity_keys_absent`, `zone_name_canary_absent`, `semantic_domains_closed`, `zone_resource_attribute_retained` | Structural metric-label policy; no Zone/resource identity labels; `d2b.zone` remains a resource attribute | | `idempotency_key.rs` | `idempotency_key_carried_in_noise_record`, `idempotency_key_not_in_relay_frame_metadata`, `replay_at_relay_level_does_not_deduplicate`, `dedup_at_child_zone_resource_api` | Idempotency is child-Zone-owned; relay carries opaquely | | `listener_sender_conformance.rs` | `conformance_vectors_listener`, `conformance_vectors_sender`, `noise_kk_prologue_binds_transport_settings`, `mismatched_fingerprint_fails_closed`, `relay_identity_not_in_subject_context` | Named stream contract; Noise KK binding; relay identity exclusion | diff --git a/docs/specs/providers/ADR-046-provider-volume-local.md b/docs/specs/providers/ADR-046-provider-volume-local.md index dadfa074e..e029136b7 100644 --- a/docs/specs/providers/ADR-046-provider-volume-local.md +++ b/docs/specs/providers/ADR-046-provider-volume-local.md @@ -1882,30 +1882,32 @@ status field, log line, OTEL span attribute, or metric label: ## OTEL metrics -All metric labels are closed at a bounded, fixed set: -`zone`, `provider` (`volume-local`), `schema_id`, `schema_version`, -`persistence_class`, `source_kind`, `operation`, `outcome`. +All metric labels are closed at a bounded, fixed semantic set: +`provider` (`volume-local`), `schema_id`, `schema_version`, +`persistence_class`, `source_kind`, `operation`, `trigger`, `view`, `access`, +and `outcome`. No schema content, raw path, instance ID, process arguments, or credential -identifier enters any metric label. +identifier enters any metric label. Zone identity is carried only in the +bounded `d2b.zone` OTEL resource attribute. | Metric | Unit | Labels | | --- | --- | --- | -| `d2b_volume_provision_total` | Counter | zone, provider, persistence_class, source_kind, outcome | -| `d2b_volume_provision_duration_ms` | Histogram | zone, provider, source_kind | -| `d2b_volume_layout_repair_total` | Counter | zone, provider, outcome | -| `d2b_volume_state_size_bytes` | Gauge | zone, provider, schema_id | -| `d2b_volume_state_migration_total` | Counter | zone, provider, schema_id, outcome | -| `d2b_volume_state_migration_duration_ms` | Histogram | zone, provider, schema_id | -| `d2b_volume_state_snapshot_total` | Counter | zone, provider, schema_id, trigger | -| `d2b_volume_state_marker_check_total` | Counter | zone, provider, outcome | -| `d2b_volume_state_quota_exceeded_total` | Counter | zone, provider | -| `d2b_volume_store_sync_total` | Counter | zone, provider, outcome | -| `d2b_volume_store_sync_duration_ms` | Histogram | zone, provider | -| `d2b_volume_relocation_total` | Counter | zone, provider, outcome | -| `d2b_volume_sealing_rotation_total` | Counter | zone, provider, outcome | -| `d2b_volume_unclaimed_gc_total` | Counter | zone, provider, persistence_class | -| `d2b_volume_fd_handoff_total` | Counter | zone, provider, view, access, outcome | +| `d2b_volume_provision_total` | Counter | provider, persistence_class, source_kind, outcome | +| `d2b_volume_provision_duration_ms` | Histogram | provider, source_kind | +| `d2b_volume_layout_repair_total` | Counter | provider, outcome | +| `d2b_volume_state_size_bytes` | Gauge | provider, schema_id | +| `d2b_volume_state_migration_total` | Counter | provider, schema_id, outcome | +| `d2b_volume_state_migration_duration_ms` | Histogram | provider, schema_id | +| `d2b_volume_state_snapshot_total` | Counter | provider, schema_id, trigger | +| `d2b_volume_state_marker_check_total` | Counter | provider, outcome | +| `d2b_volume_state_quota_exceeded_total` | Counter | provider | +| `d2b_volume_store_sync_total` | Counter | provider, outcome | +| `d2b_volume_store_sync_duration_ms` | Histogram | provider | +| `d2b_volume_relocation_total` | Counter | provider, outcome | +| `d2b_volume_sealing_rotation_total` | Counter | provider, outcome | +| `d2b_volume_unclaimed_gc_total` | Counter | provider, persistence_class | +| `d2b_volume_fd_handoff_total` | Counter | provider, view, access, outcome | --- @@ -2186,7 +2188,7 @@ Required test files and minimum coverage: | `tests/sealing_unit.rs` | Seal on write; read sealed payload; rotation state machine via `RotateSealingKey` effect op: `rotation-pending` → op success → `sealed`; op failure → `rotation-failed`; no raw key in any output; no EphemeralProcess dispatch | | `tests/snapshot_unit.rs` | `snapshotPolicy` enforcement; retention count; retention TTL; `triggerOnMigration` auto-snapshot; snapshot EphemeralProcess dispatch; list in Volume status | | `tests/relocation_unit.rs` | Finalizer set; EphemeralProcess created; commit: source deleted; failure: source retained; state machine round-trip | -| `tests/audit_unit.rs` | Golden audit record for each event kind; no paths in any record; no credential material; cardinality check for OTEL labels | +| `tests/audit_unit.rs` | Golden audit record for each event kind; no paths in any record; no credential material; structural OTEL label-policy check with exact absence of `vm`, `zone`, `zone_id`, `zone_uid`, and resource-name-derived keys | | `tests/error_messages.rs` | Every error code emitted with bounded (≤512 byte) message; no host path in any error message | ### `integration/` @@ -2411,10 +2413,10 @@ Documents: | Current source | `d2b-state/src/audit.rs` (main `6faa5256`); OTEL cardinality model from `d2b-provider-observability-local/src/` (main `a1cc0b2d`) | | Reuse action | adapt | | Destination | `src/audit.rs`; `src/otel.rs`; `src/error.rs`; `tests/audit_unit.rs`; `integration/audit.rs` | -| Detailed design | Event types and Zone audit emission per §Audit events; OTEL metric definitions per §OTEL metrics; error catalog per §Error catalog; no-path invariant enforced in all outputs | +| Detailed design | Event types and Zone audit emission per §Audit events; OTEL metric definitions per §OTEL metrics with closed semantic labels and no Zone/resource-name-derived dimensions; Zone identity remains in `d2b.zone` resource attributes; error catalog per §Error catalog; no-path invariant enforced in all outputs | | Integration | Every lifecycle transition calls `audit::emit_volume_event`; OTEL metrics exported via `observability-otel` Provider | | Data migration | None — full d2b 3.0 reset; no prior state to migrate | -| Validation | `tests/audit_unit.rs` golden records; `tests/error_messages.rs` bounded messages; OTEL label cardinality; `integration/audit.rs` live stream | +| Validation | `tests/audit_unit.rs` golden records and structural metric descriptor assertions for exact absence of `vm`, `zone`, `zone_id`, `zone_uid`, and resource-name-derived keys plus resource-name canary absence; `tests/error_messages.rs` bounded messages; `integration/audit.rs` live stream | | Removal proof | Not applicable | ### ADR046-vl-010 — Nix configuration and resource compiler integration From 23ce2a0fedc7b0b3921fb1a8544e4efb03848655 Mon Sep 17 00:00:00 2001 From: John Vicondoa Date: Fri, 24 Jul 2026 06:58:23 -0700 Subject: [PATCH 091/115] docs: integrate ADR 0046 second-panel corrections --- docs/specs/ADR-046-decision-register.md | 4 +- docs/specs/ADR-046-implementation-graph.json | 1090 ++++++++++++++++- docs/specs/ADR-046-implementation-graph.md | 2 + .../ADR-046-primitive-resource-composition.md | 2 +- .../ADR-046-resource-api-and-authorization.md | 6 +- docs/specs/ADR-046-resource-object-model.md | 2 +- docs/specs/ADR-046-resources-credential.md | 19 +- docs/specs/ADR-046-resources-network.md | 10 +- docs/specs/ADR-046-spec-set.json | 70 +- .../ADR-046-telemetry-audit-and-support.md | 4 +- .../ADR-046-terminology-and-identities.md | 8 +- docs/specs/ADR-046-validation-and-delivery.md | 6 +- docs/specs/ADR-046-work-items.json | 190 +-- .../ADR-046-provider-activation-nixos.md | 4 +- .../ADR-046-provider-audio-pipewire.md | 3 +- ...46-provider-credential-managed-identity.md | 11 +- ...-046-provider-credential-secret-service.md | 19 +- .../providers/ADR-046-provider-device-tpm.md | 5 +- .../ADR-046-provider-device-usbip.md | 13 +- .../ADR-046-provider-display-wayland.md | 11 +- .../ADR-046-provider-network-local.md | 8 +- .../ADR-046-provider-notification-desktop.md | 3 +- .../ADR-046-provider-observability-otel.md | 2 +- .../ADR-046-provider-runtime-qemu-media.md | 4 +- .../ADR-046-provider-transport-azure-relay.md | 11 +- .../ADR-046-provider-volume-virtiofs.md | 4 +- 26 files changed, 1301 insertions(+), 210 deletions(-) diff --git a/docs/specs/ADR-046-decision-register.md b/docs/specs/ADR-046-decision-register.md index 65899a2f4..401779708 100644 --- a/docs/specs/ADR-046-decision-register.md +++ b/docs/specs/ADR-046-decision-register.md @@ -36,7 +36,7 @@ review metadata, or implementation status. | D014 | Use one ADR/spec PR with user review before the final panel and again after it. | The panel runs only after the first approval. Any content change invalidates validation/panel evidence and repeats the final review. | Validation/delivery | | D015 | Every field ending in `Ref` names another same-Zone resource using `/`. | Plain enums use plain names. Standard ResourceTypes are Zone-unique; vendor types are qualified; API binding collisions fail. | Resource object/API, all resource specs | | D016 | Every resource belongs to exactly one Zone. | Refs never resolve across Zones. A Provider must be installed as a Zone-local `Provider/` resource before use. Cross-Zone resource relationships require separate review. | Zone, Provider, routing, resource API | -| D017 | Every Zone store has exactly one authoritative `Zone/` self resource; parents use local `ZoneLink/` resources. | Parent access uses the child Zone API; a parent does not mount or mirror the child store. | Zone resources/routing | +| D017 | Every Zone store has exactly one authoritative `Zone/` self resource; each non-root child stores one child-local `ZoneLink/` uplink, while compiler-only `parentZone` selects the parent allocator. | Parent access uses the child Zone API; a parent does not mount or mirror the child store and stores no reciprocal ZoneLink. | Zone resources/routing | | D018 | Physical/local host execution contexts are `Host/` resources; VM, sandbox, cloud, and remote execution contexts are `Guest/` resources. | A Zone may have multiple Hosts reconciled by Provider/system-core and multiple Guests reconciled by runtime Providers. Process/EphemeralProcess uses one executionRef to either type. | Host/Guest, Provider model, process placement | | D019 | Host and Guest share one ExecutionPolicy with plain `system|user` domains. | `defaultDomain`, `allowedDomains`, `defaultUserRef`, budgets, and attachment defaults are common. Process uses executionRef, optional domain, and conditional userRef; user-only Hosts/Guests reject system processes. | Host/Guest/Process specs | | D020 | Resource ownership is singular. | Every resource has zero or one ownerRef. Any child mutation triggers owner reconciliation; owner cycles fail; deletion is child-first; UID binding prevents name-reuse confusion. | Resource object/store/reconciliation | @@ -111,7 +111,7 @@ review metadata, or implementation status. | D089 | Desired `spec` mirrors the D088 three-layer shape with symmetric frozen names: (1) universal Resource envelope/metadata rules; (2) a ResourceType base Spec schema carrying every provider-neutral required/optional field and semantic shared across implementations (including `spec.providerRef`, which stays base); and (3) a single canonical selected-Provider extension envelope `spec.provider = { schemaId, schemaVersion, settings }`. | The canonical `spec.provider` envelope replaces the inconsistent Host/Guest/Credential `providerSettings`, Device `settings`, and all ad hoc extensions; names are frozen symmetric to status (`spec.provider.settings` mirrors `status.provider.details`; `spec.provider` omits `providerRef`/`observedProviderGeneration` because `spec.providerRef` is base and spec is desired, not observed). Every Provider `ResourceApiBinding` declares and MUST implement the exact ResourceType base schema version/fingerprint and pass base lifecycle/status/error/finalizer/conformance; it MUST accept the canonical minimal valid base Spec; it MAY reject an optional base capability only via its signed standard capability matrix and a typed provider-neutral `unsupported-capability`, and MUST NOT ignore, reinterpret, rename, duplicate, or weaken the bounds of any base field, or require extension data for base-required behavior. The `spec.provider` extension is optional unless the selected Provider's signed schema requires it; it is strict deny-unknown, bounded, versioned/digested, validated against `spec.providerRef` at Nix build and API admission, carries implementation-only desired settings, and cannot shadow a base field. Shared fields/semantics are promoted to the ResourceType base. Generic CLI/controllers operate on base spec and base status; the selected `spec.provider` schema and the `status.provider` schema align for the same Provider. Qualified Provider-defined ResourceTypes carry the universal envelope, their own type-base spec, and an optional implementation extension. The `Provider` resource's `{ artifactId, config }` self-description remains the one documented exception, because a Provider cannot carry a non-circular `spec.providerRef`. | Resource object/API/store/reconcile, Nix/security/validation/feasibility, Provider model/packaging, all resource specs and Provider dossiers | | D090 | Resource `Create`/`UpdateSpec`/`Delete` support an authorized expedited (`waitForReconcile`) mode: under one mutation ticket/`operationId`/deadline, Core admission plus a reserved-revision redb transaction run in parallel with the owning controller's preflight/plan, but the controller performs NO external effect, finalizer release, or status mutation until Core supplies a typed `CommittedRevisionProof { resourceUid, generation, revision, operationId }`; a DB failure sends Abort and no effect occurs, and a durable commit is never rolled back because reconcile later fails or times out. | The API returns success only if the initial resource mutation durably committed. Commit also enqueues the ordinary reconcile hint; the expedited request enters a bounded priority lane into the SAME per-resource single-flight executor, and any normally queued reconcile stays queued and runs after the expedited pass. All effect IDs/idempotency keys derive from `(UID, generation, revision, operationId)`; a normal re-entry observes converged/progressing state and no-ops/rejoins, never duplicating. The API waits in parallel for durable commit plus one expedited reconcile pass and returns the committed resource object, the post-pass projected layered status, `statusPersistence: pending|committed`, and the last persisted status revision; status persistence is asynchronous and the response need not wait for it, so no uncommitted status is represented as durable. Delete returns an event-only Deleted projection / not-found outcome. Expedited completion means one pass reaches a `Converged|Progressing|Blocked|UpgradeRequired|Failed` disposition, not long-running external Ready; timeout/cancel after commit returns a typed committed-but-reconcile-pending response while the ordinary queue continues. Priority quotas/fairness prevent starvation/DoS; only authorized UX mutations/core may request expedited mode, and the admin `resource reconcile` CLI action may reuse it. | Resource API/store/reconcile, core controllers, CLI, security, validation/feasibility, controller toolkit | | D091 | The universal status base gains a provider-neutral bounded `status.update` currency object on every resource, and controllers expose `assess_update`/`plan_upgrade`/`execute_upgrade`; a controller MUST return `UpgradeRequired` rather than apply a disruptive in-place change, while non-disruptive changes reconcile normally. Upgrades preserve Resource UID/spec identity and durable/state/secret Volumes and TPM identity where possible, recycling only realization/owned ephemeral Processes/endpoints; `Replace` of the resource-row identity is used only when explicitly required and planned with ownership/state transfer, and full factory reset remains separate and destructive. | `status.update` carries `state` (`Current|UpdateAvailable|UpgradeRequired|Upgrading|Blocked|Unknown`), a closed `reasons` enum (`CoreGenerationChanged, ProviderGenerationChanged, ArtifactChanged, ImageOrSystemGenerationChanged, SpecChanged, DependencyChanged, SecurityPolicyChanged`), bounded non-secret observed/target generation/digest IDs, `disruption` (`None|Reload|Restart|Recycle|Replace`), `preserveState`, optional `operationId`, `lastAssessedAt`, and bounded/truncated owned/dependency aggregate counts and refs. These shared currency fields are universal/ResourceType base per D088 and are never duplicated in a provider extension. Core invokes dependency/owner triggers and aggregates self/owned/dependency currency for list/get; a dependency-aware planner topologically drains/recycles/restarts affected owned/dependent resources (e.g. a GPU marks `UpgradeRequired`/`Blocked` while applications depend on it, then drains dependent Processes/Guests, recycles the GPU realization, and restarts dependents — no surprise disruption). Triggers include d2b/core rollout, Provider package generation, selected artifact/image/NixOS generation, an immutable spec field, and dependency updates. Spec adds a provider-neutral update policy in the universal/ResourceType base per D089 (manual disruptive default; auto non-disruptive permitted); a `spec.provider` extension may add implementation knobs but cannot bypass the disruption policy. The core Operation ledger persists upgrade operation/idempotency/progress and resumes after crash/restart; per-resource single-flight serializes reconcile/upgrade; status carries only the latest bounded plan/result, not a second ledger. CLI list/get always show self/owned/dependency update state and disruption; `resource upgrade ` plans by default and `--recursive --apply` executes. | All resource specs, resource API/store/reconcile, core controllers, CLI, Nix, security, reset, telemetry, validation/feasibility, all Provider dossiers | -| D092 | Stable managed endpoint identities (TPM `endpointId` and similar) MUST be proper resources, not opaque IDs, unless demonstrably controller-internal or high-churn. A general **promotion test** governs every entity: it MUST be a ResourceType when any of these holds — stable across a reconcile pass/restart; independently lifecycle/readiness/finalizer/lease managed; referenced by another resource/controller/Provider; independently subject to authorization/policy/audit; visible in resource status/CLI; or required by the ownership/dependency graph. An opaque ID/handle is permitted only when it is confined to one controller/process, not API/status-visible as a locator, has no independent lifecycle/authz, and/or is per-connection/high-churn (pidfd, fd index, named stream id, transport byte-stream handle, reconcile operation token). Every retained public `*Id`/`Handle` documents its rationale. | A new standard ResourceType `Endpoint` (17 standard types now) has the D089 layered spec and D088 layered status. Endpoint base spec: `providerRef`, `producerRef` (`Process`/`Device`/`Guest`/`Host` or qualified resource), `endpointClass` (`service|device|transport|control|data`), `transport` (closed `unix|vsock|tcp|fd-attachment|opaque-carriage`), `purpose`, service/schema fingerprint or capability class, locality/visibility, attachment support/policy, consumer policy/ref bounds, lifecycle/recycle policy — and NO raw path/address/CID/port/fd/credential. Endpoint base status: readiness, observed producer/resource generation, connection/lease availability, endpoint generation, closed capability/locality/transport observations, `status.update` currency, conditions, optional implementation extension, and NO raw locator. Core/ProviderSupervisor resolves an `Endpoint` resource to a private transport/FD only through EffectPort/LaunchTicket. Endpoints are controller-created by the signed component/semantic owner (static Endpoints may be Nix/API-authored only where the schema permits); `ownerRef` gives child-first deletion; consumers use `Endpoint/` ResourceRefs. Stable endpoints are migrated to `Endpoint` resources: TPM `tpmEndpointId`, GPU `crossDomainEndpointId`/sidecar sockets, Wayland cross-domain/proxy endpoints, vhost-user sound/video, QMP/CH control, guest-control vsock, notification/clipboard/shell persistent service endpoints, stable Provider service endpoints, and stable transport listener bindings. `ProcessSpec` inline `endpoints` is removed; a stable endpoint a Process produces is an owned `Endpoint` resource with `producerRef: Process/` (no duplicate inline field). The virtiofs `Export` resource remains the lifecycle owner and references its `Endpoint` where independently consumed. Per-session named streams/`OwnedTransport` handles remain internal/high-churn (not Endpoints); an allocator stable listener may be an Endpoint if its lifecycle is visible. No compatibility alias for prior fields (clean reset). | All resource specs, resource API/store/index/reconcile, core controllers, CLI, Nix, security, reset, validation/feasibility, parent ADR, all Provider dossiers | +| D092 | Stable managed endpoint identities (TPM `endpointId` and similar) MUST be proper resources, not opaque IDs, unless demonstrably controller-internal or high-churn. A general **promotion test** governs every entity: it MUST be a ResourceType when any of these holds — stable across a reconcile pass/restart; independently lifecycle/readiness/finalizer/lease managed; referenced by another resource/controller/Provider; independently subject to authorization/policy/audit; visible in resource status/CLI; or required by the ownership/dependency graph. An opaque ID/handle is permitted only when it is confined to one controller/process, not API/status-visible as a locator, has no independent lifecycle/authz, and/or is per-connection/high-churn (pidfd, fd index, named stream id, transport byte-stream handle, reconcile operation token). Every retained public `*Id`/`Handle` documents its rationale. | A new standard ResourceType `Endpoint` (the 17th type at D092; D096 later raises the frozen catalog to 19) has the D089 layered spec and D088 layered status. Endpoint base spec: `providerRef`, `producerRef` (`Process`/`Device`/`Guest`/`Host` or qualified resource), `endpointClass` (`service|device|transport|control|data`), `transport` (closed `unix|vsock|tcp|fd-attachment|opaque-carriage`), `purpose`, service/schema fingerprint or capability class, locality/visibility, attachment support/policy, consumer policy/ref bounds, lifecycle/recycle policy — and NO raw path/address/CID/port/fd/credential. Endpoint base status: readiness, observed producer/resource generation, connection/lease availability, endpoint generation, closed capability/locality/transport observations, `status.update` currency, conditions, optional implementation extension, and NO raw locator. Core/ProviderSupervisor resolves an `Endpoint` resource to a private transport/FD only through EffectPort/LaunchTicket. Endpoints are controller-created by the signed component/semantic owner (static Endpoints may be Nix/API-authored only where the schema permits); `ownerRef` gives child-first deletion; consumers use `Endpoint/` ResourceRefs. Stable endpoints are migrated to `Endpoint` resources: TPM `tpmEndpointId`, GPU `crossDomainEndpointId`/sidecar sockets, Wayland cross-domain/proxy endpoints, vhost-user sound/video, QMP/CH control, guest-control vsock, notification/clipboard/shell persistent service endpoints, stable Provider service endpoints, and stable transport listener bindings. `ProcessSpec` inline `endpoints` is removed; a stable endpoint a Process produces is an owned `Endpoint` resource with `producerRef: Process/` (no duplicate inline field). The virtiofs `Export` resource remains the lifecycle owner and references its `Endpoint` where independently consumed. Per-session named streams/`OwnedTransport` handles remain internal/high-churn (not Endpoints); an allocator stable listener may be an Endpoint if its lifecycle is visible. No compatibility alias for prior fields (clean reset). | All resource specs, resource API/store/index/reconcile, core controllers, CLI, Nix, security, reset, validation/feasibility, parent ADR, all Provider dossiers | | D093 | `Provider/credential-entra` login and token acquisition are grounded in an Entrablau-enabled identity `Guest`, never an abstract/in-process Host login. The controller is secret-free; each Entra `Credential` binds an exact same-Zone `identityGuestRef: Guest/` and a stable `Endpoint/` implementing a standard typed Entra login/token service inside that Guest. There is no Host login/token chain, `DefaultAzureCredential`, env, DBus, path, or browser fallback. | The identity Guest's NixOS system is consumer-composed with the sibling `vicondoa/entrablau.nix` (Himmelblau/Entrablau) module; d2b core never imports the sibling flake. The Guest package supplies the manifest-declared login-service Process + `Endpoint` contract and owns its secret/token/machine-credential storage, TPM binding, enrollment, refresh-token state, and interactive login implementation inside the Guest (guest-local, secret/large/private — outside resource status). The Credential ResourceType-common base adds shared interactive-login operations `BeginLogin`/`ObserveLogin`/`CancelLogin`, a closed `interactionState` (`NotRequired|Required|Starting|AwaitingUser|Authenticated|Failed|Unknown`), bounded non-secret challenge/progress metadata, and login-session generation/deadline; the provider extension carries only Entra-specific non-secret modes/reasons. No device code that confers authority, URL, cookie, token, or user PII appears in status/audit/OTEL; the interactive byte/UI stream is a named ComponentSession stream direct to the Guest login service. `BeginLogin` opens an authenticated end-to-end session to the Entrablau service, which conducts the browser/device/desktop login inside the Guest; on success the Entra agent requests an access-token lease on demand and the raw access token is delivered end-to-end (Noise_KK) only to the exact authenticated consumer process permitted by the Credential `consumerRef`/scope/RBAC — Host/bus/intermediate controllers see ciphertext only. Refresh tokens and private login state never leave the Entrablau Guest; there is no cross-Zone ResourceRef. Expedited reconcile (D090) may return `InteractionRequired`/projected status after create/update but never waits for human login past the deadline. The token API client stays injected/typed but its production implementation is the Entrablau Guest `Endpoint`; the credential-entra controller/agent performs no direct Entra network egress (Entrablau owns the Entra network/TLS flows). Reset preserves or explicitly destroys the identity Guest TPM/login state, never silently wiping it. ACA and Azure VM consumers obtain their access tokens over end-to-end KK from the identity Guest. Entrablau is a sibling/external component; target integration is ADR-only, validated by a feasibility spike with a fake Entrablau service (and manual real-Guest login), with no live Entra in CI. | Credential resource, credential-entra dossier, runtime-azure-container-apps/runtime-azure-virtual-machine consumers, resource API/reconcile, CLI, Nix, security, reset, validation/feasibility, provider catalog/work items | | D094 | Every `src/` unit test and crate `tests/` hermetic suite MUST execute fast so the hermetic suite is the default inner loop on every change; all slower coverage belongs only in `integration/`. Execution-time budgets are measured after build (warm cache, compilation excluded) against a recorded reference runner: an individual normal hermetic test has p95 ≤50 ms with no wall-clock sleep; each Provider crate `cargo test -p d2b-provider-- --lib --tests` executes in ≤2 s; all 27 Provider hermetic suites execute in ≤30 s aggregate wall when sharded and ≤60 s single-host; each Layer-1 hermetic shard executes in ≤60 s. Bounded cryptographic/property tests are the only classified exception: each is named individually, declares a capped case count, and declares its own higher per-test budget; nothing else may sleep, retry on wall clock, spawn a process, use containers/network/DBus/systemd/broker daemon/Nix eval or build/KVM/USB/GPU/TPM hardware/live cloud, or build filesystem trees beyond tiny temp fixtures. | Hermetic tests are in-process only: deterministic fake clock/RNG; fake ResourceClient/EffectPort/broker/transport/credential/systemd; in-memory or tiny-temp bounded redb fixtures; parallel-safe with no global mutable or shared ports/paths; exact bounded property case counts. `integration/` owns any real process, socket rendezvous, container, Nix eval/build, guest VM/KVM/TCG, broker/systemd/DBus, real filesystem quota/mount/namespace, hardware, or live cloud/Entrablau; it may be slower but still has a lane timeout/budget, parallel isolation, and fake external services by default, with live/hardware kept a separate manual tier. A test violating placement is a policy failure and MUST move to the correct tier — never gain a sleep, larger timeout, or `#[ignore]`. Cold compile time is tracked and optimized separately via shared cache and dependency discipline; correctness never depends on any cache. A machine-readable test-runtime ledger plus a timing gate built on the existing tooling (no new test framework) measure execution-only time and enforce per-test/crate/shard budgets, top-slow-test reporting, and a historical regression threshold as a CI artifact. Legacy overhead is removed, not accumulated: when ADR 0046 replaces a behavior, the minimum reusable semantic assertions migrate into the new hermetic suite and the old duplicate tests, shell gates, fixtures, static artifacts, CI jobs, and manifest entries are deleted once successor coverage and removal proof pass; old and new suites never run indefinitely. Every current-code migration/replacement work item names the exact old test selectors/files with a keep/adapt/move/delete disposition and a removal gate, and updates `tests/layer1-jobs.json`, closed gate manifests, flake/matrix/Nix-unit pins, generated ledgers, and CI workflow shards. No new top-level `tests/*.sh` gate is added; Rust unit/property and existing generated drift/policy gates are preferred. The eight-wave entry/exit criteria require the fast hermetic suite as the default inner loop on every change, with integration running after focused green and concurrently in later lanes. | Validation/delivery, feasibility/spikes, streamline, current-code migration map, provider model/packaging, all Provider dossiers, test layout | | D095 | The ADR 0046 set MUST ship a generated, machine-readable implementation DAG (`docs/specs/ADR-046-implementation-graph.json` plus the human view `docs/specs/ADR-046-implementation-graph.md`) that maps every one of the 55 member specs and every work item exactly once to a dependency-ordered launch wave (`W0`–`W7`) and a file-disjoint parallel group, so no author has to re-derive launch order or parallelism from prose. The graph derives its source spec DAG from `ADR-046-spec-set.json.members[].dependsOn` and its 8-wave implementation topology from `ADR-046-validation-and-delivery.md` §3; it adds nodes for specs and work items, typed edges (`spec-depends-on`, `work-item-depends-on`, `implements-spec`, `shared-contract`, `file-overlap-order`), exact wave, parallel group, owner/destinations, entry contracts, prerequisites, blockers, exit gate, and topological rank. | The graph is a generated non-member artifact: it is NOT one of the 55 `ADR-046-spec-set.json` members and does not change that count, but it is committed, deterministic (no timestamps/host paths), regenerated after any spec/work-item edit, and drift-gated. It MUST embody the anti-serialization invariant: every ready, file-disjoint parallel group launches concurrently; a same-wave dependency is a prep-barrier edge (`shared-contract`/`file-overlap-order`), never a reason to serialize a whole wave. Dependencies of any node MUST resolve to an earlier wave or an explicit same-wave prep barrier that precedes its consumers. `ADR-046-validation-and-delivery` owns the artifact contract, generation command, validation rules, and the ready-wave query; `ADR-046-streamline`'s spec-registry generator (`ADR046-streamline-001`) emits the files; the graph carries no normative authority beyond the specs/work items it indexes. | Validation/delivery, streamline, spec-set/work-items manifests, parent ADR index, all specs and Provider dossiers | diff --git a/docs/specs/ADR-046-implementation-graph.json b/docs/specs/ADR-046-implementation-graph.json index f467e38c4..7488769b7 100644 --- a/docs/specs/ADR-046-implementation-graph.json +++ b/docs/specs/ADR-046-implementation-graph.json @@ -11544,6 +11544,7 @@ "destinations" : [ "`packages/d2b-provider-runtime-azure-container-apps/src/controller.rs`" ], + "detailedDesign" : "Async `Guest` reconcile loop: `describe` → `validateSpec` → `plan` → `reconcile` → `observe` → `finalize`. Adoption before first `RuntimeEnsure`. Operation ledger persisted to Volume inside gateway Guest. Credential lease acquire/revoke per call. Noise KK enrollment via ZoneLink (from gateway Guest to managed ACA sandbox). `providerPhase` and `guestIdentityDigest` in `status.provider.details`; no raw endpoint/path in any status field. **ProviderDeployment creates both static Processes; ACA controller never instantiates its own Processes and never writes Provider status directly. All Processes run inside the gateway Guest. No Host Process, no Host Credential, no Host Azure HTTP socket. Long-running cloud ops return `progressing`/`requeue-at` immediately; never block watch loop.** Primary reuse disposition: `replace`. Preserved source-plan detail: REPLACE (old) + ADAPT (main types/traits).", "entryContracts" : [ "ADR-046-provider-runtime-azure-container-apps" ], @@ -11558,6 +11559,7 @@ ], "specId" : "ADR-046-provider-runtime-azure-container-apps", "topologicalRank" : 11, + "validation" : "Controller conformance suite; adoption/ambiguity tests; deadline/cancellation matrix; redaction coverage; **gateway Guest placement validation: assert no Process has `executionRef: Host/*`**; Process spec field schema tests (`spec.template`, canonical `sandbox`/`budget`/`networkUsage`/`endpoints`/`readiness`/`restartPolicy` fields, `mounts` with `required: true`, `providerRef: Provider/system-minijail`); ProviderDeployment creates both Processes (controller never self-spawns); no raw endpoint/path in Guest status", "wave" : "W6" }, { @@ -11565,6 +11567,7 @@ "destinations" : [ "`packages/d2b-provider-runtime-azure-container-apps/src/deployment_service.rs`" ], + "detailedDesign" : "`ProviderAgentProcess`-shaped binary; bounded dispatch (64 in-flight); bounded audit ring (1024 capacity); shutdown within 5 s; serves `d2b.aca.v3.deployment` service schema including `GuestHealth` (health probing folded in from former health worker). All ACA API calls go through the injected `AcaControl` port — no ambient network call, no SDK default chain. Long-running ops return `progressing`/`requeue-at` to the caller; no blocking on Azure API completion. Primary reuse disposition: `adapt`. Preserved source-plan detail: COPY/ADAPT (partial); exclude `aca_workload.rs`.", "entryContracts" : [ "ADR-046-provider-runtime-azure-container-apps" ], @@ -11579,6 +11582,7 @@ ], "specId" : "ADR-046-provider-runtime-azure-container-apps", "topologicalRank" : 12, + "validation" : "Service dispatch matrix; RBAC refusal tests; redaction tests; shutdown deadline tests", "wave" : "W6" }, { @@ -11586,6 +11590,7 @@ "destinations" : [ "`packages/d2b-contracts/src/provider_effects/aca.rs` (shared `d2b-contracts` provider-effects module; no new crate; provider crate remains one package)" ], + "detailedDesign" : "`AcaCredentialLeaseClient`, `AcaCredentialLease`, `AcaCredentialLeaseRequest`, and `AcaCredentialPurpose` live in the shared `d2b-contracts` provider-effects module. Adapt `CredentialLease` to v3 Credential resource opaque lease handle. `AcaCredentialPurpose` maps to `allowedOperations` check against `Credential.spec`. Lease expiry capped at call deadline. Cleanup job pattern retained verbatim. Primary reuse disposition: `adapt`. Preserved source-plan detail: RETAIN+ADAPT.", "entryContracts" : [ "ADR-046-provider-runtime-azure-container-apps" ], @@ -11600,6 +11605,7 @@ ], "specId" : "ADR-046-provider-runtime-azure-container-apps", "topologicalRank" : 12, + "validation" : "Mock credential client tests; lease cleanup timeout tests; token non-exposure assertion", "wave" : "W6" }, { @@ -11607,6 +11613,7 @@ "destinations" : [ "ZoneLink resource `spec.transportSettings` (§15.4); `Provider/transport-azure-relay` dossier (separate)" ], + "detailedDesign" : "`AcaRelayTransportConfig` fields mapped to `transportSettings.relayNamespace`, `.relayHybridConnection`, `.relayCredentialRef`. KK enrollment replaces ZoneLink transport. Primary reuse disposition: `replace`. Preserved source-plan detail: REPLACE (both); ADAPT config fields to ZoneLink transport settings.", "entryContracts" : [ "ADR-046-provider-runtime-azure-container-apps" ], @@ -11621,6 +11628,7 @@ ], "specId" : "ADR-046-provider-runtime-azure-container-apps", "topologicalRank" : 11, + "validation" : "ZoneLink enrollment tests; relay unavailability tests; KK re-enrollment after sandbox restart", "wave" : "W6" }, { @@ -11628,6 +11636,7 @@ "destinations" : [ "`packages/d2b-provider-runtime-azure-container-apps/src/types.rs`" ], + "detailedDesign" : "Replace `RealmId`/`WorkloadId` with v3 `Zone`/`Guest` UID types. `AcaResourceBinding` keys the adoption query. The provider declares **no** Provider state Volume: bounded, non-secret sandbox binding/adoption metadata lives in `Guest.status` (latest bounded observed handle digests) and in-flight operation/requeue truth lives in the core Operation ledger (D087). Neither Process mounts a state Volume; there is no `sandbox-state`/`service-state` Volume, no `User/d2b-aca-controller`/`User/d2b-aca-deployment-service` state-layout principal, and no empty identity-only Volume. On restart the controller re-derives observed binding from `Guest.status`, the core Operation ledger, and an external `find_sandboxes` query, treating status as observation, never authority. Host never holds cloud binding, admission, PSK, or operation state. Primary reuse disposition: `adapt`. Preserved source-plan detail: RETAIN+ADAPT.", "entryContracts" : [ "ADR-046-provider-runtime-azure-container-apps" ], @@ -11642,6 +11651,7 @@ ], "specId" : "ADR-046-provider-runtime-azure-container-apps", "topologicalRank" : 12, + "validation" : "Controller/service declare empty `stateNamespaces`; no `sandbox-state`/`service-state` Volume created; neither Process mounts a state Volume; `Guest.status` binding/adoption fields are bounded, non-secret, and carry no credential/endpoint/poll-URL bytes; restart re-derivation from status/core ledger/external `find_sandboxes` without a Volume; core Operation ledger adapter integration test", "wave" : "W6" }, { @@ -11649,6 +11659,7 @@ "destinations" : [ "`nixos-modules/` (generated Guest resource options); `packages/d2b-provider-runtime-azure-container-apps/src/{audit,metrics}.rs`" ], + "detailedDesign" : "Eval-time assertions for ACA-specific invariants (§15.6). Closed OTEL label set (§13.4). Audit event schema (§13.3). Tracing target constant `d2b_provider_runtime_azure_container_apps::credential_lease_cleanup` retained. Primary reuse disposition: `replace`. Preserved source-plan detail: REPLACE (Nix emitter) + ADAPT (metric/audit shapes).", "entryContracts" : [ "ADR-046-provider-runtime-azure-container-apps" ], @@ -11663,6 +11674,7 @@ ], "specId" : "ADR-046-provider-runtime-azure-container-apps", "topologicalRank" : 12, + "validation" : "Label cardinality policy test; audit commit-before-complete test; Nix assertion eval tests", "wave" : "W6" }, { @@ -11670,6 +11682,7 @@ "destinations" : [ "`nixos-modules/` (gateway Guest declaration, Process template wiring, Credential scope assertion); eval-time validation module" ], + "detailedDesign" : "Nix eval-time assertions for: (a) `gatewayExecutionRef` resolves to a `Guest` resource, not `Host/*`; (b) Credential `scope.executionRef` matches `gatewayExecutionRef`; (c) all Process templates emitted for this Provider have `executionRef` equal to `gatewayExecutionRef`. No `User` resource or `users.users.*` declarations required — component principals are framework-assigned and not OS accounts. Gateway Guest NixOS closure includes only the two ACA component binaries (§15.6). Assertion error messages name the offending resource and the required `gatewayExecutionRef`.", "entryContracts" : [ "ADR-046-provider-runtime-azure-container-apps" ], @@ -11684,6 +11697,7 @@ ], "specId" : "ADR-046-provider-runtime-azure-container-apps", "topologicalRank" : 12, + "validation" : "Nix eval assertion tests (wrong `executionRef` → assertion fires; correct setup → passes); §15.7 assertion coverage tests", "wave" : "W6" }, { @@ -11691,6 +11705,7 @@ "destinations" : [ "packages/d2b-host/src/bin/d2b-activation-helper.rs" ], + "detailedDesign" : "Replace the helper CLI flag interface with structured JSON input and JSON output, accept bounded systemArtifactId and activationMode, resolve store path internally, emit bounded outcome code, write no resource metadata, and preserve the no-bash-fallback invariant. Primary reuse disposition: `adapt`. Preserved source-plan detail: adapt in place.", "entryContracts" : [ "ADR-046-provider-activation-nixos" ], @@ -11704,6 +11719,7 @@ ], "specId" : "ADR-046-provider-activation-nixos", "topologicalRank" : 11, + "validation" : "Unit tests for JSON protocol, bounded outcomes, no resource metadata writes, and no Command::new bash fallback.", "wave" : "W6" }, { @@ -11711,6 +11727,7 @@ "destinations" : [ "docs/reference/schemas/v3/activation-nixos.d2bus.org.NixosGeneration.json and packages/d2b-contracts/src/activation_nixos.rs" ], + "detailedDesign" : "Define JSON schema and Rust DTOs for activation-nixos.d2bus.org.NixosGeneration, with systemArtifactId as the only artifact reference, no store path fields, activationDetail as a closed status.resource enum absent from spec, and phase constrained to the common framework enum values. Primary reuse disposition: `create`. Preserved source-plan detail: net-new schema and DTOs.", "entryContracts" : [ "ADR-046-provider-activation-nixos" ], @@ -11724,6 +11741,7 @@ ], "specId" : "ADR-046-provider-activation-nixos", "topologicalRank" : 11, + "validation" : "Schema golden vectors, serde unknown-field rejection, phase enum tests, activationDetail-not-in-spec test, and no-store-path-in-spec-or-status test.", "wave" : "W6" }, { @@ -11731,6 +11749,7 @@ "destinations" : [ "packages/d2b-provider-activation-nixos/src/controller/" ], + "detailedDesign" : "Implement the reconcile loop for activation-nixos.d2bus.org.NixosGeneration: validate executionRef, systemArtifactId, and priorGenerationRef; dispatch one activation-runner EphemeralProcess with canonical startRoot=true shape; observe runner status; mark superseded generations; prune by retainedGenerations through the finalizer protocol; emit §12.3 metrics with fixed `mode`/`outcome` semantics and no Zone or resource-name-derived labels; never perform direct store-path operations, nix-collect-garbage, explicit VolumeGcRequest, raw argv composition, or store path writes to resources. Primary reuse disposition: `replace`. Preserved source-plan detail: replace top-level imperative activation flow with resource controller logic.", "entryContracts" : [ "ADR-046-provider-activation-nixos" ], @@ -11745,6 +11764,7 @@ ], "specId" : "ADR-046-provider-activation-nixos", "topologicalRank" : 12, + "validation" : "Controller tests for retention, finalizer sequence, no TTL retention, no direct store ops, no store path in status, deleted event-only removal, runner shape, and a structural metric descriptor assertion that `vm`, `zone`, `zone_id`, `zone_uid`, and resource-name-derived keys are absent and a generation/Zone-name canary never enters labels.", "wave" : "W6" }, { @@ -11752,6 +11772,7 @@ "destinations" : [ "packages/d2b-provider-activation-nixos/src/runner/" ], + "detailedDesign" : "Implement target-local activation-runner worker that executes on NixosGeneration.spec.executionRef for Host and Guest targets using the same contract, reads private artifact-catalog.json through the integrity channel, resolves systemArtifactId to a store path in memory only, invokes d2b-activation-helper through structured JSON, executes target-local switch-to-configuration through typed helper dispatch with no raw exec or SSH, emits structured outcome JSON, and never outputs store paths. Primary reuse disposition: `adapt`. Preserved source-plan detail: net-new runner invoking adapted helper.", "entryContracts" : [ "ADR-046-provider-activation-nixos" ], @@ -11766,6 +11787,7 @@ ], "specId" : "ADR-046-provider-activation-nixos", "topologicalRank" : 13, + "validation" : "Runner tests for artifact lookup, JSON helper invocation, Host and Guest target parity, no raw argv, no SSH, no store path in output, and terminal nonzero handling.", "wave" : "W6" }, { @@ -11773,6 +11795,7 @@ "destinations" : [ "packages/d2b/src/activation.rs" ], + "detailedDesign" : "Implement d2b activation build, switch, boot, test, rollback, adopt, generations, gc, and migrate subcommands, projecting ADR-046 CLI and Operations behavior and ensuring no store path, digest, or artifact catalog field appears in JSON or human output. Primary reuse disposition: `replace`. Preserved source-plan detail: replace with grouped CLI projection.", "entryContracts" : [ "ADR-046-provider-activation-nixos" ], @@ -11786,6 +11809,7 @@ ], "specId" : "ADR-046-provider-activation-nixos", "topologicalRank" : 11, + "validation" : "CLI integration tests for subcommand parsing, authorization, resource creation/listing, rollback priorGenerationRef, gc ownership release, and output redaction.", "wave" : "W6" }, { @@ -11793,6 +11817,7 @@ "destinations" : [ "nixos-modules/providers/activation-nixos.nix" ], + "detailedDesign" : "Emit Provider spec and activation-nixos.d2bus.org.NixosGeneration resources per target, flow retainedGenerations only through Provider.spec.config.retainedGenerations, reference systems by systemArtifactId only, omit store paths from all emitted resources, and avoid dedicated state-layout User or ComponentPrincipal because ProviderStateSet is empty. Primary reuse disposition: `adapt`. Preserved source-plan detail: net-new resource emitter adapted from existing Nix activation inputs.", "entryContracts" : [ "ADR-046-provider-activation-nixos" ], @@ -11806,6 +11831,7 @@ ], "specId" : "ADR-046-provider-activation-nixos", "topologicalRank" : 11, + "validation" : "Nix eval tests for Provider config, NixosGeneration shape, retainedGenerations source, no systemStorePath in bundle, no state Volume or state-layout principal, and artifact ID resolution.", "wave" : "W6" }, { @@ -11813,6 +11839,7 @@ "destinations" : [ "packages/d2b/src/lib.rs" ], + "detailedDesign" : "Remove the legacy top-level activation command functions and their dispatcher registrations after the grouped d2b activation namespace passes integration tests. Primary reuse disposition: `delete-after-cutover`. Preserved source-plan detail: delete legacy top-level commands.", "entryContracts" : [ "ADR-046-provider-activation-nixos" ], @@ -11827,6 +11854,7 @@ ], "specId" : "ADR-046-provider-activation-nixos", "topologicalRank" : 12, + "validation" : "CLI integration matrix for d2b activation passes; grep or contract test confirms old cmd_* symbols and dispatcher registrations are absent.", "wave" : "W6" }, { @@ -11834,6 +11862,7 @@ "destinations" : [ "`packages/d2b-contracts/proto/d2b-resource-v3.proto`, `packages/d2b-resource-api/src/service.rs`, `client.rs`" ], + "detailedDesign" : "Async methods, contexts, preconditions, limits, errors, status/finalizer separation, batch API Primary reuse disposition: `adapt`. Preserved source-plan detail: extract and adapt.", "entryContracts" : [ "ADR-046-resource-api-and-authorization" ], @@ -11847,6 +11876,7 @@ ], "specId" : "ADR-046-resource-api-and-authorization", "topologicalRank" : 5, + "validation" : "Protocol vectors; malformed/oversize/conflict/status-owner tests", "wave" : "W0" }, { @@ -11854,6 +11884,7 @@ "destinations" : [ "`packages/d2b-resource-api/src/authz.rs`, `packages/d2b-core-controller/src/rbac.rs`" ], + "detailedDesign" : "Role/RoleBinding schemas/evaluator/cache/revision invalidation, ComponentSession subject mapping, parent Zone access", "entryContracts" : [ "ADR-046-resource-api-and-authorization" ], @@ -11868,6 +11899,7 @@ ], "specId" : "ADR-046-resource-api-and-authorization", "topologicalRank" : 6, + "validation" : "Decision matrix/property tests; revocation/cache/outage/parent-child tests", "wave" : "W0" }, { @@ -11875,6 +11907,7 @@ "destinations" : [ "`packages/d2b-provider-audio-pipewire/src/audio_policy.rs`; re-exported from crate root" ], + "detailedDesign" : "`LevelPercent`, `AudioGrant`, `AudioPolicyState`, `parse_audio_state`, `to_v2_bytes`, `AudioPolicyError` copy unchanged. `AudioPolicyState` is the canonical in-memory representation of `AudioBinding.spec.grants`. `parse_audio_state`/`to_v2_bytes` are used only once during first-activation migration from a prior v1/v2 on-disk file; there is no ongoing state file in v3. Primary reuse disposition: `copy-unchanged`. Preserved source-plan detail: `copy-unchanged` (no daemon imports; pure DTO library).", "entryContracts" : [ "ADR-046-provider-audio-pipewire" ], @@ -11888,6 +11921,7 @@ ], "specId" : "ADR-046-provider-audio-pipewire", "topologicalRank" : 11, + "validation" : "`tests/audio_policy.rs`: all existing tests from `d2b-core/tests/audio_policy.rs` plus AudioBinding spec serialization tests", "wave" : "W6" }, { @@ -11895,6 +11929,7 @@ "destinations" : [ "`packages/d2b-provider-audio-pipewire/src/argv.rs` (component template renderer)" ], + "detailedDesign" : "`generate_audio_argv` remains the canonical argv builder for the `vhost-user-sound-worker` component template. The resulting argv/env/executableRef are sealed into the LaunchTicket. The per-Guest binary copy path enforcement remains via the LaunchTicket verifier. The live Process resource spec contains no argv or executableRef. The `--socket` argument is removed; the vhost-user service identity is `Endpoint/corp-vm-audio-vhost-user`, while the backing locator is resolved into the LaunchTicket under authorization. Primary reuse disposition: `adapt`. Preserved source-plan detail: `adapt` — argv builder retained; becomes a signed component-template projection, not a live Process spec field.", "entryContracts" : [ "ADR-046-provider-audio-pipewire" ], @@ -11909,6 +11944,7 @@ ], "specId" : "ADR-046-provider-audio-pipewire", "topologicalRank" : 12, + "validation" : "`tests/argv.rs`: rejection matrix (Nix store path, symlink, cross-guest copy, empty name); no-socket-in-argv assertion; no-argv-in-process-spec assertion", "wave" : "W6" }, { @@ -11916,6 +11952,7 @@ "destinations" : [ "`packages/d2b-provider-audio-pipewire/src/mediator/enforcement.rs`" ], + "detailedDesign" : "Owner AudioService only: speaker `SetGrant` maps `\"off\"` to `pw_node_set_param(SPA_PARAM_Props, mute=true, target.object=-1)` on the worker's node and `\"on\"` to `mute=false`; `SetLevel` maps to a bounded volume. Microphone `\"on\"` is applied only after the owner authority grants its single capture slot; queued/blocked consumers remain muted, and release mutes/disconnects before handoff. `captureAlias` resolves privately through the registry. A projection AudioService routes the operation to the remote owner over its import stream and is denied any local mediator/PipeWire open. `FakeAudioMediator` is the hermetic test double. No state file, wpctl, EphemeralProcess, or node ID in any external surface. Primary reuse disposition: `adapt`. Preserved source-plan detail: `adapt` — enforcement logic becomes a libpipewire API implementation behind the `SetGrant`/`SetLevel` ComponentSession service.", "entryContracts" : [ "ADR-046-provider-audio-pipewire" ], @@ -11930,6 +11967,7 @@ ], "specId" : "ADR-046-provider-audio-pipewire", "topologicalRank" : 12, + "validation" : "`tests/mediator.rs` and `tests/enforcement.rs`: owner-Service SetGrant/SetLevel round-trip; speaker mixing; microphone queued consumers remain muted; release/lease-expiry mute-before-handoff and no-overlap proof; projection routing with fake streams; projection-PipeWire-open denial; no-node-id-in-bus-message; ProviderSessionUnavailable; captureAlias registry resolution", "wave" : "W6" }, { @@ -11937,6 +11975,7 @@ "destinations" : [ "`packages/d2b-provider-audio-pipewire/src/{resource_type,admission,provider_extension}.rs` (strict implementation extensions and binding only; common base lives under ADR046-provider-004)" ], + "detailedDesign" : "Bind the shared D098 `audio.d2bus.org.AudioService` and `audio.d2bus.org.AudioBinding` base schema versions/fingerprints from ADR046-provider-004 and define only strict audio-pipewire Provider extensions/admission. AudioService validates immutable `serviceRole`, same-Zone local Endpoint refs, owner-only D097 AuthorityDescriptor, projection-only `ownerRef: ResourceImport/`, Core-only projection creation, and projection `spec.provider` rejection. Initial-v3 owner admission requires speaker `multiplexed` and microphone `exclusive` and rejects multiplexed capture plus every consent/approval/priority placeholder. Owner Service status carries only bounded aggregate microphone active/request/Zone counts; Binding status carries only its own arbitration state. AudioBinding validates Guest ownership, required immutable same-Zone `serviceRef`, grants/levels/users, and forbids authority/export/projection semantics. Semantic authority/import/attachment observations stay under `status.resource`; implementation observations stay under `status.provider`. PipeWire fields are rejected from base spec/status. Register no provider-qualified or AudioState identifier and no serde/schema alias. Primary reuse disposition: `adapt`. Preserved source-plan detail: `adapt` provider-neutral closed enums; remove `AudioProviderKind` from base status; `ADR-only` for schema/admission.", "entryContracts" : [ "ADR-046-provider-audio-pipewire" ], @@ -11951,6 +11990,7 @@ ], "specId" : "ADR-046-provider-audio-pipewire", "topologicalRank" : 11, + "validation" : "`tests/resource_type.rs`: consume the ADR046-provider-004 common fixtures/fingerprints; canonical minimal base without `spec.provider`; neutral qualified-name registration; both schema/status round-trips including bounded aggregate owner queue status and per-Binding arbitration state; clean-break rejection of provider-qualified names, every AudioState spelling, and all aliases; fake alternate-provider base conformance; strict base/provider unknown-field matrices; projection `spec.provider` rejection; D088 `status.resource`/`status.provider` placement; PipeWire fields only in strict provider envelopes/config; Service role/AuthorityDescriptor/ownerRef/Endpoint-locality rules; initial-v3 exclusive-mic/mixed-speaker schema and consent/approval/priority/concurrent-capture rejection; Core-only projection admission; AudioBinding required same-Zone serviceRef and Guest owner; immutable refs; out-of-range levels/users; explicit tests that AudioBinding cannot be exported or projected", "wave" : "W6" }, { @@ -11958,6 +11998,7 @@ "destinations" : [ "`packages/d2b-provider-audio-pipewire/src/controller/audio_service.rs`; `src/controller/audio_binding.rs`" ], + "detailedDesign" : "One controller binary registers deterministic handlers for the two neutral ResourceTypes, constrained to immutable `spec.providerRef: Provider/audio-pipewire`. Service handler watches AudioService, its ResourceImport owner and local Endpoints; owner semantics claim/revalidate D097, local mediator, and aggregate exclusive-mic queue state, while projection semantics bind only encrypted import streams and deny PipeWire. It cannot create/delete projection Service. Binding handler watches AudioBinding, same-Zone serviceRef, Guest/User, owned Process, and private Endpoints; creates the vhost-user worker, GuestAudioAgents, and private Endpoints, then calls the resolved Service and guest agents. It maps `Applied|Queued|MicQueueFull` into the closed conditions/status/phase, and release/delete/revocation cancels queue state before child teardown. A Service event enqueues serviceRef-indexed Bindings. A resource selecting another conforming Provider is ignored and cannot be status/finalizer-mutated. Neither handler uses broker/pidfd/EphemeralProcess/Volume/User operations or direct filesystem access. Primary reuse disposition: `adapt`. Preserved source-plan detail: `adapt` — dispatch logic is the reference for step ordering only.", "entryContracts" : [ "ADR-046-provider-audio-pipewire" ], @@ -11975,6 +12016,7 @@ ], "specId" : "ADR-046-provider-audio-pipewire", "topologicalRank" : 14, + "validation" : "Fast hermetic `tests/audio_service_controller.rs`: neutral type/provider selection, foreign-provider ignore/deny, owner authority, bounded aggregate microphone status, projection ownerRef/import chain, core-only create/delete, projection no-PipeWire-open, revocation queue cancellation, and D091 propagation. `tests/audio_binding_controller.rs`: neutral type/provider selection, required same-Zone serviceRef, owner/projection dispatch, child Process/private Endpoint state machine, `Applied|Queued|MicQueueFull` status mapping, off/delete/revocation cancellation, grant changes, absence/failures/deletion. Conformance asserts no AudioBinding export/projection, no broker/pidfd/EphemeralProcess/Volume/User ops. ProviderDeployment integration remains fake-only and validates empty ProviderStateSet.", "wave" : "W6" }, { @@ -11982,6 +12024,7 @@ "destinations" : [ "`packages/d2b-provider-audio-pipewire/src/mediator/mod.rs`; `src/bin/audio_pipewire_mediator.rs`" ], + "detailedDesign" : "Owner AudioService implementation only. Long-lived user-session Process maintains per-AudioBinding nodes under the single owner backing, receives the pre-opened local PipeWire portal FD, and exposes `SetGrant`/`SetLevel` through `Endpoint/audio-pipewire-authority`. It enforces the authority arbiter's single microphone slot and mute-before-handoff result while speaker nodes remain mixed. Projection Services never start/call a local mediator and cannot receive its FD. No EphemeralProcess, wpctl, remote Ref, or node identity in external surfaces.", "entryContracts" : [ "ADR-046-provider-audio-pipewire" ], @@ -11996,6 +12039,7 @@ ], "specId" : "ADR-046-provider-audio-pipewire", "topologicalRank" : 13, + "validation" : "`tests/mediator.rs`: owner-Service FD handoff and calls; captureAlias; node-id sealing; session-unavailable; concurrent speaker Guest isolation; exclusive microphone no-overlap and mute-before-handoff; teardown; projection Service cannot resolve mediator Endpoint or portal attachment", "wave" : "W6" }, { @@ -12003,6 +12047,7 @@ "destinations" : [ "`nixos-modules/components/audio/v3-resource.nix`; `nixos-modules/components/audio/host-config.nix`; `nixos-modules/components/audio/guest-config.nix`" ], + "detailedDesign" : "`v3-resource.nix` emits only `audio.d2bus.org.AudioService` and `audio.d2bus.org.AudioBinding`, selected by `Provider/audio-pipewire`, with required same-Zone serviceRef. For projection identity, ResourceExport emits `resourceRef` plus canonical `serviceType`, `projectionSchemaFingerprint`, and `factoryFingerprint`; its Service retains the Endpoint. ResourceImport emits the matching three `expected*` fields. Projection Services are never authored: core materializes them from ResourceImport. Eval rejects every obsolete Export/Import field in the explicit rejection matrix, provider-qualified ResourceTypes, every AudioState spelling, aliases, PipeWire fields in neutral base spec/status, AudioBinding export/projection, non-exclusive microphone or consent/approval/priority/concurrent-capture fields, Service role/ownerRef/authority mismatches, cross-Zone Endpoint/service refs, and duplicate owner authority. Existing captureAlias stays in Provider config; guestUsers/group injection, runtime-audio derivation, host stream rules, and guest stack remain.", "entryContracts" : [ "ADR-046-provider-audio-pipewire" ], @@ -12017,6 +12062,7 @@ ], "specId" : "ADR-046-provider-audio-pipewire", "topologicalRank" : 12, + "validation" : "`tests/unit/nix/cases/audio-v3-resource.nix`: exact neutral type names; provider-qualified/AudioState/alias rejection; strict provider-field placement; owner Service and Binding round-trip; same-Zone serviceRef; exact canonical Export/Import Service type and both fingerprints; obsolete Export/Import field rejection; Export Endpoint-field rejection; projection core-only ownerRef chain; AudioBinding export/projection rejection; exclusive-mic/mixed-speaker and consent/approval/priority/concurrent-capture rejection; authority uniqueness; Endpoint locality; plus existing grants/users/captureAlias/deprecation/no-wpctl/no-audioFrontend assertions", "wave" : "W6" }, { @@ -12024,6 +12070,7 @@ "destinations" : [ "`packages/d2b-provider-audio-pipewire/tests/minijail_contract.rs` (provider-local); retain cross-bundle source greps in `d2b-contract-tests`" ], + "detailedDesign" : "Retain worker sandbox assertions and add role-sensitive attachment checks: an owner-Service worker may receive only the local AudioMediator attachment; a projection-Service worker receives only its same-Zone route Endpoint and can never receive a PipeWire FD. Binding-owned private Endpoint ownership/producerRef is explicit. All Service/Binding/Process/Endpoint serialized forms remain locator-free.", "entryContracts" : [ "ADR-046-provider-audio-pipewire" ], @@ -12038,6 +12085,7 @@ ], "specId" : "ADR-046-provider-audio-pipewire", "topologicalRank" : 12, + "validation" : "`cargo test -p d2b-provider-audio-pipewire -- minijail` must pass; existing cross-bundle tests must continue to pass", "wave" : "W6" }, { @@ -12045,6 +12093,7 @@ "destinations" : [ "`packages/d2b-provider-audio-pipewire/src/telemetry.rs`" ], + "detailedDesign" : "Emit closed-label Service and Binding metrics plus post-commit audit. Service events distinguish only `owner\\|projection` and closed outcomes; microphone arbitration emits closed transition/outcome plus bounded aggregate counts only. Metrics expose active count `0|1`, aggregate queue depth, and closed arbitration state without Zone/Binding/handle/position labels. Events omit authority keys, import/export keys, remote identity, stream/session ids, endpoints, client timestamps, and queue entries. Enforcement metrics cover owner-local and projection-routed calls without exposing route identity. ProcessEffect audit remains Process Provider-owned.", "entryContracts" : [ "ADR-046-provider-audio-pipewire" ], @@ -12061,6 +12110,7 @@ ], "specId" : "ADR-046-provider-audio-pipewire", "topologicalRank" : 15, + "validation" : "`tests/audio_telemetry.rs`: Service/Binding event separation, microphone aggregate metrics and transition audit, redaction of Zone/Binding/handle/position/timestamp, post-commit ordering, label cardinality, forbidden authority/import/stream/path fields, no ProcessEffect duplication", "wave" : "W6" }, { @@ -12068,6 +12118,7 @@ "destinations" : [ "`packages/d2b-provider-audio-pipewire/src/guest_agent/mod.rs`; `src/guest_agent/enforcement.rs`; `src/bin/audio_pipewire_guest_agent.rs`" ], + "detailedDesign" : "Long-lived user-domain Process running in the Guest under the guest workload user's UID. One Process resource per entry in `AudioBinding.spec.guestUsers`; each named by opaque UID digest (`ag-`) and carrying label `audio-pipewire.d2bus.org/role: guest-audio-agent`. `userRef` is the corresponding `User/` Zone resource. Opens a PipeWire connection in the Guest's compositor session (same-UID, natural access). Exposes a typed `AudioSet` ComponentSession service through an owned `Endpoint/ag--audio-set` (vsock transport, Guest→Zone d2b-bus). `AudioSet(mic, speaker, speakerLevel, micGain)` applies changes via libpipewire API (`pw_node_set_param` with `SPA_PARAM_Props`, `pw_stream_set_control`) on the guest virtio-snd PipeWire node. No wpctl binary, no command path, no EphemeralProcess. Controller calls ALL active GuestAudioAgent instances in parallel for each grant change and aggregates failures. `FakeGuestAudioAgent` is a test double behind `#[cfg(test)]`. Primary reuse disposition: `adapt`. Preserved source-plan detail: `ADR-only` (new component; supersedes guestd wpctl dispatch path).", "entryContracts" : [ "ADR-046-provider-audio-pipewire" ], @@ -12082,6 +12133,7 @@ ], "specId" : "ADR-046-provider-audio-pipewire", "topologicalRank" : 13, + "validation" : "`tests/guest_agent.rs`: AudioSet service call → libpipewire apply; mute/route/level; session-unavailable path; reconnect state restore; no wpctl binary; no command path; N-agent creation (one per guestUser); parallel call and aggregated failure", "wave" : "W6" }, { @@ -12089,6 +12141,7 @@ "destinations" : [ "`packages/d2b-provider-audio-pipewire/src/share_adapter.rs`" ], + "detailedDesign" : "Implement signed `Provider/audio-pipewire` adapters only for canonical `serviceType: audio.d2bus.org.AudioService` when `spec.providerRef` selects this Provider. ResourceExport carries that `serviceType` plus the signed `projectionSchemaFingerprint` and semantic `factoryFingerprint`; ResourceImport carries the matching `expectedServiceType`, `expectedProjectionSchemaFingerprint`, and `expectedFactoryFingerprint`. Export adapter admits only the owner Service. Its local authority Endpoint remains Service-owned and is never an Export field. Core creates/deletes the projection AudioService with `ownerRef: ResourceImport/`, `providerRef`, and semantic base/import fields but no `spec.provider`; routing derives from the signed local descriptor and ResourceImport record. The semantic factory fingerprint binds factory metadata plus projection-protocol version, never Provider/adapter identity, which the signed descriptor authenticates separately. The import adapter reconciles its semantic route and never creates AudioBinding or opens PipeWire. Per-Guest AudioBindings are ordinary consumer resources with same-Zone serviceRef. No provider-qualified type alias, FD/path/socket/remote Ref crosses a Zone. Primary reuse disposition: `adapt`. Preserved source-plan detail: net-new (implement the signed audio export/import adapter).", "entryContracts" : [ "ADR-046-provider-audio-pipewire" ], @@ -12104,6 +12157,7 @@ ], "specId" : "ADR-046-provider-audio-pipewire", "topologicalRank" : 13, + "validation" : "Fast hermetic `tests/share_adapter.rs`: exact neutral AudioService `serviceType`; exact projection-schema/factory fingerprint match; explicit rejection of obsolete `endpointRef`, `exportedType`, `baseSchemaFingerprint`, `expectedType`, `expectedBaseSchemaFingerprint`, and `projectionType`; reject every Export Endpoint field, provider-qualified alias, and AudioBinding export/projection; accept owner AudioService export; Core-only projection creation/deletion; exact ResourceImport -> projection AudioService ownerRef chain with no `spec.provider`; semantic factory fingerprint unchanged by Provider/adapter identity mutation while signed identity authentication remains exact; projection never opens PipeWire; reconnect/revocation/D091 propagation with fake streams. Only `integration/real_stream.rs` exercises a real encrypted named stream.", "wave" : "W6" }, { @@ -12111,6 +12165,7 @@ "destinations" : [ "`packages/d2b-provider-audio-pipewire/src/authority.rs` (speaker mixer + mic arbiter); `AuthorityDescriptor` on owner `AudioService`" ], + "detailedDesign" : "Exactly one owner AudioService holds the real PipeWire connection and D097 AuthorityDescriptor. Projection Services and AudioBindings cannot carry it. Speaker streams remain multiplexed/mixed. Microphone capture has exactly one slot across owner and importing Zones. The arbiter keys requests by authenticated Zone plus route-scoped opaque consumer handle, permits one pending entry per handle, bounds pending entries to 16 per Zone and 64 total, uses owner-sequenced FIFO per Zone and round-robin across non-empty Zones, and ignores client timestamps/priority. Its 30-second active lease renews only while no other Zone waits; contended expiry mutes/disconnects, dequeues the next Zone, and atomically requeues a still-requesting old holder at its Zone FIFO tail. Off/delete/revoke/disconnect cancel idempotently. Queue entries are memory-only and restart rebuild fails closed with capture muted. Multiplexed capture and consent/approval/priority/concurrent-capture surfaces are rejected; a future spec must define a concrete consent authorization ResourceType and resource-API verb before concurrent capture exists. Core rejects duplicate owner Services before open and adopts by ownerProof. No new ProcessRole, broker path, or state file. Primary reuse disposition: `adapt`. Preserved source-plan detail: `adapt` — the host controller becomes the single authority service; no daemon `Mutex`/state-file wrapping.", "entryContracts" : [ "ADR-046-provider-audio-pipewire" ], @@ -12127,6 +12182,7 @@ ], "specId" : "ADR-046-provider-audio-pipewire", "topologicalRank" : 13, + "validation" : "Fast hermetic `tests/authority.rs`: AuthorityDescriptor accepted only on owner AudioService; Binding/projection rejection; duplicate conflict; multiplexed speaker mix/quota; one active mic across local/imported Zones; per-Zone FIFO and cross-Zone round-robin; one-entry-per-handle, per-Zone/total bounds and `MicQueueFull`; idempotent cancellation; contended 30-second lease; mute-before-handoff/no overlap; restart-muted rebuild; multiplexed-capture and consent/approval/priority/concurrent-verb rejection; ownerProof adoption; D091 drain/recycle with fake clock/FakeHostController", "wave" : "W6" }, { @@ -12134,6 +12190,7 @@ "destinations" : [ "`packages/d2b-provider-audio-pipewire/src/streams.rs`" ], + "detailedDesign" : "Per-import audio frames flow only over bounded encrypted named streams: one stream with two `StreamChannel`s for playback/capture split direction and a single `StreamAuthz` (a consumer never opens two authz contexts to split direction), credit-based backpressure (a sender spends only receiver-granted credit), per-import session generation, cancel, and deadline. `StreamKind::AudioPlayback`/`AudioCapture` require `Capability::AudioPlayback`/`AudioCapture`. Playback streams may run concurrently; the owner activates capture frames for only its single granted opaque consumer handle, and revocation/disconnect cancels that Zone's active/pending requests before route teardown. No PipeWire FD/socket crosses a Zone; intermediaries see ciphertext. Guest audio calls (`audio_set`/`audio_status`) are issued to all active guests concurrently and results aggregated. Volume/gain (`LevelPercent`), queue identity/position, and node identity are redacted from audit/OTEL/logs. Primary reuse disposition: `adapt`. Preserved source-plan detail: `adapt` — audio frames ride the existing ComponentSession named-stream credit machinery.", "entryContracts" : [ "ADR-046-provider-audio-pipewire" ], @@ -12149,6 +12206,7 @@ ], "specId" : "ADR-046-provider-audio-pipewire", "topologicalRank" : 14, + "validation" : "Fast hermetic `tests/streams.rs`: projection-Service ownerRef/import binding, split-direction single-authz stream, credits, generation isolation, cancel/deadline, concurrent playback, one active capture across imports, route loss cancels that Zone's active/pending capture, ciphertext-only intermediary, redaction. Only `integration/real_stream.rs` runs the slower real encrypted stream.", "wave" : "W6" }, { @@ -12156,6 +12214,7 @@ "destinations" : [ "`packages/d2b-audit/src/{hash_chain.rs,segment.rs,rate_limit.rs,record_types.rs,sink.rs,export.rs}`" ], + "detailedDesign" : "`d2b-audit` provides: typed record structs per class; canonical serialization with `zone` replacing `realm: RealmPath`; SHA-256 hash chain (extracted from `daemon_audit.rs`); segment writer (O_APPEND CLOEXEC, 64 MiB / UTC-midnight rotation); 30-day compaction (adapts `prune_old` from `JsonlGatewayAudit`); `AuditWriteClass::{Privileged,Standard,BestEffort}` (extends current `{Privileged,Unprivileged}`); rate-limit with privileged-never-dropped invariant; export iterator with inline hash-break reporting. `AuditStreamKind` re-versioned: `Daemon→Zone`, `Gateway→ZoneLink`, `RemoteNode→RemoteZone`. `AuditChainRecord` re-versioned: `{zone: String}` replaces `{realm: RealmPath, node: NodeId}`. Primary reuse disposition: `adapt`. Preserved source-plan detail: extract unchanged: `AuditHash`, `AuditChainLink` from `d2b-realm-core/src/audit.rs`; copy hash-chain append algorithm from `daemon_audit.rs`; copy `AuditWriteClass`/rate-limit/rotation/prune from broker `audit.rs`; adapt `JsonlGatewayAudit` segment writer; adapt `OpAuditRecord` to `BrokerEffect` record class.", "entryContracts" : [ "ADR-046-telemetry-audit-and-support" ], @@ -12169,6 +12228,7 @@ ], "specId" : "ADR-046-telemetry-audit-and-support", "topologicalRank" : 10, + "validation" : "`audit_record_hash_chain`, `audit_record_schema` (no `realm`/`node` fields), `audit_segment_rotation`, `audit_rate_limit_privileged_never_dropped`, `audit_unavailable_blocks_privileged`", "wave" : "W5" }, { @@ -12176,6 +12236,7 @@ "destinations" : [ "`packages/d2b-resource-store-redb/src/audit.rs`, `packages/d2b-core-controller/src/authz_audit.rs`" ], + "detailedDesign" : "`ResourceMutation` records emitted by the store actor inside the write transaction before commit returns. The audit sink must durably fsync the audit record before returning the commit success (privileged durability class). `RBACChange` emitted by the authz handler in the same write transaction. `subject_digest` = SHA-256 of normalized canonical subject string from v3 `AuthenticatedSubjectContext` (ADR-046-componentsession-and-bus). Primary reuse disposition: `adapt`. Preserved source-plan detail: adapt `OpAuditRecord` structural pattern for `ResourceMutation` / `RBACChange` record classes; adapt `PrincipalId` → `subject_digest` derivation.", "entryContracts" : [ "ADR-046-telemetry-audit-and-support" ], @@ -12191,6 +12252,7 @@ ], "specId" : "ADR-046-telemetry-audit-and-support", "topologicalRank" : 11, + "validation" : "Integration test: 100 mutations → verify hash-chained audit records with `zone` field, no `realm` field", "wave" : "W5" }, { @@ -12198,6 +12260,7 @@ "destinations" : [ "`packages/d2b-session/src/audit.rs`, `packages/d2b-bus/src/audit.rs`" ], + "detailedDesign" : "`SessionConnect` records emitted at handshake completion. `GatewayAuditKind::DisplaySessionOpenAdmitted/Denied` → `event=\"connect\"`, `authz_decision=\"allowed/denied\"`. `GatewayAuditKind::DisplaySessionRunning` → informational `ProcessEffect`. `GatewayAuditKind::DisplaySessionClosed` → `event=\"close\"`. `transport_class=zone_link` covers what the current `AuditStreamKind::Gateway` stream recorded for gateway-backed realm sessions. `RouteAdmission` records emitted at bus route resolution for denied routes. Primary reuse disposition: `adapt`. Preserved source-plan detail: adapt `GatewayAudit` trait pattern for `SessionConnect` and `RouteAdmission` record classes; `NoopGatewayAudit` pattern reused for test sinks.", "entryContracts" : [ "ADR-046-telemetry-audit-and-support" ], @@ -12214,6 +12277,7 @@ ], "specId" : "ADR-046-telemetry-audit-and-support", "topologicalRank" : 11, + "validation" : "Session connect/close/auth-failure audit tests; `GatewayAuditKind` → `SessionConnect` mapping test", "wave" : "W5" }, { @@ -12221,6 +12285,7 @@ "destinations" : [ "`packages/d2b/src/zone_audit.rs` (new `d2b zone audit export` subcommand); `packages/d2b/tests/zone_audit_contract.rs`" ], + "detailedDesign" : "`d2b zone audit export` opens segments read-only (shared flock), streams NDJSON to stdout, validates hash chain inline, reports breaks as inline error records, enforces `audit-export` verb via resource API (admin-only, same `SO_PEERCRED`/Role check as current `ExportBrokerAuditOk`). Assert no `realm`, `node`, `workload_id` fields in exported records. Primary reuse disposition: `adapt`. Preserved source-plan detail: adapt audit CLI contract test (daemon-down/exit behavior); adapt broker export test to new `zone` field and v3 record schema.", "entryContracts" : [ "ADR-046-telemetry-audit-and-support" ], @@ -12235,6 +12300,7 @@ ], "specId" : "ADR-046-telemetry-audit-and-support", "topologicalRank" : 11, + "validation" : "`export_audit.rs`: admin-only, hash break inline, no old field names (`realm`/`node`/`workload_id`), no path/argv in output, exit 0 on clean chain", "wave" : "W5" }, { @@ -12242,6 +12308,7 @@ "destinations" : [ "`src/{lib.rs,config.rs,schema.rs,error.rs,effect/mod.rs}`" ], + "detailedDesign" : "Provider descriptor/manifest; `spec.config` schema; Guest spec.provider.settings schema; `AzureEffectPort` trait + `AzureOperationHandle`; `AzureVmError` enum; `SandboxSpec` with semantic classes; `BudgetSpec` with SI suffix memory fields; `restartPolicy` class/backoffBase/backoffMax; `networkUsage.allowEgress=false`; Endpoint ResourceType templates with name/transport/purpose Primary reuse disposition: `adapt`. Preserved source-plan detail: Extract and adapt; DELETE `InfrastructureProvider` after this Provider is operational.", "entryContracts" : [ "ADR-046-provider-runtime-azure-virtual-machine" ], @@ -12255,6 +12322,7 @@ ], "specId" : "ADR-046-provider-runtime-azure-virtual-machine", "topologicalRank" : 11, + "validation" : "Provider catalog; descriptor fingerprint; schema/conformance tests", "wave" : "W6" }, { @@ -12262,6 +12330,7 @@ "destinations" : [ "`src/effect/{mod.rs,real.rs,fake.rs,rate_limit.rs}`" ], + "detailedDesign" : "`AzureEffectPort` async trait; opaque `AzureOperationHandle` (bounded bytes, no poll URL); real `azure_core`/`azure_mgmt_compute` impl; `FakeAzureEffectPort` for hermetic tests; ARM 429/503/409 handling Primary reuse disposition: `adapt`. Preserved source-plan detail: Copy and adapt.", "entryContracts" : [ "ADR-046-provider-runtime-azure-virtual-machine" ], @@ -12276,6 +12345,7 @@ ], "specId" : "ADR-046-provider-runtime-azure-virtual-machine", "topologicalRank" : 12, + "validation" : "`tests/lifecycle_hermetic.rs`; all ARM paths via `FakeAzureEffectPort`; no ARM URL in test assertions", "wave" : "W6" }, { @@ -12283,6 +12353,7 @@ "destinations" : [ "`src/controller/{mod.rs,lifecycle.rs,idempotency.rs}`" ], + "detailedDesign" : "Non-blocking reconcile: `start_*(...)` → persist `AzureOperationHandle` → `requeue-at`; `poll_lro` on subsequent ticks; controller as authorized `update-status` writer for Guest resources; finalizer held until ARM delete confirmed; top-level `phase`, `status.resource`, and Azure `status.provider.details.providerPhase` written atomically Primary reuse disposition: `adapt`. Preserved source-plan detail: Copy/adapt main toolkit; adapt conformance shape.", "entryContracts" : [ "ADR-046-provider-runtime-azure-virtual-machine" ], @@ -12298,6 +12369,7 @@ ], "specId" : "ADR-046-provider-runtime-azure-virtual-machine", "topologicalRank" : 13, + "validation" : "`tests/lifecycle_hermetic.rs`; `tests/conformance.rs`", "wave" : "W6" }, { @@ -12305,6 +12377,7 @@ "destinations" : [ "`src/controller/bootstrap.rs`; `src/bootstrap_svc/{mod.rs,admission.rs,enrollment.rs}`" ], + "detailedDesign" : "PSK generation; sealed PSK/admission/enrollment recovery material (ciphertext) in the controller's single guest-local sealed recovery Volume; `GrantBootstrapAdmission` typed bus call; IKpsk2 in bootstrap-svc; enrollment record; enrolled KK; the bootstrap-svc declares **no** state Volume (session state in process memory; obtains sealed PSK/admission from the controller only); the controller's sealed recovery Volume is an ordinary Volume resource created by core ProviderDeployment (before component Process start) from the controller's single `stateNamespaces` declaration with a Nix-preprovisioned `User/azure-vm-controller` layout principal; ARM operation/idempotency records live in the core Operation ledger and non-secret observed cloud phase lives in `Guest.status` (D087); controller does not own, create, or add Volume to exported ResourceTypes; it consumes its view dirfd only Primary reuse disposition: `adapt`. Preserved source-plan detail: Copy/adapt main `BootstrapPsk`/`BootstrapAdmission`.", "entryContracts" : [ "ADR-046-provider-runtime-azure-virtual-machine" ], @@ -12319,6 +12392,7 @@ ], "specId" : "ADR-046-provider-runtime-azure-virtual-machine", "topologicalRank" : 12, + "validation" : "`tests/bootstrap_hermetic.rs`; `tests/error_redaction.rs`", "wave" : "W6" }, { @@ -12326,6 +12400,7 @@ "destinations" : [ "`src/credential.rs`" ], + "detailedDesign" : "ARM credential via enrolled KK `AcquireToken`; zeroizing token handling; no ambient credential fallback; `credential-managed-identity` guest-agent placement Primary reuse disposition: `adapt`. Preserved source-plan detail: Retain `OpaqueAzureRef` directly; adapt credential acquisition to enrolled KK.", "entryContracts" : [ "ADR-046-provider-runtime-azure-virtual-machine" ], @@ -12340,6 +12415,7 @@ ], "specId" : "ADR-046-provider-runtime-azure-virtual-machine", "topologicalRank" : 14, + "validation" : "`tests/credential_hermetic.rs`; `tests/error_redaction.rs`", "wave" : "W6" }, { @@ -12347,6 +12423,7 @@ "destinations" : [ "`src/controller/idempotency.rs`" ], + "detailedDesign" : "Deterministic ARM request ID derivation; `AzureOperationHandle` opaque persistence (no poll URL in state); ARM 409 adoption; finalizer held through async deletion Primary reuse disposition: `adapt`. Preserved source-plan detail: Adapt to deterministic per-Guest keys.", "entryContracts" : [ "ADR-046-provider-runtime-azure-virtual-machine" ], @@ -12361,6 +12438,7 @@ ], "specId" : "ADR-046-provider-runtime-azure-virtual-machine", "topologicalRank" : 14, + "validation" : "`tests/idempotency.rs`; restart-recovery scenario", "wave" : "W6" }, { @@ -12368,6 +12446,7 @@ "destinations" : [ "`nixos-modules/` (Provider/Guest resource emitters); crate Nix build" ], + "detailedDesign" : "Nix `spec.config` shape; `controllerExecutionRef`/`networkRef` eval-time assertions; no Volume refs for data disks; `systemArtifactId=null` enforcement; the single controller sealed recovery Volume is an ordinary Volume resource created by core ProviderDeployment (not in Zone bundle; not operator-authored); the bootstrap-svc declares no state Volume; guest-local placement — reconciled by the Guest-local volume-local instance and expressed by `source.executionRef` = config gateway Guest; host MUST NOT hold ARM binding, admission, PSK, or operation state; ARM operation/idempotency records live in the core Operation ledger and non-secret observed cloud phase in `Guest.status` (D087); no virtiofs or host-to-guest attachment; manifest freezes guest-local with no fallback; controller does not create, own, or list Volume in exported ResourceTypes; `Provider/volume-local` is the sole Volume reconciler; controller consumes required view dirfd only; **the recovery Volume is `kind: state`, `persistenceClass: persistent`, `storageNeed: secret`, sealed via `sealingCredentialRef`, with nonzero `quotaBytes`, `quota.maxBytes`, `quota.maxInodes`, and `source.settings.sourcePolicyId`; `persistenceClass: ephemeral` and zero quotas are rejected**; it survives component/Provider restart and participates in upgrade/destroy/reset; full canonical Volume spec including `stateSchema`, `source`, `layout` with a Nix-preprovisioned `User/` principal (not ComponentPrincipal), `views`, `identityMarker`, `snapshotPolicy: null`, `retentionPolicy: null`; `sensitivityClass: private` and `volume-domain-mismatch` isolation enforced; canonical `SandboxSpec` fields with `namespaceClasses`/`capabilityClasses`/`seccompClass`/`noNewPrivileges`/`startRoot`/`environmentClass`/`readOnlyRoot`; `BudgetSpec` with SI suffix; `restartPolicy` class/backoffBase/backoffMax; Endpoint ResourceType templates with name/transport/purpose", "entryContracts" : [ "ADR-046-provider-runtime-azure-virtual-machine" ], @@ -12382,6 +12461,7 @@ ], "specId" : "ADR-046-provider-runtime-azure-virtual-machine", "topologicalRank" : 12, + "validation" : "Nix eval tests; `make test-flake`; `make test-drift`", "wave" : "W6" }, { @@ -12389,6 +12469,7 @@ "destinations" : [ "`src/{telemetry.rs,audit.rs}`" ], + "detailedDesign" : "Closed metric labels; OTEL span attributes; audit durability classes; `azure-vm-deleted` appended post-commit; no ARM URI, ARM resource ID, or cloud endpoint in any telemetry surface Primary reuse disposition: `adapt`. Preserved source-plan detail: Adapt audit shape; replace Prometheus with d2b-telemetry emitter.", "entryContracts" : [ "ADR-046-provider-runtime-azure-virtual-machine" ], @@ -12403,6 +12484,7 @@ ], "specId" : "ADR-046-provider-runtime-azure-virtual-machine", "topologicalRank" : 14, + "validation" : "`tests/error_redaction.rs`; `d2b-contract-tests/tests/policy_observability.rs` updated", "wave" : "W6" }, { @@ -12410,6 +12492,7 @@ "destinations" : [ "`tests/`; `integration/`" ], + "detailedDesign" : "See §Test requirements Primary reuse disposition: `adapt`. Preserved source-plan detail: Copy/adapt fake toolkit; write new tests.", "entryContracts" : [ "ADR-046-provider-runtime-azure-virtual-machine" ], @@ -12423,6 +12506,7 @@ ], "specId" : "ADR-046-provider-runtime-azure-virtual-machine", "topologicalRank" : 11, + "validation" : "All tests pass", "wave" : "W6" }, { @@ -12430,6 +12514,7 @@ "destinations" : [ "`packages/d2b-bus/src/{router,registry,authorization,streams,operations}.rs`" ], + "detailedDesign" : "Exact service/resource routes, RBAC, pinned reverse route, cancellation, named stream bridge, no wildcard pub/sub Primary reuse disposition: `adapt`. Preserved source-plan detail: extract/adapt.", "entryContracts" : [ "ADR-046-componentsession-and-bus" ], @@ -12443,6 +12528,7 @@ ], "specId" : "ADR-046-componentsession-and-bus", "topologicalRank" : 6, + "validation" : "Message isolation, route/auth revocation, fairness, reconnect, no direct-store path", "wave" : "W1" }, { @@ -12450,6 +12536,7 @@ "destinations" : [ "`packages/d2b-provider-runtime-cloud-hypervisor/src/controller.rs`" ], + "detailedDesign" : "End-to-end: single Guest reconcile → synchronous dependency-readiness check via ResourceClient → VMM Process creation → guest-control health check in observe handler → status write. Uses fake bus/store/supervisor stubs from toolkit. Proves fast-path latency gates (≤5 ms hint, ≤20 ms VMM Process creation when all deps ready). No EphemeralProcess resources at any step. Primary reuse disposition: `adapt`. Preserved source-plan detail: Extract and adapt.", "entryContracts" : [ "ADR-046-provider-runtime-cloud-hypervisor" ], @@ -12463,6 +12550,7 @@ ], "specId" : "ADR-046-provider-runtime-cloud-hypervisor", "topologicalRank" : 11, + "validation" : "Unit: reconcile state machine, fast-path latency, adoption/ambiguity, finalize ordering. Integration: end-to-end VMM boot with real KVM and guest-control session (requires `make test-host-integration`)", "wave" : "W6" }, { @@ -12470,6 +12558,7 @@ "destinations" : [ "`packages/d2b-provider-runtime-cloud-hypervisor/src/bootstrap_graph.rs`" ], + "detailedDesign" : "Single owned VMM Process resource; synchronous ResourceClient dependency check (Device/kvm + all declared Devices, Networks, virtiofs Volumes); immediate Process creation when all deps ready; no EphemeralProcess resources; conditional net-VM Guest creation; per-dependency readiness tracking in reconcile loop Primary reuse disposition: `replace`. Preserved source-plan detail: EXTRACT and REPLACE.", "entryContracts" : [ "ADR-046-provider-runtime-cloud-hypervisor" ], @@ -12484,6 +12573,7 @@ ], "specId" : "ADR-046-provider-runtime-cloud-hypervisor", "topologicalRank" : 12, + "validation" : "Golden VMM Process spec vectors; dependency-ordering tests; parallel Guest tests (8 concurrent); net-VM creation tests; Device/kvm explicit-ref enforcement", "wave" : "W6" }, { @@ -12491,6 +12581,7 @@ "destinations" : [ "`packages/d2b-provider-runtime-cloud-hypervisor/src/vmm_argv.rs`; `tests/vmm_argv_golden_test.rs`" ], + "detailedDesign" : "`VmmArgvInput` derived from validated `GuestSpec.spec.provider.settings`; kernel/initrd/rootfs paths resolved privately from artifact catalog at dispatch time; no path in spec/status; golden tests for headless/q35/microvm/gpu/video/macvtap variants Primary reuse disposition: `adapt`. Preserved source-plan detail: COPY/ADAPT.", "entryContracts" : [ "ADR-046-provider-runtime-cloud-hypervisor" ], @@ -12505,6 +12596,7 @@ ], "specId" : "ADR-046-provider-runtime-cloud-hypervisor", "topologicalRank" : 13, + "validation" : "Golden argv vectors matching `cloud-hypervisor-argv-*.txt` shapes with v3 adaptations; redaction test (no store path in Debug output)", "wave" : "W6" }, { @@ -12512,6 +12604,7 @@ "destinations" : [ "`packages/d2b-provider-runtime-cloud-hypervisor/nix/` (Nix emitter); `nixos-modules/` option extension for `runtime-cloud-hypervisor` Guest schema" ], + "detailedDesign" : "`d2b.zones..resources.` with `type = \"Guest\"` and `spec.provider.settings` validated against signed Provider schema; `spec.systemArtifactId` top-level field; artifact catalog `type = \"nixos-system\"` enforced by rule 17; Guest-control `Endpoint` resource emitted without raw locator; `make test-drift` gate for schema/Nix drift Primary reuse disposition: `adapt`. Preserved source-plan detail: ADAPT and REPLACE.", "entryContracts" : [ "ADR-046-provider-runtime-cloud-hypervisor" ], @@ -12527,6 +12620,7 @@ ], "specId" : "ADR-046-provider-runtime-cloud-hypervisor", "topologicalRank" : 13, + "validation" : "nix-unit eval tests: rule CH-1 through CH-4 + rules 1–17; golden resource bundle JSON (no store path); type-mismatch eval errors; raw locator rejection; `spec.systemArtifactId` at top-level in JSON (not in `spec.provider.settings`)", "wave" : "W6" }, { @@ -12534,6 +12628,7 @@ "destinations" : [ "`packages/d2b-provider-runtime-cloud-hypervisor/src/health.rs`; `src/adoption.rs`" ], + "detailedDesign" : "Authenticated KK ComponentSession health check over vsock; adoption verification (pid/cgroup/executable/generation) within `adoptionWindow`; ambiguity → Unknown/Degraded, never broad kill; graceful shutdown via guest-control session before SIGTERM", "entryContracts" : [ "ADR-046-provider-runtime-cloud-hypervisor" ], @@ -12549,6 +12644,7 @@ ], "specId" : "ADR-046-provider-runtime-cloud-hypervisor", "topologicalRank" : 12, + "validation" : "Fake guest-control server test; health check timeout/failure/retry; adoption property test (ambiguity, gone, stale pid); graceful shutdown ordering", "wave" : "W6" }, { @@ -12556,6 +12652,7 @@ "destinations" : [ "`packages/d2b-provider-runtime-cloud-hypervisor/src/metrics.rs`; `src/audit.rs`" ], + "detailedDesign" : "`d2b_runtime_ch_*` metrics from §18.3 with closed semantic labels and no `vm`, `zone`, `zone_id`, `zone_uid`, or resource-name-derived key; bounded durable audit records from §17.3; no path/argv/socket in any field; identity retained in the closed OTEL resource-attribute allowlist extended per §18.4", "entryContracts" : [ "ADR-046-provider-runtime-cloud-hypervisor" ], @@ -12571,6 +12668,7 @@ ], "specId" : "ADR-046-provider-runtime-cloud-hypervisor", "topologicalRank" : 12, + "validation" : "`policy_observability.rs` updated with v3 allowlist; structural descriptor tests assert exact absence of `vm`, `zone`, `zone_id`, `zone_uid`, and resource-name-derived keys plus Guest/Zone-name canary absence; bounded message/field tests; audit record schema golden vectors", "wave" : "W6" }, { @@ -12578,6 +12676,7 @@ "destinations" : [ "`packages/d2b-provider-runtime-cloud-hypervisor/src/state.rs`; `packages/d2b-provider-runtime-cloud-hypervisor/tests/state_status_test.rs`" ], + "detailedDesign" : "`state.rs` owns the controller's bounded non-secret operational-state projection into the owning resource's `status` subresource (reconcile stage, per-Guest launch/adoption observations, bounded counters, closed-enum error detail) — the controller declares no Provider state Volume and mounts no `/state`; on restart it re-derives observed state from the Zone resource store, the core Operation ledger, and external observation (running VMM/virtiofsd re-adopted from cgroup leaves + fresh pidfds), treating `status` as observation, never authority (D087); status writes occur only on material change and stay within the status bounds. The superseded state-Volume integration, migration, validation, and removal rows are rejected: this Provider has no state Volume, state mount, or `StateEnvelope` startup path. Primary reuse disposition: `replace`. Preserved source-plan detail: REPLACE (storage.rs).", "entryContracts" : [ "ADR-046-provider-runtime-cloud-hypervisor" ], @@ -12593,6 +12692,7 @@ ], "specId" : "ADR-046-provider-runtime-cloud-hypervisor", "topologicalRank" : 12, + "validation" : "`state_status_test.rs` (hermetic): status projection round-trip and bound enforcement; restart re-derivation from store/ledger/external observation without a state Volume; no secret/path/argv/PID in status", "wave" : "W6" }, { @@ -12600,6 +12700,7 @@ "destinations" : [ "`packages/d2b/src/lib.rs`, `packages/d2b/src/context.rs`, `packages/d2b/src/dispatch.rs`" ], + "detailedDesign" : "Introduce `ZoneContext` (zone name, socket path, ComponentSession client); implement `--zone`/`D2B_ZONE`/nearest-socket discovery using adapted `LocalUnixDaemonAccess::connect_component_session()` chain; introduce `ResourceRef` argument parser; introduce unified `--json`/`--human`/`--deadline` flag infrastructure bounded by `MAX_REQUEST_LIFETIME_MS=900s`; freeze `--json` schema version 1; stabilize exit code table. Excluded ADR 0045 assumptions: `TargetInput::Realm`, `TargetInput::Workload`, `TargetInput::Provider` variants; `RealmPath::parse(\"local-root\")` / `RealmId::derive` pattern; `RealmPath`-based service owner types.", "entryContracts" : [ "ADR-046-cli-and-operations" ], @@ -12615,6 +12716,7 @@ ], "specId" : "ADR-046-cli-and-operations", "topologicalRank" : 10, + "validation" : "Zone-unavailable/fallback tests; ResourceRef parse/reject vectors; exit-code round-trip tests; TTY detection tests; adapt `client.rs:typed_routes_select_exact_transport_without_fallback` (line 1053), `connector_discovers_and_authenticates_the_driver_generation` (line 1254), `daemon_transport_rejects_ancillary_data_and_oversized_packets` (line 1312)", "wave" : "W5" }, { @@ -12622,6 +12724,7 @@ "destinations" : [ "`packages/d2b/src/guest.rs` (`d2b guest start/stop/restart/list/status`); unsafe-local workloads go to `packages/d2b/src/host.rs` (`d2b host list/status/get`), NOT guest.rs" ], + "detailedDesign" : "Route Guest lifecycle (WorkloadProviderKind: LocalVm/QemuMedia/ProviderManaged) through `d2b.resource.v3` Get/UpdateSpec/Watch; map dry-run/apply to resource API precondition; `--no-wait-ready` exits on accepted; with-wait uses `d2b status --watch` loop. WorkloadProviderKind::UnsafeLocal entries MUST route to `d2b host` commands only; any code path that would return an unsafe-local entry from `d2b guest list` is a correctness violation. v2 commands (`d2b up/down/restart/list/status`, `d2b vm start/stop/restart/list/status`) are deleted at 3.0; replacement guidance remains documentation-only, with no migration-diagnostic command.", "entryContracts" : [ "ADR-046-cli-and-operations" ], @@ -12637,6 +12740,7 @@ ], "specId" : "ADR-046-cli-and-operations", "topologicalRank" : 11, + "validation" : "Dry-run/apply/wait/no-wait-ready tests; zone-unavailable degraded path; JSON output schema tests; confirm v2 command paths are absent (compilation failure if any cmd_vm_start/stop alias re-introduced)", "wave" : "W5" }, { @@ -12644,6 +12748,7 @@ "destinations" : [ "`packages/d2b/src/exec.rs` (`d2b exec run/attach/wait/status/list/logs/kill`)" ], + "detailedDesign" : "Map EphemeralProcess resource lifecycle; `exec run` creates resource and returns ref; `exec attach` opens named stream via adapted `DaemonClient::open_terminal(DaemonMethod::Exec, ...)` → `DaemonTerminal`; retain full `exec_client.rs` FSM and TTY machinery from baseline; retain `--json` envelope fields `source`/`reason`/`guestExitCode`/`signal`/`transportExitCode`; retain reserved exit codes 42/69/70/75/76/77. v2 commands (`d2b vm exec *`) are deleted at 3.0; no dispatch wiring. Excluded ADR 0045: `GuestClient` vsock/guest-control proxy path; `TargetInput::Workload`; old `WorkloadName`-keyed exec management. Primary reuse disposition: `adapt`. Preserved source-plan detail: copy-then-adapt.", "entryContracts" : [ "ADR-046-cli-and-operations" ], @@ -12659,6 +12764,7 @@ ], "specId" : "ADR-046-cli-and-operations", "topologicalRank" : 11, + "validation" : "Full `exec_client.rs` test suite migrated; adapted tests from main `client.rs:terminal_*` and `guest_exec_*`; TTY/raw-mode/RAII/signal tests; `--json` envelope/disambiguation tests; capacity/transport/auth/protocol exit-code tests; confirm v2 `cmd_vm_exec` path is absent", "wave" : "W5" }, { @@ -12666,6 +12772,7 @@ "destinations" : [ "`packages/d2b/src/shell.rs` (`d2b shell open/attach/list/detach/kill/status`)" ], + "detailedDesign" : "Route ShellSession resource lifecycle through resource API using adapted `ShellService` generated types; `shell open` → `ShellCreate` → `DaemonClient::open_terminal(Shell)` → `DaemonTerminal`; retain FSM/TTY/signal/RAII behavior from `run_shell_fsm`; `--name` required for kill; SIGHUP detaches without kill. v2 commands (`d2b shell *`) are deleted at 3.0; no dispatch wiring. Excluded: gateway relay path (`VmTargetRoute::Gateway`); old `ShellOp`/`ShellOpResponse` seqpacket protocol; `TargetInput::Workload`-keyed realm routing. Primary reuse disposition: `adapt`. Preserved source-plan detail: copy-then-adapt.", "entryContracts" : [ "ADR-046-cli-and-operations" ], @@ -12680,6 +12787,7 @@ ], "specId" : "ADR-046-cli-and-operations", "topologicalRank" : 11, + "validation" : "Shell list/detach/kill/attach unit tests (adapted from existing); adapted `client.rs:shell_management_*` and `named_stream_*` tests; TTY RAII/signal tests; confirm v2 `cmd_shell` path is absent", "wave" : "W5" }, { @@ -12687,6 +12795,7 @@ "destinations" : [ "`packages/d2b/src/provider.rs` (`d2b provider list/get/status/inspect`; dynamic projection loading)" ], + "detailedDesign" : "`d2b provider list/get/status/inspect`; InspectSchema call returns dynamic projection descriptor using `ConnectedClient::invoke` with generated provider service types; projection bounds enforcement (64 KiB, 32 sub-verbs, 2s deadline, shell-escape, newline strip); define the single canonical built-in command registry consumed by parser/help/completion and Provider projection binding; make bind-time rejection the sole authority for built-in and Provider/Provider top-level-name collisions, with no dispatch fallback; render projected commands from their declared top-level subcommand names; audio/clipboard/display are the first providers to migrate their projections", "entryContracts" : [ "ADR-046-cli-and-operations" ], @@ -12701,6 +12810,7 @@ ], "specId" : "ADR-046-cli-and-operations", "topologicalRank" : 11, + "validation" : "Projection size/name/timeout bounds tests; registry-completeness test asserting exact equality with all built-in parser commands; bind-time built-in and Provider/Provider collision rejection tests; negative test proving no collision fallback dispatch exists; audio/clipboard/display projection conformance tests asserting `d2b audio ...`, `d2b clipboard ...`, and `d2b display ...` rendering; completion script safety tests; adapted `conformance.rs` tests", "wave" : "W5" }, { @@ -12708,6 +12818,7 @@ "destinations" : [ "`packages/d2b/src/complete.rs` (`d2b complete bash/zsh/fish`)" ], + "detailedDesign" : "`d2b complete ` emits completion script; uses clap `CommandFactory::command()` plus dynamic projection fetch (2s per-Provider, 10s total); result bounded at 256 KiB; shell-escaped; newlines stripped Primary reuse disposition: `adapt`. Preserved source-plan detail: copy-unchanged (clap_complete).", "entryContracts" : [ "ADR-046-cli-and-operations" ], @@ -12722,6 +12833,7 @@ ], "specId" : "ADR-046-cli-and-operations", "topologicalRank" : 11, + "validation" : "Completion script tests (bash/zsh/fish syntax valid); projection injection safety tests; deadline/partial-Provider tests", "wave" : "W5" }, { @@ -12729,6 +12841,7 @@ "destinations" : [ "`packages/d2b/src/activation.rs` (`d2b activation build/generations/switch/boot/test/rollback/gc/migrate/keys/trust/rotate-known-host/config`)" ], + "detailedDesign" : "Route through `activation-nixos` Provider service via `ConnectedClient::invoke` using adapted `DaemonMethod::Apply`/lifecycle dispatch pattern; retain dry-run/apply; retain guest-control transport for config sync (no SSH). v2 top-level activation commands (`d2b build/switch/boot/test/rollback/gc/migrate/keys/trust/rotate-known-host/config`) are deleted at 3.0; no dispatch wiring.", "entryContracts" : [ "ADR-046-cli-and-operations" ], @@ -12743,6 +12856,7 @@ ], "specId" : "ADR-046-cli-and-operations", "topologicalRank" : 11, + "validation" : "All existing switch/boot/test/rollback/keys tests adapted; config sync/diff/approve/reject tests; confirm v2 top-level activation paths are absent; adapted `client.rs:daemon_typed_list_preserves_projection_and_truncation` apply pattern", "wave" : "W5" }, { @@ -12750,6 +12864,7 @@ "destinations" : [ "`packages/d2b/src/host.rs` (all `d2b host` subcommands)" ], + "detailedDesign" : "Route `host prepare/destroy` through Zone resource API Host reconcile operation via `ConnectedClient::invoke`; retain broker-mediated mutation and ownership-marker semantics; `host doctor` prefers Zone resource API status, falls back to local state files; `host check` retains exit-code 3; `host validate` retains wave/evidence-dir/scripts-dir/signature", "entryContracts" : [ "ADR-046-cli-and-operations" ], @@ -12764,6 +12879,7 @@ ], "specId" : "ADR-046-cli-and-operations", "topologicalRank" : 11, + "validation" : "All existing host-check/prepare/destroy/doctor/install/reconcile/validate tests; exit-code 3 regression; doctor Zone-fallback/local-state-fallback tests", "wave" : "W5" }, { @@ -12771,6 +12887,7 @@ "destinations" : [ "`packages/d2b/src/zone.rs` (`d2b zone get/list/status`)" ], + "detailedDesign" : "`d2b zone get []` fetches Zone self resource via `ConnectedClient::invoke`; `d2b zone list` lists ZoneLink resources. v2 commands (`d2b realm list/inspect/enter/run`) are deleted at 3.0; no dispatch wiring. Excluded ADR 0045: `RealmServiceServer`/`RealmServiceProcess` multi-realm service; `RemoteNodeRegistration` constellation routing; `TargetInput::Realm`; `RealmMethod::ResolveRoute`/`AuthorizeShortcut`/`RevokeShortcut`.", "entryContracts" : [ "ADR-046-cli-and-operations" ], @@ -12785,6 +12902,7 @@ ], "specId" : "ADR-046-cli-and-operations", "topologicalRank" : 11, + "validation" : "Zone get/list tests; confirm v2 `cmd_realm_*` paths are absent", "wave" : "W5" }, { @@ -12792,6 +12910,7 @@ "destinations" : [ "`packages/d2b/src/resource.rs` (standard `d2b get/list/watch/create/update-spec/delete/status` top-level verbs)" ], + "detailedDesign" : "Generic typed dispatch to resource API Get/List/Watch/Create/UpdateSpec/Delete using `ConnectedClient::invoke` (Get/List/Create/UpdateSpec/Delete) and `ConnectedClient::open_server_stream` + `NamedStream` (Watch); ResourceRef argument parsing and validation; page token pagination; `--phase`/`--label-selector` filters; `--deadline` bounded by `MAX_REQUEST_LIFETIME_MS=900s` via `DeadlineBudget`; Watch output streams resource events as JSON lines; JSON schema version field; `CancellationToken` wired to process signal handlers. Excluded: `GuestClient` vsock exec/shell routing; `TargetInput` realm/workload/provider variants. Primary reuse disposition: `adapt`. Preserved source-plan detail: copy-then-adapt.", "entryContracts" : [ "ADR-046-cli-and-operations" ], @@ -12806,6 +12925,7 @@ ], "specId" : "ADR-046-cli-and-operations", "topologicalRank" : 11, + "validation" : "Get/list/watch/create/update-spec/delete tests per ResourceType; pagination/filter/watch-deadline tests; error-class/exit-code tests; adapted `client.rs:metadata_retries_*` and `mutating_retries_*` and `concurrent_named_streams_*` tests", "wave" : "W5" }, { @@ -12813,6 +12933,7 @@ "destinations" : [ "Nix: `nixos-modules/options-zones.nix` (unified `d2b.zones..resources` attrset; per-type `spec` sub-options generated from ResourceTypeSchema/Provider schema), `nixos-modules/bundle-emit.nix` (canonical JSON emit + SHA256 pin), `nixos-modules/assertions.nix` (updated); core controller: `packages/d2b-core-controller/src/configuration.rs`, `packages/d2b-core-controller/src/cleanup.rs`; Contracts: `packages/d2b-contracts/src/zone_bundle.rs` (new)" ], + "detailedDesign" : "**Nix shape:** `d2b.zones..resources` is `attrsOf (submodule { type; optional metadata { ownerRef; labels; annotations }; spec })`. `spec` sub-options per `type` are generated from ResourceTypeSchema and signed Provider schemas; field names remain identical. `metadata.name`/`metadata.zone`/`apiVersion` are derived; status and all core metadata are rejected in input. Vendor-qualified types are admitted only when their schema is installed. **Nix emit:** `bundle-emit.nix` emits `/etc/d2b/zones//resource-bundle.json` plus its integrity pin with canonical resource ordering and schema digests. **Core-controller apply:** `configuration.rs` verifies bundle/catalog integrity, applies Create/Update/no-op intents with bounded async concurrency, refreshes `configurationGeneration` for unchanged configuration-managed resources without waking their controller, handles controller/API name collisions per-item without seizing them, and asynchronously deletes only persisted `managedBy=configuration` resources absent from the new configured set. `cleanup.rs` consumes `Deleted` revision watches and maintains `PendingCleanup`; it never force-removes finalizers. **Prior generation retention:** `d2b.zones..retainedGenerations`, default 3 and range 1–16, is a compiler setting outside `Zone.spec`; no TTL. Rollback reapplies a retained bundle as a new higher generation.", "entryContracts" : [ "ADR-046-cli-and-operations" ], @@ -12831,6 +12952,7 @@ ], "specId" : "ADR-046-cli-and-operations", "topologicalRank" : 14, + "validation" : "Runtime integration: all CLI-visible cleanup/status/rollback/gc/audit tests (§CLI-visible tests for activation and cleanup), including no force-finalizer path; Nix unit and build tests owned by ADR-046-nix-configuration spec; canonical-example fixture validates every authored resource against the authoritative Host, Guest, and Provider schemas, resolves every artifact/resource reference, rejects unsupported fields, and proves the compiled Guest `spec` is field-for-field identical to the adjacent JSON", "wave" : "W5" }, { @@ -12838,6 +12960,7 @@ "destinations" : [ "`packages/d2b/src/endpoint.rs` (`d2b endpoint get/list/watch/status/resolve`)" ], + "detailedDesign" : "Add `Endpoint` to the frozen standard ResourceType set (19 types; local ResourceRef parser accepts `Endpoint/` with no Zone round-trip). Implement `d2b endpoint get/list/watch/status` over the ADR046-cli-010 generic verbs and `d2b endpoint resolve ` as a provider-neutral resolution projection printing `producerRef`, `endpointClass`, transport class, readiness, and capability/locality observations. Endpoint output is the base envelope only (base spec + base status per D092); any `status.provider` projection is bounded, redacted, and deny-unknown. **No raw locator** (path/address/CID/port/fd/credential) appears in any CLI field or the `resolve` projection. `status.update` currency and `d2b upgrade Endpoint/` visibility follow the standard verbs and D091. `create`/`update-spec`/`delete` are rejected on the operator surface except for statically Nix/API-authored Endpoints whose schema permits it.", "entryContracts" : [ "ADR-046-cli-and-operations" ], @@ -12853,6 +12976,7 @@ ], "specId" : "ADR-046-cli-and-operations", "topologicalRank" : 12, + "validation" : "`Endpoint` accepted as a frozen standard type; `Endpoint/` parses locally without a Zone round-trip; `d2b endpoint get/list/watch/status/resolve` parse and route correctly; `get`/`resolve` output carries no raw path/address/CID/port/fd/credential; provider status projection is bounded/redacted; `d2b list Endpoint --updates` and `d2b upgrade Endpoint/` expose `status.update` currency and disruption; operator `create`/`update-spec`/`delete` rejected for controller-owned Endpoints", "wave" : "W5" }, { @@ -12860,6 +12984,7 @@ "destinations" : [ "`packages/d2b/src/share.rs` (`d2b export …` and `d2b import …` nouns)" ], + "detailedDesign" : "Add `ResourceExport` and `ResourceImport` to the frozen 19-type standard set while leaving qualified semantic/provider-neutral Service/Binding types dynamically cataloged. Implement export/import generic verbs, `d2b import projection`, and `d2b import graph`. Export accepts only a factory-bound owner Service. Import status exposes the same-semantic-type projection Service and verified factory/schema fingerprints; local `providerRef` selects the implementation. Graph renders import → projection Service → authored Bindings → controller-owned Process/Endpoint children with deterministic ownership/dependency edges. Binding spec is desired intent only and observations are status-only. Reject Device/Endpoint/Binding export targets, cross-Zone refs, absent/mismatched factory/type/fingerprints, implementation detail in the base, forbidden Providers, USB policy denial, unauthorized Zones, and capability-ceiling violations. No backing/remote refs, internal sessions/streams, FD, secret, path, locator, or bytes in output. Delete waits for authored Bindings to be removed/retargeted and never cascades them.", "entryContracts" : [ "ADR-046-cli-and-operations" ], @@ -12876,6 +13001,7 @@ ], "specId" : "ADR-046-cli-and-operations", "topologicalRank" : 12, + "validation" : "Both standard types parse locally; qualified semantic Service/Binding resolve dynamically; verbs route; Service-only export and factory/type/fingerprint/policy/capability rejections; projection preserves semantic Service type across local implementation selection; canonical minimal base works without `spec.provider`; graph shape exact and bounded; Binding never auto-created/deleted; Binding spec intent-only/status observations; no implementation detail/backing/remote-ref/session/stream/FD/secret/path/locator/bytes leakage", "wave" : "W5" }, { @@ -12883,6 +13009,7 @@ "destinations" : [ "`packages/d2b-client/src/` (updated for v3 Zone API, replacing ADR45 daemon verbs with Zone resource operations)" ], + "detailedDesign" : "**Selected**: `Client::connect()` target-resolve → ComponentSession-open → `ConnectedClient` lifecycle; `RetryPolicy` 1..8 bound + `retryable_failure()` safe-only retry detection; `NamedStream::send/receive/close_local/reset` lifecycle; `ComponentSessionConnector` trait as connector abstraction; `HostSocketConnector::from_seqpacket_fd` + `local_daemon_endpoint_identity` for Zone runtime socket connector; `RouteTable` ambiguous-route rejection; `ServiceHandle`/`GeneratedClient`/`MethodHandle` typed service client pattern; `map_ttrpc_error`/`validate_reply`/`map_retry` error-handling chain; `ClientError`/`RemoteErrorKind`/`RetryClass` error taxonomy; `DaemonClient` call-options and outcome helpers (infrastructure only). **Excluded ADR45 assumptions**: `DaemonMethod` enum (lines 29–56 of daemon_service.rs): ADR45 daemon verbs (`vm_start`, `vm_stop`, `list_realms`, etc.) — replaced with Zone API verbs. `GuestClient`/`guest_service.rs`: ADR45 guest operations; excluded until v3 Guest transport work item. Hardcoded socket path `PUBLIC_SOCKET_PATH = \"/run/d2b/public.sock\"` in `host_socket.rs`: replaced by Zone-resource-managed path. `TransportKind::LocalUnix` restriction in daemon-access: v3 allows multiple transport kinds per ZoneLink binding. Primary reuse disposition: `adapt`. Preserved source-plan detail: copy + adapt.", "entryContracts" : [ "ADR-046-resources-zone-control" ], @@ -12900,6 +13027,7 @@ ], "specId" : "ADR-046-resources-zone-control", "topologicalRank" : 13, + "validation" : "`client-retry-policy-max-8-attempts-enforced`, `client-named-stream-close-local-then-remote-close-transitions-closed`, `client-route-table-ambiguous-route-rejected`, `client-host-socket-peer-uid-verified-on-connect`, `client-retryable-failure-only-safe-mutations`", "wave" : "W5" }, { @@ -12907,6 +13035,7 @@ "destinations" : [ "packages/d2b-provider-clipboard-wayland/ with src, tests, integration, README.md, and binaries clipboard-controller, clipd-host, picker-session" ], + "detailedDesign" : "Create the provider crate skeleton, required source layout, three binaries, and README covering purpose, component map, local build instructions, test commands, and display-wayland fake dependency for integration tests.", "entryContracts" : [ "ADR-046-provider-clipboard-wayland" ], @@ -12920,6 +13049,7 @@ ], "specId" : "ADR-046-provider-clipboard-wayland", "topologicalRank" : 11, + "validation" : "Workspace provider layout policy plus README content review and follow-on make test-rust -p d2b-provider-clipboard-wayland once implementation exists.", "wave" : "W6" }, { @@ -12927,6 +13057,7 @@ "destinations" : [ "packages/d2b-provider-clipboard-wayland/src/clipd_host/ service binary modules such as service, display_client, bridge, picker_coord, policy, fd, audit, history" ], + "detailedDesign" : "Adapt d2b-clipd into clipd-host: call RequestPickerSession over d2b.clipboard.picker-coord.v3, consume d2b.display.host-clipboard.v3 instead of WAYLAND_DISPLAY or NIRI_SOCKET, serve d2b.clipboard.bridge.v3 on Endpoint/clipboard-bridge, remove filesystem bridge and peer group ACL logic, and preserve MIME allowlist, FD safety, fail-closed audit, loop suppression, and LRU history algorithms. Primary reuse disposition: `adapt`. Preserved source-plan detail: port and adapt algorithms; replace direct compositor, picker subprocess, Unix bridge, SO_PEERCRED, bridge directory, and group ACL paths.", "entryContracts" : [ "ADR-046-provider-clipboard-wayland" ], @@ -12941,6 +13072,7 @@ ], "specId" : "ADR-046-provider-clipboard-wayland", "topologicalRank" : 12, + "validation" : "make test-rust -p d2b-provider-clipboard-wayland plus unit coverage for MIME policy, FD safety, audit fail-closed queue, history bounds, lifecycle purge and suspension, no filesystem bridge, and no bytes in status or audit.", "wave" : "W6" }, { @@ -12948,6 +13080,7 @@ "destinations" : [ "packages/d2b-provider-clipboard-wayland/src/controller/ and clipboard-controller binary" ], + "detailedDesign" : "Implement Process/clipboard-controller as a system-domain system-minijail Process serving d2b.clipboard.picker-coord.v3, creating picker EphemeralProcesses from the signed picker-session template, observing picker status, relaying Guest lifecycle messages to clipd-host, creating clipboard RBAC Roles and RoleBindings, writing only bounded redacted operational observations through the optimistic status writer, and never owning or mounting Provider state Volumes. Primary reuse disposition: `create`. Preserved source-plan detail: net-new controller using existing resource API and ComponentSession contracts.", "entryContracts" : [ "ADR-046-provider-clipboard-wayland" ], @@ -12962,6 +13095,7 @@ ], "specId" : "ADR-046-provider-clipboard-wayland", "topologicalRank" : 12, + "validation" : "Controller unit tests for picker request validation, EphemeralProcess spec shape, terminal status callback, GuestStopped and GuestLocked handling, RBAC resources, status bounds, and empty ProviderStateSet.", "wave" : "W6" }, { @@ -12969,6 +13103,7 @@ "destinations" : [ "packages/d2b-provider-clipboard-wayland/src/picker_session/ and picker-session binary" ], + "detailedDesign" : "Implement picker-session as a user-domain worker EphemeralProcess with minimal environment, metadata over inherited ComponentSession named stream, restricted WAYLAND_SOCKET FD from display-wayland presentation portal, GTK4 closure-contained runtime, one Select or Cancel response, no clipboard FDs, no clipboard bytes, no compositor credentials, no socket paths, and typed PickerStartFailed on install or start failure instead of bypass. Primary reuse disposition: `adapt`. Preserved source-plan detail: rewrite as EphemeralProcess worker.", "entryContracts" : [ "ADR-046-provider-clipboard-wayland" ], @@ -12984,6 +13119,7 @@ ], "specId" : "ADR-046-provider-clipboard-wayland", "topologicalRank" : 13, + "validation" : "Contract test that picker cannot bind zwlr_data_control_manager_v1 plus unit tests for processClass worker, no FDs or payload in picker config, TTL defaults, response framing, and requirePickerForPaste false bypass semantics in clipd-host.", "wave" : "W6" }, { @@ -12991,6 +13127,7 @@ "destinations" : [ "packages/d2b-provider-clipboard-wayland service descriptors and generated Rust async ttrpc bindings, plus any shared contracts crate selected by ADR-046-componentsession-and-bus" ], + "detailedDesign" : "Generate stubs for d2b.clipboard.bridge.v3, d2b.clipboard.picker-coord.v3, and d2b.clipboard.v3, consume display-wayland d2b.display.host-clipboard.v3 client stubs, reject service-name collisions, and declare attachment classes clipboard-transfer-fd, host-selection-transfer-fd, and host-selection-supply-fd in the signed descriptor for ComponentSession handshake validation. Primary reuse disposition: `create`. Preserved source-plan detail: net-new generation of service stubs and named-stream types.", "entryContracts" : [ "ADR-046-provider-clipboard-wayland" ], @@ -13005,6 +13142,7 @@ ], "specId" : "ADR-046-provider-clipboard-wayland", "topologicalRank" : 12, + "validation" : "Contract tests for wire format, service-name collision rejection, attachment class matching, and descriptor handshake validation.", "wave" : "W6" }, { @@ -13012,6 +13150,7 @@ "destinations" : [ "nixos-modules/providers/clipboard-wayland.nix and d2b.artifacts.clipboard-wayland catalog entry" ], + "detailedDesign" : "Implement Nix module emitting d2b.zones..resources. Provider resources with spec.artifactId and spec.config, validate hostExecutionRef, hostUserRef, displayWaylandRef, and pickerArtifactId, forbid spec.componentPlacements, spec.settings, and spec.status, and remove nixos-modules/clipboard.nix in the same landing sequence as the new module. Primary reuse disposition: `replace`. Preserved source-plan detail: replace option surface with Provider resource Nix module.", "entryContracts" : [ "ADR-046-provider-clipboard-wayland" ], @@ -13025,6 +13164,7 @@ ], "specId" : "ADR-046-provider-clipboard-wayland", "topologicalRank" : 11, + "validation" : "Nix eval tests for resource shape, reference validation, null displayWaylandRef host-only mode, artifact catalog lookup, and absence of deprecated spec fields.", "wave" : "W6" }, { @@ -13032,6 +13172,7 @@ "destinations" : [ "packages/d2b-provider-clipboard-wayland/src/controller/rbac.rs or equivalent controller reconcile module" ], + "detailedDesign" : "Controller creates Role/clipboard-admin, Role/clipboard-viewer, Role/clipboard-bridge-peer, Role/clipboard-picker-worker and RoleBindings display-wayland-bridge, host-admin-clipboard, picker-session-worker, all Zone-scoped, owned by Process/clipboard-controller, selector-bound for Process/picker-*, and cleaned up when Provider is deleted.", "entryContracts" : [ "ADR-046-provider-clipboard-wayland" ], @@ -13046,6 +13187,7 @@ ], "specId" : "ADR-046-provider-clipboard-wayland", "topologicalRank" : 13, + "validation" : "Controller RBAC reconcile tests for create, idempotent update, provider deletion cleanup, selector scoping, bridge peer authorization, and denied unauthorized management calls.", "wave" : "W6" }, { @@ -13053,6 +13195,7 @@ "destinations" : [ "packages/d2b-provider-clipboard-wayland/src/service/audit.rs and packages/d2b-provider-clipboard-wayland/src/service/metrics.rs" ], + "detailedDesign" : "Implement ClipboardAuditEvent and fail-closed Zone audit queue by porting baseline audit code, renaming realm fields to source_zone_id and dest_zone_id, making ReasonCode a closed enum with unknown protobuf fields rejected, replacing exact byte counts with SizeBucket, emitting to d2b.audit.v3, and adding closed-semantic-label OTEL metrics and spans from the dossier tables. Metric descriptors carry no Zone/resource-name-derived identity; `d2b.zone` remains a resource attribute. Primary reuse disposition: `adapt`. Preserved source-plan detail: port and adapt audit plus resource-name-free metrics and redaction changes.", "entryContracts" : [ "ADR-046-provider-clipboard-wayland" ], @@ -13067,6 +13210,7 @@ ], "specId" : "ADR-046-provider-clipboard-wayland", "topologicalRank" : 13, + "validation" : "Audit tests for no bytes in events, closed ReasonCode deserialization, fail-closed queue rejection, SizeBucket discretization, excluded span attributes, and structural metric descriptor assertions for exact absence of `vm`, `zone`, `zone_id`, `zone_uid`, and resource-name-derived keys plus clipboard/Zone-name canary absence while preserving `d2b.zone` resource attributes.", "wave" : "W6" }, { @@ -13074,6 +13218,7 @@ "destinations" : [ "packages/d2b-provider-clipboard-wayland/tests/" ], + "detailedDesign" : "Create hermetic unit and Cargo integration tests covering closed MIME policy, secret-hint suppression, FD validation and bounds, LRU and TTL history, fail-closed audit, lifecycle purge and suspend, picker EphemeralProcess invariants, no filesystem bridge, core-created Processes, empty ProviderStateSet, no state mounts or state-layout principals, status-first observation, and no clipboard bytes in status, audit, metrics, Operations, or Volumes. Primary reuse disposition: `extract`. Preserved source-plan detail: extract semantic assertions into hermetic provider tests.", "entryContracts" : [ "ADR-046-provider-clipboard-wayland" ], @@ -13095,6 +13240,7 @@ ], "specId" : "ADR-046-provider-clipboard-wayland", "topologicalRank" : 14, + "validation" : "All tests listed in the Required test coverage table must pass under make test-rust -p d2b-provider-clipboard-wayland.", "wave" : "W6" }, { @@ -13102,6 +13248,7 @@ "destinations" : [ "packages/d2b-provider-clipboard-wayland/integration/" ], + "detailedDesign" : "Implement e2e paste, host capture, bridge backpressure, rate limiting, echo suppression, dependency absent host-only mode, GuestDestroyed purge, audit fail-closed, picker start timeout, and cross-zone denied scenarios using fake d2b.display.host-clipboard.v3 server and fake wayland-proxy bridge client without requiring a live compositor. Primary reuse disposition: `create`. Preserved source-plan detail: net-new integration suite with fake display-wayland and fake wayland-proxy.", "entryContracts" : [ "ADR-046-provider-clipboard-wayland" ], @@ -13116,6 +13263,7 @@ ], "specId" : "ADR-046-provider-clipboard-wayland", "topologicalRank" : 15, + "validation" : "All integration scenarios in the dossier table pass and assert no live Wayland compositor dependency.", "wave" : "W6" }, { @@ -13123,6 +13271,7 @@ "destinations" : [ "packages/d2b-contract-tests/tests/policy_clipboard.rs" ], + "detailedDesign" : "Add contract tests for d2b.clipboard.bridge.v3 and d2b.clipboard.picker-coord.v3 wire formats, ReasonCode numeric stability, and attachment class descriptor names while removing tests that assume shared filesystem bridge paths or SO_PEERCRED config. Primary reuse disposition: `adapt`. Preserved source-plan detail: adapt contract tests and delete obsolete filesystem bridge assumptions.", "entryContracts" : [ "ADR-046-provider-clipboard-wayland" ], @@ -13137,6 +13286,7 @@ ], "specId" : "ADR-046-provider-clipboard-wayland", "topologicalRank" : 13, + "validation" : "packages/d2b-contract-tests policy_clipboard.rs passes with v3 wire format and attachment descriptor assertions.", "wave" : "W6" }, { @@ -13144,6 +13294,7 @@ "destinations" : [ "nixos-modules/default.nix, docs/how-to/ migration guide, tests/static.sh example iteration expectations, CHANGELOG.md" ], + "detailedDesign" : "After the new Provider Nix module validates, delete nixos-modules/clipboard.nix, remove its default.nix import, update migration guide with the option mapping table, ensure tests/static.sh examples no longer rely on removed paths, and add an Unreleased changelog entry. Primary reuse disposition: `delete-after-cutover`. Preserved source-plan detail: delete superseded module and update migration docs.", "entryContracts" : [ "ADR-046-provider-clipboard-wayland" ], @@ -13158,6 +13309,7 @@ ], "specId" : "ADR-046-provider-clipboard-wayland", "topologicalRank" : 12, + "validation" : "New module eval tests pass and tests/static.sh example iteration has no references to removed option paths.", "wave" : "W6" }, { @@ -13165,6 +13317,7 @@ "destinations" : [ "`packages/d2b-core-controller/src/{main,configuration,api_catalog,authz,providers,controllers,ownership,watches,cleanup,zone_links,budgets,store}.rs`" ], + "detailedDesign" : "One fixed process, isolated handlers, async ResourceClient, health/startup/restart Primary reuse disposition: `adapt`. Preserved source-plan detail: extract and adapt.", "entryContracts" : [ "ADR-046-core-controllers" ], @@ -13178,6 +13331,7 @@ ], "specId" : "ADR-046-core-controllers", "topologicalRank" : 9, + "validation" : "Per-handler unit/property tests plus multi-process startup/restart", "wave" : "W4" }, { @@ -13185,6 +13339,7 @@ "destinations" : [ "`packages/d2b-core-controller/tests/system_core_coordination.rs`" ], + "detailedDesign" : "Coordination-only acceptance: prove the fixed core controller loads the manifest/audit boundary from ADR046-system-core-001 and routes Host/User reconciliation to the canonical ADR046-exec-003/004 handlers after ADR046-exec-005 bootstrap ordering. This item defines no schema, handler, status, capability, or `packages/d2b-provider-system-core/src/{host,user}.rs` destination. Primary reuse disposition: `adapt`. Preserved source-plan detail: adapt only the integration acceptance.", "entryContracts" : [ "ADR-046-core-controllers" ], @@ -13203,6 +13358,7 @@ ], "specId" : "ADR-046-core-controllers", "topologicalRank" : 14, + "validation" : "Coordination test asserts one Host handler owner, one User handler owner, manifest load after bootstrap ordering, and no duplicate Host/User destination or reconcile path in core-controller", "wave" : "W6" }, { @@ -13210,6 +13366,7 @@ "destinations" : [ "`packages/d2b-provider-credential-entra/src/{lib.rs,controller.rs,service.rs,controller_main.rs,agent_main.rs,audit.rs,telemetry.rs}` and corresponding tests/integration docs" ], + "detailedDesign" : "Implement secret-free controller/helper; require Credential base `identityGuestRef` and `loginEndpointRef`; resolve dependency alias `entra-login-token`; validate same-Zone Guest placement and exact `consumerRef`; implement typed `EntraTokenLeaseClient` whose production implementation is the Entrablau Endpoint; implement `BeginLogin`/`ObserveLogin`/`CancelLogin` projection to Credential base interaction status; route access-token leases end-to-end from Entrablau service to exact consumer over Noise_KK; keep all refresh/login/TPM state inside the Entrablau Guest; declare no Provider state Volume; reject Host placement and all ambient fallback chains. Primary reuse disposition: `adapt`. Preserved source-plan detail: Adapt tests/types where non-secret; replace production token acquisition with Entrablau Endpoint client.", "entryContracts" : [ "ADR-046-provider-credential-entra" ], @@ -13225,6 +13382,7 @@ ], "specId" : "ADR-046-provider-credential-entra", "topologicalRank" : 11, + "validation" : "See §13", "wave" : "W6" }, { @@ -13232,6 +13390,7 @@ "destinations" : [ "`packages/d2b-provider-credential-managed-identity/src/{lib.rs, controller.rs, agent.rs, service.rs, audit.rs, telemetry.rs}`; `packages/d2b-provider-credential-managed-identity/{controller/main.rs, agent/main.rs}`; `packages/d2b-provider-credential-managed-identity/tests/{lifecycle.rs, conformance.rs, faults.rs, canary.rs, delivery.rs, placement.rs, topology.rs}`; `packages/d2b-provider-credential-managed-identity/integration/{container-service.sh, host-guest-placement.nix, aca-credential-ref.sh, cleanup-rollback.sh}`; `packages/d2b-provider-credential-managed-identity/README.md`" ], + "detailedDesign" : "(1) Adapt `ManagedIdentityCredentialProvider` to `d2b.credential.v3` service interface; split controller and agent roles (see `ADR046-mi-topology-001`). (2) Enforce `ManagedIdentityCredentialOwner::ExactSdkConsumer` in agent via `AuthenticatedSubjectContext` from ComponentSession, independently of scope fields. (3) Reject `user-agent` placement: `scope.domainFilter=user` returns `credential-placement-mismatch` before agent spawn. (4) Validate `clientId` using `OpaqueAzureRef::parse` from v3 baseline; artifact IDs match `^[a-z][a-z0-9-]*$`. (5) Validate `imdsEndpointAlias` against closed enum `{azure-imds, azure-imds-aca}`; project into LaunchTicket at spawn time (never into Process spec config or env); co-located runtime Provider constructs `ManagedIdentityCredentialClient` from LaunchTicket projection and supplies via effect port; resolved URL never in any output surface. (6) Agent Process declares `networkUsage.allowEgress=false`; uses canonical Process template shape (see `ADR046-mi-topology-001` design item 6). (7) Reject `sign-challenge` with `credential-schema-invalid` immediately. (8) Map `ManagedIdentityClientState::Unavailable` to `credential-provider-unavailable`; no `InteractionRequired` state. (9) Implement `ManagedIdentityLeaseHandle` as opaque bounded newtype with redacted `Debug`. (10) All token bytes held by injected client in agent; delivered only via agent-terminated `Noise_KK` delivery session. (11) Integrate with Provider resource descriptor and controller toolkit. (12) Confirm `credential_canary` never appears in any service response, status field, delivery record outer header, or audit record. (13) Apply D087 status-first state: declare no Provider state Volume, keep ProviderStateSet empty, and write only bounded non-secret lease observation to `Credential.status` plus the Operation ledger. Primary reuse disposition: `adapt`. Preserved source-plan detail: copy and adapt.", "entryContracts" : [ "ADR-046-provider-credential-managed-identity" ], @@ -13249,6 +13408,7 @@ ], "specId" : "ADR-046-provider-credential-managed-identity", "topologicalRank" : 12, + "validation" : "See §Test matrix", "wave" : "W6" }, { @@ -13256,6 +13416,7 @@ "destinations" : [ "packages/d2b-provider-credential-managed-identity/src/controller.rs; packages/d2b-contracts/src/v3/credential_controller.rs" ], + "detailedDesign" : "Managed-identity-specific controller design: implement async reconcile and agent spawn/teardown from §Async reconcile; enforce system-only domain; spawn agent on Credential admission plus dependency-ready, not on `phase=Ready`; implement `observeInterval=30s` health-check RPC to the agent, which calls `InspectMetadata` on the injected client; controller never calls IMDS; derive idempotency key as `SHA-256(UID \\|\\| \":\" \\|\\| rotationGeneration.to_le_bytes() \\|\\| \":\" \\|\\| operation_class_byte)`; enforce `MAX_LOCAL_LEASES=256` in the resource store; implement Deleted-phase closure by clearing `provider-revoke` only after agent Process deletion and revocation confirmation while core/audit own Deleted revision and deletion record. Primary reuse disposition: `adapt`. Preserved source-plan detail: adapt shared Credential controller lifecycle to managed-identity controller/agent spawn and teardown.", "entryContracts" : [ "ADR-046-provider-credential-managed-identity" ], @@ -13274,6 +13435,7 @@ ], "specId" : "ADR-046-provider-credential-managed-identity", "topologicalRank" : 12, + "validation" : "Managed-identity reconcile/controller tests; shared Credential reconciliation tests; topology tests validating agent spawn/teardown and Deleted-phase cleanup", "wave" : "W6" }, { @@ -13281,6 +13443,7 @@ "destinations" : [ "nixos-modules/options-resources.nix; nixos-modules/activation-nixos-cleanup.nix; integration/aca-credential-ref.sh" ], + "detailedDesign" : "Shared Nix and cleanup: implement Nix eval-time assertions 1–12 from §Eval-time assertions, closed enum schema for `imdsEndpointAlias`, `clientId` validation via `OpaqueAzureRef` charset, generation cleanup contract, and artifact catalog validation for `credential-managed-identity-bin`. Integration fixture asserts that the migrated ACA Provider config carries `credentialRef: \"Credential/aca-relay-mi\"` and the raw `managed_identity_client_id` string field is absent from rendered Provider config JSON and ACA runtime bundle. Primary reuse disposition: `replace`. Preserved source-plan detail: replace raw ACA managed identity client-id fields with Credential resource references and v3 Provider/Credential Nix emission.", "entryContracts" : [ "ADR-046-provider-credential-managed-identity" ], @@ -13297,6 +13460,7 @@ ], "specId" : "ADR-046-provider-credential-managed-identity", "topologicalRank" : 13, + "validation" : "Nix eval assertion tests; artifact catalog validation tests; `integration/aca-credential-ref.sh`", "wave" : "W6" }, { @@ -13304,6 +13468,7 @@ "destinations" : [ "packages/d2b-provider-credential-managed-identity/src/{audit.rs,telemetry.rs}; packages/d2b-contract-tests/tests/credential_audit.rs" ], + "detailedDesign" : "Shared audit/OTEL: emit audit records for all methods and controller events per §Audit; emit OTEL spans and metrics per §OTEL and metrics with no Zone/Credential/resource-name-derived label; report expiry as the minimum for each provider/placement aggregate; add `d2b_credential_imds_calls_total` counter with bounded `alias` label; enforce `contains_sensitive_shape` on all string fields in audit records and metric labels; add canary tests for `managed-identity-canary`, `credential_canary`, `imds-endpoint-canary`, Credential `metadata.name`, and Zone name in `canary.rs`. Primary reuse disposition: `adapt`. Preserved source-plan detail: adapt existing sensitive-shape guard and canary pattern to v3 audit, OTEL, and metric surfaces.", "entryContracts" : [ "ADR-046-provider-credential-managed-identity" ], @@ -13319,6 +13484,7 @@ ], "specId" : "ADR-046-provider-credential-managed-identity", "topologicalRank" : 13, + "validation" : "`packages/d2b-contract-tests/tests/credential_audit.rs`; managed-identity `canary.rs`; audit/OTEL unit tests structurally assert exact absence of `vm`, `zone`, `zone_id`, `zone_uid`, `credential_name`, and every resource-name-derived label key, reject Credential/Zone-name label canaries, preserve allowed OTEL resource identity attributes, reject identity span attributes, and reject sensitive shapes", "wave" : "W6" }, { @@ -13326,6 +13492,7 @@ "destinations" : [ "packages/d2b-contracts/src/v3/credential.rs" ], + "detailedDesign" : "Contract types: define `CredentialSpec`, `CredentialStatus`, `CredentialLeaseHandle`, `OperationClass`, `PlacementBinding`, `CredentialConditionType`, and serde/validation/redaction helpers. Reuse `OpaqueAzureRef` from the v3 baseline. Full detail remains in `ADR-046-resources-credential` §Implementation work items. Primary reuse disposition: `adapt`. Preserved source-plan detail: adapt current credential/status concepts into v3 ResourceType DTOs; reuse `OpaqueAzureRef` directly where applicable.", "entryContracts" : [ "ADR-046-provider-credential-secret-service" ], @@ -13339,6 +13506,7 @@ ], "specId" : "ADR-046-provider-credential-secret-service", "topologicalRank" : 11, + "validation" : "Credential ResourceType schema/serde/redaction tests from `ADR-046-resources-credential`; credential-secret-service conformance consumes the shared types", "wave" : "W6" }, { @@ -13346,6 +13514,7 @@ "destinations" : [ "packages/d2b-contracts/proto/v3/credential.proto; packages/d2b-credential-service/" ], + "detailedDesign" : "Service proto: define the `d2b.credential.v3` protobuf/ttrpc service and generate typed client/server code. Full detail remains in `ADR-046-resources-credential` §Implementation work items. Primary reuse disposition: `create`. Preserved source-plan detail: net-new service contract replacing the v2 in-process `CredentialProvider` trait.", "entryContracts" : [ "ADR-046-provider-credential-secret-service" ], @@ -13359,6 +13528,7 @@ ], "specId" : "ADR-046-provider-credential-secret-service", "topologicalRank" : 11, + "validation" : "Generated-code compile tests and credential service contract tests from `ADR-046-resources-credential`; credential-secret-service lifecycle/delivery tests consume the generated service", "wave" : "W6" }, { @@ -13366,6 +13536,7 @@ "destinations" : [ "`packages/d2b-provider-credential-secret-service/src/{lib.rs, controller.rs, service.rs, main.rs}`; `packages/d2b-provider-credential-secret-service/tests/{lifecycle.rs, conformance.rs, faults.rs, canary.rs, delivery.rs, placement.rs}`; `packages/d2b-provider-credential-secret-service/integration/{container-service.sh, host-placement.nix, guest-placement.nix, cleanup-rollback.sh}`; `packages/d2b-provider-credential-secret-service/README.md`" ], + "detailedDesign" : "Adapt `SecretServiceCredentialProvider` and `SecretServiceCredentialProviderFactory` to v3 `d2b.credential.v3` service; replace v2 `CredentialProvider` trait with v3 controller/service handler; retain `Oo7SecretServicePort` trait methods unchanged; ensure `SecretServiceOwner::Userd` placement guard rejects system-domain and guest-agent construction; validate `collectionAlias` against provider-internal charset (not `OpaqueAzureRef`; collection aliases may include spaces); integrate with Provider resource descriptor and controller toolkit; test that `credential_canary` never appears in any service response; create a Process resource per `(Zone, User, executionRef)` triple with `template = \"secret-service-controller\"` (plain string), canonical `sandbox` fields (`namespaceClasses`, `capabilityClasses`, `seccompClass`, `noNewPrivileges`, `startRoot`, `environmentClass`, `readOnlyRoot`), `budget` with nested `cpu`/`memory`/`pids`/`fds` sub-fields, `networkUsage: null`, no inline endpoint fields, an owned credential-service `Endpoint` resource, and `readiness.class = \"provider-defined\"`; component descriptor declares the required authenticated `dbus-session` FD attachment carried privately by the LaunchTicket; D087 status-first state model: no Provider state Volume is declared, ProviderStateSet is optional/query-time and empty, no Volume mount or layout principal is required, and the storage-need test is not met; bounded non-secret lease/acquisition/retry observation lives in `Credential.status` plus the core Operation ledger; any opaque status handle is non-secret, non-authorizing, bounded, safe for authorized status readers, and independently revalidated; no token/object-path/lease bytes persist anywhere; finalize() emits revoke outcome audit but MUST NOT emit the resource-deleted closure audit (audit subsystem only); controller writes only scoped Credential/Process health (core aggregates Provider status) Primary reuse disposition: `adapt`. Preserved source-plan detail: copy and adapt.", "entryContracts" : [ "ADR-046-provider-credential-secret-service" ], @@ -13382,6 +13553,7 @@ ], "specId" : "ADR-046-provider-credential-secret-service", "topologicalRank" : 12, + "validation" : "See §16", "wave" : "W6" }, { @@ -13389,6 +13561,7 @@ "destinations" : [ "packages/d2b-provider-credential-/src/controller.rs" ], + "detailedDesign" : "Controller toolkit: implement the common Credential controller handler conforming to the `ADR-046-resource-reconciliation` async loop. Secret-service-specific controller code plugs into this handler while keeping provider bytes out of status/store/audit. Primary reuse disposition: `create`. Preserved source-plan detail: net-new shared controller handler pattern specialized by each Credential Provider.", "entryContracts" : [ "ADR-046-provider-credential-secret-service" ], @@ -13402,6 +13575,7 @@ ], "specId" : "ADR-046-provider-credential-secret-service", "topologicalRank" : 11, + "validation" : "Shared reconciliation tests from `ADR-046-resources-credential`; credential-secret-service lifecycle/fault tests verify the handler integration", "wave" : "W6" }, { @@ -13409,6 +13583,7 @@ "destinations" : [ "nixos-modules/options-resources.nix; nixos-modules/activation-nixos-cleanup.nix" ], + "detailedDesign" : "Nix compiler: implement `d2b.zones..resources.` authoring, eval-time assertions, canonical JSON emission, artifact catalog, bundle digest, and generation cleanup contract. Full detail remains in `ADR-046-resources-credential` §Implementation work items. Primary reuse disposition: `create`. Preserved source-plan detail: net-new Nix resource emission and cleanup contract.", "entryContracts" : [ "ADR-046-provider-credential-secret-service" ], @@ -13422,6 +13597,7 @@ ], "specId" : "ADR-046-provider-credential-secret-service", "topologicalRank" : 11, + "validation" : "Nix eval/assertion/golden tests from `ADR-046-resources-credential`; credential-secret-service cleanup rollback integration fixture", "wave" : "W6" }, { @@ -13429,6 +13605,7 @@ "destinations" : [ "packages/d2b-provider-credential-secret-service/src/{audit.rs,telemetry.rs}" ], + "detailedDesign" : "Audit/OTEL: emit audit records and OTEL spans/metrics for all credential service methods and controller events, with canary enforcement, expiry aggregated across user-agent leases, no Zone/Credential/resource-name-derived metric label, and no token/object-path/lease bytes in status, delivery outer headers, audit, metrics, spans, or logs. Full detail remains in `ADR-046-resources-credential` §Implementation work items. Primary reuse disposition: `adapt`. Preserved source-plan detail: adapt zero-secret invariant and canary test pattern to credential-secret-service audit/OTEL surfaces.", "entryContracts" : [ "ADR-046-provider-credential-secret-service" ], @@ -13442,6 +13619,7 @@ ], "specId" : "ADR-046-provider-credential-secret-service", "topologicalRank" : 11, + "validation" : "Credential audit/OTEL tests from `ADR-046-resources-credential`; `tests/canary.rs` structurally asserts exact absence of `vm`, `zone`, `zone_id`, `zone_uid`, `credential_name`, and every resource-name-derived metric key plus Credential/Zone-name label canary absence while preserving allowed OTEL resource identity attributes and rejecting identity span attributes; `tests/delivery.rs` for credential-secret-service", "wave" : "W6" }, { @@ -13449,6 +13627,7 @@ "destinations" : [ "`packages/d2b-contracts/src/v3/credential.rs`" ], + "detailedDesign" : "Define `CredentialSpec`, `CredentialStatus`, `CredentialLeaseHandle` (opaque bounded newtype), `CredentialRotationPolicy`, `CredentialRevocationPolicy`, `CredentialScope`, `OperationClass` enum, `CredentialLeaseState`, `PlacementBinding`, `CredentialConditionType`, and all serde/validation/redaction helpers; reuse `OpaqueAzureRef` from v3 baseline directly; enforce zero-secret-bytes charset validation on all string fields at construction Primary reuse disposition: `adapt`. Preserved source-plan detail: extract and adapt.", "entryContracts" : [ "ADR-046-resources-credential" ], @@ -13464,6 +13643,7 @@ ], "specId" : "ADR-046-resources-credential", "topologicalRank" : 9, + "validation" : "Schema golden vectors; charset/length tests; serde unknown-field rejection; `OpaqueAzureRef` round-trip and secret-shape rejection parity; `leaseHandle` and `sourceVersion` opaque newtype tests; status redaction tests", "wave" : "W4" }, { @@ -13471,6 +13651,7 @@ "destinations" : [ "`packages/d2b-contracts/proto/v3/credential.proto`; `packages/d2b-credential-service/src/{service.rs, client.rs, server.rs}`" ], + "detailedDesign" : "Define `d2b.credential.v3` protobuf service with methods: `Status`, `AcquireToken`, `RefreshToken`, `RevokeToken`, `SignChallenge`, `InspectMetadata`; each request carries `credential_ref`, `operation_class`, `operation_id`, `idempotency_key`, `requested_expiry_unix_ms`, `deadline_unix_ms`; `Status`, `RevokeToken`, and `InspectMetadata` responses carry only non-secret metadata (leaseHandle digest, rotationGeneration, sourceVersion, expiresAtUnixMs, state, outcome code); `AcquireToken`, `RefreshToken`, and `SignChallenge` responses additionally include a `delivery_session_params` field carrying the binding contract fields required to establish the end-to-end credential-delivery ComponentSession (see §Credential-delivery endpoint contract); the token bytes themselves travel in the separate Noise-encrypted delivery session, never in the outer DTO; strict unknown-field rejection; bounded message sizes; all record wrappers for delivery sessions must be zeroizing types", "entryContracts" : [ "ADR-046-resources-credential" ], @@ -13487,6 +13668,7 @@ ], "specId" : "ADR-046-resources-credential", "topologicalRank" : 10, + "validation" : "Protocol golden vectors for each method; malformed/oversize rejection; `leaseHandle` opacity tests (secret-canary must not appear in outer DTO or delivery routing metadata); locked/unavailable/denied/expired state tests; delivery session binding contract round-trip; zeroizing record type unit tests; delivery channel never materialized in non-delivery method tests", "wave" : "W4" }, { @@ -13494,6 +13676,7 @@ "destinations" : [ "`packages/d2b-provider-credential-secret-service/src/{lib.rs, controller.rs, service.rs, main.rs}` (implementation + binary); `packages/d2b-provider-credential-secret-service/tests/{lifecycle.rs, conformance.rs, faults.rs, canary.rs, delivery.rs, placement.rs}` (hermetic Cargo integration); `packages/d2b-provider-credential-secret-service/integration/{container-service.sh, host-placement.nix, cleanup-rollback.sh}` (orchestration fixtures); `packages/d2b-provider-credential-secret-service/README.md` (all §Provider README required sections)" ], + "detailedDesign" : "Adapt `SecretServiceCredentialProvider` and `SecretServiceCredentialProviderFactory` to use v3 `d2b.credential.v3` service interface; replace v2 `CredentialProvider` trait impl with v3 controller/service handler; adapt `Oo7SecretServicePort` trait (retain all methods unchanged); ensure `SecretServiceOwner::Userd` placement restriction rejects system-domain and guest-agent construction; validate `collectionAlias` against provider-internal charset (not `OpaqueAzureRef`; collection aliases may include spaces); integrate with Provider resource descriptor and controller toolkit; test `credential_canary` never appears in any service response Primary reuse disposition: `adapt`. Preserved source-plan detail: copy and adapt.", "entryContracts" : [ "ADR-046-resources-credential" ], @@ -13510,6 +13693,7 @@ ], "specId" : "ADR-046-resources-credential", "topologicalRank" : 11, + "validation" : "**`src/` unit** (`#[cfg(test)]` in `src/`): `Oo7SecretServicePort` trait API surface, `SecretServiceOwner` placement guard, `collectionAlias` charset, `lockPolicy` state transitions. **`tests/` Cargo integration** (`cargo test -p d2b-provider-credential-secret-service`): copied test suite from main with v3 type substitutions; add `lifecycle.rs` (acquire/refresh/revoke/inspect end-to-end with `FakeOo7Port`); `conformance.rs` (all 11 `check_provider_conformance` arms pass); `faults.rs` (locked state → `credential-provider-unavailable`, unavailable, cardinality limit); `canary.rs` (`credential_canary` and `object_path_canary` absent from every response, status field, and delivery record); `delivery.rs` (delivery-session binding contract, zeroizing buffer, replay-safe sequence); `placement.rs` (system-domain and guest-agent construction rejected). **`integration/` fixtures**: `container-service.sh` (container-backed Provider service start/stop/drain); `host-placement.nix` (user-domain Host/Process placement in runNixOSTest); `cleanup-rollback.sh` (Nix-generation removal triggers async Delete and Provider-revoke finalizer).", "wave" : "W4" }, { @@ -13517,6 +13701,7 @@ "destinations" : [ "`packages/d2b-provider-credential-entra/src/{lib.rs, controller.rs, service.rs, main.rs}`; `packages/d2b-provider-credential-entra/tests/{lifecycle.rs, conformance.rs, faults.rs, canary.rs, delivery.rs, placement.rs}`; `packages/d2b-provider-credential-entra/integration/{container-service.sh, guest-placement.nix, cleanup-rollback.sh}`; `packages/d2b-provider-credential-entra/README.md` (all §Provider README required sections)" ], + "detailedDesign" : "Adapt `EntraCredentialProvider` and `EntraCredentialProviderFactory` to v3 service; replace v2 `AgentPlacementBinding` with v3 `PlacementBinding` enum (user-agent, guest-agent only; reject host-system); validate `tenantId` config field using `OpaqueAzureRef::parse` from v3 baseline `d2b-realm-provider/src/credential.rs` (note: current v3 source field is named via `AzureControlPlaneRef`; target field name is `tenantId`); retain `EntraCredentialClient` trait unchanged; map `EntraClientError::InteractionRequired` to `credential-provider-unavailable` (not denied); enforce `EntraCredentialOwner::ExactConsumer` so only the declared `consumerRef` may acquire Primary reuse disposition: `adapt`. Preserved source-plan detail: copy and adapt.", "entryContracts" : [ "ADR-046-resources-credential" ], @@ -13533,6 +13718,7 @@ ], "specId" : "ADR-046-resources-credential", "topologicalRank" : 11, + "validation" : "**`src/` unit**: `EntraCredentialClient` trait API, `OpaqueAzureRef::parse` on `tenantId`, `EntraCredentialOwner::ExactConsumer` guard, `EntraClientState` transitions. **`tests/` Cargo integration**: `lifecycle.rs` (acquire/refresh/revoke/inspect with `FakeEntraClient`); `conformance.rs` (all conformance arms); `faults.rs` (interaction-required → unavailable, generation-mismatch, colocated-consumer rejection); `canary.rs` (`credential_canary` and `endpoint_canary` absent from every response and delivery record); `delivery.rs` (delivery-session binding, zeroizing, replay-safe); `placement.rs` (host-system placement rejected). **`integration/` fixtures**: `container-service.sh`; `guest-placement.nix` (user-domain and system-domain Process on Guest in runNixOSTest); `cleanup-rollback.sh`.", "wave" : "W4" }, { @@ -13540,6 +13726,7 @@ "destinations" : [ "`packages/d2b-provider-credential-managed-identity/src/{lib.rs, controller.rs, service.rs, main.rs}`; `packages/d2b-provider-credential-managed-identity/tests/{lifecycle.rs, conformance.rs, faults.rs, canary.rs, delivery.rs, placement.rs}`; `packages/d2b-provider-credential-managed-identity/integration/{container-service.sh, host-guest-placement.nix, aca-credential-ref.sh, cleanup-rollback.sh}`; `packages/d2b-provider-credential-managed-identity/README.md` (all §Provider README required sections)" ], + "detailedDesign" : "Adapt `ManagedIdentityCredentialProvider` to v3 service; enforce `ManagedIdentityCredentialOwner::ExactSdkConsumer`; reject user-agent placement (IMDS is machine-local, not user-session); validate `clientId` config using `OpaqueAzureRef::parse` directly from v3 baseline (note: current v3 source field is named via `ManagedIdentityRef.client_id`; target field name is `clientId`); retain `ManagedIdentityCredentialClient` trait unchanged; map `ManagedIdentityClientState::Unavailable` to `credential-provider-unavailable`; `sign-challenge` operation class returns schema-invalid immediately Primary reuse disposition: `adapt`. Preserved source-plan detail: copy and adapt.", "entryContracts" : [ "ADR-046-resources-credential" ], @@ -13556,6 +13743,7 @@ ], "specId" : "ADR-046-resources-credential", "topologicalRank" : 11, + "validation" : "**`src/` unit**: `ManagedIdentityCredentialClient` trait, `OpaqueAzureRef::parse` on `clientId`, `ManagedIdentityCredentialOwner::ExactSdkConsumer` guard, `imdsEndpointAlias` validation, `sign-challenge` schema-invalid fast path. **`tests/` Cargo integration**: `lifecycle.rs` (acquire/refresh/revoke/inspect with `FakeClient`); `conformance.rs`; `faults.rs` (unavailable state, colocated-consumer rejection); `canary.rs` (canary absent from all responses and delivery records); `delivery.rs`; `placement.rs` (user-agent placement rejected). **`integration/` fixtures**: `container-service.sh`; `host-guest-placement.nix` (system-domain Host and Guest placement in runNixOSTest); `aca-credential-ref.sh` (ACA Provider config uses `credentialRef`; raw `managed_identity_client_id` absent); `cleanup-rollback.sh`.", "wave" : "W4" }, { @@ -13563,6 +13751,7 @@ "destinations" : [ "`packages/d2b-provider-credential-/src/controller.rs`; `packages/d2b-contracts/src/v3/credential_controller.rs`" ], + "detailedDesign" : "Implement Credential controller handler conforming to `ADR-046-resource-reconciliation` async loop; implement `reconcile`, `observe`, `finalize`, `drain`, and `health` handlers; implement rotation state machine (proactive/on-expiry/on-demand policies); implement `provider-revoke` finalizer execution with `revocation.onOwnerDelete` policy; implement provider-generation-change detection and revocation; implement `CredentialReady`, `RotationDue`, `ProviderUnavailable`, `LeaseRevoked` condition logic; implement bounded idempotency key derivation (Credential UID + rotationGeneration + operation class, no secret material); implement `observeInterval=30s` health check calling `InspectMetadata`; bounded retry/backpressure with typed `credential-rotation-failed` outcome; enforce `MAX_LOCAL_LEASES=256` per controller provider instance", "entryContracts" : [ "ADR-046-resources-credential" ], @@ -13580,6 +13769,7 @@ ], "specId" : "ADR-046-resources-credential", "topologicalRank" : 11, + "validation" : "Controller state-machine golden vectors; rotation-policy matrix (proactive/on-demand/on-expiry × success/locked/unavailable/expired); finalizer execution tests; provider-generation-change revocation tests; idempotency key derivation tests; observe-interval drift detection test; canary tests confirm zero secret bytes in all controller-written status fields", "wave" : "W4" }, { @@ -13587,6 +13777,7 @@ "destinations" : [ "`nixos-modules/options-resources.nix` (generic schema-derived resource options; not type-specific), `nixos-modules/activation-nixos-cleanup.nix`" ], + "detailedDesign" : "**(1) Schema-derived options and eval-time validation**: implement `d2b.zones..resources. = { type = \"...\"; spec = { ... }; }` as a generic attrset option; an optional `metadata` sub-attr may contain `ownerRef` and/or presentation `labels`/`annotations`. Nix option types, defaults, and inline docs for `spec` fields are generated from the committed `ResourceTypeSchema` JSON (`docs/reference/schemas/v3/credential.json`) and the signed Provider schema — no bespoke options module is maintained separately. `metadata.name` derives from the attr key; `metadata.zone` from the Zone attr key; `apiVersion` defaults to `resources.d2bus.org/v3`; `status`/`uid`/`generation`/`revision`/timestamps/`managedBy`/`configurationGeneration` are not authored. Core assigns `metadata.managedBy = \"configuration\"` and `metadata.configurationGeneration = ` to all configuration-managed resources at create/update time; these are never authored in Nix. Eval-time assertions (applied to all entries with `type = \"Credential\"`): `spec.providerRef` resolves a Provider in the same Zone whose `credentialDomains` includes `spec.scope.domainFilter` and whose `spec.artifactId` (a sibling of `spec.config` on the Provider resource, not inside `spec.config`) resolves an artifact catalog entry of `type = \"provider\"`; `spec.audience` charset (`^[A-Za-z0-9._:/@-]+$`, max 256); `spec.rotation.proactiveWindowMs < maxLeaseLifetimeMs / 2`; `spec.consumerRef`/`scope.executionRef`/`scope.userRef` resolve declared Zone resources; duplicate `(providerRef, executionRef, userRef, audience)` tuple rejected; `contains_sensitive_shape` on all string fields; Provider-specific placement constraints; `allowedOperations` ⊆ `providerRef.supportedOperations`. **(2) Canonical JSON and bundle emission**: render `spec` attr directly to `spec` object in canonical JSON (no field renames/re-nesting); `metadata` in output contains only derived `name`/`zone` and optionally Nix-authored `ownerRef`/`labels`/`annotations`; `apiVersion` is top-level, not inside `metadata`; `finalizers` is omitted from the Nix-rendered input (core manages finalizers, never accepts them from the bundle); no management labels are emitted by Nix; sort bundle by `(type, name)`; write to `/etc/d2b/zones//resource-bundle.json` with digest. **(2b) Artifact catalog emission**: derivation-valued inputs (`d2b.artifacts.`) are compiled separately into an integrity-pinned artifact catalog (`/etc/d2b/zones//artifact-catalog.json`) with its own digest header; each entry records `id`, `type`, `sha256`, and bounded closure metadata; store paths are private catalog implementation data absent from the resource bundle, status, audit, and logs; `activation-nixos` verifies both digests before any create/update; missing or wrong-type `artifactId` references fail the NixOS build. **(3) Build-time schema validation**: validate rendered JSON against `docs/reference/schemas/v3/credential.json` and Provider-specific schema; enforce `secretRef` fields use `Credential/` refs; enforce no store paths in any resource bundle or status output; drift gate (`make test-drift`) regenerates schemas with `cargo xtask gen-schemas` and asserts `git diff --exit-code`; Nix options module drift checked in the same gate. **(4) Generation transition and cleanup contract**: activation-nixos controller verifies SHA-256 digest of both resource bundle and artifact catalog, creates/updates desired-set resources, activates without blocking on cleanup, issues async Delete for absent configuration-managed resources (those with `metadata.managedBy = \"configuration\"`), sets Degraded/Cleanup=True on removed resources; retains up to `retainedConfigurationMax` (default 3, range 1..16) prior bundles; oldest prune when count exceeded; no time-based rollback window. **(5) Configuration-managed vs controller/API isolation**: `managedBy` and `ownerRef` are orthogonal; configuration-managed and API-created resources may each carry an optional same-Zone `ownerRef` and participate in owner cascade. Cleanup checks `metadata.managedBy = \"configuration\"` before issuing Delete; resources with `metadata.managedBy = \"controller\"` or `metadata.managedBy = \"api\"` are never deleted by this path; API-created resources persist until explicit Delete and are never generation-swept.", "entryContracts" : [ "ADR-046-resources-credential" ], @@ -13604,6 +13795,7 @@ ], "specId" : "ADR-046-resources-credential", "topologicalRank" : 10, + "validation" : "**(eval/build)**: nix-unit golden JSON envelope for each example (spec shape, no management labels in Nix output, sort, digest); assertion-failure tests for secret-shaped audience, mismatched providerRef/domainFilter, proactiveWindow > half maxLifetime, duplicate binding tuple, unresolved refs; artifact catalog: assertion-failure for missing `artifactId`, wrong-type `artifactId`, duplicate catalog ID; bundle + artifact catalog digest round-trip; artifact catalog store-path absence from resource bundle and status; Provider-specific schema cross-check; `make test-drift` schema drift gate. **(runtime integration in `tests/host-integration/`)**: `credential-cleanup-basic` (removed resource reaches Deleted); `credential-cleanup-nonblocking` (activation Ready before cleanup finalizer finishes); `credential-cleanup-pending-status` (Cleanup=True on removed resource, PendingCleanup=True on Provider); `credential-cleanup-stalled` (Degraded stall detection and recovery); `credential-cleanup-controller-children-preserved` (ownerRef children cleaned by Credential controller); `credential-cleanup-no-dynamic-deletion` (controller-created Credential with `managedBy = \"controller\"` not deleted); `credential-retained-generation-count` (up to retainedConfigurationMax bundles retained; rollback re-creates from retained bundle; oldest pruned when count exceeded); `credential-bundle-digest-mismatch` (tampered bundle aborts activation).", "wave" : "W4" }, { @@ -13611,6 +13803,7 @@ "destinations" : [ "`packages/d2b-provider-credential-/src/audit.rs`, `telemetry.rs`; `packages/d2b-contract-tests/tests/credential_audit.rs`" ], + "detailedDesign" : "Implement audit record emission for all credential service methods and controller events using the field set defined in §Audit; implement OTEL span/metric emission using the closed semantic label set in §OTEL and metrics, with expiry reported as a provider/placement aggregate and no Credential/Zone/resource-name-derived label; implement `contains_sensitive_shape` check in all string fields of audit records and metric label values (adapted from `d2b-realm-provider/src/error.rs:contains_sensitive_shape`); add canary-enforcement tests that verify `\"secret-canary\"`, `\"entra-token-canary\"`, `\"managed-identity-canary\"`, Credential `metadata.name`, and Zone name values never appear in any metric label, span attribute, log line, or status field; identity remains only in allowed OTEL resource attributes and permitted audit fields", "entryContracts" : [ "ADR-046-resources-credential" ], @@ -13626,6 +13819,7 @@ ], "specId" : "ADR-046-resources-credential", "topologicalRank" : 12, + "validation" : "Canary tests across all three Provider crates; audit record field-presence tests; structural metric descriptor tests assert exact absence of `vm`, `zone`, `zone_id`, `zone_uid`, `credential_name`, and every resource-name-derived key plus Credential/Zone-name canary absence; resource attribute tests preserve allowed `d2b.zone`/`d2b.credential.name` identity while span attribute tests reject identity and forbidden fields", "wave" : "W4" }, { @@ -13633,6 +13827,7 @@ "destinations" : [ "`docs/specs/ADR-046-decision-register.md`" ], + "detailedDesign" : "Record every evidence-underdetermined choice before dependent spec work proceeds", "entryContracts" : [ "ADR-046-decision-register" ], @@ -13646,6 +13841,7 @@ ], "specId" : "ADR-046-decision-register", "topologicalRank" : 1, + "validation" : "Zero unresolved entries before pre-panel review; manifest/link consistency", "wave" : "W0" }, { @@ -13653,6 +13849,7 @@ "destinations" : [ "`packages/xtask/src/heavy_gate.rs`; `Makefile` targets `heavy-check`, `heavy-test-integration`, `heavy-test-host-integration`, `heavy-test-hardware`, `heavy-cargo-test`, `heavy-flake-check`" ], + "detailedDesign" : "Two-slot per-UID OFD-locked semaphore, 250 ms nonblocking retry up to 30 minutes, fail-closed on unsupported locking, duplicated locked-FD handoff to child, wrapper-owned group-signal/reap, as specified in §11 Primary reuse disposition: `adapt`. Preserved source-plan detail: copy-unchanged, then adapt paths/crate names to this repository's `packages/xtask` layout.", "entryContracts" : [ "ADR-046-validation-and-delivery" ], @@ -13666,6 +13863,7 @@ ], "specId" : "ADR-046-validation-and-delivery", "topologicalRank" : 13, + "validation" : "Unit tests for slot acquisition/timeout/fail-closed paths; integration test spawning two concurrent heavy-gate invocations and asserting the second blocks until the first releases", "wave" : "W7" }, { @@ -13673,6 +13871,7 @@ "destinations" : [ "`packages/xtask/src/delivery/snapshot.rs`" ], + "detailedDesign" : "Binds base/head OIDs, dependency graph, repository set into `candidate_id`/`content_id`/`snapshot_sha256` per §12.1 Primary reuse disposition: `adapt`. Preserved source-plan detail: copy-unchanged, then adapt.", "entryContracts" : [ "ADR-046-validation-and-delivery" ], @@ -13686,6 +13885,7 @@ ], "specId" : "ADR-046-validation-and-delivery", "topologicalRank" : 13, + "validation" : "Unit tests asserting identical inputs produce identical digests and any single-byte content change produces a different `content_id`", "wave" : "W7" }, { @@ -13693,6 +13893,7 @@ "destinations" : [ "`packages/xtask/src/delivery/validate_import.rs`; external candidate-ID-addressed evidence directory (never under Git)" ], + "detailedDesign" : "Imports CI/local/host validator command/result evidence, keyed by `candidate_id`, per §12.2 Primary reuse disposition: `adapt`. Preserved source-plan detail: copy-unchanged, then adapt.", "entryContracts" : [ "ADR-046-validation-and-delivery" ], @@ -13707,6 +13908,7 @@ ], "specId" : "ADR-046-validation-and-delivery", "topologicalRank" : 14, + "validation" : "Test asserting evidence for a stale `candidate_id` is rejected; test asserting raw command output never lands in a tracked file", "wave" : "W7" }, { @@ -13714,6 +13916,7 @@ "destinations" : [ "`packages/xtask/src/gen_spec_set.rs`; `docs/specs/ADR-046-spec-set.json`, `docs/specs/ADR-046-work-items.json`" ], + "detailedDesign" : "Enumerates every `docs/specs/ADR-046-*.md` and `docs/specs/providers/ADR-046-provider-*.md` file, its metadata table, bytewise-sorted `workItemPrefixes` registry, content digest, and every `### ADR046--` work item, per §8. Each prefix belongs globally to exactly one member, and generation resolves ownership only through the registry rather than by splitting IDs or filenames. Primary reuse disposition: `adapt`. Preserved source-plan detail: adapt (new generator, following the existing `xtask gen-schemas`/`gen-nix-options` pattern already used for other generated artifacts).", "entryContracts" : [ "ADR-046-validation-and-delivery" ], @@ -13728,6 +13931,7 @@ ], "specId" : "ADR-046-validation-and-delivery", "topologicalRank" : 14, + "validation" : "Golden-fixture test against a small synthetic spec directory; drift test against the real `docs/specs/` tree", "wave" : "W7" }, { @@ -13735,6 +13939,7 @@ "destinations" : [ "`packages/xtask/src/delivery/panel.rs`" ], + "detailedDesign" : "`panel-request` writes the candidate-bound request naming the exact ten roles and required model; `panel-attest` validates a directory of exactly ten strict 13-field records, rejecting wrong model/candidate binding, duplicate provider/run provenance, or inconsistent `signoff`/`recommendations`, per §12.3 Primary reuse disposition: `adapt`. Preserved source-plan detail: copy-unchanged, then adapt to bind the fixed `gemini-3.1-pro-preview`/`github-copilot` model/provider pair and this repository's existing ten-role roster (§12.3).", "entryContracts" : [ "ADR-046-validation-and-delivery" ], @@ -13750,6 +13955,7 @@ ], "specId" : "ADR-046-validation-and-delivery", "topologicalRank" : 15, + "validation" : "Unit tests for every rejection class (wrong model, missing role, duplicate run_id, `signoff:true` with non-empty `recommendations`); integration test with ten synthetic valid records passing", "wave" : "W7" }, { @@ -13757,6 +13963,7 @@ "destinations" : [ "`packages/xtask/src/delivery/{seal,eligibility,history_proof}.rs`" ], + "detailedDesign" : "`seal` requires all ten panel records unanimous and bound to the same candidate/content/snapshot digests plus every validator lane passing; `merge-eligibility` checks each stacked PR's current base/head against the sealed OIDs or a passing history-proof; `history_proof` verifies byte-identical integrated content/generated artifacts/dependency diff/repository set across a rebase, per §12.4/§12.6 Primary reuse disposition: `adapt`. Preserved source-plan detail: copy-unchanged, then adapt.", "entryContracts" : [ "ADR-046-validation-and-delivery" ], @@ -13773,6 +13980,7 @@ ], "specId" : "ADR-046-validation-and-delivery", "topologicalRank" : 16, + "validation" : "Unit tests for seal rejection on any missing/mismatched record; integration test proving a history-only rebase with identical content passes `history_proof` and reuses panel evidence, while any content change fails it", "wave" : "W7" }, { @@ -13780,6 +13988,7 @@ "destinations" : [ "`packages/xtask/src/test_runtime_ledger.rs`; a `make`-invokable timing gate reusing `make test-rust`/Layer-1 shard targets" ], + "detailedDesign" : "Measures execution-only time (after build, warm cache) per test/crate/shard against §10.16 budgets, records the reference runner/repetitions/p95, reports the top slow tests, applies a historical regression threshold, and emits a machine-readable CI artifact; the placement lint rejects a hermetic-tier test that sleeps, spawns a process, or touches network/containers/DBus/systemd/broker/Nix/KVM/hardware/live cloud, and the deterministic-clock/sleep lint rejects wall-clock sleep/retry in `src/`/`tests/`", "entryContracts" : [ "ADR-046-validation-and-delivery" ], @@ -13793,6 +14002,7 @@ ], "specId" : "ADR-046-validation-and-delivery", "topologicalRank" : 13, + "validation" : "Policy self-tests: an intentional slow/sleep/process/network hermetic test is rejected; a synthetic timing regression fails the gate; parallel isolation holds under shuffled/parallel execution; a retired legacy selector is absent from `tests/layer1-jobs.json`, closed gate manifests, and CI shards", "wave" : "W7" }, { @@ -13800,6 +14010,7 @@ "destinations" : [ "`docs/specs/ADR-046-implementation-graph.json`, `docs/specs/ADR-046-implementation-graph.md` (generated by `ADR046-streamline-001`'s `xtask implementation-graph`); the artifact contract, generation, validation, and ready-wave query are owned by §3.5 of this spec" ], + "detailedDesign" : "Owns the D095 implementation-graph contract: `artifactKind`/`schemaVersion`/`adr`/`status`; one node per member spec and per work item mapped exactly once to a `W0`–`W7` wave and a file-disjoint `parallelGroup`, with `owner`/`destinations`/`entryContracts`/`prerequisites`/`blockers`/`exitGate`/`topologicalRank`; work-item nodes additionally embed the manifest's exact `detailedDesign` and `validation` text byte-for-byte; typed `spec-depends-on`/`shared-contract`/`work-item-depends-on`/`implements-spec`/`file-overlap-order` edges; the §3.5.1 ready-wave query; and the anti-serialization invariant that every ready file-disjoint group launches concurrently while a same-wave dependency is a prep barrier, not whole-wave serialization. The graph is a generated non-member artifact and does not change the 55-member `ADR-046-spec-set.json` count. Primary reuse disposition: `adapt`. Preserved source-plan detail: net-new (D095 artifact contract).", "entryContracts" : [ "ADR-046-validation-and-delivery" ], @@ -13814,6 +14025,7 @@ ], "specId" : "ADR-046-validation-and-delivery", "topologicalRank" : 13, + "validation" : "Every 55 spec node and every work item present exactly once; all edge endpoints resolve; graph acyclic; waves monotonic (dependencies earlier or explicit same-wave prep barrier); parallel groups claim no ordering absent a dependency/file-overlap edge; deterministic JSON with no timestamps/host paths; every Mermaid node ID valid; the ready-wave query returns the expected concurrently-launchable groups on a seeded fixture", "wave" : "W7" }, { @@ -13821,6 +14033,7 @@ "destinations" : [ "`packages/xtask/src/gen_spec_set.rs`; `packages/d2b-contract-tests/tests/policy_adr046_work_items.rs`; generated spec-set, work-item, and implementation-graph drift checks" ], + "detailedDesign" : "Parse every normative member's exact level-three `### ADR046--` headings and tables; `##` or `####` item declarations are invalid. Require an exact Markdown/manifest bijection; exact `specId` and `specPath`; a bytewise-sorted, nonempty `workItemPrefixes` list for each item-owning member; global one-member ownership for every registered prefix; registry-based ID ownership; three-digit nonzero ordinals; every mandatory field exactly once and nonempty; one closed scalar `reuseAction`; and `reuseSource: null` for `create`. Reject dropped, extra, malformed, wrong-level, duplicate, ambiguous, heuristic-split, unregistered-prefix, or unconsumed items before writing any artifact. Validate all dependency endpoints, DAG acyclicity, wave monotonicity, and single-wave parallel groups before atomically publishing all generated files.", "entryContracts" : [ "ADR-046-validation-and-delivery" ], @@ -13836,6 +14049,7 @@ ], "specId" : "ADR-046-validation-and-delivery", "topologicalRank" : 15, + "validation" : "Fixtures fail for a dropped heading, `##`/`####` item heading, extra manifest row, duplicate ID, duplicate cross-member prefix, unsorted/empty required prefix registry, wrong owner/path/prefix, heuristic-only prefix match, two-digit/zero ordinal, missing/duplicate mandatory field, free-form/compound action, `create` with a reuse source, dangling dependency, cyclic DAG, backward-wave dependency, and cross-wave parallel group; the exact 55-spec real tree passes with every item once", "wave" : "W7" }, { @@ -13843,6 +14057,7 @@ "destinations" : [ "`packages/d2b-contracts/src/v3/device.rs`" ], + "detailedDesign" : "Device ResourceType schema (spec/status/conditions/claims/inventory); closed-set error codes; Device RBAC verbs; broker operation effect-limit constants; shared Device telemetry contract requires fixed semantic metric labels with no Zone/resource-name-derived identity and retains `d2b.zone`/`d2b.provider` only as OTEL resource attributes. Primary reuse disposition: `adapt`. Preserved source-plan detail: extract and adapt.", "entryContracts" : [ "ADR-046-resources-device" ], @@ -13856,6 +14071,7 @@ ], "specId" : "ADR-046-resources-device", "topologicalRank" : 10, + "validation" : "Schema golden vectors; unknown-field denial; exclusive/shared conflict rejection; arbitration/maxClaims invariant; cross-Provider structural descriptor test asserts exact absence of `vm`, `zone`, `zone_id`, `zone_uid`, and resource-name-derived keys plus Device/Zone-name canary absence while preserving `d2b.zone` resource attributes", "wave" : "W5" }, { @@ -13863,6 +14079,7 @@ "destinations" : [ "`packages/d2b-provider-device-tpm/src/` (controller, swtpm runner, state-dir logic); `packages/d2b-provider-device-tpm/tests/` (hermetic Cargo integration); `packages/d2b-provider-device-tpm/integration/` (container/Host scenarios); `packages/d2b-provider-device-tpm/README.md`" ], + "detailedDesign" : "Device spec/status; flush EphemeralProcess → swtpm Process sequencing; state-dir hardening; tamper-marker; finalizer non-deletion of Volume; Nix emitter; all four required crate paths present (see \"Provider crate layout\") Primary reuse disposition: `adapt`. Preserved source-plan detail: extract and adapt.", "entryContracts" : [ "ADR-046-resources-device" ], @@ -13877,6 +14094,7 @@ ], "specId" : "ADR-046-resources-device", "topologicalRank" : 11, + "validation" : "`src/`: swtpm argv golden, state-dir, flush sequencing, finalizer no-delete; `tests/`: `controller_state_machine.rs`, `conformance.rs`, `fault_swtpm_missing.rs`; `integration/`: `provision_and_reboot/`, `tamper_marker_survives/`, `finalizer_no_delete/`; workspace policy check: `make test-policy` passes with all four paths present", "wave" : "W5" }, { @@ -13884,6 +14102,7 @@ "destinations" : [ "`packages/d2b-provider-device-usbip/src/` (controller, daemon Process, bind/unbind EphemeralProcess, firewall); `packages/d2b-provider-device-usbip/tests/` (hermetic Cargo integration); `packages/d2b-provider-device-usbip/integration/` (container/Host scenarios); `packages/d2b-provider-device-usbip/README.md`" ], + "detailedDesign" : "Device spec/status; bus ID validation; firewall rule ownership-marker; bind/unbind EphemeralProcess; per-Device daemon Process (owned by device-usbip; Network supplies dependency/firewall interface); Nix emitter; all four required crate paths present (see \"Provider crate layout\") Primary reuse disposition: `adapt`. Preserved source-plan detail: extract and adapt.", "entryContracts" : [ "ADR-046-resources-device" ], @@ -13898,6 +14117,7 @@ ], "specId" : "ADR-046-resources-device", "topologicalRank" : 11, + "validation" : "`src/`: bus ID corpus, firewall marker format, EphemeralProcess creation; `tests/`: `arbitration_conflict.rs`, `conformance.rs`, `firewall_marker.rs`, `explicit_attach_split.rs`; `integration/`: `arbitration_conflict/`, `busid_bind_cycle/`, `network_firewall_coexistence/`; workspace policy check: `make test-policy` passes with all four paths present", "wave" : "W5" }, { @@ -13905,6 +14125,7 @@ "destinations" : [ "`packages/d2b-provider-device-security-key/src/` (controller, relay Process, guest frontend Process, lease/session ring); `packages/d2b-provider-device-security-key/tests/` (hermetic Cargo integration); `packages/d2b-provider-device-security-key/integration/` (container/Host/Guest scenarios); `packages/d2b-provider-device-security-key/README.md`" ], + "detailedDesign" : "Device spec/status; unprivileged relay Process (`device--sk-relay`); guest frontend Process (`device--sk-frontend`, `executionRef: Guest/`); ceremony/CID/lease/session ring (max 1 session per Device); broker hidraw-only access; mandatory Core-derived `(Host, physical-usb-backing, opaqueKeyDigest)` claim shared with every USB Provider before effects; Nix emitter; all four required crate paths present (see \"Provider crate layout\") Primary reuse disposition: `adapt`. Preserved source-plan detail: extract and adapt.", "entryContracts" : [ "ADR-046-resources-device" ], @@ -13919,6 +14140,7 @@ ], "specId" : "ADR-046-resources-device", "topologicalRank" : 11, + "validation" : "`src/`: lease transitions, session ring eviction, broker op path-free, CID round-trip; `tests/`: `lease_state_machine.rs`, `session_ring.rs`, `mutual_exclusion.rs` proves security-key and USB implementations resolve one token to a byte-identical physical backing key and the loser receives `physical-usb-backing-conflict` before any effect, `conformance.rs`, `guest_frontend_process.rs`; `integration/`: `lease_acquire_cancel/`, `session_ring_capacity/`, `guest_frontend_connect/`; workspace policy check: `make test-policy` passes with all four paths present", "wave" : "W5" }, { @@ -13926,6 +14148,7 @@ "destinations" : [ "`packages/d2b-provider-device-gpu/src/` (controller, GPU/render-node/video worker Processes, broker token set); `packages/d2b-provider-device-gpu/tests/` (hermetic Cargo integration); `packages/d2b-provider-device-gpu/integration/` (container/Host/Guest scenarios); `packages/d2b-provider-device-gpu/README.md`" ], + "detailedDesign" : "Combined Device spec/status; GPU worker Process (`device--gpu`, exclusive); render-node Process (`device--render-node`, exclusive default, shared when explicit); video-decoder Process (`device--video`); broker token set; wire-contract constants; shared render-node arbitration enforcement; Nix emitter; all four required crate paths present (see \"Provider crate layout\") Primary reuse disposition: `adapt`. Preserved source-plan detail: extract and adapt.", "entryContracts" : [ "ADR-046-resources-device" ], @@ -13940,6 +14163,7 @@ ], "specId" : "ADR-046-resources-device", "topologicalRank" : 11, + "validation" : "`src/`: process role selection, wire-constant snapshot, render-node vs full-GPU branching; `tests/`: `combined_reconcile.rs`, `render_node_enforcement.rs`, `wire_constant_snapshot.rs`, `conformance.rs`; `integration/`: `gpu_worker_start/`, `render_node_shared/`, `video_dependency/`; workspace policy check: `make test-policy` passes with all four paths present", "wave" : "W5" }, { @@ -13947,6 +14171,7 @@ "destinations" : [ "`nixos-modules/resources-device.nix`; `nixos-modules/bundle-artifacts.nix` (bundle emission for resource store); `nixos-modules/assertions.nix` (six eval-time device assertions)" ], + "detailedDesign" : "Nix authoring shape `d2b.zones..resources. = { type = \"Device\"; metadata.ownerRef = ...; spec = { ...exact ResourceSpec fields... }; };` as specified in \"Nix configuration\" section; `metadata.name`/`metadata.zone`/`apiVersion` derived automatically; `status` and Core management fields (`managedBy`, `configurationGeneration`, uid, generation, revision, timestamps, finalizers) omitted from emitted JSON; `spec` field names/types/defaults identical to ResourceTypeSchema with no renaming; per-Provider `spec.provider.settings` validated against signed Provider schema; no `artifactId` or store-path fields in Device `spec.provider.settings` — binary paths are resolved from Provider package closures; Credential-ref enforcement; artifact catalog emitted as a separate private integrity-pinned map (ID→type/digest/closure) by its own emitter; six eval-time validation assertions; canonical sorted-key full resource-envelope JSON emission (`apiVersion`, `type`, `metadata`, `spec` only); Zone resource bundle with `contentDigest` as specified in \"Zone resource bundle/generation\" section; Core sets `metadata.managedBy=configuration` and `metadata.configurationGeneration` at activation", "entryContracts" : [ "ADR-046-resources-device" ], @@ -13965,6 +14190,7 @@ ], "specId" : "ADR-046-resources-device", "topologicalRank" : 12, + "validation" : "nix-unit: `device-tpm-eval.nix`, `device-usbip-eval.nix`, `device-security-key-eval.nix`, `device-gpu-eval.nix`, `device-schema-validation.nix`, `device-gen-cleanup-eval.nix`, `device-bundle-canonical.nix`, `device-inline-secret-rejected.nix`, `device-artifact-catalog.nix`; contract tests: `device_resource_schema.rs`, `device_bundle_canonical.rs`, `device_gen_cleanup.rs`", "wave" : "W5" }, { @@ -13972,6 +14198,7 @@ "destinations" : [ "`packages/d2b-core-controller/src/configuration.rs`; `packages/d2b-contract-tests/tests/device_gen_cleanup.rs`" ], + "detailedDesign" : "Implement the cleanup contract described in \"Zone generation and cleanup\": (1) on new generation activation, diff `resources` against prior generation's `resources` by (type, name) — resources absent from new generation that have `managedBy=configuration` go into `pendingDeletion`; (2) Zone phase transitions to `Degraded/pending-cleanup` until all items in `pendingDeletion` reach terminal Delete; (3) items in `pendingDeletion` with `managedBy=controller` or `managedBy=api` are rejected with `cleanup-config-ownership-mismatch`; (4) `managedBy=controller` and `managedBy=api` resources are never touched by generation cleanup — `cleanup-controller-resource-protected` emitted if attempted; `managedBy=api` resources persist until explicit Delete; (5) prior generations retained by count: default 3, range 1..16, configured via `d2b.zones..retainedGenerations`; (6) each deletion is non-blocking; (7) finalizer-stuck timeout emits `cleanup-finalizer-stuck` and leaves Zone in `Degraded/pending-cleanup`; (8) all deletions emit `config-resource-deletion-requested` audit record with digested resource identity", "entryContracts" : [ "ADR-046-resources-device" ], @@ -13987,6 +14214,7 @@ ], "specId" : "ADR-046-resources-device", "topologicalRank" : 13, + "validation" : "Fast hermetic (D094): in-process Rust selectors under the owning crate `tests/` — `packages/d2b-core-controller/tests/gen_cleanup.rs`, `packages/d2b-core-controller/tests/gen_cleanup_controller_protected.rs`, `packages/d2b-core-controller/tests/gen_cleanup_audit.rs` — driven by a fake Zone store/clock with deterministic ordering and no wall-clock sleep; slower coverage reuses the existing integration directory scenarios `tests/integration/containers/device-gen-cleanup.sh`, `device-controller-resource-protected.sh`, and `device-gen-cleanup-audit.sh`; feasibility proof: generation cleanup with fake Zone runtime. No new top-level shell gate is added.", "wave" : "W5" }, { @@ -13994,6 +14222,7 @@ "destinations" : [ "`packages/xtask/src/main.rs` (`check-provider-layout` subcommand); `packages/d2b-contract-tests/tests/workspace_policy.rs` (provider-layout policy assertions)" ], + "detailedDesign" : "Add `cargo xtask check-provider-layout`: enumerate workspace members matching `d2b-provider-*`; for each, assert `src/`, `tests/`, `integration/`, and `README.md` all exist relative to the crate root; report all missing paths before failing; no opt-out flag. Add companion test in `workspace_policy.rs` that asserts the same invariant against the static crate list in `Cargo.toml`. Wire `cargo xtask check-provider-layout` into `make test-policy` alongside existing workspace naming and sort checks. The check must also run in CI as part of the policy gate. Primary reuse disposition: `adapt`. Preserved source-plan detail: extend.", "entryContracts" : [ "ADR-046-resources-device" ], @@ -14011,6 +14240,7 @@ ], "specId" : "ADR-046-resources-device", "topologicalRank" : 12, + "validation" : "Policy gate passes once all four Device Provider crates exist with required paths; gate fails predictably when any path is removed from a Provider crate fixture; test fixture crate with one missing path must produce a named error identifying the exact missing path", "wave" : "W5" }, { @@ -14018,6 +14248,7 @@ "destinations" : [ "packages/d2b-provider-device-tpm/{src/,tests/,integration/README.md,README.md}; Cargo workspace membership" ], + "detailedDesign" : "Crate scaffold: create `packages/d2b-provider-device-tpm/` with `src/`, `tests/`, `integration/README.md`, and `README.md`; add it to the Cargo workspace; workspace policy test must pass. Primary reuse disposition: `adapt`. Preserved source-plan detail: net-new crate scaffold; later items adapt baseline TPM behavior into the new Provider boundary.", "entryContracts" : [ "ADR-046-provider-device-tpm" ], @@ -14031,6 +14262,7 @@ ], "specId" : "ADR-046-provider-device-tpm", "topologicalRank" : 11, + "validation" : "Workspace policy test for required crate paths and Cargo workspace membership", "wave" : "W6" }, { @@ -14038,6 +14270,7 @@ "destinations" : [ "packages/d2b-provider-device-tpm/src/{effect_port.rs,effect_impl.rs}; packages/d2b-provider-device-tpm/tests/effect_fake.rs" ], + "detailedDesign" : "TpmEffectPort and FakeTpmEffectPort: implement the effect trait, typed TPM EndpointRef handoff, and fake test port. Prove non-test files contain no `use d2b_priv_broker::` and the controller sees only opaque resource IDs and EndpointRefs. Primary reuse disposition: `wrap`. Preserved source-plan detail: wrap privileged effects behind an injected async `TpmEffectPort`; keep broker operations only behind `volume-local` and `system-minijail`.", "entryContracts" : [ "ADR-046-provider-device-tpm" ], @@ -14052,6 +14285,7 @@ ], "specId" : "ADR-046-provider-device-tpm", "topologicalRank" : 12, + "validation" : "`tests/effect_fake.rs`; static proof that non-test files do not import `d2b_priv_broker`", "wave" : "W6" }, { @@ -14059,6 +14293,7 @@ "destinations" : [ "packages/d2b-provider-device-tpm/src/controller.rs; packages/d2b-provider-device-tpm/tests/controller_fsm.rs" ], + "detailedDesign" : "Controller reconcile state machine: implement the Device reconcile algorithm from §11.1 against `FakeTpmEffectPort`, covering happy path, Volume not-ready, marker fail-closed, flush failure, swtpm maxRestarts, and finalizer behavior where Process is deleted and Volume retained; emit §14 metrics with only closed `phase`/`outcome`/`status` semantics and no Zone/resource-name-derived label. Primary reuse disposition: `replace`. Preserved source-plan detail: replace direct daemon lifecycle with Provider reconcile against `FakeTpmEffectPort` and resource status.", "entryContracts" : [ "ADR-046-provider-device-tpm" ], @@ -14073,6 +14308,7 @@ ], "specId" : "ADR-046-provider-device-tpm", "topologicalRank" : 13, + "validation" : "`tests/controller_fsm.rs` covering happy path, Volume not-ready, marker fail-closed, flush failed, swtpm maxRestarts, and finalizer behavior; `tests/metrics_labels.rs` structurally asserts exact absence of `vm`, `zone`, `zone_id`, `zone_uid`, and resource-name-derived keys and Device/Zone-name canary absence", "wave" : "W6" }, { @@ -14080,6 +14316,7 @@ "destinations" : [ "packages/d2b-provider-device-tpm/src/resources.rs; packages/d2b-provider-device-tpm/tests/volume_create.rs" ], + "detailedDesign" : "Controller-created Volume spec: implement `build_tpm_state_volume_spec` with `cleanupPolicy: never`, `repairPolicy: fail-closed`, `adoptionPolicy: quarantine-on-ambiguity`, `sensitivity: secret-adjacent`, required invariants, `source.sourceId`, no `hostPath`, no top-level identityMarker/persistenceClass/quotaBytes/stateSchema, `ownerRef: Device/`, `managedBy: controller`, empty attachments, and `quota: null`. Primary reuse disposition: `replace`. Preserved source-plan detail: replace VM-level TPM option/state path with controller-created Device-owned Volume spec.", "entryContracts" : [ "ADR-046-provider-device-tpm" ], @@ -14094,6 +14331,7 @@ ], "specId" : "ADR-046-provider-device-tpm", "topologicalRank" : 12, + "validation" : "`tests/volume_create.rs` proving every canonical Volume field and forbidden field listed in this item", "wave" : "W6" }, { @@ -14101,6 +14339,7 @@ "destinations" : [ "packages/d2b-provider-device-tpm/src/resources.rs; Process spec tests under packages/d2b-provider-device-tpm/tests/" ], + "detailedDesign" : "Canonical swtpm Process spec: implement `build_swtpm_process_spec` with `readOnlyRoot: true`, `userNamespace.mappingClass: process-principal-root`, namespace classes `[pid, mount, user]`, empty capability classes, `seccompClass: w1-swtpm`, two Device-owned Endpoint resources (`tpm` and `ctrl`), `mounts[0].required: true`, and no socket path, binary path, UID integer, or GID integer in any spec field. Primary reuse disposition: `adapt`. Preserved source-plan detail: extract swtpm argv/sandbox intent into canonical Process resources; remove caller-supplied binary path, UID, GID, and socket path fields.", "entryContracts" : [ "ADR-046-provider-device-tpm" ], @@ -14115,6 +14354,7 @@ ], "specId" : "ADR-046-provider-device-tpm", "topologicalRank" : 13, + "validation" : "Process spec golden tests proving all required and forbidden fields; preserved `minijail_swtpm_video.rs` contract tests", "wave" : "W6" }, { @@ -14122,6 +14362,7 @@ "destinations" : [ "packages/d2b-provider-device-tpm/src/resources.rs; packages/d2b-provider-device-tpm/tests/flush_mandatory.rs" ], + "detailedDesign" : "Mandatory flush EphemeralProcess spec: implement `build_flush_ephemeral_process_spec`; no `startupClear` field exists; flush is always created before swtpm Process start with no skip path; TTLs are `successfulTtl: \"1h\"` and `failedTtl: \"24h\"`; no userNamespace on flush Process; deadlines are `startDeadline: \"30s\"` and `runtimeDeadline: \"60s\"`. Primary reuse disposition: `adapt`. Preserved source-plan detail: extract flush intent into mandatory EphemeralProcess resource; remove configurable `startupClear` path and caller-supplied binary path fields.", "entryContracts" : [ "ADR-046-provider-device-tpm" ], @@ -14136,6 +14377,7 @@ ], "specId" : "ADR-046-provider-device-tpm", "topologicalRank" : 14, + "validation" : "`tests/flush_mandatory.rs` plus contract proof that user-NS is long-lived only", "wave" : "W6" }, { @@ -14143,6 +14385,7 @@ "destinations" : [ "packages/d2b-provider-device-tpm/src/status.rs; packages/d2b-provider-device-tpm/tests/{endpoint_ref.rs,redaction.rs}" ], + "detailedDesign" : "Device status builder: implement `build_device_status`; `tpmEndpointRef` is an `Endpoint/` ResourceRef with no opaque endpoint ID compatibility alias and never a filesystem path; `stateVolumeRef` and `swtpmProcessRef` are canonical ResourceRef strings; no path, socket name, UID, GID, PID, or pidfd appears in status; `markerStatus` is one of `verified`, `missing`, `replaced`, or `unknown`. Primary reuse disposition: `create`. Preserved source-plan detail: net-new bounded Device status projection; do not reuse path/socket/UID/GID/PID observations.", "entryContracts" : [ "ADR-046-provider-device-tpm" ], @@ -14157,6 +14400,7 @@ ], "specId" : "ADR-046-provider-device-tpm", "topologicalRank" : 14, + "validation" : "`tests/endpoint_ref.rs`; `tests/redaction.rs`; status builder tests for allowed `markerStatus` values", "wave" : "W6" }, { @@ -14164,6 +14408,7 @@ "destinations" : [ "packages/d2b-provider-device-tpm/src/{effect_port.rs,status.rs}; packages/d2b-provider-device-tpm/integration/guest_endpoint.rs" ], + "detailedDesign" : "EndpointRef handoff: hermetic tests prove `tpmEndpointRef` is an EndpointRef and never a path; integration proves Guest runtime Provider reads `tpmEndpointRef` and obtains the socket fd from the Zone runtime endpoint resolver with no path string in Guest spec or LaunchTicket API surface. Primary reuse disposition: `replace`. Preserved source-plan detail: replace path handoff with EndpointRef and Zone runtime endpoint resolver fd acquisition.", "entryContracts" : [ "ADR-046-provider-device-tpm" ], @@ -14178,6 +14423,7 @@ ], "specId" : "ADR-046-provider-device-tpm", "topologicalRank" : 15, + "validation" : "`tests/endpoint_ref.rs`; `integration/guest_endpoint.rs`", "wave" : "W6" }, { @@ -14185,6 +14431,7 @@ "destinations" : [ "packages/d2b-provider-device-tpm/tests/marker_fail_closed.rs; packages/d2b-provider-device-tpm/integration/marker_tamper.rs" ], + "detailedDesign" : "Marker fail-closed test: FakeTpmEffectPort returning `markerStatus: replaced` makes Device Failed, prevents a second `ensure_state_volume` call, and prevents swtpm Process creation. Integration physically replaces `swtpm/`; volume-local sets Volume Failed; Device fails with no auto-recovery. Primary reuse disposition: `adapt`. Preserved source-plan detail: preserve fail-closed marker semantics while moving ownership to Volume/Device resources.", "entryContracts" : [ "ADR-046-provider-device-tpm" ], @@ -14199,6 +14446,7 @@ ], "specId" : "ADR-046-provider-device-tpm", "topologicalRank" : 13, + "validation" : "`tests/marker_fail_closed.rs`; `integration/marker_tamper.rs`", "wave" : "W6" }, { @@ -14206,6 +14454,7 @@ "destinations" : [ "packages/d2b-provider-device-tpm/src/resources.rs; packages/d2b-provider-device-tpm/tests/controller_process.rs" ], + "detailedDesign" : "Controller Process (status-first; no Provider state Volume): implement the controller Process spec from §4.1 with `processClass: controller`, `readOnlyRoot: true`, empty mounts, no controller-scratch namespace, no scratch mount, no `User/device-tpm-controller-system` state-layout principal, no permission to create Provider-owned Volumes, and restart re-derivation from resource store plus external marker/process observations while treating status as observation. Primary reuse disposition: `create`. Preserved source-plan detail: net-new status-first controller Process spec; no Provider state Volume is reused or created.", "entryContracts" : [ "ADR-046-provider-device-tpm" ], @@ -14220,6 +14469,7 @@ ], "specId" : "ADR-046-provider-device-tpm", "topologicalRank" : 12, + "validation" : "Controller Process spec tests proving the bullets in this item", "wave" : "W6" }, { @@ -14227,6 +14477,7 @@ "destinations" : [ "nixos-modules/options-resources.nix and Nix eval/golden tests for §17.1 Device JSON" ], + "detailedDesign" : "Nix roundtrip test: Device Nix spec from §17.1 round-trips through the Nix emitter to expected resource JSON; emitted bundle contains no Volume, Process, or EphemeralProcess resources because controller-managed resources are not in the Nix bundle. Primary reuse disposition: `replace`. Preserved source-plan detail: replace VM-level TPM enable option with v3 Device resource emission and assertions.", "entryContracts" : [ "ADR-046-provider-device-tpm" ], @@ -14241,6 +14492,7 @@ ], "specId" : "ADR-046-provider-device-tpm", "topologicalRank" : 12, + "validation" : "Nix roundtrip/golden test for §17.1 and emitted-bundle absence of controller-managed resources", "wave" : "W6" }, { @@ -14248,6 +14500,7 @@ "destinations" : [ "packages/d2b-provider-device-tpm/src/controller.rs; packages/d2b-provider-device-tpm/tests/finalizer.rs" ], + "detailedDesign" : "Finalizer: Volume retained on Device deletion. Hermetic tests cover Device deletion finalizer leading to swtpm Process deletion, TPM state Volume not deleted because `cleanupPolicy: never`, Volume persists, Core emits `phase=Deleted` for Device after finalizer clears, and audit carries no path/UID. Primary reuse disposition: `adapt`. Preserved source-plan detail: preserve TPM identity retention while moving deletion sequencing to Resource finalizers.", "entryContracts" : [ "ADR-046-provider-device-tpm" ], @@ -14262,6 +14515,7 @@ ], "specId" : "ADR-046-provider-device-tpm", "topologicalRank" : 14, + "validation" : "`tests/finalizer.rs` proving Process deletion, retained Volume, core Deleted phase, and redacted audit", "wave" : "W6" }, { @@ -14269,6 +14523,7 @@ "destinations" : [ "packages/d2bd/src/*; packages/d2b-core/src/processes.rs; packages/d2b-provider-device-tpm/src/; packages/d2b-host/src/swtpm_argv.rs" ], + "detailedDesign" : "Remove direct broker references: remove pre-ADR-0046 daemon swtpm broker call sites, retire `ProcessRole::Swtpm` and `ProcessRole::SwtpmPreStartFlush`, move argv builders from `d2b-host/src/swtpm_argv.rs` to `d2b-provider-device-tpm/src/` with binary path fields removed, while retaining `d2b-priv-broker/src/ops/swtpm_dir.rs` for `volume-local` and `spawn_runner.rs` for `system-minijail`. Primary reuse disposition: `delete-after-cutover`. Preserved source-plan detail: remove direct broker references from daemon and move argv builders into the Provider with binary path fields removed; retain broker ops only behind resource providers.", "entryContracts" : [ "ADR-046-provider-device-tpm" ], @@ -14283,6 +14538,7 @@ ], "specId" : "ADR-046-provider-device-tpm", "topologicalRank" : 13, + "validation" : "Static search/proof for no direct broker swtpm references in daemon/controller plus preserved swtpm contract tests", "wave" : "W6" }, { @@ -14290,6 +14546,7 @@ "destinations" : [ "`packages/d2b-provider-display-wayland/src/`" ], + "detailedDesign" : "Create Provider crate layout (`src/`, `tests/`, `integration/`, `README.md`); extract `FilterPolicy`, `PolicyInput`, `DecorationManager`, `BridgeConfig`, `ProxyReadinessEvent`, `ProxyIdentity`, `ClipboardGlobalDisposition` from `d2b-wayland-proxy`; implement single `display-controller` using toolkit `ResourceClient`/`Reconciler` to manage both `WaylandSession` and `WaylandPolicy` resources; implement `display-user-portal` as a separately sandboxed user-domain service that receives pre-opened compositor connections from the fixed user session supervisor (never reads `WAYLAND_DISPLAY`), validates same-user via `SO_PEERCRED`, and issues bounded per-session compositor connection attachment grants to `ProviderSupervisor`; implement LaunchTicket composition with opaque attachment grant handles (compositor, GPU endpoint) so no fd transits through the controller; implement pool-slot acquisition using opaque hash-derived account names (`d2b-wlp-` for bundle sessions, `d2b-wlp-p` for pool) that fails closed with `NoPrincipalAvailable` when all pool slots are occupied; implement `wl-cross-domain-proxy` guest frontend binary at `src/bin/wl-cross-domain-proxy.rs` within the Provider package; implement provider-neutral `display_fails_closed_when_unsupported` conformance; assert D087 status-first state: ProviderStateSet is empty and bounded operational state is in resource status plus the core Operation ledger Primary reuse disposition: `adapt`. Preserved source-plan detail: extract and adapt.", "entryContracts" : [ "ADR-046-provider-display-wayland" ], @@ -14305,6 +14562,7 @@ ], "specId" : "ADR-046-provider-display-wayland", "topologicalRank" : 11, + "validation" : "conformance vectors, fake-bus tests, filter policy golden tests (migrate from `packages/d2b-wayland-proxy/`), redaction/audit contract tests, no-fallback test, `controller_unknown_interface_fails_closed`, `controller_finalizer_ambiguous_retained`, `user_portal_unavailable_blocks_pending`, `provider_state_set_empty_status_first`", "wave" : "W6" }, { @@ -14312,6 +14570,7 @@ "destinations" : [ "Zone bundle emitter for `WaylandSession` / `WaylandPolicy` ResourceSpecs under `d2b.zones..resources.*`; `WaylandSession` color resolution in Nix bundle emitter" ], + "detailedDesign" : "Emit `WaylandSession` and `WaylandPolicy` ResourceSpecs from Nix; derive colors from `d2b-niri-border` palette (§16.2); enforce `crossDomainTrusted = true` at eval time; emit v3 `display-wayland-provider` artifact catalog entry with `spec.config.principalPoolSize` (default 4, bounds 1..32) and `spec.config.runtimeVolumePolicyId`; provision opaque hash-derived OS accounts (`d2b-wlp-` for bundle-declared sessions, `d2b-wlp-p` for pool; `isSystemUser = true`, no raw UID/GID) for all sessions and pool slots; validate that account names are unique and do not expose guest/session identity; validate signed Provider config schema; enforce eval-time bound that bundle session count + pool size does not exceed provisioned principal count; configure `display-controller` and `display-user-portal` in the signed ProviderDeployment manifest; nix-unit tests for color derivation, opaque principal name derivation, spec shape, JSON round-trip, and principal provisioning uniqueness", "entryContracts" : [ "ADR-046-provider-display-wayland" ], @@ -14326,6 +14585,7 @@ ], "specId" : "ADR-046-provider-display-wayland", "topologicalRank" : 12, + "validation" : "nix-unit spec-shape tests, eval-time guard tests (crossDomainTrusted=false rejected), color derivation golden tests, principal provisioning count bound test", "wave" : "W6" }, { @@ -14333,6 +14593,7 @@ "destinations" : [ "`packages/d2b-provider-display-wayland/src/audit.rs`, `packages/d2b-provider-display-wayland/src/metrics.rs`" ], + "detailedDesign" : "Implement audit record types for all events in §14.1; implement OTEL metric counters/gauges in §14.2; adapt `DiagRateLimiter` to use closed semantic label sets with no Zone UID/name or resource-name-derived key; retain Zone identity in `d2b.zone` resource attributes; validate that no socket path, user identity, window title, or app-id appears in any log/audit/metric surface. Primary reuse disposition: `adapt`. Preserved source-plan detail: extract and adapt.", "entryContracts" : [ "ADR-046-provider-display-wayland" ], @@ -14347,6 +14608,7 @@ ], "specId" : "ADR-046-provider-display-wayland", "topologicalRank" : 12, + "validation" : "Redaction contract tests (`policy_observability.rs` pattern), audit record schema tests, structural label-policy tests asserting exact absence of `vm`, `zone`, `zone_id`, `zone_uid`, and resource-name-derived keys plus WaylandSession/Zone-name canary absence", "wave" : "W6" }, { @@ -14354,6 +14616,7 @@ "destinations" : [ "`packages/d2b-provider-display-wayland/integration/`" ], + "detailedDesign" : "Container/Host/Guest/cross-process integration fixtures for: (a) end-to-end WaylandSession create → proxy Process ready → guest frontend ready; (b) GPU endpoint unavailable → Pending; (c) proxy crash → Failed backoff; (d) policy policy warning production; (e) clipboard boundary denial; (f) crossDomainTrusted=false admission rejection. Follows `ADR-046-provider-model-and-packaging` integration/ convention.", "entryContracts" : [ "ADR-046-provider-display-wayland" ], @@ -14368,6 +14631,7 @@ ], "specId" : "ADR-046-provider-display-wayland", "topologicalRank" : 12, + "validation" : "All six scenarios above pass; no socket paths in test output", "wave" : "W6" }, { @@ -14375,6 +14639,7 @@ "destinations" : [ "`packages/d2b/src/zone_doctor.rs`, `packages/d2b/tests/zone_doctor_contract.rs`" ], + "detailedDesign" : "`d2b zone doctor [--zone ] [--json]` reads resource status from Zone API (read-only verb), OTEL self-metrics from `observability-otel` Provider endpoint (optional), and audit segment inventory from `d2b-audit` segment reader. Named check set from this spec. Exit 0 on all-ready; 1 on any warn/error. Env-redirect sandbox for all test fixtures. Current `MANIFEST_JSON` fixture pattern adapted: `\"_observability\": {\"enabled\": false}` test ensures OTEL probe short-circuits cleanly. Primary reuse disposition: `adapt`. Preserved source-plan detail: adapt env-redirect sandbox test harness; adapt `defects` array pattern for `audit-hash-chain-clean` check; adapt `broker_ready` → `zone_phase` field.", "entryContracts" : [ "ADR-046-telemetry-audit-and-support" ], @@ -14390,6 +14655,7 @@ ], "specId" : "ADR-046-telemetry-audit-and-support", "topologicalRank" : 11, + "validation" : "`zone_doctor_contract.rs`: all-ready/degraded/quarantine/otel-absent/audit-absent fixtures; no resource names/paths/argv/PIDs; `zone_phase` field present; no legacy `broker_ready` field", "wave" : "W5" }, { @@ -14397,6 +14663,7 @@ "destinations" : [ "`packages/d2b/src/zone_support_bundle.rs`, `packages/d2b/tests/zone_support_bundle_contract.rs`" ], + "detailedDesign" : "`d2b zone support-bundle [--zone ]` requires `support-bundle` verb. Reads bounded resource status snapshots (32 per type, 512 total; metadata + status only; no spec bytes; no `metadata.name`), controller queue depths, schema catalog (names+versions only), audit segment inventory, OTEL collector metrics summary, bounded structured log ring (2000 entries). NDJSON output. On quarantine: `bundle_completeness: \"partial\"`, exit 1. Primary reuse disposition: `adapt`. Preserved source-plan detail: reuse env-redirect sandbox scaffold.", "entryContracts" : [ "ADR-046-telemetry-audit-and-support" ], @@ -14411,6 +14678,7 @@ ], "specId" : "ADR-046-telemetry-audit-and-support", "topologicalRank" : 12, + "validation" : "`zone_support_bundle_contract.rs`: complete/partial bundles; no spec/name/path/argv; field completeness", "wave" : "W5" }, { @@ -14418,6 +14686,7 @@ "destinations" : [ "`packages/d2b-contracts/src/v3/host.rs`, `packages/d2b-contracts/src/v3/guest.rs`, `packages/d2b-contracts/src/v3/execution_policy.rs`, `packages/d2b-contracts/src/v3/process.rs`, `packages/d2b-contracts/src/v3/ephemeral_process.rs`, `packages/d2b-contracts/src/v3/user.rs`" ], + "detailedDesign" : "Implement strict typed Rust structs for HostSpec, GuestSpec, ExecutionPolicy, ExecutionSpec, SandboxSpec, BudgetSpec, MountSpec, NetworkUsageSpec, DeviceUsageSpec, EndpointSpec, TelemetrySpec, ProcessSpec, EphemeralProcessSpec, UserSpec; strict serde deny_unknown_fields; bounds/redaction on all string fields; stable error types; `UserSpec.osUsername` validated as OS username (1..255 bytes, no NUL/control/path-separator), not ResourceName grammar Primary reuse disposition: `adapt`. Preserved source-plan detail: extract and adapt.", "entryContracts" : [ "ADR-046-resources-host-guest-process-user" ], @@ -14431,6 +14700,7 @@ ], "specId" : "ADR-046-resources-host-guest-process-user", "topologicalRank" : 10, + "validation" : "Golden JSON vectors for each ResourceType; serde unknown-field rejection; bounds enforcement; `UserSpec.osUsername` OS-username validation (underscore allowed, NUL rejected)", "wave" : "W5" }, { @@ -14438,6 +14708,7 @@ "destinations" : [ "`packages/d2b-contracts/src/v3/process_provider.rs`: LaunchTicket, ProcessIdentityDigest, AdoptionCandidate, PidfdEvidence, WaitReapOwner, BrokerTerminalResult, ProcessOutcome, ExitClass" ], + "detailedDesign" : "LaunchTicket (Process/EphemeralProcess ref/UID/revision/generation, owner Provider/component/template, executionRef/domain/userRef, providerRef, compiled sandbox/budget/mount/device/network/endpoint digest, inherited FD table, operation/deadline/cancellation, expected identity/readiness); ProcessIdentityDigest (opaque bounded hex string); AdoptionCandidate (cgroup leaf path relative to controller root, start-time token, executable hash); BrokerTerminalResult binds process identity/operation to the clone3 parent's exact-once wait/reap status and cannot be constructed from pidfd readability; all types zeroizing where credential-adjacent Primary reuse disposition: `adapt`. Preserved source-plan detail: extract and adapt.", "entryContracts" : [ "ADR-046-resources-host-guest-process-user" ], @@ -14452,6 +14723,7 @@ ], "specId" : "ADR-046-resources-host-guest-process-user", "topologicalRank" : 11, + "validation" : "Golden LaunchTicket and BrokerTerminalResult vectors; field redaction test; digest-binding test; duplicate/mismatched/non-parent terminal relay rejection", "wave" : "W5" }, { @@ -14459,6 +14731,7 @@ "destinations" : [ "`packages/d2b-provider-system-core/src/host.rs`: HostReconciler; status/conditions/capability probe implementation; `packages/d2b-provider-system-core/tests/`: hermetic reconcile/conformance/fault tests; `packages/d2b-provider-system-core/integration/`: Host probe and lifecycle integration scenarios; `packages/d2b-provider-system-core/README.md`: Provider identity, `spec.provider.settings` schema, ResourceTypes, placement, RBAC, security posture, telemetry labels, build/test commands (provider crate standard layout — see §Provider crate standard layout)" ], + "detailedDesign" : "Async Host reconcile loop per this spec's Reconcile section; HostCapabilityClass probe set (kvm/pidfd/cgroup-v2/user-namespace/wayland/audio-pipewire/gpu-render/tpm2/usbip); bounded OS probes with timeout; mandatory system-minijail placement gate for Linux ≥5.14 plus writable delegated-leaf `cgroup.kill` independent of optional `kernelVersionMin`; `isolationPosture` validation and status; aggregate budget reservation tracking via List; status write with expected revision Primary reuse disposition: `adapt`. Preserved source-plan detail: extract and adapt.", "entryContracts" : [ "ADR-046-resources-host-guest-process-user" ], @@ -14473,6 +14746,7 @@ ], "specId" : "ADR-046-resources-host-guest-process-user", "topologicalRank" : 11, + "validation" : "Multiple Hosts per Zone; system-only and user-only Hosts; capability probe mocks; Linux <5.14 and missing/unwritable `cgroup.kill` reject system-minijail before spawn; Linux ≥5.14 positive probe; `isolationPosture=\"none\"` rejection of system processes; budget overcommit rejection; `tests/` all pass under `cargo test`; `integration/` scenario passes in container fixture; `README.md` present and covers all required sections (provider crate standard layout acceptance)", "wave" : "W5" }, { @@ -14480,6 +14754,7 @@ "destinations" : [ "`packages/d2b-provider-system-core/src/user.rs`: UserReconciler; NSS lookup implementation" ], + "detailedDesign" : "Async User reconcile loop per this spec's Reconcile section; NSS `getpwnam(spec.osUsername)` with bounded timeout (default 5 s); `spec.osUsername` validated on admission (1..255 bytes, no NUL/control/path-separator); `metadata.name` used only as Zone-local resource identity and `User/` ref; uid/gid/home/shell/group discovery written to status; session manager availability check via fixed user supervisor; status write; phase Degraded on SessionManagerReady=False Primary reuse disposition: `adapt`. Preserved source-plan detail: extract and adapt.", "entryContracts" : [ "ADR-046-resources-host-guest-process-user" ], @@ -14494,6 +14769,7 @@ ], "specId" : "ADR-046-resources-host-guest-process-user", "topologicalRank" : 11, + "validation" : "User found/not-found; group membership; sessionManagerAvailable; multiple Users; deletion blocked by Process userRef; `spec.osUsername` with underscore succeeds NSS lookup where ResourceName grammar would reject it; `metadata.name` and `spec.osUsername` differ (e.g. `alice-admin` / `alice_admin`); `spec.osUsername` containing NUL/control/path-separator rejected at admission", "wave" : "W5" }, { @@ -14501,6 +14777,7 @@ "destinations" : [ "`packages/d2b-provider-system-core/src/host.rs` (continued); bootstrap startup sequence" ], + "detailedDesign" : "ProviderSystem-core fixed bootstrap: start with embedded Zone runtime and compiled bootstrap authorization; run Host reconcile before any Process Provider launches; create initial User resources from Nix; publish initial Role/RoleBinding from Nix config; hand off to stored RBAC", "entryContracts" : [ "ADR-046-resources-host-guest-process-user" ], @@ -14516,6 +14793,7 @@ ], "specId" : "ADR-046-resources-host-guest-process-user", "topologicalRank" : 12, + "validation" : "Bootstrap without prior Host; User-before-Process ordering; compilation bootstrap authorization closed-set tests", "wave" : "W5" }, { @@ -14523,6 +14801,7 @@ "destinations" : [ "`packages/d2b-provider-system-systemd/src/`: launch.rs, adoption.rs, pidfd.rs, wait.rs, user_supervisor.rs; `packages/d2b-provider-system-systemd/tests/`: hermetic lifecycle/conformance/fault tests; `packages/d2b-provider-system-systemd/integration/`: transient-unit and user-supervisor integration scenarios; `packages/d2b-provider-system-systemd/README.md`: Provider identity, `spec.provider.settings` schema, ResourceTypes, placement, RBAC, security posture, telemetry labels, build/test commands (provider crate standard layout — see §Provider crate standard layout)" ], + "detailedDesign" : "system-systemd Process/EphemeralProcess provider conformance per this spec's system-systemd conformance section; transient system unit (Type=exec); InvocationID+cgroup+MainPID+start-time binding before pidfd_open; systemd-owned wait/reap; user domain via fixed user supervisor; adoption re-verification; sandboxSpec compilation to systemd hardening directives; runtimeDeadline enforcement; drainTimeout SIGTERM/SIGKILL sequence Primary reuse disposition: `adapt`. Preserved source-plan detail: extract and adapt.", "entryContracts" : [ "ADR-046-resources-host-guest-process-user" ], @@ -14538,6 +14817,7 @@ ], "specId" : "ADR-046-resources-host-guest-process-user", "topologicalRank" : 12, + "validation" : "Shared process conformance test matrix (lifecycle/readiness/crash/drain/adoption/user-domain/sandboxSpec/pidfd); system-specific InvocationID binding; no-static-unit test; `tests/` all pass under `cargo test`; `integration/` scenario passes in container fixture; `README.md` present and covers all required sections (provider crate standard layout acceptance)", "wave" : "W5" }, { @@ -14545,6 +14825,7 @@ "destinations" : [ "`packages/d2b-provider-system-minijail/src/`: sandbox_compiler.rs, launch.rs, adoption.rs, pidfd.rs, wait.rs, user_ns.rs; `packages/d2b-provider-system-minijail/tests/`: hermetic sandbox-compilation/lifecycle/conformance/fault tests; `packages/d2b-provider-system-minijail/integration/`: clone3/user-namespace and broker-spawn integration scenarios; `packages/d2b-provider-system-minijail/README.md`: Provider identity, `spec.provider.settings` schema, ResourceTypes, placement, RBAC, security posture (capabilities, namespaces, seccomp), telemetry labels, build/test commands (provider crate standard layout — see §Provider crate standard layout)" ], + "detailedDesign" : "system-minijail Process/EphemeralProcess provider conformance per this spec's system-minijail conformance section; SandboxSpec-to-minijail plan compilation; Linux ≥5.14/cgroup.kill placement gate; broker clone3(CLONE_PIDFD|CLONE_INTO_CGROUP) parent retains sole waitid(P_PIDFD)/reap/exit-status ownership and relays a typed terminal result; ProviderSupervisor polls a verified duplicate and retains exact-main pidfd_send_signal semantics but never waits/reaps; adoption verifies original broker parent; runtimeDeadline/drainTimeout use graceful main signal then anchored leaf cgroup.kill, broker wait/reap, and empty-leaf proof; no PID/PGID fallback; EphemeralProcess one-shot launch Primary reuse disposition: `adapt`. Preserved source-plan detail: extract and adapt.", "entryContracts" : [ "ADR-046-resources-host-guest-process-user" ], @@ -14560,6 +14841,7 @@ ], "specId" : "ADR-046-resources-host-guest-process-user", "topologicalRank" : 12, + "validation" : "Shared process conformance test matrix; minijail-specific sandbox compilation tests; user namespace tests; clone3 parent-only wait/reap and broker-relay tests; poll-readability-not-status test; pidfd_send_signal duplicate-holder test; setsid descendant/recycled-PGID cgroup.kill teardown test; Linux 5.14/cgroup.kill platform-gate tests; adoption quarantine asserts no signal/cgroup.kill; `tests/` all pass under `cargo test`; `integration/` scenario passes; `README.md` present and covers all required sections (provider crate standard layout acceptance)", "wave" : "W5" }, { @@ -14567,6 +14849,7 @@ "destinations" : [ "`packages/d2b-process-conformance/src/`: shared conformance test matrix run against both system-systemd and system-minijail providers" ], + "detailedDesign" : "Full process lifecycle tests: start/ready/crash/restart/maxRestarts/drain/stop/delete; EphemeralProcess start/succeed/fail/ttl/cleanup; adoption after controller restart (fresh/quarantine); sandboxSpec compilation contract; pidfd rules (never-serialized/never-exported/re-verified-after-supervisor-restart; clone3 broker parent alone waits/reaps; non-parent readability is not status; verified duplicate holder may pidfd_send_signal exact main); system-minijail intentional teardown uses anchored cgroup.kill against setsid descendant and recycled-PGID fixtures, with no kill on ambiguity; Linux ≥5.14/cgroup.kill platform gate; user domain (system-systemd only); desiredLifecycle=stopped; fast path latency gate (<=5ms/<=20ms p95); 1/10/100 concurrent Process start", "entryContracts" : [ "ADR-046-resources-host-guest-process-user" ], @@ -14582,6 +14865,7 @@ ], "specId" : "ADR-046-resources-host-guest-process-user", "topologicalRank" : 13, + "validation" : "Hard pass/fail per-test; latency gates enforced; no exception for partial conformance", "wave" : "W5" }, { @@ -14589,6 +14873,7 @@ "destinations" : [ "`packages/d2b-provider-system-core/src/host.rs` (user-only no-isolation Host); `nixos-modules/options-zones.nix` (Nix unsafe-local Host declaration)" ], + "detailedDesign" : "v3 unsafe-local migration: `kind = \"unsafe-local\"` in the current Nix Realm workload model becomes a Host resource with `providerRef: Provider/system-core`, `spec.isolationPosture: \"none\"`, `defaultDomain: user`, `allowedDomains: [user]`, `defaultUserRef: User/`. This is a Host ResourceType, not a Guest and not a v3 Provider. Child Process and EphemeralProcess resources on this Host use the normal Process Providers (Provider/system-systemd for user-domain transient user scope; Provider/system-minijail is also valid for callers that explicitly request namespace isolation within the user session). No special unsafe-local-specific Provider is introduced. The explicit no-isolation posture and its warnings are preserved: Host status reflects `isolationPosture=\"none\"` and this is surfaced in every operator CLI/UI view as an explicit \"no isolation boundary\" warning; `ProcessEffect` audit records (launch, stop, adopt, quarantine) for child Processes and EphemeralProcesses carry the stable `no_isolation=true` attribute; operator CLI/UI always shows the warning and may not suppress it. The `no_isolation=true` attribute belongs on ProcessEffect records only — it must NOT appear on OTEL metric labels, span attributes, log fields, or audit records for other event kinds. The legacy helper protocol (`d2b-unsafe-local-helper`) is not exposed as a v3 ComponentSession service.", "entryContracts" : [ "ADR-046-resources-host-guest-process-user" ], @@ -14603,6 +14888,7 @@ ], "specId" : "ADR-046-resources-host-guest-process-user", "topologicalRank" : 11, + "validation" : "User-only no-isolation Host rejected for system processes; `isolationPosture=\"none\"` Host rejected for `allowedDomains` containing `system`; `allowedDomains=[\"user\"]`+`defaultDomain=user`+`defaultUserRef` set with `isolationPosture=null` rejected at eval time (bidirectional evasion test); posture warning visible in CLI/UI status; `no_isolation=true` attribute present on ProcessEffect launch/stop/adopt/quarantine audit records for child Processes/EphemeralProcesses; `no_isolation=true` absent from OTEL span attributes, metric labels, log fields, and non-ProcessEffect audit records; user-domain Process under user-only Host starts correctly with normal Process Provider", "wave" : "W5" }, { @@ -14610,6 +14896,7 @@ "destinations" : [ "`packages/d2b-provider-system-systemd/src/guest_exec.rs` (guest-domain EphemeralProcess launch via systemd-run inside guest); `packages/d2b-session/` (ComponentSession replacing ad-hoc guest ttrpc); runtime Provider guest bootstrap Process" ], + "detailedDesign" : "Guest-side execution transitions to EphemeralProcess/Process resources under the owning Guest; guestd service becomes a fixed bootstrap Process owned by the runtime Provider controller; exec operations become EphemeralProcess creates via the Zone ResourceClient; detached sessions become EphemeralProcess with failedTtl=24h; shell sessions become shell-terminal Provider Processes; guestd auth.rs becomes ComponentSession via d2b-session (copy/adapt from ADR046-session-001 per ComponentSession spec) Primary reuse disposition: `adapt`. Preserved source-plan detail: extract and adapt.", "entryContracts" : [ "ADR-046-resources-host-guest-process-user" ], @@ -14625,6 +14912,7 @@ ], "specId" : "ADR-046-resources-host-guest-process-user", "topologicalRank" : 13, + "validation" : "Guest EphemeralProcess exec lifecycle; detached exec TTL; guestd auth replaced by ComponentSession; no SSH fallback", "wave" : "W5" }, { @@ -14632,6 +14920,7 @@ "destinations" : [ "guest-domain process attachment becomes a ComponentSession named stream to the EphemeralProcess running in the guest; `UserExecSession` trait reimplemented as a typed ResourceClient+ComponentSession attachment" ], + "detailedDesign" : "`UserdConfig.socket_name` replaced by Zone-local EphemeralProcess ResourceRef and ComponentSession attach verb; `UserSessionIdentity.uid`/`gid` become Process `userRef` resolved against User resource status; `UserAttachRequest.exec_id`/`tty`/`initial_size` become ComponentSession method parameters on the EphemeralProcess attachment service; `UserdError` becomes stable v3 error codes; `validate_attach_request` tty/size validation retained in the ComponentSession session schema Primary reuse disposition: `adapt`. Preserved source-plan detail: adapt (logic retained; protocol replaced).", "entryContracts" : [ "ADR-046-resources-host-guest-process-user" ], @@ -14647,6 +14936,7 @@ ], "specId" : "ADR-046-resources-host-guest-process-user", "topologicalRank" : 14, + "validation" : "Attach/detach; TTY size; stream close; error path parity with UserdError variants", "wave" : "W5" }, { @@ -14654,6 +14944,7 @@ "destinations" : [ "`nixos-modules/options-zones.nix`: `d2b.zones..resources` option as `types.attrsOf (types.submodule resourceModule)` where each resource module has `type` (required enum), optional `metadata` submodule (`ownerRef`, `labels`, `annotations`), and `spec` (type-dependent, auto-generated submodule); `nixos-modules/zone-bundle.nix`: zone resource bundle emitter (see ADR046-exec-014); `nixos-modules/resource-schemas/`: generated per-type Nix option modules imported by `options-zones.nix`" ], + "detailedDesign" : "`d2b.zones..resources` is a flat attrset; each entry has `type`, optional `metadata` (`ownerRef`, `labels`, `annotations`), and `spec`. The attrset key is the resource name. `spec` submodule fields and their Nix types/defaults/docs are auto-generated from the committed `packages/d2b-contracts/src/v3/schemas/.json` via `xtask gen-resource-nix-options`; no second vocabulary and no renaming of `spec.*` fields. `spec.provider.settings` sub-fields are constrained to the specific Provider's `providerNixSettingsSchema` attribute if present. All 17 eval-time validation rules from the \"Eval-time validation rules\" section are enforced by `lib.assertMsg` on the flat resource attrset. The `spec` object in the emitted JSON is the direct 1:1 serialization of the `spec` submodule. `metadata.ownerRef`, `metadata.labels`, `metadata.annotations` are serialized into the `metadata` object of the ResourceEnvelope JSON. `metadata.managedBy` and `metadata.configurationGeneration` are NOT in the bundle; they are set by the activation controller at runtime. `metadata.name` is the attrset key; `metadata.zone` is the enclosing zone key. Eval errors carry stable rule codes (1–17). Status is never present in the Nix option. `Guest.spec.systemArtifactId` (top-level spec field, not in `spec.provider.settings`) is validated against `d2b.artifacts` by rule 17; the `spec` submodule never contains derivation values or store paths.", "entryContracts" : [ "ADR-046-resources-host-guest-process-user" ], @@ -14668,6 +14959,7 @@ ], "specId" : "ADR-046-resources-host-guest-process-user", "topologicalRank" : 11, + "validation" : "nix-unit eval/build tests 1–18 from the \"Tests\" section; eval validation rule tests 1–17 with expected error messages and stable codes; `spec` fields in emitted JSON match `spec` submodule values exactly (1:1 invariant test); `type` field in JSON matches `type` option value; `metadata.name` = attrset key; `metadata.zone` = enclosing zone key; `Guest.spec.systemArtifactId` plain string in resource bundle JSON at top-level spec (no store path, not in `spec.provider.settings`); missing/wrong-type artifact ID raises rule 17 eval error", "wave" : "W5" }, { @@ -14675,6 +14967,7 @@ "destinations" : [ "`packages/d2b-core-controller/src/cleanup.rs`: EphemeralProcess TTL cleanup controller handler" ], + "detailedDesign" : "Cleanup controller handler as specified in core-controllers spec; watches EphemeralProcess resources for terminal phase; computes cleanupEligibleAt from successfulTtl/failedTtl + completedAt; handles incidentHold; respects finalizers; issues normal Delete via ResourceClient; does not remove rows directly; bounded requeue-at for TTL expiry", "entryContracts" : [ "ADR-046-resources-host-guest-process-user" ], @@ -14690,6 +14983,7 @@ ], "specId" : "ADR-046-resources-host-guest-process-user", "topologicalRank" : 14, + "validation" : "Succeeded TTL default 1h; Failed TTL default 24h; incidentHold blocking; finalizer blocking; Delete with expected revision; cleanup controller restart recovery", "wave" : "W5" }, { @@ -14697,6 +14991,7 @@ "destinations" : [ "`nixos-modules/zone-bundle.nix`: Zone resource bundle emitter; `nixos-modules/resource-schemas/`: generated per-type Nix option submodules; `packages/d2b-contracts/src/v3/resource_bundle.rs`: `ResourceBundle`, `ResourceEnvelope`, `BundleManifest`, `BundleIntegrityPin` Rust types; `packages/xtask/src/gen_resource_schemas.rs`: `xtask gen-resource-schemas` (generates schema JSON and Nix option modules)" ], + "detailedDesign" : "`zone-bundle.nix` iterates the flat `d2b.zones..resources` attrset (all types share the same attrset; each entry has `type`, optional `metadata`, and `spec`). For each entry it serializes the `spec` submodule to canonical JSON 1:1 (field names unchanged; keys sorted at every level; order-significant arrays preserve declaration order; semantically unordered arrays sorted). `metadata.ownerRef`, `metadata.labels`, `metadata.annotations` are serialized from the author-supplied `metadata` submodule into the envelope's `metadata` object. `metadata.managedBy` and `metadata.configurationGeneration` are NOT in the bundle envelope; they are set by the activation controller at runtime. `metadata.name` is the attrset key; `metadata.zone` is the zone key. `xtask gen-resource-schemas` generates both the schema JSON files AND the per-type Nix option submodule files under `nixos-modules/resource-schemas/` from the Rust DTO definitions; both must be regenerated when any ResourceType spec or Rust struct changes (same drift-gate pattern as current `make test-drift` / `xtask gen-schemas`). The bundle sorts all envelopes by `type` then `metadata.name` alphabetically, computes `bundleSha256` from the canonical JSON of the `resources` array, and emits the bundle manifest as a NixOS store artifact. No secret values, credentials, or OS paths not already declared in `spec` or `metadata` fields may appear in any envelope JSON. The bundle file includes private integrity metadata (resource type schema fingerprints, per-Provider schema fingerprints) alongside the `resources` array; these are bundle-level fields, never per-envelope fields. Alongside the resource bundle, the emitter installs the global private artifact catalog at `/etc/d2b/artifact-catalog.json` (root:d2bd 0640) mapping each `d2b.artifacts.` to `{ \"sha256\", \"size\", \"storePath\", \"type\" }`; this catalog is never included in public resource bundle envelopes. The bundle outer wrapper includes a `catalogSha256` binding the catalog to the bundle for activation-time integrity verification.", "entryContracts" : [ "ADR-046-resources-host-guest-process-user" ], @@ -14712,6 +15007,7 @@ ], "specId" : "ADR-046-resources-host-guest-process-user", "topologicalRank" : 12, + "validation" : "nix-unit test: bundle sort order (Host < Guest < Process; within type, names alphabetical); nix-unit test: `bundleSha256` recomputed from resources array matches recorded value; nix-unit test: schema fingerprints appear in private bundle file fields, not in any individual ResourceEnvelope `metadata` or `spec` object; nix-unit test: `catalogSha256` changes when any artifact derivation changes; nix-unit test: `schemaFingerprint` changes when ResourceTypeSchema JSON changes; nix-unit test: `providerSchemaFingerprint` changes when Provider settings schema changes and is null when Provider declares no schema; nix-unit test: no inline secret value passes through to bundle JSON; nix-unit test: identical configuration produces byte-identical bundle JSON across two builds; nix-unit test: artifact catalog JSON contains `storePath` field for each entry; nix-unit test: no envelope in `resources` array contains `storePath`, `nixSystem`, `schemaFingerprint`, or `providerSchemaFingerprint`", "wave" : "W5" }, { @@ -14719,6 +15015,7 @@ "destinations" : [ "`packages/d2b-core-controller/src/configuration.rs`: `ZoneConfigController`, `GenerationState`, `PendingCleanup`, `BundleActivation`, `ActivationResult`, `ActivationError`, `GenerationDiff`, `DiffEntry`, `DiffKind`, `CleanupRecord`, `CleanupPhase`, `CleanupOutcome`, `RetentionPolicy`, `RetentionState`; `packages/d2b-core-controller/src/audit.rs`: audit event emission per the \"Audit events\" section" ], + "detailedDesign" : "The Zone configuration controller (`packages/d2b-core-controller/src/configuration.rs`) runs as a fixed process in the `d2b-core-controller` crate. It watches `/etc/d2b/zones//resource-bundle.json` (inotify or polling). On change: (1) read and verify `bundleSha256` integrity; fail closed on mismatch, emit `d2b.zone.config.activate.error` with `config-bundle-integrity-failed`, make no changes. (2) Compare candidate `bundleSha256` against the currently active bundle record; if identical, this is a no-op re-activation (return immediately). (3) Verify resource type schema fingerprints (from bundle private fields) against committed schemas at `packages/d2b-contracts/src/v3/schemas/.json`; any mismatch fails the entire bundle closed with `config-schema-mismatch`, emits error, makes no changes. (4) Verify Provider schema fingerprints against installed Provider schemas; any mismatch fails the entire bundle closed with `provider-schema-mismatch`, emits error, makes no changes. (5) Verify `catalogSha256` binding against `/etc/d2b/artifact-catalog.json`; mismatch fails bundle closed. (6) Fetch current `metadata.managedBy=\"configuration\"` resources via ResourceClient List (one call per type). (7) Compute `GenerationDiff` (new/changed/unchanged/removed) by type+name key. (8) Submit Create, UpdateSpec, and Delete intents concurrently with bounded async concurrency (default max 32 in-flight); activation returns after all intents are durably queued by the resource store, without waiting for reconcile loops to complete. For unchanged specs: issue UpdateConfigGeneration to refresh `metadata.configurationGeneration` to the new generation number; no controller reconcile triggered. For new resources: core sets `metadata.managedBy=\"configuration\"` and `metadata.configurationGeneration=` on Create; if a same-name resource already exists with `metadata.managedBy=\"controller\"` or `metadata.managedBy=\"api\"`, record a per-item `config-collision` error for that resource without seizing it, emit error, continue other intents. For changed specs: submit UpdateSpec with `expectedRevision`; retry on optimistic lock conflict. For removed resources: submit Delete; set `metadata.deletionRequestedAt`. (9) Set Zone `phase=Pending` while create/update intents are outstanding. (10) Return after durable queue commit; do not block on reconcile completion. (11) The cleanup controller watches ResourceClient Watch streams for `Deleted` revision events (not polling GET) for each pending-cleanup resource by type+name+expectedRevision. When all finalizers release, the resource store commits the `Deleted` revision event atomically with row and index removal in a single transaction; following this commit, the audit subsystem appends the deletion audit record using a dedup/exactly-once recovery key (audit is NOT part of the atomic store transaction). Zone transitions to `phase=Degraded` immediately whenever any pending-cleanup item is outstanding. When all pending-cleanup items receive `Deleted` Watch events, `pendingCleanup` empties and Zone transitions to `phase=Ready`. Cleanup-stuck threshold: 10 min default; configurable; stuck resources remain Degraded without blocking later activations. Prior generation retention: controller retains the N most recently activated, cleanup-complete bundle records (default N=3; range 1..16; no time-based TTL).", "entryContracts" : [ "ADR-046-resources-host-guest-process-user" ], @@ -14736,6 +15033,7 @@ ], "specId" : "ADR-046-resources-host-guest-process-user", "topologicalRank" : 15, + "validation" : "Runtime/integration tests 19–23 from the \"Tests for Nix configuration and ResourceType-specific lifecycle\" section in this spec; additionally: `GenerationDiff` hermetic unit tests (new/changed/unchanged/removed classification); `bundleSha256` integrity failure aborts and emits correct audit event; `catalogSha256` mismatch aborts bundle; UpdateSpec optimistic lock conflict retried correctly; Watch `Deleted` revision events consumed (not polling GET) to track cleanup completion; Zone `phase=Pending` while intents outstanding; Zone `phase=Degraded` immediately when any cleanup outstanding (no grace window); Zone `phase=Ready` when complete; activation returns after durable queue commit, not after reconcile; same-name `managedBy=controller` OR `managedBy=api` collision emits per-item `config-collision` error without seizing resource, other intents continue; unchanged spec refreshes `configurationGeneration` without triggering controller reconcile; final deletion: atomic tx commits `Deleted` revision event + row/index removal only; audit append follows committed revision via dedup/exactly-once recovery (NOT part of atomic tx); recovery retry produces no duplicate audit record; prior bundle record released after cleanup-complete and retention count exceeded; activation with zero diff and identical bundleSha256 is a no-op", "wave" : "W5" }, { @@ -14743,6 +15041,7 @@ "destinations" : [ "`packages/d2b-bus-session/src/`: all above modules verbatim; `packages/d2b-bus-session/tests/`: all above tests verbatim" ], + "detailedDesign" : "The entire `d2b-session` portable ComponentSession runtime is transport-agnostic and contains no ADR 0045 realm-specific types. All Noise handshake parameters, record framing, fragmentation, named stream mux, fair scheduler, cancellation, deadlines, bootstrap PSK, and attachment bindings are directly reusable. `ComponentSessionDriver` trait and `serve_ttrpc_services` are the primary integration surface for every v3 bus service. The `sessions/lib.rs` re-export boundary (`pub use d2b_contracts::v2_component_session as contract`) must be updated to point at the v3 wire contract module. Primary reuse disposition: `adapt`. Preserved source-plan detail: copy verbatim; rename crate from `d2b-session` to `d2b-bus-session` or retain name.", "entryContracts" : [ "ADR-046-resources-host-guest-process-user" ], @@ -14757,6 +15056,7 @@ ], "specId" : "ADR-046-resources-host-guest-process-user", "topologicalRank" : 11, + "validation" : "Copy tests verbatim; all tests must pass on v3 baseline without modification; re-run `tests/noise_vectors.rs` golden vectors; add one v3-specific test: endpoint policy identity uses v3 `ZoneId`/zone-name binding rather than ADR45 `RealmId`", "wave" : "W5" }, { @@ -14764,6 +15064,7 @@ "destinations" : [ "`packages/d2b-bus-session-unix/src/`: all above modules verbatim; `packages/d2b-bus-session-unix/tests/`: all above tests verbatim" ], + "detailedDesign" : "Provides the Linux-specific `OwnedTransport` implementation for Unix seqpacket and stream sockets. `CreditPool`/`ProcessCreditLimit` enforces per-scope FD attachment budget (ADR45 constants: `MAX_PROCESS_ATTACHMENT_CREDITS = 2048`, `MAX_HOST_ATTACHMENT_CREDITS = 8192`, `RESERVED_CONTROL_FDS = 64`). `PidfdIdentityVerifier` provides the `/proc//fdinfo/` parse path that Process controllers use to verify process identity before `pidfd_open(2)` — this is a direct dependency of system-systemd and system-minijail Process Providers (ADR046-exec-006, ADR046-exec-007). Primary reuse disposition: `adapt`. Preserved source-plan detail: copy verbatim; rename crate from `d2b-session-unix` to `d2b-bus-session-unix` or retain name.", "entryContracts" : [ "ADR-046-resources-host-guest-process-user" ], @@ -14778,6 +15079,7 @@ ], "specId" : "ADR-046-resources-host-guest-process-user", "topologicalRank" : 12, + "validation" : "Copy all 20+ tests verbatim; all must pass; add v3-specific test: `PathnamePeerVerifier` verifies against `d2b-zonert` daemon uid; credit pool per-Zone-runtime limits match v3 constants", "wave" : "W5" }, { @@ -14785,6 +15087,7 @@ "destinations" : [ "`packages/d2b-bus-wire/src/session.rs`: v3 bus protocol constants and wire types; all numeric constants copied verbatim; `PREFACE_MAGIC` retained; `EndpointPolicy` and `EndpointPolicyIdentity` adapted to use v3 `ZoneId`/`ProviderId` instead of ADR45 `RealmId` in the policy identity fingerprint" ], + "detailedDesign" : "All numeric constants (frame sizes, credit limits, deadline values, reconnect limits) are directly reusable without change — they are derived from protocol analysis, not from realm semantics. `LimitProfile::local_default()` is the source for `serve_ttrpc_services` capacity; retain the value. `EndpointPolicy` carries the Noise static key and schema fingerprint; the fingerprint computation does not embed realm names and is reusable. The `EndpointPolicyIdentity` type carries the zone runtime's static public key — update from ADR45 `RealmId` to v3 `ZoneId` string encoding. Primary reuse disposition: `adapt`. Preserved source-plan detail: copy and adapt.", "entryContracts" : [ "ADR-046-resources-host-guest-process-user" ], @@ -14799,6 +15102,7 @@ ], "specId" : "ADR-046-resources-host-guest-process-user", "topologicalRank" : 12, + "validation" : "Compile-time assertions on all copied numeric constants matching source values; `EndpointPolicyIdentity` golden-vector test with v3 zone name encoding; `LimitProfile::local_default()` round-trip test", "wave" : "W5" }, { @@ -14806,6 +15110,7 @@ "destinations" : [ "`packages/d2b-provider-runtime/src/`: `registry.rs`, `rpc.rs`, `instance.rs`, `context.rs`, `error.rs`; provider trait objects moved to `d2b-bus-wire` or `d2b-provider-contracts`" ], + "detailedDesign" : "`ProviderRegistry` manages in-flight permits, draining, and provider lifecycle. `RegistryLimits { total_in_flight, per_provider_in_flight }` is directly reusable. `RpcProviderProxy` wraps a `ComponentSessionDriver` and dispatches typed `RpcCall` to an `AuthenticatedProviderRpc` implementation — the proxy pattern is fully reusable for v3 Provider resource controllers. `SessionIdentity { peer_role, service, provider_id, provider_type, provider_generation }` maps directly to a v3 Provider session credential. `InFlightPermit` RAII guard is directly reusable. The provider trait object set (`RuntimeProvider`, `StorageProvider`, etc.) adapts to v3 Provider resource typed methods; the trait hierarchy is preserved but `ProviderMethod` enum variant names may be renamed to drop ADR45 workload terminology. `ProviderRegistry::MAX_PROVIDER_REGISTRY_ENTRIES` bound from v2_provider is retained. Primary reuse disposition: `adapt`. Preserved source-plan detail: copy and adapt.", "entryContracts" : [ "ADR-046-resources-host-guest-process-user" ], @@ -14821,6 +15126,7 @@ ], "specId" : "ADR-046-resources-host-guest-process-user", "topologicalRank" : 13, + "validation" : "Copy `d2b-provider` tests; `RegistryLimits::validate` enforces non-zero and per≤total; `InFlightPermit` RAII release; drain/retire state machine; `RpcProviderProxy` round-trip over `FakeProvider` (from toolkit ADR046-exec-020)", "wave" : "W5" }, { @@ -14828,6 +15134,7 @@ "destinations" : [ "`packages/d2b-provider-toolkit/src/`: retain all modules verbatim; adapt `ProviderAgentAdapter` to use v3 `ProviderRegistry` (ADR046-exec-019) and v3 bus wire types; adapt `GeneratedProviderServiceServer` to use v3 generated service stubs (ADR046-exec-021); `packages/d2b-provider-agent/src/`: adapted from `gateway-runtime/src/provider_agent.rs`" ], + "detailedDesign" : "`ProviderAgentAdapter` is the core: it drives a `ComponentSessionDriver` receive loop, dispatches decoded ttrpc frames to `ProviderRegistry`, and forwards responses. `GeneratedProviderServiceServer` closes the loop by registering all generated service stubs with `serve_ttrpc_services`. `FakeProvider` implements every v2 Provider trait with deterministic outputs — adapt each trait method to v3 Provider resource semantics while retaining the fixture pattern. `Redacted` / `Secret` zero-copy wrappers are used in every audit log path; copy verbatim. Provider conformance check pattern is retained: descriptor validation, capability publication, fixture round-trip, observability query result. Primary reuse disposition: `adapt`. Preserved source-plan detail: copy and adapt.", "entryContracts" : [ "ADR-046-resources-host-guest-process-user" ], @@ -14842,6 +15149,7 @@ ], "specId" : "ADR-046-resources-host-guest-process-user", "topologicalRank" : 14, + "validation" : "Copy conformance tests verbatim; `check_descriptor_conformance` passes on a `FakeProvider` descriptor; `check_provider_conformance` covers all five `ConformanceError` variants; `ProviderAgentProcess` shutdown within deadline test; `MAX_DISPATCH_IN_FLIGHT` semaphore back-pressure test", "wave" : "W5" }, { @@ -14849,6 +15157,7 @@ "destinations" : [ "`packages/d2b-bus-contracts/src/generated_v3_services/`: v3 generated ttrpc stubs for Zone service methods (Resource CRUD, Watch, ComponentSession service verbs); `packages/d2b-zone-service/src/`: Zone runtime service handler adapted from `DaemonServiceV2` pattern; `packages/d2b-zone-service/src/admission.rs`, `handler.rs`, `routing.rs`" ], + "detailedDesign" : "`StrictWireMessage` trait (decode_strict, encode_strict) is directly reusable — it enforces deny-unknown-fields decode and schema-pinned fingerprint validation. `ServiceInventoryDocument` / `service_schema_fingerprint` pattern provides the service schema publication mechanism that v3 Provider resources use to advertise their ComponentSession service interface. `DaemonServiceV2` / `DaemonOperationHandler` pattern becomes the v3 Zone service handler base: `DaemonCallContext` → v3 `ZoneCallContext` with `ZoneId`, principal `User/`, operation deadline; `DaemonMethod` enum → v3 `ZoneMethod` (ResourceGet, ResourceList, ResourceWatch, ResourceCreate, ResourceUpdateSpec, ResourceUpdateStatus, ResourceDelete, BusAttach); `daemon_endpoint_policy` → v3 zone endpoint policy with v3 `ZoneId`-bound static key and schema fingerprint. `server_stream_name`/`parse_server_stream_name` for Watch stream naming is reusable verbatim. `TerminalStreamValidator` / `ServerStreamLease` for terminal byte stream safety is reusable for EphemeralProcess attach. Primary reuse disposition: `adapt`. Preserved source-plan detail: adapt; v3 generates new protobuf definitions and new ttrpc stubs; reuse message shaping, method dispatch patterns, and service inventory pattern.", "entryContracts" : [ "ADR-046-resources-host-guest-process-user" ], @@ -14864,6 +15173,7 @@ ], "specId" : "ADR-046-resources-host-guest-process-user", "topologicalRank" : 13, + "validation" : "`StrictWireMessage` decode rejects unknown fields on all v3 message types; `service_schema_fingerprint` is stable across builds; `ZoneCallContext` deadline enforcement test; `DaemonSeqpacketTransport` → `ZoneSeqpacketTransport` end-to-end roundtrip; generated v3 stub compile check", "wave" : "W5" }, { @@ -14871,6 +15181,7 @@ "destinations" : [ "`packages/d2b-bus-client/src/`: all above modules; `DaemonClient` → `ZoneClient` (v3 Resource CRUD/Watch verbs); `GuestClient` → `ProcessAttachClient`; `HostSocketConnector` → `ZoneSocketConnector`; `LocalDaemonSession` → `LocalZoneSession`" ], + "detailedDesign" : "`Client` is the core typed async client with bounded retry, wall-clock injection, and cancellation. `ConnectedSession` wraps a `ComponentSessionDriver` and provides `call()`, `open_stream()`, and `close()`. `ComponentSessionConnector` trait decouples connection establishment from the client — v3 `ZoneSocketConnector` implements this for the local Zone runtime public socket. `TargetResolver`/`RouteTable` provides request routing to local vs remote Zone runtimes. `HostSocketConnector::local_daemon_endpoint_identity` provides the peer identity pinning that prevents MITM on the local socket — this is a security-critical invariant; copy verbatim, rename from `d2bd` to `d2b-zonert` uid. `DaemonClient` method table adapts to v3 Resource verbs: `ResourceGet`, `ResourceList`, `ResourceWatch` (streaming), `ResourceCreate`, `ResourceUpdateSpec`, `ResourceUpdateStatus`, `ResourceDelete`. `ServiceHandle`/`MethodHandle`/`GeneratedClient` provide the typed client stub generation pattern. `RetryPolicy`/`RetryClass`/`RemoteErrorKind` error classification is directly reusable. Primary reuse disposition: `adapt`. Preserved source-plan detail: copy and adapt.", "entryContracts" : [ "ADR-046-resources-host-guest-process-user" ], @@ -14886,6 +15197,7 @@ ], "specId" : "ADR-046-resources-host-guest-process-user", "topologicalRank" : 14, + "validation" : "Copy `tests/client.rs` verbatim; add v3-specific tests: `ResourceWatch` streaming teardown; `ZoneSocketConnector` peer-uid mismatch rejection; retry policy respects `RetryClass::Transient`/`Permanent`; `TargetInput`→`ResolvedTarget` for local-only v3 Zone; `local_daemon_endpoint_identity` returns correct v3 zone-rt uid", "wave" : "W5" }, { @@ -14893,6 +15205,7 @@ "destinations" : [ "`packages/d2b-zone-router/src/`: `router.rs` (v3 `ZoneOperationRouter` — idempotency semantics copied verbatim; dedup key namespace adapted from `(realm, principal, node, kind, key)` to `(zone, resource-type, resource-name, verb, idempotency-key)`); `service.rs` (v3 `ZoneServiceLimits`, `ZoneServiceServer`, `ZoneAuditEvent`); `resolver.rs` (v3 `ZoneTargetResolver`, `ZoneEntrypointTable`)" ], + "detailedDesign" : "The idempotency/dedup semantics from `OperationRouter` are security-critical and must be copied exactly: (1) dedup key is the full 5-tuple namespace — reusing a key under a different principal is a conflict, not a replay; (2) expired keys leave tombstones for a no-reuse horizon; (3) same-key/same-request returns the original `operation_id` and recorded result; (4) same-key/different-request returns conflict error fail-closed. These semantics apply to all v3 Resource mutation verbs (Create, UpdateSpec, UpdateStatus, Delete) that carry an idempotency key. `RealmServiceLimits` numeric bounds are copied verbatim: `MAX_DISPATCH_IN_FLIGHT=64` gates concurrent resource mutations per Zone session. `RealmSessionAuthority` principal-binding model (session principal MUST match request principal field, derived in trusted code from authenticated session) maps directly to v3 Zone RBAC: `ZoneCallContext` carries the authenticated `User/` principal from `SO_PEERCRED`; no caller-supplied principal field is accepted. `DurableExecTable`/`DEFAULT_MAX_EXECUTIONS` from `execution.rs` provides the EphemeralProcess in-flight table bound for the Zone router.", "entryContracts" : [ "ADR-046-resources-host-guest-process-user" ], @@ -14908,6 +15221,7 @@ ], "specId" : "ADR-046-resources-host-guest-process-user", "topologicalRank" : 14, + "validation" : "Idempotency replay returns original result; conflict returns error; expired tombstone fails closed; `MAX_DISPATCH_IN_FLIGHT` semaphore back-pressure; principal-binding enforcement (mismatched principal returns auth-denied); `DurableExecTable` capacity limit; v3 5-tuple dedup key golden vector test", "wave" : "W5" }, { @@ -14915,6 +15229,7 @@ "destinations" : [ "`proofs/redb-resource-store-spike/`" ], + "detailedDesign" : "Implements SPIKE-01 and SPIKE-02: the eight-table schema, fair write queue, blocking store-actor, watch registrar, and hint bus described in those two spike entries Primary reuse disposition: `adapt`. Preserved source-plan detail: `adapt` (the atomic-write/idempotency discipline in `storage.rs`/`sync.rs` is adapted into the spike's write-transaction algorithm; redb itself is used unmodified).", "entryContracts" : [ "ADR-046-feasibility-and-spikes" ], @@ -14928,6 +15243,7 @@ ], "specId" : "ADR-046-feasibility-and-spikes", "topologicalRank" : 11, + "validation" : "SPIKE-01 metrics (1)-(5) and SPIKE-02 metrics (1) across all 3 concurrency profiles, per those entries' exact pass/fail thresholds", "wave" : "W7" }, { @@ -14935,6 +15251,7 @@ "destinations" : [ "`proofs/process-fastlaunch-spike/`" ], + "detailedDesign" : "Implements SPIKE-03: the fake Process controller loop, fake `ProcessLaunchEffectPort`, and the 1/10/100-concurrency commit-to-launch-attempt and next-dispatch-independence benchmarks Primary reuse disposition: `adapt`. Preserved source-plan detail: `adapt` (current DAG ordering/readiness concepts are adapted into the spike's per-resource single-flight/parallel-semaphore loop).", "entryContracts" : [ "ADR-046-feasibility-and-spikes" ], @@ -14949,6 +15266,7 @@ ], "specId" : "ADR-046-feasibility-and-spikes", "topologicalRank" : 12, + "validation" : "SPIKE-03 metrics (1)-(3) and thresholds", "wave" : "W7" }, { @@ -14956,6 +15274,7 @@ "destinations" : [ "`proofs/effectport-async-spike/`" ], + "detailedDesign" : "Implements SPIKE-04: the four fake EffectPort traits, the deliberately slow blocking-primitive backends, and the current-thread-runtime heartbeat-jitter detector", "entryContracts" : [ "ADR-046-feasibility-and-spikes" ], @@ -14969,6 +15288,7 @@ ], "specId" : "ADR-046-feasibility-and-spikes", "topologicalRank" : 11, + "validation" : "SPIKE-04 heartbeat-jitter metric and threshold", "wave" : "W7" }, { @@ -14976,6 +15296,7 @@ "destinations" : [ "`proofs/provider-packaging-spike/`" ], + "detailedDesign" : "Implements SPIKE-05: the two-binary crate, hand-authored manifest, fake `ProviderDeployment`, and the `cargo metadata` dependency-edge check Primary reuse disposition: `adapt`. Preserved source-plan detail: `adapt` (the crate-layout policy check reuses the same `src/`/`tests/`/`integration/`/`README.md` structure already enforced elsewhere in this repository's workspace policy tests).", "entryContracts" : [ "ADR-046-feasibility-and-spikes" ], @@ -14989,6 +15310,7 @@ ], "specId" : "ADR-046-feasibility-and-spikes", "topologicalRank" : 11, + "validation" : "SPIKE-05 metrics (1)-(4) across 20 repeated randomized-order manifest loads", "wave" : "W7" }, { @@ -14996,6 +15318,7 @@ "destinations" : [ "`proofs/bus-routing-noise-spike/`, `proofs/transport-opaque-streams-spike/`, `proofs/credential-kk-e2e-spike/`" ], + "detailedDesign" : "Implements SPIKE-06 (exact-addressed routing + per-recipient Noise isolation), SPIKE-07 (Unix/vsock/relay-shaped opaque byte-stream conformance across 3 backends), and SPIKE-08 (Credential Provider → consumer Provider KK delivery with the 13-field binding contract) Primary reuse disposition: `adapt`. Preserved source-plan detail: `copy-unchanged` for the Noise/record/transport machinery (path-dependency on a pinned local checkout of `a1cc0b2d`); `adapt` for the fake router/relay/credential-delivery wrapper code that SPIKE-06/07/08 add on top.", "entryContracts" : [ "ADR-046-feasibility-and-spikes" ], @@ -15009,6 +15332,7 @@ ], "specId" : "ADR-046-feasibility-and-spikes", "topologicalRank" : 11, + "validation" : "SPIKE-06 metrics (1)-(3), SPIKE-07 metrics (1)-(4) against the exact numeric gates already committed in the transport-unix/vsock dossiers, SPIKE-08 metrics (1)-(6)", "wave" : "W7" }, { @@ -15016,6 +15340,7 @@ "destinations" : [ "`proofs/provider-state-export-spike/`, `proofs/volume-policy-spike/`" ], + "detailedDesign" : "Implements SPIKE-09 (optional declared state-Volume creation order, guest-local/host-backed-guest placement, virtiofs Export child ownership) and SPIKE-10 (Volume ACL/`sourcePolicyId`/quota/lifecycle-marker policy conformance)", "entryContracts" : [ "ADR-046-feasibility-and-spikes" ], @@ -15029,6 +15354,7 @@ ], "specId" : "ADR-046-feasibility-and-spikes", "topologicalRank" : 11, + "validation" : "SPIKE-09 metrics (1)-(4); SPIKE-10 metrics (1)-(5), zero-tolerance on path leakage", "wave" : "W7" }, { @@ -15036,6 +15362,7 @@ "destinations" : [ "`proofs/process-provider-conformance-spike/`" ], + "detailedDesign" : "Implements SPIKE-11: the shared `ProcessProviderHarness` trait, the minijail-shaped `clone3(CLONE_PIDFD)` launcher, and the systemd transient-user-scope launcher, plus the identity-drift/quarantine and clean-exit cases", "entryContracts" : [ "ADR-046-feasibility-and-spikes" ], @@ -15049,6 +15376,7 @@ ], "specId" : "ADR-046-feasibility-and-spikes", "topologicalRank" : 11, + "validation" : "SPIKE-11 metrics (1)-(4), zero-tolerance on false adoption", "wave" : "W7" }, { @@ -15056,6 +15384,7 @@ "destinations" : [ "`proofs/nix-authoring-spike/`" ], + "detailedDesign" : "Implements SPIKE-12: the minimal flake, the two synthetic ResourceTypes, the hand-written committed schemas, the standalone `gen-schemas`-shaped drift check, and the two-generation removed-resource cleanup simulation", "entryContracts" : [ "ADR-046-feasibility-and-spikes" ], @@ -15069,6 +15398,7 @@ ], "specId" : "ADR-046-feasibility-and-spikes", "topologicalRank" : 11, + "validation" : "SPIKE-12 metrics (1)-(5), byte-for-byte reproducibility across 3 hermetic builds", "wave" : "W7" }, { @@ -15076,6 +15406,7 @@ "destinations" : [ "`proofs/cli-discovery-spike/`, `proofs/clean-cutover-spike/`" ], + "detailedDesign" : "Implements SPIKE-13 (dynamic Provider-projection discovery, bounds, latency isolation) and SPIKE-14 (zero v2 dispatch, fresh Zone bootstrap ignoring legacy state)", "entryContracts" : [ "ADR-046-feasibility-and-spikes" ], @@ -15089,6 +15420,7 @@ ], "specId" : "ADR-046-feasibility-and-spikes", "topologicalRank" : 11, + "validation" : "SPIKE-13 metrics (1)-(7); SPIKE-14 metrics (1)-(3), zero-tolerance on legacy-file access", "wave" : "W7" }, { @@ -15096,6 +15428,7 @@ "destinations" : [ "`proofs/e2e-composition-spike/`" ], + "detailedDesign" : "Implements SPIKE-15: the three representative compositions (local/cloud-hypervisor, cloud/azure, interaction/shell-terminal-or-wayland), wired from the fakes built by `-001` through `-009`, plus the combined 3-Zone aggregate RSS measurement", "entryContracts" : [ "ADR-046-feasibility-and-spikes" ], @@ -15111,6 +15444,7 @@ ], "specId" : "ADR-046-feasibility-and-spikes", "topologicalRank" : 12, + "validation" : "SPIKE-15 metrics (1)-(4) across all three compositions", "wave" : "W7" }, { @@ -15118,6 +15452,7 @@ "destinations" : [ "`proofs/test-runtime-budget-spike/`; the committed baseline ledger consumed by `ADR046-delivery-007`" ], + "detailedDesign" : "Establishes the D094 measurement baseline: records the reference runner class, repetition count, and per-test/crate/shard p95 for a representative hermetic crate; proves the §10.16 budgets (individual normal test p95 ≤50 ms, per-crate `--lib --tests` ≤2 s, Layer-1 hermetic shard ≤60 s) are met on the reference runner and that an injected slow/sleeping test is detected as a regression", "entryContracts" : [ "ADR-046-feasibility-and-spikes" ], @@ -15132,6 +15467,7 @@ ], "specId" : "ADR-046-feasibility-and-spikes", "topologicalRank" : 14, + "validation" : "The representative crate meets every budget; a synthetic slow/sleep/process/network test is flagged; cold compile time is recorded on a separate line and excluded from the execution budgets", "wave" : "W7" }, { @@ -15139,6 +15475,7 @@ "destinations" : [ "`packages/d2b-provider-device-gpu/` with `src/`, `tests/`, `integration/`, `README.md`; add to workspace `Cargo.toml` members list (alphanumerically sorted)" ], + "detailedDesign" : "Crate scaffold: `Cargo.toml` with `d2b-host`, `d2b-contracts`, `d2b-provider-toolkit`, `d2b-core` dependencies; `lib.rs` exporting controller binary entry points; `error.rs` with `DeviceGpuError` closed-set enum; placeholder `controller.rs` Primary reuse disposition: `extract`. Preserved source-plan detail: `extract` both argv files into `d2b-provider-device-gpu/src/argv.rs` as re-exports; do not copy logic.", "entryContracts" : [ "ADR-046-provider-device-gpu" ], @@ -15152,13 +15489,15 @@ ], "specId" : "ADR-046-provider-device-gpu", "topologicalRank" : 11, + "validation" : "`cargo build -p d2b-provider-device-gpu`; workspace policy crate-layout check passes", "wave" : "W6" }, { "blockers" : [], "destinations" : [ - "`packages/d2b-provider-device-gpu/src/controller.rs`" + "`packages/d2b-provider-device-gpu/src/{controller.rs,telemetry.rs}`" ], + "detailedDesign" : "Five triggers: `spec-generation-changed`, `deletion-requested`, `dependency-changed`, `scheduled-observe`, `owned-resource-changed`. Each trigger handler writes optimistic `ResourceMutationBatch`. Status writer in `status.rs`. Async watch task + per-resource reconcile tasks. Independent resources in parallel. Telemetry uses only closed `mode`/`video_sidecar`/`arbitration` semantics; `d2b.zone` and `d2b.provider` remain resource attributes and no Zone/resource-name-derived key is a metric label. Primary reuse disposition: `adapt`. Preserved source-plan detail: `adapt` — implement the five-trigger reconcile loop using Provider toolkit async reconciler.", "entryContracts" : [ "ADR-046-provider-device-gpu" ], @@ -15173,6 +15512,7 @@ ], "specId" : "ADR-046-provider-device-gpu", "topologicalRank" : 12, + "validation" : "`cargo test -p d2b-provider-device-gpu --test combined_reconcile`; all five trigger handlers must reach their expected output state; structural metric descriptor test asserts exact absence of `vm`, `zone`, `zone_id`, `zone_uid`, and resource-name-derived keys plus GPU Device/Zone-name canary absence while preserving `d2b.zone` resource attributes", "wave" : "W6" }, { @@ -15180,6 +15520,7 @@ "destinations" : [ "`packages/d2b-provider-device-gpu/src/probe.rs`" ], + "detailedDesign" : "Call `GpuEffectPort::probe_drm_device(selector)` on each `scheduled-observe` trigger; the effect port resolves device presence against the trusted device table and returns a presence/health result without exposing raw sysfs or device paths to the controller. Three-strike failure counter; `observe_interval_secs` (10–60, default 30); emit `DevicePresent` condition and update `lastProbedAt`.", "entryContracts" : [ "ADR-046-provider-device-gpu" ], @@ -15194,6 +15535,7 @@ ], "specId" : "ADR-046-provider-device-gpu", "topologicalRank" : 13, + "validation" : "`tests/conformance.rs` contains probe-mock path; `cargo test` passes", "wave" : "W6" }, { @@ -15201,6 +15543,7 @@ "destinations" : [ "`packages/d2b-provider-device-gpu/src/arbitration.rs`" ], + "detailedDesign" : "On `spec-generation-changed` and each new claim: check `arbitration` vs `maxConcurrentClaims` vs current `holderRefs` length. Exclusive: reject any second claim with `ClaimConflict` condition, set requesting Device phase `Degraded`. Shared render-node: accept up to `maxConcurrentClaims`. Admission: `shared + renderNodeOnly=false` fails with `shared-arbitration-requires-render-node-only`.", "entryContracts" : [ "ADR-046-provider-device-gpu" ], @@ -15215,6 +15558,7 @@ ], "specId" : "ADR-046-provider-device-gpu", "topologicalRank" : 13, + "validation" : "`cargo test -p d2b-provider-device-gpu --test arbitration_conflict`; `cargo test -p d2b-provider-device-gpu --test render_node_enforcement`", "wave" : "W6" }, { @@ -15222,6 +15566,7 @@ "destinations" : [ "`packages/d2b-provider-device-gpu/src/worker_gpu.rs`" ], + "detailedDesign" : "Build and commit `Process` resource record with `template: gpu-worker` or `template: render-node-worker`; set `sandbox.seccompClass` (`w1-gpu` or `w1-gpu-render-node`), `sandbox.userNamespace: {mappingClass: process-principal-root}` (uid/gid resolved privately by core from signed worker template — controller does NOT write numeric values), `sandbox.namespaceClasses`, `sandbox.capabilityClasses=[]`, `sandbox.startRoot=false`; set `deviceUsage[{deviceRef,access,purpose}]`, `networkUsage: null`, `endpoints[{name,transport,purpose}]`, `budget` (including `pids` and `fds` bounded limits), `readiness` (with `class`, `initialDelay`, `timeout`, `failureThreshold`, `successThreshold`), and `restartPolicy` (with `class`, `backoffBase`, `backoffMax`, `backoffMultiplier`, `maxRestarts`, `resetAfter`). Provider/system-minijail validates and resolves the LaunchTicket and sends effect requests via `MinijailProcessEffectPort`; the core EffectPort adapter routes them to the **privileged broker** which performs `SpawnRunner`, `OpenDevice`, `clone3`, `uid_map`/`gid_map` writes, and fd transfer — the device-gpu controller does not have execution authority or fd access. `crossDomainTrusted` gating: the signed descriptor is static; `crossDomainTrusted` is projected from the Device setting into the LaunchTicket by Provider/system-minijail, which omits `GpuContextType::CrossDomain` from runtime argv when false. Primary reuse disposition: `adapt`. Preserved source-plan detail: `extract` argv builder logic into `argv.rs` as re-export from `d2b-host` (used by Provider/system-minijail at LaunchTicket resolution time; the signed component descriptor is static and is not rewritten per Device); `adapt` device allowlist token set from `bundle_resolver.rs` into `worker_gpu.rs` `GPU_DEVICE_ALLOWLIST` constant for `deviceUsage` population.", "entryContracts" : [ "ADR-046-provider-device-gpu" ], @@ -15236,6 +15581,7 @@ ], "specId" : "ADR-046-provider-device-gpu", "topologicalRank" : 13, + "validation" : "`cargo test -p d2b-provider-device-gpu`; `cargo test -p d2b-contract-tests --test minijail_gpu` continues to pass", "wave" : "W6" }, { @@ -15243,6 +15589,7 @@ "destinations" : [ "`packages/d2b-provider-device-gpu/src/worker_video.rs`, `tests/wire_constant_snapshot.rs`" ], + "detailedDesign" : "Controller creates `Process/device--video` only after `GpuWorkerReady=True`. `worker_video.rs` builds `VideoArgvInput` from resolved device spec and signed descriptor binary path. Validates `wire_contract_snapshot()` matches committed golden at startup; fails closed if mismatch (error `device-wire-contract-mismatch`). NVIDIA device gating: include `nvidia-ctl`, `nvidia-device`, `nvidia-uvm` tokens in `deviceUsage[]` entries only when `videoNvidiaDecode=true`; the **privileged broker** opens the fds when executing the effect request from the core EffectPort adapter. Distinct allocator-assigned principal enforced by LaunchTicket (internal invariant; not expressed in the resource spec); `template: video-worker` descriptor declares no Wayland/audio endpoint capability. `sandbox.seccompClass: w1-video`; `sandbox.namespaceClasses` includes `pid`; `userNamespace: null` (explicit, tested invariant). Primary reuse disposition: `adapt`. Preserved source-plan detail: `extract` argv generator (re-export from `argv.rs`); `copy-unchanged` wire-contract constants into `tests/wire_constant_snapshot.rs` golden comparison.", "entryContracts" : [ "ADR-046-provider-device-gpu" ], @@ -15257,6 +15604,7 @@ ], "specId" : "ADR-046-provider-device-gpu", "topologicalRank" : 14, + "validation" : "`cargo test -p d2b-provider-device-gpu --test video_dependency`; `cargo test -p d2b-provider-device-gpu --test wire_constant_snapshot`; `cargo test -p d2b-contract-tests --test video_binary_contract` continues to pass", "wave" : "W6" }, { @@ -15264,6 +15612,7 @@ "destinations" : [ "`nixos-modules/assertions.nix` (new GPU Device eval assertions); `tests/unit/nix/cases/device-gpu-eval.nix` (new Nix eval case); committed settings schema `docs/reference/schemas/v3/providers/device-gpu.settings.json`" ], + "detailedDesign" : "Eval assertions as documented in § Nix configuration / Eval-time assertions. Canonical JSON golden as documented. Settings schema drift gate via `make test-drift`. `d2b.vms..graphics.*` options are deprecated (emit deprecation warning) until a transition generation removes them; they are not removed in the same commit that adds the Device spec option. Primary reuse disposition: `adapt`. Preserved source-plan detail: `adapt` — map old `d2b.vms..graphics.*` fields to `d2b.zones..resources.` Device spec settings fields; add eval assertions.", "entryContracts" : [ "ADR-046-provider-device-gpu" ], @@ -15278,6 +15627,7 @@ ], "specId" : "ADR-046-provider-device-gpu", "topologicalRank" : 12, + "validation" : "`nix-unit tests/unit/nix/cases/device-gpu-eval.nix`; `make test-drift`; `make test-flake`", "wave" : "W6" }, { @@ -15285,6 +15635,7 @@ "destinations" : [ "`packages/d2b-provider-device-gpu/` component descriptor; controller/status tests" ], + "detailedDesign" : "Do **not** declare a controller Provider state Volume. The device-gpu component descriptor declares an empty ProviderStateSet; controller and worker Process templates contain no `/state` mount. Bounded non-secret operational state is published to Device/Provider status and the core Operation ledger. GPU has no Device-payload Volume; render-node access remains a Device attachment resolved by LaunchTicket and broker policy. Primary reuse disposition: `create`. Preserved source-plan detail: `new` — status-first state assertions in the component descriptor and controller tests.", "entryContracts" : [ "ADR-046-provider-device-gpu" ], @@ -15299,6 +15650,7 @@ ], "specId" : "ADR-046-provider-device-gpu", "topologicalRank" : 12, + "validation" : "`cargo test -p d2b-provider-device-gpu --test status_state`; component descriptor golden has no Provider state Volume declaration; controller Process template has no `/state` mount; ProviderStateSet query is empty; status/core-ledger fields carry bounded operational observations", "wave" : "W6" }, { @@ -15306,6 +15658,7 @@ "destinations" : [ "`packages/d2b-provider-device-gpu/README.md`" ], + "detailedDesign" : "Must include: Provider identity, supported ResourceTypes, controller/service/worker binary descriptions, placement (Host, system domain), dependencies (system-minijail, volume-local, observability-otel), RBAC roles, security model summary, state/telemetry contract, build command (`cargo build -p d2b-provider-device-gpu`), test commands (`cargo test -p d2b-provider-device-gpu`), integration command (`make test-integration`), hardware test note (see `integration/README.md`), standalone-repository consumption stub.", "entryContracts" : [ "ADR-046-provider-device-gpu" ], @@ -15320,6 +15673,7 @@ ], "specId" : "ADR-046-provider-device-gpu", "topologicalRank" : 12, + "validation" : "`make test-policy` (workspace crate layout policy check)", "wave" : "W6" }, { @@ -15327,6 +15681,7 @@ "destinations" : [ "`packages/d2b-provider-system-core/src/{host_reconciler.rs,host_status.rs,host_process_audit.rs}`; adapted `nixos-modules/unsafe-local-workloads-json.nix`; `packages/d2b-provider-system-core/tests/host_posture_contract.rs`" ], + "detailedDesign" : "`Provider/system-core` reconciler: (1) On user-only `Host` resource creation (`defaultDomain=user`, `allowedDomains=[user]`), set `status.isolationPosture = \"none\"` and `status.isolationPostureMessage = \"...\"` unconditionally; reject any operator-supplied value for these fields. Host resources with other execution policies do not receive `isolationPosture`. (2) On every user-only Host process launch: emit `ProcessEffect{event:\"launch\", provider:\"system-core-user\", domain:\"user\", no_isolation:true, ...}` audit record. (3) On every user-only Host process stop: emit `ProcessEffect{event:\"stop\", ...}`. (4) `d2b zone list`/`inspect` CLI renders `⚠ no isolation boundary (user domain)` annotation only for `Host` resources with `isolationPosture: \"none\"`; annotation is not suppressible. (5) `isolation-posture-declared` doctor check: passes when user-only `Host` resource status has `isolationPosture: \"none\"`; omitted when Zone has no user-only `Host` resources. (6) `no_isolation=true` is emitted in `ProcessEffect` records only; it does not appear in any OTEL span attribute, log field, or metric label. Primary reuse disposition: `adapt`. Preserved source-plan detail: adapt `UnsafeLocalWorkload` private-bundle contract for the `Host` resource spec payload; adapt `HelperRegistry::allowed_uids` constraint as `defaultUserRef=User/` validation; adapt Nix `unsafe-local-workloads-json.nix` emitter for the new Host resource shape; gap-fill: add `ProcessEffect{no_isolation:true}` at `dispatch_launch` / stop call sites.", "entryContracts" : [ "ADR-046-telemetry-audit-and-support" ], @@ -15342,6 +15697,7 @@ ], "specId" : "ADR-046-telemetry-audit-and-support", "topologicalRank" : 11, + "validation" : "`host_posture_contract.rs` tests from the Host posture tests section of this spec; `d2b-contract-tests/tests/policy_telemetry_redaction.rs` asserts `no_isolation` key absent from all span/metric/log surfaces", "wave" : "W5" }, { @@ -15349,6 +15705,7 @@ "destinations" : [ "`packages/d2b-contracts/src/v3/identity.rs`, `packages/d2b-contracts/src/v3/resource_ref.rs`" ], + "detailedDesign" : "Add ZoneId, ResourceTypeName, ResourceName, ResourceUid, ResourceRef, generation/revision newtypes, exact parsing/serde/Debug/redaction, and golden vectors Primary reuse disposition: `adapt`. Preserved source-plan detail: extract and adapt.", "entryContracts" : [ "ADR-046-terminology-and-identities" ], @@ -15362,6 +15719,7 @@ ], "specId" : "ADR-046-terminology-and-identities", "topologicalRank" : 2, + "validation" : "Rust property/vector tests; pure-Nix vector parity; malformed/collision/UID-recreate tests", "wave" : "W0" }, { @@ -15369,6 +15727,7 @@ "destinations" : [ "`nixos-modules/options-zones.nix`, `nixos-modules/resources.nix`, `nixos-modules/index.nix`" ], + "detailedDesign" : "Validate Zone names, ResourceTypes/names/refs, shared Host/Guest ExecutionPolicy, and canonical sorted resource identities", "entryContracts" : [ "ADR-046-terminology-and-identities" ], @@ -15383,6 +15742,7 @@ ], "specId" : "ADR-046-terminology-and-identities", "topologicalRank" : 3, + "validation" : "nix-unit vectors and rendered contract tests", "wave" : "W0" }, { @@ -15390,6 +15750,7 @@ "destinations" : [ "packages/d2b-provider-credential-managed-identity/src/{controller.rs,agent.rs}; packages/d2b-provider-credential-managed-identity/{controller/main.rs,agent/main.rs}; packages/d2b-provider-credential-managed-identity/tests/topology.rs" ], + "detailedDesign" : "Implement the controller/agent process split: separate `d2b-managed-identity-controller` binary with no IMDS client and no KK delivery, and `d2b-managed-identity-agent` binary with injected IMDS client via effect port and KK delivery. Controller manages Credential resources, spawns/monitors agent Processes, uses canonical Process templates, attaches LaunchTickets projecting `imdsEndpointAlias` and `credentialRef`, monitors agent Process health with bounded backoff, performs Deleted-phase cleanup without emitting Deleted closure audit, and applies D087 status-first state with no Provider state Volume. Agent validates `ExactSdkConsumer` via `AuthenticatedSubjectContext`, serves token-delivery methods, terminates Noise_KK delivery sessions, reports lease state, declares no direct IMDS egress, and keeps token bytes transient. Primary reuse disposition: `adapt`. Preserved source-plan detail: copy and adapt the main managed-identity Provider; replace v2 provider registry/session assumptions with v3 controller/agent Process topology and `d2b.credential.v3` service.", "entryContracts" : [ "ADR-046-provider-credential-managed-identity" ], @@ -15405,6 +15766,7 @@ ], "specId" : "ADR-046-provider-credential-managed-identity", "topologicalRank" : 11, + "validation" : "`tests/topology.rs`; `integration/host-guest-placement.nix`; `make test-rust`; `make test-integration`; `make test-host-integration`", "wave" : "W6" }, { @@ -15412,6 +15774,7 @@ "destinations" : [ "`packages/d2b-provider-system-minijail/src/sandbox_compiler.rs`" ], + "detailedDesign" : "Accept `SandboxSpec` from common contracts; compile NamespaceClass/CapabilityClass/SeccompClass/UserNamespaceSpec/mount/environment/rlimit/umask into a versioned `CompiledSandboxPlan`; compute `sandboxRevisionDigest`; all rejection conditions from §12.1; no raw bitmask/BPF/argv/path in any output type; golden round-trip test vectors Primary reuse disposition: `adapt`. Preserved source-plan detail: EXTRACT/ADAPT.", "entryContracts" : [ "ADR-046-provider-system-minijail" ], @@ -15427,6 +15790,7 @@ ], "specId" : "ADR-046-provider-system-minijail", "topologicalRank" : 11, + "validation" : "`tests/sandbox_compilation.rs`; `tests/schema.rs`; golden vectors", "wave" : "W6" }, { @@ -15434,6 +15798,7 @@ "destinations" : [ "`packages/d2b-provider-system-minijail/src/launch.rs`" ], + "detailedDesign" : "LaunchTicket construction with compiled sandbox/budget/mount digests; ticket verification on ProviderSupervisor receipt; `d2b.supervisor.v3/IssueLaunchTicket` service call; expired/revoked/malformed ticket rejection", "entryContracts" : [ "ADR-046-provider-system-minijail" ], @@ -15448,6 +15813,7 @@ ], "specId" : "ADR-046-provider-system-minijail", "topologicalRank" : 12, + "validation" : "`tests/lifecycle.rs`; `tests/fault_injection.rs`; `tests/fast_path.rs`", "wave" : "W6" }, { @@ -15455,6 +15821,7 @@ "destinations" : [ "Broker-side: `d2b-priv-broker` retains `SpawnRunner` op, invoked by the `MinijailProcessEffectPort` implementation owned by core/ProviderSupervisor; Provider-side: `packages/d2b-provider-system-minijail/src/launch.rs` calls `MinijailProcessEffectPort` with opaque Process/LaunchTicket/profile IDs; `user_ns.rs` implements the user namespace pre-establishment protocol" ], + "detailedDesign" : "Linux ≥5.14 and delegated-leaf `cgroup.kill` platform gate; `clone3(CLONE_PIDFD | CLONE_INTO_CGROUP)` with pre-declared cgroup leaf FD; broker retained as child parent and sole `waitid(P_PIDFD)`/reap/exit-status owner; verified duplicate returned privately to ProviderSupervisor for poll/readiness and exact-main `pidfd_send_signal`; anchored `cgroup.kill` write for unambiguous intentional teardown; user namespace pre-establishment sequence (§7.7) when `userNamespace` set; host UID 0 rejection; parent name-to-inode re-validation; zero-host-capability invariant (ADR 0021); `MinijailProcessEffectPort` privately maps opaque IDs to SpawnRunner/OpenDevice/clone3/uid-map/FD effects; Provider crate imports no broker service/client/DTO", "entryContracts" : [ "ADR-046-provider-system-minijail" ], @@ -15469,6 +15836,7 @@ ], "specId" : "ADR-046-provider-system-minijail", "topologicalRank" : 12, + "validation" : "`tests/fault_injection.rs`; `tests/platform_gate.rs`; `tests/broker_wait_contract.rs`; `tests/cgroup_kill_finalize.rs`; `integration/clone3_pidfd/`; `integration/user_namespace/`; `integration/broker_parent_reap/`; `integration/cgroup_kill_subtree/`; `integration/kernel_platform_gate/`", "wave" : "W6" }, { @@ -15476,6 +15844,7 @@ "destinations" : [ "Broker-side parent wait/reap and typed terminal relay in `packages/d2b-priv-broker/src/`; non-parent observation/status consumption in `packages/d2b-provider-system-minijail/src/{pidfd,wait}.rs`" ], + "detailedDesign" : "Broker that called `clone3` alone calls `waitid(P_PIDFD)`, collects exit status, and reaps exactly once; ProviderSupervisor `AsyncFd` readability is a hint only and never a wait/status source; controller consumes the identity-bound broker relay and holds no raw pidfd; ProviderSupervisor duplicate reacquisition is dispatched through a bounded blocking adapter with explicit timeout; pidfd never serialized; verified broker/ProviderSupervisor holder retains exact-main `pidfd_send_signal`; no PID/PGID fallback; graceful deadline followed by mandatory anchored leaf `cgroup.kill`; empty-leaf proof before rmdir; exit class classification (clean-exit/crash/signal/timeout/unknown) Primary reuse disposition: `adapt`. Preserved source-plan detail: EXTRACT/ADAPT.", "entryContracts" : [ "ADR-046-provider-system-minijail" ], @@ -15490,6 +15859,7 @@ ], "specId" : "ADR-046-provider-system-minijail", "topologicalRank" : 13, + "validation" : "`tests/lifecycle.rs`; `tests/broker_wait_contract.rs` (only clone3 parent calls waitid/reaps; poll readability cannot supply status); `tests/cgroup_kill_finalize.rs` (setsid descendant and PGID reuse); `tests/redaction.rs` (PID never in log/status/audit); `tests/blocking_adapter.rs` (duplicate/status relay via adapter; timeout → error)", "wave" : "W6" }, { @@ -15497,6 +15867,7 @@ "destinations" : [ "`packages/d2b-provider-system-minijail/src/` — controller binary entry point; reconcile loop; adoption; quarantine; bootstrap authz; health/status; restart; finalize" ], + "detailedDesign" : "Full Process/EphemeralProcess reconcile algorithm (§8); fast path ≤5/≤20 ms gates; spawn via `MinijailProcessEffectPort` (opaque IDs; no broker DTO imported); adoption algorithm (§8.5) with `/proc` reads, cgroup enumeration, and original-broker-parent verification via bounded blocking adapters; quarantine on ambiguity; quarantine reuse blocked until externally established process-absence proof or full Zone reset; no signal or cgroup.kill write to quarantined/ambiguous identity; restart/backoff driven only by broker-relayed terminal status; finalize (§8.6) with exact-main SIGTERM, bounded grace, mandatory cgroup.kill, broker wait/reap, empty-leaf proof, and no PGID ownership; EphemeralProcess continuation recovery (§9); bootstrap authz scope (§3); post-bootstrap RBAC; metric label closed-set enforcement (no `zone` label); controller writes status only on Process/EphemeralProcess resources; Provider resource status aggregated by core; the controller declares no Provider state Volume and mounts none — its bounded non-secret operational state lives in `status`/the core Operation ledger (§5.1, D087) and running units are re-adopted from cgroup leaves + fresh pidfds on restart", "entryContracts" : [ "ADR-046-provider-system-minijail" ], @@ -15516,6 +15887,7 @@ ], "specId" : "ADR-046-provider-system-minijail", "topologicalRank" : 14, + "validation" : "`tests/lifecycle.rs`; `tests/ephemeral_lifecycle.rs`; `tests/conformance.rs`; `tests/adoption_quarantine.rs`; `tests/broker_wait_contract.rs`; `tests/cgroup_kill_finalize.rs`; `tests/platform_gate.rs`; `tests/bootstrap_authz.rs`; `tests/fast_path.rs`; `tests/blocking_adapter.rs`; `integration/adoption_restart/`; `integration/quarantine_scenario/`; `integration/broker_parent_reap/`; `integration/cgroup_kill_subtree/`; `integration/kernel_platform_gate/`; `integration/latency_gate/`; shared conformance suite in `d2b-process-conformance`", "wave" : "W6" }, { @@ -15523,6 +15895,7 @@ "destinations" : [ "`nixos-modules/` — v3 Nix `Process`/`EphemeralProcess` resource authoring; Provider catalog entry; `docs/reference/schemas/v3/Process.json`; `docs/reference/schemas/v3/EphemeralProcess.json`; `make test-drift` schema drift gate" ], + "detailedDesign" : "Nix module accepts `d2b.zones..resources.` with `type = \"Process\"` or `\"EphemeralProcess\"`; eval-time validation rules (§16.4); build-time JSON validation (§16.5); artifact catalog integration; cleanup contract tests (§16.5)", "entryContracts" : [ "ADR-046-provider-system-minijail" ], @@ -15537,6 +15910,7 @@ ], "specId" : "ADR-046-provider-system-minijail", "topologicalRank" : 15, + "validation" : "`nix-unit` eval cases for every validation rule; schema drift gate; `tests/schema.rs`", "wave" : "W6" }, { @@ -15544,6 +15918,7 @@ "destinations" : [ "`packages/d2b-contracts/src/v3/network.rs`: NetworkSpec, NetworkStatus, AttachmentSpec, AttachmentStatus, ExternalAttachmentSpec, ExternalAttachmentStatus, PortForwardSpec, NetworkConditionType; `packages/d2b-contracts/src/v3/ifname.rs`: IfName newtype, derivation, collision detection (extracted from `d2b-host/src/ifname.rs`). Also defines `User/net-local-controller` as a proper Resource with explicit lifecycle: `Provider/network-local`'s Nix package/module provisions the reserved `net-local-controller` OS account with a private fixed UID/GID in Host prerequisites and in the generic net-VM nixos-system artifact (same account, same UID/GID inside the Guest); the network-local controller creates and owns the User Resource (`spec.osUsername: net-local-controller`, `ownerRef: Provider/network-local`, `managedBy: controller`); `Provider/system-core` verifies the account via NSS lookup and reconciles the User Resource to Ready — it does not provision the OS account. No numeric UID/GID enters any ResourceSpec field, authz check, or audit record; `User.status` MAY carry diagnostic `uid`/`gid` values discovered by NSS lookup, but those are informational only and are never authorization inputs. The network-local controller waits for `User/net-local-controller` to reach `Ready` before creating any config Volume (reconcile precondition, not a bootstrap side effect)." ], + "detailedDesign" : "Strict ResourceEnvelope with Network-specific spec/status. IfName newtype: IFNAMSIZ-1 validated, FNV-1a 64-bit derivation, base32 Crockford, 8-char suffix, bridge/tap role prefixes, detect_collisions over IfNameMapping slice. cidrOverlaps: pure Rust IPv4 arithmetic, same algorithm as lib.nix. NetworkSpec validators: /24 lanCidr with .0 base, /30 uplinkCidr, unique attachment indices 2–250, default hostBlocklist enforcement. Primary reuse disposition: `adapt`. Preserved source-plan detail: extract and adapt.", "entryContracts" : [ "ADR-046-resources-network" ], @@ -15557,6 +15932,7 @@ ], "specId" : "ADR-046-resources-network", "topologicalRank" : 9, + "validation" : "Golden JSON/CBOR vectors; CIDR overlap property tests; IfName collision and derivation determinism tests; default hostBlocklist enforcement; attachment index uniqueness; `User/net-local-controller` User resource lifecycle/readiness test: controller creates User Resource with `spec.osUsername = \"net-local-controller\"` (`ownerRef: Provider/network-local`); controller waits for User resource to reach `Ready` before proceeding; controller aborts with `ConfigVolumeReady=False/user-not-ready` if User resource is not Ready; verifies no numeric UID/GID appears in the Resource spec, authz check, or audit record; verifies that any diagnostic `uid`/`gid` in `User.status` is never used as an authorization input", "wave" : "W4" }, { @@ -15564,6 +15940,7 @@ "destinations" : [ "`packages/d2b-provider-network-local/src/ifname.rs`, `bridge_port.rs`, `nftables.rs`, `routes.rs`, `netlink.rs`" ], + "detailedDesign" : "Move IfName derivation to `d2b-contracts` (ADR046-network-001); keep bridge_port, nftables, routes, netlink in Provider crate. nftables: retain the Network-owned `inet d2b` chain layout, ownership markers, and coexistence matrix; emit no USBIP/TCP-3240 rule and compute drift over only the Network UID ownership projection. routes: adapt dnsmasq-bound check to use Network status instead of `HostJson.environments`. netlink: keep IPv6-off sequence; add defense-in-depth re-application path. Primary reuse disposition: `adapt`. Preserved source-plan detail: extract (ifname, bridge_port, nftables) into shared network-local Provider library; adapt (routes, netlink) into controller observe loop.", "entryContracts" : [ "ADR-046-resources-network" ], @@ -15578,6 +15955,7 @@ ], "specId" : "ADR-046-resources-network", "topologicalRank" : 10, + "validation" : "Existing `bridge_port::tests::readback_matches_defaults`, `ops::tap::tests::set_bridge_port_flags_readback_drift_fails_closed`, `netlink::tests::ipv6_off_sequence_runs_in_order`, nftables coexistence matrix tests; all pinned in `tests/golden/pinned/host-prepare-network.txt` and `tests/golden/pinned/net-canaries.txt`", "wave" : "W4" }, { @@ -15585,6 +15963,7 @@ "destinations" : [ "`packages/d2b-provider-network-local/` — artifact catalog integration for net-VM nixos-system artifact resolution; `packages/d2b-provider-network-local/nix/` — default net-VM NixOS module (parameterized successor to net.nix), built and registered as a nixos-system artifact in `d2b.artifacts`" ], + "detailedDesign" : "`Network.spec.netVmSystemArtifactId` is REQUIRED. It must reference a declared `d2b.artifacts` entry with `type = \"nixos-system\"`; verified at Nix build time (Stage 2 check, hard build error if absent or wrong type). No implicit default exists; Provider artifacts cannot silently provide a separately typed system artifact. The controller sets `Guest.spec.systemArtifactId` to the artifact ID value at reconcile time (the value is already validated by the build; the controller fails closed if absent at runtime). The net-VM nixos-system artifact is **generic** (INV-NET-008): it contains the guest-agent binary and runtime, kernel, base NixOS services, systemd-networkd NIC bootstrap, and the `net-local-controller` **OS account** provisioned by `Provider/network-local`'s Nix module (same private fixed UID/GID as on the Host, so that virtiofs view ACLs on config Volume layout entries are enforced consistently inside the Guest; `Provider/system-core` performs NSS lookup reconciliation, not OS account provisioning; no numeric UID/GID appears in any ResourceSpec field, authz check, or audit record; `User.status` MAY carry diagnostic `uid`/`gid` from NSS lookup but those are informational only and never authorization inputs). It does NOT encode per-Network desired data; per-Network config (dnsmasq, nftables, routing, attachments) is delivered via the controller-owned config Volume and applied by the guest-agent Process. The artifact preserves compile-time-fixed content: `lib.mkForce` on 10-eth-dhcp (INV-NET-001); two systemd-networkd interface units matched by MAC; IPv6 suppression sysctls on NIC interfaces; ip6 filter table drop-all policy. **mDNS reflector and local dnsmasq DNS bridge are separate owned Process resources** (D-NETWORK-001); they are not inline services in the artifact.", "entryContracts" : [ "ADR-046-resources-network" ], @@ -15600,6 +15979,7 @@ ], "specId" : "ADR-046-resources-network", "topologicalRank" : 11, + "validation" : "nix-unit: `tests/unit/nix/cases/net-vm-network.nix` (adapted to v3 resource API); INV-NET-001 assertion in new nix-unit case; no mDNS inline service appears in the generated artifact; no per-Network dnsmasq or nftables data in artifact (INV-NET-008); integration test: mDNS Process resources are created when `spec.mdns.enable = true`; Stage 2 build test: absent `netVmSystemArtifactId` fails with required-field build error; wrong artifact type fails with `artifact-type-mismatch` error; `packages/d2b-provider-network-local/tests/net_vm_artifact_is_generic.rs` — two Networks with different CIDRs produce same `systemArtifactId` and different config Volume content", "wave" : "W4" }, { @@ -15607,6 +15987,7 @@ "destinations" : [ "`nixos-modules/resources-network.nix`: Nix resource object emitter for Network ResourceType; `nixos-modules/index.nix`: network resource compilation section" ], + "detailedDesign" : "The emitter replaces `d2b.envs.` with `d2b.zones..resources. = { type = \"Network\"; spec = { ... }; }` (attr key = resource name; `type` explicit field; `spec` fields identical to the canonical ResourceSpec JSON — no bespoke Nix vocabulary). It validates CIDR shape, attachment index uniqueness, external attachment constraints, and CIDR overlap (reusing `cidrOverlaps` from `lib.nix`). **Bridges are NOT emitted as `systemd.network.netdevs` entries** (D-NETWORK-003 resolved; bridges are created dynamically by the broker `CreateBridge` op at reconcile time). The Nix emitter provisions only bootstrap/static prerequisites that do not require runtime bridge IfNames: `networking.networkmanager.unmanaged` pattern for the `d2b-*` prefix (covers all dynamically-created bridges and taps regardless of specific IfNames; emitted to `00-d2b-unmanaged.conf`); schema validation and controller binary deployment artifacts. Current `d2b.vms..env` + `d2b.vms..index` attachment semantics (`options-vms.nix` lines 944/962) become `Network.spec.attachments[].executionRef` + `index`. Current `d2b.site.allowUnsafeEastWest` (`options-site.nix` line 48) moves to the per-Network `isolation.allowEastWest` field; Zone.spec is empty in v3. Current `d2b.hostLanCidrs` (`options-site.nix` line 382) becomes the Host resource's network inventory, queried at runtime; at Nix build time the eval may validate CIDRs against declared host configuration input. The emitter does not emit `boot.kernel.sysctl` entries per bridge IfName (bridges do not exist at activation time; IPv6 suppression is applied by `CreateBridge` and `ApplySysctl` per INV-NET-002). **Nix option types** for `spec.*` fields are generated from `Network.schema.json`; they are not hand-written. **Bundle generation**: the emitter collects all declared `Network` resource objects, sorts them lexicographically by `(type, name)`, serializes each as canonical JSON **omitting `managedBy` and `configurationGeneration`** (core sets these at activation), and assembles the Zone resource bundle at `$out/bundle.json` (see [Nix configuration contract — Stage 3](#stage-3--build-output-zone-resource-bundle)). The emitter records a `providerSchemaDigest` entry for `Provider/network-local` in the bundle resolved from the artifact catalog. The bundle's `contentHash` is a SHA-256 of the sorted canonical resource array; the derivation is fixed-output so that identical configuration always produces the same store path. The `managedBy` field is NOT set by the emitter; core sets `managedBy = \"configuration\"` and assigns the `configurationGeneration` counter when applying the bundle. The core controller retains prior bundle copies under `/var/lib/d2b/zones//configuration/prior/` per [Generation lifecycle and cleanup contract](#generation-lifecycle-and-cleanup-contract).", "entryContracts" : [ "ADR-046-resources-network" ], @@ -15623,6 +16004,7 @@ ], "specId" : "ADR-046-resources-network", "topologicalRank" : 12, + "validation" : "nix-unit CIDR overlap, assertion eval, and bridge-sysctl cases; `make test-flake` with updated examples; `make test-drift` for schema/emitter parity; `packages/d2b-contracts/tests/generation_bundle.rs` for bundle format and `contentHash` stability; nix-unit `tests/unit/nix/cases/generation-cleanup-absent-network.nix` for removed-resource scheduling (added by ADR046-network-008)", "wave" : "W4" }, { @@ -15630,6 +16012,7 @@ "destinations" : [ "`packages/d2b-provider-network-local/src/controller.rs`: async NetworkReconciler; `packages/d2b-provider-network-local/src/plan.rs`: ReconcilePlan computation; `packages/d2b-provider-network-local/src/observe.rs`: drift-detection observe loop. Full crate layout required (see [Package and crate boundary](#package-and-crate-boundary)): `src/` (controller/plan/observe + colocated unit tests), `tests/` (hermetic conformance and state-machine tests), `integration/` (provider-system reconcile fixtures), `README.md` (Network ResourceType, controller binary, placement, RBAC, security invariants, build/test/integration commands)." ], + "detailedDesign" : "Implements full async reconcile interface from `ADR-046-resource-reconciliation`. `plan()` computes desired vs. actual bridge-presence, sysctl, host-side nftables, hosts-file, NM-unmanaged, config Volume content, Guest, guest-agent Process, and mDNS-Process states. `reconcile()` dispatches in order: `CreateBridge` for each bridge not present (broker applies IPv6 sysctls atomically; `CreateBridge` failure sets `FabricReady=False/bridge-create-error` and aborts) → `ApplySysctl` (defense-in-depth IPv6) → `ApplyNftables` (host-side `inet d2b` table) → `ApplyNmUnmanaged` → `ApplyRoute` → `UpdateHostsFile` → `SeedDnsmasqLease` for new reservations → **Volume upsert** (two-phase): Phase 1 — create `Volume/net--config` with `kind: ephemeral`, `source.executionRef: Host/`, `source.settings.kind: tmpfs`, `quota: {maxBytes: 4194304, maxInodes: 128, enforcement: hard}` (tmpfs quota charged to Host memory budget), `layout` entries (root directory with `type: directory` and four config files each with `type: file`, `ownerRef: User/net-local-controller`, `groupRef: User/net-local-controller`, `mode: \"0640\"`, `accessAcl: []`, `defaultAcl: []`, `noFollow: true`, conservative create/repair/cleanup policies), `views: {guest-readonly: {path: \"\", rights: [read, traverse]}}`, `attachments: []` (no Guest attachment); abort on terminal error with `ConfigVolumeReady=False/config-volume-error`. Wait for Volume backing to reach `Ready`; requeue on `Degraded`/`Failed` with `ConfigVolumeReady=False/volume-not-ready`. Write rendered config content through Volume write service (no raw host paths). Phase 2 — create Guest upsert with `systemArtifactId` from REQUIRED `Network.spec.netVmSystemArtifactId`. Wait for Guest `Ready`. Then update Volume with Guest attachment: `attachments: [{executionRef: Guest/, transport: virtiofs, view: guest-readonly, access: read-only, mountPath: \"/run/d2b/net-config\", settings: {posixAcl: false, xattr: false, cache: auto, inodeFileHandles: never, threadPoolSize: null, socketGroup: null}}]`; wait for attachment `Ready`; requeue on `Degraded` with `ConfigVolumeReady=False/attachment-not-ready`. Create or update guest-agent Process `Process/net--agent` with `processClass: worker`, `sandbox: {namespaceClasses: [], capabilityClasses: [network-admin, network-bind, network-raw]}` (inherits Guest network namespace; `network-admin`→`CAP_NET_ADMIN`, `network-bind`→`CAP_NET_BIND_SERVICE`, `network-raw`→`CAP_NET_RAW`, all effective in Guest network namespace only; INV-NET-009), `mounts: [{volumeRef: Volume/net--config, view: guest-readonly, mountPath: \"/run/d2b/net-config\", access: read-only, required: true}]`. mDNS Process upsert when `spec.mdns.enable = true` (D-NETWORK-001) → `SetBridgePortFlags` per tap. Each broker op returns typed audit evidence. `observe()` re-reads `firewallDigest` (host-side), bridge isolation flags, IPv6 sysctls, and guest-agent Process status (`dnsmasq-bound`, `firewall-applied` predicates); queues reconcile on drift. Metrics use only the fixed semantic labels in §OTEL spans and metrics; Zone/Network identity remains in OTEL resource attributes and permitted audit fields and never enters metric labels or span attributes. **Finalizer** (strictly child-first): `NetworkDraining` → delete guest-agent Process and mDNS Processes; wait for each Deleted watch event → update Volume to remove Guest attachment (`attachments: []`); wait for attachment removal confirmed → delete `Guest/`; wait for Deleted watch event → delete `Volume/net--config`; wait for Deleted watch event → `ApplyNftables` (empty) → `ApplyNmUnmanaged` (empty) → `UpdateHostsFile` (empty) → `DeleteBridge` for each bridge (idempotent) → clear finalizer. No USBIP rules installed by Network; `UsbipBindFirewallRule` issued by device-usbip directly (D-NETWORK-002).", "entryContracts" : [ "ADR-046-resources-network" ], @@ -15644,6 +16027,7 @@ ], "specId" : "ADR-046-resources-network", "topologicalRank" : 10, + "validation" : "`ADR046-reconcile-001` toolkit conformance; latency gates (p95 ≤5 ms hint-to-handler); Network-specific: CIDR conflict blocks reconcile, `CreateBridge` failure sets `FabricReady=False`, Volume creation failure sets `ConfigVolumeReady=False/config-volume-error`, `User/net-local-controller` not Ready aborts with `ConfigVolumeReady=False/user-not-ready`, Volume schema round-trip (kind=ephemeral, source.settings.kind=tmpfs, quota.enforcement=hard, layout type=file entries, views.guest-readonly.rights=[read,traverse]), tmpfs quota charged to Host memory budget (test Volume creation fails when Host memory budget exceeded), Guest not created before Volume backing `Ready`, Guest attachment not added before Guest `Ready`, guest-agent Process created after attachment `Ready` (`processClass: worker`, `namespaceClasses: []`, `capabilityClasses: [network-admin, network-bind, network-raw]`, `access: read-only`, `required: true`), host-capability leakage test: no host-netns process gains `CAP_NET_ADMIN`/`CAP_NET_BIND_SERVICE`/`CAP_NET_RAW` as result of guest-agent launch (INV-NET-009; `tests/host-integration/guest-agent-cap-confinement.nix`), `DeleteBridge` called on finalizer, Volume attachment removed before Guest deletion in finalizer (test order: agent Deleted → attachment removed → Guest Deleted → Volume Deleted → bridges), east-west invariant (INV-NET-003), hostBlocklist enforcement (INV-NET-004), macvtap attachment status (delegated to runtime-ch), mDNS Process created/deleted with `spec.mdns.enable` toggle, broker INV-NET-002 tests, config-only spec change updates Volume content and triggers agent reload without Guest restart (INV-NET-008); golden tests updated for v3 IfNames; structural metric descriptor test asserts exact absence of `vm`, `zone`, `zone_id`, `zone_uid`, `network`, and every resource-name-derived label and verifies a Network-name canary is absent from emitted label values", "wave" : "W4" }, { @@ -15651,6 +16035,7 @@ "destinations" : [ "`tests/unit/nix/cases/net-vm-network.nix` (adapted to v3 resource API); updated golden pins; `tests/host-integration/bridge-isolation.nix` (adapted); `packages/d2b-priv-broker/tests/bridge_lifecycle.rs` (new hermetic broker tests). Provider crate test directories: `packages/d2b-provider-network-local/tests/` — hermetic Cargo integration tests (conformance suite, controller state machine, CIDR validation vectors, IfName determinism, invariant tests INV-NET-001–007, reconcile/observe/finalize with deterministic clock, fault injection); `packages/d2b-provider-network-local/integration/` — container/Host/Guest lifecycle fixtures invoked by `make test-integration` (bridge isolation, east-west double opt-in, nftables drift detection, macvtap lifecycle). Both directories required by package policy." ], + "detailedDesign" : "Rust integration tests: NetworkSpec CIDR validation golden vectors; AttachmentSpec index uniqueness; ExternalAttachmentSpec mutual-exclusion validation; IfName derivation determinism; CIDR overlap arithmetic; INV-NET-001 through INV-NET-009 invariant tests; reconcile/observe/finalize state machine (deterministic clock). Broker tests: `create_bridge_applies_ipv6_sysctl` (INV-NET-002 layer 1); `delete_bridge_is_idempotent`; `create_bridge_parameters_match_spec` (MTU, STP disabled, multicast snooping disabled). Controller tests: `reconcile_applies_sysctl_defense_in_depth` (INV-NET-002 layer 2); `volume_created_before_guest`; `guest_not_created_until_volume_ready`; `agent_process_created_after_guest`; `finalizer_order_agent_then_guest_then_volume_then_bridges`; `config_only_spec_change_updates_volume_no_guest_restart` (INV-NET-008); `finalizer_calls_delete_bridge`; `mdns_process_created_on_enable`; `mdns_process_deleted_on_disable`; `host_capability_leakage` (INV-NET-009). nix-unit: INV-NET-001 lib.mkForce assertion; net-VM artifact has no inline mDNS service and no per-Network dnsmasq/nftables data (INV-NET-008); Network emitter CIDR constraint assertions; no `systemd.network.netdevs` bridge entries emitted. Host integration: bridge isolation with east-west opt-in; nftables drift detection; macvtap create/delete lifecycle; config Volume update propagates to guest-agent without Guest restart; `tests/host-integration/guest-agent-cap-confinement.nix` (INV-NET-009 zero leakage to host netns).", "entryContracts" : [ "ADR-046-resources-network" ], @@ -15666,6 +16051,7 @@ ], "specId" : "ADR-046-resources-network", "topologicalRank" : 11, + "validation" : "All listed tests must pass before `nixos-modules/network.nix` removal is eligible", "wave" : "W4" }, { @@ -15673,6 +16059,7 @@ "destinations" : [ "`Provider/device-usbip` owns one relay Process/Endpoint authority per Network plus the typed EffectPort adapter for `UsbipBindFirewallRule`. The controller watches only the `networkRef` resource's identity/readiness/generation; Core privately resolves Network UID to relay attachment and firewall intent. Network spec/status is not mutated with USBIP fields. Full crate layout required for `packages/d2b-provider-device-usbip/` (see [Package and crate boundary](#package-and-crate-boundary)): `src/` (controller and usbip runner + unit tests), `tests/` (hermetic conformance, dependency-watch state machine, UsbipBindFirewallRule round-trip), `integration/` (Host/Guest USBIP attach/detach lifecycle fixtures), `README.md` (Provider identity, provider-neutral USB Service/Binding types, USBIP Processes/Endpoints, Network least-privilege dependency contract, RBAC, security invariants, standalone-repo path)." ], + "detailedDesign" : "Device-usbip's typed EffectPort is the sole semantic owner of every USBIP TCP/3240 rule. Its Core adapter resolves the opaque per-Network/per-busid intent and issues `UsbipBindFirewallRule`; its strict provider status owns firewall digest/drift. Network-local emits no generic host or net-VM TCP/3240 allow and ignores device-usbip ownership markers in Network drift. The device Provider owns exactly one multiplexed relay Endpoint authority per Network and supplies Binding proxies only authorized connected streams through LaunchTickets. Primary reuse disposition: `adapt`. Preserved source-plan detail: extract and adapt.", "entryContracts" : [ "ADR-046-resources-network" ], @@ -15687,6 +16074,7 @@ ], "specId" : "ADR-046-resources-network", "topologicalRank" : 11, + "validation" : "device-usbip conformance tests cover exact per-Network/per-busid scoping, one relay Endpoint authority, ownership-scoped drift/status, foreign-marker rejection, and release; network-local nftables tests assert no TCP/3240/USBIP rule on host or net VM and prove USBIP rule churn does not change Network `FirewallReady`; the pinned USBIP firewall golden moves to device-usbip ownership", "wave" : "W4" }, { @@ -15694,6 +16082,7 @@ "destinations" : [ "`packages/d2b-core-controller/src/configuration.rs`: bundle application, diff, generation-transition logic (including per-item name-conflict handling), prior-bundle retention under `/var/lib/d2b/zones//configuration/prior/`; `packages/d2b-core-controller/src/cleanup.rs`: removal scheduling and `PendingCleanup` condition tracking; `packages/d2b-contracts/src/generation_bundle.rs`: `ZoneBundle`/`BundleResource`/`BundleMetadata` **input** DTOs — MUST NOT include `managedBy` or `configurationGeneration` (both are persisted resource metadata set by core at activation, not bundle input fields); `ManagedBy` closed enum `{ Configuration, Controller, Api }` and `configurationGeneration: u64` live in `packages/d2b-core-controller/src/resource_store.rs` as persisted resource metadata fields; `nixos-modules/resources-network.nix` (emits bundle with `managedBy`/`configurationGeneration` absent; core sets both at activation per ADR046-network-004); `d2b.zones..retainedGenerations` Nix/compiler-level Zone option (outside `Zone.spec`; default `3`, range `1..16`); `tests/unit/nix/cases/generation-cleanup-absent-network.nix`; `packages/d2b-contracts/tests/generation_bundle.rs`; `tests/host-integration/nix-generation-cleanup.nix`" ], + "detailedDesign" : "**Core generation tracking** (`packages/d2b-core-controller/src/configuration.rs`): core maintains a monotone `configurationGeneration` counter per Zone in its durable state. On each bundle application it compares the incoming `contentHash` against the prior applied hash. If different, it increments `configurationGeneration`, sets `managedBy = \"configuration\"` and the new counter value on each resource in the bundle, and performs the resource diff (create/update/delete scheduling). The `managedBy` and `configurationGeneration` fields are absent from the Nix-emitted bundle and are set exclusively by core at activation time. **`managedBy` field and per-item name-conflict handling**: `ManagedBy` is a closed enum (`Configuration`, `Controller`, `Api`) persisted in resource metadata at `packages/d2b-core-controller/src/resource_store.rs`. It is NOT a field in `ZoneBundle`/`BundleResource` input DTOs; core sets it at activation. Controllers set `ManagedBy::Controller` when creating owned children (net-VM Guest, config Volume, guest-agent Process, mDNS Processes); exact controller identity/UID/generation are tracked in separate internal metadata, not embedded in the `managedBy` value. API-created resources carry `ManagedBy::Api` and persist until explicit delete with no bundle-driven lifecycle. Core's generation-transition logic only schedules bundle-driven Delete for `ManagedBy::Configuration` resources. **Per-item name-conflict handling**: when a bundle item's `(zone, name)` already exists with `managedBy ≠ \"configuration\"`, core skips that item and records it with `phase = Degraded, reason: name-conflict`; a `ResourceConflictSkipped` audit record is emitted for that item. All non-conflicting items in the bundle proceed normally (Provider-state contract). The existing resource is left completely untouched. The operator deletes the conflicting resource via the resource API; the next bundle application applies the item. **Removal scheduling**: on generation N+1 activation, core performs a set difference: `prev_configuration_managed - new_configuration_managed` = resources to delete. For each, it sets `metadata.deletionRequestedAt` in the resource store and emits a `ResourceDeletionScheduled` audit record. Normal finalizer-path Delete proceeds asynchronously. **`PendingCleanup` condition**: the Zone self resource carries a `PendingCleanup = True` condition while any `managedBy = Configuration` resource has `deletionRequestedAt` set and has not yet been atomically removed. Aggregate Zone `phase = Degraded` applies. The condition transitions to `False` and Zone phase returns to `Ready` when all scheduled deletions complete. **Prior generation bundle retention** (`cleanup.rs`): count-based (`d2b.zones..retainedGenerations`, outside `Zone.spec`, default 3, range 1..16); no TTL. Core copies prior bundles to `/var/lib/d2b/zones//configuration/prior/.json`. A generation is eligible for pruning when all configuration-managed resources from it have either been atomically removed or are present unchanged in a newer generation, AND the count would be exceeded. **`BundleActivated` audit record**: emitted at each generation transition with `contentHash`, `configurationGeneration`, `resourceCount`, and `providerSchemaDigests` map (digests from `type=provider` artifacts via `Provider.spec.artifactId`); no spec contents, CIDRs, or resource names appear in the record. Provider schema digests in the bundle are re-verified against installed Provider artifact digests at application time; a mismatch aborts application with a `BundleRejected` audit record.", "entryContracts" : [ "ADR-046-resources-network" ], @@ -15710,6 +16099,7 @@ ], "specId" : "ADR-046-resources-network", "topologicalRank" : 13, + "validation" : "**nix-unit**: `tests/unit/nix/cases/generation-cleanup-absent-network.nix` — verifies that a Network resource present in generation N and absent from generation N+1 receives `deletionRequestedAt` and appears in the `PendingCleanup` condition; verifies that a controller-owned `Guest` (`managedBy = \"controller\"`) does NOT receive a direct bundle-driven Delete; verifies that a re-declared (identical spec) Network is NOT scheduled for Delete; verifies `retainedGenerations` default is 3. **Rust contract tests**: Two separate test files — (1) `packages/d2b-contracts/tests/generation_bundle.rs`: tests the **input** bundle DTO only: `ZoneBundle`/`BundleResource`/`BundleMetadata` JSON round-trip, `contentHash` stability across serialization, `providerSchemaDigests` presence, `managedBy` and `configurationGeneration` fields ABSENT from `BundleResource` input struct (verified by both compile-time type check: the fields must not exist on the `BundleResource` type, and runtime JSON serialization: the serialized object must not contain those keys). (2) `packages/d2b-core-controller/tests/resource_metadata.rs`: `ManagedBy` closed enum round-trip with `\"configuration\"`/`\"controller\"`/`\"api\"` values tested separately here since `ManagedBy` is persisted resource metadata in `resource_store.rs`, not a field of the input bundle DTO. **Controller integration tests**: async Delete triggered through finalizers for Network; mDNS Process child deleted before Network finalizer clears; bridge `DeleteBridge` broker call made exactly once during finalizer; controller waits for Deleted watch event (not a persistent phase=Deleted row) before proceeding. **Host integration**: `tests/host-integration/nix-generation-cleanup.nix` — runNixOSTest scenario: apply generation 1 with Network resource, then apply generation 2 with that Network absent; assert Zone enters `Degraded/PendingCleanup`; assert Network `phase = Degraded` with `NetworkDraining = True` and `deletionRequestedAt` set and `reason: configuration-generation-removed`; assert cleanup completes (single store transaction: Deleted REVISION event + row/index removal; dedup-guarded audit append follows committed transaction) and Zone returns to `Ready`; assert no controller-owned children deleted directly by core; assert prior bundle copied to `/var/lib/d2b/zones//configuration/prior/` and retained until cleanup complete; assert bundle pruned when `retainedGenerations` exceeded and generation eligible. **INV-NET-LIFECYCLE-001**: core never schedules bundle-driven Delete for `managedBy ≠ \"configuration\"` resources; verified by static analysis of core's generation-transition diff function, which is bounded at compile time to iterate only the `configuration_managed_resources` set. **INV-NET-LIFECYCLE-002**: per-item name-conflict — when a bundle item collides with `managedBy = \"controller\"` or `\"api\"`, that item is recorded as `Degraded/name-conflict`; the existing resource is left untouched; non-conflicting items continue to activate; tested by `packages/d2b-core-controller/tests/configuration_name_conflict.rs` (three cases: collision with a controller-owned child, an API-created resource, and a same-name configuration resource from a prior generation that completed deletion; each case asserts non-conflicting items still activate).", "wave" : "W4" }, { @@ -15717,6 +16107,7 @@ "destinations" : [ "`packages/d2b-contracts/src/v3/network.rs` external-attachment sharing schema/status; `packages/d2b-core-controller/src/authority.rs` Core-derived physical-NIC identity and Host-global claim; Provider/network-local descriptor/reconcile/finalizer" ], + "detailedDesign" : "Resolve operator-declared `parentInterface` against trusted Host inventory and derive an opaque `external-physical-nic/v1` digest; index `(Host, external-physical-nic, opaqueKeyDigest)` before any macvtap/VMM effect. `passthru`, `private`, and `vepa` are exclusive. `bridge` defaults exclusive and is multiplexed only under explicitly authored compatible policy. Use typed `external-physical-nic-conflict`; expose only bounded authority availability/holder-count/queue/arbitration/update-currency and conditions; keep digest, interface identity, and owner proof private. Parent/mode/policy update drains and releases the old claim before replacement; deletion closes macvtap/VMM ownership before releasing the claim; restart adopts exact owner proof and quarantines ambiguity. Primary reuse disposition: `adapt`. Preserved source-plan detail: adapt the existing private macvtap-FD spawn path; add authority admission before it.", "entryContracts" : [ "ADR-046-resources-network" ], @@ -15732,6 +16123,7 @@ ], "specId" : "ADR-046-resources-network", "topologicalRank" : 11, + "validation" : "Hermetic authority tests cover same-Zone and cross-Zone exclusive collisions, mixed-policy conflicts, non-bridge multiplex rejection, explicit compatible bridge multiplex admission, Core-derived key equality for two selectors resolving to one fake NIC, caller-supplied digest rejection, no-effect conflict, owner-proof adoption/ambiguity, disruptive update, and release-after-close ordering. Nix eval covers schema and declared cross-Zone conflicts; host integration covers create/update/delete with a fake macvtap parent and status/condition transitions without raw identity exposure.", "wave" : "W4" }, { @@ -15739,6 +16131,7 @@ "destinations" : [ "`nixos-modules/options-zones.nix` (Zone-level options: `label`, `parentZone`, `retainedGenerations`, `trustedPublishers` — compiler settings, not Zone spec fields); `nixos-modules/options-zones-resources.nix` (unified `resources` attrset)" ], + "detailedDesign" : "`d2b.zones..resources. = { type = \"\"; spec = { ... }; }` — single attrset covering all ResourceTypes; `type` discriminates dispatch; `spec` fields mirror exact ResourceTypeSchema field names and nesting; Nix option types/defaults/docs generated from `docs/reference/schemas/v3/.json`; no Nix-only fields inside resource declarations; `metadata.name` derives from attr key; `metadata.zone` derives from enclosing zone attr key; `apiVersion` defaulted; `uid`/`generation`/`revision`/`status`/`managedBy` never in Nix; `resource_name` regex `^[a-z][a-z0-9-]*$`; ref validation assertions; `WorkloadProviderKind` → Guest/Host mapping per disposition table above; `Capability` → Role verb mapping per resource-api/authz foundation spec; Zone self-resource spec is `{}`; `parentZone` is a required non-root/forbidden-root compiler-only plain Zone name compiled into sealed allocator topology; `retainedGenerations`/`trustedPublishers` are likewise Zone-level compiler settings not emitted in Zone spec", "entryContracts" : [ "ADR-046-nix-configuration" ], @@ -15754,6 +16147,7 @@ ], "specId" : "ADR-046-nix-configuration", "topologicalRank" : 10, + "validation" : "nix-unit vectors for each ResourceType; ref-validation rejection vectors; malformed ref error shape; `parentZone` is missing on non-root/forbidden on local-root/unknown/self/cyclic/over-depth rejection; scalar module conflicts prove one parent; child-local ZoneLink `childZoneName` must equal its enclosing Zone key; a second uplink resource (even disabled) and a local-root uplink fail eval; missing/local-unresolved `transportProviderRef` fails eval; `managedBy` in spec rejected at eval; Zone spec is `{}` (no `parentZone`, `parentRef`, `retainedGenerations`, etc.)", "wave" : "W5" }, { @@ -15761,6 +16155,7 @@ "destinations" : [ "`Network` resource fields in `nixos-modules/options-zones-resources.nix`; `Guest` resource fields" ], + "detailedDesign" : "`d2b.envs.work.lanSubnet` → `d2b.zones.work.resources.work-lan = { type = \"Network\"; spec = { lanSubnet = \"...\"; ... }; }`; CIDR overlap assertion migrated; `sys-` reserved prefix and VM-name regex retained; `d2b.vms..tpm.enable` → `d2b.zones..resources.vm-tpm = { type = \"Device\"; spec = { providerRef = \"Provider/device-tpm\"; ... }; }`", "entryContracts" : [ "ADR-046-nix-configuration" ], @@ -15775,6 +16170,7 @@ ], "specId" : "ADR-046-nix-configuration", "topologicalRank" : 11, + "validation" : "nix-unit CIDR rejection; eval assertion for `sys-` prefix; VM-name regex", "wave" : "W5" }, { @@ -15782,6 +16178,7 @@ "destinations" : [ "`nixos-modules/options-site.nix` (retained); per-Zone options in `options-zones.nix`" ], + "detailedDesign" : "`d2b.zones..retainedGenerations` (default 3, range 1..16, compiler setting — not emitted in Zone spec); `d2b.site.stateDir` maps to Zone storage roots; `d2b.site.usePrebuiltHostTools` retained Primary reuse disposition: `adapt`. Preserved source-plan detail: retain and extend.", "entryContracts" : [ "ADR-046-nix-configuration" ], @@ -15796,6 +16193,7 @@ ], "specId" : "ADR-046-nix-configuration", "topologicalRank" : 11, + "validation" : "nix-unit case verifies `retainedGenerations` default/range, `site.stateDir` mapping, and `usePrebuiltHostTools` retention.", "wave" : "W5" }, { @@ -15803,6 +16201,7 @@ "destinations" : [ "`nixos-modules/index.nix` (rewritten); emits `/etc/d2b/index.json`" ], + "detailedDesign" : "Cross-Zone normalized index: zone/host/guest/network/closure entries; executionIndex; networkIndex; closureIndex; sorted output; `cfg._index` attribute tree retained as internal helper during migration Primary reuse disposition: `adapt`. Preserved source-plan detail: rewrite.", "entryContracts" : [ "ADR-046-nix-configuration" ], @@ -15818,6 +16217,7 @@ ], "specId" : "ADR-046-nix-configuration", "topologicalRank" : 12, + "validation" : "nix-unit golden vectors for index shape; drift gate: `xtask gen-index` round-trip", "wave" : "W5" }, { @@ -15825,6 +16225,7 @@ "destinations" : [ "`nixos-modules/bundle-zones.nix` (per-Zone bundle derivation); common helpers retained in `bundle-artifacts.nix`" ], + "detailedDesign" : "Per-Zone `bundle.json` with `candidateId`/`contentId` binding; SHA256 digest chain; `generationIndex`; atomic activation pointer; `manifestVersion` → `schemaVersion` rename Primary reuse disposition: `adapt`. Preserved source-plan detail: extend and rewrite.", "entryContracts" : [ "ADR-046-nix-configuration" ], @@ -15839,6 +16240,7 @@ ], "specId" : "ADR-046-nix-configuration", "topologicalRank" : 13, + "validation" : "Artifact-shape contract tests in `packages/d2b-contract-tests/tests/`; determinism test (build twice, diff outputs)", "wave" : "W5" }, { @@ -15846,6 +16248,7 @@ "destinations" : [ "`nixos-modules/resources-zones-processes.nix`; emits `zones//processes.json`" ], + "detailedDesign" : "Process/EphemeralProcess resource serialization per disposition table and the frozen `ExecutionSpec`: exact `providerRef`/`executionRef`/domain/user/processClass/template/config/credential/mount/sandbox/budget/`networkUsage`/`deviceUsage`/telemetry names; semantic owner from `metadata.ownerRef` with template fallback through the Process Provider; no `packageRef`, free-form `binaryPath`, `argv`, renamed network/device fields, or inline endpoints; stable produced endpoints are separate owned `Endpoint` resources with `producerRef`; Process uses canonical `restartPolicy` fields; EphemeralProcess uses `runtimeDeadline` and has no restart policy; VsockRelay → `Process` under `Provider/transport-vsock`; GuestSshReadiness retired at v3 cutover; Usbip long-lived backend/proxy → `Process`, Usbip per-busid attach/detach → `EphemeralProcess`, all owned by `Provider/device-usbip` Primary reuse disposition: `adapt`. Preserved source-plan detail: extract and adapt.", "entryContracts" : [ "ADR-046-nix-configuration" ], @@ -15861,6 +16264,7 @@ ], "specId" : "ADR-046-nix-configuration", "topologicalRank" : 14, + "validation" : "Process and EphemeralProcess exact-schema vectors; reject `packageRef`, `network`, `devices`, `endpoints`, `restart`, and `runDeadline`; accept canonical `networkUsage`, `deviceUsage`, `restartPolicy`, `runtimeDeadline`, and separately owned Endpoint with `producerRef`; no-raw-path assertion; ProcessRole parity test (every variant has a test case)", "wave" : "W5" }, { @@ -15868,6 +16272,7 @@ "destinations" : [ "`nixos-modules/resources-zones-volumes.nix`; emits `zones//volumes.json`; OFD lock rows move to `d2b-contracts` internals" ], + "detailedDesign" : "Volume layout/views/ACL/no-follow/repair preserving current policy; `PrincipalRef { kind: \"uid\" }` → `User/` typed ref only; OFD rows removed from Nix artifacts Primary reuse disposition: `adapt`. Preserved source-plan detail: extract storage policy → adapt; retire sync rows.", "entryContracts" : [ "ADR-046-nix-configuration" ], @@ -15883,6 +16288,7 @@ ], "specId" : "ADR-046-nix-configuration", "topologicalRank" : 14, + "validation" : "Volume schema vectors; ACL/no-follow/view policy tests", "wave" : "W5" }, { @@ -15890,6 +16296,7 @@ "destinations" : [ "Compiler-only `parentZone` map in `nixos-modules/options-zones.nix`; local-root allocator bootstrap compiler/sealer; runtime-created Zone self-resource with `spec = {}`; child-local ZoneLink resource; `realm-controllers.json` RETAINED during migration (live d2bd reads it)" ], + "detailedDesign" : "Resolve each non-root `parentZone` to one declared Zone; reject a root value, missing/unknown/self/conflicting/cyclic parent, and ancestry deeper than 16 names. Canonicalize sorted child→parent rows and feed them to the local-root allocator's private bootstrap sealer. Parent allocators derive/pre-bind privileged socket/listener state from that topology; no socket path or `parentZone` enters `Zone.spec` or a ResourceRef. Child-local ZoneLinks provide transport/local route state. `realm-controllers.json` remains published until `realm_access_resolver` is replaced. Primary reuse disposition: `adapt`. Preserved source-plan detail: adapt and retire.", "entryContracts" : [ "ADR-046-nix-configuration" ], @@ -15904,6 +16311,7 @@ ], "specId" : "ADR-046-nix-configuration", "topologicalRank" : 13, + "validation" : "Zone self-resource round-trip with exact empty spec; `parentZone` required/root-forbidden/existence/one-parent/self/cycle/16-name-depth vectors; topology seal excludes `parentZone` from Zone/resource bundles; bootstrap socket/listener regression tests", "wave" : "W5" }, { @@ -15911,6 +16319,7 @@ "destinations" : [ "Provider/display-wayland and Provider/shell-terminal Process configs in `zones//processes.json`; `Provider/credential-entra` Credential resource; `realm-identity.json` RETAINED during migration" ], + "detailedDesign" : "Launcher metadata folded into Process resource annotations; identity config → Credential resource fields (`providerRef`, `scope`, `audience`, `allowedOperations`, canonical `spec.provider` extension where Provider schema declares it; no secret bytes); `realm-identity.json` must remain until d2bd `RealmIdentityConfigJson` loading is replaced by Credential resource reader", "entryContracts" : [ "ADR-046-nix-configuration" ], @@ -15925,6 +16334,7 @@ ], "specId" : "ADR-046-nix-configuration", "topologicalRank" : 15, + "validation" : "Launcher metadata shape regression; no-secret assertion vectors", "wave" : "W5" }, { @@ -15932,6 +16342,7 @@ "destinations" : [ "User-only `Host` resource in `zones//hosts.json` (`spec.isolationPosture: \"none\"`, `defaultDomain: user`, `allowedDomains: [user]`, `defaultUserRef: User/`); child `Process` resources in `zones//processes.json` using normal Process Providers; shell session supervisor → `Process` under `Provider/shell-terminal`; never a `Guest`; not a v3 Provider" ], + "detailedDesign" : "`isolationPosture: \"none\"` is a promoted Host base field declared at top-level `spec.isolationPosture` in the Host schema; enforced at eval time; user-only Host rejects system-domain Process refs; `NoIsolation` condition in Host status; `status.isolationPosture: none`; every `ProcessEffect` audit event under this Host carries `no_isolation=true`; OTEL telemetry never carries an isolation label; CLI/UI warning non-suppressible", "entryContracts" : [ "ADR-046-nix-configuration" ], @@ -15946,6 +16357,7 @@ ], "specId" : "ADR-046-nix-configuration", "topologicalRank" : 11, + "validation" : "User-only Host rejection of system-domain Process refs; `spec.isolationPosture != \"none\"` assertion rejection for user-only system-core Hosts; `NoIsolation` condition present in status; `no_isolation=true` in `ProcessEffect` audit event; OTEL attribute absent; no Guest emitted for unsafe-local declaration", "wave" : "W5" }, { @@ -15953,6 +16365,7 @@ "destinations" : [ "`nixos-modules/privileges-json.nix` (retained); `/etc/d2b/privileges.json` (retained, site-wide)" ], + "detailedDesign" : "Broker op catalog is not Zone-scoped; no structural change required Primary reuse disposition: `copy-unchanged`. Preserved source-plan detail: retain.", "entryContracts" : [ "ADR-046-nix-configuration" ], @@ -15966,6 +16379,7 @@ ], "specId" : "ADR-046-nix-configuration", "topologicalRank" : 10, + "validation" : "Existing `tests/unit/gates/drift-check.sh`", "wave" : "W5" }, { @@ -15973,6 +16387,7 @@ "destinations" : [ "`nixos-modules/closures-json.nix` (rewritten, keyed by artifact ID from `d2b.artifacts` with `type = \"nixos-system\"`); `nixos-modules/minijail-profiles.nix` (retained, adapted to reference Zone Guests)" ], + "detailedDesign" : "Closure emitter iterates `d2b.artifacts` entries with `type = \"nixos-system\"`, computes `pkgs.closureInfo`, records `storePath`/digest/size in artifact catalog (private root:d2bd 0640 field; absent from all public ResourceSpecs/status/audit/OTEL); `Guest.spec.systemArtifactId` links Guest to artifact; `Volume.source.systemArtifactId` links Volume to artifact; minijail profile emitter structurally unchanged; old `d2b.vms.` keying retired", "entryContracts" : [ "ADR-046-nix-configuration" ], @@ -15988,6 +16403,7 @@ ], "specId" : "ADR-046-nix-configuration", "topologicalRank" : 15, + "validation" : "Closure map round-trip; per-VM store hardlink integrity; `storePath` present in private catalog; `storePath` absent from all emitted public ResourceSpecs and status/audit/OTEL surfaces", "wave" : "W5" }, { @@ -15995,6 +16411,7 @@ "destinations" : [ "Per-Zone `zones//bundle.json` (`schemaVersion`); Host resource in `zones//hosts.json`" ], + "detailedDesign" : "`manifestVersion` → `schemaVersion`; `host.json` host config folded into Host resource spec; CHANGELOG entry for rename required", "entryContracts" : [ "ADR-046-nix-configuration" ], @@ -16009,6 +16426,7 @@ ], "specId" : "ADR-046-nix-configuration", "topologicalRank" : 14, + "validation" : "Schema drift gate; CHANGELOG enforcement", "wave" : "W5" }, { @@ -16016,6 +16434,7 @@ "destinations" : [ "`nixos-modules/assertions.nix`" ], + "detailedDesign" : "Migrate existing assertions to Zone/Resource terminology; add ref validation, owner cycles, CIDR overlap (Zones), provider resolution, RoleBinding verb set assertions Primary reuse disposition: `adapt`. Preserved source-plan detail: extend in place.", "entryContracts" : [ "ADR-046-nix-configuration" ], @@ -16032,6 +16451,7 @@ ], "specId" : "ADR-046-nix-configuration", "topologicalRank" : 13, + "validation" : "Each new assertion has a failing-config test vector", "wave" : "W5" }, { @@ -16039,6 +16459,7 @@ "destinations" : [ "Same files; updated to use Zone bundle activation path and Zone resource state dirs" ], + "detailedDesign" : "`d2b-activation-helper` updated to validate/stage per-Zone bundles; `d2bd.service` updated to read Zone bundle; `d2b` group retained for `SO_PEERCRED` Primary reuse disposition: `adapt`. Preserved source-plan detail: retain and adapt.", "entryContracts" : [ "ADR-046-nix-configuration" ], @@ -16053,6 +16474,7 @@ ], "specId" : "ADR-046-nix-configuration", "topologicalRank" : 11, + "validation" : "Host-integration test with Zone bundle activation and daemon readiness", "wave" : "W5" }, { @@ -16060,6 +16482,7 @@ "destinations" : [ "Network reconciliation by `Provider/network-local` Process resources" ], + "detailedDesign" : "Current bridge/NAT/DHCP/firewall Nix units retained; `Provider/network-local` controller emits equivalent configuration from Network resources; `lib.mkForce` neutralization preserved Primary reuse disposition: `copy-unchanged`. Preserved source-plan detail: retain until Provider successor.", "entryContracts" : [ "ADR-046-nix-configuration" ], @@ -16074,6 +16497,7 @@ ], "specId" : "ADR-046-nix-configuration", "topologicalRank" : 12, + "validation" : "`tests/net-vm-network-eval.sh` passes against Network resource spec", "wave" : "W5" }, { @@ -16081,6 +16505,7 @@ "destinations" : [ "Per-VM store reconciliation by `Provider/volume-virtiofs` EphemeralProcess/Process resources" ], + "detailedDesign" : "`store.nix` retained; `Provider/volume-virtiofs` controller creates equivalent EphemeralProcess; per-VM store path derived from Zone stateDir + Guest name via Provider, not raw path in spec Primary reuse disposition: `copy-unchanged`. Preserved source-plan detail: retain until Provider successor.", "entryContracts" : [ "ADR-046-nix-configuration" ], @@ -16095,6 +16520,7 @@ ], "specId" : "ADR-046-nix-configuration", "topologicalRank" : 16, + "validation" : "Store hardlink integrity; no direct `/nix/store` export", "wave" : "W5" }, { @@ -16102,6 +16528,7 @@ "destinations" : [ "`Provider/device-tpm`, `Provider/device-usbip`, `Provider/device-gpu`, `Provider/audio-pipewire` resource install declarations in `options-zones-resources.nix`" ], + "detailedDesign" : "`d2b.vms..tpm.enable = true` → `d2b.zones..resources.vm-tpm = { type = \"Device\"; spec = { providerRef = \"Provider/device-tpm\"; ... }; }`; all component eval assertions migrated to `assertions.nix`; GuestSshReadiness retired at v3 cutover; Usbip long-lived backend/proxy → `Process`, per-busid attach/detach → `EphemeralProcess`, both owned by `Provider/device-usbip` Primary reuse disposition: `replace`. Preserved source-plan detail: each component becomes a Provider install resource + Device/Guest spec field.", "entryContracts" : [ "ADR-046-nix-configuration" ], @@ -16116,6 +16543,7 @@ ], "specId" : "ADR-046-nix-configuration", "topologicalRank" : 12, + "validation" : "Existing component eval tests; `tests/usbip-gating-eval.sh`; `tests/video-contract-eval.sh`", "wave" : "W5" }, { @@ -16123,6 +16551,7 @@ "destinations" : [ "`docs/reference/schemas/v3/.json` for each ResourceType; `nixos-modules/resource-schema-validation.nix` (validates emitted spec against committed JSON Schema at build time); `nixos-modules/provider-settings-validation.nix` (validates `spec.provider.settings` where declared in schema, and Provider `config`, against Provider-embedded schema at build time); `nixos-modules/assertions.nix` (Credential ref enforcement, secret-pattern rejection)" ], + "detailedDesign" : "`cargo xtask gen-schemas` emits one JSON Schema per ResourceType under `docs/reference/schemas/v3/`; Nix derivation reads these schemas from `pkgs.d2b-resource-schemas` and validates every emitted `spec` JSON before producing the Zone bundle; Provider-settings validation reads `settingsSchemaDigest` from `provider-catalog.json` and resolves the schema from the Provider package closure; Credential ref enforcement: eval assertion rejects any `spec` string field matching `-----BEGIN`, `eyJ`, or a hex string ≥ 32 bytes in a secret-typed field; `managedBy` in any input spec rejected at eval (core-set runtime field, never in Nix input); bundle integrity: `candidateId`/`contentId` computed over canonical sorted output Primary reuse disposition: `adapt`. Preserved source-plan detail: extend xtask schema generation; new Nix eval/build validation hooks.", "entryContracts" : [ "ADR-046-nix-configuration" ], @@ -16140,6 +16569,7 @@ ], "specId" : "ADR-046-nix-configuration", "topologicalRank" : 15, + "validation" : "Schema round-trip: emit spec, validate against schema, verify byte-identical re-emit; `spec.provider.settings` rejection test (unknown field, out-of-bounds value, raw store path, secret bytes) where schema declares the field; Provider `config` rejection test (unknown field); Credential ref enforcement: PEM-in-spec rejected; secret-pattern-in-spec rejected; valid `Credential/` ref accepted; `managedBy` in spec input rejected at eval", "wave" : "W5" }, { @@ -16147,6 +16577,7 @@ "destinations" : [ "Configuration-publication controller handler in `packages/d2bd/src/config_publication.rs`; `ConfigurationOwnedClassifier`; `AbsentResourceReaper`; `Zone` status conditions in `d2b-contracts/src/v3/zone_status.rs`; cleanup audit emitter in `d2b-state/src/audit_segments.rs`" ], + "detailedDesign" : "`ConfigurationOwnedClassifier`: classify resources by core-set `managedBy` field only — `managedBy=configuration` resources are owned by config publication; `managedBy=controller` and `managedBy=api` resources are never touched. At activation, diff new-generation bundle name+type set against all resources with `managedBy=configuration` in the Zone store; resources absent from the new bundle are enqueued for Delete. Never infer ownership from `ownerRef`, labels, or absence from emitted files. `AbsentResourceReaper`: processes the Absent queue asynchronously; does not block pointer swap (step 4); sets `status.phase=Pending` + `PendingDeletion` condition (`reason: AbsentFromConfiguration`); waits for all finalizers to clear; commits a single store transaction that appends the `Deleted` revision and removes the resource row and its indexes; subsequent `Get` returns not-found; the `ResourceDelete` audit event is appended afterward with dedup/exactly-once recovery. Zone phase: `Pending` during pointer-swap-to-first-reconcile window; `Degraded` while any `managedBy=configuration` resource carries `PendingDeletion` or a ZoneLink lags; `Ready` when all reconciled. Generation pruning: prune when `generationIndex ≤ activeIndex - retainedGenerations` AND all enqueued resources from that generation have reached `Deleted`. Rollback: re-adopt `managedBy=configuration` resources in `Pending/PendingDeletion` back to the rollback target generation's owned set", "entryContracts" : [ "ADR-046-nix-configuration" ], @@ -16162,6 +16593,7 @@ ], "specId" : "ADR-046-nix-configuration", "topologicalRank" : 14, + "validation" : "Classification: `managedBy=controller` resource never enqueued (even if absent from bundle). `managedBy=api` resource never enqueued. `managedBy=configuration` resource absent from new bundle always enqueued. Finalizer safety: resource with active finalizer enters DeletionBlocked; not force-deleted; stays in `Pending`. Final deletion: a single store transaction appends the `Deleted` revision and removes the resource row and its indexes; subsequent `Get` returns not-found; `ResourceDelete` audit event is appended afterward with dedup/exactly-once recovery. Zone status: `Pending` during activation; `Degraded` while PendingDeletion outstanding; `Ready` when clean. Audit: `ResourceDelete` event includes `configurationGeneration` field.", "wave" : "W5" }, { @@ -16169,6 +16601,7 @@ "destinations" : [ "`packages/d2b-contract-tests/tests/provider-crate-layout.rs`; workspace scan in `packages/xtask/src/main.rs` (extend `check-workspace` or add `check-provider-layout` subcommand)" ], + "detailedDesign" : "Parse the root `packages/Cargo.toml` workspace member list; for every member path matching `packages/d2b-provider-*-*`: assert (1) `src/` directory exists and contains at least one `.rs` file; (2) `tests/` directory exists and contains at least one `.rs` file; (3) `integration/` directory exists and contains at least one `.rs` or fixture file; (4) `README.md` exists and is ≥ 200 bytes. All four conditions required; any single failure fails the test with a structured message naming the crate and missing path. Test runs as `cargo test -p d2b-contract-tests provider_crate_layout`; wired into `make test-policy`.", "entryContracts" : [ "ADR-046-nix-configuration" ], @@ -16183,6 +16616,7 @@ ], "specId" : "ADR-046-nix-configuration", "topologicalRank" : 11, + "validation" : "Fixture: add a stub `packages/d2b-provider-test-missing-integration/` with `src/lib.rs` and `tests/smoke.rs` but no `integration/` and no `README.md`; assert test fails naming both missing paths. Add complete stub with all four paths; assert test passes.", "wave" : "W5" }, { @@ -16190,6 +16624,7 @@ "destinations" : [ "`nixos-modules/artifact-catalog.nix` (new emitter); `nixos-modules/options-artifacts.nix` (new option: `d2b.artifacts. = { package; type; }`); `/etc/d2b/artifact-catalog.json` (output artifact, `root:d2bd` 0640); `nixos-modules/bundle-zones.nix` (extend to include artifact catalog digest in `bundle.json`); `nixos-modules/options-zones-resources.nix` (replace `closureRef` / `nixosSystem` helpers with `systemArtifactId` validation)" ], + "detailedDesign" : "`d2b.artifacts.` attrset option: `id` matches `^[a-z][a-z0-9-]*$`; `type ∈ { \"provider\", \"nixos-system\", \"nixos-module-set\", \"config-bundle\" }`; no other fields. Emitter computes `pkgs.closureInfo` for each entry and writes `artifact-catalog.json` with sorted entries (by `artifactId`) containing `artifactId`, `type`, `storePath` (private, for activation-helper staging), `packageDigest`, `closureDigest`, `closureSize`. `storePath` is a private field of the root:d2bd 0640 file; it is never emitted in public ResourceSpecs, status fields, audit records, or OTEL telemetry. The `bundle.json` manifest includes the artifact catalog file entry and its SHA256 digest. `d2b-activation-helper` reads `storePath` from the catalog to resolve and stage each artifact; verifies catalog digest before staging. Build-time validation: `artifactId` / `systemArtifactId` / `source.systemArtifactId` fields in resource specs resolve against `d2b.artifacts`; type-mismatch fails with a structured error. `d2b.providerCatalog..package` option is removed; replaced by `d2b.providerCatalog..artifactId`. `Guest.spec.systemArtifactId` replaces the former `nixosSystem` Nix-only helper. `Volume.source.systemArtifactId` replaces `source.closureRef`.", "entryContracts" : [ "ADR-046-nix-configuration" ], @@ -16205,6 +16640,7 @@ ], "specId" : "ADR-046-nix-configuration", "topologicalRank" : 14, + "validation" : "Artifact catalog round-trip: declare artifact, build, verify JSON entry present with correct type/digests/storePath; missing artifact ID fails build with structured error; wrong-type artifact fails build; `storePath` absent from all public ResourceSpecs and status/audit/OTEL surfaces", "wave" : "W5" }, { @@ -16212,6 +16648,7 @@ "destinations" : [ "`packages/d2b-bus/src/session/` (new crate `d2b-bus`); `ComponentSessionDriver` becomes the central abstraction for all Zone bus sessions." ], + "detailedDesign" : "Complete session protocol: preface negotiation, Noise handshake profiles, record protection, replay cache, fair scheduler, fragmentation/reassembly, keepalive, ttrpc bridge, generation discovery, and `ComponentSessionDriver` application surface. Primary reuse disposition: `adapt`. Preserved source-plan detail: Reuse from main commit `a1cc0b2d`; adapt ADR45 EndpointPurpose/ServicePackage names listed in `ADR45 exclusions` while preserving wire tag values..", "entryContracts" : [ "ADR-046-nix-configuration" ], @@ -16226,6 +16663,7 @@ ], "specId" : "ADR-046-nix-configuration", "topologicalRank" : 11, + "validation" : "Port `component_session.rs` and `noise_vectors.rs`; run `make test-rust`; Noise KAT vectors must pass after copy.", "wave" : "W5" }, { @@ -16233,6 +16671,7 @@ "destinations" : [ "`packages/d2b-bus/src/session/` (same crate as ADR046-nix-023)." ], + "detailedDesign" : "Attachment ownership validates descriptors only after authenticated decrypt; unconsumed payloads close on drop. NamedStreamMux preserves half-close, reset, per-stream limits, aggregate limits, and credit-based flow control. Primary reuse disposition: `adapt`. Preserved source-plan detail: Reuse from main commit `a1cc0b2d`; keep attachment/stream semantics and descriptor validation, with no ADR45 realm adaptation required for this item..", "entryContracts" : [ "ADR-046-nix-configuration" ], @@ -16248,6 +16687,7 @@ ], "specId" : "ADR-046-nix-configuration", "topologicalRank" : 12, + "validation" : "Port `component_session.rs` attachment ownership, stream mux, and credit-accounting tests.", "wave" : "W5" }, { @@ -16255,6 +16695,7 @@ "destinations" : [ "`packages/d2b-bus/src/session/`." ], + "detailedDesign" : "Per-generation `RequestRegistry`, request-wide cancellation, `DeadlineBudget::admit_metadata()` as the inbound metadata gate, and single-use `BootstrapAdmission::consume()` replay protection. Primary reuse disposition: `adapt`. Preserved source-plan detail: Reuse from main commit `a1cc0b2d`; adapt `CancelRequest`/`CancelResponse` module paths if v3 services move the common proto types..", "entryContracts" : [ "ADR-046-nix-configuration" ], @@ -16270,6 +16711,7 @@ ], "specId" : "ADR-046-nix-configuration", "topologicalRank" : 12, + "validation" : "Port cancel-before-dispatch, cancel-after-dispatch, generation-mismatch cancel, and deadline admit tests; add v3 common-proto path adapter test.", "wave" : "W5" }, { @@ -16277,6 +16719,7 @@ "destinations" : [ "`packages/d2b-bus/src/transport/unix/`." ], + "detailedDesign" : "Unix seqpacket/stream transports, pidfd identity verification, six-scope credit pool with host/process bounds, systemd-activated seqpacket listener support, and length-prefixed vsock framing. Primary reuse disposition: `adapt`. Preserved source-plan detail: Reuse from main commit `a1cc0b2d`; retain host-socket/native-vsock feature gates and replace only ADR45 socket-name strings with Zone bootstrap config..", "entryContracts" : [ "ADR-046-nix-configuration" ], @@ -16291,6 +16734,7 @@ ], "specId" : "ADR-046-nix-configuration", "topologicalRank" : 12, + "validation" : "Port `unix_session.rs` seqpacket, stream, pidfd, FD attachment, and credit pool tests; add Zone bootstrap socket-name fixture.", "wave" : "W5" }, { @@ -16298,6 +16742,7 @@ "destinations" : [ "`packages/d2b-contracts/src/v3/component_session.rs`." ], + "detailedDesign" : "Canonical wire values, bounded vectors, binary size constants, closed-enum tag codecs, and fail-closed validation for the ComponentSession protocol. Primary reuse disposition: `adapt`. Preserved source-plan detail: Reuse wire constants/types from main commit `a1cc0b2d`; rename ADR45 realm-oriented variants only as directed by the bus owning spec and preserve numeric tags..", "entryContracts" : [ "ADR-046-nix-configuration" ], @@ -16311,6 +16756,7 @@ ], "specId" : "ADR-046-nix-configuration", "topologicalRank" : 10, + "validation" : "Port component session and Noise vector tests; add wire-tag stability gates for renamed role/service/purpose variants.", "wave" : "W5" }, { @@ -16318,6 +16764,7 @@ "destinations" : [ "`packages/d2b-contracts/src/v3/services/`." ], + "detailedDesign" : "Versioned ttrpc service modules preserve server dispatch tables, shared request/cancel/outcome/error types, and service fingerprint inventory. Primary reuse disposition: `adapt`. Preserved source-plan detail: Reuse generated ttrpc service tables from main commit `a1cc0b2d`; adapt ADR45 Realm/Workload fields and service names to v3 Zone/Resource naming per owning bus spec..", "entryContracts" : [ "ADR-046-nix-configuration" ], @@ -16332,6 +16779,7 @@ ], "specId" : "ADR-046-nix-configuration", "topologicalRank" : 11, + "validation" : "Port provider-toolkit conformance and service fingerprint assertions; add v3 service-name gate for Zone service and ResourceName field adaptation.", "wave" : "W5" }, { @@ -16339,6 +16787,7 @@ "destinations" : [ "`packages/d2b-provider/src/` (adapt in place)." ], + "detailedDesign" : "ProviderRegistry snapshot admission/drain, bounded in-flight caps, typed RpcProviderProxy dispatch, and build-time RegistryLimits validation. Primary reuse disposition: `adapt`. Preserved source-plan detail: Reuse ProviderRegistry/RPC runtime from main commit `a1cc0b2d`; adapt `SessionIdentity` to Zone naming and update EndpointRole/ServicePackage names per owning bus spec..", "entryContracts" : [ "ADR-046-nix-configuration" ], @@ -16354,6 +16803,7 @@ ], "specId" : "ADR-046-nix-configuration", "topologicalRank" : 12, + "validation" : "Port `d2b-provider` `runtime.rs` tests; add v3 AdmissionOptions peer-role and SessionIdentity zone-name gates.", "wave" : "W5" }, { @@ -16361,6 +16811,7 @@ "destinations" : [ "`packages/d2b-provider-toolkit/src/` (adapt in place)." ], + "detailedDesign" : "ProviderAgentAdapter validates ComponentSession identity, GeneratedProviderServiceServer dispatches all ProviderType method families with bounded stores/in-flight state, and conformance kit gates every Provider implementation. Primary reuse disposition: `adapt`. Preserved source-plan detail: Reuse provider toolkit from main commit `a1cc0b2d`; adapt endpoint role/service variant names and v2_identity imports while preserving wire tags..", "entryContracts" : [ "ADR-046-nix-configuration" ], @@ -16376,6 +16827,7 @@ ], "specId" : "ADR-046-nix-configuration", "topologicalRank" : 13, + "validation" : "Port `conformance.rs`; assert endpoint role/service wire tags remain 7/4 after any variant rename.", "wave" : "W5" }, { @@ -16383,6 +16835,7 @@ "destinations" : [ "`nixos-modules/resources-sharing.nix`; `nixos-modules/assertions.nix`; `tests/unit/nix/cases/resource-sharing.nix`" ], + "detailedDesign" : "Compile the eight exact D098 semantic Service/Binding types plus `ResourceExport`/`ResourceImport` without a second vocabulary. Nix authors authority Services and consumer Bindings, never projection Services; Binding requires same-Zone `serviceRef` and allowed target, while all observed realization remains absent from spec. Core receives a stable projection name/factory fingerprint and creates exactly one same-type Service with ResourceImport ownership, `providerRef`, semantic base/import fields, and no `spec.provider`. Reject Device/Endpoint/Binding export/projection, implementation-qualified/former `*State` aliases, implementation detail in base schemas, cross-Zone refs, USB export without every policy opt-in, and any USB/security-key physical resource that does not use the exact Core-derived `physical-usb-backing` tuple class.", "entryContracts" : [ "ADR-046-nix-configuration" ], @@ -16401,6 +16854,7 @@ ], "specId" : "ADR-046-nix-configuration", "topologicalRank" : 16, + "validation" : "Fast Nix eval/build tests cover all four exact pairs, common base schema discovery, canonical minimal base without `spec.provider`, stable lowering across repeated evaluations, same-Zone refs/targets, strict Provider extension placement, Service-only export, exactly-one Core projection metadata with no `spec.provider`, no auto-Binding, status-field rejection in spec, USB/security-key resolution to one byte-identical Host-global `(Host, physical-usb-backing, opaqueKeyDigest)` collision before effects, Provider-private-class bypass rejection, and rejection of all forbidden names/aliases.", "wave" : "W5" }, { @@ -16408,6 +16862,7 @@ "destinations" : [ "`packages/d2b-client/src/` (adapt in place)." ], + "detailedDesign" : "Transport-neutral typed async client, ComponentSessionConnector abstraction, precise SessionFailure classification, signed metadata envelopes, named-stream client abstraction, and host-socket connector. Primary reuse disposition: `adapt`. Preserved source-plan detail: Reuse async client from main commit `a1cc0b2d`; adapt TargetInput, ServiceOwner, service packages, and local socket identity to Zone/ResourceRef semantics..", "entryContracts" : [ "ADR-046-nix-configuration" ], @@ -16423,6 +16878,7 @@ ], "specId" : "ADR-046-nix-configuration", "topologicalRank" : 13, + "validation" : "Port `d2b-client` tests; add ResourceRef TargetInput and Zone bootstrap socket identity gates.", "wave" : "W5" }, { @@ -16430,6 +16886,7 @@ "destinations" : [ "`packages/d2b-bus/src/routing/zone_service.rs`." ], + "detailedDesign" : "ZoneSessionAuthority preserves no-host-credential custody, gateway/ZoneLink custody behavior, bounded concurrent dispatch, and graceful shutdown semantics. Primary reuse disposition: `adapt`. Preserved source-plan detail: Reuse routing service behavior from main commit `a1cc0b2d`; rename RealmSessionAuthority/service identifiers to Zone terms per owning bus spec while preserving custody behavior..", "entryContracts" : [ "ADR-046-nix-configuration" ], @@ -16445,6 +16902,7 @@ ], "specId" : "ADR-046-nix-configuration", "topologicalRank" : 12, + "validation" : "Port `realm_service_v2.rs` and topology harness tests with Zone naming; add service-name wire gate.", "wave" : "W5" }, { @@ -16452,6 +16910,7 @@ "destinations" : [ "`packages/d2bd/src/provider_registry.rs` (adapt in place)." ], + "detailedDesign" : "Named ProviderCompositionError variants, first-party factory instantiation, bundle resolver validation, version/schema constants, and monotone lifecycle operation IDs. Primary reuse disposition: `adapt`. Preserved source-plan detail: Reuse fail-closed provider composition from main commit `a1cc0b2d`; adapt v2 identity/bundle artifact types to v3 provider registry contracts and bump bundle versions..", "entryContracts" : [ "ADR-046-nix-configuration" ], @@ -16468,6 +16927,7 @@ ], "specId" : "ADR-046-nix-configuration", "topologicalRank" : 13, + "validation" : "Port provider_registry composition tests; add PROVIDER_BUNDLE_VERSION/SCHEMA_VERSION v3 bump gate and ZoneId/ResourceName binding test.", "wave" : "W5" }, { @@ -16475,6 +16935,7 @@ "destinations" : [ "`packages/d2bd/src/provider_effects.rs` (adapt in place)." ], + "detailedDesign" : "DaemonEffectAdapters bind semantic effect ports; ProviderLifecycleDispatch bounds lifecycle mutations and routes broker start/stop through blocking adapters with idempotency-key deduplication. Primary reuse disposition: `adapt`. Preserved source-plan detail: Reuse descriptor-bound effect dispatch from main commit `a1cc0b2d`; adapt lifecycle request wire type from VM name to Zone-scoped Guest ResourceRef..", "entryContracts" : [ "ADR-046-nix-configuration" ], @@ -16490,6 +16951,7 @@ ], "specId" : "ADR-046-nix-configuration", "topologicalRank" : 14, + "validation" : "Port `provider_effects.rs` lifecycle dispatch tests; add ResourceRef Guest lifecycle request and BrokerCallerRole compatibility gates.", "wave" : "W5" }, { @@ -16497,6 +16959,7 @@ "destinations" : [ "`d2b-contracts` trait plus `d2b-core` core adapter; maps to broker wire operations and audit emission." ], + "detailedDesign" : "Implement `NetworkEffectPort` core adapter in `d2b-core`; map to broker wire ops; emit audit records. Versioning: minor releases may add methods with default impls; major releases require Provider upgrade. The trait lives in `d2b-contracts`; the adapter in `d2b-core`.", "entryContracts" : [ "ADR-046-provider-network-local" ], @@ -16510,6 +16973,7 @@ ], "specId" : "ADR-046-provider-network-local", "topologicalRank" : 11, + "validation" : "`packages/d2b-provider-network-local/tests/fault_injection.rs` verifies fake `NetworkEffectPort` behavior, error mapping, no broker socket in provider context, and audit-safe adapter boundaries.", "wave" : "W6" }, { @@ -16517,6 +16981,7 @@ "destinations" : [ "Broker wire contract and broker/core adapter operation table for `CreateBridge`, `DeleteBridge`, `ReadNftablesDigest`, `ReadSysctlState`, and `ReadBridgePortFlags`." ], + "detailedDesign" : "Add `CreateBridge`, `DeleteBridge`, `ReadNftablesDigest`, `ReadSysctlState`, `ReadBridgePortFlags` broker ops. Primary reuse disposition: `adapt`. Preserved source-plan detail: extend broker wire with net-new operations and reuse existing closed broker-operation dispatch shape.", "entryContracts" : [ "ADR-046-provider-network-local" ], @@ -16531,6 +16996,7 @@ ], "specId" : "ADR-046-provider-network-local", "topologicalRank" : 12, + "validation" : "`integration/host_fabric.rs` covers bridge create/delete, nftables apply/digest, IPv6 suppression, NetworkManager unmanaged handling, and real `NetworkEffectPort` implementation.", "wave" : "W6" }, { @@ -16538,6 +17004,7 @@ "destinations" : [ "`d2b-contracts` opaque byte-array newtypes; core-held HMAC key and provider-facing redacted handle types." ], + "detailedDesign" : "Implement `AttachmentHandle` and `FabricHandle` as opaque byte-array newtypes (32 bytes of HMAC-SHA-256 over internal identity material; key held by core). Each handle is single-use; revocation is implicit when the owning Network is deleted. These types are declared in `d2b-contracts`, not in the provider crate.", "entryContracts" : [ "ADR-046-provider-network-local" ], @@ -16551,6 +17018,7 @@ ], "specId" : "ADR-046-provider-network-local", "topologicalRank" : 11, + "validation" : "`tests/fault_injection.rs` and `tests/controller_state.rs` cover opaque-handle mismatch, revocation-on-delete, and no raw IfName/IP/MAC public surface.", "wave" : "W6" }, { @@ -16558,6 +17026,7 @@ "destinations" : [ "Core LaunchTicket builder and dependency resolver that walks `Guest.ownerRef: Network/` to resolved tap FDs." ], + "detailedDesign" : "Implement LaunchTicket FD resolution: when core builds the LaunchTicket for a Guest with `ownerRef: Network/`, it walks the owner graph, locates the Network, reads its internally-held `AttachmentHandle` set, and includes the corresponding tap FDs in the ticket. No API surface for the provider or runtime is required beyond the existing LaunchTicket mechanism. Primary reuse disposition: `create`. Preserved source-plan detail: net-new LaunchTicket integration; reuse existing LaunchTicket mechanism without adding provider/runtime API surface.", "entryContracts" : [ "ADR-046-provider-network-local" ], @@ -16572,6 +17041,7 @@ ], "specId" : "ADR-046-provider-network-local", "topologicalRank" : 12, + "validation" : "`integration/guest_lifecycle.rs` validates net-VM/workload Guest lifecycle, opaque attachment handle resolution, and `systemArtifactId` binding.", "wave" : "W6" }, { @@ -16579,6 +17049,7 @@ "destinations" : [ "Core adapter imports `d2b-host` modules; `packages/d2b-provider-network-local/src/ifname.rs` re-exports `d2b_host::ifname::derive_ifname` only." ], + "detailedDesign" : "The `d2b-host` IfName/nftables/bridge/route modules are consumed directly by the core adapter (not by the provider crate). The provider crate re-exports only `d2b_host::ifname::derive_ifname` for validation purposes. No additional extraction work is required beyond confirming the `d2b-host` API surface is stable. Primary reuse disposition: `adapt`. Preserved source-plan detail: reuse directly in core adapter; provider re-exports only `derive_ifname` for validation.", "entryContracts" : [ "ADR-046-provider-network-local" ], @@ -16592,13 +17063,15 @@ ], "specId" : "ADR-046-provider-network-local", "topologicalRank" : 11, + "validation" : "`tests/ifname_derive.rs`, `tests/fault_injection.rs`, and `integration/host_fabric.rs` prove derivation, adapter reuse, and real host-fabric behavior.", "wave" : "W6" }, { "blockers" : [], "destinations" : [ - "`packages/d2b-provider-network-local/src/controller.rs`." + "`packages/d2b-provider-network-local/src/{controller.rs,metrics.rs}`." ], + "detailedDesign" : "Implement `controller.rs` reconcile/observe/finalize handlers with `NetworkEffectPort` injection and the §21 metric descriptors with closed semantic labels. No descriptor may carry `vm`, `zone`, `zone_id`, `zone_uid`, `network`, or another resource-name-derived key; Network/Zone identity stays only in OTEL resource attributes and permitted audit fields. Primary reuse disposition: `adapt`. Preserved source-plan detail: port semantics into provider reconcile state machine; do not reuse static per-env systemd/Nix ownership.", "entryContracts" : [ "ADR-046-provider-network-local" ], @@ -16617,6 +17090,7 @@ ], "specId" : "ADR-046-provider-network-local", "topologicalRank" : 13, + "validation" : "`tests/controller_state.rs` covers normal reconcile, errors, finalizer ordering, adoption on restart, and observe/drift cycles with deterministic clock; `tests/metrics_labels.rs` structurally asserts exact identity-key absence and that a Network-name canary never enters metric label values.", "wave" : "W6" }, { @@ -16624,6 +17098,7 @@ "destinations" : [ "`packages/d2b-provider-network-local/src/process_specs.rs` agent template plus agent service implementation in the net-VM artifact." ], + "detailedDesign" : "Implement `NetworkAgentService` Noise-KK vsock ComponentSession (Reload + ReadinessQuery methods). Agent reconnect policy: if the controller cannot reach the agent vsock (Guest restart in progress), it retries with exponential backoff up to `drainTimeout` of the agent Process; after timeout it deletes and re-creates the agent Process resource. Primary reuse disposition: `create`. Preserved source-plan detail: net-new service; preserve semantic nftables/routes reload behavior from v1 net VM configuration.", "entryContracts" : [ "ADR-046-provider-network-local" ], @@ -16637,6 +17112,7 @@ ], "specId" : "ADR-046-provider-network-local", "topologicalRank" : 11, + "validation" : "`integration/agent_reload.rs` validates Reload, `nft_applied` and `routes_applied` predicates, reconnect behavior, and config digest matching.", "wave" : "W6" }, { @@ -16644,6 +17120,7 @@ "destinations" : [ "`packages/d2b-provider-network-local/src/config_volume.rs`." ], + "detailedDesign" : "Implement config Volume content rendering (dnsmasq.conf, nftables.rules, routing.conf, attachments.json). Primary reuse disposition: `adapt`. Preserved source-plan detail: port and render into bounded config Volume files.", "entryContracts" : [ "ADR-046-provider-network-local" ], @@ -16657,6 +17134,7 @@ ], "specId" : "ADR-046-provider-network-local", "topologicalRank" : 11, + "validation" : "`tests/controller_state.rs`, `integration/agent_reload.rs`, and `integration/delete_sequence.rs` validate rendering, write flow, reload, and cleanup ordering.", "wave" : "W6" }, { @@ -16664,6 +17142,7 @@ "destinations" : [ "`packages/d2b-provider-network-local/src/process_specs.rs`." ], + "detailedDesign" : "Implement canonical Process spec builders for agent, dnsmasq, mdns-reflector, mdns-dnsbridge. Primary reuse disposition: `adapt`. Preserved source-plan detail: port service semantics into canonical Process resource specs.", "entryContracts" : [ "ADR-046-provider-network-local" ], @@ -16677,6 +17156,7 @@ ], "specId" : "ADR-046-provider-network-local", "topologicalRank" : 11, + "validation" : "`tests/controller_state.rs`, `integration/mdns_reflector.rs`, and eval case `process-sandbox-netns.nix` validate Process shape, optional mDNS, and guest-netns capability isolation.", "wave" : "W6" }, { @@ -16684,6 +17164,7 @@ "destinations" : [ "`net-vm-base` nixos-system artifact and artifact catalog entry `d2b.artifacts.net-vm-base`." ], + "detailedDesign" : "Build generic `net-vm-base` nixos-system artifact with net-agent binary, agent-service endpoint, guest-agent binary, standard NIC bootstrap, lib.mkForce override; bake `net-local-controller` account with the UID/GID allocated from the host-users reservation table (documented in `nixos-modules/host-users.nix`). Primary reuse disposition: `adapt`. Preserved source-plan detail: preserve generic boot/safety invariants; exclude per-Network static config.", "entryContracts" : [ "ADR-046-provider-network-local" ], @@ -16697,6 +17178,7 @@ ], "specId" : "ADR-046-provider-network-local", "topologicalRank" : 11, + "validation" : "Eval cases `net-vm-artifact-id-eval.nix` and `network-spec-eval.nix`, plus `tests/net-vm-network-eval.sh` for the lib.mkForce invariant.", "wave" : "W6" }, { @@ -16704,6 +17186,7 @@ "destinations" : [ "Nix module resource emission for `Provider/network-local`, `User/net-local-controller`, host OS account, `provider-network-local`, and `net-vm-base` artifacts." ], + "detailedDesign" : "Nix module for `Provider/network-local` resource declaration; `User/net-local-controller` declaration; OS account provisioning; artifact catalog entries. Primary reuse disposition: `adapt`. Preserved source-plan detail: adapt Nix option/resource emission and account provisioning to v3 resources.", "entryContracts" : [ "ADR-046-provider-network-local" ], @@ -16717,6 +17200,7 @@ ], "specId" : "ADR-046-provider-network-local", "topologicalRank" : 11, + "validation" : "Eval cases `network-spec-eval.nix`, `user-no-managed-by-eval.nix`, `net-vm-artifact-id-eval.nix`, and `make test-policy` for artifact/package paths.", "wave" : "W6" }, { @@ -16724,6 +17208,7 @@ "destinations" : [ "Nix flake/resource schema checks for declared Networks and provider `validate.rs` parity." ], + "detailedDesign" : "Build-time CIDR overlap check for declared Networks in flake check. Primary reuse disposition: `adapt`. Preserved source-plan detail: port/reuse overlap semantics in v3 eval checks and provider validation.", "entryContracts" : [ "ADR-046-provider-network-local" ], @@ -16737,6 +17222,7 @@ ], "specId" : "ADR-046-provider-network-local", "topologicalRank" : 11, + "validation" : "Eval case `network-cidr-overlap-eval.nix` and `tests/cidr_overlap.rs` cover same-Network, cross-Network, external CIDR, and adjacency cases.", "wave" : "W6" }, { @@ -16744,6 +17230,7 @@ "destinations" : [ "`packages/d2b-provider-network-local/tests/schema_roundtrip.rs`, `tests/ifname_derive.rs`, and `tests/cidr_overlap.rs`." ], + "detailedDesign" : "Conformance suite: NetworkSpec round-trip, IfName derivation, CIDR validation matrix. Primary reuse disposition: `adapt`. Preserved source-plan detail: adapt existing IfName/CIDR assertions into provider conformance tests.", "entryContracts" : [ "ADR-046-provider-network-local" ], @@ -16757,6 +17244,7 @@ ], "specId" : "ADR-046-provider-network-local", "topologicalRank" : 11, + "validation" : "The listed conformance tests themselves are the validation, with workspace policy ensuring `tests/` exists.", "wave" : "W6" }, { @@ -16764,6 +17252,7 @@ "destinations" : [ "`packages/d2b-provider-network-local/tests/controller_state.rs`." ], + "detailedDesign" : "Controller state-machine unit tests with fake `NetworkEffectPort` (from d2b-contracts mock) and deterministic clock.", "entryContracts" : [ "ADR-046-provider-network-local" ], @@ -16778,6 +17267,7 @@ ], "specId" : "ADR-046-provider-network-local", "topologicalRank" : 14, + "validation" : "`tests/controller_state.rs` covers normal path, CIDR conflict, User not Ready, Volume error, Guest timeout, agent reload failure, finalizer sequence, adoption, and drift.", "wave" : "W6" }, { @@ -16785,6 +17275,7 @@ "destinations" : [ "`packages/d2b-provider-network-local/integration/host_fabric.rs`, `guest_lifecycle.rs`, `agent_reload.rs`, and `delete_sequence.rs`." ], + "detailedDesign" : "Integration tests: full Network lifecycle (create, config update, agent Reload, delete sequence) in container environment. Primary reuse disposition: `adapt`. Preserved source-plan detail: adapt reusable semantic assertions into v3 integration coverage.", "entryContracts" : [ "ADR-046-provider-network-local" ], @@ -16798,6 +17289,7 @@ ], "specId" : "ADR-046-provider-network-local", "topologicalRank" : 11, + "validation" : "`make test-integration` for container tests and `make test-host-integration` where guest lifecycle requires host/KVM coverage.", "wave" : "W6" }, { @@ -16805,6 +17297,7 @@ "destinations" : [ "Process templates for agent and dnsmasq plus sandbox/eval tests." ], + "detailedDesign" : "Verify INV-NET-008 (Guest-network-admin isolation): Process Provider correctly inherits Guest netns for agent/dnsmasq. Primary reuse disposition: `adapt`. Preserved source-plan detail: preserve and verify existing guest-netns isolation invariant in v3 Process specs.", "entryContracts" : [ "ADR-046-provider-network-local" ], @@ -16818,6 +17311,7 @@ ], "specId" : "ADR-046-provider-network-local", "topologicalRank" : 11, + "validation" : "`tests/unit/nix/cases/process-sandbox-netns.nix` and provider Process-template tests assert no host capability or host network namespace grant.", "wave" : "W6" }, { @@ -16825,6 +17319,7 @@ "destinations" : [ "`packages/d2b-provider-network-local/README.md`." ], + "detailedDesign" : "`packages/d2b-provider-network-local/README.md` covering all 7 required topics. Primary reuse disposition: `create`. Preserved source-plan detail: net-new documentation.", "entryContracts" : [ "ADR-046-provider-network-local" ], @@ -16838,6 +17333,7 @@ ], "specId" : "ADR-046-provider-network-local", "topologicalRank" : 11, + "validation" : "`make test-policy` / `xtask workspace-policy` verifies required provider crate paths and README presence.", "wave" : "W6" }, { @@ -16845,6 +17341,7 @@ "destinations" : [ "Device-usbip EffectPort/adapter owns USBIP rules, drift, and strict provider status; network-local host/net-VM renderers and status cover only Network-owned policy." ], + "detailedDesign" : "`UsbipBindFirewallRule` remains the sole broker mutation path for exact per-Network/per-busid TCP/3240 exposure. Network-local emits no TCP/3240 match, excludes device-usbip ownership markers from its digest, and never reports USBIP drift. Primary reuse disposition: `adapt`. Preserved source-plan detail: preserve the typed closed broker op; remove both generic network-local USBIP allows.", "entryContracts" : [ "ADR-046-provider-network-local" ], @@ -16858,6 +17355,7 @@ ], "specId" : "ADR-046-provider-network-local", "topologicalRank" : 11, + "validation" : "Network-local host and net-VM firewall intent tests assert no TCP/3240/USBIP rule; USBIP rule churn leaves Network digest/`FirewallReady` unchanged; device-usbip tests own exact scoping, drift, status, and release.", "wave" : "W6" }, { @@ -16865,6 +17363,7 @@ "destinations" : [ "Provider descriptor, controller-main deployment, `tests/state_schema_roundtrip.rs`, and eval case `provider-state-volume-eval.nix`." ], + "detailedDesign" : "Confirm `controller-main` declares no stateNamespace and core ProviderDeployment creates no Provider state Volume or state mount; validate ProviderStateSet query returns empty for `Provider/network-local`; validate bounded operational state is written to revisioned/redacted status and the core Operation ledger with `status-oversize` conformance; confirm per-Network config Volumes remain `ownerRef: Network/` runtime/config operational Volumes outside the ProviderStateSet and `Volume` is not in `ResourceTypes implemented`. Primary reuse disposition: `create`. Preserved source-plan detail: net-new status-first provider-state conformance.", "entryContracts" : [ "ADR-046-provider-network-local" ], @@ -16878,6 +17377,7 @@ ], "specId" : "ADR-046-provider-network-local", "topologicalRank" : 11, + "validation" : "`tests/state_schema_roundtrip.rs` and `tests/unit/nix/cases/provider-state-volume-eval.nix` validate empty ProviderStateSet, status bounds/redaction, and config Volume exclusion.", "wave" : "W6" }, { @@ -16885,6 +17385,7 @@ "destinations" : [ "Network schema/Provider descriptor, Core authority index, Network reconcile/update/finalizer, runtime LaunchTicket resolver, and authority tests" ], + "detailedDesign" : "Register the external physical-NIC `AuthorityDescriptor`: Host-global `external-physical-nic/v1` Core-derived identity, `zero-or-one` authority, exclusive `passthru`/`private`/`vepa`, exclusive-by-default `bridge`, explicitly compatible bounded multiplexing only for bridge, `external-physical-nic-conflict`, exact owner proof, drain-release-reacquire update, forbidden export, and bounded FIFO holder policy. Primary reuse disposition: `adapt`. Preserved source-plan detail: adapt the existing broker-internal macvtap-FD creation path behind mandatory Core authority admission.", "entryContracts" : [ "ADR-046-provider-network-local" ], @@ -16898,6 +17399,7 @@ ], "specId" : "ADR-046-provider-network-local", "topologicalRank" : 11, + "validation" : "`external_nic_authority.rs` covers Core-derived identity, same-/cross-Zone conflicts, explicit bridge multiplexing, incompatible policy, non-bridge multiplex denial, no-effect rejection, adoption ambiguity, owner transfer, disruptive update, release ordering, and redaction; Nix eval and host integration cover declared configuration and lifecycle.", "wave" : "W6" }, { @@ -16905,6 +17407,7 @@ "destinations" : [ "`packages/d2b-provider-notification-desktop/src/{types,redact,action_nonce}.rs`" ], + "detailedDesign" : "`NotificationRequest`/`NotificationResult` DTOs and stream record types; bounded fields; closed category set; icon catalog contract; `ActionNonce`/`ActionNonceStore` adapted from main; no ResourceType DTO Primary reuse disposition: `adapt`. Preserved source-plan detail: copy/adapt.", "entryContracts" : [ "ADR-046-provider-notification-desktop" ], @@ -16918,6 +17421,7 @@ ], "specId" : "ADR-046-provider-notification-desktop", "topologicalRank" : 11, + "validation" : "`tests/stream_record.rs` — DTO schema vectors; `tests/action_nonce.rs` — single-use/TTL/capacity/replay", "wave" : "W6" }, { @@ -16925,6 +17429,7 @@ "destinations" : [ "`packages/d2b-provider-notification-desktop/src/stream_admission.rs`" ], + "detailedDesign" : "Session admission checks, Noise profile enforcement, transport class validation Primary reuse disposition: `adapt`. Preserved source-plan detail: copy/adapt.", "entryContracts" : [ "ADR-046-provider-notification-desktop" ], @@ -16940,6 +17445,7 @@ ], "specId" : "ADR-046-provider-notification-desktop", "topologicalRank" : 11, + "validation" : "`tests/stream_admission.rs` — all rejection vectors", "wave" : "W6" }, { @@ -16947,6 +17453,7 @@ "destinations" : [ "`packages/d2b-provider-notification-desktop/src/controller.rs`" ], + "detailedDesign" : "Async Process placement controller; watches `guestSources` Guest refs; creates/drains/deletes guest-source Processes; creates/stops host-sink Process on display-wayland readiness change; declares no Provider state Volume and does not own/add/create/delete Volumes; bounded non-secret operational state lives in `status`/the core Operation ledger (D087); notification delivery state (in-memory projection, action nonce store) is host-sink process memory only; no ResourceType reconcile loop", "entryContracts" : [ "ADR-046-provider-notification-desktop" ], @@ -16962,6 +17469,7 @@ ], "specId" : "ADR-046-provider-notification-desktop", "topologicalRank" : 12, + "validation" : "Unit tests for placement FSM in `tests/stream_record.rs`; Volume creation/deletion lifecycle in `tests/volume_lifecycle.rs`; see also `integration/cross_zone_source.rs` end-to-end", "wave" : "W6" }, { @@ -16969,6 +17477,7 @@ "destinations" : [ "`packages/d2b-provider-notification-desktop/src/host_sink.rs`" ], + "detailedDesign" : "D-Bus client; `DesktopNotificationSink` stream consumer; action nonce issuance; `DesktopNotificationObserver` projection (in-memory, not persisted); display-wayland ComponentSession bootstrap for pre-opened D-Bus FD", "entryContracts" : [ "ADR-046-provider-notification-desktop" ], @@ -16984,6 +17493,7 @@ ], "specId" : "ADR-046-provider-notification-desktop", "topologicalRank" : 13, + "validation" : "`integration/dbus_sink.rs`, `integration/observer_client.rs`, `integration/action_invoke.rs`", "wave" : "W6" }, { @@ -16991,6 +17501,7 @@ "destinations" : [ "`packages/d2b-provider-notification-desktop/src/guest_source.rs`" ], + "detailedDesign" : "Guest-side vsock ComponentSession; `NotificationRequest` record validation and field bounding; category filter; `DesktopNotificationSink` stream forwarding; `NotificationResult` handling; no host-side resource creation", "entryContracts" : [ "ADR-046-provider-notification-desktop" ], @@ -17005,6 +17516,7 @@ ], "specId" : "ADR-046-provider-notification-desktop", "topologicalRank" : 12, + "validation" : "`integration/cross_zone_source.rs`", "wave" : "W6" }, { @@ -17012,6 +17524,7 @@ "destinations" : [ "Nix: Zone resource authoring in `nixos-modules/`; metrics: `packages/d2b-provider-notification-desktop/src/`" ], + "detailedDesign" : "Zone Provider resource and RoleBinding Nix compiler output; `spec.config.guestSources` authoring and eval-time assertions; OTEL metric emitters with closed semantic labels and no Zone/resource-name-derived dimensions while retaining `d2b.zone` as a resource attribute; audit record emitters", "entryContracts" : [ "ADR-046-provider-notification-desktop" ], @@ -17026,6 +17539,7 @@ ], "specId" : "ADR-046-provider-notification-desktop", "topologicalRank" : 12, + "validation" : "Eval tests for category enforcement, displayWaylandRef assertion, guestRef resolution; `tests/stream_redaction.rs` for content-free telemetry; `tests/metrics_labels.rs` for structural exact absence of `vm`, `zone`, `zone_id`, `zone_uid`, and resource-name-derived keys plus notification/Zone-name canary absence", "wave" : "W6" }, { @@ -17033,6 +17547,7 @@ "destinations" : [ "`packages/d2b-contracts/src/v3/resource.rs`, `resource_status.rs`, `resource_schema.rs`" ], + "detailedDesign" : "Implement strict ResourceEnvelope, metadata, the three-layer spec shape (universal envelope + ResourceType base `spec.*` incl. `spec.providerRef` + optional canonical `spec.provider` `{ schemaId, schemaVersion, settings }`), the three-layer status shape (universal base + `status.resource` + optional `status.provider` with `providerRef`/`schemaId`/`schemaVersion`/`observedProviderGeneration`/`details`), phase/condition/outcome, canonical JSON, per-layer bounds/redaction, ownerRef/UID fields Primary reuse disposition: `adapt`. Preserved source-plan detail: extract and adapt.", "entryContracts" : [ "ADR-046-resource-object-model" ], @@ -17046,6 +17561,7 @@ ], "specId" : "ADR-046-resource-object-model", "topologicalRank" : 3, + "validation" : "Golden JSON/protobuf vectors; serde unknown-field; three-layer spec shape round-trip; canonical minimal base-spec acceptance; base-schema version/fingerprint conformance; `spec.provider` deny-unknown/version-mismatch/shadow rejection and providerRef-binding; three-layer status shape round-trip; base-only projection (universal + `status.resource`) ignores/omits `status.provider`; `status.provider` unknown-field/version-mismatch rejection; status redaction/size/time/phase tests; `status.update` currency object round-trip (state/reasons/disruption/preserveState/owned+dependency refs bounded); `spec.updatePolicy` base round-trip", "wave" : "W0" }, { @@ -17053,6 +17569,7 @@ "destinations" : [ "`packages/d2b-resource-store-redb/src/ownership.rs`, `packages/d2b-controller-toolkit/src/owner_hints.rs`" ], + "detailedDesign" : "Singular ownerRef resolution/UID binding, cycle/depth property checks, reverse index, owner hints, child-first deletion", "entryContracts" : [ "ADR-046-resource-object-model" ], @@ -17067,6 +17584,7 @@ ], "specId" : "ADR-046-resource-object-model", "topologicalRank" : 4, + "validation" : "Property tests for cycles/reparent/name reuse; integration tests for child drift repair and owner cascades", "wave" : "W0" }, { @@ -17074,6 +17592,7 @@ "destinations" : [ "`packages/d2b-provider-observability-otel/src/{forwarder_bin,controller,binding}.rs`" ], + "detailedDesign" : "Binding-owned forwarder: accept OTLP only from the exact Guest producer, relay through the Binding-private Endpoint/Volume to its edge collector, enforce bounded frames/quota/session timeout, and use no OTEL SDK. Map forwarder readiness to `status.provider`, then derive provider-neutral Binding ingest readiness; Process Provider owns launch/pidfd. Primary reuse disposition: `adapt`. Preserved source-plan detail: adapt (`OtelHostBridgeArgvInputs` vsock logic → native Rust OTLP relay); adapt (`OtelHostBridgeReadiness` → `TelemetryBinding` conditions); delete-after-cutover (`RunnerRole::OtelHostBridge`, `ProcessRole::OtelHostBridge`).", "entryContracts" : [ "ADR-046-provider-observability-otel" ], @@ -17088,6 +17607,7 @@ ], "specId" : "ADR-046-provider-observability-otel", "topologicalRank" : 11, + "validation" : "`integration/scenario_obs_zone_forwarding.rs`; adapted `minijail_relay_otel.rs` shape test for Provider-managed runner; assert `RunnerRole::OtelHostBridge` is absent from `d2b-contracts` after removal", "wave" : "W6" }, { @@ -17095,6 +17615,7 @@ "destinations" : [ "`packages/d2b-provider-observability-otel/src/{collector_bin,emitter_socket,exporter,controller,service,binding}.rs`; updated Nix observability modules" ], + "detailedDesign" : "Register the initial implementation of both provider-neutral qualified ResourceTypes by binding the exact ADR046-provider-004 base versions/fingerprints, then define only strict observability-otel Service/Binding spec and status extensions. Reconcile each Binding into an edge collector, private Endpoints, runtime Volume, and optional forwarder. Collector links the full OTEL SDK, resolves `serviceRef`, stamps trusted producer identity, and enforces common signals/quota/policy plus strict batching extension. Write generic Service/Binding observations to `status.resource` and SigNoz/OTLP/OTEL observations only to `status.provider`; no state file or Provider state Volume. Provider root config remains installation-only. Primary reuse disposition: `adapt`. Preserved source-plan detail: adapt Nix pipeline shape (replace per-VM `vmName` with per-Zone name; replace socat runner with vsock-forwarder long-lived Process; adapt `ingressSources` per-Zone entry).", "entryContracts" : [ "ADR-046-provider-observability-otel" ], @@ -17112,6 +17633,7 @@ ], "specId" : "ADR-046-provider-observability-otel", "topologicalRank" : 12, + "validation" : "Common-fixture/fingerprint and canonical-minimal-base conformance (including a fake alternate telemetry Provider); `tests/emitter_socket_receive.rs`; `tests/exporter_outage.rs`; `tests/exporter_backpressure.rs`; `integration/scenario_full_pipeline.rs`; adapted `policy_observability.rs` (retain all existing assertions; add new `d2b.zone`, `d2b.provider` allowlist entries)", "wave" : "W6" }, { @@ -17119,6 +17641,7 @@ "destinations" : [ "`packages/d2b-provider-observability-otel/src/nix/journald.nix`; `packages/d2b-provider-observability-otel/src/journald.rs`" ], + "detailedDesign" : "Per `ADR-046-telemetry-audit-and-support` §journald stdout/stderr ingestion: cgroup filter derived from trusted `producerRef`; redaction drops credential/secret/path fields, `_CMDLINE`, `_EXE`, and `INVOCATION_ID`; the strict `TelemetryBinding.spec.provider.settings.journald.enable` defaults false. Primary reuse disposition: `adapt`. Preserved source-plan detail: adapt journald receiver config for per-Zone cgroup filter.", "entryContracts" : [ "ADR-046-provider-observability-otel" ], @@ -17135,6 +17658,7 @@ ], "specId" : "ADR-046-provider-observability-otel", "topologicalRank" : 13, + "validation" : "Nix eval test: filter expression set when enabled; assert `_CMDLINE` and `INVOCATION_ID` in drop list; `tests/redaction.rs` for journald field redaction", "wave" : "W6" }, { @@ -17142,6 +17666,7 @@ "destinations" : [ "`packages/d2b-contract-tests/tests/policy_observability.rs` (updated); `packages/d2b-contract-tests/tests/policy_telemetry_redaction.rs` (new, per ADR046-telem-008); `packages/d2b-provider-observability-otel/tests/no_vm_label_in_metrics.rs`" ], + "detailedDesign" : "(1) Extend `loki_native_otel_resource_attributes` allowlist with `d2b.zone`, `d2b.provider`, `d2b.component`, `service.version`. (2) Add gate: `no_isolation` must not appear in any Provider `MetricDescriptor` label or span attribute catalog. (3) Adapt `minijail_relay_otel.rs` shape test for Provider-managed runner (no broker `RunnerRole::OtelHostBridge`). (4) Add metric inventory gates for `d2b_otel_*` instruments from this spec. (5) Retain: `startup_tracing_avoids_host_path_fields`; SigNoz-only backend assertion; `tempo_guest_collector_shape`; `config_source = \"realm-controllers\"` absence gate. Primary reuse disposition: `adapt`. Preserved source-plan detail: adapt and extend existing tests; keep existing test assertions.", "entryContracts" : [ "ADR-046-provider-observability-otel" ], @@ -17156,6 +17681,7 @@ ], "specId" : "ADR-046-provider-observability-otel", "topologicalRank" : 13, + "validation" : "All contract-tests pass after update; existing allowlist test does not regress", "wave" : "W6" }, { @@ -17163,6 +17689,7 @@ "destinations" : [ "`packages/d2b-provider-observability-otel/src/share_adapter.rs`" ], + "detailedDesign" : "Implement the signed adapter: `sys-obs` exports only the authority `TelemetryService`; its local ingest Endpoint remains Service-owned implementation transport and is never an Export field. ResourceExport uses only canonical `serviceType`, `projectionSchemaFingerprint`, and `factoryFingerprint`; ResourceImport uses the corresponding `expectedServiceType`, `expectedProjectionSchemaFingerprint`, and `expectedFactoryFingerprint`. Every producer import causes Core to create one local `TelemetryService` projection with ResourceImport ownership, `providerRef`, semantic base/import fields, and no `spec.provider`. Routing derives from the signed local descriptor and ResourceImport record. The semantic factory fingerprint binds factory metadata plus projection-protocol version, never Provider/adapter identity, which the signed descriptor authenticates separately. Binding `serviceRef` targets that projection. Enforce many-to-one quota/credit/backpressure/schema/source-stamp/redaction/cardinality over bounded encrypted streams; no FD/socket crosses a Zone and audit authority stays local. Primary reuse disposition: `adapt`. Preserved source-plan detail: net-new (implement the signed observability export/import adapter).", "entryContracts" : [ "ADR-046-provider-observability-otel" ], @@ -17178,6 +17705,7 @@ ], "specId" : "ADR-046-provider-observability-otel", "topologicalRank" : 13, + "validation" : "Fast `projection_chain.rs` proves Service projection semantics with a fake stream, exact canonical Export/Import type and fingerprint fields, rejection of obsolete `endpointRef`/`exportedType`/`baseSchemaFingerprint`/`expectedType`/`expectedBaseSchemaFingerprint`/`projectionType`, rejection of projection `spec.provider`, and semantic-fingerprint stability under Provider/adapter identity mutation while signed descriptor authentication remains exact; integration alone runs real encrypted streams and SigNoz; revocation/reconnect, quotas, source stamp, redaction/cardinality, no FD crossing, and audit locality", "wave" : "W6" }, { @@ -17185,6 +17713,7 @@ "destinations" : [ "`packages/d2b-provider-observability-otel/src/{authority,service,binding,projection}.rs`; `AuthorityDescriptor` on the `sys-obs` `TelemetryService`" ], + "detailedDesign" : "Implement one provider-neutral D097 authority Service with generic telemetry-ingest Endpoint refs and common service/signals/quota/policy fields. Its strict observability-otel `spec.provider` alone selects the loopback SigNoz stack, backend Endpoints, and OTLP. Core rejects duplicates and adopts by owner proof. Implement core-owned imported Service projections with no `spec.provider` or backend ownership. Implement per-producer Bindings with common service/producer/signals/quota/policy fields and strict implementation extension; Bindings own/cause edge collector/forwarder/private Endpoints/runtime Volume. Keep generic observations in `status.resource` and SigNoz/OTLP/OTEL observations in `status.provider`. Preserve trusted source upsert, retry/queue, bounded cardinality/redaction, audit non-transfer, status-first state, and no OTEL SDK in core. Endpoint is transport only. Primary reuse disposition: `adapt`. Preserved source-plan detail: net-new universal provider-neutral `telemetry.d2bus.org.TelemetryService`/`telemetry.d2bus.org.TelemetryBinding` pair; adapt existing authority/edge behavior as the initial observability-otel implementation.", "entryContracts" : [ "ADR-046-provider-observability-otel" ], @@ -17201,6 +17730,7 @@ ], "specId" : "ADR-046-provider-observability-otel", "topologicalRank" : 14, + "validation" : "Fast `resource_service_binding.rs` and `projection_chain.rs` plus reused nix-unit/policy tests prove provider-neutral names, base/Provider field separation, projection `spec.provider` rejection, exact D088 placement of semantic observations under `status.resource` and implementation observations under `status.provider`, ownership, schemas, stamping, quotas, redaction, and projection chain. Real SigNoz and real stream scenarios are integration-only.", "wave" : "W6" }, { @@ -17208,6 +17738,7 @@ "destinations" : [ "`packages/d2b-contract-tests/tests/policy_provider_crate_layout.rs` (new file; gated under D2B_FIXTURES in existing `tests/tools/rust-workspace-checks.sh`)" ], + "detailedDesign" : "Implement `policy_provider_crate_layout.rs` with the following test functions: (1) `every_provider_crate_has_src` — walk `packages/d2b-provider-*/` directories in the workspace, assert each contains `src/`; failure names crate and missing path; (2) `every_provider_crate_has_tests` — assert `tests/` present; (3) `every_provider_crate_has_integration` — assert `integration/` present; (4) `every_provider_crate_has_readme` — assert `README.md` present; (5) `every_provider_readme_has_required_sections` — read `README.md`, check for all nine section headings from §4.8.3 (case-insensitive, after stripping `#` and whitespace); failure names the missing heading(s); (6) `every_integration_file_has_target_declaration` — for each `integration/*.rs` file, scan first 20 lines for exactly one `//! integration-target: (container|host-integration)` declaration; failure names the file and the violation (missing/multiple/invalid value); (7) `non_provider_crates_exempt` — verify the check does not run on non-`d2b-provider-*` crates. All checks are filesystem-only (no compilation). Workspace member list is discovered by parsing `packages/Cargo.toml` `[workspace].members`. Gate: add the new test file to `tests/tools/rust-workspace-checks.sh` D2B_FIXTURES list alongside existing policy tests", "entryContracts" : [ "ADR-046-resources-zone-control" ], @@ -17222,6 +17753,7 @@ ], "specId" : "ADR-046-resources-zone-control", "topologicalRank" : 13, + "validation" : "§15.3 layout conformance tests: `provider-crate-layout-src-required`, `provider-crate-layout-tests-required`, `provider-crate-layout-integration-required`, `provider-crate-layout-readme-required`, `provider-readme-sections-all-present`, `provider-readme-sections-partial-missing`, `provider-integration-target-declared`, `provider-integration-target-unique`, `provider-integration-target-valid-values`, `provider-crate-naming-convention`, `provider-crate-layout-non-provider-exempt`", "wave" : "W5" }, { @@ -17229,6 +17761,7 @@ "destinations" : [ "`packages/d2b-contracts/src/v3/host.rs`, `guest.rs`, `execution_policy.rs`, `process.rs`, `volume.rs`, `user.rs`, `network.rs`, `device.rs`, `credential.rs`" ], + "detailedDesign" : "Complete minimal ResourceType schemas and shared execution/Volume sub-schemas Primary reuse disposition: `adapt`. Preserved source-plan detail: extract and adapt.", "entryContracts" : [ "ADR-046-primitive-resource-composition" ], @@ -17242,6 +17775,7 @@ ], "specId" : "ADR-046-primitive-resource-composition", "topologicalRank" : 7, + "validation" : "Schema vectors and folded-field/no-duplicate-type policy tests", "wave" : "W2" }, { @@ -17249,6 +17783,7 @@ "destinations" : [ "`packages/d2b-provider-system-systemd/`, `packages/d2b-provider-system-minijail/`, shared neutral process conformance library" ], + "detailedDesign" : "Common Process/EphemeralProcess, provider-specific launch/pidfd/wait/adoption/status Primary reuse disposition: `adapt`. Preserved source-plan detail: extract and adapt.", "entryContracts" : [ "ADR-046-primitive-resource-composition" ], @@ -17262,6 +17797,7 @@ ], "specId" : "ADR-046-primitive-resource-composition", "topologicalRank" : 7, + "validation" : "Shared conformance plus Host/Guest/user integration", "wave" : "W2" }, { @@ -17269,6 +17805,7 @@ "destinations" : [ "`packages/d2b-provider-volume-*/`; `nixos-modules/resources-volume.nix`" ], + "detailedDesign" : "Fine-grained Volume layout/views, host-path policy, virtiofs attachments/status/owned Process Primary reuse disposition: `adapt`. Preserved source-plan detail: extract and adapt.", "entryContracts" : [ "ADR-046-primitive-resource-composition" ], @@ -17282,6 +17819,7 @@ ], "specId" : "ADR-046-primitive-resource-composition", "topologicalRank" : 7, + "validation" : "ACL/no-follow/marker, sharing/views, virtiofs host/guest mount tests", "wave" : "W2" }, { @@ -17289,6 +17827,7 @@ "destinations" : [ "`packages/d2b-process/src/`, `packages/d2b-provider-supervisor/src/`" ], + "detailedDesign" : "Common spec/status/tickets/pidfd/adoption/naming/cgroup/fast path Primary reuse disposition: `adapt`. Preserved source-plan detail: extract and adapt.", "entryContracts" : [ "ADR-046-components-processes-and-sandbox" ], @@ -17302,6 +17841,7 @@ ], "specId" : "ADR-046-components-processes-and-sandbox", "topologicalRank" : 9, + "validation" : "Shared conformance/fault/latency tests", "wave" : "W4" }, { @@ -17309,6 +17849,7 @@ "destinations" : [ "`packages/d2b-provider-system-systemd/`, `packages/d2b-provider-system-minijail/`" ], + "detailedDesign" : "Two Process/EphemeralProcess implementations, pidfd/wait ownership, system/user domains Primary reuse disposition: `adapt`. Preserved source-plan detail: extract/adapt.", "entryContracts" : [ "ADR-046-components-processes-and-sandbox" ], @@ -17323,6 +17864,7 @@ ], "specId" : "ADR-046-components-processes-and-sandbox", "topologicalRank" : 10, + "validation" : "Identical schema/status conformance plus provider-specific adoption", "wave" : "W4" }, { @@ -17330,6 +17872,7 @@ "destinations" : [ "`packages/d2b-contracts/src/v3/provider.rs`, `packages/d2b-provider/src/lib.rs`, `packages/d2b-provider-toolkit/`" ], + "detailedDesign" : "Provider resource/manifest/components/dependencies/services/trust/compatibility/toolkit Primary reuse disposition: `adapt`. Preserved source-plan detail: extract and adapt.", "entryContracts" : [ "ADR-046-provider-model-and-packaging" ], @@ -17343,6 +17886,7 @@ ], "specId" : "ADR-046-provider-model-and-packaging", "topologicalRank" : 8, + "validation" : "Contract vectors, fake/malicious Provider, one-crate/one-identity policy", "wave" : "W3" }, { @@ -17350,6 +17894,7 @@ "destinations" : [ "one `packages/d2b-provider--/` per Provider with mandatory src/, tests/, integration/, README.md; generic Nix Provider package/catalog emitter" ], + "detailedDesign" : "Split current combined/composition crates; exact outputs/manifests/conformance/layout/documentation", "entryContracts" : [ "ADR-046-provider-model-and-packaging" ], @@ -17363,6 +17908,7 @@ ], "specId" : "ADR-046-provider-model-and-packaging", "topologicalRank" : 8, + "validation" : "Workspace naming/dependency/output/dossier/catalog parity policy", "wave" : "W3" }, { @@ -17370,6 +17916,7 @@ "destinations" : [ "`packages/d2b-provider-system-core/`, `d2b-provider-system-systemd/`, `d2b-provider-system-minijail/`" ], + "detailedDesign" : "Bootstrap system-core; common Process/EphemeralProcess providers and pidfd conformance Primary reuse disposition: `adapt`. Preserved source-plan detail: extract and adapt.", "entryContracts" : [ "ADR-046-provider-model-and-packaging" ], @@ -17383,6 +17930,7 @@ ], "specId" : "ADR-046-provider-model-and-packaging", "topologicalRank" : 8, + "validation" : "Shared conformance and host/user/non-Host tests", "wave" : "W3" }, { @@ -17390,6 +17938,7 @@ "destinations" : [ "`packages/d2b-contracts/src/v3/semantic_services/{mod,audio,security_key,telemetry,usb}.rs`; generated schema artifacts for the eight exact qualified ResourceTypes" ], + "detailedDesign" : "Define one shared strict base spec/status DTO and schema contract for each frozen D098 Service/Binding pair, including exact semantic type/schema IDs, versions, fingerprints, minimal valid base fixtures without `spec.provider`, authority/projection Service union, same-Zone Binding `serviceRef`/target rules, D088 `status.resource` layering, status-only observations, and projection-factory type binding. A Core-generated projection permits only `providerRef`, semantic base/import fields, and ResourceImport ownership; it rejects `spec.provider`. Register no implementation-qualified or former `*State` alias.", "entryContracts" : [ "ADR-046-provider-model-and-packaging" ], @@ -17404,6 +17953,7 @@ ], "specId" : "ADR-046-provider-model-and-packaging", "topologicalRank" : 9, + "validation" : "Shared contract tests cover exact names, strict serde/schema round trips, common base discoverability without any Provider package, canonical minimal base acceptance without `spec.provider`, same-Zone refs/targets, owner/projection discrimination, Core projection rejection of `spec.provider`, common fields only under `status.resource`, implementation observation only under `status.provider`, status-only observations, no Device/Endpoint/Binding projection, implementation-detail rejection, semantic factory-fingerprint stability under Provider/adapter identity changes, and rejection of every implementation-qualified/former `*State` alias. Each initial and fake alternate Provider must pass the identical base conformance fixture.", "wave" : "W3" }, { @@ -17411,6 +17961,7 @@ "destinations" : [ "`packages/d2b-provider/src/agent.rs` (v3 provider agent dispatch); `packages/d2b-contracts/src/v3/provider_registry.rs` (v3 provider registry wire contract)" ], + "detailedDesign" : "**Selected**: `ProviderAgentProcess::serve` dispatch loop with semaphore in-flight limit; unsupported-service/method → ttrpc error; negative-timeout rejection; `SessionClosed` clean termination; `ProtocolViolation` audit-and-terminate path; bounded audit ring; `GeneratedProviderServiceServer` single-service-per-agent requirement; `ProviderRegistryV2` entry validation: provider-id derivation, schema fingerprint, scope digest, generation exactness; `MAX_PROVIDER_MAPPING_IDS=64` → Provider component mapping bound; `ProviderBindingV2` non-exhaustive + explicit `UnsupportedProviderBindingV2` fallback (never panics on unknown axis); `ProviderIntentId` `max 128 bytes` label rules → Provider component `spec.intentRef`; all 10 `provider_registry_v2.rs` tests ported. **Excluded ADR45 assumptions**: `aca_workload.rs` (`AcaGatewayWorkload`): ADR45 ACA external provider adapter, excluded entirely. `waypipe_display.rs` (`WaypipeDisplayProvider`): ADR45 display provider, excluded. `ProviderRegistryV2.registry_generation` / `configuration_fingerprint` bind to ADR45 bundle generation; v3 Provider resource version is tracked in redb store, not a JSON bundle. `run()` binary entrypoint uses the fixed `d2b-provider-agent` command; v3 provider processes use normal Zone runtime Process launch. `TrustedFirstPartyInProcess` is the only placement in v2; v3 Provider resources extend to `HostLocal`/`GatewayVm` etc. Primary reuse disposition: `adapt`. Preserved source-plan detail: copy + adapt.", "entryContracts" : [ "ADR-046-resources-zone-control" ], @@ -17427,6 +17978,7 @@ ], "specId" : "ADR-046-resources-zone-control", "topologicalRank" : 13, + "validation" : "`provider-agent-dispatch-unsupported-service-returns-ttrpc-error`, `provider-agent-negative-timeout-rejected`, `provider-agent-session-closed-terminates-serve-loop`, `provider-agent-audit-ring-capacity-bounded` (ported from `audit_capacity_is_bounded`), `provider-registry-entry-fingerprint-generation-exact` (ported from `rejects_generation_and_exact_identity_mismatches`), `provider-registry-unknown-axis-fallback-non-exhaustive` (ported from `unknown_binding_axis_remains_rejected_on_the_wire`), `provider-registry-duplicate-ids-rejected` (ported from `rejects_duplicate_or_unbounded_mapping_ids`)", "wave" : "W5" }, { @@ -17434,6 +17986,7 @@ "destinations" : [ "`packages/d2b-contracts/src/v3/volume_state.rs`" ], + "detailedDesign" : "`VolumeStateSchema` struct (`schemaId`, `schemaVersion`, `schemaDigest`, `migrationPolicy`); `PersistenceClass` and `SensitivityClass` enums; `VolumeStateStatus` extension (`stateSchemaPhase`, `installedSchemaVersion`, `markerStatus`, `sealingStatus`, `quotaUsage`, `lastMigrationAt`); `StateEnvelope` (replaces v2 `StateEnvelope`); canonical JSON serde and digest helpers Primary reuse disposition: `adapt`. Preserved source-plan detail: extract and adapt.", "entryContracts" : [ "ADR-046-provider-state" ], @@ -17448,6 +18001,7 @@ ], "specId" : "ADR-046-provider-state", "topologicalRank" : 9, + "validation" : "Schema golden vectors; phase/reason round-trip; StateEnvelope digest tests", "wave" : "W4" }, { @@ -17455,6 +18009,7 @@ "destinations" : [ "`packages/d2b-contracts/src/v3/provider.rs` (component descriptor `stateNamespaces` field)" ], + "detailedDesign" : "Add `stateNamespaces: Vec` to the component descriptor (zero or more entries; a component declares an entry only when a payload passes the storage-need test; stateless components declare none); each entry includes `id`, `kind` (always `state`), `schemaId` (non-null), `schemaVersion`, `schemaDigest`, `persistenceClass` (must be `persistent`; `ephemeral`/`cache` rejected), `sensitivityClass`, `migrationPolicy`, `quotaBytes` (nonzero; minimum 4096), `storageNeed` (`secret` \\| `large-binary` \\| `private-unsafe-for-status` \\| `revision-unsuitable`), `sealingRequired`, `placementMode` (`guest-local` or `host-backed-guest` for Guest-targeted; omitted for Host-targeted), `hostCustodyPermitted` (required `true` for `host-backed-guest`; absent/false for `guest-local`), and `views`; there is no empty-payload (`schemaId: null`) namespace", "entryContracts" : [ "ADR-046-provider-state" ], @@ -17469,6 +18024,7 @@ ], "specId" : "ADR-046-provider-state", "topologicalRank" : 10, + "validation" : "Descriptor schema golden vectors; descriptor-Volume consistency property test; stateless-component-declares-no-namespace round-trip; storage-need justification enforcement (namespace whose payload is derivable from spec/status/core ledger/external observation → `component-state-not-justified`); `kind != state` → `component-kind-invalid`; `persistenceClass: ephemeral` → `component-persistence-class-forbidden`; `quotaBytes: 0` or `1024` → `component-quota-too-small`; base `quota.maxBytes == quotaBytes` and `quota.maxInodes > 0`; Guest-targeted with `placementMode: guest-local` → source.executionRef=Guest; Guest-targeted with `host-backed-guest` + `hostCustodyPermitted: true` → source on Host, Export created; `host-backed-guest` without `hostCustodyPermitted: true` → `placement-host-custody-violation`; credential/audit schema with `host-backed-guest` → `guest-local-required`; `placementMode` change → descriptor version increment enforced", "wave" : "W4" }, { @@ -17476,6 +18032,7 @@ "destinations" : [ "`packages/d2b-provider-volume-local/` (new crate, full scaffold required): `src/{atomic.rs, path.rs, lock.rs, marker.rs, effect_port.rs}`; `tests/volume_local.rs` (marker missing/replaced/mismatch, domain-isolation rejection, quota enforcement); `integration/volume_local.rs` (real Host filesystem provision, broker-maintained marker check, domain-isolation rejection cross-process); `README.md`" ], + "detailedDesign" : "Anchored Volume root provision, identity marker write/check, quota soft-check on write, domain-isolation validation, fd-relative layout creation/repair/cleanup, broker-maintained marker root protocol; layout `ownerRef`/`groupRef` must reference a Nix-preprovisioned User principal or bounded system pool — Volume admission rejects runtime-created principals; `VolumeEffectPort` returns opaque IDs and named view dirfds only — no raw host path returned by any EffectPort operation; volume-local must support `source.executionRef: Guest/` for `guest-local` placement (controller running inside the Guest): when executing in a Guest domain, volume-local may not create, read, or hold dirfds/paths for Volumes sourced in another domain; `host-backed-guest` placement creates a `virtiofs.d2bus.org.Export` child per attachment entry and validates `hostCustodyPermitted: true` in the signed descriptor Primary reuse disposition: `adapt`. Preserved source-plan detail: copy-unchanged (path.rs) / adapt (atomic.rs, lock.rs) / adapt (swtpm_dir.rs marker algorithm).", "entryContracts" : [ "ADR-046-provider-state" ], @@ -17490,6 +18047,7 @@ ], "specId" : "ADR-046-provider-state", "topologicalRank" : 10, + "validation" : "Marker missing/replaced/mismatch tests; domain-isolation rejection tests; quota enforcement tests; crash at every provision step", "wave" : "W4" }, { @@ -17497,6 +18055,7 @@ "destinations" : [ "`packages/d2b-provider-volume-local/src/migration.rs`; `packages/d2b-provider-volume-local/tests/migration_unit.rs` (hermetic staging Volume and prepare/commit/rollback); `packages/d2b-provider-volume-local/integration/migration.rs` (real Host crash-injection at each migration step, N-Volume cross-component coordination); `packages/d2b-controller-toolkit/src/state_migration.rs`" ], + "detailedDesign" : "Pre-launch migration EphemeralProcess template, staging Volume create/destroy lifecycle, prepare/commit/rollback protocol implementation, roll-forward on restart detection, migration idempotency", "entryContracts" : [ "ADR-046-provider-state" ], @@ -17512,6 +18071,7 @@ ], "specId" : "ADR-046-provider-state", "topologicalRank" : 11, + "validation" : "Migration with and without crash at each step; rollback after failed EphemeralProcess; roll-forward after interrupted commit; cross-component N-Volume coordination", "wave" : "W4" }, { @@ -17519,6 +18079,7 @@ "destinations" : [ "`packages/d2b-provider-volume-local/src/sealing.rs`; `packages/d2b-provider-volume-local/tests/sealing_unit.rs` (hermetic seal/rotation state machine); `packages/d2b-provider-volume-local/integration/sealing.rs` (real Credential lease flow, interrupted key rotation with live Credential Provider)" ], + "detailedDesign" : "Envelope encryption on write using Credential lease key material; key rotation EphemeralProcess re-encrypt; no raw key on disk; `sealingStatus` transitions; `rotation-failed` fail-safe", "entryContracts" : [ "ADR-046-provider-state" ], @@ -17533,6 +18094,7 @@ ], "specId" : "ADR-046-provider-state", "topologicalRank" : 11, + "validation" : "Seal/read/rotation tests; rotation interrupted at commit; credential revoked during runtime", "wave" : "W4" }, { @@ -17540,6 +18102,7 @@ "destinations" : [ "`packages/d2b-provider-volume-local/src/snapshot.rs`; `packages/d2b-provider-volume-local/tests/snapshot_unit.rs` (hermetic `snapshotPolicy` enforcement, retention count and TTL logic); `packages/d2b-provider-volume-local/integration/snapshot.rs` (real Host filesystem snapshot creation, retention expiry, pre-migration auto-snapshot with interrupted migration)" ], + "detailedDesign" : "Snapshot EphemeralProcess; bounded `.snapshots/` sub-tree; `snapshotPolicy` enforcement; retention count and TTL cleanup; snapshot status tracking in Volume status", "entryContracts" : [ "ADR-046-provider-state" ], @@ -17555,6 +18118,7 @@ ], "specId" : "ADR-046-provider-state", "topologicalRank" : 12, + "validation" : "Create/read/expire/retention-limit tests; snapshot before interrupted migration; snapshot list in Volume status", "wave" : "W4" }, { @@ -17562,6 +18126,7 @@ "destinations" : [ "`packages/d2b-provider-volume-local/src/relocation.rs`; `packages/d2b-provider-volume-local/tests/relocation_unit.rs` (hermetic finalizer set/clear, commit/failure state machine); `packages/d2b-provider-volume-local/integration/relocation.rs` (real Host-to-Host anchored file copy, crash at copy midpoint, virtiofs source re-point after successful relocation)" ], + "detailedDesign" : "Relocation EphemeralProcess; source finalizer; anchored copy; source Volume (volume-local) relocation for components with Guest attachment (the attachment Volume backed by volume-virtiofs is re-pointed to the new source after copy; see `ADR-046-primitive-resource-composition` Volume attachment spec); commit/failure handling", "entryContracts" : [ "ADR-046-provider-state" ], @@ -17576,6 +18141,7 @@ ], "specId" : "ADR-046-provider-state", "topologicalRank" : 11, + "validation" : "Relocation with crash at copy midpoint; failed relocation source preservation; virtiofs source relocation test", "wave" : "W4" }, { @@ -17583,6 +18149,7 @@ "destinations" : [ "`packages/d2b-provider-volume-local/src/audit.rs`; `packages/d2b-provider-volume-local/src/otel.rs`; `packages/d2b-provider-volume-local/tests/audit_unit.rs` (hermetic audit golden records, OTEL cardinality label tests)" ], + "detailedDesign" : "Volume-state audit event types and Zone audit emission; OTEL metric definitions with closed semantic label sets and no Zone or resource-name-derived label keys; Zone identity remains in the `d2b.zone` OTEL resource attribute", "entryContracts" : [ "ADR-046-provider-state" ], @@ -17597,6 +18164,7 @@ ], "specId" : "ADR-046-provider-state", "topologicalRank" : 10, + "validation" : "Audit event golden records; no content/path/credential in audit payload; structural OTEL label-policy tests assert exact absence of `vm`, `zone`, `zone_id`, `zone_uid`, and resource-name-derived keys and preserve `d2b.zone` as a resource attribute", "wave" : "W4" }, { @@ -17604,6 +18172,7 @@ "destinations" : [ "`packages/d2b-provider-volume-local/tests/state.rs` (ported hermetic atomic/lock/quarantine/lease tests); `packages/d2b-provider-volume-local/tests/migration.rs` (ported migration fault-injection, cross-component N-Volume coordination); `packages/d2b-provider-volume-local/integration/provider_state.rs` (end-to-end: live daemon, real Host Volume mount, cross-process worker subview, full audit stream); `packages/d2b-provider-volume-local/integration/audit.rs` (live Zone audit stream emission and OTEL export)" ], + "detailedDesign" : "Port all d2b-state integration tests replacing ADR 0045 contract setup with v3 Volume/StateEnvelope; add provider-state-specific migration, marker, quota, sealing, relocation, snapshot, incident-hold, and unclaimed-GC tests; include cross-component N-Volume coordination test", "entryContracts" : [ "ADR-046-provider-state" ], @@ -17625,6 +18194,7 @@ ], "specId" : "ADR-046-provider-state", "topologicalRank" : 13, + "validation" : "All ported tests pass under v3 contracts; test coverage includes every fault-injection scenario listed in d2b-state/tests/state.rs plus new provider-state cases; stateless-component-declares-no-Volume test passes; shared-Volume attempt rejected; `guest-local` Volume creation inside Guest domain (source.executionRef=Guest, no Export created, Host volume-local holds no dirfd/path); `host-backed-guest` Volume creation (source on Host, Export created, Export reaches Ready, Guest Process mounts source Volume view); `host-backed-guest` without `hostCustodyPermitted: true` → `placement-host-custody-violation`; credential/audit schema with `host-backed-guest` → `guest-local-required`; cross-domain isolation: Guest-local volume-local does not create or observe Host-domain Volumes", "wave" : "W4" }, { @@ -17632,6 +18202,7 @@ "destinations" : [ "`nixos-modules/zone-resources.nix` (per-Zone bundle emitter NixOS module); `packages/d2b-core/src/v3/zone_bundle.rs` (shared bundle DTOs: `ZoneResourceBundle`, `BundleResource`, `contentId` computation); `packages/d2b-core-controller/src/configuration.rs` (diff/apply loop, name-conflict detection, `pending-cleanup` Zone status, `maxFinalizerDurationSeconds` stall detection — NOT in `d2b-provider-volume-local`); `packages/d2b-core-controller/tests/configuration.rs` (hermetic bundle diff, absent-resource Delete dispatch, name-conflict `Degraded/name-conflict` item, integrity-failure abort); `packages/d2b-core-controller/integration/configuration.rs` (container-based generation activation with running Providers, absent-resource cleanup, rollback, finalizer-timeout stall detection)" ], + "detailedDesign" : "Generic `d2b.zones..resources. = { type = \"…\"; spec = { …exact ResourceTypeSpec fields… }; }` attrset; `metadata.name` derived from attr key, `metadata.zone` from Zone key, `apiVersion` defaulted; `status` omitted (read-only); Nix option types for `spec.*` generated from committed `ResourceTypeSchema` for each `type`; Nix option types for `spec.config.*` generated from the Provider artifact's config schema module (resolved at eval time via `spec.artifactId` from `d2b.artifacts`); credential-ref guard (`credentialRef: true` schema fields accept only `Credential/[a-z][a-z0-9-]*`); build-phase full JSON Schema validation of rendered `spec` against Provider manifest; canonical sorted bundle emission with `contentId` (SHA-256 of sorted `resources` array); configuration service sets `metadata.managedBy: configuration` and `configurationGeneration` in the resource store when persisting activated bundle resources (not in the bundle input; user authors only `type` + `spec`); diff compares new configured set against persisted resource store entries where `managedBy: configuration` (not against the prior bundle file); name-conflict detection: `(type, name)` collision with existing `managedBy: controller` or `managedBy: api` resource → `Degraded/name-conflict` activation item, existing resource untouched, `managedBy` never seized; unchanged-spec resources receive updated `configurationGeneration` with no controller reconcile triggered; absent-resource async Delete with owner-child/finalizer ordering; `Degraded/pending-cleanup` Zone status condition; per-Zone prior bundle count retention (range 1..16, default 3; no TTL); `maxFinalizerDurationSeconds` stall detection and `Degraded/finalizer-timeout` condition (no force-clear)", "entryContracts" : [ "ADR-046-provider-state" ], @@ -17646,6 +18217,7 @@ ], "specId" : "ADR-046-provider-state", "topologicalRank" : 10, + "validation" : "All eight removed-resource cleanup tests enumerated in [Required tests for removed-resource cleanup](#required-tests-for-removed-resource-cleanup); eval credential-ref guard (test: raw value in `credentialRef` field → NixOS eval error, no bundle emitted); Provider schema conformance golden vector (test: unknown `config` key → build fails); `contentId` determinism (test: two independent builds of identical Nix inputs produce byte-identical bundles)", "wave" : "W4" }, { @@ -17653,6 +18225,7 @@ "destinations" : [ "`packages/xtask/src/provider_crate_policy.rs`; `tests/unit/gates/provider-crate-layout-check.sh`" ], + "detailedDesign" : "`cargo xtask check-provider-crate-layout` command: walks every workspace member matching `d2b-provider-*`; for each, asserts presence of `src/`, `tests/`, `integration/`, and `README.md`; asserts `integration/` contains at least one `.rs` file and a `README.md`; fails closed with a typed `missing-provider-crate-path` error listing every absent path; wired into `make test-policy` via `tests/unit/gates/provider-crate-layout-check.sh`; output is machine-readable JSON (`{ \"crate\": \"…\", \"missing\": [\"integration/\"] }` per violation) Primary reuse disposition: `adapt`. Preserved source-plan detail: extend.", "entryContracts" : [ "ADR-046-provider-state" ], @@ -17667,6 +18240,7 @@ ], "specId" : "ADR-046-provider-state", "topologicalRank" : 11, + "validation" : "Policy gate detects missing `src/` → error; missing `tests/` → error; missing `integration/` → error; missing `README.md` → error; empty `integration/` (no `.rs` files) → error; all four paths present and non-empty → pass; existing non-provider `d2b-*` crates not flagged; gate is idempotent across re-runs", "wave" : "W4" }, { @@ -17674,6 +18248,7 @@ "destinations" : [ "`packages/d2b-core-controller/src/optional_state_admission.rs` (storage-need admission: reject a declared namespace whose payload is derivable from spec/status/core ledger/external observation with `component-state-not-justified`; only declared namespaces produce a Volume; stateless components produce none); `packages/d2b-core-controller/tests/optional_state_admission.rs` (hermetic: stateless component → no Volume; declared `storageNeed` variants accepted; unjustified namespace rejected; status-first restart revalidation — controller re-derives observed state from status/core ledger/external observation after restart and never treats a status field as authority); `packages/d2b-provider-volume-local/tests/status_bounds.rs` (hermetic: total canonical serialized status cap and provider-specific detail cap enforced; oversize status write → typed rejection; status carries no secret/path/argv/PID/unit/stream/ring content)" ], + "detailedDesign" : "Optional state-Volume admission is a fixed step in Core ProviderDeployment: for each component, if it declares no `stateNamespaces` entry it gets no Volume; for each declared entry, verify the `storageNeed` justification (`secret` \\| `large-binary` \\| `private-unsafe-for-status` \\| `revision-unsuitable`) and reject a namespace whose payload is fully derivable from spec/status/core ledger/external observation with `component-state-not-justified`. Fixed bootstrap components (`system-core`, `system-minijail`, first `volume-local` instance) declare no state Volume and reach Ready using resource `status`, the core Operation ledger, and external observation only; there is no bootstrap-storage mechanism. Status-bound enforcement: reject a status write whose total canonical serialized size exceeds the single canonical status cap, or whose provider-specific detail exceeds the detail cap, or whose condition/count/list/map entries exceed the bounded limits, with the typed status-oversize rejection; status writes occur only on material change.", "entryContracts" : [ "ADR-046-provider-state" ], @@ -17689,6 +18264,7 @@ ], "specId" : "ADR-046-provider-state", "topologicalRank" : 11, + "validation" : "Stateless component → no Volume created; each `storageNeed` variant accepted with a declared Volume; unjustified namespace → `component-state-not-justified`; status-first restart: controller re-derives observed state and reverifies against external reality, never treating status as authority; oversize/over-detail/over-cardinality status write → typed rejection; status contains no secret/path/argv/PID/unit/stream/ring content", "wave" : "W4" }, { @@ -17696,6 +18272,7 @@ "destinations" : [ "packages/d2b-provider-runtime-qemu-media/{src/lib.rs,tests/provider_layout.rs,integration/mod.rs,README.md}" ], + "detailedDesign" : "Crate scaffold and layout gate: create the crate with the four required paths, commit a README.md stub meeting §1 requirements, and wire the workspace policy gate so the crate cannot land without `src/`, `tests/`, `integration/`, and `README.md`.", "entryContracts" : [ "ADR-046-provider-runtime-qemu-media" ], @@ -17709,6 +18286,7 @@ ], "specId" : "ADR-046-provider-runtime-qemu-media", "topologicalRank" : 11, + "validation" : "`make test-policy` (workspace policy gate)", "wave" : "W6" }, { @@ -17716,6 +18294,7 @@ "destinations" : [ "packages/d2b-provider-runtime-qemu-media/src/types/guest.rs" ], + "detailedDesign" : "Guest ResourceType schema and serde: define `GuestSpec`, `GuestStatus`, and `GuestProviderSpecSettings` with serde and `schemars` JSON Schema. Fields must match §4, §5, and §16 exactly. Enforce `bootMediaRef` as a `Volume/` ResourceRef, `removableVolumeRefs` max 4 entries, `providerPhase` max 64 chars with the closed value set, and no argv/path/credential bytes in any serialized type. Primary reuse disposition: `adapt`. Preserved source-plan detail: adapt selected baseline field concepts; discard raw paths, argv, and credential-carrying fields.", "entryContracts" : [ "ADR-046-provider-runtime-qemu-media" ], @@ -17730,6 +18309,7 @@ ], "specId" : "ADR-046-provider-runtime-qemu-media", "topologicalRank" : 12, + "validation" : "`tests/guest_schema_roundtrip.rs`; `tests/guest_provider_settings_bounds.rs`", "wave" : "W6" }, { @@ -17737,6 +18317,7 @@ "destinations" : [ "packages/d2b-provider-runtime-qemu-media/src/config.rs" ], + "detailedDesign" : "Provider config schema and projection: define `ProviderConfig`, derive JSON Schema, require `controllerExecutionRef`, validate bounds, and project config only to the controller component. Worker processes receive no root config, no ResourceAPI authority, and no d2b-bus authority. Primary reuse disposition: `adapt`. Preserved source-plan detail: adapt bounded timeout/quota concepts into v3 Provider config; project only to the controller component.", "entryContracts" : [ "ADR-046-provider-runtime-qemu-media" ], @@ -17751,6 +18332,7 @@ ], "specId" : "ADR-046-provider-runtime-qemu-media", "topologicalRank" : 12, + "validation" : "`tests/config_schema_projection.rs`", "wave" : "W6" }, { @@ -17758,6 +18340,7 @@ "destinations" : [ "packages/d2b-provider-runtime-qemu-media/src/{descriptor.rs,state.rs}; no Volume management code for Provider state" ], + "detailedDesign" : "Controller status-first operational state (no state Volume): controller component descriptor declares an empty `stateNamespaces` list; ProviderDeployment creates no controller state Volume; controller writes reconcile stage, per-Guest launch/adoption observations, bounded counters, and closed-enum error detail to `status` on material change without secrets, paths, argv, PIDs, or unit names; restart re-derives observed state from the Zone resource store, core Operation ledger, and independent external observation with fresh pidfds. Worker Processes and the controller receive no state-Volume mount.", "entryContracts" : [ "ADR-046-provider-runtime-qemu-media" ], @@ -17773,6 +18356,7 @@ ], "specId" : "ADR-046-provider-runtime-qemu-media", "topologicalRank" : 13, + "validation" : "`tests/state_status_spec.rs`; `tests/state_status_restart.rs`; `tests/state_mount_exclusivity.rs`", "wave" : "W6" }, { @@ -17780,6 +18364,7 @@ "destinations" : [ "packages/d2b-provider-runtime-qemu-media/src/controller/volume.rs" ], + "detailedDesign" : "Runtime tmpfs Volume resource: controller creates the per-Guest runtime tmpfs Volume specified in §6.1. The emitted spec must exactly match the canonical YAML, including all layout entries, views, quota, and `cleanupPolicy: vm-stop-with-proof`. Primary reuse disposition: `adapt`. Preserved source-plan detail: adapt naming intent into controller-created Volume resources; replace raw runtime directory paths with `Volume` specs.", "entryContracts" : [ "ADR-046-provider-runtime-qemu-media" ], @@ -17795,6 +18380,7 @@ ], "specId" : "ADR-046-provider-runtime-qemu-media", "topologicalRank" : 13, + "validation" : "`tests/runtime_volume_spec.rs`; `tests/volume_cleanup_policy.rs`", "wave" : "W6" }, { @@ -17802,6 +18388,7 @@ "destinations" : [ "packages/d2b-provider-runtime-qemu-media/src/controller/media_watch.rs" ], + "detailedDesign" : "Media Volume watch and virtio-blk attachment validation: controller watches `bootMediaRef` and `removableVolumeRefs` Volumes for `Ready` status and validates that each has a `virtio-blk` attachment for the owning Guest. It performs no path inspection. Primary reuse disposition: `adapt`. Preserved source-plan detail: adapt media kind concepts to Volume source-kind assertions and ResourceRef watches.", "entryContracts" : [ "ADR-046-provider-runtime-qemu-media" ], @@ -17817,6 +18404,7 @@ ], "specId" : "ADR-046-provider-runtime-qemu-media", "topologicalRank" : 14, + "validation" : "`tests/media_volume_watch.rs`; `tests/media_attachment_validation.rs`", "wave" : "W6" }, { @@ -17824,6 +18412,7 @@ "destinations" : [ "packages/d2b-provider-runtime-qemu-media/src/controller/device_watch.rs" ], + "detailedDesign" : "KVM Device watch: controller watches `Device/host-kvm` from `spec.deviceAttachments` for `Ready` status and gates runner launch on it, propagating Pending/Ready/Failed transitions to Guest conditions.", "entryContracts" : [ "ADR-046-provider-runtime-qemu-media" ], @@ -17838,6 +18427,7 @@ ], "specId" : "ADR-046-provider-runtime-qemu-media", "topologicalRank" : 13, + "validation" : "`tests/kvm_device_watch.rs`", "wave" : "W6" }, { @@ -17845,6 +18435,7 @@ "destinations" : [ "packages/d2b-provider-runtime-qemu-media/src/controller/display.rs" ], + "detailedDesign" : "WaylandSession resource management: when `spec.provider.settings.displayWindow = true`, controller creates, updates, deletes, and watches a `display-wayland.d2bus.org.WaylandSession` resource using the exact ResourceSpec from the display-wayland dossier. It reads the EndpointRef attachment from status using only display-wayland-defined field names. Primary reuse disposition: `create`. Preserved source-plan detail: net-new against the display-wayland Resource contract.", "entryContracts" : [ "ADR-046-provider-runtime-qemu-media" ], @@ -17859,6 +18450,7 @@ ], "specId" : "ADR-046-provider-runtime-qemu-media", "topologicalRank" : 13, + "validation" : "`tests/wayland_session_create.rs`; `tests/wayland_session_attachment_read.rs`; `tests/wayland_session_missing_provider.rs`", "wave" : "W6" }, { @@ -17866,6 +18458,7 @@ "destinations" : [ "packages/d2b-provider-runtime-qemu-media/src/controller/process_builder.rs" ], + "detailedDesign" : "Process spec builder and LaunchTicket assembly: build the canonical `qemu-media-runner` Process ResourceSpec from §10.1 and assemble the LaunchTicket with sealed fd slots for kvm, tap, media, and optional display fds. No raw path, argv, executable path, or principal appears in any public field. Primary reuse disposition: `adapt`. Preserved source-plan detail: adapt fd-slot and sandbox/budget concepts to canonical Process resources and sealed LaunchTickets; do not copy raw argv strings or path construction.", "entryContracts" : [ "ADR-046-provider-runtime-qemu-media" ], @@ -17885,6 +18478,7 @@ ], "specId" : "ADR-046-provider-runtime-qemu-media", "topologicalRank" : 15, + "validation" : "`tests/process_spec_golden.rs`; `tests/launch_ticket_fd_slots.rs`; `tests/no_raw_argv_in_spec.rs`", "wave" : "W6" }, { @@ -17892,6 +18486,7 @@ "destinations" : [ "packages/d2b-provider-runtime-qemu-media/src/qmp/" ], + "detailedDesign" : "QMP endpoint attachment handling: consume `qmp` and `serial` Endpoint connection attachments delivered by the ProviderSupervisor ComponentSession channel; implement QMP capability negotiation, command dispatch, and health check using only the delivered fd, never direct socket path access. Primary reuse disposition: `adapt`. Preserved source-plan detail: adapt QMP command payloads to internal DTOs; discard broker wire ops and all socket path/fd-open code.", "entryContracts" : [ "ADR-046-provider-runtime-qemu-media" ], @@ -17906,6 +18501,7 @@ ], "specId" : "ADR-046-provider-runtime-qemu-media", "topologicalRank" : 16, + "validation" : "`tests/qmp_capability_negotiation.rs`; `tests/qmp_command_dispatch.rs`; `tests/qmp_greeting_timeout.rs`; `tests/qmp_health_check.rs`", "wave" : "W6" }, { @@ -17913,6 +18509,7 @@ "destinations" : [ "packages/d2b-provider-runtime-qemu-media/src/controller/hotplug.rs" ], + "detailedDesign" : "Hotplug attach/detach protocol: on `removableVolumeRefs` update, request a Volume fd from the `volume-local` ComponentSession service and issue `blockdev-add`/`device_add` QMP commands; reverse the sequence for detach; QMP failures set Degraded with `hotplug-media-failed`. Primary reuse disposition: `adapt`. Preserved source-plan detail: adapt QMP hotplug command bodies; delete broker op wiring.", "entryContracts" : [ "ADR-046-provider-runtime-qemu-media" ], @@ -17928,6 +18525,7 @@ ], "specId" : "ADR-046-provider-runtime-qemu-media", "topologicalRank" : 17, + "validation" : "`tests/hotplug_attach_sequence.rs`; `tests/hotplug_detach_sequence.rs`; `tests/hotplug_qmp_failure.rs`", "wave" : "W6" }, { @@ -17935,6 +18533,7 @@ "destinations" : [ "packages/d2b-provider-runtime-qemu-media/src/controller/network.rs" ], + "detailedDesign" : "Network tap fd acquisition: call the `network-local` ComponentSession service to request a tap fd for a Guest MAC/bridge assignment and include the fd in the LaunchTicket. No bridge name or interface name appears in any public field. Primary reuse disposition: `create`. Preserved source-plan detail: net-new against the `network-local` ComponentSession contract.", "entryContracts" : [ "ADR-046-provider-runtime-qemu-media" ], @@ -17949,6 +18548,7 @@ ], "specId" : "ADR-046-provider-runtime-qemu-media", "topologicalRank" : 13, + "validation" : "`tests/tap_fd_acquisition.rs`; `tests/tap_fd_unavailable.rs`", "wave" : "W6" }, { @@ -17956,6 +18556,7 @@ "destinations" : [ "packages/d2b-provider-runtime-qemu-media/src/controller/reconcile.rs" ], + "detailedDesign" : "Reconcile loop and finalize: implement the full async reconcile loop from §11.3 and finalize sequence from §11.4, including dependency gating, providerPhase transitions, condition management, runner exit handling, and WaylandSession cleanup. Primary reuse disposition: `create`. Preserved source-plan detail: net-new reconcile/finalize implementation using the v3 Resource API.", "entryContracts" : [ "ADR-046-provider-runtime-qemu-media" ], @@ -17977,6 +18578,7 @@ ], "specId" : "ADR-046-provider-runtime-qemu-media", "topologicalRank" : 18, + "validation" : "`tests/reconcile_dependency_gating.rs`; `tests/reconcile_runner_exit_handling.rs`; `tests/finalize_sequence.rs`; `tests/finalize_wayland_session_cleanup.rs`", "wave" : "W6" }, { @@ -17984,6 +18586,7 @@ "destinations" : [ "packages/d2b-provider-runtime-qemu-media/src/controller/status.rs" ], + "detailedDesign" : "Status, conditions, and error reporting: implement all phase transitions from §16.1, providerPhase values from §16.2, condition types from §16.3, error codes from §16.4, and bounds enforcement on `providerPhase`. Primary reuse disposition: `create`. Preserved source-plan detail: net-new status/error projection for the v3 Guest ResourceType.", "entryContracts" : [ "ADR-046-provider-runtime-qemu-media" ], @@ -17998,6 +18601,7 @@ ], "specId" : "ADR-046-provider-runtime-qemu-media", "topologicalRank" : 19, + "validation" : "`tests/status_phase_transitions.rs`; `tests/condition_reason_codes.rs`", "wave" : "W6" }, { @@ -18005,6 +18609,7 @@ "destinations" : [ "packages/d2b-provider-runtime-qemu-media/src/audit.rs" ], + "detailedDesign" : "Audit event emission: emit all audit events in §17 and verify that no sensitive fields such as paths, argv, fds, or socket paths appear in any payload. Primary reuse disposition: `create`. Preserved source-plan detail: net-new audit emission for the Provider events in §17.", "entryContracts" : [ "ADR-046-provider-runtime-qemu-media" ], @@ -18020,6 +18625,7 @@ ], "specId" : "ADR-046-provider-runtime-qemu-media", "topologicalRank" : 20, + "validation" : "`tests/audit_event_shapes.rs`; `tests/audit_no_sensitive_fields.rs`", "wave" : "W6" }, { @@ -18027,6 +18633,7 @@ "destinations" : [ "packages/d2b-provider-runtime-qemu-media/src/telemetry.rs" ], + "detailedDesign" : "Metrics and OTEL spans: implement all metrics from §18 and OTEL trace spans with structural closed-label enforcement and no Zone/VM/resource name, user identity, path, or other sensitive value in any metric label; retain Zone/resource identity only in bounded OTEL resource attributes and permitted audit fields. Primary reuse disposition: `create`. Preserved source-plan detail: net-new telemetry emission for the Provider metrics and spans in §18.", "entryContracts" : [ "ADR-046-provider-runtime-qemu-media" ], @@ -18042,6 +18649,7 @@ ], "specId" : "ADR-046-provider-runtime-qemu-media", "topologicalRank" : 20, + "validation" : "`tests/metrics_label_cardinality.rs` asserts exact absence of `vm`, `zone`, `zone_id`, `zone_uid`, and resource-name-derived keys plus Guest/Zone-name canary absence; `tests/otel_span_attributes.rs` preserves allowed OTEL resource identity attributes and rejects identity span attributes", "wave" : "W6" }, { @@ -18049,6 +18657,7 @@ "destinations" : [ "nixos-modules/options-guest-qemu-media.nix; nixos-modules/assertions.nix" ], + "detailedDesign" : "Nix module and assertions: implement the Guest resource declaration from §19 and eval-time assertions from §19.8. Rewrite qemu-media options as v3 spec fields and reject raw path options. Primary reuse disposition: `adapt`. Preserved source-plan detail: adapt option names into v3 Guest/Provider spec fields; remove raw path options; extend existing assertion predicates.", "entryContracts" : [ "ADR-046-provider-runtime-qemu-media" ], @@ -18065,6 +18674,7 @@ ], "specId" : "ADR-046-provider-runtime-qemu-media", "topologicalRank" : 14, + "validation" : "`tests/unit/nix/cases/guest-qemu-media-spec.nix`; `tests/assertions-eval.sh` new assertion cases", "wave" : "W6" }, { @@ -18072,6 +18682,7 @@ "destinations" : [ "packages/d2b-provider-runtime-qemu-media/tests/conformance_guest.rs" ], + "detailedDesign" : "d2b-provider-toolkit conformance: pass the Provider conformance kit for the Guest ResourceType axis, including reconcile/finalize contract, phase machine, condition typing, audit shape, and telemetry cardinality. Primary reuse disposition: `adapt`. Preserved source-plan detail: reuse conformance harness; add runtime-qemu-media Guest ResourceType coverage.", "entryContracts" : [ "ADR-046-provider-runtime-qemu-media" ], @@ -18089,6 +18700,7 @@ ], "specId" : "ADR-046-provider-runtime-qemu-media", "topologicalRank" : 21, + "validation" : "`make test-rust` (runs conformance suite)", "wave" : "W6" }, { @@ -18096,6 +18708,7 @@ "destinations" : [ "packages/d2b-provider-runtime-qemu-media/integration/" ], + "detailedDesign" : "Integration tests: implement container/fake-Host scenarios for full reconcile from Created to Ready with fake dependencies, finalize sequence, hotplug attach/detach, and restart recovery. Primary reuse disposition: `create`. Preserved source-plan detail: net-new integration fixtures.", "entryContracts" : [ "ADR-046-provider-runtime-qemu-media" ], @@ -18123,6 +18736,7 @@ ], "specId" : "ADR-046-provider-runtime-qemu-media", "topologicalRank" : 22, + "validation" : "`make test-integration`", "wave" : "W6" }, { @@ -18130,6 +18744,7 @@ "destinations" : [ "`packages/d2b-controller-toolkit/src/lib.rs`, `runner.rs`, `queue.rs`, `context.rs`, `result.rs`" ], + "detailedDesign" : "Async ResourceReconciler, watch receiver, coalescing, per-resource serialization, parallel tasks, retry/checkpoint/finalize; expedited priority lane and `CommittedRevisionProof`-gated effects (D090); `assess_update`/`plan_upgrade`/`execute_upgrade` methods serialized in the same single-flight (D091) Primary reuse disposition: `adapt`. Preserved source-plan detail: extract and adapt.", "entryContracts" : [ "ADR-046-resource-reconciliation" ], @@ -18143,6 +18758,7 @@ ], "specId" : "ADR-046-resource-reconciliation", "topologicalRank" : 6, + "validation" : "Golden state-machine vectors, deterministic clocks, conflict/restart/queue tests; D090: commit-fails/Abort → no effect, controller finishes-before-commit gated on proof, effects-gate, status-write-delayed (`statusPersistence: pending`), normal-queued no-op/rejoin, concurrent mutation, delete event-only projection, expedited timeout committed-but-pending, restart re-entry no duplicate; D091: current/non-disruptive/each-trigger assess, UpgradeRequired-not-in-place, dependency propagation/topological drain-recycle-restart, GPU blocking, state/TPM preservation, crash/re-entry resume, single-flight reconcile-vs-upgrade serialization", "wave" : "W1" }, { @@ -18150,6 +18766,7 @@ "destinations" : [ "`packages/d2b-core-controller/src/hints.rs`, `dependencies.rs`, `owner_reconcile.rs`" ], + "detailedDesign" : "Watch-plan validation, indexes, suppression, owner/dependency hints, leases, startup relist, fair admission", "entryContracts" : [ "ADR-046-resource-reconciliation" ], @@ -18164,6 +18781,7 @@ ], "specId" : "ADR-046-resource-reconciliation", "topologicalRank" : 7, + "validation" : "Owner/dependency chains, suppression/no-loss, restart/relist, lease withdrawal", "wave" : "W1" }, { @@ -18171,6 +18789,7 @@ "destinations" : [ "`packages/d2b-controller-toolkit/benches/reaction.rs`, Process Provider integration tests" ], + "detailedDesign" : "Commit-to-handler/launch fast path, nonblocking watch, parallel ready resources", "entryContracts" : [ "ADR-046-resource-reconciliation" ], @@ -18184,6 +18803,7 @@ ], "specId" : "ADR-046-resource-reconciliation", "topologicalRank" : 6, + "validation" : "Hard <=5 ms/<=20 ms p95 gates and 1/10/100 Process concurrency", "wave" : "W1" }, { @@ -18191,6 +18811,7 @@ "destinations" : [ "`packages/d2b-cutover/src/{inventory,snapshot,checkpoint}.rs`" ], + "detailedDesign" : "Implement the seven closed inventories of [Authoritative inventories](#authoritative-inventories); the `checkpoint_id` digest algorithm of [Preflight and immutable snapshot](#preflight-and-immutable-snapshot); the atomic snapshot-write sequence (temp file, fsync, rename, parent fsync, post-rename immutability) Primary reuse disposition: `adapt`. Preserved source-plan detail: extract and adapt.", "entryContracts" : [ "ADR-046-reset-and-cutover" ], @@ -18204,6 +18825,7 @@ ], "specId" : "ADR-046-reset-and-cutover", "topologicalRank" : 11, + "validation" : "`checkpoint_id` determinism property test; snapshot atomic-write crash-injection test; `cutover_preflight_refuses_dirty_flake_check`", "wave" : "W7" }, { @@ -18211,6 +18833,7 @@ "destinations" : [ "`packages/d2b-cutover/src/{bundle_validate,trust_preflight}.rs`" ], + "detailedDesign" : "Independent legacy-flake-check gate; candidate v3 bundle schema/cross-ref/determinism validation per [Config/artifact/schema validation](#configartifactschema-validation); Provider trust preflight per `ADR-046-provider-model-and-packaging` \"Trust\"", "entryContracts" : [ "ADR-046-reset-and-cutover" ], @@ -18225,6 +18848,7 @@ ], "specId" : "ADR-046-reset-and-cutover", "topologicalRank" : 12, + "validation" : "`cutover-candidate-bundle-validation.nix`; trust-preflight rejection tests for each of digest/publisher/signature/deny/provenance/conformance failure modes", "wave" : "W7" }, { @@ -18232,6 +18856,7 @@ "destinations" : [ "`packages/d2b-cutover/src/{consent,drain,disposition}.rs`" ], + "detailedDesign" : "Exact-consent-phrase gate bound to `checkpoint_id`; Phase 3 drain algorithm (§ [Old daemon/unit/process drain](#old-daemonunitprocess-drain)); the [Disposition framework](#disposition-framework)'s Adopt/Preserve/Destroy executor, delegating every Adopt to ADR046-reset-004 Primary reuse disposition: `adapt`. Preserved source-plan detail: extract and adapt.", "entryContracts" : [ "ADR-046-reset-and-cutover" ], @@ -18247,6 +18872,7 @@ ], "specId" : "ADR-046-reset-and-cutover", "topologicalRank" : 13, + "validation" : "`cutover_apply_requires_exact_consent_phrase`; `cutover_drain_refuses_on_live_process`", "wave" : "W7" }, { @@ -18254,6 +18880,7 @@ "destinations" : [ "`packages/d2b-cutover/src/adopt.rs`, thin wrapper invoking `ADR-046-provider-state`'s migration `EphemeralProcess` prepare/stage/commit/rollback machinery with cutover-specific source paths" ], + "detailedDesign" : "One Adopt invocation per matrix row tagged `Adopt` in the [migration/disposition matrix](#migrationdisposition-matrix); marker re-validation before every step; new-marker-before-old-removal ordering; idempotent re-run safety per [Crash/power-loss/retry/idempotency journals](#crashpower-lossretryidempotency-journals)", "entryContracts" : [ "ADR-046-reset-and-cutover" ], @@ -18268,6 +18895,7 @@ ], "specId" : "ADR-046-reset-and-cutover", "topologicalRank" : 14, + "validation" : "Crash-injection at every step boundary (Type 10 `cutover-crash-resume.nix`); TPM/durable-Volume Destroy-exclusion property test", "wave" : "W7" }, { @@ -18275,6 +18903,7 @@ "destinations" : [ "`packages/d2b-cutover/src/{store_bootstrap,provider_sequence}.rs`" ], + "detailedDesign" : "Phase 5 store creation per [Resource-store initialization](#resource-store-initialization); Phase 6 topological Provider install per [Provider install/topological start](#provider-installtopological-start), including the fixed staged default order and cycle-rejection check", "entryContracts" : [ "ADR-046-reset-and-cutover" ], @@ -18291,6 +18920,7 @@ ], "specId" : "ADR-046-reset-and-cutover", "topologicalRank" : 15, + "validation" : "Provider install topological-order determinism test; cycle-rejection test; store-identity mismatch fail-closed test", "wave" : "W7" }, { @@ -18298,6 +18928,7 @@ "destinations" : [ "`packages/d2b-cutover/src/{zonelink_cutover,guest_activation}.rs`" ], + "detailedDesign" : "Phase 7 translation from `EntrypointMode::GatewayBacked`/`HostResident` per [Zone/ZoneLink cutover](#zonezonelink-cutover): the child authors/stores one local uplink with self-matching `childZoneName`; compiler-only `parentZone` selects the allocator; the parent retains only sealed allocator/route state and owns no reciprocal row or ZoneLink handler. Phase 8 follows Network→Volume→Device→Guest ordering per [Guest/runtime/network/store view activation](#guestruntimenetworkstore-view-activation). The parent inventory never enumerates gateway-guest-internal credential/audit state", "entryContracts" : [ "ADR-046-reset-and-cutover" ], @@ -18312,6 +18943,7 @@ ], "specId" : "ADR-046-reset-and-cutover", "topologicalRank" : 16, + "validation" : "Gateway-custody-boundary test asserting the parent inventory never contains a gateway-guest-internal path; child-local ZoneLink test asserting one uplink, self-matching `childZoneName`, and child-store ownership; compiler test asserting `parentZone` selects the allocator but appears only in sealed bootstrap state; no-reciprocal-parent-row/no-parent-handler test; child-local ZoneLink `Degraded/waiting-on-remote` non-blocking test", "wave" : "W7" }, { @@ -18319,6 +18951,7 @@ "destinations" : [ "`packages/d2b-cutover/src/{verify,doctor,degraded}.rs`" ], + "detailedDesign" : "The ten `verify` checks in [Post-cutover verification](#post-cutover-verification); the `cutover-quarantined` degraded class and `doctor` reporting in [Failure/quarantine/manual recovery](#failurequarantinemanual-recovery); audit chain closure/genesis-record cross-check (check 9) Primary reuse disposition: `adapt`. Preserved source-plan detail: extract and adapt.", "entryContracts" : [ "ADR-046-reset-and-cutover" ], @@ -18333,6 +18966,7 @@ ], "specId" : "ADR-046-reset-and-cutover", "topologicalRank" : 17, + "validation" : "Injected-digest-mismatch test for TPM/durable-Volume verify checks; audit-genesis-cross-check test; `cutover-full-rehearsal.nix`", "wave" : "W7" }, { @@ -18340,6 +18974,7 @@ "destinations" : [ "`packages/d2b-cutover/src/finalize.rs`" ], + "detailedDesign" : "Per-candidate independent gate evaluation exactly as tabled in [Old artifact/unit/schema removal gates](#old-artifactunitschema-removal-gates); separate consent phrase from `apply`; never partial-destroys a candidate", "entryContracts" : [ "ADR-046-reset-and-cutover" ], @@ -18354,6 +18989,7 @@ ], "specId" : "ADR-046-reset-and-cutover", "topologicalRank" : 18, + "validation" : "`policy_no_destroy_without_gate`; `policy_legacy_cli_verbs_absent_after_gate`; `tpm-adopt-retirement.nix`", "wave" : "W7" }, { @@ -18361,6 +18997,7 @@ "destinations" : [ "`packages/d2b-cutover/src/{journal,rollback,hold}.rs`" ], + "detailedDesign" : "Append-only journal per [Crash/power-loss/retry/idempotency journals](#crashpower-lossretryidempotency-journals); [Rollback boundary](#rollback-boundary) enforcement (`cutover-rollback-window-closed` past phase 4); cutover-wide incident hold per [Incident hold (cutover-wide)](#incident-hold-cutover-wide)", "entryContracts" : [ "ADR-046-reset-and-cutover" ], @@ -18378,6 +19015,7 @@ ], "specId" : "ADR-046-reset-and-cutover", "topologicalRank" : 17, + "validation" : "`cutover_rollback_window_closes_after_phase_5`; incident-hold-blocks-destructive-step test", "wave" : "W7" }, { @@ -18385,6 +19023,7 @@ "destinations" : [ "`packages/d2b-cutover/src/reset_scope.rs`; `d2b host reset` CLI command" ], + "detailedDesign" : "The three reset scopes and their comparison table in [Full Zone reset vs Provider reset vs Guest reset](#full-zone-reset-vs-provider-reset-vs-guest-reset); durable-Volume preserve-by-default with explicit `--destroy-durable-volumes`/`--destroy-volumes` opt-in; OS-level authentication for the zone scope only", "entryContracts" : [ "ADR-046-reset-and-cutover" ], @@ -18401,6 +19040,7 @@ ], "specId" : "ADR-046-reset-and-cutover", "topologicalRank" : 18, + "validation" : "`host_reset_scope_isolation`; `zone-provider-guest-reset-isolation.nix`; durable-Volume-preserved-by-default property test for both Provider and Guest scopes", "wave" : "W7" }, { @@ -18408,6 +19048,7 @@ "destinations" : [ "`tests/integration/live/cutover-real-host.sh`, `tests/integration/live/cutover-real-host-cloud-guest.sh`, `tests/host-integration/hardware/cutover-real-tpm.sh`, `tests/host-integration/hardware/cutover-real-usbip-security-key.sh`" ], + "detailedDesign" : "Manual, `D2B_LIVE=1`/hardware-gated validation scripts described in [Tests](#tests) Type 11/12 rows; never run in CI; require operator sign-off and an independent out-of-band backup before execution", "entryContracts" : [ "ADR-046-reset-and-cutover" ], @@ -18431,6 +19072,7 @@ ], "specId" : "ADR-046-reset-and-cutover", "topologicalRank" : 19, + "validation" : "Manual pass/fail sign-off recorded per the project's existing live-host/hardware validation conventions", "wave" : "W7" }, { @@ -18438,6 +19080,7 @@ "destinations" : [ "`packages/d2b-session/` copied verbatim; v3 contract extension renames endpoint purpose values while preserving backward wire compatibility during transition." ], + "detailedDesign" : "Noise handshake, record protection, scheduler, named streams, cancellation, attachments, deadlines, metrics injection, ttrpc serving, and generation-discovery behavior from the selected source. Primary reuse disposition: `adapt`. Preserved source-plan detail: Copy/adopt the core ComponentSession runtime; adapt EndpointPurpose/ServicePackage/RealmSessionAuthority names listed in Excluded ADR45 assumptions and inject a `d2b-telemetry` MetricsSink..", "entryContracts" : [ "ADR-046-telemetry-audit-and-support" ], @@ -18452,6 +19095,7 @@ ], "specId" : "ADR-046-telemetry-audit-and-support", "topologicalRank" : 11, + "validation" : "Adopt `tests/component_session.rs` and `tests/noise_vectors.rs` unchanged; extend with v3 `EndpointPurpose` enum gate test; add `d2b-contract-tests/tests/component_session_v2_vectors.rs` (existing at `a1cc0b2d`) as-is.", "wave" : "W5" }, { @@ -18459,6 +19103,7 @@ "destinations" : [ "`packages/d2b-session-unix/` copied verbatim." ], + "detailedDesign" : "SO_PASSCRED verification, pidfd identity verification, multi-scope credit reservation, emergency headroom, seqpacket atomic CLOEXEC transfer, stream reassembly, vsock transport, and descriptor policy enforcement. Primary reuse disposition: `adapt`. Preserved source-plan detail: Copy verbatim; retain `host-socket`/`native-vsock` feature flags; no ADR45 topology adaptation required..", "entryContracts" : [ "ADR-046-telemetry-audit-and-support" ], @@ -18473,6 +19118,7 @@ ], "specId" : "ADR-046-telemetry-audit-and-support", "topologicalRank" : 12, + "validation" : "Adopt all `unix_session.rs` tests unchanged.", "wave" : "W5" }, { @@ -18480,6 +19126,7 @@ "destinations" : [ "`packages/d2b-client/` copied; DaemonClient/GuestClient adapted to v3 service packages; `MetadataInput::with_trace` drives `TraceContext` propagation." ], + "detailedDesign" : "MetadataInput trace/correlation/idempotency, retry policy, cancellation token, generic client over resolver/connector/clock, typed service proxies, stream terminal, and host-socket verifier. Primary reuse disposition: `adapt`. Preserved source-plan detail: Copy client layer; adapt service package/method sets, local-root Zone identity, and TraceContext propagation for v3..", "entryContracts" : [ "ADR-046-telemetry-audit-and-support" ], @@ -18494,6 +19141,7 @@ ], "specId" : "ADR-046-telemetry-audit-and-support", "topologicalRank" : 12, + "validation" : "Adopt typed-route, proxy-reuse, and cancel tests unchanged. Add v3 service-package name gate test.", "wave" : "W5" }, { @@ -18501,6 +19149,7 @@ "destinations" : [ "`packages/d2b-provider/` and `packages/d2b-provider-toolkit/` copied with v3 session admission and bus routing adaptations." ], + "detailedDesign" : "ProviderRegistry factory registration, bounded admission, RPC proxy fail-closed behavior, generated provider service admission/dispatch, redaction wrappers, and conformance gates. Primary reuse disposition: `adapt`. Preserved source-plan detail: Copy provider runtime/toolkit; adapt SessionIdentity Zone name and route through `d2b-bus` instead of daemon embedding..", "entryContracts" : [ "ADR-046-telemetry-audit-and-support" ], @@ -18516,6 +19165,7 @@ ], "specId" : "ADR-046-telemetry-audit-and-support", "topologicalRank" : 13, + "validation" : "Adopt all `conformance.rs` and `runtime.rs` tests unchanged. Add v3 `SessionIdentity` zone-name gate. Add conformance-failure → `d2b_provider_reconcile_total` metric integration test.", "wave" : "W5" }, { @@ -18523,6 +19173,7 @@ "destinations" : [ "`packages/d2b-provider-observability-otel/src/agent.rs` adapted; ComponentSessionDriver mock becomes shared Provider session fixture." ], + "detailedDesign" : "Session-bound ProviderAgentProcess construction, bounded audit snapshot, closed-set ProviderAgentError paths, and full ComponentSessionDriver mock for hermetic Provider tests. Primary reuse disposition: `adapt`. Preserved source-plan detail: Adapt provider-agent session wrapper and audit ring; replace gateway-runtime audit envelope with v3 `d2b-audit` records..", "entryContracts" : [ "ADR-046-telemetry-audit-and-support" ], @@ -18538,6 +19189,7 @@ ], "specId" : "ADR-046-telemetry-audit-and-support", "topologicalRank" : 14, + "validation" : "Adopt `provider_agent_v2.rs` mock harness unchanged as shared v3 Provider session fixture. Add v3 audit-bridge test: provider-agent session → `SessionConnect{transport_class=\"zone_link\"}` record emitted.", "wave" : "W5" }, { @@ -18545,6 +19197,7 @@ "destinations" : [ "`packages/d2b-bus/src/routing.rs` adapted from `service_v2.rs`; RemoteNodeErrorKind stable code pattern becomes v3 BusErrorKind." ], + "detailedDesign" : "Realm/Zone service ttrpc handler table, session authority constructors, host/gateway credential custody split, stable low-cardinality error codes, and binding/shortcut bounds. Primary reuse disposition: `adapt`. Preserved source-plan detail: Adapt realm service routing to Zone service routing; preserve credential-custody, bounded routing, and stable error-code behavior..", "entryContracts" : [ "ADR-046-telemetry-audit-and-support" ], @@ -18560,6 +19213,7 @@ ], "specId" : "ADR-046-telemetry-audit-and-support", "topologicalRank" : 12, + "validation" : "Adopt `authority_keeps_remote_credentials_in_gateway_guests` test renamed to `authority_keeps_remote_credentials_in_zone_link_sessions`; adapt `RealmId` → Zone name; adopt `authenticated_bootstrap_enrollment_route_and_shortcut_lifecycle` renamed with zone terminology.", "wave" : "W5" }, { @@ -18567,6 +19221,7 @@ "destinations" : [ "`packages/d2b-bus/src/service_router.rs` and `packages/d2b-core-controller/src/provider_effects.rs`." ], + "detailedDesign" : "Closed-set service.package route gates, provider lifecycle effect-adapter composition, closed-set error mapping, fixed local policy invariants, and bootstrap/enrolled credential binding shape. Primary reuse disposition: `adapt`. Preserved source-plan detail: Adapt route-gate and ProviderLifecycleDispatch patterns; exclude daemon-embedded effect composition and route through v3 bus/provider processes..", "entryContracts" : [ "ADR-046-telemetry-audit-and-support" ], @@ -18582,6 +19237,7 @@ ], "specId" : "ADR-046-telemetry-audit-and-support", "topologicalRank" : 14, + "validation" : "Port `local_daemon_policy_is_fixed_and_has_no_negotiation_or_fd_surface` invariant to v3 bus local policy test; port `every_generated_daemon_method_has_one_typed_adapter` to v3 bus method adapter completeness test.", "wave" : "W5" }, { @@ -18589,6 +19245,7 @@ "destinations" : [ "`packages/d2b-contract-tests/tests/component_session_v2_vectors.rs` and `tests/noise_vectors.rs` copied verbatim." ], + "detailedDesign" : "Pinned Noise KAT vectors, transcript/PSK mutation rejection, typed bootstrap admission fixture, and public-key corruption rejection remain the session wire security ground truth. Primary reuse disposition: `adapt`. Preserved source-plan detail: Copy the vector and conformance tests verbatim; add v3 constant gate for ComponentSession major/minor version..", "entryContracts" : [ "ADR-046-telemetry-audit-and-support" ], @@ -18603,6 +19260,7 @@ ], "specId" : "ADR-046-telemetry-audit-and-support", "topologicalRank" : 12, + "validation" : "These tests are self-validating. Add one gate: assert `COMPONENT_SESSION_MAJOR = 2` and `COMPONENT_SESSION_MINOR = 0` constants are unchanged in v3 contract.", "wave" : "W5" }, { @@ -18610,6 +19268,7 @@ "destinations" : [ "`packages/d2b-telemetry/src/session_metrics_sink.rs`." ], + "detailedDesign" : "Map every MetricEvent variant to the `d2b_session_*` inventory; convert MetricLabels closed sets to allowed v3 label strings; keep NoopMetrics for hermetic tests. Primary reuse disposition: `adapt`. Preserved source-plan detail: Adapt MetricsSink bridge to v3 OTEL instruments and rename ADR45 endpoint-purpose label values to v3 closed-set labels..", "entryContracts" : [ "ADR-046-telemetry-audit-and-support" ], @@ -18626,6 +19285,7 @@ ], "specId" : "ADR-046-telemetry-audit-and-support", "topologicalRank" : 12, + "validation" : "New test `packages/d2b-telemetry/tests/session_sink_bridge.rs`: drive `MetricEvent` variants through the sink; assert OTEL counter/gauge values; assert `MetricLabels` closed-set values map only to allowed label strings (no `DaemonLocal` string in v3 metric output).", "wave" : "W5" }, { @@ -18633,6 +19293,7 @@ "destinations" : [ "`packages/d2b-contracts/src/v3/zone_routing.rs`" ], + "detailedDesign" : "Rename RealmPath → ZonePath, RealmId → ZoneLabelId, RouteId → ZoneRouteId, ControllerGenerationId → ZoneLinkControllerGeneration; preserve all bounds/validation/serde; add ZoneLink-specific advertisement envelope fields (v3 schema version, capability ceiling field); preserve `RouteFailClosedReason` + add `zone-link-disconnected`, `hop-limit-exceeded`, `relay-denied`, `attachment-not-permitted-over-zone-link`; freeze v3 protobuf numbers separately from v2 Primary reuse disposition: `adapt`. Preserved source-plan detail: extract and adapt.", "entryContracts" : [ "ADR-046-zone-routing" ], @@ -18646,6 +19307,7 @@ ], "specId" : "ADR-046-zone-routing", "topologicalRank" : 7, + "validation" : "Golden advertisement/path/failure vectors shared by Rust/Nix; property tests for NCA/loop/ceiling; replay-window tests; hop-count tests", "wave" : "W2" }, { @@ -18653,6 +19315,7 @@ "destinations" : [ "`packages/d2b-zone-routing/src/engine.rs`" ], + "detailedDesign" : "Adapt RouteTreeEngine to ZoneRouteEngine using ZonePath/ZoneRouteId/ZoneLinkControllerGeneration from ADR046-routing-001; preserve all NCA/loop/multi-parent/capability/replay/capacity logic; add `decide_route` enforcement of `maxHops` parameter; add hop-counter decrement in relay path; expose `ZoneRoutePath` in v3 types Primary reuse disposition: `adapt`. Preserved source-plan detail: extract and adapt.", "entryContracts" : [ "ADR-046-zone-routing" ], @@ -18667,6 +19330,7 @@ ], "specId" : "ADR-046-zone-routing", "topologicalRank" : 8, + "validation" : "Copy exact `route_engine.rs` test suite adapted to ZonePath; add relay/hop-count/RBAC-narrowing/shortcut integration tests", "wave" : "W2" }, { @@ -18674,6 +19338,7 @@ "destinations" : [ "`packages/d2b-contracts/src/v3/zone_link.rs` (ZoneLink spec/status types, intent types, namespace allocation); `packages/d2b-zone-routing/src/resolver.rs` (ZoneEntrypointResolver)" ], + "detailedDesign" : "Child-local ZoneLink spec/status fields; self-name and one-uplink invariants; ZoneLinkIntent record stored in the child; ZoneLinkNamespaceAllocation issued by the exact parent allocator selected in sealed Nix-compiled `parentZone` topology; ZoneEntrypointResolver with longest-suffix match over ZonePath (adapted from `RealmEntrypointTable::resolve`); no reciprocal parent-store resource; fail-closed on unknown Zone Primary reuse disposition: `adapt`. Preserved source-plan detail: extract and adapt.", "entryContracts" : [ "ADR-046-zone-routing" ], @@ -18689,6 +19354,7 @@ ], "specId" : "ADR-046-zone-routing", "topologicalRank" : 9, + "validation" : "Longest-suffix match vectors; ZoneLink spec validation tests; resolver fail-closed test", "wave" : "W2" }, { @@ -18696,6 +19362,7 @@ "destinations" : [ "`packages/d2b-core-controller/src/zone_links.rs`" ], + "detailedDesign" : "Child-local ZoneLink handler in core-controller: manages local ResourceSpec→allocator-bound session→advertisement lifecycle; session state machine (Pending/Established/Disconnected/Reconnecting/Revoked); reconnect backoff per `reconnectPolicy`; advertisement issuance/renewal/withdrawal using enrolled KK ComponentSession; child-store route cursor and outbound intent queue; capability ceiling change handling; status writer; aggregate metrics use only closed semantic phase/reason/outcome labels and never `link_name_hash` or another ZoneLink/Zone/resource identity label; Nix-compiled `parentZone` selects the parent allocator, which alone owns privileged listeners, placement, and route namespace and creates no reciprocal resource. Primary reuse disposition: `adapt`. Preserved source-plan detail: extract and adapt.", "entryContracts" : [ "ADR-046-zone-routing" ], @@ -18710,6 +19377,7 @@ ], "specId" : "ADR-046-zone-routing", "topologicalRank" : 10, + "validation" : "Session lifecycle tests; reconnect/revocation/ceiling-change; intent queue drain; cursor resync; advertisement renewal timing; fake-child tests; structural metric descriptor test asserts `vm`, `zone`, `zone_id`, `zone_uid`, and `link_name_hash` are absent and a ZoneLink-name canary never enters label values", "wave" : "W2" }, { @@ -18717,6 +19385,7 @@ "destinations" : [ "`packages/d2b-bus/src/zone_route.rs` (cross-Zone bus routing), `packages/d2b-bus/src/relay.rs` (per-hop relay handler)" ], + "detailedDesign" : "Cross-Zone routing path in d2b-bus: ZoneEntrypointResolver → ZoneRouteEngine::decide_route → ZoneLink ComponentSession per hop; hop-counter decrement and enforcement; RBAC `relay` verb check at each intermediate hop; idempotency key namespace (full 6-tuple) in ZoneLinkIdempotencyKey; pinned reverse path tracking; cancellation forwarding; watch cursor forwarding and revision-expired handling; no-FD/credential structural rejection at serialization boundary Primary reuse disposition: `adapt`. Preserved source-plan detail: extract and adapt.", "entryContracts" : [ "ADR-046-zone-routing" ], @@ -18732,6 +19401,7 @@ ], "specId" : "ADR-046-zone-routing", "topologicalRank" : 9, + "validation" : "End-to-end K0→K1→K2 resource call; relay-denied/hop-limit/FD-rejection tests; idempotency namespace collision tests; cancellation delivery tests; watch resync tests", "wave" : "W2" }, { @@ -18739,6 +19409,7 @@ "destinations" : [ "`packages/d2b-zone-routing/tests/route_engine_vectors.rs`, `packages/d2b-zone-routing/benches/route_decision.rs`" ], + "detailedDesign" : "Copy exact advertisement/NCA/loop/capability/replay test vectors adapted to ZonePath; add K0/K1/K2 topology scenarios; add hop-count boundary tests; benchmark: p95 route decision for 1/10/100 active Zone tree entries <= 1 ms", "entryContracts" : [ "ADR-046-zone-routing" ], @@ -18753,6 +19424,7 @@ ], "specId" : "ADR-046-zone-routing", "topologicalRank" : 9, + "validation" : "All v3 baseline route_engine test cases must pass; p95 benchmark gate", "wave" : "W2" }, { @@ -18760,6 +19432,7 @@ "destinations" : [ "`packages/d2b-bus/src/session/`" ], + "detailedDesign" : "Copy `d2b-session` crate wholesale into `d2b-bus/src/session/`; adapt `EndpointPurpose`/`EndpointRole`/`ServicePackage` closed-enum tags for v3 purposes; strip `GUEST_SESSION_CREDENTIAL_*` types; strip `serve_ttrpc_services` fixed-endpoint binding (replaced by allocator-issued FD bootstrap); adapt `SessionEngine` as ZoneLink session drive loop; keep all Noise profiles (Nn/Kk/IKpsk2), generation discovery, record/fragment/keepalive/credit/cancellation/attachment logic verbatim Primary reuse disposition: `adapt`. Preserved source-plan detail: copy and adapt.", "entryContracts" : [ "ADR-046-zone-routing" ], @@ -18773,6 +19446,7 @@ ], "specId" : "ADR-046-zone-routing", "topologicalRank" : 7, + "validation" : "Port all `component_session.rs` tests; port `noise_vectors.rs`; add ZoneLink-specific KK enrollment test; add ZoneLink reconnect/revocation integration test", "wave" : "W2" }, { @@ -18780,6 +19454,7 @@ "destinations" : [ "`packages/d2b-bus/src/transport/unix.rs`, `packages/d2b-bus/src/transport/credit.rs`" ], + "detailedDesign" : "Copy `UnixSeqpacketTransport`/`UnixStreamTransport`/credit modules verbatim; adapt `PeerIdentityPolicy` for v3 Zone principal model; adapt `InheritedSocketTransport` to receive allocator-issued FD directly (not SD_LISTEN_FDS); vsock paths adapted as transport-Provider-specific implementations (not hardcoded); `PidfdIdentityPolicy` adapted for v3 Process resource pidfd model Primary reuse disposition: `adapt`. Preserved source-plan detail: copy and adapt.", "entryContracts" : [ "ADR-046-zone-routing" ], @@ -18794,6 +19469,7 @@ ], "specId" : "ADR-046-zone-routing", "topologicalRank" : 8, + "validation" : "Port all `unix_session.rs` tests; add allocator-issued FD handoff test; add inherited-socket no-SD-listen test", "wave" : "W2" }, { @@ -18801,6 +19477,7 @@ "destinations" : [ "`packages/d2b-contracts/src/v3/zone_session.rs`" ], + "detailedDesign" : "Copy all protocol constants verbatim; copy `BoundedVec`, `BinaryError`, `ContractError`; copy `ComponentSessionPreface`, `LimitProfile`, `AttachmentDescriptor`/`AttachmentKind`/`AttachmentCreditClass`/`ChannelId`/`RecordHeader`/`FragmentHeader`/`SessionErrorCode`/`CloseReason`/`Remediation` verbatim; extend `ServicePackage` closed-enum with `ZoneV3`/`ResourceV3`/`ZoneLinkV3` variants at new tag values; extend `EndpointRole` with `ZoneController`/`ZoneRelay`/`ZoneBootstrap` variants; extend `EndpointPurpose`/`PurposeClass` with v3 Zone purposes; strip `GUEST_SESSION_CREDENTIAL_*` constants and types; re-freeze protobuf field numbers for v3 services independently from v2 assignments Primary reuse disposition: `adapt`. Preserved source-plan detail: copy and adapt.", "entryContracts" : [ "ADR-046-zone-routing" ], @@ -18815,6 +19492,7 @@ ], "specId" : "ADR-046-zone-routing", "topologicalRank" : 8, + "validation" : "Updated `negotiate_offer`/`validate_exact` round-trip tests for v3 purposes; canonical encoding stability test; closed-enum exhaustiveness tests", "wave" : "W2" }, { @@ -18822,6 +19500,7 @@ "destinations" : [ "`packages/d2b-resource-client/`" ], + "detailedDesign" : "Copy `Client` generic structure; rename `ServiceOwner::Realm(RealmId)` → `ServiceOwner::Zone(ZonePath)`, `ServiceOwner::Workload{realm,workload}` → `ServiceOwner::Guest{zone,guest}`, `ServiceOwner::LocalRoot` → `ServiceOwner::ZoneLocal`; rename `TargetInput` variants to match; add `TargetInput::ZoneService(ZonePath, ZoneServiceKind)` for cross-Zone service targeting; replace `ServiceKind` (25 ADR45 variants) with v3 service inventory (`Resource`, `Zone`, `ZoneLink`, `Provider`, plus retained guest/daemon variants); adapt `RouteTable` to route by `ZonePath`; replace `HostSocketConnector` uid-based trust with allocator-issued FD + KK static key pinning; keep `SessionFailure`/retry/cancellation/`MetadataInput`/`RetryPolicy` logic verbatim Primary reuse disposition: `adapt`. Preserved source-plan detail: copy and adapt.", "entryContracts" : [ "ADR-046-zone-routing" ], @@ -18837,6 +19516,7 @@ ], "specId" : "ADR-046-zone-routing", "topologicalRank" : 9, + "validation" : "Port `client.rs` tests; add ZonePath routing test; add cross-Zone K0→K1 end-to-end test; add retry/cancellation forwarding test", "wave" : "W2" }, { @@ -18844,6 +19524,7 @@ "destinations" : [ "`nixos-modules/options-zones.nix` (new structural base), `nixos-modules/generated/resource-types.nix` (generated registry), `nixos-modules/generated/options-zones-.nix` (generated per ResourceType by `xtask gen-zone-nix-options`), `nixos-modules/assertions.nix` (new Zone assertions)" ], + "detailedDesign" : "Declare compiler-only scalar `d2b.zones..parentZone` plus structural `d2b.zones..resources. = { type = ...; spec = {}; }` as specified in the \"Option schema\" section above. `parentZone` has no default, is required for every non-root Zone, forbidden on `local-root`, resolves to one declared Zone, and never enters a ResourceSpec. Wire `options-zones.nix` and all `generated/options-zones-*.nix` files into `nixos-modules/default.nix`. Add a new `xtask gen-zone-nix-options` command that reads `docs/reference/schemas/v3/.schema.json` for each ResourceType, derives `generated/resource-types.nix`, and emits a generated submodule overlaying typed spec options (types, bounds, enum constraints, defaults, docs) onto `d2b.zones..resources..spec`. The generated registry's standard subset must equal the canonical 19-type registry from `ADR-046-resource-object-model` exactly; installed signed Provider schemas may append qualified types. These generated modules are committed and kept in sync by `xtask gen-zone-nix-options && git diff --exit-code` wired into `make test-drift`. Because the generated options carry field-level type constraints, field-level eval errors (wrong enum, out-of-range int, non-hex fingerprint) are caught without explicit assertions. Explicit assertions in `nixos-modules/assertions.nix` cover cross-resource invariants only: zone/resource key name regex, reserved names, `parentZone` required/forbidden/existence/self/cycle/16-name-depth constraints, child-local `childZoneName == zone`, at most one uplink resource per non-root Zone and none in local root, ref resolution, count limits, and transportSettings secret-key exclusion (listed in the eval-time assertions table). Conflicting parent scalar definitions fail through standard Nix module merging. Each new assertion must have a matching case in `tests/unit/nix/cases/zone-assertions.nix` (nix-unit auto-discovered). `d2b.realms` option namespace is NOT removed in this work item. Primary reuse disposition: `adapt`. Preserved source-plan detail: new module following same pattern.", "entryContracts" : [ "ADR-046-zone-routing" ], @@ -18858,6 +19539,7 @@ ], "specId" : "ADR-046-zone-routing", "topologicalRank" : 8, + "validation" : "`nix-unit: zone-name-regex`, all five `nix-unit: zone-parent-*` vectors, `nix-unit: zone-link-fingerprint`, `nix-unit: zone-link-child-name`, `nix-unit: zone-link-one-uplink`, `nix-unit: capability-ceiling-unknown-verb`, `nix-unit: transport-settings-secret-key`; add `drift: standard-resource-type-registry` asserting the generated standard subset is exactly all 19 canonical types with no omission/addition/duplicate/reordering, plus `drift: zone-nix-options` (`xtask gen-zone-nix-options && git diff --exit-code`); run `make nix-unit-pin` after adding eval cases", "wave" : "W2" }, { @@ -18865,6 +19547,7 @@ "destinations" : [ "`nixos-modules/zone-resources-json.nix` (new), private local-root allocator bootstrap compiler/sealer input (not a ResourceSpec or public bundle), `nixos-modules/bundle-artifacts.nix` (new row for per-Zone `resource-bundle.json`), `packages/xtask/src/main.rs` (`gen-zone-schemas` subcommand emitting `docs/reference/schemas/v3/.schema.json` for Zone and ZoneLink; `gen-zone-nix-options` subcommand emitting `nixos-modules/generated/options-zones-.nix`)" ], + "detailedDesign" : "`zone-resources-json.nix` iterates `d2b.zones..resources.*` to produce the canonical sorted resource list: for each entry, render `{ apiVersion, type, metadata: { name, zone, ownerRef: , labels: , annotations: }, spec: }`. Separately canonicalize sorted `{ childZone, parentZone }` rows from the compiler-only topology and seal them into the private allocator bootstrap input; `parentZone` never enters a resource bundle or `Zone.spec`, and a topology digest change releases/reallocates affected edges independently of resource `generationId`. The bundle JSON omits `managedBy` and `configurationGeneration`; the configuration service/core sets those fields when activating the validated bundle. Sort all resources by `(type, zone, name)`. Compute `generationId` as SHA-256 (lower hex) of the UTF-8 bytes of the sorted `resources` array JSON. Compute `integrity` as SHA-256 (base64url, no padding) of the full bundle JSON with integrity field zeroed. Install at `/etc/d2b/zones//resource-bundle.json` root:d2bd 0640. Canonical form: all object keys sorted lexicographically; order-significant arrays preserved; schema-declared set-like arrays sorted lexicographically; all optional fields emitted with defaults; no field renaming or restructuring. Build-time validation runs in a Nix derivation: (1) validate the complete parent map (non-root required, local-root forbidden, declared target, one scalar parent, not self, acyclic, max 16 names); (2) validate each resource against the committed JSON Schema; (3) validate `transportSettings` for each child-local ZoneLink against its same-Zone Provider's `transportSettingsSchema` — `transportProviderRef` is always explicit, never inferred or defaulted; (4) verify capability ceilings are subsets of the sealed selected-parent allocator grants; (5) verify `childZoneName == metadata.zone`, at most one uplink resource per non-root Zone, and no local-root uplink; (6) check for duplicate `(type, zone, name)` tuples. Providers MUST commit their `transportSettingsSchema` before any ZoneLink can reference them. Drift gates: `xtask gen-zone-schemas && git diff --exit-code` and `xtask gen-zone-nix-options && git diff --exit-code` both wired into `make test-drift`. Add `checks.${system}.zone-schema-drift` to `flake.nix`. Primary reuse disposition: `adapt`. Preserved source-plan detail: extend and adapt.", "entryContracts" : [ "ADR-046-zone-routing" ], @@ -18880,6 +19563,7 @@ ], "specId" : "ADR-046-zone-routing", "topologicalRank" : 9, + "validation" : "`drift: zone-resource-schema`, `drift: zone-nix-options`, `build: zone-bundle-deterministic`, `build: parent-topology-sealed`, `build: transport-settings-unknown-field`, `build: capability-ceiling-superset`, `build: missing-transport-provider`; run `make flake-matrix-pin` after adding flake checks", "wave" : "W2" }, { @@ -18887,6 +19571,7 @@ "destinations" : [ "`packages/d2b-core-controller/src/configuration.rs` (defined by ADR-046-core-controllers); shared bundle DTOs may live in `packages/d2b-core/`" ], + "detailedDesign" : "Implement the configuration ownership and cleanup contract from the \"Configuration ownership and cleanup contract\" section. `configuration.rs` owns: (1) reading and integrity-verifying `/etc/d2b/zones//resource-bundle.json` on startup and SIGHUP; (2) diffing against active generation by `generationId` (no-op if unchanged); (3) queuing Create/UpdateSpec/Delete intents — core sets `configurationGeneration` and `managedBy` when applying Create/UpdateSpec; Delete targets only resources where BOTH `managedBy` equals the configuration service's value AND `configurationGeneration` matches the prior bundle — resources with `managedBy=controller` or `managedBy=api` are never seized; (4) setting `deletionRequestedAt` on pending-delete resources immediately and adding a Pending condition; (5) writing the prior bundle into the capped ring at `/var/lib/d2b/zones//configuration/prior/.json` (default retentionCount=3, range 1..16, no TTL; prune oldest when count would exceed limit); (6) enforcing boundary invariants (no diff-delete for absent `configurationGeneration`, `managedBy` collision guard, live controller-child teardown guard); (7) driving finalizer drain + controller-child cascade before completing a Delete; (8) on successful deletion: one store transaction writes the `Deleted` revision/change event and removes the resource row and all index entries; the authoritative audit record (`zone-resource-cleanup`) is appended from the committed revision with dedup/exactly-once recovery and is NOT part of the store transaction; (9) tracking `deletionRequestedAt`/`cleanupConfigGeneration`/`cleanupError`/`cleanupAttempt` per resource; (10) on rollback: clearing `deletionRequestedAt` and Pending condition for revived resources; (11) never pruning a prior bundle while a Delete intent from its `configurationGeneration` is in flight. OFD lock on the bundle file prevents concurrent activation races. Generation state persisted atomically at `/var/lib/d2b/zones//configuration/generation.json` (root:d2bd 0640). The `spec` object comparison for UpdateSpec detection uses the canonical JSON form so two identical specs always compare equal regardless of Nix rendering order. Resource phase transitions: Pending while Create/UpdateSpec in-flight; Degraded while cleanup pending; Ready when clean; Failed on permanent error. Primary reuse disposition: `adapt`. Preserved source-plan detail: extract and adapt.", "entryContracts" : [ "ADR-046-zone-routing" ], @@ -18902,6 +19587,7 @@ ], "specId" : "ADR-046-zone-routing", "topologicalRank" : 10, + "validation" : "`host-integration: cleanup-removed-zonelink`, `host-integration: rollback-restores-zonelink`, `host-integration: dynamic-child-not-deleted`, `host-integration: zonelink-no-reciprocal-row`; unit tests: deterministic generationId, no-op on same generationId, cross-ownership invariant enforcement, prior-bundle write/prune cycle, UpdateSpec canonical comparison, store-transaction-then-audit-append ordering, exactly-once audit dedup", "wave" : "W2" }, { @@ -18909,6 +19595,7 @@ "destinations" : [ "`packages/d2b-provider/src/` (adapted in place)" ], + "detailedDesign" : "Retain `ProviderRegistry`/`ProviderRegistryBuilder`/`ProviderRegistryManager` lifecycle verbatim; adapt `SessionIdentity` to carry `ZonePath` instead of `RealmId`; adapt `AdmissionOptions::peer_role` to v3 Zone principal + RBAC binding; adapt `ProviderDescriptor` schema version to v3; `RegistryLimits` unchanged; `RpcProviderProxy` field adaptations to match v3 session identity; `ProviderInstance` variants retain all 11 types Primary reuse disposition: `adapt`. Preserved source-plan detail: copy and adapt.", "entryContracts" : [ "ADR-046-zone-routing" ], @@ -18923,6 +19610,7 @@ ], "specId" : "ADR-046-zone-routing", "topologicalRank" : 8, + "validation" : "Port inline registry lifecycle/drain/shutdown tests; add v3 ZonePath routing test; add RBAC relay-verb check at provider admission", "wave" : "W2" }, { @@ -18930,6 +19618,7 @@ "destinations" : [ "`packages/d2b-provider-toolkit/src/` (adapted in place)" ], + "detailedDesign" : "Retain `GeneratedProviderServiceServer` ttrpc dispatch verbatim; adapt `ProviderAgentProcess::from_registry` to receive ComponentSession FD from Zone allocator bootstrap instead of SD_LISTEN_FDS; adapt audit event types for v3 Zone principal; `ProviderAgentAdapter` (client-side proxy) adapted for v3 ZoneLink session; conformance kit extended for v3 Provider resource conformance checks; redaction helpers unchanged Primary reuse disposition: `adapt`. Preserved source-plan detail: copy and adapt.", "entryContracts" : [ "ADR-046-zone-routing" ], @@ -18944,6 +19633,7 @@ ], "specId" : "ADR-046-zone-routing", "topologicalRank" : 9, + "validation" : "Port `audit_capacity_is_closed_and_bounded`; add v3 bootstrap-via-allocator test; add conformance test for new Provider ResourceType schema", "wave" : "W2" }, { @@ -18951,6 +19641,7 @@ "destinations" : [ "`packages/d2b-zone-routing/src/service.rs`" ], + "detailedDesign" : "Rename `RealmServiceServer` → `ZoneServiceServer`; service wire name `d2b.realm.v2.RealmService` → `d2b.zone.v3.ZoneService`; rename methods (bootstrap→zone-bootstrap, enroll→zone-enroll, resolve_route→resolve-zone-route, authorize_shortcut→authorize-zone-shortcut, revoke_shortcut→revoke-zone-shortcut, report_shortcut_close→report-zone-shortcut-close, inspect→zone-inspect); replace `RealmSessionAuthority` with Zone principal + RBAC binding; replace `BootstrapBinding` with ZoneLink allocator-issued PSK binding; replace `EnrollmentBinding` with ZoneLink KK enrollment record; add `relay` verb RBAC check per hop; adapt shortcut model to ZonePath addressing; `RealmServiceLimits` defaults preserved; `MAX_DISPATCH_IN_FLIGHT=64`, `SHUTDOWN_TIMEOUT=5s` preserved; `CredentialCustody::GatewayGuest` excluded (all ZoneLink sessions are direct KK) Primary reuse disposition: `adapt`. Preserved source-plan detail: copy and adapt.", "entryContracts" : [ "ADR-046-zone-routing" ], @@ -18966,6 +19657,7 @@ ], "specId" : "ADR-046-zone-routing", "topologicalRank" : 8, + "validation" : "Bootstrap/enroll/resolve-route/shortcut integration tests against fake ZoneLink; relay-verb RBAC test; KK enrollment test; shortcut ZonePath addressing test; concurrent dispatch bound test (64 in-flight)", "wave" : "W2" }, { @@ -18973,6 +19665,7 @@ "destinations" : [ "`packages/d2b-contract-tests/tests/policy_telemetry_redaction.rs`" ], + "detailedDesign" : "One policy test enumerating every forbidden metric-label/audit-field value from §21 and the content-secrecy table in §20 (store paths, `no_isolation`, credential bytes, raw paths/argv/PID/cgroup, CTAP/clipboard/terminal/notification content) and asserting, by static scan of instrumentation call sites plus a redaction-guard runtime test, that no `ADR046-*` Provider crate emits any of them Primary reuse disposition: `adapt`. Preserved source-plan detail: extract and adapt.", "entryContracts" : [ "ADR-046-security-and-threat-model" ], @@ -18987,6 +19680,7 @@ ], "specId" : "ADR-046-security-and-threat-model", "topologicalRank" : 12, + "validation" : "Hermetic (`cargo test -p d2b-contract-tests policy_telemetry_redaction`); fails the build if a new Provider crate is added without a corresponding redaction test file under its `tests/`", "wave" : "W7" }, { @@ -18994,6 +19688,7 @@ "destinations" : [ "`packages/d2b-session/tests/noise_conformance.rs`, `packages/d2b-session/fuzz/fuzz_targets/{handshake_offer,record_frame}.rs`" ], + "detailedDesign" : "Property/fuzz test suite over the three Noise profiles (§7): exact NN/KK/IKpsk2 vectors and rejection mutations (copied), plus new `cargo-fuzz` targets mutating the canonical handshake offer, preface, and encrypted record frame to assert no panic/UB and that every malformed input is a typed rejection (never a partial accept) Primary reuse disposition: `adapt`. Preserved source-plan detail: copy and adapt.", "entryContracts" : [ "ADR-046-security-and-threat-model" ], @@ -19009,6 +19704,7 @@ ], "specId" : "ADR-046-security-and-threat-model", "topologicalRank" : 12, + "validation" : "Hermetic vector tests plus fuzz corpus with a minimum 4-hour nightly run and zero crashes/hangs as acceptance", "wave" : "W7" }, { @@ -19016,6 +19712,7 @@ "destinations" : [ "`packages/d2b-resource-store/tests/rbac_property.rs`" ], + "detailedDesign" : "Property test asserting, for a randomly generated Role/RoleBinding/request corpus: (1) no request whose payload sets a subject/role field ever changes the resolved `AuthenticatedSubjectContext.subjectRef`; (2) no non-core Role with a wildcard grant is ever admitted; (3) `scopeNarrowing` never widens beyond the referenced Role; (4) RoleBinding deletion never leaves an observable intermediate state under concurrent readers Primary reuse disposition: `adapt`. Preserved source-plan detail: extract and adapt.", "entryContracts" : [ "ADR-046-security-and-threat-model" ], @@ -19031,6 +19728,7 @@ ], "specId" : "ADR-046-security-and-threat-model", "topologicalRank" : 13, + "validation" : "Hermetic property test (`proptest`/`quickcheck`-style, minimum 10,000 cases per property)", "wave" : "W7" }, { @@ -19038,6 +19736,7 @@ "destinations" : [ "`packages/d2b-bus/fuzz/fuzz_targets/zonelink_frame.rs`, `packages/d2b-bus/tests/zonelink_structural_rejection.rs`" ], + "detailedDesign" : "Fuzz + property suite asserting that no mutation of a ZoneLink-bound frame (attachment count, credential-shaped byte runs, path-shaped strings, PID-shaped integers) is ever forwarded — every such mutation is rejected at serialization with `attachment-not-permitted-over-zone-link` or the transport-specific equivalent, never silently dropped or partially forwarded Primary reuse disposition: `adapt`. Preserved source-plan detail: extract and adapt (design copied from `ADR-046-zone-routing.md` structural-rejection sections).", "entryContracts" : [ "ADR-046-security-and-threat-model" ], @@ -19053,6 +19752,7 @@ ], "specId" : "ADR-046-security-and-threat-model", "topologicalRank" : 12, + "validation" : "Fuzz corpus (`cargo fuzz run zonelink_frame -- -runs=1000000`, zero crashes); container test passes in `make test-integration`", "wave" : "W7" }, { @@ -19060,6 +19760,7 @@ "destinations" : [ "`packages/xtask/src/effectport_boundary_check.rs`, wired into `make test-policy`" ], + "detailedDesign" : "For every crate under `packages/d2b-provider-*`, walk its `Cargo.toml` dependency graph and fail the build if it transitively depends on `d2b-priv-broker` or any crate exposing a raw broker client/DTO type; separately, grep-scan for direct syscalls forbidden per dossier (e.g. `socket(AF_VSOCK` in `transport-vsock`, `Command::new(\"systemctl\"` in `system-systemd`)", "entryContracts" : [ "ADR-046-security-and-threat-model" ], @@ -19073,6 +19774,7 @@ ], "specId" : "ADR-046-security-and-threat-model", "topologicalRank" : 12, + "validation" : "Hermetic (`cargo xtask effectport-boundary-check`); a negative test intentionally adds a forbidden dependency to a scratch crate and asserts the check fails", "wave" : "W7" }, { @@ -19080,6 +19782,7 @@ "destinations" : [ "`packages/d2b-provider-system-minijail/tests/launchticket_toctou.rs`" ], + "detailedDesign" : "Fault-injection test that issues a `LaunchTicket`, then mutates the referenced `CompiledSandboxPlan` digest (simulating a race between issue and exec) before the broker execs, and asserts the spawn fails closed rather than launching with the old plan; a companion test kills the broker mid-`clone3` and asserts no half-initialized process (missing cgroup placement, non-zero host capabilities) is ever observable by a concurrent reader", "entryContracts" : [ "ADR-046-security-and-threat-model" ], @@ -19095,6 +19798,7 @@ ], "specId" : "ADR-046-security-and-threat-model", "topologicalRank" : 13, + "validation" : "Hermetic fault-injection test plus `make test-host-integration` NixOS/KVM test; acceptance is zero observable non-zero-capability or missing-cgroup-placement windows across 10,000 injected-fault iterations", "wave" : "W7" }, { @@ -19102,6 +19806,7 @@ "destinations" : [ "`packages/d2b-contract-tests/tests/quarantine_not_kill_matrix.rs`" ], + "detailedDesign" : "One parameterized fault-injection matrix test, run once per adoption-capable Provider (`system-minijail`, `system-systemd`, `runtime-cloud-hypervisor`, `runtime-azure-container-apps`, `volume-local`), that restarts the controller with a deliberately ambiguous adoption candidate (duplicate InvocationID, mismatched marker inode, stale ACA operation handle) and asserts: (a) the resource transitions to `Degraded`/`Quarantined`, never `Deleted` or silently re-adopted; (b) no signal is sent to the ambiguous candidate process; (c) a `runtime-security-violation`-class audit record is emitted Primary reuse disposition: `adapt`. Preserved source-plan detail: extract and adapt.", "entryContracts" : [ "ADR-046-security-and-threat-model" ], @@ -19119,6 +19824,7 @@ ], "specId" : "ADR-046-security-and-threat-model", "topologicalRank" : 15, + "validation" : "Matrix covers all five Providers listed; acceptance is 100% pass across all five with no signal sent to the ambiguous candidate in any case", "wave" : "W7" }, { @@ -19126,6 +19832,7 @@ "destinations" : [ "`packages/d2b-provider-system-core/tests/no_isolation_propagation.rs`" ], + "detailedDesign" : "Integration test that creates a user-only `Host`, launches a Process on it, and asserts all three non-suppressible surfaces simultaneously: `status.isolationPosture == \"none\"` in JSON output; the unconditional stderr warning string is present with `--json` and non-JSON CLI invocation and cannot be suppressed by any combination of flags; the corresponding `ProcessEffect` audit record carries `no_isolation: true`; and a companion negative assertion that `no_isolation` never appears in any OTEL metric/span emitted during the same test run", "entryContracts" : [ "ADR-046-security-and-threat-model" ], @@ -19140,6 +19847,7 @@ ], "specId" : "ADR-046-security-and-threat-model", "topologicalRank" : 12, + "validation" : "Hermetic CLI/audit integration test; Nix eval test; acceptance is zero code paths reaching a live user-only-Host Process without all three surfaces firing", "wave" : "W7" }, { @@ -19147,6 +19855,7 @@ "destinations" : [ "`packages/d2b-provider-volume-local/tests/marker_tamper_fault_injection.rs`" ], + "detailedDesign" : "Fault-injection test that provisions a Volume, then out-of-band (as a simulated attacker with filesystem access) replaces the marker file, swaps the backing directory for a different inode on the same `st_dev`, and deletes the marker entirely — three separate scenarios — and asserts each transitions the Volume to `Failed` with `markerStatus: missing`/`replaced` respectively, never a silent re-provision, and that operator-only remediation is the only recovery path exercised", "entryContracts" : [ "ADR-046-security-and-threat-model" ], @@ -19161,6 +19870,7 @@ ], "specId" : "ADR-046-security-and-threat-model", "topologicalRank" : 12, + "validation" : "Hermetic + host/KVM fault-injection test; acceptance is 100% fail-closed across all three tamper scenarios", "wave" : "W7" }, { @@ -19168,6 +19878,7 @@ "destinations" : [ "`packages/d2b-contract-tests/tests/zero_secret_invariant.rs`" ], + "detailedDesign" : "Static + dynamic gate: (1) static — every DTO type reachable from a `Credential`-adjacent module must implement a hand-written redacted `Debug` and must not derive `Debug`, enforced by a `#[forbid(clippy::derive_debug_ambient)]`-style custom lint or an `xtask` AST scan; (2) dynamic — a property test that generates random `Credential` delivery sessions and asserts the delivered token/`SignChallenge` byte sequence never appears, byte-for-byte, in any captured audit record, OTEL span, log line, or resource-store row taken during the same test run Primary reuse disposition: `adapt`. Preserved source-plan detail: extract and adapt (design from `ADR-046-resources-credential.md` §1.1).", "entryContracts" : [ "ADR-046-security-and-threat-model" ], @@ -19183,6 +19894,7 @@ ], "specId" : "ADR-046-security-and-threat-model", "topologicalRank" : 12, + "validation" : "Hermetic; the dynamic test additionally runs as a canary-byte test (a unique random marker is embedded in the token and searched for across every observability surface)", "wave" : "W7" }, { @@ -19190,6 +19902,7 @@ "destinations" : [ "`packages/d2b-provider-{clipboard-wayland,shell-terminal,device-security-key,notification-desktop}/tests/stream_redaction.rs` (one per Provider, same shared test helper crate)" ], + "detailedDesign" : "Shared canary-byte test helper: each Provider's test injects a unique random marker into its sensitive content path (clipboard bytes, terminal output, CTAP payload, notification body) and asserts the marker never appears in audit, OTEL, Debug output, or CLI error text captured during the test", "entryContracts" : [ "ADR-046-security-and-threat-model" ], @@ -19204,6 +19917,7 @@ ], "specId" : "ADR-046-security-and-threat-model", "topologicalRank" : 14, + "validation" : "Hermetic canary-byte test per Provider (4 Providers, shared helper crate); container test for the two D-Bus/Wayland-mediated cases", "wave" : "W7" }, { @@ -19211,6 +19925,7 @@ "destinations" : [ "`packages/d2b-audit/tests/privileged_fail_closed.rs`" ], + "detailedDesign" : "Fault-injection test that makes the audit sink's fsync fail (simulated ENOSPC/EIO) during a privileged `ResourceMutation`/`RBACChange`/`StateReset` write, and asserts the originating operation itself fails with `audit-unavailable` rather than completing with a lost audit record; a companion test floods `Standard`/`Best-effort` records past `DEFAULT_AUDIT_WRITES_PER_SECOND` and asserts privileged records are never dropped or delayed by the resulting backpressure", "entryContracts" : [ "ADR-046-security-and-threat-model" ], @@ -19225,6 +19940,7 @@ ], "specId" : "ADR-046-security-and-threat-model", "topologicalRank" : 12, + "validation" : "Hermetic fault-injection test; acceptance is zero privileged-class operations that complete despite a failed durable audit write", "wave" : "W7" }, { @@ -19232,6 +19948,7 @@ "destinations" : [ "`packages/d2b-bus/tests/dos_ceiling_fault_injection.rs`" ], + "detailedDesign" : "Fault-injection/load test suite: (1) attachment-credit exhaustion at each of the six scopes (Packet/Request/Operation/Session/Process/Host), asserting typed rejection never a panic; (2) reconnect-storm exceeding `MAX_RECONNECT_ATTEMPTS`/`MAX_RECONNECT_WINDOW_MS`, asserting the session fails closed rather than looping; (3) ZoneLink hop-count/route-advertisement replay flood, asserting `hop-limit-exceeded`/`zone-advertisement-replay` rather than unbounded forwarding; (4) a stalled data stream under load, asserting control/cancellation traffic is never starved (priority-scheduling property) Primary reuse disposition: `adapt`. Preserved source-plan detail: copy and adapt.", "entryContracts" : [ "ADR-046-security-and-threat-model" ], @@ -19248,6 +19965,7 @@ ], "specId" : "ADR-046-security-and-threat-model", "topologicalRank" : 12, + "validation" : "Hermetic fault-injection suite; container load test; acceptance is zero panics/unbounded-growth across all four scenarios", "wave" : "W7" }, { @@ -19255,6 +19973,7 @@ "destinations" : [ "`packages/d2b/src/commands/{doctor,support_bundle}.rs`" ], + "detailedDesign" : "`d2b zone doctor` performs read-only status/audit-hash-chain checks with the redaction rules from §21 enforced on every field it prints; `d2b zone support-bundle` assembles a bounded archive of metadata+status (never spec bytes or `metadata.name`) and sets `bundle_completeness: \"partial\"` when any Provider in scope is quarantined, rather than omitting the gap silently Primary reuse disposition: `adapt`. Preserved source-plan detail: extract and adapt (design from `ADR-046-telemetry-audit-and-support.md`).", "entryContracts" : [ "ADR-046-security-and-threat-model" ], @@ -19270,6 +19989,7 @@ ], "specId" : "ADR-046-security-and-threat-model", "topologicalRank" : 13, + "validation" : "Hermetic CLI test asserting no spec byte or `metadata.name` appears in a generated bundle; container test for the quarantined-Provider case", "wave" : "W7" }, { @@ -19277,6 +19997,7 @@ "destinations" : [ "`packages/d2b-core-controller/src/reset.rs`, `packages/d2b-core-controller/tests/reset_atomicity.rs`" ], + "detailedDesign" : "Implements the `scope` (`zone`, `provider`, `host`, or `guest`) `StateReset` flow from §25: quiesce via `EmergencyPolicy`, revoke open Credential leases in scope, destroy Volumes in scope (key-shred first), commit the `StateReset` audit record durably, and only then report the reset complete. A crash-recovery path re-derives \"was this reset already committed?\" solely from the durable `StateReset` record, never from partial filesystem state Primary reuse disposition: `adapt`. Preserved source-plan detail: adapt (pattern only).", "entryContracts" : [ "ADR-046-security-and-threat-model" ], @@ -19291,6 +20012,7 @@ ], "specId" : "ADR-046-security-and-threat-model", "topologicalRank" : 12, + "validation" : "Hermetic state-machine test; host/KVM crash-injection test at all four phases; acceptance is zero non-atomic outcomes across all injected crash points", "wave" : "W7" }, { @@ -19298,6 +20020,7 @@ "destinations" : [ "`tests/unit/gates/security-matrix-coverage.sh`" ], + "detailedDesign" : "A drift-style gate that parses [Per-ResourceType threat matrix](#per-resourcetype-threat-matrix) and [Per-Provider-family threat matrix](#per-provider-family-threat-matrix), confirms every one of the 19 standard ResourceTypes and all 27 Provider dossiers under `docs/specs/providers/` has a row, and confirms every referenced dossier file actually contains a `## Security`-class section (by heading grep) — failing the gate if a new ResourceType/Provider is added without a corresponding row and dossier section Primary reuse disposition: `adapt`. Preserved source-plan detail: adapt (pattern from `tests/unit/gates/drift-check.sh`).", "entryContracts" : [ "ADR-046-security-and-threat-model" ], @@ -19311,6 +20034,7 @@ ], "specId" : "ADR-046-security-and-threat-model", "topologicalRank" : 12, + "validation" : "Hermetic shell-script gate; a negative test adds a scratch Provider dossier missing a Security section and asserts the gate fails", "wave" : "W7" }, { @@ -19318,6 +20042,7 @@ "destinations" : [ "`tests/integration/containers/malicious-child-zone.rs`" ], + "detailedDesign" : "Container-based penetration test running a real parent Zone and a deliberately malicious child Zone container that attempts, over a real ZoneLink: FD smuggling, credential-shaped byte injection, cross-Zone `ownerRef` forgery, capability-ceiling widening claims, and route-advertisement replay. Every attempt must be rejected by the parent with the specific typed error named in §10, and none may reach the parent's resource store, Credential state, or Host substrate Primary reuse disposition: `adapt`. Preserved source-plan detail: extract and adapt.", "entryContracts" : [ "ADR-046-security-and-threat-model" ], @@ -19332,6 +20057,7 @@ ], "specId" : "ADR-046-security-and-threat-model", "topologicalRank" : 12, + "validation" : "Container integration test; acceptance is zero successful attacks across all five attempted vectors", "wave" : "W7" }, { @@ -19339,6 +20065,7 @@ "destinations" : [ "`docs/reference/security-manual-validation-checklist.md` (new reference doc, out of scope for this spec's own file but named here as the required destination for the future implementation PR)" ], + "detailedDesign" : "A checklist covering the scenarios that cannot be hermetically or even container-tested: (1) real Azure Container Apps/Azure VM credential rotation and revocation under `AzureEffectPort`, confirming zeroization on a real managed-identity/Entra token; (2) real TPM 2.0 hardware NVRAM persistence/tamper-marker behavior across a real host reboot; (3) real USBIP/security-key hardware mutual-exclusion enforcement with a physical FIDO2 device; (4) real Azure Relay listener/sender credential acquisition and relay-identity-not-local-auth verification against a live relay namespace Primary reuse disposition: `adapt`. Preserved source-plan detail: adapt (checklist pattern from `SECURITY.md`'s existing portability-roadmap manual milestones and `tests/README.md`'s manual hardware tier).", "entryContracts" : [ "ADR-046-security-and-threat-model" ], @@ -19352,6 +20079,7 @@ ], "specId" : "ADR-046-security-and-threat-model", "topologicalRank" : 12, + "validation" : "Checklist sign-off recorded in the release's validation evidence, not a CI gate (matches `D2b_LIVE=1` manual-tier precedent in `AGENTS.md`)", "wave" : "W7" }, { @@ -19359,6 +20087,7 @@ "destinations" : [ "`packages/d2b-contract-tests/tests/minijail_process_ownership.rs`; `tests/host-integration/minijail-cgroup-kill.nix`" ], + "detailedDesign" : "Hermetic contract test proves only the broker that called `clone3` can produce the identity-bound `BrokerTerminalResult`; a non-parent poll-readable pidfd cannot be converted to status, while a verified duplicate holder can still request exact-main `pidfd_send_signal`. Host integration launches an owned descendant that calls `setsid(2)` plus an unrelated recycled-PGID decoy, performs graceful exact-main stop followed by anchored leaf `cgroup.kill`, and proves the owned leaf reaches `populated 0`, the broker reaps exactly once, the decoy survives, and rmdir/finalizer clearing wait for both proofs. Negative cases prove ambiguous adoption emits no signal/`cgroup.kill`, and Linux <5.14 or missing/unwritable `cgroup.kill` fails before spawn. Primary reuse disposition: `adapt`. Preserved source-plan detail: extract and adapt.", "entryContracts" : [ "ADR-046-security-and-threat-model" ], @@ -19376,6 +20105,7 @@ ], "specId" : "ADR-046-security-and-threat-model", "topologicalRank" : 15, + "validation" : "Parent-only wait/reap, poll-readability-not-status, duplicate-holder signaling, setsid/PGID-reuse resistance, quarantine no-kill, exact-once reap, and Linux ≥5.14 platform-gate assertions all pass", "wave" : "W7" }, { @@ -19383,6 +20113,7 @@ "destinations" : [ "Move to `packages/d2b-provider-device-security-key/src/session.rs` and `cid.rs`; adapt to Provider Process model (remove daemon Mutex wrapping, add async relay protocol)" ], + "detailedDesign" : "Extract the baseline lease/session constants and CID mapping into provider-local modules. Preserve `LeaseId` stale-release protection, cancel-all-active-CIDs, `CEREMONY_TIMEOUT`, `QUEUE_WAIT_TIMEOUT`, and bounded fair queue semantics; remove daemon-global `Mutex` ownership; keep the authority relay's DeviceGrant/OFD lease for its lifetime. Ceremony rows remain high-churn session records, never Resources. Primary reuse disposition: `adapt`. Preserved source-plan detail: extract and adapt.", "entryContracts" : [ "ADR-046-provider-device-security-key" ], @@ -19397,6 +20128,7 @@ ], "specId" : "ADR-046-provider-device-security-key", "topologicalRank" : 12, + "validation" : "`session_state_machine.rs`, `session_ring.rs`, `cancel_propagation.rs`, `session_timeout.rs`, `fair_queue.rs`, and `cid_isolation.rs` verify queue/active/completed/timeout transitions, ring eviction, LeaseId stale-release denial, cancel-all-CIDs, fair timeout, and per-session CID isolation with no daemon-global lease state or ceremony Resource.", "wave" : "W6" }, { @@ -19404,6 +20136,7 @@ "destinations" : [ "Move to `packages/d2b-provider-device-security-key/src/relay.rs`; replace daemon-internal Unix socket proxy with ComponentSession over the owned Service Endpoint" ], + "detailedDesign" : "Extract the CTAPHID ceremony relay behavior into the provider relay binary. Preserve one-ceremony-at-a-time proxy semantics and CTAPHID cancel handling, but replace daemon-internal Unix socket proxying with the `d2b.security-key.v3` ComponentSession over the owned CTAPHID Endpoint and named `ctaphid` stream. Primary reuse disposition: `adapt`. Preserved source-plan detail: extract and adapt.", "entryContracts" : [ "ADR-046-provider-device-security-key" ], @@ -19419,6 +20152,7 @@ ], "specId" : "ADR-046-provider-device-security-key", "topologicalRank" : 13, + "validation" : "`host_relay_guest_frontend/` integration fixture, `device_grant_no_path.rs`, `descriptor_validation.rs`, and `cancel_propagation.rs` prove relay fd injection, ComponentSession transport, cancel propagation, and absence of daemon-internal socket proxying.", "wave" : "W6" }, { @@ -19426,6 +20160,7 @@ "destinations" : [ "Adopt `main.rs` and `uhid.rs` as the v3 Process binary entry point; replace `framing.rs`/`vsock.rs` with ComponentSession client from `d2b-session-unix/src/vsock.rs`; wire as Process service in Provider crate" ], + "detailedDesign" : "Retain UHID creation and frontend entry behavior, but run it as a Binding-owned v3 user-domain Process receiving a pre-opened `/dev/uhid` fd from the `Provider/system-core` Guest-substrate DeviceGrant. Delete raw frame/vsock protocol and use the ComponentSession client/named `ctaphid` stream. No virtual/projected Device exists. Primary reuse disposition: `adapt`. Preserved source-plan detail: extract and adapt.", "entryContracts" : [ "ADR-046-provider-device-security-key" ], @@ -19440,6 +20175,7 @@ ], "specId" : "ADR-046-provider-device-security-key", "topologicalRank" : 12, + "validation" : "`host_relay_guest_frontend/`, `device_grant_no_path.rs`, `descriptor_validation.rs`, and guest Nix migration tests prove UHID fd injection, no `/dev/uhid` path, ComponentSession client use, and no raw `framing.rs`/`vsock.rs` protocol.", "wave" : "W6" }, { @@ -19447,6 +20183,7 @@ "destinations" : [ "Preserve revalidation logic; update `SecurityKeyOpenDevice` to use bundle device table `device_token` as sole open target (no iterative sysfs scan); add zone-field handling; remove sysfs fallback. **Core's LaunchTicket calls this internally; the Provider does not call it.**" ], + "detailedDesign" : "Keep the FIDO usage-page and post-open revalidation logic, but make the trusted private bundle `device_token` the only open target. Add Zone-aware request handling, reject path/sysfs fallback inputs, and keep the operation internal to Core LaunchTicket DeviceGrant resolution rather than callable by the Provider controller.", "entryContracts" : [ "ADR-046-provider-device-security-key" ], @@ -19461,6 +20198,7 @@ ], "specId" : "ADR-046-provider-device-security-key", "topologicalRank" : 14, + "validation" : "`packages/d2b-priv-broker/tests/security_key_broker.rs` updates for bundle table lookup and zone-field round trip; `device_grant_no_path.rs` proves Provider code does not call the broker and sees no device path; audit tests prove path-free grant records.", "wave" : "W6" }, { @@ -19468,6 +20206,7 @@ "destinations" : [ "Adapt to v3 Zone/ResourceRef identifiers; preserve serde shapes for zero downstream breakage where possible; remove `SecurityKeyApplyUdevRulesRequest` (ADR046-security-key-035)" ], + "detailedDesign" : "Rebase wire DTOs onto v3 Zone/ResourceRef identifiers; consume the shared ADR046-provider-004 `security-key.d2bus.org` Service/Binding bases and define only strict `device-security-key.d2bus.org` Provider-extension DTOs; reject `spec.provider` on Core projections; place authority/import/attachment semantic observations only under `status.resource` and implementation observations only under `status.provider`; preserve opaque bounded ceremony records as non-Resource DTOs; add `zone` to `SecurityKeyOpenDeviceRequest`; drop the udev-rules request because UHID comes from the Guest-substrate DeviceGrant. No provider-named ResourceType alias is admitted.", "entryContracts" : [ "ADR-046-provider-device-security-key" ], @@ -19483,6 +20222,7 @@ ], "specId" : "ADR-046-provider-device-security-key", "topologicalRank" : 12, + "validation" : "DTO serde round trips, exact provider-neutral ResourceType identity, provider-named alias rejection, canonical minimal base acceptance, Core projection `spec.provider` rejection, D088 `status.resource`/`status.provider` layering, base/Provider-extension field separation, unknown-field denial, zone-field round trip, path-redaction tests, and updated `usb_sk_contract.rs` assertions in the provider crate.", "wave" : "W6" }, { @@ -19490,6 +20230,7 @@ "destinations" : [ "Move to `packages/d2b-provider-device-security-key/tests/`; update imports and v3 type names" ], + "detailedDesign" : "Move the reusable semantic assertions for security-key DTO serde, unknown-field denial, and broker capability shape into the provider crate's hermetic `tests/` suite, updating imports and names to the v3 contract modules without weakening assertions. Primary reuse disposition: `adapt`. Preserved source-plan detail: move and adapt.", "entryContracts" : [ "ADR-046-provider-device-security-key" ], @@ -19505,6 +20246,7 @@ ], "specId" : "ADR-046-provider-device-security-key", "topologicalRank" : 13, + "validation" : "Moved tests pass under the provider crate; contract assertions are retained; D094 disposition records moved/adapted coverage before old duplicate tests are deleted.", "wave" : "W6" }, { @@ -19512,6 +20254,7 @@ "destinations" : [ "Move to `packages/d2b-provider-device-security-key/tests/`; update for v3 Process resource minijail profile; retain zero-capabilities assertion" ], + "detailedDesign" : "Move the reusable minijail/sandbox assertions into the provider crate and retarget them from `ProcessRole::SecurityKeyFrontend` to the v3 Process resource templates and relay/controller minijail profiles. Preserve zero-capabilities and seccomp-class assertions while recognizing the frontend uses `Provider/system-systemd` hardening rather than a minijail profile. Primary reuse disposition: `adapt`. Preserved source-plan detail: move and adapt.", "entryContracts" : [ "ADR-046-provider-device-security-key" ], @@ -19526,6 +20269,7 @@ ], "specId" : "ADR-046-provider-device-security-key", "topologicalRank" : 12, + "validation" : "Provider-crate tests retain zero-capability and seccomp assertions for relay/controller and assert no minijail profile is used for the frontend Process.", "wave" : "W6" }, { @@ -19533,6 +20277,7 @@ "destinations" : [ "New crate `packages/d2b-provider-device-security-key/` with `src/`, `tests/`, `integration/`, `README.md` (workspace policy requires all four)" ], + "detailedDesign" : "New crate `packages/d2b-provider-device-security-key/` with `src/`, `tests/`, `integration/`, `README.md` (workspace policy requires all four)", "entryContracts" : [ "ADR-046-provider-device-security-key" ], @@ -19546,6 +20291,7 @@ ], "specId" : "ADR-046-provider-device-security-key", "topologicalRank" : 11, + "validation" : "Workspace package-policy check rejects missing `src/`, `tests/`, `integration/`, or `README.md`; `cargo test -p d2b-provider-device-security-key --lib --tests` discovers the hermetic suite; README acceptance criteria from the provider crate standard layout are satisfied.", "wave" : "W6" }, { @@ -19553,6 +20299,7 @@ "destinations" : [ "`packages/d2b-provider-device-security-key/src/controller.rs`" ], + "detailedDesign" : "One controller implements standard reconcile for local physical Devices, authority/projection SecurityKeyServices, and SecurityKeyBindings. It observes Devices; realizes an authority Service as relay Process/Service-owned Endpoint; accepts projection Services only from Core/import after signed-factory admission; realizes each Binding as frontend Process/private Endpoint; enforces child-first finalizers and never creates an import or Device projection. Export/Import routing never treats an Endpoint as exported identity.", "entryContracts" : [ "ADR-046-provider-device-security-key" ], @@ -19571,6 +20318,7 @@ ], "specId" : "ADR-046-provider-device-security-key", "topologicalRank" : 15, + "validation" : "`controller_reconcile.rs`, `service_binding_projection.rs`, `mutual_exclusion.rs`, `status_binding.rs`, and deletion/finalizer tests cover authority/projection/Binding branches, signed-factory admission before projection reconcile, Service-owned Endpoint isolation from Export/Import identity, no Device projection, and no Volume API calls.", "wave" : "W6" }, { @@ -19578,6 +20326,7 @@ "destinations" : [ "`packages/d2b-provider-device-security-key/src/relay.rs`" ], + "detailedDesign" : "Authority Service-owned relay entry point: bounded authenticated Binding connections, one LeaseId-guarded fair ceremony queue, CID translation/cancel-all-CIDs, hidraw fd from Core DeviceGrant, CTAPHID named stream, and internal relay-control channel.", "entryContracts" : [ "ADR-046-provider-device-security-key" ], @@ -19598,6 +20347,7 @@ ], "specId" : "ADR-046-provider-device-security-key", "topologicalRank" : 16, + "validation" : "`host_relay_guest_frontend/`, `fair_queue.rs`, `device_grant_no_path.rs`, `descriptor_validation.rs`, `cancel_propagation.rs`, and `cid_isolation.rs` prove one authority open, multi-Binding fair serialization, fd-only access, LeaseId cancel, and CID isolation.", "wave" : "W6" }, { @@ -19605,6 +20355,7 @@ "destinations" : [ "`packages/d2b-provider-device-security-key/src/session.rs`" ], + "detailedDesign" : "`SessionStateMachine` with Idle/Queued/Active/Completed/TimedOut, bounded FIFO queue, monotonic LeaseId stale-release guard, per-Binding session ring, timeout/cancel, and ring eviction. Ceremony rows are non-Resource records; DeviceGrant remains held for relay lifetime.", "entryContracts" : [ "ADR-046-provider-device-security-key" ], @@ -19620,6 +20371,7 @@ ], "specId" : "ADR-046-provider-device-security-key", "topologicalRank" : 13, + "validation" : "`session_state_machine.rs`, `session_ring.rs`, `fair_queue.rs`, `session_timeout.rs`, and `cancel_propagation.rs` cover queue fairness, eviction, stale LeaseId rejection, timeout, and cancel.", "wave" : "W6" }, { @@ -19627,6 +20379,7 @@ "destinations" : [ "`packages/d2b-provider-device-security-key/src/cid.rs`" ], + "detailedDesign" : "CID translator: per-active-ceremony u32→u64 host-CID allocation, bimap, cancel-all-active-CIDs, and eviction on ceremony end", "entryContracts" : [ "ADR-046-provider-device-security-key" ], @@ -19642,6 +20395,7 @@ ], "specId" : "ADR-046-provider-device-security-key", "topologicalRank" : 13, + "validation" : "`cid_isolation.rs` verifies per-session allocation, round trip, no sharing across relays, and eviction on session end.", "wave" : "W6" }, { @@ -19649,6 +20403,7 @@ "destinations" : [ "`packages/d2b-provider-device-security-key/src/probe.rs`; Provider activation/Core private bundle device table population for label → `device_token`" ], + "detailedDesign" : "hidraw probe: `probe.rs` — calls `SecurityKeyEffectPort::observe_inventory(&device_id, &policy_id)` with opaque types injected by Core; interprets `InventoryObservation`; never reads `/sys/class/hidraw/` directly; bundle device table population at activation time (Provider activation resolves label → `device_token` via Core; stored in private bundle)", "entryContracts" : [ "ADR-046-provider-device-security-key" ], @@ -19663,6 +20418,7 @@ ], "specId" : "ADR-046-provider-device-security-key", "topologicalRank" : 13, + "validation" : "`controller_reconcile.rs` scheduled-observe tests, `descriptor_validation.rs` Debug-redaction capture, and path-safety tests prove Provider never reads sysfs and receives only opaque observations.", "wave" : "W6" }, { @@ -19670,6 +20426,7 @@ "destinations" : [ "`packages/d2b-provider-device-security-key/src/descriptor.rs`" ], + "detailedDesign" : "Declare relay↔controller service and relay↔Binding-frontend `d2b.security-key.v3` fingerprints, Noise profiles, canonical Service/Binding subject pairing, bounded encrypted-stream records, and descriptor validation; no ambient path or raw vsock CID.", "entryContracts" : [ "ADR-046-provider-device-security-key" ], @@ -19685,6 +20442,7 @@ ], "specId" : "ADR-046-provider-device-security-key", "topologicalRank" : 13, + "validation" : "`descriptor_validation.rs` covers wrong service, wrong descriptor digest, wrong SO_PEERCRED uid, unenrolled key, oversized records, no ambient path, and redacted opaque IDs.", "wave" : "W6" }, { @@ -19692,6 +20450,7 @@ "destinations" : [ "`nixos-modules/minijail-profiles.nix` entries for relay and controller; provider descriptor sandbox templates for relay/controller/frontend" ], + "detailedDesign" : "Minijail profiles for relay and controller only; frontend uses `Provider/system-systemd` hardening directives compiled from `SandboxSpec` (no minijail profile for frontend). Add relay and controller entries to `nixos-modules/minijail-profiles.nix`; `capabilityClasses: []`; `seccompClass: sk-relay` and `seccompClass: sk-controller`", "entryContracts" : [ "ADR-046-provider-device-security-key" ], @@ -19707,6 +20466,7 @@ ], "specId" : "ADR-046-provider-device-security-key", "topologicalRank" : 13, + "validation" : "`minijail_sk_frontend` successor tests, sandbox template tests, and zero-capability/seccomp assertions cover relay/controller minijail profiles and no frontend minijail profile.", "wave" : "W6" }, { @@ -19714,6 +20474,7 @@ "destinations" : [ "Provider descriptor Process templates and owned CTAPHID `Endpoint` template for `Provider/device-security-key`" ], + "detailedDesign" : "Templates: Provider controller; authority Service-owned relay Process/relay Endpoint; Binding-owned frontend Process/private Endpoint; projection Service local Endpoint. Frontend requires Guest/User and the system-core UHID DeviceGrant; no virtual Device template exists.", "entryContracts" : [ "ADR-046-provider-device-security-key" ], @@ -19730,6 +20491,7 @@ ], "specId" : "ADR-046-provider-device-security-key", "topologicalRank" : 14, + "validation" : "`controller_reconcile.rs`, Process template golden tests, Endpoint resource tests, and frontend `userRef` admission tests prove templates and Endpoint shape.", "wave" : "W6" }, { @@ -19737,6 +20499,7 @@ "destinations" : [ "Signed Provider descriptor JSON for `Provider/device-security-key` in the provider package" ], + "detailedDesign" : "Signed descriptor: config; physical Device integration; implementation claim for the provider-neutral `security-key.d2bus.org` Service/Binding base schemas/fingerprints; strict `device-security-key.d2bus.org` spec/status extensions; authority/projection union and D097 descriptor; a D096 projection factory with exact `serviceType`, `projectionSchemaFingerprint`, and semantic `factoryFingerprint`; controller/relay/frontend/Endpoint templates; export/import adapter capability; ComponentSession services; empty ProviderStateSet; permission claims. Provider/adapter identity is signed separately and Service-owned Endpoints are not factory or Export fields.", "entryContracts" : [ "ADR-046-provider-device-security-key" ], @@ -19756,6 +20519,7 @@ ], "specId" : "ADR-046-provider-device-security-key", "topologicalRank" : 15, + "validation" : "Descriptor schema validation, semantic-base versus Provider-extension fingerprints, exact projection-schema/factory fingerprint derivation and stability under Provider/adapter identity changes, exact type/no-alias tests, service inventory tests, permission claim tests, empty ProviderStateSet tests, and README/provider package conformance checks.", "wave" : "W6" }, { @@ -19763,6 +20527,7 @@ "destinations" : [ "v3 `SecurityKeyOpenDevice` broker op and Core LaunchTicket DeviceGrant resolution path" ], + "detailedDesign" : "v3 `SecurityKeyOpenDevice` broker op update: add `zone` field; implement bundle device table `device_token` lookup as sole open path; remove iterative sysfs scan from broker; add post-open revalidation steps (fstat, HIDIOCGRAWINFO, HIDIOCGRDESC). This is an internal Core operation called by LaunchTicket; the Provider controller does not call it.", "entryContracts" : [ "ADR-046-provider-device-security-key" ], @@ -19779,6 +20544,7 @@ ], "specId" : "ADR-046-provider-device-security-key", "topologicalRank" : 15, + "validation" : "Broker unit tests for zone field and token lookup, path-rejection tests, post-open revalidation tests, and provider tests proving no Provider broker call or sysfs path.", "wave" : "W6" }, { @@ -19786,6 +20552,7 @@ "destinations" : [ "`nixos-modules/` resource compiler/eval assertions for physical Device, authority Service, ResourceExport/Import, and consumer Binding" ], + "detailedDesign" : "Compile the owner Device→Service→export and consumer import→projection-Service→Binding shape. Emit Export `resourceRef`, `serviceType`, `projectionSchemaFingerprint`, and `factoryFingerprint`, and matching Import `expectedServiceType`, `expectedProjectionSchemaFingerprint`, and `expectedFactoryFingerprint`; the Import `exportKey` identifies the ResourceExport. Reject Export Endpoint/custom-key fields, authored projections, projection `spec.provider`, Device export/projection, cross-Zone refs, duplicate authorities/Bindings, paths, and any security-key/USB configuration that does not collide through the exact Core-derived `(Host, physical-usb-backing, opaqueKeyDigest)` tuple after trusted identity resolution.", "entryContracts" : [ "ADR-046-provider-device-security-key" ], @@ -19802,6 +20569,7 @@ ], "specId" : "ADR-046-provider-device-security-key", "topologicalRank" : 16, + "validation" : "Nix eval tests for label resolution, `busClass=hidraw`, exclusive arbitration, exact canonical Export/Import field emission and fingerprint matching, rejection of obsolete Export `endpointRef`/`exportedType`/`baseSchemaFingerprint`/`exportKey` and Import `expectedType`/`expectedBaseSchemaFingerprint`/`projectionType`, Core-only projection without `spec.provider`, byte-identical USB/security-key physical backing tuple collision, Provider-private-class bypass rejection, prohibited fields, and providerRef resolution.", "wave" : "W6" }, { @@ -19809,6 +20577,7 @@ "destinations" : [ "`nixos-modules/components/security-key-guest.nix` migration gate `d2b.securityKey._legacySystemdUnit`" ], + "detailedDesign" : "Guest Nix module migration gate: `d2b.securityKey._legacySystemdUnit` option, defaulting to false when Provider is installed; remove `d2b-sk-frontend.service` unit", "entryContracts" : [ "ADR-046-provider-device-security-key" ], @@ -19825,6 +20594,7 @@ ], "specId" : "ADR-046-provider-device-security-key", "topologicalRank" : 15, + "validation" : "Nix eval tests show the legacy unit is absent by default with Provider installed, can be gated only during transition if required, and `uhid` module/binary wiring remains present.", "wave" : "W6" }, { @@ -19832,6 +20602,7 @@ "destinations" : [ "Core `device-grant` audit and Provider controller Service/Binding ceremony lifecycle audit" ], + "detailedDesign" : "Path-free authority-grant records from Core and bounded Service/Binding/session digests/outcomes from controller; no path, raw target identity, LeaseId, session content, or CTAP bytes.", "entryContracts" : [ "ADR-046-provider-device-security-key" ], @@ -19847,6 +20618,7 @@ ], "specId" : "ADR-046-provider-device-security-key", "topologicalRank" : 16, + "validation" : "Audit tests assert path-free fields, bounded digests, no guest name/session content/CTAP bytes, grant emitted by Core not Provider controller, and lifecycle emitted by controller.", "wave" : "W6" }, { @@ -19854,6 +20626,7 @@ "destinations" : [ "Provider/controller bounded telemetry emitter and observability-otel handoff for security-key metrics" ], + "detailedDesign" : "OTEL metrics: `d2b_device_sk_session_total`, `d2b_device_sk_ceremony_duration_seconds`, `d2b_device_sk_relay_restarts_total` via bounded emitter ring; descriptors use only closed semantic labels and never Zone/resource-name-derived identity, while `d2b.zone` and `d2b.provider` remain OTEL resource attributes", "entryContracts" : [ "ADR-046-provider-device-security-key" ], @@ -19869,6 +20642,7 @@ ], "specId" : "ADR-046-provider-device-security-key", "topologicalRank" : 17, + "validation" : "Metrics tests structurally assert closed label sets, exact absence of `vm`, `zone`, `zone_id`, `zone_uid`, and resource-name-derived keys, Device/Zone-name canary absence, retained `d2b.zone` resource attributes, bounded ring behavior, and correct session/ceremony/restart counters.", "wave" : "W6" }, { @@ -19876,6 +20650,7 @@ "destinations" : [ "`packages/d2b-provider-device-security-key/README.md`" ], + "detailedDesign" : "README: initial Provider identity, provider-neutral Service/Binding catalog, strict Provider-extension fields, physical Device, owner/export/import/projection/Binding chain, process ownership, RBAC, invariants, status/telemetry, no-alias rule, and commands", "entryContracts" : [ "ADR-046-provider-device-security-key" ], @@ -19910,6 +20685,7 @@ ], "specId" : "ADR-046-provider-device-security-key", "topologicalRank" : 19, + "validation" : "README presence check from provider crate standard layout; documentation review verifies every listed section and command is present and matches the crate/package behavior.", "wave" : "W6" }, { @@ -19917,6 +20693,7 @@ "destinations" : [ "Authority/projection Service Endpoint and Binding private Endpoint resolution, including transport-vsock and ZoneLink encrypted streams" ], + "detailedDesign" : "Resolve each Binding only through its same-Zone Service Endpoint; enroll Noise KK for Service/Binding frontend; authority uses transport-vsock locally, projection uses per-import bounded encrypted stream with credits/backpressure/generation/deadline/cancel.", "entryContracts" : [ "ADR-046-provider-device-security-key" ], @@ -19935,6 +20712,7 @@ ], "specId" : "ADR-046-provider-device-security-key", "topologicalRank" : 17, + "validation" : "`host_relay_guest_frontend/` and `descriptor_validation.rs` verify Endpoint resolution, Noise KK enrollment, attachment opacity, and no raw vsock CID/port in status/spec.", "wave" : "W6" }, { @@ -19942,6 +20720,7 @@ "destinations" : [ "`d2b-contracts` neutral `SecurityKeyEffectPort` trait/types; `packages/d2b-provider-device-security-key/src/effect_port.rs` re-export; Core adapter implementation in `d2b-provider` or `d2b-provider-toolkit`" ], + "detailedDesign" : "Define/re-export the opaque redacting `SecurityKeyEffectPort` types in the neutral contract crate and implement the Core adapter; inject per physical Device into the Provider controller; relay and projection Service do not receive the port.", "entryContracts" : [ "ADR-046-provider-device-security-key" ], @@ -19956,6 +20735,7 @@ ], "specId" : "ADR-046-provider-device-security-key", "topologicalRank" : 12, + "validation" : "Unit tests assert Debug redaction, controller calls `observe_inventory` with injected IDs, relay has no port dependency, and fake Core adapter returns bounded `InventoryObservation`.", "wave" : "W6" }, { @@ -19963,6 +20743,7 @@ "destinations" : [ "`packages/d2b-provider-device-security-key/src/{resource_type,provider_extension,admission}.rs`; controller contracts; system-core Guest UHID authority-subresource DeviceGrant (common base lives under ADR046-provider-004)" ], + "detailedDesign" : "Bind the shared semantic authority/projection Service and Binding base versions/fingerprints from ADR046-provider-004, then define only the initial strict Provider extension and admission. The owner/Binding extension references the local physical Device/relay Endpoint and owns CTAPHID/fairness/frontend settings and observations. Projection is Core-owned by ResourceImport with `providerRef` plus semantic base/import fields, no `spec.provider`, and no Device/open; routing derives from the signed local descriptor, `providerRef`, and import record. Export admission binds the authority Service's `resourceRef` and `serviceType` to the signed projection-schema and factory fingerprints, never to its Endpoint. Binding is operator intent and the initial extension realizes its frontend Process/private Endpoint. Standard Device remains physical only; provider-named ResourceType aliases are rejected.", "entryContracts" : [ "ADR-046-provider-device-security-key" ], @@ -19979,6 +20760,7 @@ ], "specId" : "ADR-046-provider-device-security-key", "topologicalRank" : 13, + "validation" : "Fast schema/lifecycle conformance consumes the ADR046-provider-004 fixtures, accepts canonical minimal base without `spec.provider`, includes a fake alternate security-key Provider, and proves Device→provider-neutral Service→export→import→projection Service→provider-neutral Binding→frontend, exact canonical Export/Import fields, no Endpoint export, projection `spec.provider` rejection, D088 status layering, strict base/Provider-extension separation, exact types with no aliases, strict ownership/finalizers, no Device projection, and no local hidraw open in consumer Zone.", "wave" : "W6" }, { @@ -19986,6 +20768,7 @@ "destinations" : [ "Provider descriptor state declaration, controller/status logic, Process templates, and Nix principal provisioning for `Provider/device-security-key`" ], + "detailedDesign" : "Empty ProviderStateSet and strict bounded status schemas: physical presence in Device `status.resource`; semantic authority/import aggregates in Service `status.resource`; attachment aggregates in Binding `status.resource`; initial physical-backing claim, relay, Endpoint, queue, and ceremony observations only in `status.provider`. No semantic field appears directly under `status`, and Core projections contain no `spec.provider`. Ceremony rows remain high-churn non-Resource session records; CTAP/fd/LeaseId/CID data stays transient. No Process has `/state`.", "entryContracts" : [ "ADR-046-provider-device-security-key" ], @@ -20001,6 +20784,7 @@ ], "specId" : "ADR-046-provider-device-security-key", "topologicalRank" : 14, + "validation" : "`status_binding.rs` proves empty ProviderStateSet, no `/state` mounts, no Volume API calls, authority/import/attachment fields only under `status.resource`, implementation fields only under `status.provider`, no projection `spec.provider`, and no CTAP/fd/session secrets in status/log/audit/metrics.", "wave" : "W6" }, { @@ -20008,6 +20792,7 @@ "destinations" : [ "`packages/d2b-provider-device-security-key/src/share_adapter.rs`" ], + "detailedDesign" : "Signed adapters admit ResourceExport only when `resourceRef` names an authority SecurityKeyService, `serviceType` is `security-key.d2bus.org.SecurityKeyService`, and `projectionSchemaFingerprint` plus `factoryFingerprint` match the signed semantic factory. ResourceImport must supply the corresponding `expectedServiceType`, `expectedProjectionSchemaFingerprint`, and `expectedFactoryFingerprint`; its `exportKey` identifies the ResourceExport. The Service's relay Endpoint stays a Service-owned implementation child and is never an Export field. Core invokes the factory to create one projection SecurityKeyService with `ownerRef: ResourceImport/`, `providerRef`, semantic base/import fields, and no `spec.provider`; route selection comes from the signed local descriptor and ResourceImport record. The semantic factory fingerprint binds factory metadata plus projection-protocol version only, while adapter identity is authenticated separately by the signed Provider descriptor. They never project Device or auto-create Binding. Route Binding ceremonies over bounded encrypted named streams to the single authority fair queue; no FD/USBIP/hidraw/ref crosses Zones. Primary reuse disposition: `adapt`. Preserved source-plan detail: net-new (implement the signed security-key export/import adapter).", "entryContracts" : [ "ADR-046-provider-device-security-key" ], @@ -20026,6 +20811,7 @@ ], "specId" : "ADR-046-provider-device-security-key", "topologicalRank" : 18, + "validation" : "Fast fake-stream conformance proves owner Service→export→import→projection Service→Binding→frontend; exact canonical Export/Import type and fingerprint fields; rejection of Export `endpointRef`, `exportedType`, `baseSchemaFingerprint`, and `exportKey` plus Import `expectedType`, `expectedBaseSchemaFingerprint`, and `projectionType`; rejection of projection `spec.provider`; semantic factory-fingerprint stability when signed adapter identity changes; separate signed-descriptor identity authentication; one fair LeaseId-guarded ceremony; ciphertext to intermediaries; no Device projection/local hidraw/FD/USBIP; revocation degradation; and audit metadata only.", "wave" : "W6" }, { @@ -20033,6 +20819,7 @@ "destinations" : [ "`packages/d2b-provider-device-security-key/src/{authority,relay,streams}.rs`; D097 `AuthorityDescriptor` on authority SecurityKeyService" ], + "detailedDesign" : "The provider-neutral authority Service, not Device/Endpoint/Process, is the stable D097 owner and carries the semantic opaque Host-scoped zero-or-one descriptor. The initial Provider extension references the local physical Device and relay Endpoint and supplies service-specific physical-key derivation, Service+relay ownerProof, and bounded-fairness details. After trusted USB identity resolution, Core additionally derives `physical-usb-backing/v1` and atomically claims the exact `(Host, physical-usb-backing, opaqueKeyDigest)` tuple used by every USB Provider before any open, withhold, bind, module, relay, or attachment effect; Provider-private claims cannot replace it. Preserve sole Core open with double-fstat/FIDO/HID validation, async fd I/O, per-session CidTranslator, LeaseId stale-release guard, cancel-all-CIDs, one ceremony, bounded FIFO wait, and Binding-owned UHID frontend. Ceremony rows are not Resources. Primary reuse disposition: `adapt`. Preserved source-plan detail: `adapt` — relay becomes the D097 hidraw authority; transport moves to Endpoint/named-stream.", "entryContracts" : [ "ADR-046-provider-device-security-key" ], @@ -20053,6 +20840,7 @@ ], "specId" : "ADR-046-provider-device-security-key", "topologicalRank" : 16, + "validation" : "Fast hermetic tests adapt the existing `CidTranslator`/lease/cancel/UHID/broker-revalidation suites: CID alloc/translate/release, `LeaseId` stale-release, cancel-all-CIDs on disconnect, one-ceremony + 120 s timeout, 15 s fair-wait `ERR_CHANNEL_BUSY`, UHID frame round-trip, broker double-`fstat`+FIDO+HID revalidation, byte-identical USB/security-key backing tuple derivation for one fake token, and `physical-usb-backing-conflict` before effects under alternate labels/private authority classes — all with fakes/`FakeEffectPort`, no real hidraw. Integration proves cross-Zone CTAP ceremony **serialization** over the encrypted named stream and the shared physical USB collision.", "wave" : "W6" }, { @@ -20060,6 +20848,7 @@ "destinations" : [ "Removed from daemon; successor behavior lives in `packages/d2b-provider-device-security-key/src/relay.rs`, `session.rs`, and `cid.rs`" ], + "detailedDesign" : "Remove target `packages/d2bd/src/security_key.rs` — `start_sk_accept_loop`, `SecurityKeyState`, `LeaseState`, `SkRegistry` after v3 relay Process is live and stable; keep behind feature gate only if needed during transition. Primary reuse disposition: `delete-after-cutover`. Preserved source-plan detail: delete.", "entryContracts" : [ "ADR-046-provider-device-security-key" ], @@ -20078,6 +20867,7 @@ ], "specId" : "ADR-046-provider-device-security-key", "topologicalRank" : 17, + "validation" : "Provider relay/session tests pass; daemon build has no references to removed symbols; no legacy security-key accept loop starts under d2bd.", "wave" : "W6" }, { @@ -20085,6 +20875,7 @@ "destinations" : [ "Removed from daemon startup; successor launch path is ProviderDeployment/controller-created relay Process plus Endpoint/ComponentSession transport" ], + "detailedDesign" : "Remove target `packages/d2bd/src/lib.rs` — `start_sk_accept_loop` call site and daemon-internal Unix socket proxy bind after ADR046-security-key-030. Primary reuse disposition: `delete-after-cutover`. Preserved source-plan detail: delete.", "entryContracts" : [ "ADR-046-provider-device-security-key" ], @@ -20099,6 +20890,7 @@ ], "specId" : "ADR-046-provider-device-security-key", "topologicalRank" : 18, + "validation" : "d2bd startup tests/build prove no `start_sk_accept_loop` call or security-key proxy bind remains; provider integration test proves CTAPHID flow through Endpoint/ComponentSession.", "wave" : "W6" }, { @@ -20106,6 +20898,7 @@ "destinations" : [ "Removed from guest Nix module; successor is Binding-owned `Process/binding--sk-frontend`" ], + "detailedDesign" : "Remove target `nixos-modules/components/security-key-guest.nix` — `d2b-sk-frontend.service` systemd unit declaration after ADR046-security-key-020 migration gate defaults to false. Primary reuse disposition: `delete-after-cutover`. Preserved source-plan detail: delete.", "entryContracts" : [ "ADR-046-provider-device-security-key" ], @@ -20122,6 +20915,7 @@ ], "specId" : "ADR-046-provider-device-security-key", "topologicalRank" : 16, + "validation" : "Nix eval tests prove no static `d2b-sk-frontend.service` is emitted with Provider installed; frontend Process integration proves replacement lifecycle.", "wave" : "W6" }, { @@ -20129,6 +20923,7 @@ "destinations" : [ "Removed from `packages/d2b-contract-tests/tests/`; successor tests live in `packages/d2b-provider-device-security-key/tests/`" ], + "detailedDesign" : "Remove target `packages/d2b-contract-tests/tests/minijail_sk_frontend.rs` and `packages/d2b-contract-tests/tests/usb_sk_contract.rs` after ADR046-security-key-006/ADR046-security-key-007 tests are in Provider crate and cover all prior assertions. Primary reuse disposition: `delete-after-cutover`. Preserved source-plan detail: delete after move/adapt.", "entryContracts" : [ "ADR-046-provider-device-security-key" ], @@ -20144,6 +20939,7 @@ ], "specId" : "ADR-046-provider-device-security-key", "topologicalRank" : 14, + "validation" : "Provider-crate tests pass with retained assertions; old contract-test paths are absent from manifests/CI; no duplicate old/new suite runs indefinitely.", "wave" : "W6" }, { @@ -20151,6 +20947,7 @@ "destinations" : [ "Removed from `d2b-core/src/processes.rs`; successor frontend is a v3 Process resource owned by `Provider/device-security-key`" ], + "detailedDesign" : "Remove target `ProcessRole::SecurityKeyFrontend` in `d2b-core/src/processes.rs` after relay and frontend are v3 Process resources; no other code reference expected. Primary reuse disposition: `delete-after-cutover`. Preserved source-plan detail: delete.", "entryContracts" : [ "ADR-046-provider-device-security-key" ], @@ -20166,6 +20963,7 @@ ], "specId" : "ADR-046-provider-device-security-key", "topologicalRank" : 15, + "validation" : "Workspace build proves no `ProcessRole::SecurityKeyFrontend` references; provider Process template tests prove the v3 replacement; process conformance passes.", "wave" : "W6" }, { @@ -20173,6 +20971,7 @@ "destinations" : [ "Removed from contracts and broker; successor access is static guest Nix `uhid` module plus Core pre-opened `/dev/uhid` DeviceGrant for the frontend Process" ], + "detailedDesign" : "Remove `SecurityKeyApplyUdevRules`, its DTO, and related broker code after the Binding-owned frontend and system-core UHID DeviceGrant are live. Guest Nix loads `uhid` but emits no security-key udev rule. Primary reuse disposition: `delete-after-cutover`. Preserved source-plan detail: delete.", "entryContracts" : [ "ADR-046-provider-device-security-key" ], @@ -20190,6 +20989,7 @@ ], "specId" : "ADR-046-provider-device-security-key", "topologicalRank" : 16, + "validation" : "DTO unknown-field/capability tests prove `SecurityKeyApplyUdevRulesRequest` and op are absent; `device_grant_no_path.rs` proves frontend has UHID fd without udev/plugdev; broker build has no related code.", "wave" : "W6" }, { @@ -20197,6 +20997,7 @@ "destinations" : [ "`packages/d2b-contracts/src/v3/component_session.rs`, `packages/d2b-session/`" ], + "detailedDesign" : "Reversion canonical offer/records; retain NN/KK/IKpsk2; add canonical subject/authorization context hooks Primary reuse disposition: `adapt`. Preserved source-plan detail: copy and adapt.", "entryContracts" : [ "ADR-046-componentsession-and-bus" ], @@ -20210,6 +21011,7 @@ ], "specId" : "ADR-046-componentsession-and-bus", "topologicalRank" : 6, + "validation" : "Copied exact vectors/rejections plus subject/RBAC/revocation tests", "wave" : "W1" }, { @@ -20217,6 +21019,7 @@ "destinations" : [ "`packages/d2b-session-unix/`, future enrolled transport adapter crates" ], + "detailedDesign" : "Unix/socketpair/vsock owned transports, peer evidence, fd/pidfd/object validation, credits Primary reuse disposition: `adapt`. Preserved source-plan detail: copy and adapt.", "entryContracts" : [ "ADR-046-componentsession-and-bus" ], @@ -20231,6 +21034,7 @@ ], "specId" : "ADR-046-componentsession-and-bus", "topologicalRank" : 7, + "validation" : "Copied fd/peer/credit tests plus Host/Guest subject mapping", "wave" : "W1" }, { @@ -20238,6 +21042,7 @@ "destinations" : [ "`packages/d2b-provider-shell-terminal/src/resources/{pool,session}.rs`" ], + "detailedDesign" : "Implement `shell-terminal.d2bus.org.ShellPool` and `shell-terminal.d2bus.org.ShellSession` schemas with qualified names, common phases, and typed detail fields.", "entryContracts" : [ "ADR-046-provider-shell-terminal" ], @@ -20251,6 +21056,7 @@ ], "specId" : "ADR-046-provider-shell-terminal", "topologicalRank" : 11, + "validation" : "`packages/d2b-provider-shell-terminal/tests/resource_schema.rs`", "wave" : "W6" }, { @@ -20258,6 +21064,7 @@ "destinations" : [ "`packages/d2b-provider-shell-terminal/src/bin/d2b-shell-terminal-controller.rs`" ], + "detailedDesign" : "Implement `d2b-shell-terminal-controller` with pool/session reconcile loops; assert ProviderStateSet is empty; publish bounded non-secret operational state to resource status and the core Operation ledger; no controller Provider state Volume or `/state` mount exists. Primary reuse disposition: `create`. Preserved source-plan detail: net-new controller; preserve status-first ProviderStateSet-empty rule.", "entryContracts" : [ "ADR-046-provider-shell-terminal" ], @@ -20271,6 +21078,7 @@ ], "specId" : "ADR-046-provider-shell-terminal", "topologicalRank" : 11, + "validation" : "`packages/d2b-provider-shell-terminal/tests/controller_reconcile.rs`", "wave" : "W6" }, { @@ -20278,6 +21086,7 @@ "destinations" : [ "`packages/d2b-provider-shell-terminal/src/bin/d2b-shell-session-supervisor.rs`" ], + "detailedDesign" : "Implement `d2b-shell-session-supervisor` as the sole PTY owner for Host and Guest pools. Primary reuse disposition: `adapt`. Preserved source-plan detail: reuse narrow mechanics only; move PTY authority into per-session supervisor and exclude legacy protocols/identities/state storage.", "entryContracts" : [ "ADR-046-provider-shell-terminal" ], @@ -20291,6 +21100,7 @@ ], "specId" : "ADR-046-provider-shell-terminal", "topologicalRank" : 11, + "validation" : "`packages/d2b-provider-shell-terminal/tests/supervisor_runtime.rs`", "wave" : "W6" }, { @@ -20298,6 +21108,7 @@ "destinations" : [ "`packages/d2b-provider-shell-terminal/src/process_templates.rs`" ], + "detailedDesign" : "Teach the Nix compiler and controller to emit the canonical controller and user-domain supervisor `Process` templates. Primary reuse disposition: `replace`. Preserved source-plan detail: replace incorrect draft templates with canonical controller and user-domain supervisor Process templates.", "entryContracts" : [ "ADR-046-provider-shell-terminal" ], @@ -20311,6 +21122,7 @@ ], "specId" : "ADR-046-provider-shell-terminal", "topologicalRank" : 11, + "validation" : "`packages/d2b-provider-shell-terminal/tests/process_templates.rs`", "wave" : "W6" }, { @@ -20318,6 +21130,7 @@ "destinations" : [ "`packages/d2b-provider-shell-terminal/src/service/open_session.rs`" ], + "detailedDesign" : "Create sessions from pools, freeze inherited fields, and return `supervisorGeneration` to callers.", "entryContracts" : [ "ADR-046-provider-shell-terminal" ], @@ -20331,6 +21144,7 @@ ], "specId" : "ADR-046-provider-shell-terminal", "topologicalRank" : 11, + "validation" : "`packages/d2b-provider-shell-terminal/tests/open_session.rs`", "wave" : "W6" }, { @@ -20338,6 +21152,7 @@ "destinations" : [ "`packages/d2b-provider-shell-terminal/src/session/{pty,ring}.rs`" ], + "detailedDesign" : "Implement the in-memory PTY owner model, bounded ring buffer, replay, and eviction counters; do not create any management worker or `EphemeralProcess`. Primary reuse disposition: `adapt`. Preserved source-plan detail: reuse ring semantics; keep bytes in supervisor memory and remove management worker/EphemeralProcess model.", "entryContracts" : [ "ADR-046-provider-shell-terminal" ], @@ -20351,6 +21166,7 @@ ], "specId" : "ADR-046-provider-shell-terminal", "topologicalRank" : 11, + "validation" : "`packages/d2b-provider-shell-terminal/tests/ring_buffer.rs`", "wave" : "W6" }, { @@ -20358,6 +21174,7 @@ "destinations" : [ "`packages/d2b-provider-shell-terminal/src/session/adopt.rs`" ], + "detailedDesign" : "Implement restart adoption, InvocationID plus cgroup verification, route registration, and stale-generation invalidation. Primary reuse disposition: `adapt`. Preserved source-plan detail: adapt identity-verification shape; do not reuse helper protocol, identities, or state storage assumptions.", "entryContracts" : [ "ADR-046-provider-shell-terminal" ], @@ -20371,6 +21188,7 @@ ], "specId" : "ADR-046-provider-shell-terminal", "topologicalRank" : 11, + "validation" : "`packages/d2b-provider-shell-terminal/tests/adoption.rs`", "wave" : "W6" }, { @@ -20378,6 +21196,7 @@ "destinations" : [ "`packages/d2b-provider-shell-terminal/src/host_rules.rs`" ], + "detailedDesign" : "Emit Host `isolationPosture=none` warnings, same-UID verification, and relay denial for Host user-domain pools. Primary reuse disposition: `replace`. Preserved source-plan detail: replace unsafe-local helper shell policy with resource-backed Host pool warnings and same-UID checks.", "entryContracts" : [ "ADR-046-provider-shell-terminal" ], @@ -20391,6 +21210,7 @@ ], "specId" : "ADR-046-provider-shell-terminal", "topologicalRank" : 11, + "validation" : "`packages/d2b-provider-shell-terminal/tests/host_rules.rs`", "wave" : "W6" }, { @@ -20398,6 +21218,7 @@ "destinations" : [ "`packages/d2b-provider-shell-terminal/src/guest_rules.rs`" ], + "detailedDesign" : "Require Guest `allowedDomains` to include `user`, require `defaultUserRef`, and place supervisors through the Guest user manager. Primary reuse disposition: `replace`. Preserved source-plan detail: replace guestd shell authority with Guest resource user-domain admission and supervisor placement.", "entryContracts" : [ "ADR-046-provider-shell-terminal" ], @@ -20411,6 +21232,7 @@ ], "specId" : "ADR-046-provider-shell-terminal", "topologicalRank" : 11, + "validation" : "`packages/d2b-provider-shell-terminal/tests/guest_rules.rs`", "wave" : "W6" }, { @@ -20418,6 +21240,7 @@ "destinations" : [ "`packages/d2b-provider-shell-terminal/src/authz.rs`" ], + "detailedDesign" : "Gate all verbs on `Role/shell-admin` or Zone-admin superset and fail closed for relay-authenticated Host user-domain callers. Primary reuse disposition: `replace`. Preserved source-plan detail: replace legacy shell operation authorization with Role/shell-admin or Zone-admin service gates.", "entryContracts" : [ "ADR-046-provider-shell-terminal" ], @@ -20431,6 +21254,7 @@ ], "specId" : "ADR-046-provider-shell-terminal", "topologicalRank" : 11, + "validation" : "`packages/d2b-provider-shell-terminal/tests/authz.rs`", "wave" : "W6" }, { @@ -20438,6 +21262,7 @@ "destinations" : [ "`packages/d2b-provider-shell-terminal/src/{audit,telemetry}.rs`" ], + "detailedDesign" : "Implement closed-label metrics, redacted spans, and audit events with no usernames, session names, paths, or terminal bytes. Primary reuse disposition: `create`. Preserved source-plan detail: net-new redacted observability.", "entryContracts" : [ "ADR-046-provider-shell-terminal" ], @@ -20451,6 +21276,7 @@ ], "specId" : "ADR-046-provider-shell-terminal", "topologicalRank" : 11, + "validation" : "`packages/d2b-provider-shell-terminal/tests/redaction.rs`", "wave" : "W6" }, { @@ -20458,6 +21284,7 @@ "destinations" : [ "`packages/d2b-provider-shell-terminal/src/migration.rs`" ], + "detailedDesign" : "Delete superseded guestd shell runtime, unsafe-local helper shell supervisor, and public-wire `ShellOp` or `ShellOpResponse` shell protocol. Primary reuse disposition: `delete-after-cutover`. Preserved source-plan detail: delete superseded runtime/protocol surfaces after successor parity.", "entryContracts" : [ "ADR-046-provider-shell-terminal" ], @@ -20471,6 +21298,7 @@ ], "specId" : "ADR-046-provider-shell-terminal", "topologicalRank" : 11, + "validation" : "`packages/d2b-provider-shell-terminal/tests/migration.rs`", "wave" : "W6" }, { @@ -20478,6 +21306,7 @@ "destinations" : [ "`packages/d2b-provider-shell-terminal/src/service/{controller,supervisor}.rs`" ], + "detailedDesign" : "Define and implement `shell-terminal.v3` and `shell-session-supervisor.v1` ComponentSession services and the named `terminal` stream contract. Primary reuse disposition: `adapt`. Preserved source-plan detail: adapt service-shape ideas into v3 ComponentSession services and named terminal stream.", "entryContracts" : [ "ADR-046-provider-shell-terminal" ], @@ -20491,6 +21320,7 @@ ], "specId" : "ADR-046-provider-shell-terminal", "topologicalRank" : 11, + "validation" : "`packages/d2b-provider-shell-terminal/tests/service_contract.rs`", "wave" : "W6" }, { @@ -20498,6 +21328,7 @@ "destinations" : [ "`packages/d2b-resource-store/src/lib.rs`, `packages/d2b-resource-store-redb/src/lib.rs`, `schema.rs`, `keys.rs`, `transaction.rs`" ], + "detailedDesign" : "redb tables/encodings, fd backend, store identity, fair actor, MVCC reads, atomic indexes/revisions/operations/conflicts Primary reuse disposition: `adapt`. Preserved source-plan detail: extract and adapt.", "entryContracts" : [ "ADR-046-resource-store-redb" ], @@ -20511,6 +21342,7 @@ ], "specId" : "ADR-046-resource-store-redb", "topologicalRank" : 4, + "validation" : "Unit/property/fault tests and hard benchmark", "wave" : "W0" }, { @@ -20518,6 +21350,7 @@ "destinations" : [ "`packages/d2b-resource-store-redb/src/revision_log.rs`, `packages/d2b-resource-api/src/watch.rs`" ], + "detailedDesign" : "replay/live no-gap watch, cursors, owner hints, compaction floor, expired relist", "entryContracts" : [ "ADR-046-resource-store-redb" ], @@ -20532,6 +21365,7 @@ ], "specId" : "ADR-046-resource-store-redb", "topologicalRank" : 5, + "validation" : "deterministic watch/compaction/disconnect/fan-in tests", "wave" : "W0" }, { @@ -20539,6 +21373,7 @@ "destinations" : [ "`packages/d2b-resource-store-redb/src/backup.rs`, `migration.rs`; generated v3 storage row" ], + "detailedDesign" : "fd-backed provision/open, marker identity, logical backup, staged restore/upgrade, corruption quarantine", "entryContracts" : [ "ADR-046-resource-store-redb" ], @@ -20553,6 +21388,7 @@ ], "specId" : "ADR-046-resource-store-redb", "topologicalRank" : 5, + "validation" : "marker replacement, crash publication, backup/restore/upgrade tests", "wave" : "W0" }, { @@ -20560,6 +21396,7 @@ "destinations" : [ "`docs/specs/ADR-046-spec-set.json`, `docs/specs/ADR-046-work-items.json`, `docs/specs/ADR-046-implementation-graph.json`, `docs/specs/ADR-046-implementation-graph.md` (generated, committed non-member artifacts; named in `docs/specs/README.md`)" ], + "detailedDesign" : "New `cargo run -p xtask -- spec-registry` subcommand parsing every spec's metadata table + work-item tables with a Markdown-table parser, emitting the two manifest JSON files; the `implementation-graph` generator then maps every member spec and every work item exactly once to a wave and file-disjoint parallel group, emits typed edges (`spec-depends-on`, `work-item-depends-on`, `implements-spec`, `shared-contract`, `file-overlap-order`), computes topological rank, and renders the Mermaid/table human view; output is deterministic with sorted keys and no timestamps or host paths", "entryContracts" : [ "ADR-046-streamline" ], @@ -20573,6 +21410,7 @@ ], "specId" : "ADR-046-streamline", "topologicalRank" : 12, + "validation" : "`spec_registry::parse_metadata_table`, `spec_registry::acyclic_dependency_graph`, `xtask_spec_registry_regenerates_clean`, `spec_registry_json_schema_matches_doc`, `implementation_graph::every_spec_and_work_item_mapped_once`, `implementation_graph::acyclic_and_wave_monotonic`, `implementation_graph::parallel_groups_are_file_disjoint`, `implementation_graph_regenerates_clean`", "wave" : "W7" }, { @@ -20580,6 +21418,7 @@ "destinations" : [ "`docs/specs/schemas/*.schema.json` (Tier A: hand-authored-once canonical source checked into the tree, matching the frozen D010/D032/D075/D076/D080 decisions exactly); `packages/d2b-core/src/resource_schema/*.rs` (Tier B: the eventual Rust source of truth once ResourceType implementation exists, at which point the Tier A JSON becomes generated from Rust instead of hand-authored)" ], + "detailedDesign" : "Tier A: author the schema JSON once per primitive directly from the already-frozen decision-register entries (D010, D032, D075, D076, D080); Tier B: `xtask gen-spec-schemas` derives the same JSON from real Rust `#[derive(JsonSchema)]`-equivalent types once they land, replacing the hand-authored Tier A source without changing its consumers Primary reuse disposition: `create`. Preserved source-plan detail: net-new Tier A schema source; later replace source with generated output from real d2b-core ResourceType types without changing consumers.", "entryContracts" : [ "ADR-046-streamline" ], @@ -20594,6 +21433,7 @@ ], "specId" : "ADR-046-streamline", "topologicalRank" : 13, + "validation" : "`spec_schema_check::canonical_field_set`", "wave" : "W7" }, { @@ -20601,6 +21441,7 @@ "destinations" : [ "`packages/xtask/src/bin/spec_schema_check.rs`; wired as a Layer-1 policy lint at `packages/d2b-contract-tests/tests/policy_spec_schema.rs`" ], + "detailedDesign" : "Reuse the CommonMark parser already available to the Rust toolchain (the same class of dependency `xtask`'s existing `gen-*` commands already use for structured generation) to walk fenced blocks; classify each block by its enclosing heading (`## Nix authoring and configuration cleanup` vs. any other heading) to apply the current-evidence exclusion without a manual per-block marker Primary reuse disposition: `create`. Preserved source-plan detail: net-new; reuse only the established xtask dispatch pattern and Rust Markdown-parser dependency class.", "entryContracts" : [ "ADR-046-streamline" ], @@ -20616,6 +21457,7 @@ ], "specId" : "ADR-046-streamline", "topologicalRank" : 14, + "validation" : "`spec_schema_check::extract_fenced_blocks`, `spec_schema_check::canonical_field_set`, `policy_spec_schema` (Type 4/5)", "wave" : "W7" }, { @@ -20623,6 +21465,7 @@ "destinations" : [ "`docs/specs/providers/TEMPLATE.md` (committed, non-normative reference); `packages/xtask/src/bin/new_provider_dossier.rs` (`cargo run -p xtask -- new-provider-dossier --name `)" ], + "detailedDesign" : "Scaffold generator emits the template pre-filled with the requesting Provider's name, D080 qualification-grammar examples, and the canonical state-Volume snippet; does not attempt to author Provider-specific semantic sections (those remain the dossier author's normative content) Primary reuse disposition: `create`. Preserved source-plan detail: net-new scaffold; reuse canonical schema/snippet source from ADR046-streamline-002.", "entryContracts" : [ "ADR-046-streamline" ], @@ -20638,6 +21481,7 @@ ], "specId" : "ADR-046-streamline", "topologicalRank" : 14, + "validation" : "`xtask_new_provider_dossier_scaffold`; the emitted scaffold must independently pass `spec_schema_check`/`work_item_validator` on first generation", "wave" : "W7" }, { @@ -20645,6 +21489,7 @@ "destinations" : [ "`packages/d2b-contract-tests/tests/policy_spec_vocabulary.rs`, `policy_spec_ownership.rs`, `policy_spec_finalizer_phase.rs`, `policy_spec_source_policy.rs`" ], + "detailedDesign" : "Four focused lint files (one invariant family per file, matching the existing `packages/d2b-contract-tests/tests/policy_*.rs` one-concern-per-file convention) each scanning the registry output plus raw Markdown text for the specific violation patterns named in F2/F4/F5/F7/F9 Primary reuse disposition: `create`. Preserved source-plan detail: net-new policy lints over generated registry and raw Markdown.", "entryContracts" : [ "ADR-046-streamline" ], @@ -20659,6 +21504,7 @@ ], "specId" : "ADR-046-streamline", "topologicalRank" : 13, + "validation" : "`policy_spec_vocabulary`, `policy_spec_ownership`, `policy_spec_finalizer_phase`, `policy_spec_source_policy` (all Type 5)", "wave" : "W7" }, { @@ -20666,6 +21512,7 @@ "destinations" : [ "`packages/d2b-resource-store-redb/tests/provider_state_graph.rs` (or the eventual crate implementing Zone resource storage)" ], + "detailedDesign" : "A graph-walk over the real controller-registration/resource-ownership index (not Markdown) asserting the D076/D086/D087 invariants; the doc-level half of this check (dossier text describing the invariant correctly) is covered now by ADR046-streamline-005's `policy_spec_ownership` Primary reuse disposition: `create`. Preserved source-plan detail: net-new future runtime graph checker.", "entryContracts" : [ "ADR-046-streamline" ], @@ -20680,6 +21527,7 @@ ], "specId" : "ADR-046-streamline", "topologicalRank" : 13, + "validation" : "New test asserting: zero `ProviderStateSet` rows in the store; every state Volume corresponds to a declared component namespace; no empty identity-only Volume; no bootstrap state Volume or bootstrap-storage mechanism exists", "wave" : "W7" }, { @@ -20687,6 +21535,7 @@ "destinations" : [ "`packages/d2b-contract-tests/tests/policy_effectport_boundary.rs`, `policy_worker_bus_boundary.rs`" ], + "detailedDesign" : "Static source scan (import-graph/symbol-use analysis) over compiled Provider crates, mirroring the existing `policy_broker_dispositions.rs` pattern already proven against `packages/d2bd`/`packages/d2b-priv-broker` Primary reuse disposition: `adapt`. Preserved source-plan detail: adapt existing policy-broker lint pattern to Provider/Worker boundary checks.", "entryContracts" : [ "ADR-046-streamline" ], @@ -20701,6 +21550,7 @@ ], "specId" : "ADR-046-streamline", "topologicalRank" : 13, + "validation" : "New tests asserting zero direct-broker-import matches in any Provider crate; zero disallowed-capability matches in any Worker binary", "wave" : "W7" }, { @@ -20708,6 +21558,7 @@ "destinations" : [ "`packages/d2b-contract-tests/tests/policy_work_items.rs`" ], + "detailedDesign" : "Consumes `docs/specs/ADR-046-work-items.json` (generated by ADR046-streamline-001); flags duplicate IDs, missing/placeholder fields, and dangling cross-references Primary reuse disposition: `create`. Preserved source-plan detail: net-new validator consuming ADR046-streamline-001 output.", "entryContracts" : [ "ADR-046-streamline" ], @@ -20722,6 +21573,7 @@ ], "specId" : "ADR-046-streamline", "topologicalRank" : 13, + "validation" : "`work_item_validator::unique_ids`, `work_item_validator::required_fields`", "wave" : "W7" }, { @@ -20729,6 +21581,7 @@ "destinations" : [ "`docs/specs/ADR-046-provider-catalog.md` (generated, committed)" ], + "detailedDesign" : "`cargo run -p xtask -- gen-provider-catalog` reads the registry and decision-register D043-D049 rows and renders the table; drift-gated like the existing `gen-schemas`/`gen-migration-ledger` pattern Primary reuse disposition: `create`. Preserved source-plan detail: net-new generator consuming registry and decision-register rows.", "entryContracts" : [ "ADR-046-streamline" ], @@ -20743,6 +21596,7 @@ ], "specId" : "ADR-046-streamline", "topologicalRank" : 13, + "validation" : "`provider_catalog::frozen_family_membership`, `provider_catalog_matches_frozen_families`", "wave" : "W7" }, { @@ -20750,6 +21604,7 @@ "destinations" : [ "`tests/tools/reconcile-stale-base.sh` (reporting only) plus a documented `git town sync`/`git town` restack procedure this report feeds into, since this repository does not yet use Git Town and F1/F11 show plain rebase/cherry-pick was insufficient to prevent duplicate reconciliation" ], + "detailedDesign" : "`git merge-base ` plus `git diff --name-only` intersection reporting; emits a JSON report (branch, divergence SHA, commits-behind count, file-overlap list) consumed by ADR046-streamline-013's ready/launched/blocker report Primary reuse disposition: `adapt`. Preserved source-plan detail: net-new read-only git report.", "entryContracts" : [ "ADR-046-streamline" ], @@ -20764,6 +21619,7 @@ ], "specId" : "ADR-046-streamline", "topologicalRank" : 13, + "validation" : "`reconcile_helper_reports_stale_base`", "wave" : "W7" }, { @@ -20771,6 +21627,7 @@ "destinations" : [ "`packages/xtask/src/bin/handoff_manifest.rs` (schema/validator only); manifest instances are per-round artifacts referenced from PR bodies, not committed to the tree (consistent with this repository's \"Screenshot and visual artifact hygiene\"-style external-evidence posture)" ], + "detailedDesign" : "A small JSON schema (`assigned_files: [string]`, `commit_sha: string`, `test_result: {command, exit_code}`, `base_sha: string`) plus a validator rejecting incomplete records; no attribution field for any AI/tool/model, consistent with this repository's commit/PR-body attribution rule Primary reuse disposition: `create`. Preserved source-plan detail: net-new schema and validator.", "entryContracts" : [ "ADR-046-streamline" ], @@ -20784,6 +21641,7 @@ ], "specId" : "ADR-046-streamline", "topologicalRank" : 12, + "validation" : "`handoff_manifest_rejects_incomplete_record`", "wave" : "W7" }, { @@ -20791,6 +21649,7 @@ "destinations" : [ "`tests/tools/import-task-db-consistency.sh`" ], + "detailedDesign" : "Reads the session database's todo table, resolves each `done` row's expected file set against `git log --name-only` for the current branch, and reports mismatches; read-only, no database or git mutation Primary reuse disposition: `create`. Preserved source-plan detail: net-new read-only importer/checker.", "entryContracts" : [ "ADR-046-streamline" ], @@ -20805,6 +21664,7 @@ ], "specId" : "ADR-046-streamline", "topologicalRank" : 13, + "validation" : "New test seeding a synthetic task DB with one orphaned \"done\" row and confirming the importer flags it", "wave" : "W7" }, { @@ -20812,6 +21672,7 @@ "destinations" : [ "`tests/tools/anti-serialization-report.sh`" ], + "detailedDesign" : "Cross-references the spec registry's dependency graph (ADR046-streamline-001) against the set of currently open `adr0046-*` branches and the reconcile helper's (ADR046-streamline-010) staleness report; emits a per-scope ready/launched/blocked classification Primary reuse disposition: `adapt`. Preserved source-plan detail: net-new report combining registry, branch, and staleness data.", "entryContracts" : [ "ADR-046-streamline" ], @@ -20827,6 +21688,7 @@ ], "specId" : "ADR-046-streamline", "topologicalRank" : 14, + "validation" : "New test seeding a synthetic registry + branch list with one ready-but-unlaunched scope and confirming it is reported", "wave" : "W7" }, { @@ -20834,6 +21696,7 @@ "destinations" : [ "`tests/tools/run-layer.sh` extension (this repository already has `tests/tools/run-layer.sh` and `layer1-jobs.py` bounded-parallelism precedent) plus fake `EffectPort`/`ResourceClient` stub crates under `packages/d2b-provider-toolkit-fakes/`" ], + "detailedDesign" : "Extend the existing `layer1-jobs.py` bounded-parallel-shard pattern with a per-Provider-crate shard definition; fake dependency crates implement the same `EffectPort`/`ResourceClient` trait surface with in-memory stand-ins Primary reuse disposition: `adapt`. Preserved source-plan detail: adapt bounded-parallel shard pattern; add net-new fake EffectPort/ResourceClient crates.", "entryContracts" : [ "ADR-046-streamline" ], @@ -20847,6 +21710,7 @@ ], "specId" : "ADR-046-streamline", "topologicalRank" : 12, + "validation" : "New integration test proving a single Provider crate's test run succeeds with zero other Provider crates built", "wave" : "W7" }, { @@ -20854,6 +21718,7 @@ "destinations" : [ "Shared `packages/xtask` regeneration-conflict-detection helper consumed by every `gen-*`/`spec-registry` subcommand" ], + "detailedDesign" : "Each generator hashes its input set before writing; if a concurrent regeneration on a sibling branch (detected via the reconcile helper's file-overlap report) would produce a different hash for the same output path, the generator refuses to overwrite and reports the divergent input set Primary reuse disposition: `adapt`. Preserved source-plan detail: adapt existing generator/drift-gate pattern with net-new input-hash conflict detection.", "entryContracts" : [ "ADR-046-streamline" ], @@ -20870,6 +21735,7 @@ ], "specId" : "ADR-046-streamline", "topologicalRank" : 14, + "validation" : "New test simulating two divergent input sets producing conflicting output for the same generated file and confirming the conflict is reported, not silently resolved", "wave" : "W7" }, { @@ -20877,6 +21743,7 @@ "destinations" : [ "`packages/d2b-contract-tests/tests/policy_no_leaked_decision_prefix.rs`; invoked as a required precondition script `tests/tools/pre-panel-gate.sh`" ], + "detailedDesign" : "Scans registry output plus raw Markdown for `decision-required`, `TBD`, `TODO`, and any decision-ID-shaped token (`[A-Z]+-\\d+`) not matching the canonical `D\\d+` register numbering Primary reuse disposition: `create`. Preserved source-plan detail: net-new policy lint and precondition script.", "entryContracts" : [ "ADR-046-streamline" ], @@ -20892,6 +21759,7 @@ ], "specId" : "ADR-046-streamline", "topologicalRank" : 14, + "validation" : "`policy_no_leaked_decision_prefix`", "wave" : "W7" }, { @@ -20899,6 +21767,7 @@ "destinations" : [ "`docs/specs/ADR-046-streamline-evidence-commands.md` (a follow-up artifact outside this task's file scope; tracked here as a required future addition, not authored by this spec)" ], + "detailedDesign" : "A plain Markdown list of the exact commands used to produce each F1-F17 citation above (already reproduced verbatim in this spec's prose), organized by friction ID, so re-running them is copy-paste rather than re-derivation Primary reuse disposition: `adapt`. Preserved source-plan detail: extract/adapt the already-cited evidence commands into a net-new documentation artifact.", "entryContracts" : [ "ADR-046-streamline" ], @@ -20912,6 +21781,7 @@ ], "specId" : "ADR-046-streamline", "topologicalRank" : 12, + "validation" : "Manual reviewer re-run of at least one command per friction ID during panel, confirmed to reproduce the cited count/SHA", "wave" : "W7" }, { @@ -20919,6 +21789,7 @@ "destinations" : [ "`tests/tools/worktree-disk-report.sh`" ], + "detailedDesign" : "`git worktree list --porcelain` plus `du -sh` per worktree's `packages/target/`; output is a report only, never a `git worktree remove`/`rm -rf` invocation Primary reuse disposition: `adapt`. Preserved source-plan detail: adapt existing disk-hygiene reporting concepts into a net-new non-mutating script.", "entryContracts" : [ "ADR-046-streamline" ], @@ -20933,6 +21804,7 @@ ], "specId" : "ADR-046-streamline", "topologicalRank" : 14, + "validation" : "New test confirming the reporting script's exit code and output never include a mutating command string, and confirming it correctly flags a fixture worktree with a real (non-symlink) `packages/target/`", "wave" : "W7" }, { @@ -20940,6 +21812,7 @@ "destinations" : [ "`packages/xtask/src/bin/terminology_check.rs` (`cargo run -p xtask -- terminology-check`)" ], + "detailedDesign" : "Parses every \"Current-code fit\" table's \"Current anchor\" cell for symbol/crate names, cross-references against the migration map's disposition rows, and flags any current-source citation absent from the map Primary reuse disposition: `create`. Preserved source-plan detail: net-new terminology checker over existing mapping specs.", "entryContracts" : [ "ADR-046-streamline" ], @@ -20954,6 +21827,7 @@ ], "specId" : "ADR-046-streamline", "topologicalRank" : 13, + "validation" : "New test seeding a fixture spec citing a current-source symbol absent from the migration map and confirming it is flagged", "wave" : "W7" }, { @@ -20961,6 +21835,7 @@ "destinations" : [ "`packages/d2b-contract-tests/tests/policy_test_placement.rs`" ], + "detailedDesign" : "Scans hermetic-tier Rust sources for banned API surfaces (`std::process::Command`, socket/container/DBus/systemd helpers, `#[ignore]`) and for `integration/`-only markers appearing outside `integration/` Primary reuse disposition: `create`. Preserved source-plan detail: net-new policy lint.", "entryContracts" : [ "ADR-046-streamline" ], @@ -20975,6 +21850,7 @@ ], "specId" : "ADR-046-streamline", "topologicalRank" : 13, + "validation" : "Fixture crate with an intentional process-spawning hermetic test is rejected naming the file/line; a correct crate passes", "wave" : "W7" }, { @@ -20982,6 +21858,7 @@ "destinations" : [ "`packages/d2b-contract-tests/tests/policy_test_determinism.rs`" ], + "detailedDesign" : "Scans hermetic sources for banned time/sleep APIs and asserts the deterministic fake-clock/RNG fixtures from the toolkit are used; classified crypto/property exceptions are allow-listed by explicit name with a declared per-test budget Primary reuse disposition: `create`. Preserved source-plan detail: net-new policy lint layered on ADR046-streamline-020.", "entryContracts" : [ "ADR-046-streamline" ], @@ -20996,6 +21873,7 @@ ], "specId" : "ADR-046-streamline", "topologicalRank" : 14, + "validation" : "Fixture hermetic test using `thread::sleep` is rejected; a classified crypto test on the allow-list passes", "wave" : "W7" }, { @@ -21003,6 +21881,7 @@ "destinations" : [ "`packages/xtask/src/test_runtime_ledger.rs` (shared with `ADR046-delivery-007`)" ], + "detailedDesign" : "Parses per-test JSON timings, aggregates per test/crate/shard, compares against pinned budgets and the previous ledger, and fails on regression beyond the threshold Primary reuse disposition: `adapt`. Preserved source-plan detail: share/adapt ADR046-delivery-007 timing-ledger implementation for this gate.", "entryContracts" : [ "ADR-046-streamline" ], @@ -21017,6 +21896,7 @@ ], "specId" : "ADR-046-streamline", "topologicalRank" : 14, + "validation" : "Synthetic timing regression fails the gate; ledger output is deterministic and machine-readable", "wave" : "W7" }, { @@ -21024,6 +21904,7 @@ "destinations" : [ "`packages/xtask/src/bin/legacy_test_retirement.rs` (`cargo run -p xtask -- legacy-test-retirement`)" ], + "detailedDesign" : "Reads the migration map's disposition rows, cross-references the live `tests/layer1-jobs.json`/gate manifests, and emits the delete set plus an absence assertion; never deletes automatically — it produces the gated checklist and the failing test that proves incomplete retirement Primary reuse disposition: `adapt`. Preserved source-plan detail: net-new generator reading existing migration rows and gate manifests without mutating them.", "entryContracts" : [ "ADR-046-streamline" ], @@ -21039,6 +21920,7 @@ ], "specId" : "ADR-046-streamline", "topologicalRank" : 15, + "validation" : "Fixture with a replaced behavior whose old selector still appears in `tests/layer1-jobs.json` fails; once removed, the absence assertion passes", "wave" : "W7" }, { @@ -21046,6 +21928,7 @@ "destinations" : [ "`packages/xtask/src/bin/implementation_graph.rs` (`cargo run -p xtask -- implementation-graph`); folds the spec-set and test-runtime emitters into the one `xtask` dispatch" ], + "detailedDesign" : "Deterministic (sorted keys, no timestamps/host paths); regenerated after any spec or work-item change and after the two manifests; a drift gate runs it and `git diff --exit-code`. Retains the duplicate-generator findings for the spec-set manifest and the test-runtime ledger and reconciles them to the single `xtask` path rather than leaving three independent emitters Primary reuse disposition: `create`. Preserved source-plan detail: net-new generator that consumes the two manifests and the wave topology; folds the duplicate spec-set/test-runtime emitters into one canonical path.", "entryContracts" : [ "ADR-046-streamline" ], @@ -21060,6 +21943,7 @@ ], "specId" : "ADR-046-streamline", "topologicalRank" : 13, + "validation" : "`implementation_graph::every_spec_and_work_item_mapped_once`; `implementation_graph::acyclic_and_wave_monotonic`; `implementation_graph::parallel_groups_are_file_disjoint`; `implementation_graph_regenerates_clean`; duplicate-generator reconciliation asserts one canonical emitter path for spec-set and test-runtime", "wave" : "W7" }, { @@ -21067,6 +21951,7 @@ "destinations" : [ "`packages/d2b-provider-system-core/src/manifest.rs`, `packages/d2b-provider-system-core/src/audit.rs`, and `packages/d2b-provider-system-core/tests/provider_boundary.rs`" ], + "detailedDesign" : "Compile the system-core Provider manifest, empty closed config schema, Host/User component descriptors, and empty state-namespace declaration. The manifest binds both library handlers to the fixed `d2b-core-controller` bootstrap process without declaring either handler as a Process resource. The audit adapter emits one bounded, redacted `ResourceReconciled` record after each Host/User reconcile. The boundary rejects Provider config fields and proves that handler call paths neither write `Provider.status` nor emit `ProcessEffect`; core-controller infrastructure owns the former and `ADR046-exec-006`/`ADR046-exec-007` own the latter. `ADR046-pstate-012` remains the owner of generic optional-state admission; this item only declares system-core's empty state set. Primary reuse disposition: `adapt`. Preserved source-plan detail: adapt descriptor/audit patterns; implement the v3 Provider-specific boundary.", "entryContracts" : [ "ADR-046-provider-system-core" ], @@ -21087,6 +21972,7 @@ ], "specId" : "ADR-046-provider-system-core", "topologicalRank" : 13, + "validation" : "`config_schema_empty_only`, `provider_status_not_written_by_handlers`, `provider_state_set_empty`, `host_no_process_effect_emitted`, `host_resource_reconciled_audit`, and `user_resource_reconciled_audit`; manifest golden vector proves no Process descriptor and no state namespace for either handler.", "wave" : "W6" }, { @@ -21094,6 +21980,7 @@ "destinations" : [ "`packages/d2b-provider-system-systemd/src/controller.rs` (async reconcile loop), `src/launch.rs` (launch algorithm via effect port), `src/effect_port.rs` (`SystemdProcessEffectPort` trait + test double), `src/adoption.rs` (adoption algorithm), `src/sandbox.rs` (SandboxSpec → unit property compiler)" ], + "detailedDesign" : "Full §6 launch algorithm (effect port integration); §7 EphemeralProcess; §8 restart/adoption (effect port `locate_by_identity`); §9 drain (effect port `stop`/`kill`); §10 sandbox compilation; §11 bus services; ProviderSupervisor LaunchTicket integration Primary reuse disposition: `adapt`. Preserved source-plan detail: extract and adapt.", "entryContracts" : [ "ADR-046-provider-system-systemd" ], @@ -21108,6 +21995,7 @@ ], "specId" : "ADR-046-provider-system-systemd", "topologicalRank" : 11, + "validation" : "`tests/conformance.rs` (shared conformance kit); `tests/identity_binding.rs` (InvocationID/cgroup/MainPID/start-time golden vectors via mock effect port); `tests/adoption.rs` (quarantine/identity-mismatch cases); `tests/restart.rs` (backoff/maxRestarts); latency assertions (p95 ≤5 ms hint→handler, ≤20 ms commit→effect port `start` call)", "wave" : "W6" }, { @@ -21115,6 +22003,7 @@ "destinations" : [ "`nixos-modules/` (Provider ResourceSpec emission for `system-systemd`); `packages/d2b-provider-system-systemd/` package derivation and catalog entry" ], + "detailedDesign" : "§16 Nix configuration; `d2b.artifacts.system-systemd` catalog entry; Provider and Process ResourceSpec emission; eval/build validation rules; drift gate update (`xtask gen-nix-options` + `make test-drift`)", "entryContracts" : [ "ADR-046-provider-system-systemd" ], @@ -21129,6 +22018,7 @@ ], "specId" : "ADR-046-provider-system-systemd", "topologicalRank" : 12, + "validation" : "`tests/unit/nix/cases/provider-system-systemd.nix` (eval-time validation); `tests/unit/gates/drift-check.sh` covers generated option schema", "wave" : "W6" }, { @@ -21136,6 +22026,7 @@ "destinations" : [ "`packages/d2b-provider-system-systemd/tests/conformance.rs`, `tests/fault.rs`, `tests/ephemeral.rs`, `tests/sandbox_compile.rs`; `integration/host_scenario.rs`, `integration/guest_scenario.rs`" ], + "detailedDesign" : "Full §19 test/integration requirements Primary reuse disposition: `adapt`. Preserved source-plan detail: copy/adapt.", "entryContracts" : [ "ADR-046-provider-system-systemd" ], @@ -21150,6 +22041,7 @@ ], "specId" : "ADR-046-provider-system-systemd", "topologicalRank" : 12, + "validation" : "All conformance vectors pass; all fault injection scenarios reach expected phase/condition; all §19 Host and Guest test scenarios pass", "wave" : "W6" }, { @@ -21157,6 +22049,7 @@ "destinations" : [ "`packages/d2b-telemetry/src/{trace_context.rs,audit_hash.rs,emitter.rs,meter_registry.rs,redaction_guard.rs}`" ], + "detailedDesign" : "`d2b-telemetry` provides: (1) `TraceContext` / `AuditHash` / `AuditChainLink` extracted unchanged; (2) `BoundedEmitter`: `tracing`-subscriber layer that serializes span/metric events into compact frames and writes them over a private Unix datagram socket to the `observability-otel` Provider — no `opentelemetry_sdk` dependency; (3) `RedactionGuard` span wrapper that asserts the v3 resource attribute allowlist at span creation. No OTEL SDK in this crate. Primary reuse disposition: `adapt`. Preserved source-plan detail: extract unchanged (`TraceContext`, `AuditHash`, `AuditChainLink`); adapt (`OperationId`/`CorrelationId` for v3 record contract); add bounded emitter.", "entryContracts" : [ "ADR-046-telemetry-audit-and-support" ], @@ -21170,6 +22063,7 @@ ], "specId" : "ADR-046-telemetry-audit-and-support", "topologicalRank" : 10, + "validation" : "Unit test for `RedactionGuard` attribute gate; unit test for `BoundedEmitter` ring-full drop and FIFO drain; `policy_telemetry_redaction.rs::startup_tracing_avoids_host_path_fields` port; assert `config_source = \"realm-controllers\"` absent; assert no `opentelemetry_sdk` dependency in `d2b-telemetry` Cargo.toml", "wave" : "W5" }, { @@ -21177,6 +22071,7 @@ "destinations" : [ "`packages/d2b-resource-store-redb/src/metrics.rs`, `packages/d2b-resource-store-redb/src/tracing.rs`" ], + "detailedDesign" : "Instrument the store actor, write/read/group-commit paths with the metric inventory from this spec via `d2b-telemetry` `BoundedEmitter`. Emit `d2b.store.*` spans. The p95 `d2b_store_write_duration_seconds` hard target (≤10 ms) feeds the benchmark fixture. No `vm` label; `resource_type` label only from closed catalog. No OTEL SDK in the store crate. Primary reuse disposition: `adapt`. Preserved source-plan detail: adapt bucket boundary constants (rename; remove `vm` labels); replace hand-rolled `Registry` with `d2b-telemetry` `BoundedEmitter` meter API.", "entryContracts" : [ "ADR-046-telemetry-audit-and-support" ], @@ -21192,6 +22087,7 @@ ], "specId" : "ADR-046-telemetry-audit-and-support", "topologicalRank" : 11, + "validation" : "p95 write ≤10 ms benchmark fixture; metric inventory policy test asserting no `vm` label; assert old `d2b_daemon_vm_state` shape absent", "wave" : "W5" }, { @@ -21199,6 +22095,7 @@ "destinations" : [ "`packages/d2b-resource-api/src/metrics.rs`, `packages/d2b-session/src/metrics.rs`, `packages/d2b-bus/src/metrics.rs`" ], + "detailedDesign" : "Instrument resource API verb dispatch, watch delivery, bus route resolution, and session handshake/reconnect per the metric/span catalog in this spec. Propagate `TraceContext` from incoming bus request to store write transaction span as child context. Primary reuse disposition: `adapt`. Preserved source-plan detail: adapt `TraceContext` protobuf codec for v3 resource API framing; adapt routing metrics patterns.", "entryContracts" : [ "ADR-046-telemetry-audit-and-support" ], @@ -21215,6 +22112,7 @@ ], "specId" : "ADR-046-telemetry-audit-and-support", "topologicalRank" : 11, + "validation" : "API request metric inventory test; session profile/outcome label cardinality gate; bus direction label gate; assert no `realm` field in span attributes", "wave" : "W5" }, { @@ -21222,6 +22120,7 @@ "destinations" : [ "`packages/d2b-core-controller/src/metrics.rs`, `packages/d2b-core-controller/src/tracing.rs`" ], + "detailedDesign" : "Emit `d2b.controller.hint` span at the instant the post-commit dispatcher fires; emit `d2b.controller.reconcile` child span at handler entry. Interval = p95 ≤5 ms target. `handler` label from closed set; no resource name labels. Primary reuse disposition: `adapt`. Preserved source-plan detail: adapt bucket patterns; adapt trace-context-in-reconcile pattern from `allocator_engine.rs`.", "entryContracts" : [ "ADR-046-telemetry-audit-and-support" ], @@ -21237,6 +22136,7 @@ ], "specId" : "ADR-046-telemetry-audit-and-support", "topologicalRank" : 11, + "validation" : "`hint_to_handler_latency` benchmark with p95 ≤5 ms assertion; closed `handler` label set gate", "wave" : "W5" }, { @@ -21244,6 +22144,7 @@ "destinations" : [ "`packages/d2b-provider-supervisor/src/metrics.rs`, `packages/d2b-provider-supervisor/src/tracing.rs`" ], + "detailedDesign" : "`d2b_process_launch_duration_seconds`: start = instant Process controller receives commit-to-Ready hint; end = first OS spawn call (clone3 or systemd unit start). This implements p95 ≤20 ms. `provider` label replaces `vmm`/`RunnerRole` with the closed set `{minijail,systemd}`. No `vm` name label. A separate `d2b_process_ready_duration_seconds` histogram covers launch-attempt → readiness signal (not a hard target). Primary reuse disposition: `adapt`. Preserved source-plan detail: adapt launch histogram bucket constants; rename `vm` label to no label (process identity in resource attributes); rename `vmm`/`RunnerRole` → `provider` closed enum.", "entryContracts" : [ "ADR-046-telemetry-audit-and-support" ], @@ -21259,6 +22160,7 @@ ], "specId" : "ADR-046-telemetry-audit-and-support", "topologicalRank" : 11, + "validation" : "`commit_to_launch_latency` benchmark with p95 ≤20 ms assertion; assert no `vm` label in process metrics; `vmm→provider` label rename gate", "wave" : "W5" }, { @@ -21266,6 +22168,7 @@ "destinations" : [ "`packages/d2b-provider-observability-otel/src/`, `nixos-modules/components/observability/` (adapted files)" ], + "detailedDesign" : "`Provider/observability-otel` is an **ordinary optional non-bootstrap Process** (not counted toward the ≤64 MiB mandatory core aggregate). It owns: (1) per-Zone datagram receiver socket at `$ZONE_STATE/telemetry/emitter.sock` (drains frames from core emitters) and OTLP/gRPC Unix socket at `$ZONE_STATE/telemetry/otlp.sock`; (2) the full OTEL SDK with OTLP exporter — only this process links `opentelemetry_sdk`; (3) OTel Collector pipeline per Zone and per Host; (4) vsock OTLP forwarding to obs Zone (replaces socat-based `OtelHostBridgeArgvInputs`); (5) SigNoz stack Nix adapted from `stack.nix` with per-Zone `ingressSources` replacing per-VM `vmName`; (6) journald scrape (optional, disabled by default); (7) self-metrics endpoint. Zone/controller startup does not wait for this Provider. If absent or unready, Zone health is `Degraded` (not `Failed`). Readiness: socket exists and first drain cycle completes successfully. `d2b.observability.host.identityName` option preserved; `vmName` in `ingressSources` populated from Zone name. Primary reuse disposition: `adapt`. Preserved source-plan detail: adapt Nix pipeline shape (replace per-VM `vmName` with per-Zone naming); adapt `OtelHostBridgeArgvInputs` vsock forwarding to native OTLP/gRPC-over-vsock; adapt readiness gate pattern (`OtelHostBridgeReadiness::Ready` → Provider phase `Ready`); adapt `ingressSources` per-VM → per-Zone.", "entryContracts" : [ "ADR-046-telemetry-audit-and-support" ], @@ -21281,6 +22184,7 @@ ], "specId" : "ADR-046-telemetry-audit-and-support", "topologicalRank" : 10, + "validation" : "`emitter_socket_receive`, `emitter_ring_drains_on_socket_available`, `emitter_ring_drop_on_overflow`, `no_vm_label_in_metrics`, `zone_startup_proceeds_without_provider` tests; adapted `policy_observability.rs` tests (retain `loki_native_otel_resource_attributes` and SigNoz-only backend assertions); adapted `minijail_relay_otel.rs` shape test for Provider-managed runner", "wave" : "W5" }, { @@ -21288,6 +22192,7 @@ "destinations" : [ "`packages/d2b-provider-observability-otel/src/nix/journald.nix` (new Nix fragment)" ], + "detailedDesign" : "`d2b.zones..observability.journald.enable = false` (default). When enabled: journald receiver follows `z-/*` and `s-/*` cgroup filters. Collector applies redaction: drops `MESSAGE` credential/path patterns, `_CMDLINE`, `_EXE`, `INVOCATION_ID`. Current `scrapeJournal` host option is preserved unchanged. Primary reuse disposition: `adapt`. Preserved source-plan detail: adapt journald receiver config for per-Zone cgroup filter (`z-/*`, `s-/*`).", "entryContracts" : [ "ADR-046-telemetry-audit-and-support" ], @@ -21302,6 +22207,7 @@ ], "specId" : "ADR-046-telemetry-audit-and-support", "topologicalRank" : 11, + "validation" : "Nix eval test: filter expression set when enabled; test that `_CMDLINE` and `INVOCATION_ID` appear in drop list", "wave" : "W5" }, { @@ -21309,6 +22215,7 @@ "destinations" : [ "`packages/d2b-contract-tests/tests/policy_telemetry_redaction.rs` (new); updated `policy_observability.rs`; updated `policy_metrics.rs`" ], + "detailedDesign" : "(1) Extend `loki_native_otel_resource_attributes` allowlist to include `d2b.zone`, `d2b.provider`, `d2b.component`, `service.version`. (2) Add redaction lint: scan all v3 instrumentation call sites for `realm`, `workload_id`, `node_id`, `vm` (as label key), `path`, `socket`, `argv`, `pid`, `exe`. (3) Add structural metric-label policy lint: parse every v3 `MetricDescriptor`, require each label key and value domain to exist in the closed `METRIC_LABEL_POLICY`, reject exact keys `vm`, `zone`, `zone_id`, `zone_uid`, `credential_name`, `network`, `network_name`, and `link_name_hash`, reject resource-name-derived key suffixes `*_name`, `*_name_hash`, `*_name_digest`, and `*_uid`, and prove a `metadata.name` canary never enters label values. Fixed semantic labels remain allowed only with closed domains. (4) Assert the `d2b.zone` resource attribute remains present. (5) Add bucket boundary gates for 5 ms and 20 ms. (6) Retain: `startup_tracing_avoids_host_path_fields`; SigNoz-only backend assertion; `tempo_guest_collector_shape`; `config_source = \"realm-controllers\"` absence gate. Primary reuse disposition: `adapt`. Preserved source-plan detail: adapt and extend; keep existing tests; add new policy gates.", "entryContracts" : [ "ADR-046-telemetry-audit-and-support" ], @@ -21323,6 +22230,7 @@ ], "specId" : "ADR-046-telemetry-audit-and-support", "topologicalRank" : 11, + "validation" : "These tests are their own validation artifact", "wave" : "W5" }, { @@ -21330,6 +22238,7 @@ "destinations" : [ "`nixos-modules/resources.nix` (uniform `d2b.zones..resources` schema-aware option; `spec.*` option types generated from `ResourceTypeSchema` for each `type`); `nixos-modules/resources-bundle.nix` (ADR-only: sorted integrity-pinned bundle derivation)" ], + "detailedDesign" : "(1) Implement `d2b.zones..resources = lib.mkOption { type = lib.types.attrsOf (schemaAwareResourceSubmodule); }` where the submodule, given `config.type`, loads the registered `ResourceTypeSchema` and generates `spec.*` option types from it. For `type = \"Provider\"`, `spec.config.*` options are generated from the signed Provider schema for the package identified by `spec.artifactId` (see ADR-046-provider-model-and-packaging). No second bespoke vocabulary; `spec` fields mirror the canonical JSON fields exactly. (2) `resources-bundle.nix` derivation: serialize each resource to canonical sorted JSON (keys alphabetically sorted at every level); sort resources by `(type, name)`; compute generation digest; emit `zone-resources-.json` as Nix store output. Publication handler sets `metadata.managedBy = \"configuration\"` and `metadata.configurationGeneration` on activation — these fields are NOT authored in Nix. (3) `status`, UID, generation, revision, and timestamps are absent from Nix authoring; core fills them. Primary reuse disposition: `adapt`. Preserved source-plan detail: Implement uniform `d2b.zones..resources. = { type; spec; }` option with schema-driven `spec.*` generated option types; adapt option submodule pattern from `options-realms.nix`; adapt pipeline shape from `{host,stack,guest}.nix`; emit canonical sorted ResourceSpec JSON from `resources-bundle.nix`.", "entryContracts" : [ "ADR-046-telemetry-audit-and-support" ], @@ -21343,6 +22252,7 @@ ], "specId" : "ADR-046-telemetry-audit-and-support", "topologicalRank" : 10, + "validation" : "`eval_rejects_unknown_type`, `eval_rejects_invalid_emitter_ring_size`, `eval_rejects_unknown_provider_settings`, `eval_rejects_inline_secret_in_settings`, `eval_rejects_unresolved_credential_ref`, `eval_rejects_duplicate_resource_name` nix-unit cases; `bundle_is_sorted_canonically`, `bundle_digest_is_deterministic`, `bundle_contains_no_secret_values`, `bundle_schema_validates_against_provider_schema` contract tests", "wave" : "W5" }, { @@ -21350,6 +22260,7 @@ "destinations" : [ "`nixos-modules/resources-bundle.nix` (build-time validation step 4 in the `resources-bundle` derivation); `packages/d2b-core-controller/src/configuration.rs` (runtime activation checks)" ], + "detailedDesign" : "Build-time: (1) For each `Provider` resource, fetch the `resourceTypeSchema` output from the package; validate `settings` JSON against the JSON Schema; fail the build on schema mismatch or unknown fields. (2) Assert no resource spec field contains a bare secret/path/argv (forbidden-field pattern from `startup_tracing_avoids_host_path_fields`). Runtime: (3) Core-controller re-validates Provider package identity (per ADR-046-provider-model-and-packaging) against the installed package; resolves Credential refs; checks conflict/bounds; rejects with closed-enum `generation-rejected` reason on any failure; no partial activation. (4) Provider schema mismatch between the bundle's schema and the installed Provider's live schema → reject, emit `generation-rejected{reason=\"package-identity-mismatch\"}`. Primary reuse disposition: `adapt`. Preserved source-plan detail: Adapt `startup_tracing_avoids_host_path_fields` forbidden-field pattern for bundle schema gate; adapt `ProviderBindingV2` non-exhaustive contract for Provider-specific settings schema fingerprint.", "entryContracts" : [ "ADR-046-telemetry-audit-and-support" ], @@ -21366,6 +22277,7 @@ ], "specId" : "ADR-046-telemetry-audit-and-support", "topologicalRank" : 11, + "validation" : "`bundle_schema_validates_against_provider_schema` bundle contract test; `generation_rejected_emits_audit_record` cleanup contract test with a `schema-validation-failed` reason; add a nix-unit case `eval_rejects_unknown_fields_against_signed_schema` that runs the bundle derivation with a schema mismatch and asserts build failure", "wave" : "W5" }, { @@ -21373,6 +22285,7 @@ "destinations" : [ "`packages/d2b-core-controller/src/{configuration.rs, ownership.rs}`" ], + "detailedDesign" : "(1) On new generation activation, every stored `managedBy=configuration` resource absent from the new configured set receives `deletionRequestedAt` plus `deletion-pending`; controller/API-managed resources are untouched. (2) Activation returns after durable intent queueing and does not wait for cleanup. (3) The ownership handler drives child-before-parent finalizers. (4) When finalizers clear, one atomic store transaction writes the `Deleted` revision/change event and removes the row and indexes. After commit, the audit subsystem appends `ResourceMutation{event=\"deleted\", trigger=\"config-cleanup\"}` from that revision using a dedup/exactly-once recovery key; audit append is not part of the store transaction. (5) Stall detection sets `cleanup-stalled` without force-removing finalizers. (6) Prior generations use count retention, default 3 and range 1..16, with no TTL. (7) Core sets `managedBy`/`configurationGeneration` in persisted resources; input bundles omit both. Primary reuse disposition: `adapt`. Preserved source-plan detail: Adapt hash-chain append from `daemon_audit.rs` for `ResourceMutation{trigger=\"config-cleanup\"}` records; adapt prior-generation retention window from `manifest.nix` pattern.", "entryContracts" : [ "ADR-046-telemetry-audit-and-support" ], @@ -21391,6 +22304,7 @@ ], "specId" : "ADR-046-telemetry-audit-and-support", "topologicalRank" : 12, + "validation" : "All tests in \"Configuration-owned cleanup contract tests\" subsection; additionally: `managedby_configuration_set_on_activated_resources`, `controller_created_resources_have_managedby_controller`, `absent_resource_receives_delete_on_new_generation`, `deletion_sets_deletionrequestedat_not_phase`, `final_deletion_is_atomic`, `cleanup_does_not_touch_controller_children`, `pending_cleanup_condition_set_on_zone`, `zone_is_degraded_not_failed_during_cleanup`, `pending_cleanup_cleared_after_deletion_completes`, `prior_generation_retained_count_based`, `rollback_schedules_delete_for_new_generation_resources`, `audit_segments_preserved_on_provider_delete`, `cleanup_stall_condition_set`, `generation_rejected_emits_audit_record`", "wave" : "W5" }, { @@ -21398,6 +22312,7 @@ "destinations" : [ "`packages/d2b-provider-transport-azure-relay/src/relay_transport.rs`" ], + "detailedDesign" : "Adapt `RelayStream` as relay transport service process; expose named opaque byte stream on the `transport-service` Unix endpoint; add 2-byte length-prefixed framing; preserve credential redaction; TLS/WebSocket state stays in-process — only Noise record bytes traverse the named stream; register named stream with d2b-bus as `TransportHandle`; transport descriptor: `attachment_support: false`, `locality: Remote`, `atomic: false`; expose `OpenTransport`/`CloseTransport`/`ObserveTransport` interface to core; long-lived service process multiplexes sessions internally Primary reuse disposition: `adapt`. Preserved source-plan detail: extract and adapt.", "entryContracts" : [ "ADR-046-provider-transport-azure-relay" ], @@ -21411,6 +22326,7 @@ ], "specId" : "ADR-046-provider-transport-azure-relay", "topologicalRank" : 11, + "validation" : "`tests/fake_relay_transport.rs`: connect/accept, framing, credential redaction, named stream roundtrip; `tests/listener_sender_conformance.rs`: named stream contract; Noise KK binding; relay identity exclusion", "wave" : "W6" }, { @@ -21418,6 +22334,7 @@ "destinations" : [ "`packages/d2b-provider-transport-azure-relay/src/credential_client.rs`" ], + "detailedDesign" : "Async Credential KK session client for service components (service processes have d2b-bus access, enabling Credential KK; workers do not); acquire listener credential via KK inside the gateway Guest using `config.credentialBindings[listenerCredentialAlias]`; acquire sender credential independently inside the child's gateway Guest via the same KK model using `config.credentialBindings[senderCredentialAlias]`; raw credential bytes held in zeroizing memory inside the gateway Guest, presented to Azure Relay, then immediately zeroized; no credential bytes cross process, network, or Guest boundary; redacted Debug; no credential bytes in logs/audit/OTEL; core ProviderDeployment creates a private persistent Volume (per ADR-046-provider-state) for each component before its Process starts — the transport Provider does not own or create these Volumes; `Provider/volume-local` reconciles them; `migrationPolicy: none` means no migration worker is ever spawned; no relay auth token, WebSocket handle, session key, or credential byte is written to that Volume; all relay session state remains transient in-process memory", "entryContracts" : [ "ADR-046-provider-transport-azure-relay" ], @@ -21432,6 +22349,7 @@ ], "specId" : "ADR-046-provider-transport-azure-relay", "topologicalRank" : 12, + "validation" : "`tests/credential_redaction.rs`: credential bytes never reach any Debug/log/audit/OTEL path; `src/tests/integration/credential_delivery.rs`: end-to-end credential delivery using injected fake Credential effect port", "wave" : "W6" }, { @@ -21439,6 +22357,7 @@ "destinations" : [ "`packages/d2b-provider-transport-azure-relay/src/reconnect.rs`" ], + "detailedDesign" : "Relay service responds to `CloseTransport`+`OpenTransport` cycle from core; core owns reconnect policy and backoff scheduling; relay service tears down the current WebSocket when core calls `CloseTransport` and establishes a new WebSocket connection when core calls `OpenTransport`; relay service does not maintain a backoff state machine or independently retry — it starts a new WebSocket on demand and emits the connect result via `ObserveTransport`; listener and sender are long-lived service processes that do not re-spawn on reconnect", "entryContracts" : [ "ADR-046-provider-transport-azure-relay" ], @@ -21453,6 +22372,7 @@ ], "specId" : "ADR-046-provider-transport-azure-relay", "topologicalRank" : 12, + "validation" : "`tests/reconnect_backoff.rs`: relay responds to CloseTransport/OpenTransport cycle; WebSocket starts on demand; ObserveTransport reports connect result; `src/tests/integration/reconnect_scenario.rs`: full reconnect cycle including Credential re-acquisition", "wave" : "W6" }, { @@ -21460,6 +22380,7 @@ "destinations" : [ "`packages/d2b-provider-transport-azure-relay/src/transport_settings.rs`; `docs/reference/schemas/v3/providers/transport-azure-relay.transport-settings.json`" ], + "detailedDesign" : "`AzureRelayTransportSettings` Rust struct with serde; validation against committed JSON Schema; reject `secret`-annotated fields; enforce `^[a-z][a-z0-9-]*$` pattern for `listenerCredentialAlias`/`senderCredentialAlias` alias ID fields (never `Credential/` refs); xtask `gen-provider-transport-schemas` integration", "entryContracts" : [ "ADR-046-provider-transport-azure-relay" ], @@ -21474,6 +22395,7 @@ ], "specId" : "ADR-046-provider-transport-azure-relay", "topologicalRank" : 12, + "validation" : "`tests/transport_settings_schema.rs`: valid/invalid schema vectors; eval-time Nix assertion coverage from `nix-unit: transport-settings-secret-key` test (see zone-routing spec)", "wave" : "W6" }, { @@ -21481,6 +22403,7 @@ "destinations" : [ "`packages/d2b-provider-transport-azure-relay/src/backpressure.rs`" ], + "detailedDesign" : "Outbound WebSocket send buffer bounded at `MAX_AGGREGATE_NAMED_STREAM_QUEUE_BYTES`; relay WebSocket write backpressure propagates to `FairScheduler` credit; `d2b_relay_transport_backpressure_events_total` counter emitted; no unbounded memory growth under slow relay", "entryContracts" : [ "ADR-046-provider-transport-azure-relay" ], @@ -21495,6 +22418,7 @@ ], "specId" : "ADR-046-provider-transport-azure-relay", "topologicalRank" : 12, + "validation" : "`tests/backpressure_credit.rs`: slow relay writer saturates outbound queue; named-stream credit stalls before unbounded growth; source Zone never buffers beyond aggregate limit", "wave" : "W6" }, { @@ -21502,6 +22426,7 @@ "destinations" : [ "`packages/d2b-provider-transport-azure-relay/src/{metrics.rs, audit.rs}`" ], + "detailedDesign" : "Emit all OTEL metrics and audit records listed in §OTEL and §Audit; closed semantic label sets with no Zone/resource-name-derived keys; retain Zone identity only in the `d2b.zone` OTEL resource attribute; never label secret bytes; provider audit covers **carriage authentication and health observations only** — Azure auth events, WebSocket lifecycle, credential acquisition outcomes — and is **separate from resource audit** (resource lifecycle events are owned by core); audit records appended through the Zone runtime audit log interface (no atomicity guarantee with Zone resource state in redb; best-effort delivery per the Zone's audit provider configuration); OTEL via lightweight emitter ring (no direct OTEL SDK dependency in Provider)", "entryContracts" : [ "ADR-046-provider-transport-azure-relay" ], @@ -21520,6 +22445,7 @@ ], "specId" : "ADR-046-provider-transport-azure-relay", "topologicalRank" : 13, + "validation" : "`tests/credential_redaction.rs` extended to cover audit/OTEL paths; `tests/metric_labels.rs` structurally asserts exact absence of `vm`, `zone`, `zone_id`, `zone_uid`, and resource-name-derived keys and that a Zone-name canary never enters label values; `tests/fake_relay_transport.rs` asserts audit record fields against schema", "wave" : "W6" }, { @@ -21527,6 +22453,7 @@ "destinations" : [ "`packages/d2b-provider-transport-azure-relay/src/tests/integration/README`" ], + "detailedDesign" : "Required content: fake relay server setup and teardown using the injected fake Relay effect port; how to run hermetic integration tests without a live Azure service; how to configure the injected fake Credential effect port for credential delivery tests; how to run with a real Azure namespace (requires a `Credential` resource declared in `spec.config.credentialBindings`, not environment-variable credential paths); integration test scenarios and expected outcomes; CI/local execution instructions", "entryContracts" : [ "ADR-046-provider-transport-azure-relay" ], @@ -21540,6 +22467,7 @@ ], "specId" : "ADR-046-provider-transport-azure-relay", "topologicalRank" : 11, + "validation" : "File must be present; workspace policy gate enforces `src/tests/integration/README`", "wave" : "W6" }, { @@ -21547,6 +22475,7 @@ "destinations" : [ "`packages/d2b-provider-transport-unix/src/credit.rs` (imports `MAX_PACKET_ATTACHMENTS=32`, `RESERVED_CONTROL_FDS=64`, credit-class constants from v3 contract); `src/portal.rs` (imports `MAX_PACKET_ATTACHMENTS` for portal validation)" ], + "detailedDesign" : "Import credit scope capacities and headroom from `v3_zone_session.rs`; add `MAX_OPEN_TRANSPORTS: usize = 256` local constant for handle table bound. Primary reuse disposition: `adapt`. Preserved source-plan detail: Dependency on ADR046-session-001 output.", "entryContracts" : [ "ADR-046-provider-transport-unix" ], @@ -21561,6 +22490,7 @@ ], "specId" : "ADR-046-provider-transport-unix", "topologicalRank" : 11, + "validation" : "`tests/credit.rs::ancillary_capacity_is_derived_from_closed_hard_bounds` passes against v3 constants", "wave" : "W6" }, { @@ -21568,6 +22498,7 @@ "destinations" : [ "`packages/d2b-provider-transport-unix/src/{seqpacket,identity,socket}.rs`" ], + "detailedDesign" : "Copy transport structs verbatim; adapt `PeerIdentityPolicy` to report `PeerCredentials` upward to ComponentSession for subject mapping (not for direct resource lookup — that is core's responsibility); maintain `SO_PASSCRED` setup and first-packet credential extraction as documented; CLOEXEC enforcement uses `rustix` syscall wrappers over `libc` where available Primary reuse disposition: `adapt`. Preserved source-plan detail: copy and adapt.", "entryContracts" : [ "ADR-046-provider-transport-unix" ], @@ -21583,6 +22514,7 @@ ], "specId" : "ADR-046-provider-transport-unix", "topologicalRank" : 12, + "validation" : "Copy all 12 test functions; add `peercred_reported_to_componentsession_not_resolved_to_subject_here`", "wave" : "W6" }, { @@ -21590,6 +22522,7 @@ "destinations" : [ "`packages/d2b-provider-transport-unix/src/{stream,socket}.rs`" ], + "detailedDesign" : "Copy verbatim; add `attachment_support: false` in `TransportDescriptor` (stream never carries SCM_RIGHTS regardless of route class); `admission.rs::validate_route_class` rejects `attachments_enabled=true` for stream Primary reuse disposition: `adapt`. Preserved source-plan detail: copy unchanged.", "entryContracts" : [ "ADR-046-provider-transport-unix" ], @@ -21604,6 +22537,7 @@ ], "specId" : "ADR-046-provider-transport-unix", "topologicalRank" : 12, + "validation" : "`tests/portal.rs::stream_open_transport_forces_no_attachments`; `tests/identity.rs::stream_transport_reassembles_partial_and_coalesced_records`", "wave" : "W6" }, { @@ -21611,6 +22545,7 @@ "destinations" : [ "`packages/d2b-provider-transport-unix/src/credit.rs`" ], + "detailedDesign" : "Copy all five types verbatim; import scope-capacity constants from v3 contract; add `#[derive(Debug)]` with redacted Display (no raw counts in Debug output) Primary reuse disposition: `adapt`. Preserved source-plan detail: copy unchanged.", "entryContracts" : [ "ADR-046-provider-transport-unix" ], @@ -21625,6 +22560,7 @@ ], "specId" : "ADR-046-provider-transport-unix", "topologicalRank" : 12, + "validation" : "Copy all 4 credit test functions; add `credit_released_on_attachment_close` and `emergency_headroom_constant_across_fd_counts`", "wave" : "W6" }, { @@ -21632,6 +22568,7 @@ "destinations" : [ "`packages/d2b-provider-transport-unix/src/descriptor.rs`" ], + "detailedDesign" : "Copy verbatim; adapt `DescriptorPolicy::verify` to produce `AcceptedAttachment` carrying `ObjectIdentity` binding for v3 ComponentSession attachment descriptor model; `pid` not stored beyond liveness check Primary reuse disposition: `adapt`. Preserved source-plan detail: copy and adapt.", "entryContracts" : [ "ADR-046-provider-transport-unix" ], @@ -21646,6 +22583,7 @@ ], "specId" : "ADR-046-provider-transport-unix", "topologicalRank" : 13, + "validation" : "Copy `pidfd_identity_requires_live_launch_evidence_and_rejects_unrelated_process` and `duplicate_kernel_objects_are_rejected_and_cleaned_up`", "wave" : "W6" }, { @@ -21653,6 +22591,7 @@ "destinations" : [ "`packages/d2b-provider-transport-unix/src/admission.rs`" ], + "detailedDesign" : "`validate_route_class(route_class, socket_kind, attachments_enabled, received_fd)` calls `getsockopt(SO_TYPE)` (blocking adapter) on `received_fd`: `SOCK_SEQPACKET` must match `\"seqpacket\"`, `SOCK_STREAM` must match `\"stream\"`, any other type fails `invalid-socket-fd`; if `route_class == RouteClass::ZoneLink && attachments_enabled == true` fail `attachment-policy-conflict` with detail `cross-zone-attachments-forbidden`; if `socket_kind == \"stream\" && attachments_enabled == true` fail `attachment-policy-conflict`; no Noise profile enforcement (that is ComponentSession's responsibility); returns `Ok(RouteAdmission { route_class, socket_kind, attachments_enabled })`", "entryContracts" : [ "ADR-046-provider-transport-unix" ], @@ -21667,6 +22606,7 @@ ], "specId" : "ADR-046-provider-transport-unix", "topologicalRank" : 13, + "validation" : "`tests/admission.rs::seqpacket_fd_passes_seqpacket_kind`; `stream_fd_passes_stream_kind`; `seqpacket_fd_rejects_stream_kind_declaration`; `zone_link_with_attachments_enabled_fails`; `local_portal_seqpacket_with_attachments_accepted`; `stream_with_attachments_enabled_rejected`", "wave" : "W6" }, { @@ -21674,6 +22614,7 @@ "destinations" : [ "`packages/d2b-provider-transport-unix/src/{portal,service}.rs`" ], + "detailedDesign" : "`portal.rs`: `PortalHandler` struct owns a bounded `HashMap` (capacity `MAX_OPEN_TRANSPORTS=256`); `open_transport(req, attachment_fd)` validates via `admission.rs`, dups FD, allocates handle, stores `MonitorState { dup_fd, observation_senders: Vec }`; `close_transport(handle)` closes dup FD, half-closes all observation senders, removes entry; `observe_transport(handle)` registers a new `NamedStreamSender` and spawns an async epoll-watcher task on the dup FD; `TransportHandle` is a `[u8; 16]` random token; redacted in all Debug impls; `service.rs` is the binary entry: accepts the allocator-issued portal endpoint FD at launch, runs `GeneratedTransportServiceServer` over it, dispatches to `PortalHandler` Primary reuse disposition: `adapt`. Preserved source-plan detail: adapt dispatch pattern; implement portal methods as new.", "entryContracts" : [ "ADR-046-provider-transport-unix" ], @@ -21694,6 +22635,7 @@ ], "specId" : "ADR-046-provider-transport-unix", "topologicalRank" : 14, + "validation" : "`tests/portal.rs::open_transport_zone_link_validates_and_returns_ownedtransport`; `open_transport_local_portal_seqpacket_with_attachments_accepted`; `open_transport_zone_link_attachments_enabled_rejected`; `close_transport_is_idempotent_after_handle_removed`; `observe_transport_delivers_pollhup_as_peer_disconnected`; `handle_table_rejects_at_max_capacity`; `restart_clears_all_handles`", "wave" : "W6" }, { @@ -21701,6 +22643,7 @@ "destinations" : [ "`packages/d2b-provider-transport-unix/` crate Cargo.toml binary target `d2b-transport-unix-service`; Provider component descriptor JSON committed at `packages/d2b-provider-transport-unix/descriptor/unix-transport-service.json`; Nix package derivation at `packages/d2b-provider-transport-unix/`" ], + "detailedDesign" : "Component descriptor declares: `processClass=service`, `template=unix-transport-service`, `stateNamespaces=[]` (no Provider state Volume; bounded non-secret operational state in status/core ledger, D087), `sandbox.capabilityClasses=[]`, `sandbox.namespaceClasses=[mount]`, `sandbox.seccompClass=strict`, `budget.memory.limit=\"16Mi\"`, `budget.cpu.limit=\"200m\"`, `budget.fds.limit=512`, `endpoints=[{name:portal,transport:unix,purpose:transport-unix-portal}]`, `readiness={class:provider-defined,initialDelay:\"0s\",timeout:\"5s\",failureThreshold:1,successThreshold:1}`, `restartPolicy={class:always,backoffBase:\"2s\",backoffMax:\"60s\",backoffMultiplier:2.0,maxRestarts:10,resetAfter:\"1h\"}`; Provider package bundles descriptor digest; core ProviderDeployment creates the Process with empty `mounts` when `Provider/transport-unix` is installed Primary reuse disposition: `adapt`. Preserved source-plan detail: adapt; no direct symbol copy.", "entryContracts" : [ "ADR-046-provider-transport-unix" ], @@ -21716,6 +22659,7 @@ ], "specId" : "ADR-046-provider-transport-unix", "topologicalRank" : 15, + "validation" : "`tests/conformance.rs::process_resource_matches_component_descriptor`; `tests/conformance.rs::provider_state_set_is_empty`; `tests/conformance.rs::no_state_volume_mount`; sandbox policy tests against minijail conformance kit", "wave" : "W6" }, { @@ -21723,6 +22667,7 @@ "destinations" : [ "`docs/reference/schemas/v3/providers/transport-unix.transport-binding.json`; `nixos-modules/assertions.nix` (assertion additions); generated `nixos-modules/generated/options-zones-ZoneLink.nix` provider-settings submodule" ], + "detailedDesign" : "Commit the JSON Schema; run `xtask gen-zone-schemas` and `xtask gen-zone-nix-options` to regenerate committed files; add two assertions to `assertions.nix` (stream+attachments conflict; sensitive key names); `xtask gen-zone-resources` adds provider-specific settings validation step", "entryContracts" : [ "ADR-046-provider-transport-unix" ], @@ -21738,6 +22683,7 @@ ], "specId" : "ADR-046-provider-transport-unix", "topologicalRank" : 13, + "validation" : "All seven eval/build tests in the Nix section", "wave" : "W6" }, { @@ -21745,6 +22691,7 @@ "destinations" : [ "`packages/d2b-provider-transport-unix/src/{audit,metrics}.rs`" ], + "detailedDesign" : "`AuditRecordKind` enum with 6 event kinds from Audit section; `AuditRecord` carries only the fields listed (no uid/gid/pid/path/handle/ZoneLink name); emit via Zone runtime `emit_audit_record()` interface; `MetricCounter`/`MetricHistogram` with closed label types per Metrics section; emit via bounded in-process ring to OTEL Provider datagram socket; `tracing::instrument` spans on `PortalHandler` methods with the 3 permitted span attributes only", "entryContracts" : [ "ADR-046-provider-transport-unix" ], @@ -21759,6 +22706,7 @@ ], "specId" : "ADR-046-provider-transport-unix", "topologicalRank" : 15, + "validation" : "`tests/conformance.rs::audit_records_contain_no_pid_uid_or_handle`; `tests/conformance.rs::metric_labels_are_closed_set`; `tests/conformance.rs::span_attributes_contain_no_sensitive_fields`", "wave" : "W6" }, { @@ -21766,6 +22714,7 @@ "destinations" : [ "`packages/d2b-provider-transport-unix/integration/` and `integration/README.md`" ], + "detailedDesign" : "Four scenarios: `transport_open.rs` (fake Zone portal, allocator-socketpair FD attachment in → OwnedTransport attachment out → verify socket kind, CLOEXEC, SO_PASSCRED enabled; p95 latency assertion ≤2 ms); `fd_transfer.rs` (seqpacket `SCM_RIGHTS` transfer through opened transport, credit accounting, scavenge on error injection); `reconnect.rs` (CloseTransport + re-OpenTransport with fresh socketpair, verify previous handle is unknown, verify monitoring dup closed); `observation_stream.rs` (ObserveTransport stream receives `PEER_DISCONNECTED` event when peer closes socketpair end within 5 ms p95). `integration/README.md` documents prerequisites (no KVM required; all scenarios use in-process socketpairs and fake Zone API endpoint stub), invocation (`cargo test -p d2b-provider-transport-unix --test integration`), environment variables, and expected output", "entryContracts" : [ "ADR-046-provider-transport-unix" ], @@ -21783,6 +22732,7 @@ ], "specId" : "ADR-046-provider-transport-unix", "topologicalRank" : 16, + "validation" : "All four scenarios pass in CI; latency assertions enforced using monotonic timestamps; scavenge correctness verified by open-FD count before/after error injection", "wave" : "W6" }, { @@ -21790,6 +22740,7 @@ "destinations" : [ "packages/d2b-contracts/src/usbip_effect_port.rs" ], + "detailedDesign" : "Define UsbipEffectPort and UsbipGuestEffectPort in d2b-contracts with DeviceUid, NetworkUid, UsbBindingUid, LeaseToken, FirewallToken, FirewallObservation, KernelModuleClass, DeviceProbeResult, and UsbipEffectError; export traits/types only with no implementation; keep firewall apply/observe/release Network/busid-scoped, attach/detach Binding-addressed, and all fd/path/busid values private. `TransientDetail` derives `Clone, PartialEq, Eq` while retaining manual redacted Debug/Display so `UsbipEffectError`'s derives compile without disclosure. Primary reuse disposition: `create`. Preserved source-plan detail: net-new trait definition.", "entryContracts" : [ "ADR-046-provider-device-usbip" ], @@ -21803,6 +22754,7 @@ ], "specId" : "ADR-046-provider-device-usbip", "topologicalRank" : 11, + "validation" : "d2b-contracts tests for trait object safety, firewall apply/observe/release signatures, `UsbipEffectError: Clone + PartialEq + Eq`, `TransientDetail` clone/equality, redacted Debug/Display behavior, and no implementation leakage.", "wave" : "W6" }, { @@ -21810,6 +22762,7 @@ "destinations" : [ "packages/d2b-core/src/device_usbip_adapter.rs" ], + "detailedDesign" : "Implement UsbipEffectPort in the core adapter: signed-bundle busid lookup, same-Zone validation, trusted physical-USB identity resolution, mandatory Core-derived `physical-usb-backing/v1` digest and exact `(Host, physical-usb-backing, opaqueKeyDigest)` claim shared with every security-key/USB Provider, exclusive OFD claim, sole ownership of all USBIP TCP/3240 and exact per-Network/per-busid `UsbipBindFirewallRule` effects plus ownership-scoped observe/release, anti-spoof probe, one shared Host module/backend authority, one Core-derived D097 relay Endpoint/firewall authority per Network, D097 authority-index preflight/adoption, and post-effect audit; complete the shared claim before any open, withhold, bind, module, relay, firewall, or attachment effect; never expose raw busid, identity digest, path, fd, bind address, nftables body, audit structs, or broker wire types. Primary reuse disposition: `adapt`. Preserved source-plan detail: extract and adapt into framework-internal adapter.", "entryContracts" : [ "ADR-046-provider-device-usbip" ], @@ -21825,6 +22778,7 @@ ], "specId" : "ADR-046-provider-device-usbip", "topologicalRank" : 14, + "validation" : "Fast packages/d2b-core/tests/device_usbip_adapter.rs covers same-Zone gate, exact shared physical backing tuple derivation, byte-identical keys for USB/security-key views of one fake token, Provider-private-class/digest bypass rejection, `physical-usb-backing-conflict` before effects, separate USBIP module/relay authorities, one-module/one-relay reuse, exact Network/busid firewall scoping, foreign-marker failure, ownership-scoped drift/status, independent per-busid release, no network-local dependency, anti-spoof, redaction, broker mapping, and no digest/busid/path/fd exposure.", "wave" : "W6" }, { @@ -21832,6 +22786,7 @@ "destinations" : [ "packages/d2b-provider-device-usbip/" ], + "detailedDesign" : "Create the required crate layout; bind the shared D098 `UsbService`/`UsbBinding` base versions/fingerprints from ADR046-provider-004 and implement only strict USBIP Provider extensions; sign/register extension schemas and advertise explicit export only for authority `UsbService` resources implemented by this Provider; implement validation.rs and compile-checked EffectPort injection. Declare the controller user/User resource in Nix activation. Primary reuse disposition: `create`. Preserved source-plan detail: net-new crate skeleton with contract reuse.", "entryContracts" : [ "ADR-046-provider-device-usbip" ], @@ -21847,6 +22802,7 @@ ], "specId" : "ADR-046-provider-device-usbip", "topologicalRank" : 12, + "validation" : "make test-policy passes; Cargo.toml has no d2b-priv-broker dependency; fast schema/manifest tests consume the common fixtures, accept canonical minimal base without `spec.provider`, prove a fake direct-local Provider can implement the same base, and cover Service-only exportability, Binding non-exportability, Core projection ownerRef/base fields with explicit `spec.provider` rejection, D088 status layering, semantic factory-fingerprint stability across Provider/adapter identity changes, strict refs, and trait injection.", "wave" : "W6" }, { @@ -21854,6 +22810,7 @@ "destinations" : [ "packages/d2b-provider-device-usbip/src/{controller,reconcile,export_import}.rs" ], + "detailedDesign" : "Reconcile provider-neutral authority/projection `UsbService` and per-Guest `UsbBinding` resources through strict USBIP Provider extensions, consuming UsbipEffectPort, the exact shared Host-global physical USB tuple, USBIP-private D097 authorities, and signed D096 ExportAdapter/ImportAdapter. Enforce same-Zone base and provider refs; ResourceExport authority-Service-only target; same-type ResourceImport-owned projection with `providerRef`, semantic base/import fields, no `spec.provider`, and no physical fields/effects; route selection from the signed local descriptor/import record; semantic factory fingerprint independent of separately authenticated adapter identity; exclusive fair Binding admission; encrypted bounded named-stream control/data; D088 layered status; Service/Binding finalizers; restart adoption; declared/explicit modes; no session/transfer resources. Primary reuse disposition: `adapt`. Preserved source-plan detail: extract and adapt step machine into Provider reconcile loop.", "entryContracts" : [ "ADR-046-provider-device-usbip" ], @@ -21871,6 +22828,7 @@ ], "specId" : "ADR-046-provider-device-usbip", "topologicalRank" : 15, + "validation" : "Fast tests/controller_state_machine.rs, service_binding_schema.rs, export_import.rs, authority_conflict.rs, async_loop.rs, finalizer.rs, and wrong_zone.rs cover authority/projection/Binding lifecycle, Service-only export, projection `spec.provider` rejection, semantic fingerprint stability under adapter identity changes plus separate descriptor authentication, D088 `status.resource`/`status.provider` placement, shared physical tuple collision before effects, encrypted fake streams, no physical projection effect, exclusivity, restart, and WrongZone degradation.", "wave" : "W6" }, { @@ -21878,6 +22836,7 @@ "destinations" : [ "packages/d2b-provider-device-usbip/src/reconcile.rs" ], + "detailedDesign" : "Create/adopt exactly one Host backend Process authority, exactly one D097 Network relay Process/Endpoint/firewall authority bound to TCP 3240 with a Core-derived Network/policy key, and one Binding-owned Guest proxy/private Endpoint per attached Binding. Restrict relay resolution to the Core adapter and exact active-lease Binding proxies; deliver their connected streams by LaunchTicket. Use canonical system-minijail specs, signed templates, bounded budgets/readiness/restart, no argv/path/address/fd fields; attach/detach remains a one-shot EffectPort operation, not a second Process. Primary reuse disposition: `adapt`. Preserved source-plan detail: adapt into singleton Host backend, per-Network relay, and per-Binding Guest proxy management.", "entryContracts" : [ "ADR-046-provider-device-usbip" ], @@ -21892,6 +22851,7 @@ ], "specId" : "ADR-046-provider-device-usbip", "topologicalRank" : 13, + "validation" : "Fast Process/Endpoint shape tests prove one backend per Host, one Core-derived multiplexed TCP 3240 Endpoint/firewall authority per Network, deterministic duplicate conflict, adapter/exact-Binding-only resolution, LaunchTicket-connected stream, no generic Network reader access, Binding ownership/private Guest policy, no per-Device listener, no raw address/argv/path/fd, and readiness before bind/attach.", "wave" : "W6" }, { @@ -21899,6 +22859,7 @@ "destinations" : [ "packages/d2b-provider-device-usbip/src/status.rs" ], + "detailedDesign" : "Define provider-neutral `UsbService` `status.resource` with whole-device availability/access counts, authority-only `physical-usb-backing` claim state, and projection-only common import state; define `UsbBinding.status.resource` with generic attachment phase/queue/generation/timestamps. Define separate strict `status.provider` USBIP details: Device probe, owner backend/relay, USBIP-owned `firewallState`/opaque digest/observed generation, imported-route observations, and Binding proxy/private Endpoint/subphase. No common access/import/attachment field appears directly under `status`; no USBIP module, Network, Endpoint, proxy, server/client, port, firewall, or busid field enters base status; no raw backing digest, busid, path, fd, address, session/transfer ID, remote identity, or payload appears anywhere. Primary reuse disposition: `adapt`. Preserved source-plan detail: adapt state fields to typed status.provider.details.", "entryContracts" : [ "ADR-046-provider-device-usbip" ], @@ -21913,6 +22874,7 @@ ], "specId" : "ADR-046-provider-device-usbip", "topologicalRank" : 13, + "validation" : "Fast tests/status_serde.rs covers generic base status plus three strict USBIP detail schemas, exact `status.resource` placement for access/import/attachment/backing claim, exact `status.provider` placement for relay/firewall implementation observations, drift transition and ownership, rejection of semantic fields directly under `status`, mode-dependent omissions, bounded counts/refs, unknown-field denial, and a deny corpus proving USBIP-only fields are rejected from base and Network status.", "wave" : "W6" }, { @@ -21920,6 +22882,7 @@ "destinations" : [ "packages/d2b-provider-device-usbip/{src,tests,integration/README.md}; tests/host-integration/usbip-service.nix; tests/host-integration/hardware/usbip-service.sh" ], + "detailedDesign" : "Put provider-neutral Service/Binding base-schema separation, strict USBIP extensions, projection `spec.provider` rejection, D088 layered status, semantic factory-fingerprint independence from Provider/adapter identity, exact shared physical backing tuple/conflict, one Core-derived per-Network relay Endpoint/firewall authority, exact per-busid firewall ownership/drift/release, arbitration, same-type export/import, encrypted fake-stream, and least-privilege process/Endpoint shape coverage in fast Layer-1 Rust tests. Include a fake direct-local Provider proving the same base contract has no USBIP dependency. Reserve runNixOSTest for real Linux usbip_host/vhci_hcd, usbipd, namespaces/nftables, TCP 3240, zero network-local carve-out, and Guest checks; reserve the hardware script for an approved physical device. Use existing Make gates only. Primary reuse disposition: `adapt`. Preserved source-plan detail: adapt existing network-scoping assertion and add new scenarios.", "entryContracts" : [ "ADR-046-provider-device-usbip" ], @@ -21935,6 +22898,7 @@ ], "specId" : "ADR-046-provider-device-usbip", "topologicalRank" : 16, + "validation" : "`make test-host-integration` runs the non-hardware real-kernel case on a capable host; `make test-hardware` runs the explicit manual device case. No Layer-1 test opens a device, loads a module, creates a namespace, or listens on a socket.", "wave" : "W6" }, { @@ -21942,6 +22906,7 @@ "destinations" : [ "nixos-modules/components/usbip.nix, nixos-modules/options-zones.nix, nixos-modules/assertions.nix" ], + "detailedDesign" : "Add Provider config; remove the old per-VM option; emit provider-neutral authority `UsbService`, per-Guest `UsbBinding`, strict USBIP Provider envelopes, and optional ResourceExport/ResourceImport authoring shapes; imported same-type projection Services remain Core-created with no `spec.provider`. Assert same-Zone base/provider refs, projection ownerRef/forbidden implementation and physical fields, Service-only export target, one Host backend/Network relay, exact Core-derived Host-global `(Host, physical-usb-backing, opaqueKeyDigest)` exclusion shared by USB/security-key, Provider-private-class bypass rejection, and retain guest vhci_hcd/tools. Primary reuse disposition: `adapt`. Preserved source-plan detail: adapt guest module, remove host-side option surface, and extend eval assertions.", "entryContracts" : [ "ADR-046-provider-device-usbip" ], @@ -21958,6 +22923,7 @@ ], "specId" : "ADR-046-provider-device-usbip", "topologicalRank" : 15, + "validation" : "Fast tests/unit/nix/cases/usbip-*.nix cover schema shape, Core projection `spec.provider` rejection, D088 layered status fixtures including USBIP-owned firewall state, all reference/owner/export assertions, byte-identical USB/security-key tuple collision and private-class bypass rejection, one Core-derived 3240 Endpoint/firewall authority per Network, least-privilege consumer policy, absence of network-local 3240 rules, old-option removal, and guest module retention.", "wave" : "W6" }, { @@ -21965,6 +22931,7 @@ "destinations" : [ "packages/d2bd/src/, nixos-modules/network.nix, packages/d2b-core/src/processes.rs" ], + "detailedDesign" : "Remove daemon-coupled USBIP after Provider tests and integration tests pass: delete per-env autostart, state machine, and reconcile state modules after migration; remove USBIP firewall block from network.nix; remove ProcessRole::Usbip; run Layer-1 gates and confirm no d2bd or network.nix references remain outside the adapter and contracts. Primary reuse disposition: `delete-after-cutover`. Preserved source-plan detail: delete after Provider replacement reaches parity.", "entryContracts" : [ "ADR-046-provider-device-usbip" ], @@ -21980,6 +22947,7 @@ ], "specId" : "ADR-046-provider-device-usbip", "topologicalRank" : 16, + "validation" : "make test-unit and make test-flake plus grep or contract checks for removed symbols and no residual d2bd/network.nix USBIP lifecycle references.", "wave" : "W6" }, { @@ -21987,6 +22955,7 @@ "destinations" : [ "`packages/d2b-core-controller/src/user_session_authority.rs` (or a core/user-agent per-session agent Process under `Provider/system-systemd`); `AuthorityDescriptor` on the session authority" ], + "detailedDesign" : "Name and implement the **fixed user-session authority** (D097 desktop/session): `authorityScope: seat` bound to `(Host, User, login-session/seat)`, opaque `authorityKey` (never a raw socket path/XDG_RUNTIME_DIR/DISPLAY/seat name), `cardinality: exactly-one` per `(Host, User, login-session)`, `arbitration: exclusive`, owner = a core/user-agent per-user-session agent Process (NOT a new Provider), adoption by `ownerProof` (agent Process identity + login-session id), `exportability: forbidden`. It is the sole opener of the compositor/PipeWire/session-bus FDs and hands them to desktop Providers only via the EffectPort/LaunchTicket. Core's authority index rejects a duplicate session authority (or a duplicate same-user display portal, clipboard host, notification sink, audio mediator, systemd user manager, Secret Service, or seat-input claimant) with `duplicateConflict` before any FD open; multi-user/seat is admitted only up to the declared per-Host limit. Guest-stop invalidates every session authority/lease bound to that Guest across display/audio/notification/credential/shell in one dependency-aware cascade (D091), with no stale FD surviving. Host input (`wl_seat`/pointer constraints) is an `at-most-one`-per-seat authority under this session authority; pointer-constraint enforcement is a declared boundary until an interaction Provider implements it. Primary reuse disposition: `adapt`. Preserved source-plan detail: net-new (name and implement the shared user-session authority).", "entryContracts" : [ "ADR-046-resources-host-guest-process-user" ], @@ -22001,6 +22970,7 @@ ], "specId" : "ADR-046-resources-host-guest-process-user", "topologicalRank" : 12, + "validation" : "Single session authority per `(Host, User, session)`; duplicate same-user session authority / desktop service rejected with `duplicateConflict`; multi-seat declared-limit enforcement; Guest-stop invalidates all bound desktop/audio/notification/credential/shell authorities and leases (no stale compositor/PipeWire/session-bus FD); seat-input second claimant rejected; adoption by `ownerProof` and quarantine on ambiguity; hermetic with fakes", "wave" : "W5" }, { @@ -22008,6 +22978,7 @@ "destinations" : [ "`d2b-contracts/src/v3/volume_layout.rs` (LayoutEntry, EntryType, all policy enums, AclGrant, Invariant, SensitivityClass); `d2b-contracts/src/v3/volume_spec.rs` (VolumeSpec, ViewSpec, Attachment, QuotaSpec, SourceKind, `SourcePolicyId` opaque newtype); `d2b-contracts/src/v3/effect_port.rs` (`VolumeEffectPort` trait, opaque ID newtypes `VolumeId`/`LayoutEntryId`/`UserId`/`ViewId` each with custom redacted Debug, and `VolumeMountToken` opaque handle with custom redacted Debug)" ], + "detailedDesign" : "All LayoutEntry fields as documented in this dossier; enum value names preserved from `StoragePathKind`/policy enums with renames where noted; `User/` ACL principal (no numeric UID); `sourcePolicyId` opaque newtype replaces raw `hostPath` in `SourceKind::LocalPath` and `SourceKind::BlockImage` Primary reuse disposition: `adapt`. Preserved source-plan detail: extract and adapt.", "entryContracts" : [ "ADR-046-provider-volume-local" ], @@ -22022,6 +22993,7 @@ ], "specId" : "ADR-046-provider-volume-local", "topologicalRank" : 11, + "validation" : "Schema golden vectors; round-trip serde; ACL principal validation rejects numeric forms; `sourcePolicyId` present; no `hostPath` field in any volume_spec contract", "wave" : "W6" }, { @@ -22029,6 +23001,7 @@ "destinations" : [ "Full `packages/d2b-provider-volume-local/` scaffold per §Crate layout: `src/`, `tests/`, `integration/`, `README.md`; crate `Cargo.toml` depends only on `d2b-contracts`, `d2b-provider`, `d2b-provider-toolkit`" ], + "detailedDesign" : "`AnchoredDir`, `AnchoredResource`, `LeafName`, `RelativePath`; adapted `AtomicFilesystem`/`StateEnvelope`; adapted `LockGuard`/`LockSet`/`OfdTransfer`; marker write/verify/check; `src/effect_port.rs` re-exports `VolumeEffectPort` trait from `d2b-contracts::v3::effect_port` and provides Provider-side opaque ID construction helpers (no adapter implementation; adapter lives in host runtime); `sourcePolicyId` validation against declared policy list; no `openat2`/`setfacl`/`fallocate`/numeric-UID call sites in Provider crate Primary reuse disposition: `adapt`. Preserved source-plan detail: copy-unchanged (`path.rs`); adapt (`atomic.rs`, `lock.rs`); adapt swtpm_dir marker algorithm.", "entryContracts" : [ "ADR-046-provider-volume-local" ], @@ -22043,6 +23016,7 @@ ], "specId" : "ADR-046-provider-volume-local", "topologicalRank" : 12, + "validation" : "All `tests/marker.rs`, `tests/state.rs` scenarios; all `integration/provision.rs` scenarios; `cargo deny check` verifies no `d2b-priv-broker`/`d2bd` dependency", "wave" : "W6" }, { @@ -22050,6 +23024,7 @@ "destinations" : [ "`src/controller.rs`, `src/layout.rs`, `src/acl.rs`, `src/source.rs`" ], + "detailedDesign" : "Async reconcile loop; topological LayoutEntry evaluation; `VolumeEffectPort` semantic op dispatch (no direct broker connection; no `openat2`/`setfacl` call sites); ACL reconciliation cycle via effect port; drift detection; status write with expected revision; `sourcePolicyId` validation against declared `sourcePolicies`; controller watch remains responsive while per-resource effect calls run concurrently; **single watch scope** `providerRef: Provider/volume-local` — physical state reconciliation for all served Volumes (layout/ACL/quota/marker); ProviderDeployment creates/deletes Volume instances; volume-local does not issue create/delete API calls; Nix-preprovisioned `User/` layout principals; no cross-component Volume sharing; each component consumes only its declared view; empty-payload stateNamespace Volumes use `migrationPolicy: none` — no migration EphemeralProcess dispatched", "entryContracts" : [ "ADR-046-provider-volume-local" ], @@ -22065,6 +23040,7 @@ ], "specId" : "ADR-046-provider-volume-local", "topologicalRank" : 13, + "validation" : "`tests/layout_provision.rs`, `tests/layout_repair.rs`, `tests/layout_adopt.rs`, `tests/acl.rs`, `tests/view_rights.rs`, `tests/source.rs`, `integration/provision.rs`", "wave" : "W6" }, { @@ -22072,6 +23048,7 @@ "destinations" : [ "`src/store_view.rs`; `tests/store_view.rs`; `integration/store_view.rs`" ], + "detailedDesign" : "Store-view LayoutEntry matrix (see §Same-filesystem hardlink farm); private-NS sync via `run_store_sync` effect op; `StoreSyncComplete` semantic operation; OFD lock semantics; `gcroots/` and `state/` at store-view root; spec-correction enforcement", "entryContracts" : [ "ADR-046-provider-volume-local" ], @@ -22086,6 +23063,7 @@ ], "specId" : "ADR-046-provider-volume-local", "topologicalRank" : 14, + "validation" : "`tests/store_view.rs` all invariants; `integration/store_view.rs` same-filesystem boundary; private-NS sync with concurrent reader", "wave" : "W6" }, { @@ -22093,6 +23071,7 @@ "destinations" : [ "`src/swtpm_volume.rs`; `tests/swtpm_volume.rs`; `integration/swtpm_marker.rs`" ], + "detailedDesign" : "TPM LayoutEntry matrix; `create-if-never-provisioned` + fail-closed repair; broker-maintained provisioning marker; ancestor traverse ACL; `previously-provisioned-swtpm-state-missing` fail-closed detection; `secret-adjacent` sensitivity enforcement", "entryContracts" : [ "ADR-046-provider-volume-local" ], @@ -22107,6 +23086,7 @@ ], "specId" : "ADR-046-provider-volume-local", "topologicalRank" : 13, + "validation" : "`tests/swtpm_volume.rs` all scenarios; `integration/swtpm_marker.rs` real broker-maintained marker", "wave" : "W6" }, { @@ -22114,6 +23094,7 @@ "destinations" : [ "`src/source.rs` (block-image and tmpfs branches); `tests/source.rs`; `integration/block_image.rs`" ], + "detailedDesign" : "`block-image`: image file create/verify via `provision_block_image` effect op; `fallocate` performed by adapter when `preallocate: true`; FD transfer to Guest runtime via LaunchTicket via `open_volume_mount_token` effect op; `tmpfs`: `mount_tmpfs`/`umount_tmpfs` effect ops; `size=` and `nr_inodes=` derived from quota fields; cleanup via `umount_tmpfs` op", "entryContracts" : [ "ADR-046-provider-volume-local" ], @@ -22128,6 +23109,7 @@ ], "specId" : "ADR-046-provider-volume-local", "topologicalRank" : 14, + "validation" : "`tests/source.rs` allowlist pass/fail; block-image/tmpfs eval constraints; `integration/block_image.rs` real image lifecycle", "wave" : "W6" }, { @@ -22135,6 +23117,7 @@ "destinations" : [ "`src/{migration,snapshot}.rs`; `tests/{migration_unit,snapshot_unit}.rs`; `integration/{migration,snapshot}.rs`" ], + "detailedDesign" : "Schema migration (see §Schema migration) via `volume-migration-worker` EphemeralProcess; Snapshot create/list/expire (see §Snapshots) via `volume-snapshot-worker` EphemeralProcess; controller reports `stateSchemaPhase` and `snapshots` in Volume status. Sealing lifecycle (`sealingStatus`, key-shred) is a core/framework concern dispatched through `VolumeEffectPort` semantic ops, not a Provider-owned EphemeralProcess worker.", "entryContracts" : [ "ADR-046-provider-volume-local" ], @@ -22153,6 +23136,7 @@ ], "specId" : "ADR-046-provider-volume-local", "topologicalRank" : 14, + "validation" : "All `tests/migration_unit.rs`, `tests/snapshot_unit.rs`, `integration/migration.rs`, `integration/snapshot.rs` scenarios", "wave" : "W6" }, { @@ -22160,6 +23144,7 @@ "destinations" : [ "`src/relocation.rs`; `tests/relocation_unit.rs`; `integration/relocation.rs`" ], + "detailedDesign" : "As documented in §Relocation, §Retention, §Incident hold, §Unclaimed Volume GC, §Destruction", "entryContracts" : [ "ADR-046-provider-volume-local" ], @@ -22175,6 +23160,7 @@ ], "specId" : "ADR-046-provider-volume-local", "topologicalRank" : 15, + "validation" : "All `tests/relocation_unit.rs`, `integration/relocation.rs` scenarios; destruction ordering under fault injection", "wave" : "W6" }, { @@ -22182,6 +23168,7 @@ "destinations" : [ "`src/audit.rs`; `src/otel.rs`; `src/error.rs`; `tests/audit_unit.rs`; `integration/audit.rs`" ], + "detailedDesign" : "Event types and Zone audit emission per §Audit events; OTEL metric definitions per §OTEL metrics with closed semantic labels and no Zone/resource-name-derived dimensions; Zone identity remains in `d2b.zone` resource attributes; error catalog per §Error catalog; no-path invariant enforced in all outputs", "entryContracts" : [ "ADR-046-provider-volume-local" ], @@ -22196,6 +23183,7 @@ ], "specId" : "ADR-046-provider-volume-local", "topologicalRank" : 12, + "validation" : "`tests/audit_unit.rs` golden records and structural metric descriptor assertions for exact absence of `vm`, `zone`, `zone_id`, `zone_uid`, and resource-name-derived keys plus resource-name canary absence; `tests/error_messages.rs` bounded messages; `integration/audit.rs` live stream", "wave" : "W6" }, { @@ -22203,6 +23191,7 @@ "destinations" : [ "`nixos-modules/zone-resources.nix` (per §ADR046-pstate-010); `root-config.schema.json` in the Provider package" ], + "detailedDesign" : "`sourcePolicies` in Provider root config (opaque IDs; no raw host paths); path prefix injection by resource compiler into private bundle (never into ResourceSpec or operator-authored Nix); `controllerExecutionRef` in Provider config; all eval-time validation rules per §Nix configuration including `sourcePolicyId` validation; artifact catalog entry; Provider and Volume resource authoring shapes", "entryContracts" : [ "ADR-046-provider-volume-local" ], @@ -22218,6 +23207,7 @@ ], "specId" : "ADR-046-provider-volume-local", "topologicalRank" : 12, + "validation" : "All Nix eval-time validation rules; `contentId` determinism; credential-ref guard; unknown Provider config key → build fail", "wave" : "W6" }, { @@ -22225,6 +23215,7 @@ "destinations" : [ "`packages/xtask/src/provider_crate_policy.rs`; `tests/unit/gates/provider-crate-layout-check.sh`" ], + "detailedDesign" : "`cargo xtask check-provider-crate-layout` gate asserts `src/`, `tests/`, `integration/` (with at least one `.rs` file and a `README.md`), and `README.md` for every `packages/d2b-provider-*` workspace member; fails closed with typed `missing-provider-crate-path` error Primary reuse disposition: `adapt`. Preserved source-plan detail: extend (per ADR046-pstate-011).", "entryContracts" : [ "ADR-046-provider-volume-local" ], @@ -22240,6 +23231,7 @@ ], "specId" : "ADR-046-provider-volume-local", "topologicalRank" : 13, + "validation" : "Gate detects each missing path; idempotent across re-runs; existing non-provider `d2b-*` crates not flagged", "wave" : "W6" }, { @@ -22247,6 +23239,7 @@ "destinations" : [ "`packages/d2b-host/src/volume_effect_adapter.rs` (or the equivalent host-runtime crate designated by the Zone broker owner); implements the `VolumeEffectPort` trait defined in `d2b-contracts`" ], + "detailedDesign" : "Adapter holds trusted FD table keyed by `VolumeId`; resolves `SourcePolicyId` to host path prefix from private bundle; calls `openat2(RESOLVE_BENEATH)` anchored at retained FD for all FS ops; calls `setfacl`/`acl_set_fd`, `mount`/`umount`, `fallocate` from within adapter only; emits path-free audit records for each op (audit is never atomic with redb write); injected into controller via Zone runtime ComponentSession; blocking filesystem calls run in bounded blocking-thread pool Primary reuse disposition: `adapt`. Preserved source-plan detail: adapt into adapter.", "entryContracts" : [ "ADR-046-provider-volume-local" ], @@ -22262,6 +23255,7 @@ ], "specId" : "ADR-046-provider-volume-local", "topologicalRank" : 13, + "validation" : "Adapter hermetic tests: each effect op called with mock FD table and bundle; no path in any output; anchored-path rejection for RESOLVE_BENEATH violations; `cargo deny check` verifies adapter does not expose raw paths to Provider crate; `integration/provision.rs` exercises full adapter path", "wave" : "W6" }, { @@ -22269,6 +23263,7 @@ "destinations" : [ "Zone core ProviderDeployment controller-start path (outside `d2b-provider-volume-local`)" ], + "detailedDesign" : "The volume-local controller declares no Provider state Volume, so there is no bootstrap Volume, no `BootstrapProviderStateVolume` broker op, no pre-provisioned controller Volume, and no bootstrap-storage exception (D086, superseded by D087). On first install and on every daemon restart, core ProviderDeployment starts the volume-local controller Process directly; the controller reaches `Ready` from its own resource `status`, the core Operation ledger, and a resource-store relist. Once Ready, it reconciles every Volume carrying `providerRef: Provider/volume-local` (operator-created Volumes and other Providers' declared state Volumes) as they appear in its `providerRef` watch, re-verifying identity markers against external reality, never creating them itself. A Guest bootstraps its own Guest-local volume-local instance from Guest-local primitives only.", "entryContracts" : [ "ADR-046-provider-volume-local" ], @@ -22284,6 +23279,7 @@ ], "specId" : "ADR-046-provider-volume-local", "topologicalRank" : 14, + "validation" : "`integration/provider_state.rs`: controller starts and reaches Ready with no state Volume; served Volumes reconciled and markers re-verified after restart; no bootstrap Volume and no bootstrap Provider Process in the resource list", "wave" : "W6" }, { @@ -22291,6 +23287,7 @@ "destinations" : [ "`packages/d2b-contracts/src/v3/volume.rs`, `volume_layout.rs`, `volume_attachment.rs`" ], + "detailedDesign" : "Complete Volume ResourceSpec, LayoutEntry, all policy enums (values preserved from baseline), AclGrant, ViewSpec, AttachmentSpec, quota placeholder, strict serde unknown-field rejection, canonicalization, bounds Primary reuse disposition: `adapt`. Preserved source-plan detail: extract and adapt.", "entryContracts" : [ "ADR-046-resources-volume" ], @@ -22304,6 +23301,7 @@ ], "specId" : "ADR-046-resources-volume", "topologicalRank" : 10, + "validation" : "Golden JSON spec vectors; serde unknown-field; path anchor/depth/traversal validators; ACL grant bounds; policy enum coverage", "wave" : "W5" }, { @@ -22311,6 +23309,7 @@ "destinations" : [ "`packages/d2b-provider-volume-local/src/` (layout engine, store_view.rs, swtpm_volume.rs, broker op adapters); `packages/d2b-provider-volume-local/tests/` (hermetic layout/store-view/swtpm tests); `packages/d2b-provider-volume-local/integration/` (container Host-path and store-view FS fixtures); `packages/d2b-provider-volume-local/README.md`" ], + "detailedDesign" : "volume-local controller: layout engine (provision/repair/cleanup/adopt per policy), store-view mode (hardlink farm from `hardlink_farm.rs`, private-NS sync, zero-length marker, `gcroots/` and `state/` at store-view root, `sync.lock` OFD), swtpm volume hardening (provisionIfNeverProvisioned + marker + fail-closed repair as in `swtpm_dir.rs`), path-free broker audit ops, storage lifecycle report, opaque BundleOpId contract preserved from `storage_contract.rs` Primary reuse disposition: `adapt`. Preserved source-plan detail: extract and adapt.", "entryContracts" : [ "ADR-046-resources-volume" ], @@ -22325,6 +23324,7 @@ ], "specId" : "ADR-046-resources-volume", "topologicalRank" : 11, + "validation" : "`tests/unit/nix/cases/per-vm-state-ownership.nix` adapted to Volume LayoutEntry matrix; `tests/unit/smoke/smoke-eval-tpm.nix` migrated to TPM Volume invariant; `d2b-contract-tests/tests/storage_sync_contracts.rs` parity tests adapted; new: store-view same-filesystem, zero-length marker existence, sync.lock preserve-OFD, gcroots at store-view root (not meta/), state/ dir existence, swtpm fail-closed-on-missing-after-provision, anchored-path validators", "wave" : "W5" }, { @@ -22332,6 +23332,7 @@ "destinations" : [ "`packages/d2b-provider-volume-virtiofs/src/` (controller, virtiofsd_argv.rs); `packages/d2b-provider-volume-virtiofs/tests/` (hermetic argv/lifecycle/ADR-0021 tests); `packages/d2b-provider-volume-virtiofs/integration/` (virtiofsd launch and guest-mount fixtures); `packages/d2b-provider-volume-virtiofs/README.md`" ], + "detailedDesign" : "volume-virtiofs controller: attachment lifecycle, owned virtiofsd Process create/update/delete, argv generation (reuse current 14 tests), ADR 0021 invariant (`capabilityClasses: []`, `startRoot: false`, `sandbox: chroot`, user-NS via `userNamespace.mappingClass: process-principal-root`), per-attachment export socket readiness check (`unix-socket-exists` readiness kind; current v2 socket path: `/run/d2b/vms//-virtiofs-.sock`; v3: stable hash-derived private path under Zone runtime directory, never exposed in spec/status/API), guest-mount status observation, finalizer drain Primary reuse disposition: `adapt`. Preserved source-plan detail: extract and adapt.", "entryContracts" : [ "ADR-046-resources-volume" ], @@ -22346,6 +23347,7 @@ ], "specId" : "ADR-046-resources-volume", "topologicalRank" : 11, + "validation" : "Migrated `virtiofsd_argv` unit tests (14 tests); `tests/tools/gen-migration-ledger.sh` virtiofsd-argv-shape gate adapted; `minijail-validator-virtiofsd` gate adapted to Process sandbox spec; new: attachment lifecycle (create/ready/delete), ADR 0021 invariant rejection test, multi-attachment isolation, readOnly flag per access mode, store-view shared-dir = store-view/live (never /nix/store)", "wave" : "W5" }, { @@ -22353,6 +23355,7 @@ "destinations" : [ "`nixos-modules/resources-volume.nix`, `nixos-modules/options-volumes.nix`" ], + "detailedDesign" : "Nix resource compiler for Volume/LayoutEntry/View/Attachment from d2b.zones config; strict schema validation; emit canonical JSON per Volume; generate store-view Volume per Guest (from current `d2b.vms.` → future flat `d2b.zones..resources.` with `type = \"Guest\"`) with hardlink-farm layout (gcroots/, state/ at root per `hardlink_farm.rs`); generate swtpm Volume for TPM-enabled Guests; emit volume-virtiofs attachment spec per virtiofs share; migration: store-view stateDir root configuration", "entryContracts" : [ "ADR-046-resources-volume" ], @@ -22367,6 +23370,7 @@ ], "specId" : "ADR-046-resources-volume", "topologicalRank" : 11, + "validation" : "nix-unit cases for store-view Volume output (gcroots at root), TPM Volume spec, virtiofs attachment spec, anchored-path rejection; render parity with current storage.json path rows; canonical JSON golden vector; Provider schema validation rejection; symlink target validation; bundle digest coverage", "wave" : "W5" }, { @@ -22374,6 +23378,7 @@ "destinations" : [ "`packages/d2b-provider-volume-local/src/` (block-image, quota, snapshots, tmpfs, ACL reconciliation); `packages/d2b-provider-volume-local/tests/` (hermetic quota/tmpfs/ACL/block-image/snapshot tests); `packages/d2b-provider-volume-local/integration/` (block-image virtio-blk, FS-without-quota fixture, tmpfs memory-budget); `packages/d2b-provider-volume-virtiofs/src/` (single-writer enforcement, shared-write, private socket path contract); `packages/d2b-provider-volume-virtiofs/tests/` (single-writer rejection, shared-write capability gate, socket-path invariant); `packages/d2b-provider-volume-virtiofs/integration/` (shared-write cross-Guest fixture)" ], + "detailedDesign" : "(1) **block-image SourceKind**: add `SourceKind::BlockImage` to volume-local; manage raw/qcow2 image file lifecycle; emit virtio-blk attachment spec consumed by Guest Provider; `quota.maxBytes` required; add store-overlay.img migration path for current `DiskInit` plan-op. (2) **Quota hard enforcement**: implement `enforcement: hard` capability check in volume-local at Volume creation time; query backing FS for quota/limits support; test with no-project-quota fixture; enforce `maxBytes`/`maxInodes` via xfs project quota or ext4 per-dir quota where available. (3) **Volume snapshots/migrations**: design and implement EphemeralProcess templates in volume-local catalog for snapshot (copy-on-write or rsync capture) and content migration (atomic rename + sync); no CLI-only path; all operations surface through resource API. (4) **Single-writer enforcement**: volume-virtiofs controller tracks active `read-write` attachment count; rejects a second `read-write` attachment while one is active (returns `ResourceConflict` error); `shared-write` mode accepted only if Provider declares `supportsSharedWrite: true`. (5) **tmpfs source**: implement tmpfs mount/unmount lifecycle in volume-local; `maxBytes` → `size=`, `maxInodes` → `nr_inodes=` mount options; charge memory against Host/Guest budget; cleanup unmounts on Volume deletion or restart. (6) **Bounds enforcement**: enforce max 1024 layout entries, 64 Views, 64 attachments at schema validation layer; add corresponding row to API request-size limit table in `ADR-046-resource-api-and-authorization`. (7) **File/symlink first-class lifecycle**: implement independent `createPolicy`/`repairPolicy`/`cleanupPolicy` for `file` and `symlink` entries; implement `target` field validation (relative, no `..`, must resolve within Volume root); `symlink` create writes the target link. (8) **ACL principal ResourceRef**: remove bare `{type,ref}` struct from AclGrant; implement `User/` ResourceRef resolution with User resource watch and re-reconcile on User revision change. (9) **Continuous ACL reconciliation**: implement `foreignChildPolicy: preserve|fail` in broker reconcile loop; re-apply `accessAcl`/`defaultAcl` to all existing entries and children on every repair cycle; emit `ForeignAclViolation` condition when `foreignChildPolicy: fail` and unexpected entries found. (10) **virtiofsd socket path contract**: implement stable hash-derived private socket path in volume-virtiofs (deterministic hash of Zone name + Volume name + attachment executionRef); assert path never appears in public status, spec, audit, or CLI output; validate with a dedicated security invariant test.", "entryContracts" : [ "ADR-046-resources-volume" ], @@ -22389,6 +23394,7 @@ ], "specId" : "ADR-046-resources-volume", "topologicalRank" : 12, + "validation" : "(1) `VirtioblkArgvInput` unit tests; block-image integration fixture. (2) Quota-enforcement fixture with FS-without-quota; hard-enforcement failure test. (3) EphemeralProcess snapshot lifecycle test; content-migration parity test. (4) Single-writer rejection test; shared-write capability gate test. (5) tmpfs mount/unmount lifecycle test; memory-budget accounting assertion. (6) Schema bound rejection tests (1025 entries, 65 views, 65 attachments). (7) File/symlink independent lifecycle tests; target validation (absolute rejected, `..` rejected, escape rejected). (8) ACL principal ResourceRef validation; numeric form rejected; User revision trigger test. (9) foreignChildPolicy preserve/fail tests; continuous repair cycle test. (10) Socket path invariant test; no-status-leak assertion.", "wave" : "W5" }, { @@ -22396,6 +23402,7 @@ "destinations" : [ "`nixos-modules/resources-volume.nix` (Nix eval-time schema validation, canonical JSON emission, bundle digest); `packages/d2b-core-controller/src/configuration.rs` (config-publication handler cleanup logic); `packages/d2b-contracts/src/v3/zone_bundle.rs` (bundle index schema)" ], + "detailedDesign" : "**Nix eval/build validation**: implement all 15 validation steps in §Nix eval/build validation as Nix assertions; abort build on any failure with structured error (Volume name + field path + error class); provider-specific settings schema (`root-config.schema.json`, `attachment.schema.json`) read from the private artifact catalog entry for each Provider's `artifactId` by the resource compiler; validate against `lib.evalModules`-compatible schema; emit canonical sorted JSON with all defaults materialized; compute SHA-256 per resource; emit Zone resource bundle with `generationId` and `bundleDigest`. **Config-publication handler cleanup**: on new bundle activation, diff resources with `metadata.managedBy = \"configuration\"` between new and prior bundle; issue async Delete for resources absent from new bundle; mark deleted resources with `ConfigurationRemoved` condition; track pending-cleanup set in Zone status; Zone status is `Degraded/PendingCleanup` while prior-generation deletions are in progress; activation is immediate but Zone readiness reflects cleanup completion. **Config-owned vs controller-created distinction**: `metadata.managedBy = \"configuration\"` is the authoritative marker set by core at activation; the bundle index carries only digests; controller-created resources have `metadata.managedBy = \"controller\"` and are never touched by the configuration cleanup pass. **Prior generation retention**: retain `priorGenerationCount` prior generations (default 3, range 1..16); no time-based TTL; when count is exceeded prune oldest generation from the store with a tamper-evident audit record. **Generation reactivation**: re-activate any retained prior bundle via `ActivateGeneration` operation; cancel in-flight Deletes for resources being reinstated; issue Deletes for resources added by the aborted new generation.", "entryContracts" : [ "ADR-046-resources-volume" ], @@ -22411,6 +23418,7 @@ ], "specId" : "ADR-046-resources-volume", "topologicalRank" : 12, + "validation" : "Tests per §Cleanup contract — Tests for removed-resource cleanup table (10 tests); nix-unit: `volume_canonical_json_golden_vector`, `volume_bundle_digest_covers_all_resources`, `provider_schema_validation_rejects_unknown_fields`, `symlink_target_escape_rejected_at_eval`, `tmpfs_without_quota_rejected_at_eval`, `layout_bounds_1025_entries_rejected`, `attachment_bounds_65_rejected`, `conflicting_host_paths_rejected`; integration: cleanup audit redaction, generation reactivation, prior-generation pruning", "wave" : "W5" }, { @@ -22418,6 +23426,7 @@ "destinations" : [ "`packages/d2b-provider-transport-vsock/src/effect_port.rs`; test fake in `tests/effect_port_mock.rs`; redaction checks in `tests/redaction.rs`." ], + "detailedDesign" : "Implement `VsockEffectPort` trait and `OpaqueEndpointId`/`OpaqueBindingId` newtypes. Define `VsockEffectPort` async trait and opaque ID newtypes in `effect_port.rs`; implement `FakeVsockEffectPort` for tests; `redaction.rs` asserts no raw `u32` in any `Debug`/`Display` output of opaque types; no real vsock socket opened. Primary reuse disposition: `create`. Preserved source-plan detail: net-new trait/newtypes with redaction tests; no real vsock socket opened.", "entryContracts" : [ "ADR-046-provider-transport-vsock" ], @@ -22432,6 +23441,7 @@ ], "specId" : "ADR-046-provider-transport-vsock", "topologicalRank" : 11, + "validation" : "Proof type: hermetic unit + redaction test; `tests/effect_port_mock.rs` and `tests/redaction.rs`.", "wave" : "W6" }, { @@ -22439,6 +23449,7 @@ "destinations" : [ "`packages/d2b-provider-transport-vsock/src/framing.rs` and `src/bridge.rs`; tests in `packages/d2b-provider-transport-vsock/tests/framing.rs`." ], + "detailedDesign" : "Implement framing utilities and bridge task in Provider crate. Copy `FramedVsockTransport` framing-only code (length-prefix encode/decode, bounded allocation, EOF/reset) from main `a1cc0b2d` → `framing.rs`; implement bridge task pumping bytes between an opaque `AsyncRead+AsyncWrite` stream from `VsockEffectPort::open` and the named ComponentSession stream; hermetic tests using `FakeVsockEffectPort` (no real socket). Primary reuse disposition: `adapt`. Preserved source-plan detail: copy/adapt framing-only code; exclude raw AF_VSOCK socket calls and ADR 0045 endpoint-role assumptions.", "entryContracts" : [ "ADR-046-provider-transport-vsock" ], @@ -22453,6 +23464,7 @@ ], "specId" : "ADR-046-provider-transport-vsock", "topologicalRank" : 12, + "validation" : "Proof type: hermetic framing tests; `tests/framing.rs` covers partial/coalesced records, oversized frames, EOF/reset classification, and no real socket.", "wave" : "W6" }, { @@ -22460,6 +23472,7 @@ "destinations" : [ "`packages/d2b-provider-transport-vsock/src/service.rs`; tests `tests/open_close.rs`, `tests/observe.rs`, and conformance kit." ], + "detailedDesign" : "Implement `VsockTransportService` (OpenTransport/CloseTransport/ObserveTransport). Implement all three service methods in `service.rs`; `open_close.rs` and `observe.rs` test full service API against `FakeVsockEffectPort`; conformance kit passes. Primary reuse disposition: `adapt`. Preserved source-plan detail: net-new service implementation over ComponentSession and fake effect port tests.", "entryContracts" : [ "ADR-046-provider-transport-vsock" ], @@ -22475,6 +23488,7 @@ ], "specId" : "ADR-046-provider-transport-vsock", "topologicalRank" : 13, + "validation" : "Proof type: service round-trip test (mock); `tests/open_close.rs`, `tests/observe.rs`, and provider conformance tests.", "wave" : "W6" }, { @@ -22482,6 +23496,7 @@ "destinations" : [ "`d2b-core-controller` Zone runtime `LiveVsockEffectPort`; Provider receives it by dependency injection at startup." ], + "detailedDesign" : "Implement `LiveVsockEffectPort` in Zone runtime. Zone runtime provides `LiveVsockEffectPort` backed by core allocator state; resolves `OpaqueEndpointId` → CID and `OpaqueBindingId` → port; opens AF_VSOCK socket; injects into Provider service at startup; no raw CID/port exposed to Provider. Primary reuse disposition: `adapt`. Preserved source-plan detail: net-new core adapter; keep raw AF_VSOCK syscalls outside Provider crate.", "entryContracts" : [ "ADR-046-provider-transport-vsock" ], @@ -22496,6 +23511,7 @@ ], "specId" : "ADR-046-provider-transport-vsock", "topologicalRank" : 12, + "validation" : "Proof type: integration test; `integration/host_guest.rs` exercises live open/close byte round-trip with the injected effect.", "wave" : "W6" }, { @@ -22503,6 +23519,7 @@ "destinations" : [ "ProviderDeployment Volume creation/deletion path plus `packages/d2b-provider-transport-vsock/tests/state_volume.rs`." ], + "detailedDesign" : "Core ProviderDeployment creates/deletes service component state Volume. Core ProviderDeployment creates `Volume/transport-vsock--service--empty-state--*` before the component Process and deletes it after the Process finalizer; transport-vsock Provider controller does not own Volume, does not add Volume to exported ResourceTypes, and does not create its prerequisite; Volume spec: empty schema, `kind: state`, `persistenceClass: persistent`, `migrationPolicy: none`, `User/d2b-transport-vsock` owner, minimal nonzero `quota.maxBytes`/`quota.maxInodes` with `enforcement: hard`, `private` sensitivity, `broker-maintained` identity marker; `state_volume.rs` test verifies Volume spec fields against canonical schema; integration test verifies marker written at install and removed at Provider deletion; no operator-authored Volume; component receives dirfd view only. Primary reuse disposition: `create`. Preserved source-plan detail: net-new ProviderDeployment/Volume integration and tests.", "entryContracts" : [ "ADR-046-provider-transport-vsock" ], @@ -22516,6 +23533,7 @@ ], "specId" : "ADR-046-provider-transport-vsock", "topologicalRank" : 11, + "validation" : "Proof type: unit + integration test; `tests/state_volume.rs` and Provider install/remove integration tests verify schema, user refs, marker lifecycle, and no ComponentPrincipal.", "wave" : "W6" }, { @@ -22523,6 +23541,7 @@ "destinations" : [ "`packages/d2b-provider-transport-vsock/integration/host_guest.rs` and `integration/no_fd_transfer.rs`." ], + "detailedDesign" : "Integration test: real vsock socketpair + full ZoneLink open/close. `integration/host_guest.rs`: real vsock socketpair (Linux); `OpenTransport` + byte round-trip + `CloseTransport`; validates bridge throughput ≥ 512 MiB/s; `no_fd_transfer.rs`: structural rejection of attachment packets over vsock transport. Primary reuse disposition: `create`. Preserved source-plan detail: net-new integration coverage with no FD transfer over vsock.", "entryContracts" : [ "ADR-046-provider-transport-vsock" ], @@ -22538,6 +23557,7 @@ ], "specId" : "ADR-046-provider-transport-vsock", "topologicalRank" : 14, + "validation" : "Proof type: integration test; `make test-integration` runs `host_guest.rs` and `no_fd_transfer.rs`.", "wave" : "W6" }, { @@ -22545,6 +23565,7 @@ "destinations" : [ "Remove legacy paths from `d2b-host` and `d2bd`; replacement lives in `observability-otel` Provider native vsock relay and Guest resource lifecycle/bootstrap." ], + "detailedDesign" : "Delete legacy socat OTLP relay and CONNECT-proxy guest-control vsock. Remove `vsock_relay_argv.rs` socat path after `observability-otel` Provider native vsock relay passes parity; remove `guest_control_vsock.rs` CONNECT-proxy after Guest resource lifecycle + guestd vsock bootstrap reach parity; no raw CID or socat vsock path remains. Primary reuse disposition: `delete-after-cutover`. Preserved source-plan detail: delete after replacement parity; preserve reserved guest-control/OTLP port exclusions until replacements own them.", "entryContracts" : [ "ADR-046-provider-transport-vsock" ], @@ -22558,6 +23579,7 @@ ], "specId" : "ADR-046-provider-transport-vsock", "topologicalRank" : 11, + "validation" : "Proof type: deletion + parity test; parity tests for observability-otel and Guest lifecycle plus redaction checks that no raw CID/socat vsock path remains.", "wave" : "W6" }, { @@ -22565,6 +23587,7 @@ "destinations" : [ "`packages/d2b-provider-volume-virtiofs/src/virtiofsd_argv.rs`; `packages/d2b-provider-volume-virtiofs/tests/argv_golden.rs`" ], + "detailedDesign" : "Create crate skeleton with mandatory `src/`, `tests/`, `integration/`, `README.md`. Extract `VirtiofsdArgvInput` and `generate_virtiofsd_argv` with these changes: (1) replace `extra_args: Vec` with nothing (removed); (2) replace `socket_path: String` with `socket_path: SocketPath` newtype backed by `socket_path.rs`; (3) add `shared_dir_fd: i32` replacing `shared_dir: String` (FD-based); (4) replace `socket_group: Option` with `socket_group: Option`. Implement `socket_path.rs`: private path using SHA-256 of `\\x00\\x00`, truncated 8 hex chars, formatted as `/vms//vol-.vfd.sock`. Assert path length ≤ 108 bytes. Primary reuse disposition: `adapt`. Preserved source-plan detail: extract and adapt.", "entryContracts" : [ "ADR-046-provider-volume-virtiofs" ], @@ -22580,6 +23603,7 @@ ], "specId" : "ADR-046-provider-volume-virtiofs", "topologicalRank" : 12, + "validation" : "`tests/argv_golden.rs`: 14 migrated tests + `no_extra_args_ever_emitted`, `socket_path_is_not_in_args`, `shared_dir_is_fd_path`, `path_length_within_sunpath_limit`; `tests/socket_path_privacy.rs`: `socket_path_not_in_export_status`, `socket_path_not_in_volume_status`, `socket_path_not_in_audit_record`; `tests/schema_conformance.rs`: `process_spec_readiness_class_is_provider_defined`, `process_spec_readiness_has_no_kind_or_period_fields`, `process_spec_budget_cpu_request_limit_nested`, `process_spec_budget_memory_request_limit_nested`, `process_spec_budget_pids_limit_present`, `process_spec_budget_fds_limit_present`, `process_spec_sandbox_no_new_privileges_true`, `process_spec_sandbox_read_only_root_true`, `process_spec_no_host_uid_gid_in_spec`", "wave" : "W6" }, { @@ -22587,6 +23611,7 @@ "destinations" : [ "`packages/d2b-provider-volume-virtiofs/src/user_ns.rs` (conformance kit); `packages/d2b-provider-volume-virtiofs/tests/adr021_invariant.rs`" ], + "detailedDesign" : "`user_ns.rs` contains only the conformance check and template descriptor assertion: verify that the virtiofsd-worker Process template declares `capabilityClasses: []`, `startRoot: false`, `noNewPrivileges: true`, `readOnlyRoot: true`, and `sandbox.userNamespace.mappingClass: process-principal-root`. `hostUid`/`hostGid` are NOT set by the controller — system-minijail resolves the mapping from the `User/vol--vfd` principal when building the LaunchTicket via the effect port. The conformance check rejects any template mutation that adds host capability classes, sets `startRoot: true`, disables `noNewPrivileges`, or disables `readOnlyRoot`. The user-NS pre-establishment code itself remains in `d2b-priv-broker/src/sys.rs` and is invoked via the system-minijail effect port. Primary reuse disposition: `extract`. Preserved source-plan detail: extract conformance kit only; pre-establishment code stays in broker.", "entryContracts" : [ "ADR-046-provider-volume-virtiofs" ], @@ -22602,6 +23627,7 @@ ], "specId" : "ADR-046-provider-volume-virtiofs", "topologicalRank" : 13, + "validation" : "`tests/adr021_invariant.rs`: `virtiofsd_capability_classes_must_be_empty`, `virtiofsd_start_root_must_be_false`, `virtiofsd_no_new_privileges_must_be_true`, `virtiofsd_read_only_root_must_be_true`, `process_spec_has_no_host_uid_gid`, `sandbox_namespace_never_emitted`, `user_ns_single_entry_single_uid_mapping`, `uid_map_write_ordering_uid_setgroups_gid`, `child_setuid_in_ns_not_host_uid`, `clone_newns_not_in_clone3_flags`, `child_exits_user_ns_sync_on_pipe_eof`", "wave" : "W6" }, { @@ -22609,6 +23635,7 @@ "destinations" : [ "`packages/d2b-provider-volume-virtiofs/src/controller.rs`; `packages/d2b-provider-volume-virtiofs/src/export.rs`" ], + "detailedDesign" : "Implement volume-virtiofs-controller reconcile loop using toolkit ResourceClient. Watch selector: `virtiofs.d2bus.org.Export` resources (all in zone), owned Process resources, owned User resources, Volume resources (read-only for view/vcpu resolution), Guest resources (read-only for vcpu count). On `spec-generation-changed` for an Export: (1) resolve View from Volume; (2) check store-view marker if applicable; (3) resolve threadPoolSize from Guest vcpus; (4) ensure User/vol--vfd; (5) diff against current Process; (6) emit Create/UpdateSpec. On `owned-resource-changed` for a Process: update Export status. On `deletionRequestedAt` for Export: two-phase teardown (§6.2). Primary reuse disposition: `adapt`. Preserved source-plan detail: extract and adapt.", "entryContracts" : [ "ADR-046-provider-volume-virtiofs" ], @@ -22626,6 +23653,7 @@ ], "specId" : "ADR-046-provider-volume-virtiofs", "topologicalRank" : 14, + "validation" : "`tests/export_lifecycle.rs`: `export_create_spawns_virtiofsd_process`, `export_ready_when_socket_present`, `export_delete_terminates_virtiofsd`, `export_delete_waits_for_guest_mount_absent`, `export_delete_with_guest_unreachable_holds_finalizer_degraded`, `export_proof_of_ns_death_clears_finalizer`; `tests/multi_attachment.rs`: `two_guests_get_separate_exports_and_processes`, `process_failure_does_not_affect_sibling_export`; `tests/schema_conformance.rs`: `provider_state_set_volume_created_on_install`, `provider_state_set_volume_owner_ref_is_provider`, `provider_state_set_volume_layout_principal_is_user_not_component_principal`, `provider_state_set_no_cross_component_volume_sharing`", "wave" : "W6" }, { @@ -22633,6 +23661,7 @@ "destinations" : [ "`packages/d2b-provider-volume-virtiofs/src/readiness.rs`; `packages/d2b-provider-volume-virtiofs/integration/guest_mount_readiness/`" ], + "detailedDesign" : "`unix-socket-exists` readiness: check file existence at the private socket path via a bounded blocking adapter (e.g., `tokio::task::spawn_blocking` wrapping `fstatat(2)` relative to the zone runtime `OwnedFd`, or an async-safe fd-relative equivalent); no blocking syscall on the async executor thread. Probe period 1 s; timeout 30 s. On socket present → set `Export.status.exportReady: true`. Guest-mount readiness: send `VirtioFsMountReady?` probe to guest-control health endpoint over vsock. Response `MountReady` sets `guestMountReady: true`. Response `MountAbsent` or timeout sets `guestMountReady: false`. The vsock health probe is async-native. If Guest is down, set Export `phase: Unknown`. All readiness probes (unix-socket-exists, guest-mount health) use bounded blocking adapters or async-safe fd-relative equivalents; no blocking I/O on the reconcile executor thread. Primary reuse disposition: `adapt`. Preserved source-plan detail: extract and adapt.", "entryContracts" : [ "ADR-046-provider-volume-virtiofs" ], @@ -22647,6 +23676,7 @@ ], "specId" : "ADR-046-provider-volume-virtiofs", "topologicalRank" : 15, + "validation" : "`tests/export_lifecycle.rs` (extended); `integration/guest_mount_readiness/`: virtiofsd launches, socket appears, guest-control probe returns MountReady, guestMountReady flips to true; probe returns MountAbsent on umount", "wave" : "W6" }, { @@ -22654,6 +23684,7 @@ "destinations" : [ "`packages/d2b-provider-volume-virtiofs/src/controller.rs` (pre-launch prerequisite check); `packages/d2b-provider-volume-virtiofs/integration/store_view_readonly/`" ], + "detailedDesign" : "Before issuing Process Create for a store-view virtiofs Export, check that `live/.d2b-marker-` exists (zero-length, correct mode) via a bounded blocking adapter (e.g., `tokio::task::spawn_blocking` wrapping `fstatat(2)` relative to the zone runtime directory, or an async-safe fd-relative equivalent); no blocking syscall on the async executor thread directly. If absent, requeue with exponential backoff. Assert `--shared-dir` resolves to `store-view/live` (the `ro-store` View path), never to `/nix/store`. Validate in `integration/store_view_readonly/` that virtiofsd serves only paths under `store-view/live`.", "entryContracts" : [ "ADR-046-provider-volume-virtiofs" ], @@ -22670,6 +23701,7 @@ ], "specId" : "ADR-046-provider-volume-virtiofs", "topologicalRank" : 16, + "validation" : "`integration/store_view_readonly/`: mount from guest reads closure paths; no host-store path escapes; `tests/argv_golden.rs`: `store_view_shared_dir_is_live_not_nix_store`; `tests/export_lifecycle.rs`: `store_view_launch_waits_for_marker`", "wave" : "W6" }, { @@ -22677,6 +23709,7 @@ "destinations" : [ "`nixos-modules/resources-volume.nix` (store-view and user Volume attachment emission); `nixos-modules/options-volumes.nix` (optional user-facing volume/attachment options)" ], + "detailedDesign" : "Extend the Nix resource compiler to: (1) auto-emit a store-view Volume (with `ro-store` and `meta` Views, virtiofs ro-store attachment) per Guest that has a VM runtime Provider; (2) emit virtiofs attachment entries for explicitly configured user Volumes; (3) emit `User/vol--vfd` resources for each Volume with virtiofs attachments; (4) emit `Provider/volume-virtiofs` as a Provider resource when any virtiofs attachment is configured. volume-local creates Export resources at runtime (not in Nix bundle); no `virtiofs.d2bus.org.Export` resources appear in the Nix-emitted bundle. All eval validation steps (§16.5) apply.", "entryContracts" : [ "ADR-046-provider-volume-virtiofs" ], @@ -22692,6 +23725,7 @@ ], "specId" : "ADR-046-provider-volume-virtiofs", "topologicalRank" : 13, + "validation" : "nix-unit: `store_view_volume_auto_emitted_per_guest`, `volume_virtiofs_attachment_canonical_json`, `virtiofs_provider_emitted_when_attachment_configured`, `vfd_user_emitted_per_volume`, `second_read_write_attachment_rejected_at_eval`, `transport_virtiofs_requires_provider_installed`; drift-check gate for `nixos-modules/processes-json.nix` virtiofsdRunner removal", "wave" : "W6" }, { @@ -22699,6 +23733,7 @@ "destinations" : [ "`packages/d2b-provider-volume-virtiofs/src/export.rs`; `packages/d2b-contracts/src/v3/virtiofs_export.rs`" ], + "detailedDesign" : "Declare `virtiofs.d2bus.org.Export` ResourceType in `d2b-contracts`. Base fields: `providerRef`, `volumeRef`, `executionRef`, `view`, `access`, `mountPath`; virtiofs tunables live under `spec.provider.settings` (as in §4.2). Status fields: top-level `phase`/`conditions`, `status.resource.exportReady`, `status.resource.guestMountReady`, and `status.provider.details.workerProcessRef`. Strict serde `deny_unknown_fields`. Implement the conformance test fixture that validates schema fingerprint stability. The Export spec JSON schema and provider status extension schema are signed and included in the Provider package.", "entryContracts" : [ "ADR-046-provider-volume-virtiofs" ], @@ -22713,6 +23748,7 @@ ], "specId" : "ADR-046-provider-volume-virtiofs", "topologicalRank" : 11, + "validation" : "`tests/schema_conformance.rs`: `export_schema_canonical_json_stable`, `export_spec_denied_unknown_fields`, `export_status_exportready_is_boolean_not_path`, `export_owner_must_be_volume`", "wave" : "W6" }, { @@ -22720,6 +23756,7 @@ "destinations" : [ "`packages/d2b-contracts/src/v3/{services,state,identity,provider}.rs` (new v3 namespace); generated stubs regenerated from v3 proto files in `packages/d2b-contracts/proto/v3/`" ], + "detailedDesign" : "**Selected**: `MethodSpec`/`ServiceSpec`/fingerprinting infrastructure → v3 Zone API service schema fingerprinting; `StrictWireMessage`/`decode_strict`/`encode_strict`/`admit_metadata` → v3 wire decode/admit for all resource API requests; `CanonicalPayloadVerifier` payload-digest binding → v3 resource store integrity checks; audit chain types `AuditRecord`/`AuditSegment`/`AuditCheckpoint`/`detect_audit_gap` + `MAX_AUDIT_RETENTION_DAYS=14`/`MAX_AUDIT_RECORD_BYTES=8192`/`MAX_AUDIT_SEGMENT_BYTES=64MiB` → v3 Zone audit (§13.3); lock types `LockSpec`/`SyncInventory`/`LeaseRecord` + `MAX_LOCKS=1024`/`MAX_LOCK_DEADLINE_MS=300_000` → v3 resource store lock layer; bounded opaque ID pattern `[a-z][a-z0-9-]{0,63}` → v3 `OperationId`/`HandleId`/`PlanId`/`LeaseId`; `Fingerprint` 64-hex-char → v3 Provider `spec.configFingerprint`; `ProviderContractError` 34 variants → v3 Provider operation error taxonomy; canonical name constraint `^[a-z][a-z0-9-]*$` max 63 bytes → same as `ids.rs::is_label()` shared validator (§16.2). **Excluded ADR45 assumptions**: `RealmLabel`/`WorkloadName`/`RealmPath` identity types: ADR45 workload/realm address format; replaced by `metadata.name` + Zone `ResourceRef`. `ProviderType::ALL` fixed 11-type closed enum: v3 Provider type is an open string field in the Provider resource spec. `STATE_SCHEMA_VERSION=2`/`STATE_SCHEMA_GENERATION=1`: v3 store schema uses redb table versioning, not a JSON schema version field. v2 service fingerprint tests reference ADR45-specific proto files; v3 fingerprints use different proto inputs but the same `service_schema_fingerprint` seeding mechanism. Generated stubs in `generated_v2_services/` are v2-specific and excluded; v3 uses regenerated stubs from `proto/v3/`.", "entryContracts" : [ "ADR-046-resources-zone-control" ], @@ -22734,6 +23771,7 @@ ], "specId" : "ADR-046-resources-zone-control", "topologicalRank" : 11, + "validation" : "`v3-canonical-name-matches-ids-is-label-regex`, `v3-service-fingerprint-changes-on-method-mutation` (behavior ported from `public_endpoint_fingerprint_binds_daemon_and_guest_method_descriptors`), `v3-audit-gap-detection-covers-missing-segment`, `v3-strict-wire-rejects-unknown-fields`, `v3-state-envelope-digest-mismatch-rejected`, `v3-canonical-payload-verifier-binding-holds-under-mutation`", "wave" : "W5" }, { @@ -22741,6 +23779,7 @@ "destinations" : [ "`packages/d2b-contracts/src/v3/zone.rs`; `packages/d2b-core-controller/src/zone.rs`" ], + "detailedDesign" : "Zone ResourceType schema with `spec = {}`; self-resource enforcement; store_meta binding checks; phase/conditions; cardinality-1 enforcement; `zone-drain` finalizer; canonical JSON schema. Nix Zone options include compiler-only `parentZone`: required for every non-root Zone, forbidden on `local-root`, and never emitted into `Zone.spec` Primary reuse disposition: `adapt`. Preserved source-plan detail: extract and adapt (`RealmId`→`ResourceName` validators, `RealmPath` depth bound→ZoneLink depth, `RealmControllerPlacement`→Provider component placement); new (Zone resource schema/store table/handler).", "entryContracts" : [ "ADR-046-resources-zone-control" ], @@ -22757,6 +23796,7 @@ ], "specId" : "ADR-046-resources-zone-control", "topologicalRank" : 10, + "validation" : "`zone-self-resource-enforced`, `zone-uid-mismatch-quarantine`, `zone-name-mismatch-rejected`, `zone-cardinality-one`, `zone-cross-zone-ref-rejected`, `zone-owner-rejected`, `zone-deletion-only-on-drain`", "wave" : "W5" }, { @@ -22764,6 +23804,7 @@ "destinations" : [ "`packages/d2b-contracts/src/v3/zone_link.rs`; `packages/d2b-core-controller/src/zone_link.rs`" ], + "detailedDesign" : "Child-local ZoneLink schema with self-matching `spec.childZoneName` plus same-Zone `spec.transportProviderRef`, `spec.transportSettings`, and `spec.transportCredentials`; at most one uplink resource per non-root Zone and none in local root; child-store cursor/intent state; reconnect loop with exponential backoff; local intent queue (max 256 entries); local child UID change detection; drain finalizer; no reciprocal parent-store resource; compiler-only `parentZone` selects the one allocator owner, while that allocator owns privileged listener/placement/route allocation effects and exposes only a sealed bootstrap/allocation interface Primary reuse disposition: `adapt`. Preserved source-plan detail: extract and adapt (`SecurePeerSession` Noise model → ComponentSession Noise KK; `SessionLifecycle`/`SessionPhase` → ZoneLink session reconnect loop and connection detail fields; `Connecting`/`Established` current evidence phases drive `status.connected` and `status.phase` transitions to `Pending`/`Ready`, not direct phase values; `RouteTreeEngine.decide_route()` → cursor tracking; `RealmIdentityStore` enrollment → ZoneLink child key-pin).", "entryContracts" : [ "ADR-046-resources-zone-control" ], @@ -22779,6 +23820,7 @@ ], "specId" : "ADR-046-resources-zone-control", "topologicalRank" : 13, + "validation" : "`zonelink-reconnect-child-uid-change`, `zonelink-disconnect-unknown-phase`, `zonelink-intent-queue-limit`, `zonelink-disabled-no-reconnect`, `zonelink-child-auth-denied-failed`, `zonelink-drain-closes-session`, `zonelink-child-name-matches-store`, `zonelink-one-child-local-uplink`, `zonelink-parent-bootstrap-binding`, `zonelink-parent-has-no-reciprocal-row`", "wave" : "W5" }, { @@ -22786,6 +23828,7 @@ "destinations" : [ "`packages/d2b-contracts/src/v3/provider.rs`; `packages/d2b-core-controller/src/provider_lifecycle.rs`; `packages/d2b-core-controller/src/api_catalog.rs`" ], + "detailedDesign" : "Provider resource schema with all spec fields from §4.3, including resolved component bounds (max 8 controllers, 8 services, 32 worker templates, 16 ResourceTypes per controller); trust/conformance/config validation; component descriptor validation; dependency alias resolution; API binding with permission intersection; lifecycle policies; Nix Provider installation options; Provider crate layout enforcement per §4.8 (see ADR046-pkg-001) Primary reuse disposition: `adapt`. Preserved source-plan detail: extract and adapt (`workload_lists_and_advertises`/`display_fails_closed_when_unsupported` conformance behavior; `RuntimeCapabilitySet`/`WorkloadCapabilitySet`/`NodeCapabilitySet` → Provider component `supportedCapabilities`; `ProviderError`/`RetryHint` → Provider lifecycle error schema; `ProviderId` → Provider `metadata.name` validator; `ProcessRole` variants → Provider component type identifiers).", "entryContracts" : [ "ADR-046-resources-zone-control" ], @@ -22802,6 +23845,7 @@ ], "specId" : "ADR-046-resources-zone-control", "topologicalRank" : 12, + "validation" : "All §15.3 Provider tests including the resolved bounds checks", "wave" : "W5" }, { @@ -22809,6 +23853,7 @@ "destinations" : [ "`packages/d2b-contracts/src/v3/role.rs`; `packages/d2b-core-controller/src/authz.rs`" ], + "detailedDesign" : "Role resource schema with rule schema from §5.3 and resolved bounds (32 rules, 16 resourceTypes per rule, 64 resourceNames, 32 executionRefs); verb enum; explicit wildcard enforcement; index builder; phase/conditions; `role-binding-drain` finalizer; Nix Role options; audit/OTEL instrumentation Primary reuse disposition: `adapt`. Preserved source-plan detail: extract and adapt (`verb_requires_admin()` verb table → Role verb enum; `DaemonAccessDecision` error types → Role admission errors; `MappedDaemonAccessPrincipal` → subject identity model).", "entryContracts" : [ "ADR-046-resources-zone-control" ], @@ -22824,6 +23869,7 @@ ], "specId" : "ADR-046-resources-zone-control", "topologicalRank" : 11, + "validation" : "All §15.4 Role tests including the resolved bounds checks", "wave" : "W5" }, { @@ -22831,6 +23877,7 @@ "destinations" : [ "`packages/d2b-contracts/src/v3/role_binding.rs`; `packages/d2b-core-controller/src/authz.rs` (shared with Role handler)" ], + "detailedDesign" : "RoleBinding resource schema with no expiry field and max 128 subjects; subject resolution and UID binding; external principal selector; scope narrowing intersection; revocation; immediate deletion with cache invalidation; Nix RoleBinding options Primary reuse disposition: `adapt`. Preserved source-plan detail: new (RoleBinding resource schema/store table/handler); adapt (`DaemonAccessAdmissionSource` identity fields → subject selector; `map_remote_daemon_access()` logic → subject UID-binding behavior).", "entryContracts" : [ "ADR-046-resources-zone-control" ], @@ -22845,6 +23892,7 @@ ], "specId" : "ADR-046-resources-zone-control", "topologicalRank" : 12, + "validation" : "All §15.5 RoleBinding tests, including the 128-subject admission bound and no-expiry lifecycle model", "wave" : "W5" }, { @@ -22852,6 +23900,7 @@ "destinations" : [ "`packages/d2b-resource-api/src/bootstrap_authz.rs`; Zone runtime startup path" ], + "detailedDesign" : "Compiled bootstrap authorization as described in §9; exact subjects (system-core, system-minijail); closed verb table; non-configurable enforcement; atomic supersession after stored RBAC publishes; out-of-band reset path Primary reuse disposition: `adapt`. Preserved source-plan detail: extract and adapt (`authorize_peer()`/`verb_requires_admin()` verb table → bootstrap constant policy verb table; `PeerRole` two-variant model → system-core/system-minijail bootstrap subjects; `map_local_unix_daemon_access()` SO_PEERCRED mapping → bootstrap subject derivation; `LoadedRealmControllersConfig` startup path → Zone runtime startup bootstrap init sequence).", "entryContracts" : [ "ADR-046-resources-zone-control" ], @@ -22867,6 +23916,7 @@ ], "specId" : "ADR-046-resources-zone-control", "topologicalRank" : 13, + "validation" : "All §15.6 bootstrap tests", "wave" : "W5" }, { @@ -22874,6 +23924,7 @@ "destinations" : [ "`nixos-modules/options-zones.nix`; `nixos-modules/resources-zone-control.nix`; extended `nixos-modules/index.nix`" ], + "detailedDesign" : "`d2b.zones..*` Nix options for Zone/ZoneLink/Provider/Role/RoleBinding/Quota/EmergencyPolicy authoring; compiler-only scalar `parentZone` required on non-root Zones and forbidden on `local-root`; Nix eval-time validation of parent existence, one resolved parent, self/cycle rejection, 16-name ancestry depth, ResourceRefs, verb enums, digest format, subject types, child-local ZoneLink self-name/one-uplink/local transport-ref constraints, and the no-expiry RoleBinding model; canonical JSON serialization; generation-bound resource bundle output; the compiler seals the validated parent map into allocator bootstrap topology without emitting `parentZone` into `Zone.spec` or reciprocal resources Primary reuse disposition: `adapt`. Preserved source-plan detail: adapt (`providerType` submodule → Provider option submodule; label regex `^[a-z][a-z0-9-]*$` retained unchanged; `placement` option → Provider component placement; `d2b.realms.*` option namespace → `d2b.zones.*` option namespace).", "entryContracts" : [ "ADR-046-resources-zone-control" ], @@ -22895,6 +23946,7 @@ ], "specId" : "ADR-046-resources-zone-control", "topologicalRank" : 14, + "validation" : "nix-unit vectors for each Zone control type schema; cross-field constraint tests; rendered JSON contract tests (`make test-drift`)", "wave" : "W5" }, { @@ -22902,6 +23954,7 @@ "destinations" : [ "`packages/d2b-contracts/src/v3/host.rs` (Host resource schema, user-domain variant); `packages/d2b-core-controller/src/host_user.rs` (reconciler owned by Provider/system-core); Nix Host authoring via `d2b.zones..resources. = { type = \"Host\"; spec = { ... }; };` (validated per ResourceTypeSchema; no separate `options-zones-hosts.nix` submodule)" ], + "detailedDesign" : "Host ResourceType schema for user-domain variant: `spec.defaultDomain=user`, `spec.allowedDomains=[user]`, `spec.defaultUserRef=User/`, `spec.shellPolicy` (adapted from `UnsafeLocalShellPolicy`), `spec.launcherItems` (adapted from `UnsafeLocalLauncherItem`). No-isolation posture recorded in `status.observedPosture`. A dedicated Host admission gate blocks unsafe-local Host creation without opt-in. Mandatory no-isolation warning in Host `status.conditions[0].message` and CLI/UI output for all Host commands. No-isolation posture included in audit record body (`isolation=no-isolation`); it is never emitted as an OTEL metric label value or span attribute. Child processes use standard Process Providers — no Provider resource with name or kind `unsafe-local` is created. Cardinality bounds: max 256 user-domain Hosts per Zone, max 64 launcher items per Host, max 64 shell sessions per Host. Primary reuse disposition: `adapt`. Preserved source-plan detail: adapt (`WorkloadExecutionPosture` unsafe-local posture tuple → Host `status.observedPosture`; posture details in audit record body, not OTEL labels; `UnsafeLocalShellPolicy.{defaultName,maxSessions}` → Host `spec.shellPolicy`; `HelperRegistry.dispatch_launch` → Zone runtime Host process launch broker; `policy.allowUnsafeLocal` → dedicated Host admission gate); new (Host ResourceType user-domain schema with `defaultDomain`, `allowedDomains`, `defaultUserRef`, `shellPolicy`, cardinality bounds).", "entryContracts" : [ "ADR-046-resources-zone-control" ], @@ -22917,6 +23970,7 @@ ], "specId" : "ADR-046-resources-zone-control", "topologicalRank" : 13, + "validation" : "`host-user-domain-no-isolation-warning-required`, `host-user-only-disallows-system-domain`, `host-allowUnsafeLocal-gates-creation`, `host-defaultUserRef-user-type-required`, `host-shell-policy-max-sessions-bound`, `host-launcher-item-max-count-bound`, `host-audit-body-isolation-label-present`, `host-otel-no-posture-label`, `host-cli-no-isolation-warning-present`", "wave" : "W5" }, { @@ -22924,6 +23978,7 @@ "destinations" : [ "`packages/d2b-contracts/src/v3/quota.rs`; `packages/d2b-core-controller/src/quota.rs`; `packages/d2b-resource-api/src/quota_gate.rs`" ], + "detailedDesign" : "Quota resource schema with all spec/status fields from §7; ceiling bounds enforcement at admission (hard policy: reject over-quota with `quota-exceeded`; soft policy: warn); usage index built from resource scan with `quotaRef` field; per-ResourceType ceiling in `perTypeCeilings`; `core.quota-drain` finalizer that blocks Quota deletion until all dependent resources with `spec.quotaRef` pointing to this Quota are reassigned or deleted by authorized owners/operators — the controller never issues spec-updates to clear `quotaRef` on other resources; `dependentCount` status field updated from dependency index; audit event `quota-check` per admission; Nix Quota options per §7.7", "entryContracts" : [ "ADR-046-resources-zone-control" ], @@ -22939,6 +23994,7 @@ ], "specId" : "ADR-046-resources-zone-control", "topologicalRank" : 11, + "validation" : "`quota-ceiling-hard-reject`, `quota-ceiling-soft-warn`, `quota-ceiling-pertype`, `quota-drain-blocks-on-dependents`, `quota-over-quota-status`, `quota-nix-eval-bounds`, `quota-nix-build-pertype-unknown-type`", "wave" : "W5" }, { @@ -22946,6 +24002,7 @@ "destinations" : [ "`packages/d2b-contracts/src/v3/emergency_policy.rs`; `packages/d2b-core-controller/src/emergency_policy.rs`; `packages/d2b-resource-api/src/emergency_gate.rs`" ], + "detailedDesign" : "EmergencyPolicy resource schema from §8; union semantics: multiple `enabled=true` policies allowed simultaneously; effective scope = OR of all enabled policies' scope flags; effective `drainDeadlineSeconds` = minimum across enabled policies; scope flag evaluation: `stopNewAdmissions` signal to API admission gate, `disconnectZoneLinks` graceful signal to ZoneLink handler, `stopProviderProcesses` suppresses Process launch and sends stop signal to running Provider component Processes (does NOT set `deletionRequestedAt` on Process resources; reconciliation resumes on deactivation), `drainOngoingOperations` deadline drain; `core.emergency-drain` finalizer for enabled policies; audit events `emergency-policy-activated` / `emergency-policy-deactivated`; `reason` field stored in spec and included in audit record body (never in OTEL metric label values, structured log labels, or status fields); Nix EmergencyPolicy options per §8.7", "entryContracts" : [ "ADR-046-resources-zone-control" ], @@ -22961,6 +24018,7 @@ ], "specId" : "ADR-046-resources-zone-control", "topologicalRank" : 11, + "validation" : "`emergency-policy-activates-gate`, `emergency-policy-disconnects-zonelinks`, `emergency-policy-union-most-restrictive`, `emergency-policy-multi-enabled-combined-scope`, `emergency-policy-stop-processes-no-delete`, `emergency-policy-deactivation-restores-gate`, `emergency-policy-drain-finalizer`, `emergency-nix-eval-drain-deadline-bound-tightest`", "wave" : "W5" }, { @@ -22968,6 +24026,7 @@ "destinations" : [ "`packages/d2b-bus/src/{lifecycle,engine,driver,streams,transport,error}.rs` (new crate `d2b-bus`)" ], + "detailedDesign" : "**Selected**: full `SessionLifecycle` FSM including keepalive ping-nonce/timeout close (`poll_keepalive` lines 81–124), nonce-exhaustion close (`NonceExhausted` remediation `ReplaceGeneration`), reconnect attempt counting + window expiry (`begin_reconnect` lines 147–174), generation increment on reconnect; `ComponentSessionDriver` 18-method trait as the d2b-bus driver contract; `SessionEngine` full frame encode/decode, keepalive, named-stream credit/half-close/reset; `NamedStreamMux` credit model with send/receive credit, phase transitions, terminal removal; `DriverQueues` backpressure (`QueueBackpressure`) on both deliver and receive paths; all `SessionError`/`From<>` mapping chains. **Excluded ADR45 assumptions**: `establish_initiator_with_generation_discovery()` (lines 102–123): ADR45 initiator-probes-server for current generation; in v3 the generation lives in the Zone resource store — use `establish_initiator()` directly. OTEL labels in `metrics.rs` reference ADR45 realm/workload dimensions — replace with Zone/ZoneLink labels per §13.2. Primary reuse disposition: `adapt`. Preserved source-plan detail: copy + adapt.", "entryContracts" : [ "ADR-046-resources-zone-control" ], @@ -22983,6 +24042,7 @@ ], "specId" : "ADR-046-resources-zone-control", "topologicalRank" : 10, + "validation" : "`session-lifecycle-reconnect-attempts-exhausted`, `session-lifecycle-keepalive-timeout-closes`, `session-lifecycle-nonce-exhausted-close-with-replace-generation-remediation`, `session-driver-cancelled-receiver-restores-event` (ported from test at line 707), `named-stream-credit-half-close-then-remote-close-transitions-closed`, `named-stream-reset-cancels-pending-send`", "wave" : "W5" }, { @@ -22990,6 +24050,7 @@ "destinations" : [ "`packages/d2b-bus-unix/src/{adapter,socket,pidfd,credit,descriptor,error,systemd}.rs` (new crate `d2b-bus-unix`)" ], + "detailedDesign" : "**Selected**: `UnixSeqpacketTransport` seqpacket adapter implementing `OwnedTransport`; `PeerIdentityPolicy` SO_PEERCRED verification (pathname + inherited socketpair); `prearmed_seqpacket_pair()` for bootstrap inherited-socketpair path; `CreditPool`/`CreditScopeSet` six-scope credit model with per-packet/request/operation/session/process/host reservation; `ProcessCreditLimit::derive()` from `RLIMIT_NOFILE` minus `RESERVED_CONTROL_FDS`; `ProcPidfdIdentityVerifier` double-read race guard on `/proc/self/fdinfo/` with executable + cgroup digest callbacks; sealed memfd four-seal enforcement; `AncillaryCapacity` derived from `AttachmentPolicy`; `UnixSessionError` 21-variant Display strings preserved verbatim for audit log compatibility; `consume_peer_credentials()` strips-and-verifies SCM_CREDENTIALS exactly once on first packet. **Excluded ADR45 assumptions**: `vsock.rs` (`FramedVsockTransport`, `NativeVsockListener`, `NativeVsockTransport`): ADR45 guest-control vsock transport; v3 ZoneLink uses Unix sockets for host-local; vsock for Guest connections is separate work. Specific socket paths (`PUBLIC_SOCKET_PATH`, `BROKER_SOCKET_PATH`) are replaced by Zone-resource-managed paths. Primary reuse disposition: `adapt`. Preserved source-plan detail: copy + adapt.", "entryContracts" : [ "ADR-046-resources-zone-control" ], @@ -23004,6 +24065,7 @@ ], "specId" : "ADR-046-resources-zone-control", "topologicalRank" : 11, + "validation" : "`seqpacket-inherited-missing-credentials-rejected` (ported from `inherited_first_packet_requires_credentials`), `seqpacket-inherited-wrong-credentials-rejected` (ported from `inherited_first_packet_rejects_wrong_credentials`), `pidfd-double-read-race-guard-detects-pid-reuse`, `sealed-memfd-partial-seal-rejected`, `credit-scope-six-levels-ordered`, `ancillary-capacity-from-disabled-policy-is-zero`, `raw-control-unknown-header-rejected` (ported from `raw_control_scanner_rejects_unknown_and_partial_headers`), `raw-control-valid-rights-parsed` (ported from `raw_control_scanner_accepts_exact_rights_shape`)", "wave" : "W5" }, { @@ -23011,6 +24073,7 @@ "destinations" : [ "`packages/d2b-contracts/src/v3/component_session.rs` (new v3 namespace in existing contracts crate)" ], + "detailedDesign" : "**Selected**: all numeric constants verbatim; all 21 `HandshakeRejectReason` tags; `ComponentSessionPreface::parse/encode` with exact magic/length layout; `HandshakeOffer` 11-field shape; `AttachmentPolicy::validate` transport constraints (packet-atomic requires seqpacket or inherited-socketpair); `LimitProfile` 15-field profile; `NoiseHandshake` for Nn/Kk/IkPsk2 profiles; `EstablishedHandshake::transcript_hash`; `BoundedVec`. **Excluded ADR45 assumptions**: `EndpointPurpose` 19 tags and `ServicePackage` 15 tags encode ADR45-specific service families — v3 will append new tags for Zone API endpoints without renumbering existing ones. `IdentityEvidenceRequirement` + `GuestSessionCredentialV1` / `GuestBootstrapPsk` / `GUEST_SESSION_CREDENTIAL_MAGIC`: ADR45 guest bootstrap credential formats, excluded until v3 Guest bootstrap work item. Generation-discovery protocol (`encode_generation_discovery_request` lines 138–149): ADR45 initiator probes server for current generation; v3 generation lives in Zone resource store — generation-discovery excluded from initial d2b-bus copy. Primary reuse disposition: `adapt`. Preserved source-plan detail: copy + adapt.", "entryContracts" : [ "ADR-046-resources-zone-control" ], @@ -23025,6 +24088,7 @@ ], "specId" : "ADR-046-resources-zone-control", "topologicalRank" : 10, + "validation" : "`preface-magic-exact-16-bytes`, `preface-offer-len-zero-rejected`, `preface-offer-len-over-max-rejected`, `preface-offer-len-canonical-accepted`, `handshake-21-reject-reasons-all-covered`, `attachment-policy-packet-atomic-requires-seqpacket-or-inherited`, `limit-profile-local-default-all-fields-positive`", "wave" : "W5" }, { @@ -23032,6 +24096,7 @@ "destinations" : [ "`nixos-modules/options-zones.nix`, `nixos-modules/generated/resource-types.nix`, `nixos-modules/generated/options-zones-.nix`, `nixos-modules/resource-type-validators.nix`" ], + "detailedDesign" : "Consume the generator and registry established by ADR046-routing-011 to declare the unified `d2b.zones..resources.` option tree plus the Zone-level compiler-only `parentZone` scalar. The generated standard registry is exactly the canonical 19 types (`Zone`, `ZoneLink`, `Provider`, `Host`, `Guest`, `Process`, `EphemeralProcess`, `Network`, `Volume`, `Credential`, `Device`, `Endpoint`, `User`, `Role`, `RoleBinding`, `Quota`, `EmergencyPolicy`, `ResourceExport`, `ResourceImport`), and every standard type has a strict generated `spec` submodule carrying the schema's Nix types, defaults, bounds, and documentation. Installed Provider artifacts may append only signed qualified ResourceTypes whose strict schema has been verified and generated into the evaluated option set. `type` is selected from that closed combined registry: an unknown standard string, an unqualified extension, or a qualified type without an installed verified schema fails evaluation; there is no unrestricted string or free-form `spec` fallback. Require `parentZone` on every non-root Zone and forbid it on `local-root`; resolve it against declared Zone keys, reject self-parent/conflicting module definitions/cycles, and cap each ancestry path at 16 Zone names. Compile the validated map into sealed allocator bootstrap topology; never emit it into the resource bundle or `Zone.spec`. Declare the global `d2b.artifacts.` catalog with `package` (types.package, required) and `type` (closed enum, required). Provider `spec.artifactId` is a plain catalog ID; the derivation is not a `spec` field. Implement the Phase 1 cross-resource assertions (§14.10 Phase 1 table); retain `credentialRef`, `resourceRef`, and `closedEnum` helpers; reject operator-authored `Zone`; and enforce child-local ZoneLink topology. The runtime creates `Zone/`, `Provider/system-core`, and `Provider/system-minijail` with `managedBy=controller`; none is emitted or inferred from the configuration bundle. `allowUnsafeLocal` maps to the dedicated Host admission gate. Provider manifest-derived fields (`spec.exports`, `spec.components`, `spec.dependencies`, `spec.permissionClaims`, `spec.upgradePolicy`, `spec.restartPolicy`) are read-only and setting one is an eval error.", "entryContracts" : [ "ADR-046-resources-zone-control" ], @@ -23047,6 +24112,7 @@ ], "specId" : "ADR-046-resources-zone-control", "topologicalRank" : 11, + "validation" : "All Phase 1 eval tests in §15.8 (`nix-eval-name-regex-enforced`, `nix-eval-verb-closed-enum`, `nix-eval-roleref-format`, `nix-eval-subject-type-restricted`, `nix-eval-no-duplicate-subjects`, `nix-eval-bootstrap-provider-rejected`, `nix-eval-provider-missing-artifact-id`, `nix-eval-artifact-id-not-in-catalog`, `nix-eval-artifact-wrong-type`, `nix-eval-artifact-id-format`, `nix-eval-credentialref-declared`, `nix-eval-dollar-key-rejected`, all five `nix-eval-parent-zone-*` vectors, `nix-eval-zonelink-child-name-mismatch-rejected`, `nix-eval-zonelink-second-uplink-rejected`, `nix-eval-zonelink-limits-maxpendingintents-bound`); drift test asserts the standard registry and generated option modules cover exactly all 19 canonical types; negative evals reject unknown strings, unqualified extensions, unsigned or uninstalled qualified types, and unknown `spec` fields; a positive fixture admits an installed signed qualified type and validates its strict generated schema", "wave" : "W5" }, { @@ -23054,6 +24120,7 @@ "destinations" : [ "`packages/d2b-resource-compiler/src/{main,bundle,schema,validator,digest,sort,secret_lint,generation}.rs`; exposed as `pkgs.d2b-resource-compiler`; called from `nixos-modules/resource-compiler.nix`" ], + "detailedDesign" : "Implement all Phase 2 build-time checks (§14.10 Phase 2 table): dispatch on `type` to look up ResourceTypeSchema; validate each resource's `spec` canonical JSON against the committed schema (build validation compares canonical rendered JSON against ResourceTypeSchema for each core type); compile the `d2b.artifacts.*` catalog: for each entry, build/include the derivation, verify `type` is a recognized value, compute `digest` over the derivation output, extract and hash manifest and config schema files, validate signature chain and conformance attestation; detect duplicate artifact IDs; for each Provider resource, look up `spec.artifactId` in the compiled catalog (build failure if absent or wrong type), verify `configSchemaDigest` matches schema SHA-256, validate operator `spec.config` against loaded JSON Schema using a pure-Rust validator bundled in the derivation, verify `manifestDigest` and signature chain, load manifest-derived fields (`exports`, `components`, `dependencies`, `permissionClaims`, `upgradePolicy`, `restartPolicy`) into the bundle envelope; emit private integrity-pinned artifact catalog (ID → type/digest/closure metadata) as a separate private file (never merged into the public resource bundle); check `spec.rules[*].resourceTypes` against installed Provider catalogs in the bundle (Role); verify `spec.roleRef` names an existing Role in the bundle (RoleBinding); verify `spec.subjects[*]` names resolve in bundle (RoleBinding); check ResourceType short-name collision across all Zone Providers; RFC 8785 canonical JSON serialization; per-resource `digest` computation; `bundleDigest` computation over sorted `resources` array; inline-secret heuristic lint (`--strict-secrets` flag); `generation` counter persistence in Nix module state; emit `zone-resources.json` bundle with all fields per §14.9", "entryContracts" : [ "ADR-046-resources-zone-control" ], @@ -23068,6 +24135,7 @@ ], "specId" : "ADR-046-resources-zone-control", "topologicalRank" : 12, + "validation" : "All Phase 2 build tests in §15.8 (`nix-build-artifact-id-missing-from-catalog`, `nix-build-artifact-wrong-type-rejected`, `nix-build-duplicate-artifact-id`, `nix-build-artifact-store-path-absent-from-bundle`, `nix-build-artifact-store-path-absent-from-config`, `nix-build-config-schema-failure`, `nix-build-schema-digest-mismatch`, `nix-build-manifest-digest-mismatch`, `nix-build-resourcetype-collision`, `nix-build-bundle-sorted`, `nix-build-bundle-digest-stable`, `nix-build-per-resource-digest-correct`, `nix-build-credential-ref-survives-build`, `nix-build-inline-secret-lint-warning`, `nix-build-inline-secret-strict-failure`)", "wave" : "W5" }, { @@ -23075,6 +24143,7 @@ "destinations" : [ "`packages/d2b-core-controller/src/configuration.rs` (Phase 3 activation, diff, delete dispatch); `packages/d2b-core-controller/src/cleanup.rs` (pending tracking, status, stuck detection, rollback verb handler)" ], + "detailedDesign" : "Implement all Phase 3 runtime activation checks (§14.10 Phase 3 table); `bundleDigest` integrity verify; `zoneUid` consistency check; `generation` monotone check; per-resource `digest` re-verify; atomic generation advance in `store_meta`; diff computation (resources with `managedBy=configuration` absent from new bundle → async Delete; `managedBy=controller`/`managedBy=api` resources untouched); `managedBy` and `configurationGeneration` field maintenance on resources in redb store; `cleanupPendingCount` and `generationCleanupPending` maintenance; Zone.status.phase → Degraded while cleanup pending, reverts on completion; `GenerationCleanupPending`/`GenerationCleanupFailed` condition management; stuck-cleanup `GenerationCleanupFailed=True` at `cleanupStuckThreshold` (default 5 min) with exponential backoff retry; prior generation bundle retention and pruning up to configured `retainedPriorGenerationCount` (default 3, range 1..16); audit emission for all four cleanup audit kinds (§14.11); `zone.config-rollback` verb handler Primary reuse disposition: `adapt`. Preserved source-plan detail: extract exponential backoff from `begin_reconnect`.", "entryContracts" : [ "ADR-046-resources-zone-control" ], @@ -23091,6 +24160,7 @@ ], "specId" : "ADR-046-resources-zone-control", "topologicalRank" : 13, + "validation" : "All Phase 3 runtime and cleanup tests in §15.8 (`nix-runtime-bundledigest-integrity`, `nix-runtime-generation-monotone`, `nix-runtime-zoneuid-mismatch-rejected`, `nix-runtime-zonename-mismatch-rejected`, `nix-runtime-activation-nonblocking`, `nix-runtime-provider-config-invalid-continues`, all `cleanup-*` and `rollback-*` tests)", "wave" : "W5" }, { @@ -23098,6 +24168,7 @@ "destinations" : [ "`packages/d2b-provider/src/{registry,rpc}.rs` (new v3 Provider package); `packages/d2b-provider-toolkit/src/{adapter,conformance,fixture,registration,server,values,redaction}.rs` (new v3 toolkit)" ], + "detailedDesign" : "**Selected**: `ProviderRegistry` lifecycle `Accepting→Draining→Retired` with drain-waiter notify-race safety (ported from `shutdown_closes_final_permit_notify_race` / `finish_drain_closes_final_permit_notify_race` tests); `InFlightPermit` + global + per-provider in-flight quota; `ProviderRegistryManager::publish` validates snapshot before swap; `RpcProviderProxy::preflight` cancellation-check, deadline-check, method/capability match, session-identity/placement exactness; `ProviderAgentAdapter` rejects attachments with missing descriptors or non-increasing indexes (lines 79–99); `GeneratedProviderServiceServer::new` single-service-per-agent requirement + shutdown via atomic accept-flag + idle notify + timeout; `check_provider_conformance` health/inspection/observability check sequence; `Fixture`/`FakeProvider`/`DeterministicClock` as conformance harness; `Redacted`/`Secret` retained unchanged. **Excluded ADR45 assumptions**: `TrustedFirstPartyInProcess` as the only accepted placement (in `session_identity_matches_placement` lines 577–598): v3 Provider resources support multiple placements per `RealmControllerPlacement` mapping (§16.2); in-process placement is retained but not exclusive. Generated ttrpc stubs in `d2b-contracts/src/generated_v2_services/` are v2-service-specific; v3 Provider processes compile their own stubs from v3 proto files. ACA workload adapter (`d2b-gateway-runtime/src/aca_workload.rs`) excluded. Primary reuse disposition: `adapt`. Preserved source-plan detail: copy + adapt.", "entryContracts" : [ "ADR-046-resources-zone-control" ], @@ -23113,6 +24184,7 @@ ], "specId" : "ADR-046-resources-zone-control", "topologicalRank" : 11, + "validation" : "`provider-registry-drain-waiter-race-safe` (ported from both notify-race tests), `provider-registry-publish-validates-snapshot-before-swap`, `provider-rpc-proxy-placement-exact` (ported from `provider_and_user_agent_session_identities_are_placement_exact`), `provider-agent-adapter-rejects-non-monotone-attachment-indexes`, `provider-server-shutdown-drains-in-flight-requests`, `provider-conformance-health-inspection-observability-sequence`", "wave" : "W5" }, { @@ -23120,6 +24192,7 @@ "destinations" : [ "`packages/d2b-core-controller/src/zone_link.rs` (ZoneLink handler); `packages/d2b-resource-api/src/admission.rs` (request admission)" ], + "detailedDesign" : "**Selected**: `SessionPhase` 5-phase FSM (from main `d2b-session`) drives ZoneLink session state; `Established` state → `status.connected=true` + ZoneLink `status.phase=Ready`; `Disconnected`/`Reconnecting` states → `status.connected=false` + ZoneLink `status.phase=Pending` (or `Degraded` if degraded capability); session-internal phases are not exposed as `ZoneLink.status.phase` values — only the common Resource phases (`Pending|Ready|Degraded|Failed|Unknown`) appear in `status.phase` (§3.5); `begin_reconnect` window/attempt logic → ZoneLink reconnect loop; `RealmSessionAuthority` local vs gateway pattern → ZoneLink authority types for host-local vs transport-bridge sessions; `RealmServiceServer::call` wire validation (generation, request lifetime, attachment) → Zone API request admission; `RealmServiceLimits` 15 fields → ZoneLink `spec.limits`; `connect_component_session()` Nn peer-UID path → Zone runtime bootstrap ComponentSession; `NamedStream` lifecycle → ZoneLink named-stream operations; session-lifecycle tests ported as ZoneLink phase regression tests. **Excluded ADR45 assumptions**: `RealmSessionAuthority::gateway_peer()` (lines 72–87): gateway custody and `Locality::Remote` + `CredentialCustody::GatewayGuest` are ADR45 realm-gateway patterns; v3 ZoneLink transport is bound by the resolved `spec.transportProviderRef`, `spec.transportSettings`, and `spec.transportCredentials` contract instead. Realm 7-phase `SessionLifecycle` (`Allocating→…→Running→Stopping→Stopped`) is the ADR45 realm-specific lifecycle; ZoneLink uses the 5-phase d2b-session model. `GuestBootstrapPsk`/`GuestSessionCredentialV1`: ADR45 guest bootstrap, excluded. `realm_stubs.rs` `ApiService`/`ApiFrontend` dead code excluded (§16.2).", "entryContracts" : [ "ADR-046-resources-zone-control" ], @@ -23136,6 +24209,7 @@ ], "specId" : "ADR-046-resources-zone-control", "topologicalRank" : 12, + "validation" : "`zonelink-reconnect-child-uid-change`, `zonelink-disconnect-unknown-phase`, `zonelink-intent-queue-limit`, `zone-session-phase-forward-sequence-refuses-repeat` (ported from `forward_sequence_reaches_running_then_refuses_to_advance`), `zone-session-failure-records-phase` (ported from `failure_mid_establishment_rolls_into_teardown_and_records_phase`), `zone-session-stop-is-idempotent` (ported), `zone-bootstrap-session-nn-peer-uid-verified`", "wave" : "W5" }, { @@ -23143,6 +24217,7 @@ "destinations" : [ "`packages/d2b-contracts/src/v3/{resource_export,resource_import}.rs` (base schemas); `packages/d2b-core-controller/src/export_import.rs` (core ZoneLink export/import routing controller); shared adapter trait in `packages/d2b-provider/src/share_adapter.rs` (`ExportAdapter`/`ImportAdapter` signed-capability traits)" ], + "detailedDesign" : "Implement the `ResourceExport` and `ResourceImport` standard ResourceTypes per §8A plus signed Provider `ProjectionFactory` metadata binding qualified Service type, qualified Binding type, allowed owner-Service backing refs, allowed Binding target refs, projection schema/fingerprint, and aggregate factory fingerprint. Admission accepts only an owner Service as `ResourceExport.resourceRef`; matches export/import/local-factory type and fingerprints; and creates exactly one same-qualified-type projection Service (`ownerRef: ResourceImport/`). It never projects Device/Endpoint/Binding and never creates Binding. Binding spec is desired consumer intent only; observations belong only in status. No cross-Zone Ref, FD, secret, path, locator, or resource grant crosses a Zone; payload bytes use bounded encrypted named streams and high-churn sessions/streams remain internal. Export removal/ZoneLink loss revokes leases and degrades the projection Service; reconnect revalidates generation and both fingerprints. D091 currency propagates Service → export → import → projection Service → authored Binding → children. Primary reuse disposition: `adapt`. Preserved source-plan detail: net-new (extend ZoneLink controller).", "entryContracts" : [ "ADR-046-resources-zone-control" ], @@ -23158,6 +24233,7 @@ ], "specId" : "ADR-046-resources-zone-control", "topologicalRank" : 11, + "validation" : "§8A.7: fast hermetic factory absent/mismatch/tamper, Service-only export target, exactly-one same-type projection Service, no Device/Endpoint/Binding projection, no auto-Binding, intent-only spec/status-only observations, backing/target allowlists, finalizer/update propagation, Provider classification, canonical Nix stability including compiler-only `d2b.zones.work.parentZone = \"local-root\"`, child-local `ZoneLink/work-uplink` in `d2b.zones.work.resources`, local `zoneLinkRef` resolution, quotas/reconnect/revoke, and no FD/secret/path tests; slower real encrypted-stream integration for audio/security-key/observability/policy-gated USBIP", "wave" : "W5" }, { @@ -23165,6 +24241,7 @@ "destinations" : [ "`packages/d2b-core-controller/src/export_import_projection.rs` (local qualified Service projection lifecycle owned by `ResourceImport`)" ], + "detailedDesign" : "Core creates exactly one same-qualified-type projection Service per `ResourceImport` and keeps it synchronized with the remote Service lease. Operators/Nix separately author same-Zone matching Binding resources with `serviceRef` plus an allowed Guest/User/Zone target; Binding specs hold desired intent only and Binding controllers write observations only to status while owning Process/Endpoint children. On revoke, mark the projection draining/revoked and let Binding controllers stop children. On delete, wait for Bindings to be deleted/retargeted (`BindingReferencesRemain`), release the lease, delete only the projection Service/provider-owned children, then clear the import finalizer. Never create/delete Binding or project Device/Endpoint.", "entryContracts" : [ "ADR-046-resources-zone-control" ], @@ -23179,6 +24256,7 @@ ], "specId" : "ADR-046-resources-zone-control", "topologicalRank" : 12, + "validation" : "Exactly one same-type Service projection owned by import; no Device/Endpoint/Binding projection; Binding never auto-created/deleted; Binding target allowlist; intent-only spec/status-only observations; owned Process/Endpoint child cleanup; pending finalizer while Binding refs remain; reconnect only after generation/factory/schema revalidation; hermetic fake-adapter + real-stream integration tiers", "wave" : "W5" }, { @@ -23186,6 +24264,7 @@ "destinations" : [ "`packages/d2b-core-controller/src/{coordinator,configuration,zonelink}.rs`" ], + "detailedDesign" : "Per D097 core-audit findings (§8B.2): move the process-global `USBIP_BACKGROUND_RECONCILE_ACTIVE`, `FORCE_SHUTDOWN_GENERATIONS`, and `activation_locks()` state into **per-Zone** provider/resource status or a per-Zone coordinator keyed by the authority index (no process-global singletons that ignore Zone boundaries). Migrate the configuration publisher's per-VM staging symbols to **per-Zone** staging under the single configuration-publisher authority. Make ZoneLink cursor persistence and restart adoption an authority owned by the ZoneLink handler (`ownerProof`; ambiguity quarantines). All coordinated through the core authority index; no direct broker path, no process-global lock. Primary reuse disposition: `adapt`. Preserved source-plan detail: `adapt` (move process-global state to per-Zone status/coordinator).", "entryContracts" : [ "ADR-046-resources-zone-control" ], @@ -23202,6 +24281,7 @@ ], "specId" : "ADR-046-resources-zone-control", "topologicalRank" : 14, + "validation" : "Two Zones on one host do not share `USBIP_BACKGROUND_RECONCILE_ACTIVE`/`FORCE_SHUTDOWN_GENERATIONS`/activation-lock state; per-Zone configuration staging isolation; ZoneLink cursor adoption by `ownerProof` and quarantine on ambiguity; hermetic with fakes", "wave" : "W5" }, { @@ -23209,6 +24289,7 @@ "destinations" : [ "`packages/d2b-core-controller/src/authority.rs`; resource API admission hook" ], + "detailedDesign" : "Admission enforces **Provider controller cardinality** via the core authority index: most Providers are `exactly-one` per Zone; the observability Provider is `at-most-one` (zero-or-one). A `Create`/activation that would install a second controller for an `exactly-one` Provider (or a second observability Provider) is rejected with `duplicateConflict` naming the incumbent owner digest before any effect; config activation goes `Degraded`.", "entryContracts" : [ "ADR-046-resources-zone-control" ], @@ -23224,6 +24305,7 @@ ], "specId" : "ADR-046-resources-zone-control", "topologicalRank" : 12, + "validation" : "Second Provider controller for an `exactly-one` Provider rejected with `duplicateConflict`; second observability Provider rejected; single controller admitted; `Degraded` config activation names the incumbent digest; hermetic", "wave" : "W5" }, { @@ -23231,6 +24313,7 @@ "destinations" : [ "`packages/d2b-core-controller/src/{quota,emergency_policy}.rs`; `packages/d2b-contracts/src/v3/{quota,emergency_policy}.rs`" ], + "detailedDesign" : "Implement `Quota` and `EmergencyPolicy` scope-uniqueness as `exactly-one`-per-scope authorities in the core authority index: a second `Quota`/`EmergencyPolicy` claiming the same scope is a `duplicateConflict`. Add the scope-uniqueness admission, status, and the test matrix (per D094 fast hermetic tests). Primary reuse disposition: `adapt`. Preserved source-plan detail: net-new (implementation + tests).", "entryContracts" : [ "ADR-046-resources-zone-control" ], @@ -23245,6 +24328,7 @@ ], "specId" : "ADR-046-resources-zone-control", "topologicalRank" : 12, + "validation" : "Duplicate-scope `Quota`/`EmergencyPolicy` rejected with `duplicateConflict`; single-scope admitted; union/individual scope flags honored; fast hermetic tests", "wave" : "W5" }, { @@ -23252,6 +24336,7 @@ "destinations" : [ "`packages/d2b-core-controller/src/authority.rs` (Host-global index scope + hardware admission)" ], + "detailedDesign" : "Extend the core authority index so `host`, `physical-device`, `seat`, and `external-service` authorities are keyed **Host-global** (`(Host, authorityClass, opaqueKeyDigest)`), admitting exactly one owner across all Zones on the host, while `zone`-scoped authorities stay Zone-local. Enforce the §8B.3 hardware rows: GPU full-device exclusive vs render-node shared; per-Guest swtpm and physical TPM exclusive (state never wiped); one Core-derived `physical-usb-backing/v1` identity digest claimed through the exact `(Host, physical-usb-backing, opaqueKeyDigest)` tuple by every USB or security-key implementation before effects, plus the separate host-global `usbip-host` module and the Host-global `Provider/device-usbip` relay `Endpoint`, exactly one per Core-derived Network UID/signed-policy-port digest with multiplexed arbitration and `usbip-network-relay-authority-conflict` on a second owner; macvtap/NIC `parentInterface` `passthru` globally exclusive across all Zones; host-shared `/dev/kvm` and `/dev/vhost-vsock` as `Provider/system-core` grants (no 28th Provider, no `kvm` busClass); globally-unique vsock CID; other fixed listener ports as `Endpoint`s; host store + per-Guest store-view writer; Network TAP/bridge. A second Zone claiming the same physical backing receives `physical-usb-backing-conflict` before any open, bind, withhold, module, relay, or attachment effect; restart adopts by `ownerProof`; Guest-stop drains dependent leases. GPU-owned `udmabuf`/video and per-session `vhost-vsock` tokens stay authority subresources/DeviceGrants (not resources/Providers). Primary reuse disposition: `adapt`. Preserved source-plan detail: net-new (Host-global index scope for host/physical-device authorities).", "entryContracts" : [ "ADR-046-resources-zone-control" ], @@ -23267,6 +24352,7 @@ ], "specId" : "ADR-046-resources-zone-control", "topologicalRank" : 13, + "validation" : "Two Zones on one host cannot both claim one GPU/TPM/USB/`/dev/kvm`/passthru NIC/vsock CID/fixed port — second is `duplicateConflict`; security-key and USB implementations resolving the same physical token submit byte-identical `physical-usb-backing` tuple keys and the loser receives `physical-usb-backing-conflict` before any effect; a second USBIP relay `Endpoint` for one Core-derived Network UID/signed-policy-port digest receives `usbip-network-relay-authority-conflict`, while multiple admitted Services share the multiplexed owner and no `Network` authority owns the listener/firewall; Provider-private authority classes/digests cannot bypass the collision; render-node shared admits bounded holders; per-Guest swtpm exclusive and marker never wiped; host-global adoption by `ownerProof`; hardware D096 exportability (GPU/KVM/TPM/store/macvtap non-exportable; semantic USB policy-gated); fast hermetic with fakes", "wave" : "W5" } ], diff --git a/docs/specs/ADR-046-implementation-graph.md b/docs/specs/ADR-046-implementation-graph.md index ddab5ac63..1dc6a0b56 100644 --- a/docs/specs/ADR-046-implementation-graph.md +++ b/docs/specs/ADR-046-implementation-graph.md @@ -14,6 +14,8 @@ The graph maps every member spec and every work item exactly once to a dependency-ordered launch wave (`W0`–`W7`) and a file-disjoint parallel group. It includes every resolved security-key work-item dependency; no lexical tie-break or omitted dependency is used. +Each JSON work-item node also embeds the manifest's exact `detailedDesign` and +`validation` text byte-for-byte. ## Counts diff --git a/docs/specs/ADR-046-primitive-resource-composition.md b/docs/specs/ADR-046-primitive-resource-composition.md index 644a64b07..c31b59d20 100644 --- a/docs/specs/ADR-046-primitive-resource-composition.md +++ b/docs/specs/ADR-046-primitive-resource-composition.md @@ -32,7 +32,7 @@ Core control: | ResourceType | Responsibility | | --- | --- | | Zone | Zone self identity, policy, API/store status | -| ZoneLink | Parent/child delegation, transport, cursor/health | +| ZoneLink | Child-local uplink delegation, transport, cursor/health; compiler-only `parentZone` selects the parent allocator | | Provider | Installed package/config/controllers/schemas/services/status | | Role | Bounded native RBAC rules | | RoleBinding | Subjects to Role with narrowing; no time-based expiry | diff --git a/docs/specs/ADR-046-resource-api-and-authorization.md b/docs/specs/ADR-046-resource-api-and-authorization.md index 35db52669..245da0aa7 100644 --- a/docs/specs/ADR-046-resource-api-and-authorization.md +++ b/docs/specs/ADR-046-resource-api-and-authorization.md @@ -428,9 +428,9 @@ A parent: The child commits to its own store. The parent receives no database handle, credential, token, or cross-Zone ResourceRef. -A disconnected parent may record a local ZoneLink intent but cannot claim the -child resource changed. On reconnect the child reauthorizes and applies/rejects -against current revision. +A disconnected child may record a bounded outbound intent in its child-local +ZoneLink but cannot claim the parent resource changed. On reconnect the parent +reauthorizes and applies/rejects against current revision. ## Limits diff --git a/docs/specs/ADR-046-resource-object-model.md b/docs/specs/ADR-046-resource-object-model.md index 52441912a..8bfa95375 100644 --- a/docs/specs/ADR-046-resource-object-model.md +++ b/docs/specs/ADR-046-resource-object-model.md @@ -649,7 +649,7 @@ means cross-Zone sharing via D096. | ResourceType | Default `authorityScope` | Default `cardinality` | Typical `arbitration` | Exportability | | --- | --- | --- | --- | --- | | `Zone` | zone | exactly-one (self) | exclusive | forbidden (singleton, not cross-Zone) | -| `ZoneLink` | zone (parent edge) | zero-or-one per edge | exclusive | forbidden | +| `ZoneLink` | child zone (local uplink edge) | zero-or-one per edge | exclusive | forbidden | | `Provider` | zone | bounded-many (per controller cardinality) | partitioned | forbidden | | `Role` / `RoleBinding` | zone | bounded-many | n/a (policy) | forbidden | | `Quota` | zone/scope | exactly-one per scope | exclusive (scope uniqueness) | forbidden | diff --git a/docs/specs/ADR-046-resources-credential.md b/docs/specs/ADR-046-resources-credential.md index 1c4d85325..37390f748 100644 --- a/docs/specs/ADR-046-resources-credential.md +++ b/docs/specs/ADR-046-resources-credential.md @@ -865,17 +865,17 @@ Required span attributes (closed set): | Attribute | Value | | --- | --- | -| `d2b.zone` | Zone name | -| `d2b.credential.name` | Credential resource name | | `d2b.credential.provider` | Provider name (e.g. `credential-entra`) | | `d2b.credential.operation_class` | Closed enum string | | `d2b.credential.placement_binding` | `user-agent` / `host-system` / `guest-agent` | | `d2b.credential.outcome` | Stable closed outcome code | | `d2b.credential.rotation_generation` | Numeric rotation generation | -Forbidden from spans/attributes: token bytes, audience literals, provider -diagnostics, host paths, resource IDs, tenant/subscription IDs, endpoint URIs, -correlation IDs that embed secret shapes. +Zone and Credential identity use the `d2b.zone` and `d2b.credential.name` +OTEL resource attributes only. They are forbidden as span attributes. +Also forbidden from spans/resource attributes: token bytes, audience literals, +provider diagnostics, host paths, resource IDs, tenant/subscription IDs, +endpoint URIs, and correlation IDs that embed secret shapes. ### Metrics @@ -889,9 +889,8 @@ correlation IDs that embed secret shapes. Label cardinality is bounded and semantic. The expiry gauge reports the minimum seconds remaining across active leases in each provider/placement -aggregate (0 when none); it carries no Credential resource name. Zone and -Credential identity remain available in bounded OTEL attributes and permitted -audit fields, never metric labels. +aggregate (0 when none); it carries no Credential resource name. Zone and Credential identity remain available in bounded OTEL resource +attributes and permitted audit fields, never metric labels or span attributes. ## Nix configuration @@ -1999,8 +1998,8 @@ config formalizes this as an `OpaqueAzureRef` with the same charset restriction. | Current source | `packages/d2b-core/src/privileges.rs:SecretAccess` (implemented-and-reachable); `d2b-realm-provider/src/error.rs:ProviderDiagnostic`/`contains_sensitive_shape` (implemented-and-reachable); `packages/d2b-contract-tests/tests/policy_observability.rs` (reachable audit policy tests) | | Reuse action | adapt | | Destination | `packages/d2b-provider-credential-/src/audit.rs`, `telemetry.rs`; `packages/d2b-contract-tests/tests/credential_audit.rs` | -| Detailed design | Implement audit record emission for all credential service methods and controller events using the field set defined in §Audit; implement OTEL span/metric emission using the closed semantic label set in §OTEL and metrics, with expiry reported as a provider/placement aggregate and no Credential/Zone/resource-name-derived label; implement `contains_sensitive_shape` check in all string fields of audit records and metric label values (adapted from `d2b-realm-provider/src/error.rs:contains_sensitive_shape`); add canary-enforcement tests that verify `"secret-canary"`, `"entra-token-canary"`, `"managed-identity-canary"`, Credential `metadata.name`, and Zone name values never appear in any audit record, metric label, span attribute, log line, or status field except identity in allowed OTEL/audit fields | +| Detailed design | Implement audit record emission for all credential service methods and controller events using the field set defined in §Audit; implement OTEL span/metric emission using the closed semantic label set in §OTEL and metrics, with expiry reported as a provider/placement aggregate and no Credential/Zone/resource-name-derived label; implement `contains_sensitive_shape` check in all string fields of audit records and metric label values (adapted from `d2b-realm-provider/src/error.rs:contains_sensitive_shape`); add canary-enforcement tests that verify `"secret-canary"`, `"entra-token-canary"`, `"managed-identity-canary"`, Credential `metadata.name`, and Zone name values never appear in any metric label, span attribute, log line, or status field; identity remains only in allowed OTEL resource attributes and permitted audit fields | | Integration | Credential controller and service handlers emit audit records and telemetry through Zone audit/OTEL paths | | Data migration | None — full d2b 3.0 reset; no prior state to migrate | -| Validation | Canary tests across all three Provider crates; audit record field-presence tests; structural metric descriptor tests assert exact absence of `vm`, `zone`, `zone_id`, `zone_uid`, `credential_name`, and every resource-name-derived key plus Credential/Zone-name canary absence; span attribute tests preserve allowed `d2b.zone`/`d2b.credential.name` identity and reject forbidden fields | +| Validation | Canary tests across all three Provider crates; audit record field-presence tests; structural metric descriptor tests assert exact absence of `vm`, `zone`, `zone_id`, `zone_uid`, `credential_name`, and every resource-name-derived key plus Credential/Zone-name canary absence; resource attribute tests preserve allowed `d2b.zone`/`d2b.credential.name` identity while span attribute tests reject identity and forbidden fields | | Removal proof | Not applicable | diff --git a/docs/specs/ADR-046-resources-network.md b/docs/specs/ADR-046-resources-network.md index fa76fb2d4..6e54dfe5e 100644 --- a/docs/specs/ADR-046-resources-network.md +++ b/docs/specs/ADR-046-resources-network.md @@ -1439,8 +1439,6 @@ attempt: ``` d2b.network.reconcile - network.name: - network.zone: network.generation: reconcile.trigger: reconcile.attempt: @@ -1462,9 +1460,9 @@ d2b.network.mdns.sync Metric labels use closed semantic cardinality and contain no Zone or Network identity. No workload IP, MAC, hostname, nftables rule text, or DHCP lease data appears in any span attribute, metric label, or log field. Zone identity -remains in the `d2b.zone` OTEL resource attribute. The `network.name` trace -attribute is a plain ResourceName, not a hostname or FQDN, and is never copied -into a metric label. +remains in the `d2b.zone` OTEL resource attribute. Network identity is likewise +available only as a bounded OTEL resource attribute and permitted audit field, +never as a span attribute or metric label. Metrics: @@ -2440,7 +2438,7 @@ bridges at reconcile time). | Current source | `nixos-modules/network.nix` (tap/sysctl sections); `packages/d2b-host/src/{bridge_port,nftables,netlink,routes}.rs`; broker ops in `packages/d2b-contracts/src/broker_wire.rs`: **real runtime ops** `ApplyNftables`, `ApplyNmUnmanaged`, `ApplyRoute`, `ApplySysctl`, `SetBridgePortFlags`, `UpdateHostsFile`, `SeedDnsmasqLease` (all `implemented-and-reachable`); **new ops to author**: `CreateBridge`, `DeleteBridge` (do not exist in v3 baseline; must be added to `broker_wire.rs` and implemented as `RealBrokerRequest` handlers in `packages/d2b-priv-broker/src/runtime.rs`); **NOT current ops**: `CreateMacvtap` does not exist — macvtap is created inside broker's `SpawnRunner` dispatch (`packages/d2b-priv-broker/src/runtime.rs` line 5097 `live_create_macvtap_fd`) | | Reuse action | adapt | | Destination | `packages/d2b-provider-network-local/src/controller.rs`: async NetworkReconciler; `packages/d2b-provider-network-local/src/plan.rs`: ReconcilePlan computation; `packages/d2b-provider-network-local/src/observe.rs`: drift-detection observe loop. Full crate layout required (see [Package and crate boundary](#package-and-crate-boundary)): `src/` (controller/plan/observe + colocated unit tests), `tests/` (hermetic conformance and state-machine tests), `integration/` (provider-system reconcile fixtures), `README.md` (Network ResourceType, controller binary, placement, RBAC, security invariants, build/test/integration commands). | -| Detailed design | Implements full async reconcile interface from `ADR-046-resource-reconciliation`. `plan()` computes desired vs. actual bridge-presence, sysctl, host-side nftables, hosts-file, NM-unmanaged, config Volume content, Guest, guest-agent Process, and mDNS-Process states. `reconcile()` dispatches in order: `CreateBridge` for each bridge not present (broker applies IPv6 sysctls atomically; `CreateBridge` failure sets `FabricReady=False/bridge-create-error` and aborts) → `ApplySysctl` (defense-in-depth IPv6) → `ApplyNftables` (host-side `inet d2b` table) → `ApplyNmUnmanaged` → `ApplyRoute` → `UpdateHostsFile` → `SeedDnsmasqLease` for new reservations → **Volume upsert** (two-phase): Phase 1 — create `Volume/net--config` with `kind: ephemeral`, `source.executionRef: Host/`, `source.settings.kind: tmpfs`, `quota: {maxBytes: 4194304, maxInodes: 128, enforcement: hard}` (tmpfs quota charged to Host memory budget), `layout` entries (root directory with `type: directory` and four config files each with `type: file`, `ownerRef: User/net-local-controller`, `groupRef: User/net-local-controller`, `mode: "0640"`, `accessAcl: []`, `defaultAcl: []`, `noFollow: true`, conservative create/repair/cleanup policies), `views: {guest-readonly: {path: "", rights: [read, traverse]}}`, `attachments: []` (no Guest attachment); abort on terminal error with `ConfigVolumeReady=False/config-volume-error`. Wait for Volume backing to reach `Ready`; requeue on `Degraded`/`Failed` with `ConfigVolumeReady=False/volume-not-ready`. Write rendered config content through Volume write service (no raw host paths). Phase 2 — create Guest upsert with `systemArtifactId` from REQUIRED `Network.spec.netVmSystemArtifactId`. Wait for Guest `Ready`. Then update Volume with Guest attachment: `attachments: [{executionRef: Guest/, transport: virtiofs, view: guest-readonly, access: read-only, mountPath: "/run/d2b/net-config", settings: {posixAcl: false, xattr: false, cache: auto, inodeFileHandles: never, threadPoolSize: null, socketGroup: null}}]`; wait for attachment `Ready`; requeue on `Degraded` with `ConfigVolumeReady=False/attachment-not-ready`. Create or update guest-agent Process `Process/net--agent` with `processClass: worker`, `sandbox: {namespaceClasses: [], capabilityClasses: [network-admin, network-bind, network-raw]}` (inherits Guest network namespace; `network-admin`→`CAP_NET_ADMIN`, `network-bind`→`CAP_NET_BIND_SERVICE`, `network-raw`→`CAP_NET_RAW`, all effective in Guest network namespace only; INV-NET-009), `mounts: [{volumeRef: Volume/net--config, view: guest-readonly, mountPath: "/run/d2b/net-config", access: read-only, required: true}]`. mDNS Process upsert when `spec.mdns.enable = true` (D-NETWORK-001) → `SetBridgePortFlags` per tap. Each broker op returns typed audit evidence. `observe()` re-reads `firewallDigest` (host-side), bridge isolation flags, IPv6 sysctls, and guest-agent Process status (`dnsmasq-bound`, `firewall-applied` predicates); queues reconcile on drift. Metrics use only the fixed semantic labels in §OTEL spans and metrics; Zone/Network identity remains in OTEL resource/trace attributes and never enters metric labels. **Finalizer** (strictly child-first): `NetworkDraining` → delete guest-agent Process and mDNS Processes; wait for each Deleted watch event → update Volume to remove Guest attachment (`attachments: []`); wait for attachment removal confirmed → delete `Guest/`; wait for Deleted watch event → delete `Volume/net--config`; wait for Deleted watch event → `ApplyNftables` (empty) → `ApplyNmUnmanaged` (empty) → `UpdateHostsFile` (empty) → `DeleteBridge` for each bridge (idempotent) → clear finalizer. No USBIP rules installed by Network; `UsbipBindFirewallRule` issued by device-usbip directly (D-NETWORK-002). | +| Detailed design | Implements full async reconcile interface from `ADR-046-resource-reconciliation`. `plan()` computes desired vs. actual bridge-presence, sysctl, host-side nftables, hosts-file, NM-unmanaged, config Volume content, Guest, guest-agent Process, and mDNS-Process states. `reconcile()` dispatches in order: `CreateBridge` for each bridge not present (broker applies IPv6 sysctls atomically; `CreateBridge` failure sets `FabricReady=False/bridge-create-error` and aborts) → `ApplySysctl` (defense-in-depth IPv6) → `ApplyNftables` (host-side `inet d2b` table) → `ApplyNmUnmanaged` → `ApplyRoute` → `UpdateHostsFile` → `SeedDnsmasqLease` for new reservations → **Volume upsert** (two-phase): Phase 1 — create `Volume/net--config` with `kind: ephemeral`, `source.executionRef: Host/`, `source.settings.kind: tmpfs`, `quota: {maxBytes: 4194304, maxInodes: 128, enforcement: hard}` (tmpfs quota charged to Host memory budget), `layout` entries (root directory with `type: directory` and four config files each with `type: file`, `ownerRef: User/net-local-controller`, `groupRef: User/net-local-controller`, `mode: "0640"`, `accessAcl: []`, `defaultAcl: []`, `noFollow: true`, conservative create/repair/cleanup policies), `views: {guest-readonly: {path: "", rights: [read, traverse]}}`, `attachments: []` (no Guest attachment); abort on terminal error with `ConfigVolumeReady=False/config-volume-error`. Wait for Volume backing to reach `Ready`; requeue on `Degraded`/`Failed` with `ConfigVolumeReady=False/volume-not-ready`. Write rendered config content through Volume write service (no raw host paths). Phase 2 — create Guest upsert with `systemArtifactId` from REQUIRED `Network.spec.netVmSystemArtifactId`. Wait for Guest `Ready`. Then update Volume with Guest attachment: `attachments: [{executionRef: Guest/, transport: virtiofs, view: guest-readonly, access: read-only, mountPath: "/run/d2b/net-config", settings: {posixAcl: false, xattr: false, cache: auto, inodeFileHandles: never, threadPoolSize: null, socketGroup: null}}]`; wait for attachment `Ready`; requeue on `Degraded` with `ConfigVolumeReady=False/attachment-not-ready`. Create or update guest-agent Process `Process/net--agent` with `processClass: worker`, `sandbox: {namespaceClasses: [], capabilityClasses: [network-admin, network-bind, network-raw]}` (inherits Guest network namespace; `network-admin`→`CAP_NET_ADMIN`, `network-bind`→`CAP_NET_BIND_SERVICE`, `network-raw`→`CAP_NET_RAW`, all effective in Guest network namespace only; INV-NET-009), `mounts: [{volumeRef: Volume/net--config, view: guest-readonly, mountPath: "/run/d2b/net-config", access: read-only, required: true}]`. mDNS Process upsert when `spec.mdns.enable = true` (D-NETWORK-001) → `SetBridgePortFlags` per tap. Each broker op returns typed audit evidence. `observe()` re-reads `firewallDigest` (host-side), bridge isolation flags, IPv6 sysctls, and guest-agent Process status (`dnsmasq-bound`, `firewall-applied` predicates); queues reconcile on drift. Metrics use only the fixed semantic labels in §OTEL spans and metrics; Zone/Network identity remains in OTEL resource attributes and permitted audit fields and never enters metric labels or span attributes. **Finalizer** (strictly child-first): `NetworkDraining` → delete guest-agent Process and mDNS Processes; wait for each Deleted watch event → update Volume to remove Guest attachment (`attachments: []`); wait for attachment removal confirmed → delete `Guest/`; wait for Deleted watch event → delete `Volume/net--config`; wait for Deleted watch event → `ApplyNftables` (empty) → `ApplyNmUnmanaged` (empty) → `UpdateHostsFile` (empty) → `DeleteBridge` for each bridge (idempotent) → clear finalizer. No USBIP rules installed by Network; `UsbipBindFirewallRule` issued by device-usbip directly (D-NETWORK-002). | | Integration | Controller process registers descriptor, watches `Network` resources via d2b-bus/ComponentSession/ResourceClient. Owned Guest and Process mutations trigger owner reconciliation. Device-usbip watches only Network identity/readiness/generation; its Core adapter privately resolves relay/firewall effects (D-NETWORK-002). | | Data migration | None after full reset | | Validation | `ADR046-reconcile-001` toolkit conformance; latency gates (p95 ≤5 ms hint-to-handler); Network-specific: CIDR conflict blocks reconcile, `CreateBridge` failure sets `FabricReady=False`, Volume creation failure sets `ConfigVolumeReady=False/config-volume-error`, `User/net-local-controller` not Ready aborts with `ConfigVolumeReady=False/user-not-ready`, Volume schema round-trip (kind=ephemeral, source.settings.kind=tmpfs, quota.enforcement=hard, layout type=file entries, views.guest-readonly.rights=[read,traverse]), tmpfs quota charged to Host memory budget (test Volume creation fails when Host memory budget exceeded), Guest not created before Volume backing `Ready`, Guest attachment not added before Guest `Ready`, guest-agent Process created after attachment `Ready` (`processClass: worker`, `namespaceClasses: []`, `capabilityClasses: [network-admin, network-bind, network-raw]`, `access: read-only`, `required: true`), host-capability leakage test: no host-netns process gains `CAP_NET_ADMIN`/`CAP_NET_BIND_SERVICE`/`CAP_NET_RAW` as result of guest-agent launch (INV-NET-009; `tests/host-integration/guest-agent-cap-confinement.nix`), `DeleteBridge` called on finalizer, Volume attachment removed before Guest deletion in finalizer (test order: agent Deleted → attachment removed → Guest Deleted → Volume Deleted → bridges), east-west invariant (INV-NET-003), hostBlocklist enforcement (INV-NET-004), macvtap attachment status (delegated to runtime-ch), mDNS Process created/deleted with `spec.mdns.enable` toggle, broker INV-NET-002 tests, config-only spec change updates Volume content and triggers agent reload without Guest restart (INV-NET-008); golden tests updated for v3 IfNames; structural metric descriptor test asserts exact absence of `vm`, `zone`, `zone_id`, `zone_uid`, `network`, and every resource-name-derived label and verifies a Network-name canary is absent from emitted label values | diff --git a/docs/specs/ADR-046-spec-set.json b/docs/specs/ADR-046-spec-set.json index c467c9bbf..1bc09eb64 100644 --- a/docs/specs/ADR-046-spec-set.json +++ b/docs/specs/ADR-046-spec-set.json @@ -13,7 +13,7 @@ "ADR-046-terminology-and-identities" ], "path" : "docs/specs/ADR-046-cli-and-operations.md", - "sha256" : "54cb3d03cf39c5c40475e176f5a9acd306dc550709427ed180cfa7863d5eb615", + "sha256" : "abaa92ab30246ffc400a59ff5502695d23576445a240e1bfd6f606de904a8f3f", "specId" : "ADR-046-cli-and-operations", "status" : "Proposed", "supersedes" : "Current v3 `d2b` CLI contract (`packages/d2b/src/lib.rs` at baseline)", @@ -84,7 +84,7 @@ { "dependsOn" : [], "path" : "docs/specs/ADR-046-decision-register.md", - "sha256" : "d8dbd3c22bca785cd347e2b09ed3ff5c0b66c9ca5e8d0de7af17bc6550d9f690", + "sha256" : "e61e9d405c772cb305a59cca5391a1f38a0cba66d09fc32c5fe6d12573a5d4b4", "specId" : "ADR-046-decision-register", "status" : "Proposed", "supersedes" : null, @@ -145,7 +145,7 @@ "ADR-046-resource-reconciliation" ], "path" : "docs/specs/ADR-046-primitive-resource-composition.md", - "sha256" : "26c01f5c43de58396c81314ba9581486f6e168c2037d197619dee45b00e78fd2", + "sha256" : "917d208ed95aa39b3e4c5c62fa8a8d4a0ee048a11e536a82c71b87875f4f4c50", "specId" : "ADR-046-primitive-resource-composition", "status" : "Proposed", "supersedes" : "Current implementation-shaped ProcessRole composition", @@ -169,7 +169,7 @@ "ADR-046-telemetry-audit-and-support" ], "path" : "docs/specs/providers/ADR-046-provider-activation-nixos.md", - "sha256" : "cfbfec0bde69a02f769a4b889eed4b3942f5a67f4ad56bdd7d8702476a67e738", + "sha256" : "1389b1542c8dd9cf5a8a1744e56d9040bba5645e170652bf3653d56328c4b1d7", "specId" : "ADR-046-provider-activation-nixos", "status" : "Proposed", "supersedes" : "Current `d2b switch`/`boot`/`test`/`rollback`/`build`/`generations`/`gc`/`migrate` top-level verbs in `packages/d2b/src/lib.rs` and hardlink-farm activation in `packages/d2b-host/src/hardlink_farm.rs`", @@ -194,7 +194,7 @@ "ADR-046-telemetry-audit-and-support" ], "path" : "docs/specs/providers/ADR-046-provider-audio-pipewire.md", - "sha256" : "150ac5317cce68d193fbdb5188c1b0dacd26d78abea2d49e19353bf657cf8012", + "sha256" : "21d97d1131de083390174720eec9ebe7754a6d812edcd94f0df853425301a3bf", "specId" : "ADR-046-provider-audio-pipewire", "status" : "Proposed", "supersedes" : "`nixos-modules/components/audio/host.nix`, `nixos-modules/components/audio/guest.nix`, `packages/d2b-core/src/audio_policy.rs`, `packages/d2bd/src/audio_dispatch.rs`, `packages/d2bd/src/audio_host_controller.rs`, `packages/d2b-host/src/audio_argv.rs`", @@ -215,7 +215,7 @@ "ADR-046-telemetry-audit-and-support" ], "path" : "docs/specs/providers/ADR-046-provider-clipboard-wayland.md", - "sha256" : "2f270d76ee8b53f2c6b81d92b057868c50789c0f81366a3877c173beaaeba6ae", + "sha256" : "bd8d266ecff067d3475718cf434ffc1ce91a6f697d34a76de16776a2c6d90ace", "specId" : "ADR-046-provider-clipboard-wayland", "status" : "Proposed", "supersedes" : "`nixos-modules/clipboard.nix` (v3 migration), ADR-046-provider-clipboard-wayland v1", @@ -254,7 +254,7 @@ "ADR-046-telemetry-audit-and-support" ], "path" : "docs/specs/providers/ADR-046-provider-credential-managed-identity.md", - "sha256" : "8e5a211b4b74a29096605a23b9e90f85ee44363b1c6b11a7b6c25f82728cf821", + "sha256" : "5f0017c3f5c78e7504874e060c6d81ef8ab40281f0f8675d2b4e248a792c9984", "specId" : "ADR-046-provider-credential-managed-identity", "status" : "Proposed", "supersedes" : "Current v3 `ManagedIdentityRef`, `managed_identity_client_id` ACA config field, `CredentialProvider` trait (status/enrollment-only) in `d2b-realm-provider/src/credential.rs` and `provider.rs`", @@ -276,7 +276,7 @@ "ADR-046-telemetry-audit-and-support" ], "path" : "docs/specs/providers/ADR-046-provider-credential-secret-service.md", - "sha256" : "b4684b48faf8d2f359172c1c5910abf543f46b6bc5fd592df6034084a938fa76", + "sha256" : "cb8c810651f3cdc84bf7ac23a9f2d4cdfdda913aace8547373094e74fcc58d5f", "specId" : "ADR-046-provider-credential-secret-service", "status" : "Proposed", "supersedes" : "v2 `SecretServiceCredentialProvider` / `SecretServiceCredentialProviderFactory` in `d2b-realm-provider`; v2 `CredentialProvider` trait", @@ -297,7 +297,7 @@ "ADR-046-telemetry-audit-and-support" ], "path" : "docs/specs/providers/ADR-046-provider-device-gpu.md", - "sha256" : "b7bd5c4b1bf167f53256613250e2fe9ec1db6e0bd2b4b2170b7125612d0a461f", + "sha256" : "37552a2ffe27f45eabd43f6b9fc86ecfa3cc3ee5348bf72afc784849a158c453", "specId" : "ADR-046-provider-device-gpu", "status" : "Proposed", "supersedes" : "`ProcessRole::Gpu`, `ProcessRole::GpuRenderNode`, `ProcessRole::Video` in `packages/d2b-core/src/processes.rs`; Nix `nixos-modules/components/graphics.nix`; Nix `nixos-modules/components/video/guest.nix`; `d2b.vms..graphics.*` options", @@ -321,7 +321,7 @@ "ADR-046-telemetry-audit-and-support" ], "path" : "docs/specs/providers/ADR-046-provider-device-security-key.md", - "sha256" : "81f67a57285dfce25ce55eb3163025d2071cdc0ed6f7c18422b6b39216968fdc", + "sha256" : "85c299f4b747527430de8c319926b6ae32ef28101dd11d3c148032b2a700524d", "specId" : "ADR-046-provider-device-security-key", "status" : "Proposed", "supersedes" : "`ProcessRole::SecurityKeyFrontend` daemon-internal accept loop (`packages/d2bd/src/security_key.rs`), `nixos-modules/components/security-key-guest.nix` untracked guest `d2b-sk-frontend.service` unit", @@ -342,7 +342,7 @@ "ADR-046-resources-volume" ], "path" : "docs/specs/providers/ADR-046-provider-device-tpm.md", - "sha256" : "3d7b47892427a4b4932dd97740a0e270bc75c662c704d6a98cb7b75e7ea2883e", + "sha256" : "559c1f775f75218ed8fa1ff008f7083dcc893f87298dbd459b9d8dd3e7bf9d4c", "specId" : "ADR-046-provider-device-tpm", "status" : "Proposed", "supersedes" : "Current `nixos-modules/components/tpm.nix` swtpm provisioning and `d2b-priv-broker` swtpm-dir hardening path", @@ -367,7 +367,7 @@ "ADR-046-zone-routing" ], "path" : "docs/specs/providers/ADR-046-provider-device-usbip.md", - "sha256" : "7b01ffa56a0132639a19452656bfe45d97552e68ae4e8a970b12fe9e1acf6750", + "sha256" : "b9c4fff8cf7d8a10fe699030b737833f71b94553ff7341fed9029d48d9599cd3", "specId" : "ADR-046-provider-device-usbip", "status" : "Proposed", "supersedes" : "`nixos-modules/components/usbip.nix` (host-side), per-env usbipd systemd units in `nixos-modules/network.nix`, `ProcessRole::Usbip` / `RunnerRole::Usbip` in current v3 baseline", @@ -390,7 +390,7 @@ "ADR-046-telemetry-audit-and-support" ], "path" : "docs/specs/providers/ADR-046-provider-display-wayland.md", - "sha256" : "dd598b4c5f55fdb29f4a25831ab72814fa4082f23c48cda9ab551a8423a46521", + "sha256" : "e52865faa06261936fad094057072c1e3448610e0b90c5eacba5a43019067065", "specId" : "ADR-046-provider-display-wayland", "status" : "Proposed", "supersedes" : "`ProcessRole::WaylandProxy` in `packages/d2b-core/src/processes.rs`; `LocalCrossDomainWaylandProvider` in `packages/d2b-host-providers/src/lib.rs`; `generate_wayland_proxy_argv` in `packages/d2b-host/src/wayland_proxy_argv.rs`; `nixos-modules/components/graphics.nix` `graphics.waylandProxy.*` options; `nixos-modules/ui-colors.nix` VM border color resolution; current `pkgs/wl-cross-domain-proxy` guest binary", @@ -429,7 +429,7 @@ "ADR-046-telemetry-audit-and-support" ], "path" : "docs/specs/providers/ADR-046-provider-network-local.md", - "sha256" : "22e12144bf2f6a52dccc2288080f1f07777569bcbcbd7be80cac6bdb1ed2581a", + "sha256" : "0573cb8efd9ddc30d87cb214995b7ca98bf61a12d635574f4dc052d355e314a9", "specId" : "ADR-046-provider-network-local", "status" : "Proposed", "supersedes" : "`nixos-modules/network.nix`, `nixos-modules/net.nix`", @@ -449,7 +449,7 @@ "ADR-046-telemetry-audit-and-support" ], "path" : "docs/specs/providers/ADR-046-provider-notification-desktop.md", - "sha256" : "e6c7e56fd23920a53a5f1308cd750a79d283ef2415420d0ed2871d3e1bdd7333", + "sha256" : "d5b9c1e42ed51f70e95193c525331f68a2622fdd1d958985f138766ea2fc41dc", "specId" : "ADR-046-provider-notification-desktop", "status" : "Proposed", "supersedes" : null, @@ -474,7 +474,7 @@ "ADR-046-terminology-and-identities" ], "path" : "docs/specs/providers/ADR-046-provider-observability-otel.md", - "sha256" : "c096549371c1092d986bb49be22f94b2f6928213d90d1e0e734e52fb84b48535", + "sha256" : "8ff7b46472e854b057cec49a7c91fbadd88828e68b246b01bbcae7e91ac4f1d6", "specId" : "ADR-046-provider-observability-otel", "status" : "Proposed", "supersedes" : "`ProcessRole::OtelHostBridge` / `RunnerRole::OtelHostBridge`; socat-based vsock forwarder in `packages/d2b-host/src/otel_host_bridge_argv.rs`; `packages/d2bd/src/otel_host_bridge_readiness.rs`; hand-rolled per-VM `nixos-modules/components/observability/` pipeline (adapted to per-Zone)", @@ -549,7 +549,7 @@ "ADR-046-telemetry-audit-and-support" ], "path" : "docs/specs/providers/ADR-046-provider-runtime-cloud-hypervisor.md", - "sha256" : "49242d7654a4ffe54a2a0ddaab4062c5a8f746d0e8e7d0eaef98d22b676a0b8e", + "sha256" : "b5a48303baa024cc621bd39fd5cdb730bdbb28991c5ea589afa7df82d8016da7", "specId" : "ADR-046-provider-runtime-cloud-hypervisor", "status" : "Proposed", "supersedes" : "`packages/d2b-host/src/runtime_provider.rs` `CloudHypervisorRuntimeProvider`; `packages/d2bd/src/` VM lifecycle paths; `d2b-host-providers` adapter; `ProcessRole::CloudHypervisor`, `ProcessRole::Swtpm`, `ProcessRole::NetVm`; systemd unit `d2b--vm.service`; `SwtpmDir` broker op", @@ -573,7 +573,7 @@ "ADR-046-telemetry-audit-and-support" ], "path" : "docs/specs/providers/ADR-046-provider-runtime-qemu-media.md", - "sha256" : "a941d6bac5b9b8e7b10063164f54776928c104ade0753544ca184331ca78d1ce", + "sha256" : "dee669643caa90526c4f47a66a65870eba76a6a0b40bb6331b2d4435b0b27db6", "specId" : "ADR-046-provider-runtime-qemu-media", "status" : "Proposed", "supersedes" : "`docs/adr/0036-qemu-media-runtime.md`", @@ -621,7 +621,7 @@ "ADR-046-terminology-and-identities" ], "path" : "docs/specs/ADR-046-provider-state.md", - "sha256" : "070529d4af6edcc1468a4f221509cac4d44497b55588ac253e029e6ccda5f390", + "sha256" : "dcf1087dfeaff6ad8cb1d5acbc33960a5c1bc692fa1109a95a58e4f4ab7e060f", "specId" : "ADR-046-provider-state", "status" : "Proposed", "supersedes" : "`d2b-core/src/storage.rs` generated contract, ADR 0034 storage/sync/adoption model for v3", @@ -707,7 +707,7 @@ "ADR-046-zone-routing" ], "path" : "docs/specs/providers/ADR-046-provider-transport-azure-relay.md", - "sha256" : "495872099dcd76c3731734a65c4c3413bc2d241ca6cefeda9d1ecb436b6d4ced", + "sha256" : "c90b289271b227a90608f5db64ff391b82af7dfa97bdbefe6495997e5fd64289", "specId" : "ADR-046-provider-transport-azure-relay", "status" : "Proposed", "supersedes" : "`d2b-provider-relay` gateway-display relay path (`AcaWorkloadProvider` + `RelayProvider` traits in `d2b-realm-provider`); `d2b-gateway-runtime/src/bin/d2b-gateway-relay.rs`; `packages/d2b-provider-relay/src/lib.rs` as a first-party transport surface", @@ -773,7 +773,7 @@ "ADR-046-telemetry-audit-and-support" ], "path" : "docs/specs/providers/ADR-046-provider-volume-local.md", - "sha256" : "584b0a86ce1bfc3ba896eb817c43ea707009815323b3a464d7aedc923a0a07ff", + "sha256" : "38a439ed5dfe280d7570ee73829fb1d45da8cd237a69ebc3466faaccc010cade", "specId" : "ADR-046-provider-volume-local", "status" : "Proposed", "supersedes" : "`d2b-priv-broker/src/ops/{state_dir,store_sync,store_sync_audit,store_sync_export,store_view_farm,store_view_posture,swtpm_dir}.rs`; `d2b-core/src/{storage,sync,storage_lifecycle}.rs` StorageJson/SyncJson contract; `nixos-modules/store.nix` per-VM hardlink farm activation", @@ -796,7 +796,7 @@ "ADR-046-telemetry-audit-and-support" ], "path" : "docs/specs/providers/ADR-046-provider-volume-virtiofs.md", - "sha256" : "b80201a129196e56150dfa0c4520ecd0ecc2d8eccfba2860c9962efeee284725", + "sha256" : "adbd75e715c4d02bb481dd93c34a282540ee4fd4274c9d2617c8ffc566522308", "specId" : "ADR-046-provider-volume-virtiofs", "status" : "Proposed", "supersedes" : "`nixos-modules/processes-json.nix` virtiofsdRunner block; `nixos-modules/minijail-profiles.nix` virtiofsdProfiles; `packages/d2b-host/src/virtiofsd_argv.rs`; `ProcessRole::Virtiofsd` dag nodes in `packages/d2bd/src/supervisor/dag.rs`", @@ -833,7 +833,7 @@ "ADR-046-zone-routing" ], "path" : "docs/specs/ADR-046-reset-and-cutover.md", - "sha256" : "8b9968dd5479cf7d26183beb8e146b91ee5f10b6ab36b0048a8578b4c08803cf", + "sha256" : "e317fb89859580c38bce1fec411a96b32150b396fe4cd34017cb5b1a058a3360", "specId" : "ADR-046-reset-and-cutover", "status" : "Proposed", "supersedes" : "[ADR 0034](../adr/0034-storage-lifecycle-restart-and-synchronization.md) \"Migration decision\" section for the d2b 3.0 cutover; the current `d2b host migrate-storage` verb (retired per `ADR-046-current-code-migration-map` §6 and `ADR-046-cli-and-operations` \"v2 command surface removed at 3.0 clean break\": it served the one-time v1→v2 storage layout cutover only and has no v3 successor)", @@ -849,7 +849,7 @@ "ADR-046-terminology-and-identities" ], "path" : "docs/specs/ADR-046-resource-api-and-authorization.md", - "sha256" : "68351d54635ad4813ee5de78968091991b7a037eef58958c9eb8953841c57023", + "sha256" : "cc43caa901707dbe4fc1eb1a53fd90a92805d3a55c252fd8ae1c52f08114e14b", "specId" : "ADR-046-resource-api-and-authorization", "status" : "Proposed", "supersedes" : null, @@ -864,7 +864,7 @@ "ADR-046-terminology-and-identities" ], "path" : "docs/specs/ADR-046-resource-object-model.md", - "sha256" : "451c0582b8f113c4a0f54367af7cbdb4d23a476adc25694e17b7e657928a7f71", + "sha256" : "a3b2361edee0a19279a81ada3a7ede3ecbc730890df40ea882b787b320e79883", "specId" : "ADR-046-resource-object-model", "status" : "Proposed", "supersedes" : null, @@ -915,7 +915,7 @@ "ADR-046-terminology-and-identities" ], "path" : "docs/specs/ADR-046-resources-credential.md", - "sha256" : "e2f0f583c0b5c8bfbda370960343648f141efd5376cc4e2f25f0b1a346b005a0", + "sha256" : "50715a0027a760ea454c65245a5a8fa03d5ddd2696533c54c9308ea96d283086", "specId" : "ADR-046-resources-credential", "status" : "Proposed", "supersedes" : "Current v3 `CredentialProvider` trait, `CredentialStatus`, `CredentialPlane` model in `d2b-realm-provider/src/credential.rs` and `provider.rs`", @@ -934,7 +934,7 @@ "ADR-046-resource-reconciliation" ], "path" : "docs/specs/ADR-046-resources-device.md", - "sha256" : "96c14da583e220bf4c9e35de5069702eadec4b1477fbcf80840ef13fed3ea0ce", + "sha256" : "9f00cc74331399b3e87cd413d5d0ab1b4fdf7f55480afa7b2416b4a48b3bd431", "specId" : "ADR-046-resources-device", "status" : "Proposed", "supersedes" : "Current ProcessRole device sidecars (Swtpm, SwtpmPreStartFlush, Usbip, SecurityKeyFrontend, Gpu, GpuRenderNode, Video) and their Nix components", @@ -975,7 +975,7 @@ "ADR-046-terminology-and-identities" ], "path" : "docs/specs/ADR-046-resources-network.md", - "sha256" : "238d8d2e2c7ad7ad3fa6e36d949bda3334263ccdc10a19d623e8f213132eb4cc", + "sha256" : "3d74031c68d030e1bbec8b76eabfee8983c5fe52135b62cf6711dacfa57b70c0", "specId" : "ADR-046-resources-network", "status" : "Proposed", "supersedes" : "`d2b.envs.` and `d2b.realms..network` Nix surfaces (v3 reset)", @@ -1014,7 +1014,7 @@ "ADR-046-terminology-and-identities" ], "path" : "docs/specs/ADR-046-resources-zone-control.md", - "sha256" : "6116228dd5f004891aa918bd76015fd202d0fc953e428d09026a883d5834b5bf", + "sha256" : "315a8425c62fded2acc8beff719eac35b695a866917e22b80ed2fa481f8fcde2", "specId" : "ADR-046-resources-zone-control", "status" : "Proposed", "supersedes" : null, @@ -1081,7 +1081,7 @@ "ADR-046-zone-routing" ], "path" : "docs/specs/ADR-046-security-and-threat-model.md", - "sha256" : "6f1c04852e3e7276d2744d20abd01afb47517b9fe2e6a67d458ff3bf6b59bccf", + "sha256" : "cc78ea774b8b248c729613ed57e254d4fced2c3881d1dd0521a8b9881331f083", "specId" : "ADR-046-security-and-threat-model", "status" : "Proposed", "supersedes" : null, @@ -1103,7 +1103,7 @@ "ADR-046-zone-routing" ], "path" : "docs/specs/ADR-046-streamline.md", - "sha256" : "54d78c6479cc97618beda2503b03e1283f37c52432ccf0c0fb2802e8241a0cd6", + "sha256" : "e8dffc8d7f35457d502cf0836e7956b7f8279e36788e4d391b23accbfdb1c063", "specId" : "ADR-046-streamline", "status" : "Proposed", "supersedes" : null, @@ -1123,7 +1123,7 @@ "ADR-046-terminology-and-identities" ], "path" : "docs/specs/ADR-046-telemetry-audit-and-support.md", - "sha256" : "9acf45aed9f496d72b30a9d093c00cff474beb272a09110f7c3cc4887b922720", + "sha256" : "0979726e4dfe84e1167628727e93d23caa810d247deb5d54036cff46b955777d", "specId" : "ADR-046-telemetry-audit-and-support", "status" : "Proposed", "supersedes" : "Current `d2bd` hand-rolled Prometheus registry; current daemon/broker/gateway JSONL audit paths", @@ -1141,7 +1141,7 @@ "ADR-046-decision-register" ], "path" : "docs/specs/ADR-046-terminology-and-identities.md", - "sha256" : "9a0d16015424be77cc0803642806f7884f596dc0d3daea5f0ad13231943678d4", + "sha256" : "1039934d58fa0777d8cf8216a0f8742eb46b09786715e1c3eb62ad53789e8531", "specId" : "ADR-046-terminology-and-identities", "status" : "Proposed", "supersedes" : "Public Realm terminology selected by ADR 0043 for d2b v3", @@ -1181,7 +1181,7 @@ "ADR-046-zone-routing" ], "path" : "docs/specs/ADR-046-validation-and-delivery.md", - "sha256" : "671aae07fa95d66ea3b7e7588868cec460aad9008a433a7499b4e59c952fb174", + "sha256" : "8a872ac456acf0fd29550e366a340dc4c844056b8311d0af9952f5f9e1f58816", "specId" : "ADR-046-validation-and-delivery", "status" : "Proposed", "supersedes" : "This repository's current `AGENTS.md` \"Panel review\" phase-gate as the *sole* review mechanism for ADR 0046 work (extended, not replaced, per §12); ad hoc per-agent validation ordering for ADR 0046 implementation", @@ -1199,7 +1199,7 @@ "ADR-046-terminology-and-identities" ], "path" : "docs/specs/ADR-046-zone-routing.md", - "sha256" : "06d3ebfaae8849a009e74b9652925ed4fa8b42bddf68248d2365eb95c6885455", + "sha256" : "71f678a258f8fb39762f625b98ac8e48ce4d7660511c4fa46f6c636e7adb74f8", "specId" : "ADR-046-zone-routing", "status" : "Proposed", "supersedes" : "`RealmEntrypointTable`/`EntrypointMode`/`RealmControllerPlacement` enum (→ compiler-only `parentZone` topology + ZoneLink transport + private Zone runtime bootstrap placement); `RouteTreeEngine`/`routing.rs` route contracts (adapted, not deleted); `RemoteNodeRegistry` (→ ZoneLink controller); `OperationRouter` (→ ZoneLinkIdempotencyKey in d2b-bus); `realm_access_resolver` module (→ ZoneEntrypointResolver); `realm-controllers.json`/`realm-identity.json` bundle artifacts (→ Nix-authored Zone settings/ZoneLink resources); `d2b.realms.*` Nix option namespace (→ `d2b.zones.*`); `WorkloadId` routing targets (→ typed Guest/Host resource refs); `CapabilitySet`-only routing authz (→ per-hop RBAC narrowing + capability ceiling)", diff --git a/docs/specs/ADR-046-telemetry-audit-and-support.md b/docs/specs/ADR-046-telemetry-audit-and-support.md index bb5d1851f..9506486e8 100644 --- a/docs/specs/ADR-046-telemetry-audit-and-support.md +++ b/docs/specs/ADR-046-telemetry-audit-and-support.md @@ -40,7 +40,7 @@ baseline symbols named below. | `WorkloadIdentity` / `WorkloadTarget` / `RealmTarget` (`d2b-core/src/workload_identity.rs`) | Zone self-resource reference `Zone/` | implemented-and-reachable | | `d2b.realms` Nix option (`nixos-modules/options-realms.nix`) | `d2b.zones` Nix option (ADR-only target) | generated-or-eval-contract | | `realm-controllers.json` bundle artifact | Zone runtime config (new generated artifact; existing file is retired) | generated-or-eval-contract | -| `d2b_daemon_vm_*` metrics with `vm` label (`packages/d2bd/src/metrics.rs`) | `vm` label (VM name) removed from v3 metric labels; VM-identity carried only in OTEL resource attributes and trace context | implemented-and-reachable | +| `d2b_daemon_vm_*` metrics with `vm` label (`packages/d2bd/src/metrics.rs`) | `vm` label (VM name) removed from v3 metric labels; VM identity carried only in bounded OTEL resource attributes and permitted audit fields | implemented-and-reachable | | `vm.name`, `vm.env`, `vm.role` OTEL resource attributes (`nixos-modules/components/observability/{host,stack,guest}.nix`) | Preserved in v3 (advisory from edge collector; re-stamped at ingress boundary). Extended with `d2b.zone`, `d2b.provider`, `d2b.component` (ADR-only additions) | implemented-and-reachable | | `d2b.observability.vmName` / `identityName` Nix options | `d2b.zones..observability.*` Nix options (ADR-only target) | generated-or-eval-contract | | `config_source = "realm-controllers"` tracing field (`d2b-priv-broker/src/runtime.rs`) | `config_source = "zone-config"` in v3 startup tracing | implemented-and-reachable | @@ -540,7 +540,7 @@ attributes (they are opaque digests). | `d2b.process.launch` | Internal | `provider`, `domain`, `outcome` | Commit-to-Ready → launch attempt | | `d2b.process.stop` | Internal | `provider`, `domain`, `stop_class`, `outcome` | | | `d2b.provider.reconcile` | Internal | `resource_type`, `outcome` | Per Provider instance | -| `d2b.provider.install` | Internal | `provider_name`, `outcome` | | +| `d2b.provider.install` | Internal | `provider_class`, `outcome` | Fixed implementation class, not Provider resource identity | ### Trace context propagation diff --git a/docs/specs/ADR-046-terminology-and-identities.md b/docs/specs/ADR-046-terminology-and-identities.md index 313426701..334bac951 100644 --- a/docs/specs/ADR-046-terminology-and-identities.md +++ b/docs/specs/ADR-046-terminology-and-identities.md @@ -31,9 +31,11 @@ Every Zone store contains exactly one authoritative: Zone/ ``` -The resource's metadata.zone equals ``. A parent represents a child -with a parent-local `ZoneLink/` resource. Parent access uses the child -Zone API; resources and ordinary refs are not copied across Zones. +The resource's metadata.zone equals ``. Each non-root child stores +one child-local `ZoneLink/` uplink. Its compiler-only `parentZone` +setting selects the parent allocator and is never emitted as a resource. +Parent access uses the child Zone API; resources and ordinary refs are not +copied across Zones. `Realm` remains current v3 baseline terminology and migration evidence. New d2b 3.0 public schemas, CLI, APIs, errors, and docs use `Zone`. diff --git a/docs/specs/ADR-046-validation-and-delivery.md b/docs/specs/ADR-046-validation-and-delivery.md index 69a838e1b..b20c23531 100644 --- a/docs/specs/ADR-046-validation-and-delivery.md +++ b/docs/specs/ADR-046-validation-and-delivery.md @@ -578,8 +578,8 @@ Ported verbatim (per `ADR046-session-001/002`) from main's class (Unix pathname/socketpair, enrolled KK key, bootstrap IKpsk2, native vsock); Role/RoleBinding decision-matrix property tests; revocation-latency test (relevant resource revision invalidates cache immediately after - durable commit); parent/child Zone access tests (disconnected-parent local - intent, reconnect reauthorization against current revision); attachment + durable commit); parent/child Zone access tests (disconnected child-local + outbound intent, reconnect reauthorization against current revision); attachment descriptor validation (encrypted, service/method/request/operation/ generation bound, CLOEXEC, duplicate-object rejection). @@ -1381,7 +1381,7 @@ binaries. | Reuse source | `ADR-046-spec-set.json`, `ADR-046-work-items.json`, and §3.1–§3.4/§3.5 of this spec; no new framework | | Reuse action | adapt | | Destination | `docs/specs/ADR-046-implementation-graph.json`, `docs/specs/ADR-046-implementation-graph.md` (generated by `ADR046-streamline-001`'s `xtask implementation-graph`); the artifact contract, generation, validation, and ready-wave query are owned by §3.5 of this spec | -| Detailed design | Owns the D095 implementation-graph contract: `artifactKind`/`schemaVersion`/`adr`/`status`; one node per member spec and per work item mapped exactly once to a `W0`–`W7` wave and a file-disjoint `parallelGroup`, with `owner`/`destinations`/`entryContracts`/`prerequisites`/`blockers`/`exitGate`/`topologicalRank`; typed `spec-depends-on`/`shared-contract`/`work-item-depends-on`/`implements-spec`/`file-overlap-order` edges; the §3.5.1 ready-wave query; and the anti-serialization invariant that every ready file-disjoint group launches concurrently while a same-wave dependency is a prep barrier, not whole-wave serialization. The graph is a generated non-member artifact and does not change the 55-member `ADR-046-spec-set.json` count. Primary reuse disposition: `adapt`. Preserved source-plan detail: net-new (D095 artifact contract). | +| Detailed design | Owns the D095 implementation-graph contract: `artifactKind`/`schemaVersion`/`adr`/`status`; one node per member spec and per work item mapped exactly once to a `W0`–`W7` wave and a file-disjoint `parallelGroup`, with `owner`/`destinations`/`entryContracts`/`prerequisites`/`blockers`/`exitGate`/`topologicalRank`; work-item nodes additionally embed the manifest's exact `detailedDesign` and `validation` text byte-for-byte; typed `spec-depends-on`/`shared-contract`/`work-item-depends-on`/`implements-spec`/`file-overlap-order` edges; the §3.5.1 ready-wave query; and the anti-serialization invariant that every ready file-disjoint group launches concurrently while a same-wave dependency is a prep barrier, not whole-wave serialization. The graph is a generated non-member artifact and does not change the 55-member `ADR-046-spec-set.json` count. Primary reuse disposition: `adapt`. Preserved source-plan detail: net-new (D095 artifact contract). | | Integration | Consumed by §4 wave entry/exit and §6 anti-serialization checks and by `ADR046-streamline-013`; a `tests/unit/gates/` drift gate regenerates and `git diff --exit-code`s the graph after any spec/work-item edit | | Data migration | None — docs/tooling only; no runtime state | | Validation | Every 55 spec node and every work item present exactly once; all edge endpoints resolve; graph acyclic; waves monotonic (dependencies earlier or explicit same-wave prep barrier); parallel groups claim no ordering absent a dependency/file-overlap edge; deterministic JSON with no timestamps/host paths; every Mermaid node ID valid; the ready-wave query returns the expected concurrently-launchable groups on a seeded fixture | diff --git a/docs/specs/ADR-046-work-items.json b/docs/specs/ADR-046-work-items.json index 202f7094c..39a744e5e 100644 --- a/docs/specs/ADR-046-work-items.json +++ b/docs/specs/ADR-046-work-items.json @@ -142,14 +142,14 @@ "dataMigration" : "Full d2b 3.0 reset; adopt mode records an existing active generation but does not import v2 controller state", "dependencyOwner" : "ADR046-activation-002; activation-nixos controller owner", "destination" : "packages/d2b-provider-activation-nixos/src/controller/", - "detailedDesign" : "Implement the reconcile loop for activation-nixos.d2bus.org.NixosGeneration: validate executionRef, systemArtifactId, and priorGenerationRef; dispatch one activation-runner EphemeralProcess with canonical startRoot=true shape; observe runner status; mark superseded generations; prune by retainedGenerations through the finalizer protocol; never perform direct store-path operations, nix-collect-garbage, explicit VolumeGcRequest, raw argv composition, or store path writes to resources. Primary reuse disposition: `replace`. Preserved source-plan detail: replace top-level imperative activation flow with resource controller logic.", + "detailedDesign" : "Implement the reconcile loop for activation-nixos.d2bus.org.NixosGeneration: validate executionRef, systemArtifactId, and priorGenerationRef; dispatch one activation-runner EphemeralProcess with canonical startRoot=true shape; observe runner status; mark superseded generations; prune by retainedGenerations through the finalizer protocol; emit §12.3 metrics with fixed `mode`/`outcome` semantics and no Zone or resource-name-derived labels; never perform direct store-path operations, nix-collect-garbage, explicit VolumeGcRequest, raw argv composition, or store path writes to resources. Primary reuse disposition: `replace`. Preserved source-plan detail: replace top-level imperative activation flow with resource controller logic.", "integration" : "Controller watches NixosGeneration resources through Zone resource API, creates activation-runner EphemeralProcesses, releases ownership references for Provider/volume-local, and writes bounded status.", "removalProof" : "Direct hardlink-farm and garbage-collection calls from activation-nixos reachable paths are absent after controller and runner tests pass.", "reuseAction" : "replace", "reuseSource" : null, "specId" : "ADR-046-provider-activation-nixos", "specPath" : "docs/specs/providers/ADR-046-provider-activation-nixos.md", - "validation" : "Controller tests for retention, finalizer sequence, no TTL retention, no direct store ops, no store path in status, deleted event-only removal, and runner shape.", + "validation" : "Controller tests for retention, finalizer sequence, no TTL retention, no direct store ops, no store path in status, deleted event-only removal, runner shape, and a structural metric descriptor assertion that `vm`, `zone`, `zone_id`, `zone_uid`, and resource-name-derived keys are absent and a generation/Zone-name canary never enters labels.", "workItemId" : "ADR046-activation-003" }, { @@ -311,7 +311,7 @@ "integration" : "Registered with Zone core as a controller under `Provider/audio-pipewire`.", "removalProof" : "Supersedes `audio_dispatch.rs`; `d2bd` audio dispatch deleted after e2e parity test confirms", "reuseAction" : "adapt", - "reuseSource" : null, + "reuseSource" : "None directly; reconcile flow is new async controller", "specId" : "ADR-046-provider-audio-pipewire", "specPath" : "docs/specs/providers/ADR-046-provider-audio-pipewire.md", "validation" : "Fast hermetic `tests/audio_service_controller.rs`: neutral type/provider selection, foreign-provider ignore/deny, owner authority, bounded aggregate microphone status, projection ownerRef/import chain, core-only create/delete, projection no-PipeWire-open, revocation queue cancellation, and D091 propagation. `tests/audio_binding_controller.rs`: neutral type/provider selection, required same-Zone serviceRef, owner/projection dispatch, child Process/private Endpoint state machine, `Applied|Queued|MicQueueFull` status mapping, off/delete/revocation cancellation, grant changes, absence/failures/deletion. Conformance asserts no AudioBinding export/projection, no broker/pidfd/EphemeralProcess/Volume/User ops. ProviderDeployment integration remains fake-only and validates empty ProviderStateSet.", @@ -326,7 +326,7 @@ "integration" : "Second binary in the `d2b-provider-audio-pipewire` package. Registered as a user-session service under `Provider/audio-pipewire`.", "removalProof" : "Supersedes `d2bd`'s `PipeWireHostController` direct session access; `d2bd` audio host controller deleted after e2e parity", "reuseAction" : "create", - "reuseSource" : null, + "reuseSource" : "None; new component", "specId" : "ADR-046-provider-audio-pipewire", "specPath" : "docs/specs/providers/ADR-046-provider-audio-pipewire.md", "validation" : "`tests/mediator.rs`: owner-Service FD handoff and calls; captureAlias; node-id sealing; session-unavailable; concurrent speaker Guest isolation; exclusive microphone no-overlap and mute-before-handoff; teardown; projection Service cannot resolve mediator Endpoint or portal attachment", @@ -727,14 +727,14 @@ "dataMigration" : "`d2b_daemon_vm_*` metrics retired; consumers must update dashboards", "dependencyOwner" : "ADR046-ch-001; telemetry foundation (`ADR046-telem-001`)", "destination" : "`packages/d2b-provider-runtime-cloud-hypervisor/src/metrics.rs`; `src/audit.rs`", - "detailedDesign" : "`d2b_runtime_ch_*` metrics from §18.3; bounded durable audit records from §17.3; no `vm=` metric label; no path/argv/socket in any field; closed OTEL attribute allowlist extended per §18.4", + "detailedDesign" : "`d2b_runtime_ch_*` metrics from §18.3 with closed semantic labels and no `vm`, `zone`, `zone_id`, `zone_uid`, or resource-name-derived key; bounded durable audit records from §17.3; no path/argv/socket in any field; identity retained in the closed OTEL resource-attribute allowlist extended per §18.4", "integration" : "Zone lightweight bounded emitter; `Provider/observability-otel` forwarding", "removalProof" : "Hand-rolled Prometheus registry (`d2bd/src/metrics.rs` `d2b_daemon_vm_*` section) deleted after migration", "reuseAction" : "replace", "reuseSource" : null, "specId" : "ADR-046-provider-runtime-cloud-hypervisor", "specPath" : "docs/specs/providers/ADR-046-provider-runtime-cloud-hypervisor.md", - "validation" : "`policy_observability.rs` updated with v3 allowlist; cardinality tests; bounded message/field tests; audit record schema golden vectors", + "validation" : "`policy_observability.rs` updated with v3 allowlist; structural descriptor tests assert exact absence of `vm`, `zone`, `zone_id`, `zone_uid`, and resource-name-derived keys plus Guest/Zone-name canary absence; bounded message/field tests; audit record schema golden vectors", "workItemId" : "ADR046-ch-006" }, { @@ -911,7 +911,7 @@ "integration" : "Nix build → per-Zone `resource-bundle.json` + global private artifact catalog → `d2b activation switch` → `d2b-core-controller` configuration service → resource API Create/Update/Delete → owner controllers → finalizer cascade → cleanup watcher → Zone status update", "removalProof" : "Old `nixos-modules/manifest.nix`, `nixos-modules/bundle-artifacts.nix` emitters removed only after `bundle-emit.nix` produces equivalent-or-superseding output and all downstream consumers of the old bundle format are migrated", "reuseAction" : "replace", - "reuseSource" : null, + "reuseSource" : "None (new implementation; no main `a1cc0b2d` reuse — this is the Nix/Zone side, not the CLI client side)", "specId" : "ADR-046-cli-and-operations", "specPath" : "docs/specs/ADR-046-cli-and-operations.md", "validation" : "Runtime integration: all CLI-visible cleanup/status/rollback/gc/audit tests (§CLI-visible tests for activation and cleanup), including no force-finalizer path; Nix unit and build tests owned by ADR-046-nix-configuration spec; canonical-example fixture validates every authored resource against the authoritative Host, Guest, and Provider schemas, resolves every artifact/resource reference, rejects unsupported fields, and proves the compiled Guest `spec` is field-for-field identical to the adjacent JSON", @@ -922,7 +922,7 @@ "dataMigration" : "None — full d2b 3.0 reset; no prior state to migrate", "dependencyOwner" : "ADR046-cli-001, ADR046-cli-010; CLI crate owner", "destination" : "`packages/d2b/src/endpoint.rs` (`d2b endpoint get/list/watch/status/resolve`)", - "detailedDesign" : "Add `Endpoint` to the frozen standard ResourceType set (17 types; local ResourceRef parser accepts `Endpoint/` with no Zone round-trip). Implement `d2b endpoint get/list/watch/status` over the ADR046-cli-010 generic verbs and `d2b endpoint resolve ` as a provider-neutral resolution projection printing `producerRef`, `endpointClass`, transport class, readiness, and capability/locality observations. Endpoint output is the base envelope only (base spec + base status per D092); any `status.provider` projection is bounded, redacted, and deny-unknown. **No raw locator** (path/address/CID/port/fd/credential) appears in any CLI field or the `resolve` projection. `status.update` currency and `d2b upgrade Endpoint/` visibility follow the standard verbs and D091. `create`/`update-spec`/`delete` are rejected on the operator surface except for statically Nix/API-authored Endpoints whose schema permits it.", + "detailedDesign" : "Add `Endpoint` to the frozen standard ResourceType set (19 types; local ResourceRef parser accepts `Endpoint/` with no Zone round-trip). Implement `d2b endpoint get/list/watch/status` over the ADR046-cli-010 generic verbs and `d2b endpoint resolve ` as a provider-neutral resolution projection printing `producerRef`, `endpointClass`, transport class, readiness, and capability/locality observations. Endpoint output is the base envelope only (base spec + base status per D092); any `status.provider` projection is bounded, redacted, and deny-unknown. **No raw locator** (path/address/CID/port/fd/credential) appears in any CLI field or the `resolve` projection. `status.update` currency and `d2b upgrade Endpoint/` visibility follow the standard verbs and D091. `create`/`update-spec`/`delete` are rejected on the operator surface except for statically Nix/API-authored Endpoints whose schema permits it.", "integration" : "ZoneContext → `ConnectedClient` → resource API for `Endpoint`; consumers reference endpoints only through `Endpoint/` ResourceRefs", "removalProof" : "Not applicable (new surface)", "reuseAction" : "adapt", @@ -1072,14 +1072,14 @@ "dataMigration" : "Full d2b 3.0 reset; audit stream is v3 Zone-local and no v2 audit records are imported", "dependencyOwner" : "ADR046-clipboard-002; ADR-046-telemetry-audit-and-support; clipboard observability owner", "destination" : "packages/d2b-provider-clipboard-wayland/src/service/audit.rs and packages/d2b-provider-clipboard-wayland/src/service/metrics.rs", - "detailedDesign" : "Implement ClipboardAuditEvent and fail-closed Zone audit queue by porting baseline audit code, renaming realm fields to source_zone_id and dest_zone_id, making ReasonCode a closed enum with unknown protobuf fields rejected, replacing exact byte counts with SizeBucket, emitting to d2b.audit.v3, and adding closed-label OTEL metrics and spans from the dossier tables. Primary reuse disposition: `adapt`. Preserved source-plan detail: port and adapt audit plus metrics with zone names and redaction changes.", + "detailedDesign" : "Implement ClipboardAuditEvent and fail-closed Zone audit queue by porting baseline audit code, renaming realm fields to source_zone_id and dest_zone_id, making ReasonCode a closed enum with unknown protobuf fields rejected, replacing exact byte counts with SizeBucket, emitting to d2b.audit.v3, and adding closed-semantic-label OTEL metrics and spans from the dossier tables. Metric descriptors carry no Zone/resource-name-derived identity; `d2b.zone` remains a resource attribute. Primary reuse disposition: `adapt`. Preserved source-plan detail: port and adapt audit plus resource-name-free metrics and redaction changes.", "integration" : "clipd-host emits audit events to the Zone audit sink and OTEL metrics/spans to the observability Provider pipeline during clipboard operations.", "removalProof" : "Old audit shape with realm field names and exact byte counts is absent after ported tests assert the v3 ClipboardAuditEvent schema.", "reuseAction" : "adapt", "reuseSource" : null, "specId" : "ADR-046-provider-clipboard-wayland", "specPath" : "docs/specs/providers/ADR-046-provider-clipboard-wayland.md", - "validation" : "Audit tests for no bytes in events, closed ReasonCode deserialization, fail-closed queue rejection, SizeBucket discretization, allowed metric labels, and excluded span attributes.", + "validation" : "Audit tests for no bytes in events, closed ReasonCode deserialization, fail-closed queue rejection, SizeBucket discretization, excluded span attributes, and structural metric descriptor assertions for exact absence of `vm`, `zone`, `zone_id`, `zone_uid`, and resource-name-derived keys plus clipboard/Zone-name canary absence while preserving `d2b.zone` resource attributes.", "workItemId" : "ADR046-clipboard-008" }, { @@ -1237,14 +1237,14 @@ "dataMigration" : "None — audit/telemetry only; no runtime state import", "dependencyOwner" : "Depends on ADR046-cred-mi-001 and ADR046-mi-topology-001; owner: credential-managed-identity audit/telemetry implementation", "destination" : "packages/d2b-provider-credential-managed-identity/src/{audit.rs,telemetry.rs}; packages/d2b-contract-tests/tests/credential_audit.rs", - "detailedDesign" : "Shared audit/OTEL: emit audit records for all methods and controller events per §Audit; emit OTEL spans and metrics per §OTEL and metrics; add `d2b_credential_imds_calls_total` counter with bounded `alias` label; enforce `contains_sensitive_shape` on all string fields in audit records and metric labels; add canary tests for `managed-identity-canary`, `credential_canary`, and `imds-endpoint-canary` in `canary.rs`. Primary reuse disposition: `adapt`. Preserved source-plan detail: adapt existing sensitive-shape guard and canary pattern to v3 audit, OTEL, and metric surfaces.", + "detailedDesign" : "Shared audit/OTEL: emit audit records for all methods and controller events per §Audit; emit OTEL spans and metrics per §OTEL and metrics with no Zone/Credential/resource-name-derived label; report expiry as the minimum for each provider/placement aggregate; add `d2b_credential_imds_calls_total` counter with bounded `alias` label; enforce `contains_sensitive_shape` on all string fields in audit records and metric labels; add canary tests for `managed-identity-canary`, `credential_canary`, `imds-endpoint-canary`, Credential `metadata.name`, and Zone name in `canary.rs`. Primary reuse disposition: `adapt`. Preserved source-plan detail: adapt existing sensitive-shape guard and canary pattern to v3 audit, OTEL, and metric surfaces.", "integration" : "Controller and agent service methods call audit/telemetry helpers; audit subsystem and OTEL exporters consume bounded redacted records; contract tests validate credential audit shape across providers.", "removalProof" : "None — audit/telemetry helpers are additive; no prior owner to remove", "reuseAction" : "adapt", "reuseSource" : null, "specId" : "ADR-046-provider-credential-managed-identity", "specPath" : "docs/specs/providers/ADR-046-provider-credential-managed-identity.md", - "validation" : "`packages/d2b-contract-tests/tests/credential_audit.rs`; managed-identity `canary.rs`; audit/OTEL unit tests for labels and sensitive-shape rejection", + "validation" : "`packages/d2b-contract-tests/tests/credential_audit.rs`; managed-identity `canary.rs`; audit/OTEL unit tests structurally assert exact absence of `vm`, `zone`, `zone_id`, `zone_uid`, `credential_name`, and every resource-name-derived label key, reject Credential/Zone-name label canaries, preserve allowed OTEL resource identity attributes, reject identity span attributes, and reject sensitive shapes", "workItemId" : "ADR046-cred-mi-004" }, { @@ -1327,14 +1327,14 @@ "dataMigration" : "None — audit/telemetry only; no runtime state migration", "dependencyOwner" : "Dependency for ADR046-cred-ss-003; owner: credential-secret-service audit and telemetry implementation", "destination" : "packages/d2b-provider-credential-secret-service/src/{audit.rs,telemetry.rs}", - "detailedDesign" : "Audit/OTEL: emit audit records and OTEL spans/metrics for all credential service methods and controller events, with canary enforcement and no token/object-path/lease bytes in status, delivery outer headers, audit, metrics, spans, or logs. Full detail remains in `ADR-046-resources-credential` §Implementation work items. Primary reuse disposition: `adapt`. Preserved source-plan detail: adapt zero-secret invariant and canary test pattern to credential-secret-service audit/OTEL surfaces.", + "detailedDesign" : "Audit/OTEL: emit audit records and OTEL spans/metrics for all credential service methods and controller events, with canary enforcement, expiry aggregated across user-agent leases, no Zone/Credential/resource-name-derived metric label, and no token/object-path/lease bytes in status, delivery outer headers, audit, metrics, spans, or logs. Full detail remains in `ADR-046-resources-credential` §Implementation work items. Primary reuse disposition: `adapt`. Preserved source-plan detail: adapt zero-secret invariant and canary test pattern to credential-secret-service audit/OTEL surfaces.", "integration" : "Controller and service methods call audit/telemetry helpers; audit subsystem and OTEL exporters consume bounded event/span/metric records; canary tests verify every public observable surface stays secret-free.", "removalProof" : "None — audit/telemetry helpers are new; no prior owner to remove", "reuseAction" : "adapt", "reuseSource" : null, "specId" : "ADR-046-provider-credential-secret-service", "specPath" : "docs/specs/providers/ADR-046-provider-credential-secret-service.md", - "validation" : "Credential audit/OTEL tests from `ADR-046-resources-credential`; `tests/canary.rs` and `tests/delivery.rs` for credential-secret-service", + "validation" : "Credential audit/OTEL tests from `ADR-046-resources-credential`; `tests/canary.rs` structurally asserts exact absence of `vm`, `zone`, `zone_id`, `zone_uid`, `credential_name`, and every resource-name-derived metric key plus Credential/Zone-name label canary absence while preserving allowed OTEL resource identity attributes and rejecting identity span attributes; `tests/delivery.rs` for credential-secret-service", "workItemId" : "ADR046-cred-ss-006" }, { @@ -1447,14 +1447,14 @@ "dataMigration" : "None — full d2b 3.0 reset; no prior state to migrate", "dependencyOwner" : "`ADR046-credential-001`, `ADR046-credential-006`; audit/OTEL integrator", "destination" : "`packages/d2b-provider-credential-/src/audit.rs`, `telemetry.rs`; `packages/d2b-contract-tests/tests/credential_audit.rs`", - "detailedDesign" : "Implement audit record emission for all credential service methods and controller events using the field set defined in §Audit; implement OTEL span/metric emission using the closed label set in §OTEL and metrics; implement `contains_sensitive_shape` check in all string fields of audit records and metric label values (adapted from `d2b-realm-provider/src/error.rs:contains_sensitive_shape`); add canary-enforcement tests that verify `\"secret-canary\"`, `\"entra-token-canary\"`, and `\"managed-identity-canary\"` values never appear in any audit record, metric label, span attribute, log line, or status field across all Provider test suites", + "detailedDesign" : "Implement audit record emission for all credential service methods and controller events using the field set defined in §Audit; implement OTEL span/metric emission using the closed semantic label set in §OTEL and metrics, with expiry reported as a provider/placement aggregate and no Credential/Zone/resource-name-derived label; implement `contains_sensitive_shape` check in all string fields of audit records and metric label values (adapted from `d2b-realm-provider/src/error.rs:contains_sensitive_shape`); add canary-enforcement tests that verify `\"secret-canary\"`, `\"entra-token-canary\"`, `\"managed-identity-canary\"`, Credential `metadata.name`, and Zone name values never appear in any metric label, span attribute, log line, or status field; identity remains only in allowed OTEL resource attributes and permitted audit fields", "integration" : "Credential controller and service handlers emit audit records and telemetry through Zone audit/OTEL paths", "removalProof" : "Not applicable", "reuseAction" : "adapt", "reuseSource" : null, "specId" : "ADR-046-resources-credential", "specPath" : "docs/specs/ADR-046-resources-credential.md", - "validation" : "Canary tests across all three Provider crates; audit record field-presence tests; metric label cardinality tests; span attribute absence tests for forbidden fields", + "validation" : "Canary tests across all three Provider crates; audit record field-presence tests; structural metric descriptor tests assert exact absence of `vm`, `zone`, `zone_id`, `zone_uid`, `credential_name`, and every resource-name-derived key plus Credential/Zone-name canary absence; resource attribute tests preserve allowed `d2b.zone`/`d2b.credential.name` identity while span attribute tests reject identity and forbidden fields", "workItemId" : "ADR046-credential-008" }, { @@ -1526,7 +1526,7 @@ "integration" : "`make test-drift` gains a row running this generator and `git diff --exit-code`; every wave's exit criteria (§4) require it committed as the wave's last commit", "removalProof" : "Not applicable", "reuseAction" : "adapt", - "reuseSource" : null, + "reuseSource" : "none required — this generator is specific to the `docs/specs/ADR-046-*` manifest shape", "specId" : "ADR-046-validation-and-delivery", "specPath" : "docs/specs/ADR-046-validation-and-delivery.md", "validation" : "Golden-fixture test against a small synthetic spec directory; drift test against the real `docs/specs/` tree", @@ -1582,7 +1582,7 @@ "dataMigration" : "None — docs/tooling only; no runtime state", "dependencyOwner" : "`ADR046-streamline-001`, `ADR046-W0`; delivery-tooling integrator", "destination" : "`docs/specs/ADR-046-implementation-graph.json`, `docs/specs/ADR-046-implementation-graph.md` (generated by `ADR046-streamline-001`'s `xtask implementation-graph`); the artifact contract, generation, validation, and ready-wave query are owned by §3.5 of this spec", - "detailedDesign" : "Owns the D095 implementation-graph contract: `artifactKind`/`schemaVersion`/`adr`/`status`; one node per member spec and per work item mapped exactly once to a `W0`–`W7` wave and a file-disjoint `parallelGroup`, with `owner`/`destinations`/`entryContracts`/`prerequisites`/`blockers`/`exitGate`/`topologicalRank`; typed `spec-depends-on`/`shared-contract`/`work-item-depends-on`/`implements-spec`/`file-overlap-order` edges; the §3.5.1 ready-wave query; and the anti-serialization invariant that every ready file-disjoint group launches concurrently while a same-wave dependency is a prep barrier, not whole-wave serialization. The graph is a generated non-member artifact and does not change the 55-member `ADR-046-spec-set.json` count. Primary reuse disposition: `adapt`. Preserved source-plan detail: net-new (D095 artifact contract).", + "detailedDesign" : "Owns the D095 implementation-graph contract: `artifactKind`/`schemaVersion`/`adr`/`status`; one node per member spec and per work item mapped exactly once to a `W0`–`W7` wave and a file-disjoint `parallelGroup`, with `owner`/`destinations`/`entryContracts`/`prerequisites`/`blockers`/`exitGate`/`topologicalRank`; work-item nodes additionally embed the manifest's exact `detailedDesign` and `validation` text byte-for-byte; typed `spec-depends-on`/`shared-contract`/`work-item-depends-on`/`implements-spec`/`file-overlap-order` edges; the §3.5.1 ready-wave query; and the anti-serialization invariant that every ready file-disjoint group launches concurrently while a same-wave dependency is a prep barrier, not whole-wave serialization. The graph is a generated non-member artifact and does not change the 55-member `ADR-046-spec-set.json` count. Primary reuse disposition: `adapt`. Preserved source-plan detail: net-new (D095 artifact contract).", "integration" : "Consumed by §4 wave entry/exit and §6 anti-serialization checks and by `ADR046-streamline-013`; a `tests/unit/gates/` drift gate regenerates and `git diff --exit-code`s the graph after any spec/work-item edit", "removalProof" : "Not applicable", "reuseAction" : "adapt", @@ -1612,14 +1612,14 @@ "dataMigration" : "Full reset; no v2 device object import", "dependencyOwner" : "W0 shared contract root; `d2b-contracts`", "destination" : "`packages/d2b-contracts/src/v3/device.rs`", - "detailedDesign" : "Device ResourceType schema (spec/status/conditions/claims/inventory); closed-set error codes; Device RBAC verbs; broker operation effect-limit constants Primary reuse disposition: `adapt`. Preserved source-plan detail: extract and adapt.", + "detailedDesign" : "Device ResourceType schema (spec/status/conditions/claims/inventory); closed-set error codes; Device RBAC verbs; broker operation effect-limit constants; shared Device telemetry contract requires fixed semantic metric labels with no Zone/resource-name-derived identity and retains `d2b.zone`/`d2b.provider` only as OTEL resource attributes. Primary reuse disposition: `adapt`. Preserved source-plan detail: extract and adapt.", "integration" : "Provider dossiers, resource API/store, CLI status surfaces", "removalProof" : "Old ProcessRole/DTO branches retained until Provider integrations are live", "reuseAction" : "adapt", "reuseSource" : null, "specId" : "ADR-046-resources-device", "specPath" : "docs/specs/ADR-046-resources-device.md", - "validation" : "Schema golden vectors; unknown-field denial; exclusive/shared conflict rejection; arbitration/maxClaims invariant", + "validation" : "Schema golden vectors; unknown-field denial; exclusive/shared conflict rejection; arbitration/maxClaims invariant; cross-Provider structural descriptor test asserts exact absence of `vm`, `zone`, `zone_id`, `zone_uid`, and resource-name-derived keys plus Device/Zone-name canary absence while preserving `d2b.zone` resource attributes", "workItemId" : "ADR046-device-001" }, { @@ -1762,14 +1762,14 @@ "dataMigration" : "Existing TPM state migration follows §17.3: the old `/var/lib/d2b/vms//swtpm/` directory moves to the controller-created Volume path with the provisioning marker preserved and re-keyed; this item must not silently recreate missing state.", "dependencyOwner" : "P0; blocked by ADR046-device-tpm-002; owner: device-tpm controller FSM", "destination" : "packages/d2b-provider-device-tpm/src/controller.rs; packages/d2b-provider-device-tpm/tests/controller_fsm.rs", - "detailedDesign" : "Controller reconcile state machine: implement the Device reconcile algorithm from §11.1 against `FakeTpmEffectPort`, covering happy path, Volume not-ready, marker fail-closed, flush failure, swtpm maxRestarts, and finalizer behavior where Process is deleted and Volume retained. Primary reuse disposition: `replace`. Preserved source-plan detail: replace direct daemon lifecycle with Provider reconcile against `FakeTpmEffectPort` and resource status.", + "detailedDesign" : "Controller reconcile state machine: implement the Device reconcile algorithm from §11.1 against `FakeTpmEffectPort`, covering happy path, Volume not-ready, marker fail-closed, flush failure, swtpm maxRestarts, and finalizer behavior where Process is deleted and Volume retained; emit §14 metrics with only closed `phase`/`outcome`/`status` semantics and no Zone/resource-name-derived label. Primary reuse disposition: `replace`. Preserved source-plan detail: replace direct daemon lifecycle with Provider reconcile against `FakeTpmEffectPort` and resource status.", "integration" : "Resource watches drive the controller; controller creates/observes Volume, Process, EphemeralProcess, and Endpoint resources through `TpmEffectPort`; Device status/finalizers expose outcomes to the ResourceAPI.", "removalProof" : "Direct daemon swtpm lifecycle logic is removable after this Provider reconcile FSM reaches parity and ADR046-device-tpm-013 removes the old call sites", "reuseAction" : "replace", "reuseSource" : null, "specId" : "ADR-046-provider-device-tpm", "specPath" : "docs/specs/providers/ADR-046-provider-device-tpm.md", - "validation" : "`tests/controller_fsm.rs` covering happy path, Volume not-ready, marker fail-closed, flush failed, swtpm maxRestarts, and finalizer behavior", + "validation" : "`tests/controller_fsm.rs` covering happy path, Volume not-ready, marker fail-closed, flush failed, swtpm maxRestarts, and finalizer behavior; `tests/metrics_labels.rs` structurally asserts exact absence of `vm`, `zone`, `zone_id`, `zone_uid`, and resource-name-derived keys and Device/Zone-name canary absence", "workItemId" : "ADR046-device-tpm-003" }, { @@ -1957,14 +1957,14 @@ "dataMigration" : "None — full d2b 3.0 reset; no prior state to migrate", "dependencyOwner" : "`ADR046-display-001`; telemetry/audit owner", "destination" : "`packages/d2b-provider-display-wayland/src/audit.rs`, `packages/d2b-provider-display-wayland/src/metrics.rs`", - "detailedDesign" : "Implement audit record types for all events in §14.1; implement OTEL metric counters/gauges in §14.2; adapt `DiagRateLimiter` to use closed label sets; validate that no socket path, user identity, window title, or app-id appears in any log/audit/metric surface Primary reuse disposition: `adapt`. Preserved source-plan detail: extract and adapt.", + "detailedDesign" : "Implement audit record types for all events in §14.1; implement OTEL metric counters/gauges in §14.2; adapt `DiagRateLimiter` to use closed semantic label sets with no Zone UID/name or resource-name-derived key; retain Zone identity in `d2b.zone` resource attributes; validate that no socket path, user identity, window title, or app-id appears in any log/audit/metric surface. Primary reuse disposition: `adapt`. Preserved source-plan detail: extract and adapt.", "integration" : "Providers emit via Zone telemetry emitter; audit records committed before operation completion", "removalProof" : "N/A (new code)", "reuseAction" : "adapt", "reuseSource" : null, "specId" : "ADR-046-provider-display-wayland", "specPath" : "docs/specs/providers/ADR-046-provider-display-wayland.md", - "validation" : "Redaction contract tests (`policy_observability.rs` pattern), audit record schema tests, label-cardinality tests", + "validation" : "Redaction contract tests (`policy_observability.rs` pattern), audit record schema tests, structural label-policy tests asserting exact absence of `vm`, `zone`, `zone_id`, `zone_uid`, and resource-name-derived keys plus WaylandSession/Zone-name canary absence", "workItemId" : "ADR046-display-003" }, { @@ -2366,7 +2366,7 @@ "integration" : "None (standalone; no d2b-bus/ComponentSession/broker dependency)", "removalProof" : "Per SPIKE-01/SPIKE-02 Cleanup rows: deleted once `packages/d2b-resource-store-redb` and `packages/d2b-controller-toolkit/benches/reaction.rs` reproduce equal-or-stricter coverage", "reuseAction" : "adapt", - "reuseSource" : null, + "reuseSource" : "None (redb is a new external dependency; no main or v3 code implements it)", "specId" : "ADR-046-feasibility-and-spikes", "specPath" : "docs/specs/ADR-046-feasibility-and-spikes.md", "validation" : "SPIKE-01 metrics (1)-(5) and SPIKE-02 metrics (1) across all 3 concurrency profiles, per those entries' exact pass/fail thresholds", @@ -2381,7 +2381,7 @@ "integration" : "Consumes `ADR046-feasibility-001`'s hint-bus shape as its watch-receiver input", "removalProof" : "Deleted once `packages/d2b-controller-toolkit/benches/reaction.rs` and the Process Provider integration tests named by `ADR046-reconcile-003` reproduce equal-or-stricter coverage", "reuseAction" : "adapt", - "reuseSource" : null, + "reuseSource" : "None (the generic async controller loop is ADR-only per `ADR-046-resource-reconciliation`'s own current-code-fit row)", "specId" : "ADR-046-feasibility-and-spikes", "specPath" : "docs/specs/ADR-046-feasibility-and-spikes.md", "validation" : "SPIKE-03 metrics (1)-(3) and thresholds", @@ -2396,7 +2396,7 @@ "integration" : "None (standalone)", "removalProof" : "Deleted once `packages/d2b-provider-supervisor` and the volume-domain effect adapter each carry an equal-or-stricter in-tree blocking-adapter regression test", "reuseAction" : "adapt", - "reuseSource" : null, + "reuseSource" : "None", "specId" : "ADR-046-feasibility-and-spikes", "specPath" : "docs/specs/ADR-046-feasibility-and-spikes.md", "validation" : "SPIKE-04 heartbeat-jitter metric and threshold", @@ -2411,7 +2411,7 @@ "integration" : "None (standalone)", "removalProof" : "Deleted once the real Provider-toolkit crate (`ADR046-provider-001` destination) ships equal-or-stricter manifest-parsing/enumeration/workspace-policy coverage", "reuseAction" : "adapt", - "reuseSource" : null, + "reuseSource" : "None", "specId" : "ADR-046-feasibility-and-spikes", "specPath" : "docs/specs/ADR-046-feasibility-and-spikes.md", "validation" : "SPIKE-05 metrics (1)-(4) across 20 repeated randomized-order manifest loads", @@ -2441,7 +2441,7 @@ "integration" : "None between the two spikes beyond sharing the same fake resource-store oracle shape", "removalProof" : "Deleted per each spike's Cleanup row: the real `ADR-046-provider-state` work-item destination for SPIKE-09; `d2b-provider-volume-local`'s own `tests/`/`integration/` suite for SPIKE-10", "reuseAction" : "adapt", - "reuseSource" : null, + "reuseSource" : "None", "specId" : "ADR-046-feasibility-and-spikes", "specPath" : "docs/specs/ADR-046-feasibility-and-spikes.md", "validation" : "SPIKE-09 metrics (1)-(4); SPIKE-10 metrics (1)-(5), zero-tolerance on path leakage", @@ -2456,7 +2456,7 @@ "integration" : "None (standalone; requires a Linux host with `clone3`/`pidfd_open`, and optionally a running `systemd --user` instance behind the `systemd-user` feature)", "removalProof" : "Deleted once `packages/d2b-provider-system-systemd` and `packages/d2b-provider-system-minijail` each carry this exact shared conformance suite in their own `tests/`", "reuseAction" : "adapt", - "reuseSource" : null, + "reuseSource" : "None", "specId" : "ADR-046-feasibility-and-spikes", "specPath" : "docs/specs/ADR-046-feasibility-and-spikes.md", "validation" : "SPIKE-11 metrics (1)-(4), zero-tolerance on false adoption", @@ -2471,7 +2471,7 @@ "integration" : "None (standalone flake; no dependency on the main `flake.nix`)", "removalProof" : "Deleted once the real `nixos-modules/resources.nix` and `packages/xtask` `gen-schemas` implementation reproduce these metrics as part of `make test-drift`/`make test-flake`", "reuseAction" : "adapt", - "reuseSource" : null, + "reuseSource" : "None", "specId" : "ADR-046-feasibility-and-spikes", "specPath" : "docs/specs/ADR-046-feasibility-and-spikes.md", "validation" : "SPIKE-12 metrics (1)-(5), byte-for-byte reproducibility across 3 hermetic builds", @@ -2486,7 +2486,7 @@ "integration" : "None between the two spikes beyond sharing the same fixture command-table shape", "removalProof" : "Deleted per each spike's Cleanup row: the real `d2b` CLI crate's own discovery conformance test for SPIKE-13; the real CLI crate's workspace-policy/lint gate plus the real bootstrap sequence for SPIKE-14", "reuseAction" : "adapt", - "reuseSource" : null, + "reuseSource" : "None", "specId" : "ADR-046-feasibility-and-spikes", "specPath" : "docs/specs/ADR-046-feasibility-and-spikes.md", "validation" : "SPIKE-13 metrics (1)-(7); SPIKE-14 metrics (1)-(3), zero-tolerance on legacy-file access", @@ -2501,7 +2501,7 @@ "integration" : "Depends on and imports the fake shapes from `proofs/redb-resource-store-spike/`, `proofs/process-fastlaunch-spike/`, `proofs/effectport-async-spike/`, `proofs/provider-packaging-spike/`, `proofs/bus-routing-noise-spike/`, `proofs/transport-opaque-streams-spike/`, `proofs/credential-kk-e2e-spike/`, `proofs/provider-state-export-spike/`, `proofs/volume-policy-spike/`, and `proofs/process-provider-conformance-spike/`", "removalProof" : "Deleted once the real integration test suites named by the individual Provider dossiers (`integration/` per D059) collectively reproduce all three compositions against real, non-fake Zone/store/bus/broker code", "reuseAction" : "adapt", - "reuseSource" : null, + "reuseSource" : "None beyond what `-001` through `-009` already reuse", "specId" : "ADR-046-feasibility-and-spikes", "specPath" : "docs/specs/ADR-046-feasibility-and-spikes.md", "validation" : "SPIKE-15 metrics (1)-(4) across all three compositions", @@ -2541,15 +2541,15 @@ "currentSource" : "`packages/d2bd/src/usbip_state_machine.rs` (implemented-and-reachable) as reconcile loop pattern reference. GPU/video reconcile state is `ADR-only`.", "dataMigration" : "None — full d2b 3.0 reset; no prior state to migrate", "dependencyOwner" : "ADR046-gpu-001; `ADR-046-resource-reconciliation` implementation present; Provider toolkit `ResourceClient` available", - "destination" : "`packages/d2b-provider-device-gpu/src/controller.rs`", - "detailedDesign" : "Five triggers: `spec-generation-changed`, `deletion-requested`, `dependency-changed`, `scheduled-observe`, `owned-resource-changed`. Each trigger handler writes optimistic `ResourceMutationBatch`. Status writer in `status.rs`. Async watch task + per-resource reconcile tasks. Independent resources in parallel. Primary reuse disposition: `adapt`. Preserved source-plan detail: `adapt` — implement the five-trigger reconcile loop using Provider toolkit async reconciler.", + "destination" : "`packages/d2b-provider-device-gpu/src/{controller.rs,telemetry.rs}`", + "detailedDesign" : "Five triggers: `spec-generation-changed`, `deletion-requested`, `dependency-changed`, `scheduled-observe`, `owned-resource-changed`. Each trigger handler writes optimistic `ResourceMutationBatch`. Status writer in `status.rs`. Async watch task + per-resource reconcile tasks. Independent resources in parallel. Telemetry uses only closed `mode`/`video_sidecar`/`arbitration` semantics; `d2b.zone` and `d2b.provider` remain resource attributes and no Zone/resource-name-derived key is a metric label. Primary reuse disposition: `adapt`. Preserved source-plan detail: `adapt` — implement the five-trigger reconcile loop using Provider toolkit async reconciler.", "integration" : "Resource API (ADR046 store) must be present; fake ResourceClient available from Provider toolkit; `tests/combined_reconcile.rs` validates trigger dispatch", "removalProof" : "Current ProcessRole::Gpu/Video/GpuRenderNode retained until this test passes; see ProcessRole disposition table", "reuseAction" : "adapt", "reuseSource" : "Pattern only: `packages/d2bd/src/usbip_state_machine.rs` (baseline). No code copy.", "specId" : "ADR-046-provider-device-gpu", "specPath" : "docs/specs/providers/ADR-046-provider-device-gpu.md", - "validation" : "`cargo test -p d2b-provider-device-gpu --test combined_reconcile`; all five trigger handlers must reach their expected output state", + "validation" : "`cargo test -p d2b-provider-device-gpu --test combined_reconcile`; all five trigger handlers must reach their expected output state; structural metric descriptor test asserts exact absence of `vm`, `zone`, `zone_id`, `zone_uid`, and resource-name-derived keys plus GPU Device/Zone-name canary absence while preserving `d2b.zone` resource attributes", "workItemId" : "ADR046-gpu-002" }, { @@ -2561,7 +2561,7 @@ "integration" : "`scheduled-observe` trigger from reconcile loop calls `probe::check_drm_device`", "removalProof" : "N/A (new module)", "reuseAction" : "create", - "reuseSource" : null, + "reuseSource" : "None; probe is `ADR-only`", "specId" : "ADR-046-provider-device-gpu", "specPath" : "docs/specs/providers/ADR-046-provider-device-gpu.md", "validation" : "`tests/conformance.rs` contains probe-mock path; `cargo test` passes", @@ -2576,7 +2576,7 @@ "integration" : "Tested by `tests/arbitration_conflict.rs`; integration fixture `render_node_shared/`", "removalProof" : "N/A (new module)", "reuseAction" : "create", - "reuseSource" : null, + "reuseSource" : "None", "specId" : "ADR-046-provider-device-gpu", "specPath" : "docs/specs/providers/ADR-046-provider-device-gpu.md", "validation" : "`cargo test -p d2b-provider-device-gpu --test arbitration_conflict`; `cargo test -p d2b-provider-device-gpu --test render_node_enforcement`", @@ -2636,7 +2636,7 @@ "integration" : "`tests/status_state.rs`; `integration/gpu_worker_start/` verifies controller startup is gated by resource dependencies and status writer authority, not by a Provider state Volume", "removalProof" : "`StorageRoot`/`StoragePathSpec` lifecycle tracking entries for GPU/video roles in `d2b-core/src/storage.rs` removed after Device/Process status-first lifecycle and restart-adoption integration tests pass in a live Zone", "reuseAction" : "create", - "reuseSource" : null, + "reuseSource" : "None", "specId" : "ADR-046-provider-device-gpu", "specPath" : "docs/specs/providers/ADR-046-provider-device-gpu.md", "validation" : "`cargo test -p d2b-provider-device-gpu --test status_state`; component descriptor golden has no Provider state Volume declaration; controller Process template has no `/state` mount; ProviderStateSet query is empty; status/core-ledger fields carry bounded operational observations", @@ -2651,7 +2651,7 @@ "integration" : "Workspace policy checks for `README.md` presence", "removalProof" : "N/A (new file)", "reuseAction" : "create", - "reuseSource" : null, + "reuseSource" : "None", "specId" : "ADR-046-provider-device-gpu", "specPath" : "docs/specs/providers/ADR-046-provider-device-gpu.md", "validation" : "`make test-policy` (workspace crate layout policy check)", @@ -2816,7 +2816,7 @@ "integration" : "Provider dossiers, Nix resource compiler, resource store/API bind these canonical types", "removalProof" : "Old `d2b_core::host::NetEnv` and related types removed only after v3 resource API consumers use `d2b_contracts::v3::network` types", "reuseAction" : "adapt", - "reuseSource" : null, + "reuseSource" : "None from main; all from v3 baseline", "specId" : "ADR-046-resources-network", "specPath" : "docs/specs/ADR-046-resources-network.md", "validation" : "Golden JSON/CBOR vectors; CIDR overlap property tests; IfName collision and derivation determinism tests; default hostBlocklist enforcement; attachment index uniqueness; `User/net-local-controller` User resource lifecycle/readiness test: controller creates User Resource with `spec.osUsername = \"net-local-controller\"` (`ownerRef: Provider/network-local`); controller waits for User resource to reach `Ready` before proceeding; controller aborts with `ConfigVolumeReady=False/user-not-ready` if User resource is not Ready; verifies no numeric UID/GID appears in the Resource spec, authz check, or audit record; verifies that any diagnostic `uid`/`gid` in `User.status` is never used as an authorization input", @@ -2872,14 +2872,14 @@ "dataMigration" : "None after full reset", "dependencyOwner" : "ADR046-network-001–004; network-local controller owner; D-NETWORK-001, D-NETWORK-002, and D-NETWORK-003 resolved", "destination" : "`packages/d2b-provider-network-local/src/controller.rs`: async NetworkReconciler; `packages/d2b-provider-network-local/src/plan.rs`: ReconcilePlan computation; `packages/d2b-provider-network-local/src/observe.rs`: drift-detection observe loop. Full crate layout required (see [Package and crate boundary](#package-and-crate-boundary)): `src/` (controller/plan/observe + colocated unit tests), `tests/` (hermetic conformance and state-machine tests), `integration/` (provider-system reconcile fixtures), `README.md` (Network ResourceType, controller binary, placement, RBAC, security invariants, build/test/integration commands).", - "detailedDesign" : "Implements full async reconcile interface from `ADR-046-resource-reconciliation`. `plan()` computes desired vs. actual bridge-presence, sysctl, host-side nftables, hosts-file, NM-unmanaged, config Volume content, Guest, guest-agent Process, and mDNS-Process states. `reconcile()` dispatches in order: `CreateBridge` for each bridge not present (broker applies IPv6 sysctls atomically; `CreateBridge` failure sets `FabricReady=False/bridge-create-error` and aborts) → `ApplySysctl` (defense-in-depth IPv6) → `ApplyNftables` (host-side `inet d2b` table) → `ApplyNmUnmanaged` → `ApplyRoute` → `UpdateHostsFile` → `SeedDnsmasqLease` for new reservations → **Volume upsert** (two-phase): Phase 1 — create `Volume/net--config` with `kind: ephemeral`, `source.executionRef: Host/`, `source.settings.kind: tmpfs`, `quota: {maxBytes: 4194304, maxInodes: 128, enforcement: hard}` (tmpfs quota charged to Host memory budget), `layout` entries (root directory with `type: directory` and four config files each with `type: file`, `ownerRef: User/net-local-controller`, `groupRef: User/net-local-controller`, `mode: \"0640\"`, `accessAcl: []`, `defaultAcl: []`, `noFollow: true`, conservative create/repair/cleanup policies), `views: {guest-readonly: {path: \"\", rights: [read, traverse]}}`, `attachments: []` (no Guest attachment); abort on terminal error with `ConfigVolumeReady=False/config-volume-error`. Wait for Volume backing to reach `Ready`; requeue on `Degraded`/`Failed` with `ConfigVolumeReady=False/volume-not-ready`. Write rendered config content through Volume write service (no raw host paths). Phase 2 — create Guest upsert with `systemArtifactId` from REQUIRED `Network.spec.netVmSystemArtifactId`. Wait for Guest `Ready`. Then update Volume with Guest attachment: `attachments: [{executionRef: Guest/, transport: virtiofs, view: guest-readonly, access: read-only, mountPath: \"/run/d2b/net-config\", settings: {posixAcl: false, xattr: false, cache: auto, inodeFileHandles: never, threadPoolSize: null, socketGroup: null}}]`; wait for attachment `Ready`; requeue on `Degraded` with `ConfigVolumeReady=False/attachment-not-ready`. Create or update guest-agent Process `Process/net--agent` with `processClass: worker`, `sandbox: {namespaceClasses: [], capabilityClasses: [network-admin, network-bind, network-raw]}` (inherits Guest network namespace; `network-admin`→`CAP_NET_ADMIN`, `network-bind`→`CAP_NET_BIND_SERVICE`, `network-raw`→`CAP_NET_RAW`, all effective in Guest network namespace only; INV-NET-009), `mounts: [{volumeRef: Volume/net--config, view: guest-readonly, mountPath: \"/run/d2b/net-config\", access: read-only, required: true}]`. mDNS Process upsert when `spec.mdns.enable = true` (D-NETWORK-001) → `SetBridgePortFlags` per tap. Each broker op returns typed audit evidence. `observe()` re-reads `firewallDigest` (host-side), bridge isolation flags, IPv6 sysctls, and guest-agent Process status (`dnsmasq-bound`, `firewall-applied` predicates); queues reconcile on drift. **Finalizer** (strictly child-first): `NetworkDraining` → delete guest-agent Process and mDNS Processes; wait for each Deleted watch event → update Volume to remove Guest attachment (`attachments: []`); wait for attachment removal confirmed → delete `Guest/`; wait for Deleted watch event → delete `Volume/net--config`; wait for Deleted watch event → `ApplyNftables` (empty) → `ApplyNmUnmanaged` (empty) → `UpdateHostsFile` (empty) → `DeleteBridge` for each bridge (idempotent) → clear finalizer. No USBIP rules installed by Network; `UsbipBindFirewallRule` issued by device-usbip directly (D-NETWORK-002).", + "detailedDesign" : "Implements full async reconcile interface from `ADR-046-resource-reconciliation`. `plan()` computes desired vs. actual bridge-presence, sysctl, host-side nftables, hosts-file, NM-unmanaged, config Volume content, Guest, guest-agent Process, and mDNS-Process states. `reconcile()` dispatches in order: `CreateBridge` for each bridge not present (broker applies IPv6 sysctls atomically; `CreateBridge` failure sets `FabricReady=False/bridge-create-error` and aborts) → `ApplySysctl` (defense-in-depth IPv6) → `ApplyNftables` (host-side `inet d2b` table) → `ApplyNmUnmanaged` → `ApplyRoute` → `UpdateHostsFile` → `SeedDnsmasqLease` for new reservations → **Volume upsert** (two-phase): Phase 1 — create `Volume/net--config` with `kind: ephemeral`, `source.executionRef: Host/`, `source.settings.kind: tmpfs`, `quota: {maxBytes: 4194304, maxInodes: 128, enforcement: hard}` (tmpfs quota charged to Host memory budget), `layout` entries (root directory with `type: directory` and four config files each with `type: file`, `ownerRef: User/net-local-controller`, `groupRef: User/net-local-controller`, `mode: \"0640\"`, `accessAcl: []`, `defaultAcl: []`, `noFollow: true`, conservative create/repair/cleanup policies), `views: {guest-readonly: {path: \"\", rights: [read, traverse]}}`, `attachments: []` (no Guest attachment); abort on terminal error with `ConfigVolumeReady=False/config-volume-error`. Wait for Volume backing to reach `Ready`; requeue on `Degraded`/`Failed` with `ConfigVolumeReady=False/volume-not-ready`. Write rendered config content through Volume write service (no raw host paths). Phase 2 — create Guest upsert with `systemArtifactId` from REQUIRED `Network.spec.netVmSystemArtifactId`. Wait for Guest `Ready`. Then update Volume with Guest attachment: `attachments: [{executionRef: Guest/, transport: virtiofs, view: guest-readonly, access: read-only, mountPath: \"/run/d2b/net-config\", settings: {posixAcl: false, xattr: false, cache: auto, inodeFileHandles: never, threadPoolSize: null, socketGroup: null}}]`; wait for attachment `Ready`; requeue on `Degraded` with `ConfigVolumeReady=False/attachment-not-ready`. Create or update guest-agent Process `Process/net--agent` with `processClass: worker`, `sandbox: {namespaceClasses: [], capabilityClasses: [network-admin, network-bind, network-raw]}` (inherits Guest network namespace; `network-admin`→`CAP_NET_ADMIN`, `network-bind`→`CAP_NET_BIND_SERVICE`, `network-raw`→`CAP_NET_RAW`, all effective in Guest network namespace only; INV-NET-009), `mounts: [{volumeRef: Volume/net--config, view: guest-readonly, mountPath: \"/run/d2b/net-config\", access: read-only, required: true}]`. mDNS Process upsert when `spec.mdns.enable = true` (D-NETWORK-001) → `SetBridgePortFlags` per tap. Each broker op returns typed audit evidence. `observe()` re-reads `firewallDigest` (host-side), bridge isolation flags, IPv6 sysctls, and guest-agent Process status (`dnsmasq-bound`, `firewall-applied` predicates); queues reconcile on drift. Metrics use only the fixed semantic labels in §OTEL spans and metrics; Zone/Network identity remains in OTEL resource attributes and permitted audit fields and never enters metric labels or span attributes. **Finalizer** (strictly child-first): `NetworkDraining` → delete guest-agent Process and mDNS Processes; wait for each Deleted watch event → update Volume to remove Guest attachment (`attachments: []`); wait for attachment removal confirmed → delete `Guest/`; wait for Deleted watch event → delete `Volume/net--config`; wait for Deleted watch event → `ApplyNftables` (empty) → `ApplyNmUnmanaged` (empty) → `UpdateHostsFile` (empty) → `DeleteBridge` for each bridge (idempotent) → clear finalizer. No USBIP rules installed by Network; `UsbipBindFirewallRule` issued by device-usbip directly (D-NETWORK-002).", "integration" : "Controller process registers descriptor, watches `Network` resources via d2b-bus/ComponentSession/ResourceClient. Owned Guest and Process mutations trigger owner reconciliation. Device-usbip watches only Network identity/readiness/generation; its Core adapter privately resolves relay/firewall effects (D-NETWORK-002).", "removalProof" : "Daemon-orchestrated network/bridge lifecycle removed only after controller passes conformance and parity tests", "reuseAction" : "adapt", "reuseSource" : null, "specId" : "ADR-046-resources-network", "specPath" : "docs/specs/ADR-046-resources-network.md", - "validation" : "`ADR046-reconcile-001` toolkit conformance; latency gates (p95 ≤5 ms hint-to-handler); Network-specific: CIDR conflict blocks reconcile, `CreateBridge` failure sets `FabricReady=False`, Volume creation failure sets `ConfigVolumeReady=False/config-volume-error`, `User/net-local-controller` not Ready aborts with `ConfigVolumeReady=False/user-not-ready`, Volume schema round-trip (kind=ephemeral, source.settings.kind=tmpfs, quota.enforcement=hard, layout type=file entries, views.guest-readonly.rights=[read,traverse]), tmpfs quota charged to Host memory budget (test Volume creation fails when Host memory budget exceeded), Guest not created before Volume backing `Ready`, Guest attachment not added before Guest `Ready`, guest-agent Process created after attachment `Ready` (`processClass: worker`, `namespaceClasses: []`, `capabilityClasses: [network-admin, network-bind, network-raw]`, `access: read-only`, `required: true`), host-capability leakage test: no host-netns process gains `CAP_NET_ADMIN`/`CAP_NET_BIND_SERVICE`/`CAP_NET_RAW` as result of guest-agent launch (INV-NET-009; `tests/host-integration/guest-agent-cap-confinement.nix`), `DeleteBridge` called on finalizer, Volume attachment removed before Guest deletion in finalizer (test order: agent Deleted → attachment removed → Guest Deleted → Volume Deleted → bridges), east-west invariant (INV-NET-003), hostBlocklist enforcement (INV-NET-004), macvtap attachment status (delegated to runtime-ch), mDNS Process created/deleted with `spec.mdns.enable` toggle, broker INV-NET-002 tests, config-only spec change updates Volume content and triggers agent reload without Guest restart (INV-NET-008); golden tests updated for v3 IfNames", + "validation" : "`ADR046-reconcile-001` toolkit conformance; latency gates (p95 ≤5 ms hint-to-handler); Network-specific: CIDR conflict blocks reconcile, `CreateBridge` failure sets `FabricReady=False`, Volume creation failure sets `ConfigVolumeReady=False/config-volume-error`, `User/net-local-controller` not Ready aborts with `ConfigVolumeReady=False/user-not-ready`, Volume schema round-trip (kind=ephemeral, source.settings.kind=tmpfs, quota.enforcement=hard, layout type=file entries, views.guest-readonly.rights=[read,traverse]), tmpfs quota charged to Host memory budget (test Volume creation fails when Host memory budget exceeded), Guest not created before Volume backing `Ready`, Guest attachment not added before Guest `Ready`, guest-agent Process created after attachment `Ready` (`processClass: worker`, `namespaceClasses: []`, `capabilityClasses: [network-admin, network-bind, network-raw]`, `access: read-only`, `required: true`), host-capability leakage test: no host-netns process gains `CAP_NET_ADMIN`/`CAP_NET_BIND_SERVICE`/`CAP_NET_RAW` as result of guest-agent launch (INV-NET-009; `tests/host-integration/guest-agent-cap-confinement.nix`), `DeleteBridge` called on finalizer, Volume attachment removed before Guest deletion in finalizer (test order: agent Deleted → attachment removed → Guest Deleted → Volume Deleted → bridges), east-west invariant (INV-NET-003), hostBlocklist enforcement (INV-NET-004), macvtap attachment status (delegated to runtime-ch), mDNS Process created/deleted with `spec.mdns.enable` toggle, broker INV-NET-002 tests, config-only spec change updates Volume content and triggers agent reload without Guest restart (INV-NET-008); golden tests updated for v3 IfNames; structural metric descriptor test asserts exact absence of `vm`, `zone`, `zone_id`, `zone_uid`, `network`, and every resource-name-derived label and verifies a Network-name canary is absent from emitted label values", "workItemId" : "ADR046-network-005" }, { @@ -3546,15 +3546,15 @@ "currentSource" : "None — net-new v3 provider controller; v1 behavior lived in `nixos-modules/network.nix` and `nixos-modules/net.nix` static NixOS module logic.", "dataMigration" : "Full d2b 3.0 reset; no v2 state/config import.", "dependencyOwner" : "Provider; depends on ADR046-nl-001 through ADR046-nl-005 and owns the Network reconcile/observe/finalize handlers.", - "destination" : "`packages/d2b-provider-network-local/src/controller.rs`.", - "detailedDesign" : "Implement `controller.rs` reconcile/observe/finalize handlers with `NetworkEffectPort` injection. Primary reuse disposition: `adapt`. Preserved source-plan detail: port semantics into provider reconcile state machine; do not reuse static per-env systemd/Nix ownership.", + "destination" : "`packages/d2b-provider-network-local/src/{controller.rs,metrics.rs}`.", + "detailedDesign" : "Implement `controller.rs` reconcile/observe/finalize handlers with `NetworkEffectPort` injection and the §21 metric descriptors with closed semantic labels. No descriptor may carry `vm`, `zone`, `zone_id`, `zone_uid`, `network`, or another resource-name-derived key; Network/Zone identity stays only in OTEL resource attributes and permitted audit fields. Primary reuse disposition: `adapt`. Preserved source-plan detail: port semantics into provider reconcile state machine; do not reuse static per-env systemd/Nix ownership.", "integration" : "Controller watches Network, Guest, Volume, Process, User, Host, and Zone resources; creates child resources, writes status, invokes `NetworkEffectPort`, and drives finalizers.", "removalProof" : "Supersedes static per-env lifecycle in `nixos-modules/network.nix` and `nixos-modules/net.nix`; removal proof is successor controller coverage plus deletion of duplicate old gates when this provider lands.", "reuseAction" : "adapt", "reuseSource" : null, "specId" : "ADR-046-provider-network-local", "specPath" : "docs/specs/providers/ADR-046-provider-network-local.md", - "validation" : "`tests/controller_state.rs` covers normal reconcile, errors, finalizer ordering, adoption on restart, and observe/drift cycles with deterministic clock.", + "validation" : "`tests/controller_state.rs` covers normal reconcile, errors, finalizer ordering, adoption on restart, and observe/drift cycles with deterministic clock; `tests/metrics_labels.rs` structurally asserts exact identity-key absence and that a Network-name canary never enters metric label values.", "workItemId" : "ADR046-nl-006" }, { @@ -3847,14 +3847,14 @@ "dataMigration" : "`d2b.notifications.*` Nix options retired; `d2b.zones..resources.notification-desktop` with `spec.config.guestSources` replaces", "dependencyOwner" : "ADR046-notify-001; Nix/telemetry owner", "destination" : "Nix: Zone resource authoring in `nixos-modules/`; metrics: `packages/d2b-provider-notification-desktop/src/`", - "detailedDesign" : "Zone Provider resource and RoleBinding Nix compiler output; `spec.config.guestSources` authoring and eval-time assertions; OTEL metric emitters with closed labels; audit record emitters", + "detailedDesign" : "Zone Provider resource and RoleBinding Nix compiler output; `spec.config.guestSources` authoring and eval-time assertions; OTEL metric emitters with closed semantic labels and no Zone/resource-name-derived dimensions while retaining `d2b.zone` as a resource attribute; audit record emitters", "integration" : "Nix configuration compiler; OTEL emitter ring; authoritative audit", "removalProof" : "`nixos-modules/notifications.nix` removed after Zone resource equivalence confirmed by eval test", "reuseAction" : "adapt", "reuseSource" : null, "specId" : "ADR-046-provider-notification-desktop", "specPath" : "docs/specs/providers/ADR-046-provider-notification-desktop.md", - "validation" : "Eval tests for category enforcement, displayWaylandRef assertion, guestRef resolution; `tests/stream_redaction.rs` for content-free telemetry", + "validation" : "Eval tests for category enforcement, displayWaylandRef assertion, guestRef resolution; `tests/stream_redaction.rs` for content-free telemetry; `tests/metrics_labels.rs` for structural exact absence of `vm`, `zone`, `zone_id`, `zone_uid`, and resource-name-derived keys plus notification/Zone-name canary absence", "workItemId" : "ADR046-notify-006" }, { @@ -3986,7 +3986,7 @@ "integration" : "`make test-policy` and `make check` both fail if any provider crate violates §4.8; consistent with existing `no-bash-ast-walker` and workspace-sort gates; ADR046-zone-control-003 references §4.8 for Provider package conventions", "removalProof" : "No existing code removed; additive policy test only", "reuseAction" : "create", - "reuseSource" : null, + "reuseSource" : "None from main; workspace policy tests are repo-specific", "specId" : "ADR-046-resources-zone-control", "specPath" : "docs/specs/ADR-046-resources-zone-control.md", "validation" : "§15.3 layout conformance tests: `provider-crate-layout-src-required`, `provider-crate-layout-tests-required`, `provider-crate-layout-integration-required`, `provider-crate-layout-readme-required`, `provider-readme-sections-all-present`, `provider-readme-sections-partial-missing`, `provider-integration-target-declared`, `provider-integration-target-unique`, `provider-integration-target-valid-values`, `provider-crate-naming-convention`, `provider-crate-layout-non-provider-exempt`", @@ -4252,14 +4252,14 @@ "dataMigration" : "None — full d2b 3.0 reset; no prior state to migrate", "dependencyOwner" : "ADR046-pstate-001; Zone audit stream, OTEL provider owners", "destination" : "`packages/d2b-provider-volume-local/src/audit.rs`; `packages/d2b-provider-volume-local/src/otel.rs`; `packages/d2b-provider-volume-local/tests/audit_unit.rs` (hermetic audit golden records, OTEL cardinality label tests)", - "detailedDesign" : "Volume-state audit event types and Zone audit emission; OTEL metric definitions with closed cardinality label sets", + "detailedDesign" : "Volume-state audit event types and Zone audit emission; OTEL metric definitions with closed semantic label sets and no Zone or resource-name-derived label keys; Zone identity remains in the `d2b.zone` OTEL resource attribute", "integration" : "Every state lifecycle transition calls `audit::emit_volume_event`; OTEL metrics exported via `observability-otel` Provider", "removalProof" : "Not applicable", "reuseAction" : "adapt", "reuseSource" : null, "specId" : "ADR-046-provider-state", "specPath" : "docs/specs/ADR-046-provider-state.md", - "validation" : "Audit event golden records; no content/path/credential in audit payload; OTEL cardinality label tests", + "validation" : "Audit event golden records; no content/path/credential in audit payload; structural OTEL label-policy tests assert exact absence of `vm`, `zone`, `zone_id`, `zone_uid`, and resource-name-derived keys and preserve `d2b.zone` as a resource attribute", "workItemId" : "ADR046-pstate-008" }, { @@ -4552,14 +4552,14 @@ "dataMigration" : "None — telemetry-only work; no runtime state import", "dependencyOwner" : "P2; depends on ADR046-qemu-media-013 and ADR046-qemu-media-014; owner: runtime-qemu-media telemetry integration", "destination" : "packages/d2b-provider-runtime-qemu-media/src/telemetry.rs", - "detailedDesign" : "Metrics and OTEL spans: implement all metrics from §18 and OTEL trace spans with label cardinality enforcement and no VM name, user identity, path, or other sensitive value in any label or attribute. Primary reuse disposition: `create`. Preserved source-plan detail: net-new telemetry emission for the Provider metrics and spans in §18.", + "detailedDesign" : "Metrics and OTEL spans: implement all metrics from §18 and OTEL trace spans with structural closed-label enforcement and no Zone/VM/resource name, user identity, path, or other sensitive value in any metric label; retain Zone/resource identity only in bounded OTEL resource attributes and permitted audit fields. Primary reuse disposition: `create`. Preserved source-plan detail: net-new telemetry emission for the Provider metrics and spans in §18.", "integration" : "Controller, QMP, hotplug, and dependency-watch paths call telemetry helpers; OTEL/metrics exporters consume only closed, bounded labels for support dashboards.", "removalProof" : "None — telemetry helpers are new for this Provider; no prior owner to remove", "reuseAction" : "create", "reuseSource" : null, "specId" : "ADR-046-provider-runtime-qemu-media", "specPath" : "docs/specs/providers/ADR-046-provider-runtime-qemu-media.md", - "validation" : "`tests/metrics_label_cardinality.rs`; `tests/otel_span_attributes.rs`", + "validation" : "`tests/metrics_label_cardinality.rs` asserts exact absence of `vm`, `zone`, `zone_id`, `zone_uid`, and resource-name-derived keys plus Guest/Zone-name canary absence; `tests/otel_span_attributes.rs` preserves allowed OTEL resource identity attributes and rejects identity span attributes", "workItemId" : "ADR046-qemu-media-016" }, { @@ -4661,7 +4661,7 @@ "integration" : "`d2b host cutover preflight`/`plan` CLI commands consume this crate exclusively; no other crate re-implements inventory walking", "removalProof" : "Not applicable (net-new capability)", "reuseAction" : "adapt", - "reuseSource" : null, + "reuseSource" : "None from main; this is a v3-only cross-cutting concern with no main-branch equivalent", "specId" : "ADR-046-reset-and-cutover", "specPath" : "docs/specs/ADR-046-reset-and-cutover.md", "validation" : "`checkpoint_id` determinism property test; snapshot atomic-write crash-injection test; `cutover_preflight_refuses_dirty_flake_check`", @@ -4676,7 +4676,7 @@ "integration" : "Invoked by `preflight` before the snapshot is written; failures block `plan` from being offered", "removalProof" : "Not applicable", "reuseAction" : "adapt", - "reuseSource" : null, + "reuseSource" : "None from main", "specId" : "ADR-046-reset-and-cutover", "specPath" : "docs/specs/ADR-046-reset-and-cutover.md", "validation" : "`cutover-candidate-bundle-validation.nix`; trust-preflight rejection tests for each of digest/publisher/signature/deny/provenance/conformance failure modes", @@ -4691,7 +4691,7 @@ "integration" : "`d2b host cutover apply` orchestrates drain then disposition execution then hands off to Phase 5 (ADR046-reset-005)", "removalProof" : "Not applicable", "reuseAction" : "adapt", - "reuseSource" : null, + "reuseSource" : "None from main", "specId" : "ADR-046-reset-and-cutover", "specPath" : "docs/specs/ADR-046-reset-and-cutover.md", "validation" : "`cutover_apply_requires_exact_consent_phrase`; `cutover_drain_refuses_on_live_process`", @@ -4706,7 +4706,7 @@ "integration" : "Called by ADR046-reset-003's disposition executor for every Adopt row; writes to the state Volumes ADR046-device-tpm-004/ADR046-vl-004/ADR046-vl-006 define", "removalProof" : "Not applicable (the mechanism is retained permanently for later Full/Provider/Guest reset relocation use, not retired after first use)", "reuseAction" : "adapt", - "reuseSource" : null, + "reuseSource" : "None from main", "specId" : "ADR-046-reset-and-cutover", "specPath" : "docs/specs/ADR-046-reset-and-cutover.md", "validation" : "Crash-injection at every step boundary (Type 10 `cutover-crash-resume.nix`); TPM/durable-Volume Destroy-exclusion property test", @@ -4721,25 +4721,25 @@ "integration" : "Invoked immediately after ADR046-reset-003/004 complete; hands off to Phase 7 (ADR046-reset-006)", "removalProof" : "Not applicable", "reuseAction" : "create", - "reuseSource" : null, + "reuseSource" : "None from main", "specId" : "ADR-046-reset-and-cutover", "specPath" : "docs/specs/ADR-046-reset-and-cutover.md", "validation" : "Provider install topological-order determinism test; cycle-rejection test; store-identity mismatch fail-closed test", "workItemId" : "ADR046-reset-005" }, { - "currentSource" : "ADR 0032 gateway guest custody evidence (`d2b-realm-router/src/service_v2.rs` `CredentialCustody`); `ADR-046-zone-routing` §3 evidence", - "dataMigration" : "None (ZoneLink resources are ordinary Nix-authored configuration, not migrated credential bytes)", + "currentSource" : "ADR 0032 gateway guest custody evidence (`d2b-realm-router/src/service_v2.rs` `CredentialCustody`); frozen target model in `ADR-046-resources-zone-control` §3.1/§10.3 and `ADR-046-nix-configuration` \"Zone declaration\"/\"ZoneLink\"", + "dataMigration" : "None (the child-local ZoneLink is ordinary Nix-authored configuration, `parentZone` is recompiled into sealed allocator state, and neither is migrated credential material)", "dependencyOwner" : "ADR046-reset-005; `ADR-046-zone-routing` owner; `ADR-046-resources-zone-control` owner", "destination" : "`packages/d2b-cutover/src/{zonelink_cutover,guest_activation}.rs`", - "detailedDesign" : "Phase 7 ZoneLink translation from `EntrypointMode::GatewayBacked`/`HostResident` per [Zone/ZoneLink cutover](#zonezonelink-cutover); Phase 8 Network→Volume→Device→Guest ordering per [Guest/runtime/network/store view activation](#guestruntimenetworkstore-view-activation); enforcement that the parent inventory never enumerates gateway-guest-internal credential/audit state", + "detailedDesign" : "Phase 7 translation from `EntrypointMode::GatewayBacked`/`HostResident` per [Zone/ZoneLink cutover](#zonezonelink-cutover): the child authors/stores one local uplink with self-matching `childZoneName`; compiler-only `parentZone` selects the allocator; the parent retains only sealed allocator/route state and owns no reciprocal row or ZoneLink handler. Phase 8 follows Network→Volume→Device→Guest ordering per [Guest/runtime/network/store view activation](#guestruntimenetworkstore-view-activation). The parent inventory never enumerates gateway-guest-internal credential/audit state", "integration" : "Consumes Providers installed by ADR046-reset-005; hands off to ADR046-reset-007 (verification)", "removalProof" : "Not applicable", "reuseAction" : "adapt", - "reuseSource" : null, + "reuseSource" : "None from main", "specId" : "ADR-046-reset-and-cutover", "specPath" : "docs/specs/ADR-046-reset-and-cutover.md", - "validation" : "Gateway-custody-boundary test asserting the parent inventory never contains a gateway-guest-internal path; ZoneLink `Degraded/waiting-on-remote` non-blocking test", + "validation" : "Gateway-custody-boundary test asserting the parent inventory never contains a gateway-guest-internal path; child-local ZoneLink test asserting one uplink, self-matching `childZoneName`, and child-store ownership; compiler test asserting `parentZone` selects the allocator but appears only in sealed bootstrap state; no-reciprocal-parent-row/no-parent-handler test; child-local ZoneLink `Degraded/waiting-on-remote` non-blocking test", "workItemId" : "ADR046-reset-006" }, { @@ -4751,7 +4751,7 @@ "integration" : "`d2b host cutover verify`/`doctor` CLI commands; consumed by the Phase 10 finalize gate table", "removalProof" : "Not applicable", "reuseAction" : "adapt", - "reuseSource" : null, + "reuseSource" : "None from main", "specId" : "ADR-046-reset-and-cutover", "specPath" : "docs/specs/ADR-046-reset-and-cutover.md", "validation" : "Injected-digest-mismatch test for TPM/durable-Volume verify checks; audit-genesis-cross-check test; `cutover-full-rehearsal.nix`", @@ -4766,7 +4766,7 @@ "integration" : "`d2b host cutover finalize` CLI command; reads gate status from ADR046-reset-007's verify results plus each named policy-lint/integration test's pass/fail recorded in CI", "removalProof" : "Each candidate's own row in [Old artifact/unit/schema removal gates](#old-artifactunitschema-removal-gates) states its exact removal proof", "reuseAction" : "create", - "reuseSource" : null, + "reuseSource" : "None from main", "specId" : "ADR-046-reset-and-cutover", "specPath" : "docs/specs/ADR-046-reset-and-cutover.md", "validation" : "`policy_no_destroy_without_gate`; `policy_legacy_cli_verbs_absent_after_gate`; `tpm-adopt-retirement.nix`", @@ -4781,7 +4781,7 @@ "integration" : "`d2b host cutover rollback`/`hold` CLI commands; consulted by ADR046-reset-003's disposition executor and ADR046-reset-008's finalize gate before every mutating step", "removalProof" : "Not applicable", "reuseAction" : "adapt", - "reuseSource" : null, + "reuseSource" : "None from main", "specId" : "ADR-046-reset-and-cutover", "specPath" : "docs/specs/ADR-046-reset-and-cutover.md", "validation" : "`cutover_rollback_window_closes_after_phase_5`; incident-hold-blocks-destructive-step test", @@ -4796,7 +4796,7 @@ "integration" : "Standalone from the cutover Phases 0-10 above; usable at any later time as a recovery/maintenance lever once a Zone exists", "removalProof" : "Not applicable", "reuseAction" : "adapt", - "reuseSource" : null, + "reuseSource" : "None from main", "specId" : "ADR-046-reset-and-cutover", "specPath" : "docs/specs/ADR-046-reset-and-cutover.md", "validation" : "`host_reset_scope_isolation`; `zone-provider-guest-reset-isolation.nix`; durable-Volume-preserved-by-default property test for both Provider and Guest scopes", @@ -4811,7 +4811,7 @@ "integration" : "Run manually by an operator against a real host/device before the reset-and-cutover implementation is declared production-ready", "removalProof" : "Not applicable", "reuseAction" : "create", - "reuseSource" : null, + "reuseSource" : "None from main", "specId" : "ADR-046-reset-and-cutover", "specPath" : "docs/specs/ADR-046-reset-and-cutover.md", "validation" : "Manual pass/fail sign-off recorded per the project's existing live-host/hardware validation conventions", @@ -5002,14 +5002,14 @@ "dataMigration" : "New ZoneLink resources from Nix configuration; no prior enrollment compatibility", "dependencyOwner" : "ADR046-routing-003; core-controller ZoneLink handler owner", "destination" : "`packages/d2b-core-controller/src/zone_links.rs`", - "detailedDesign" : "Child-local ZoneLink handler in core-controller: manages local ResourceSpec→allocator-bound session→advertisement lifecycle; session state machine (Pending/Established/Disconnected/Reconnecting/Revoked); reconnect backoff per `reconnectPolicy`; advertisement issuance/renewal/withdrawal using enrolled KK ComponentSession; child-store route cursor and outbound intent queue; capability ceiling change handling; status writer; no copied parent/child resource content; Nix-compiled `parentZone` selects the parent allocator, which alone owns privileged listeners, placement, and route namespace and creates no reciprocal resource Primary reuse disposition: `adapt`. Preserved source-plan detail: extract and adapt.", + "detailedDesign" : "Child-local ZoneLink handler in core-controller: manages local ResourceSpec→allocator-bound session→advertisement lifecycle; session state machine (Pending/Established/Disconnected/Reconnecting/Revoked); reconnect backoff per `reconnectPolicy`; advertisement issuance/renewal/withdrawal using enrolled KK ComponentSession; child-store route cursor and outbound intent queue; capability ceiling change handling; status writer; aggregate metrics use only closed semantic phase/reason/outcome labels and never `link_name_hash` or another ZoneLink/Zone/resource identity label; Nix-compiled `parentZone` selects the parent allocator, which alone owns privileged listeners, placement, and route namespace and creates no reciprocal resource. Primary reuse disposition: `adapt`. Preserved source-plan detail: extract and adapt.", "integration" : "Child core-controller process → local transport Provider → sealed binding for the allocator selected by `parentZone` → d2b-bus ComponentSession; child ZoneLink handler exchanges advertisements while that parent ZoneRouteEngine admits/withdraws them", "removalProof" : "`RemoteNodeRegistry` retired after all enrolled peer routing moves to ZoneLink handler", "reuseAction" : "adapt", "reuseSource" : "Same v3 baseline commit `b5ddbed6`", "specId" : "ADR-046-zone-routing", "specPath" : "docs/specs/ADR-046-zone-routing.md", - "validation" : "Session lifecycle tests; reconnect/revocation/ceiling-change; intent queue drain; cursor resync; advertisement renewal timing; fake-child tests", + "validation" : "Session lifecycle tests; reconnect/revocation/ceiling-change; intent queue drain; cursor resync; advertisement renewal timing; fake-child tests; structural metric descriptor test asserts `vm`, `zone`, `zone_id`, `zone_uid`, and `link_name_hash` are absent and a ZoneLink-name canary never enters label values", "workItemId" : "ADR046-routing-004" }, { @@ -5201,7 +5201,7 @@ "integration" : "Runs as part of `make test-lint`/`make test-rust`; every Provider crate's own redaction test (e.g. `tests/stream_redaction.rs`) is a per-Provider instance of the same closed list", "removalProof" : "Not applicable — this is a permanent gate, not a migration", "reuseAction" : "adapt", - "reuseSource" : null, + "reuseSource" : "None (new cross-cutting gate; no equivalent exists in main)", "specId" : "ADR-046-security-and-threat-model", "specPath" : "docs/specs/ADR-046-security-and-threat-model.md", "validation" : "Hermetic (`cargo test -p d2b-contract-tests policy_telemetry_redaction`); fails the build if a new Provider crate is added without a corresponding redaction test file under its `tests/`", @@ -5231,7 +5231,7 @@ "integration" : "Runs against the real redb-backed resource store test harness, not a mock", "removalProof" : "Not applicable", "reuseAction" : "adapt", - "reuseSource" : null, + "reuseSource" : "None beyond the verb-table adaptation already tracked by `ADR046-zone-control-004`", "specId" : "ADR-046-security-and-threat-model", "specPath" : "docs/specs/ADR-046-security-and-threat-model.md", "validation" : "Hermetic property test (`proptest`/`quickcheck`-style, minimum 10,000 cases per property)", @@ -5246,7 +5246,7 @@ "integration" : "`make test-fuzz`; a companion container test (`tests/integration/containers/zonelink-cross-zone.rs`) runs two real Zone runtime containers connected by a real ZoneLink and asserts the same property end to end over the wire, not just in the frame-serialization unit", "removalProof" : "Not applicable", "reuseAction" : "adapt", - "reuseSource" : null, + "reuseSource" : "None", "specId" : "ADR-046-security-and-threat-model", "specPath" : "docs/specs/ADR-046-security-and-threat-model.md", "validation" : "Fuzz corpus (`cargo fuzz run zonelink_frame -- -runs=1000000`, zero crashes); container test passes in `make test-integration`", @@ -5321,7 +5321,7 @@ "integration" : "`make test-rust`; a host-integration variant (`tests/host-integration/volume-marker-tamper.nix`) repeats the inode-swap scenario against the real broker-maintained marker root on a real filesystem", "removalProof" : "Not applicable", "reuseAction" : "adapt", - "reuseSource" : null, + "reuseSource" : "None new", "specId" : "ADR-046-security-and-threat-model", "specPath" : "docs/specs/ADR-046-security-and-threat-model.md", "validation" : "Hermetic + host/KVM fault-injection test; acceptance is 100% fail-closed across all three tamper scenarios", @@ -5336,7 +5336,7 @@ "integration" : "`make test-lint` (static scan) and `make test-rust` (dynamic property test)", "removalProof" : "Not applicable", "reuseAction" : "adapt", - "reuseSource" : null, + "reuseSource" : "None", "specId" : "ADR-046-security-and-threat-model", "specPath" : "docs/specs/ADR-046-security-and-threat-model.md", "validation" : "Hermetic; the dynamic test additionally runs as a canary-byte test (a unique random marker is embedded in the token and searched for across every observability surface)", @@ -5396,7 +5396,7 @@ "integration" : "`make test-rust` (CLI integration tests); a container test (`tests/integration/containers/support-bundle-quarantined.rs`) runs a real Zone with one quarantined Provider and asserts the bundle correctly reports `partial`", "removalProof" : "Not applicable", "reuseAction" : "adapt", - "reuseSource" : null, + "reuseSource" : "None", "specId" : "ADR-046-security-and-threat-model", "specPath" : "docs/specs/ADR-046-security-and-threat-model.md", "validation" : "Hermetic CLI test asserting no spec byte or `metadata.name` appears in a generated bundle; container test for the quarantined-Provider case", @@ -5426,7 +5426,7 @@ "integration" : "`make test-drift`", "removalProof" : "Not applicable — permanent gate", "reuseAction" : "adapt", - "reuseSource" : null, + "reuseSource" : "None", "specId" : "ADR-046-security-and-threat-model", "specPath" : "docs/specs/ADR-046-security-and-threat-model.md", "validation" : "Hermetic shell-script gate; a negative test adds a scratch Provider dossier missing a Security section and asserts the gate fails", @@ -5441,7 +5441,7 @@ "integration" : "`make test-integration` (requires podman, per `AGENTS.md` \"Local Layer 1 + container integration\")", "removalProof" : "Not applicable", "reuseAction" : "adapt", - "reuseSource" : null, + "reuseSource" : "None", "specId" : "ADR-046-security-and-threat-model", "specPath" : "docs/specs/ADR-046-security-and-threat-model.md", "validation" : "Container integration test; acceptance is zero successful attacks across all five attempted vectors", @@ -5456,7 +5456,7 @@ "integration" : "Run manually before each tagged release touching a cloud/hardware Provider, per the existing `tests/README.md` manual-tier convention", "removalProof" : "Not applicable", "reuseAction" : "adapt", - "reuseSource" : null, + "reuseSource" : "None", "specId" : "ADR-046-security-and-threat-model", "specPath" : "docs/specs/ADR-046-security-and-threat-model.md", "validation" : "Checklist sign-off recorded in the release's validation evidence, not a CI gate (matches `D2b_LIVE=1` manual-tier precedent in `AGENTS.md`)", @@ -5797,14 +5797,14 @@ "dataMigration" : "Full d2b 3.0 reset; no v2 telemetry import", "dependencyOwner" : "Observability owner; depends on ADR046-security-key-010 relay, ADR046-security-key-009 controller, and ADR-046-telemetry-audit-and-support.", "destination" : "Provider/controller bounded telemetry emitter and observability-otel handoff for security-key metrics", - "detailedDesign" : "OTEL metrics: `d2b_device_sk_session_total`, `d2b_device_sk_ceremony_duration_seconds`, `d2b_device_sk_relay_restarts_total` via bounded emitter ring", + "detailedDesign" : "OTEL metrics: `d2b_device_sk_session_total`, `d2b_device_sk_ceremony_duration_seconds`, `d2b_device_sk_relay_restarts_total` via bounded emitter ring; descriptors use only closed semantic labels and never Zone/resource-name-derived identity, while `d2b.zone` and `d2b.provider` remain OTEL resource attributes", "integration" : "Relay/controller write metric events to the bounded ring; observability-otel Provider drains and exports; dashboards/CLI consume closed labels and bounded histograms.", "removalProof" : "None — net-new; no prior owner to remove", "reuseAction" : "create", "reuseSource" : null, "specId" : "ADR-046-provider-device-security-key", "specPath" : "docs/specs/providers/ADR-046-provider-device-security-key.md", - "validation" : "Metrics tests assert closed label sets, no device/session/guest/path labels, bounded ring behavior, and correct session/ceremony/restart counters.", + "validation" : "Metrics tests structurally assert closed label sets, exact absence of `vm`, `zone`, `zone_id`, `zone_uid`, and resource-name-derived keys, Device/Zone-name canary absence, retained `d2b.zone` resource attributes, bounded ring behavior, and correct session/ceremony/restart counters.", "workItemId" : "ADR046-security-key-022" }, { @@ -6802,7 +6802,7 @@ "dataMigration" : "Full d2b 3.0 reset; no v2 state/config import", "dependencyOwner" : "ADR046-telem-001; policy/contract-tests owner", "destination" : "`packages/d2b-contract-tests/tests/policy_telemetry_redaction.rs` (new); updated `policy_observability.rs`; updated `policy_metrics.rs`", - "detailedDesign" : "(1) Extend `loki_native_otel_resource_attributes` allowlist to include `d2b.zone`, `d2b.provider`, `d2b.component`, `service.version`. (2) Add redaction lint: scan all v3 instrumentation call sites for `realm`, `workload_id`, `node_id`, `vm` (as label key), `path`, `socket`, `argv`, `pid`, `exe`. (3) Add metric label gate: assert no v3 `MetricDescriptor` carries a `vm` label. (4) Add bucket boundary gates for 5 ms and 20 ms. (5) Retain: `startup_tracing_avoids_host_path_fields`; SigNoz-only backend assertion; `tempo_guest_collector_shape`; `config_source = \"realm-controllers\"` absence gate. Primary reuse disposition: `adapt`. Preserved source-plan detail: adapt and extend; keep existing tests; add new policy gates.", + "detailedDesign" : "(1) Extend `loki_native_otel_resource_attributes` allowlist to include `d2b.zone`, `d2b.provider`, `d2b.component`, `service.version`. (2) Add redaction lint: scan all v3 instrumentation call sites for `realm`, `workload_id`, `node_id`, `vm` (as label key), `path`, `socket`, `argv`, `pid`, `exe`. (3) Add structural metric-label policy lint: parse every v3 `MetricDescriptor`, require each label key and value domain to exist in the closed `METRIC_LABEL_POLICY`, reject exact keys `vm`, `zone`, `zone_id`, `zone_uid`, `credential_name`, `network`, `network_name`, and `link_name_hash`, reject resource-name-derived key suffixes `*_name`, `*_name_hash`, `*_name_digest`, and `*_uid`, and prove a `metadata.name` canary never enters label values. Fixed semantic labels remain allowed only with closed domains. (4) Assert the `d2b.zone` resource attribute remains present. (5) Add bucket boundary gates for 5 ms and 20 ms. (6) Retain: `startup_tracing_avoids_host_path_fields`; SigNoz-only backend assertion; `tempo_guest_collector_shape`; `config_source = \"realm-controllers\"` absence gate. Primary reuse disposition: `adapt`. Preserved source-plan detail: adapt and extend; keep existing tests; add new policy gates.", "integration" : "Contract-tests run in workspace check and `make test-drift`", "removalProof" : "None — net-new; no prior owner to remove", "reuseAction" : "adapt", @@ -6937,14 +6937,14 @@ "dataMigration" : "None — full d2b 3.0 reset; no prior state to migrate", "dependencyOwner" : "ADR046-transport-relay-001 through ADR046-transport-relay-005; telemetry/audit owner", "destination" : "`packages/d2b-provider-transport-azure-relay/src/{metrics.rs, audit.rs}`", - "detailedDesign" : "Emit all OTEL metrics and audit records listed in §OTEL and §Audit; closed label sets; never label secret bytes; provider audit covers **carriage authentication and health observations only** — Azure auth events, WebSocket lifecycle, credential acquisition outcomes — and is **separate from resource audit** (resource lifecycle events are owned by core); audit records appended through the Zone runtime audit log interface (no atomicity guarantee with Zone resource state in redb; best-effort delivery per the Zone's audit provider configuration); OTEL via lightweight emitter ring (no direct OTEL SDK dependency in Provider)", + "detailedDesign" : "Emit all OTEL metrics and audit records listed in §OTEL and §Audit; closed semantic label sets with no Zone/resource-name-derived keys; retain Zone identity only in the `d2b.zone` OTEL resource attribute; never label secret bytes; provider audit covers **carriage authentication and health observations only** — Azure auth events, WebSocket lifecycle, credential acquisition outcomes — and is **separate from resource audit** (resource lifecycle events are owned by core); audit records appended through the Zone runtime audit log interface (no atomicity guarantee with Zone resource state in redb; best-effort delivery per the Zone's audit provider configuration); OTEL via lightweight emitter ring (no direct OTEL SDK dependency in Provider)", "integration" : "`Provider/observability-otel` receives emitter ring frames; audit log via Zone runtime `d2b.audit.transport` category", "removalProof" : "N/A; new module", "reuseAction" : "create", "reuseSource" : null, "specId" : "ADR-046-provider-transport-azure-relay", "specPath" : "docs/specs/providers/ADR-046-provider-transport-azure-relay.md", - "validation" : "`tests/credential_redaction.rs` extended to cover audit/OTEL paths; `tests/fake_relay_transport.rs` asserts audit record fields against schema", + "validation" : "`tests/credential_redaction.rs` extended to cover audit/OTEL paths; `tests/metric_labels.rs` structurally asserts exact absence of `vm`, `zone`, `zone_id`, `zone_uid`, and resource-name-derived keys and that a Zone-name canary never enters label values; `tests/fake_relay_transport.rs` asserts audit record fields against schema", "workItemId" : "ADR046-transport-relay-006" }, { @@ -7091,7 +7091,7 @@ "integration" : "Build emitter validates `spec.provider.settings` against schema before computing `generationId`; drift gate enforces sync", "removalProof" : "`nixos-modules/options-realms.nix` realm wiring retired after Zone resource bundle activation replaces it", "reuseAction" : "create", - "reuseSource" : null, + "reuseSource" : "None; new schema file", "specId" : "ADR-046-provider-transport-unix", "specPath" : "docs/specs/providers/ADR-046-provider-transport-unix.md", "validation" : "All seven eval/build tests in the Nix section", @@ -7106,7 +7106,7 @@ "integration" : "`portal.rs` calls `audit.rs::emit_*` before returning from each portal method; `seqpacket.rs` calls `metrics.rs::record_*` on every accept/packet/attachment", "removalProof" : "`d2bd/src/metrics.rs` hand-rolled registry retired after metric surface migration", "reuseAction" : "create", - "reuseSource" : null, + "reuseSource" : "None; new per v3 telemetry separation invariant", "specId" : "ADR-046-provider-transport-unix", "specPath" : "docs/specs/providers/ADR-046-provider-transport-unix.md", "validation" : "`tests/conformance.rs::audit_records_contain_no_pid_uid_or_handle`; `tests/conformance.rs::metric_labels_are_closed_set`; `tests/conformance.rs::span_attributes_contain_no_sensitive_fields`", @@ -7402,14 +7402,14 @@ "dataMigration" : "None — full d2b 3.0 reset; no prior state to migrate", "dependencyOwner" : "ADR046-vl-001; Zone audit stream owner; `observability-otel` Provider owner", "destination" : "`src/audit.rs`; `src/otel.rs`; `src/error.rs`; `tests/audit_unit.rs`; `integration/audit.rs`", - "detailedDesign" : "Event types and Zone audit emission per §Audit events; OTEL metric definitions per §OTEL metrics; error catalog per §Error catalog; no-path invariant enforced in all outputs", + "detailedDesign" : "Event types and Zone audit emission per §Audit events; OTEL metric definitions per §OTEL metrics with closed semantic labels and no Zone/resource-name-derived dimensions; Zone identity remains in `d2b.zone` resource attributes; error catalog per §Error catalog; no-path invariant enforced in all outputs", "integration" : "Every lifecycle transition calls `audit::emit_volume_event`; OTEL metrics exported via `observability-otel` Provider", "removalProof" : "Not applicable", "reuseAction" : "adapt", "reuseSource" : null, "specId" : "ADR-046-provider-volume-local", "specPath" : "docs/specs/providers/ADR-046-provider-volume-local.md", - "validation" : "`tests/audit_unit.rs` golden records; `tests/error_messages.rs` bounded messages; OTEL label cardinality; `integration/audit.rs` live stream", + "validation" : "`tests/audit_unit.rs` golden records and structural metric descriptor assertions for exact absence of `vm`, `zone`, `zone_id`, `zone_uid`, and resource-name-derived keys plus resource-name canary absence; `tests/error_messages.rs` bounded messages; `integration/audit.rs` live stream", "workItemId" : "ADR046-vl-009" }, { @@ -7916,7 +7916,7 @@ "integration" : "Resource API admission gate (`packages/d2b-resource-api/src/quota_gate.rs`) called for every `create` verb; Zone controller triggers quota reconcile on resource-created/deleted/quotaRef-changed events; quota handler registered in core-controller process", "removalProof" : "Additive; no existing code removed", "reuseAction" : "create", - "reuseSource" : null, + "reuseSource" : "None from main; Quota is a new ResourceType with no main-branch analog", "specId" : "ADR-046-resources-zone-control", "specPath" : "docs/specs/ADR-046-resources-zone-control.md", "validation" : "`quota-ceiling-hard-reject`, `quota-ceiling-soft-warn`, `quota-ceiling-pertype`, `quota-drain-blocks-on-dependents`, `quota-over-quota-status`, `quota-nix-eval-bounds`, `quota-nix-build-pertype-unknown-type`", @@ -7931,7 +7931,7 @@ "integration" : "API admission gate checks union of enabled EmergencyPolicies before every admitted request; ZoneLink handler subscribes to EmergencyPolicy watch triggers; Provider process lifecycle listens for effective `stopProviderProcesses` and resumes launch on deactivation", "removalProof" : "Replaces the inline `emergencyDisable` field from the proposed Zone.spec option B; that option was not implemented", "reuseAction" : "create", - "reuseSource" : null, + "reuseSource" : "None from main; EmergencyPolicy is a new ResourceType", "specId" : "ADR-046-resources-zone-control", "specPath" : "docs/specs/ADR-046-resources-zone-control.md", "validation" : "`emergency-policy-activates-gate`, `emergency-policy-disconnects-zonelinks`, `emergency-policy-union-most-restrictive`, `emergency-policy-multi-enabled-combined-scope`, `emergency-policy-stop-processes-no-delete`, `emergency-policy-deactivation-restores-gate`, `emergency-policy-drain-finalizer`, `emergency-nix-eval-drain-deadline-bound-tightest`", @@ -7991,7 +7991,7 @@ "integration" : "ADR046-routing-011 supplies the one canonical 19-type registry and generated option family; validated `parentZone` feeds the allocator bootstrap sealer; the closed `d2b.zones..resources.*` tree is consumed by ADR046-zone-control-015; the Zone controller (ADR046-zone-control-001) reads the resulting bundle; Provider package conventions come from ADR046-zone-control-003", "removalProof" : "`nixos-modules/options-realms.nix`, `nixos-modules/realm-controller-config-json.nix`, `nixos-modules/realm-identity-config-json.nix` deleted after Zone controller and resource compiler reach full parity; `nixos-modules/assertions.nix` lines referencing `allowUnsafeLocal`/realm names removed after Host admission validation replaces them", "reuseAction" : "create", - "reuseSource" : null, + "reuseSource" : "None from main; eval-time validation is Nix-native", "specId" : "ADR-046-resources-zone-control", "specPath" : "docs/specs/ADR-046-resources-zone-control.md", "validation" : "All Phase 1 eval tests in §15.8 (`nix-eval-name-regex-enforced`, `nix-eval-verb-closed-enum`, `nix-eval-roleref-format`, `nix-eval-subject-type-restricted`, `nix-eval-no-duplicate-subjects`, `nix-eval-bootstrap-provider-rejected`, `nix-eval-provider-missing-artifact-id`, `nix-eval-artifact-id-not-in-catalog`, `nix-eval-artifact-wrong-type`, `nix-eval-artifact-id-format`, `nix-eval-credentialref-declared`, `nix-eval-dollar-key-rejected`, all five `nix-eval-parent-zone-*` vectors, `nix-eval-zonelink-child-name-mismatch-rejected`, `nix-eval-zonelink-second-uplink-rejected`, `nix-eval-zonelink-limits-maxpendingintents-bound`); drift test asserts the standard registry and generated option modules cover exactly all 19 canonical types; negative evals reject unknown strings, unqualified extensions, unsigned or uninstalled qualified types, and unknown `spec` fields; a positive fixture admits an installed signed qualified type and validates its strict generated schema", @@ -8006,7 +8006,7 @@ "integration" : "Reads from `d2b.zones..resources.*` (ADR046-zone-control-014); emits bundle consumed by ADR046-zone-control-001 configuration publication handler; generation counter stored as Nix module derivation input hash (hermetic) or in a NixOS state file (impure) — exact mechanism is implementation decision", "removalProof" : "No current equivalent; additive only; no prior code removed", "reuseAction" : "create", - "reuseSource" : null, + "reuseSource" : "None from main; the resource compiler is a new pure-Rust Nix derivation", "specId" : "ADR-046-resources-zone-control", "specPath" : "docs/specs/ADR-046-resources-zone-control.md", "validation" : "All Phase 2 build tests in §15.8 (`nix-build-artifact-id-missing-from-catalog`, `nix-build-artifact-wrong-type-rejected`, `nix-build-duplicate-artifact-id`, `nix-build-artifact-store-path-absent-from-bundle`, `nix-build-artifact-store-path-absent-from-config`, `nix-build-config-schema-failure`, `nix-build-schema-digest-mismatch`, `nix-build-manifest-digest-mismatch`, `nix-build-resourcetype-collision`, `nix-build-bundle-sorted`, `nix-build-bundle-digest-stable`, `nix-build-per-resource-digest-correct`, `nix-build-credential-ref-survives-build`, `nix-build-inline-secret-lint-warning`, `nix-build-inline-secret-strict-failure`)", @@ -8081,7 +8081,7 @@ "integration" : "ADR046-zone-control-019 controller; owner/dependency reconcile (§11, ADR046-reconcile-*); local semantic Provider import adapter", "removalProof" : "Not applicable (new surface)", "reuseAction" : "create", - "reuseSource" : null, + "reuseSource" : "None from main; projection ownership reuses the core owner/child reconcile machinery (§11)", "specId" : "ADR-046-resources-zone-control", "specPath" : "docs/specs/ADR-046-resources-zone-control.md", "validation" : "Exactly one same-type Service projection owned by import; no Device/Endpoint/Binding projection; Binding never auto-created/deleted; Binding target allowlist; intent-only spec/status-only observations; owned Process/Endpoint child cleanup; pending finalizer while Binding refs remain; reconnect only after generation/factory/schema revalidation; hermetic fake-adapter + real-stream integration tiers", @@ -8137,14 +8137,14 @@ "dataMigration" : "None — full d2b 3.0 reset", "dependencyOwner" : "ADR046-zone-control-019, ADR046-zone-control-022; `d2b-core-controller` owner", "destination" : "`packages/d2b-core-controller/src/authority.rs` (Host-global index scope + hardware admission)", - "detailedDesign" : "Extend the core authority index so `host`, `physical-device`, `seat`, and `external-service` authorities are keyed **Host-global** (`(Host, authorityClass, opaqueKeyDigest)`), admitting exactly one owner across all Zones on the host, while `zone`-scoped authorities stay Zone-local. Enforce the §8B.3 hardware rows: GPU full-device exclusive vs render-node shared; per-Guest swtpm and physical TPM exclusive (state never wiped); one Core-derived `physical-usb-backing/v1` identity digest claimed through the exact `(Host, physical-usb-backing, opaqueKeyDigest)` tuple by every USB or security-key implementation before effects, plus the separate host-global `usbip-host` module; macvtap/NIC `parentInterface` `passthru` globally exclusive across all Zones; host-shared `/dev/kvm` and `/dev/vhost-vsock` as `Provider/system-core` grants (no 28th Provider, no `kvm` busClass); globally-unique vsock CID; fixed listener ports as `Endpoint`s; host store + per-Guest store-view writer; Network TAP/bridge. A second Zone claiming the same physical backing receives `physical-usb-backing-conflict` before any open, bind, withhold, module, relay, or attachment effect; restart adopts by `ownerProof`; Guest-stop drains dependent leases. GPU-owned `udmabuf`/video and per-session `vhost-vsock` tokens stay authority subresources/DeviceGrants (not resources/Providers). Primary reuse disposition: `adapt`. Preserved source-plan detail: net-new (Host-global index scope for host/physical-device authorities).", + "detailedDesign" : "Extend the core authority index so `host`, `physical-device`, `seat`, and `external-service` authorities are keyed **Host-global** (`(Host, authorityClass, opaqueKeyDigest)`), admitting exactly one owner across all Zones on the host, while `zone`-scoped authorities stay Zone-local. Enforce the §8B.3 hardware rows: GPU full-device exclusive vs render-node shared; per-Guest swtpm and physical TPM exclusive (state never wiped); one Core-derived `physical-usb-backing/v1` identity digest claimed through the exact `(Host, physical-usb-backing, opaqueKeyDigest)` tuple by every USB or security-key implementation before effects, plus the separate host-global `usbip-host` module and the Host-global `Provider/device-usbip` relay `Endpoint`, exactly one per Core-derived Network UID/signed-policy-port digest with multiplexed arbitration and `usbip-network-relay-authority-conflict` on a second owner; macvtap/NIC `parentInterface` `passthru` globally exclusive across all Zones; host-shared `/dev/kvm` and `/dev/vhost-vsock` as `Provider/system-core` grants (no 28th Provider, no `kvm` busClass); globally-unique vsock CID; other fixed listener ports as `Endpoint`s; host store + per-Guest store-view writer; Network TAP/bridge. A second Zone claiming the same physical backing receives `physical-usb-backing-conflict` before any open, bind, withhold, module, relay, or attachment effect; restart adopts by `ownerProof`; Guest-stop drains dependent leases. GPU-owned `udmabuf`/video and per-session `vhost-vsock` tokens stay authority subresources/DeviceGrants (not resources/Providers). Primary reuse disposition: `adapt`. Preserved source-plan detail: net-new (Host-global index scope for host/physical-device authorities).", "integration" : "Core authority index (ADR046-zone-control-019); Provider cardinality admission (ADR046-zone-control-022); `Provider/system-core` KVM/vhost-vsock grant; `Provider/device-*` and `Network` authorities", "removalProof" : "Not applicable (new surface)", "reuseAction" : "adapt", "reuseSource" : "Core authority index (ADR046-zone-control-019)", "specId" : "ADR-046-resources-zone-control", "specPath" : "docs/specs/ADR-046-resources-zone-control.md", - "validation" : "Two Zones on one host cannot both claim one GPU/TPM/USB/`/dev/kvm`/passthru NIC/vsock CID/fixed port — second is `duplicateConflict`; security-key and USB implementations resolving the same physical token submit byte-identical `physical-usb-backing` tuple keys and the loser receives `physical-usb-backing-conflict` before any effect; Provider-private authority classes/digests cannot bypass the collision; render-node shared admits bounded holders; per-Guest swtpm exclusive and marker never wiped; host-global adoption by `ownerProof`; hardware D096 exportability (GPU/KVM/TPM/store/macvtap non-exportable; semantic USB policy-gated); fast hermetic with fakes", + "validation" : "Two Zones on one host cannot both claim one GPU/TPM/USB/`/dev/kvm`/passthru NIC/vsock CID/fixed port — second is `duplicateConflict`; security-key and USB implementations resolving the same physical token submit byte-identical `physical-usb-backing` tuple keys and the loser receives `physical-usb-backing-conflict` before any effect; a second USBIP relay `Endpoint` for one Core-derived Network UID/signed-policy-port digest receives `usbip-network-relay-authority-conflict`, while multiple admitted Services share the multiplexed owner and no `Network` authority owns the listener/firewall; Provider-private authority classes/digests cannot bypass the collision; render-node shared admits bounded holders; per-Guest swtpm exclusive and marker never wiped; host-global adoption by `ownerProof`; hardware D096 exportability (GPU/KVM/TPM/store/macvtap non-exportable; semantic USB policy-gated); fast hermetic with fakes", "workItemId" : "ADR046-zone-control-024" } ], diff --git a/docs/specs/providers/ADR-046-provider-activation-nixos.md b/docs/specs/providers/ADR-046-provider-activation-nixos.md index 7ab5e1f63..d947bc1b9 100644 --- a/docs/specs/providers/ADR-046-provider-activation-nixos.md +++ b/docs/specs/providers/ADR-046-provider-activation-nixos.md @@ -954,12 +954,12 @@ The controller emits one span per generation reconcile cycle. Attributes: | Attribute | Type | Notes | | --- | --- | --- | -| `d2b.zone` | string | Zone name | -| `d2b.activation.target` | string | `executionRef` resource name (no store path) | | `d2b.activation.mode` | string | `switch\|boot\|test\|adopt` | | `d2b.activation.detail` | string | Current `activationDetail` value | | `d2b.activation.outcome` | string | `succeeded\|failed\|timeout` on terminal transition | +Zone and target identity are available only in bounded OTEL resource attributes +and permitted audit fields, never as span attributes. No `systemArtifactId` value, store path, digest, or artifact catalog field appears as a span attribute. diff --git a/docs/specs/providers/ADR-046-provider-audio-pipewire.md b/docs/specs/providers/ADR-046-provider-audio-pipewire.md index 0734434a3..6635d3b92 100644 --- a/docs/specs/providers/ADR-046-provider-audio-pipewire.md +++ b/docs/specs/providers/ADR-046-provider-audio-pipewire.md @@ -2015,13 +2015,14 @@ direction transitions; it does not record the level value. ### Span attributes Controller spans carry: -- `d2b.zone`: Zone name - `d2b.provider`: `audio-pipewire` - `d2b.component`: `audio-binding-controller` or `audio-mediator` - `d2b.resource.type`: `AudioService`, `AudioBinding`, `Process`, or `Endpoint` - `d2b.resource.generation`: current `metadata.generation` - `d2b.outcome`: outcome code +Zone/resource identity is available only in bounded OTEL resource attributes +and permitted audit fields, never as a span attribute. Spans must not carry socket paths, PipeWire paths, PipeWire node IDs, level values, gain values, or guest workload usernames. diff --git a/docs/specs/providers/ADR-046-provider-credential-managed-identity.md b/docs/specs/providers/ADR-046-provider-credential-managed-identity.md index 047471e15..9607c25b0 100644 --- a/docs/specs/providers/ADR-046-provider-credential-managed-identity.md +++ b/docs/specs/providers/ADR-046-provider-credential-managed-identity.md @@ -1336,15 +1336,16 @@ d2b.credential.provider_health_check | Attribute | Value | | --- | --- | -| `d2b.zone` | Zone name | -| `d2b.credential.name` | Credential resource name | | `d2b.credential.provider` | `credential-managed-identity` (literal) | | `d2b.credential.operation_class` | Closed enum string | | `d2b.credential.placement_binding` | `host-system` or `guest-agent` | | `d2b.credential.outcome` | Stable closed outcome code | | `d2b.credential.rotation_generation` | Numeric rotation generation | -### Forbidden from spans and attributes +Zone and Credential identity use the `d2b.zone` and `d2b.credential.name` +OTEL resource attributes only. They are forbidden as span attributes. + +### Forbidden from spans and resource attributes Token bytes, audience literal, IMDS URL fragments, `clientId` value, `imdsEndpointAlias` value, provider-internal diagnostics, host filesystem paths, @@ -1838,7 +1839,7 @@ item. | Detailed design | Shared audit/OTEL: emit audit records for all methods and controller events per §Audit; emit OTEL spans and metrics per §OTEL and metrics with no Zone/Credential/resource-name-derived label; report expiry as the minimum for each provider/placement aggregate; add `d2b_credential_imds_calls_total` counter with bounded `alias` label; enforce `contains_sensitive_shape` on all string fields in audit records and metric labels; add canary tests for `managed-identity-canary`, `credential_canary`, `imds-endpoint-canary`, Credential `metadata.name`, and Zone name in `canary.rs`. Primary reuse disposition: `adapt`. Preserved source-plan detail: adapt existing sensitive-shape guard and canary pattern to v3 audit, OTEL, and metric surfaces. | | Integration | Controller and agent service methods call audit/telemetry helpers; audit subsystem and OTEL exporters consume bounded redacted records; contract tests validate credential audit shape across providers. | | Data migration | None — audit/telemetry only; no runtime state import | -| Validation | `packages/d2b-contract-tests/tests/credential_audit.rs`; managed-identity `canary.rs`; audit/OTEL unit tests structurally assert exact absence of `vm`, `zone`, `zone_id`, `zone_uid`, `credential_name`, and every resource-name-derived label key, reject Credential/Zone-name label canaries, preserve allowed OTEL identity attributes, and reject sensitive shapes | +| Validation | `packages/d2b-contract-tests/tests/credential_audit.rs`; managed-identity `canary.rs`; audit/OTEL unit tests structurally assert exact absence of `vm`, `zone`, `zone_id`, `zone_uid`, `credential_name`, and every resource-name-derived label key, reject Credential/Zone-name label canaries, preserve allowed OTEL resource identity attributes, reject identity span attributes, and reject sensitive shapes | | Removal proof | None — audit/telemetry helpers are additive; no prior owner to remove | --- @@ -1940,7 +1941,7 @@ audit record field set conformance, delivery session binding contract, RBAC | `clientId` value absent from audit records, metric labels, OTEL spans, error messages, and log lines | Config field exclusion | | `imdsEndpointAlias` value absent from OTEL span attributes, audit records, and error messages | Alias exclusion | | IMDS response-shaped string absent from status, audit, OTEL, and logs | IMDS response content exclusion | -| Metric descriptors contain no `vm`, `zone`, `zone_id`, `zone_uid`, `credential_name`, or resource-name-derived key; Credential/Zone-name canaries are absent from emitted label values while allowed OTEL identity attributes remain | Structural metric identity-label exclusion | +| Metric descriptors contain no `vm`, `zone`, `zone_id`, `zone_uid`, `credential_name`, or resource-name-derived key; Credential/Zone-name canaries are absent from emitted label values while allowed OTEL resource identity attributes remain and identity span attributes are absent | Structural metric identity-label exclusion | #### `delivery.rs` diff --git a/docs/specs/providers/ADR-046-provider-credential-secret-service.md b/docs/specs/providers/ADR-046-provider-credential-secret-service.md index b2328507d..3385a0bab 100644 --- a/docs/specs/providers/ADR-046-provider-credential-secret-service.md +++ b/docs/specs/providers/ADR-046-provider-credential-secret-service.md @@ -1015,17 +1015,17 @@ Required span attributes (closed set): | Attribute | Value | | --- | --- | -| `d2b.zone` | Zone name | -| `d2b.credential.name` | Credential resource name | | `d2b.credential.provider` | `credential-secret-service` | | `d2b.credential.operation_class` | Closed enum string | | `d2b.credential.placement_binding` | `user-agent` | | `d2b.credential.outcome` | Stable closed outcome code | | `d2b.credential.rotation_generation` | Numeric rotation generation | -Forbidden: token bytes, audience literals, provider diagnostics, host paths, -Secret Service object paths, collection names, resource IDs, correlation IDs -embedding secret shapes. +Zone and Credential identity use the `d2b.zone` and `d2b.credential.name` +OTEL resource attributes only. They are forbidden as span attributes. Also +forbidden: token bytes, audience literals, provider diagnostics, host paths, +Secret Service object paths, collection names, resource IDs, and correlation +IDs embedding secret shapes. ### 11.5 Metrics @@ -1039,8 +1039,9 @@ embedding secret shapes. The expiry gauge reports the minimum seconds remaining across active user-agent leases (0 when none). Label cardinality is bounded and semantic; -Credential and Zone identity remain in bounded OTEL attributes and permitted -audit fields, never labels. Secret-shape assertions run on all label values. +Credential and Zone identity remain in bounded OTEL resource attributes and +permitted audit fields, never metric labels or span attributes. Secret-shape +assertions run on all label values. --- @@ -1305,7 +1306,7 @@ generation cleanup contract in `nixos-modules/options-resources.nix` and | Detailed design | Audit/OTEL: emit audit records and OTEL spans/metrics for all credential service methods and controller events, with canary enforcement, expiry aggregated across user-agent leases, no Zone/Credential/resource-name-derived metric label, and no token/object-path/lease bytes in status, delivery outer headers, audit, metrics, spans, or logs. Full detail remains in `ADR-046-resources-credential` §Implementation work items. Primary reuse disposition: `adapt`. Preserved source-plan detail: adapt zero-secret invariant and canary test pattern to credential-secret-service audit/OTEL surfaces. | | Integration | Controller and service methods call audit/telemetry helpers; audit subsystem and OTEL exporters consume bounded event/span/metric records; canary tests verify every public observable surface stays secret-free. | | Data migration | None — audit/telemetry only; no runtime state migration | -| Validation | Credential audit/OTEL tests from `ADR-046-resources-credential`; `tests/canary.rs` structurally asserts exact absence of `vm`, `zone`, `zone_id`, `zone_uid`, `credential_name`, and every resource-name-derived metric key plus Credential/Zone-name label canary absence while preserving allowed OTEL identity attributes; `tests/delivery.rs` for credential-secret-service | +| Validation | Credential audit/OTEL tests from `ADR-046-resources-credential`; `tests/canary.rs` structurally asserts exact absence of `vm`, `zone`, `zone_id`, `zone_uid`, `credential_name`, and every resource-name-derived metric key plus Credential/Zone-name label canary absence while preserving allowed OTEL resource identity attributes and rejecting identity span attributes; `tests/delivery.rs` for credential-secret-service | | Removal proof | None — audit/telemetry helpers are new; no prior owner to remove | Implements audit record and OTEL span/metric emission for all credential @@ -1378,7 +1379,7 @@ All `check_provider_conformance` arms pass for `d2b-provider-credential-secret-s | `object_path_absent_all_responses` | `object_path_canary` absent from all response DTOs | | `canary_absent_audit_records` | `credential_canary` and `object_path_canary` absent from all audit record JSON | | `canary_absent_span_attributes` | Neither canary present in any OTEL span attribute captured by test subscriber | -| `metric_identity_labels_absent` | No descriptor key is `vm`, `zone`, `zone_id`, `zone_uid`, `credential_name`, or resource-name-derived; Credential/Zone-name canaries are absent from values; allowed OTEL identity attributes remain | +| `metric_identity_labels_absent` | No descriptor key is `vm`, `zone`, `zone_id`, `zone_uid`, `credential_name`, or resource-name-derived; Credential/Zone-name canaries are absent from values; allowed OTEL resource identity attributes remain and identity span attributes are absent | | `canary_absent_delivery_binding` | Neither canary present in delivery session binding parameters | #### `tests/delivery.rs` diff --git a/docs/specs/providers/ADR-046-provider-device-tpm.md b/docs/specs/providers/ADR-046-provider-device-tpm.md index 19a0bbeac..e6f01bae1 100644 --- a/docs/specs/providers/ADR-046-provider-device-tpm.md +++ b/docs/specs/providers/ADR-046-provider-device-tpm.md @@ -1060,8 +1060,9 @@ UID, GID, PID, or socket address. Zone identity remains in the bounded | `device-tpm.endpoint-ready` | TPM EndpointRef resolution | | `device-tpm.finalize` | Finalizer execution | -Span attributes: `zone` (string), `device_uid` (opaque ≤ 64 chars), -`outcome` (bounded stable label). No path, argv, socket, UID, GID, or PID. +Span attributes contain only `outcome` (bounded stable value). Zone and Device +identity remain in bounded OTEL resource attributes and permitted audit fields. +No path, argv, socket, UID, GID, or PID appears in a span attribute. --- diff --git a/docs/specs/providers/ADR-046-provider-device-usbip.md b/docs/specs/providers/ADR-046-provider-device-usbip.md index b8a0e1690..4a935c329 100644 --- a/docs/specs/providers/ADR-046-provider-device-usbip.md +++ b/docs/specs/providers/ADR-046-provider-device-usbip.md @@ -1229,8 +1229,8 @@ The controller emits OTEL spans for its own reconcile operations: | Span name | Attributes | | --- | --- | -| `device-usbip.service.reconcile` | `zone`, `service.name_digest`, `mode`, `phase`, `trigger_reason` | -| `device-usbip.binding.reconcile` | `zone`, `binding.name_digest`, `phase`, `trigger_reason` | +| `device-usbip.service.reconcile` | `mode`, `phase`, `trigger_reason` | +| `device-usbip.binding.reconcile` | `phase`, `trigger_reason` | | `device-usbip.effect.ensure_kernel_module` | `outcome`, `error_class` | | `device-usbip.effect.acquire_lease` | `outcome`, `error_class` | | `device-usbip.effect.withhold_device` | `outcome`, `error_class` | @@ -1243,10 +1243,11 @@ The controller emits OTEL spans for its own reconcile operations: | `device-usbip.process.binding_proxy_start` | `outcome`, `error_class` | Attributes must never carry raw busids, lock paths, nftables text, binary -paths, Endpoint addresses, import/export keys, session/transfer identifiers, or -operator/user identifiers. Cardinality is bounded: `error_class` is a closed -enum; resource names are fixed-length digests; `zone` is bounded by Zone -cardinality. +paths, Endpoint addresses, import/export keys, session/transfer identifiers, +operator/user identifiers, Zone identity, or resource-name-derived identity. +Cardinality is bounded: `error_class`, `mode`, `phase`, `trigger_reason`, and +`outcome` are closed semantic enums. Zone identity is retained only as the +`d2b.zone` OTEL resource attribute and in the authorized audit record. --- diff --git a/docs/specs/providers/ADR-046-provider-display-wayland.md b/docs/specs/providers/ADR-046-provider-display-wayland.md index 679fe44d5..77544a519 100644 --- a/docs/specs/providers/ADR-046-provider-display-wayland.md +++ b/docs/specs/providers/ADR-046-provider-display-wayland.md @@ -1304,14 +1304,13 @@ enums; no Zone UID or resource name is a metric dimension. OTEL spans are emitted for the following controller operations: - `display_wayland.session.reconcile` — reconcile a single `WaylandSession`; - attributes: `d2b.zone`, `d2b.provider`, `d2b.component`; - `display_wayland.proxy.start` — proxy Process spawn ticket issue; - attributes: `d2b.zone`, `d2b.provider`, `d2b.component`; -- `display_wayland.policy.compile` — policy compilation from `WaylandPolicy`; - attributes: `d2b.zone`, `d2b.provider`, `d2b.component`. +- `display_wayland.policy.compile` — policy compilation from `WaylandPolicy`. -No span carries socket paths, guest names (beyond the stable Zone/Provider/ -component attributes), user identities, window titles, or clipboard content. +Zone, Provider, component, and resource identity remain only in the OTEL +resource attributes below and permitted audit fields. No span carries those +identities, socket paths, guest names, user identities, window titles, or +clipboard content. ### 14.4 OTEL resource attributes diff --git a/docs/specs/providers/ADR-046-provider-network-local.md b/docs/specs/providers/ADR-046-provider-network-local.md index 4023b4944..50f3e8c39 100644 --- a/docs/specs/providers/ADR-046-provider-network-local.md +++ b/docs/specs/providers/ADR-046-provider-network-local.md @@ -1934,8 +1934,6 @@ Root span per reconcile attempt: ``` d2b.network.reconcile - network.name: # plain name; NOT hostname - network.zone: network.generation: reconcile.trigger: reconcile.attempt: @@ -1961,8 +1959,8 @@ d2b.network.observe.drift_check Metric labels use closed semantic cardinality and carry no Zone or Network identity. Zone identity remains in the `d2b.zone` OTEL resource attribute. -`network.name` is a ResourceName (bounded `^[a-z][a-z0-9-]*$`), never a -hostname or FQDN, and is never copied into a metric label. +Network identity is likewise available only as a bounded OTEL resource +attribute and permitted audit field, never as a span attribute or metric label. Metrics: @@ -2365,7 +2363,7 @@ On controller binary upgrade: | Current source | None — net-new v3 provider controller; v1 behavior lived in `nixos-modules/network.nix` and `nixos-modules/net.nix` static NixOS module logic. | | Reuse action | adapt | | Destination | `packages/d2b-provider-network-local/src/{controller.rs,metrics.rs}`. | -| Detailed design | Implement `controller.rs` reconcile/observe/finalize handlers with `NetworkEffectPort` injection and the §21 metric descriptors with closed semantic labels. No descriptor may carry `vm`, `zone`, `zone_id`, `zone_uid`, `network`, or another resource-name-derived key; Network/Zone identity stays in trace/resource attributes. Primary reuse disposition: `adapt`. Preserved source-plan detail: port semantics into provider reconcile state machine; do not reuse static per-env systemd/Nix ownership. | +| Detailed design | Implement `controller.rs` reconcile/observe/finalize handlers with `NetworkEffectPort` injection and the §21 metric descriptors with closed semantic labels. No descriptor may carry `vm`, `zone`, `zone_id`, `zone_uid`, `network`, or another resource-name-derived key; Network/Zone identity stays only in OTEL resource attributes and permitted audit fields. Primary reuse disposition: `adapt`. Preserved source-plan detail: port semantics into provider reconcile state machine; do not reuse static per-env systemd/Nix ownership. | | Integration | Controller watches Network, Guest, Volume, Process, User, Host, and Zone resources; creates child resources, writes status, invokes `NetworkEffectPort`, and drives finalizers. | | Data migration | Full d2b 3.0 reset; no v2 state/config import. | | Validation | `tests/controller_state.rs` covers normal reconcile, errors, finalizer ordering, adoption on restart, and observe/drift cycles with deterministic clock; `tests/metrics_labels.rs` structurally asserts exact identity-key absence and that a Network-name canary never enters metric label values. | diff --git a/docs/specs/providers/ADR-046-provider-notification-desktop.md b/docs/specs/providers/ADR-046-provider-notification-desktop.md index a41588cdb..37e979a9c 100644 --- a/docs/specs/providers/ADR-046-provider-notification-desktop.md +++ b/docs/specs/providers/ADR-046-provider-notification-desktop.md @@ -1085,13 +1085,14 @@ Traces are created at notification record admission (root span on | Attribute | Value | | --- | --- | -| `d2b.zone` | Zone name | | `d2b.provider` | `notification-desktop` | | `d2b.component` | `host-sink` \| `guest-source` | | `d2b.notification.category` | stable category token | | `d2b.notification.urgency` | `low` \| `normal` \| `critical` | | `d2b.notification.request_digest` | `sha256:` of the opaque request handle | +Zone/resource identity is available only in bounded OTEL resource attributes +and permitted audit fields, never as a span attribute. No span carries summary, body, action label, icon ref, or content-derived values. The `correlationId` from the request is carried as the W3C TraceContext `tracestate` join key only, never as a span attribute containing content bytes. diff --git a/docs/specs/providers/ADR-046-provider-observability-otel.md b/docs/specs/providers/ADR-046-provider-observability-otel.md index 6be334446..2f9c4c6c9 100644 --- a/docs/specs/providers/ADR-046-provider-observability-otel.md +++ b/docs/specs/providers/ADR-046-provider-observability-otel.md @@ -1131,7 +1131,7 @@ processes): | `d2b.otel.collector.drain` | Internal | `signal`, `frame_count`, `outcome` | Per drain cycle | | `d2b.otel.collector.export` | Client | `signal`, `batch_size`, `outcome` | Per OTLP export call | | `d2b.otel.collector.startup` | Internal | `outcome`, `socket_ready` | Provider startup | -| `d2b.otel.forwarder.session` | Internal | `outcome`, `guest_uid_digest` | Per vsock session; no guest name | +| `d2b.otel.forwarder.session` | Internal | `outcome` | Per vsock session; no guest identity | | `d2b.otel.journald.cycle` | Internal | `records_total`, `redacted`, `dropped`, `outcome` | Journald batch | Forbidden span attributes: path, socket path, endpoint address, argv, pid, diff --git a/docs/specs/providers/ADR-046-provider-runtime-qemu-media.md b/docs/specs/providers/ADR-046-provider-runtime-qemu-media.md index 8f7dd6c29..19e25d27f 100644 --- a/docs/specs/providers/ADR-046-provider-runtime-qemu-media.md +++ b/docs/specs/providers/ADR-046-provider-runtime-qemu-media.md @@ -1777,10 +1777,10 @@ destination in `packages/d2b-provider-runtime-qemu-media/`. | Current source | None — net-new v3 work; no pre-ADR45 baseline equivalent | | Reuse action | create | | Destination | packages/d2b-provider-runtime-qemu-media/src/telemetry.rs | -| Detailed design | Metrics and OTEL spans: implement all metrics from §18 and OTEL trace spans with structural closed-label enforcement and no Zone/VM/resource name, user identity, path, or other sensitive value in any metric label; retain Zone/resource identity only in bounded OTEL attributes and permitted audit fields. Primary reuse disposition: `create`. Preserved source-plan detail: net-new telemetry emission for the Provider metrics and spans in §18. | +| Detailed design | Metrics and OTEL spans: implement all metrics from §18 and OTEL trace spans with structural closed-label enforcement and no Zone/VM/resource name, user identity, path, or other sensitive value in any metric label; retain Zone/resource identity only in bounded OTEL resource attributes and permitted audit fields. Primary reuse disposition: `create`. Preserved source-plan detail: net-new telemetry emission for the Provider metrics and spans in §18. | | Integration | Controller, QMP, hotplug, and dependency-watch paths call telemetry helpers; OTEL/metrics exporters consume only closed, bounded labels for support dashboards. | | Data migration | None — telemetry-only work; no runtime state import | -| Validation | `tests/metrics_label_cardinality.rs` asserts exact absence of `vm`, `zone`, `zone_id`, `zone_uid`, and resource-name-derived keys plus Guest/Zone-name canary absence; `tests/otel_span_attributes.rs` preserves allowed identity attributes | +| Validation | `tests/metrics_label_cardinality.rs` asserts exact absence of `vm`, `zone`, `zone_id`, `zone_uid`, and resource-name-derived keys plus Guest/Zone-name canary absence; `tests/otel_span_attributes.rs` preserves allowed OTEL resource identity attributes and rejects identity span attributes | | Removal proof | None — telemetry helpers are new for this Provider; no prior owner to remove | --- diff --git a/docs/specs/providers/ADR-046-provider-transport-azure-relay.md b/docs/specs/providers/ADR-046-provider-transport-azure-relay.md index 874f7fa82..b4471a823 100644 --- a/docs/specs/providers/ADR-046-provider-transport-azure-relay.md +++ b/docs/specs/providers/ADR-046-provider-transport-azure-relay.md @@ -373,12 +373,13 @@ Before a ZoneLink becomes Ready, both Zone controllers must complete an out-of-band enrollment exchange that establishes: - **Parent** (listener side): the parent's controller enrolls its own static - 25519 key pair. The public key fingerprint is committed in the parent's - Zone config and pinned in `ZoneLink.spec.childStaticKeyFingerprint` for the - reverse direction. + 25519 key pair. The public key fingerprint is sealed into the selected-parent + allocator binding delivered to the child controller; it is not stored in a + reciprocal parent ZoneLink. - **Child** (sender side): the child's controller enrolls its own static - 25519 key pair. Its public key fingerprint is committed in - `ZoneLink.spec.childStaticKeyFingerprint` on the parent's ZoneLink spec. + 25519 key pair. Its public key fingerprint is committed in the child-local + `ZoneLink.spec.childStaticKeyFingerprint` and verified by the parent + allocator when it admits the session. The enrollment record carries the static public keys in an opaque, bounded format. No private key material enters any resource spec, status, bundle diff --git a/docs/specs/providers/ADR-046-provider-volume-virtiofs.md b/docs/specs/providers/ADR-046-provider-volume-virtiofs.md index 1a06b99b9..bd527e37f 100644 --- a/docs/specs/providers/ADR-046-provider-volume-virtiofs.md +++ b/docs/specs/providers/ADR-046-provider-volume-virtiofs.md @@ -1032,8 +1032,8 @@ no host paths, no socket paths, no guest names beyond a stable opaque digest. The `zone` and `execution` resource attributes are set at the OTEL resource level (from the Process resource context) and are not repeated as metric labels. -VM name / Guest name is never a metric label. It may appear in OTEL trace context resource -attributes only, re-stamped at ingress boundary. +VM name / Guest name is never a metric label or span attribute. It may appear +only in bounded OTEL resource attributes, re-stamped at the ingress boundary. ### 15.3 Key metrics From ac98963b535b722c7fb71ccbe59b077140799e98 Mon Sep 17 00:00:00 2001 From: John Vicondoa Date: Fri, 24 Jul 2026 07:40:56 -0700 Subject: [PATCH 092/115] docs: define sealing-key rotation contract --- .../ADR-046-provider-volume-local.md | 310 +++++++++++++++--- 1 file changed, 272 insertions(+), 38 deletions(-) diff --git a/docs/specs/providers/ADR-046-provider-volume-local.md b/docs/specs/providers/ADR-046-provider-volume-local.md index e029136b7..5079b4a4b 100644 --- a/docs/specs/providers/ADR-046-provider-volume-local.md +++ b/docs/specs/providers/ADR-046-provider-volume-local.md @@ -71,7 +71,7 @@ The v3 target name appears in parentheses or an explicit mapping. | Reconciled ResourceType | `Volume` — exported as volume-local's primary ResourceType; reconciles physical state (layout, ACL, quota, identity marker) for all assigned Volumes; operators create/delete ordinary Volumes via Resource API; **core ProviderDeployment** creates/deletes component state Volumes before/after component Processes; volume-local never issues Volume create/delete API calls; component Processes consume their required view only | | Source kinds | `local-path`, `block-image`, `tmpfs` | | Controller component | `volume-local-controller`; `Process` under `Host/host-system`, `domain: system`; `controllerExecutionRef: Host/host-system` | -| Effect operations | `ProvisionLayoutEntry`, `RepairLayoutEntry`, `CleanupLayoutEntry`, `PrepareSwtpmDir`, `StoreSyncComplete`, `MountTmpfs`, `ProvisionBlockImage` (all via injected `VolumeEffectPort`; no direct broker connection) | +| Effect operations | `ProvisionLayoutEntry`, `RepairLayoutEntry`, `CleanupLayoutEntry`, `PrepareSwtpmDir`, `StoreSyncComplete`, `MountTmpfs`, `ProvisionBlockImage`, `RotateSealingKey` (all via injected `VolumeEffectPort`; no direct broker connection) | | Permissions | No special host-path permission; all host path resolution in core/broker adapter; broker ops not called directly from Provider process | | ProviderStateSet | Optional query-time logical grouping (not a ResourceType): `{ v : Volume \| ownerRef == "Provider/volume-local" }`; **empty** — volume-local declares no state Volume of its own (its bounded non-secret operational state lives in `status`/the core Operation ledger, D087). Volume-local is the **sole reconciler** for all assigned Volumes carrying `providerRef: Provider/volume-local` (operator-created Volumes and other Providers' *declared* state Volumes; Volume is its exported type) and never issues Volume create/delete API calls; **core ProviderDeployment** creates/deletes other Providers' declared state Volume instances before/after their component Processes; a declared component state Volume is created only when its payload passes the storage-need test; Nix-preprovisioned `User/` layout principals; no cross-component sharing; no empty identity-only Volume | | Finalizers | `volume-local/layout` | @@ -880,12 +880,66 @@ pub struct SourcePolicyId(pub(crate) String); pub struct LayoutEntryId(pub(crate) String); pub struct UserId(pub(crate) String); // resolves from User/ resource pub struct ViewId(pub(crate) String); // bounded view name; max 63 chars +pub struct SealingPolicyId(pub(crate) String); impl fmt::Debug for VolumeId { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { f.write_str("VolumeId([redacted])") } } impl fmt::Debug for SourcePolicyId{ fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { f.write_str("SourcePolicyId([redacted])") } } impl fmt::Debug for LayoutEntryId { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { f.write_str("LayoutEntryId([redacted])") } } impl fmt::Debug for UserId { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { f.write_str("UserId([redacted])") } } impl fmt::Debug for ViewId { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { f.write_str("ViewId([redacted])") } } +impl fmt::Debug for SealingPolicyId { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { f.write_str("SealingPolicyId([redacted])") } } + +/// Closed, deny-unknown request. OperationId is the opaque identifier from the +/// committed Resource operation; none of these fields contains key bytes, +/// credential bytes, a host path, or a broker-resolved key handle. +#[derive(Serialize, Deserialize)] +#[serde(deny_unknown_fields)] +pub struct RotateSealingKeyRequest { + pub volume: VolumeId, + pub policy: SealingPolicyId, + pub expected_volume_generation: u64, + pub expected_resource_revision: u64, + pub expected_current_key_generation: u64, + pub target_key_generation: u64, + pub operation_id: OperationId, +} + +pub enum RotateSealingKeyDisposition { + Rotated, + AlreadyCommitted, + RecoveredCommitted, +} + +/// Returned only after the target generation is active, the previous +/// generation is retired, and the success audit record is durable. +pub struct RotateSealingKeyResult { + pub disposition: RotateSealingKeyDisposition, + pub volume_generation: u64, + pub active_key_generation: u64, +} + +pub enum RotationPrecondition { + VolumeGeneration, + ResourceRevision, + PolicyBinding, + CurrentKeyGeneration, + TargetKeyGeneration, +} + +/// All variants have bounded, path-free wire encodings and redacted Debug. +pub enum RotateSealingKeyError { + Unauthorized, + PreconditionFailed { precondition: RotationPrecondition }, + TargetKeyUnavailable { retry_after_ms: Option }, + TargetKeyRevoked, + RotationConflict, + IdempotencyConflict, + IntegrityViolation, + ResourceExhausted, + BackendUnavailable, + DeadlineExceeded, + CommitPendingAudit, +} /// Opaque mount authorization handle. Contains no OwnedFd visible to the /// Provider. Core routes the anchored FD directly from the EffectPort adapter @@ -947,6 +1001,11 @@ pub trait VolumeEffectPort: Send + Sync + 'static { async fn run_store_sync(&self, vol: VolumeId, generation: u64) -> Result; + /// Atomically rewrap all sealed StateEnvelopes from the expected key + /// generation to the target generation through the closed broker operation. + async fn rotate_sealing_key(&self, request: RotateSealingKeyRequest) + -> Result; + /// Request a VolumeMountToken for delivery to a Process supervisor. /// Returns an opaque authorization handle; the anchored FD is routed /// directly by core from the adapter to the target ProviderSupervisor @@ -977,7 +1036,11 @@ The core/broker adapter implementing this trait: target ProviderSupervisor out-of-band; the FD is never returned to the Provider process. The `VolumeMountToken` returned to the controller is an opaque authorization/correlation handle only. -7. Emits path-free audit records naming only the Volume UID, entry type, and +7. Maps `rotate_sealing_key` one-to-one to the planned closed broker operation + `RotateSealingKey`. The adapter and broker independently resolve and authorize + the opaque `VolumeId` and `SealingPolicyId`; neither wire carries key bytes, + credential bytes, key handles, or paths. +8. Emits path-free audit records naming only the Volume UID, entry type, and action class; audit is broker/core-owned and is never atomic with any redb write. @@ -995,6 +1058,7 @@ UID. Every path is derived by the adapter from opaque IDs via the private bundle | `StoreSyncComplete` | Complete a hardlink farm sync cycle; acquire OFD lock, build farm, release | Volume UID, generation number | | `MountTmpfs` | Mount or unmount a tmpfs at the Volume root with quota limits | Volume UID, action: `mount` \| `umount` | | `ProvisionBlockImage` | Create or verify an image file at declared size | Volume UID, action: `create` \| `verify` | +| `RotateSealingKey` | Atomically rewrap sealed envelopes through the one-to-one planned closed broker operation `RotateSealingKey`; request contains only opaque Volume/policy IDs and committed generation/precondition fields | Volume UID, policy ID digest, from/to generation, operation ID digest, result class | | `OpenVolumeMountToken` | Open a Volume-root FD and route it directly to the target ProviderSupervisor; return opaque `VolumeMountToken` handle (no FD in Provider) | Volume UID, view ID, access class | | `AnchorMarkerRoot` | Ensure the `volume-local-markers/` root exists and is correctly owned | action class | | `WriteVolumeMarker` | Write the identity marker for a Volume (called at first provision) | Volume UID | @@ -1398,28 +1462,152 @@ true` requires a `sealingCredentialRef`: sealingCredentialRef: Credential/example-provider-state-key ``` -The referenced Credential must be `Ready` before the Volume is provisioned. The -controller: +The referenced Credential must be `Ready` before the Volume is provisioned. +The controller observes only Credential identity, readiness, and generation. It +never reads a Credential lease or obtains envelope-key material. The admitted +Volume binds `sealingCredentialRef` to an opaque `SealingPolicyId`; the trusted +core/broker adapter resolves that policy and performs initial wrapping. Raw keys +remain inside the Credential authority and closed broker operation. + +### Canonical sealing-key rotation + +Credential generation advance and a sealing-policy change both use only +`VolumeEffectPort::rotate_sealing_key(RotateSealingKeyRequest)`. There is no +direct Credential-lease read, filesystem rewrite, generic broker call, or +Provider-owned `volume-sealing-rotation-worker` fallback. + +The request fields and preconditions are exact: + +- `volume` must resolve to the admitted Volume UID, still assigned to + `Provider/volume-local` in the caller's Zone. +- `policy` must be the `SealingPolicyId` bound to the Volume's admitted + `sealingCredentialRef`; a caller cannot substitute another policy. +- `expected_volume_generation` must match the desired-object generation that + caused reconcile. `expected_resource_revision` must be the new revision + returned by the committed status-first mutation described below. +- `expected_current_key_generation` must match the authenticated sealing marker + currently active on disk. `target_key_generation` must be greater, `Ready`, + permitted by the policy, and not revoked. +- `operation_id` must be the one in the corresponding + `CommittedRevisionProof`. It is opaque and persisted by the core Operation + ledger, not invented by the Provider. + +Before any external effect, the controller commits, with expected revision, a +status update containing `sealingStatus: rotation-pending`, +`sealingKeyGeneration: `, and +`sealingRotation: { fromGeneration, toGeneration }`, plus a +`SealingReady=False/rotation-pending` condition at the Volume generation. Only +the proof for that committed status revision permits the adapter call. A status +conflict causes re-read and re-plan without an effect. This status-first rule +applies equally to normal reconcile and `execute_upgrade`. + +The adapter authorizes the controller ComponentSession principal for the closed +`volume.rotate-sealing-key` capability, checks same-Zone ownership and +`providerRef`, verifies the committed revision proof, and then maps the method +one-to-one to the planned closed broker operation `RotateSealingKey`. The broker +independently repeats the capability, Volume/policy binding, generation, and +revision checks against its signed private policy table. The broker request +contains only opaque `VolumeId`, opaque `SealingPolicyId`, generation/revision +preconditions, opaque `operation_id`, and the derived idempotency key. No key +bytes, Credential bytes, KDF parameters, host paths, relative paths, key +handles, or caller-selected file descriptors cross either boundary. + +The idempotency key is: -1. Reads the Credential lease to obtain envelope encryption key material. -2. Wraps each `StateEnvelope` under the envelope key before writing. -3. Never stores raw key material on disk. -4. Sets `sealingStatus: sealed` in Volume status. - -Key rotation on Credential generation change: +```text +SHA-256( + "d2b.volume.rotate-sealing-key.v1" || + volume-uid || sealing-policy-id || + expected-volume-generation || expected-resource-revision || + expected-current-key-generation || target-key-generation || + operation-id +) +``` -1. Controller detects `Credential.status.observedGeneration` advance. -2. Sets `sealingStatus: rotation-pending`. -3. Sends `RotateSealingKey` effect op to `VolumeEffectPort`; the adapter re-encrypts layout data under the new key material inside a bounded blocking thread pool call. -4. On effect op success: sets `sealingStatus: sealed`. -5. On effect op failure: sets `sealingStatus: rotation-failed`; Volume remains readable - under old key until operator resolves. +Fields use their canonical length-prefixed wire encodings. Core derives the key; +the adapter and broker recompute it. Reusing a key with different request bytes +is `IdempotencyConflict`. A retry of byte-identical request fields returns +`AlreadyCommitted` or `RecoveredCommitted` without another rewrite or duplicate +success audit. A distinct in-flight request for the same Volume is +`RotationConflict`; rotation is single-flight per Volume. -No `volume-sealing-rotation-worker` EphemeralProcess is created; the key -rotation is handled synchronously in the core/broker adapter via `VolumeEffectPort`. +Result dispositions have one meaning: -No raw credential bytes, key material, or KDF parameters enter Volume status, -audit records, OTEL spans, or log output at any cardinality. +| Disposition | Meaning | +| --- | --- | +| `Rotated` | This call performed and committed the fresh rotation | +| `AlreadyCommitted` | The broker dedup record already proved the same request, retired prior generation, and durable success audit | +| `RecoveredCommitted` | Crash recovery proved/switched the target, completed retirement and the missing durable audit, if any | + +All three return the matching Volume generation and active target key +generation; no partial or merely staged state returns success. + +The broker uses an anchored, fsync'd rotation journal keyed by the idempotency +key and a staged sealing generation: + +1. `Prepared`: authorize and lease the expected and target policy generations; + persist only their opaque policy/generation references and request digest. +2. `Staged`: rewrap every sealed `StateEnvelope` into an anchored staging + generation, verify every authentication tag, then fsync files and directory. +3. `Committed`: atomically switch authenticated sealing metadata to the target + generation and fsync its parent. Readers see either the complete old + generation or the complete target generation, never a mixed generation. +4. `Audited`: append and durably commit the exactly-once broker success audit, + then retire the previous generation's staged data and complete the journal. + +No journal contains key material or paths. A crash before the metadata switch +discards or resumes staging while the old generation remains authoritative. A +crash after the switch is roll-forward only: startup verifies the authenticated +target marker, completes the missing success audit under the same idempotency +key, retires the old generation, and returns `RecoveredCommitted`. The broker +never switches back to the old generation. If the audit sink is unavailable +after the data commit, it returns retryable `CommitPendingAudit`; the controller +keeps `rotation-pending` and retries the identical request until recovery and +audit complete. + +`Rotated`, `AlreadyCommitted`, and `RecoveredCommitted` results are returned +only when the requested Volume generation still matches, the target generation +is active, the prior generation is retired, and the success audit is durable. +The controller then atomically writes `sealingStatus: sealed`, advances +`sealingKeyGeneration`, clears `sealingRotation`, and sets +`SealingReady=True/rotation-complete`. A generation/revision/policy +precondition error causes re-read/re-plan and leaves the status pending until +the new plan is committed. `TargetKeyUnavailable`, `ResourceExhausted`, +`BackendUnavailable`, `DeadlineExceeded`, and `CommitPendingAudit` are retryable +with the identical request and bounded exponential backoff with full jitter +(250 ms initial, 30 s cap). `Unauthorized`, `TargetKeyRevoked`, +`RotationConflict`, `IdempotencyConflict`, and `IntegrityViolation` are not +blindly retried; the controller records `rotation-failed` and a bounded, +path-free reason. Revocation or a new committed Resource generation requires a +new plan and operation ID. + +| Typed error | Retry semantics | Controller/status action | +| --- | --- | --- | +| `Unauthorized` | Never retry automatically | `rotation-failed`; require policy/authorization correction | +| `PreconditionFailed` | Do not retry the same request | Re-read; commit a new plan or clear a stale pending plan | +| `TargetKeyUnavailable` | Retry identical request after hint/backoff | Keep `rotation-pending` | +| `TargetKeyRevoked` | Never retry that target | `rotation-failed`; require a newer admitted target | +| `RotationConflict` | Do not race or replace in-flight work | `rotation-failed`; re-read broker/resource state | +| `IdempotencyConflict` | Never retry | `rotation-failed`; integrity/operator investigation | +| `IntegrityViolation` | Never retry or auto-repair | Volume `Failed`; preserve evidence | +| `ResourceExhausted` | Retry identical request with backoff | Keep `rotation-pending` | +| `BackendUnavailable` | Retry identical request with backoff | Keep `rotation-pending` | +| `DeadlineExceeded` | Commit outcome is unknown; retry identical request | Keep `rotation-pending` | +| `CommitPendingAudit` | Data is committed; retry identical request until audit completes | Keep `rotation-pending`; do not report success | + +On controller or daemon restart, the startup relist finds +`rotation-pending`, obtains the original operation ID/request fingerprint from +the core Operation ledger, and retries the identical typed request before any +new rotation plan. If status says `sealed` but the broker reports a different +active generation, reconcile sets `SealingReady=False/sealing-generation-drift` +and fails closed rather than issuing an implicit rewrite. + +The controller emits path-free start/failure/commit lifecycle audit events. The +broker emits one durable `RotateSealingKey` success record containing only +Volume UID, policy ID digest, from/to generations, operation ID digest, +idempotency-key digest, and result class. No raw credential bytes, key material, +KDF parameters, data counts/sizes, paths, or content enter status, audit, OTEL, +errors, `Debug`, or logs at any cardinality. --- @@ -1605,6 +1793,14 @@ Volume reconciliation follows `ADR-046-resource-reconciliation`: 6. On attachment create, volume-virtiofs receives `owned-resource-changed` from the Volume. +Credential watches join the same per-Volume single-flight. An observed sealing +generation advance first commits the `rotation-pending` status transition, then +calls the typed `rotate_sealing_key` method with the resulting committed proof. +Restart, timeout, cancellation, and retry reuse the operation ID/request +fingerprint from the core Operation ledger. Reconcile never obtains keys or +performs a direct rewrite, and it never starts a newer rotation while an older +status-first operation is pending. + Owner triggers: every Volume spec/status/finalizer mutation produces an `owned-resource-changed` hint for the Volume's `ownerRef` (typically a Guest). @@ -1744,6 +1940,16 @@ only the controller `Process`, and `Replace` of a Volume row is allowed only with explicit ownership/state transfer. No raw host path or secret enters `status.update`. +A Credential or sealing-policy generation change is a non-disruptive, +state-preserving upgrade step, but not an in-place implicit rewrite: +`plan_upgrade` records the expected current/target sealing generations and +`execute_upgrade` commits `rotation-pending` status before invoking the same +typed `VolumeEffectPort::rotate_sealing_key` operation used by reconcile. An +upgrade completes only after its result and durable broker audit are reflected +in `sealed` status. Restart resumes the original idempotency key; a changed +Resource generation causes re-plan rather than reuse under different +preconditions. + D090 expedited `waitForReconcile` on `Create`/`UpdateSpec`/`Delete` performs no external effect, finalizer change, or status mutation until Core supplies `CommittedRevisionProof {resourceUid,generation,revision,operationId}`. The @@ -1770,6 +1976,10 @@ status: status: "True" reason: all-attachments-ready observedGeneration: 1 + - type: SealingReady + status: "True" + reason: rotation-complete + observedGeneration: 1 resource: layoutPhase: Ready # Pending | Ready | Degraded | Failed layoutConditions: [] # per-entry: EntryMissing | EntryDrift | EntryQuarantined | InvariantViolated | ForeignAclViolation @@ -1777,6 +1987,8 @@ status: stateSchemaPhase: current markerStatus: verified sealingStatus: sealed + sealingKeyGeneration: 2 + sealingRotation: null quotaUsage: { usedBytes: 0, inodeCount: 0 } provider: providerRef: Provider/volume-local @@ -1800,7 +2012,9 @@ carry raw host paths, secret bytes, or unbounded records. | `stateSchemaPhase` | `current`, `migration-required`, `migrating`, `migration-committed`, `migration-failed` | Blocks pre-launch Processes when not `current` | | `installedSchemaVersion` | semver string or null | Version on disk at last verified marker read | | `markerStatus` | `verified`, `missing`, `replaced`, `tampered`, `unknown` | Non-`verified` blocks pre-launch Processes | -| `sealingStatus` | `none`, `sealed`, `rotation-pending`, `rotation-failed` | | +| `sealingStatus` | `none`, `sealed`, `rotation-pending`, `rotation-failed` | The committed status-first gate for `rotate_sealing_key`; `rotation-pending` is durable before any effect | +| `sealingKeyGeneration` | integer or null | Last broker-confirmed active generation; never inferred only from Credential status | +| `sealingRotation` | `{ fromGeneration, toGeneration }` or null | Safe restart/reconcile projection; operation ID and idempotency key remain only in the core Operation ledger | | `quotaUsage` | `{ usedBytes: N, inodeCount: N }` or null | Polled at max 60 s intervals | | `lastMigrationAt` | RFC 3339 UTC timestamp or null | | | `snapshots` | `SnapshotRecord[]` | Bounded list; each record: `id` (opaque), `createdAt`, `schemaVersion`, `sizeBytes`, `trigger`, `phase` | @@ -1827,6 +2041,15 @@ carry raw host paths, secret bytes, or unbounded records. | `entry-quarantined` | Adoption ambiguity | Degraded | | `volume-local/layout` finalizer timeout | Controller exceeded `maxFinalizerDurationSeconds` | Degraded/finalizer-timeout | | `marker-tampered` | Marker HMAC validation failed | Failed | +| `sealing-rotation-unauthorized` | Caller lacks the closed `volume.rotate-sealing-key` capability or Volume/policy binding | rotation-failed | +| `sealing-rotation-precondition` | Volume generation, Resource revision, policy binding, or active/target key generation no longer matches | rotation-pending; re-read/re-plan | +| `sealing-target-unavailable` | Target policy generation is not yet available | rotation-pending; retry same request | +| `sealing-target-revoked` | Target policy generation is revoked | rotation-failed | +| `sealing-rotation-conflict` | A distinct rotation is already in flight for the Volume | rotation-failed | +| `sealing-idempotency-conflict` | One idempotency key is presented with different canonical request bytes | rotation-failed | +| `sealing-integrity-violation` | Existing envelope, marker, staging generation, or journal fails authentication | Failed | +| `sealing-rotation-retryable` | Resource exhaustion, backend unavailability, or deadline before known commit | rotation-pending; retry same request | +| `sealing-audit-pending` | Target committed but durable success audit is not yet confirmed | rotation-pending; retry same request | All error messages are bounded (512 bytes), UTF-8/control-character validated, and must not contain host paths, secret content, process data, or terminal bytes. @@ -1850,8 +2073,9 @@ and must not contain host paths, secret content, process data, or terminal bytes | `volume-relocation-committed` | Relocation complete | zone, volume-ref, to-execution-ref | | `volume-incident-hold-set` | `IncidentHold` condition added | zone, volume-ref, actor-digest | | `volume-incident-hold-cleared` | `IncidentHold` condition removed | zone, volume-ref, actor-digest | -| `volume-sealing-rotation-start` | Credential rotation triggered | zone, volume-ref | -| `volume-sealing-rotation-committed` | Credential rotation complete | zone, volume-ref | +| `volume-sealing-rotation-start` | Status-first rotation-pending transition committed | zone, volume-ref, from-generation, to-generation, operation-id-digest | +| `volume-sealing-rotation-failed` | Typed rotation returns a terminal error | zone, volume-ref, from-generation, to-generation, bounded-reason | +| `volume-sealing-rotation-committed` | Typed result and broker success audit confirmed | zone, volume-ref, from-generation, to-generation, result-class | | `volume-destroyed` | Volume fully destroyed | zone, volume-ref, schemaId | | `volume-marker-check` | Marker verification result | zone, volume-ref, result-class | | `volume-quota-exceeded` | Write rejected due to quota | zone, volume-ref | @@ -1861,6 +2085,7 @@ and must not contain host paths, secret content, process data, or terminal bytes | `RepairLayoutEntry` (broker) | Entry repaired | Volume UID, entry type, repair action class | | `CleanupLayoutEntry` (broker) | Entry removed | Volume UID, entry type, cleanup trigger | | `StoreSyncComplete` (broker) | Store sync complete | Volume UID, generation number | +| `RotateSealingKey` (broker) | Rotation committed/recovered and success audit made durable | Volume UID, policy ID digest, from/to generation, operation ID digest, idempotency-key digest, result-class | ### Excluded from all audit records @@ -2144,7 +2369,7 @@ Required modules: | `src/marker.rs` | Identity marker write/verify/check; HMAC generation and validation; fail-closed detection | | `src/quota.rs` | Quota enforcement; `statfs` polling; write-reject gate | | `src/migration.rs` | Pre-launch and online migration dispatch; staging Volume lifecycle; cross-component prepare/commit/rollback | -| `src/sealing.rs` | Envelope encryption on write; `sealingStatus` transitions; `RotateSealingKey` effect op dispatch; no EphemeralProcess worker | +| `src/sealing.rs` | Status-first sealing state machine; constructs canonical `RotateSealingKeyRequest`; dispatches only `VolumeEffectPort::rotate_sealing_key`; classifies typed result/errors and retry; no key lease, direct rewrite, generic broker call, or EphemeralProcess worker | | `src/snapshot.rs` | Snapshot EphemeralProcess dispatch; `.snapshots/` subtree; `snapshotPolicy` enforcement | | `src/relocation.rs` | Relocation EphemeralProcess dispatch; source finalizer; anchored copy; commit/failure handling | | `src/store_view.rs` | Store-view Volume specifics: hardlink farm layout, private-NS sync, generation meta, gcroots, `sync.lock` | @@ -2185,7 +2410,7 @@ Required test files and minimum coverage: | `tests/view_rights.rs` | Mount with rights subset of View: admitted; mount with extra right: `volume-view-rights-exceeded`; `read-write` access on `read-only` View: rejected; single-writer constraint: second `read-write` rejected | | `tests/state.rs` | Ported `d2b-state/tests/state.rs` scenarios under v3 StateEnvelope: atomic write, fsync ordering, crash between rename steps, OFD lock acquire/release, quarantine record, generation bound, state-envelope digest | | `tests/migration_unit.rs` | Pre-launch migration dispatch; staging Volume create/destroy; EphemeralProcess succeeded → commit; EphemeralProcess failed → rollback; N-Volume cross-component prepare/commit/rollback protocol; roll-forward on restart detection | -| `tests/sealing_unit.rs` | Seal on write; read sealed payload; rotation state machine via `RotateSealingKey` effect op: `rotation-pending` → op success → `sealed`; op failure → `rotation-failed`; no raw key in any output; no EphemeralProcess dispatch | +| `tests/sealing_unit.rs` | Initial seal/read without exposing a key lease; status CAS commits `rotation-pending` before the first effect; exact request fields and generation/revision/policy preconditions; operation ID comes from committed proof; deterministic idempotency vector; byte-identical timeout/restart retry; duplicate success → `AlreadyCommitted`; recovered commit → `RecoveredCommitted`; changed bytes under one key → `IdempotencyConflict`; concurrent different rotation → `RotationConflict`; retryable/terminal error table; new generation re-plan; success → `sealed`; integrity failure → Failed; no key/path/handle in DTO, status, error, Debug, log, audit, or OTEL; no direct rewrite/generic broker/EphemeralProcess dispatch | | `tests/snapshot_unit.rs` | `snapshotPolicy` enforcement; retention count; retention TTL; `triggerOnMigration` auto-snapshot; snapshot EphemeralProcess dispatch; list in Volume status | | `tests/relocation_unit.rs` | Finalizer set; EphemeralProcess created; commit: source deleted; failure: source retained; state machine round-trip | | `tests/audit_unit.rs` | Golden audit record for each event kind; no paths in any record; no credential material; structural OTEL label-policy check with exact absence of `vm`, `zone`, `zone_id`, `zone_uid`, and resource-name-derived keys | @@ -2209,7 +2434,7 @@ Required files: | `integration/swtpm_marker.rs` | Real broker-maintained marker: provision, restart verify, inject `st_ino` mismatch → Failed; remove marker → Failed; marker HMAC tampered → Failed; ancestor traverse ACL applied to real inode | | `integration/block_image.rs` | Block-image lifecycle: create raw image at size, verify `fallocate` when `preallocate: true`, FD transfer to fake Guest runtime process, cleanup removes image file | | `integration/migration.rs` | Real Host crash-injection at each migration step (OS-level `SIGKILL` between `rename` steps); roll-forward on restart; N-Volume cross-component coordination with real staging Volume; staging orphan GC after Provider removal | -| `integration/sealing.rs` | Real Credential lease flow: seal on write, read sealed payload, key rotation via `RotateSealingKey` effect op with live Credential Provider, `rotation-failed` on revoked credential | +| `integration/sealing.rs` | Live Credential authority plus real adapter and planned closed broker `RotateSealingKey`: Provider never receives a lease; same-Zone capability/policy authorization and denials; crash injection at Prepared, Staged, metadata switch, audit append, and retirement; old-or-target atomic visibility; roll-forward recovery; exactly-once success audit; revoked/unavailable target behavior; controller restart resumes pending operation with identical idempotency key; precondition drift re-plans; request/broker journal/audit contain no key bytes or paths | | `integration/snapshot.rs` | Real Host filesystem snapshot byte-equality verification; retention expiry removes snapshot directory; pre-migration auto-snapshot with interrupted migration; snapshot list in status | | `integration/relocation.rs` | Real Host-to-Host anchored file copy; crash at copy midpoint → source preserved; successful relocation → source deleted; virtiofsd source re-point after relocation (via volume-virtiofs stub) | | `integration/domain_isolation.rs` | Cross-process domain-isolation rejection: two fake Processes in different domains attempt same Volume mount; `volume-domain-mismatch` returned | @@ -2291,11 +2516,11 @@ Documents: | Depends on | `ADR046-pstate-001` (VolumeStateSchema/PersistenceClass/SensitivityClass/StateEnvelope in `d2b-contracts/src/v3/volume_state.rs`) | | Current source | `d2b-core/src/storage.rs` (`StoragePathSpec`, `StoragePathKind`, policy enums); `d2b-core/src/sync.rs` (`SyncJson`, `LockSpec`) | | Reuse action | adapt | -| Destination | `d2b-contracts/src/v3/volume_layout.rs` (LayoutEntry, EntryType, all policy enums, AclGrant, Invariant, SensitivityClass); `d2b-contracts/src/v3/volume_spec.rs` (VolumeSpec, ViewSpec, Attachment, QuotaSpec, SourceKind, `SourcePolicyId` opaque newtype); `d2b-contracts/src/v3/effect_port.rs` (`VolumeEffectPort` trait, opaque ID newtypes `VolumeId`/`LayoutEntryId`/`UserId`/`ViewId` each with custom redacted Debug, and `VolumeMountToken` opaque handle with custom redacted Debug) | -| Detailed design | All LayoutEntry fields as documented in this dossier; enum value names preserved from `StoragePathKind`/policy enums with renames where noted; `User/` ACL principal (no numeric UID); `sourcePolicyId` opaque newtype replaces raw `hostPath` in `SourceKind::LocalPath` and `SourceKind::BlockImage` Primary reuse disposition: `adapt`. Preserved source-plan detail: extract and adapt. | +| Destination | `d2b-contracts/src/v3/volume_layout.rs` (LayoutEntry, EntryType, all policy enums, AclGrant, Invariant, SensitivityClass); `d2b-contracts/src/v3/volume_spec.rs` (VolumeSpec, ViewSpec, Attachment, QuotaSpec, SourceKind, `SourcePolicyId` opaque newtype); `d2b-contracts/src/v3/effect_port.rs` (`VolumeEffectPort` trait, opaque ID newtypes `VolumeId`/`LayoutEntryId`/`UserId`/`ViewId`/`SealingPolicyId` each with custom redacted Debug, `VolumeMountToken`, and canonical `RotateSealingKeyRequest`/`Result`/`Error` types) | +| Detailed design | All LayoutEntry fields as documented in this dossier; enum value names preserved from `StoragePathKind`/policy enums with renames where noted; `User/` ACL principal (no numeric UID); `sourcePolicyId` opaque newtype replaces raw `hostPath` in `SourceKind::LocalPath` and `SourceKind::BlockImage`; deny-unknown sealing-rotation request contains only opaque Volume/policy/operation IDs and generation/revision preconditions, with no key bytes/path/handle Primary reuse disposition: `adapt`. Preserved source-plan detail: extract and adapt. | | Integration | Volume spec and status structs; Provider descriptor component stateNamespace; Nix resource compiler schema validation | | Data migration | Full v3 reset; no row-level import | -| Validation | Schema golden vectors; round-trip serde; ACL principal validation rejects numeric forms; `sourcePolicyId` present; no `hostPath` field in any volume_spec contract | +| Validation | Schema golden vectors; round-trip serde; ACL principal validation rejects numeric forms; `sourcePolicyId` present; no `hostPath` field in any volume_spec contract; sealing-rotation request deny-unknown/round-trip and redacted-Debug tests; compile-time trait conformance includes `rotate_sealing_key` | | Removal proof | `d2b-core/src/storage.rs` StoragePathSpec/policy enums removed only after all Provider descriptor consumers are on v3 Volume spec | ### ADR046-vl-002 — Crate scaffold and filesystem primitives @@ -2382,11 +2607,11 @@ Documents: | Dependency/owner | ADR046-vl-002; ADR046-vl-003; ADR046-pstate-004 through ADR046-pstate-006 | | Current source | `d2b-state/src/atomic.rs` (main); no existing migration/snapshot infrastructure in v3 | | Reuse action | adapt | -| Destination | `src/{migration,snapshot}.rs`; `tests/{migration_unit,snapshot_unit}.rs`; `integration/{migration,snapshot}.rs` | -| Detailed design | Schema migration (see §Schema migration) via `volume-migration-worker` EphemeralProcess; Snapshot create/list/expire (see §Snapshots) via `volume-snapshot-worker` EphemeralProcess; controller reports `stateSchemaPhase` and `snapshots` in Volume status. Sealing lifecycle (`sealingStatus`, key-shred) is a core/framework concern dispatched through `VolumeEffectPort` semantic ops, not a Provider-owned EphemeralProcess worker. | -| Integration | Controller's reconcile handler dispatches EphemeralProcess via d2b-bus `ResourceClient`; volume-local reports `stateSchemaPhase`, `snapshots` in Volume status | +| Destination | `src/{migration,snapshot,sealing}.rs`; `tests/{migration_unit,snapshot_unit,sealing_unit}.rs`; `integration/{migration,snapshot,sealing}.rs` | +| Detailed design | Schema migration (see §Schema migration) via `volume-migration-worker` EphemeralProcess; Snapshot create/list/expire (see §Snapshots) via `volume-snapshot-worker` EphemeralProcess; controller reports `stateSchemaPhase` and `snapshots` in Volume status. Sealing lifecycle uses the status-first `rotation-pending` transition and only canonical `VolumeEffectPort::rotate_sealing_key`; it persists/resumes the core Operation-ledger fingerprint, classifies exact typed errors, and has no key lease, direct rewrite, generic broker call, or Provider-owned EphemeralProcess worker. | +| Integration | Controller's reconcile handler dispatches migration/snapshot EphemeralProcess via d2b-bus `ResourceClient`; sealing reconcile/upgrade commits status before effect and maps typed results to `sealed`/`rotation-failed`; volume-local reports state schema, snapshots, and safe sealing generations in Volume status | | Data migration | None (new protocol) | -| Validation | All `tests/migration_unit.rs`, `tests/snapshot_unit.rs`, `integration/migration.rs`, `integration/snapshot.rs` scenarios | +| Validation | All `tests/migration_unit.rs`, `tests/snapshot_unit.rs`, `tests/sealing_unit.rs`, `integration/migration.rs`, `integration/snapshot.rs`, and `integration/sealing.rs` scenarios, including restart/idempotency and status-before-effect assertions | | Removal proof | Not applicable (new) | ### ADR046-vl-008 — Relocation, retention, incident hold, unclaimed GC, destruction @@ -2413,10 +2638,10 @@ Documents: | Current source | `d2b-state/src/audit.rs` (main `6faa5256`); OTEL cardinality model from `d2b-provider-observability-local/src/` (main `a1cc0b2d`) | | Reuse action | adapt | | Destination | `src/audit.rs`; `src/otel.rs`; `src/error.rs`; `tests/audit_unit.rs`; `integration/audit.rs` | -| Detailed design | Event types and Zone audit emission per §Audit events; OTEL metric definitions per §OTEL metrics with closed semantic labels and no Zone/resource-name-derived dimensions; Zone identity remains in `d2b.zone` resource attributes; error catalog per §Error catalog; no-path invariant enforced in all outputs | +| Detailed design | Event types and Zone audit emission per §Audit events, including controller rotation start/failure/commit and exactly-once broker `RotateSealingKey` success; OTEL metric definitions per §OTEL metrics with closed semantic labels and no Zone/resource-name-derived dimensions; Zone identity remains in `d2b.zone` resource attributes; error catalog per §Error catalog; no-path/no-key invariant enforced in all outputs | | Integration | Every lifecycle transition calls `audit::emit_volume_event`; OTEL metrics exported via `observability-otel` Provider | | Data migration | None — full d2b 3.0 reset; no prior state to migrate | -| Validation | `tests/audit_unit.rs` golden records and structural metric descriptor assertions for exact absence of `vm`, `zone`, `zone_id`, `zone_uid`, and resource-name-derived keys plus resource-name canary absence; `tests/error_messages.rs` bounded messages; `integration/audit.rs` live stream | +| Validation | `tests/audit_unit.rs` golden records and structural metric descriptor assertions for exact absence of `vm`, `zone`, `zone_id`, `zone_uid`, and resource-name-derived keys plus resource-name canary absence; rotation audit exact-once/digest-only vectors; `tests/error_messages.rs` bounded messages; `integration/audit.rs` live stream | | Removal proof | Not applicable | ### ADR046-vl-010 — Nix configuration and resource compiler integration @@ -2458,11 +2683,11 @@ Documents: | Depends on | `ADR046-pstate-003`; `ADR-046-provider-model-and-packaging` (generic effect-port injection contract) | | Current source | `d2b-priv-broker/src/ops/{state_dir,storage_contract,swtpm_dir,store_sync,store_view_posture}.rs`; `d2b-host/src/hardlink_farm.rs` | | Reuse action | adapt | -| Destination | `packages/d2b-host/src/volume_effect_adapter.rs` (or the equivalent host-runtime crate designated by the Zone broker owner); implements the `VolumeEffectPort` trait defined in `d2b-contracts` | -| Detailed design | Adapter holds trusted FD table keyed by `VolumeId`; resolves `SourcePolicyId` to host path prefix from private bundle; calls `openat2(RESOLVE_BENEATH)` anchored at retained FD for all FS ops; calls `setfacl`/`acl_set_fd`, `mount`/`umount`, `fallocate` from within adapter only; emits path-free audit records for each op (audit is never atomic with redb write); injected into controller via Zone runtime ComponentSession; blocking filesystem calls run in bounded blocking-thread pool Primary reuse disposition: `adapt`. Preserved source-plan detail: adapt into adapter. | +| Destination | `packages/d2b-host/src/volume_effect_adapter.rs` (or the equivalent host-runtime crate designated by the Zone broker owner), implementing the `VolumeEffectPort` trait defined in `d2b-contracts`; planned `d2b-priv-broker/src/ops/rotate_sealing_key.rs` closed operation | +| Detailed design | Adapter holds trusted FD table keyed by `VolumeId`; resolves `SourcePolicyId` to host path prefix from private bundle; calls `openat2(RESOLVE_BENEATH)` anchored at retained FD for all FS ops; calls `setfacl`/`acl_set_fd`, `mount`/`umount`, `fallocate` from within adapter only; authorizes `volume.rotate-sealing-key`, verifies committed proof, recomputes the canonical idempotency key, and maps `rotate_sealing_key` one-to-one to the closed broker `RotateSealingKey` operation. Broker independently resolves opaque `VolumeId`/`SealingPolicyId`, checks policy/generation/preconditions, performs journaled atomic rewrap and roll-forward recovery, and durably emits exactly one success audit before returning. Neither boundary accepts key bytes, credential bytes, key handles, or paths. Other blocking filesystem calls run in the bounded blocking-thread pool Primary reuse disposition: `adapt`. Preserved source-plan detail: adapt into adapter. | | Integration | Zone runtime creates adapter with required FD table and bundle reference at provider startup; passes `Arc` to controller via ComponentSession bootstrap | | Data migration | None (adapter replaces direct broker-op call sites) | -| Validation | Adapter hermetic tests: each effect op called with mock FD table and bundle; no path in any output; anchored-path rejection for RESOLVE_BENEATH violations; `cargo deny check` verifies adapter does not expose raw paths to Provider crate; `integration/provision.rs` exercises full adapter path | +| Validation | Adapter hermetic tests: each effect op called with mock FD table and bundle; rotation authorization, policy binding, all generation/revision preconditions, canonical idempotency vectors, byte-identical duplicate/retry, different-payload conflict, typed retry classification, and no key/path/handle in wire/Debug/error/audit; broker crash injection at every journal boundary with old-or-target visibility, roll-forward, and exactly-once success audit; anchored-path rejection for RESOLVE_BENEATH violations; `cargo deny check` verifies adapter exposes neither raw paths nor broker implementation to Provider crate; `integration/{provision,sealing}.rs` exercise full adapter paths | | Removal proof | Baseline broker op handlers (`state_dir.rs`, `storage_contract.rs`, `swtpm_dir.rs`, `store_sync.rs`, `store_view_posture.rs`) retired only after Volume controller parity is confirmed and all callers are on the adapter | ### ADR046-vl-013 — No bootstrap-state exception (status-first controller start) @@ -2550,6 +2775,15 @@ Documents: `component-state-not-justified`. There is no empty identity-only state Volume. +13. **Sealing rotation is one closed, status-first effect**: the Provider commits + `rotation-pending` before calling only + `VolumeEffectPort::rotate_sealing_key`; the adapter maps it one-to-one to the + closed broker `RotateSealingKey` operation. Both boundaries authorize and + verify the opaque Volume/policy binding and committed preconditions. Key + bytes, Credential leases, handles, and paths never cross those boundaries; + crash recovery is roll-forward, retries reuse the canonical idempotency key, + and success requires a durable exactly-once broker audit. + --- ## Removal proof table From 5539300e48fa32e8624f1a3197b1e5bebfe1408f Mon Sep 17 00:00:00 2001 From: John Vicondoa Date: Fri, 24 Jul 2026 07:35:36 -0700 Subject: [PATCH 093/115] docs: enforce ADR 0046 telemetry identity policy --- .../ADR-046-telemetry-audit-and-support.md | 76 ++++++++++-- .../ADR-046-provider-device-security-key.md | 25 ++-- .../ADR-046-provider-observability-otel.md | 116 +++++++++++++++--- .../ADR-046-provider-runtime-qemu-media.md | 30 +++-- 4 files changed, 203 insertions(+), 44 deletions(-) diff --git a/docs/specs/ADR-046-telemetry-audit-and-support.md b/docs/specs/ADR-046-telemetry-audit-and-support.md index 9506486e8..7dfd3e3e2 100644 --- a/docs/specs/ADR-046-telemetry-audit-and-support.md +++ b/docs/specs/ADR-046-telemetry-audit-and-support.md @@ -319,6 +319,51 @@ Zone identity remains available as the bounded `d2b.zone` OTEL resource attribute. Removing identity labels MUST NOT remove that resource attribute or the Zone/resource identity fields permitted by the audit contract. +### Collector ingress enforcement + +`METRIC_LABEL_POLICY` is both a descriptor registry and a runtime admission +contract. The `observability-otel` collector MUST run one shared structural +validator over metrics from every ingress: compact frames on the Unix emitter, +OTLP on the private Unix socket, OTLP forwarded over vsock, and the D096 import +stream. Validation runs after bounded decode and trusted OTEL Resource stamping, +but before SDK aggregation, queue insertion, batching, retry, or export. It +parses descriptor labels plus every data-point and exemplar attribute map. + +The whole frame is rejected and dropped when a key is outside +`METRIC_LABEL_POLICY`; is exactly `vm`, `zone`, `zone_id`, `zone_uid`, +`credential_name`, `network`, `network_name`, or `link_name_hash`; ends in +`_name`, `_name_hash`, `_name_digest`, or `_uid`; or when a value equals a +trusted producer/resource `metadata.name`, UID, ResourceRef, or other +resource-identity canary. No adapter may enqueue an unchecked frame, and a +rejected OTLP frame cannot export its otherwise-valid siblings. + +Unix datagrams are dropped without a response. Stream ingress returns only +`invalid-telemetry-frame`; three violations quarantine the connection for at +most 30 seconds and set its credits to zero. Quarantine is in-memory, capped at +64 connections per Binding, and retains only an opaque connection handle, +expiry, ingress class, and the closed error class—not payload, rejected +key/value, or producer/resource identity. Structural validation remains ahead +of queue capacity during exporter backpressure; invalid frames never consume +queue or retry capacity. + +The collector reports only: + +```text +d2b_otel_ingress_policy_total{ + ingress = emitter_unix | otlp_unix | otlp_vsock | import_stream, + outcome = accepted | rejected | quarantined, + error_class = none | key_not_allowlisted | key_forbidden | + key_suffix_forbidden | value_identity | malformed | oversize +} +``` + +These fixed domains are themselves registered by `METRIC_LABEL_POLICY`. +`d2b_telemetry_drop_total` uses only the additional closed reasons +`policy_violation` and `ingress_quarantine`. No metric, span, log, status, +protocol error, or quarantine record echoes a forbidden key or value. Valid +identity remains only in allow-listed OTEL Resource attributes. Authoritative +audit remains a separate writer, payload, and export path. + Note: the current `d2b_daemon_vm_*` metrics in `packages/d2bd/src/metrics.rs` use `vm` labels with VM name values (e.g. labels `["vm", "state"]`, `["vm", "outcome"]`, `["vm", "vmm", "outcome"]`, `["vm", "reason"]`). @@ -487,8 +532,9 @@ Target crates: individual Provider crates (ADR-only). | Metric | Type | Labels | Buckets | | --- | --- | --- | --- | -| `d2b_telemetry_drop_total` | counter | `signal={metric,trace,log}`, `reason={buffer_full,export_error}` | — | +| `d2b_telemetry_drop_total` | counter | `signal={metric,trace,log}`, `reason={buffer_full,export_error,policy_violation,ingress_quarantine}` | — | | `d2b_telemetry_export_total` | counter | `signal`, `outcome={ok,error}` | — | +| `d2b_otel_ingress_policy_total` | counter | `ingress={emitter_unix,otlp_unix,otlp_vsock,import_stream}`, `outcome={accepted,rejected,quarantined}`, `error_class={none,key_not_allowlisted,key_forbidden,key_suffix_forbidden,value_identity,malformed,oversize}` | — | | `d2b_audit_write_total` | counter | `record_class`, `outcome={ok,rate_limited,error}` | — | | `d2b_audit_drop_total` | counter | `record_class={privileged,unprivileged}` | — | @@ -625,6 +671,10 @@ Zone runtime, core-controller, and all other core processes use a - serializes metric increments and span events into compact frames; - writes frames over a private Unix datagram socket to the `observability-otel` Provider process; +- relies on the collector's mandatory structural metric admission at every + Unix-emitter, OTLP-Unix, OTLP/vsock, and import-stream ingress before any + batching or export; emitter-side validation is defense in depth, never a + substitute for the collector gate; - holds a bounded in-process ring (default 4 MiB metrics, 4 MiB traces, 2 MiB logs per process — configurable via the observability-otel Provider spec); - drops oldest frames on ring-full, incrementing `d2b_telemetry_drop_total`. @@ -1737,6 +1787,18 @@ New `packages/d2b-provider-observability-otel/tests/`: datagram socket; Provider drains and forwards; assert spans arrive at the mock OTLP sink with correct `d2b.zone` resource attribute and no forbidden labels. +- `ingress_metric_policy`: run one table-driven corpus through the Unix + emitter, OTLP Unix, OTLP/vsock, and D096 import-stream adapters. Every adapter + rejects exact keys `vm`, `zone`, `zone_id`, `zone_uid`, `credential_name`, + `network`, `network_name`, `link_name_hash`, structural suffixes + `*_name`/`*_name_hash`/`*_name_digest`/`*_uid`, and + `metadata.name`/UID/ResourceRef/resource-identity canary values before queue + insertion or batching. Assert no valid sibling of a rejected frame exports. +- The same ingress test asserts exact bounded outcome/error-class domains, no + rejected key/value echo, datagram drop, stream quarantine and bounds, zero + import credits while quarantined, policy-before-capacity under backpressure, + valid-frame passage, OTEL Resource identity preservation, and audit + separation. - `emitter_ring_drains_on_socket_available`: emitter writes frames before socket exists; socket appears; assert buffered frames arrive in FIFO order. - `emitter_ring_drop_on_overflow`: fill ring past capacity; assert @@ -1876,11 +1938,11 @@ New `packages/d2b-core-controller/tests/config_cleanup.rs`: | Dependency/owner | W0/W1a; telemetry crate owner | | Current source | `packages/d2b-realm-core/src/trace_context.rs` (`TraceContext`, `MAX_TRACE_FIELD_LEN`); `packages/d2b-realm-core/src/audit.rs` (`AuditHash`, `AuditHashError`, `AuditChainLink`, `AuditChainRecord`); `packages/d2b-realm-core/src/ids.rs` (`OperationId`, `CorrelationId`); `packages/d2b-realm-codec-protobuf/src/lib.rs` (`encode_trace_context`, `decode_trace_context`); `packages/d2b-contract-tests/tests/policy_observability.rs::startup_tracing_avoids_host_path_fields` | | Reuse action | adapt | -| Destination | `packages/d2b-telemetry/src/{trace_context.rs,audit_hash.rs,emitter.rs,meter_registry.rs,redaction_guard.rs}` | -| Detailed design | `d2b-telemetry` provides: (1) `TraceContext` / `AuditHash` / `AuditChainLink` extracted unchanged; (2) `BoundedEmitter`: `tracing`-subscriber layer that serializes span/metric events into compact frames and writes them over a private Unix datagram socket to the `observability-otel` Provider — no `opentelemetry_sdk` dependency; (3) `RedactionGuard` span wrapper that asserts the v3 resource attribute allowlist at span creation. No OTEL SDK in this crate. Primary reuse disposition: `adapt`. Preserved source-plan detail: extract unchanged (`TraceContext`, `AuditHash`, `AuditChainLink`); adapt (`OperationId`/`CorrelationId` for v3 record contract); add bounded emitter. | +| Destination | `packages/d2b-telemetry/src/{trace_context.rs,audit_hash.rs,emitter.rs,meter_registry.rs,metric_label_policy.rs,redaction_guard.rs}` | +| Detailed design | `d2b-telemetry` provides: (1) `TraceContext` / `AuditHash` / `AuditChainLink` extracted unchanged; (2) `BoundedEmitter`: `tracing`-subscriber layer that serializes span/metric events into compact frames and writes them over a private Unix datagram socket to the `observability-otel` Provider — no `opentelemetry_sdk` dependency; (3) the canonical closed `METRIC_LABEL_POLICY`, structural descriptor/data-point/exemplar validator, exact forbidden-key/suffix predicates, and non-serializable resource-identity canary matcher used by emitter defense in depth and the mandatory collector ingress gate; (4) `RedactionGuard` span wrapper that asserts the v3 resource attribute allowlist at span creation. No OTEL SDK in this crate. Primary reuse disposition: `adapt`. Preserved source-plan detail: extract unchanged (`TraceContext`, `AuditHash`, `AuditChainLink`); adapt (`OperationId`/`CorrelationId` for v3 record contract); add bounded emitter. | | Integration | Every v3 core process initializes a `BoundedEmitter` pointing at `$ZONE_STATE/telemetry/emitter.sock`; v3 audit records use `AuditHash`/`AuditChainLink` from this crate | | Data migration | Full d2b 3.0 reset; no v2 state/config import | -| Validation | Unit test for `RedactionGuard` attribute gate; unit test for `BoundedEmitter` ring-full drop and FIFO drain; `policy_telemetry_redaction.rs::startup_tracing_avoids_host_path_fields` port; assert `config_source = "realm-controllers"` absent; assert no `opentelemetry_sdk` dependency in `d2b-telemetry` Cargo.toml | +| Validation | Unit test for `RedactionGuard` attribute gate; unit test for `BoundedEmitter` ring-full drop and FIFO drain; table tests for `METRIC_LABEL_POLICY` exact keys, suffixes, and `metadata.name`/UID/ResourceRef identity canaries; `policy_telemetry_redaction.rs::startup_tracing_avoids_host_path_fields` port; assert `config_source = "realm-controllers"` absent; assert no `opentelemetry_sdk` dependency in `d2b-telemetry` Cargo.toml | | Removal proof | None — net-new; no prior owner to remove | ### ADR046-telem-002 @@ -1952,10 +2014,10 @@ New `packages/d2b-core-controller/tests/config_cleanup.rs`: | Current source | `nixos-modules/components/observability/host.nix` (`otelRuntimeDir = "/run/d2b/otel"`, `hostEgressSocket`, ACL `setfacl` pattern, `scrapeJournal`, `hostCfg.identityName`); `nixos-modules/components/observability/stack.nix` (SigNoz stack, `ingressSources`, per-source `vmName`, `receiverGrpcPort`/`receiverHttpPort`, loopback binding, `cfg.signoz.listenPort`); `nixos-modules/components/observability/guest.nix` (`vm.name`/`vm.env`/`vm.role` identity stamping, guest collector); `packages/d2b-host/src/otel_host_bridge_argv.rs` (`OtelHostBridgeArgvInputs`; vsock forwarding); `packages/d2bd/src/otel_host_bridge_readiness.rs` (readiness gate pattern: `OtelHostBridgeReadiness::{Ready,Pending,Failed}`); `packages/d2b-core/src/processes.rs::ProcessRole::OtelHostBridge`; `packages/d2b-contracts/src/broker_wire.rs::RunnerRole::OtelHostBridge`; `packages/d2b-contract-tests/tests/{policy_observability.rs,minijail_relay_otel.rs}` | | Reuse action | adapt | | Destination | `packages/d2b-provider-observability-otel/src/`, `nixos-modules/components/observability/` (adapted files) | -| Detailed design | `Provider/observability-otel` is an **ordinary optional non-bootstrap Process** (not counted toward the ≤64 MiB mandatory core aggregate). It owns: (1) per-Zone datagram receiver socket at `$ZONE_STATE/telemetry/emitter.sock` (drains frames from core emitters) and OTLP/gRPC Unix socket at `$ZONE_STATE/telemetry/otlp.sock`; (2) the full OTEL SDK with OTLP exporter — only this process links `opentelemetry_sdk`; (3) OTel Collector pipeline per Zone and per Host; (4) vsock OTLP forwarding to obs Zone (replaces socat-based `OtelHostBridgeArgvInputs`); (5) SigNoz stack Nix adapted from `stack.nix` with per-Zone `ingressSources` replacing per-VM `vmName`; (6) journald scrape (optional, disabled by default); (7) self-metrics endpoint. Zone/controller startup does not wait for this Provider. If absent or unready, Zone health is `Degraded` (not `Failed`). Readiness: socket exists and first drain cycle completes successfully. `d2b.observability.host.identityName` option preserved; `vmName` in `ingressSources` populated from Zone name. Primary reuse disposition: `adapt`. Preserved source-plan detail: adapt Nix pipeline shape (replace per-VM `vmName` with per-Zone naming); adapt `OtelHostBridgeArgvInputs` vsock forwarding to native OTLP/gRPC-over-vsock; adapt readiness gate pattern (`OtelHostBridgeReadiness::Ready` → Provider phase `Ready`); adapt `ingressSources` per-VM → per-Zone. | +| Detailed design | `Provider/observability-otel` is an **ordinary optional non-bootstrap Process** (not counted toward the ≤64 MiB mandatory core aggregate). It owns: (1) per-Zone datagram receiver socket at `$ZONE_STATE/telemetry/emitter.sock` (drains frames from core emitters) and OTLP/gRPC Unix socket at `$ZONE_STATE/telemetry/otlp.sock`; (2) the full OTEL SDK with OTLP exporter — only this process links `opentelemetry_sdk`; (3) OTel Collector pipeline per Zone and per Host; (4) vsock OTLP forwarding to obs Zone (replaces socat-based `OtelHostBridgeArgvInputs`); (5) D096 import-stream ingest; (6) one structural `METRIC_LABEL_POLICY` gate shared by Unix emitter, OTLP Unix, OTLP/vsock, and import-stream metrics before aggregation, queueing, batching, retry, or export, with bounded non-echoing errors/quarantine/backpressure; (7) SigNoz stack Nix adapted from `stack.nix` with per-Zone `ingressSources` replacing per-VM `vmName`; (8) journald scrape (optional, disabled by default); (9) self-metrics endpoint. Trusted producer identity is stamped only into allow-listed OTEL Resource attributes; audit remains separate. Zone/controller startup does not wait for this Provider. If absent or unready, Zone health is `Degraded` (not `Failed`). Readiness: socket exists and first drain cycle completes successfully. `d2b.observability.host.identityName` option preserved; `vmName` in `ingressSources` populated from Zone name. Primary reuse disposition: `adapt`. Preserved source-plan detail: adapt Nix pipeline shape (replace per-VM `vmName` with per-Zone naming); adapt `OtelHostBridgeArgvInputs` vsock forwarding to native OTLP/gRPC-over-vsock; adapt readiness gate pattern (`OtelHostBridgeReadiness::Ready` → Provider phase `Ready`); adapt `ingressSources` per-VM → per-Zone. | | Integration | Core process `BoundedEmitter` → `emitter.sock` → observability-otel collector → `otlp.sock` → vsock → obs Zone SigNoz; Zone startup independent of Provider readiness | | Data migration | Existing SigNoz data not migrated; v3 starts fresh | -| Validation | `emitter_socket_receive`, `emitter_ring_drains_on_socket_available`, `emitter_ring_drop_on_overflow`, `no_vm_label_in_metrics`, `zone_startup_proceeds_without_provider` tests; adapted `policy_observability.rs` tests (retain `loki_native_otel_resource_attributes` and SigNoz-only backend assertions); adapted `minijail_relay_otel.rs` shape test for Provider-managed runner | +| Validation | `emitter_socket_receive`, `emitter_ring_drains_on_socket_available`, `emitter_ring_drop_on_overflow`, table-driven `ingress_metric_policy` across all four ingress adapters, `no_vm_label_in_metrics`, and `zone_startup_proceeds_without_provider` tests; adapted `policy_observability.rs` tests (retain `loki_native_otel_resource_attributes` and SigNoz-only backend assertions); adapted `minijail_relay_otel.rs` shape test for Provider-managed runner | | Removal proof | `otel_host_bridge_argv.rs` socat runner and `otel_host_bridge_readiness.rs` retired after `observability-otel` Provider delivers native OTLP/vsock and passes conformance; `ProcessRole::OtelHostBridge` and `RunnerRole::OtelHostBridge` retired from `d2b-core/src/processes.rs` and `d2b-contracts/src/broker_wire.rs` after Provider migration | ### ADR046-audit-001 @@ -2072,7 +2134,7 @@ New `packages/d2b-core-controller/tests/config_cleanup.rs`: | Current source | `packages/d2b-contract-tests/tests/policy_observability.rs` (`loki_native_otel_resource_attributes` allowlist: `["deployment.environment","host.name","service.name","service.namespace","source","vm.env","vm.name","vm.role"]`; `tempo_stack_signoz_backend_and_collector` SigNoz-only assertion; `startup_tracing_avoids_host_path_fields` forbidden fields); `packages/d2b-contract-tests/tests/policy_metrics.rs` (`EXPECTED_METRICS` table parity with `docs/reference/daemon-metrics.md`) | | Reuse action | adapt | | Destination | `packages/d2b-contract-tests/tests/policy_telemetry_redaction.rs` (new); updated `policy_observability.rs`; updated `policy_metrics.rs` | -| Detailed design | (1) Extend `loki_native_otel_resource_attributes` allowlist to include `d2b.zone`, `d2b.provider`, `d2b.component`, `service.version`. (2) Add redaction lint: scan all v3 instrumentation call sites for `realm`, `workload_id`, `node_id`, `vm` (as label key), `path`, `socket`, `argv`, `pid`, `exe`. (3) Add structural metric-label policy lint: parse every v3 `MetricDescriptor`, require each label key and value domain to exist in the closed `METRIC_LABEL_POLICY`, reject exact keys `vm`, `zone`, `zone_id`, `zone_uid`, `credential_name`, `network`, `network_name`, and `link_name_hash`, reject resource-name-derived key suffixes `*_name`, `*_name_hash`, `*_name_digest`, and `*_uid`, and prove a `metadata.name` canary never enters label values. Fixed semantic labels remain allowed only with closed domains. (4) Assert the `d2b.zone` resource attribute remains present. (5) Add bucket boundary gates for 5 ms and 20 ms. (6) Retain: `startup_tracing_avoids_host_path_fields`; SigNoz-only backend assertion; `tempo_guest_collector_shape`; `config_source = "realm-controllers"` absence gate. Primary reuse disposition: `adapt`. Preserved source-plan detail: adapt and extend; keep existing tests; add new policy gates. | +| Detailed design | (1) Extend `loki_native_otel_resource_attributes` allowlist to include `d2b.zone`, `d2b.provider`, `d2b.component`, `service.version`. (2) Add redaction lint: scan all v3 instrumentation call sites for `realm`, `workload_id`, `node_id`, `vm` (as label key), `path`, `socket`, `argv`, `pid`, `exe`. (3) Add structural metric-label policy lint: parse every v3 `MetricDescriptor`, require each label key and value domain to exist in the closed `METRIC_LABEL_POLICY`, reject exact keys `vm`, `zone`, `zone_id`, `zone_uid`, `credential_name`, `network`, `network_name`, and `link_name_hash`, reject resource-name-derived key suffixes `*_name`, `*_name_hash`, `*_name_digest`, and `*_uid`, and prove `metadata.name`, UID, ResourceRef, and resource-identity canaries never enter label values. Fixed semantic labels remain allowed only with closed domains. (4) Prove the observability Provider's Unix-emitter, OTLP-Unix, OTLP/vsock, and import-stream adapters all invoke that validator before queue/batch/export; assert whole-frame rejection, bounded non-echoing error classes/quarantine, and policy-before-capacity backpressure. (5) Assert the `d2b.zone` Resource attribute remains present and audit is unchanged. (6) Add bucket boundary gates for 5 ms and 20 ms. (7) Retain: `startup_tracing_avoids_host_path_fields`; SigNoz-only backend assertion; `tempo_guest_collector_shape`; `config_source = "realm-controllers"` absence gate. Primary reuse disposition: `adapt`. Preserved source-plan detail: adapt and extend; keep existing tests; add new policy gates. | | Integration | Contract-tests run in workspace check and `make test-drift` | | Data migration | Full d2b 3.0 reset; no v2 state/config import | | Validation | These tests are their own validation artifact | diff --git a/docs/specs/providers/ADR-046-provider-device-security-key.md b/docs/specs/providers/ADR-046-provider-device-security-key.md index bee6a6dd5..4551df98b 100644 --- a/docs/specs/providers/ADR-046-provider-device-security-key.md +++ b/docs/specs/providers/ADR-046-provider-device-security-key.md @@ -1119,8 +1119,10 @@ the relay does not accept in-band identity claims from the peer. Raw CTAP payloads, PINs, CBOR assertions, credential IDs, WebAuthn responses, and signature bytes are never logged, audited, or included in OTEL spans or -metrics. Only target identity (opaque digest), Service/Binding digests, high-level -op type (acquire/release/timeout/cancel), and lease lifecycle events are emitted. +metrics. Authorized bounded audit records may carry the target, +Service/Binding, and session digests specified below. OTEL spans and metrics +carry only fixed semantic operation, phase, outcome, and error-class values; +they carry no target or resource identity, including opaque digests. ### I-8: Relay has narrow ComponentSession service authority @@ -1519,6 +1521,11 @@ the Provider controller; path-free): } ``` +`resource_name_digest` is permitted only in this bounded Core audit record, +after the caller has already been authorized for the DeviceGrant operation. It +is not a telemetry field and must never be copied into a metric label, span +attribute, OTEL log, collector diagnostic, or support summary. + Excluded: hidraw node path, sysfs bus ID, vendor/product string, serial, device file descriptor number, CTAP payload, guest VM name. The Provider controller does not emit this record; Core emits it at DeviceGrant resolution time. @@ -1552,8 +1559,11 @@ Constraints specific to this Provider: - Metric `d2b_device_sk_session_total{outcome}`: counter; `outcome` ∈ `{success, timeout, cancelled, busy, conflict, error}`. - Metric `d2b_device_sk_ceremony_duration_seconds`: histogram; bucketed 0–120 s. - Metric `d2b_device_sk_relay_restarts_total`: counter. -- No metric or span attribute carries device name (only `resource_name_digest`), - session ID, guest name, hidraw path, or serial. +- No metric label or span attribute carries a device/resource/Service/Binding + name, UID, ref, digest (including `resource_name_digest`), session ID, guest + name, hidraw path, serial, or derived identity token. Spans use only fixed + semantic operation, phase, outcome, and closed error-class attributes. +- `resource_name_digest` remains audit-only under the authorization rule above. - OTEL emitter: lightweight bounded ring (no OTEL SDK in the Provider process; tracing crate only). The `observability-otel` Provider drains and forwards. @@ -2180,7 +2190,7 @@ class. | Current source | None — net-new v3 work; no pre-ADR45 baseline equivalent | | Reuse action | create | | Destination | Core `device-grant` audit and Provider controller Service/Binding ceremony lifecycle audit | -| Detailed design | Path-free authority-grant records from Core and bounded Service/Binding/session digests/outcomes from controller; no path, raw target identity, LeaseId, session content, or CTAP bytes. | +| Detailed design | Path-free authority-grant records from Core and bounded Service/Binding/session digests/outcomes from controller; no path, raw target identity, LeaseId, session content, or CTAP bytes. `resource_name_digest` is admitted only in the Core authority-grant audit after DeviceGrant authorization and is never copied to OTEL. | | Integration | Core emits grant audit; controller emits Service/Binding lifecycle audit; Zone stream stores bounded records; CLI/support consumes digests/outcomes. | | Data migration | Full d2b 3.0 reset; no v2 audit import | | Validation | Audit tests assert path-free fields, bounded digests, no guest name/session content/CTAP bytes, grant emitted by Core not Provider controller, and lifecycle emitted by controller. | @@ -2194,10 +2204,10 @@ class. | Current source | None — net-new v3 work; no pre-ADR45 baseline equivalent | | Reuse action | create | | Destination | Provider/controller bounded telemetry emitter and observability-otel handoff for security-key metrics | -| Detailed design | OTEL metrics: `d2b_device_sk_session_total`, `d2b_device_sk_ceremony_duration_seconds`, `d2b_device_sk_relay_restarts_total` via bounded emitter ring; descriptors use only closed semantic labels and never Zone/resource-name-derived identity, while `d2b.zone` and `d2b.provider` remain OTEL resource attributes | +| Detailed design | OTEL metrics: `d2b_device_sk_session_total`, `d2b_device_sk_ceremony_duration_seconds`, `d2b_device_sk_relay_restarts_total` via bounded emitter ring; descriptors use only closed semantic labels and never Zone/resource-name-derived identity. Provider spans use only fixed operation/phase/outcome/error-class attributes. Neither metrics nor spans admit a resource name, UID, ref, digest (including `resource_name_digest`), session ID, or derived identity token, while `d2b.zone` and `d2b.provider` remain OTEL Resource attributes. | | Integration | Relay/controller write metric events to the bounded ring; observability-otel Provider drains and exports; dashboards/CLI consume closed labels and bounded histograms. | | Data migration | Full d2b 3.0 reset; no v2 telemetry import | -| Validation | Metrics tests structurally assert closed label sets, exact absence of `vm`, `zone`, `zone_id`, `zone_uid`, and resource-name-derived keys, Device/Zone-name canary absence, retained `d2b.zone` resource attributes, bounded ring behavior, and correct session/ceremony/restart counters. | +| Validation | `telemetry_identity_canaries.rs` and metric inventory tests structurally assert closed label/span-attribute sets; exact absence of `vm`, `zone`, `zone_id`, `zone_uid`, resource-name-derived keys, and `resource_name_digest`; Device/Service/Binding/Guest/Zone name, UID, ref, and digest canary absence from metrics and spans; retained `d2b.zone` Resource attributes; bounded ring behavior; and correct session/ceremony/restart counters. | | Removal proof | None — net-new; no prior owner to remove | ### ADR046-security-key-023 @@ -2418,6 +2428,7 @@ per-test budget. | `cid_isolation.rs` | Different Bindings/ceremonies do not share CID maps; round trip and cancel-all-CIDs; canonical Service/Binding subject only | | `descriptor_validation.rs` | Relay-control and Service/Binding Noise identities, fingerprints, encrypted stream bounds, SO_PEERCRED, no ambient path/raw CID, and opaque-ID Debug redaction | | `status_binding.rs` | Empty ProviderStateSet; authority/import/attachment observations occur only in `status.resource`, while shared-backing implementation state and relay/frontend/queue/ceremony observations remain in `status.provider`; no semantic field appears directly under `status`; ceremony rows are not resources/status history; CTAP, fd, LeaseId, CID, and session keys absent | +| `telemetry_identity_canaries.rs` | Exact semantic metric/span allowlists; `resource_name_digest` and Device/Service/Binding/Guest/Zone name, UID, ref, and digest canaries never enter metric labels or span attributes; allow-listed OTEL Resource identity remains | ### Integration (in `integration/`) diff --git a/docs/specs/providers/ADR-046-provider-observability-otel.md b/docs/specs/providers/ADR-046-provider-observability-otel.md index 2f9c4c6c9..a41a05185 100644 --- a/docs/specs/providers/ADR-046-provider-observability-otel.md +++ b/docs/specs/providers/ADR-046-provider-observability-otel.md @@ -101,6 +101,7 @@ packages/d2b-provider-observability-otel/ collector_bin.rs collector binary entry point (full OTEL SDK) forwarder_bin.rs vsock-forwarder long-lived Process entry point emitter_socket.rs datagram socket drain loop + ingress_policy.rs structural metric-frame admission for every ingress exporter.rs OTLP/gRPC exporter setup, retry, backpressure metrics.rs self-metrics (d2b_otel_*) definitions journald.rs optional journald receiver (disabled by default) @@ -111,6 +112,7 @@ packages/d2b-provider-observability-otel/ emitter_socket_receive.rs emitter_ring_drains_on_socket_available.rs emitter_ring_drop_on_overflow.rs + ingress_metric_policy.rs no_vm_label_in_metrics.rs zone_startup_proceeds_without_provider.rs exporter_outage.rs @@ -162,12 +164,14 @@ OTEL SDK and sends only through the Binding's same-Zone `serviceRef`. compact telemetry frames from authorized `BoundedEmitter` instances. 2. Owns the Binding-private `otlp.sock` Endpoint for Provider processes that embed the full SDK. -3. Decodes frames and reconstructs the selected OTEL metrics/traces/logs. +3. Decodes frames and sends metrics from the Unix emitter, OTLP Unix socket, + OTLP/vsock forwarder, and imported named stream through the same structural + `METRIC_LABEL_POLICY` admission gate before aggregation or queueing. 4. Resolves the same-Zone `TelemetryService` named by `TelemetryBinding.spec.serviceRef` and exports only through its authorized route. It never resolves a remote ResourceRef. -5. Enforces the Binding's quotas, redaction/cardinality policy, and - drop/backpressure behavior before sending. +5. Enforces the Binding's quotas, redaction/cardinality policy, bounded + quarantine, and drop/backpressure behavior before batching or sending. 6. Upserts source identity from the trusted `producerRef` observation; incoming self-asserted Zone/Guest identity never overrides that stamp. 7. Runs optional journald ingestion only when enabled by the strict @@ -1087,6 +1091,59 @@ d2b.zone, d2b.provider, d2b.component, service.version Frames with extra keys are **dropped** (not forwarded); `d2b_otel_frames_decoded_total{outcome="error"}` increments. +### Structural metric ingress policy + +Every metrics ingress calls the single `src/ingress_policy.rs` admission path: + +1. compact metric frames from the Binding-private `emitter.sock` Unix datagram + receiver; +2. OTLP metrics received on the Binding-private `otlp.sock` Unix socket; +3. OTLP metrics received through the Guest forwarder's private vsock Endpoint; +4. OTLP metrics received through a D096 import named stream. + +After bounded size/decode checks and trusted OTEL Resource stamping, but before +SDK aggregation, queue insertion, batching, retry, or export, the gate parses +every metric descriptor, data-point attribute map, and exemplar attribute map. +It requires every key and value domain to exist in the closed +`METRIC_LABEL_POLICY`. A whole frame is rejected and dropped if a metric label: + +- uses exact key `vm`, `zone`, `zone_id`, `zone_uid`, `credential_name`, + `network`, `network_name`, or `link_name_hash`; +- ends in `_name`, `_name_hash`, `_name_digest`, or `_uid`; +- contains a producer/resource `metadata.name`, UID, ResourceRef, or other + resource-identity canary value supplied by the trusted Binding observation. + +This is structural admission, not a grep or post-export redaction pass. No +adapter may enqueue an unchecked frame, and splitting one OTLP request into +frames cannot preserve its valid subset after any frame violates the policy. +Valid identity is preserved only in the separate allow-listed OTEL Resource +attributes; the gate never moves Resource identity into metric attributes. +Audit remains a separate sink and is never inspected or forwarded here. + +An invalid Unix datagram is silently dropped. An invalid stream frame receives +only the bounded protocol error `invalid-telemetry-frame`; three violations on +one connection quarantine that connection for at most 30 seconds. Quarantine +state is in-memory, capped at 64 connections per Binding, and retains only an +opaque connection handle, expiry, ingress class, and closed error class—not the +frame, forbidden key/value, or producer/resource identity. Import credits are +zero while that stream is quarantined. + +The non-recursive self-metric is: + +```text +d2b_otel_ingress_policy_total{ + ingress = emitter_unix | otlp_unix | otlp_vsock | import_stream, + outcome = accepted | rejected | quarantined, + error_class = none | key_not_allowlisted | key_forbidden | + key_suffix_forbidden | value_identity | malformed | oversize +} +``` + +Those are the complete label domains. Diagnostics, status, logs, spans, +metrics, and protocol errors never echo the rejected key or value. +`d2b_telemetry_drop_total{reason="policy_violation"}` counts rejected frames; +`reason="ingress_quarantine"` counts frames refused while quarantined. + ### Forbidden metric label values All of the following are unconditionally forbidden as metric label values in @@ -1114,9 +1171,10 @@ any data processed or forwarded by this Provider (per - Any OTEL data emitted or forwarded by this Provider This invariant applies to data originating from this Provider's own processes -and to ingested data from the emitter socket and OTLP socket. The redaction -filter (`src/redaction.rs`) drops any span attribute, log body field, or metric -exemplar field named `no_isolation` from forwarded data. +and to data ingested through the Unix emitter, OTLP Unix, OTLP/vsock, and import +stream adapters. The redaction filter (`src/redaction.rs`) drops any span +attribute or log body field named `no_isolation`; the structural metric gate +rejects a frame carrying it in any label or exemplar map. --- @@ -1193,13 +1251,18 @@ When the OTLP SDK queue reaches the lesser of the Binding's common quota and the extension `maxQueueSize`: 1. `d2b_otel_backpressure_active` gauge is set to 1. -2. New incoming frames from `emitter.sock` that cannot be enqueued are dropped - from the emitter ring in FIFO order. -3. `d2b_telemetry_drop_total{reason="buffer_full"}` increments per dropped frame. -4. Controller sets `BackpressureActive=True` (`QueueFull`) on the Binding; import +2. Every ingress continues to run structural admission before observing queue + capacity. Invalid frames are dropped as policy violations and never consume + queue space, retry budget, or an export batch. +3. Valid new frames from `emitter.sock` that cannot be enqueued are dropped + from the emitter ring in FIFO order. OTLP Unix/vsock and import streams + receive zero credits/resource-exhausted backpressure without closing the + policy gate; quarantined streams remain at zero credits. +4. `d2b_telemetry_drop_total{reason="buffer_full"}` increments per dropped valid frame. +5. Controller sets `BackpressureActive=True` (`QueueFull`) on the Binding; import credit exhaustion also sets `QuotaSaturated=True` on the projected Service. -5. The affected Binding becomes `Degraded` (not `Failed`). -6. When queue depth drops below `maxQueueSize * 0.75` (75% watermark): +6. The affected Binding becomes `Degraded` (not `Failed`). +7. When queue depth drops below `maxQueueSize * 0.75` (75% watermark): - `d2b_otel_backpressure_active` is reset to 0. - Controller clears the Binding/Service backpressure conditions. - The Binding returns to `Ready` if no other degraded condition remains. @@ -1827,6 +1890,25 @@ integration-only. - Assert `d2b_otel_*` label keys are all from the closed set enumerated in this spec. - Assert `no_isolation` does not appear as a label key in any descriptor. +#### `tests/ingress_metric_policy.rs` + +- Run the same table-driven frame corpus through the Unix emitter, OTLP Unix, + OTLP/vsock, and D096 import-stream adapters. +- For every adapter, reject exact keys `vm`, `zone`, `zone_id`, `zone_uid`, + `credential_name`, `network`, `network_name`, and `link_name_hash`, every + `_name`/`_name_hash`/`_name_digest`/`_uid` suffix, and + `metadata.name`/ResourceRef/UID/resource-identity canary values. +- Assert rejection occurs before queue insertion and batching, no valid sibling + from a rejected OTLP frame is exported, and no forbidden key/value is echoed + in an error, self-metric, span, log, status, or quarantine record. +- Assert the bounded `ingress`, `outcome`, and `error_class` domains exactly, + stream quarantine after three violations, the 64-entry/30-second bounds, + datagram drop behavior, zero import credits while quarantined, and continued + structural checks during exporter backpressure. +- Assert a valid frame passes every ingress unchanged while trusted + `d2b.zone`/`d2b.provider` and other allow-listed identity remain only in OTEL + Resource attributes. Audit fixtures remain byte-identical and separate. + #### `tests/zone_startup_proceeds_without_provider.rs` **Source:** specified in `ADR-046-telemetry-audit-and-support` §OTEL endpoint tests. @@ -2134,11 +2216,11 @@ Old and new suites never run in parallel indefinitely. | Dependency/owner | ADR046-otel-001 + ADR046-telem-001 + ADR046-provider-001 (Provider toolkit) + ADR046-provider-004 (common telemetry Service/Binding base) + resource/Endpoint/Volume contracts; W2; observability owner | | Current source | `nixos-modules/components/observability/host.nix` (`otelRuntimeDir`, `hostEgressSocket`, `setfacl` ACL pattern, `scrapeJournal` option, `identityName`); `nixos-modules/components/observability/stack.nix` (`ingressSources`, `vmName`, `receiverGrpcPort`, loopback binding, `signoz.listenPort`) | | Reuse action | adapt | -| Destination | `packages/d2b-provider-observability-otel/src/{collector_bin,emitter_socket,exporter,controller,service,binding}.rs`; updated Nix observability modules | -| Detailed design | Register the initial implementation of both provider-neutral qualified ResourceTypes by binding the exact ADR046-provider-004 base versions/fingerprints, then define only strict observability-otel Service/Binding spec and status extensions. Reconcile each Binding into an edge collector, private Endpoints, runtime Volume, and optional forwarder. Collector links the full OTEL SDK, resolves `serviceRef`, stamps trusted producer identity, and enforces common signals/quota/policy plus strict batching extension. Write generic Service/Binding observations to `status.resource` and SigNoz/OTLP/OTEL observations only to `status.provider`; no state file or Provider state Volume. Provider root config remains installation-only. Primary reuse disposition: `adapt`. Preserved source-plan detail: adapt Nix pipeline shape (replace per-VM `vmName` with per-Zone name; replace socat runner with vsock-forwarder long-lived Process; adapt `ingressSources` per-Zone entry). | +| Destination | `packages/d2b-provider-observability-otel/src/{collector_bin,emitter_socket,ingress_policy,exporter,controller,service,binding}.rs`; updated Nix observability modules | +| Detailed design | Register the initial implementation of both provider-neutral qualified ResourceTypes by binding the exact ADR046-provider-004 base versions/fingerprints, then define only strict observability-otel Service/Binding spec and status extensions. Reconcile each Binding into an edge collector, private Endpoints, runtime Volume, and optional forwarder. Collector links the full OTEL SDK, resolves `serviceRef`, stamps trusted producer identity into allow-listed OTEL Resource attributes, and routes Unix-emitter, OTLP-Unix, OTLP/vsock, and import-stream metrics through one structural `METRIC_LABEL_POLICY` gate before aggregation, queueing, batching, retry, or export. The gate rejects exact/suffix identity keys and trusted `metadata.name`/resource-identity canary values, applies bounded non-echoing error classes and connection quarantine, and never consumes audit. Write generic Service/Binding observations to `status.resource` and SigNoz/OTLP/OTEL observations only to `status.provider`; no state file or Provider state Volume. Provider root config remains installation-only. Primary reuse disposition: `adapt`. Preserved source-plan detail: adapt Nix pipeline shape (replace per-VM `vmName` with per-Zone name; replace socat runner with vsock-forwarder long-lived Process; adapt `ingressSources` per-Zone entry). | | Integration | `BoundedEmitter` → Binding-private Endpoint → edge collector/OTEL SDK → same-Zone authority or projected Service → SigNoz | | Data migration | Existing SigNoz data not migrated; v3 starts fresh per Zone | -| Validation | Common-fixture/fingerprint and canonical-minimal-base conformance (including a fake alternate telemetry Provider); `tests/emitter_socket_receive.rs`; `tests/exporter_outage.rs`; `tests/exporter_backpressure.rs`; `integration/scenario_full_pipeline.rs`; adapted `policy_observability.rs` (retain all existing assertions; add new `d2b.zone`, `d2b.provider` allowlist entries) | +| Validation | Common-fixture/fingerprint and canonical-minimal-base conformance (including a fake alternate telemetry Provider); `tests/emitter_socket_receive.rs`; table-driven `tests/ingress_metric_policy.rs` across all four ingress adapters; `tests/exporter_outage.rs`; `tests/exporter_backpressure.rs`; `integration/scenario_full_pipeline.rs`; adapted `policy_observability.rs` (retain all existing assertions; add new `d2b.zone`, `d2b.provider` allowlist entries) | | Removal proof | `guest.nix` per-VM guest collector retired after `integration/scenario_obs_zone_forwarding.rs` passes | ### ADR046-otel-003 @@ -2164,8 +2246,8 @@ Old and new suites never run in parallel indefinitely. | Dependency/owner | ADR046-otel-002; policy/contract-tests owner | | Current source | `packages/d2b-contract-tests/tests/policy_observability.rs` (`loki_native_otel_resource_attributes` allowlist; `tempo_stack_signoz_backend_and_collector`; `startup_tracing_avoids_host_path_fields`); `packages/d2b-contract-tests/tests/policy_metrics.rs` (`EXPECTED_METRICS` table); `packages/d2b-contract-tests/tests/minijail_relay_otel.rs` | | Reuse action | adapt | -| Destination | `packages/d2b-contract-tests/tests/policy_observability.rs` (updated); `packages/d2b-contract-tests/tests/policy_telemetry_redaction.rs` (new, per ADR046-telem-008); `packages/d2b-provider-observability-otel/tests/no_vm_label_in_metrics.rs` | -| Detailed design | (1) Extend `loki_native_otel_resource_attributes` allowlist with `d2b.zone`, `d2b.provider`, `d2b.component`, `service.version`. (2) Add gate: `no_isolation` must not appear in any Provider `MetricDescriptor` label or span attribute catalog. (3) Adapt `minijail_relay_otel.rs` shape test for Provider-managed runner (no broker `RunnerRole::OtelHostBridge`). (4) Add metric inventory gates for `d2b_otel_*` instruments from this spec. (5) Retain: `startup_tracing_avoids_host_path_fields`; SigNoz-only backend assertion; `tempo_guest_collector_shape`; `config_source = "realm-controllers"` absence gate. Primary reuse disposition: `adapt`. Preserved source-plan detail: adapt and extend existing tests; keep existing test assertions. | +| Destination | `packages/d2b-contract-tests/tests/policy_observability.rs` (updated); `packages/d2b-contract-tests/tests/policy_telemetry_redaction.rs` (new, per ADR046-telem-008); `packages/d2b-provider-observability-otel/tests/{no_vm_label_in_metrics,ingress_metric_policy}.rs` | +| Detailed design | (1) Extend `loki_native_otel_resource_attributes` allowlist with `d2b.zone`, `d2b.provider`, `d2b.component`, `service.version`. (2) Add gate: `no_isolation` must not appear in any Provider `MetricDescriptor` label or span attribute catalog. (3) Adapt `minijail_relay_otel.rs` shape test for Provider-managed runner (no broker `RunnerRole::OtelHostBridge`). (4) Add metric inventory gates for `d2b_otel_*` instruments from this spec. (5) Structurally prove that all four metrics ingress adapters call the shared pre-batch policy gate; run exact forbidden-key, suffix, `metadata.name`, ResourceRef, UID, and resource-identity canaries through each adapter; assert bounded non-echoing outcomes/quarantine/backpressure and valid OTEL Resource identity preservation. (6) Retain: `startup_tracing_avoids_host_path_fields`; SigNoz-only backend assertion; `tempo_guest_collector_shape`; `config_source = "realm-controllers"` absence gate. Primary reuse disposition: `adapt`. Preserved source-plan detail: adapt and extend existing tests; keep existing test assertions. | | Integration | Contract-tests run in workspace `make test-drift` and `make test-lint` | | Data migration | None — full d2b 3.0 reset; no prior state to migrate | | Validation | All contract-tests pass after update; existing allowlist test does not regress | diff --git a/docs/specs/providers/ADR-046-provider-runtime-qemu-media.md b/docs/specs/providers/ADR-046-provider-runtime-qemu-media.md index 19e25d27f..c38edead8 100644 --- a/docs/specs/providers/ADR-046-provider-runtime-qemu-media.md +++ b/docs/specs/providers/ADR-046-provider-runtime-qemu-media.md @@ -1273,16 +1273,20 @@ OTEL trace spans: | Span | Parent | Attributes | | --- | --- | --- | -| `guest.reconcile` | — | zone, provider, guest_uid_short, phase | -| `guest.runner.launch` | `guest.reconcile` | zone, provider, process_ref_uid_short | -| `guest.qmp.connect` | `guest.runner.launch` | zone, provider | -| `guest.qmp.command` | `guest.reconcile` | zone, provider, command (closed set) | -| `guest.media.hotplug` | `guest.reconcile` | zone, provider, operation | -| `guest.finalize` | `guest.reconcile` | zone, provider | - -No span attribute may carry: argv, executable paths, VM memory, host fs -paths, fds, socket paths, raw process output, or user-supplied opaque data. -`guest_uid_short` = first 12 hex chars of Guest UID (not human name). +| `guest.reconcile` | — | phase, outcome | +| `guest.runner.launch` | `guest.reconcile` | outcome | +| `guest.qmp.connect` | `guest.runner.launch` | outcome | +| `guest.qmp.command` | `guest.reconcile` | command (closed set), outcome | +| `guest.media.hotplug` | `guest.reconcile` | operation (attach/detach), outcome | +| `guest.finalize` | `guest.reconcile` | outcome | + +Span attributes are fixed semantic classifiers and outcomes only. No span +attribute may carry Zone, Guest, Process, Provider-resource, or other resource +identity in any form, including a name, UID, short UID, digest, ResourceRef, or +derived token. Identity belongs only in allow-listed OTEL Resource attributes +such as `d2b.zone` and `d2b.provider`, or in an authorized bounded audit record. +Spans also exclude argv, executable paths, VM memory, host filesystem paths, +fds, socket paths, raw process output, and user-supplied opaque data. --- @@ -1777,10 +1781,10 @@ destination in `packages/d2b-provider-runtime-qemu-media/`. | Current source | None — net-new v3 work; no pre-ADR45 baseline equivalent | | Reuse action | create | | Destination | packages/d2b-provider-runtime-qemu-media/src/telemetry.rs | -| Detailed design | Metrics and OTEL spans: implement all metrics from §18 and OTEL trace spans with structural closed-label enforcement and no Zone/VM/resource name, user identity, path, or other sensitive value in any metric label; retain Zone/resource identity only in bounded OTEL resource attributes and permitted audit fields. Primary reuse disposition: `create`. Preserved source-plan detail: net-new telemetry emission for the Provider metrics and spans in §18. | +| Detailed design | Metrics and OTEL spans: implement all metrics from §18 and OTEL trace spans with structural closed-label enforcement and no Zone/VM/resource name, user identity, path, or other sensitive value in any metric label. Span attributes use only the exact fixed semantic fields and `outcome` listed in §18; no resource name, UID, shortened UID, digest, ref, or derived identity is admitted. Retain identity only in allow-listed OTEL Resource attributes and permitted bounded audit fields. Primary reuse disposition: `create`. Preserved source-plan detail: net-new telemetry emission for the Provider metrics and spans in §18. | | Integration | Controller, QMP, hotplug, and dependency-watch paths call telemetry helpers; OTEL/metrics exporters consume only closed, bounded labels for support dashboards. | | Data migration | None — telemetry-only work; no runtime state import | -| Validation | `tests/metrics_label_cardinality.rs` asserts exact absence of `vm`, `zone`, `zone_id`, `zone_uid`, and resource-name-derived keys plus Guest/Zone-name canary absence; `tests/otel_span_attributes.rs` preserves allowed OTEL resource identity attributes and rejects identity span attributes | +| Validation | `tests/metrics_label_cardinality.rs` asserts exact absence of `vm`, `zone`, `zone_id`, `zone_uid`, and resource-name-derived keys plus Guest/Zone-name canary absence; `tests/otel_span_attributes.rs` asserts the exact per-span semantic allowlist, preserves allowed OTEL Resource identity attributes, and rejects Zone/Guest/Process/Provider-resource names, refs, UIDs, shortened UIDs, digests, and identity canary values in span attributes | | Removal proof | None — telemetry helpers are new for this Provider; no prior owner to remove | --- @@ -1890,7 +1894,7 @@ per-test budget. | `audit_event_shapes.rs` | Golden shape for every event in §17 | | `audit_no_sensitive_fields.rs` | Property test: no path/argv/fd/socket-path in payload | | `metrics_label_cardinality.rs` | Structural closed-label policy; exact identity-key absence; no Guest/Zone/resource-name canary or path in values; `d2b.zone` resource attribute retained | -| `otel_span_attributes.rs` | No sensitive attribute in any span | +| `otel_span_attributes.rs` | Exact fixed semantic attribute allowlist per span; Zone/Guest/Process/Provider-resource names, refs, UIDs, shortened UIDs, digests, and identity canaries rejected; allow-listed OTEL Resource identity retained | | `provider_layout.rs` | Workspace layout conformance invocation | | `conformance_guest.rs` | d2b-provider-toolkit conformance suite | From 6838ae356c0030fcf0ad1ae6827ebc330381b472 Mon Sep 17 00:00:00 2001 From: John Vicondoa Date: Fri, 24 Jul 2026 07:42:37 -0700 Subject: [PATCH 094/115] docs: make ADR46 ZoneLinks child-local --- docs/specs/ADR-046-cli-and-operations.md | 58 ++++++++++++++----- .../ADR-046-security-and-threat-model.md | 55 ++++++++++++------ docs/specs/ADR-046-zone-routing.md | 54 +++++++++-------- 3 files changed, 110 insertions(+), 57 deletions(-) diff --git a/docs/specs/ADR-046-cli-and-operations.md b/docs/specs/ADR-046-cli-and-operations.md index 1be4e3d41..d31968808 100644 --- a/docs/specs/ADR-046-cli-and-operations.md +++ b/docs/specs/ADR-046-cli-and-operations.md @@ -53,12 +53,12 @@ the "target:" annotation. | Current baseline symbol | Package | Target name | | --- | --- | --- | -| `RealmId`, `RealmPath`, `TargetName` | `d2b-realm-core/src/ids.rs`, `realm.rs`, `target.rs` | `Zone/` ResourceRef; multi-level path → `ZoneLink` hierarchy | -| `RealmControllerPlacement`, `EntrypointMode` | `d2b-realm-core/src/realm.rs` | Zone runtime placement/mode (ZoneLink `mode` field) | -| `RealmEntrypointDocument`, `RealmEntrypointConfig` | `packages/d2b/src/lib.rs:5163` | Nix-generated static `realm-entrypoints.json`; target: Zone self resource + ZoneLink resources in redb store | -| `RealmPolicyOutputV1 { realm, mode, gateway_vm, gateway_target, gateway_state, cross_realm_policy, credential_boundary }` | `d2b-contracts/src/cli_output.rs:345` | ZoneLink resource fields; `gateway_vm` → ZoneLink `gatewayGuestRef`; `mode` → ZoneLink `placement` | -| `RealmListOutputV1 { realms: Vec }` | `d2b-contracts/src/cli_output.rs:285` | Target: `d2b zone list` response listing `ZoneLink` resources | -| `RealmInspectOutputV1 { realm: RealmPolicyOutputV1 }` | `d2b-contracts/src/cli_output.rs:292` | Target: `d2b zone get ` response | +| `RealmId`, `RealmPath`, `TargetName` | `d2b-realm-core/src/ids.rs`, `realm.rs`, `target.rs` | `Zone/` addressing; ancestry comes from sealed compiler-only `{ childZone, parentZone }` topology and authenticated route projections | +| `RealmControllerPlacement`, `EntrypointMode` | `d2b-realm-core/src/realm.rs` | Private Zone runtime bootstrap placement plus compiler-only parent topology; transport state remains in the child's local ZoneLink | +| `RealmEntrypointDocument`, `RealmEntrypointConfig` | `packages/d2b/src/lib.rs:5163` | Static `realm-entrypoints.json` retires; target is the read-only topology projection over sealed `{ childZone, parentZone }` rows and authenticated route state | +| `RealmPolicyOutputV1 { realm, mode, gateway_vm, gateway_target, gateway_state, cross_realm_policy, credential_boundary }` | `d2b-contracts/src/cli_output.rs:345` | Retired; successor rows expose compiler topology plus authenticated route/projection status, never child-local ZoneLink spec or status | +| `RealmListOutputV1 { realms: Vec }` | `d2b-contracts/src/cli_output.rs:285` | Target: `d2b zone list` topology-projection response | +| `RealmInspectOutputV1 { realm: RealmPolicyOutputV1 }` | `d2b-contracts/src/cli_output.rs:292` | Target: `d2b zone get ` topology/route projection response | | `WorkloadId`, `WorkloadTarget` (`= RealmTarget`) | `d2b-realm-core/src/ids.rs`, `d2b-core/src/workload_identity.rs` | `Guest/` ResourceRef for VM/sandbox/cloud/remote; `Host/` for unsafe-local (NEVER `Guest`) | | `WorkloadProviderKind::LocalVm` | `d2b-realm-core/src/workload.rs:16` | `Guest` under `Provider/runtime-cloud-hypervisor` | | `WorkloadProviderKind::QemuMedia` | `d2b-realm-core/src/workload.rs:19` | `Guest` under `Provider/runtime-qemu-media` | @@ -70,7 +70,7 @@ the "target:" annotation. | `VmStatus { vm: String, lifecycle: VmLifecycle, … }` | `d2b-contracts/src/public_wire.rs:2530` | Guest resource status | | `VmLifecycleState::Stopped/Starting/Booted/Running/Stopping/Restarting/Failed/Unknown` | `d2b-contracts/src/public_wire.rs:2605` | Guest resource `phase`; target phases are `Pending\|Ready\|Succeeded\|Degraded\|Failed\|Deleted\|Unknown`; `Starting`/`Stopping`/`Restarting` map to conditions/reasons on `Pending`/`Degraded`, not separate phases | | `VmTargetRoute::Local { vm }` | `packages/d2b/src/lib.rs:5577` | `Guest/` ResourceRef in the current Zone | -| `VmTargetRoute::Gateway { realm, gateway_vm }` | `packages/d2b/src/lib.rs:5578` | Cross-Zone via `ZoneLink/` ComponentSession; `gateway_vm` = `Guest/` | +| `VmTargetRoute::Gateway { realm, gateway_vm }` | `packages/d2b/src/lib.rs:5578` | Cross-Zone through `ZoneRouteEngine` using sealed topology and an authenticated admitted route; no parent-local ZoneLink lookup | | `ProcessRole::Virtiofsd` | `d2b-core/src/processes.rs:199` | `Process` under volume-virtiofs Provider | | `ProcessRole::Swtpm` | `d2b-core/src/processes.rs:203` | `Process` under device-tpm Provider | | `ProcessRole::CloudHypervisorRunner` | `d2b-core/src/processes.rs:213` | `Process` under runtime-cloud-hypervisor Provider | @@ -1678,11 +1678,11 @@ top-level `d2b --help` output. Each fetch is bounded by the per-Provider invocation only. No disk cache or cross-invocation cache is maintained. Startup latency for non-provider commands is zero. -## `d2b zone` — Zone resource commands +## `d2b zone` — Zone topology and self-resource commands ``` d2b zone get [] # omitting name fetches the current Zone self resource -d2b zone list # local ZoneLink resources (child Zones) +d2b zone list # compiler topology plus authenticated route/projection status d2b zone status [] [--watch] [--deadline ] ``` @@ -1703,7 +1703,35 @@ have `mode: host-resident|gateway-backed` and optional `gateway` VM name. `packages/d2b-contracts/src/cli_output.rs:345`). Evidence class: `implemented-and-reachable` for realm list/inspect (static file read); -`ADR-only` for Zone/ZoneLink resource API (live daemon query replacing static file). +`ADR-only` for the Zone self-resource API and Zone topology/route projection +(live daemon query replacing the static file). + +**Command algorithms and output boundary:** + +1. `d2b zone list` invokes the local `ZoneService` topology-projection method. + The service starts from the sealed, compiler-only sorted + `{ childZone, parentZone }` rows and joins only route/projection status + derived from currently authenticated, admitted advertisements. The result is + sorted by `(parentZone, childZone)`. +2. `d2b zone get` with no name, or with the current Zone name, fetches only the + current Zone's cardinality-one self resource. For another declared Zone, it + resolves the exact topology row and returns its authenticated route/projection + inspection; when the route is current, the service may augment that + inspection with an authenticated call to the target Zone's self resource. + Missing, withdrawn, stale, or unauthenticated route state is reported as + unavailable and is never replaced by static guesses. +3. `d2b zone status --watch` watches the current Zone self resource for the + current Zone and the topology/route projection for any child or descendant. + Reconnect resumes from the projection revision; it never opens a watch on a + parent-store ZoneLink row. + +The generated CLI DTOs and golden output fixtures contain the topology +`childZone`/`parentZone` pair and bounded authenticated route/projection status. +They contain no ZoneLink resource name, UID, spec, status, Provider ref, +fingerprint, transport setting, or parent-side link handle. A child's one +ZoneLink remains in that child's store and is reconciled by that child's +core-controller; a parent stores only sealed topology/allocation state and +authenticated route projections. **Replacement/deletion:** `d2b realm enter` and `d2b realm run` are replaced by `d2b guest start ` plus `d2b exec run -- `. @@ -2472,7 +2500,7 @@ baseline (only a deprecation notice remains at `lib.rs:2424`). | Field | Value | | --- | --- | | Work item ID | `ADR046-cli-006` | -| Dependency/owner | ADR046-cli-001; CLI crate owner | +| Dependency/owner | ADR046-cli-001, ADR046-routing-012, ADR046-routing-016; CLI crate owner | | Current source | None (no completion exists in v3 baseline) | | Reuse source | Optional: clap_complete crate (version to be pinned); no main-branch source | | Reuse action | adapt | @@ -2522,13 +2550,13 @@ baseline (only a deprecation notice remains at `lib.rs:2424`). | Work item ID | `ADR046-cli-009` | | Dependency/owner | ADR046-cli-001; CLI crate owner | | Current source | `packages/d2b/src/lib.rs`: `cmd_realm_list` (reads static `realm-entrypoints.json` via `realm_policy_rows_raw()`), `cmd_realm_inspect`, `cmd_realm_enter` (→ `realm_gateway_exec_args` → `cmd_vm_exec` with `-it bash -l`), `cmd_realm_run` (→ `cmd_vm_exec` with caller argv); wire output types: `RealmListOutputV1 { realms: Vec }`, `RealmInspectOutputV1 { realm: RealmPolicyOutputV1 }` from `packages/d2b-contracts/src/cli_output.rs:285,292,345`; `RealmPolicyOutputV1` fields: `realm` (= `RealmId`), `mode`, `gateway_vm`, `gateway_target`, `gateway_state`, `cross_realm_policy`, `credential_boundary`; `target_routing.rs`: `Route::Local { vm }`, `Route::Gateway { gateway, target }`, `resolve_access_route()`, `VmTargetRoute`; `d2b-realm-router::RealmEntrypointTable` | -| Reuse source | main `a1cc0b2d` — reference only (no copy): `packages/d2b-realm-router/src/service_v2.rs` `RealmServiceServer`, `RealmServiceProcess`, `RealmMethod::Inspect`, `RealmMethod::ResolveRoute` — server-side multi-realm routing; this is the ADR 0045 multi-Zone topology and is **excluded** from v3 CLI as a direct reuse source; `packages/d2b-realm-router/src/remote_node.rs` `RemoteNodeRegistration`, `RemoteNodeEntry` — constellation remote routing; also excluded; note: `packages/d2b-client/src/daemon_service.rs` `DaemonClient::list_workloads()` and `DaemonMethod::ListRealms` are the closest live list-call patterns, but their zone/workload scoping uses `RealmPath`/`RealmId` types that are ADR 0045-specific; adapt `ConnectedClient::invoke()` with a v3 Zone List request type instead; no main symbols are copied unchanged for cli-009; the zone resource API type design is an ADR-only deliverable pending Zone resource spec | +| Reuse source | main `a1cc0b2d` — reference only (no copy): `packages/d2b-realm-router/src/service_v2.rs` `RealmServiceServer`, `RealmServiceProcess`, `RealmMethod::Inspect`, `RealmMethod::ResolveRoute` — server-side multi-realm routing; this is the ADR 0045 multi-Zone topology and is **excluded** from v3 CLI as a direct reuse source; `packages/d2b-realm-router/src/remote_node.rs` `RemoteNodeRegistration`, `RemoteNodeEntry` — constellation remote routing; also excluded; note: `packages/d2b-client/src/daemon_service.rs` `DaemonClient::list_workloads()` and `DaemonMethod::ListRealms` are the closest live list-call patterns, but their zone/workload scoping uses `RealmPath`/`RealmId` types that are ADR 0045-specific; adapt `ConnectedClient::invoke()` with the v3 `ZoneService` topology-projection request instead; no main symbols are copied unchanged for cli-009; the projection type is an ADR-only deliverable owned by zone routing | | Reuse action | adapt | | Destination | `packages/d2b/src/zone.rs` (`d2b zone get/list/status`) | -| Detailed design | `d2b zone get []` fetches Zone self resource via `ConnectedClient::invoke`; `d2b zone list` lists ZoneLink resources. v2 commands (`d2b realm list/inspect/enter/run`) are deleted at 3.0; no dispatch wiring. Excluded ADR 0045: `RealmServiceServer`/`RealmServiceProcess` multi-realm service; `RemoteNodeRegistration` constellation routing; `TargetInput::Realm`; `RealmMethod::ResolveRoute`/`AuthorizeShortcut`/`RevokeShortcut`. | -| Integration | ZoneContext → Zone resource Get/List via `ConnectedClient::invoke` | +| Detailed design | `d2b zone get []` fetches the current Zone self resource only for the local name; another declared name is inspected through the read-only Zone topology projection. `d2b zone list` lists sorted compiler-only `{ childZone, parentZone }` rows joined with authenticated route/projection status. `d2b zone status --watch` watches the corresponding projection revision. None of these commands lists or gets a parent-local ZoneLink: the sole ZoneLink resource and handler are child-local, while the parent owns only sealed allocator topology and authenticated route state. Generated DTO/golden tests reject ZoneLink names/spec/status/provider refs/fingerprints/transport settings in parent-side output. v2 commands (`d2b realm list/inspect/enter/run`) are deleted at 3.0; no dispatch wiring. Excluded ADR 0045: `RealmServiceServer`/`RealmServiceProcess` multi-realm service; `RemoteNodeRegistration` constellation routing; `TargetInput::Realm`; `RealmMethod::ResolveRoute`/`AuthorizeShortcut`/`RevokeShortcut`. | +| Integration | ZoneContext → local Zone self-resource Get or `ZoneService` topology/route projection via `ConnectedClient::invoke` | | Data migration | None — full d2b 3.0 reset; no prior state to migrate | -| Validation | Zone get/list tests; confirm v2 `cmd_realm_*` paths are absent | +| Validation | Zone self-get plus topology list/get/status/watch tests; disconnected and stale authenticated-route projections; golden output contains `{ childZone, parentZone }` and route/projection status but no ZoneLink fields; parent store has no ZoneLink row or watch; confirm v2 `cmd_realm_*` paths are absent | | Removal proof | `cmd_realm_*` and `target_routing.rs` removed only after zone routes pass equivalence tests | ### ADR046-cli-010 diff --git a/docs/specs/ADR-046-security-and-threat-model.md b/docs/specs/ADR-046-security-and-threat-model.md index af957521e..99654ba19 100644 --- a/docs/specs/ADR-046-security-and-threat-model.md +++ b/docs/specs/ADR-046-security-and-threat-model.md @@ -115,7 +115,8 @@ Nix build/eval (offline, hermetic) -> privileged broker (sole privileged executor; allocator leases) -> Host kernel / Guest VMM / cloud control plane -> ComponentSession/d2b-bus (Noise NN/KK/IKpsk2; RBAC per hop) - -> ZoneLink (parent Zone <-> child Zone; carriage-only, no FD/path/credential) + -> child-local ZoneLink (parent keeps sealed topology/route state only; + carriage-only, no FD/path/credential) -> Gateway Guest (relay/cloud credential custody; never the Host) ``` @@ -195,8 +196,9 @@ authorization grant. quarantine (INV-NET-009); pidfd/InvocationID re-verification on controller restart catches an impersonating re-attach. - **Recovery:** Guest quarantine (`Degraded`, no broad kill); Volume/Network/ - Device detach through normal finalizer teardown; ZoneLink revocation if the - Guest was itself hosting a child Zone gateway. + Device detach through normal finalizer teardown; parent allocator route + revocation plus child-local ZoneLink session teardown if the Guest was itself + hosting a child Zone gateway. The parent never deletes a child-store resource. ### AC3: Compromised Host or local same-UID malicious process @@ -236,9 +238,10 @@ input. (ZR lines 554-580); route advertisement replay window and signature check (ZR lines 1835-1895); malformed/exhausted hop count rejected at the source bus (ZR lines 1880-1895). -- **Recovery:** ZoneLink revocation sets the capability ceiling to empty, - withdraws routes, and closes existing streams with `zone-link-revoked`; - already-committed child-Zone operations are not rolled back (ZR lines +- **Recovery:** parent allocator revocation sets the admitted capability ceiling + to empty and withdraws its private route projection; the child-local ZoneLink + handler then closes existing streams with `zone-link-revoked`. + Already-committed child-Zone operations are not rolled back (ZR lines 1835-1895). ### AC5: Forged or tampered Nix artifact / configuration bundle @@ -575,11 +578,14 @@ closes both. ## ZoneLink: no FD/resource grants, transport carriage-only -Every resource belongs to exactly one Zone (D016). A parent Zone represents a -child with a local `ZoneLink/` resource and accesses the child's -resources only through the child's own Zone API — a parent never mounts or -mirrors the child store, and ordinary resource references never cross Zones -(D017). +Every resource belongs to exactly one Zone (D016). Each non-root child stores +and reconciles its own self-matching `ZoneLink/` uplink. The compiler-only +`{ childZone, parentZone }` topology selects the parent allocator; that parent +keeps only sealed allocation/topology state and authenticated route/projection +status. It has no reciprocal ZoneLink row or ZoneLink handler. A parent accesses +child resources only through the child's own Zone API over the authenticated +route — it never mounts or mirrors the child store, and ordinary resource +references never cross Zones (D017). **Structural, not policy, enforcement.** No FD, credential, or host path is forwarded across a ZoneLink. This is enforced as a **structural @@ -605,9 +611,10 @@ misconfigured away (ZR lines 1779-1801): **Transport carriage-only.** Transport Providers (`transport-unix`, `transport-vsock`, `transport-azure-relay`) never own ZoneLink state (D081). -The core ZoneLink handler alone reads/writes/finalizes `ZoneLink` and owns -Noise/session/reconnect/route/idempotency/intent state; it calls typed -`OpenTransport`/`CloseTransport`/`ObserveTransport` on the installed +The child Zone's core ZoneLink handler alone reads/writes/finalizes its local +`ZoneLink` and owns Noise/session/reconnect/route/idempotency/intent state; the +parent has only its allocator and route-engine state. The child handler calls +typed `OpenTransport`/`CloseTransport`/`ObserveTransport` on the installed Transport Provider, which returns only opaque `OwnedTransport`/byte-stream handles and observations. A Transport Provider crate does not call the raw transport syscalls itself (e.g. `transport-vsock` does not depend on @@ -758,11 +765,20 @@ Network/Device Providers themselves. | --- | --- | --- | --- | | Process | `MinijailProcessEffectPort` | `Provider/system-minijail` controller + `d2b-priv-broker` `SpawnRunner` | Compile-time dependency audit: the Provider crate imports no `d2b.broker.v3` service/client/DTO (`ADR-046-provider-system-minijail.md` lines 1621-1628) | | Process (systemd) | `SystemdProcessEffectPort` | `Provider/system-systemd` controller via D-Bus transient unit API | Controller never connects to the systemd D-Bus socket directly and never calls `systemctl` as a subprocess | -| Volume | `VolumeEffectPort` | `Provider/volume-local` controller + broker `ProvisionLayoutEntry`/`RepairLayoutEntry`/`CleanupLayoutEntry`/`RotateSealingKey`/`PrepareSwtpmDir` | "The controller process holds no claim that grants access to raw host paths" (`ADR-046-provider-volume-local.md` lines 1739-1776) | -| Network | `NetworkEffectPort` | `Provider/network-local` controller + broker `CreateBridge`/`CreatePersistentTap`/`SetBridgePortFlags`/`ApplyNftables`/`ApplySysctl` | "The controller holds no broker role and no `network-admin` capability" (`ADR-046-provider-network-local.md` lines 1680-1682) | +| Volume | `VolumeEffectPort` | `Provider/volume-local` controller + broker `ProvisionLayoutEntry`/`RepairLayoutEntry`/`CleanupLayoutEntry`/`RotateSealingKey`/`PrepareSwtpmDir`; key rotation is requested only through `VolumeEffectPort::rotate_sealing_key` | "The controller process holds no claim that grants access to raw host paths" (`ADR-046-provider-volume-local.md` lines 1739-1776) | +| Network | `NetworkEffectPort` | `Provider/network-local` controller + broker `CreatePersistentTap`/`DeletePersistentTap`/`SetBridgePortFlags`/`ApplyNftables`/`ApplySysctl` | "The controller holds no broker role and no `network-admin` capability" (`ADR-046-provider-network-local.md` lines 1680-1682) | +| Device (USBIP) | `UsbipEffectPort` | Core adapter + broker `UsbipBindFirewallRule { action: Ensure \| Remove }` for both acquisition and release | Network-local never owns USBIP TCP/3240 exposure; there is no separate release operation | | Device (vsock) | `VsockEffectPort` | Zone runtime `LiveVsockEffectPort` | `tokio-vsock` is not a dependency of `transport-vsock` (INV-VSOCK-004) | | Cloud (ARM/ACA) | `AzureEffectPort` | The cloud runtime Provider's own controller, confined to the gateway Guest | All calls non-blocking; `AzureOperationHandle` is opaque, max 256 bytes | +These names are the planned closed ADR 0046 broker-operation contract even +where the baseline has no corresponding variant yet. In particular, +`RotateSealingKey` is the sole broker effect behind +`VolumeEffectPort::rotate_sealing_key`; `DeletePersistentTap` is a new +idempotent peer to `CreatePersistentTap`; and USBIP release dispatches the +existing `UsbipBindFirewallRule` with closed `action: Remove`, not a renamed or +second release variant. `action: Ensure` is the only acquisition form. + The broker remains the sole privileged executor and independent audit owner (`ADR-046-provider-model-and-packaging`, D077). It re-derives every privileged parameter from allocator-approved leases and the trusted bundle, @@ -1817,8 +1833,9 @@ time rejection, not an operational recommendation. - A Transport Provider calling raw transport syscalls itself (`AF_VSOCK`, raw `socket(2)`, etc.) instead of going through the core effect adapter (§10). -- A Transport Provider owning ZoneLink state directly instead of returning - opaque handles to the core ZoneLink handler (§10, D081). +- A Transport Provider or parent Zone owning child-local ZoneLink state + directly instead of the child core handler owning the resource and receiving + opaque transport handles (§10, D081). **Telemetry and audit.** @@ -2173,7 +2190,7 @@ close. Each maps to the attacker class it is scoped against. | Reuse source | None | | Reuse action | adapt | | Destination | `tests/integration/containers/malicious-child-zone.rs` | -| Detailed design | Container-based penetration test running a real parent Zone and a deliberately malicious child Zone container that attempts, over a real ZoneLink: FD smuggling, credential-shaped byte injection, cross-Zone `ownerRef` forgery, capability-ceiling widening claims, and route-advertisement replay. Every attempt must be rejected by the parent with the specific typed error named in §10, and none may reach the parent's resource store, Credential state, or Host substrate Primary reuse disposition: `adapt`. Preserved source-plan detail: extract and adapt. | +| Detailed design | Container-based penetration test running a real parent Zone and a deliberately malicious child Zone container. Before attack injection, assert the one ZoneLink row/handler exists only in the child store/runtime and the parent has only sealed `{ childZone, parentZone }` topology plus authenticated allocator/route projection state. The child then attempts, over that link: FD smuggling, credential-shaped byte injection, cross-Zone `ownerRef` forgery, capability-ceiling widening claims, and route-advertisement replay. Every attempt must be rejected by the parent with the specific typed error named in §10, and none may create a parent ZoneLink row or reach the parent's resource store, Credential state, or Host substrate Primary reuse disposition: `adapt`. Preserved source-plan detail: extract and adapt. | | Integration | `make test-integration` (requires podman, per `AGENTS.md` "Local Layer 1 + container integration") | | Data migration | None — full d2b 3.0 reset; no prior state to migrate | | Validation | Container integration test; acceptance is zero successful attacks across all five attempted vectors | diff --git a/docs/specs/ADR-046-zone-routing.md b/docs/specs/ADR-046-zone-routing.md index de6e3461b..c8911fa4a 100644 --- a/docs/specs/ADR-046-zone-routing.md +++ b/docs/specs/ADR-046-zone-routing.md @@ -433,7 +433,9 @@ fields. `EntrypointMode` (`HostResident`/`GatewayBacked`) is used in the CLI target router (`d2b/src/target_routing.rs`) to classify routing decisions. In v3 this mode enum is subsumed into ZoneLink `spec.transportProviderRef`; -the CLI derives routing from ZoneLink resources, not from a static enum. +the child alone reads that local transport binding. Parent-side CLI routing and +inspection consume the sealed compiler topology and authenticated +route/projection status, never a parent-local ZoneLink resource. ### Provider trait model @@ -574,9 +576,9 @@ homed in a different Zone. The d2b v3 model is intentionally constrained: - A parent calls the child Zone's `d2b.resource.v3` service through an authenticated ZoneLink ComponentSession; it does not obtain a database handle, process credential, host path, or cross-Zone resource reference. -- Cross-Zone routing traverses the Zone tree by parent/child ZoneLinks - only. No direct lateral (sibling-to-sibling) route is provisioned - without going through a common ancestor. +- Cross-Zone routing traverses the Zone tree through sessions established by + each child's local uplink. No direct lateral (sibling-to-sibling) route is + provisioned without going through a common ancestor. - The d2b-bus resolves the outbound route for every service call and enforces native RBAC at each hop. @@ -1278,7 +1280,7 @@ status: reason: pending-delete-intents message: "2 resources pending deletion" pendingCleanup: - - { type: ZoneLink, name: old-link, zone: k0, deletionRequestedAt: 2026-07-22T21:00:00Z } + - { type: ZoneLink, name: old-link, zone: k1, deletionRequestedAt: 2026-07-22T21:00:00Z } - { type: Zone, name: removed-zone, zone: removed-zone, deletionRequestedAt: 2026-07-22T21:00:00Z } priorConfigurationGeneration: lastGenerationChange: 2026-07-22T21:00:00Z @@ -1410,8 +1412,11 @@ Required before ADR046-routing-011 through ADR046-routing-013 are complete: | `host-integration: zonelink-no-reciprocal-row` | NixOS test | Activating a child-local uplink creates no ZoneLink row in the parent store | ## Authenticated advertisements, withdrawal, and renewal -its enrolled KK key. The parent validates the signature before admitting -to its `RouteTreeEngine`. The v3 contract adapts the existing + +The child-local ZoneLink handler signs each advertisement with its enrolled KK +key. The parent validates the signature before admitting it to the parent's +`RouteTreeEngine`; this creates authenticated in-memory route projection state, +not a parent-store ZoneLink row. The v3 contract adapts the existing `RouteAdvertisement` / `RouteNamespaceAllocation` types. ### Advertisement envelope (v3 adaptation) @@ -1495,8 +1500,9 @@ route is treated as a new advertisement. ### Namespace allocation -A parent allocates a `ZoneLinkNamespaceAllocation` to the child at link -creation and when the child-local ZoneLink's `capabilityCeiling` changes: +A parent allocates a `ZoneLinkNamespaceAllocation` when the child-local +ZoneLink requests activation and when that local resource's +`capabilityCeiling` changes: ```text ZoneLinkNamespaceAllocation { @@ -2089,6 +2095,7 @@ status from locally queued intents. ``` K0: parent Zone (Host-based, runs on physical host) Host/host-system + sealed topology/route state only (no ZoneLink resource or handler) K1: child Zone (Guest VM, cloud-hypervisor) ZoneLink/k1-uplink -> K0 allocator (transport: unix socket) @@ -2133,6 +2140,7 @@ K2: subject=K1-link-principal verb=get resourceType=Process name=web-server ``` K0 (Host/host-system, local Zone) + sealed topology/route state only (no ZoneLink resource or handler) K1 (Guest/workvm, cloud-hypervisor VM) ZoneLink/k1-uplink -> K0 allocator @@ -2305,8 +2313,8 @@ Evidence classes: **A** = implemented-and-reachable from production binary, | `RealmAccessBinding`, `RealmTransportBinding`, `RealmAccessResolverRequest/Response/Error` | `access.rs` | **B** | `realm_access_resolver.rs` only (which is itself unwired) | → ZoneAccessBinding; ZoneEntrypointResolver | | `resolve_local_root_realm_access()` | `d2bd/src/realm_access_resolver.rs` | **B** | `pub mod` declared at `d2bd/src/lib.rs:117`; no callers in running daemon | → ZoneEntrypointResolver (routing-003) | | `RealmEntrypointTable`, `DispatchTarget` | `d2b-realm-router/src/target_resolver.rs` | **A** | `d2b/src/lib.rs:5240` (`load_realm_entrypoint_table()`); `d2b/src/target_routing.rs` | → ZoneEntrypointResolver (routing-003) | -| `Route::Local`/`Route::GatewayBacked` dispatch | `d2b/src/target_routing.rs` | **A** | CLI live routing path | → ZoneLink-based routing | -| `realm list`, `realm inspect` CLI commands | `d2b/src/lib.rs:5942` | **A** | CLI; reads `realm-entrypoints.json` | → `zone list`/`zone inspect` from Zone resources | +| `Route::Local`/`Route::GatewayBacked` dispatch | `d2b/src/target_routing.rs` | **A** | CLI live routing path | → sealed topology + authenticated `ZoneRouteEngine` projection routing | +| `realm list`, `realm inspect` CLI commands | `d2b/src/lib.rs:5942` | **A** | CLI; reads `realm-entrypoints.json` | → `zone list`/`zone inspect` from compiler topology joined with authenticated route/projection status; never a parent ZoneLink list | ### Operation routing and session layer @@ -2370,8 +2378,8 @@ The following transitions are NOT simple textual renames: 2. **`WorkloadId` → Guest/Host split**: VM/sandbox workloads become `Guest`; local/bare-metal become `Host`. Classification is semantic, not mechanical. 3. **`CapabilitySet`-only authz → RBAC + capability ceiling**: current engine checks capability ceiling only. Per-hop `relay` verb RBAC is new. 4. **`RealmPath` DNS target form → Zone resource path**: grammar preserved; wire address format changes. -5. **`EntrypointMode` enum → ZoneLink transport**: `HostResident`/`GatewayBacked` mode are subsumed into ZoneLink spec. -6. **`realm-controllers.json`/`realm-identity.json` → Nix Zone/ZoneLink resources**: data is loaded today but routing/trust sessions are explicitly inert. +5. **`EntrypointMode` enum → child-local ZoneLink transport plus topology projection**: `HostResident`/`GatewayBacked` mode is replaced by the child's transport spec; parent routing and CLI inspection use sealed topology and authenticated route projection state. +6. **`realm-controllers.json`/`realm-identity.json` → sealed parent topology + child-local Zone/ZoneLink state**: data is loaded today but routing/trust sessions are explicitly inert. 7. **`d2b-realm-provider` trait family → Provider ResourceType**: trait dispatch is replaced by typed resource controllers. | Item | Treatment | @@ -2427,10 +2435,10 @@ The following transitions are NOT simple textual renames: | Reuse source | Same v3 baseline commit `b5ddbed6` | | Reuse action | adapt | | Destination | `packages/d2b-contracts/src/v3/zone_link.rs` (ZoneLink spec/status types, intent types, namespace allocation); `packages/d2b-zone-routing/src/resolver.rs` (ZoneEntrypointResolver) | -| Detailed design | Child-local ZoneLink spec/status fields; self-name and one-uplink invariants; ZoneLinkIntent record stored in the child; ZoneLinkNamespaceAllocation issued by the exact parent allocator selected in sealed Nix-compiled `parentZone` topology; ZoneEntrypointResolver with longest-suffix match over ZonePath (adapted from `RealmEntrypointTable::resolve`); no reciprocal parent-store resource; fail-closed on unknown Zone Primary reuse disposition: `adapt`. Preserved source-plan detail: extract and adapt. | -| Integration | Core-controller ZoneLink handler manages ZoneLink resources; ZoneEntrypointResolver in d2b-bus for per-call dispatch decision | +| Detailed design | Child-local ZoneLink spec/status fields; self-name and one-uplink invariants; ZoneLinkIntent record stored in the child; ZoneLinkNamespaceAllocation issued by the exact parent allocator selected in sealed Nix-compiled `parentZone` topology; ZoneEntrypointResolver with longest-suffix match over ZonePath (adapted from `RealmEntrypointTable::resolve`) driven only by sealed `{ childZone, parentZone }` rows plus authenticated admitted route projections; no reciprocal parent-store resource or parent ZoneLink handler; fail closed on unknown topology, absent/stale projection, or unauthenticated route Primary reuse disposition: `adapt`. Preserved source-plan detail: extract and adapt. | +| Integration | Child core-controller ZoneLink handler manages child-store ZoneLink resources; parent d2b-bus feeds sealed topology and authenticated `ZoneRouteEngine` projection state to ZoneEntrypointResolver for per-call dispatch | | Data migration | None; ZoneLink resources created from Nix configuration at v3 reset | -| Validation | Longest-suffix match vectors; ZoneLink spec validation tests; resolver fail-closed test | +| Validation | Longest-suffix match vectors over sealed topology; child-local ZoneLink spec validation; resolver rejects unknown/stale/withdrawn/unauthenticated route projections; parent-store fixture contains no ZoneLink row or handler | | Removal proof | `RealmEntrypointTable` retired after all host-daemon routing paths use ZoneEntrypointResolver | ### ADR046-routing-004 @@ -2461,7 +2469,7 @@ The following transitions are NOT simple textual renames: | Reuse source | Same v3 baseline commit `b5ddbed6` | | Reuse action | adapt | | Destination | `packages/d2b-bus/src/zone_route.rs` (cross-Zone bus routing), `packages/d2b-bus/src/relay.rs` (per-hop relay handler) | -| Detailed design | Cross-Zone routing path in d2b-bus: ZoneEntrypointResolver → ZoneRouteEngine::decide_route → ZoneLink ComponentSession per hop; hop-counter decrement and enforcement; RBAC `relay` verb check at each intermediate hop; idempotency key namespace (full 6-tuple) in ZoneLinkIdempotencyKey; pinned reverse path tracking; cancellation forwarding; watch cursor forwarding and revision-expired handling; no-FD/credential structural rejection at serialization boundary Primary reuse disposition: `adapt`. Preserved source-plan detail: extract and adapt. | +| Detailed design | Cross-Zone routing path in d2b-bus: ZoneEntrypointResolver consumes sealed topology plus authenticated route projections → ZoneRouteEngine::decide_route → admitted ComponentSession established by each next-hop child's local ZoneLink; hop-counter decrement and enforcement; RBAC `relay` verb check at each intermediate hop; idempotency key namespace (full 6-tuple) in ZoneLinkIdempotencyKey; pinned reverse path tracking; cancellation forwarding; watch cursor forwarding and revision-expired handling; no-FD/credential structural rejection at serialization boundary. No parent route step performs Resource API Get/List/Watch on ZoneLink Primary reuse disposition: `adapt`. Preserved source-plan detail: extract and adapt. | | Integration | ResourceClient → d2b-bus → ZoneLink CS → intermediate zone → target zone; cancel/watch/stream all use the same routing path | | Data migration | None — full d2b 3.0 reset; no prior state to migrate | | Validation | End-to-end K0→K1→K2 resource call; relay-denied/hop-limit/FD-rejection tests; idempotency namespace collision tests; cancellation delivery tests; watch resync tests | @@ -2584,15 +2592,15 @@ The following transitions are NOT simple textual renames: | Field | Value | | --- | --- | | Work item ID | `ADR046-routing-016` | -| Dependency/owner | ADR046-routing-007, ADR046-routing-001; Zone service owner | +| Dependency/owner | ADR046-routing-007, ADR046-routing-002, ADR046-routing-004, ADR046-routing-012; Zone service owner | | Current source | `packages/d2b-realm-router/src/service_v2.rs` (v3 baseline `b5ddbed6`): `RealmServiceServer` (bootstrap/enroll/resolve_route/authorize_shortcut/revoke_shortcut/report_shortcut_close/inspect/cancel), `RealmServiceProcess`, `RealmSessionAuthority`, `CredentialCustody`, `RealmServiceLimits`, `RealmAuditEvent`/`RealmMethod`/`RealmAuditOutcome`, `BootstrapBinding`/`EnrollmentBinding`/`ShortcutBinding`/`MutationRecord`; constants `DEFAULT_MAX_REALM_BINDINGS=256`, `DEFAULT_MAX_SHORTCUTS=256`, `DEFAULT_MAX_MUTATION_RECORDS=1024`, `DEFAULT_AUDIT_CAPACITY=1024`, `MAX_CONFIGURED_BOUND=4096`, `MAX_DISPATCH_IN_FLIGHT=64`, `SHUTDOWN_TIMEOUT=5s` (evidence: v3 baseline, not main; see Baseline section — **B** from d2bd/CLI perspective, **A** within realm-router display-session use) | | Main reuse source | `packages/d2b-realm-router/src/service_v2.rs` (commit `a1cc0b2d`): same symbols, unchanged from v3 baseline in the main commit. All evidence class notes apply equally to main. | | Reuse action | adapt | | Destination | `packages/d2b-zone-routing/src/service.rs` | -| Detailed design | Rename `RealmServiceServer` → `ZoneServiceServer`; service wire name `d2b.realm.v2.RealmService` → `d2b.zone.v3.ZoneService`; rename methods (bootstrap→zone-bootstrap, enroll→zone-enroll, resolve_route→resolve-zone-route, authorize_shortcut→authorize-zone-shortcut, revoke_shortcut→revoke-zone-shortcut, report_shortcut_close→report-zone-shortcut-close, inspect→zone-inspect); replace `RealmSessionAuthority` with Zone principal + RBAC binding; replace `BootstrapBinding` with ZoneLink allocator-issued PSK binding; replace `EnrollmentBinding` with ZoneLink KK enrollment record; add `relay` verb RBAC check per hop; adapt shortcut model to ZonePath addressing; `RealmServiceLimits` defaults preserved; `MAX_DISPATCH_IN_FLIGHT=64`, `SHUTDOWN_TIMEOUT=5s` preserved; `CredentialCustody::GatewayGuest` excluded (all ZoneLink sessions are direct KK) Primary reuse disposition: `adapt`. Preserved source-plan detail: copy and adapt. | -| Integration | Zone runtime instantiates one `ZoneServiceServer` per Zone; d2b-bus routes `d2b.zone.v3.ZoneService` calls to this server; CLI uses `ZoneServiceClient` (from ADR046-routing-010) for zone inspect/enroll/route-resolve | +| Detailed design | Rename `RealmServiceServer` → `ZoneServiceServer`; service wire name `d2b.realm.v2.RealmService` → `d2b.zone.v3.ZoneService`; rename methods (bootstrap→zone-bootstrap, enroll→zone-enroll, resolve_route→resolve-zone-route, authorize_shortcut→authorize-zone-shortcut, revoke_shortcut→revoke-zone-shortcut, report_shortcut_close→report-zone-shortcut-close, inspect→zone-inspect) and add list/watch topology-projection methods. The read-only projection starts from the sealed sorted `{ childZone, parentZone }` compiler input and joins only authenticated, admitted `ZoneRouteEngine` route/projection status. It exposes no ZoneLink resource name, UID, spec, status, Provider ref, fingerprint, transport setting, or handle. Replace `RealmSessionAuthority` with Zone principal + RBAC binding; replace `BootstrapBinding` with allocator-issued PSK binding associated with the child's local ZoneLink; replace `EnrollmentBinding` with the corresponding KK enrollment record; add `relay` verb RBAC check per hop; adapt shortcut model to ZonePath addressing; `RealmServiceLimits` defaults preserved; `MAX_DISPATCH_IN_FLIGHT=64`, `SHUTDOWN_TIMEOUT=5s` preserved; `CredentialCustody::GatewayGuest` excluded (all ZoneLink sessions are direct KK) Primary reuse disposition: `adapt`. Preserved source-plan detail: copy and adapt. | +| Integration | Zone runtime instantiates one `ZoneServiceServer` per Zone; d2b-bus routes `d2b.zone.v3.ZoneService` calls to this server; CLI uses `ZoneServiceClient` (from ADR046-routing-010) for topology list/inspect/watch, enrollment, and route resolution | | Data migration | None; v3 Zone service is new; no v2 realm-service compatibility | -| Validation | Bootstrap/enroll/resolve-route/shortcut integration tests against fake ZoneLink; relay-verb RBAC test; KK enrollment test; shortcut ZonePath addressing test; concurrent dispatch bound test (64 in-flight) | +| Validation | Bootstrap/enroll/resolve-route/shortcut integration tests against a child-local fake ZoneLink; topology list/inspect/watch golden vectors contain exact `{ childZone, parentZone }` rows plus authenticated status and no ZoneLink fields; stale/withdrawn/unauthenticated projection tests; parent-store no-row/no-handler test; relay-verb RBAC test; KK enrollment test; shortcut ZonePath addressing test; concurrent dispatch bound test (64 in-flight) | | Removal proof | `RealmServiceServer` on `d2b.realm.v2` retires after `ZoneServiceServer` handles all routing; display-session path migrates separately as part of Provider resource work | ### ADR046-routing-011 @@ -2621,10 +2629,10 @@ The following transitions are NOT simple textual renames: | Reuse source | `realm-controller-config-json.nix` structural template; `xtask gen-schemas` extension point (main `a1cc0b2d` unchanged in this area) | | Reuse action | adapt | | Destination | `nixos-modules/zone-resources-json.nix` (new), private local-root allocator bootstrap compiler/sealer input (not a ResourceSpec or public bundle), `nixos-modules/bundle-artifacts.nix` (new row for per-Zone `resource-bundle.json`), `packages/xtask/src/main.rs` (`gen-zone-schemas` subcommand emitting `docs/reference/schemas/v3/.schema.json` for Zone and ZoneLink; `gen-zone-nix-options` subcommand emitting `nixos-modules/generated/options-zones-.nix`) | -| Detailed design | `zone-resources-json.nix` iterates `d2b.zones..resources.*` to produce the canonical sorted resource list: for each entry, render `{ apiVersion, type, metadata: { name, zone, ownerRef: , labels: , annotations: }, spec: }`. Separately canonicalize sorted `{ childZone, parentZone }` rows from the compiler-only topology and seal them into the private allocator bootstrap input; `parentZone` never enters a resource bundle or `Zone.spec`, and a topology digest change releases/reallocates affected edges independently of resource `generationId`. The bundle JSON omits `managedBy` and `configurationGeneration`; the configuration service/core sets those fields when activating the validated bundle. Sort all resources by `(type, zone, name)`. Compute `generationId` as SHA-256 (lower hex) of the UTF-8 bytes of the sorted `resources` array JSON. Compute `integrity` as SHA-256 (base64url, no padding) of the full bundle JSON with integrity field zeroed. Install at `/etc/d2b/zones//resource-bundle.json` root:d2bd 0640. Canonical form: all object keys sorted lexicographically; order-significant arrays preserved; schema-declared set-like arrays sorted lexicographically; all optional fields emitted with defaults; no field renaming or restructuring. Build-time validation runs in a Nix derivation: (1) validate the complete parent map (non-root required, local-root forbidden, declared target, one scalar parent, not self, acyclic, max 16 names); (2) validate each resource against the committed JSON Schema; (3) validate `transportSettings` for each child-local ZoneLink against its same-Zone Provider's `transportSettingsSchema` — `transportProviderRef` is always explicit, never inferred or defaulted; (4) verify capability ceilings are subsets of the sealed selected-parent allocator grants; (5) verify `childZoneName == metadata.zone`, at most one uplink resource per non-root Zone, and no local-root uplink; (6) check for duplicate `(type, zone, name)` tuples. Providers MUST commit their `transportSettingsSchema` before any ZoneLink can reference them. Drift gates: `xtask gen-zone-schemas && git diff --exit-code` and `xtask gen-zone-nix-options && git diff --exit-code` both wired into `make test-drift`. Add `checks.${system}.zone-schema-drift` to `flake.nix`. Primary reuse disposition: `adapt`. Preserved source-plan detail: extend and adapt. | +| Detailed design | `zone-resources-json.nix` iterates `d2b.zones..resources.*` to produce the canonical sorted resource list: for each entry, render `{ apiVersion, type, metadata: { name, zone, ownerRef: , labels: , annotations: }, spec: }`. Separately canonicalize sorted `{ childZone, parentZone }` rows from the compiler-only topology and seal them into the private allocator bootstrap input; `parentZone` never enters a resource bundle or `Zone.spec`, and a topology digest change releases/reallocates affected edges independently of resource `generationId`. Per-Zone generation is strict: local root's generated bundle contains no ZoneLink; a non-root Zone's enabled uplink and referenced transport Provider appear together only in that child's bundle; no emitter copies either resource into the selected parent's bundle. The bundle JSON omits `managedBy` and `configurationGeneration`; the configuration service/core sets those fields when activating the validated bundle. Sort all resources by `(type, zone, name)`. Compute `generationId` as SHA-256 (lower hex) of the UTF-8 bytes of the sorted `resources` array JSON. Compute `integrity` as SHA-256 (base64url, no padding) of the full bundle JSON with integrity field zeroed. Install at `/etc/d2b/zones//resource-bundle.json` root:d2bd 0640. Canonical form: all object keys sorted lexicographically; order-significant arrays preserved; schema-declared set-like arrays sorted lexicographically; all optional fields emitted with defaults; no field renaming or restructuring. Build-time validation runs in a Nix derivation: (1) validate the complete parent map (non-root required, local-root forbidden, declared target, one scalar parent, not self, acyclic, max 16 names); (2) validate each resource against the committed JSON Schema; (3) validate `transportSettings` for each child-local ZoneLink against its same-Zone Provider's `transportSettingsSchema` — `transportProviderRef` is always explicit, never inferred or defaulted; (4) verify capability ceilings are subsets of the sealed selected-parent allocator grants; (5) verify `childZoneName == metadata.zone`, at most one uplink resource per non-root Zone, and no local-root uplink; (6) check for duplicate `(type, zone, name)` tuples. Providers MUST commit their `transportSettingsSchema` before any ZoneLink can reference them. Drift gates: `xtask gen-zone-schemas && git diff --exit-code` and `xtask gen-zone-nix-options && git diff --exit-code` both wired into `make test-drift`. Add `checks.${system}.zone-schema-drift` to `flake.nix`. Primary reuse disposition: `adapt`. Preserved source-plan detail: extend and adapt. | | Integration | The local-root allocator consumes sealed parent topology independently of resource bundles; `nixos-modules/bundle-artifacts.nix` installs each per-Zone `resource-bundle.json`; ADR046-routing-013 Zone runtime reads it on startup | | Data migration | None; new artifact file | -| Validation | `drift: zone-resource-schema`, `drift: zone-nix-options`, `build: zone-bundle-deterministic`, `build: parent-topology-sealed`, `build: transport-settings-unknown-field`, `build: capability-ceiling-superset`, `build: missing-transport-provider`; run `make flake-matrix-pin` after adding flake checks | +| Validation | `drift: zone-resource-schema`, `drift: zone-nix-options`, `build: zone-bundle-deterministic`, `build: parent-topology-sealed`, `build: child-local-zonelink-bundle` (K0 has no ZoneLink; K1 contains its self-matching ZoneLink and same-Zone transport Provider; neither is copied to K0), `build: transport-settings-unknown-field`, `build: capability-ceiling-superset`, `build: missing-transport-provider`; run `make flake-matrix-pin` after adding flake checks | | Removal proof | `realm-controllers.json` artifact retires after Zone runtime is live and all hosts migrated | ### ADR046-routing-013 From 57c4026ae04d7d01f37501b1ca417775cf45327a Mon Sep 17 00:00:00 2001 From: John Vicondoa Date: Fri, 24 Jul 2026 07:42:00 -0700 Subject: [PATCH 095/115] docs: model ACA sandbox as provider endpoint --- ...6-provider-runtime-azure-container-apps.md | 411 +++++++++++++----- 1 file changed, 305 insertions(+), 106 deletions(-) diff --git a/docs/specs/providers/ADR-046-provider-runtime-azure-container-apps.md b/docs/specs/providers/ADR-046-provider-runtime-azure-container-apps.md index 8b01376be..3896bb50b 100644 --- a/docs/specs/providers/ADR-046-provider-runtime-azure-container-apps.md +++ b/docs/specs/providers/ADR-046-provider-runtime-azure-container-apps.md @@ -10,7 +10,7 @@ | Main reuse | `a1cc0b2da4a08ca3240a770a972fe4da6f912bef` | | Normative | Yes | | Owners | `packages/d2b-provider-runtime-azure-container-apps/` | -| Depends on | `ADR-046-provider-model-and-packaging`, `ADR-046-resources-host-guest-process-user`, `ADR-046-resources-credential`, `ADR-046-componentsession-and-bus`, `ADR-046-zone-routing`, `ADR-046-provider-state`, `ADR-046-telemetry-audit-and-support`, `ADR-046-nix-configuration` | +| Depends on | `ADR-046-provider-model-and-packaging`, `ADR-046-resources-host-guest-process-user`, `ADR-046-resources-credential`, `ADR-046-componentsession-and-bus`, `ADR-046-provider-state`, `ADR-046-telemetry-audit-and-support`, `ADR-046-nix-configuration` | | Supersedes | `packages/d2b-provider-aca/` (`AcaWorkloadProvider`, `GuestControlEndpointProvider`), `AcaRelayTransportConfig`, direct vsock guest-control path | --- @@ -23,13 +23,21 @@ | ResourceRef | `Provider/runtime-azure-container-apps` | | Implementation ID | `azure-container-apps` | | Crate | `packages/d2b-provider-runtime-azure-container-apps/` | -| Implements | `Guest` ResourceType | +| Implements | `Guest` ResourceType; standard semantic `Endpoint` resources for Provider services | | Domain | `system` only | | Placement | System-domain Processes inside the dedicated gateway Guest | -The Provider owns exactly one ResourceType: `Guest`. Two component Processes — controller and deployment service — run as system-domain Process resources **inside a dedicated gateway Guest** (`spec.config.gatewayExecutionRef`), never on the local physical Host. This is required by ADR-032: realm/cloud credentials, remote node state, and cloud-plane I/O belong inside a per-realm gateway guest VM, never in host processes, the broker, the host bundle, or host-readable storage. +The Provider's primary ResourceType is `Guest`. It also implements the standard +`Endpoint` base schema for its deployment service and per-Guest sandbox-agent +service; it defines no ACA-specific Endpoint ResourceType. Two component +Processes — controller and deployment service — run as system-domain Process +resources **inside a dedicated gateway Guest** +(`spec.config.gatewayExecutionRef`), never on the local physical Host. This is +required by ADR-032: realm/cloud credentials, remote node state, and cloud-plane +I/O belong inside a per-realm gateway guest VM, never in host processes, the +broker, the host bundle, or host-readable storage. -The Host holds only the opaque ZoneLink transport binding to the gateway Guest (standard `runtime-cloud-hypervisor` or equivalent relationship). The Host has no Process resources from this Provider, no Credential resources scoped to the ACA environment, no Azure management HTTP sockets, and no co-located `AcaControl` implementation. There is no Host-fallback mode. +The Host participates only in the ordinary local-VM realization of the gateway Guest (`runtime-cloud-hypervisor` or equivalent). The Host has no Process resources from this Provider, no Credential resources scoped to the ACA environment, no Azure management HTTP sockets, no remote-sandbox Endpoint, and no co-located `AcaControl` implementation. There is no Host-fallback mode. All interaction with Azure Container Apps APIs crosses the injected async effect port (`AcaControl`) and the injected credential lease client (`AcaCredentialLeaseClient`), both running inside the gateway Guest. Neither interface is reachable via ambient environment variable, SDK default chain, or fallback discovery. @@ -40,7 +48,16 @@ All interaction with Azure Container Apps APIs crosses the injected async effect A `Guest` resource backed by `Provider/runtime-azure-container-apps` represents a **remote cloud sandbox**. This Provider operates a **two-tier Guest model**: - **Tier 1 — gateway Guest** (`spec.config.gatewayExecutionRef`): A Zone-local Guest VM (backed by `Provider/runtime-cloud-hypervisor` or equivalent) that runs the ACA controller and deployment service Processes. This is the credential and cloud-control boundary. Azure credentials, Azure HTTP sockets, the `AcaControl` implementation, and the `AcaCredentialLeaseClient` all live exclusively inside this gateway Guest. -- **Tier 2 — managed ACA sandbox** (the `Guest` resources this Provider reconciles): Remote Azure Container Apps sandboxes. The controller, running inside the gateway Guest, manages the lifecycle of these remote sandboxes via the injected `AcaControl` port and reaches them over the `Provider/transport-azure-relay` ZoneLink. +- **Tier 2 — managed ACA sandbox** (the `Guest` resources this Provider reconciles): Remote Azure Container Apps sandboxes. The controller, running inside the gateway Guest, manages the lifecycle of these remote sandboxes via the injected `AcaControl` port. It reaches each sandbox agent by resolving that Guest's Provider-owned semantic `Endpoint` to an opaque byte-stream capability from `Provider/transport-azure-relay`, then establishes the authenticated ACA Provider service/session over that capability. + +A managed ACA sandbox does **not** run a d2b Zone runtime and therefore is not +a Zone. Its `Guest` resource remains in the same owning Zone as the gateway +Guest. The remote connection creates no child Zone, cross-Zone ResourceRef, +ZoneLink resource, ZoneLink route/status/cursor, or ZoneLink authority. A future +ACA image that runs a real d2b Zone runtime would be a distinct, explicitly +configured child-Zone mode with its own Zone lifecycle; that mode is out of +scope for this Provider version and is never inferred from the presence of an +ACA sandbox agent. A managed ACA sandbox `Guest` differs from a local VM Guest in the following fixed ways: @@ -48,9 +65,9 @@ A managed ACA sandbox `Guest` differs from a local VM Guest in the following fix | --- | --- | --- | | Execution substrate | Cloud Hypervisor or QEMU process on Host | Azure Container Apps sandbox in a remote ACA environment | | `spec.systemArtifactId` | NixOS system closure artifact ID | `null` — no Nix-built system; image is declared via `spec.provider.settings.configuredImageId` or `spec.provider.settings.configuredDiskId` | -| Bootstrap process | VMM Process + virtiofsd on Host | None on Host; bootstrap is the ACA sandbox reaching the gateway Guest via `Provider/transport-azure-relay` ZoneLink | +| Bootstrap process | VMM Process + virtiofsd on Host | None on Host; bootstrap is the sandbox agent opening an authenticated Provider session to the gateway Guest through its semantic Endpoint | | `spec.allowedDomains` | `[system, user]` typical | `[system]` — no local PAM user manager; user domain processes inside the sandbox are not d2b Process resources | -| Attachment types | virtiofs Volume, local Network bridge | ZoneLink only; no local virtiofsd, no Host-local bridge attachment | +| Attachment types | virtiofs Volume, local Network bridge | Provider-owned semantic Endpoint resolved to opaque transport carriage; no ZoneLink, local virtiofsd, or Host-local bridge attachment | | Controller location | VMM controller runs on Host | Controller runs inside gateway Guest; Host has no Process from this Provider | | `status.resource.bootstrapReady` | Set after VMM + guest-control vsock reach Ready | Set after the enrolled Noise KK ComponentSession from the gateway Guest to the ACA sandbox becomes established and the sandbox passes the Provider's authenticated health check | | `status.provider.details.guestIdentityDigest` | Provider-specific bounded digest | SHA-256 hex of `(sandboxId_bytes \|\| providerGeneration_be64 \|\| configFingerprint_bytes)` — not the ACA resource ID string | @@ -102,8 +119,8 @@ config: # Network reference — required for deployment service egress (all ACA API calls) networkRef: "Network/aca-gateway-egress" # optional; null disables egress in deployment service - # ZoneLink transport alias (must match a declared ZoneLink dependency alias in the Zone config) - zoneLinkAlias: "aca-relay" + # Provider transport-capability alias; resolves carriage, never a ZoneLink resource + sandboxTransportAlias: "aca-relay" ``` ### Field constraints @@ -126,6 +143,7 @@ config: | `readiness.intervalMs` | u32 | No | 1..10000; default 5000 | | `completedOperationCapacity` | usize | No | 1..1024; default 512 | | `networkRef` | ResourceRef | No | Resolves `Network/` in same Zone; passed to deployment service `networkUsage.networkRef`; null = no egress (deployment service `allowEgress: false`) | +| `sandboxTransportAlias` | string | Yes | Bounded Provider-manifest dependency alias resolving a transport Provider with opaque byte-stream carriage; not a ResourceRef and must not resolve or refer to a ZoneLink | No field may carry a subscription key, SAS token, client secret, certificate bytes, connection string, or any value that functions as an authentication secret. Fields `tenantId`, `clientId`, `subscriptionId`, `environmentId`, and `resourceGroupId` are opaque plain identifiers, not credentials. @@ -222,7 +240,10 @@ When the authorized consumer Process (the ACA deployment service credential-cons - Initiator and responder are fully enrolled Provider/component identities with registered KK static public keys. For Entra, the prologue also binds `identityGuestRef`, `loginEndpointRef`, and the observed Endpoint generation. - Profile is `Noise_KK_25519_ChaChaPoly_SHA256`; NN and IKpsk2 are forbidden for this channel. - The Noise prologue binds: Credential ResourceRef/UID/generation, consumer Provider/component generations, audience token (non-secret opaque string), route, schema fingerprint, limits, expiry/deadline, and authorization revisions. -- d2b-bus ZoneLink intermediaries (between the gateway Guest and the managed ACA sandbox) authorize route establishment but forward opaque Noise-encrypted records for the outer guest-to-sandbox session; credential token KK records are never terminated by the Host or bus. +- Transport-carriage intermediaries between the gateway Guest and the managed + ACA sandbox forward opaque Noise-encrypted records for the Provider session; + they receive no ACA semantic authority, and credential token KK records are + never terminated by the Host, transport Provider, or bus. - Token payload has a strict small bound (`MAX_TOKEN_PAYLOAD_BYTES = 8192`), zeroizing buffers, redacted Debug, replay-safe sequence counter, no logging, no audit, no metrics, and immediate close with zeroize after delivery. - Ambiguous delivery is never treated as success and is not automatically replayed outside the credential method's explicit idempotency contract. @@ -250,6 +271,30 @@ The completed-operation ledger records the opaque `OperationBinding` — not the Both components run as system-domain Process resources **inside the dedicated gateway Guest** (`spec.config.gatewayExecutionRef`). The framework `ProviderDeployment` creates these two static component Processes; neither declares a Provider state Volume (bounded non-secret operational state lives in `Guest.status`/the core Operation ledger, D087). The ACA controller never creates its own peer Processes and never writes Provider resource status directly. No component runs on the local Host. No component runs inside the managed ACA sandbox. +The remote sandbox agent is part of the ACA image, not a d2b `Process` and not +a Zone controller. The complete process/resource graph is: + +```text +owning Zone +├── Guest/aca-gateway +│ ├── Process/aca-controller +│ └── Process/aca-deployment-service +├── Guest/ (remote realization; still in owning Zone) +│ └── Endpoint/-sandbox-agent (semantic control Endpoint) +└── Provider/transport-azure-relay (carriage capability only) + +Process/aca-controller + → Endpoint/-sandbox-agent + → authorized opaque OwnedTransport/byte stream + → Noise KK ComponentSession + → d2b.aca.v3.sandbox-agent service in the remote ACA sandbox +``` + +No node in this graph is a child Zone or ZoneLink. The ACA Provider owns the +Endpoint implementation and session semantics; the Endpoint is a lifecycle +child of the ACA-backed `Guest`, while the transport Provider owns only the +opaque carriage capability. + ### 6.2 Controller component descriptor ```yaml @@ -263,7 +308,7 @@ allowedDomains: [system] cardinality: one-per-zone requiredDependencies: - alias: credential # resolves Provider/credential-managed-identity or credential-entra - - alias: zone-link # resolves Provider/transport-azure-relay + - alias: aca-relay # resolves Provider/transport-azure-relay carriage capability, not ZoneLink optionalDependencies: [] stateNamespaces: [] # no Provider state Volume; sandbox binding/adoption metadata lives in Guest.status; operation/requeue in the core Operation ledger (D087) process: @@ -475,17 +520,61 @@ spec: lifecyclePolicy: recycle-with-producer ``` +For every ACA-backed Guest, the controller also creates one stable semantic +Endpoint after the sandbox exists and before bootstrap can become ready: + +```yaml +apiVersion: resources.d2bus.org/v3 +type: Endpoint +metadata: + name: -sandbox-agent + zone: + ownerRef: Guest/ +spec: + providerRef: Provider/runtime-azure-container-apps + producerRef: Guest/ + endpointClass: control + transport: opaque-carriage + purpose: aca-sandbox-agent + serviceFingerprint: runtime-azure-container-apps.d2bus.org/sandbox-agent/v1 + locality: cross-domain + visibility: provider-internal + attachmentPolicy: launch-ticket-only + consumerPolicy: [Provider/runtime-azure-container-apps] + lifecyclePolicy: recycle-with-producer +``` + +`metadata.ownerRef` makes the Endpoint a child of the ACA-backed Guest for +child-first deletion. `spec.providerRef` makes the ACA Provider the semantic +Endpoint implementation/controller. `producerRef` does not assert that the +sandbox is a Zone; it identifies the Guest realization that produces the +service. The controller creates, observes, updates, and finalizes this Endpoint. +Thus “Provider-owned” means semantic implementation, authority, and reconcile +ownership; `ownerRef: Guest/` is the separate lifecycle edge. Nix +does not author a second remote-resource object. + ### Endpoint resources (D092) `Provider/runtime-azure-container-apps` declares conformance to the standard -`Endpoint` base schema. Stable controller/deployment service endpoints are -owned `Endpoint` resources with `ownerRef` and `producerRef`; consumers use -`Endpoint/` ResourceRefs. No raw Unix path, relay URL, fd, credential, or -cloud endpoint appears in resource spec/status or CLI output. Resolution occurs -only through authorized EffectPort/LaunchTicket flows, and unauthorized resolve -fails `endpoint-resolve-denied`. A producer restart bumps `endpointGeneration` -and delivers the normal `dependency-changed` trigger. Per-session ACA relay and -transport handles are internal and are not Endpoint resources. +`Endpoint` base schema. The stable controller/deployment service Endpoint and +each per-Guest sandbox-agent Endpoint are owned `Endpoint` resources with +`ownerRef` and `producerRef`; consumers use same-Zone `Endpoint/` +ResourceRefs. No raw Unix path, relay URL, fd, credential, cloud endpoint, or +cross-Zone reference appears in resource spec/status or CLI output. Resolution +occurs only through authorized EffectPort/LaunchTicket flows, and unauthorized +resolve fails `endpoint-resolve-denied`. A producer or sandbox-agent restart +bumps `endpointGeneration` and delivers the normal `dependency-changed` +trigger. Per-session relay and `OwnedTransport` handles are internal and are +not Endpoint resources. + +The sandbox-agent Endpoint has only standard Endpoint status: +`readiness`, `observedProducerGeneration`, `observedResourceGeneration`, +`endpointGeneration`, `connectionAvailability`, `leaseAvailability`, +`capability`, `locality`, `transport`, and bounded conditions. It has no route +phase, route intent, peer Zone, reconnect cursor, receive/send cursor, or +authority field. The ACA Provider's Operation ledger and current authenticated +session generation drive reconciliation; Endpoint status remains observation, +never authority. ### Retained opaque handles @@ -699,12 +788,20 @@ The `RuntimeAdopt` method reconciles a Guest resource that was created with a pr 3. If exactly one candidate matches by binding: record the observed `AcaSandboxRecord` binding digest in `Guest.status.provider.details` (bounded, non-secret, reverified), transition sandbox lifecycle to observed - state, set `status.resource.bootstrapReady = false` until the enrolled - ComponentSession is re-established. + state, ensure the same-Zone `-sandbox-agent` Endpoint has the + expected Guest owner/producer and Provider generation, set + `status.resource.bootstrapReady = false`, resolve a fresh opaque transport + capability, and re-establish the enrolled ComponentSession. 4. If zero candidates match: the controller proceeds with fresh `RuntimeEnsure`. 5. If multiple candidates match: set `Guest.status.phase = Degraded` with `reason: ambiguous-adoption`, emit a `critical`-severity bounded audit record (`AuditEventKind::GuestAdoptionAmbiguous`), and requeue. Do not proceed with any candidate. -Adoption is driven on every controller restart before any `RuntimeEnsure` is attempted. +Adoption is driven on every controller restart before any `RuntimeEnsure` is +attempted. No transport handle survives restart: the controller re-resolves the +Endpoint, obtains a new launch ticket and `OwnedTransport`, authenticates a new +Noise KK session, and bumps Endpoint generation when the observed agent +generation changed. It never reads, reconstructs, or accepts a ZoneLink +status/route/reconnect cursor, and no ZoneLink authority can authorize +adoption. --- @@ -728,6 +825,20 @@ All methods: - are delivered via d2b-bus `ComponentSession`; no direct Unix path, socket, or fd is exposed; - produce no log lines, trace spans, or metric events containing sandbox IDs, Azure resource identifiers, or lease handles. +The separate provider-private `d2b.aca.v3.sandbox-agent` service is produced by +each ACA-backed Guest's semantic Endpoint. It exposes only `AgentHealth`, +`AgentDrain`, and `AgentSessionClose`; it cannot mutate Zone resources or call +Azure management APIs. The gateway controller resolves the Endpoint and opens +one authenticated ComponentSession over the returned opaque transport +capability. Every request carries a monotonic deadline and cancellation token; +cancel is propagated to the in-flight service call. Named streams and records +use negotiated byte/record windows and bounded queues, so exhausted credits +apply backpressure rather than unbounded buffering. Deadline expiry, +cancellation, generation change, authentication failure, or queue saturation +closes the session with a stable bounded outcome. No request or response +contains a ZoneRef, ZoneLinkRef, route cursor, transport locator, or transport +authority. + --- ## 11 RBAC @@ -753,9 +864,9 @@ spec: - resourceTypes: [Credential] verbs: [get, update-status] providerRef: Provider/credential-managed-identity - - resourceTypes: [ZoneLink] - verbs: [get] - resourceNames: [""] + - resourceTypes: [Endpoint] + verbs: [get, create, update-spec, update-status, delete] + providerRef: Provider/runtime-azure-container-apps ``` ```yaml @@ -782,42 +893,60 @@ The `aca-deployment-service` component may only invoke methods on the `d2b.aca.v ## 12 Transport dependencies -### 12.1 ZoneLink placement and requirements - -The ZoneLink connecting to managed ACA sandboxes originates from the **gateway Guest** (`spec.config.gatewayExecutionRef`), not from the Host. The Host has no ZoneLink for this Provider and no direct path to Azure Container Apps APIs. This is the correct layering for ADR-032 compliance: the azure relay transport credential and relay connection state live inside the gateway Guest, not on the Host. - -A `Provider/runtime-azure-container-apps`-backed Guest uses a `ZoneLink` resource to connect the gateway Guest runtime to the remote ACA sandbox. The ZoneLink: - -- is declared by the operator via Nix, scoped to the Zone; -- references `Provider/transport-azure-relay` as its `transportProviderRef`; -- carries the enrolled KK static public key fingerprint of the ACA sandbox agent component; -- requires the `aca-relay` dependency alias declared in the Provider manifest to resolve this ZoneLink at runtime inside the gateway Guest. -- its `relayCredentialRef` must resolve to a Credential resource whose `scope.executionRef` matches `config.gatewayExecutionRef`. - -```yaml -apiVersion: resources.d2bus.org/v3 -type: ZoneLink -metadata: - name: aca-relay-link - zone: -spec: - transportProviderRef: Provider/transport-azure-relay - childStaticKeyFingerprint: "sha256:<64-hex-chars>" - childZoneName: null # ACA-backed zones are leaf zones; no further child Zone hierarchy - relayNamespace: "d2b-relay" - relayHybridConnection: "aca-" - transportSettings: - relayCredentialRef: "Credential/aca-managed-identity" -``` +### 12.1 Semantic Endpoint and transport capabilities + +The managed ACA sandbox is not a Zone. The gateway-to-sandbox connection is +therefore represented by the same-Zone semantic +`Endpoint/-sandbox-agent` from §7, not by a ZoneLink. The Guest +resource, Endpoint, controller Processes, Role, and Provider all remain in the +owning Zone. There is no child-Zone ResourceRef, route edge, reciprocal resource +row, or remote Zone store. + +At session establishment the ACA controller: + +1. authorizes and resolves the Endpoint through its private LaunchTicket flow; +2. resolves `config.sandboxTransportAlias` to a transport Provider capability; +3. calls the transport Provider's typed `OpenTransport`, receiving only an + opaque `OwnedTransport` byte-stream handle; +4. runs the ACA Provider's enrollment/authentication and ComponentSession + protocol over that handle; and +5. closes the session and calls `CloseTransport` on drain, cancellation, + generation change, or finalization. + +`Provider/transport-azure-relay` owns carriage only. Its relay credential and +private connection configuration are scoped to the gateway Guest and remain in +that transport Provider's configuration/credential boundary. It cannot read or +write ACA Guest or Endpoint status, authenticate the ACA service principal, +interpret service records, retain ACA session cursors, or grant semantic +authority. The ACA Provider owns Endpoint reconciliation, authenticated session +generation, deadlines, cancellation, bounded record/stream windows, and +backpressure. + +Configuration that supplies `zoneLinkAlias`, a ZoneLink ResourceRef, +`childZoneName`, Zone route intent, or ZoneLink cursor/authority state for an +ordinary ACA sandbox is rejected with +`InvalidConfiguration(aca-sandbox-is-not-zone)`. A future explicit child-Zone +mode requires a sandbox image with a real d2b Zone runtime and a separate +Provider/schema version; it is out of scope here. ### 12.2 Noise profiles for remote sessions All ComponentSession connections between the gateway Guest's ACA controller and the ACA sandbox agent use `Noise_KK_25519_ChaChaPoly_SHA256`: -- Both static public keys are known before handshake: the local Provider/controller key from the gateway Guest's KK registry; the ACA sandbox agent key from `spec.childStaticKeyFingerprint`. -- No NN session is permitted for cross-ZoneLink channels. +- Both static public keys are known before handshake: the local + Provider/controller key from the gateway Guest's KK registry and the ACA + sandbox agent key enrolled for the exact Guest UID and Endpoint generation. +- No NN session is permitted for the sandbox-agent service. - Bootstrap IKpsk2 is used only for first enrollment of a newly created sandbox; the PSK is bound to the `GuestProvision` operation ID, the sandbox UID, and a bounded expiry; it is consumed exactly once. - Replay, expiry, wrong operation/subject/purpose: fail closed. +- The KK prologue binds the owning Zone UID, Guest UID/generation, Endpoint UID/ + generation, Provider generation, service fingerprint, transport-capability + class, negotiated limits, deadline, and authorization revision. It binds no + child Zone or route. +- ComponentSession enforces monotonic deadlines, explicit cancellation, bounded + record and named-stream windows, credit-based backpressure, and bounded + queues. A peer that exceeds negotiated credits or ignores cancellation is + closed fail-closed. ### 12.3 No Host-level Azure transport from this Provider @@ -826,13 +955,17 @@ The Host holds **no** process, credential, socket, or transport binding for Azur - No Host Process with `providerRef: Provider/runtime-azure-container-apps` - No Host-scoped Credential (`scope.executionRef: Host/*`) for this Provider - No Azure management HTTPS socket opened on the Host by this Provider -- No ZoneLink connecting the Host directly to the managed ACA sandbox through this Provider +- No Host-owned remote-sandbox Endpoint or transport capability +- No ZoneLink for an ordinary managed ACA sandbox; such a resource is rejected The controller (`aca-controller`) Process, running inside the gateway Guest, holds no Azure management SDK HTTP client, no HTTPS socket to `management.azure.com`, and no direct path to ACA APIs in the Host network namespace. All Azure API calls are mediated by the co-located `AcaControl` implementation (which is instantiated by the Provider supervisor ticket inside the gateway Guest, not by the controller itself). The controller's only outbound IPC surfaces are: -- d2b-bus ComponentSession to credential and ZoneLink transport Providers (both inside the gateway Guest); +- d2b-bus ComponentSession to credential Providers and typed + `OpenTransport`/`CloseTransport`/`ObserveTransport` capability calls to the + transport Provider (all inside the gateway Guest); - d2b-bus ComponentSession to the `aca-deployment-service` (also inside the gateway Guest); -- Volume read/write via kernel filesystem operations inside the gateway Guest. +- the authenticated `d2b.aca.v3.sandbox-agent` ComponentSession resolved + through the semantic Endpoint. --- @@ -887,6 +1020,16 @@ priority lane inside the same per-resource single-flight. | `status.resource.activeProcessCount` | Integer count of non-terminal Process resources targeting this Guest | — | | Condition messages | Bounded stable reason codes (max 256 chars); no dynamic data | Token values, ARM IDs, sandbox hostnames, internal error messages | +The sandbox-agent connection is observed only on its Endpoint through the +standard fields listed in §7. `Guest.status.resource.bootstrapReady` becomes +true only when the Endpoint is Ready, its current generation has an +authenticated KK ComponentSession, and `AgentHealth` succeeds. Guest status +does not duplicate Endpoint generation or connection availability. +`Guest.status.provider.details` and Endpoint status expressly contain no +ZoneLink phase, route/peer Zone, send/receive/reconnect cursor, route intent, +transport handle, or authority. Unknown fields are rejected by the signed +schemas. + ### 13.2 Error codes (stable, bounded) `AcaControlErrorKind` maps to stable Provider error codes: @@ -931,7 +1074,12 @@ The finalization path follows the cleanup normalization from the resource store 3. **Post-commit audit append**: After the store transaction commits successfully, the controller appends `ResourceMutation { event: "deleted", trigger: "config-cleanup", zone: , resource_uid: }` to the Zone audit log using a dedup/exactly-once recovery key `(resource_uid, "deleted", generation)`. If the append fails, the controller retries using the recovery key; the record is appended exactly once even if the controller restarts after the store commit. -4. **Revocation before finalization**: All active Credential leases are revoked, the ACA sandbox is stopped and the ACA resource deleted via `AcaControl.delete_sandbox`, the enrolled KK ComponentSession is closed, and the ZoneLink transport is released — all before the final store transaction. +4. **Revocation before finalization**: All active Credential leases are + revoked, the ACA sandbox is stopped and the ACA resource deleted via + `AcaControl.delete_sandbox`, the enrolled KK ComponentSession is closed, its + opaque `OwnedTransport` is released, and the Provider-owned semantic Endpoint + is finalized child-first — all before the final store transaction. There is + no ZoneLink finalizer or ZoneLink state to release. 5. **Generation retention**: Audit revision records use count-based retention (default 3, range 1..16). No TTL-based retention. The count applies to the resource's audit revision trail, not to the ACA resource history. @@ -960,7 +1108,12 @@ Spans emitted by the controller carry only: - `d2b.operation` = stable operation name - `d2b.outcome` = `success|failure|cancelled` -No span carries the Azure sandbox ID, ARM resource path, management hostname, subscription scope, or credential-adjacent value. Trace context (`traceparent`/`tracestate`) is propagated over the ZoneLink ComponentSession channel as opaque bytes; it does not carry identity material. +No span carries the Azure sandbox ID, ARM resource path, management hostname, +subscription scope, or credential-adjacent value. Trace context +(`traceparent`/`tracestate`) is propagated over the authenticated ACA Provider +ComponentSession as opaque bytes; it does not carry identity material. No +transport locator, Endpoint name, session generation, cursor, or authority is +an attribute. --- @@ -1048,7 +1201,7 @@ d2b.zones.my-zone.resources = { intervalMs = 5000; }; completedOperationCapacity = 512; - zoneLinkAlias = "aca-relay"; + sandboxTransportAlias = "aca-relay"; }; }; }; @@ -1084,27 +1237,28 @@ d2b.zones.my-zone.resources = { }; ``` -### 15.4 ZoneLink resource - -The ZoneLink is declared in the Zone that contains the gateway Guest. The relay credential must be scoped to the gateway Guest (matching `gatewayExecutionRef`). The ZoneLink connects the gateway Guest to the managed ACA sandboxes — it is NOT a Host-to-sandbox link. - -```nix -d2b.zones.my-zone.resources = { - aca-relay-link = { - type = "ZoneLink"; - spec = { - transportProviderRef = "Provider/transport-azure-relay"; - childStaticKeyFingerprint = "sha256:<64-hex-chars>"; - transportSettings = { - relayNamespace = "d2b-relay"; - relayHybridConnection = "aca-my-zone"; - # Relay credential must also have scope.executionRef = "Guest/aca-gateway" - relayCredentialRef = "Credential/aca-managed-identity"; - }; - }; - }; -}; -``` +### 15.4 Transport capability and Endpoint ownership + +Nix declares the installed `Provider/transport-azure-relay` dependency and its +gateway-Guest-scoped private configuration through that transport Provider's +own schema. `sandboxTransportAlias = "aca-relay"` selects its opaque +`OpenTransport`/`CloseTransport`/`ObserveTransport` capability from the signed +ACA Provider manifest. It does not name a resource and cannot resolve to a +ZoneLink. + +Nix does **not** declare one connection resource per sandbox. After an +ACA-backed Guest is committed, the ACA controller creates +`Endpoint/-sandbox-agent` in the Guest's owning Zone with +`ownerRef` and `producerRef` both naming that Guest and `providerRef` naming +this Provider. The resource-store dependency graph therefore remains +Zone-local and gives deletion the required Endpoint-before-Guest order. + +Any ACA Nix option or resource fragment containing `zoneLinkAlias`, +`zoneLinkRef`, `childZoneName`, route intent, or ZoneLink cursor/authority +fields fails evaluation with an `aca-sandbox-is-not-zone` assertion. There is +no compatibility translation to `sandboxTransportAlias`. Explicit child-Zone +mode for a future image that runs a real Zone runtime requires a separate +schema and is not emitted by this module. ### 15.5 Guest resource @@ -1171,7 +1325,15 @@ The Nix compiler enforces at eval time: - exactly one of `configuredDiskId` or `configuredImageId` is set in `spec.provider.settings`; - `diskName` is present when `configuredImageId` is set; - all `spec.provider.settings` string IDs match the `^[a-z][a-z0-9-]*$` pattern and respect length bounds; -- `config.zoneLinkAlias` is declared in the Provider manifest's dependency aliases. +- `config.sandboxTransportAlias` is declared in the Provider manifest's + dependency aliases and resolves a transport carriage capability, not a + resource; +- no ordinary ACA-backed Guest declares or references a ZoneLink, child Zone, + cross-Zone ResourceRef, route intent, route cursor, or ZoneLink authority; +- generated Endpoint templates use `providerRef = + "Provider/runtime-azure-container-apps"`, same-Zone Guest owner/producer + refs, `transport = "opaque-carriage"`, and contain no relay locator or + credential. No Host-scoped Credential (`scope.executionRef` matching `Host/*`) is accepted for `controlCredentialRef` or `pullCredentialRef`. The assertion fires at eval time with a descriptive message that names the offending credential resource and the required `gatewayExecutionRef`. @@ -1189,6 +1351,9 @@ Provider Pending → no Provider state Volume is created (bounded non-secret operational state lives in Guest.status/the core Operation ledger, D087) → Provider Ready → Guest resources reconciled by aca-controller (running inside gateway Guest) + → controller creates each Provider-owned sandbox-agent Endpoint in the same Zone + → Endpoint resolution returns opaque transport capability + → Noise KK Provider session + AgentHealth make Guest bootstrapReady ``` The Host is not involved in any of these steps beyond its standard hypervisor relationship to the gateway Guest. If the gateway Guest transitions to `Degraded` or `Stopped`, the Provider transitions to `Degraded` with `reason: gateway-guest-unavailable`. There is no fallback to running any controller Process on the Host. @@ -1201,7 +1366,11 @@ A Provider generation change (new package digest, config update, or credential r 2. All active Credential leases are revoked (per `revocation.onProviderGeneration = immediate`). 3. ProviderDeployment replaces controller and deployment-service Processes under the new generation. 4. The operation ledger is retained across generation changes; entries bound to old generation IDs remain valid for their remaining TTL but are not returned to callers on new-generation operations. -5. All Guest resources receive a reconcile hint; each adopts or re-provisions under the new generation. +5. All Guest and owned sandbox-agent Endpoint resources receive a reconcile + hint; each Guest adopts or re-provisions under the new generation. +6. Existing Provider sessions drain, old opaque transport handles close, each + Endpoint generation advances, and the controller resolves and authenticates + a fresh session. No route or ZoneLink cursor is migrated. ### 16.3 State schema migration @@ -1218,7 +1387,10 @@ migration infrastructure. The current `AcaWorkloadProvider` / `GuestControlEndpointProvider` implementation uses the v2 `WorkloadProvider` / `GuestControlEndpointProvider` traits and direct vsock guest-control. These traits have no compatibility window in d2b 3.0: - The old vsock guest-control path is inert at d2b 3.0 cutover. -- `AcaRelayTransportConfig` is adapted into `ZoneLink` transport settings. +- `AcaRelayTransportConfig` is split: relay-private configuration moves under + `Provider/transport-azure-relay`, while this Provider retains only the + `sandboxTransportAlias` capability selection. No ZoneLink settings are + produced. - The provider agent binary (`d2b-gateway-runtime/src/provider_agent.rs`) serves as reuse source for the deployment service component; the ACA-specific `aca_workload.rs` is excluded (see §17). ### 16.5 Removal gate @@ -1227,7 +1399,10 @@ The current `AcaWorkloadProvider` / `GuestControlEndpointProvider` implementatio - `Provider/runtime-azure-container-apps` passes the full controller conformance suite; - all test coverage from the old `d2b-provider-aca/src/tests.rs` is ported to `d2b-provider-runtime-azure-container-apps/tests/`; -- the migration map removal proof for `ADR046-aca-001` through `ADR046-aca-006` is complete. +- the migration map removal proof for `ADR046-aca-001` through `ADR046-aca-006` is complete; +- no ACA Nix emitter, schema, controller, status adapter, adoption path, + finalizer, fixture, or test constructs or consumes ZoneLink state for an + ordinary managed sandbox; the replacement Endpoint/session tests are green. Per D094, each replaced current-code test is retired with an explicit keep/adapt/move/delete disposition and a removal gate: the minimum reusable @@ -1247,7 +1422,7 @@ All sources in this section are from main commit `a1cc0b2da4a08ca3240a770a972fe4 | Source | Current location | Evidence class | Disposition | v3 Destination | | --- | --- | --- | --- | --- | | `AcaWorkloadProvider` + `GuestControlEndpointProvider impl` | `packages/d2b-provider-aca/src/lib.rs` | production-reachable | REPLACE | `d2b-provider-runtime-azure-container-apps/src/controller.rs` — new async `Guest` reconcile loop; vsock path retired | -| `AcaRelayTransportConfig` | `packages/d2b-provider-aca/src/lib.rs` (relay transport config) | production-reachable | ADAPT | ZoneLink transport settings under Provider config §15.4 | +| `AcaRelayTransportConfig` | `packages/d2b-provider-aca/src/lib.rs` (relay transport config) | production-reachable | ADAPT | Relay-private fields move to `Provider/transport-azure-relay`; ACA retains only `sandboxTransportAlias` and its semantic Endpoint/session contract (§§12, 15.4) | | `AcaControl` trait (9 methods) | `packages/d2b-provider-runtime-azure-container-apps/src/control.rs` (main) | test-only at v3 baseline | RETAIN+ADAPT | Move to `packages/d2b-contracts/src/provider_effects/aca.rs` (shared provider-effects module; no new crate); no direct provider implementation dependency from core; adapt `OperationBinding` to v3 `ProviderOperationContext` contract | | `AcaCredentialLeaseClient` trait | `packages/d2b-provider-runtime-azure-container-apps/src/control.rs` (main) | test-only at v3 baseline | RETAIN+ADAPT | Move to `packages/d2b-contracts/src/provider_effects/aca.rs`; adapt `CredentialLease` to v3 Credential resource model; provider crate remains one package | | `AcaRuntimeConfig` / `AcaSandboxProfile` / bounds constants | `packages/d2b-provider-runtime-azure-container-apps/src/types.rs` (main) | test-only at v3 baseline | RETAIN+ADAPT | Adapt to v3 `spec.provider.settings` schema fields; all bounds constants preserved | @@ -1256,15 +1431,20 @@ All sources in this section are from main commit `a1cc0b2da4a08ca3240a770a972fe4 | Lease cleanup job/executor pattern | `packages/d2b-provider-runtime-azure-container-apps/src/provider.rs` (main) | test-only at v3 baseline | RETAIN | Retain `LeaseCleanupJob`/`LeaseCleanupExecutor`/`TracingLeaseCleanupObserver` verbatim; target tracing key unchanged | | Retry/backoff (`AcaControlErrorKind` + `RetryClass`) | `packages/d2b-provider-runtime-azure-container-apps/src/control.rs` (main) | test-only at v3 baseline | RETAIN | Retain all error kind/diagnostic variants and `MAX_ACA_RETRY_AFTER_MS` | | Provider agent process entry point | `packages/d2b-gateway-runtime/src/provider_agent.rs` (main) | production-reachable at main | COPY/ADAPT (partial) | Adapt `ProviderAgentProcess`/`run_registered`/`run` as deployment service binary skeleton; exclude `aca_workload.rs` | -| `AzureRelayTransportProvider` | `packages/d2b-provider-relay/src/lib.rs` (v3 baseline) | production-reachable | REPLACE | Moved to `Provider/transport-azure-relay`; ACA-specific relay config becomes ZoneLink `transportSettings` | +| `AzureRelayTransportProvider` | `packages/d2b-provider-relay/src/lib.rs` (v3 baseline) | production-reachable | REPLACE | Moved to `Provider/transport-azure-relay` as carriage-only capability; ACA Provider owns semantic Endpoint and authenticated service/session | | v3 `d2b-provider-aca/src/tests.rs` | `packages/d2b-provider-aca/src/tests.rs` | test-only | EXTRACT+PORT | Port all test coverage to `packages/d2b-provider-runtime-azure-container-apps/tests/` | **Excluded from reuse:** - `packages/d2b-gateway-runtime/src/aca_workload.rs` — ACA-specific workload lifecycle using the main ACA Provider V2 registration path; not compatible with v3 resource model. -- `packages/d2b-daemon-access/src/relay.rs` (main) — relay credential format changed; v3 uses `d2b-provider-relay` ZoneLink format. +- `packages/d2b-daemon-access/src/relay.rs` (main) — relay credential format and + ownership changed; v3 transport carriage is supplied by + `Provider/transport-azure-relay` and ACA semantics remain in this Provider. - `packages/d2b-gateway/` orchestrator — uses main's Zone model; excluded. -- Any main code that references `GUEST_SESSION_CREDENTIAL_*` handshake constants or `EndpointRole::GuestBootstrap`/`GuestDirect` — these are ADR45 guest bootstrap paths; v3 guest enrollment uses the ZoneLink resource model. +- Any main code that references `GUEST_SESSION_CREDENTIAL_*` handshake constants + or `EndpointRole::GuestBootstrap`/`GuestDirect` — these are ADR45 guest + bootstrap paths; v3 ACA enrollment uses the owned semantic Endpoint and + authenticated Provider service/session. --- @@ -1278,10 +1458,10 @@ All sources in this section are from main commit `a1cc0b2da4a08ca3240a770a972fe4 | Current source | `packages/d2b-provider-aca/src/lib.rs`: `AcaWorkloadProvider`, 2841 lines production-reachable; `packages/d2b-provider-runtime-azure-container-apps/src/provider.rs`: `AzureContainerAppsRuntimeProvider`, 2796 lines (test-only at v3 baseline) | | Reuse action | replace | | Destination | `packages/d2b-provider-runtime-azure-container-apps/src/controller.rs` | -| Detailed design | Async `Guest` reconcile loop: `describe` → `validateSpec` → `plan` → `reconcile` → `observe` → `finalize`. Adoption before first `RuntimeEnsure`. Operation ledger persisted to Volume inside gateway Guest. Credential lease acquire/revoke per call. Noise KK enrollment via ZoneLink (from gateway Guest to managed ACA sandbox). `providerPhase` and `guestIdentityDigest` in `status.provider.details`; no raw endpoint/path in any status field. **ProviderDeployment creates both static Processes; ACA controller never instantiates its own Processes and never writes Provider status directly. All Processes run inside the gateway Guest. No Host Process, no Host Credential, no Host Azure HTTP socket. Long-running cloud ops return `progressing`/`requeue-at` immediately; never block watch loop.** Primary reuse disposition: `replace`. Preserved source-plan detail: REPLACE (old) + ADAPT (main types/traits). | +| Detailed design | Async `Guest` reconcile loop: `describe` → `validateSpec` → `plan` → `reconcile` → `observe` → `finalize`. Adoption before first `RuntimeEnsure`; operation/requeue truth remains in the core Operation ledger and no Provider state Volume is created. Credential lease acquire/revoke per call. The controller creates a same-Zone Provider-owned semantic sandbox-agent Endpoint (`ownerRef` remains the Guest lifecycle edge), resolves opaque transport carriage, and performs Noise KK enrollment for the ACA Provider service/session. `providerPhase` and `guestIdentityDigest` stay in `status.provider.details`; Endpoint readiness/generation/availability stay only in Endpoint status; no raw endpoint/path, cross-Zone ref, route cursor, transport handle, or authority appears in status. **ProviderDeployment creates both static Processes; ACA controller never instantiates its own Processes and never writes Provider status directly. All Processes run inside the gateway Guest. The managed sandbox remains a Guest in the owning Zone and is not a Zone. No Host Process, no Host Credential, no Host Azure HTTP socket. Long-running cloud ops return `progressing`/`requeue-at` immediately; never block watch loop.** Primary reuse disposition: `replace`. Preserved source-plan detail: REPLACE (old) + ADAPT (main types/traits). | | Integration | Zone ResourceClient → ProviderDeployment → Process launch inside gateway Guest → d2b-bus → deployment service | | Data migration | Full d2b 3.0 reset; no v2 provider state compatibility | -| Validation | Controller conformance suite; adoption/ambiguity tests; deadline/cancellation matrix; redaction coverage; **gateway Guest placement validation: assert no Process has `executionRef: Host/*`**; Process spec field schema tests (`spec.template`, canonical `sandbox`/`budget`/`networkUsage`/`endpoints`/`readiness`/`restartPolicy` fields, `mounts` with `required: true`, `providerRef: Provider/system-minijail`); ProviderDeployment creates both Processes (controller never self-spawns); no raw endpoint/path in Guest status | +| Validation | Controller conformance suite; adoption/ambiguity tests; Endpoint create/adopt/finalize and generation tests; deadline/cancellation/backpressure matrix; redaction coverage; **gateway Guest placement validation: assert no Process has `executionRef: Host/*`**; Process spec field schema tests (`spec.template`, canonical `sandbox`/`budget`/`networkUsage`/`endpoints`/`readiness`/`restartPolicy` fields, `mounts` with `required: true`, `providerRef: Provider/system-minijail`); ProviderDeployment creates both Processes (controller never self-spawns); no raw endpoint/path or ZoneLink status/cursor/authority in Guest or Endpoint status | | Removal proof | `packages/d2b-provider-aca/` removed only after conformance suite green | ### ADR046-aca-007 @@ -1292,10 +1472,10 @@ All sources in this section are from main commit `a1cc0b2da4a08ca3240a770a972fe4 | Current source | n/a — new requirement (gateway Guest placement) | | Reuse action | create | | Destination | `nixos-modules/` (gateway Guest declaration, Process template wiring, Credential scope assertion); eval-time validation module | -| Detailed design | Nix eval-time assertions for: (a) `gatewayExecutionRef` resolves to a `Guest` resource, not `Host/*`; (b) Credential `scope.executionRef` matches `gatewayExecutionRef`; (c) all Process templates emitted for this Provider have `executionRef` equal to `gatewayExecutionRef`. No `User` resource or `users.users.*` declarations required — component principals are framework-assigned and not OS accounts. Gateway Guest NixOS closure includes only the two ACA component binaries (§15.6). Assertion error messages name the offending resource and the required `gatewayExecutionRef`. | +| Detailed design | Nix eval-time assertions for: (a) `gatewayExecutionRef` resolves to a `Guest` resource, not `Host/*`; (b) Credential `scope.executionRef` matches `gatewayExecutionRef`; (c) all Process templates emitted for this Provider have `executionRef` equal to `gatewayExecutionRef`; (d) `sandboxTransportAlias` resolves the signed carriage capability; and (e) an ordinary ACA sandbox cannot declare/reference ZoneLink, child-Zone, route-cursor, or ZoneLink-authority fields. The controller, not Nix, creates each same-Zone Provider-owned sandbox-agent Endpoint with the Guest as lifecycle `ownerRef`. No `User` resource or `users.users.*` declarations required — component principals are framework-assigned and not OS accounts. Gateway Guest NixOS closure includes only the two ACA component binaries (§15.6). Assertion error messages name the offending resource and the required `gatewayExecutionRef`; Zone-shaped ACA config fails `aca-sandbox-is-not-zone`. | | Integration | Nix eval gate; `d2b.zones.*.resources` validation pass; consumer flake usage example | | Data migration | None — full d2b 3.0 reset; no prior state to migrate | -| Validation | Nix eval assertion tests (wrong `executionRef` → assertion fires; correct setup → passes); §15.7 assertion coverage tests | +| Validation | Nix eval assertion tests (wrong `executionRef` → assertion fires; correct setup → passes; ACA ZoneLink/child-Zone fields → `aca-sandbox-is-not-zone`); Endpoint template ownership/shape and §15.7 assertion coverage tests | | Removal proof | n/a — ongoing eval-time constraint | ### ADR046-aca-002 @@ -1330,15 +1510,15 @@ All sources in this section are from main commit `a1cc0b2da4a08ca3240a770a972fe4 | Field | Value | | --- | --- | -| Dependency/owner | ADR046-session-001; ZoneLink transport owner | +| Dependency/owner | ADR046-session-001; ACA Endpoint/session and transport-capability integration owner | | Current source | `packages/d2b-provider-relay/src/lib.rs`: `AzureRelayTransportProvider`; `packages/d2b-provider-aca/src/lib.rs`: `AcaRelayTransportConfig` | | Reuse action | replace | -| Destination | ZoneLink resource `spec.transportSettings` (§15.4); `Provider/transport-azure-relay` dossier (separate) | -| Detailed design | `AcaRelayTransportConfig` fields mapped to `transportSettings.relayNamespace`, `.relayHybridConnection`, `.relayCredentialRef`. KK enrollment replaces ZoneLink transport. Primary reuse disposition: `replace`. Preserved source-plan detail: REPLACE (both); ADAPT config fields to ZoneLink transport settings. | -| Integration | ZoneLink controller resolves transport Provider; ACA controller establishes enrolled KK ComponentSession over relay transport after `GuestProvision` | +| Destination | ACA sandbox-agent Endpoint/session controller (§§7, 12); `Provider/transport-azure-relay` private configuration and carriage dossier (separate) | +| Detailed design | Split `AcaRelayTransportConfig`: relay-private namespace/connection/credential fields move behind `Provider/transport-azure-relay`; this Provider retains only `sandboxTransportAlias`. The ACA controller creates the Provider-owned semantic Endpoint (with the Guest lifecycle edge), resolves an opaque `OwnedTransport`, and owns KK enrollment, authenticated `d2b.aca.v3.sandbox-agent` service/session generation, deadlines, cancel, bounded queues, credits/backpressure, reconcile, adoption, and finalization. The ordinary managed sandbox is explicitly not a Zone; no cross-Zone ResourceRef or ZoneLink status/cursor/authority is accepted or emitted. Primary reuse disposition: `replace`. Preserved source-plan detail: REPLACE (both); ADAPT relay fields only into the carriage Provider's private config. | +| Integration | ACA controller resolves the semantic Endpoint and transport Provider capability, then establishes the enrolled KK ComponentSession after `GuestProvision`; transport Provider supplies carriage only | | Data migration | No relay session compatibility; re-enroll on first `RuntimeAdopt` | -| Validation | ZoneLink enrollment tests; relay unavailability tests; KK re-enrollment after sandbox restart | -| Removal proof | `packages/d2b-provider-relay/` removed after `transport-azure-relay` Provider conformance | +| Validation | Endpoint ownership/resolution tests; relay unavailability tests; KK re-enrollment after sandbox/controller restart; deadline/cancel/credit-backpressure tests; schema/status tests reject ZoneLink refs, phase, cursors, and authority | +| Removal proof | `packages/d2b-provider-relay/` removed after `transport-azure-relay` Provider conformance; no ACA schema, Nix emitter, controller, status adapter, fixture, or test retains an ordinary-sandbox ZoneLink path | ### ADR046-aca-005 @@ -1348,10 +1528,10 @@ All sources in this section are from main commit `a1cc0b2da4a08ca3240a770a972fe4 | Current source | `packages/d2b-provider-runtime-azure-container-apps/src/types.rs` (main): `AcaRuntimeConfig`, `AcaSandboxProfile`, `AcaResourceBinding`, `AcaWorkloadQuery` — test-only at v3 baseline | | Reuse action | adapt | | Destination | `packages/d2b-provider-runtime-azure-container-apps/src/types.rs` | -| Detailed design | Replace `RealmId`/`WorkloadId` with v3 `Zone`/`Guest` UID types. `AcaResourceBinding` keys the adoption query. The provider declares **no** Provider state Volume: bounded, non-secret sandbox binding/adoption metadata lives in `Guest.status` (latest bounded observed handle digests) and in-flight operation/requeue truth lives in the core Operation ledger (D087). Neither Process mounts a state Volume; there is no `sandbox-state`/`service-state` Volume, no `User/d2b-aca-controller`/`User/d2b-aca-deployment-service` state-layout principal, and no empty identity-only Volume. On restart the controller re-derives observed binding from `Guest.status`, the core Operation ledger, and an external `find_sandboxes` query, treating status as observation, never authority. Host never holds cloud binding, admission, PSK, or operation state. Primary reuse disposition: `adapt`. Preserved source-plan detail: RETAIN+ADAPT. | -| Integration | No Provider state Volume is created before Processes start; the controller writes its bounded observed binding/adoption metadata to `Guest.status` on material change and reads in-flight operation state from the core Operation ledger adapter | +| Detailed design | Replace `RealmId`/`WorkloadId` with v3 owning-`Zone`/`Guest` UID types. `AcaResourceBinding` keys the adoption query but never names a child Zone. The provider declares **no** Provider state Volume: bounded, non-secret sandbox binding/adoption metadata lives in `Guest.status` (latest bounded observed handle digests) and in-flight operation/requeue truth lives in the core Operation ledger (D087). Neither Process mounts a state Volume; there is no `sandbox-state`/`service-state` Volume, no `User/d2b-aca-controller`/`User/d2b-aca-deployment-service` state-layout principal, and no empty identity-only Volume. On restart the controller re-derives observed binding from `Guest.status`, the core Operation ledger, and an external `find_sandboxes` query, ensures the Provider-owned Endpoint, resolves fresh transport carriage, and authenticates a fresh Provider session, treating all status as observation and never authority. Host never holds cloud binding, admission, PSK, operation, Endpoint, or session state. Primary reuse disposition: `adapt`. Preserved source-plan detail: RETAIN+ADAPT. | +| Integration | No Provider state Volume is created before Processes start; the controller writes bounded observed binding/adoption metadata to `Guest.status`, writes only standard Endpoint observations to Endpoint status, reads in-flight operation state from the core Operation ledger adapter, and retains no transport or ZoneLink cursor across restart | | Data migration | None — no state Volume at v3 `1.0` | -| Validation | Controller/service declare empty `stateNamespaces`; no `sandbox-state`/`service-state` Volume created; neither Process mounts a state Volume; `Guest.status` binding/adoption fields are bounded, non-secret, and carry no credential/endpoint/poll-URL bytes; restart re-derivation from status/core ledger/external `find_sandboxes` without a Volume; core Operation ledger adapter integration test | +| Validation | Controller/service declare empty `stateNamespaces`; no `sandbox-state`/`service-state` Volume created; neither Process mounts a state Volume; `Guest.status` binding/adoption fields and standard Endpoint status are bounded, non-secret, and carry no credential/endpoint/poll-URL/ZoneLink cursor or authority bytes; restart re-derivation from status/core ledger/external `find_sandboxes`, Endpoint ensure, and fresh KK session without a Volume; core Operation ledger adapter integration test | | Removal proof | Old in-memory-only operation ledger removed after core Operation ledger adapter passes; `operation-ledger` stateNamespace absent from component descriptor | ### ADR046-aca-006 @@ -1362,10 +1542,10 @@ All sources in this section are from main commit `a1cc0b2da4a08ca3240a770a972fe4 | Current source | `nixos-modules/options-realms-workloads.nix`: `kind = "ProviderManaged"` → ACA; `packages/d2b-provider-aca/src/lib.rs`: tracing fields | | Reuse action | replace | | Destination | `nixos-modules/` (generated Guest resource options); `packages/d2b-provider-runtime-azure-container-apps/src/{audit,metrics}.rs` | -| Detailed design | Eval-time assertions for ACA-specific invariants (§15.6). Closed OTEL label set (§13.4). Audit event schema (§13.3). Tracing target constant `d2b_provider_runtime_azure_container_apps::credential_lease_cleanup` retained. Primary reuse disposition: `replace`. Preserved source-plan detail: REPLACE (Nix emitter) + ADAPT (metric/audit shapes). | +| Detailed design | Eval-time assertions for ACA-specific invariants (§15.7), including rejection of ZoneLink/child-Zone fields for an ordinary sandbox and exact same-Zone sandbox-agent Endpoint template ownership. Closed OTEL label set (§13.4). Audit event schema (§13.3). Tracing target constant `d2b_provider_runtime_azure_container_apps::credential_lease_cleanup` retained. Primary reuse disposition: `replace`. Preserved source-plan detail: REPLACE (Nix emitter) + ADAPT (metric/audit shapes). | | Integration | Nix eval gate; `observability-otel` Provider OTEL pipeline | | Data migration | None — full d2b 3.0 reset; no prior state to migrate | -| Validation | Label cardinality policy test; audit commit-before-complete test; Nix assertion eval tests | +| Validation | Label cardinality policy test; audit commit-before-complete test; Nix assertion eval tests; generated resource scan proves no ACA sandbox ZoneLink is emitted | | Removal proof | Old Nix `ProviderManaged` workload options retired after Guest resource Nix emitter parity | --- @@ -1410,7 +1590,7 @@ Every test in `tests/` must: - compile and run with `cargo test -p d2b-provider-runtime-azure-container-apps` in a standard CI environment without network access, Azure credentials, or running Azure services; - use only injected `AcaControl` and `AcaCredentialLeaseClient` mock implementations; no real SDK adapter is linked; - be safe to run in parallel (`#[tokio::test]` with no shared mutable global state); -- not write to any path outside the test's temporary directory (injected as a fake Volume `main`-view dirfd); +- not write to any path outside the test's injected fixture directory; - pass the `no_secret_bytes_in_any_public_surface` assertion for every public struct's `Debug` output. ### 19.3 Integration README @@ -1431,6 +1611,15 @@ The controller must pass the toolkit's black-box conformance suite (`d2b-provide - `Guest` ResourceType spec validation (invalid cpuMillis, missing disk source, user domain rejection); - reconcile/observe/finalize happy path; - adoption (match, no-match, ambiguous); +- same-Zone sandbox-agent Endpoint create/observe/adopt/generation/finalize, + including Guest child-first deletion and fresh capability resolution after + controller or agent restart; +- authenticated `d2b.aca.v3.sandbox-agent` Noise KK service/session with + monotonic deadlines, cancel propagation, bounded queues, stream/record + credits, and backpressure; +- ordinary ACA sandboxes remain Guests in their owning Zone: ZoneLink refs, + child-Zone fields, route/peer status, cursors, intent, and authority are + rejected and never emitted; - credential lease acquire/revoke around each method; - status field redaction (guestIdentityDigest contains no raw sandbox ID string); - error code stability under all `AcaControlErrorKind` variants; @@ -1445,8 +1634,18 @@ The `integration/mock_azure/` module implements an in-process HTTP server that r - `rate_limit_after: Option` — returns 429 with `retry-after-ms` header after N calls; - `ambiguous_candidates: bool` — `find_sandboxes` returns multiple matches; - `lifecycle_sequence: Vec` — mock advances through states on successive reads. +- `agent_generation: u64` — advances the semantic Endpoint/session generation + without creating a Zone or route. The mock verifies that: - no call to the mock server carries a raw credential byte in any header or body (the `AcaControl` implementation under test must redact all credential material before the HTTP call); -- all sandbox IDs in API responses are treated as opaque and are never echoed into resource status or audit records. +- all sandbox IDs in API responses are treated as opaque and are never echoed into resource status or audit records; +- transport fakes expose only opaque byte-stream capabilities and cannot grant + ACA service authority; +- restart/adoption discards old capabilities, resolves the Provider-owned + Endpoint again, and authenticates a fresh KK session without reading any + ZoneLink cursor or status; +- schema, Nix, status, audit, and removal scans contain no live ZoneLink path + for an ordinary ACA sandbox; only the explicit rejection and out-of-scope + future child-Zone distinction are permitted. From 2505daf29f46eb873f2322ee82c199e82f9bfa2d Mon Sep 17 00:00:00 2001 From: John Vicondoa Date: Fri, 24 Jul 2026 07:56:59 -0700 Subject: [PATCH 096/115] docs: close broker network teardown contracts Define generation-fenced persistent tap deletion and bind USBIP firewall teardown to the existing closed action request. --- docs/specs/ADR-046-resources-network.md | 144 +++++++++---- .../ADR-046-provider-device-usbip.md | 200 ++++++++++++++---- .../ADR-046-provider-network-local.md | 143 +++++++++---- 3 files changed, 358 insertions(+), 129 deletions(-) diff --git a/docs/specs/ADR-046-resources-network.md b/docs/specs/ADR-046-resources-network.md index 6e54dfe5e..077d8203e 100644 --- a/docs/specs/ADR-046-resources-network.md +++ b/docs/specs/ADR-046-resources-network.md @@ -799,9 +799,11 @@ The broker's `CreateBridge` operation: atomically before returning, closing any race between interface creation and the controller's subsequent `ApplySysctl` defense-in-depth step. -The broker's `DeleteBridge` operation removes the kernel bridge device and all -tap ports still attached to it. It is idempotent (returns success if the -interface is already absent). +The broker's `DeleteBridge` operation removes only the kernel bridge device, +after every persistent tap has been confirmed removed through +`DeletePersistentTap`. It never cascades deletion to an attached tap: a +remaining d2b-owned tap is retryable after tap cleanup, and a foreign +port/marker fails closed. It is idempotent when the bridge is already absent. The network-local controller performs the following runtime effects through the broker's typed effect interface: @@ -809,11 +811,13 @@ broker's typed effect interface: | Broker op | Current source | Purpose | | --- | --- | --- | | `CreateBridge` | **New broker op** (v3; no v3-baseline equivalent) | Create host kernel bridge with derived IfName, MTU, STP/multicast-snooping disabled, IPv6 suppression sysctls applied atomically | -| `DeleteBridge` | **New broker op** (v3; no v3-baseline equivalent) | Remove host kernel bridge device; idempotent on absence | +| `DeleteBridge` | **New broker op** (v3; no v3-baseline equivalent) | Remove an empty host kernel bridge device; never cascade tap deletion; idempotent on absence | | `ApplyNftables` | `d2b_contracts::broker_wire::ApplyNftablesRequest`; `d2b-host/src/nftables.rs` | Install/replace the host-side `inet d2b` table | | `ApplyNmUnmanaged` | `d2b_contracts::broker_wire::ApplyNmUnmanagedRequest` | Write `00-d2b-unmanaged.conf` bridge/tap pattern block | | `ApplyRoute` | `d2b_contracts::broker_wire::ApplyRouteRequest`; `d2b-host/src/routes.rs` | Static host route to LAN CIDR via uplink bridge | | `ApplySysctl` | `d2b_contracts::broker_wire::ApplySysctlRequest`; `d2b-host/src/netlink.rs` | Per-bridge IPv6 suppression defense-in-depth (re-applied after networkd restart or sysctl drift) | +| `CreatePersistentTap` | Existing closed broker op | Create/adopt the persistent tap for an opaque attachment realization | +| `DeletePersistentTap` | **New closed broker op** paired with `CreatePersistentTap` | Delete exactly one opaque, generation-fenced, d2b-owned persistent tap; validated absence is success | | `SetBridgePortFlags` | `d2b_contracts::broker_wire::SetBridgePortFlagsRequest`; `d2b-host/src/bridge_port.rs` | Isolated/neigh-suppress per-tap after tap creation | | `UpdateHostsFile` | `d2b_contracts::broker_wire::UpdateHostsFileRequest` | VM→IP entries in the `d2b-managed` /etc/hosts block | | `SeedDnsmasqLease` | `d2b_contracts::broker_wire::SeedDnsmasqLeaseRequest` | Pre-seed DHCP reservations for known attachment MACs | @@ -824,10 +828,23 @@ AND via `ApplySysctl` at each reconcile cycle (defense-in-depth, handling boot-time Nix sysctl entry is required for specific bridge IfNames because bridges are created dynamically and do not exist at host activation. -**Tap creation** (`CreatePersistentTap` / `CreateTapFd` broker ops) is called by -`Provider/runtime-cloud-hypervisor` when reconciling a Guest resource, not by -the network-local controller. The network-local controller observes tap state -through the owned Guest status fields. +**Persistent-tap creation** is declared by the network-local controller through +its semantic EffectPort and maps to `CreatePersistentTap`; Core supplies the +resulting FD privately to `Provider/runtime-cloud-hypervisor` through the +LaunchTicket path. `CreateTapFd` remains the runtime's non-persistent FD path. +On attachment removal or Network finalization, network-local waits for the +Guest/VMM FD owner to close and invokes the paired `DeletePersistentTap` +through its EffectPort. + +`DeletePersistentTapRequest` contains only an opaque attachment ID, +`expectedNetworkGeneration`, and `expectedAttachmentGeneration`. It accepts no +IfName, path, or caller-authored marker. The broker resolves trusted private +realization state, validates both generations and the d2b ownership marker, +then deletes only that tap. Already-absent is idempotent success only when the +trusted record and marker state show no foreign replacement; a stale +generation or foreign marker fails closed without deletion. The controller +retains the opaque attachment handle and retries retryable failures before +advancing or clearing a finalizer. ### DHCP and DNS lifecycle (inside net VM) @@ -961,7 +978,8 @@ The device-usbip controller consumes `Network/work-net` through a per Network, declares its own backend/relay/Binding-proxy `Process` resources, and invokes its typed `UsbipEffectPort`. The Core adapter alone resolves the opaque per-Network/per-busid intent and dispatches the closed -`UsbipBindFirewallRule` broker operation. That path owns all USBIP TCP/3240 +`UsbipBindFirewallRule` broker operation with the closed action enum +`Ensure|Remove`. That path owns all USBIP TCP/3240 exposure, ownership markers, drift observation, and status. A Binding proxy receives an authorized connected relay stream through Endpoint resolution and its LaunchTicket; network-local therefore emits no generic TCP/3240 allow rule @@ -1003,8 +1021,11 @@ networks: If a Guest is listed in `spec.attachments` but its `Guest` resource does not exist, the attachment remains `Pending`; the Network is not blocked from -becoming `Ready` on its other conditions. Removal of an attachment entry -triggers `DeleteTap` for the guest's tap interface via the broker. +becoming `Ready` on its other conditions. Removal of an attachment entry waits for the Guest/VMM FD owner to close, then +triggers `DeletePersistentTap` with the retained opaque attachment ID and the +expected Network/attachment generations. The request has no IfName/path input; +validated absence succeeds, while a stale generation or foreign ownership +marker fails closed without deleting anything. ### Process network attachment @@ -1221,7 +1242,13 @@ The network-local controller implements the full reconciliation contract from `Process` resource (and local DNS bridge `Process` if `dnsmasqLocal = true`) with `executionRef: Guest/`. j. Set bridge port flags (Isolated, neigh-suppress) for each tap via broker. - k. Commit a `ResourceMutationBatch` with the Volume, Guest, and Process updates + k. For each attachment removed from the current spec, wait for its Guest/VMM + FD ownership to close, then invoke `DeletePersistentTap` through the + EffectPort with the retained opaque attachment ID and current expected + Network/attachment generations. Retain the handle across retryable + failures; refresh/requeue on generation mismatch; fail closed on an + ownership-marker conflict. + l. Commit a `ResourceMutationBatch` with the Volume, Guest, and Process updates and status. 4. Report conditions and phase. 5. On any child (Volume, Guest, or Process) mutation, receive @@ -1282,29 +1309,34 @@ controller. On deletion (strictly child-first order): request their own deletion (through their owner chain, not directly). 3. Waits for all attachment-phase statuses to become non-Ready (workload Guests are stopped by their own controllers). -4. Requests deletion of the owned guest-agent `Process/net--agent` +4. Calls `DeletePersistentTap` for every retained attachment realization using + its opaque attachment ID and expected Network/attachment generations. Each + tap is removed only after its Guest/VMM FD owner has closed. Validated absence + is success; transient failures retain the handle and retry; stale generations + refresh and requeue; a foreign ownership marker blocks cleanup. +5. Requests deletion of the owned guest-agent `Process/net--agent` and any owned mDNS `Process` resources. Waits for their Deleted watch events (each Deleted step is a single store transaction: the REVISION event with `phase = Deleted` and row/index removal happen atomically; there is no persistent phase=Deleted row for the controller to observe). -5. Updates the Volume to remove the Guest attachment entry (sets `attachments: []`); +6. Updates the Volume to remove the Guest attachment entry (sets `attachments: []`); waits for the attachment removal to be confirmed. This unbinds the read-only view from the net-VM before the Guest is stopped. -6. Deletes the owned net-VM `Guest/` resource; waits for the Deleted +7. Deletes the owned net-VM `Guest/` resource; waits for the Deleted watch event. The net VM's macvtap FD (external attachment, if any) is released as part of the VMM teardown inside `Provider/runtime-cloud-hypervisor` — the broker destroys the macvtap interface when the SpawnRunner child exits. -7. Deletes the owned `Volume/net--config` resource; waits for the +8. Deletes the owned `Volume/net--config` resource; waits for the Deleted watch event. At this point the Guest attachment has already been removed - (step 5) and the Volume backing is released cleanly. -8. Removes `inet d2b` rules scoped to this Network's ownership-id via broker + (step 6) and the Volume backing is released cleanly. +9. Removes `inet d2b` rules scoped to this Network's ownership-id via broker `ApplyNftables` (empty rule set for this UID). -9. Clears NetworkManager unmanaged config for the removed patterns via broker +10. Clears NetworkManager unmanaged config for the removed patterns via broker `ApplyNmUnmanaged`. -10. Clears /etc/hosts entries for this Network's VMs via broker `UpdateHostsFile`. -11. Deletes the LAN bridge and uplink bridge via broker `DeleteBridge` for each. - `DeleteBridge` is idempotent and succeeds if the bridge is already absent. -12. Clears the finalizer. +11. Clears /etc/hosts entries for this Network's VMs via broker `UpdateHostsFile`. +12. Deletes the LAN bridge and uplink bridge via broker `DeleteBridge` for each. + `DeleteBridge` is idempotent and succeeds if the bridge is already absent. +13. Clears the finalizer. Deletion is strictly child-first. If any owned child (Process, Guest, or Volume) cannot be deleted (finalizer blocked by a dependency), the Network deletion is @@ -1429,8 +1461,13 @@ from `ADR-046-resource-api-and-authorization`. Network-specific additions: | Bridge/tap drift reason | Yes (stable code, no paths) | diagnostic | Broker operations (`ApplyNftables`, `ApplyNmUnmanaged`, `ApplyRoute`, -`ApplySysctl`, `SetBridgePortFlags`, `UpdateHostsFile`, `SeedDnsmasqLease`, etc.) -emit their own audit records with path-free outcome codes. +`ApplySysctl`, `CreatePersistentTap`, `DeletePersistentTap`, +`SetBridgePortFlags`, `UpdateHostsFile`, `SeedDnsmasqLease`, etc.) emit their +own audit records with path-free outcome codes. `DeletePersistentTap` audit is +post-effect and contains only the exact op name, an opaque attachment digest, +the expected Network/attachment generations, outcome, error class, and +correlation ID. It contains no attachment-handle bytes, IfName, path, or +ownership-marker body. ### OTEL spans and metrics @@ -2140,9 +2177,11 @@ When `Network/work-net` is present in generation N but absent from generation N+ 2. The `NetworkDraining` condition is set by the network-local controller with `reason: configuration-generation-removed`. 3. The controller runs its normal Delete path (see [Delete](#delete)): requests - deletion of owned children in child-first order (guest-agent Process and - mDNS Processes → net-VM Guest → config Volume), waits for each Deleted - watch event, then calls broker ops and clears the finalizer. + deletion of owned children in child-first order (workload Guest/VMM FD close + → generation-fenced `DeletePersistentTap` confirmation → guest-agent Process + and mDNS Processes → net-VM Guest → config Volume), waits for each required + confirmation/Deleted watch event, then calls remaining broker ops and clears + the finalizer. 4. Core observes the finalizer cleared; the final store transaction atomically removes `Network/work-net` from the resource store and index and emits the `Deleted` REVISION event. A dedup-guarded `CleanupComplete` audit record follows @@ -2207,8 +2246,8 @@ the new spec through its normal reconcile path. Spec-driven child changes: | Spec change | Controller action | | --- | --- | -| New `attachments[]` entry | Updates config Volume (attachments.json, dnsmasq.conf); creates tap; sets bridge port flags | -| Removed `attachments[]` entry | Updates config Volume; deletes tap via broker | +| New `attachments[]` entry | Updates config Volume (attachments.json, dnsmasq.conf); creates persistent tap; stores opaque attachment realization; sets bridge port flags | +| Removed `attachments[]` entry | Updates config Volume; waits for Guest/VMM FD closure; calls generation-fenced `DeletePersistentTap`; retains handle until confirmed | | `spec.dhcp.*` / `spec.dns.*` change | Updates config Volume (dnsmasq.conf); guest-agent applies SIGHUP reload; no Guest restart required | | `spec.routing.hostBlocklist` change | Updates config Volume (nftables.rules); guest-agent applies atomic `nft replace`; no Guest restart required | | `spec.isolation.allowEastWest` change | Updates config Volume (nftables.rules); guest-agent applies atomic `nft replace`; also updates bridge port flags via broker | @@ -2246,12 +2285,16 @@ they are not Nix store paths and incur modest disk cost proportional to bundle s ### Error handling during cleanup If the network-local controller's Delete path encounters a retryable error -(e.g., broker `DeleteBridge` returns a temporary failure): +(e.g., broker `DeletePersistentTap` or `DeleteBridge` returns a temporary +failure): - The controller sets `ReconcileError` with a stable coded reason on `Network/`. - Retries proceed with exponential backoff bounded by the `ADR-046-resource-reconciliation` retry policy. +- A persistent-tap transient failure retains the opaque handle and retries with + the same fence. A generation mismatch first refreshes Network/attachment + realization and then requeues; it never blindly retries a stale delete. - The Zone `PendingCleanup` condition persists; the Zone phase remains `Degraded`. @@ -2263,6 +2306,11 @@ If cleanup is permanently blocked (terminal error): or re-declare the Network in Nix (reverting the removal) to stop the cleanup and allow re-reconciliation. +A `DeletePersistentTap` foreign-marker/ownership conflict is terminal and +fail-closed. The controller neither deletes the interface nor clears the +finalizer; status and audit expose only the stable +`attachment-ownership-conflict` code. + ### Audit records for generation transitions Core emits one audit record per generation-driven lifecycle event. All records @@ -2285,7 +2333,7 @@ resource names appear in these records. | Current anchor | `nixos-modules/network.nix` (bridge/NAT/sysctl, 500+ lines), `nixos-modules/net.nix` (net-VM NixOS config, 450 lines), `nixos-modules/options-envs.nix` (`d2b.envs..*`), `nixos-modules/options-realms-network.nix` (`d2b.realms..network.*` mode/cidrs), `nixos-modules/options-vms.nix` (`d2b.vms..env` line 944, `d2b.vms..index` line 962, `d2b.vms..staticIp` deprecated line 974), `nixos-modules/options-site.nix` (`d2b.site.allowUnsafeEastWest` line 48, `d2b.hostLanCidrs` line 382), `nixos-modules/options-realms-workloads.nix` (`d2b.realms..workloads..networkIndex` line 326), `nixos-modules/host-json.nix` (emits `host.json` `environments[].nftables`, `environments[].ifNameMappings`, `environments[].usbipBusidLocks`), `nixos-modules/processes-json.nix` (emits `processes.json` `ProcessNetworkInterface`/`ProcessMacvtapInterface` per-VM runner), `nixos-modules/lib.nix` (`subnetIp` line 399, `subnetMask` line 408, `mkMac` ~line 60, `cidrOverlaps` lines 429–462), `nixos-modules/index.nix` (netMeta section), `packages/d2b-core/src/host.rs` (`NetEnv` lines 290–328, `VmRuntimeRow` lines 155–167 with `tap`/`bridge`/`net_vm`/`env` fields, `ExternalNetworkPolicy` lines 332–413, `NftablesModel` lines 520–549, `BridgePortFlags`, `TapRole`, `Ipv6SysctlEntry`, `IfNameMapping` lines 242–256), `packages/d2b-core/src/processes.rs` (`ProcessNetworkInterface` lines 98–113, `ProcessMacvtapInterface`), `packages/d2b-contracts/src/broker_wire.rs` (`ApplyNftables`, `ApplyNmUnmanaged`, `ApplyRoute`, `ApplySysctl`, `CreatePersistentTap`, `CreateTapFd`, `SetBridgePortFlags`, `UpdateHostsFile`, `SeedDnsmasqLease`), `packages/d2b-host/src/ifname.rs` (FNV-1a derivation), `packages/d2b-host/src/nftables.rs` (`NftBatch`, `hash_inet_d2b_table`, coexistence policy), `packages/d2b-host/src/bridge_port.rs`, `packages/d2b-host/src/routes.rs`, `packages/d2b-host/src/netlink.rs`, `packages/d2b-host-providers/src/lib.rs` (unwired ADR 0032 runtime/display/substrate provider adapters; no network Provider trait) | | Evidence class | All current network Nix modules, host.rs/processes.rs DTOs, and broker ops are `implemented-and-reachable`. `d2b.realms..network` with `mode="declared"` is `implemented-and-reachable` as the v2-native transitional surface (superseded by Network ResourceType at v3 reset). `packages/d2b-host-providers/src/lib.rs` provider trait surface is `implemented-but-unwired` (ADR 0032 realm trait adapters). The v3 Network ResourceType, `Provider/network-local`, and controller are `ADR-only`. `ProcessNetworkInterface`/`ProcessMacvtapInterface` are `implemented-and-reachable` in the current daemon but map to Guest spec network fields under `Provider/runtime-cloud-hypervisor`, not to NetworkSpec. | | Behavior retained | `lib.mkForce` 10-eth-dhcp neutralization; bridge isolation (`Isolated=true` default); IPv6 suppression at boot and runtime; `cidrOverlaps` arithmetic; `hostBlocklist` default set; IfName FNV-1a derivation and collision detection; per-Network east-west opt-in (`isolation.allowEastWest`); dnsmasq DHCP static reservations with `dhcp-ignore-names`; `bind-interfaces`; hardened systemd confinement; nftables `inet d2b` table with ownership markers; firewall coexistence policy matrix; net-VM nftables drop IPv6 on all chains; MSS clamp; macvtap external attachment (via SpawnRunner/runtime-ch); DHCP/static IPv4 on `external0`; egress CIDRs and MASQUERADE; port-forward DNAT; `ConfigureWithoutCarrier` on uplink bridge (emitter-owned) | -| Required delta | Network ResourceType schema; spec/status API; `network-local` Provider and controller crate; async reconcile loop; owned net-VM Guest lifecycle; owned config Volume lifecycle (`Volume/net--config`) and guest-agent Process lifecycle (`Process/net--agent`) for runtime config delivery; owned mDNS Process lifecycle (D-NETWORK-001); CIDR overlap validation at reconcile time; RBAC for network resources (Network, Guest, Volume, Process, Host); OTEL spans/metrics; Nix resource emitter (`resources-network.nix`, bootstrap/static prerequisites only — no `systemd.network.netdevs`); removal of `d2b.envs.*` and `d2b.realms..network` surfaces; new broker ops `CreateBridge` and `DeleteBridge` in `broker_wire.rs` and `runtime.rs` (D-NETWORK-003) | +| Required delta | Network ResourceType schema; spec/status API; `network-local` Provider and controller crate; async reconcile loop; owned net-VM Guest lifecycle; owned config Volume lifecycle (`Volume/net--config`) and guest-agent Process lifecycle (`Process/net--agent`) for runtime config delivery; owned mDNS Process lifecycle (D-NETWORK-001); CIDR overlap validation at reconcile time; RBAC for network resources (Network, Guest, Volume, Process, Host); OTEL spans/metrics; Nix resource emitter (`resources-network.nix`, bootstrap/static prerequisites only — no `systemd.network.netdevs`); removal of `d2b.envs.*` and `d2b.realms..network` surfaces; new canonical `DeletePersistentTap` paired with `CreatePersistentTap`, plus new broker ops `CreateBridge` and `DeleteBridge`, in `broker_wire.rs` and `runtime.rs` (D-NETWORK-003) | | Reuse path | Extract `subnetIp`/`mkMac`/`cidrOverlaps` from `lib.nix`; copy IfName/derive/detect_collisions from `ifname.rs`; adapt `NetEnv`/`ExternalNetworkPolicy`/`NftablesModel`/`BridgePortFlags`/`TapRole`/`Ipv6SysctlEntry`/`IfNameMapping` from `host.rs`; extract nftables/bridge-port/routes/netlink modules from `d2b-host`; adapt `net.nix` and `network.nix` into sealed v3 template and controller. `VmRuntimeRow.tap`/`bridge`/`net_vm`/`env` fields (host.rs lines 155–167) become Network attachment status fields. `ProcessNetworkInterface`/`ProcessMacvtapInterface` (processes.rs) migrate to Guest spec under Provider/runtime-cloud-hypervisor (not NetworkSpec). | | Replacement/deletion | `nixos-modules/network.nix`, `nixos-modules/net.nix`, `nixos-modules/options-envs.nix`, `nixos-modules/options-realms-network.nix`, `nixos-modules/index.nix` envMeta section removed only after `nixos-modules/resources-network.nix` and Provider/network-local controller pass parity tests; `d2b.envs.*` options removed only after the v3 cutover and consumer migration | | Feasibility proof | All network invariants have passing golden/integration tests at v3 baseline; IfName derivation has property tests; bridge-port isolation has integration tests; nftables apply has coexistence matrix unit tests; no new proof required before spec acceptance | @@ -2326,12 +2374,16 @@ ADR046-network-005 (controller creates mDNS Process resources in reconcile step `Provider/device-usbip`, not the Network controller. The device controller watches only Network identity/readiness/generation. Its typed EffectPort privately resolves the Network UID and dispatches the closed -`UsbipBindFirewallRule` broker operation for exact per-Network/per-busid -TCP/3240 exposure. Device-usbip owns one multiplexed relay `Endpoint` authority +`UsbipBindFirewallRule` broker operation with closed action enum +`Ensure|Remove` for exact per-Network/per-busid TCP/3240 exposure. +`Remove` is generation-bound, ownership-scoped, foreign-marker fail-closed, and +idempotent after validated absence. Device-usbip owns one multiplexed relay `Endpoint` authority per Network and owns its firewall drift/status. Network-local emits no USBIP rule on either host or net VM, and its digest excludes device-usbip ownership markers. `Network.spec` has no `usbipCarveOut` or device-usbip extension field; -the device-usbip controller does not mutate Network desired spec. +the device-usbip controller does not mutate Network desired spec. Firewall +status/token and relay authority are released only after the broker confirms +the `Remove` effect. **Rationale**: USBIP is a device access mechanism, not a network routing mechanism. Placing its process lifecycle inside the Network controller would @@ -2377,8 +2429,8 @@ bridges at reconcile time). | Current source | `packages/d2b-core/src/host.rs` lines 290–520 (`NetEnv`, `IfName`, `ExternalNetworkPolicy`, `NftablesModel`, `BridgePortFlags`, `TapRole`, `Ipv6SysctlEntry`, `IfNameMapping` lines 242–256; **also** `VmRuntimeRow` lines 155–167 with `tap`/`bridge`/`net_vm`/`env` fields — attachment status precursors); `packages/d2b-core/src/processes.rs` lines 98–141 (`ProcessNetworkInterface`, `ProcessNetworkInterfaceType`, `ProcessMacvtapInterface` — current VMM runner network interface DTOs; these are per-Guest VMM fields, not Network-level fields, and migrate to Guest spec under `Provider/runtime-cloud-hypervisor`); `packages/d2b-contracts/src/broker_wire.rs` (authoritative broker op list; network-relevant: `ApplyNftables`, `ApplyNmUnmanaged`, `ApplyRoute`, `ApplySysctl`, `SetBridgePortFlags`, `UpdateHostsFile`, `SeedDnsmasqLease`, `CreatePersistentTap`, `CreateTapFd`); `nixos-modules/lib.nix` lines 396–460 (`subnetIp`, `subnetMask`, `mkMac`, `cidrOverlaps`) | | Reuse source | None from main; all from v3 baseline | | Reuse action | adapt | -| Destination | `packages/d2b-contracts/src/v3/network.rs`: NetworkSpec, NetworkStatus, AttachmentSpec, AttachmentStatus, ExternalAttachmentSpec, ExternalAttachmentStatus, PortForwardSpec, NetworkConditionType; `packages/d2b-contracts/src/v3/ifname.rs`: IfName newtype, derivation, collision detection (extracted from `d2b-host/src/ifname.rs`). Also defines `User/net-local-controller` as a proper Resource with explicit lifecycle: `Provider/network-local`'s Nix package/module provisions the reserved `net-local-controller` OS account with a private fixed UID/GID in Host prerequisites and in the generic net-VM nixos-system artifact (same account, same UID/GID inside the Guest); the network-local controller creates and owns the User Resource (`spec.osUsername: net-local-controller`, `ownerRef: Provider/network-local`, `managedBy: controller`); `Provider/system-core` verifies the account via NSS lookup and reconciles the User Resource to Ready — it does not provision the OS account. No numeric UID/GID enters any ResourceSpec field, authz check, or audit record; `User.status` MAY carry diagnostic `uid`/`gid` values discovered by NSS lookup, but those are informational only and are never authorization inputs. The network-local controller waits for `User/net-local-controller` to reach `Ready` before creating any config Volume (reconcile precondition, not a bootstrap side effect). | -| Detailed design | Strict ResourceEnvelope with Network-specific spec/status. IfName newtype: IFNAMSIZ-1 validated, FNV-1a 64-bit derivation, base32 Crockford, 8-char suffix, bridge/tap role prefixes, detect_collisions over IfNameMapping slice. cidrOverlaps: pure Rust IPv4 arithmetic, same algorithm as lib.nix. NetworkSpec validators: /24 lanCidr with .0 base, /30 uplinkCidr, unique attachment indices 2–250, default hostBlocklist enforcement. Primary reuse disposition: `adapt`. Preserved source-plan detail: extract and adapt. | +| Destination | `packages/d2b-contracts/src/v3/network.rs`: NetworkSpec, NetworkStatus, AttachmentSpec, AttachmentStatus, ExternalAttachmentSpec, ExternalAttachmentStatus, PortForwardSpec, NetworkConditionType, opaque AttachmentHandle, and AttachmentGenerationFence; `packages/d2b-contracts/src/v3/ifname.rs`: IfName newtype, derivation, collision detection (extracted from `d2b-host/src/ifname.rs`). Also defines `User/net-local-controller` as a proper Resource with explicit lifecycle: `Provider/network-local`'s Nix package/module provisions the reserved `net-local-controller` OS account with a private fixed UID/GID in Host prerequisites and in the generic net-VM nixos-system artifact (same account, same UID/GID inside the Guest); the network-local controller creates and owns the User Resource (`spec.osUsername: net-local-controller`, `ownerRef: Provider/network-local`, `managedBy: controller`); `Provider/system-core` verifies the account via NSS lookup and reconciles the User Resource to Ready — it does not provision the OS account. No numeric UID/GID enters any ResourceSpec field, authz check, or audit record; `User.status` MAY carry diagnostic `uid`/`gid` values discovered by NSS lookup, but those are informational only and are never authorization inputs. The network-local controller waits for `User/net-local-controller` to reach `Ready` before creating any config Volume (reconcile precondition, not a bootstrap side effect). | +| Detailed design | Strict ResourceEnvelope with Network-specific spec/status. IfName newtype: IFNAMSIZ-1 validated, FNV-1a 64-bit derivation, base32 Crockford, 8-char suffix, bridge/tap role prefixes, detect_collisions over IfNameMapping slice. cidrOverlaps: pure Rust IPv4 arithmetic, same algorithm as lib.nix. NetworkSpec validators: /24 lanCidr with .0 base, /30 uplinkCidr, unique attachment indices 2–250, default hostBlocklist enforcement. The opaque attachment realization binds Network UID/generation and attachment UID/generation so deletion can supply a non-printable ID plus explicit expected generation fence without an IfName/path. Primary reuse disposition: `adapt`. Preserved source-plan detail: extract and adapt. | | Integration | Provider dossiers, Nix resource compiler, resource store/API bind these canonical types | | Data migration | Full d2b 3.0 reset; no env→Network import | | Validation | Golden JSON/CBOR vectors; CIDR overlap property tests; IfName collision and derivation determinism tests; default hostBlocklist enforcement; attachment index uniqueness; `User/net-local-controller` User resource lifecycle/readiness test: controller creates User Resource with `spec.osUsername = "net-local-controller"` (`ownerRef: Provider/network-local`); controller waits for User resource to reach `Ready` before proceeding; controller aborts with `ConfigVolumeReady=False/user-not-ready` if User resource is not Ready; verifies no numeric UID/GID appears in the Resource spec, authz check, or audit record; verifies that any diagnostic `uid`/`gid` in `User.status` is never used as an authorization input | @@ -2435,13 +2487,13 @@ bridges at reconcile time). | --- | --- | | Work item ID | `ADR046-network-005` | | Dependency/owner | ADR046-network-001–004; network-local controller owner; D-NETWORK-001, D-NETWORK-002, and D-NETWORK-003 resolved | -| Current source | `nixos-modules/network.nix` (tap/sysctl sections); `packages/d2b-host/src/{bridge_port,nftables,netlink,routes}.rs`; broker ops in `packages/d2b-contracts/src/broker_wire.rs`: **real runtime ops** `ApplyNftables`, `ApplyNmUnmanaged`, `ApplyRoute`, `ApplySysctl`, `SetBridgePortFlags`, `UpdateHostsFile`, `SeedDnsmasqLease` (all `implemented-and-reachable`); **new ops to author**: `CreateBridge`, `DeleteBridge` (do not exist in v3 baseline; must be added to `broker_wire.rs` and implemented as `RealBrokerRequest` handlers in `packages/d2b-priv-broker/src/runtime.rs`); **NOT current ops**: `CreateMacvtap` does not exist — macvtap is created inside broker's `SpawnRunner` dispatch (`packages/d2b-priv-broker/src/runtime.rs` line 5097 `live_create_macvtap_fd`) | +| Current source | `nixos-modules/network.nix` (tap/sysctl sections); `packages/d2b-host/src/{bridge_port,nftables,netlink,routes}.rs`; broker ops in `packages/d2b-contracts/src/broker_wire.rs`: **real runtime ops** `ApplyNftables`, `ApplyNmUnmanaged`, `ApplyRoute`, `ApplySysctl`, `CreatePersistentTap`, `SetBridgePortFlags`, `UpdateHostsFile`, `SeedDnsmasqLease` (all `implemented-and-reachable`); **new ops to author**: canonical `DeletePersistentTap` paired with `CreatePersistentTap`, plus `CreateBridge` and `DeleteBridge` (do not exist in v3 baseline; must be added to `broker_wire.rs` and implemented as `RealBrokerRequest` handlers in `packages/d2b-priv-broker/src/runtime.rs`); **NOT current ops**: no unsuffixed tap-deletion alias is valid, and `CreateMacvtap` does not exist — macvtap is created inside broker's `SpawnRunner` dispatch (`packages/d2b-priv-broker/src/runtime.rs` line 5097 `live_create_macvtap_fd`) | | Reuse action | adapt | | Destination | `packages/d2b-provider-network-local/src/controller.rs`: async NetworkReconciler; `packages/d2b-provider-network-local/src/plan.rs`: ReconcilePlan computation; `packages/d2b-provider-network-local/src/observe.rs`: drift-detection observe loop. Full crate layout required (see [Package and crate boundary](#package-and-crate-boundary)): `src/` (controller/plan/observe + colocated unit tests), `tests/` (hermetic conformance and state-machine tests), `integration/` (provider-system reconcile fixtures), `README.md` (Network ResourceType, controller binary, placement, RBAC, security invariants, build/test/integration commands). | -| Detailed design | Implements full async reconcile interface from `ADR-046-resource-reconciliation`. `plan()` computes desired vs. actual bridge-presence, sysctl, host-side nftables, hosts-file, NM-unmanaged, config Volume content, Guest, guest-agent Process, and mDNS-Process states. `reconcile()` dispatches in order: `CreateBridge` for each bridge not present (broker applies IPv6 sysctls atomically; `CreateBridge` failure sets `FabricReady=False/bridge-create-error` and aborts) → `ApplySysctl` (defense-in-depth IPv6) → `ApplyNftables` (host-side `inet d2b` table) → `ApplyNmUnmanaged` → `ApplyRoute` → `UpdateHostsFile` → `SeedDnsmasqLease` for new reservations → **Volume upsert** (two-phase): Phase 1 — create `Volume/net--config` with `kind: ephemeral`, `source.executionRef: Host/`, `source.settings.kind: tmpfs`, `quota: {maxBytes: 4194304, maxInodes: 128, enforcement: hard}` (tmpfs quota charged to Host memory budget), `layout` entries (root directory with `type: directory` and four config files each with `type: file`, `ownerRef: User/net-local-controller`, `groupRef: User/net-local-controller`, `mode: "0640"`, `accessAcl: []`, `defaultAcl: []`, `noFollow: true`, conservative create/repair/cleanup policies), `views: {guest-readonly: {path: "", rights: [read, traverse]}}`, `attachments: []` (no Guest attachment); abort on terminal error with `ConfigVolumeReady=False/config-volume-error`. Wait for Volume backing to reach `Ready`; requeue on `Degraded`/`Failed` with `ConfigVolumeReady=False/volume-not-ready`. Write rendered config content through Volume write service (no raw host paths). Phase 2 — create Guest upsert with `systemArtifactId` from REQUIRED `Network.spec.netVmSystemArtifactId`. Wait for Guest `Ready`. Then update Volume with Guest attachment: `attachments: [{executionRef: Guest/, transport: virtiofs, view: guest-readonly, access: read-only, mountPath: "/run/d2b/net-config", settings: {posixAcl: false, xattr: false, cache: auto, inodeFileHandles: never, threadPoolSize: null, socketGroup: null}}]`; wait for attachment `Ready`; requeue on `Degraded` with `ConfigVolumeReady=False/attachment-not-ready`. Create or update guest-agent Process `Process/net--agent` with `processClass: worker`, `sandbox: {namespaceClasses: [], capabilityClasses: [network-admin, network-bind, network-raw]}` (inherits Guest network namespace; `network-admin`→`CAP_NET_ADMIN`, `network-bind`→`CAP_NET_BIND_SERVICE`, `network-raw`→`CAP_NET_RAW`, all effective in Guest network namespace only; INV-NET-009), `mounts: [{volumeRef: Volume/net--config, view: guest-readonly, mountPath: "/run/d2b/net-config", access: read-only, required: true}]`. mDNS Process upsert when `spec.mdns.enable = true` (D-NETWORK-001) → `SetBridgePortFlags` per tap. Each broker op returns typed audit evidence. `observe()` re-reads `firewallDigest` (host-side), bridge isolation flags, IPv6 sysctls, and guest-agent Process status (`dnsmasq-bound`, `firewall-applied` predicates); queues reconcile on drift. Metrics use only the fixed semantic labels in §OTEL spans and metrics; Zone/Network identity remains in OTEL resource attributes and permitted audit fields and never enters metric labels or span attributes. **Finalizer** (strictly child-first): `NetworkDraining` → delete guest-agent Process and mDNS Processes; wait for each Deleted watch event → update Volume to remove Guest attachment (`attachments: []`); wait for attachment removal confirmed → delete `Guest/`; wait for Deleted watch event → delete `Volume/net--config`; wait for Deleted watch event → `ApplyNftables` (empty) → `ApplyNmUnmanaged` (empty) → `UpdateHostsFile` (empty) → `DeleteBridge` for each bridge (idempotent) → clear finalizer. No USBIP rules installed by Network; `UsbipBindFirewallRule` issued by device-usbip directly (D-NETWORK-002). | +| Detailed design | Implements full async reconcile interface from `ADR-046-resource-reconciliation`. `plan()` computes desired vs. actual bridge-presence, sysctl, host-side nftables, hosts-file, NM-unmanaged, config Volume content, Guest, guest-agent Process, and mDNS-Process states. `reconcile()` dispatches in order: `CreateBridge` for each bridge not present (broker applies IPv6 sysctls atomically; `CreateBridge` failure sets `FabricReady=False/bridge-create-error` and aborts) → `ApplySysctl` (defense-in-depth IPv6) → `ApplyNftables` (host-side `inet d2b` table) → `ApplyNmUnmanaged` → `ApplyRoute` → `UpdateHostsFile` → `SeedDnsmasqLease` for new reservations → **Volume upsert** (two-phase): Phase 1 — create `Volume/net--config` with `kind: ephemeral`, `source.executionRef: Host/`, `source.settings.kind: tmpfs`, `quota: {maxBytes: 4194304, maxInodes: 128, enforcement: hard}` (tmpfs quota charged to Host memory budget), `layout` entries (root directory with `type: directory` and four config files each with `type: file`, `ownerRef: User/net-local-controller`, `groupRef: User/net-local-controller`, `mode: "0640"`, `accessAcl: []`, `defaultAcl: []`, `noFollow: true`, conservative create/repair/cleanup policies), `views: {guest-readonly: {path: "", rights: [read, traverse]}}`, `attachments: []` (no Guest attachment); abort on terminal error with `ConfigVolumeReady=False/config-volume-error`. Wait for Volume backing to reach `Ready`; requeue on `Degraded`/`Failed` with `ConfigVolumeReady=False/volume-not-ready`. Write rendered config content through Volume write service (no raw host paths). Phase 2 — create Guest upsert with `systemArtifactId` from REQUIRED `Network.spec.netVmSystemArtifactId`. Wait for Guest `Ready`. Then update Volume with Guest attachment: `attachments: [{executionRef: Guest/, transport: virtiofs, view: guest-readonly, access: read-only, mountPath: "/run/d2b/net-config", settings: {posixAcl: false, xattr: false, cache: auto, inodeFileHandles: never, threadPoolSize: null, socketGroup: null}}]`; wait for attachment `Ready`; requeue on `Degraded` with `ConfigVolumeReady=False/attachment-not-ready`. Create or update guest-agent Process `Process/net--agent` with `processClass: worker`, `sandbox: {namespaceClasses: [], capabilityClasses: [network-admin, network-bind, network-raw]}` (inherits Guest network namespace; `network-admin`→`CAP_NET_ADMIN`, `network-bind`→`CAP_NET_BIND_SERVICE`, `network-raw`→`CAP_NET_RAW`, all effective in Guest network namespace only; INV-NET-009), `mounts: [{volumeRef: Volume/net--config, view: guest-readonly, mountPath: "/run/d2b/net-config", access: read-only, required: true}]`. mDNS Process upsert when `spec.mdns.enable = true` (D-NETWORK-001) → `SetBridgePortFlags` per tap. Removed attachments first wait for Guest/VMM FD ownership to close, then issue `DeletePersistentTap` with the retained opaque attachment ID and current expected Network/attachment generations; the handle remains retained until confirmed effect or validated absence. Stale generation refreshes/requeues, transient kernel error retries, and foreign marker fails closed. Each broker op returns typed audit evidence. `observe()` re-reads `firewallDigest` (host-side), bridge isolation flags, IPv6 sysctls, and guest-agent Process status (`dnsmasq-bound`, `firewall-applied` predicates); queues reconcile on drift. Metrics use only the fixed semantic labels in §OTEL spans and metrics; Zone/Network identity remains in OTEL resource attributes and permitted audit fields and never enters metric labels or span attributes. **Finalizer** (strictly child-first): `NetworkDraining` → stop workload Guests and await VMM FD closure → generation-fenced `DeletePersistentTap` for each retained attachment, awaiting confirmation → delete guest-agent Process and mDNS Processes; wait for each Deleted watch event → update Volume to remove Guest attachment (`attachments: []`); wait for attachment removal confirmed → delete `Guest/`; wait for Deleted watch event → delete `Volume/net--config`; wait for Deleted watch event → `ApplyNftables` (empty) → `ApplyNmUnmanaged` (empty) → `UpdateHostsFile` (empty) → `DeleteBridge` for each bridge (idempotent) → clear finalizer. No USBIP rules installed by Network; device-usbip issues the existing `UsbipBindFirewallRule` request with action `Ensure` for apply or `Remove` for release (D-NETWORK-002). | | Integration | Controller process registers descriptor, watches `Network` resources via d2b-bus/ComponentSession/ResourceClient. Owned Guest and Process mutations trigger owner reconciliation. Device-usbip watches only Network identity/readiness/generation; its Core adapter privately resolves relay/firewall effects (D-NETWORK-002). | | Data migration | None after full reset | -| Validation | `ADR046-reconcile-001` toolkit conformance; latency gates (p95 ≤5 ms hint-to-handler); Network-specific: CIDR conflict blocks reconcile, `CreateBridge` failure sets `FabricReady=False`, Volume creation failure sets `ConfigVolumeReady=False/config-volume-error`, `User/net-local-controller` not Ready aborts with `ConfigVolumeReady=False/user-not-ready`, Volume schema round-trip (kind=ephemeral, source.settings.kind=tmpfs, quota.enforcement=hard, layout type=file entries, views.guest-readonly.rights=[read,traverse]), tmpfs quota charged to Host memory budget (test Volume creation fails when Host memory budget exceeded), Guest not created before Volume backing `Ready`, Guest attachment not added before Guest `Ready`, guest-agent Process created after attachment `Ready` (`processClass: worker`, `namespaceClasses: []`, `capabilityClasses: [network-admin, network-bind, network-raw]`, `access: read-only`, `required: true`), host-capability leakage test: no host-netns process gains `CAP_NET_ADMIN`/`CAP_NET_BIND_SERVICE`/`CAP_NET_RAW` as result of guest-agent launch (INV-NET-009; `tests/host-integration/guest-agent-cap-confinement.nix`), `DeleteBridge` called on finalizer, Volume attachment removed before Guest deletion in finalizer (test order: agent Deleted → attachment removed → Guest Deleted → Volume Deleted → bridges), east-west invariant (INV-NET-003), hostBlocklist enforcement (INV-NET-004), macvtap attachment status (delegated to runtime-ch), mDNS Process created/deleted with `spec.mdns.enable` toggle, broker INV-NET-002 tests, config-only spec change updates Volume content and triggers agent reload without Guest restart (INV-NET-008); golden tests updated for v3 IfNames; structural metric descriptor test asserts exact absence of `vm`, `zone`, `zone_id`, `zone_uid`, `network`, and every resource-name-derived label and verifies a Network-name canary is absent from emitted label values | +| Validation | `ADR046-reconcile-001` toolkit conformance; latency gates (p95 ≤5 ms hint-to-handler); Network-specific: CIDR conflict blocks reconcile, `CreateBridge` failure sets `FabricReady=False`, Volume creation failure sets `ConfigVolumeReady=False/config-volume-error`, `User/net-local-controller` not Ready aborts with `ConfigVolumeReady=False/user-not-ready`, Volume schema round-trip (kind=ephemeral, source.settings.kind=tmpfs, quota.enforcement=hard, layout type=file entries, views.guest-readonly.rights=[read,traverse]), tmpfs quota charged to Host memory budget (test Volume creation fails when Host memory budget exceeded), Guest not created before Volume backing `Ready`, Guest attachment not added before Guest `Ready`, guest-agent Process created after attachment `Ready` (`processClass: worker`, `namespaceClasses: []`, `capabilityClasses: [network-admin, network-bind, network-raw]`, `access: read-only`, `required: true`), host-capability leakage test: no host-netns process gains `CAP_NET_ADMIN`/`CAP_NET_BIND_SERVICE`/`CAP_NET_RAW` as result of guest-agent launch (INV-NET-009; `tests/host-integration/guest-agent-cap-confinement.nix`), removed attachment and finalizer call `DeletePersistentTap` only after Guest/VMM FD closure with opaque ID/current generations, validated absence succeeds, transient failure retains handle/retries, stale generation refreshes, foreign marker blocks without deletion, request/audit contain no IfName/path, `DeleteBridge` called only after tap confirmations, Volume attachment removed before net-VM Guest deletion in finalizer (test order: workload FD closure → persistent taps deleted → agent Deleted → Volume attachment removed → net-VM Guest Deleted → Volume Deleted → bridges), east-west invariant (INV-NET-003), hostBlocklist enforcement (INV-NET-004), macvtap attachment status (delegated to runtime-ch), mDNS Process created/deleted with `spec.mdns.enable` toggle, broker INV-NET-002 tests, config-only spec change updates Volume content and triggers agent reload without Guest restart (INV-NET-008); golden tests updated for v3 IfNames; structural metric descriptor test asserts exact absence of `vm`, `zone`, `zone_id`, `zone_uid`, `network`, and every resource-name-derived label and verifies a Network-name canary is absent from emitted label values | | Removal proof | Daemon-orchestrated network/bridge lifecycle removed only after controller passes conformance and parity tests | ### ADR046-network-006 @@ -2452,8 +2504,8 @@ bridges at reconcile time). | Dependency/owner | ADR046-network-001, ADR046-network-005; test owner | | Current source | `tests/unit/nix/cases/net-vm-network.nix`; `tests/golden/pinned/net-vm-bundle-gate.txt`; `tests/golden/pinned/net-canaries.txt`; `tests/golden/pinned/host-prepare-network.txt`; `tests/host-integration/bridge-isolation.nix`; `tests/integration/live/network-isolation.sh` | | Reuse action | adapt | -| Destination | `tests/unit/nix/cases/net-vm-network.nix` (adapted to v3 resource API); updated golden pins; `tests/host-integration/bridge-isolation.nix` (adapted); `packages/d2b-priv-broker/tests/bridge_lifecycle.rs` (new hermetic broker tests). Provider crate test directories: `packages/d2b-provider-network-local/tests/` — hermetic Cargo integration tests (conformance suite, controller state machine, CIDR validation vectors, IfName determinism, invariant tests INV-NET-001–007, reconcile/observe/finalize with deterministic clock, fault injection); `packages/d2b-provider-network-local/integration/` — container/Host/Guest lifecycle fixtures invoked by `make test-integration` (bridge isolation, east-west double opt-in, nftables drift detection, macvtap lifecycle). Both directories required by package policy. | -| Detailed design | Rust integration tests: NetworkSpec CIDR validation golden vectors; AttachmentSpec index uniqueness; ExternalAttachmentSpec mutual-exclusion validation; IfName derivation determinism; CIDR overlap arithmetic; INV-NET-001 through INV-NET-009 invariant tests; reconcile/observe/finalize state machine (deterministic clock). Broker tests: `create_bridge_applies_ipv6_sysctl` (INV-NET-002 layer 1); `delete_bridge_is_idempotent`; `create_bridge_parameters_match_spec` (MTU, STP disabled, multicast snooping disabled). Controller tests: `reconcile_applies_sysctl_defense_in_depth` (INV-NET-002 layer 2); `volume_created_before_guest`; `guest_not_created_until_volume_ready`; `agent_process_created_after_guest`; `finalizer_order_agent_then_guest_then_volume_then_bridges`; `config_only_spec_change_updates_volume_no_guest_restart` (INV-NET-008); `finalizer_calls_delete_bridge`; `mdns_process_created_on_enable`; `mdns_process_deleted_on_disable`; `host_capability_leakage` (INV-NET-009). nix-unit: INV-NET-001 lib.mkForce assertion; net-VM artifact has no inline mDNS service and no per-Network dnsmasq/nftables data (INV-NET-008); Network emitter CIDR constraint assertions; no `systemd.network.netdevs` bridge entries emitted. Host integration: bridge isolation with east-west opt-in; nftables drift detection; macvtap create/delete lifecycle; config Volume update propagates to guest-agent without Guest restart; `tests/host-integration/guest-agent-cap-confinement.nix` (INV-NET-009 zero leakage to host netns). | +| Destination | `tests/unit/nix/cases/net-vm-network.nix` (adapted to v3 resource API); updated golden pins; `tests/host-integration/bridge-isolation.nix` (adapted); `packages/d2b-priv-broker/tests/{bridge_lifecycle,persistent_tap_lifecycle}.rs` (new hermetic broker tests). Provider crate test directories: `packages/d2b-provider-network-local/tests/` — hermetic Cargo integration tests (conformance suite, controller state machine, CIDR validation vectors, IfName determinism, invariant tests INV-NET-001–007, reconcile/observe/finalize with deterministic clock, fault injection); `packages/d2b-provider-network-local/integration/` — container/Host/Guest lifecycle fixtures invoked by `make test-integration` (bridge isolation, east-west double opt-in, nftables drift detection, persistent-tap and macvtap lifecycle). Both directories required by package policy. | +| Detailed design | Rust integration tests: NetworkSpec CIDR validation golden vectors; AttachmentSpec index uniqueness; ExternalAttachmentSpec mutual-exclusion validation; IfName derivation determinism; CIDR overlap arithmetic; INV-NET-001 through INV-NET-009 invariant tests; reconcile/observe/finalize state machine (deterministic clock). Broker tests: `create_bridge_applies_ipv6_sysctl` (INV-NET-002 layer 1); `delete_bridge_is_idempotent`; `delete_bridge_never_cascades_attached_tap`; `create_bridge_parameters_match_spec` (MTU, STP disabled, multicast snooping disabled); `delete_persistent_tap_pairs_with_create`; `delete_persistent_tap_absent_is_idempotent_after_ownership_validation`; `delete_persistent_tap_rejects_stale_network_generation`; `delete_persistent_tap_rejects_stale_attachment_generation`; `delete_persistent_tap_foreign_marker_fails_closed`; `delete_persistent_tap_request_and_audit_have_no_ifname_or_path`. Controller tests: `reconcile_applies_sysctl_defense_in_depth` (INV-NET-002 layer 2); `volume_created_before_guest`; `guest_not_created_until_volume_ready`; `agent_process_created_after_guest`; `removed_attachment_waits_for_vmm_then_delete_persistent_tap`; `finalizer_order_vmm_then_taps_then_agent_then_guest_then_volume_then_bridges`; `delete_persistent_tap_transient_retry_retains_handle`; `delete_persistent_tap_generation_mismatch_refreshes`; `delete_persistent_tap_foreign_marker_blocks_finalizer`; `config_only_spec_change_updates_volume_no_guest_restart` (INV-NET-008); `finalizer_calls_delete_bridge`; `mdns_process_created_on_enable`; `mdns_process_deleted_on_disable`; `host_capability_leakage` (INV-NET-009). nix-unit: INV-NET-001 lib.mkForce assertion; net-VM artifact has no inline mDNS service and no per-Network dnsmasq/nftables data (INV-NET-008); Network emitter CIDR constraint assertions; no `systemd.network.netdevs` bridge entries emitted. Host integration: bridge isolation with east-west opt-in; nftables drift detection; persistent-tap and macvtap create/delete lifecycle; config Volume update propagates to guest-agent without Guest restart; `tests/host-integration/guest-agent-cap-confinement.nix` (INV-NET-009 zero leakage to host netns). | | Integration | Pinned tests registered in `tests/golden/pinned/`; nix-unit cases in `tests/unit/nix/cases/`; host integration in `tests/host-integration/` | | Data migration | None — full d2b 3.0 reset; no prior state to migrate | | Validation | All listed tests must pass before `nixos-modules/network.nix` removal is eligible | @@ -2467,11 +2519,11 @@ bridges at reconcile time). | Dependency/owner | ADR046-network-005; device-usbip Provider dossier; D-NETWORK-002 resolved | | Current source | `nixos-modules/network.nix` lines 444–461 (USBIP host firewall); `packages/d2b-core/src/host.rs` lines 324–328 (usbip_backend_port, usbip_busid_locks in NetEnv); `packages/d2b-host/src/` usbip_argv.rs | | Reuse action | adapt | -| Destination | `Provider/device-usbip` owns one relay Process/Endpoint authority per Network plus the typed EffectPort adapter for `UsbipBindFirewallRule`. The controller watches only the `networkRef` resource's identity/readiness/generation; Core privately resolves Network UID to relay attachment and firewall intent. Network spec/status is not mutated with USBIP fields. Full crate layout required for `packages/d2b-provider-device-usbip/` (see [Package and crate boundary](#package-and-crate-boundary)): `src/` (controller and usbip runner + unit tests), `tests/` (hermetic conformance, dependency-watch state machine, UsbipBindFirewallRule round-trip), `integration/` (Host/Guest USBIP attach/detach lifecycle fixtures), `README.md` (Provider identity, provider-neutral USB Service/Binding types, USBIP Processes/Endpoints, Network least-privilege dependency contract, RBAC, security invariants, standalone-repo path). | -| Detailed design | Device-usbip's typed EffectPort is the sole semantic owner of every USBIP TCP/3240 rule. Its Core adapter resolves the opaque per-Network/per-busid intent and issues `UsbipBindFirewallRule`; its strict provider status owns firewall digest/drift. Network-local emits no generic host or net-VM TCP/3240 allow and ignores device-usbip ownership markers in Network drift. The device Provider owns exactly one multiplexed relay Endpoint authority per Network and supplies Binding proxies only authorized connected streams through LaunchTickets. Primary reuse disposition: `adapt`. Preserved source-plan detail: extract and adapt. | -| Integration | device-usbip watches Network readiness → Core adapter resolves opaque Network attachment → `UsbipBindFirewallRule` + one relay Endpoint authority → Binding proxy LaunchTicket | +| Destination | `Provider/device-usbip` owns one relay Process/Endpoint authority per Network plus the typed EffectPort adapter for the existing closed `UsbipBindFirewallRule` request with closed action enum `Ensure|Remove`. The controller watches only the `networkRef` resource's identity/readiness/generation; Core privately resolves Network UID to relay attachment and firewall intent. Network spec/status is not mutated with USBIP fields. Full crate layout required for `packages/d2b-provider-device-usbip/` (see [Package and crate boundary](#package-and-crate-boundary)): `src/` (controller and usbip runner + unit tests), `tests/` (hermetic conformance, dependency-watch state machine, `UsbipBindFirewallRule` `Ensure|Remove` round-trip), `integration/` (Host/Guest USBIP attach/detach lifecycle fixtures), `README.md` (Provider identity, provider-neutral USB Service/Binding types, USBIP Processes/Endpoints, Network least-privilege dependency contract, RBAC, security invariants, standalone-repo path). | +| Detailed design | Device-usbip's typed EffectPort is the sole semantic owner of every USBIP TCP/3240 rule. Its Core adapter resolves the opaque per-Network/per-busid intent and issues the same `UsbipBindFirewallRule` request with action `Ensure` for apply and `Remove` for release; no separate release op exists. `Remove` is generation-bound, ownership-scoped, idempotent after validated absence, and foreign-marker fail-closed. The controller retains firewall token/status and the relay authority reference until the broker confirms `Remove`; its strict provider status owns firewall digest/drift. Network-local emits no generic host or net-VM TCP/3240 allow and ignores device-usbip ownership markers in Network drift. The device Provider owns exactly one multiplexed relay Endpoint authority per Network and supplies Binding proxies only authorized connected streams through LaunchTickets. Primary reuse disposition: `adapt`. Preserved source-plan detail: extract and adapt. | +| Integration | device-usbip watches Network readiness → Core adapter resolves opaque Network attachment → `UsbipBindFirewallRule { action: Ensure, ... }` for apply or `{ action: Remove, ... }` for release + one relay Endpoint authority → Binding proxy LaunchTicket; release clears status/authority only after confirmed `Remove` | | Data migration | Current network.nix USBIP carve-out replaced by UsbipBindFirewallRule broker op | -| Validation | device-usbip conformance tests cover exact per-Network/per-busid scoping, one relay Endpoint authority, ownership-scoped drift/status, foreign-marker rejection, and release; network-local nftables tests assert no TCP/3240/USBIP rule on host or net VM and prove USBIP rule churn does not change Network `FirewallReady`; the pinned USBIP firewall golden moves to device-usbip ownership | +| Validation | device-usbip conformance tests cover the exact closed `Ensure|Remove` enum (unknown actions rejected), same-request broker mapping for apply/release, expected Network/Service generation binding, exact per-Network/per-busid scoping, idempotent validated-absence `Remove`, one relay Endpoint authority, ownership-scoped drift/status, foreign-marker rejection, transient retry, and retention of status/token/authority until effect confirmation; network-local nftables tests assert no TCP/3240/USBIP rule on host or net VM and prove USBIP rule churn does not change Network `FirewallReady`; the pinned USBIP firewall golden moves to device-usbip ownership | | Removal proof | Network.nix USBIP sections removed only after UsbipBindFirewallRule mechanism passes conformance | ### ADR046-network-008 diff --git a/docs/specs/providers/ADR-046-provider-device-usbip.md b/docs/specs/providers/ADR-046-provider-device-usbip.md index 4a935c329..6d06db85a 100644 --- a/docs/specs/providers/ADR-046-provider-device-usbip.md +++ b/docs/specs/providers/ADR-046-provider-device-usbip.md @@ -626,7 +626,9 @@ Process resources. They are semantic steps executed through injected EffectPorts - `usbip bind --busid ` — `BindBusid` host EffectPort step - `usbip unbind --busid ` — `UnbindBusid` host EffectPort step - Per-busid OFD lock acquisition / release — `AcquireLease` / `ReleaseLease` host EffectPort steps -- nftables firewall carve-out — `ApplyFirewall` / `ReleaseFirewall` host EffectPort steps +- nftables firewall carve-out — `apply_firewall` / + `UsbipBindFirewallRule { action: Ensure, ... }` and `release_firewall` / + `UsbipBindFirewallRule { action: Remove, ... }` - Guest-side `usbip attach` / `usbip detach` — `UsbipGuestEffectPort` steps The host adapter and guest supervisor adapter execute these internally. @@ -653,6 +655,10 @@ pub struct UsbBindingUid([u8; 32]); /// Zeroized on drop; Debug is redacted. pub struct LeaseToken([u8; 32]); pub struct FirewallToken([u8; 16]); +pub struct FirewallGenerationFence { + pub expected_network_generation: u64, + pub expected_service_generation: u64, +} pub struct FirewallObservation { pub matches_expected: bool, pub digest: [u8; 32], // opaque ownership-scoped digest @@ -664,6 +670,7 @@ impl fmt::Debug for NetworkUid { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> impl fmt::Debug for UsbBindingUid{ fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { write!(f, "UsbBindingUid()") } } impl fmt::Debug for LeaseToken { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { write!(f, "LeaseToken()") } } impl fmt::Debug for FirewallToken{ fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { write!(f, "FirewallToken()") } } +impl fmt::Debug for FirewallGenerationFence { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { write!(f, "FirewallGenerationFence()") } } // Opaque UID/token Clone impls are manual (not derived) to avoid // clippy::expl_impl_clone_on_copy on the non-Copy structs; this also prevents @@ -695,6 +702,7 @@ pub enum UsbipEffectError { LeaseDenied, LeaseNotHeld, FirewallDenied, + FirewallGenerationMismatch, FirewallForeignConflict, WrongZone, BackendNotReady, @@ -764,7 +772,8 @@ pub trait UsbipEffectPort: Send + Sync { lease_token: &LeaseToken, ) -> Result<(), UsbipEffectError>; - /// Acquire a reference to the Network relay's nftables carve-out. + /// Acquire a reference to the Network relay's nftables carve-out through + /// UsbipBindFirewallRule with action Ensure. /// The adapter validates that device_uid and network_uid are in the same Zone /// before dispatching UsbipBindFirewallRule through the privileged broker. /// Wrong-Zone returns WrongZone immediately without any host mutation. @@ -773,6 +782,7 @@ pub trait UsbipEffectPort: Send + Sync { device_uid: &DeviceUid, network_uid: &NetworkUid, lease_token: &LeaseToken, + expected: &FirewallGenerationFence, ) -> Result; /// Observe the exact per-Network/per-busid USBIP ownership projection. @@ -785,11 +795,15 @@ pub trait UsbipEffectPort: Send + Sync { firewall_token: &FirewallToken, ) -> Result; - /// Remove the nftables carve-out. + /// Remove the exact nftables carve-out through the same + /// UsbipBindFirewallRule request with action Remove. Validated absence is + /// success. The caller retains status/authority until this returns success. async fn release_firewall( &self, - device_uid: &DeviceUid, - firewall_token: FirewallToken, + device_uid: &DeviceUid, + network_uid: &NetworkUid, + firewall_token: &FirewallToken, + expected: &FirewallGenerationFence, ) -> Result<(), UsbipEffectError>; /// Bind the physical device to usbip-host (kernel-level). @@ -821,6 +835,35 @@ pub trait UsbipGuestEffectPort: Send + Sync { } ``` +The framework-internal broker DTO is one closed request, not separate apply and +release operations: + +```rust +#[derive(Serialize, Deserialize)] +pub enum UsbipBindFirewallRuleAction { + Ensure, + Remove, +} + +#[derive(Serialize, Deserialize)] +#[serde(deny_unknown_fields)] +pub struct UsbipBindFirewallRuleRequest { + pub action: UsbipBindFirewallRuleAction, + pub scope: OpaqueUsbipFirewallScope, + pub expected_network_generation: u64, + pub expected_service_generation: u64, +} +``` + +The closed action values are exactly `Ensure|Remove`; unknown values fail +decoding. Both actions bind the expected Network and authority Service +generations. `apply_firewall` maps only to `Ensure`; `release_firewall` maps only +to `Remove`. `OpaqueUsbipFirewallScope` is resolved by Core from the validated +Device/Network identities and carries the exact per-Network/per-busid ownership +projection without exposing its bytes to the Provider. The request accepts no +Provider-supplied IfName, address, path, busid, ownership marker, or rule body. +There is no release variant or second broker op. + ### Adapter responsibilities (framework-internal, NOT in Provider crate) The core adapter that implements `UsbipEffectPort` is owned by the framework @@ -833,9 +876,11 @@ The core adapter that implements `UsbipEffectPort` is owned by the framework if they differ. 3. Acquires / releases the per-busid OFD lock at `/run/d2b/locks/usbip/` — adapter-internal; no controller visibility. -4. Dispatches `UsbipBindFirewallRule` through the privileged broker (the sole - privileged executor) for the complete per-Network/per-busid TCP/3240 - exposure. No generic network-local allow is a prerequisite. +4. Dispatches the existing closed `UsbipBindFirewallRule` request through the + privileged broker (the sole privileged executor), using action `Ensure` for + apply and `Remove` for release, for the complete per-Network/per-busid + TCP/3240 exposure. No generic network-local allow is a prerequisite and no + separate release op exists. 5. Records the broker/core audit record **post-effect** (after the host mutation completes); audit is not atomic with resource store commits. 6. Reads the same ownership projection for drift through `observe_firewall`; @@ -843,6 +888,14 @@ The core adapter that implements `UsbipEffectPort` is owned by the framework 7. Returns typed `UsbipEffectError` variants to the controller; no broker wire types, lock paths, audit structs, or nftables details leak. +For `Remove`, the adapter supplies the opaque Network/busid ownership identity, +the token, and expected Network/Service generations. The broker validates the +generation fence and exact ownership marker before mutation. It removes only +that ownership projection; validated absence is idempotent success, while a +foreign marker fails closed. A transient error is retryable with the retained +token/fence. `FirewallGenerationMismatch` causes a fresh dependency/status read +before requeue and never deletes a newer realization. + The audit record emitted by the adapter contains: zone, device name-digest, network name-digest, operation tag, outcome, error_class, and correlation_id. No raw busid, lock path, nftables body, or vendor/product ID appears in audit. @@ -865,7 +918,7 @@ host-module/backend → physical-lock → withhold → network-firewall/relay | Host module/backend | `ensure_kernel_module` and adopt/create the Host backend Process | exactly one per Host; idempotently shared | | Physical lock | `acquire_lease(device_uid)` | exclusive OFD claim for the Service's local Device | | Withhold | `withhold_device(device_uid, token)` | requires the exclusive token | -| Network firewall/relay | reference-counted `apply_firewall`, `observe_firewall`, and adopt/create the Network relay Process/Endpoint authority | exactly one TCP 3240 multiplexer and firewall authority per Network; exact per-busid rules | +| Network firewall/relay | reference-counted `apply_firewall` → `UsbipBindFirewallRule { action: Ensure, ... }`, `observe_firewall`, and adopt/create the Network relay Process/Endpoint authority | exactly one TCP 3240 multiplexer and firewall authority per Network; exact per-busid rules; expected Network/Service generations required | | Bind busid | `bind_busid(device_uid, token)` | requires backend and relay Ready | | Service Ready | commit Service authority status | no Guest is attached yet | @@ -880,10 +933,19 @@ An attached Binding then reconciles: Binding teardown is Guest detach → private Endpoint/proxy deletion → Service slot release. Service deletion first drains all Bindings and exports/imports, then -unbinds the busid, releases firewall/relay references, releases withhold and +unbinds the busid, confirms `UsbipBindFirewallRule { action: Remove, ... }`, +then releases firewall status/token and relay authority references, releases withhold and physical lock, and finally releases Host backend/module references. Shared Host or Network authorities remain while another Service references them. +During `Remove`, Service status remains `firewallState: releasing`; the +`FirewallToken`, expected Network/Service generation fence, and relay authority +reference remain held. Only confirmed broker success (including +ownership-validated already-absent success) permits the controller to clear +firewall status/token and decrement/release the relay authority. A transient +failure retries without releasing either authority; a generation mismatch +refreshes dependencies and requeues; a foreign marker blocks teardown. + Each observe cycle calls `observe_firewall` for the Service's exact Network/busid claim. A mismatch changes only the USB Service's strict provider firewall state, degrades that Service, and queues device-usbip reconcile. @@ -1123,7 +1185,9 @@ holds a raw address or IfName. The Network controller does **not** own the USBIP firewall carve-out. `D-NETWORK-002` in `ADR-046-resources-network` confirms: `device-usbip` owns the firewall semantic authority. The `apply_firewall` -EffectPort step is the only path that creates or removes the nftables rule; +EffectPort step maps to `UsbipBindFirewallRule` action `Ensure`, and +`release_firewall` maps to action `Remove` on that same request. These are the +only paths that create or remove the nftables rule; no separate release op exists; the Network controller must not create, remove, or reference USBIP firewall rules. `observe_firewall` is the only USBIP drift reader, and strict USBIP provider status is the only status authority. Network-local emits no generic @@ -1144,16 +1208,29 @@ Teardown on deletion request: 2. For a Binding, detaches the Guest, drains/deletes its private Endpoint/proxy, and releases the Service/import lease. 3. For an authority Service, drains dependent Bindings and exports, unbinds its - busid, releases its relay/firewall/backend references, withhold, and physical - lease; shared authorities remain until their last reference is gone. + busid, calls `release_firewall(device_uid, network_uid, firewall_token, + expected_fence)` which maps to `UsbipBindFirewallRule` action `Remove`, waits + for confirmed effect, and only then clears firewall status/token and releases + the relay authority reference, releases withhold and physical lease, and + finally releases the Host backend/module reference; shared authorities remain + until their last reference is gone. 4. Marks teardown progress in the deleting Service or Binding status; restart is idempotent. 5. Clears the finalizer only after all teardown steps succeed. 6. Core commits the finalizer removal; the resource is garbage-collected. -If a teardown step fails terminally, the controller sets `phase: Degraded` and +If a teardown step fails retryably, the controller sets `phase: Degraded` and the typed Service/Binding `teardownBlocked: true`, emits a structured event, and -requeues under exponential backoff. +requeues under exponential backoff. A terminal foreign-marker conflict sets +`phase: Failed`, leaves the finalizer/status/authority in place, emits the +closed error class, and requires operator remediation rather than automatic +retry. + +`Remove` is idempotent only for ownership-validated absence. A transient broker +failure retains the token, fence, status, and authority and retries with bounded +backoff. `FirewallGenerationMismatch` refreshes Network/Service generations +before requeue; `FirewallForeignConflict` is terminal and never removes the +foreign rule or releases status/authority. --- @@ -1197,8 +1274,9 @@ from any persisted snapshot. ### Audit (broker/core) -The privileged broker emits an audit record **after** each `UsbipBindFirewallRule` -effect completes. The core resource store emits its own audit record after each +The privileged broker emits an audit record **after** each +`UsbipBindFirewallRule` attempt reaches a completed success, failure, or denial +outcome. The core resource store emits its own audit record after each `ResourceMutationBatch` commit. These two audit records are independent events; the Provider controller does not own, submit, or claim ownership of either. @@ -1212,9 +1290,12 @@ Each broker-emitted audit record contains: | --- | --- | | `subject` | Provider Process identity digest | | `zone` | Zone name | -| `op` | `UsbipBindFirewallRule` (or release variant) | +| `op` | `UsbipBindFirewallRule` | +| `action` | `Ensure` or `Remove` | | `resource_type` | `usb.d2bus.org.UsbService` | | `resource_name_digest` | Stable hash of Service name; never raw busid or path | +| `expected_network_generation` | Generation fence supplied to the closed request | +| `expected_service_generation` | Authority Service generation fence supplied to the closed request | | `outcome` | `success \| failure \| denied` | | `error_class` | closed-set slug | | `correlation_id` | operation/trace ID from reconcile context | @@ -1223,6 +1304,13 @@ Each broker-emitted audit record contains: No raw busid, lock path, nftables rule body, vendor/product ID, or network interface name appears in any audit record field. +`Remove` audit is emitted only after the broker has validated the generation +fence and ownership marker and completed the effect (including validated +already-absent success). A foreign marker emits +`action: Remove`, `outcome: denied`, and the closed +`firewall-foreign-conflict` error class without mutating the rule. There is no +release op name or release audit variant. + ### OTEL telemetry (Provider controller) The controller emits OTEL spans for its own reconcile operations: @@ -1347,8 +1435,10 @@ authoritative values come from the signed bundle. ### Firewall ownership `Provider/device-usbip` is the **semantic owner** of the USBIP nftables -carve-out. Only the `apply_firewall` and `release_firewall` EffectPort calls -may create or remove USBIP TCP/3240 exposure; `observe_firewall` is its only +carve-out. Only `apply_firewall` mapped to +`UsbipBindFirewallRule { action: Ensure, ... }` and `release_firewall` mapped +to `UsbipBindFirewallRule { action: Remove, ... }` may create or remove USBIP +TCP/3240 exposure; there is no separate release op. `observe_firewall` is its only drift reader. Network-local emits no host or net-VM USBIP allow and owns no USBIP status. The ownership marker used in the nftables comment is constructed and verified by the adapter from opaque Network and busid identity, not the @@ -1363,15 +1453,25 @@ The adapter enforces that: per-Network relay; releasing one busid cannot retain or remove another's exposure; - the closed `UsbipBindFirewallRule` operation installs every required - host-ingress/coexistence hook atomically, so no generic uplink allow can - bypass the per-busid decision; + host-ingress/coexistence hook atomically for action `Ensure`, and action + `Remove` removes only the exact ownership projection, so no generic uplink + allow can bypass the per-busid decision; - `observe_firewall` hashes only the exact device-usbip ownership projection and writes drift solely to the owning Service's strict provider status; +- `Ensure` and `Remove` are the only accepted action values; decoding any + other value fails closed; +- both actions are bound to expected Network and authority Service + generations; stale values return + `UsbipEffectError::FirewallGenerationMismatch` with no mutation; +- `Remove` is idempotent when the exact owned rule is already absent and the + trusted ownership state has no foreign replacement; - a `release_firewall` with a token that does not match the installed rule returns `UsbipEffectError::FirewallDenied`; - a foreign rule with the same ownership marker prefix causes the adapter to return `UsbipEffectError::FirewallForeignConflict` and emit a - `foreign-rule-conflict` audit event. + `foreign-rule-conflict` audit event without mutation; +- the controller releases firewall status/token and relay authority only after + confirmed `Remove`; every failure retains them for retry or operator action. ### Relay and private proxy bind addresses @@ -1581,7 +1681,8 @@ assert resolve(export.resourceRef).spec.mode == "authority"; | `anti-spoof-failed` | `Failed` | no | Vendor/product/serial mismatch | | `lease-denied` | `Degraded` | yes | `acquire_lease` failed; adapter contention | | `withhold-failed` | `Degraded` | yes | sysfs write failed | -| `firewall-denied` | `Degraded` | yes | Adapter rejected `apply_firewall` | +| `firewall-denied` | `Degraded` | yes | Adapter rejected `Ensure` or token-bound `Remove` | +| `firewall-generation-mismatch` | `Degraded` | yes, after refresh | Expected Network or authority Service generation is stale; no mutation; refresh dependencies before requeue | | `firewall-foreign-conflict` | `Failed` | no | Foreign ownership marker at expected position | | `firewall-drift` | `Degraded` | yes | Device-usbip ownership projection differs from expected Network/busid rules | | `backend-start-failed` | `Degraded` | yes | Host backend authority failed to become Ready | @@ -1594,7 +1695,7 @@ assert resolve(export.resourceRef).spec.mode == "authority"; | `invalid-export-target` | `Failed` | no | ResourceExport target is not an authority-mode `UsbService` | | `import-revoked` | `Degraded` | yes | ResourceImport/ZoneLink lease was revoked | | `stream-generation-mismatch` | `Degraded` | yes | Encrypted import stream generation/fingerprint is stale | -| `teardown-blocked` | `Degraded` | yes | One or more teardown steps failed | +| `teardown-blocked` | `Degraded` | yes unless foreign-marker terminal | One or more teardown steps failed; firewall token/status/authority remain held until confirmed `Remove` | | `claim-arbitration-conflict` | `Pending` | yes | Second Binding waits fairly for the exclusive Service slot | --- @@ -1607,7 +1708,7 @@ assert resolve(export.resourceRef).spec.mode == "authority"; | `packages/d2bd/src/usbip_state_machine.rs` — `CANONICAL_STEPS`, `UsbipBusidStep`, step ordering | Adapt step ordering into `src/reconcile.rs` EffectPort model; remove all broker-call sites | Step semantics and idempotency invariants preserved | | `packages/d2bd/src/usbip_reconcile_state.rs` — desired/carrier/bind/proxy state enums | Split into typed Service authority and per-Guest Binding status | Restart-safe reconcile model preserved without putting attachment state on Device | | `packages/d2b-host/src/usbip_argv.rs` — argv generators | Remain in `d2b-host`; called by the core adapter only | Provider crate has no compile dependency on `d2b-host` | -| `packages/d2b-priv-broker/src/ops/usbip_firewall.rs` — `bind_firewall_rule`, audit structs | Adapter-internal only; Provider crate never imports this | Audit structs are broker-internal; `UsbipBindFirewallRuleAudit` never visible to Provider | +| `packages/d2b-priv-broker/src/ops/usbip_firewall.rs` — `bind_firewall_rule`, audit structs | Adapter-internal only; Provider crate never imports this | Preserve the one closed `UsbipBindFirewallRule` request and extend it with exact action enum `Ensure|Remove` plus generation fencing; no separate release op; audit structs remain broker-internal and `UsbipBindFirewallRuleAudit` never becomes visible to Provider | | `packages/d2b-priv-broker/src/ops/usbip_host.rs` — `withhold_device` impl | Adapter-internal | Same as above | | `packages/d2b-priv-broker/src/ops/usbip_lock.rs` — OFD lock | Adapter-internal | Lock fd never leaves adapter | | `packages/d2b-contract-tests/tests/usbip_policy_network_scoping.rs` | Split into fast Provider `tests/wrong_zone.rs` admission coverage and real `tests/host-integration/usbip-service.nix` firewall coverage | Old duplicate retires only after both successors pass | @@ -1626,15 +1727,16 @@ assert resolve(export.resourceRef).spec.mode == "authority"; | Current source | None — net-new v3 work; no pre-ADR45 baseline equivalent | | Reuse action | create | | Destination | packages/d2b-contracts/src/usbip_effect_port.rs | -| Detailed design | Define UsbipEffectPort and UsbipGuestEffectPort in d2b-contracts with DeviceUid, NetworkUid, UsbBindingUid, LeaseToken, FirewallToken, FirewallObservation, KernelModuleClass, DeviceProbeResult, and UsbipEffectError; export traits/types only with no implementation; keep firewall apply/observe/release Network/busid-scoped, attach/detach Binding-addressed, and all fd/path/busid values private. `TransientDetail` derives `Clone, PartialEq, Eq` while retaining manual redacted Debug/Display so `UsbipEffectError`'s derives compile without disclosure. Primary reuse disposition: `create`. Preserved source-plan detail: net-new trait definition. | +| Detailed design | Define UsbipEffectPort and UsbipGuestEffectPort in d2b-contracts with DeviceUid, NetworkUid, UsbBindingUid, LeaseToken, FirewallToken, FirewallGenerationFence, FirewallObservation, KernelModuleClass, DeviceProbeResult, and UsbipEffectError; export traits/types only with no implementation. `apply_firewall` and `release_firewall` both accept expected Network/Service generations; release also accepts NetworkUid and borrows the token so the controller can retain it until confirmed effect. Keep firewall apply/observe/release Network/busid-scoped, attach/detach Binding-addressed, and all fd/path/busid values private. `TransientDetail` derives `Clone, PartialEq, Eq` while retaining manual redacted Debug/Display so `UsbipEffectError`'s derives compile without disclosure. Primary reuse disposition: `create`. Preserved source-plan detail: net-new trait definition. | | Integration | Provider/device-usbip controller depends on this trait for injected semantic effects; the framework core adapter implements it in ADR046-usbip-002. | | Data migration | None — docs/tooling only; no runtime state | -| Validation | d2b-contracts tests for trait object safety, firewall apply/observe/release signatures, `UsbipEffectError: Clone + PartialEq + Eq`, `TransientDetail` clone/equality, redacted Debug/Display behavior, and no implementation leakage. | +| Validation | d2b-contracts tests for trait object safety, generation-fenced firewall apply/observe/release signatures (release requires NetworkUid and token), `FirewallGenerationMismatch`, `UsbipEffectError: Clone + PartialEq + Eq`, `TransientDetail` clone/equality, redacted Debug/Display behavior, and no implementation leakage. | | Removal proof | None — net-new; no prior owner to remove | Define the `UsbipEffectPort` async trait with the method set in § UsbipEffectPort. Define `DeviceUid`, `NetworkUid`, `UsbBindingUid`, `LeaseToken`, -`FirewallToken`, `FirewallObservation`, `KernelModuleClass`, `DeviceProbeResult`, and +`FirewallToken`, `FirewallGenerationFence`, `FirewallObservation`, +`KernelModuleClass`, `DeviceProbeResult`, and `UsbipEffectError`. Export from `d2b-contracts`. No implementation in `d2b-contracts`; trait only. Add conformance tests in `d2b-contracts/tests/usbip_effect_port.rs`. @@ -1648,14 +1750,15 @@ in `d2b-contracts`; trait only. Add conformance tests in `d2b-contracts/tests/us | Current source | packages/d2bd/src/usbip_state_machine.rs, packages/d2bd/src/usbip_reconcile_state.rs, packages/d2b-host/src/usbip_argv.rs, packages/d2b-priv-broker/src/ops/usbip_firewall.rs, usbip_host.rs, and usbip_lock.rs | | Reuse action | adapt | | Destination | packages/d2b-core/src/device_usbip_adapter.rs | -| Detailed design | Implement UsbipEffectPort in the core adapter: signed-bundle busid lookup, same-Zone validation, trusted physical-USB identity resolution, mandatory Core-derived `physical-usb-backing/v1` digest and exact `(Host, physical-usb-backing, opaqueKeyDigest)` claim shared with every security-key/USB Provider, exclusive OFD claim, sole ownership of all USBIP TCP/3240 and exact per-Network/per-busid `UsbipBindFirewallRule` effects plus ownership-scoped observe/release, anti-spoof probe, one shared Host module/backend authority, one Core-derived D097 relay Endpoint/firewall authority per Network, D097 authority-index preflight/adoption, and post-effect audit; complete the shared claim before any open, withhold, bind, module, relay, firewall, or attachment effect; never expose raw busid, identity digest, path, fd, bind address, nftables body, audit structs, or broker wire types. Primary reuse disposition: `adapt`. Preserved source-plan detail: extract and adapt into framework-internal adapter. | +| Detailed design | Implement UsbipEffectPort in the core adapter: signed-bundle busid lookup, same-Zone validation, trusted physical-USB identity resolution, mandatory Core-derived `physical-usb-backing/v1` digest and exact `(Host, physical-usb-backing, opaqueKeyDigest)` claim shared with every security-key/USB Provider, exclusive OFD claim, sole ownership of all USBIP TCP/3240 and exact per-Network/per-busid effects through the existing closed `UsbipBindFirewallRule` request with action enum `Ensure|Remove`, ownership-scoped observe/release, anti-spoof probe, one shared Host module/backend authority, one Core-derived D097 relay Endpoint/firewall authority per Network, D097 authority-index preflight/adoption, and post-effect audit. `Ensure` applies; `Remove` uses the same request, binds expected Network/Service generations, validates exact ownership, succeeds idempotently on validated absence, and fails closed on a foreign marker. No separate release op exists. Complete the shared claim before any open, withhold, bind, module, relay, firewall, or attachment effect; never expose raw busid, identity digest, path, fd, bind address, nftables body, audit structs, or broker wire types. Primary reuse disposition: `adapt`. Preserved source-plan detail: extract and adapt into framework-internal adapter. | | Integration | Reconcile framework injects the adapter into Provider/device-usbip; D097 authority index gates effects; adapter calls privileged broker and d2b-host argv helpers behind the semantic trait. | | Data migration | Full d2b 3.0 reset; adapter resumes from Service/Binding status and authority owner proofs rather than daemon-coupled snapshots | -| Validation | Fast packages/d2b-core/tests/device_usbip_adapter.rs covers same-Zone gate, exact shared physical backing tuple derivation, byte-identical keys for USB/security-key views of one fake token, Provider-private-class/digest bypass rejection, `physical-usb-backing-conflict` before effects, separate USBIP module/relay authorities, one-module/one-relay reuse, exact Network/busid firewall scoping, foreign-marker failure, ownership-scoped drift/status, independent per-busid release, no network-local dependency, anti-spoof, redaction, broker mapping, and no digest/busid/path/fd exposure. | +| Validation | Fast packages/d2b-core/tests/device_usbip_adapter.rs covers same-Zone gate, exact shared physical backing tuple derivation, byte-identical keys for USB/security-key views of one fake token, Provider-private-class/digest bypass rejection, `physical-usb-backing-conflict` before effects, separate USBIP module/relay authorities, one-module/one-relay reuse, exact closed `Ensure|Remove` decoding and same-request broker mapping, unknown-action rejection, expected Network/Service generation binding, exact Network/busid firewall scoping, validated-absence `Remove`, foreign-marker failure without mutation, ownership-scoped drift/status, transient retry with retained token/authority, independent per-busid release, no network-local dependency, anti-spoof, redaction, and no digest/busid/path/fd exposure. | | Removal proof | Old daemon-coupled adapter call sites are removed by ADR046-usbip-009 after Provider wiring and adapter tests pass. | Implement the adapter: busid lookup from signed bundle, same-Zone check, OFD lock -management, broker dispatch for `UsbipBindFirewallRule`, sysfs withhold, post-effect +management, broker dispatch for `UsbipBindFirewallRule` actions +`Ensure|Remove`, sysfs withhold, post-effect audit emission. The adapter MUST NOT expose any raw busid, lock path, or broker wire type to the trait caller. Add unit tests for same-Zone gate and anti-spoof logic in `packages/d2b-core/tests/device_usbip_adapter.rs`. @@ -1698,10 +1801,10 @@ of this Provider. | Current source | packages/d2bd/src/usbip_state_machine.rs and packages/d2bd/src/usbip_reconcile_state.rs | | Reuse action | adapt | | Destination | packages/d2b-provider-device-usbip/src/{controller,reconcile,export_import}.rs | -| Detailed design | Reconcile provider-neutral authority/projection `UsbService` and per-Guest `UsbBinding` resources through strict USBIP Provider extensions, consuming UsbipEffectPort, the exact shared Host-global physical USB tuple, USBIP-private D097 authorities, and signed D096 ExportAdapter/ImportAdapter. Enforce same-Zone base and provider refs; ResourceExport authority-Service-only target; same-type ResourceImport-owned projection with `providerRef`, semantic base/import fields, no `spec.provider`, and no physical fields/effects; route selection from the signed local descriptor/import record; semantic factory fingerprint independent of separately authenticated adapter identity; exclusive fair Binding admission; encrypted bounded named-stream control/data; D088 layered status; Service/Binding finalizers; restart adoption; declared/explicit modes; no session/transfer resources. Primary reuse disposition: `adapt`. Preserved source-plan detail: extract and adapt step machine into Provider reconcile loop. | +| Detailed design | Reconcile provider-neutral authority/projection `UsbService` and per-Guest `UsbBinding` resources through strict USBIP Provider extensions, consuming UsbipEffectPort, the exact shared Host-global physical USB tuple, USBIP-private D097 authorities, and signed D096 ExportAdapter/ImportAdapter. Enforce same-Zone base and provider refs; ResourceExport authority-Service-only target; same-type ResourceImport-owned projection with `providerRef`, semantic base/import fields, no `spec.provider`, and no physical fields/effects; route selection from the signed local descriptor/import record; semantic factory fingerprint independent of separately authenticated adapter identity; exclusive fair Binding admission; encrypted bounded named-stream control/data; D088 layered status; Service/Binding finalizers; restart adoption; declared/explicit modes; no session/transfer resources. Firewall reconcile maps apply to `UsbipBindFirewallRule` action `Ensure`; finalization maps release to action `Remove` on the same request with expected Network/Service generations. Finalization retains firewall token/status and relay authority until confirmed effect, retries transient failure, refreshes on generation mismatch, and blocks fail-closed on a foreign marker. Primary reuse disposition: `adapt`. Preserved source-plan detail: extract and adapt step machine into Provider reconcile loop. | | Integration | Controller watches Device/Host/Network/Guest/Endpoint/Export/Import dependencies, calls injected EffectPorts, commits ResourceMutationBatch updates, coordinates children from ADR046-usbip-005, and delegates only semantic export/import admission to the Provider adapter while core owns D096 routing/lifecycle. | | Data migration | Full d2b 3.0 reset; no direct import of d2bd usbip_reconcile_state snapshots | -| Validation | Fast tests/controller_state_machine.rs, service_binding_schema.rs, export_import.rs, authority_conflict.rs, async_loop.rs, finalizer.rs, and wrong_zone.rs cover authority/projection/Binding lifecycle, Service-only export, projection `spec.provider` rejection, semantic fingerprint stability under adapter identity changes plus separate descriptor authentication, D088 `status.resource`/`status.provider` placement, shared physical tuple collision before effects, encrypted fake streams, no physical projection effect, exclusivity, restart, and WrongZone degradation. | +| Validation | Fast tests/controller_state_machine.rs, service_binding_schema.rs, export_import.rs, authority_conflict.rs, async_loop.rs, finalizer.rs, and wrong_zone.rs cover authority/projection/Binding lifecycle, Service-only export, projection `spec.provider` rejection, semantic fingerprint stability under adapter identity changes plus separate descriptor authentication, D088 `status.resource`/`status.provider` placement, shared physical tuple collision before effects, encrypted fake streams, no physical projection effect, exclusivity, restart, WrongZone degradation, exact `Ensure|Remove` mapping, and firewall status/token/authority retention until `Remove` confirmation. | | Removal proof | packages/d2bd/src/usbip_state_machine.rs and usbip_reconcile_state.rs are deleted by ADR046-usbip-009 once Provider parity tests pass. | Implement the Service authority/projection and Binding attachment step machines. @@ -1711,9 +1814,12 @@ non-blocking watch dispatch, fair exclusive Binding admission, declared/explicit mode, and D096 export/import stream revocation. Tests required: -- `tests/controller_state_machine.rs`: full bring-up / teardown with a `FakeUsbipEffectPort` +- `tests/controller_state_machine.rs`: full bring-up / teardown with a + `FakeUsbipEffectPort`, including `Ensure`/`Remove`, generation mismatch, + validated absence, transient retry, and foreign-marker failure - `tests/async_loop.rs`: receiver dispatches Service/Binding B while A awaits an effect -- `tests/finalizer.rs`: finalizer add/clear through partial progress +- `tests/finalizer.rs`: finalizer add/clear through partial progress; no + firewall status/token or relay authority release before confirmed `Remove` - `tests/wrong_zone.rs`: WrongZone error → Degraded phase + correct error class --- @@ -1747,10 +1853,10 @@ private proxies/Endpoints. Attach/detach remains an EffectPort call. | Current source | packages/d2bd/src/usbip_reconcile_state.rs state fields | | Reuse action | adapt | | Destination | packages/d2b-provider-device-usbip/src/status.rs | -| Detailed design | Define provider-neutral `UsbService` `status.resource` with whole-device availability/access counts, authority-only `physical-usb-backing` claim state, and projection-only common import state; define `UsbBinding.status.resource` with generic attachment phase/queue/generation/timestamps. Define separate strict `status.provider` USBIP details: Device probe, owner backend/relay, USBIP-owned `firewallState`/opaque digest/observed generation, imported-route observations, and Binding proxy/private Endpoint/subphase. No common access/import/attachment field appears directly under `status`; no USBIP module, Network, Endpoint, proxy, server/client, port, firewall, or busid field enters base status; no raw backing digest, busid, path, fd, address, session/transfer ID, remote identity, or payload appears anywhere. Primary reuse disposition: `adapt`. Preserved source-plan detail: adapt state fields to typed status.provider.details. | +| Detailed design | Define provider-neutral `UsbService` `status.resource` with whole-device availability/access counts, authority-only `physical-usb-backing` claim state, and projection-only common import state; define `UsbBinding.status.resource` with generic attachment phase/queue/generation/timestamps. Define separate strict `status.provider` USBIP details: Device probe, owner backend/relay, USBIP-owned `firewallState`/opaque digest/observed generation, imported-route observations, and Binding proxy/private Endpoint/subphase. During `UsbipBindFirewallRule` action `Remove`, strict status remains `firewallState: releasing`; the token and relay authority remain held until confirmed success, including validated absence. No common access/import/attachment field appears directly under `status`; no USBIP module, Network, Endpoint, proxy, server/client, port, firewall, or busid field enters base status; no raw backing digest, busid, path, fd, address, session/transfer ID, remote identity, or payload appears anywhere. Primary reuse disposition: `adapt`. Preserved source-plan detail: adapt state fields to typed status.provider.details. | | Integration | Controller writes each extension atomically with its resource's common status; dependency/update propagation is Device/Export → Service/projection → Binding. | | Data migration | Full d2b 3.0 reset; current d2bd reconcile state is not imported | -| Validation | Fast tests/status_serde.rs covers generic base status plus three strict USBIP detail schemas, exact `status.resource` placement for access/import/attachment/backing claim, exact `status.provider` placement for relay/firewall implementation observations, drift transition and ownership, rejection of semantic fields directly under `status`, mode-dependent omissions, bounded counts/refs, unknown-field denial, and a deny corpus proving USBIP-only fields are rejected from base and Network status. | +| Validation | Fast tests/status_serde.rs covers generic base status plus three strict USBIP detail schemas, exact `status.resource` placement for access/import/attachment/backing claim, exact `status.provider` placement for relay/firewall implementation observations, drift transition and ownership, `releasing` retention until `Remove` confirmation, rejection of semantic fields directly under `status`, mode-dependent omissions, bounded counts/refs, unknown-field denial, and a deny corpus proving USBIP-only fields are rejected from base and Network status. | | Removal proof | Old d2bd USBIP reconcile-state structs are removed by ADR046-usbip-009 after status extension coverage passes. | Define generic `UsbServiceStatus`/`UsbBindingStatus` base projections and strict @@ -1769,7 +1875,7 @@ not ResourceTypes or compatibility aliases. | Current source | packages/d2b-contract-tests/tests/usbip_policy_network_scoping.rs plus new integration scenarios | | Reuse action | adapt | | Destination | packages/d2b-provider-device-usbip/{src,tests,integration/README.md}; tests/host-integration/usbip-service.nix; tests/host-integration/hardware/usbip-service.sh | -| Detailed design | Put provider-neutral Service/Binding base-schema separation, strict USBIP extensions, projection `spec.provider` rejection, D088 layered status, semantic factory-fingerprint independence from Provider/adapter identity, exact shared physical backing tuple/conflict, one Core-derived per-Network relay Endpoint/firewall authority, exact per-busid firewall ownership/drift/release, arbitration, same-type export/import, encrypted fake-stream, and least-privilege process/Endpoint shape coverage in fast Layer-1 Rust tests. Include a fake direct-local Provider proving the same base contract has no USBIP dependency. Reserve runNixOSTest for real Linux usbip_host/vhci_hcd, usbipd, namespaces/nftables, TCP 3240, zero network-local carve-out, and Guest checks; reserve the hardware script for an approved physical device. Use existing Make gates only. Primary reuse disposition: `adapt`. Preserved source-plan detail: adapt existing network-scoping assertion and add new scenarios. | +| Detailed design | Put provider-neutral Service/Binding base-schema separation, strict USBIP extensions, projection `spec.provider` rejection, D088 layered status, semantic factory-fingerprint independence from Provider/adapter identity, exact shared physical backing tuple/conflict, one Core-derived per-Network relay Endpoint/firewall authority, exact per-busid firewall ownership/drift/release through one `UsbipBindFirewallRule` request with closed `Ensure|Remove`, generation fencing, idempotent validated-absence `Remove`, foreign-marker fail-closed behavior, and status/token/authority retention until confirmation, arbitration, same-type export/import, encrypted fake-stream, and least-privilege process/Endpoint shape coverage in fast Layer-1 Rust tests. Include a fake direct-local Provider proving the same base contract has no USBIP dependency. Reserve runNixOSTest for real Linux usbip_host/vhci_hcd, usbipd, namespaces/nftables, TCP 3240, zero network-local carve-out, and Guest checks; reserve the hardware script for an approved physical device. Use existing Make gates only. Primary reuse disposition: `adapt`. Preserved source-plan detail: adapt existing network-scoping assertion and add new scenarios. | | Integration | Layer-2 lanes exercise actual kernel/backend/relay/Guest/device paths and do not duplicate pure controller/schema cases. Cross-Zone protocol logic remains hermetic with fake peers; the runNixOSTest only proves its real-system integration. | | Data migration | None — docs/tooling only; no runtime state | | Validation | `make test-host-integration` runs the non-hardware real-kernel case on a capable host; `make test-hardware` runs the explicit manual device case. No Layer-1 test opens a device, loads a module, creates a namespace, or listens on a socket. | @@ -1779,7 +1885,7 @@ Required tests: | Path | Scenario | Gate | | --- | --- | --- | -| `tests/host-integration/usbip-service.nix` | Real modules, one Host backend, one Core-derived multiplexed Network TCP 3240 Endpoint/firewall authority, exact per-busid rules, ownership-scoped drift/status/release, zero network-local carve-out, wrong-Zone denial, and least-privilege Guest Binding proxy/attach/revocation with fake USB backend | `make test-host-integration` | +| `tests/host-integration/usbip-service.nix` | Real modules, one Host backend, one Core-derived multiplexed Network TCP 3240 Endpoint/firewall authority, exact per-busid rules, `UsbipBindFirewallRule` `Ensure|Remove` only, generation-bound ownership-scoped drift/status/release, idempotent validated-absence `Remove`, foreign-marker fail-closed, authority retained until effect confirmation, zero network-local carve-out, wrong-Zone denial, and least-privilege Guest Binding proxy/attach/revocation with fake USB backend | `make test-host-integration` | | `tests/host-integration/hardware/usbip-service.sh` | Approved physical USB device, exclusive busid, second-Binding fairness, shared Host-global USB/security-key authority conflict, data/detach, no fd/path crossing | `make test-hardware`; manual only | `packages/d2b-provider-device-usbip/integration/README.md` must document: @@ -1799,10 +1905,10 @@ Required tests: | Current source | nixos-modules/components/usbip.nix guest wiring and new Zone resource declarations | | Reuse action | adapt | | Destination | nixos-modules/components/usbip.nix, nixos-modules/options-zones.nix, nixos-modules/assertions.nix | -| Detailed design | Add Provider config; remove the old per-VM option; emit provider-neutral authority `UsbService`, per-Guest `UsbBinding`, strict USBIP Provider envelopes, and optional ResourceExport/ResourceImport authoring shapes; imported same-type projection Services remain Core-created with no `spec.provider`. Assert same-Zone base/provider refs, projection ownerRef/forbidden implementation and physical fields, Service-only export target, one Host backend/Network relay, exact Core-derived Host-global `(Host, physical-usb-backing, opaqueKeyDigest)` exclusion shared by USB/security-key, Provider-private-class bypass rejection, and retain guest vhci_hcd/tools. Primary reuse disposition: `adapt`. Preserved source-plan detail: adapt guest module, remove host-side option surface, and extend eval assertions. | +| Detailed design | Add Provider config; remove the old per-VM option; emit provider-neutral authority `UsbService`, per-Guest `UsbBinding`, strict USBIP Provider envelopes, and optional ResourceExport/ResourceImport authoring shapes; imported same-type projection Services remain Core-created with no `spec.provider`. Assert same-Zone base/provider refs, projection ownerRef/forbidden implementation and physical fields, Service-only export target, one Host backend/Network relay, exact Core-derived Host-global `(Host, physical-usb-backing, opaqueKeyDigest)` exclusion shared by USB/security-key, Provider-private-class bypass rejection, one `UsbipBindFirewallRule` contract with exact actions `Ensure|Remove` and no separate release operation, and retain guest vhci_hcd/tools. Primary reuse disposition: `adapt`. Preserved source-plan detail: adapt guest module, remove host-side option surface, and extend eval assertions. | | Integration | Nix compiler emits Device plus provider-neutral `UsbService`/`UsbBinding` and explicit D096 resources consumed by core/Provider; guest runtime supplies USBIP proxy/attach tools; generated base and provider-extension schemas/fingerprints remain canonical. | | Data migration | Full d2b 3.0 reset; operators reauthor old per-VM options as Device + authority/projection Service + per-Guest Binding | -| Validation | Fast tests/unit/nix/cases/usbip-*.nix cover schema shape, Core projection `spec.provider` rejection, D088 layered status fixtures including USBIP-owned firewall state, all reference/owner/export assertions, byte-identical USB/security-key tuple collision and private-class bypass rejection, one Core-derived 3240 Endpoint/firewall authority per Network, least-privilege consumer policy, absence of network-local 3240 rules, old-option removal, and guest module retention. | +| Validation | Fast tests/unit/nix/cases/usbip-*.nix cover schema shape, Core projection `spec.provider` rejection, D088 layered status fixtures including USBIP-owned firewall state and `releasing` retention, all reference/owner/export assertions, byte-identical USB/security-key tuple collision and private-class bypass rejection, one Core-derived 3240 Endpoint/firewall authority per Network, exact `Ensure|Remove` broker action surface with unknown values rejected and no second op, least-privilege consumer policy, absence of network-local 3240 rules, old-option removal, and guest module retention. | | Removal proof | d2b.vms..usbip.yubikey and host-side USBIP module paths are removed at reset once Zone resource emitter coverage passes. | - Add `d2b.zones..providers.device-usbip.config.controllerExecutionRef` option. @@ -1869,11 +1975,11 @@ with a capped case count and declared higher per-test budget. | --- | --- | | `service_binding_schema.rs` | Provider-neutral authority/projection `UsbService` and `UsbBinding` base schemas plus strict USBIP Provider envelopes; canonical minimal base; same-Zone refs; imported projection ResourceImport ownerRef, `spec.provider` rejection, and physical-field denial; same-type Service-only explicit export; Device/Endpoint/Binding export denial | | `provider_neutral_base.rs` | Base specs/status reject USBIP port, Network, Endpoint, proxy, server/client, firewall, and busid fields; a fake direct-local Provider implements the same base types without USBIP schemas or aliases | -| `controller_state_machine.rs` | Authority and Binding sequences with fake ports; shared Host backend/Network relay; Binding-owned proxy/private Endpoint; declared/explicit attach; restart and reverse teardown | +| `controller_state_machine.rs` | Authority and Binding sequences with fake ports; shared Host backend/Network relay; Binding-owned proxy/private Endpoint; declared/explicit attach; restart and reverse teardown; apply maps to `Ensure`, release maps to `Remove`, and teardown retains firewall token/status/relay authority until confirmation | | `authority_conflict.rs` | USBIP, fake direct-local USB, and security-key Providers resolve one fake token to a byte-identical Core-derived `(Host, physical-usb-backing, opaqueKeyDigest)` tuple; private-class/digest bypass fails; the second claim returns `physical-usb-backing-conflict` before effects; one Host USBIP module/backend; one Core-derived TCP 3240 relay Endpoint/firewall authority per Network; multiplex reuse and deterministic conflicts | | `export_import.rs` | ResourceExport targets authority `usb.d2bus.org.UsbService` only; ResourceImport preserves that exact type and creates an ownerRef projection with no `spec.provider`; semantic factory fingerprint is stable under Provider/adapter identity changes while signed descriptor authentication remains exact; encrypted bounded fake control/data streams; generation/fingerprint/revocation; no fd/path/busid; sessions/transfers remain internal | -| `effect_port_contract.rs` | `UsbipEffectPort` and `UsbipGuestEffectPort` trait object safety; firewall apply/observe/release signatures callable from Provider crate; no import of broker types or `d2b-priv-broker`; compile-time `UsbipEffectError: Clone + PartialEq + Eq`; `TransientDetail` clone/equality and manual Debug/Display both produce `` | -| `firewall_ownership.rs` | Exact per-Network/per-busid intent; one relay listener/base hook with independent busid entries; foreign marker rejection; ownership-scoped drift and strict Service provider status; release one busid preserves another; Network digest/status unaffected; no network-local generic rule dependency | +| `effect_port_contract.rs` | `UsbipEffectPort` and `UsbipGuestEffectPort` trait object safety; firewall apply/observe/release signatures callable from Provider crate; release requires NetworkUid, borrowed FirewallToken, and FirewallGenerationFence; `FirewallGenerationMismatch`; no import of broker types or `d2b-priv-broker`; compile-time `UsbipEffectError: Clone + PartialEq + Eq`; `TransientDetail` clone/equality and manual Debug/Display both produce `` | +| `firewall_ownership.rs` | Exact per-Network/per-busid intent; one closed `UsbipBindFirewallRule` request; exact `Ensure|Remove` decode with unknown-action rejection; apply/release same-request mapping; expected Network/Service generation binding; one relay listener/base hook with independent busid entries; idempotent validated-absence `Remove`; foreign marker fail-closed without mutation; ownership-scoped drift and strict Service provider status; transient retry retains token/status/authority; release one busid preserves another; Network digest/status unaffected; no network-local generic rule dependency | | `relay_endpoint_authority.rs` | Exactly one D097 relay Endpoint authority per Network; Core-derived key; multiplexed Service holders; duplicate conflict before listen/firewall effect; adapter/exact-active-Binding-only resolution; LaunchTicket connected stream; generic Network readers denied | | `conformance.rs` | Device/`UsbService`/`UsbBinding` ResourceTypeSchema round-trip, signed USBIP extension fingerprints, deny_unknown_fields, and Provider capability advertisement | | `state_volume.rs` | Controller Volume schema conformance: `stateSchema: {}`, layout `ownerRef: User/` (not ComponentPrincipal), `sensitivityClass: private`, single `state` view; no cross-component Volume; dirfd delivery to controller only | @@ -1881,7 +1987,7 @@ with a capped case count and declared higher per-test budget. | `validation_corpus.rs` | Bus-id max length (31 chars); metachar rejection; leading-zero segment rejection; vendor/product id exactly 4 hex digits; `busClass != usb` → `unsupported-bus-class` | | `mutual_exclusion.rs` | USBIP, fake direct-local USB, and security-key claims for one Host-global backing key conflict; second Binding queues fairly and causes no second bind/open | | `wrong_zone.rs` | Every base and USBIP provider ref is same-Zone; cross-Zone use requires D096 import; wrong-Zone firewall causes Service degradation and no effect | -| `finalizer.rs` | Service/Binding finalizers start only after effect/lease and clear only after child/lease teardown; restart resumes partial teardown | +| `finalizer.rs` | Service/Binding finalizers start only after effect/lease and clear only after child/lease teardown; Service calls generation-fenced `Remove`, retains firewall token/status/relay authority on transient, stale-generation, and foreign-marker outcomes, clears them only after confirmation, and restart resumes partial teardown | | `async_loop.rs` | Independent Service/Binding dispatch remains concurrent while per-Service single-flight preserves arbitration | All rows above are Layer-1 Rust tests in the crate's `src/` or `tests/`; they @@ -1892,7 +1998,7 @@ network bind, namespace operation, process spawn, or device open. | Path | Scenario | Gate | | --- | --- | --- | -| `tests/host-integration/usbip-service.nix` | Real NixOS boot, `usbip_host`/`vhci_hcd`, one Network TCP 3240 relay Endpoint authority multiplexing two fake USB backends, exact per-busid rules, drift/status/reapply, independent release, zero network-local TCP/3240 rule, wrong-Zone denial, least-privilege Binding proxy/attach/revocation | `make test-host-integration`; capable host | +| `tests/host-integration/usbip-service.nix` | Real NixOS boot, `usbip_host`/`vhci_hcd`, one Network TCP 3240 relay Endpoint authority multiplexing two fake USB backends, exact per-busid rules, closed `Ensure|Remove` request, drift/status/reapply, generation-bound independent release, validated-absence idempotency, foreign-marker fail-closed, status/authority retention until confirmed `Remove`, zero network-local TCP/3240 rule, wrong-Zone denial, least-privilege Binding proxy/attach/revocation | `make test-host-integration`; capable host | | `tests/host-integration/hardware/usbip-service.sh` | Approved physical USB device: shared Host-global backing and exclusive busid claims, attach/data/detach, second Binding fairness, USBIP/security-key conflict, and no fd/path crossing | `make test-hardware`; manual hardware only | `packages/d2b-provider-device-usbip/integration/README.md` indexes these Layer-2 diff --git a/docs/specs/providers/ADR-046-provider-network-local.md b/docs/specs/providers/ADR-046-provider-network-local.md index 50f3e8c39..cff42a993 100644 --- a/docs/specs/providers/ADR-046-provider-network-local.md +++ b/docs/specs/providers/ADR-046-provider-network-local.md @@ -286,10 +286,13 @@ pub trait NetworkEffectPort: Send + Sync { intent: &TapIntent, ) -> Result; - /// Revoke a previously declared tap attachment. Removes the tap device. + /// Revoke a previously declared persistent tap attachment. The generation + /// fence prevents a stale finalizer from deleting a replacement attachment. + /// Absence is success only after ownership validation. async fn revoke_attachment_tap( &self, handle: &AttachmentHandle, + expected: &AttachmentGenerationFence, ) -> Result<(), EffectError>; /// Set the isolation flag on a tap's bridge port. @@ -390,6 +393,7 @@ serialized to JSON or transmitted over the resource API wire. | `FabricIntent` | `mtu`, `stp_disabled`, `multicast_snooping_disabled`, `ipv6_suppress` | All fields from declared spec | | `TapIntent` | `attachment_index`, `neigh_suppress` | Index from declared spec | | `AttachmentHandle` | opaque seal over internal `(network_uid, attachment_uid)` | Redacted Debug; not Clone/Copy/Serialize | +| `AttachmentGenerationFence` | `expected_network_generation`, `expected_attachment_generation` | Both are non-zero resource generations captured from the current realization; stale values fail closed | | `FabricHandle` | opaque seal over internal `network_uid` | Redacted Debug; not Clone/Copy/Serialize | | `FirewallIntent` | `rules: Vec` (rules reference attachment handles, not IfNames) | No raw IfNames and no USBIP/TCP-3240 rule | | `FirewallDigest` | opaque `[u8; 32]` SHA-256 of the Network-UID ownership projection | Stored in status for Network-owned drift comparison only; excludes device-usbip | @@ -411,7 +415,7 @@ provider crate. | `create_fabric` | `CreateBridge` (new v3 op) | none (v3 new) | | `delete_fabric` | `DeleteBridge` (new v3 op) | none (v3 new) | | `declare_attachment_tap` | `CreatePersistentTap` + `SetBridgePortFlags` | `d2b-priv-broker/src/runtime.rs` tap ops | -| `revoke_attachment_tap` | `DeleteTap` | existing v3 baseline | +| `revoke_attachment_tap` | `DeletePersistentTap` (planned closed op paired with `CreatePersistentTap`) | new v3 op | | `set_attachment_isolation` | `SetBridgePortFlags` | `d2b-host/src/bridge_port.rs` | | `apply_host_firewall` | `ApplyNftables` | `d2b_contracts::broker_wire::ApplyNftablesRequest` | | `remove_host_firewall` | `ApplyNftables` (empty set) | same | @@ -425,6 +429,36 @@ provider crate. | `read_sysctl_state` | `ReadSysctlState` (new v3 op) | `d2b-host/src/netlink.rs` | | `read_attachment_isolation` | `ReadBridgePortFlags` (new v3 op) | `d2b-host/src/bridge_port.rs` | +`DeletePersistentTapRequest` is a closed broker request containing only the +opaque attachment ID resolved from `AttachmentHandle`, +`expected_network_generation`, and `expected_attachment_generation`. It has no +IfName, path, or caller-supplied ownership-marker field. The broker resolves the +private realization record, validates both generations and the d2b ownership +marker, and then deletes only that persistent tap. An already-absent tap is +success when the trusted realization record and marker state prove that no +foreign object occupies the attachment; an ownership-marker conflict fails +closed. A generation mismatch never deletes anything and makes the controller +refresh status before retrying. + +```rust +#[serde(deny_unknown_fields)] +pub struct DeletePersistentTapRequest { + pub attachment_id: OpaqueAttachmentId, + pub expected_network_generation: u64, + pub expected_attachment_generation: u64, +} +``` + +The broker appends a post-effect, path-free audit record with +`op: DeletePersistentTap`, an opaque attachment digest, both expected +generations, `outcome`, `error_class`, and `correlation_id`. It never records an +IfName, path, marker body, or attachment-handle bytes. Retryable kernel failures +map to `AttachmentDeleteFailed`; stale fences map to +`AttachmentGenerationMismatch` and requeue after a fresh read; marker conflicts +map to terminal `AttachmentOwnershipConflict`. The finalizer retains the +attachment handle and does not advance until the broker confirms success, +including validated already-absent success. + ### 5.5 Runtime Provider attachment FD path When `Provider/runtime-cloud-hypervisor` reconciles the net-VM Guest, it needs the @@ -1276,7 +1310,10 @@ A NixOS generation switch is NOT required to create or remove a Network. before returning (closes race between creation and subsequent sysctl step). `delete_fabric` (via core adapter `DeleteBridge` broker op): -- removes the kernel bridge device and all tap ports still attached; +- removes only the kernel bridge device after every persistent tap has been + confirmed removed through `DeletePersistentTap`; +- never cascades deletion to an attached tap; a remaining owned tap is + retryable and a foreign port/marker fails closed; - idempotent (returns success if already absent). ### 12.2 IPv6 suppression (defense-in-depth) @@ -1291,15 +1328,25 @@ bridges are created dynamically. ### 12.3 Tap lifecycle -Tap creation for workload Guests is performed by **`Provider/runtime-cloud-hypervisor`**, -not by the network-local controller. The network-local controller: +Persistent-tap creation is declared by the network-local controller through +`NetworkEffectPort`; Core maps that declaration to `CreatePersistentTap` and +supplies the resulting FD privately to +**`Provider/runtime-cloud-hypervisor`** through LaunchTicket. The +network-local controller: 1. Calls `NetworkEffectPort.declare_attachment_tap()` to record the attachment intent and receive an `AttachmentHandle`; 2. Stores the handle in `Network.status.resource.attachments[]`; 3. Calls `NetworkEffectPort.set_attachment_isolation()` to apply isolated/ neigh-suppress bridge port flags when the tap is created. -The runtime calls `revoke_attachment_tap()` during Guest deletion. +Core resolves the handle privately when the runtime Provider needs the tap FD; +the runtime does not own persistent-tap deletion. On attachment removal or +Network finalization, the network-local controller waits until the Guest/VMM no +longer owns the FD, then calls `revoke_attachment_tap(handle, +AttachmentGenerationFence { expected_network_generation, +expected_attachment_generation })`. The core adapter maps that call only to +`DeletePersistentTap`; the handle/fence never becomes an IfName or path input +from the Provider. ### 12.4 NM unmanaged and /etc/hosts @@ -1602,9 +1649,15 @@ using a bounded priority lane. 11. Set_attachment_isolation for each workload tap via NetworkEffectPort -12. Commit ResourceMutationBatch with all child resource mutations + status +12. For each attachment removed from the current spec, wait for its Guest/VMM FD + ownership to close, then call revoke_attachment_tap with the retained opaque + handle and current Network/attachment generation fence + └─ DeletePersistentTap absent success is accepted only after ownership validation + └─ generation mismatch refreshes status and requeues; marker conflict is terminal -13. Evaluate conditions; report phase +13. Commit ResourceMutationBatch with all child resource mutations + status + +14. Evaluate conditions; report phase └─ all conditions True → phase: Ready └─ any terminal error → phase: Failed └─ partial → phase: Degraded @@ -1621,22 +1674,28 @@ network.d2bus.org/fabric-cleanup finalizer 3. Wait for all attachment phases to become non-Ready (workload Guests stopped) -4. Delete mDNS Process resources (if any); wait for each Deleted watch event +4. For each retained attachment handle, call revoke_attachment_tap with the + current expected Network and attachment generations + └─ core dispatches DeletePersistentTap; no IfName/path crosses the port + └─ retain handle and retry transient failures before advancing + └─ stale generation refreshes/requeues; foreign ownership marker blocks cleanup + +5. Delete mDNS Process resources (if any); wait for each Deleted watch event └─ Deleted event: single atomic store transaction (row+index removed); no persistent phase=Deleted row; audit record separate from deletion tx -5. Delete Process/net--agent; wait for Deleted event +6. Delete Process/net--agent; wait for Deleted event -6. Delete Process/net--dnsmasq; wait for Deleted event +7. Delete Process/net--dnsmasq; wait for Deleted event -7. Update Volume attachments to [] (remove Guest attachment); wait for removal +8. Update Volume attachments to [] (remove Guest attachment); wait for removal -8. Delete Guest/; wait for Deleted event +9. Delete Guest/; wait for Deleted event └─ confirms the VMM and macvtap FD owner are gone -9. Delete Volume/net--config; wait for Deleted event +10. Delete Volume/net--config; wait for Deleted event -10. [background tasks, independent]: +11. [background tasks, independent]: remove_host_firewall(network_uid) remove_host_routes(network_uid) update_hosts_file(network_uid, empty) @@ -1645,11 +1704,11 @@ network.d2bus.org/fabric-cleanup finalizer apply_nm_unmanaged(empty pattern for this network) Each is idempotent; failure is retried before clearing finalizer -11. Release the Host-global external-NIC authority claim, if present +12. Release the Host-global external-NIC authority claim, if present └─ release is forbidden until Guest/VMM/macvtap ownership is closed └─ multiplexed owner transfer is an atomic Core authority-index operation -12. Clear finalizer +13. Clear finalizer ``` Each step is driven by `owned-resource-changed` hints rather than polling. @@ -1852,11 +1911,15 @@ the network-local controller is authorized to resolve and call this service | `network-cidr-conflict` | Failed | CIDR overlap detected | | `ifname-collision` | Failed | Derived IfName collision; terminal | | `bridge-create-error` | Degraded | create_fabric failed | +| `bridge-delete-error` | Degraded | delete_fabric failed or an owned persistent tap remains; retry after tap cleanup | | `sysctl-error` | Degraded | apply_host_sysctls failed | | `nftables-error` | Degraded | apply_host_firewall failed | | `nftables-drift` | Degraded | Firewall digest mismatch detected at observe | | `nm-unmanaged-error` | Degraded | apply_nm_unmanaged failed | | `route-error` | Degraded | apply_host_routes failed | +| `attachment-delete-failed` | Degraded | `DeletePersistentTap` failed transiently; retry with the same fence | +| `attachment-generation-mismatch` | Degraded | stale Network/attachment generation fence; refresh realization and requeue without deleting | +| `attachment-ownership-conflict` | Failed | persistent tap ownership marker does not match; fail closed without deleting | | `config-volume-error` | Degraded | Volume create failed | | `volume-backing-error` | Degraded | Volume backing not Ready | | `attachment-not-ready` | Degraded | Volume Guest attachment not Ready | @@ -1916,6 +1979,7 @@ Network-specific audit payload: | `firewallDigest` | Yes | drift evidence (opaque hex; no rule text) | | Bridge/tap drift reason | Yes (stable code; no raw IfName) | diagnostic | | `network.attachments[].attachmentHandle` | Yes (opaque; no IfName) | fabric identity | +| `DeletePersistentTap` expected generations and opaque attachment digest | Yes | deletion fence and effect correlation; no handle bytes | | Workload hostname, IP, MAC | **No** | redacted from API-level audit | | nftables rule text | **No** | redacted | | DHCP lease data | **No** | never written to audit | @@ -1926,7 +1990,10 @@ Network-specific audit payload: | Error message body | **No** (error code only) | no kernel output | Broker operations emit their own audit records (path-free outcome codes) from within -the core adapter. +the core adapter. `DeletePersistentTap` audit uses the exact op name and carries +only the opaque attachment digest, expected Network/attachment generations, +outcome, error class, and correlation ID; it never carries an IfName, path, or +ownership-marker body. ### 21.2 OTEL spans and metrics @@ -1945,6 +2012,7 @@ Child spans (no raw IfName, IP, MAC, rule text, or lease data in any attribute): ``` d2b.network.effect.create_fabric d2b.network.effect.delete_fabric +d2b.network.effect.delete_persistent_tap d2b.network.effect.apply_firewall d2b.network.effect.apply_routes d2b.network.effect.apply_sysctls @@ -2297,7 +2365,7 @@ On controller binary upgrade: | Current source | None — net-new v3 work; no pre-ADR45 baseline equivalent for a provider-neutral `NetworkEffectPort` core adapter. | | Reuse action | create | | Destination | `d2b-contracts` trait plus `d2b-core` core adapter; maps to broker wire operations and audit emission. | -| Detailed design | Implement `NetworkEffectPort` core adapter in `d2b-core`; map to broker wire ops; emit audit records. Versioning: minor releases may add methods with default impls; major releases require Provider upgrade. The trait lives in `d2b-contracts`; the adapter in `d2b-core`. | +| Detailed design | Implement `NetworkEffectPort` core adapter in `d2b-core`; map to broker wire ops; emit audit records. `revoke_attachment_tap` accepts only an opaque `AttachmentHandle` plus `AttachmentGenerationFence { expected_network_generation, expected_attachment_generation }` and maps to `DeletePersistentTap`; no IfName/path or caller-authored marker crosses the trait. Versioning: minor releases may add methods with default impls; major releases require Provider upgrade. The trait lives in `d2b-contracts`; the adapter in `d2b-core`. | | Integration | `Provider/network-local` reconcile calls injected `NetworkEffectPort`; the core adapter resolves opaque Network intents to closed broker wire ops and emits broker-level audit records. | | Data migration | Full d2b 3.0 reset; no v2 state/config import. | | Validation | `packages/d2b-provider-network-local/tests/fault_injection.rs` verifies fake `NetworkEffectPort` behavior, error mapping, no broker socket in provider context, and audit-safe adapter boundaries. | @@ -2307,13 +2375,13 @@ On controller binary upgrade: | Field | Value | | --- | --- | | Dependency/owner | Core; broker/core contract work consumed by ADR046-nl-001. | -| Current source | Existing broker wire has related ApplyNftables, ApplyRoute, ApplySysctl, ApplyNmUnmanaged, UpdateHostsFile, and SeedDnsmasqLease operations, but no `CreateBridge`, `DeleteBridge`, `ReadNftablesDigest`, `ReadSysctlState`, or `ReadBridgePortFlags` v3 ops. | +| Current source | Existing broker wire has related ApplyNftables, ApplyRoute, ApplySysctl, ApplyNmUnmanaged, UpdateHostsFile, SeedDnsmasqLease, and `CreatePersistentTap` operations, but no paired `DeletePersistentTap`, `CreateBridge`, `DeleteBridge`, `ReadNftablesDigest`, `ReadSysctlState`, or `ReadBridgePortFlags` v3 ops. | | Reuse action | adapt | -| Destination | Broker wire contract and broker/core adapter operation table for `CreateBridge`, `DeleteBridge`, `ReadNftablesDigest`, `ReadSysctlState`, and `ReadBridgePortFlags`. | -| Detailed design | Add `CreateBridge`, `DeleteBridge`, `ReadNftablesDigest`, `ReadSysctlState`, `ReadBridgePortFlags` broker ops. Primary reuse disposition: `adapt`. Preserved source-plan detail: extend broker wire with net-new operations and reuse existing closed broker-operation dispatch shape. | -| Integration | `NetworkEffectPort` core adapter invokes these broker ops for bridge lifecycle and observe/drift checks; `Provider/network-local` receives only typed results and opaque digests/handles. | +| Destination | Broker wire contract and broker/core adapter operation table for `DeletePersistentTap`, `CreateBridge`, `DeleteBridge`, `ReadNftablesDigest`, `ReadSysctlState`, and `ReadBridgePortFlags`. | +| Detailed design | Add canonical closed `DeletePersistentTap` paired with `CreatePersistentTap`, plus `CreateBridge`, `DeleteBridge`, `ReadNftablesDigest`, `ReadSysctlState`, and `ReadBridgePortFlags`. `DeletePersistentTapRequest` contains only an opaque attachment ID and expected Network/attachment generations. The broker resolves trusted realization state, validates generations and ownership marker, treats validated absence as success, rejects foreign markers without deletion, and emits path-free post-effect audit. No request accepts an IfName or path. Primary reuse disposition: `adapt`. Preserved source-plan detail: extend broker wire with net-new operations and reuse existing closed broker-operation dispatch shape. | +| Integration | `NetworkEffectPort` core adapter invokes these broker ops for attachment/fabric lifecycle and observe/drift checks; `Provider/network-local` receives only typed results and opaque digests/handles. Attachment removal and Network finalization retain the handle until `DeletePersistentTap` confirms deletion or validated absence. | | Data migration | Full d2b 3.0 reset; no v2 state/config import. | -| Validation | `integration/host_fabric.rs` covers bridge create/delete, nftables apply/digest, IPv6 suppression, NetworkManager unmanaged handling, and real `NetworkEffectPort` implementation. | +| Validation | Broker tests cover `DeletePersistentTap` success, validated already-absent idempotency, stale Network/attachment generations, foreign-marker fail-closed behavior, path-free audit, and rejection of any IfName/path field; `integration/host_fabric.rs` covers persistent-tap deletion, bridge create/delete, nftables apply/digest, IPv6 suppression, NetworkManager unmanaged handling, and real `NetworkEffectPort` implementation. | | Removal proof | None — net-new broker ops; remove only if no Provider consumes them per the removal checklist. | ### ADR046-nl-003 @@ -2323,10 +2391,10 @@ On controller binary upgrade: | Current source | None — net-new v3 work; no public pre-ADR45 baseline equivalent for opaque `AttachmentHandle` or `FabricHandle`. | | Reuse action | create | | Destination | `d2b-contracts` opaque byte-array newtypes; core-held HMAC key and provider-facing redacted handle types. | -| Detailed design | Implement `AttachmentHandle` and `FabricHandle` as opaque byte-array newtypes (32 bytes of HMAC-SHA-256 over internal identity material; key held by core). Each handle is single-use; revocation is implicit when the owning Network is deleted. These types are declared in `d2b-contracts`, not in the provider crate. | +| Detailed design | Implement `AttachmentHandle` and `FabricHandle` as opaque byte-array newtypes (32 bytes of HMAC-SHA-256 over internal identity material; key held by core). Each attachment handle identifies one generation-fenced realization and is retained until explicit `DeletePersistentTap` confirmation during attachment removal or Network finalization. These types are declared in `d2b-contracts`, not in the provider crate. | | Integration | Core creates handles from Network and attachment identity, stores them only in internal state/status-resource attachment realization, and supplies resolved tap FDs through LaunchTicket without exposing IfNames or MACs. | | Data migration | Full d2b 3.0 reset; no v2 state/config import. | -| Validation | `tests/fault_injection.rs` and `tests/controller_state.rs` cover opaque-handle mismatch, revocation-on-delete, and no raw IfName/IP/MAC public surface. | +| Validation | `tests/fault_injection.rs` and `tests/controller_state.rs` cover opaque-handle mismatch, generation-fenced `DeletePersistentTap`, retained handle until confirmation, and no raw IfName/IP/MAC/path public surface. | | Removal proof | None — net-new; no prior owner to remove. | ### ADR046-nl-004 @@ -2363,7 +2431,7 @@ On controller binary upgrade: | Current source | None — net-new v3 provider controller; v1 behavior lived in `nixos-modules/network.nix` and `nixos-modules/net.nix` static NixOS module logic. | | Reuse action | adapt | | Destination | `packages/d2b-provider-network-local/src/{controller.rs,metrics.rs}`. | -| Detailed design | Implement `controller.rs` reconcile/observe/finalize handlers with `NetworkEffectPort` injection and the §21 metric descriptors with closed semantic labels. No descriptor may carry `vm`, `zone`, `zone_id`, `zone_uid`, `network`, or another resource-name-derived key; Network/Zone identity stays only in OTEL resource attributes and permitted audit fields. Primary reuse disposition: `adapt`. Preserved source-plan detail: port semantics into provider reconcile state machine; do not reuse static per-env systemd/Nix ownership. | +| Detailed design | Implement `controller.rs` reconcile/observe/finalize handlers with `NetworkEffectPort` injection and the §21 metric descriptors with closed semantic labels. Attachment removal and finalization wait for Guest/VMM FD ownership to close, then call `revoke_attachment_tap` with the retained opaque handle and current Network/attachment generation fence; transient deletion retries retain the handle, stale generations refresh/requeue, and ownership conflict blocks finalizer clearing. No descriptor may carry `vm`, `zone`, `zone_id`, `zone_uid`, `network`, or another resource-name-derived key; Network/Zone identity stays only in OTEL resource attributes and permitted audit fields. Primary reuse disposition: `adapt`. Preserved source-plan detail: port semantics into provider reconcile state machine; do not reuse static per-env systemd/Nix ownership. | | Integration | Controller watches Network, Guest, Volume, Process, User, Host, and Zone resources; creates child resources, writes status, invokes `NetworkEffectPort`, and drives finalizers. | | Data migration | Full d2b 3.0 reset; no v2 state/config import. | | Validation | `tests/controller_state.rs` covers normal reconcile, errors, finalizer ordering, adoption on restart, and observe/drift cycles with deterministic clock; `tests/metrics_labels.rs` structurally asserts exact identity-key absence and that a Network-name canary never enters metric label values. | @@ -2470,10 +2538,10 @@ On controller binary upgrade: | Current source | None — net-new v3 controller state-machine test; v1 shell/Nix gates are not a controller-reconcile equivalent. | | Reuse action | create | | Destination | `packages/d2b-provider-network-local/tests/controller_state.rs`. | -| Detailed design | Controller state-machine unit tests with fake `NetworkEffectPort` (from d2b-contracts mock) and deterministic clock. | +| Detailed design | Controller state-machine unit tests with fake `NetworkEffectPort` (from d2b-contracts mock) and deterministic clock, including attachment removal/finalizer calls to generation-fenced `DeletePersistentTap`. | | Integration | Hermetic fake effect port drives reconcile, observe, finalizer, and adoption transitions without real broker, systemd, container, or network dependencies. | | Data migration | None — docs/tooling only; no runtime state. | -| Validation | `tests/controller_state.rs` covers normal path, CIDR conflict, User not Ready, Volume error, Guest timeout, agent reload failure, finalizer sequence, adoption, and drift. | +| Validation | `tests/controller_state.rs` covers normal path, CIDR conflict, User not Ready, Volume error, Guest timeout, agent reload failure, finalizer sequence, `DeletePersistentTap` validated absence, transient retry with retained handle, stale-generation refresh, foreign-marker block, adoption, and drift. | | Removal proof | None — net-new; no prior owner to remove. | ### ADR046-nl-015 @@ -2483,7 +2551,7 @@ On controller binary upgrade: | Current source | Existing Layer-1 eval and shell gates cover fragments; no v3 provider lifecycle integration test exists. | | Reuse action | adapt | | Destination | `packages/d2b-provider-network-local/integration/host_fabric.rs`, `guest_lifecycle.rs`, `agent_reload.rs`, and `delete_sequence.rs`. | -| Detailed design | Integration tests: full Network lifecycle (create, config update, agent Reload, delete sequence) in container environment. Primary reuse disposition: `adapt`. Preserved source-plan detail: adapt reusable semantic assertions into v3 integration coverage. | +| Detailed design | Integration tests: full Network lifecycle (create, config update, agent Reload, generation-fenced persistent-tap deletion, delete sequence) in container environment. Primary reuse disposition: `adapt`. Preserved source-plan detail: adapt reusable semantic assertions into v3 integration coverage. | | Integration | Integration tests exercise resource publication, host fabric effects, config Volume updates, ComponentSession reload, Process lifecycle, and finalizer cleanup through the provider stack. | | Data migration | None — docs/tooling only; no runtime state. | | Validation | `make test-integration` for container tests and `make test-host-integration` where guest lifecycle requires host/KVM coverage. | @@ -2598,22 +2666,22 @@ budget. | `state_schema_roundtrip.rs` | Provider descriptor has no stateNamespace for `controller-main`; no Provider state Volume, state mount, identity marker, migration worker, or state-layout principal is emitted; ProviderStateSet query returns empty; bounded operational observations live in status/core Operation ledger and pass redaction/size-bound checks; per-Network config Volumes are excluded from ProviderStateSet (ownerRef mismatch) | | `ifname_derive.rs` | IfName derivation determinism; collision detection; 15-byte constraint; all role prefixes | | `cidr_overlap.rs` | CIDR overlap matrix: same Network, cross-Network, external CIDR; all boundaries; no-false-positive at adjacent CIDRs | -| `controller_state.rs` | Full reconcile state machine: Normal path; CIDR conflict; User not Ready; Volume error; Guest timeout; agent reload failure; finalizer sequence (all child ordering, external authority release after VMM/macvtap close); adoption on restart; Network-owned drift detection | +| `controller_state.rs` | Full reconcile state machine: Normal path; CIDR conflict; User not Ready; Volume error; Guest timeout; agent reload failure; attachment removal and finalizer issue generation-fenced `DeletePersistentTap` only after Guest/VMM FD closure, retain handles across transient retry, refresh on stale generation, block on foreign marker, and accept validated absence; all remaining child ordering; external authority release after VMM/macvtap close; adoption on restart; Network-owned drift detection | | `external_nic_authority.rs` | Core-derived Host-global identity; same-/cross-Zone exclusive collision; non-bridge multiplex denial; explicit compatible bridge multiplex; mixed-policy conflict; no effect before admission; owner-proof adoption/ambiguity; owner transfer; update/release ordering; no raw identity in status | | `firewall_ownership.rs` | Host and net-VM intents contain no TCP/3240/USBIP rule; device-usbip marker/rule churn does not alter Network digest or `FirewallReady` | | `conformance.rs` | Provider toolkit black-box conformance suite; descriptor validation; ResourceType schema fingerprint | -| `fault_injection.rs` | `NetworkEffectPort` returns each `EffectError` variant; each step fails independently; retry/requeue classification; reconcile context has no broker socket; provider crate has no broker import | +| `fault_injection.rs` | `NetworkEffectPort` returns each `EffectError` variant; `DeletePersistentTap` transient/generation/ownership errors have exact retry/terminal classification; each step fails independently; reconcile context has no broker socket; provider crate has no broker import | | `metrics_labels.rs` | Every metric descriptor uses only closed semantic labels; exact absence of `vm`, `zone`, `zone_id`, `zone_uid`, `network`, and resource-name-derived keys; Network-name canary absent from emitted labels; `d2b.zone` retained as an OTEL resource attribute | ### 26.3 Integration tests (`integration/`) | Test file | Coverage | Runner | | --- | --- | --- | -| `host_fabric.rs` | Bridge create/delete; Network-owned nftables apply; no USBIP/TCP-3240 allow; IPv6 suppression; NM unmanaged; ownership-scoped drift detection; NetworkEffectPort real impl | `make test-integration` (container) | +| `host_fabric.rs` | Persistent tap create/delete pairing; opaque attachment ID and generation fences; validated absent success; foreign-marker fail-closed; no IfName/path request or audit; bridge create/delete; Network-owned nftables apply; no USBIP/TCP-3240 allow; IPv6 suppression; NM unmanaged; ownership-scoped drift detection; NetworkEffectPort real impl | `make test-integration` (container) | | `guest_lifecycle.rs` | net-VM Guest create/delete; opaque attachment handle resolution; systemArtifactId binding | `make test-host-integration` | | `agent_reload.rs` | Agent service Reload() call; nft-applied + routes-applied predicates; config digest match | `make test-host-integration` | | `mdns_reflector.rs` | mDNS reflector Process lifecycle; create when mdns.enable; delete on Network delete | `make test-integration` (container) | -| `delete_sequence.rs` | Full finalizer ordering: Process Deleted events, Volume attachment removal, Guest Deleted, Volume Deleted, fabric cleanup | `make test-host-integration` | +| `delete_sequence.rs` | Full finalizer ordering: workload Guest/VMM FD closure, generation-fenced `DeletePersistentTap` confirmation, Process Deleted events, Volume attachment removal, net-VM Guest Deleted, Volume Deleted, fabric cleanup; transient delete retry retains the handle | `make test-host-integration` | | `external_nic_lifecycle.rs` | Fake physical NIC: Host-global claim before SpawnRunner; cross-Zone conflict has no effect; explicit bridge multiplex; update drain/reacquire; delete closes macvtap before claim release | `make test-host-integration` | ### 26.4 Eval tests (Layer-1, `tests/unit/nix/cases/`) @@ -2652,8 +2720,11 @@ When `Provider/network-local` is retired (superseded or removed): - [ ] `provider-network-local` artifact catalog entry must be removed. - [ ] `d2b-provider-network-local` crate must be removed from workspace members and the members list must remain alphanumerically sorted. -- [ ] Broker ops `CreateBridge` and `DeleteBridge` may be retired if no other - Provider uses them; consult the broker op table in `docs/reference/privileges.md`. +- [ ] Broker ops `CreatePersistentTap` and `DeletePersistentTap` are retired as + a pair only if no other Provider uses them and no retained attachment + realization exists; `CreateBridge` and `DeleteBridge` may be retired if no + other Provider uses them. Consult the broker op table in + `docs/reference/privileges.md`. - [ ] `NetworkEffectPort` trait declaration in `d2b-contracts` must be removed or marked `#[deprecated]` when no other Provider uses it; the core adapter implementation is removed alongside the Provider. From 69183fe09a9ef090745fd51ba98f1379327f3b15 Mon Sep 17 00:00:00 2001 From: John Vicondoa Date: Fri, 24 Jul 2026 07:55:46 -0700 Subject: [PATCH 097/115] docs: make transport ZoneLinks child-local --- .../ADR-046-provider-transport-azure-relay.md | 241 ++++++++++----- .../ADR-046-provider-transport-unix.md | 238 ++++++++------ .../ADR-046-provider-transport-vsock.md | 291 +++++++++++------- 3 files changed, 484 insertions(+), 286 deletions(-) diff --git a/docs/specs/providers/ADR-046-provider-transport-azure-relay.md b/docs/specs/providers/ADR-046-provider-transport-azure-relay.md index b4471a823..e74b8da74 100644 --- a/docs/specs/providers/ADR-046-provider-transport-azure-relay.md +++ b/docs/specs/providers/ADR-046-provider-transport-azure-relay.md @@ -20,6 +20,12 @@ Hybrid Connections. It is one of three initial ZoneLink transport Providers alongside `Provider/transport-unix` (local allocator-issued FD) and `Provider/transport-vsock` (local host/guest vsock). +The Provider is installed in the child Zone that owns the ZoneLink. Its +same-Zone Network, Credential, Guest, Process, and Endpoint refs never cross the +Zone boundary. The child Zone's compiler-only `parentZone` selects the parent +allocator; that parent keeps only sealed allocator/route state and never stores +a reciprocal ZoneLink or selected transport Provider resource. + The Provider's responsibilities are: - Accept typed `spec.provider.settings` from a `ZoneLink` spec and @@ -231,13 +237,13 @@ against it before emitting the resource bundle. }, "listenerCredentialAlias": { "type": "string", - "description": "Alias ID (plain string) resolved against the parent Zone's Provider.spec.config.credentialBindings map to obtain the Listen-role Credential ref. No Credential ref, SAS key, SAS token, or bearer token byte may appear here.", + "description": "Alias ID (plain string) resolved against the child Zone's selected Provider.spec.config.credentialBindings map when the sealed allocation selects the Listen role. No Credential ref, SAS key, SAS token, or bearer token byte may appear here.", "pattern": "^[a-z][a-z0-9-]*$", "maxLength": 64 }, "senderCredentialAlias": { "type": "string", - "description": "Alias ID (plain string) resolved against the child Zone's Provider.spec.config.credentialBindings map to obtain the Send-role Credential ref. Each Zone instance acquires its own credential locally inside its gateway Guest. No Credential ref, SAS key, or token byte may appear here.", + "description": "Alias ID (plain string) resolved against the child Zone's selected Provider.spec.config.credentialBindings map when the sealed allocation selects the Send role. The credential is acquired locally inside the child gateway Guest. No Credential ref, SAS key, or token byte may appear here.", "pattern": "^[a-z][a-z0-9-]*$", "maxLength": 64 } @@ -251,8 +257,8 @@ against it before emitting the resource bundle. | --- | --- | --- | --- | | `relayNamespaceId` | Yes | No | Plain Azure Relay namespace label only; no `.servicebus.windows.net` suffix, no scheme; validated by regex; max 50 chars | | `relayEntityId` | Yes | No | Hybrid Connection entity name; lowercase kebab; max 50 chars | -| `listenerCredentialAlias` | Yes | No | Plain alias ID; resolved by parent Zone's Provider instance against its own `config.credentialBindings` map; never a Credential ref or token | -| `senderCredentialAlias` | Yes | No | Plain alias ID; resolved by child Zone's Provider instance against its own `config.credentialBindings` map; never a Credential ref or token | +| `listenerCredentialAlias` | Yes | No | Plain alias ID; resolved by the child Zone's selected Provider against its same-Zone `config.credentialBindings` map when assigned the Listen role; never a Credential ref or token | +| `senderCredentialAlias` | Yes | No | Plain alias ID; resolved by the child Zone's selected Provider against its same-Zone `config.credentialBindings` map when assigned the Send role; never a Credential ref or token | The build emitter **rejects** any `spec.provider.settings` field: @@ -272,9 +278,17 @@ is exclusive. ### Nix authoring example ```nix +# local-root is the provisioning parent. It has no ZoneLink or transport +# Provider resource for this edge; only its allocator's sealed route state exists. +d2b.zones.local-root = {}; + +# K2 is the CHILD Zone. parentZone is compiler-only and selects local-root's +# allocator; it is absent from every emitted resource. +d2b.zones.k2.parentZone = "local-root"; + # Network resource that governs egress routing to Azure Relay. # TLS trust is governed by Network policy; no Credential ref needed. -d2b.zones.k1.resources.relay-egress = { +d2b.zones.k2.resources.relay-egress = { type = "Network"; spec = { allowEgress = true; @@ -284,7 +298,7 @@ d2b.zones.k1.resources.relay-egress = { # Credential for the Listener-role SAS key. # scope.executionRef must match Provider config.executionRef. -d2b.zones.k1.resources.relay-listen-k2 = { +d2b.zones.k2.resources.relay-listen = { type = "Credential"; spec = { providerRef = "Provider/credential-secret-service"; @@ -297,9 +311,25 @@ d2b.zones.k1.resources.relay-listen-k2 = { }; }; +# Credential for the Sender-role SAS key. Both role credentials remain local to +# K2 and are resolved by K2's selected Provider instance. +d2b.zones.k2.resources.relay-send = { + type = "Credential"; + spec = { + providerRef = "Provider/credential-secret-service"; + audience = "azure-relay-send"; + allowedOperations = [ "acquire-token" ]; + consumerRef = "Provider/transport-azure-relay"; + scope.executionRef = "Guest/work-gateway"; + rotation.policy = "proactive"; + rotation.proactiveWindowMs = 300000; + }; +}; + # Provider resource. config.executionRef gates all component placement; -# config.networkRef governs egress routing. -d2b.zones.k1.resources.transport-azure-relay = { +# config.networkRef governs egress routing. It is in the same CHILD Zone as the +# ZoneLink and every ref it resolves. +d2b.zones.k2.resources.transport-azure-relay = { type = "Provider"; spec = { artifactId = "provider-transport-azure-relay"; @@ -307,7 +337,8 @@ d2b.zones.k1.resources.transport-azure-relay = { executionRef = "Guest/work-gateway"; # required; service components run here networkRef = "Network/relay-egress"; # required; Network governs TLS trust credentialBindings = { - relay-listen = "Credential/relay-listen-k2"; + relay-listen = "Credential/relay-listen"; + relay-send = "Credential/relay-send"; }; maxConcurrentSessions = 32; connectTimeoutSeconds = 30; @@ -315,10 +346,9 @@ d2b.zones.k1.resources.transport-azure-relay = { }; }; -# ZoneLink K1→K2 using transport-azure-relay. -# spec.provider.settings carries plain alias IDs; parent and child each -# resolve aliases locally inside their respective gateway Guests. -d2b.zones.k1.resources.k2-guest = { +# K2's sole child-local uplink. childZoneName self-matches its enclosing Zone; +# local-root gets no reciprocal resource. +d2b.zones.k2.resources.k2-uplink = { type = "ZoneLink"; spec = { childZoneName = "k2"; @@ -329,8 +359,8 @@ d2b.zones.k1.resources.k2-guest = { settings = { relayNamespaceId = "relns-d2b-prod"; # non-secret namespace label relayEntityId = "hc-d2b-k2"; # non-secret entity name - listenerCredentialAlias = "relay-listen"; # alias ID; parent resolves locally - senderCredentialAlias = "relay-send"; # alias ID; child resolves locally + listenerCredentialAlias = "relay-listen"; # K2-local alias; selected Provider resolves + senderCredentialAlias = "relay-send"; # K2-local alias; selected Provider resolves }; }; childStaticKeyFingerprint = "9d2e1f...7f01"; # 64 lower-hex chars @@ -348,6 +378,30 @@ d2b.zones.k1.resources.k2-guest = { }; ``` +The compiler derives `metadata.zone: k2` for both resources. The emitted +ZoneLink identity is therefore child-local: + +```yaml +apiVersion: resources.d2bus.org/v3 +type: ZoneLink +metadata: + name: k2-uplink + zone: k2 +spec: + childZoneName: k2 + providerRef: Provider/transport-azure-relay + provider: + schemaId: transport-azure-relay.d2bus.org/ZoneLink/spec + schemaVersion: "1.0" + settings: + relayNamespaceId: relns-d2b-prod + relayEntityId: hc-d2b-k2 + listenerCredentialAlias: relay-listen + senderCredentialAlias: relay-send +``` + +No corresponding object is emitted into local-root's resource bundle. + --- ## Enrolled Noise KK end-to-end authentication @@ -369,13 +423,15 @@ never authenticates a d2b Zone subject. ### KK enrollment contract -Before a ZoneLink becomes Ready, both Zone controllers must complete an -out-of-band enrollment exchange that establishes: +Before the child-local ZoneLink becomes Ready, the child controller and the +selected parent allocator's sealed route endpoint complete an out-of-band +enrollment exchange that establishes: -- **Parent** (listener side): the parent's controller enrolls its own static - 25519 key pair. The public key fingerprint is sealed into the selected-parent - allocator binding delivered to the child controller; it is not stored in a - reciprocal parent ZoneLink. +- **Selected parent route endpoint**: the parent allocator binds its route + principal's static 25519 public-key fingerprint into the sealed allocation + delivered to the child controller. The parent keeps only that sealed + allocator/route state; it has no ZoneLink row, transport Provider resource, or + parent-side ZoneLink handler. - **Child** (sender side): the child's controller enrolls its own static 25519 key pair. Its public key fingerprint is committed in the child-local `ZoneLink.spec.childStaticKeyFingerprint` and verified by the parent @@ -416,8 +472,8 @@ from Azure Relay auth material: - A managed identity token proving the `Send` Entra claim does not grant access to the Zone's resource API beyond what the KK-enrolled child key authorizes. - The Provider reports relay auth success/failure only as transport connection - status; the ZoneLink controller maps only the KK-enrolled static key to the - child Zone subject. + status; the child Zone's ZoneLink controller maps only the KK-enrolled static + key to the child Zone subject. This is a load-bearing invariant from ADR 0032 (§Load-bearing invariant: relay identity is not local auth) carried forward to d2b 3.0. @@ -463,20 +519,17 @@ scope does not match is refused by the Credential controller with Alias IDs are plain strings carrying no credential bytes. `ZoneLink.spec.provider.settings` carries `listenerCredentialAlias` and -`senderCredentialAlias` — plain alias ID strings, not Credential refs: - -- **Parent Zone** (`listenerCredentialAlias`): the parent gateway Guest's - listener service resolves the alias against its own `config.credentialBindings` - to find the Credential ref, then acquires that credential inside the gateway - Guest via the Credential KK session. -- **Child bootstrap**: the child receives `spec.provider.settings` as part of the - ZoneLink bootstrap. The child gateway Guest's sender service resolves - `senderCredentialAlias` against **its own** `config.credentialBindings` and - acquires its credential independently, inside its own gateway Guest. - -No cross-Zone credential bytes exist. No parent-minted token is delivered to the -child. Each Zone's gateway service component is independently responsible for -credential acquisition and relay authentication. +`senderCredentialAlias` — plain alias ID strings, not Credential refs. The +selected Provider and ZoneLink are both in the child Zone. Its gateway Guest +resolves either alias against that Provider's same-Zone +`config.credentialBindings` when the sealed allocation assigns the corresponding +Listen or Send role, and acquires the credential through the local Credential KK +session. + +No alias, Credential ref, or credential byte crosses the Zone boundary. No +parent-minted token is delivered to the child, and the parent allocator does not +resolve child aliases. The parent keeps only sealed allocator/route state for +its endpoint; it has no Provider or Credential resource for the child edge. ### Credential acquisition over Noise KK @@ -514,7 +567,7 @@ would make credential acquisition impossible. | Type | service | | Binary | `d2b-transport-azure-relay-listener` | | Execution domain | system | -| Placement | Gateway Guest identified by `config.executionRef` | +| Placement | Gateway Guest identified by the child-local Provider's `config.executionRef` | | Cardinality | 0/1 per Provider instance; internally multiplexes up to `maxConcurrentSessions` relay sessions across all active ZoneLinks | | ResourceTypes owned | none (returns `RelayTransportObservation` to core-controller ZoneLink handler; does not write ZoneLink.status) | @@ -531,8 +584,8 @@ Responsibilities: - TLS and WebSocket state remain in this process; only end-to-end Noise record bytes traverse the named byte stream. The listener cannot decrypt them. - Returns typed `RelayTransportObservation` values to the core-controller - ZoneLink handler; the ZoneLink handler is the sole `update-status` writer on - ZoneLink resources. + child Zone's core-controller ZoneLink handler; that child-local handler is the + sole `update-status` writer on the ZoneLink resource. - Emits OTEL metrics and spans (see §OTEL). - Emits audit records for authentication events (see §Audit). @@ -550,6 +603,7 @@ apiVersion: resources.d2bus.org/v3 type: Process metadata: name: transport-azure-relay-listener + zone: k2 ownerRef: Provider/transport-azure-relay spec: template: listener @@ -609,7 +663,7 @@ spec: | Type | service | | Binary | `d2b-transport-azure-relay-sender` | | Execution domain | system | -| Placement | Gateway Guest identified by `config.executionRef` of the child Zone's Provider instance | +| Placement | Gateway Guest identified by `config.executionRef` of the child Zone's selected Provider instance | | Cardinality | 0/1 per Provider instance; internally multiplexes bounded send sessions | Responsibilities: @@ -621,7 +675,8 @@ Responsibilities: - Pumps TLS/WebSocket bytes between the relay connection and the local ComponentSession transport; remains alive for the duration of each session. - Internally multiplexes active sender sessions up to `maxConcurrentSessions`. -- Returns typed observations to the core-controller ZoneLink handler. +- Returns typed observations to the child Zone's core-controller ZoneLink + handler. The sender service does **not**: @@ -635,6 +690,7 @@ apiVersion: resources.d2bus.org/v3 type: Process metadata: name: transport-azure-relay-sender + zone: k2 ownerRef: Provider/transport-azure-relay spec: template: sender @@ -698,6 +754,7 @@ apiVersion: resources.d2bus.org/v3 type: Endpoint metadata: name: transport-azure-relay-listener-service + zone: k2 ownerRef: Process/transport-azure-relay-listener spec: providerRef: Provider/transport-azure-relay @@ -725,6 +782,7 @@ apiVersion: resources.d2bus.org/v3 type: Endpoint metadata: name: transport-azure-relay-sender-service + zone: k2 ownerRef: Process/transport-azure-relay-sender spec: providerRef: Provider/transport-azure-relay @@ -753,7 +811,9 @@ Consumers refer to `Endpoint/` and resolve it only through the authorized EffectPort/LaunchTicket path; unauthorized callers receive `endpoint-resolve-denied`. A listener or sender Process restart bumps the child Endpoint `endpointGeneration`, which dependents observe as `dependency-changed`. -ZoneLink session state remains owned by the core ZoneLink controller. +ZoneLink session state remains owned by the child Zone's core ZoneLink +controller. The parent store contains no matching Endpoint, Provider, or +ZoneLink resource; only sealed allocator/route state exists there. ### Retained opaque handles (D092) @@ -788,8 +848,9 @@ memory (`OwnedTransport`/WebSocket handles, per D081) and is never persisted. Its bounded non-secret operational state — listener/sender readiness, transport-open/close reconcile stage, bounded reconnect/connection counters, and closed-enum error detail — lives in the owning resource's `status` -subresource and the core Operation ledger (D087). All ZoneLink session state is -owned by the core ZoneLink controller under the `ZoneLink` resource. +subresource and the child Zone's core Operation ledger (D087). All ZoneLink session state is +owned by the child Zone's core ZoneLink controller under its local `ZoneLink` +resource. No relay auth token, WebSocket handle, session key, or credential byte is ever persisted; those bytes cross the sensitive KK delivery path in process memory @@ -805,16 +866,19 @@ state-layout `User/` principal. There is no empty identity-only Volume. ### Transport service interface -The core ZoneLink controller drives the relay transport lifecycle. The relay -Provider is a **typed transport service**; it does not read ZoneLink resources, -own Zone session state, or initiate Zone-level operations. Core calls the -Provider with already-validated `spec.provider.settings` and receives an opaque -byte-stream handle in return. +The child Zone's core ZoneLink controller drives the relay transport lifecycle +through the selected Provider in that same child Zone. The relay Provider is a +**typed transport service**; it does not read ZoneLink resources, own Zone +session state, or initiate Zone-level operations. The child-local controller +calls the Provider with already-validated `spec.provider.settings` and receives +an opaque byte-stream handle in return. The parent route endpoint is selected +only through sealed allocator state and has no reciprocal Provider or ZoneLink +resource. #### `OpenTransport(spec.provider.settings) → TransportHandle` -Called by the core ZoneLink controller when it needs a new relay channel. The -listener service: +Called by the child Zone's core ZoneLink controller when it needs a new relay +channel. The child-local service assigned the listener role: 1. Resolves `listenerCredentialAlias` from `config.credentialBindings`, acquires the Azure credential via KK inside the gateway Guest, and authenticates to @@ -834,8 +898,9 @@ reconnect policy and issues a new `OpenTransport` call. #### `CloseTransport(handle)` -Called by the core ZoneLink controller to tear down the relay channel. The -relay service closes the WebSocket and releases the carriage session. +Called by the child Zone's core ZoneLink controller to tear down the relay +channel. The relay service closes the WebSocket and releases the carriage +session. #### `ObserveTransport(handle) → Stream` @@ -846,8 +911,9 @@ write ZoneLink status directly. ### Core ownership -The following are owned exclusively by the core ZoneLink controller. The relay -transport service has no authority or visibility over them: +The following are owned exclusively by the child Zone's core ZoneLink +controller. The relay transport service has no authority or visibility over +them, and the parent has no ZoneLink handler: | Concern | Core responsibility | | --- | --- | @@ -855,12 +921,13 @@ transport service has no authority or visibility over them: | Session generation counter | Core increments on each reconnect | | Reconnect policy and backoff | Core's reconnect policy drives reconnect; core calls `CloseTransport` then `OpenTransport` after applying its own backoff | | Idempotency key tracking | Core assigns and deduplicates `ZoneLinkIdempotencyKey` | -| Route state and Watch cursors | Core manages; relay has no view | -| ZoneLink resource status and finalizer | Core writes; relay returns observations only | +| Route state and Watch cursors | Child core manages local session/cursor state; the parent retains only sealed allocator/route state; relay has no view | +| ZoneLink resource status and finalizer | Child core writes its local resource; relay returns observations only | ### Currency and upgrade (D091) -The core ZoneLink controller, not the relay transport service, implements +The child Zone's core ZoneLink controller, not the relay transport service or +the parent allocator, implements `assess_update`, `plan_upgrade`, and `execute_upgrade`. A Provider generation or signed artifact generation/digest change updates universal `status.update` with `state: UpdateAvailable` or `state: UpgradeRequired`, `reasons` including @@ -869,9 +936,9 @@ digest IDs, `disruption: Reload` or `disruption: Restart`, `preserveState: true`, bounded `owned`/`dependencies`, and `lastAssessedAt`. Disruptive changes MUST return `UpgradeRequired` rather than applying in place; non-disruptive changes reconcile normally. Upgrade recycles the relay service realization; -open byte-stream handles are re-established by core reconnect. ZoneLink session -state remains owned by the core ZoneLink controller. `status.update` MUST NOT -contain secrets. +open byte-stream handles are re-established by child-core reconnect. ZoneLink +session state remains owned in the child store by the child Zone's core +ZoneLink controller. `status.update` MUST NOT contain secrets. ### Expedited reconcile on mutation (D090) @@ -964,8 +1031,9 @@ Granted: Not held: - `get`, `watch`, `update-status`, `create`, `update-spec`, or `delete` on any - ZoneLink (the core ZoneLink controller owns those; the relay service receives - `spec.provider.settings` as a parameter from core, not by reading the resource); + ZoneLink (the child Zone's core ZoneLink controller owns its local resource; + the relay service receives `spec.provider.settings` as a parameter from that + same-Zone controller, not by reading the resource); - any ResourceAPI verb on Zone resources other than the above; - any relay-forwarding verb for routing calls to child Zones. @@ -1017,26 +1085,28 @@ Credential resource. ### Core-aggregated transport status -The relay transport service does **not** write ZoneLink status. Core receives -a `Stream` from `ObserveTransport` and aggregates those -observations into the ZoneLink transport status sub-object. +The relay transport service does **not** write ZoneLink status. The child +Zone's core controller receives a `Stream` from +`ObserveTransport` and aggregates those observations into its local ZoneLink +transport status sub-object. There is no parent-side ZoneLink status or handler. -Per D088, core writes the ZoneLink universal `ResourceStatus` base at top-level +Per D088, child core writes the ZoneLink universal `ResourceStatus` base at top-level `status.*` and cross-provider ZoneLink/transport observation under `status.resource`. Azure-specific bounded, non-secret observation belongs in `status.provider` with `providerRef: Provider/transport-azure-relay`, qualified `schemaId: transport-azure-relay.d2bus.org/ZoneLink/status`, `schemaVersion` (semver MAJOR.MINOR), `observedProviderGeneration`, and a strict unknown-field-denied, ≤32 KiB, -redacted `details` object registered and signed in the Provider manifest. Core -writes all present layers atomically in one status mutation; shared fields are -promoted to `status.resource` and never duplicated into `status.provider`. +redacted `details` object registered and signed in the Provider manifest. Child +core writes all present layers atomically in one child-store status mutation; +shared fields are promoted to `status.resource` and never duplicated into +`status.provider`. The ZoneLink handler writes the following D088 shape based on observations received: ```yaml status: - # ZoneLink core status (managed by core ZoneLink controller): + # Child-local ZoneLink status (managed by K2's core ZoneLink controller): phase: Ready conditions: [...] resource: @@ -1098,10 +1168,10 @@ Rules: | `Failed` | Core reconnect policy exhausted or credential unrecoverable | | `Unknown` | Core cannot determine carriage state from `ObserveTransport` stream | -Core transitions the ZoneLink resource to phase `Failed` after the reconnect -policy is exhausted or when the listener Credential transitions to `LeaseRevoked` -with no replacement; the relay service reports the carriage health observation -and core owns the final status write. +The child Zone's core controller transitions its ZoneLink resource to phase +`Failed` after the reconnect policy is exhausted or when the listener Credential +transitions to `LeaseRevoked` with no replacement; the relay service reports the +carriage health observation and child core owns the final status write. --- @@ -1316,7 +1386,7 @@ download, or PATH scan. | Reuse action | adapt | | Destination | `packages/d2b-provider-transport-azure-relay/src/relay_transport.rs` | | Detailed design | Adapt `RelayStream` as relay transport service process; expose named opaque byte stream on the `transport-service` Unix endpoint; add 2-byte length-prefixed framing; preserve credential redaction; TLS/WebSocket state stays in-process — only Noise record bytes traverse the named stream; register named stream with d2b-bus as `TransportHandle`; transport descriptor: `attachment_support: false`, `locality: Remote`, `atomic: false`; expose `OpenTransport`/`CloseTransport`/`ObserveTransport` interface to core; long-lived service process multiplexes sessions internally Primary reuse disposition: `adapt`. Preserved source-plan detail: extract and adapt. | -| Integration | Core ZoneLink controller calls `OpenTransport(spec.provider.settings)` → receives named byte stream handle; relay service cannot interpret plaintext bytes; one carriage per call; WebSocket loss closes the named stream | +| Integration | The child Zone's core ZoneLink controller calls its same-Zone selected Provider's `OpenTransport(spec.provider.settings)` → receives named byte stream handle; relay service cannot interpret plaintext bytes; one carriage per call; WebSocket loss closes the named stream; the parent has only sealed allocator/route state | | Data migration | No compatibility with current relay sessions; v3 sessions are independent | | Validation | `tests/fake_relay_transport.rs`: connect/accept, framing, credential redaction, named stream roundtrip; `tests/listener_sender_conformance.rs`: named stream contract; Noise KK binding; relay identity exclusion | | Removal proof | `d2b-provider-relay/src/lib.rs` relay plumbing retained until ACA display migration completes | @@ -1329,8 +1399,8 @@ download, or PATH scan. | Current source | None (new) | | Reuse action | create | | Destination | `packages/d2b-provider-transport-azure-relay/src/credential_client.rs` | -| Detailed design | Async Credential KK session client for service components (service processes have d2b-bus access, enabling Credential KK; workers do not); acquire listener credential via KK inside the gateway Guest using `config.credentialBindings[listenerCredentialAlias]`; acquire sender credential independently inside the child's gateway Guest via the same KK model using `config.credentialBindings[senderCredentialAlias]`; raw credential bytes held in zeroizing memory inside the gateway Guest, presented to Azure Relay, then immediately zeroized; no credential bytes cross process, network, or Guest boundary; redacted Debug; no credential bytes in logs/audit/OTEL; core ProviderDeployment creates a private persistent Volume (per ADR-046-provider-state) for each component before its Process starts — the transport Provider does not own or create these Volumes; `Provider/volume-local` reconciles them; `migrationPolicy: none` means no migration worker is ever spawned; no relay auth token, WebSocket handle, session key, or credential byte is written to that Volume; all relay session state remains transient in-process memory | -| Integration | Listener service invokes before each relay connect attempt inside gateway Guest; child Zone's sender service acquires its own credential independently inside its own gateway Guest | +| Detailed design | Async Credential KK session client for service components (service processes have d2b-bus access, enabling Credential KK; workers do not); the child-local selected Provider acquires either listener or sender credential via KK inside its gateway Guest using the corresponding same-Zone `config.credentialBindings` alias; raw credential bytes are held in zeroizing memory inside the gateway Guest, presented to Azure Relay, then immediately zeroized; no credential ref or bytes cross a Zone, process, network, or Guest boundary; the parent allocator receives neither aliases nor credentials and keeps only sealed route state; redacted Debug; no credential bytes in logs/audit/OTEL; the child Zone's core ProviderDeployment creates a private persistent Volume (per ADR-046-provider-state) for each component before its Process starts — the transport Provider does not own or create these Volumes; `Provider/volume-local` reconciles them; `migrationPolicy: none` means no migration worker is ever spawned; no relay auth token, WebSocket handle, session key, or credential byte is written to that Volume; all relay session state remains transient in-process memory | +| Integration | The selected Provider's role service invokes acquisition inside the child gateway Guest; the parent allocator has only sealed route state and no Provider/Credential/ZoneLink resource | | Data migration | None — full d2b 3.0 reset; no prior state to migrate | | Validation | `tests/credential_redaction.rs`: credential bytes never reach any Debug/log/audit/OTEL path; `src/tests/integration/credential_delivery.rs`: end-to-end credential delivery using injected fake Credential effect port | | Removal proof | N/A; new module | @@ -1339,7 +1409,7 @@ download, or PATH scan. | Field | Value | | --- | --- | -| Dependency/owner | ADR046-transport-relay-001; reconnect contract; ZoneLink handler | +| Dependency/owner | ADR046-transport-relay-001; reconnect contract; child Zone's ZoneLink handler | | Current source | None (new; core drives reconnect, not the transport Provider) | | Reuse action | create | | Destination | `packages/d2b-provider-transport-azure-relay/src/reconnect.rs` | @@ -1357,7 +1427,7 @@ download, or PATH scan. | Current source | `docs/specs/ADR-046-zone-routing.md` transport settings Nix example | | Reuse action | create | | Destination | `packages/d2b-provider-transport-azure-relay/src/transport_settings.rs`; `docs/reference/schemas/v3/providers/transport-azure-relay.transport-settings.json` | -| Detailed design | `AzureRelayTransportSettings` Rust struct with serde; validation against committed JSON Schema; reject `secret`-annotated fields; enforce `^[a-z][a-z0-9-]*$` pattern for `listenerCredentialAlias`/`senderCredentialAlias` alias ID fields (never `Credential/` refs); xtask `gen-provider-transport-schemas` integration | +| Detailed design | `AzureRelayTransportSettings` Rust struct with serde; validation against committed JSON Schema; reject `secret`-annotated fields; enforce `^[a-z][a-z0-9-]*$` pattern for `listenerCredentialAlias`/`senderCredentialAlias` alias ID fields (never `Credential/` refs); both aliases resolve only through the selected Provider in the same child Zone as the ZoneLink; xtask `gen-provider-transport-schemas` integration | | Integration | `make test-drift` gate: `xtask gen-provider-transport-schemas && git diff --exit-code` | | Data migration | None — full d2b 3.0 reset; no prior state to migrate | | Validation | `tests/transport_settings_schema.rs`: valid/invalid schema vectors; eval-time Nix assertion coverage from `nix-unit: transport-settings-secret-key` test (see zone-routing spec) | @@ -1399,7 +1469,7 @@ download, or PATH scan. | Current source | None — net-new v3 work; no pre-ADR45 baseline equivalent | | Reuse action | create | | Destination | `packages/d2b-provider-transport-azure-relay/src/tests/integration/README` | -| Detailed design | Required content: fake relay server setup and teardown using the injected fake Relay effect port; how to run hermetic integration tests without a live Azure service; how to configure the injected fake Credential effect port for credential delivery tests; how to run with a real Azure namespace (requires a `Credential` resource declared in `spec.config.credentialBindings`, not environment-variable credential paths); integration test scenarios and expected outcomes; CI/local execution instructions | +| Detailed design | Required content: fake relay server setup and teardown using the injected fake Relay effect port; how to run hermetic integration tests without a live Azure service; how to configure the injected fake Credential effect port for credential delivery tests; the fixture declares compiler-only `k2.parentZone = "local-root"` and puts the ZoneLink, selected Provider, Network, Credentials, Process, and Endpoint resources only in K2; local-root's store is asserted to contain no reciprocal ZoneLink or Provider; how to run with a real Azure namespace (requires same-child-Zone `Credential` resources declared in `spec.config.credentialBindings`, not environment-variable credential paths); integration test scenarios and expected outcomes; CI/local execution instructions | | Integration | `make test-integration` invokes `tests/integration/containers/` scenarios which inject the fake relay and credential port implementations from `src/tests/integration/fake_relay_server.rs` | | Data migration | None — full d2b 3.0 reset; no prior state to migrate | | Validation | File must be present; workspace policy gate enforces `src/tests/integration/README` | @@ -1424,12 +1494,13 @@ wave: | `metric_labels.rs` | `identity_keys_absent`, `zone_name_canary_absent`, `semantic_domains_closed`, `zone_resource_attribute_retained` | Structural metric-label policy; no Zone/resource identity labels; `d2b.zone` remains a resource attribute | | `idempotency_key.rs` | `idempotency_key_carried_in_noise_record`, `idempotency_key_not_in_relay_frame_metadata`, `replay_at_relay_level_does_not_deduplicate`, `dedup_at_child_zone_resource_api` | Idempotency is child-Zone-owned; relay carries opaquely | | `listener_sender_conformance.rs` | `conformance_vectors_listener`, `conformance_vectors_sender`, `noise_kk_prologue_binds_transport_settings`, `mismatched_fingerprint_fails_closed`, `relay_identity_not_in_subject_context` | Named stream contract; Noise KK binding; relay identity exclusion | +| `child_local_topology.rs` | `provider_and_zonelink_are_in_child_zone`, `child_zone_name_self_matches`, `parent_zone_is_compiler_only`, `parent_store_has_no_reciprocal_resources`, `credential_aliases_resolve_only_in_child` | K2 owns the ZoneLink and selected Provider; local-root is selected only as allocator and retains sealed route state; no cross-Zone ref or credential path | ### Integration tests (in `src/tests/integration/`) | Test | Fixture | What it proves | | --- | --- | --- | -| `zone_link_connect.rs` | `fake_relay_server.rs` (in-process async fake relay via injected effect port) | Full ZoneLink bootstrap over fake relay: connect, Noise KK, resource API ping, Watch | +| `zone_link_connect.rs` | `fake_relay_server.rs` (in-process async fake relay via injected effect port) | Full child-local ZoneLink bootstrap over fake relay: K2-local Provider/link, compiler-only `k2.parentZone = "local-root"`, no local-root reciprocal row, connect, Noise KK, resource API ping, Watch | | `credential_delivery.rs` | Injected fake Credential effect port | Listener credential acquired via KK session; token bytes zeroized; sender credential acquired independently by child Zone instance; no cross-Zone token minting | | `reconnect_scenario.rs` | Fake relay server with injected disconnect | Disconnect triggers new relay connect; new Noise KK; Watch resumes from last revision; queued intents replayed | | `fake_relay_server.rs` | — | Fake Azure Relay server that accepts listener and sender WebSocket roles; no real Azure service required; controllable inject-disconnect API; passed as constructor argument or injected effect port to test subjects | @@ -1479,8 +1550,8 @@ are met: | Existing symbol/crate | Removal condition | | --- | --- | -| `packages/d2b-provider-relay/src/lib.rs` relay WebSocket plumbing | All callers replaced by `d2b-provider-transport-azure-relay`; ACA display path migrated to v3 Provider model (`ADR046-transport-relay-001` complete and ACA Provider dossier integrates relay) | -| `packages/d2b-gateway-runtime/src/bin/d2b-gateway-relay.rs` | ACA gateway display path migrated to `Provider/runtime-azure-container-apps` + `Provider/transport-azure-relay`; gateway guest ZoneLink bootstrap tested | +| `packages/d2b-provider-relay/src/lib.rs` relay WebSocket plumbing | All callers replaced by the child-local `d2b-provider-transport-azure-relay`; ACA display path migrated to v3 Provider model; topology tests prove the parent keeps only sealed allocator/route state and has no reciprocal Provider/ZoneLink resource (`ADR046-transport-relay-001` complete and ACA Provider dossier integrates relay) | +| `packages/d2b-gateway-runtime/src/bin/d2b-gateway-relay.rs` | ACA gateway display path migrated to the child Zone's `Provider/runtime-azure-container-apps` + `Provider/transport-azure-relay`; child-local gateway ZoneLink bootstrap and absent parent-store reciprocal row tested | | `d2b-realm-provider` `RelayProvider` / `TransportProvider` traits | All implementations migrated to Provider ResourceType model; `d2b-provider-relay` usage in `d2bd` removed | | `D2B_RELAY_NAMESPACE` / `D2B_RELAY_ENTITY` environment variables in `d2b-relay.rs` CLI | Relay CLI replaced by `d2b transport status` and Provider-managed lifecycle; live integration tests updated to use Credential resources in config | diff --git a/docs/specs/providers/ADR-046-provider-transport-unix.md b/docs/specs/providers/ADR-046-provider-transport-unix.md index 7ab0f1a61..2a712b3ff 100644 --- a/docs/specs/providers/ADR-046-provider-transport-unix.md +++ b/docs/specs/providers/ADR-046-provider-transport-unix.md @@ -120,9 +120,13 @@ implement. They are owned by ADR046-session-001 in `packages/d2b-bus/src/transpo ## Overview `Provider/transport-unix` is a **transport service Provider**. It exposes one -long-lived `service` Process that the core ZoneLink controller invokes over a -typed portal to obtain, close, and observe Unix-domain socket transports. It -owns no ResourceTypes, reconciles no resources, and holds no session state. +long-lived `service` Process in the child Zone that owns the ZoneLink. That +child Zone's core ZoneLink controller invokes the Provider over a typed portal +to obtain, close, and observe Unix-domain socket transports. It owns no +ResourceTypes, reconciles no resources, and holds no session state. The +compiler-only child `parentZone` setting selects the allocator that pre-binds +the edge; the parent retains only sealed allocator/route state and no reciprocal +ZoneLink, Provider, Process, or Endpoint resource. Its boundaries: @@ -136,12 +140,14 @@ Its boundaries: | Stable transport-layer error codes | Admission of ZoneLink or child Zone identity | | `route_class` enforcement: ZoneLink transports are always FD-attachment-free | SCM_RIGHTS grants across Zone boundaries (prohibited by ZoneLink contract) | -The core ZoneLink controller (ADR-046-core-controllers) is the sole reconciler -for `ZoneLink` resources, including watching, status updates, condition -transitions, finalizer management, route advertisement, and queued-intent -lifecycle. It calls `OpenTransport`/`CloseTransport`/`ObserveTransport` on this -Provider to obtain the transport primitive; everything else is core's and -ComponentSession/d2b-bus's responsibility. +The child Zone's core ZoneLink controller (ADR-046-core-controllers) is the sole +reconciler for its local `ZoneLink` resource, including watching, status +updates, condition transitions, finalizer management, route advertisement, and +queued-intent lifecycle. It calls +`OpenTransport`/`CloseTransport`/`ObserveTransport` on the selected Provider in +that same child Zone to obtain the transport primitive; everything else is +child core's and ComponentSession/d2b-bus's responsibility. No parent-side +ZoneLink handler exists. --- @@ -152,7 +158,7 @@ ComponentSession/d2b-bus's responsibility. | `providerRef` | `Provider/transport-unix` | | Crate | `d2b-provider-transport-unix` | | Workspace path | `packages/d2b-provider-transport-unix/` | -| Implements | Transport service for local Unix-socket ZoneLink sessions and within-Zone bus connections | +| Implements | Child-local transport service for Unix-socket ZoneLink sessions and within-Zone bus connections | | Artifact type | `transport` | | Component type | `service` | | Process domains | `system` only | @@ -238,7 +244,8 @@ readiness, transport-open/close reconcile stage, bounded connection counters, and closed-enum error detail — lives in the owning resource's `status` subresource and the core Operation ledger (D087). All ZoneLink session state (generation, reconnect count, queued intents, route revision) is owned by the -core ZoneLink controller in the Zone redb store under the `ZoneLink` resource. +child Zone's core ZoneLink controller in that child's redb store under its local +`ZoneLink` resource. The transport service holds only opaque byte-stream handles in its own process memory (`OwnedTransport`, per D081), which are never persisted. @@ -266,7 +273,7 @@ docs/reference/schemas/v3/providers/transport-unix.transport-binding.json { "$schema": "http://json-schema.org/draft-07/schema#", "title": "UnixTransportSettings", - "description": "ZoneLink spec.provider.settings for Provider/transport-unix. Normally empty ({}). The ZoneLink contract prohibits FD/resource grants across Zone boundaries; FD attachment (SCM_RIGHTS) is therefore not a configurable field here. Core always opens ZoneLink transports with attachments_enabled=false. The allocator/core pre-binds the socket and supplies it as an opaque FD attachment to the OpenTransport portal call; no path or credential is needed here.", + "description": "ZoneLink spec.provider.settings for Provider/transport-unix. Normally empty ({}). The ZoneLink contract prohibits FD/resource grants across Zone boundaries; FD attachment (SCM_RIGHTS) is therefore not a configurable field here. Child core always opens ZoneLink transports with attachments_enabled=false. The selected parent allocator pre-binds the edge, retains its endpoint only as sealed route state, and injects the child endpoint through sealed bootstrap; child core supplies that endpoint locally to the same-Zone Provider's OpenTransport portal. No parent resource, cross-Zone FD transfer, path, or credential is needed here.", "type": "object", "properties": { "socketKind": { @@ -301,9 +308,10 @@ glance. Runtime validation also enforces the schema independently of the build s is optional and defaults to `"seqpacket"`. - **`attachmentsEnabled` is not a ZoneLink `spec.provider.settings` field.** The ZoneLink contract prohibits FD and resource grants across Zone boundaries — - even for same-kernel parent/child links. Core always opens ZoneLink transports - with `attachments_enabled=false`; a cross-Zone SCM_RIGHTS attempt fails - structurally at the core level before `OpenTransport` is called. + even for same-kernel parent/child links. Child core always opens ZoneLink + transports with `attachments_enabled=false`; a cross-Zone SCM_RIGHTS attempt + fails structurally at the child-core boundary before `OpenTransport` is + called. - `socketKind: "seqpacket"` over a ZoneLink provides packet-boundary semantics only. No SCM_RIGHTS ancillary data is sent or received; only Noise-protected record payloads traverse the link. @@ -400,13 +408,16 @@ encoded in the allocator-issued FD and never surfaced in the ResourceSpec. ### `InheritedSocketpair` Used when the allocator creates a `socketpair(AF_UNIX, SOCK_SEQPACKET, 0)` and -passes one end to the parent Zone's transport service (via `OpenTransport` -attachment) and the other end to the child Zone runtime at spawn. The kernel -fills `SO_PEERCRED` at `socketpair` time; the transport reads `SO_PEERCRED` -on the accepted socket end immediately. +retains the parent route endpoint only in the selected allocator's sealed state +while injecting the other endpoint into the child runtime's sealed bootstrap. +The child core passes its endpoint as a **same-Zone portal attachment** to its +local `Provider/transport-unix`; neither endpoint is represented by a parent +resource and no FD is transferred across the ZoneLink. The kernel fills +`SO_PEERCRED` at `socketpair` time; the transport reads `SO_PEERCRED` on the +child endpoint immediately. -`InheritedSocketpair` is the default policy for ZoneLink sessions between a -parent Zone and a local child Zone runtime. +`InheritedSocketpair` is the default policy for a child-local ZoneLink whose +compiler-only `parentZone` selects a local parent allocator. ### SO_PASSCRED on first packet @@ -546,7 +557,7 @@ component: stateNamespaces: [] # no Provider state Volume; operational state in status/core ledger (D087) permissionClaims: - verb: receive-attachment # receives allocator-issued FD attachments via OpenTransport - - verb: invoke # called by core ZoneLink controller over portal + - verb: invoke # called by the child Zone's core handler over its local portal readiness: class: provider-defined initialDelay: "0s" @@ -560,8 +571,8 @@ component: limit: "50m" ``` -The core ProviderDeployment (not a Provider controller — no such controller -exists for this Provider) creates the following `Process` resource when +The child Zone's core ProviderDeployment (not a Provider controller — no such +controller exists for this Provider) creates the following `Process` resource when `Provider/transport-unix` is installed (not authored by Nix; emitted by the ProviderDeployment reconciler): @@ -570,7 +581,7 @@ apiVersion: resources.d2bus.org/v3 type: Process metadata: name: transport-unix-service - zone: k0 + zone: k1 ownerRef: Provider/transport-unix # owned; deleted when Provider is removed spec: providerRef: Provider/system-minijail @@ -627,10 +638,10 @@ spec: credentialRefs: [] ``` -The `Process` resource has `ownerRef: Provider/transport-unix`. The core -ProviderDeployment aggregates `Provider/transport-unix` status from the -Process phase and portal readiness condition. If the Process spec drifts, the -ProviderDeployment reconciler restores it. +The `Process` resource has `ownerRef: Provider/transport-unix`. The child Zone's +core ProviderDeployment aggregates `Provider/transport-unix` status from the +Process phase and portal readiness condition. If the Process spec drifts, that +child-local ProviderDeployment reconciler restores it. The service's stable local portal is not an inline `ProcessSpec` field. ProviderDeployment creates this owned standard `Endpoint` child when the @@ -641,7 +652,7 @@ apiVersion: resources.d2bus.org/v3 type: Endpoint metadata: name: transport-unix-portal - zone: k0 + zone: k1 ownerRef: Process/transport-unix-service spec: providerRef: Provider/transport-unix @@ -675,7 +686,9 @@ address, CID, port, FD number, or credential. Authorized resolution occurs only through the EffectPort/LaunchTicket path; unauthorized callers receive `endpoint-resolve-denied`. A transport service Process restart bumps the Endpoint `endpointGeneration`, and dependents observe `dependency-changed`. -ZoneLink session state remains owned by the core ZoneLink controller. +ZoneLink session state remains owned by the child Zone's core ZoneLink +controller. The parent store contains no reciprocal Endpoint, Process, +Provider, or ZoneLink resource. ### Retained opaque handles (D092) @@ -696,20 +709,21 @@ Process reaches terminal status. ProviderDeployment clears its own finalizer onl after the Process has been fully deleted; the service's `status` disappears with the resource row and its revision. The Provider itself holds no finalizer on ZoneLink resources; ZoneLink finalizer and status transitions -remain exclusively with the core ZoneLink controller. +remain exclusively with the child Zone's core ZoneLink controller. --- ## Service API The transport-unix service process exposes three typed portal methods. All -methods are invoked by the core ZoneLink controller via d2b-bus over a -ComponentSession authenticated to the service's enrolled `portal` endpoint. -No other caller may invoke these methods (see RBAC section). +methods are invoked by the child Zone's core ZoneLink controller via d2b-bus +over a same-Zone ComponentSession authenticated to the service's enrolled +`portal` endpoint. No other caller may invoke these methods (see RBAC section). ### `OpenTransport` -**Direction**: core ZoneLink controller → transport-unix service +**Direction**: child Zone's core ZoneLink controller → same-child-Zone +transport-unix service **Input**: ```text @@ -720,9 +734,10 @@ OpenTransportRequest { // "zone-link": inter-Zone link; cross-Zone FD grants are prohibited // "local-portal": within-Zone ComponentSession or portal; seqpacket may carry SCM_RIGHTS } -// Attachment index 0: the pre-bound Unix socket FD from allocator/core. +// Attachment index 0: the child endpoint from sealed allocator bootstrap. // This is a connected socketpair end or a just-accepted listener connection. -// The FD is supplied as an OwnedAttachment by the core invoker. +// Child core supplies it locally as an OwnedAttachment to its same-Zone Provider. +// No FD crosses a Zone boundary. ``` **Provider behavior**: @@ -769,7 +784,8 @@ nothing to it. ### `CloseTransport` -**Direction**: core ZoneLink controller → transport-unix service +**Direction**: child Zone's core ZoneLink controller → same-child-Zone +transport-unix service **Input**: ```text @@ -786,17 +802,19 @@ CloseTransportRequest { transport_handle: "" } **Output**: `CloseTransportResponse {}` -Core calls `CloseTransport` when: -- The ZoneLink is deleted: the core ZoneLink controller calls `CloseTransport` - to release the transport handle before removing its own ZoneLink transport - finalizer. The ProviderDeployment holds no ZoneLink finalizer. -- The ZoneLink spec changes in a way requiring a new transport FD (core +Child core calls `CloseTransport` when: +- The ZoneLink is deleted: the child Zone's core ZoneLink controller calls + `CloseTransport` to release the transport handle before removing its own + ZoneLink transport finalizer. The ProviderDeployment holds no ZoneLink + finalizer. +- The ZoneLink spec changes in a way requiring a new child endpoint (child core re-calls `OpenTransport` after `CloseTransport`). - The session engine has permanently failed on the returned FD. ### `ObserveTransport` -**Direction**: core ZoneLink controller → transport-unix service (named stream) +**Direction**: child Zone's core ZoneLink controller → same-child-Zone +transport-unix service (named stream) **Input**: ```text @@ -855,15 +873,16 @@ from OS-enforced SO_PEERCRED, not a peer-supplied long-term key. - Both static public keys are known before handshake. - The child Zone's enrolled static key is pinned in - `ZoneLink.spec.childStaticKeyFingerprint` and verified by the core ZoneLink - controller after the handshake — not by this Provider. + `ZoneLink.spec.childStaticKeyFingerprint` and verified by the child Zone's + core ZoneLink controller after the handshake — not by this Provider. - Unix seqpacket or stream carries the handshake bytes; the transport is unaware of the Noise content. - SO_PEERCRED is still verified by `PeerIdentityPolicy`; for KK sessions its role is supplemental provenance, not primary authentication. -The core ZoneLink controller enforces KK on ZoneLink sessions; this Provider -accepts whichever Noise profile the session engine negotiates on its FD. +The child Zone's core ZoneLink controller enforces KK on its ZoneLink session; +this Provider accepts whichever Noise profile the session engine negotiates on +its FD. ### IKpsk2 — one-time bootstrap @@ -876,30 +895,34 @@ or validate PSKs. ## d2b-bus and core integration ```text -core ZoneLink controller - -> d2b-bus portal route -> transport-unix service Process +child Zone K1 core ZoneLink controller + -> K1 d2b-bus portal route -> K1 transport-unix service Process OpenTransport(socket_kind, attachments_enabled) - + attachment[0]: allocator-issued Unix socket FD + + attachment[0]: child endpoint injected by sealed allocator bootstrap <- OpenTransportResponse(transport_handle, transport_class, ...) + attachment[0]: validated OwnedTransport FD -core ZoneLink controller - -> d2b-bus +selected parent allocator (chosen by compiler-only K1.parentZone) + -> retains only the peer endpoint and route binding as sealed state + -> creates no ZoneLink/Provider/Process/Endpoint row in the parent store + +child Zone K1 core ZoneLink controller + -> K1 d2b-bus hands OwnedTransport to session engine -> ComponentSession (KK handshake, record protection, named streams, reconnect) - -> child Zone d2b.resource.v3 service + -> selected parent allocator's route endpoint ``` After `OpenTransport` returns, the transport FD is owned by the session engine. -d2b-bus: +K1's d2b-bus: - wraps the FD as `UnixSeqpacketTransport` or `UnixStreamTransport`; - establishes the KK ComponentSession handshake; - owns per-session FD credits, named stream scheduling, reconnect generation; -- forwards resource API traffic to the child Zone. +- forwards resource API traffic over K1's allocator-bound uplink. -The core ZoneLink controller: -- reconciles the `ZoneLink` resource (status, conditions, finalizers); +The child Zone's core ZoneLink controller: +- reconciles K1's local `ZoneLink` resource (status, conditions, finalizers); - manages reconnect policy and intent queue; - publishes/withdraws route advertisements to the `RouteTreeEngine`; - calls `CloseTransport` when the ZoneLink is deleted or the transport must be replaced; @@ -908,7 +931,8 @@ The core ZoneLink controller: ### Currency and upgrade (D091) -The core ZoneLink controller, not the transport-unix service, implements +The child Zone's core ZoneLink controller, not the transport-unix service or +the parent allocator, implements `assess_update`, `plan_upgrade`, and `execute_upgrade`. A Provider generation or signed artifact generation/digest change updates universal `status.update` with `state: UpdateAvailable` or `state: UpgradeRequired`, `reasons` including @@ -917,9 +941,9 @@ digest IDs, `disruption: Reload` or `disruption: Restart`, `preserveState: true`, bounded `owned`/`dependencies`, and `lastAssessedAt`. Disruptive changes MUST return `UpgradeRequired` rather than applying in place; non-disruptive changes reconcile normally. Upgrade recycles the transport service realization; -open byte-stream handles are re-established by core reconnect. ZoneLink session -state remains owned by the core ZoneLink controller. `status.update` MUST NOT -contain secrets. +open byte-stream handles are re-established by child-core reconnect. ZoneLink +session state remains owned in the child store by the child Zone's core +ZoneLink controller. `status.update` MUST NOT contain secrets. ### Expedited reconcile on mutation (D090) @@ -942,9 +966,10 @@ resource. Core addresses the handle by the opaque token returned from `OpenTransport`. **No fallback**: if the transport-unix service is not Ready or `OpenTransport` -returns an error, the core ZoneLink controller sets the `SessionEstablished` -condition to `False` with the appropriate reason code and retries per the ZoneLink -reconnect policy. There is no automatic downgrade to stream, vsock, or TCP. +returns an error, the child Zone's core ZoneLink controller sets its local +`SessionEstablished` condition to `False` with the appropriate reason code and +retries per the ZoneLink reconnect policy. There is no automatic downgrade to +stream, vsock, or TCP. --- @@ -952,8 +977,8 @@ reconnect policy. There is no automatic downgrade to stream, vsock, or TCP. ### Role and RoleBinding requirements -The core ZoneLink controller requires one `invoke` permission on the -transport-unix portal service in its Zone: +The child Zone's core ZoneLink controller requires one `invoke` permission on +the transport-unix portal service in that same child Zone: | Verb | Scope | Notes | | --- | --- | --- | @@ -967,9 +992,14 @@ no resource plane operations. The transport-unix service Process requires only the permissions established by Provider/system-minijail's process model: - Receive its allocator-issued portal endpoint FD at spawn. -- Receive FD attachments in `OpenTransport` requests from authorized callers. +- Receive child-endpoint FD attachments in `OpenTransport` requests from its + authorized same-Zone child core caller. - Emit metrics to the Zone OTEL datagram socket. +The selected parent allocator receives no portal permission and makes no +Provider call. Its peer endpoint and route binding remain sealed allocator/route +state; no cross-Zone FD, ResourceRef, or status mutation is permitted. + ### No ambient authority The Provider holds no persistent secret, socket path, or session state. @@ -1012,7 +1042,7 @@ The following values are **never** logged, audited, or emitted as metric labels: | No credential bytes in `spec.provider.settings` | JSON Schema + build-time validation | | `attachmentsEnabled` not a ZoneLink `spec.provider.settings` field | JSON Schema (`additionalProperties:false` + `not/anyOf`) | | `additionalProperties: false` rejects all unknown keys | JSON Schema | -| ZoneLink transports always have `attachments_enabled=false` | Core ZoneLink controller (structural, pre-call); `admission.rs::validate_route_class` (belt-and-suspenders) | +| ZoneLink transports always have `attachments_enabled=false` | Child Zone's core ZoneLink controller (structural, pre-call); `admission.rs::validate_route_class` (belt-and-suspenders) | | `route_class=zone-link` + `attachments_enabled=true` → `attachment-policy-conflict` | `portal.rs::open_transport` step 3 | | CLOEXEC on every received FD | `portal.rs::open_transport` (blocking adapter) | | Credit rollback on any validation failure | `CreditBundle` RAII drop | @@ -1030,10 +1060,11 @@ The following values are **never** logged, audited, or emitted as metric labels: ### Provider resource status -The Provider resource `spec` contains the `artifactId` and signed descriptor -digests. Its `status` is managed by the core ProviderDeployment: +The child-local Provider resource `spec` contains the `artifactId` and signed +descriptor digests. Its `status` is managed by that child Zone's core +ProviderDeployment; the parent has no Provider resource or Provider status: -Per D088, core writes the Provider universal `ResourceStatus` base at top-level +Per D088, child core writes the Provider universal `ResourceStatus` base at top-level `status.*` and any Provider ResourceType-common observation in `Provider.status.resource` (core-derived per D085). `Provider/transport-unix` does not write Provider status directly. If this Provider ever writes bounded, @@ -1209,10 +1240,19 @@ These targets gate the Provider's integration tests: ### Option schema The Nix option tree mirrors the canonical `ResourceSpec` schema directly. The -`Provider/transport-unix` resource spec is authored as: +selected `Provider/transport-unix` is authored in the same child Zone as its +ZoneLink. The parent Zone has no reciprocal resource: ```nix -d2b.zones.k0.resources.transport-unix = { +# local-root is the distinguished root. parentZone is forbidden and no +# reciprocal ZoneLink or transport Provider is authored in its store. +d2b.zones.local-root = {}; + +# K1 is the CHILD Zone; this compiler-only setting selects local-root's +# allocator and is not emitted into any resource. +d2b.zones.k1.parentZone = "local-root"; + +d2b.zones.k1.resources.transport-unix = { type = "Provider"; spec = { artifactId = "provider-transport-unix"; # matches d2b.artifacts.provider-transport-unix @@ -1233,8 +1273,8 @@ d2b.artifacts.provider-transport-unix = { The `ZoneLink` spec is authored in the standard resource syntax: ```nix -# Host-local parent→child Zone link (most common case; seqpacket, no FD attachment) -d2b.zones.k0.resources.k1-link = { +# K1's sole child-local uplink (seqpacket, no cross-Zone FD attachment). +d2b.zones.k1.resources.k1-uplink = { type = "ZoneLink"; spec = { childZoneName = "k1"; @@ -1253,8 +1293,18 @@ d2b.zones.k0.resources.k1-link = { }; }; -# Stream variant (framed byte stream; no packet boundaries) -d2b.zones.k0.resources.k3-link = { +# Independent K3 child using the stream variant. It needs its own same-Zone +# selected Provider; a Provider ref never resolves across Zones. +d2b.zones.k3.parentZone = "local-root"; +d2b.zones.k3.resources.transport-unix = { + type = "Provider"; + spec = { + artifactId = "provider-transport-unix"; + config = {}; + }; +}; + +d2b.zones.k3.resources.k3-uplink = { type = "ZoneLink"; spec = { childZoneName = "k3"; @@ -1279,7 +1329,7 @@ d2b.zones.k0.resources.k3-link = { { "apiVersion": "resources.d2bus.org/v3", "type": "ZoneLink", - "metadata": { "name": "k1-link", "zone": "k0" }, + "metadata": { "name": "k1-uplink", "zone": "k1" }, "spec": { "childZoneName": "k1", "providerRef": "Provider/transport-unix", @@ -1309,7 +1359,9 @@ d2b.zones.k0.resources.k3-link = { `attachmentsEnabled` does not appear in the canonical JSON. Core enforces `attachments_enabled=false` for all ZoneLink transports at the OpenTransport call site regardless of socket kind. The emitter fills the `socketKind` schema -default before computing `generationId`. +default before computing `generationId`. `k1.parentZone` is absent because it +is compiled only into the selected allocator's sealed bootstrap state; no +reciprocal local-root resource is emitted. ### Eval-time assertions @@ -1318,6 +1370,7 @@ These supplement generated per-field option type checks and live in | Assertion | Error message | | --- | --- | +| ZoneLink and selected Provider are declared in the same child Zone; `spec.childZoneName` equals that Zone; compiler-only `parentZone` resolves to the allocator owner; no reciprocal parent resource is emitted | `zones.: transport-unix ZoneLink and Provider must be child-local with a self-matching childZoneName` | | `spec.provider.settings` must not contain `attachmentsEnabled` | `zones..resources.: spec.provider.settings.attachmentsEnabled is not a valid ZoneLink transport field; FD attachment across Zone boundaries is prohibited` | | `spec.provider.settings` contains no top-level key `socketPath`, `hostPath`, `password`, `token`, or `key` | `zones..resources.: spec.provider.settings must not contain host paths, socket paths, or secret material` | @@ -1337,6 +1390,7 @@ The `xtask gen-zone-resources` step adds for `Provider/transport-unix` links: | Test ID | Kind | What it proves | | --- | --- | --- | +| `nix-unit: transport-unix-child-local-topology` | nix-unit eval | Provider and ZoneLink are in K1, `childZoneName = "k1"`, `k1.parentZone = "local-root"` selects only the allocator, and no local-root reciprocal resource is emitted | | `nix-unit: transport-unix-empty-settings` | nix-unit eval | `spec.provider.settings = {}` passes all assertions | | `nix-unit: transport-unix-socket-path-rejected` | nix-unit eval | `spec.provider.settings.socketPath = "/run/..."` rejected at eval | | `nix-unit: transport-unix-attachments-enabled-rejected` | nix-unit eval | `spec.provider.settings.attachmentsEnabled = false` rejected as unknown field at eval | @@ -1387,9 +1441,9 @@ Old and new suites never run in parallel indefinitely. | Service Process resource with full sandbox/budget/endpoints spec | none in v3 baseline | `ADR-only` | new | | ProviderStateSet | ownerRef=Provider/transport-unix Volume query | `ADR-only` | Empty — `Provider/transport-unix` declares no Provider state Volume; its bounded non-secret operational state lives in `status`/the core Operation ledger (D087) | | ZoneLink `spec.provider.settings` schema | none | `ADR-only` | new in this spec | -| FD pre-bind pattern (broker FD pre-binding) | `d2b-priv-broker/src/ops/{swtpm_dir,spawn_runner}.rs` (v3 baseline) | `implemented-and-reachable` | allocator/core adapts the pattern; Provider is a passive recipient | -| Route advertisement / RouteTreeEngine | `d2b-realm-core/src/route_engine.rs` (v3 baseline) | `implemented-but-unwired` | core ZoneLink controller owns; not in this Provider | -| ZoneLink controller (reconcile/status/finalizer/routes) | none in v3 baseline | `ADR-only` | owned by core-controller (ADR-046-core-controllers); not in this Provider | +| FD pre-bind pattern (broker FD pre-binding) | `d2b-priv-broker/src/ops/{swtpm_dir,spawn_runner}.rs` (v3 baseline) | `implemented-and-reachable` | selected parent allocator adapts the pattern, retains its peer endpoint as sealed route state, and injects only the child endpoint through sealed bootstrap; the child-local Provider is a passive recipient and no FD crosses the ZoneLink | +| Route advertisement / RouteTreeEngine | `d2b-realm-core/src/route_engine.rs` (v3 baseline) | `implemented-but-unwired` | child core owns local ZoneLink session/cursor state; selected parent allocator owns only sealed route state; not in this Provider | +| ZoneLink controller (reconcile/status/finalizer/routes) | none in v3 baseline | `ADR-only` | owned by the child Zone's core-controller (ADR-046-core-controllers); no parent-side handler and not in this Provider | | OTEL bounded emitter | v3 `tracing` crate usage (baseline) | `implemented-and-reachable` | extend existing pattern | --- @@ -1428,7 +1482,7 @@ Old and new suites never run in parallel indefinitely. | Integration | `portal.rs::open_transport` calls `SeqpacketSocket::getsockopt(SO_TYPE)` and `setsockopt(SO_PASSCRED)`, constructs `UnixSeqpacketTransport`, hands OwnedTransport FD back to caller | | Data migration | None — full d2b 3.0 reset; no prior state to migrate | | Validation | Copy all 12 test functions; add `peercred_reported_to_componentsession_not_resolved_to_subject_here` | -| Removal proof | `d2b-realm-transport` seqpacket path retired after ZoneLink sessions migrate | +| Removal proof | `d2b-realm-transport` seqpacket path retired after ZoneLink sessions migrate to child-local Providers and tests prove no reciprocal parent-store resource or cross-Zone FD transfer remains | --- @@ -1515,9 +1569,9 @@ Old and new suites never run in parallel indefinitely. | Reuse action | adapt | | Destination | `packages/d2b-provider-transport-unix/src/{portal,service}.rs` | | Detailed design | `portal.rs`: `PortalHandler` struct owns a bounded `HashMap` (capacity `MAX_OPEN_TRANSPORTS=256`); `open_transport(req, attachment_fd)` validates via `admission.rs`, dups FD, allocates handle, stores `MonitorState { dup_fd, observation_senders: Vec }`; `close_transport(handle)` closes dup FD, half-closes all observation senders, removes entry; `observe_transport(handle)` registers a new `NamedStreamSender` and spawns an async epoll-watcher task on the dup FD; `TransportHandle` is a `[u8; 16]` random token; redacted in all Debug impls; `service.rs` is the binary entry: accepts the allocator-issued portal endpoint FD at launch, runs `GeneratedTransportServiceServer` over it, dispatches to `PortalHandler` Primary reuse disposition: `adapt`. Preserved source-plan detail: adapt dispatch pattern; implement portal methods as new. | -| Integration | Core ZoneLink controller calls the three methods via d2b-bus; portal endpoint FD is supplied by Zone runtime/allocator at Process spawn, not SD_LISTEN_FDS | +| Integration | The child Zone's core ZoneLink controller calls the three methods via same-Zone d2b-bus; the selected parent allocator retains its peer endpoint as sealed route state and injects the child endpoint through sealed bootstrap; the child runtime supplies that FD locally at Process spawn, not through a parent Provider, a cross-Zone attachment, or `SD_LISTEN_FDS` | | Data migration | None — full d2b 3.0 reset; no prior state to migrate | -| Validation | `tests/portal.rs::open_transport_zone_link_validates_and_returns_ownedtransport`; `open_transport_local_portal_seqpacket_with_attachments_accepted`; `open_transport_zone_link_attachments_enabled_rejected`; `close_transport_is_idempotent_after_handle_removed`; `observe_transport_delivers_pollhup_as_peer_disconnected`; `handle_table_rejects_at_max_capacity`; `restart_clears_all_handles` | +| Validation | `tests/portal.rs::open_transport_zone_link_validates_and_returns_ownedtransport`; `open_transport_uses_child_bootstrap_endpoint_only`; `parent_endpoint_never_enters_provider_portal`; `open_transport_local_portal_seqpacket_with_attachments_accepted`; `open_transport_zone_link_attachments_enabled_rejected`; `close_transport_is_idempotent_after_handle_removed`; `observe_transport_delivers_pollhup_as_peer_disconnected`; `handle_table_rejects_at_max_capacity`; `restart_clears_all_handles` | | Removal proof | Ad-hoc IPC stubs in `d2bd/src/` retired after portal migration | --- @@ -1550,10 +1604,10 @@ Old and new suites never run in parallel indefinitely. | Reuse source | None; new schema file | | Reuse action | create | | Destination | `docs/reference/schemas/v3/providers/transport-unix.transport-binding.json`; `nixos-modules/assertions.nix` (assertion additions); generated `nixos-modules/generated/options-zones-ZoneLink.nix` provider-settings submodule | -| Detailed design | Commit the JSON Schema; run `xtask gen-zone-schemas` and `xtask gen-zone-nix-options` to regenerate committed files; add two assertions to `assertions.nix` (stream+attachments conflict; sensitive key names); `xtask gen-zone-resources` adds provider-specific settings validation step | -| Integration | Build emitter validates `spec.provider.settings` against schema before computing `generationId`; drift gate enforces sync | +| Detailed design | Commit the JSON Schema; run `xtask gen-zone-schemas` and `xtask gen-zone-nix-options` to regenerate committed files; add assertions for stream+attachments conflict and sensitive key names; reuse the common topology assertions to require the ZoneLink and selected Provider in the same child Zone, self-matching `childZoneName`, compiler-only non-root `parentZone`, and no reciprocal parent resource; `xtask gen-zone-resources` adds provider-specific settings validation step | +| Integration | Build emitter validates `spec.provider.settings` against schema before computing `generationId`; the topology compiler seals child→parent allocator selection separately and emits only the child-local Provider/ZoneLink resources; drift gate enforces sync | | Data migration | `d2b.realms.*` Nix options superseded by `d2b.zones.*`; no compatibility bridge (v3 reset) | -| Validation | All seven eval/build tests in the Nix section | +| Validation | All eval/build tests in the Nix section, including `transport-unix-child-local-topology` and a generated-bundle assertion that the parent store has no reciprocal Provider/ZoneLink row | | Removal proof | `nixos-modules/options-realms.nix` realm wiring retired after Zone resource bundle activation replaces it | --- @@ -1586,8 +1640,8 @@ Old and new suites never run in parallel indefinitely. | Reuse source | Test scenario shapes from `d2b-session-unix/tests/unix_session.rs` end-to-end test (main `a1cc0b2d`) | | Reuse action | adapt | | Destination | `packages/d2b-provider-transport-unix/integration/` and `integration/README.md` | -| Detailed design | Four scenarios: `transport_open.rs` (fake Zone portal, allocator-socketpair FD attachment in → OwnedTransport attachment out → verify socket kind, CLOEXEC, SO_PASSCRED enabled; p95 latency assertion ≤2 ms); `fd_transfer.rs` (seqpacket `SCM_RIGHTS` transfer through opened transport, credit accounting, scavenge on error injection); `reconnect.rs` (CloseTransport + re-OpenTransport with fresh socketpair, verify previous handle is unknown, verify monitoring dup closed); `observation_stream.rs` (ObserveTransport stream receives `PEER_DISCONNECTED` event when peer closes socketpair end within 5 ms p95). `integration/README.md` documents prerequisites (no KVM required; all scenarios use in-process socketpairs and fake Zone API endpoint stub), invocation (`cargo test -p d2b-provider-transport-unix --test integration`), environment variables, and expected output | -| Integration | Invoked by `make test-integration`; no host mutation; each scenario creates its own socketpairs | +| Detailed design | Four scenarios: `transport_open.rs` (fixture models compiler-only `k1.parentZone = "local-root"`; fake selected allocator retains one socketpair endpoint as sealed route state and injects the other into K1 bootstrap; K1 core passes only that child endpoint to its same-Zone Provider → OwnedTransport out; verify socket kind, CLOEXEC, SO_PASSCRED, K1-local Provider/ZoneLink, and absent local-root reciprocal row; p95 latency assertion ≤2 ms); `fd_transfer.rs` (within-Zone local-portal seqpacket `SCM_RIGHTS` transfer, credit accounting, scavenge on error injection; ZoneLink route rejects the same packet); `reconnect.rs` (CloseTransport + re-OpenTransport with a fresh sealed child endpoint, verify previous handle is unknown and monitoring dup closed); `observation_stream.rs` (ObserveTransport stream receives `PEER_DISCONNECTED` when peer closes within 5 ms p95). `integration/README.md` documents prerequisites (no KVM required; all scenarios use in-process socketpairs and fake Zone API endpoint stub), invocation (`cargo test -p d2b-provider-transport-unix --test integration`), environment variables, and expected output | +| Integration | Invoked by `make test-integration`; no host mutation; each scenario creates its own socketpair, keeps parent endpoint state outside the resource stores, and exposes only the child endpoint to the child-local Provider | | Data migration | None — full d2b 3.0 reset; no prior state to migrate | | Validation | All four scenarios pass in CI; latency assertions enforced using monotonic timestamps; scavenge correctness verified by open-FD count before/after error injection | | Removal proof | Ad-hoc IPC test stubs retired after scenario parity | diff --git a/docs/specs/providers/ADR-046-provider-transport-vsock.md b/docs/specs/providers/ADR-046-provider-transport-vsock.md index 6a626ca2c..b94d304ff 100644 --- a/docs/specs/providers/ADR-046-provider-transport-vsock.md +++ b/docs/specs/providers/ADR-046-provider-transport-vsock.md @@ -135,13 +135,17 @@ is consumed from `d2b-session`. **Role**: carriage-acquisition service Provider. `Provider/transport-vsock` acquires AF_VSOCK byte channels on behalf of the -core Zone-link/delegation controller. It implements no ResourceType and owns -no reconcile loop, no status writes, no route publication, no finalizer, and -no session-generation management. Those responsibilities belong exclusively to -the core ZoneLink/delegation controller (`d2b-core-controller` crate). The -Provider runs one `service` Process and responds to three typed service method -invocations from core: `OpenTransport`, `CloseTransport`, and -`ObserveTransport`. +child Zone's core Zone-link/delegation controller. The selected Provider and +ZoneLink are installed in that same child Zone. The Provider implements no +ResourceType and owns no reconcile loop, no status writes, no route +publication, no finalizer, and no session-generation management. Those +responsibilities belong exclusively to the child Zone's core +ZoneLink/delegation controller (`d2b-core-controller` crate). The Provider runs +one `service` Process and responds to three typed service method invocations +from child core: `OpenTransport`, `CloseTransport`, and `ObserveTransport`. +Compiler-only `parentZone` selects the allocator; the parent keeps only sealed +allocator/route state and has no reciprocal ZoneLink, Provider, Process, +Endpoint, or status handler. **Scope** (what this Provider does): - Implements `d2b.transport.vsock.v3.VsockTransportService`. @@ -158,14 +162,16 @@ invocations from core: `OpenTransport`, `CloseTransport`, and on the vsock `TransportDescriptor`). **Not in scope for this Provider** — owned by other components: -- ZoneLink reconcile loop, status condition writes, finalizers, route - advertisement → core ZoneLink/delegation controller. +- ZoneLink reconcile loop, status condition writes, finalizers, and local + route/session state → child Zone's core ZoneLink/delegation controller. - Noise handshake (KK, IKpsk2), ComponentSession lifecycle, credit/flow control, stream multiplexing, session-generation management → d2b-bus / ComponentSession. -- Port allocation and the port registry → core Zone allocator state. -- Reconnect policy and generation increment → core ZoneLink/delegation - controller; Provider only acquires the underlying vsock socket on demand. +- Port allocation and the port registry → selected parent allocator's sealed + route state; child code receives only the sealed local binding. +- Reconnect policy and generation increment → child Zone's core + ZoneLink/delegation controller; Provider only acquires the underlying vsock + socket on demand. - Guest-control ttrpc channel (port 14318) → ADR 0028 guest-control path. - OTLP vsock relay (ports 14317/14319) → `observability-otel` Provider. - Unix-socket ZoneLinks → `transport-unix` Provider. @@ -173,7 +179,8 @@ invocations from core: `OpenTransport`, `CloseTransport`, and ### Currency and upgrade (D091) -The core ZoneLink controller, not the transport-vsock service, implements +The child Zone's core ZoneLink controller, not the transport-vsock service or +the parent allocator, implements `assess_update`, `plan_upgrade`, and `execute_upgrade`. A Provider generation or signed artifact generation/digest change updates universal `status.update` with `state: UpdateAvailable` or `state: UpgradeRequired`, `reasons` including @@ -182,9 +189,9 @@ digest IDs, `disruption: Reload` or `disruption: Restart`, `preserveState: true`, bounded `owned`/`dependencies`, and `lastAssessedAt`. Disruptive changes MUST return `UpgradeRequired` rather than applying in place; non-disruptive changes reconcile normally. Upgrade recycles the transport service realization; -open byte-stream handles are re-established by core reconnect. ZoneLink session -state remains owned by the core ZoneLink controller. `status.update` MUST NOT -contain secrets. +open byte-stream handles are re-established by child-core reconnect. ZoneLink +session state remains owned in the child store by the child Zone's core +ZoneLink controller. `status.update` MUST NOT contain secrets. ### Expedited reconcile on mutation (D090) @@ -294,10 +301,11 @@ background workers beyond this single service process. | --- | --- | --- | --- | | `executionRef` | `ResourceRef` | required | `Host/` or `Guest/` in the same Zone; determines where the single service Process runs | -The Provider's controller creates exactly one `Process/transport-vsock-service` -resource using this `executionRef`. There is one service Process per installed -Provider instance; all ZoneLink `OpenTransport`/`CloseTransport`/`ObserveTransport` -calls for the Zone are handled by that single process. +The child Zone's core ProviderDeployment creates exactly one +`Process/transport-vsock-service` resource using this `executionRef`. There is +one service Process per installed child-local Provider instance; all +`OpenTransport`/`CloseTransport`/`ObserveTransport` calls for that child Zone's +ZoneLink are handled by that single process. ### Process resource template @@ -306,6 +314,7 @@ apiVersion: resources.d2bus.org/v3 type: Process metadata: name: transport-vsock-service + zone: k1 ownerRef: Provider/transport-vsock spec: providerRef: Provider/system-minijail @@ -361,6 +370,7 @@ apiVersion: resources.d2bus.org/v3 type: Endpoint metadata: name: transport-vsock-service + zone: k1 ownerRef: Process/transport-vsock-service spec: providerRef: Provider/transport-vsock @@ -387,14 +397,17 @@ status: ### Endpoint resources (D092) -Stable allocator/listener bindings with visible lifecycle are standard -`Endpoint` resources. Consumers refer to `Endpoint/` and receive no raw -CID, port, socket address, FD, or credential from `Endpoint.spec` or -`Endpoint.status`; resolution is through the authorized EffectPort/LaunchTicket -path, and unauthorized callers fail with `endpoint-resolve-denied`. A producer -Process restart bumps `endpointGeneration`, which consumers observe as -`dependency-changed`. ZoneLink session state remains owned by the core ZoneLink -controller. +The child-local service binding with visible lifecycle is the standard +`Endpoint` resource above. Consumers in that child Zone refer to +`Endpoint/` and receive no raw CID, port, socket address, FD, or credential +from `Endpoint.spec` or `Endpoint.status`; resolution is through the authorized +EffectPort/LaunchTicket path, and unauthorized callers fail with +`endpoint-resolve-denied`. The selected parent allocator's listener/route +binding remains sealed allocator state and is **not** an Endpoint or any other +parent-store resource. A producer Process restart bumps the child Endpoint +`endpointGeneration`, which child-local dependents observe as +`dependency-changed`. ZoneLink session state remains owned by the child Zone's +core ZoneLink controller. ### Retained opaque handles (D092) @@ -414,8 +427,9 @@ Prohibited fields (never present in any Process or EphemeralProcess spec): ## Service API The Provider implements the following service on its ComponentSession with -d2b-bus. All three methods are invoked exclusively by the core -Zone-link/delegation controller. No other caller is authorized. +d2b-bus. All three methods are invoked exclusively by the child Zone's core +Zone-link/delegation controller over a same-Zone service session. No other +caller is authorized, and the parent allocator never invokes this Provider. ### `OpenTransport` @@ -427,9 +441,9 @@ rpc OpenTransport(OpenTransportRequest) -> OpenTransportResponse | Field | Type | Semantics | | --- | --- | --- | -| `endpoint_id` | `OpaqueEndpointId` | Core-allocated internal endpoint-resolution token (encodes peer CID; opaque to Provider; not an `Endpoint` resource) | -| `binding_id` | `OpaqueBindingId` | Core-allocated binding identity (encodes allocated port; opaque to Provider) | -| `role` | `TransportRole` | `Initiator` (parent-to-child connect) or `Responder` (child-side accept) | +| `endpoint_id` | `OpaqueEndpointId` | Child-local opaque endpoint-resolution token derived from the selected allocator's sealed binding; opaque to Provider; not an `Endpoint` resource | +| `binding_id` | `OpaqueBindingId` | Child-local opaque binding identity derived from the selected allocator's sealed port allocation; opaque to Provider | +| `role` | `TransportRole` | `Initiator` (child endpoint connects to the selected parent route endpoint) or `Responder` (child endpoint accepts that parent-facing route); never a parent Provider call | | `deadline_ms` | `u32` | Connect/accept deadline in ms from call arrival; range 1 000–60 000 | **Response fields**: @@ -526,10 +540,12 @@ pub trait VsockEffectPort: Send + Sync + 'static { ``` `OpaqueEndpointId` and `OpaqueBindingId` are newtype wrappers over bounded -strings with no public CID/port accessors. The Zone runtime provides a -`LiveVsockEffectPort` implementation that resolves opaque IDs against core -allocator state and opens the actual AF_VSOCK sockets. Tests inject a -`MockVsockEffectPort`. +strings with no public CID/port accessors. The child Zone runtime provides a +`LiveVsockEffectPort` implementation that consumes its sealed local binding for +the allocator selected by compiler-only `parentZone` and opens the child +AF_VSOCK endpoint. The parent allocator retains only its peer binding and route +allocation as sealed state. Neither a ResourceRef nor an FD crosses the Zone +boundary. Tests inject a `MockVsockEffectPort`. The Provider never receives, stores, logs, or emits raw CID (`u32`) or port (`u32`) values. Any path that would write a raw CID or port to a log, @@ -540,16 +556,17 @@ defect and must be caught by the `redaction.rs` test. ## `spec.provider.settings` schema -The `settings` object inside `ZoneLink.spec.provider` carries Provider-specific -configuration when `spec.providerRef = Provider/transport-vsock`. Core -resolves the opaque endpoint and binding IDs from these settings; the Provider -never receives the raw resolution. +The `settings` object inside the child-local `ZoneLink.spec.provider` carries +Provider-specific configuration when +`spec.providerRef = Provider/transport-vsock`. The child Zone's core resolves +the opaque endpoint and binding IDs from these settings plus its sealed +selected-parent allocation; the Provider never receives the raw resolution. **JSON Schema ID**: `docs/reference/schemas/v3/providers/transport-vsock.transport-binding.json` | Field | Type | Default | Semantics | | --- | --- | --- | --- | -| `guestRef` | `ResourceRef` | required | `Guest/` in the same Zone; core resolves to vsock CID internally | +| `guestRef` | `ResourceRef` | required | `Guest/` in the same child Zone as the ZoneLink and selected Provider; child core resolves the local endpoint through the sealed allocation | | `portClass` | `string` (enum) | `"d2b-link"` | Port class; core allocates a port from the class range; `"d2b-link"` → range `14420–14499` | | `connectTimeoutSeconds` | `integer` [1, 60] | `30` | Passed as `deadline_ms` in `OpenTransport` | @@ -578,9 +595,11 @@ declares no state Volume. `ProviderStateSet` is empty. Its bounded non-secret operational state — service readiness, transport-open/close reconcile stage, bounded connection/port observation counters, and closed-enum error detail — lives in the owning -resource's `status` subresource and the core Operation ledger (D087). Port -allocation and the port registry are core allocator state; all ZoneLink session -state is owned by the core ZoneLink controller under the `ZoneLink` resource. +resource's `status` subresource and the child core Operation ledger (D087). +Port allocation and the port registry are selected-parent allocator state; the +parent retains only that sealed allocator/route state. All ZoneLink session +state is owned by the child Zone's core ZoneLink controller under the child-local +`ZoneLink` resource. The service holds only opaque byte-stream handles in its own process memory (`OwnedTransport`, per D081), which are never persisted. @@ -596,15 +615,15 @@ is no empty identity-only Volume. ### RBAC grants for the core ZoneLink controller (caller) -The core ZoneLink/delegation controller is the only principal authorized to -invoke the Provider's service methods. No other principal (other Providers, -operators, end-users) may invoke `OpenTransport`, `CloseTransport`, or -`ObserveTransport`. +The child Zone's core ZoneLink/delegation controller is the only principal +authorized to invoke the Provider's service methods. No other principal (the +parent allocator, other Providers, operators, or end-users) may invoke +`OpenTransport`, `CloseTransport`, or `ObserveTransport`. ```yaml RBACPolicy: subject: Principal/core-zone-link-controller - zone: + zone: rules: - resource: d2b.transport.vsock.v3.VsockTransportService verbs: [invoke] @@ -616,9 +635,10 @@ RBACPolicy: `Provider/transport-vsock` requires NO verbs on `ZoneLink`, `Guest`, `Zone`, `Route`, `Credential`, `Certificate`, or any other ResourceType. All -ZoneLink resource access is performed exclusively by the core controller. The -Provider service holds no permissions to read, watch, update, or delete any -resource. +ZoneLink resource access is performed exclusively by the child Zone's core +controller. The Provider service holds no permissions to read, watch, update, +or delete any resource. The parent allocator has no resource row or RBAC grant +for this child-local Provider/ZoneLink. --- @@ -642,64 +662,68 @@ resource. ## Lifecycle Per D088, `Provider/transport-vsock` does not write resource status directly: -core owns the universal `ResourceStatus` base and the ResourceType-common +child core owns the universal `ResourceStatus` base and the ResourceType-common `status.resource` projection for Provider and ZoneLink resources. Cross-provider -transport observations returned to core are promoted to `ZoneLink.status.resource`; -any bounded, non-secret vsock-only observation that core persists for this +transport observations returned to child core are promoted to the child-local +`ZoneLink.status.resource`; any bounded, non-secret vsock-only observation that +child core persists for this Provider uses `ZoneLink.status.provider` with `providerRef: Provider/transport-vsock`, qualified `schemaId: transport-vsock.d2bus.org/ZoneLink/status`, `schemaVersion` (semver MAJOR.MINOR), `observedProviderGeneration`, and a strict unknown-field-denied, ≤32 KiB, -redacted `details` schema registered and signed in the Provider manifest. Core -writes all present layers atomically and never copies shared +redacted `details` schema registered and signed in the Provider manifest. Child +core writes all present layers atomically in the child store and never copies shared `status.resource` fields into `status.provider`. ### Provider installation -1. Nix module emits `Provider/transport-vsock` resource into the Zone resource - store. -2. Core ProviderDeployment creates +1. Nix module emits `Provider/transport-vsock` into the same child Zone store as + its ZoneLink. The parent store receives no Provider or ZoneLink row. +2. The child Zone's core ProviderDeployment creates `Volume/transport-vsock--service--empty-state--` with `ownerRef: Provider/transport-vsock`; waits for Volume `Ready` (reconciled by `Provider/volume-local`). The transport-vsock Provider controller does not participate in Volume creation. -3. Provider controller creates `Process/transport-vsock-service` (with `mounts` - referencing the pre-created state Volume); waits for Process `Ready`. +3. The child Zone's core ProviderDeployment creates + `Process/transport-vsock-service` (with `mounts` referencing the pre-created + state Volume); waits for Process `Ready`. 4. Service process connects to d2b-bus; receives a dirfd into its `/state` view from the volume-local Provider; registers `d2b.transport.vsock.v3.VsockTransportService` on the Zone service registry. -5. Service emits readiness; core ProviderDeployment observes it and sets - `Provider/transport-vsock` status to `Ready`. +5. Service emits readiness; child-local core ProviderDeployment observes it and + sets `Provider/transport-vsock` status to `Ready`. ### Transport open (per ZoneLink session request from core) -1. Core ZoneLink/delegation controller calls `OpenTransport(endpoint_id, +1. The child Zone's core ZoneLink/delegation controller calls `OpenTransport(endpoint_id, binding_id, role, deadline_ms)` on the Provider's ComponentSession. 2. Provider validates opaque IDs. 3. Provider calls `VsockEffectPort::open(...)`. -4. Zone runtime (`LiveVsockEffectPort`) resolves `endpoint_id` → CID and - `binding_id` → port from core allocator state; opens or accepts the - AF_VSOCK socket. +4. Child Zone runtime (`LiveVsockEffectPort`) resolves the opaque IDs from its + sealed selected-parent binding and opens or accepts the child AF_VSOCK + endpoint. The parent endpoint remains sealed allocator/route state; no FD or + ResourceRef crosses Zones. 5. Provider opens a named stream on its ComponentSession and spawns a bridge task. -6. Provider returns `transport_handle` + `stream_id` to core. -7. Core hands `stream_id` to d2b-bus as the `OwnedTransport` for the ZoneLink. +6. Provider returns `transport_handle` + `stream_id` to child core. +7. Child core hands `stream_id` to d2b-bus as the `OwnedTransport` for its local + ZoneLink. 8. d2b-bus runs Noise KK or IKpsk2 handshake on top of the raw bytes. ### Transport close -1. Core calls `CloseTransport(transport_handle)`. +1. Child core calls `CloseTransport(transport_handle)`. 2. Provider signals bridge task to stop; waits up to `CLOSE_GRACE_MS`. 3. Provider closes named stream and vsock socket. 4. Provider emits `transport.release` audit event. ### Provider removal -1. Core calls `CloseTransport` for every open transport handle. +1. Child core calls `CloseTransport` for every open transport handle. 2. Zone runtime stops `Process/transport-vsock-service`; Process finalizer - completes. The transport-vsock Provider controller does not delete the - Volume. -3. Core ProviderDeployment deletes + completes. The child Zone's core ProviderDeployment owns deletion ordering; + the transport-vsock service does not delete the Volume. +3. Child-local core ProviderDeployment deletes `Volume/transport-vsock--service--empty-state--*` after the Process finalizer; waits for Volume `Deleted` (identity marker removed by broker). 4. Zone resource store marks `Provider/transport-vsock` `Deleted`. @@ -780,24 +804,35 @@ identifier beyond the closed enum of label values. ## Nix authoring ```nix -# Provider resource (authored by the Zone operator): -d2b.zones.k0.resources.transport-vsock = { +# local-root is the provisioning parent. It has no ZoneLink or transport +# Provider resource for this edge; only its allocator's sealed route state exists. +d2b.zones.local-root = {}; + +# K1 is the CHILD Zone. parentZone is compiler-only and selects local-root's +# allocator; it is not emitted into Zone/k1 or any other resource. +d2b.zones.k1.parentZone = "local-root"; + +# This focused fragment assumes Guest/k1-vm is declared in K1. + +# Selected Provider resource, authored in the same CHILD Zone as the ZoneLink: +d2b.zones.k1.resources.transport-vsock = { type = "Provider"; spec = { artifactId = "transport-vsock"; # resolves to d2b.artifacts entry config = { - executionRef = "Host/host-system"; # required; Host/ or Guest/ + executionRef = "Guest/k1-vm"; # same-Zone Host/ or Guest/ }; }; }; -# The Provider's controller creates Process/transport-vsock-service automatically -# using spec.config.executionRef. The operator does NOT author that Process resource. +# K1's core ProviderDeployment creates Process/transport-vsock-service +# automatically. The operator does NOT author that Process resource. -# Example ZoneLink using this Provider (authored by the Zone operator): -d2b.zones.k0.resources.link-to-k1 = { +# K1's sole child-local uplink. childZoneName self-matches; local-root gets no +# reciprocal ZoneLink or Provider resource. +d2b.zones.k1.resources.k1-uplink = { type = "ZoneLink"; spec = { - childZoneRef = "Zone/k1"; + childZoneName = "k1"; providerRef = "Provider/transport-vsock"; provider = { schemaId = "transport-vsock.d2bus.org/ZoneLink/spec"; @@ -812,9 +847,35 @@ d2b.zones.k0.resources.link-to-k1 = { }; ``` +The compiler derives `metadata.zone: k1` for both the Provider and ZoneLink. +The emitted ZoneLink identity is: + +```yaml +apiVersion: resources.d2bus.org/v3 +type: ZoneLink +metadata: + name: k1-uplink + zone: k1 +spec: + childZoneName: k1 + providerRef: Provider/transport-vsock + provider: + schemaId: transport-vsock.d2bus.org/ZoneLink/spec + schemaVersion: "1.0" + settings: + guestRef: Guest/k1-vm + portClass: d2b-link + connectTimeoutSeconds: 30 +``` + +local-root's resource bundle contains no reciprocal ZoneLink or selected Provider. + `spec.config.executionRef` is validated at eval time against declared Zone -resources; referential existence is verified at bundle activation time. The -Nix module also validates `spec.provider.settings` against the +resources in the same child Zone; referential existence is verified at bundle +activation time. The compiler separately seals +`k1.parentZone = "local-root"` into +allocator bootstrap state and emits no parent-store reciprocal resource. The Nix +module also validates `spec.provider.settings` against the `transport-vsock.transport-binding.json` schema and rejects any prohibited field (`cid`, `port`, `socketPath`, etc.). Validation is performed by the `d2b.zones..resources` schema checker, not by the Provider's own Nix @@ -836,7 +897,7 @@ expression. | `vsock.rs` v3 stub | `d2b-session-unix/src/vsock.rs` (v3 baseline) | `ADR-only` | Delta: stub becomes `FramedVsockTransport` implementation; entry point is `VsockEffectPort` not raw syscall | | Framing tests | `d2b-session-unix/tests/unix_session.rs` vsock subset | `implemented-but-unwired` | Retained in `tests/framing.rs` (Provider crate): `vsock_framing_handles_partial_and_coalesced_records`, `vsock_frame_too_large_rejects_before_allocating`, `vsock_clean_eof_versus_reset_are_distinct` | | CID mismatch enforcement | `vsock_cid_mismatch_closes_without_processing` | `implemented-but-unwired` | Adapted in `tests/effect_port_mock.rs` as `OpaqueEndpointId` mismatch case; raw CID variant moves to core adapter tests | -| ZoneLink resource | `ADR-046-zone-routing.md` | `generated-or-eval-contract` | Retained: ZoneLink base spec shape and `spec.provider.settings`; core controller owns all status/routes/finalizer | +| ZoneLink resource | `ADR-046-zone-routing.md` | `generated-or-eval-contract` | Retained: child-local ZoneLink base spec shape and `spec.provider.settings`; child core owns local status/routes/finalizer; selected parent retains only sealed allocator/route state with no reciprocal row | | ComponentSession / Noise | `ADR-046-componentsession-and-bus.md` | `generated-or-eval-contract` | Retained: owned by d2b-bus; Provider is opaque carriage only | --- @@ -851,7 +912,7 @@ expression. | Reuse action | create | | Destination | `packages/d2b-provider-transport-vsock/src/effect_port.rs`; test fake in `tests/effect_port_mock.rs`; redaction checks in `tests/redaction.rs`. | | Detailed design | Implement `VsockEffectPort` trait and `OpaqueEndpointId`/`OpaqueBindingId` newtypes. Define `VsockEffectPort` async trait and opaque ID newtypes in `effect_port.rs`; implement `FakeVsockEffectPort` for tests; `redaction.rs` asserts no raw `u32` in any `Debug`/`Display` output of opaque types; no real vsock socket opened. Primary reuse disposition: `create`. Preserved source-plan detail: net-new trait/newtypes with redaction tests; no real vsock socket opened. | -| Integration | Core ZoneLink/delegation controller calls the Provider service with opaque IDs; Provider calls injected `VsockEffectPort`; live AF_VSOCK resolution remains in core runtime, not the Provider crate. | +| Integration | Child Zone's core ZoneLink/delegation controller calls its same-Zone Provider with opaque IDs; Provider calls injected `VsockEffectPort`; live AF_VSOCK child-endpoint resolution remains in child core runtime while the selected parent retains only sealed peer/route state. | | Data migration | Full d2b 3.0 reset; no v2 state/config import. | | Validation | Proof type: hermetic unit + redaction test; `tests/effect_port_mock.rs` and `tests/redaction.rs`. | | Removal proof | None — net-new; no prior owner to remove. | @@ -878,7 +939,7 @@ expression. | Reuse action | adapt | | Destination | `packages/d2b-provider-transport-vsock/src/service.rs`; tests `tests/open_close.rs`, `tests/observe.rs`, and conformance kit. | | Detailed design | Implement `VsockTransportService` (OpenTransport/CloseTransport/ObserveTransport). Implement all three service methods in `service.rs`; `open_close.rs` and `observe.rs` test full service API against `FakeVsockEffectPort`; conformance kit passes. Primary reuse disposition: `adapt`. Preserved source-plan detail: net-new service implementation over ComponentSession and fake effect port tests. | -| Integration | Core ZoneLink/delegation controller is the only authorized caller; service opens named stream handles for d2b-bus, releases them on close, and streams transport events for observe. | +| Integration | Child Zone's core ZoneLink/delegation controller is the only authorized caller; service opens named stream handles for child d2b-bus, releases them on close, and streams transport events for observe; no parent-side Provider or handler exists. | | Data migration | Full d2b 3.0 reset; no v2 state/config import. | | Validation | Proof type: service round-trip test (mock); `tests/open_close.rs`, `tests/observe.rs`, and provider conformance tests. | | Removal proof | None — net-new; no prior owner to remove. | @@ -886,25 +947,25 @@ expression. ### ADR046-vsock-004 | Field | Value | | --- | --- | -| Dependency/owner | Title: Implement `LiveVsockEffectPort` in Zone runtime; Phase 2; Priority P0; Depends on ADR046-vsock-001 and the Zone allocator (`ADR-046-resources-zone-control`); Owner crate `d2b-core-controller`. | +| Dependency/owner | Title: Implement `LiveVsockEffectPort` in child Zone runtime; Phase 2; Priority P0; Depends on ADR046-vsock-001 and the Zone allocator (`ADR-046-resources-zone-control`); Owner crate `d2b-core-controller`. | | Current source | Evidence class `ADR-only`; baseline has guest-control and relay vsock paths, but no allocator-backed `LiveVsockEffectPort` for ZoneLink transport. | | Reuse action | adapt | -| Destination | `d2b-core-controller` Zone runtime `LiveVsockEffectPort`; Provider receives it by dependency injection at startup. | -| Detailed design | Implement `LiveVsockEffectPort` in Zone runtime. Zone runtime provides `LiveVsockEffectPort` backed by core allocator state; resolves `OpaqueEndpointId` → CID and `OpaqueBindingId` → port; opens AF_VSOCK socket; injects into Provider service at startup; no raw CID/port exposed to Provider. Primary reuse disposition: `adapt`. Preserved source-plan detail: net-new core adapter; keep raw AF_VSOCK syscalls outside Provider crate. | -| Integration | Zone allocator issues endpoint/binding IDs; core runtime resolves them, opens/accepts AF_VSOCK sockets, returns opaque streams to Provider service, and excludes reserved ports. | +| Destination | `d2b-core-controller` child Zone runtime `LiveVsockEffectPort`; child-local Provider receives it by dependency injection at startup. | +| Detailed design | Implement `LiveVsockEffectPort` in the child Zone runtime. It consumes the child's sealed binding for the allocator selected by compiler-only `parentZone`, resolves opaque endpoint/binding IDs only inside the effect adapter, opens the child AF_VSOCK endpoint, and injects an opaque stream into the same-Zone Provider service; the selected parent keeps its peer binding and port registry only as sealed allocator/route state; no raw CID/port is exposed to the Provider and no FD or ResourceRef crosses Zones. Primary reuse disposition: `adapt`. Preserved source-plan detail: net-new core adapter; keep raw AF_VSOCK syscalls outside Provider crate. | +| Integration | Selected parent allocator issues a sealed endpoint/binding allocation without creating resources; child core resolves its local side, opens/accepts the AF_VSOCK endpoint, returns an opaque stream to its Provider service, and excludes reserved ports. | | Data migration | Full d2b 3.0 reset; no v2 state/config import. | -| Validation | Proof type: integration test; `integration/host_guest.rs` exercises live open/close byte round-trip with the injected effect. | +| Validation | Proof type: integration test; `integration/host_guest.rs` exercises live open/close byte round-trip with the injected effect and proves the selected parent has only sealed allocator/route state, with no parent-store Provider/ZoneLink row. | | Removal proof | None — net-new core adapter; no prior owner to remove. | ### ADR046-vsock-005 | Field | Value | | --- | --- | -| Dependency/owner | Title: Core ProviderDeployment creates/deletes service component state Volume; Phase 1; Priority P0; Depends on the volume-local Provider (`ADR-046-provider-volume-local`); Owner crate `d2b-provider-transport-vsock`. | +| Dependency/owner | Title: Child core ProviderDeployment creates/deletes service component state Volume; Phase 1; Priority P0; Depends on the volume-local Provider (`ADR-046-provider-volume-local`); Owner crate `d2b-provider-transport-vsock`. | | Current source | Evidence class `test-only-or-preview`; no operator-authored v3 state Volume exists for transport-vsock in baseline. | | Reuse action | create | | Destination | ProviderDeployment Volume creation/deletion path plus `packages/d2b-provider-transport-vsock/tests/state_volume.rs`. | -| Detailed design | Core ProviderDeployment creates/deletes service component state Volume. Core ProviderDeployment creates `Volume/transport-vsock--service--empty-state--*` before the component Process and deletes it after the Process finalizer; transport-vsock Provider controller does not own Volume, does not add Volume to exported ResourceTypes, and does not create its prerequisite; Volume spec: empty schema, `kind: state`, `persistenceClass: persistent`, `migrationPolicy: none`, `User/d2b-transport-vsock` owner, minimal nonzero `quota.maxBytes`/`quota.maxInodes` with `enforcement: hard`, `private` sensitivity, `broker-maintained` identity marker; `state_volume.rs` test verifies Volume spec fields against canonical schema; integration test verifies marker written at install and removed at Provider deletion; no operator-authored Volume; component receives dirfd view only. Primary reuse disposition: `create`. Preserved source-plan detail: net-new ProviderDeployment/Volume integration and tests. | -| Integration | Core ProviderDeployment creates Volume before Process, volume-local reconciles it, Provider process receives only a dirfd view, and Provider deletion removes the Process before deleting the Volume/identity marker. | +| Detailed design | Child core ProviderDeployment creates/deletes the service component state Volume in the same child Zone. It creates `Volume/transport-vsock--service--empty-state--*` before the component Process and deletes it after the Process finalizer; the transport-vsock service does not own Volume, add Volume to exported ResourceTypes, or create its prerequisite; Volume spec: empty schema, `kind: state`, `persistenceClass: persistent`, `migrationPolicy: none`, `User/d2b-transport-vsock` owner, minimal nonzero `quota.maxBytes`/`quota.maxInodes` with `enforcement: hard`, `private` sensitivity, `broker-maintained` identity marker; `state_volume.rs` tests the canonical schema; installation/removal tests verify marker lifecycle; no operator-authored or parent-store Volume exists; component receives only its child-local dirfd view. Primary reuse disposition: `create`. Preserved source-plan detail: net-new ProviderDeployment/Volume integration and tests. | +| Integration | Child core ProviderDeployment creates the Volume before Process, volume-local reconciles it, the Provider process receives only a child-local dirfd view, and Provider deletion removes Process before Volume/identity marker; parent state remains allocator/route-only. | | Data migration | Full d2b 3.0 reset; no v2 state/config import; state Volume is created fresh with `migrationPolicy: none`. | | Validation | Proof type: unit + integration test; `tests/state_volume.rs` and Provider install/remove integration tests verify schema, user refs, marker lifecycle, and no ComponentPrincipal. | | Removal proof | Remove the state Volume and its broker-maintained identity marker during Provider deletion; no operator-authored Volume remains. | @@ -916,8 +977,8 @@ expression. | Current source | Evidence class `test-only-or-preview`; existing guest-control compile proof and socat relay tests are not full ZoneLink transport coverage. | | Reuse action | create | | Destination | `packages/d2b-provider-transport-vsock/integration/host_guest.rs` and `integration/no_fd_transfer.rs`. | -| Detailed design | Integration test: real vsock socketpair + full ZoneLink open/close. `integration/host_guest.rs`: real vsock socketpair (Linux); `OpenTransport` + byte round-trip + `CloseTransport`; validates bridge throughput ≥ 512 MiB/s; `no_fd_transfer.rs`: structural rejection of attachment packets over vsock transport. Primary reuse disposition: `create`. Preserved source-plan detail: net-new integration coverage with no FD transfer over vsock. | -| Integration | Test drives Provider service, LiveVsockEffectPort, d2b-bus `OwnedTransport`, byte bridge, close path, and attachment rejection across the integration lane. | +| Detailed design | Integration test: fixture declares compiler-only `k1.parentZone = "local-root"`, puts the ZoneLink and selected Provider only in K1, and gives local-root only sealed allocator/route state; `integration/host_guest.rs` opens a real Linux vsock path through K1's `LiveVsockEffectPort`, then exercises `OpenTransport` + byte round-trip + `CloseTransport` and validates bridge throughput ≥ 512 MiB/s; `no_fd_transfer.rs` structurally rejects attachment packets and asserts no FD/ResourceRef crosses the Zone boundary. Primary reuse disposition: `create`. Preserved source-plan detail: net-new integration coverage with no FD transfer over vsock. | +| Integration | Test drives K1-local Provider service, child `LiveVsockEffectPort`, d2b-bus `OwnedTransport`, byte bridge, close path, absent local-root reciprocal resource row, and attachment rejection across the integration lane. | | Data migration | None — docs/tooling only; no runtime state. | | Validation | Proof type: integration test; `make test-integration` runs `host_guest.rs` and `no_fd_transfer.rs`. | | Removal proof | None — test coverage net-new; old duplicate vsock tests are retired only after successor assertions migrate. | @@ -951,6 +1012,9 @@ expression. | `no_raw_port_in_debug_or_display` | `tests/redaction.rs` | unit | `cargo test` | | `transport_settings_schema_rejects_cid_field` | `tests/schema.rs` | unit | `cargo test` | | `transport_settings_schema_rejects_port_field` | `tests/schema.rs` | unit | `cargo test` | +| `provider_and_zonelink_are_child_local` | `tests/topology.rs` | unit | `cargo test` | +| `child_zone_name_self_matches_and_parent_is_compiler_only` | `tests/topology.rs` | unit | `cargo test` | +| `parent_store_has_no_reciprocal_resources` | `tests/topology.rs` | unit | `cargo test` | | `state_volume_spec_matches_canonical_schema` | `tests/state_volume.rs` | unit | `cargo test` | | `state_volume_layout_uses_nix_user_ref_not_component_principal` | `tests/state_volume.rs` | unit | `cargo test` | | `conformance_provider_registers_service` | `tests/` (conformance kit) | unit | `cargo test` | @@ -979,8 +1043,8 @@ budget. ## Removal criteria `Provider/transport-vsock` (and its crate) may not be removed while: -1. Any `ZoneLink` resource with `spec.providerRef: Provider/transport-vsock` - exists in any Zone. +1. Any child-local `ZoneLink` resource with + `spec.providerRef: Provider/transport-vsock` exists in its owning child Zone. 2. Any `d2b-link` port-class vsock session is active on any Zone runtime. 3. The state Volume (`Volume/transport-vsock--service--empty-state--*`) has not been deleted and its identity marker has not been cleared. @@ -989,9 +1053,12 @@ budget. 5. The `guest_control_vsock.rs` CONNECT-proxy path has not been fully replaced by the Guest resource lifecycle bootstrap (ADR046-vsock-007). -When all four conditions are clear, the removal commit must delete +When all five conditions are clear, the removal commit must delete `packages/d2b-provider-transport-vsock/` and the `transport-vsock` entry in -the Provider catalog in `ADR-046-provider-model-and-packaging.md`. +the Provider catalog in `ADR-046-provider-model-and-packaging.md`. Removal +proof must also show that topology fixtures contain no parent-store reciprocal +Provider/ZoneLink row and that no cross-Zone FD/ResourceRef path replaced the +transport. Per D094, each replaced current-code test is retired with an explicit keep/adapt/move/delete disposition and a removal gate: the minimum reusable @@ -1009,13 +1076,18 @@ Old and new suites never run in parallel indefinitely. `packages/d2b-provider-transport-vsock/README.md` must document: - Provider identity: `Provider/transport-vsock`; carriage-acquisition service Provider. -- Role: `service` component; no ResourceType ownership; core ZoneLink/delegation - controller is the sole ZoneLink reconciler and the only caller of this service. +- Placement: Provider and ZoneLink are in the same child Zone; + `spec.childZoneName` self-matches; compiler-only `parentZone` selects the + allocator; the parent has only sealed allocator/route state and no reciprocal + resource. +- Role: `service` component; no ResourceType ownership; the child Zone's core + ZoneLink/delegation controller is the sole ZoneLink reconciler and the only + caller of this service. - ProviderStateSet: empty — `Provider/transport-vsock` declares no Provider state Volume; its bounded non-secret operational state lives in `status`/the - core Operation ledger (D087). All ZoneLink session state is owned by the core - ZoneLink controller; the service holds only opaque byte-stream handles in - process memory. + child core Operation ledger (D087). All ZoneLink session state is owned by the + child Zone's core ZoneLink controller; the service holds only opaque + byte-stream handles in process memory. - `Provider.spec.config.executionRef`: required `Host/` or `Guest/`; one service Process per Provider instance, not per ZoneLink. - `spec.provider.settings`: `guestRef` / `portClass` fields; what is forbidden. @@ -1024,7 +1096,8 @@ Old and new suites never run in parallel indefinitely. `tokio-vsock` is NOT a Provider crate dependency. - Components: `d2b-transport-vsock` binary; one service process per Zone. - Dependencies: `d2b-session`, `d2b-contracts`, `d2b-provider`, `tokio` (no `tokio-vsock`). -- RBAC: only core ZoneLink controller may invoke service methods. +- RBAC: only the child Zone's core ZoneLink controller may invoke service + methods. - Build: `cargo build -p d2b-provider-transport-vsock`. - Tests: `cargo test -p d2b-provider-transport-vsock`. - Integration: `make test-integration`. From 27c1b1921545deb59932eb5deb0048703489736c Mon Sep 17 00:00:00 2001 From: John Vicondoa Date: Fri, 24 Jul 2026 08:15:39 -0700 Subject: [PATCH 098/115] docs: align transport ZoneLinks with canonical spec --- .../ADR-046-provider-transport-azure-relay.md | 257 ++++++++---------- .../ADR-046-provider-transport-unix.md | 170 ++++++------ .../ADR-046-provider-transport-vsock.md | 109 ++++---- 3 files changed, 262 insertions(+), 274 deletions(-) diff --git a/docs/specs/providers/ADR-046-provider-transport-azure-relay.md b/docs/specs/providers/ADR-046-provider-transport-azure-relay.md index e74b8da74..8ef727864 100644 --- a/docs/specs/providers/ADR-046-provider-transport-azure-relay.md +++ b/docs/specs/providers/ADR-046-provider-transport-azure-relay.md @@ -28,7 +28,7 @@ a reciprocal ZoneLink or selected transport Provider resource. The Provider's responsibilities are: -- Accept typed `spec.provider.settings` from a `ZoneLink` spec and +- Accept typed `spec.transportSettings` from a `ZoneLink` spec and establish a reliable bidirectional byte-stream channel to the remote Zone over an Azure Relay Hybrid Connection. - Present the established channel as a named opaque byte stream to @@ -91,20 +91,17 @@ Provider/transport-azure-relay | ResourceTypes consumed | `Credential`, `Network`, `Provider` (ZoneLink is read and reconciled only by core) | | Placement | Gateway Guest only (per ADR 0032; see §Provider config schema) | -**D089 desired-spec shape.** This transport Provider owns no ResourceType; core -reconciles the `ZoneLink` base `spec.*` fields, including `spec.providerRef`. -Azure-Relay desired transport input is carried only by the canonical -`ZoneLink.spec.provider = { schemaId, schemaVersion, settings }` envelope, whose -`settings` object mirrors `status.provider.details`, is registered/signed in the -Provider manifest, deny-unknown, bounded, versioned/digested, validated against -`spec.providerRef` at Nix build and API admission, and cannot shadow base -fields. Shared fields are promoted to the ResourceType base. The Provider -implements the exact base spec/status schema version/fingerprint, accepts the -canonical minimal base Spec, passes base conformance, and rejects an -unsupported optional base capability only through its signed capability matrix plus -provider-neutral `unsupported-capability`. -`spec.provider` aligns with `status.provider`. The `Provider` resource itself -keeps the D075 `spec.{artifactId, config}` exception. +**D089 desired-spec shape.** This transport Provider owns no ResourceType; child +core reconciles the exact canonical ZoneLink base fields: +`childZoneName`, `transportProviderRef`, `transportSettings`, +`transportCredentials`, `disabled`, and `limits`. Azure-Relay desired transport +input is carried only by `spec.transportSettings`, whose deny-unknown schema is +registered and signed by the Provider selected through +`spec.transportProviderRef`. Credential references are carried only by +`spec.transportCredentials`; no desired-state provider envelope or schema +metadata appears in ZoneLink spec. `status.provider` remains the D088 +implementation-observation layer and does not mirror desired spec. The +`Provider` resource itself keeps the D075 `spec.{artifactId, config}` exception. Crate layout (enforced by workspace policy): @@ -124,6 +121,7 @@ packages/d2b-provider-transport-azure-relay/ audit.rs tests/ transport_settings_schema.rs + transport_credentials.rs credential_redaction.rs fake_relay_transport.rs reconnect_open_transport.rs @@ -165,15 +163,6 @@ spec: # TLS trust is governed by Network policy; no Credential ref is used for TLS. networkRef: Network/relay-egress # required; must be Network/ - # Credential alias bindings: maps bounded alias IDs (used in - # ZoneLink.spec.provider.settings) to same-Zone Credential refs. - # Alias IDs match ^[a-z][a-z0-9-]*$; max 16 entries. - # Each referenced Credential must have scope.executionRef matching - # config.executionRef. No credential byte, SAS key, or token appears here. - credentialBindings: - relay-listen: Credential/relay-listen-k2 # example; operator-chosen - relay-send: Credential/relay-send-k2 # example - # Maximum number of concurrent relay sessions this Provider instance # may multiplex across all ZoneLinks it serves. maxConcurrentSessions: 32 # 1–256; default 32 @@ -188,18 +177,17 @@ Config field rules: | --- | --- | --- | --- | | `executionRef` | ResourceRef | Yes | Must be `Guest/`; no host value accepted; all service components execute in this Guest | | `networkRef` | ResourceRef | Yes | Must be `Network/`; used for service Process egress routing; TLS trust governed by Network policy | -| `credentialBindings` | map[alias→ResourceRef] | Yes | Alias keys match `^[a-z][a-z0-9-]*$`; values must be `Credential/` in same Zone with `scope.executionRef = config.executionRef`; max 16 entries; no secret bytes | | `maxConcurrentSessions` | u32 | No | 1–256; default 32 | | `connectTimeoutSeconds` | u32 | No | 5–300; default 30 | No SAS key, SAS token, bearer token, private key, connection string, or TLS certificate byte may appear in `config` at any path. All credential material -arrives via `Credential/` refs and is acquired at runtime through the -end-to-end KK Credential session inside the gateway Guest. +is named only by ZoneLink `spec.transportCredentials` refs and acquired at +runtime through the end-to-end KK Credential session inside the gateway Guest. --- -## `spec.provider.settings` schema +## `spec.transportSettings` schema The transport Provider publishes a signed settings schema at: @@ -209,10 +197,10 @@ docs/reference/schemas/v3/providers/transport-azure-relay.transport-settings.jso This schema is committed alongside the crate and kept in sync by `make test-drift` (via `xtask gen-provider-transport-schemas && git diff --exit-code`). -The Nix build phase validates every `ZoneLink.spec.provider.settings` object +The Nix build phase validates every `ZoneLink.spec.transportSettings` object against it before emitting the resource bundle. -### Canonical `spec.provider.settings` object +### Canonical `spec.transportSettings` object ```json { @@ -221,7 +209,7 @@ against it before emitting the resource bundle. "title": "AzureRelayTransportSettings", "type": "object", "additionalProperties": false, - "required": ["relayNamespaceId", "relayEntityId", "listenerCredentialAlias", "senderCredentialAlias"], + "required": ["relayNamespaceId", "relayEntityId"], "properties": { "relayNamespaceId": { "type": "string", @@ -234,18 +222,6 @@ against it before emitting the resource bundle. "description": "Hybrid Connection entity name within the namespace. Example: 'hc-d2b-k2'. Non-secret. Validated against ^[a-z][a-z0-9-]{1,49}$.", "pattern": "^[a-z][a-z0-9-]{1,49}$", "maxLength": 50 - }, - "listenerCredentialAlias": { - "type": "string", - "description": "Alias ID (plain string) resolved against the child Zone's selected Provider.spec.config.credentialBindings map when the sealed allocation selects the Listen role. No Credential ref, SAS key, SAS token, or bearer token byte may appear here.", - "pattern": "^[a-z][a-z0-9-]*$", - "maxLength": 64 - }, - "senderCredentialAlias": { - "type": "string", - "description": "Alias ID (plain string) resolved against the child Zone's selected Provider.spec.config.credentialBindings map when the sealed allocation selects the Send role. The credential is acquired locally inside the child gateway Guest. No Credential ref, SAS key, or token byte may appear here.", - "pattern": "^[a-z][a-z0-9-]*$", - "maxLength": 64 } } } @@ -257,15 +233,13 @@ against it before emitting the resource bundle. | --- | --- | --- | --- | | `relayNamespaceId` | Yes | No | Plain Azure Relay namespace label only; no `.servicebus.windows.net` suffix, no scheme; validated by regex; max 50 chars | | `relayEntityId` | Yes | No | Hybrid Connection entity name; lowercase kebab; max 50 chars | -| `listenerCredentialAlias` | Yes | No | Plain alias ID; resolved by the child Zone's selected Provider against its same-Zone `config.credentialBindings` map when assigned the Listen role; never a Credential ref or token | -| `senderCredentialAlias` | Yes | No | Plain alias ID; resolved by the child Zone's selected Provider against its same-Zone `config.credentialBindings` map when assigned the Send role; never a Credential ref or token | -The build emitter **rejects** any `spec.provider.settings` field: +The build emitter **rejects** any `spec.transportSettings` field: - annotated `"secret": true`; - containing a top-level key named `socketPath`, `hostPath`, `password`, `token`, `key`, or any key ending in `CredRef` or `Credential` (Credential - refs must not appear in `spec.provider.settings`; only alias IDs are permitted); + refs belong only in `spec.transportCredentials`); - containing a value matching a SAS token shape (`SharedAccessSignature sr=…` or `?sv=…&sig=…`); - containing a value that is a private-key PEM block. @@ -336,10 +310,6 @@ d2b.zones.k2.resources.transport-azure-relay = { config = { executionRef = "Guest/work-gateway"; # required; service components run here networkRef = "Network/relay-egress"; # required; Network governs TLS trust - credentialBindings = { - relay-listen = "Credential/relay-listen"; - relay-send = "Credential/relay-send"; - }; maxConcurrentSessions = 32; connectTimeoutSeconds = 30; }; @@ -351,29 +321,23 @@ d2b.zones.k2.resources.transport-azure-relay = { d2b.zones.k2.resources.k2-uplink = { type = "ZoneLink"; spec = { - childZoneName = "k2"; - providerRef = "Provider/transport-azure-relay"; - provider = { - schemaId = "transport-azure-relay.d2bus.org/ZoneLink/spec"; - schemaVersion = "1.0"; - settings = { - relayNamespaceId = "relns-d2b-prod"; # non-secret namespace label - relayEntityId = "hc-d2b-k2"; # non-secret entity name - listenerCredentialAlias = "relay-listen"; # K2-local alias; selected Provider resolves - senderCredentialAlias = "relay-send"; # K2-local alias; selected Provider resolves - }; + childZoneName = "k2"; + transportProviderRef = "Provider/transport-azure-relay"; + transportSettings = { + relayNamespaceId = "relns-d2b-prod"; # non-secret namespace label + relayEntityId = "hc-d2b-k2"; # non-secret entity name }; - childStaticKeyFingerprint = "9d2e1f...7f01"; # 64 lower-hex chars - capabilityCeiling = { - resourceTypes = [ "Process" ]; - verbs = [ "create" "get" "list" "watch" ]; - zones = []; - executionRefs = []; + transportCredentials = [ + "Credential/relay-listen" + "Credential/relay-send" + ]; + disabled = false; + limits = { + maxPendingIntents = 256; + maxActiveStreams = 32; + reconnectMaxAttempts = 10; + reconnectWindowSecs = 300; }; - localIntentPolicy = "queue"; - maxQueuedIntents = 128; - routeRenewalSeconds = 120; - maxHops = 4; }; }; ``` @@ -389,15 +353,19 @@ metadata: zone: k2 spec: childZoneName: k2 - providerRef: Provider/transport-azure-relay - provider: - schemaId: transport-azure-relay.d2bus.org/ZoneLink/spec - schemaVersion: "1.0" - settings: - relayNamespaceId: relns-d2b-prod - relayEntityId: hc-d2b-k2 - listenerCredentialAlias: relay-listen - senderCredentialAlias: relay-send + transportProviderRef: Provider/transport-azure-relay + transportSettings: + relayNamespaceId: relns-d2b-prod + relayEntityId: hc-d2b-k2 + transportCredentials: + - Credential/relay-listen + - Credential/relay-send + disabled: false + limits: + maxPendingIntents: 256 + maxActiveStreams: 32 + reconnectMaxAttempts: 10 + reconnectWindowSecs: 300 ``` No corresponding object is emitted into local-root's resource bundle. @@ -433,9 +401,9 @@ enrollment exchange that establishes: allocator/route state; it has no ZoneLink row, transport Provider resource, or parent-side ZoneLink handler. - **Child** (sender side): the child's controller enrolls its own static - 25519 key pair. Its public key fingerprint is committed in the child-local - `ZoneLink.spec.childStaticKeyFingerprint` and verified by the parent - allocator when it admits the session. + 25519 key pair. Its public key fingerprint is pinned in sealed + enrollment/bootstrap state and verified by the parent allocator when it + admits the session; it is not a ZoneLink spec field. The enrollment record carries the static public keys in an opaque, bounded format. No private key material enters any resource spec, status, bundle @@ -444,9 +412,10 @@ artifact, OTEL span, or audit record. On every session establishment: 1. The sender initiates a Noise KK handshake (`-> e, es, ss` / `<- e, ee, se`). -2. The Noise prologue binds the canonical ZoneLink `spec` object (preface ‖ - canonical offer), including `childStaticKeyFingerprint`, `childZoneName`, - `spec.provider.settings`, `capabilityCeiling`, and `reconnectGeneration`. +2. The Noise prologue binds the canonical ZoneLink identity and exact spec + digest: `childZoneName`, `transportProviderRef`, `transportSettings`, + `transportCredentials`, `disabled`, and `limits`, plus the sealed enrollment + key digests carried by the canonical offer. 3. The listener verifies that the initiator's static public key matches the enrolled fingerprint. Any mismatch fails closed. 4. On success, both sides derive directional Noise transport keys used for @@ -506,29 +475,26 @@ environment variable. `executionRef` absent or referencing the host. This enforces ADR 0032: no host process may hold relay credential bytes or present them to Azure. -The Credential resources bound by `credentialBindings` must have -`spec.scope.executionRef` equal to `config.executionRef`. A Credential whose -scope does not match is refused by the Credential controller with -`authorization-denied` when the gateway service component attempts -`acquire-token`. +The Credential resources listed in `ZoneLink.spec.transportCredentials` must +have `spec.scope.executionRef` equal to the selected Provider's +`config.executionRef`. A Credential whose scope does not match is refused by +the Credential controller with `authorization-denied` when the gateway service +component attempts `acquire-token`. -### Alias-based credential binding +### Canonical transport credential binding -`Provider.spec.config.credentialBindings` maps bounded alias IDs (e.g. -`"relay-listen"`, `"relay-send"`) to same-Zone `Credential/` refs. -Alias IDs are plain strings carrying no credential bytes. +`ZoneLink.spec.transportCredentials` contains exactly two canonical same-Zone +`Credential/` refs. One referenced Credential must have audience +`azure-relay-listen`; the other must have audience `azure-relay-send`. Missing, +duplicate, cross-Zone, or differently scoped refs fail admission. Child core +selects the unique ref for the allocator-assigned Listen or Send role and +supplies that ref to the same-Zone relay service, which acquires the credential +through the local Credential KK session. Credential refs never appear in +`transportSettings`. -`ZoneLink.spec.provider.settings` carries `listenerCredentialAlias` and -`senderCredentialAlias` — plain alias ID strings, not Credential refs. The -selected Provider and ZoneLink are both in the child Zone. Its gateway Guest -resolves either alias against that Provider's same-Zone -`config.credentialBindings` when the sealed allocation assigns the corresponding -Listen or Send role, and acquires the credential through the local Credential KK -session. - -No alias, Credential ref, or credential byte crosses the Zone boundary. No +No Credential ref or credential byte crosses the Zone boundary. No parent-minted token is delivered to the child, and the parent allocator does not -resolve child aliases. The parent keeps only sealed allocator/route state for +resolve child credentials. The parent keeps only sealed allocator/route state for its endpoint; it has no Provider or Credential resource for the child edge. ### Credential acquisition over Noise KK @@ -574,8 +540,8 @@ would make credential acquisition impossible. Responsibilities: - Acquires the listener Azure credential inside the gateway Guest via the - Credential KK session (resolving `listenerCredentialAlias` from - `config.credentialBindings`); zeroizes raw bytes after relay authentication. + Credential KK session using the unique `azure-relay-listen` ref from + `spec.transportCredentials`; zeroizes raw bytes after relay authentication. - Opens and maintains the Azure Relay Hybrid Connection control channel. - Accepts incoming WebSocket connections from sender services; multiplexes sessions up to `maxConcurrentSessions`. @@ -669,8 +635,8 @@ spec: Responsibilities: - Acquires the sender Azure credential inside the child's gateway Guest via the - Credential KK session (resolving `senderCredentialAlias` from the child's own - `config.credentialBindings`); zeroizes raw bytes after relay authentication. + Credential KK session using the unique `azure-relay-send` ref from + `spec.transportCredentials`; zeroizes raw bytes after relay authentication. - Dials the Azure Relay Hybrid Connection as the Send role. - Pumps TLS/WebSocket bytes between the relay connection and the local ComponentSession transport; remains alive for the duration of each session. @@ -870,19 +836,23 @@ The child Zone's core ZoneLink controller drives the relay transport lifecycle through the selected Provider in that same child Zone. The relay Provider is a **typed transport service**; it does not read ZoneLink resources, own Zone session state, or initiate Zone-level operations. The child-local controller -calls the Provider with already-validated `spec.provider.settings` and receives -an opaque byte-stream handle in return. The parent route endpoint is selected +calls the Provider with already-validated `spec.transportSettings` and the +role-scoped same-Zone ref from `spec.transportCredentials`, then receives an +opaque byte-stream handle in return. The parent route endpoint is selected only through sealed allocator state and has no reciprocal Provider or ZoneLink -resource. +resource. Child core makes no service call while `spec.disabled` is true and +enforces `spec.limits` before opening or queueing a stream; the Provider's +`maxConcurrentSessions` remains an additional local safety ceiling. -#### `OpenTransport(spec.provider.settings) → TransportHandle` +#### `OpenTransport(transportSettings, credentialRef) → TransportHandle` Called by the child Zone's core ZoneLink controller when it needs a new relay channel. The child-local service assigned the listener role: -1. Resolves `listenerCredentialAlias` from `config.credentialBindings`, acquires - the Azure credential via KK inside the gateway Guest, and authenticates to - Azure Relay (carriage auth only; raw bytes zeroized after auth). +1. Validates the role-scoped Credential ref selected from + `spec.transportCredentials`, acquires the Azure credential via KK inside the + gateway Guest, and authenticates to Azure Relay (carriage auth only; raw + bytes zeroized after auth). 2. Waits for a sender service connection on the Hybrid Connection control channel. 3. Returns a `TransportHandle` representing a **named opaque byte stream** exposed by the listener service process on its `transport-service` Unix @@ -1018,12 +988,13 @@ used for all ZoneLink named streams: The transport-azure-relay service component processes are authorized by native RBAC only for what a typed transport service requires. They do **not** receive ZoneLink or ResourceAPI access; core calls them with already-validated -`spec.provider.settings`. +`spec.transportSettings` and a role-scoped canonical Credential ref. Granted: -- `acquire-token` on the `Credential` resources bound by - `config.credentialBindings` (subject to `Credential.spec.consumerRef = +- `acquire-token` on the `Credential` resources listed by the child-local + ZoneLink's `spec.transportCredentials` (subject to + `Credential.spec.consumerRef = "Provider/transport-azure-relay"`, `allowedOperations` includes `acquire-token`, and `scope.executionRef` matches the service component's gateway Guest); - `get` on the `Network` resource named by `config.networkRef`. @@ -1032,8 +1003,9 @@ Not held: - `get`, `watch`, `update-status`, `create`, `update-spec`, or `delete` on any ZoneLink (the child Zone's core ZoneLink controller owns its local resource; - the relay service receives `spec.provider.settings` as a parameter from that - same-Zone controller, not by reading the resource); + the relay service receives `spec.transportSettings` and one validated + same-Zone Credential ref as parameters from that controller, not by reading + the resource); - any ResourceAPI verb on Zone resources other than the above; - any relay-forwarding verb for routing calls to child Zones. @@ -1052,9 +1024,9 @@ accepted as a d2b authorization credential at any layer: The relay server's certificate is not used as a d2b trust anchor; it is carriage TLS only. -Zone subjects are established exclusively by the Noise KK enrolled static -key registered in `ZoneLink.spec.childStaticKeyFingerprint`. All other auth -material is scoped to the relay transport layer. +Zone subjects are established exclusively by the Noise KK static identity +pinned in sealed enrollment/bootstrap state. All other auth material is scoped +to the relay transport layer. ### Sandboxing @@ -1129,8 +1101,8 @@ status: observedProviderGeneration: 3 details: relayEndpoint: - namespaceId: relns-d2b-prod # non-secret; echoed from spec.provider.settings - entityId: hc-d2b-k2 # non-secret; echoed from spec.provider.settings + namespaceId: relns-d2b-prod # non-secret; echoed from spec.transportSettings + entityId: hc-d2b-k2 # non-secret; echoed from spec.transportSettings credentialExpiresAtUnixMs: 1753232401000 # listener credential expiry conditions: - type: RelayConnected @@ -1146,7 +1118,7 @@ status: Rules: - `status.provider.details.relayEndpoint.namespaceId` and `.entityId` are - non-secret Azure identifiers echoed from `spec.provider.settings` for operator + non-secret Azure identifiers echoed from `spec.transportSettings` for operator diagnostics. - `lastDisconnectReason` is a bounded (max 256 chars), redacted string. It never contains token bytes, SAS values, stack traces, or internal paths. @@ -1192,7 +1164,7 @@ Stable error codes returned by the relay transport service via the | `relay-websocket-error` | Relay closed the WebSocket with an error frame | | `relay-credential-unavailable` | Credential Provider could not supply a credential within the deadline | | `relay-max-reconnect-exhausted` | Reconnect attempts exceeded `maxAttempts` | -| `relay-invalid-transport-settings` | `spec.provider.settings` failed runtime schema validation | +| `relay-invalid-transport-settings` | `spec.transportSettings` failed runtime schema validation | Error observation fields: @@ -1216,8 +1188,8 @@ interface; appends are not atomic with Zone resource state in redb. | `relay-carriage-auth-success` | `zone`, `zoneLinkName`, `relayNamespaceId`, `relayEntityId`, `correlationId` | Azure Relay WebSocket authenticated successfully (carriage only; no Noise KK outcome here) | | `relay-carriage-auth-failed` | `zone`, `zoneLinkName`, `relayNamespaceId`, `relayEntityId`, `reason`, `correlationId` | Azure Relay returned auth failure; bounded `reason` code | | `relay-carriage-closed` | `zone`, `zoneLinkName`, `relayNamespaceId`, `relayEntityId`, `reason`, `correlationId` | WebSocket closed; bounded `reason` code | -| `relay-credential-acquired` | `zone`, `zoneLinkName`, `credentialAliasId`, `leaseHandle`, `operationClass`, `correlationId` | Credential successfully acquired; opaque `leaseHandle` digest only, never token bytes | -| `relay-credential-failed` | `zone`, `zoneLinkName`, `credentialAliasId`, `reason`, `correlationId` | Credential acquisition failed; bounded reason code; no token material | +| `relay-credential-acquired` | `zone`, `zoneLinkName`, `credentialRefDigest`, `leaseHandle`, `operationClass`, `correlationId` | Credential successfully acquired; only bounded opaque digests, never ref names or token bytes | +| `relay-credential-failed` | `zone`, `zoneLinkName`, `credentialRefDigest`, `reason`, `correlationId` | Credential acquisition failed; bounded reason code; no ref name or token material | Rules: @@ -1349,7 +1321,7 @@ is committed, version-controlled, and kept in sync with the Rust The artifact **never** includes: - a SAS key, SAS token, or bearer token; -- a relay namespace FQDN (it is a runtime `spec.provider.settings` field, not an +- a relay namespace FQDN (it is a runtime `spec.transportSettings` field, not an artifact constant); - a TLS private key. @@ -1368,7 +1340,7 @@ download, or PATH scan. | Reuse source | `d2b-provider-relay/src/lib.rs`: extract `RelayEndpoint`, `RelayCredential`, `RelayRole`, `RelayStream`, `connect()`, `listen()`, `mint_sas()`, credential redaction; adapt for v3 named byte-stream transport inside long-lived service process | | Excluded current behavior | Gateway-display relay path (`d2b-gateway-runtime/src/bin/d2b-gateway-relay.rs`); ACA provider composition (`d2b-provider-aca`, `AcaWorkloadProvider`); `RelayProvider` trait objects from `d2b-realm-provider` | | Excluded ADR45 assumptions | ADR45 fixed 4-unit PID1 endpoint set; `SD_LISTEN_FDS` relay bootstrap; `d2b-realm-router` ProviderInstance relay composition; ADR45 bundle version constants | -| Required delta | Provider resource/catalog; one crate with mandatory layout; named byte-stream transport via `transport-service` Unix endpoint; Credential KK session acquisition inside gateway Guest; `config.executionRef`/`networkRef` placement gates; typed `spec.provider.settings` schema; long-lived service process multiplexing; reconnect response to `CloseTransport`/`OpenTransport`; backpressure/credit integration; audit/OTEL; Nix artifact | +| Required delta | Provider resource/catalog; one crate with mandatory layout; named byte-stream transport via `transport-service` Unix endpoint; Credential KK session acquisition inside gateway Guest from canonical `spec.transportCredentials`; `config.executionRef`/`networkRef` placement gates; typed `spec.transportSettings` schema; long-lived service process multiplexing; reconnect response to `CloseTransport`/`OpenTransport`; backpressure/credit integration; audit/OTEL; Nix artifact | | Behavior retained | Relay WebSocket connect/accept mechanics; credential redaction invariant; SAS mint logic; auth-error mapping | | Removal proof | `d2b-provider-relay` retired only after gateway-display path migrates to Provider resource model; `d2b-gateway-relay.rs` binary retired only after ACA Provider dossier integrates | | Feasibility proof | Fake relay server in `src/tests/integration/` enabling hermetic reconnect and credential scenarios without live Azure service | @@ -1386,7 +1358,7 @@ download, or PATH scan. | Reuse action | adapt | | Destination | `packages/d2b-provider-transport-azure-relay/src/relay_transport.rs` | | Detailed design | Adapt `RelayStream` as relay transport service process; expose named opaque byte stream on the `transport-service` Unix endpoint; add 2-byte length-prefixed framing; preserve credential redaction; TLS/WebSocket state stays in-process — only Noise record bytes traverse the named stream; register named stream with d2b-bus as `TransportHandle`; transport descriptor: `attachment_support: false`, `locality: Remote`, `atomic: false`; expose `OpenTransport`/`CloseTransport`/`ObserveTransport` interface to core; long-lived service process multiplexes sessions internally Primary reuse disposition: `adapt`. Preserved source-plan detail: extract and adapt. | -| Integration | The child Zone's core ZoneLink controller calls its same-Zone selected Provider's `OpenTransport(spec.provider.settings)` → receives named byte stream handle; relay service cannot interpret plaintext bytes; one carriage per call; WebSocket loss closes the named stream; the parent has only sealed allocator/route state | +| Integration | The child Zone's core ZoneLink controller calls its same-Zone selected Provider's `OpenTransport(spec.transportSettings, roleCredentialRef)` using the role ref selected from `spec.transportCredentials` → receives named byte stream handle; relay service cannot interpret plaintext bytes; one carriage per call; WebSocket loss closes the named stream; the parent has only sealed allocator/route state | | Data migration | No compatibility with current relay sessions; v3 sessions are independent | | Validation | `tests/fake_relay_transport.rs`: connect/accept, framing, credential redaction, named stream roundtrip; `tests/listener_sender_conformance.rs`: named stream contract; Noise KK binding; relay identity exclusion | | Removal proof | `d2b-provider-relay/src/lib.rs` relay plumbing retained until ACA display migration completes | @@ -1399,7 +1371,7 @@ download, or PATH scan. | Current source | None (new) | | Reuse action | create | | Destination | `packages/d2b-provider-transport-azure-relay/src/credential_client.rs` | -| Detailed design | Async Credential KK session client for service components (service processes have d2b-bus access, enabling Credential KK; workers do not); the child-local selected Provider acquires either listener or sender credential via KK inside its gateway Guest using the corresponding same-Zone `config.credentialBindings` alias; raw credential bytes are held in zeroizing memory inside the gateway Guest, presented to Azure Relay, then immediately zeroized; no credential ref or bytes cross a Zone, process, network, or Guest boundary; the parent allocator receives neither aliases nor credentials and keeps only sealed route state; redacted Debug; no credential bytes in logs/audit/OTEL; the child Zone's core ProviderDeployment creates a private persistent Volume (per ADR-046-provider-state) for each component before its Process starts — the transport Provider does not own or create these Volumes; `Provider/volume-local` reconciles them; `migrationPolicy: none` means no migration worker is ever spawned; no relay auth token, WebSocket handle, session key, or credential byte is written to that Volume; all relay session state remains transient in-process memory | +| Detailed design | Async Credential KK session client for service components (service processes have d2b-bus access, enabling Credential KK; workers do not); child core validates the same-Zone refs in `spec.transportCredentials`, selects the unique listener or sender ref by Credential audience, and supplies it to the child-local Provider service inside its gateway Guest; raw credential bytes are held in zeroizing memory inside the gateway Guest, presented to Azure Relay, then immediately zeroized; no Credential ref or byte crosses a Zone or Guest boundary, and byte delivery between the Credential Provider and consuming service remains inside the protected KK session; the parent allocator receives neither refs nor credentials and keeps only sealed route state; redacted Debug; no credential bytes in logs/audit/OTEL; the child Zone's core ProviderDeployment creates a private persistent Volume (per ADR-046-provider-state) for each component before its Process starts — the transport Provider does not own or create these Volumes; `Provider/volume-local` reconciles them; `migrationPolicy: none` means no migration worker is ever spawned; no relay auth token, WebSocket handle, session key, or credential byte is written to that Volume; all relay session state remains transient in-process memory | | Integration | The selected Provider's role service invokes acquisition inside the child gateway Guest; the parent allocator has only sealed route state and no Provider/Credential/ZoneLink resource | | Data migration | None — full d2b 3.0 reset; no prior state to migrate | | Validation | `tests/credential_redaction.rs`: credential bytes never reach any Debug/log/audit/OTEL path; `src/tests/integration/credential_delivery.rs`: end-to-end credential delivery using injected fake Credential effect port | @@ -1427,10 +1399,10 @@ download, or PATH scan. | Current source | `docs/specs/ADR-046-zone-routing.md` transport settings Nix example | | Reuse action | create | | Destination | `packages/d2b-provider-transport-azure-relay/src/transport_settings.rs`; `docs/reference/schemas/v3/providers/transport-azure-relay.transport-settings.json` | -| Detailed design | `AzureRelayTransportSettings` Rust struct with serde; validation against committed JSON Schema; reject `secret`-annotated fields; enforce `^[a-z][a-z0-9-]*$` pattern for `listenerCredentialAlias`/`senderCredentialAlias` alias ID fields (never `Credential/` refs); both aliases resolve only through the selected Provider in the same child Zone as the ZoneLink; xtask `gen-provider-transport-schemas` integration | +| Detailed design | `AzureRelayTransportSettings` Rust struct with serde for only `relayNamespaceId` and `relayEntityId`; validation against committed JSON Schema; reject secret-shaped fields/values; generate and admit the exact six-field ZoneLink base; reject legacy provider envelopes and allocator-private fingerprint/capability fields; resolve `spec.transportProviderRef` before schema validation; validate exactly two same-Zone `spec.transportCredentials` refs with one `azure-relay-listen` and one `azure-relay-send` audience; enforce `disabled`/`limits` in child core; xtask `gen-provider-transport-schemas` integration | | Integration | `make test-drift` gate: `xtask gen-provider-transport-schemas && git diff --exit-code` | | Data migration | None — full d2b 3.0 reset; no prior state to migrate | -| Validation | `tests/transport_settings_schema.rs`: valid/invalid schema vectors; eval-time Nix assertion coverage from `nix-unit: transport-settings-secret-key` test (see zone-routing spec) | +| Validation | `tests/transport_settings_schema.rs`: valid/invalid schema vectors; `tests/transport_credentials.rs`: exact canonical ZoneLink field set, same-Zone ref/count/audience/scope checks, and rejection of credential refs inside `transportSettings`; eval-time Nix assertion coverage from `nix-unit: transport-settings-secret-key` test (see zone-routing spec) | | Removal proof | N/A; new contract | ### ADR046-transport-relay-005 @@ -1469,7 +1441,7 @@ download, or PATH scan. | Current source | None — net-new v3 work; no pre-ADR45 baseline equivalent | | Reuse action | create | | Destination | `packages/d2b-provider-transport-azure-relay/src/tests/integration/README` | -| Detailed design | Required content: fake relay server setup and teardown using the injected fake Relay effect port; how to run hermetic integration tests without a live Azure service; how to configure the injected fake Credential effect port for credential delivery tests; the fixture declares compiler-only `k2.parentZone = "local-root"` and puts the ZoneLink, selected Provider, Network, Credentials, Process, and Endpoint resources only in K2; local-root's store is asserted to contain no reciprocal ZoneLink or Provider; how to run with a real Azure namespace (requires same-child-Zone `Credential` resources declared in `spec.config.credentialBindings`, not environment-variable credential paths); integration test scenarios and expected outcomes; CI/local execution instructions | +| Detailed design | Required content: fake relay server setup and teardown using the injected fake Relay effect port; how to run hermetic integration tests without a live Azure service; how to configure the injected fake Credential effect port for credential delivery tests; the fixture declares compiler-only `k2.parentZone = "local-root"` and puts the exact-shape ZoneLink, selected Provider, Network, Credentials, Process, and Endpoint resources only in K2; local-root's store is asserted to contain no reciprocal ZoneLink or Provider; how to run with a real Azure namespace (requires same-child-Zone `Credential` resources listed in `spec.transportCredentials`, not environment-variable credential paths); integration test scenarios and expected outcomes; CI/local execution instructions | | Integration | `make test-integration` invokes `tests/integration/containers/` scenarios which inject the fake relay and credential port implementations from `src/tests/integration/fake_relay_server.rs` | | Data migration | None — full d2b 3.0 reset; no prior state to migrate | | Validation | File must be present; workspace policy gate enforces `src/tests/integration/README` | @@ -1486,15 +1458,16 @@ wave: | Test module | Required test functions | What they prove | | --- | --- | --- | -| `transport_settings_schema.rs` | `valid_settings_roundtrip`, `missing_required_field_rejected`, `secret_key_field_rejected`, `sas_token_value_rejected`, `pem_key_value_rejected`, `unknown_field_rejected`, `credential_alias_pattern_enforced`, `credential_ref_in_settings_rejected` | Schema validation vectors; no secret-shaped value admitted; alias IDs accepted, Credential refs rejected | +| `transport_settings_schema.rs` | `valid_settings_roundtrip`, `missing_required_field_rejected`, `secret_key_field_rejected`, `sas_token_value_rejected`, `pem_key_value_rejected`, `unknown_field_rejected`, `credential_ref_in_settings_rejected` | Schema validation vectors; only relay namespace/entity settings admitted; credential refs rejected from settings | +| `transport_credentials.rs` | `canonical_zone_link_fields_are_exact`, `exactly_two_refs_required`, `refs_must_be_same_zone`, `listener_and_sender_audiences_required`, `credential_scope_matches_execution_ref`, `legacy_provider_envelope_rejected` | Exact canonical ZoneLink spec; credentials use only canonical refs and never legacy aliases/envelopes | | `credential_redaction.rs` | `sas_key_debug_is_redacted`, `entra_bearer_debug_is_redacted`, `sas_token_debug_is_redacted`, `credential_not_in_audit_record`, `credential_not_in_otel_span`, `credential_not_in_status`, `credential_not_in_log` | Credential bytes never reach any observable surface | | `fake_relay_transport.rs` | `listener_accepts_sender_connection`, `framing_is_length_prefixed`, `send_receive_roundtrip_over_fake_relay`, `attachment_support_is_false`, `locality_is_remote`, `transport_descriptor_contract` | Named byte-stream contract; framing; transport descriptor | | `reconnect_open_transport.rs` | `websocket_starts_on_open_transport`, `websocket_closes_on_close_transport`, `observe_transport_reports_connect_result`, `reconnect_clears_generation`, `reconnect_triggers_new_kk` | Relay responds to CloseTransport/OpenTransport cycle; named stream closes on WebSocket loss | | `backpressure_credit.rs` | `slow_relay_stalls_credit`, `aggregate_queue_bounded`, `source_never_buffers_beyond_limit`, `backpressure_event_counter_increments` | Backpressure and credit invariants | | `metric_labels.rs` | `identity_keys_absent`, `zone_name_canary_absent`, `semantic_domains_closed`, `zone_resource_attribute_retained` | Structural metric-label policy; no Zone/resource identity labels; `d2b.zone` remains a resource attribute | | `idempotency_key.rs` | `idempotency_key_carried_in_noise_record`, `idempotency_key_not_in_relay_frame_metadata`, `replay_at_relay_level_does_not_deduplicate`, `dedup_at_child_zone_resource_api` | Idempotency is child-Zone-owned; relay carries opaquely | -| `listener_sender_conformance.rs` | `conformance_vectors_listener`, `conformance_vectors_sender`, `noise_kk_prologue_binds_transport_settings`, `mismatched_fingerprint_fails_closed`, `relay_identity_not_in_subject_context` | Named stream contract; Noise KK binding; relay identity exclusion | -| `child_local_topology.rs` | `provider_and_zonelink_are_in_child_zone`, `child_zone_name_self_matches`, `parent_zone_is_compiler_only`, `parent_store_has_no_reciprocal_resources`, `credential_aliases_resolve_only_in_child` | K2 owns the ZoneLink and selected Provider; local-root is selected only as allocator and retains sealed route state; no cross-Zone ref or credential path | +| `listener_sender_conformance.rs` | `conformance_vectors_listener`, `conformance_vectors_sender`, `noise_kk_prologue_binds_exact_zonelink_spec`, `mismatched_enrolled_key_fails_closed`, `relay_identity_not_in_subject_context` | Named stream contract; exact canonical spec and sealed identity binding; relay identity exclusion | +| `child_local_topology.rs` | `provider_and_zonelink_are_in_child_zone`, `child_zone_name_self_matches`, `parent_zone_is_compiler_only`, `parent_store_has_no_reciprocal_resources`, `transport_credentials_resolve_only_in_child` | K2 owns the exact-shape ZoneLink, selected Provider, and Credential refs; local-root is selected only as allocator and retains sealed route state; no cross-Zone ref or credential path | ### Integration tests (in `src/tests/integration/`) @@ -1518,7 +1491,7 @@ It must NOT require network access to `*.servicebus.windows.net` in CI. The fake relay server and fake Credential port are injected as constructor arguments or via the toolkit's fake-port infrastructure; no environment variable activates them. Live/manual integration tests that target a real -Azure namespace must declare Credential resources in `spec.config.credentialBindings` +Azure namespace must declare Credential resources in `spec.transportCredentials` and supply those Credential resources in the test configuration — never via `D2B_RELAY_NAMESPACE`, `D2B_RELAY_ENTITY`, `D2B_RELAY_SAS_ENV`, or any environment-variable credential path. Tests that require a live Azure endpoint diff --git a/docs/specs/providers/ADR-046-provider-transport-unix.md b/docs/specs/providers/ADR-046-provider-transport-unix.md index 2a712b3ff..7640191c7 100644 --- a/docs/specs/providers/ADR-046-provider-transport-unix.md +++ b/docs/specs/providers/ADR-046-provider-transport-unix.md @@ -165,20 +165,18 @@ ZoneLink handler exists. | ResourceTypes owned | None | | ResourceTypes consumed | `Host` (executionRef target); `Provider/system-minijail` (Process Provider); `Volume` (view only: component mounts its ProviderDeployment-created Volume view; `Provider/transport-unix` does not own, reconcile, or create Volume resources) | -**D089 desired-spec shape.** This transport Provider owns no ResourceType; core -reconciles the `ZoneLink` base `spec.*` fields, including `spec.providerRef`. -The only selected-Provider desired payload is the usually-empty -`ZoneLink.spec.provider = { schemaId, schemaVersion, settings }` envelope, whose -`settings` object mirrors `status.provider.details`, is registered/signed in the -Provider manifest, deny-unknown, bounded, versioned/digested, validated against -`spec.providerRef` at Nix build and API admission, and cannot shadow base -fields. Shared fields are promoted to the ResourceType base. The Provider -implements the exact base spec/status schema version/fingerprint, accepts the -canonical minimal base Spec, passes base conformance, and rejects an -unsupported optional base capability only through its signed capability matrix plus -provider-neutral `unsupported-capability`. -`spec.provider` aligns with `status.provider`. The `Provider` resource itself -keeps the D075 `spec.{artifactId, config}` exception. +**D089 desired-spec shape.** This transport Provider owns no ResourceType; child +core reconciles the exact canonical ZoneLink base fields: +`childZoneName`, `transportProviderRef`, `transportSettings`, +`transportCredentials`, `disabled`, and `limits`. Unix-specific desired input +is carried only by `spec.transportSettings`, whose deny-unknown schema is +registered and signed by the Provider selected through +`spec.transportProviderRef`. Unix uses an empty +`spec.transportCredentials` list. No desired-state provider envelope or schema +metadata appears in ZoneLink spec. +`status.provider` remains the D088 implementation-observation layer and does not +mirror desired spec. The `Provider` resource itself keeps the D075 +`spec.{artifactId, config}` exception. --- @@ -257,10 +255,11 @@ is no empty identity-only Volume. --- -## ZoneLink `spec.provider.settings` schema +## ZoneLink `spec.transportSettings` schema -The `settings` object inside `ZoneLink.spec.provider` is validated at build -time against the transport Provider's signed settings schema. For +`ZoneLink.spec.transportSettings` is validated at build time against the +settings schema signed by the Provider selected through +`spec.transportProviderRef`. For `Provider/transport-unix` this schema is committed at: ``` @@ -273,7 +272,7 @@ docs/reference/schemas/v3/providers/transport-unix.transport-binding.json { "$schema": "http://json-schema.org/draft-07/schema#", "title": "UnixTransportSettings", - "description": "ZoneLink spec.provider.settings for Provider/transport-unix. Normally empty ({}). The ZoneLink contract prohibits FD/resource grants across Zone boundaries; FD attachment (SCM_RIGHTS) is therefore not a configurable field here. Child core always opens ZoneLink transports with attachments_enabled=false. The selected parent allocator pre-binds the edge, retains its endpoint only as sealed route state, and injects the child endpoint through sealed bootstrap; child core supplies that endpoint locally to the same-Zone Provider's OpenTransport portal. No parent resource, cross-Zone FD transfer, path, or credential is needed here.", + "description": "ZoneLink spec.transportSettings for Provider/transport-unix. Normally empty ({}). The ZoneLink contract prohibits FD/resource grants across Zone boundaries; FD attachment (SCM_RIGHTS) is therefore not configurable here. Child core always opens ZoneLink transports with attachments_enabled=false. The selected parent allocator pre-binds the edge, retains its endpoint only as sealed route state, and injects the child endpoint through sealed bootstrap; child core supplies that endpoint locally to the same-Zone Provider's OpenTransport portal. No parent resource, cross-Zone FD transfer, path, or credential is needed here.", "type": "object", "properties": { "socketKind": { @@ -304,9 +303,9 @@ glance. Runtime validation also enforces the schema independently of the build s ### Rules -- `spec.provider.settings: {}` is the normal and recommended value. `socketKind` +- `spec.transportSettings: {}` is the normal and recommended value. `socketKind` is optional and defaults to `"seqpacket"`. -- **`attachmentsEnabled` is not a ZoneLink `spec.provider.settings` field.** The +- **`attachmentsEnabled` is not a ZoneLink `spec.transportSettings` field.** The ZoneLink contract prohibits FD and resource grants across Zone boundaries — even for same-kernel parent/child links. Child core always opens ZoneLink transports with `attachments_enabled=false`; a cross-Zone SCM_RIGHTS attempt @@ -402,7 +401,7 @@ the connecting process's uid/gid/pid. The transport verifies `ucred.uid == expected_uid && ucred.gid == expected_gid` on the first data packet; mismatch closes the connection without response. -The socket path itself never appears in `spec.provider.settings`; the path is +The socket path itself never appears in `spec.transportSettings`; the path is encoded in the allocator-issued FD and never surfaced in the ResourceSpec. ### `InheritedSocketpair` @@ -719,6 +718,11 @@ The transport-unix service process exposes three typed portal methods. All methods are invoked by the child Zone's core ZoneLink controller via d2b-bus over a same-Zone ComponentSession authenticated to the service's enrolled `portal` endpoint. No other caller may invoke these methods (see RBAC section). +For a ZoneLink call, child core first resolves `spec.transportProviderRef`, +validates `spec.transportSettings`, requires `spec.transportCredentials = []`, +checks `spec.disabled`, enforces `spec.limits`, and derives `socket_kind` from +the validated settings. The service never receives a ZoneLink spec or legacy +provider envelope. ### `OpenTransport` @@ -872,9 +876,9 @@ from OS-enforced SO_PEERCRED, not a peer-supplied long-term key. `Noise_KK_25519_ChaChaPoly_SHA256` is used for all ZoneLink ComponentSessions: - Both static public keys are known before handshake. -- The child Zone's enrolled static key is pinned in - `ZoneLink.spec.childStaticKeyFingerprint` and verified by the child Zone's - core ZoneLink controller after the handshake — not by this Provider. +- The child Zone's enrolled static identity is pinned in sealed + enrollment/bootstrap state and verified by the child Zone's core ZoneLink + controller after the handshake — not by this Provider. - Unix seqpacket or stream carries the handshake bytes; the transport is unaware of the Noise content. - SO_PEERCRED is still verified by `PeerIdentityPolicy`; for KK sessions its @@ -1038,9 +1042,9 @@ The following values are **never** logged, audited, or emitted as metric labels: | Invariant | Enforcement point | | --- | --- | -| No socket path in `spec.provider.settings` | JSON Schema + eval-time assertion | -| No credential bytes in `spec.provider.settings` | JSON Schema + build-time validation | -| `attachmentsEnabled` not a ZoneLink `spec.provider.settings` field | JSON Schema (`additionalProperties:false` + `not/anyOf`) | +| No socket path in `spec.transportSettings` | JSON Schema + eval-time assertion | +| No credential bytes in `spec.transportSettings` | JSON Schema + build-time validation | +| `attachmentsEnabled` not a ZoneLink `spec.transportSettings` field | JSON Schema (`additionalProperties:false` + `not/anyOf`) | | `additionalProperties: false` rejects all unknown keys | JSON Schema | | ZoneLink transports always have `attachments_enabled=false` | Child Zone's core ZoneLink controller (structural, pre-call); `admission.rs::validate_route_class` (belt-and-suspenders) | | `route_class=zone-link` + `attachments_enabled=true` → `attachment-policy-conflict` | `portal.rs::open_transport` step 3 | @@ -1131,7 +1135,7 @@ never reused. | Code | Stable tag | Meaning | | --- | --- | --- | | `socket-kind-mismatch` | 1 | `getsockopt(SO_TYPE)` on the received FD does not match the declared `socketKind` | -| `attachment-policy-conflict` | 2 | `route_class=zone-link` with `attachments_enabled=true` (cross-Zone FD grants are prohibited by the ZoneLink contract); or `socketKind=stream` with `attachments_enabled=true`; or `attachmentsEnabled` field present in ZoneLink `spec.provider.settings` (rejected structurally before Provider) | +| `attachment-policy-conflict` | 2 | `route_class=zone-link` with `attachments_enabled=true` (cross-Zone FD grants are prohibited by the ZoneLink contract); or `socketKind=stream` with `attachments_enabled=true`; or `attachmentsEnabled` field present in ZoneLink `spec.transportSettings` (rejected structurally before Provider) | | `open-transport-bad-attachment` | 3 | `OpenTransport` request has no FD attachment or carries more than one FD | | `invalid-socket-fd` | 4 | Received FD is not a valid `AF_UNIX` socket (wrong address family or type) | | `cloexec-set-failed` | 5 | `fcntl(F_SETFD, FD_CLOEXEC)` failed; FD closed | @@ -1142,7 +1146,7 @@ never reused. | `pidfd-liveness-check-failed` | 10 | `PidfdIdentityPolicy` rejected the received pidfd (process reuse, fdinfo mismatch, kcmp failure) | | `handle-table-full` | 11 | `MAX_OPEN_TRANSPORTS=256` open transport handles already active | | `unknown-handle` | 12 | `CloseTransport` or `ObserveTransport` supplied an unrecognized or already-closed `transport_handle`; idempotent for `CloseTransport` | -| `transport-settings-schema-violation` | 13 | `spec.provider.settings` violates the JSON Schema at runtime (belt-and-suspenders; build/eval guards should precede this) | +| `transport-settings-schema-violation` | 13 | `spec.transportSettings` violates the JSON Schema at runtime (belt-and-suspenders; build/eval guards should precede this) | Retriable: `handle-table-full` (after delay; core may retry after a prior transport is closed). All others are non-retriable from the Provider's perspective; @@ -1277,19 +1281,17 @@ The `ZoneLink` spec is authored in the standard resource syntax: d2b.zones.k1.resources.k1-uplink = { type = "ZoneLink"; spec = { - childZoneName = "k1"; - providerRef = "Provider/transport-unix"; - provider = { - schemaId = "transport-unix.d2bus.org/ZoneLink/spec"; - schemaVersion = "1.0"; - settings = {}; # allocator/core-issued FD; attachments forbidden on ZoneLink + childZoneName = "k1"; + transportProviderRef = "Provider/transport-unix"; + transportSettings = {}; # child bootstrap endpoint; no ZoneLink attachments + transportCredentials = []; + disabled = false; + limits = { + maxPendingIntents = 256; + maxActiveStreams = 32; + reconnectMaxAttempts = 10; + reconnectWindowSecs = 300; }; - childStaticKeyFingerprint = "a3f1e2d4c5b6a7f890e1d2c3b4a5f6e7d8c9b0a1f2e3d4c5b6a7f8e9d0c1b2a3"; - capabilityCeiling = { - resourceTypes = [ "Network" "Process" ]; - verbs = [ "create" "get" "list" "update-spec" "watch" ]; - }; - # All remaining fields use schema defaults }; }; @@ -1307,23 +1309,22 @@ d2b.zones.k3.resources.transport-unix = { d2b.zones.k3.resources.k3-uplink = { type = "ZoneLink"; spec = { - childZoneName = "k3"; - providerRef = "Provider/transport-unix"; - provider = { - schemaId = "transport-unix.d2bus.org/ZoneLink/spec"; - schemaVersion = "1.0"; - settings = { socketKind = "stream"; }; - }; - childStaticKeyFingerprint = "b4c5d6e7f8a9b0c1d2e3f4a5b6c7d8e9f0a1b2c3d4e5f6a7b8c9d0e1f2a3b4c5"; - capabilityCeiling = { - resourceTypes = [ "Process" ]; - verbs = [ "get" "list" "watch" ]; + childZoneName = "k3"; + transportProviderRef = "Provider/transport-unix"; + transportSettings = { socketKind = "stream"; }; + transportCredentials = []; + disabled = false; + limits = { + maxPendingIntents = 256; + maxActiveStreams = 32; + reconnectMaxAttempts = 10; + reconnectWindowSecs = 300; }; }; }; ``` -### Canonical emitted `ZoneLink` JSON with default `spec.provider.settings` +### Canonical emitted `ZoneLink` JSON with default `spec.transportSettings` ```json { @@ -1332,26 +1333,18 @@ d2b.zones.k3.resources.k3-uplink = { "metadata": { "name": "k1-uplink", "zone": "k1" }, "spec": { "childZoneName": "k1", - "providerRef": "Provider/transport-unix", - "provider": { - "schemaId": "transport-unix.d2bus.org/ZoneLink/spec", - "schemaVersion": "1.0", - "settings": { - "socketKind": "seqpacket" - } + "transportProviderRef": "Provider/transport-unix", + "transportSettings": { + "socketKind": "seqpacket" }, - "childStaticKeyFingerprint": "a3f1e2d4c5b6a7f890e1d2c3b4a5f6e7d8c9b0a1f2e3d4c5b6a7f8e9d0c1b2a3", - "capabilityCeiling": { - "executionRefs": [], - "resourceTypes": ["Network", "Process"], - "verbs": ["create", "get", "list", "update-spec", "watch"], - "zones": [] - }, - "reconnectPolicy": { "initialDelaySeconds": 1, "jitterFactor": 0.25, "maxDelaySeconds": 60 }, - "localIntentPolicy": "queue", - "maxHops": 8, - "maxQueuedIntents": 256, - "routeRenewalSeconds": 300 + "transportCredentials": [], + "disabled": false, + "limits": { + "maxPendingIntents": 256, + "maxActiveStreams": 32, + "reconnectMaxAttempts": 10, + "reconnectWindowSecs": 300 + } } } ``` @@ -1371,8 +1364,8 @@ These supplement generated per-field option type checks and live in | Assertion | Error message | | --- | --- | | ZoneLink and selected Provider are declared in the same child Zone; `spec.childZoneName` equals that Zone; compiler-only `parentZone` resolves to the allocator owner; no reciprocal parent resource is emitted | `zones.: transport-unix ZoneLink and Provider must be child-local with a self-matching childZoneName` | -| `spec.provider.settings` must not contain `attachmentsEnabled` | `zones..resources.: spec.provider.settings.attachmentsEnabled is not a valid ZoneLink transport field; FD attachment across Zone boundaries is prohibited` | -| `spec.provider.settings` contains no top-level key `socketPath`, `hostPath`, `password`, `token`, or `key` | `zones..resources.: spec.provider.settings must not contain host paths, socket paths, or secret material` | +| `spec.transportSettings` must not contain `attachmentsEnabled` | `zones..resources.: spec.transportSettings.attachmentsEnabled is not a valid ZoneLink transport field; FD attachment across Zone boundaries is prohibited` | +| `spec.transportSettings` contains no top-level key `socketPath`, `hostPath`, `password`, `token`, or `key` | `zones..resources.: spec.transportSettings must not contain host paths, socket paths, or secret material` | These are belt-and-suspenders: the JSON Schema `additionalProperties: false` and the `not/anyOf` block already reject these keys. The eval assertion catches them @@ -1383,21 +1376,26 @@ rationale. The `xtask gen-zone-resources` step adds for `Provider/transport-unix` links: -1. Validate `spec.provider.settings` against `docs/reference/schemas/v3/providers/transport-unix.transport-binding.json`. -2. Reject `attachmentsEnabled` as a ZoneLink `spec.provider.settings` key (structurally covered by additionalProperties:false, and explicitly named in `not/anyOf`). +1. Resolve `spec.transportProviderRef` to the same-Zone Provider and validate + `spec.transportSettings` against + `docs/reference/schemas/v3/providers/transport-unix.transport-binding.json`. +2. Reject `attachmentsEnabled` as a ZoneLink `spec.transportSettings` key + (structurally covered by `additionalProperties:false`, and explicitly named + in `not/anyOf`). ### Eval and build tests | Test ID | Kind | What it proves | | --- | --- | --- | | `nix-unit: transport-unix-child-local-topology` | nix-unit eval | Provider and ZoneLink are in K1, `childZoneName = "k1"`, `k1.parentZone = "local-root"` selects only the allocator, and no local-root reciprocal resource is emitted | -| `nix-unit: transport-unix-empty-settings` | nix-unit eval | `spec.provider.settings = {}` passes all assertions | -| `nix-unit: transport-unix-socket-path-rejected` | nix-unit eval | `spec.provider.settings.socketPath = "/run/..."` rejected at eval | -| `nix-unit: transport-unix-attachments-enabled-rejected` | nix-unit eval | `spec.provider.settings.attachmentsEnabled = false` rejected as unknown field at eval | +| `nix-unit: transport-unix-exact-zonelink-spec` | nix-unit eval | Emitted spec has exactly `childZoneName`, `transportProviderRef`, `transportSettings`, `transportCredentials`, `disabled`, and `limits`; legacy provider/fingerprint/capability fields are rejected | +| `nix-unit: transport-unix-empty-settings` | nix-unit eval | `spec.transportSettings = {}` passes all assertions | +| `nix-unit: transport-unix-socket-path-rejected` | nix-unit eval | `spec.transportSettings.socketPath = "/run/..."` rejected at eval | +| `nix-unit: transport-unix-attachments-enabled-rejected` | nix-unit eval | `spec.transportSettings.attachmentsEnabled = false` rejected as unknown field at eval | | `nix-unit: transport-unix-stream-variant` | nix-unit eval | `socketKind = "stream"` accepted; no other field needed | | `drift: transport-unix-transport-binding-schema` | `make test-drift` | `xtask gen-zone-schemas && git diff --exit-code` for transport binding schema | -| `build: transport-unix-unknown-field-rejected` | flake check | Unknown key in `spec.provider.settings` fails build | -| `build: transport-unix-attachments-enabled-is-unknown-field` | flake check | `attachmentsEnabled` field in ZoneLink `spec.provider.settings` fails build (not in schema) | +| `build: transport-unix-unknown-field-rejected` | flake check | Unknown key in `spec.transportSettings` fails build | +| `build: transport-unix-attachments-enabled-is-unknown-field` | flake check | `attachmentsEnabled` field in ZoneLink `spec.transportSettings` fails build (not in schema) | ### Fast hermetic execution and test placement (D094) @@ -1440,7 +1438,7 @@ Old and new suites never run in parallel indefinitely. | OpenTransport/CloseTransport/ObserveTransport service API | none | `ADR-only` | new | | Service Process resource with full sandbox/budget/endpoints spec | none in v3 baseline | `ADR-only` | new | | ProviderStateSet | ownerRef=Provider/transport-unix Volume query | `ADR-only` | Empty — `Provider/transport-unix` declares no Provider state Volume; its bounded non-secret operational state lives in `status`/the core Operation ledger (D087) | -| ZoneLink `spec.provider.settings` schema | none | `ADR-only` | new in this spec | +| ZoneLink `spec.transportSettings` schema | none | `ADR-only` | new in this spec | | FD pre-bind pattern (broker FD pre-binding) | `d2b-priv-broker/src/ops/{swtpm_dir,spawn_runner}.rs` (v3 baseline) | `implemented-and-reachable` | selected parent allocator adapts the pattern, retains its peer endpoint as sealed route state, and injects only the child endpoint through sealed bootstrap; the child-local Provider is a passive recipient and no FD crosses the ZoneLink | | Route advertisement / RouteTreeEngine | `d2b-realm-core/src/route_engine.rs` (v3 baseline) | `implemented-but-unwired` | child core owns local ZoneLink session/cursor state; selected parent allocator owns only sealed route state; not in this Provider | | ZoneLink controller (reconcile/status/finalizer/routes) | none in v3 baseline | `ADR-only` | owned by the child Zone's core-controller (ADR-046-core-controllers); no parent-side handler and not in this Provider | @@ -1603,11 +1601,11 @@ Old and new suites never run in parallel indefinitely. | Current source | `nixos-modules/options-realms.nix` realm options (v3 baseline); `nixos-modules/assertions.nix` | | Reuse source | None; new schema file | | Reuse action | create | -| Destination | `docs/reference/schemas/v3/providers/transport-unix.transport-binding.json`; `nixos-modules/assertions.nix` (assertion additions); generated `nixos-modules/generated/options-zones-ZoneLink.nix` provider-settings submodule | -| Detailed design | Commit the JSON Schema; run `xtask gen-zone-schemas` and `xtask gen-zone-nix-options` to regenerate committed files; add assertions for stream+attachments conflict and sensitive key names; reuse the common topology assertions to require the ZoneLink and selected Provider in the same child Zone, self-matching `childZoneName`, compiler-only non-root `parentZone`, and no reciprocal parent resource; `xtask gen-zone-resources` adds provider-specific settings validation step | -| Integration | Build emitter validates `spec.provider.settings` against schema before computing `generationId`; the topology compiler seals child→parent allocator selection separately and emits only the child-local Provider/ZoneLink resources; drift gate enforces sync | +| Destination | `docs/reference/schemas/v3/providers/transport-unix.transport-binding.json`; `nixos-modules/assertions.nix` (assertion additions); generated `nixos-modules/generated/options-zones-ZoneLink.nix` `transportSettings` submodule | +| Detailed design | Commit the JSON Schema; run `xtask gen-zone-schemas` and `xtask gen-zone-nix-options` to regenerate committed files; generate the exact six-field ZoneLink base; reject legacy provider envelopes and allocator-private fingerprint/capability fields; add assertions for stream+attachments conflict and sensitive key names; require an empty `transportCredentials` list; reuse the common topology assertions to require the ZoneLink and selected Provider in the same child Zone, self-matching `childZoneName`, compiler-only non-root `parentZone`, and no reciprocal parent resource; `xtask gen-zone-resources` adds Provider-selected `transportSettings` validation | +| Integration | Build emitter resolves `spec.transportProviderRef`, validates `spec.transportSettings` against that Provider's schema, and validates the empty `spec.transportCredentials` list before computing `generationId`; the topology compiler seals child→parent allocator selection separately and emits only the child-local Provider/ZoneLink resources; drift gate enforces sync | | Data migration | `d2b.realms.*` Nix options superseded by `d2b.zones.*`; no compatibility bridge (v3 reset) | -| Validation | All eval/build tests in the Nix section, including `transport-unix-child-local-topology` and a generated-bundle assertion that the parent store has no reciprocal Provider/ZoneLink row | +| Validation | All eval/build tests in the Nix section, including `transport-unix-exact-zonelink-spec`, `transport-unix-child-local-topology`, legacy-field rejection, empty-credential enforcement, and a generated-bundle assertion that the parent store has no reciprocal Provider/ZoneLink row | | Removal proof | `nixos-modules/options-realms.nix` realm wiring retired after Zone resource bundle activation replaces it | --- diff --git a/docs/specs/providers/ADR-046-provider-transport-vsock.md b/docs/specs/providers/ADR-046-provider-transport-vsock.md index b94d304ff..4ca209015 100644 --- a/docs/specs/providers/ADR-046-provider-transport-vsock.md +++ b/docs/specs/providers/ADR-046-provider-transport-vsock.md @@ -119,7 +119,7 @@ is consumed from `d2b-session`. | `generate_vsock_relay_argv` | `packages/d2b-host/src/vsock_relay_argv.rs` | `implemented-and-reachable` | socat relay for OTLP (ports 14317/14319); uses hard-coded CID 2 for guest egress; not a ComponentSession transport | | `GUEST_CONTROL_CONNECT_PORT = 14318` | `packages/d2bd/src/guest_control_vsock.rs` | `implemented-and-reachable` | Reserved for guestd ttrpc; must not be reused by transport-vsock ZoneLink | | `VsockRelayArgvInput` | `packages/d2b-host/src/vsock_relay_argv.rs` | `implemented-and-reachable` | OTLP relay shape; socat path; superseded by Provider OTLP/vsock in observability work | -| `SocatEndpoint::VsockConnect { cid, port }` | `packages/d2b-host/src/vsock_relay_argv.rs` | `implemented-and-reachable` | Raw CID in socat argv; pattern excluded from v3 ZoneLink `spec.provider.settings` | +| `SocatEndpoint::VsockConnect { cid, port }` | `packages/d2b-host/src/vsock_relay_argv.rs` | `implemented-and-reachable` | Raw CID in socat argv; pattern excluded from v3 ZoneLink `spec.transportSettings` | | vsock addr `vsock://-1:14318` | `packages/d2b-host/tests/guest_vsock_ttrpc_compile.rs` | `test-only-or-preview` | Guest-side ttrpc compile proof; cfg-gated; not a production path | | `vsock.rs` vsock transports | `packages/d2b-session-unix/src/vsock.rs` (v3 baseline) | `ADR-only` | Stub file; no implementation in v3 baseline; marked as Provider-specific | @@ -207,20 +207,18 @@ object, post-pass projected layered status, disposition `(UID,generation,revision,operationId)` and use the same per-resource single-flight priority lane. -**D089 desired-spec shape.** This transport Provider owns no ResourceType; core -reconciles the `ZoneLink` base `spec.*` fields, including `spec.providerRef`. -Vsock-specific desired input is carried only by the canonical -`ZoneLink.spec.provider = { schemaId, schemaVersion, settings }` envelope, whose -`settings` object mirrors `status.provider.details`, is registered/signed in the -Provider manifest, deny-unknown, bounded, versioned/digested, validated against -`spec.providerRef` at Nix build and API admission, and cannot shadow base -fields. Shared fields are promoted to the ResourceType base. The Provider -implements the exact base spec/status schema version/fingerprint, accepts the -canonical minimal base Spec, passes base conformance, and rejects an -unsupported optional base capability only through its signed capability matrix plus -provider-neutral `unsupported-capability`. -`spec.provider` aligns with `status.provider`. The `Provider` resource itself -keeps the D075 `spec.{artifactId, config}` exception. +**D089 desired-spec shape.** This transport Provider owns no ResourceType; child +core reconciles the exact canonical ZoneLink base fields: +`childZoneName`, `transportProviderRef`, `transportSettings`, +`transportCredentials`, `disabled`, and `limits`. Vsock-specific desired input +is carried only by `spec.transportSettings`, whose deny-unknown schema is +registered and signed by the Provider selected through +`spec.transportProviderRef`. Vsock uses an empty +`spec.transportCredentials` list. No desired-state provider envelope or schema +metadata appears in ZoneLink spec. +`status.provider` remains the D088 implementation-observation layer and does not +mirror desired spec. The `Provider` resource itself keeps the D075 +`spec.{artifactId, config}` exception. --- @@ -243,7 +241,7 @@ packages/d2b-provider-transport-vsock/ open_close.rs — OpenTransport / CloseTransport service round-trip (fake port) observe.rs — ObserveTransport event stream (fake port) redaction.rs — no CID / port / path in Debug / log / audit output - schema.rs — `spec.provider.settings` JSON Schema round-trip and rejection + schema.rs — `spec.transportSettings` JSON Schema round-trip and rejection state_volume.rs — state Volume spec shape, User/ layout principal, no ComponentPrincipal integration/ host_guest.rs — real vsock socketpair via injected effect; OpenTransport + byte round-trip @@ -260,7 +258,7 @@ The crate depends only on: - `d2b-provider` (`ProviderRegistry`, `AuthenticatedProviderRpc`) - `tracing` (structured spans; no log macros that could emit raw IDs) - `opentelemetry` (metric emission only; no OTEL SDK link) -- `serde` / `serde_json` (`spec.provider.settings` schema validation) +- `serde` / `serde_json` (`spec.transportSettings` schema validation) - `tokio` (async runtime; no `tokio-vsock` here) - Test-only: `d2b-provider-toolkit` (conformance kit) @@ -430,6 +428,11 @@ The Provider implements the following service on its ComponentSession with d2b-bus. All three methods are invoked exclusively by the child Zone's core Zone-link/delegation controller over a same-Zone service session. No other caller is authorized, and the parent allocator never invokes this Provider. +Before `OpenTransport`, child core resolves `spec.transportProviderRef`, +validates `spec.transportSettings`, requires `spec.transportCredentials = []`, +checks `spec.disabled`, enforces `spec.limits`, and derives the opaque +endpoint/binding IDs and deadline. The Provider receives only those derived +values, never the ZoneLink spec or a legacy provider envelope. ### `OpenTransport` @@ -554,11 +557,11 @@ defect and must be caught by the `redaction.rs` test. --- -## `spec.provider.settings` schema +## `spec.transportSettings` schema -The `settings` object inside the child-local `ZoneLink.spec.provider` carries +The child-local `ZoneLink.spec.transportSettings` object carries Provider-specific configuration when -`spec.providerRef = Provider/transport-vsock`. The child Zone's core resolves +`spec.transportProviderRef = Provider/transport-vsock`. The child Zone's core resolves the opaque endpoint and binding IDs from these settings plus its sealed selected-parent allocation; the Provider never receives the raw resolution. @@ -651,7 +654,7 @@ for this child-local Provider/ZoneLink. | INV-VSOCK-003 | No file descriptor is transferred over the vsock byte channel | `TransportDescriptor.attachment_support = false`; d2b-bus serialization boundary checks `descriptor()` before dispatch; `no_fd_transfer.rs` integration test | | INV-VSOCK-004 | Provider never calls `socket(AF_VSOCK, …)`, `connect`, or `bind` directly | `VsockEffectPort` is the only AF_VSOCK call surface; `cfg(target_os = "linux")` feature gate; all syscall paths in `LiveVsockEffectPort` only; seccomp strict profile on Provider process | | INV-VSOCK-005 | Provider never accesses or modifies ZoneLink, Guest, Route, or any other ResourceType | RBAC grants contain no resource-type verbs; Provider holds no resource API client; conformance test asserts no resource calls | -| INV-VSOCK-006 | `spec.provider.settings` schema rejects any field carrying a raw socket address, port number, CID, path, or credential | JSON Schema `additionalProperties: false`; build-time emitter secret-key scanner | +| INV-VSOCK-006 | `spec.transportSettings` schema rejects any field carrying a raw socket address, port number, CID, path, or credential | JSON Schema `additionalProperties: false`; build-time emitter secret-key scanner | | INV-VSOCK-007 | Provider process: no new privileges, strict seccomp, no network namespace join, read-only root filesystem | `sandbox.seccompClass: strict`, `sandbox.noNewPrivileges: true`, `sandbox.readOnlyRoot: true` in Process template; `Provider/system-minijail` profile | | INV-VSOCK-008 | Port range `14420–14499` is reserved for `d2b-link` ZoneLink vsock sessions; ports 14317, 14318, 14319 are never allocated by `portClass: "d2b-link"` | Core allocator exclusion list; enforced in core, not in Provider | | INV-VSOCK-009 | Service component receives only a dirfd into its local `service` view of its own state Volume; no raw filesystem path, no parent-directory access, no cross-component Volume mount | volume-local Provider validates `sensitivityClass: private` and `mountPath` scope before handing dirfd to process; domain isolation enforced at mount time | @@ -832,16 +835,20 @@ d2b.zones.k1.resources.transport-vsock = { d2b.zones.k1.resources.k1-uplink = { type = "ZoneLink"; spec = { - childZoneName = "k1"; - providerRef = "Provider/transport-vsock"; - provider = { - schemaId = "transport-vsock.d2bus.org/ZoneLink/spec"; - schemaVersion = "1.0"; - settings = { - guestRef = "Guest/k1-vm"; - portClass = "d2b-link"; - connectTimeoutSeconds = 30; - }; + childZoneName = "k1"; + transportProviderRef = "Provider/transport-vsock"; + transportSettings = { + guestRef = "Guest/k1-vm"; + portClass = "d2b-link"; + connectTimeoutSeconds = 30; + }; + transportCredentials = []; + disabled = false; + limits = { + maxPendingIntents = 256; + maxActiveStreams = 32; + reconnectMaxAttempts = 10; + reconnectWindowSecs = 300; }; }; }; @@ -858,14 +865,18 @@ metadata: zone: k1 spec: childZoneName: k1 - providerRef: Provider/transport-vsock - provider: - schemaId: transport-vsock.d2bus.org/ZoneLink/spec - schemaVersion: "1.0" - settings: - guestRef: Guest/k1-vm - portClass: d2b-link - connectTimeoutSeconds: 30 + transportProviderRef: Provider/transport-vsock + transportSettings: + guestRef: Guest/k1-vm + portClass: d2b-link + connectTimeoutSeconds: 30 + transportCredentials: [] + disabled: false + limits: + maxPendingIntents: 256 + maxActiveStreams: 32 + reconnectMaxAttempts: 10 + reconnectWindowSecs: 300 ``` local-root's resource bundle contains no reciprocal ZoneLink or selected Provider. @@ -875,7 +886,8 @@ resources in the same child Zone; referential existence is verified at bundle activation time. The compiler separately seals `k1.parentZone = "local-root"` into allocator bootstrap state and emits no parent-store reciprocal resource. The Nix -module also validates `spec.provider.settings` against the +module also resolves `spec.transportProviderRef` and validates +`spec.transportSettings` against the `transport-vsock.transport-binding.json` schema and rejects any prohibited field (`cid`, `port`, `socketPath`, etc.). Validation is performed by the `d2b.zones..resources` schema checker, not by the Provider's own Nix @@ -897,7 +909,7 @@ expression. | `vsock.rs` v3 stub | `d2b-session-unix/src/vsock.rs` (v3 baseline) | `ADR-only` | Delta: stub becomes `FramedVsockTransport` implementation; entry point is `VsockEffectPort` not raw syscall | | Framing tests | `d2b-session-unix/tests/unix_session.rs` vsock subset | `implemented-but-unwired` | Retained in `tests/framing.rs` (Provider crate): `vsock_framing_handles_partial_and_coalesced_records`, `vsock_frame_too_large_rejects_before_allocating`, `vsock_clean_eof_versus_reset_are_distinct` | | CID mismatch enforcement | `vsock_cid_mismatch_closes_without_processing` | `implemented-but-unwired` | Adapted in `tests/effect_port_mock.rs` as `OpaqueEndpointId` mismatch case; raw CID variant moves to core adapter tests | -| ZoneLink resource | `ADR-046-zone-routing.md` | `generated-or-eval-contract` | Retained: child-local ZoneLink base spec shape and `spec.provider.settings`; child core owns local status/routes/finalizer; selected parent retains only sealed allocator/route state with no reciprocal row | +| ZoneLink resource | `ADR-046-zone-routing.md` | `generated-or-eval-contract` | Retained: exact child-local ZoneLink base spec with `transportProviderRef`, `transportSettings`, empty `transportCredentials`, `disabled`, and `limits`; child core owns local status/routes/finalizer; selected parent retains only sealed allocator/route state with no reciprocal row | | ComponentSession / Noise | `ADR-046-componentsession-and-bus.md` | `generated-or-eval-contract` | Retained: owned by d2b-bus; Provider is opaque carriage only | --- @@ -938,10 +950,10 @@ expression. | Current source | Evidence class `test-only-or-preview`; no current v3 generic `VsockTransportService` implementation exists. | | Reuse action | adapt | | Destination | `packages/d2b-provider-transport-vsock/src/service.rs`; tests `tests/open_close.rs`, `tests/observe.rs`, and conformance kit. | -| Detailed design | Implement `VsockTransportService` (OpenTransport/CloseTransport/ObserveTransport). Implement all three service methods in `service.rs`; `open_close.rs` and `observe.rs` test full service API against `FakeVsockEffectPort`; conformance kit passes. Primary reuse disposition: `adapt`. Preserved source-plan detail: net-new service implementation over ComponentSession and fake effect port tests. | +| Detailed design | Implement `VsockTransportService` (OpenTransport/CloseTransport/ObserveTransport). Child core resolves the exact six-field ZoneLink spec, validates `transportSettings` through the Provider selected by `transportProviderRef`, rejects non-empty `transportCredentials`, and passes only derived opaque IDs/deadline to `service.rs`; `open_close.rs`, `observe.rs`, and topology tests cover the service and reject legacy ZoneLink provider/fingerprint/capability fields; conformance kit passes. Primary reuse disposition: `adapt`. Preserved source-plan detail: net-new service implementation over ComponentSession and fake effect port tests. | | Integration | Child Zone's core ZoneLink/delegation controller is the only authorized caller; service opens named stream handles for child d2b-bus, releases them on close, and streams transport events for observe; no parent-side Provider or handler exists. | | Data migration | Full d2b 3.0 reset; no v2 state/config import. | -| Validation | Proof type: service round-trip test (mock); `tests/open_close.rs`, `tests/observe.rs`, and provider conformance tests. | +| Validation | Proof type: service round-trip plus exact-shape tests; `tests/open_close.rs`, `tests/observe.rs`, `tests/topology.rs::{canonical_zonelink_spec_fields_are_exact,legacy_zonelink_provider_fields_are_rejected,transport_credentials_must_be_empty}`, and provider conformance tests. | | Removal proof | None — net-new; no prior owner to remove. | ### ADR046-vsock-004 @@ -977,7 +989,7 @@ expression. | Current source | Evidence class `test-only-or-preview`; existing guest-control compile proof and socat relay tests are not full ZoneLink transport coverage. | | Reuse action | create | | Destination | `packages/d2b-provider-transport-vsock/integration/host_guest.rs` and `integration/no_fd_transfer.rs`. | -| Detailed design | Integration test: fixture declares compiler-only `k1.parentZone = "local-root"`, puts the ZoneLink and selected Provider only in K1, and gives local-root only sealed allocator/route state; `integration/host_guest.rs` opens a real Linux vsock path through K1's `LiveVsockEffectPort`, then exercises `OpenTransport` + byte round-trip + `CloseTransport` and validates bridge throughput ≥ 512 MiB/s; `no_fd_transfer.rs` structurally rejects attachment packets and asserts no FD/ResourceRef crosses the Zone boundary. Primary reuse disposition: `create`. Preserved source-plan detail: net-new integration coverage with no FD transfer over vsock. | +| Detailed design | Integration test: fixture declares compiler-only `k1.parentZone = "local-root"`, puts the selected Provider and an exact six-field ZoneLink (`transportProviderRef`, validated `transportSettings`, empty `transportCredentials`, `disabled`, and `limits`, plus self-matching `childZoneName`) only in K1, and gives local-root only sealed allocator/route state; `integration/host_guest.rs` opens a real Linux vsock path through K1's `LiveVsockEffectPort`, then exercises `OpenTransport` + byte round-trip + `CloseTransport` and validates bridge throughput ≥ 512 MiB/s; `no_fd_transfer.rs` structurally rejects attachment packets and asserts no FD/ResourceRef crosses the Zone boundary. Primary reuse disposition: `create`. Preserved source-plan detail: net-new integration coverage with no FD transfer over vsock. | | Integration | Test drives K1-local Provider service, child `LiveVsockEffectPort`, d2b-bus `OwnedTransport`, byte bridge, close path, absent local-root reciprocal resource row, and attachment rejection across the integration lane. | | Data migration | None — docs/tooling only; no runtime state. | | Validation | Proof type: integration test; `make test-integration` runs `host_guest.rs` and `no_fd_transfer.rs`. | @@ -1012,6 +1024,9 @@ expression. | `no_raw_port_in_debug_or_display` | `tests/redaction.rs` | unit | `cargo test` | | `transport_settings_schema_rejects_cid_field` | `tests/schema.rs` | unit | `cargo test` | | `transport_settings_schema_rejects_port_field` | `tests/schema.rs` | unit | `cargo test` | +| `canonical_zonelink_spec_fields_are_exact` | `tests/topology.rs` | unit | `cargo test` | +| `legacy_zonelink_provider_fields_are_rejected` | `tests/topology.rs` | unit | `cargo test` | +| `transport_credentials_must_be_empty` | `tests/topology.rs` | unit | `cargo test` | | `provider_and_zonelink_are_child_local` | `tests/topology.rs` | unit | `cargo test` | | `child_zone_name_self_matches_and_parent_is_compiler_only` | `tests/topology.rs` | unit | `cargo test` | | `parent_store_has_no_reciprocal_resources` | `tests/topology.rs` | unit | `cargo test` | @@ -1044,7 +1059,8 @@ budget. `Provider/transport-vsock` (and its crate) may not be removed while: 1. Any child-local `ZoneLink` resource with - `spec.providerRef: Provider/transport-vsock` exists in its owning child Zone. + `spec.transportProviderRef: Provider/transport-vsock` exists in its owning + child Zone. 2. Any `d2b-link` port-class vsock session is active on any Zone runtime. 3. The state Volume (`Volume/transport-vsock--service--empty-state--*`) has not been deleted and its identity marker has not been cleared. @@ -1090,7 +1106,8 @@ Old and new suites never run in parallel indefinitely. byte-stream handles in process memory. - `Provider.spec.config.executionRef`: required `Host/` or `Guest/`; one service Process per Provider instance, not per ZoneLink. -- `spec.provider.settings`: `guestRef` / `portClass` fields; what is forbidden. +- `spec.transportSettings`: `guestRef` / `portClass` fields and forbidden raw + endpoint values; `spec.transportCredentials` must be empty. - Port range `14420–14499` reservation; ports 14317/14318/14319 excluded. - `VsockEffectPort` injection: Provider never calls AF_VSOCK syscalls directly; `tokio-vsock` is NOT a Provider crate dependency. From c6f5386b2e9d6ffb688e0aade83ac4c7b14ce9f2 Mon Sep 17 00:00:00 2001 From: John Vicondoa Date: Fri, 24 Jul 2026 08:54:46 -0700 Subject: [PATCH 099/115] docs: integrate ADR 0046 third-panel corrections --- docs/specs/ADR-046-implementation-graph.json | 438 +++++++++++++----- docs/specs/ADR-046-implementation-graph.md | 2 +- docs/specs/ADR-046-reset-and-cutover.md | 6 +- docs/specs/ADR-046-resources-device.md | 19 +- docs/specs/ADR-046-resources-zone-control.md | 14 +- docs/specs/ADR-046-spec-set.json | 70 ++- docs/specs/ADR-046-work-items.json | 342 +++++++------- .../ADR-046-provider-audio-pipewire.md | 2 +- .../providers/ADR-046-provider-device-gpu.md | 2 +- .../ADR-046-provider-transport-unix.md | 4 +- 10 files changed, 568 insertions(+), 331 deletions(-) diff --git a/docs/specs/ADR-046-implementation-graph.json b/docs/specs/ADR-046-implementation-graph.json index 7488769b7..07581c677 100644 --- a/docs/specs/ADR-046-implementation-graph.json +++ b/docs/specs/ADR-046-implementation-graph.json @@ -2,7 +2,7 @@ "adr" : "0046", "artifactKind" : "d2b-adr-implementation-graph", "counts" : { - "edges" : 1908, + "edges" : 1938, "maxTopologicalRank" : 22, "nodes" : 598, "specNodes" : 55, @@ -3777,11 +3777,6 @@ "to" : "ADR-046-telemetry-audit-and-support", "type" : "spec-depends-on" }, - { - "from" : "ADR-046-provider-runtime-azure-container-apps", - "to" : "ADR-046-zone-routing", - "type" : "spec-depends-on" - }, { "from" : "ADR-046-provider-runtime-azure-virtual-machine", "to" : "ADR-046-components-processes-and-sandbox", @@ -5187,16 +5182,151 @@ "to" : "ADR-046-primitive-resource-composition", "type" : "spec-depends-on" }, + { + "from" : "ADR-046-validation-and-delivery", + "to" : "ADR-046-provider-activation-nixos", + "type" : "spec-depends-on" + }, + { + "from" : "ADR-046-validation-and-delivery", + "to" : "ADR-046-provider-audio-pipewire", + "type" : "spec-depends-on" + }, + { + "from" : "ADR-046-validation-and-delivery", + "to" : "ADR-046-provider-clipboard-wayland", + "type" : "spec-depends-on" + }, + { + "from" : "ADR-046-validation-and-delivery", + "to" : "ADR-046-provider-credential-entra", + "type" : "spec-depends-on" + }, + { + "from" : "ADR-046-validation-and-delivery", + "to" : "ADR-046-provider-credential-managed-identity", + "type" : "spec-depends-on" + }, + { + "from" : "ADR-046-validation-and-delivery", + "to" : "ADR-046-provider-credential-secret-service", + "type" : "spec-depends-on" + }, + { + "from" : "ADR-046-validation-and-delivery", + "to" : "ADR-046-provider-device-gpu", + "type" : "spec-depends-on" + }, + { + "from" : "ADR-046-validation-and-delivery", + "to" : "ADR-046-provider-device-security-key", + "type" : "spec-depends-on" + }, + { + "from" : "ADR-046-validation-and-delivery", + "to" : "ADR-046-provider-device-tpm", + "type" : "spec-depends-on" + }, + { + "from" : "ADR-046-validation-and-delivery", + "to" : "ADR-046-provider-device-usbip", + "type" : "spec-depends-on" + }, + { + "from" : "ADR-046-validation-and-delivery", + "to" : "ADR-046-provider-display-wayland", + "type" : "spec-depends-on" + }, { "from" : "ADR-046-validation-and-delivery", "to" : "ADR-046-provider-model-and-packaging", "type" : "spec-depends-on" }, + { + "from" : "ADR-046-validation-and-delivery", + "to" : "ADR-046-provider-network-local", + "type" : "spec-depends-on" + }, + { + "from" : "ADR-046-validation-and-delivery", + "to" : "ADR-046-provider-notification-desktop", + "type" : "spec-depends-on" + }, + { + "from" : "ADR-046-validation-and-delivery", + "to" : "ADR-046-provider-observability-otel", + "type" : "spec-depends-on" + }, + { + "from" : "ADR-046-validation-and-delivery", + "to" : "ADR-046-provider-runtime-azure-container-apps", + "type" : "spec-depends-on" + }, + { + "from" : "ADR-046-validation-and-delivery", + "to" : "ADR-046-provider-runtime-azure-virtual-machine", + "type" : "spec-depends-on" + }, + { + "from" : "ADR-046-validation-and-delivery", + "to" : "ADR-046-provider-runtime-cloud-hypervisor", + "type" : "spec-depends-on" + }, + { + "from" : "ADR-046-validation-and-delivery", + "to" : "ADR-046-provider-runtime-qemu-media", + "type" : "spec-depends-on" + }, + { + "from" : "ADR-046-validation-and-delivery", + "to" : "ADR-046-provider-shell-terminal", + "type" : "spec-depends-on" + }, { "from" : "ADR-046-validation-and-delivery", "to" : "ADR-046-provider-state", "type" : "spec-depends-on" }, + { + "from" : "ADR-046-validation-and-delivery", + "to" : "ADR-046-provider-system-core", + "type" : "spec-depends-on" + }, + { + "from" : "ADR-046-validation-and-delivery", + "to" : "ADR-046-provider-system-minijail", + "type" : "spec-depends-on" + }, + { + "from" : "ADR-046-validation-and-delivery", + "to" : "ADR-046-provider-system-systemd", + "type" : "spec-depends-on" + }, + { + "from" : "ADR-046-validation-and-delivery", + "to" : "ADR-046-provider-transport-azure-relay", + "type" : "spec-depends-on" + }, + { + "from" : "ADR-046-validation-and-delivery", + "to" : "ADR-046-provider-transport-unix", + "type" : "spec-depends-on" + }, + { + "from" : "ADR-046-validation-and-delivery", + "to" : "ADR-046-provider-transport-vsock", + "type" : "spec-depends-on" + }, + { + "from" : "ADR-046-validation-and-delivery", + "to" : "ADR-046-provider-volume-local", + "type" : "spec-depends-on" + }, + { + "from" : "ADR-046-validation-and-delivery", + "to" : "ADR-046-provider-volume-virtiofs", + "type" : "spec-depends-on" + }, { "from" : "ADR-046-validation-and-delivery", "to" : "ADR-046-reset-and-cutover", @@ -5627,6 +5757,16 @@ "to" : "ADR046-cli-001", "type" : "work-item-depends-on" }, + { + "from" : "ADR046-cli-006", + "to" : "ADR046-routing-012", + "type" : "work-item-depends-on" + }, + { + "from" : "ADR046-cli-006", + "to" : "ADR046-routing-016", + "type" : "work-item-depends-on" + }, { "from" : "ADR046-cli-007", "to" : "ADR046-cli-001", @@ -7794,7 +7934,12 @@ }, { "from" : "ADR046-routing-016", - "to" : "ADR046-routing-001", + "to" : "ADR046-routing-002", + "type" : "work-item-depends-on" + }, + { + "from" : "ADR046-routing-016", + "to" : "ADR046-routing-004", "type" : "work-item-depends-on" }, { @@ -7802,6 +7947,11 @@ "to" : "ADR046-routing-007", "type" : "work-item-depends-on" }, + { + "from" : "ADR046-routing-016", + "to" : "ADR046-routing-012", + "type" : "work-item-depends-on" + }, { "from" : "ADR046-security-001", "to" : "ADR046-telem-008", @@ -10369,8 +10519,7 @@ "ADR-046-provider-state", "ADR-046-resources-credential", "ADR-046-resources-host-guest-process-user", - "ADR-046-telemetry-audit-and-support", - "ADR-046-zone-routing" + "ADR-046-telemetry-audit-and-support" ], "exitGate" : "ADR046-W6 exit criteria (ADR-046-validation-and-delivery §4): every spec/work item in this wave Merged with clean destinations, all validators green, and the ten-role panel seal recorded", "id" : "ADR-046-provider-runtime-azure-container-apps", @@ -10384,8 +10533,7 @@ "ADR-046-provider-state", "ADR-046-resources-credential", "ADR-046-resources-host-guest-process-user", - "ADR-046-telemetry-audit-and-support", - "ADR-046-zone-routing" + "ADR-046-telemetry-audit-and-support" ], "specId" : "ADR-046-provider-runtime-azure-container-apps", "topologicalRank" : 10, @@ -11454,8 +11602,35 @@ "ADR-046-feasibility-and-spikes", "ADR-046-nix-configuration", "ADR-046-primitive-resource-composition", + "ADR-046-provider-activation-nixos", + "ADR-046-provider-audio-pipewire", + "ADR-046-provider-clipboard-wayland", + "ADR-046-provider-credential-entra", + "ADR-046-provider-credential-managed-identity", + "ADR-046-provider-credential-secret-service", + "ADR-046-provider-device-gpu", + "ADR-046-provider-device-security-key", + "ADR-046-provider-device-tpm", + "ADR-046-provider-device-usbip", + "ADR-046-provider-display-wayland", "ADR-046-provider-model-and-packaging", + "ADR-046-provider-network-local", + "ADR-046-provider-notification-desktop", + "ADR-046-provider-observability-otel", + "ADR-046-provider-runtime-azure-container-apps", + "ADR-046-provider-runtime-azure-virtual-machine", + "ADR-046-provider-runtime-cloud-hypervisor", + "ADR-046-provider-runtime-qemu-media", + "ADR-046-provider-shell-terminal", "ADR-046-provider-state", + "ADR-046-provider-system-core", + "ADR-046-provider-system-minijail", + "ADR-046-provider-system-systemd", + "ADR-046-provider-transport-azure-relay", + "ADR-046-provider-transport-unix", + "ADR-046-provider-transport-vsock", + "ADR-046-provider-volume-local", + "ADR-046-provider-volume-virtiofs", "ADR-046-reset-and-cutover", "ADR-046-resource-api-and-authorization", "ADR-046-resource-object-model", @@ -11488,8 +11663,35 @@ "ADR-046-feasibility-and-spikes", "ADR-046-nix-configuration", "ADR-046-primitive-resource-composition", + "ADR-046-provider-activation-nixos", + "ADR-046-provider-audio-pipewire", + "ADR-046-provider-clipboard-wayland", + "ADR-046-provider-credential-entra", + "ADR-046-provider-credential-managed-identity", + "ADR-046-provider-credential-secret-service", + "ADR-046-provider-device-gpu", + "ADR-046-provider-device-security-key", + "ADR-046-provider-device-tpm", + "ADR-046-provider-device-usbip", + "ADR-046-provider-display-wayland", "ADR-046-provider-model-and-packaging", + "ADR-046-provider-network-local", + "ADR-046-provider-notification-desktop", + "ADR-046-provider-observability-otel", + "ADR-046-provider-runtime-azure-container-apps", + "ADR-046-provider-runtime-azure-virtual-machine", + "ADR-046-provider-runtime-cloud-hypervisor", + "ADR-046-provider-runtime-qemu-media", + "ADR-046-provider-shell-terminal", "ADR-046-provider-state", + "ADR-046-provider-system-core", + "ADR-046-provider-system-minijail", + "ADR-046-provider-system-systemd", + "ADR-046-provider-transport-azure-relay", + "ADR-046-provider-transport-unix", + "ADR-046-provider-transport-vsock", + "ADR-046-provider-volume-local", + "ADR-046-provider-volume-virtiofs", "ADR-046-reset-and-cutover", "ADR-046-resource-api-and-authorization", "ADR-046-resource-object-model", @@ -11544,7 +11746,7 @@ "destinations" : [ "`packages/d2b-provider-runtime-azure-container-apps/src/controller.rs`" ], - "detailedDesign" : "Async `Guest` reconcile loop: `describe` → `validateSpec` → `plan` → `reconcile` → `observe` → `finalize`. Adoption before first `RuntimeEnsure`. Operation ledger persisted to Volume inside gateway Guest. Credential lease acquire/revoke per call. Noise KK enrollment via ZoneLink (from gateway Guest to managed ACA sandbox). `providerPhase` and `guestIdentityDigest` in `status.provider.details`; no raw endpoint/path in any status field. **ProviderDeployment creates both static Processes; ACA controller never instantiates its own Processes and never writes Provider status directly. All Processes run inside the gateway Guest. No Host Process, no Host Credential, no Host Azure HTTP socket. Long-running cloud ops return `progressing`/`requeue-at` immediately; never block watch loop.** Primary reuse disposition: `replace`. Preserved source-plan detail: REPLACE (old) + ADAPT (main types/traits).", + "detailedDesign" : "Async `Guest` reconcile loop: `describe` → `validateSpec` → `plan` → `reconcile` → `observe` → `finalize`. Adoption before first `RuntimeEnsure`; operation/requeue truth remains in the core Operation ledger and no Provider state Volume is created. Credential lease acquire/revoke per call. The controller creates a same-Zone Provider-owned semantic sandbox-agent Endpoint (`ownerRef` remains the Guest lifecycle edge), resolves opaque transport carriage, and performs Noise KK enrollment for the ACA Provider service/session. `providerPhase` and `guestIdentityDigest` stay in `status.provider.details`; Endpoint readiness/generation/availability stay only in Endpoint status; no raw endpoint/path, cross-Zone ref, route cursor, transport handle, or authority appears in status. **ProviderDeployment creates both static Processes; ACA controller never instantiates its own Processes and never writes Provider status directly. All Processes run inside the gateway Guest. The managed sandbox remains a Guest in the owning Zone and is not a Zone. No Host Process, no Host Credential, no Host Azure HTTP socket. Long-running cloud ops return `progressing`/`requeue-at` immediately; never block watch loop.** Primary reuse disposition: `replace`. Preserved source-plan detail: REPLACE (old) + ADAPT (main types/traits).", "entryContracts" : [ "ADR-046-provider-runtime-azure-container-apps" ], @@ -11559,7 +11761,7 @@ ], "specId" : "ADR-046-provider-runtime-azure-container-apps", "topologicalRank" : 11, - "validation" : "Controller conformance suite; adoption/ambiguity tests; deadline/cancellation matrix; redaction coverage; **gateway Guest placement validation: assert no Process has `executionRef: Host/*`**; Process spec field schema tests (`spec.template`, canonical `sandbox`/`budget`/`networkUsage`/`endpoints`/`readiness`/`restartPolicy` fields, `mounts` with `required: true`, `providerRef: Provider/system-minijail`); ProviderDeployment creates both Processes (controller never self-spawns); no raw endpoint/path in Guest status", + "validation" : "Controller conformance suite; adoption/ambiguity tests; Endpoint create/adopt/finalize and generation tests; deadline/cancellation/backpressure matrix; redaction coverage; **gateway Guest placement validation: assert no Process has `executionRef: Host/*`**; Process spec field schema tests (`spec.template`, canonical `sandbox`/`budget`/`networkUsage`/`endpoints`/`readiness`/`restartPolicy` fields, `mounts` with `required: true`, `providerRef: Provider/system-minijail`); ProviderDeployment creates both Processes (controller never self-spawns); no raw endpoint/path or ZoneLink status/cursor/authority in Guest or Endpoint status", "wave" : "W6" }, { @@ -11611,16 +11813,16 @@ { "blockers" : [], "destinations" : [ - "ZoneLink resource `spec.transportSettings` (§15.4); `Provider/transport-azure-relay` dossier (separate)" + "ACA sandbox-agent Endpoint/session controller (§§7, 12); `Provider/transport-azure-relay` private configuration and carriage dossier (separate)" ], - "detailedDesign" : "`AcaRelayTransportConfig` fields mapped to `transportSettings.relayNamespace`, `.relayHybridConnection`, `.relayCredentialRef`. KK enrollment replaces ZoneLink transport. Primary reuse disposition: `replace`. Preserved source-plan detail: REPLACE (both); ADAPT config fields to ZoneLink transport settings.", + "detailedDesign" : "Split `AcaRelayTransportConfig`: relay-private namespace/connection/credential fields move behind `Provider/transport-azure-relay`; this Provider retains only `sandboxTransportAlias`. The ACA controller creates the Provider-owned semantic Endpoint (with the Guest lifecycle edge), resolves an opaque `OwnedTransport`, and owns KK enrollment, authenticated `d2b.aca.v3.sandbox-agent` service/session generation, deadlines, cancel, bounded queues, credits/backpressure, reconcile, adoption, and finalization. The ordinary managed sandbox is explicitly not a Zone; no cross-Zone ResourceRef or ZoneLink status/cursor/authority is accepted or emitted. Primary reuse disposition: `replace`. Preserved source-plan detail: REPLACE (both); ADAPT relay fields only into the carriage Provider's private config.", "entryContracts" : [ "ADR-046-provider-runtime-azure-container-apps" ], "exitGate" : "ADR046-W6 exit criteria (ADR-046-validation-and-delivery §4): every spec/work item in this wave Merged with clean destinations, all validators green, and the ten-role panel seal recorded", "id" : "ADR046-aca-004", "kind" : "work-item", - "owner" : "ADR046-session-001; ZoneLink transport owner", + "owner" : "ADR046-session-001; ACA Endpoint/session and transport-capability integration owner", "parallelGroup" : "wi:ADR-046-provider-runtime-azure-container-apps", "prerequisites" : [ "ADR-046-provider-runtime-azure-container-apps", @@ -11628,7 +11830,7 @@ ], "specId" : "ADR-046-provider-runtime-azure-container-apps", "topologicalRank" : 11, - "validation" : "ZoneLink enrollment tests; relay unavailability tests; KK re-enrollment after sandbox restart", + "validation" : "Endpoint ownership/resolution tests; relay unavailability tests; KK re-enrollment after sandbox/controller restart; deadline/cancel/credit-backpressure tests; schema/status tests reject ZoneLink refs, phase, cursors, and authority", "wave" : "W6" }, { @@ -11636,7 +11838,7 @@ "destinations" : [ "`packages/d2b-provider-runtime-azure-container-apps/src/types.rs`" ], - "detailedDesign" : "Replace `RealmId`/`WorkloadId` with v3 `Zone`/`Guest` UID types. `AcaResourceBinding` keys the adoption query. The provider declares **no** Provider state Volume: bounded, non-secret sandbox binding/adoption metadata lives in `Guest.status` (latest bounded observed handle digests) and in-flight operation/requeue truth lives in the core Operation ledger (D087). Neither Process mounts a state Volume; there is no `sandbox-state`/`service-state` Volume, no `User/d2b-aca-controller`/`User/d2b-aca-deployment-service` state-layout principal, and no empty identity-only Volume. On restart the controller re-derives observed binding from `Guest.status`, the core Operation ledger, and an external `find_sandboxes` query, treating status as observation, never authority. Host never holds cloud binding, admission, PSK, or operation state. Primary reuse disposition: `adapt`. Preserved source-plan detail: RETAIN+ADAPT.", + "detailedDesign" : "Replace `RealmId`/`WorkloadId` with v3 owning-`Zone`/`Guest` UID types. `AcaResourceBinding` keys the adoption query but never names a child Zone. The provider declares **no** Provider state Volume: bounded, non-secret sandbox binding/adoption metadata lives in `Guest.status` (latest bounded observed handle digests) and in-flight operation/requeue truth lives in the core Operation ledger (D087). Neither Process mounts a state Volume; there is no `sandbox-state`/`service-state` Volume, no `User/d2b-aca-controller`/`User/d2b-aca-deployment-service` state-layout principal, and no empty identity-only Volume. On restart the controller re-derives observed binding from `Guest.status`, the core Operation ledger, and an external `find_sandboxes` query, ensures the Provider-owned Endpoint, resolves fresh transport carriage, and authenticates a fresh Provider session, treating all status as observation and never authority. Host never holds cloud binding, admission, PSK, operation, Endpoint, or session state. Primary reuse disposition: `adapt`. Preserved source-plan detail: RETAIN+ADAPT.", "entryContracts" : [ "ADR-046-provider-runtime-azure-container-apps" ], @@ -11651,7 +11853,7 @@ ], "specId" : "ADR-046-provider-runtime-azure-container-apps", "topologicalRank" : 12, - "validation" : "Controller/service declare empty `stateNamespaces`; no `sandbox-state`/`service-state` Volume created; neither Process mounts a state Volume; `Guest.status` binding/adoption fields are bounded, non-secret, and carry no credential/endpoint/poll-URL bytes; restart re-derivation from status/core ledger/external `find_sandboxes` without a Volume; core Operation ledger adapter integration test", + "validation" : "Controller/service declare empty `stateNamespaces`; no `sandbox-state`/`service-state` Volume created; neither Process mounts a state Volume; `Guest.status` binding/adoption fields and standard Endpoint status are bounded, non-secret, and carry no credential/endpoint/poll-URL/ZoneLink cursor or authority bytes; restart re-derivation from status/core ledger/external `find_sandboxes`, Endpoint ensure, and fresh KK session without a Volume; core Operation ledger adapter integration test", "wave" : "W6" }, { @@ -11659,7 +11861,7 @@ "destinations" : [ "`nixos-modules/` (generated Guest resource options); `packages/d2b-provider-runtime-azure-container-apps/src/{audit,metrics}.rs`" ], - "detailedDesign" : "Eval-time assertions for ACA-specific invariants (§15.6). Closed OTEL label set (§13.4). Audit event schema (§13.3). Tracing target constant `d2b_provider_runtime_azure_container_apps::credential_lease_cleanup` retained. Primary reuse disposition: `replace`. Preserved source-plan detail: REPLACE (Nix emitter) + ADAPT (metric/audit shapes).", + "detailedDesign" : "Eval-time assertions for ACA-specific invariants (§15.7), including rejection of ZoneLink/child-Zone fields for an ordinary sandbox and exact same-Zone sandbox-agent Endpoint template ownership. Closed OTEL label set (§13.4). Audit event schema (§13.3). Tracing target constant `d2b_provider_runtime_azure_container_apps::credential_lease_cleanup` retained. Primary reuse disposition: `replace`. Preserved source-plan detail: REPLACE (Nix emitter) + ADAPT (metric/audit shapes).", "entryContracts" : [ "ADR-046-provider-runtime-azure-container-apps" ], @@ -11674,7 +11876,7 @@ ], "specId" : "ADR-046-provider-runtime-azure-container-apps", "topologicalRank" : 12, - "validation" : "Label cardinality policy test; audit commit-before-complete test; Nix assertion eval tests", + "validation" : "Label cardinality policy test; audit commit-before-complete test; Nix assertion eval tests; generated resource scan proves no ACA sandbox ZoneLink is emitted", "wave" : "W6" }, { @@ -11682,7 +11884,7 @@ "destinations" : [ "`nixos-modules/` (gateway Guest declaration, Process template wiring, Credential scope assertion); eval-time validation module" ], - "detailedDesign" : "Nix eval-time assertions for: (a) `gatewayExecutionRef` resolves to a `Guest` resource, not `Host/*`; (b) Credential `scope.executionRef` matches `gatewayExecutionRef`; (c) all Process templates emitted for this Provider have `executionRef` equal to `gatewayExecutionRef`. No `User` resource or `users.users.*` declarations required — component principals are framework-assigned and not OS accounts. Gateway Guest NixOS closure includes only the two ACA component binaries (§15.6). Assertion error messages name the offending resource and the required `gatewayExecutionRef`.", + "detailedDesign" : "Nix eval-time assertions for: (a) `gatewayExecutionRef` resolves to a `Guest` resource, not `Host/*`; (b) Credential `scope.executionRef` matches `gatewayExecutionRef`; (c) all Process templates emitted for this Provider have `executionRef` equal to `gatewayExecutionRef`; (d) `sandboxTransportAlias` resolves the signed carriage capability; and (e) an ordinary ACA sandbox cannot declare/reference ZoneLink, child-Zone, route-cursor, or ZoneLink-authority fields. The controller, not Nix, creates each same-Zone Provider-owned sandbox-agent Endpoint with the Guest as lifecycle `ownerRef`. No `User` resource or `users.users.*` declarations required — component principals are framework-assigned and not OS accounts. Gateway Guest NixOS closure includes only the two ACA component binaries (§15.6). Assertion error messages name the offending resource and the required `gatewayExecutionRef`; Zone-shaped ACA config fails `aca-sandbox-is-not-zone`.", "entryContracts" : [ "ADR-046-provider-runtime-azure-container-apps" ], @@ -11697,7 +11899,7 @@ ], "specId" : "ADR-046-provider-runtime-azure-container-apps", "topologicalRank" : 12, - "validation" : "Nix eval assertion tests (wrong `executionRef` → assertion fires; correct setup → passes); §15.7 assertion coverage tests", + "validation" : "Nix eval assertion tests (wrong `executionRef` → assertion fires; correct setup → passes; ACA ZoneLink/child-Zone fields → `aca-sandbox-is-not-zone`); Endpoint template ownership/shape and §15.7 assertion coverage tests", "wave" : "W6" }, { @@ -12825,14 +13027,16 @@ "exitGate" : "ADR046-W5 exit criteria (ADR-046-validation-and-delivery §4): every spec/work item in this wave Merged with clean destinations, all validators green, and the ten-role panel seal recorded", "id" : "ADR046-cli-006", "kind" : "work-item", - "owner" : "ADR046-cli-001; CLI crate owner", + "owner" : "ADR046-cli-001, ADR046-routing-012, ADR046-routing-016; CLI crate owner", "parallelGroup" : "wi:ADR-046-cli-and-operations", "prerequisites" : [ "ADR-046-cli-and-operations", - "ADR046-cli-001" + "ADR046-cli-001", + "ADR046-routing-012", + "ADR046-routing-016" ], "specId" : "ADR-046-cli-and-operations", - "topologicalRank" : 11, + "topologicalRank" : 12, "validation" : "Completion script tests (bash/zsh/fish syntax valid); projection injection safety tests; deadline/partial-Provider tests", "wave" : "W5" }, @@ -12887,7 +13091,7 @@ "destinations" : [ "`packages/d2b/src/zone.rs` (`d2b zone get/list/status`)" ], - "detailedDesign" : "`d2b zone get []` fetches Zone self resource via `ConnectedClient::invoke`; `d2b zone list` lists ZoneLink resources. v2 commands (`d2b realm list/inspect/enter/run`) are deleted at 3.0; no dispatch wiring. Excluded ADR 0045: `RealmServiceServer`/`RealmServiceProcess` multi-realm service; `RemoteNodeRegistration` constellation routing; `TargetInput::Realm`; `RealmMethod::ResolveRoute`/`AuthorizeShortcut`/`RevokeShortcut`.", + "detailedDesign" : "`d2b zone get []` fetches the current Zone self resource only for the local name; another declared name is inspected through the read-only Zone topology projection. `d2b zone list` lists sorted compiler-only `{ childZone, parentZone }` rows joined with authenticated route/projection status. `d2b zone status --watch` watches the corresponding projection revision. None of these commands lists or gets a parent-local ZoneLink: the sole ZoneLink resource and handler are child-local, while the parent owns only sealed allocator topology and authenticated route state. Generated DTO/golden tests reject ZoneLink names/spec/status/provider refs/fingerprints/transport settings in parent-side output. v2 commands (`d2b realm list/inspect/enter/run`) are deleted at 3.0; no dispatch wiring. Excluded ADR 0045: `RealmServiceServer`/`RealmServiceProcess` multi-realm service; `RemoteNodeRegistration` constellation routing; `TargetInput::Realm`; `RealmMethod::ResolveRoute`/`AuthorizeShortcut`/`RevokeShortcut`.", "entryContracts" : [ "ADR-046-cli-and-operations" ], @@ -12902,7 +13106,7 @@ ], "specId" : "ADR-046-cli-and-operations", "topologicalRank" : 11, - "validation" : "Zone get/list tests; confirm v2 `cmd_realm_*` paths are absent", + "validation" : "Zone self-get plus topology list/get/status/watch tests; disconnected and stale authenticated-route projections; golden output contains `{ childZone, parentZone }` and route/projection status but no ZoneLink fields; parent store has no ZoneLink row or watch; confirm v2 `cmd_realm_*` paths are absent", "wave" : "W5" }, { @@ -15916,9 +16120,9 @@ { "blockers" : [], "destinations" : [ - "`packages/d2b-contracts/src/v3/network.rs`: NetworkSpec, NetworkStatus, AttachmentSpec, AttachmentStatus, ExternalAttachmentSpec, ExternalAttachmentStatus, PortForwardSpec, NetworkConditionType; `packages/d2b-contracts/src/v3/ifname.rs`: IfName newtype, derivation, collision detection (extracted from `d2b-host/src/ifname.rs`). Also defines `User/net-local-controller` as a proper Resource with explicit lifecycle: `Provider/network-local`'s Nix package/module provisions the reserved `net-local-controller` OS account with a private fixed UID/GID in Host prerequisites and in the generic net-VM nixos-system artifact (same account, same UID/GID inside the Guest); the network-local controller creates and owns the User Resource (`spec.osUsername: net-local-controller`, `ownerRef: Provider/network-local`, `managedBy: controller`); `Provider/system-core` verifies the account via NSS lookup and reconciles the User Resource to Ready — it does not provision the OS account. No numeric UID/GID enters any ResourceSpec field, authz check, or audit record; `User.status` MAY carry diagnostic `uid`/`gid` values discovered by NSS lookup, but those are informational only and are never authorization inputs. The network-local controller waits for `User/net-local-controller` to reach `Ready` before creating any config Volume (reconcile precondition, not a bootstrap side effect)." + "`packages/d2b-contracts/src/v3/network.rs`: NetworkSpec, NetworkStatus, AttachmentSpec, AttachmentStatus, ExternalAttachmentSpec, ExternalAttachmentStatus, PortForwardSpec, NetworkConditionType, opaque AttachmentHandle, and AttachmentGenerationFence; `packages/d2b-contracts/src/v3/ifname.rs`: IfName newtype, derivation, collision detection (extracted from `d2b-host/src/ifname.rs`). Also defines `User/net-local-controller` as a proper Resource with explicit lifecycle: `Provider/network-local`'s Nix package/module provisions the reserved `net-local-controller` OS account with a private fixed UID/GID in Host prerequisites and in the generic net-VM nixos-system artifact (same account, same UID/GID inside the Guest); the network-local controller creates and owns the User Resource (`spec.osUsername: net-local-controller`, `ownerRef: Provider/network-local`, `managedBy: controller`); `Provider/system-core` verifies the account via NSS lookup and reconciles the User Resource to Ready — it does not provision the OS account. No numeric UID/GID enters any ResourceSpec field, authz check, or audit record; `User.status` MAY carry diagnostic `uid`/`gid` values discovered by NSS lookup, but those are informational only and are never authorization inputs. The network-local controller waits for `User/net-local-controller` to reach `Ready` before creating any config Volume (reconcile precondition, not a bootstrap side effect)." ], - "detailedDesign" : "Strict ResourceEnvelope with Network-specific spec/status. IfName newtype: IFNAMSIZ-1 validated, FNV-1a 64-bit derivation, base32 Crockford, 8-char suffix, bridge/tap role prefixes, detect_collisions over IfNameMapping slice. cidrOverlaps: pure Rust IPv4 arithmetic, same algorithm as lib.nix. NetworkSpec validators: /24 lanCidr with .0 base, /30 uplinkCidr, unique attachment indices 2–250, default hostBlocklist enforcement. Primary reuse disposition: `adapt`. Preserved source-plan detail: extract and adapt.", + "detailedDesign" : "Strict ResourceEnvelope with Network-specific spec/status. IfName newtype: IFNAMSIZ-1 validated, FNV-1a 64-bit derivation, base32 Crockford, 8-char suffix, bridge/tap role prefixes, detect_collisions over IfNameMapping slice. cidrOverlaps: pure Rust IPv4 arithmetic, same algorithm as lib.nix. NetworkSpec validators: /24 lanCidr with .0 base, /30 uplinkCidr, unique attachment indices 2–250, default hostBlocklist enforcement. The opaque attachment realization binds Network UID/generation and attachment UID/generation so deletion can supply a non-printable ID plus explicit expected generation fence without an IfName/path. Primary reuse disposition: `adapt`. Preserved source-plan detail: extract and adapt.", "entryContracts" : [ "ADR-046-resources-network" ], @@ -16012,7 +16216,7 @@ "destinations" : [ "`packages/d2b-provider-network-local/src/controller.rs`: async NetworkReconciler; `packages/d2b-provider-network-local/src/plan.rs`: ReconcilePlan computation; `packages/d2b-provider-network-local/src/observe.rs`: drift-detection observe loop. Full crate layout required (see [Package and crate boundary](#package-and-crate-boundary)): `src/` (controller/plan/observe + colocated unit tests), `tests/` (hermetic conformance and state-machine tests), `integration/` (provider-system reconcile fixtures), `README.md` (Network ResourceType, controller binary, placement, RBAC, security invariants, build/test/integration commands)." ], - "detailedDesign" : "Implements full async reconcile interface from `ADR-046-resource-reconciliation`. `plan()` computes desired vs. actual bridge-presence, sysctl, host-side nftables, hosts-file, NM-unmanaged, config Volume content, Guest, guest-agent Process, and mDNS-Process states. `reconcile()` dispatches in order: `CreateBridge` for each bridge not present (broker applies IPv6 sysctls atomically; `CreateBridge` failure sets `FabricReady=False/bridge-create-error` and aborts) → `ApplySysctl` (defense-in-depth IPv6) → `ApplyNftables` (host-side `inet d2b` table) → `ApplyNmUnmanaged` → `ApplyRoute` → `UpdateHostsFile` → `SeedDnsmasqLease` for new reservations → **Volume upsert** (two-phase): Phase 1 — create `Volume/net--config` with `kind: ephemeral`, `source.executionRef: Host/`, `source.settings.kind: tmpfs`, `quota: {maxBytes: 4194304, maxInodes: 128, enforcement: hard}` (tmpfs quota charged to Host memory budget), `layout` entries (root directory with `type: directory` and four config files each with `type: file`, `ownerRef: User/net-local-controller`, `groupRef: User/net-local-controller`, `mode: \"0640\"`, `accessAcl: []`, `defaultAcl: []`, `noFollow: true`, conservative create/repair/cleanup policies), `views: {guest-readonly: {path: \"\", rights: [read, traverse]}}`, `attachments: []` (no Guest attachment); abort on terminal error with `ConfigVolumeReady=False/config-volume-error`. Wait for Volume backing to reach `Ready`; requeue on `Degraded`/`Failed` with `ConfigVolumeReady=False/volume-not-ready`. Write rendered config content through Volume write service (no raw host paths). Phase 2 — create Guest upsert with `systemArtifactId` from REQUIRED `Network.spec.netVmSystemArtifactId`. Wait for Guest `Ready`. Then update Volume with Guest attachment: `attachments: [{executionRef: Guest/, transport: virtiofs, view: guest-readonly, access: read-only, mountPath: \"/run/d2b/net-config\", settings: {posixAcl: false, xattr: false, cache: auto, inodeFileHandles: never, threadPoolSize: null, socketGroup: null}}]`; wait for attachment `Ready`; requeue on `Degraded` with `ConfigVolumeReady=False/attachment-not-ready`. Create or update guest-agent Process `Process/net--agent` with `processClass: worker`, `sandbox: {namespaceClasses: [], capabilityClasses: [network-admin, network-bind, network-raw]}` (inherits Guest network namespace; `network-admin`→`CAP_NET_ADMIN`, `network-bind`→`CAP_NET_BIND_SERVICE`, `network-raw`→`CAP_NET_RAW`, all effective in Guest network namespace only; INV-NET-009), `mounts: [{volumeRef: Volume/net--config, view: guest-readonly, mountPath: \"/run/d2b/net-config\", access: read-only, required: true}]`. mDNS Process upsert when `spec.mdns.enable = true` (D-NETWORK-001) → `SetBridgePortFlags` per tap. Each broker op returns typed audit evidence. `observe()` re-reads `firewallDigest` (host-side), bridge isolation flags, IPv6 sysctls, and guest-agent Process status (`dnsmasq-bound`, `firewall-applied` predicates); queues reconcile on drift. Metrics use only the fixed semantic labels in §OTEL spans and metrics; Zone/Network identity remains in OTEL resource attributes and permitted audit fields and never enters metric labels or span attributes. **Finalizer** (strictly child-first): `NetworkDraining` → delete guest-agent Process and mDNS Processes; wait for each Deleted watch event → update Volume to remove Guest attachment (`attachments: []`); wait for attachment removal confirmed → delete `Guest/`; wait for Deleted watch event → delete `Volume/net--config`; wait for Deleted watch event → `ApplyNftables` (empty) → `ApplyNmUnmanaged` (empty) → `UpdateHostsFile` (empty) → `DeleteBridge` for each bridge (idempotent) → clear finalizer. No USBIP rules installed by Network; `UsbipBindFirewallRule` issued by device-usbip directly (D-NETWORK-002).", + "detailedDesign" : "Implements full async reconcile interface from `ADR-046-resource-reconciliation`. `plan()` computes desired vs. actual bridge-presence, sysctl, host-side nftables, hosts-file, NM-unmanaged, config Volume content, Guest, guest-agent Process, and mDNS-Process states. `reconcile()` dispatches in order: `CreateBridge` for each bridge not present (broker applies IPv6 sysctls atomically; `CreateBridge` failure sets `FabricReady=False/bridge-create-error` and aborts) → `ApplySysctl` (defense-in-depth IPv6) → `ApplyNftables` (host-side `inet d2b` table) → `ApplyNmUnmanaged` → `ApplyRoute` → `UpdateHostsFile` → `SeedDnsmasqLease` for new reservations → **Volume upsert** (two-phase): Phase 1 — create `Volume/net--config` with `kind: ephemeral`, `source.executionRef: Host/`, `source.settings.kind: tmpfs`, `quota: {maxBytes: 4194304, maxInodes: 128, enforcement: hard}` (tmpfs quota charged to Host memory budget), `layout` entries (root directory with `type: directory` and four config files each with `type: file`, `ownerRef: User/net-local-controller`, `groupRef: User/net-local-controller`, `mode: \"0640\"`, `accessAcl: []`, `defaultAcl: []`, `noFollow: true`, conservative create/repair/cleanup policies), `views: {guest-readonly: {path: \"\", rights: [read, traverse]}}`, `attachments: []` (no Guest attachment); abort on terminal error with `ConfigVolumeReady=False/config-volume-error`. Wait for Volume backing to reach `Ready`; requeue on `Degraded`/`Failed` with `ConfigVolumeReady=False/volume-not-ready`. Write rendered config content through Volume write service (no raw host paths). Phase 2 — create Guest upsert with `systemArtifactId` from REQUIRED `Network.spec.netVmSystemArtifactId`. Wait for Guest `Ready`. Then update Volume with Guest attachment: `attachments: [{executionRef: Guest/, transport: virtiofs, view: guest-readonly, access: read-only, mountPath: \"/run/d2b/net-config\", settings: {posixAcl: false, xattr: false, cache: auto, inodeFileHandles: never, threadPoolSize: null, socketGroup: null}}]`; wait for attachment `Ready`; requeue on `Degraded` with `ConfigVolumeReady=False/attachment-not-ready`. Create or update guest-agent Process `Process/net--agent` with `processClass: worker`, `sandbox: {namespaceClasses: [], capabilityClasses: [network-admin, network-bind, network-raw]}` (inherits Guest network namespace; `network-admin`→`CAP_NET_ADMIN`, `network-bind`→`CAP_NET_BIND_SERVICE`, `network-raw`→`CAP_NET_RAW`, all effective in Guest network namespace only; INV-NET-009), `mounts: [{volumeRef: Volume/net--config, view: guest-readonly, mountPath: \"/run/d2b/net-config\", access: read-only, required: true}]`. mDNS Process upsert when `spec.mdns.enable = true` (D-NETWORK-001) → `SetBridgePortFlags` per tap. Removed attachments first wait for Guest/VMM FD ownership to close, then issue `DeletePersistentTap` with the retained opaque attachment ID and current expected Network/attachment generations; the handle remains retained until confirmed effect or validated absence. Stale generation refreshes/requeues, transient kernel error retries, and foreign marker fails closed. Each broker op returns typed audit evidence. `observe()` re-reads `firewallDigest` (host-side), bridge isolation flags, IPv6 sysctls, and guest-agent Process status (`dnsmasq-bound`, `firewall-applied` predicates); queues reconcile on drift. Metrics use only the fixed semantic labels in §OTEL spans and metrics; Zone/Network identity remains in OTEL resource attributes and permitted audit fields and never enters metric labels or span attributes. **Finalizer** (strictly child-first): `NetworkDraining` → stop workload Guests and await VMM FD closure → generation-fenced `DeletePersistentTap` for each retained attachment, awaiting confirmation → delete guest-agent Process and mDNS Processes; wait for each Deleted watch event → update Volume to remove Guest attachment (`attachments: []`); wait for attachment removal confirmed → delete `Guest/`; wait for Deleted watch event → delete `Volume/net--config`; wait for Deleted watch event → `ApplyNftables` (empty) → `ApplyNmUnmanaged` (empty) → `UpdateHostsFile` (empty) → `DeleteBridge` for each bridge (idempotent) → clear finalizer. No USBIP rules installed by Network; device-usbip issues the existing `UsbipBindFirewallRule` request with action `Ensure` for apply or `Remove` for release (D-NETWORK-002).", "entryContracts" : [ "ADR-046-resources-network" ], @@ -16027,15 +16231,15 @@ ], "specId" : "ADR-046-resources-network", "topologicalRank" : 10, - "validation" : "`ADR046-reconcile-001` toolkit conformance; latency gates (p95 ≤5 ms hint-to-handler); Network-specific: CIDR conflict blocks reconcile, `CreateBridge` failure sets `FabricReady=False`, Volume creation failure sets `ConfigVolumeReady=False/config-volume-error`, `User/net-local-controller` not Ready aborts with `ConfigVolumeReady=False/user-not-ready`, Volume schema round-trip (kind=ephemeral, source.settings.kind=tmpfs, quota.enforcement=hard, layout type=file entries, views.guest-readonly.rights=[read,traverse]), tmpfs quota charged to Host memory budget (test Volume creation fails when Host memory budget exceeded), Guest not created before Volume backing `Ready`, Guest attachment not added before Guest `Ready`, guest-agent Process created after attachment `Ready` (`processClass: worker`, `namespaceClasses: []`, `capabilityClasses: [network-admin, network-bind, network-raw]`, `access: read-only`, `required: true`), host-capability leakage test: no host-netns process gains `CAP_NET_ADMIN`/`CAP_NET_BIND_SERVICE`/`CAP_NET_RAW` as result of guest-agent launch (INV-NET-009; `tests/host-integration/guest-agent-cap-confinement.nix`), `DeleteBridge` called on finalizer, Volume attachment removed before Guest deletion in finalizer (test order: agent Deleted → attachment removed → Guest Deleted → Volume Deleted → bridges), east-west invariant (INV-NET-003), hostBlocklist enforcement (INV-NET-004), macvtap attachment status (delegated to runtime-ch), mDNS Process created/deleted with `spec.mdns.enable` toggle, broker INV-NET-002 tests, config-only spec change updates Volume content and triggers agent reload without Guest restart (INV-NET-008); golden tests updated for v3 IfNames; structural metric descriptor test asserts exact absence of `vm`, `zone`, `zone_id`, `zone_uid`, `network`, and every resource-name-derived label and verifies a Network-name canary is absent from emitted label values", + "validation" : "`ADR046-reconcile-001` toolkit conformance; latency gates (p95 ≤5 ms hint-to-handler); Network-specific: CIDR conflict blocks reconcile, `CreateBridge` failure sets `FabricReady=False`, Volume creation failure sets `ConfigVolumeReady=False/config-volume-error`, `User/net-local-controller` not Ready aborts with `ConfigVolumeReady=False/user-not-ready`, Volume schema round-trip (kind=ephemeral, source.settings.kind=tmpfs, quota.enforcement=hard, layout type=file entries, views.guest-readonly.rights=[read,traverse]), tmpfs quota charged to Host memory budget (test Volume creation fails when Host memory budget exceeded), Guest not created before Volume backing `Ready`, Guest attachment not added before Guest `Ready`, guest-agent Process created after attachment `Ready` (`processClass: worker`, `namespaceClasses: []`, `capabilityClasses: [network-admin, network-bind, network-raw]`, `access: read-only`, `required: true`), host-capability leakage test: no host-netns process gains `CAP_NET_ADMIN`/`CAP_NET_BIND_SERVICE`/`CAP_NET_RAW` as result of guest-agent launch (INV-NET-009; `tests/host-integration/guest-agent-cap-confinement.nix`), removed attachment and finalizer call `DeletePersistentTap` only after Guest/VMM FD closure with opaque ID/current generations, validated absence succeeds, transient failure retains handle/retries, stale generation refreshes, foreign marker blocks without deletion, request/audit contain no IfName/path, `DeleteBridge` called only after tap confirmations, Volume attachment removed before net-VM Guest deletion in finalizer (test order: workload FD closure → persistent taps deleted → agent Deleted → Volume attachment removed → net-VM Guest Deleted → Volume Deleted → bridges), east-west invariant (INV-NET-003), hostBlocklist enforcement (INV-NET-004), macvtap attachment status (delegated to runtime-ch), mDNS Process created/deleted with `spec.mdns.enable` toggle, broker INV-NET-002 tests, config-only spec change updates Volume content and triggers agent reload without Guest restart (INV-NET-008); golden tests updated for v3 IfNames; structural metric descriptor test asserts exact absence of `vm`, `zone`, `zone_id`, `zone_uid`, `network`, and every resource-name-derived label and verifies a Network-name canary is absent from emitted label values", "wave" : "W4" }, { "blockers" : [], "destinations" : [ - "`tests/unit/nix/cases/net-vm-network.nix` (adapted to v3 resource API); updated golden pins; `tests/host-integration/bridge-isolation.nix` (adapted); `packages/d2b-priv-broker/tests/bridge_lifecycle.rs` (new hermetic broker tests). Provider crate test directories: `packages/d2b-provider-network-local/tests/` — hermetic Cargo integration tests (conformance suite, controller state machine, CIDR validation vectors, IfName determinism, invariant tests INV-NET-001–007, reconcile/observe/finalize with deterministic clock, fault injection); `packages/d2b-provider-network-local/integration/` — container/Host/Guest lifecycle fixtures invoked by `make test-integration` (bridge isolation, east-west double opt-in, nftables drift detection, macvtap lifecycle). Both directories required by package policy." + "`tests/unit/nix/cases/net-vm-network.nix` (adapted to v3 resource API); updated golden pins; `tests/host-integration/bridge-isolation.nix` (adapted); `packages/d2b-priv-broker/tests/{bridge_lifecycle,persistent_tap_lifecycle}.rs` (new hermetic broker tests). Provider crate test directories: `packages/d2b-provider-network-local/tests/` — hermetic Cargo integration tests (conformance suite, controller state machine, CIDR validation vectors, IfName determinism, invariant tests INV-NET-001–007, reconcile/observe/finalize with deterministic clock, fault injection); `packages/d2b-provider-network-local/integration/` — container/Host/Guest lifecycle fixtures invoked by `make test-integration` (bridge isolation, east-west double opt-in, nftables drift detection, persistent-tap and macvtap lifecycle). Both directories required by package policy." ], - "detailedDesign" : "Rust integration tests: NetworkSpec CIDR validation golden vectors; AttachmentSpec index uniqueness; ExternalAttachmentSpec mutual-exclusion validation; IfName derivation determinism; CIDR overlap arithmetic; INV-NET-001 through INV-NET-009 invariant tests; reconcile/observe/finalize state machine (deterministic clock). Broker tests: `create_bridge_applies_ipv6_sysctl` (INV-NET-002 layer 1); `delete_bridge_is_idempotent`; `create_bridge_parameters_match_spec` (MTU, STP disabled, multicast snooping disabled). Controller tests: `reconcile_applies_sysctl_defense_in_depth` (INV-NET-002 layer 2); `volume_created_before_guest`; `guest_not_created_until_volume_ready`; `agent_process_created_after_guest`; `finalizer_order_agent_then_guest_then_volume_then_bridges`; `config_only_spec_change_updates_volume_no_guest_restart` (INV-NET-008); `finalizer_calls_delete_bridge`; `mdns_process_created_on_enable`; `mdns_process_deleted_on_disable`; `host_capability_leakage` (INV-NET-009). nix-unit: INV-NET-001 lib.mkForce assertion; net-VM artifact has no inline mDNS service and no per-Network dnsmasq/nftables data (INV-NET-008); Network emitter CIDR constraint assertions; no `systemd.network.netdevs` bridge entries emitted. Host integration: bridge isolation with east-west opt-in; nftables drift detection; macvtap create/delete lifecycle; config Volume update propagates to guest-agent without Guest restart; `tests/host-integration/guest-agent-cap-confinement.nix` (INV-NET-009 zero leakage to host netns).", + "detailedDesign" : "Rust integration tests: NetworkSpec CIDR validation golden vectors; AttachmentSpec index uniqueness; ExternalAttachmentSpec mutual-exclusion validation; IfName derivation determinism; CIDR overlap arithmetic; INV-NET-001 through INV-NET-009 invariant tests; reconcile/observe/finalize state machine (deterministic clock). Broker tests: `create_bridge_applies_ipv6_sysctl` (INV-NET-002 layer 1); `delete_bridge_is_idempotent`; `delete_bridge_never_cascades_attached_tap`; `create_bridge_parameters_match_spec` (MTU, STP disabled, multicast snooping disabled); `delete_persistent_tap_pairs_with_create`; `delete_persistent_tap_absent_is_idempotent_after_ownership_validation`; `delete_persistent_tap_rejects_stale_network_generation`; `delete_persistent_tap_rejects_stale_attachment_generation`; `delete_persistent_tap_foreign_marker_fails_closed`; `delete_persistent_tap_request_and_audit_have_no_ifname_or_path`. Controller tests: `reconcile_applies_sysctl_defense_in_depth` (INV-NET-002 layer 2); `volume_created_before_guest`; `guest_not_created_until_volume_ready`; `agent_process_created_after_guest`; `removed_attachment_waits_for_vmm_then_delete_persistent_tap`; `finalizer_order_vmm_then_taps_then_agent_then_guest_then_volume_then_bridges`; `delete_persistent_tap_transient_retry_retains_handle`; `delete_persistent_tap_generation_mismatch_refreshes`; `delete_persistent_tap_foreign_marker_blocks_finalizer`; `config_only_spec_change_updates_volume_no_guest_restart` (INV-NET-008); `finalizer_calls_delete_bridge`; `mdns_process_created_on_enable`; `mdns_process_deleted_on_disable`; `host_capability_leakage` (INV-NET-009). nix-unit: INV-NET-001 lib.mkForce assertion; net-VM artifact has no inline mDNS service and no per-Network dnsmasq/nftables data (INV-NET-008); Network emitter CIDR constraint assertions; no `systemd.network.netdevs` bridge entries emitted. Host integration: bridge isolation with east-west opt-in; nftables drift detection; persistent-tap and macvtap create/delete lifecycle; config Volume update propagates to guest-agent without Guest restart; `tests/host-integration/guest-agent-cap-confinement.nix` (INV-NET-009 zero leakage to host netns).", "entryContracts" : [ "ADR-046-resources-network" ], @@ -16057,9 +16261,9 @@ { "blockers" : [], "destinations" : [ - "`Provider/device-usbip` owns one relay Process/Endpoint authority per Network plus the typed EffectPort adapter for `UsbipBindFirewallRule`. The controller watches only the `networkRef` resource's identity/readiness/generation; Core privately resolves Network UID to relay attachment and firewall intent. Network spec/status is not mutated with USBIP fields. Full crate layout required for `packages/d2b-provider-device-usbip/` (see [Package and crate boundary](#package-and-crate-boundary)): `src/` (controller and usbip runner + unit tests), `tests/` (hermetic conformance, dependency-watch state machine, UsbipBindFirewallRule round-trip), `integration/` (Host/Guest USBIP attach/detach lifecycle fixtures), `README.md` (Provider identity, provider-neutral USB Service/Binding types, USBIP Processes/Endpoints, Network least-privilege dependency contract, RBAC, security invariants, standalone-repo path)." + "`Provider/device-usbip` owns one relay Process/Endpoint authority per Network plus the typed EffectPort adapter for the existing closed `UsbipBindFirewallRule` request with closed action enum `Ensure|Remove`. The controller watches only the `networkRef` resource's identity/readiness/generation; Core privately resolves Network UID to relay attachment and firewall intent. Network spec/status is not mutated with USBIP fields. Full crate layout required for `packages/d2b-provider-device-usbip/` (see [Package and crate boundary](#package-and-crate-boundary)): `src/` (controller and usbip runner + unit tests), `tests/` (hermetic conformance, dependency-watch state machine, `UsbipBindFirewallRule` `Ensure|Remove` round-trip), `integration/` (Host/Guest USBIP attach/detach lifecycle fixtures), `README.md` (Provider identity, provider-neutral USB Service/Binding types, USBIP Processes/Endpoints, Network least-privilege dependency contract, RBAC, security invariants, standalone-repo path)." ], - "detailedDesign" : "Device-usbip's typed EffectPort is the sole semantic owner of every USBIP TCP/3240 rule. Its Core adapter resolves the opaque per-Network/per-busid intent and issues `UsbipBindFirewallRule`; its strict provider status owns firewall digest/drift. Network-local emits no generic host or net-VM TCP/3240 allow and ignores device-usbip ownership markers in Network drift. The device Provider owns exactly one multiplexed relay Endpoint authority per Network and supplies Binding proxies only authorized connected streams through LaunchTickets. Primary reuse disposition: `adapt`. Preserved source-plan detail: extract and adapt.", + "detailedDesign" : "Device-usbip's typed EffectPort is the sole semantic owner of every USBIP TCP/3240 rule. Its Core adapter resolves the opaque per-Network/per-busid intent and issues the same `UsbipBindFirewallRule` request with action `Ensure` for apply and `Remove` for release; no separate release op exists. `Remove` is generation-bound, ownership-scoped, idempotent after validated absence, and foreign-marker fail-closed. The controller retains firewall token/status and the relay authority reference until the broker confirms `Remove`; its strict provider status owns firewall digest/drift. Network-local emits no generic host or net-VM TCP/3240 allow and ignores device-usbip ownership markers in Network drift. The device Provider owns exactly one multiplexed relay Endpoint authority per Network and supplies Binding proxies only authorized connected streams through LaunchTickets. Primary reuse disposition: `adapt`. Preserved source-plan detail: extract and adapt.", "entryContracts" : [ "ADR-046-resources-network" ], @@ -16074,7 +16278,7 @@ ], "specId" : "ADR-046-resources-network", "topologicalRank" : 11, - "validation" : "device-usbip conformance tests cover exact per-Network/per-busid scoping, one relay Endpoint authority, ownership-scoped drift/status, foreign-marker rejection, and release; network-local nftables tests assert no TCP/3240/USBIP rule on host or net VM and prove USBIP rule churn does not change Network `FirewallReady`; the pinned USBIP firewall golden moves to device-usbip ownership", + "validation" : "device-usbip conformance tests cover the exact closed `Ensure|Remove` enum (unknown actions rejected), same-request broker mapping for apply/release, expected Network/Service generation binding, exact per-Network/per-busid scoping, idempotent validated-absence `Remove`, one relay Endpoint authority, ownership-scoped drift/status, foreign-marker rejection, transient retry, and retention of status/token/authority until effect confirmation; network-local nftables tests assert no TCP/3240/USBIP rule on host or net VM and prove USBIP rule churn does not change Network `FirewallReady`; the pinned USBIP firewall golden moves to device-usbip ownership", "wave" : "W4" }, { @@ -16959,7 +17163,7 @@ "destinations" : [ "`d2b-contracts` trait plus `d2b-core` core adapter; maps to broker wire operations and audit emission." ], - "detailedDesign" : "Implement `NetworkEffectPort` core adapter in `d2b-core`; map to broker wire ops; emit audit records. Versioning: minor releases may add methods with default impls; major releases require Provider upgrade. The trait lives in `d2b-contracts`; the adapter in `d2b-core`.", + "detailedDesign" : "Implement `NetworkEffectPort` core adapter in `d2b-core`; map to broker wire ops; emit audit records. `revoke_attachment_tap` accepts only an opaque `AttachmentHandle` plus `AttachmentGenerationFence { expected_network_generation, expected_attachment_generation }` and maps to `DeletePersistentTap`; no IfName/path or caller-authored marker crosses the trait. Versioning: minor releases may add methods with default impls; major releases require Provider upgrade. The trait lives in `d2b-contracts`; the adapter in `d2b-core`.", "entryContracts" : [ "ADR-046-provider-network-local" ], @@ -16979,9 +17183,9 @@ { "blockers" : [], "destinations" : [ - "Broker wire contract and broker/core adapter operation table for `CreateBridge`, `DeleteBridge`, `ReadNftablesDigest`, `ReadSysctlState`, and `ReadBridgePortFlags`." + "Broker wire contract and broker/core adapter operation table for `DeletePersistentTap`, `CreateBridge`, `DeleteBridge`, `ReadNftablesDigest`, `ReadSysctlState`, and `ReadBridgePortFlags`." ], - "detailedDesign" : "Add `CreateBridge`, `DeleteBridge`, `ReadNftablesDigest`, `ReadSysctlState`, `ReadBridgePortFlags` broker ops. Primary reuse disposition: `adapt`. Preserved source-plan detail: extend broker wire with net-new operations and reuse existing closed broker-operation dispatch shape.", + "detailedDesign" : "Add canonical closed `DeletePersistentTap` paired with `CreatePersistentTap`, plus `CreateBridge`, `DeleteBridge`, `ReadNftablesDigest`, `ReadSysctlState`, and `ReadBridgePortFlags`. `DeletePersistentTapRequest` contains only an opaque attachment ID and expected Network/attachment generations. The broker resolves trusted realization state, validates generations and ownership marker, treats validated absence as success, rejects foreign markers without deletion, and emits path-free post-effect audit. No request accepts an IfName or path. Primary reuse disposition: `adapt`. Preserved source-plan detail: extend broker wire with net-new operations and reuse existing closed broker-operation dispatch shape.", "entryContracts" : [ "ADR-046-provider-network-local" ], @@ -16996,7 +17200,7 @@ ], "specId" : "ADR-046-provider-network-local", "topologicalRank" : 12, - "validation" : "`integration/host_fabric.rs` covers bridge create/delete, nftables apply/digest, IPv6 suppression, NetworkManager unmanaged handling, and real `NetworkEffectPort` implementation.", + "validation" : "Broker tests cover `DeletePersistentTap` success, validated already-absent idempotency, stale Network/attachment generations, foreign-marker fail-closed behavior, path-free audit, and rejection of any IfName/path field; `integration/host_fabric.rs` covers persistent-tap deletion, bridge create/delete, nftables apply/digest, IPv6 suppression, NetworkManager unmanaged handling, and real `NetworkEffectPort` implementation.", "wave" : "W6" }, { @@ -17004,7 +17208,7 @@ "destinations" : [ "`d2b-contracts` opaque byte-array newtypes; core-held HMAC key and provider-facing redacted handle types." ], - "detailedDesign" : "Implement `AttachmentHandle` and `FabricHandle` as opaque byte-array newtypes (32 bytes of HMAC-SHA-256 over internal identity material; key held by core). Each handle is single-use; revocation is implicit when the owning Network is deleted. These types are declared in `d2b-contracts`, not in the provider crate.", + "detailedDesign" : "Implement `AttachmentHandle` and `FabricHandle` as opaque byte-array newtypes (32 bytes of HMAC-SHA-256 over internal identity material; key held by core). Each attachment handle identifies one generation-fenced realization and is retained until explicit `DeletePersistentTap` confirmation during attachment removal or Network finalization. These types are declared in `d2b-contracts`, not in the provider crate.", "entryContracts" : [ "ADR-046-provider-network-local" ], @@ -17018,7 +17222,7 @@ ], "specId" : "ADR-046-provider-network-local", "topologicalRank" : 11, - "validation" : "`tests/fault_injection.rs` and `tests/controller_state.rs` cover opaque-handle mismatch, revocation-on-delete, and no raw IfName/IP/MAC public surface.", + "validation" : "`tests/fault_injection.rs` and `tests/controller_state.rs` cover opaque-handle mismatch, generation-fenced `DeletePersistentTap`, retained handle until confirmation, and no raw IfName/IP/MAC/path public surface.", "wave" : "W6" }, { @@ -17071,7 +17275,7 @@ "destinations" : [ "`packages/d2b-provider-network-local/src/{controller.rs,metrics.rs}`." ], - "detailedDesign" : "Implement `controller.rs` reconcile/observe/finalize handlers with `NetworkEffectPort` injection and the §21 metric descriptors with closed semantic labels. No descriptor may carry `vm`, `zone`, `zone_id`, `zone_uid`, `network`, or another resource-name-derived key; Network/Zone identity stays only in OTEL resource attributes and permitted audit fields. Primary reuse disposition: `adapt`. Preserved source-plan detail: port semantics into provider reconcile state machine; do not reuse static per-env systemd/Nix ownership.", + "detailedDesign" : "Implement `controller.rs` reconcile/observe/finalize handlers with `NetworkEffectPort` injection and the §21 metric descriptors with closed semantic labels. Attachment removal and finalization wait for Guest/VMM FD ownership to close, then call `revoke_attachment_tap` with the retained opaque handle and current Network/attachment generation fence; transient deletion retries retain the handle, stale generations refresh/requeue, and ownership conflict blocks finalizer clearing. No descriptor may carry `vm`, `zone`, `zone_id`, `zone_uid`, `network`, or another resource-name-derived key; Network/Zone identity stays only in OTEL resource attributes and permitted audit fields. Primary reuse disposition: `adapt`. Preserved source-plan detail: port semantics into provider reconcile state machine; do not reuse static per-env systemd/Nix ownership.", "entryContracts" : [ "ADR-046-provider-network-local" ], @@ -17252,7 +17456,7 @@ "destinations" : [ "`packages/d2b-provider-network-local/tests/controller_state.rs`." ], - "detailedDesign" : "Controller state-machine unit tests with fake `NetworkEffectPort` (from d2b-contracts mock) and deterministic clock.", + "detailedDesign" : "Controller state-machine unit tests with fake `NetworkEffectPort` (from d2b-contracts mock) and deterministic clock, including attachment removal/finalizer calls to generation-fenced `DeletePersistentTap`.", "entryContracts" : [ "ADR-046-provider-network-local" ], @@ -17267,7 +17471,7 @@ ], "specId" : "ADR-046-provider-network-local", "topologicalRank" : 14, - "validation" : "`tests/controller_state.rs` covers normal path, CIDR conflict, User not Ready, Volume error, Guest timeout, agent reload failure, finalizer sequence, adoption, and drift.", + "validation" : "`tests/controller_state.rs` covers normal path, CIDR conflict, User not Ready, Volume error, Guest timeout, agent reload failure, finalizer sequence, `DeletePersistentTap` validated absence, transient retry with retained handle, stale-generation refresh, foreign-marker block, adoption, and drift.", "wave" : "W6" }, { @@ -17275,7 +17479,7 @@ "destinations" : [ "`packages/d2b-provider-network-local/integration/host_fabric.rs`, `guest_lifecycle.rs`, `agent_reload.rs`, and `delete_sequence.rs`." ], - "detailedDesign" : "Integration tests: full Network lifecycle (create, config update, agent Reload, delete sequence) in container environment. Primary reuse disposition: `adapt`. Preserved source-plan detail: adapt reusable semantic assertions into v3 integration coverage.", + "detailedDesign" : "Integration tests: full Network lifecycle (create, config update, agent Reload, generation-fenced persistent-tap deletion, delete sequence) in container environment. Primary reuse disposition: `adapt`. Preserved source-plan detail: adapt reusable semantic assertions into v3 integration coverage.", "entryContracts" : [ "ADR-046-provider-network-local" ], @@ -17613,9 +17817,9 @@ { "blockers" : [], "destinations" : [ - "`packages/d2b-provider-observability-otel/src/{collector_bin,emitter_socket,exporter,controller,service,binding}.rs`; updated Nix observability modules" + "`packages/d2b-provider-observability-otel/src/{collector_bin,emitter_socket,ingress_policy,exporter,controller,service,binding}.rs`; updated Nix observability modules" ], - "detailedDesign" : "Register the initial implementation of both provider-neutral qualified ResourceTypes by binding the exact ADR046-provider-004 base versions/fingerprints, then define only strict observability-otel Service/Binding spec and status extensions. Reconcile each Binding into an edge collector, private Endpoints, runtime Volume, and optional forwarder. Collector links the full OTEL SDK, resolves `serviceRef`, stamps trusted producer identity, and enforces common signals/quota/policy plus strict batching extension. Write generic Service/Binding observations to `status.resource` and SigNoz/OTLP/OTEL observations only to `status.provider`; no state file or Provider state Volume. Provider root config remains installation-only. Primary reuse disposition: `adapt`. Preserved source-plan detail: adapt Nix pipeline shape (replace per-VM `vmName` with per-Zone name; replace socat runner with vsock-forwarder long-lived Process; adapt `ingressSources` per-Zone entry).", + "detailedDesign" : "Register the initial implementation of both provider-neutral qualified ResourceTypes by binding the exact ADR046-provider-004 base versions/fingerprints, then define only strict observability-otel Service/Binding spec and status extensions. Reconcile each Binding into an edge collector, private Endpoints, runtime Volume, and optional forwarder. Collector links the full OTEL SDK, resolves `serviceRef`, stamps trusted producer identity into allow-listed OTEL Resource attributes, and routes Unix-emitter, OTLP-Unix, OTLP/vsock, and import-stream metrics through one structural `METRIC_LABEL_POLICY` gate before aggregation, queueing, batching, retry, or export. The gate rejects exact/suffix identity keys and trusted `metadata.name`/resource-identity canary values, applies bounded non-echoing error classes and connection quarantine, and never consumes audit. Write generic Service/Binding observations to `status.resource` and SigNoz/OTLP/OTEL observations only to `status.provider`; no state file or Provider state Volume. Provider root config remains installation-only. Primary reuse disposition: `adapt`. Preserved source-plan detail: adapt Nix pipeline shape (replace per-VM `vmName` with per-Zone name; replace socat runner with vsock-forwarder long-lived Process; adapt `ingressSources` per-Zone entry).", "entryContracts" : [ "ADR-046-provider-observability-otel" ], @@ -17633,7 +17837,7 @@ ], "specId" : "ADR-046-provider-observability-otel", "topologicalRank" : 12, - "validation" : "Common-fixture/fingerprint and canonical-minimal-base conformance (including a fake alternate telemetry Provider); `tests/emitter_socket_receive.rs`; `tests/exporter_outage.rs`; `tests/exporter_backpressure.rs`; `integration/scenario_full_pipeline.rs`; adapted `policy_observability.rs` (retain all existing assertions; add new `d2b.zone`, `d2b.provider` allowlist entries)", + "validation" : "Common-fixture/fingerprint and canonical-minimal-base conformance (including a fake alternate telemetry Provider); `tests/emitter_socket_receive.rs`; table-driven `tests/ingress_metric_policy.rs` across all four ingress adapters; `tests/exporter_outage.rs`; `tests/exporter_backpressure.rs`; `integration/scenario_full_pipeline.rs`; adapted `policy_observability.rs` (retain all existing assertions; add new `d2b.zone`, `d2b.provider` allowlist entries)", "wave" : "W6" }, { @@ -17664,9 +17868,9 @@ { "blockers" : [], "destinations" : [ - "`packages/d2b-contract-tests/tests/policy_observability.rs` (updated); `packages/d2b-contract-tests/tests/policy_telemetry_redaction.rs` (new, per ADR046-telem-008); `packages/d2b-provider-observability-otel/tests/no_vm_label_in_metrics.rs`" + "`packages/d2b-contract-tests/tests/policy_observability.rs` (updated); `packages/d2b-contract-tests/tests/policy_telemetry_redaction.rs` (new, per ADR046-telem-008); `packages/d2b-provider-observability-otel/tests/{no_vm_label_in_metrics,ingress_metric_policy}.rs`" ], - "detailedDesign" : "(1) Extend `loki_native_otel_resource_attributes` allowlist with `d2b.zone`, `d2b.provider`, `d2b.component`, `service.version`. (2) Add gate: `no_isolation` must not appear in any Provider `MetricDescriptor` label or span attribute catalog. (3) Adapt `minijail_relay_otel.rs` shape test for Provider-managed runner (no broker `RunnerRole::OtelHostBridge`). (4) Add metric inventory gates for `d2b_otel_*` instruments from this spec. (5) Retain: `startup_tracing_avoids_host_path_fields`; SigNoz-only backend assertion; `tempo_guest_collector_shape`; `config_source = \"realm-controllers\"` absence gate. Primary reuse disposition: `adapt`. Preserved source-plan detail: adapt and extend existing tests; keep existing test assertions.", + "detailedDesign" : "(1) Extend `loki_native_otel_resource_attributes` allowlist with `d2b.zone`, `d2b.provider`, `d2b.component`, `service.version`. (2) Add gate: `no_isolation` must not appear in any Provider `MetricDescriptor` label or span attribute catalog. (3) Adapt `minijail_relay_otel.rs` shape test for Provider-managed runner (no broker `RunnerRole::OtelHostBridge`). (4) Add metric inventory gates for `d2b_otel_*` instruments from this spec. (5) Structurally prove that all four metrics ingress adapters call the shared pre-batch policy gate; run exact forbidden-key, suffix, `metadata.name`, ResourceRef, UID, and resource-identity canaries through each adapter; assert bounded non-echoing outcomes/quarantine/backpressure and valid OTEL Resource identity preservation. (6) Retain: `startup_tracing_avoids_host_path_fields`; SigNoz-only backend assertion; `tempo_guest_collector_shape`; `config_source = \"realm-controllers\"` absence gate. Primary reuse disposition: `adapt`. Preserved source-plan detail: adapt and extend existing tests; keep existing test assertions.", "entryContracts" : [ "ADR-046-provider-observability-otel" ], @@ -18633,7 +18837,7 @@ "destinations" : [ "packages/d2b-provider-runtime-qemu-media/src/telemetry.rs" ], - "detailedDesign" : "Metrics and OTEL spans: implement all metrics from §18 and OTEL trace spans with structural closed-label enforcement and no Zone/VM/resource name, user identity, path, or other sensitive value in any metric label; retain Zone/resource identity only in bounded OTEL resource attributes and permitted audit fields. Primary reuse disposition: `create`. Preserved source-plan detail: net-new telemetry emission for the Provider metrics and spans in §18.", + "detailedDesign" : "Metrics and OTEL spans: implement all metrics from §18 and OTEL trace spans with structural closed-label enforcement and no Zone/VM/resource name, user identity, path, or other sensitive value in any metric label. Span attributes use only the exact fixed semantic fields and `outcome` listed in §18; no resource name, UID, shortened UID, digest, ref, or derived identity is admitted. Retain identity only in allow-listed OTEL Resource attributes and permitted bounded audit fields. Primary reuse disposition: `create`. Preserved source-plan detail: net-new telemetry emission for the Provider metrics and spans in §18.", "entryContracts" : [ "ADR-046-provider-runtime-qemu-media" ], @@ -18649,7 +18853,7 @@ ], "specId" : "ADR-046-provider-runtime-qemu-media", "topologicalRank" : 20, - "validation" : "`tests/metrics_label_cardinality.rs` asserts exact absence of `vm`, `zone`, `zone_id`, `zone_uid`, and resource-name-derived keys plus Guest/Zone-name canary absence; `tests/otel_span_attributes.rs` preserves allowed OTEL resource identity attributes and rejects identity span attributes", + "validation" : "`tests/metrics_label_cardinality.rs` asserts exact absence of `vm`, `zone`, `zone_id`, `zone_uid`, and resource-name-derived keys plus Guest/Zone-name canary absence; `tests/otel_span_attributes.rs` asserts the exact per-span semantic allowlist, preserves allowed OTEL Resource identity attributes, and rejects Zone/Guest/Process/Provider-resource names, refs, UIDs, shortened UIDs, digests, and identity canary values in span attributes", "wave" : "W6" }, { @@ -19338,7 +19542,7 @@ "destinations" : [ "`packages/d2b-contracts/src/v3/zone_link.rs` (ZoneLink spec/status types, intent types, namespace allocation); `packages/d2b-zone-routing/src/resolver.rs` (ZoneEntrypointResolver)" ], - "detailedDesign" : "Child-local ZoneLink spec/status fields; self-name and one-uplink invariants; ZoneLinkIntent record stored in the child; ZoneLinkNamespaceAllocation issued by the exact parent allocator selected in sealed Nix-compiled `parentZone` topology; ZoneEntrypointResolver with longest-suffix match over ZonePath (adapted from `RealmEntrypointTable::resolve`); no reciprocal parent-store resource; fail-closed on unknown Zone Primary reuse disposition: `adapt`. Preserved source-plan detail: extract and adapt.", + "detailedDesign" : "Child-local ZoneLink spec/status fields; self-name and one-uplink invariants; ZoneLinkIntent record stored in the child; ZoneLinkNamespaceAllocation issued by the exact parent allocator selected in sealed Nix-compiled `parentZone` topology; ZoneEntrypointResolver with longest-suffix match over ZonePath (adapted from `RealmEntrypointTable::resolve`) driven only by sealed `{ childZone, parentZone }` rows plus authenticated admitted route projections; no reciprocal parent-store resource or parent ZoneLink handler; fail closed on unknown topology, absent/stale projection, or unauthenticated route Primary reuse disposition: `adapt`. Preserved source-plan detail: extract and adapt.", "entryContracts" : [ "ADR-046-zone-routing" ], @@ -19354,7 +19558,7 @@ ], "specId" : "ADR-046-zone-routing", "topologicalRank" : 9, - "validation" : "Longest-suffix match vectors; ZoneLink spec validation tests; resolver fail-closed test", + "validation" : "Longest-suffix match vectors over sealed topology; child-local ZoneLink spec validation; resolver rejects unknown/stale/withdrawn/unauthenticated route projections; parent-store fixture contains no ZoneLink row or handler", "wave" : "W2" }, { @@ -19385,7 +19589,7 @@ "destinations" : [ "`packages/d2b-bus/src/zone_route.rs` (cross-Zone bus routing), `packages/d2b-bus/src/relay.rs` (per-hop relay handler)" ], - "detailedDesign" : "Cross-Zone routing path in d2b-bus: ZoneEntrypointResolver → ZoneRouteEngine::decide_route → ZoneLink ComponentSession per hop; hop-counter decrement and enforcement; RBAC `relay` verb check at each intermediate hop; idempotency key namespace (full 6-tuple) in ZoneLinkIdempotencyKey; pinned reverse path tracking; cancellation forwarding; watch cursor forwarding and revision-expired handling; no-FD/credential structural rejection at serialization boundary Primary reuse disposition: `adapt`. Preserved source-plan detail: extract and adapt.", + "detailedDesign" : "Cross-Zone routing path in d2b-bus: ZoneEntrypointResolver consumes sealed topology plus authenticated route projections → ZoneRouteEngine::decide_route → admitted ComponentSession established by each next-hop child's local ZoneLink; hop-counter decrement and enforcement; RBAC `relay` verb check at each intermediate hop; idempotency key namespace (full 6-tuple) in ZoneLinkIdempotencyKey; pinned reverse path tracking; cancellation forwarding; watch cursor forwarding and revision-expired handling; no-FD/credential structural rejection at serialization boundary. No parent route step performs Resource API Get/List/Watch on ZoneLink Primary reuse disposition: `adapt`. Preserved source-plan detail: extract and adapt.", "entryContracts" : [ "ADR-046-zone-routing" ], @@ -19547,7 +19751,7 @@ "destinations" : [ "`nixos-modules/zone-resources-json.nix` (new), private local-root allocator bootstrap compiler/sealer input (not a ResourceSpec or public bundle), `nixos-modules/bundle-artifacts.nix` (new row for per-Zone `resource-bundle.json`), `packages/xtask/src/main.rs` (`gen-zone-schemas` subcommand emitting `docs/reference/schemas/v3/.schema.json` for Zone and ZoneLink; `gen-zone-nix-options` subcommand emitting `nixos-modules/generated/options-zones-.nix`)" ], - "detailedDesign" : "`zone-resources-json.nix` iterates `d2b.zones..resources.*` to produce the canonical sorted resource list: for each entry, render `{ apiVersion, type, metadata: { name, zone, ownerRef: , labels: , annotations: }, spec: }`. Separately canonicalize sorted `{ childZone, parentZone }` rows from the compiler-only topology and seal them into the private allocator bootstrap input; `parentZone` never enters a resource bundle or `Zone.spec`, and a topology digest change releases/reallocates affected edges independently of resource `generationId`. The bundle JSON omits `managedBy` and `configurationGeneration`; the configuration service/core sets those fields when activating the validated bundle. Sort all resources by `(type, zone, name)`. Compute `generationId` as SHA-256 (lower hex) of the UTF-8 bytes of the sorted `resources` array JSON. Compute `integrity` as SHA-256 (base64url, no padding) of the full bundle JSON with integrity field zeroed. Install at `/etc/d2b/zones//resource-bundle.json` root:d2bd 0640. Canonical form: all object keys sorted lexicographically; order-significant arrays preserved; schema-declared set-like arrays sorted lexicographically; all optional fields emitted with defaults; no field renaming or restructuring. Build-time validation runs in a Nix derivation: (1) validate the complete parent map (non-root required, local-root forbidden, declared target, one scalar parent, not self, acyclic, max 16 names); (2) validate each resource against the committed JSON Schema; (3) validate `transportSettings` for each child-local ZoneLink against its same-Zone Provider's `transportSettingsSchema` — `transportProviderRef` is always explicit, never inferred or defaulted; (4) verify capability ceilings are subsets of the sealed selected-parent allocator grants; (5) verify `childZoneName == metadata.zone`, at most one uplink resource per non-root Zone, and no local-root uplink; (6) check for duplicate `(type, zone, name)` tuples. Providers MUST commit their `transportSettingsSchema` before any ZoneLink can reference them. Drift gates: `xtask gen-zone-schemas && git diff --exit-code` and `xtask gen-zone-nix-options && git diff --exit-code` both wired into `make test-drift`. Add `checks.${system}.zone-schema-drift` to `flake.nix`. Primary reuse disposition: `adapt`. Preserved source-plan detail: extend and adapt.", + "detailedDesign" : "`zone-resources-json.nix` iterates `d2b.zones..resources.*` to produce the canonical sorted resource list: for each entry, render `{ apiVersion, type, metadata: { name, zone, ownerRef: , labels: , annotations: }, spec: }`. Separately canonicalize sorted `{ childZone, parentZone }` rows from the compiler-only topology and seal them into the private allocator bootstrap input; `parentZone` never enters a resource bundle or `Zone.spec`, and a topology digest change releases/reallocates affected edges independently of resource `generationId`. Per-Zone generation is strict: local root's generated bundle contains no ZoneLink; a non-root Zone's enabled uplink and referenced transport Provider appear together only in that child's bundle; no emitter copies either resource into the selected parent's bundle. The bundle JSON omits `managedBy` and `configurationGeneration`; the configuration service/core sets those fields when activating the validated bundle. Sort all resources by `(type, zone, name)`. Compute `generationId` as SHA-256 (lower hex) of the UTF-8 bytes of the sorted `resources` array JSON. Compute `integrity` as SHA-256 (base64url, no padding) of the full bundle JSON with integrity field zeroed. Install at `/etc/d2b/zones//resource-bundle.json` root:d2bd 0640. Canonical form: all object keys sorted lexicographically; order-significant arrays preserved; schema-declared set-like arrays sorted lexicographically; all optional fields emitted with defaults; no field renaming or restructuring. Build-time validation runs in a Nix derivation: (1) validate the complete parent map (non-root required, local-root forbidden, declared target, one scalar parent, not self, acyclic, max 16 names); (2) validate each resource against the committed JSON Schema; (3) validate `transportSettings` for each child-local ZoneLink against its same-Zone Provider's `transportSettingsSchema` — `transportProviderRef` is always explicit, never inferred or defaulted; (4) verify capability ceilings are subsets of the sealed selected-parent allocator grants; (5) verify `childZoneName == metadata.zone`, at most one uplink resource per non-root Zone, and no local-root uplink; (6) check for duplicate `(type, zone, name)` tuples. Providers MUST commit their `transportSettingsSchema` before any ZoneLink can reference them. Drift gates: `xtask gen-zone-schemas && git diff --exit-code` and `xtask gen-zone-nix-options && git diff --exit-code` both wired into `make test-drift`. Add `checks.${system}.zone-schema-drift` to `flake.nix`. Primary reuse disposition: `adapt`. Preserved source-plan detail: extend and adapt.", "entryContracts" : [ "ADR-046-zone-routing" ], @@ -19563,7 +19767,7 @@ ], "specId" : "ADR-046-zone-routing", "topologicalRank" : 9, - "validation" : "`drift: zone-resource-schema`, `drift: zone-nix-options`, `build: zone-bundle-deterministic`, `build: parent-topology-sealed`, `build: transport-settings-unknown-field`, `build: capability-ceiling-superset`, `build: missing-transport-provider`; run `make flake-matrix-pin` after adding flake checks", + "validation" : "`drift: zone-resource-schema`, `drift: zone-nix-options`, `build: zone-bundle-deterministic`, `build: parent-topology-sealed`, `build: child-local-zonelink-bundle` (K0 has no ZoneLink; K1 contains its self-matching ZoneLink and same-Zone transport Provider; neither is copied to K0), `build: transport-settings-unknown-field`, `build: capability-ceiling-superset`, `build: missing-transport-provider`; run `make flake-matrix-pin` after adding flake checks", "wave" : "W2" }, { @@ -19641,23 +19845,25 @@ "destinations" : [ "`packages/d2b-zone-routing/src/service.rs`" ], - "detailedDesign" : "Rename `RealmServiceServer` → `ZoneServiceServer`; service wire name `d2b.realm.v2.RealmService` → `d2b.zone.v3.ZoneService`; rename methods (bootstrap→zone-bootstrap, enroll→zone-enroll, resolve_route→resolve-zone-route, authorize_shortcut→authorize-zone-shortcut, revoke_shortcut→revoke-zone-shortcut, report_shortcut_close→report-zone-shortcut-close, inspect→zone-inspect); replace `RealmSessionAuthority` with Zone principal + RBAC binding; replace `BootstrapBinding` with ZoneLink allocator-issued PSK binding; replace `EnrollmentBinding` with ZoneLink KK enrollment record; add `relay` verb RBAC check per hop; adapt shortcut model to ZonePath addressing; `RealmServiceLimits` defaults preserved; `MAX_DISPATCH_IN_FLIGHT=64`, `SHUTDOWN_TIMEOUT=5s` preserved; `CredentialCustody::GatewayGuest` excluded (all ZoneLink sessions are direct KK) Primary reuse disposition: `adapt`. Preserved source-plan detail: copy and adapt.", + "detailedDesign" : "Rename `RealmServiceServer` → `ZoneServiceServer`; service wire name `d2b.realm.v2.RealmService` → `d2b.zone.v3.ZoneService`; rename methods (bootstrap→zone-bootstrap, enroll→zone-enroll, resolve_route→resolve-zone-route, authorize_shortcut→authorize-zone-shortcut, revoke_shortcut→revoke-zone-shortcut, report_shortcut_close→report-zone-shortcut-close, inspect→zone-inspect) and add list/watch topology-projection methods. The read-only projection starts from the sealed sorted `{ childZone, parentZone }` compiler input and joins only authenticated, admitted `ZoneRouteEngine` route/projection status. It exposes no ZoneLink resource name, UID, spec, status, Provider ref, fingerprint, transport setting, or handle. Replace `RealmSessionAuthority` with Zone principal + RBAC binding; replace `BootstrapBinding` with allocator-issued PSK binding associated with the child's local ZoneLink; replace `EnrollmentBinding` with the corresponding KK enrollment record; add `relay` verb RBAC check per hop; adapt shortcut model to ZonePath addressing; `RealmServiceLimits` defaults preserved; `MAX_DISPATCH_IN_FLIGHT=64`, `SHUTDOWN_TIMEOUT=5s` preserved; `CredentialCustody::GatewayGuest` excluded (all ZoneLink sessions are direct KK) Primary reuse disposition: `adapt`. Preserved source-plan detail: copy and adapt.", "entryContracts" : [ "ADR-046-zone-routing" ], "exitGate" : "ADR046-W2 exit criteria (ADR-046-validation-and-delivery §4): every spec/work item in this wave Merged with clean destinations, all validators green, and the ten-role panel seal recorded", "id" : "ADR046-routing-016", "kind" : "work-item", - "owner" : "ADR046-routing-007, ADR046-routing-001; Zone service owner", + "owner" : "ADR046-routing-007, ADR046-routing-002, ADR046-routing-004, ADR046-routing-012; Zone service owner", "parallelGroup" : "wi:ADR-046-zone-routing", "prerequisites" : [ "ADR-046-zone-routing", - "ADR046-routing-001", - "ADR046-routing-007" + "ADR046-routing-002", + "ADR046-routing-004", + "ADR046-routing-007", + "ADR046-routing-012" ], "specId" : "ADR-046-zone-routing", - "topologicalRank" : 8, - "validation" : "Bootstrap/enroll/resolve-route/shortcut integration tests against fake ZoneLink; relay-verb RBAC test; KK enrollment test; shortcut ZonePath addressing test; concurrent dispatch bound test (64 in-flight)", + "topologicalRank" : 11, + "validation" : "Bootstrap/enroll/resolve-route/shortcut integration tests against a child-local fake ZoneLink; topology list/inspect/watch golden vectors contain exact `{ childZone, parentZone }` rows plus authenticated status and no ZoneLink fields; stale/withdrawn/unauthenticated projection tests; parent-store no-row/no-handler test; relay-verb RBAC test; KK enrollment test; shortcut ZonePath addressing test; concurrent dispatch bound test (64 in-flight)", "wave" : "W2" }, { @@ -20042,7 +20248,7 @@ "destinations" : [ "`tests/integration/containers/malicious-child-zone.rs`" ], - "detailedDesign" : "Container-based penetration test running a real parent Zone and a deliberately malicious child Zone container that attempts, over a real ZoneLink: FD smuggling, credential-shaped byte injection, cross-Zone `ownerRef` forgery, capability-ceiling widening claims, and route-advertisement replay. Every attempt must be rejected by the parent with the specific typed error named in §10, and none may reach the parent's resource store, Credential state, or Host substrate Primary reuse disposition: `adapt`. Preserved source-plan detail: extract and adapt.", + "detailedDesign" : "Container-based penetration test running a real parent Zone and a deliberately malicious child Zone container. Before attack injection, assert the one ZoneLink row/handler exists only in the child store/runtime and the parent has only sealed `{ childZone, parentZone }` topology plus authenticated allocator/route projection state. The child then attempts, over that link: FD smuggling, credential-shaped byte injection, cross-Zone `ownerRef` forgery, capability-ceiling widening claims, and route-advertisement replay. Every attempt must be rejected by the parent with the specific typed error named in §10, and none may create a parent ZoneLink row or reach the parent's resource store, Credential state, or Host substrate Primary reuse disposition: `adapt`. Preserved source-plan detail: extract and adapt.", "entryContracts" : [ "ADR-046-security-and-threat-model" ], @@ -20602,7 +20808,7 @@ "destinations" : [ "Core `device-grant` audit and Provider controller Service/Binding ceremony lifecycle audit" ], - "detailedDesign" : "Path-free authority-grant records from Core and bounded Service/Binding/session digests/outcomes from controller; no path, raw target identity, LeaseId, session content, or CTAP bytes.", + "detailedDesign" : "Path-free authority-grant records from Core and bounded Service/Binding/session digests/outcomes from controller; no path, raw target identity, LeaseId, session content, or CTAP bytes. `resource_name_digest` is admitted only in the Core authority-grant audit after DeviceGrant authorization and is never copied to OTEL.", "entryContracts" : [ "ADR-046-provider-device-security-key" ], @@ -20626,7 +20832,7 @@ "destinations" : [ "Provider/controller bounded telemetry emitter and observability-otel handoff for security-key metrics" ], - "detailedDesign" : "OTEL metrics: `d2b_device_sk_session_total`, `d2b_device_sk_ceremony_duration_seconds`, `d2b_device_sk_relay_restarts_total` via bounded emitter ring; descriptors use only closed semantic labels and never Zone/resource-name-derived identity, while `d2b.zone` and `d2b.provider` remain OTEL resource attributes", + "detailedDesign" : "OTEL metrics: `d2b_device_sk_session_total`, `d2b_device_sk_ceremony_duration_seconds`, `d2b_device_sk_relay_restarts_total` via bounded emitter ring; descriptors use only closed semantic labels and never Zone/resource-name-derived identity. Provider spans use only fixed operation/phase/outcome/error-class attributes. Neither metrics nor spans admit a resource name, UID, ref, digest (including `resource_name_digest`), session ID, or derived identity token, while `d2b.zone` and `d2b.provider` remain OTEL Resource attributes.", "entryContracts" : [ "ADR-046-provider-device-security-key" ], @@ -20642,7 +20848,7 @@ ], "specId" : "ADR-046-provider-device-security-key", "topologicalRank" : 17, - "validation" : "Metrics tests structurally assert closed label sets, exact absence of `vm`, `zone`, `zone_id`, `zone_uid`, and resource-name-derived keys, Device/Zone-name canary absence, retained `d2b.zone` resource attributes, bounded ring behavior, and correct session/ceremony/restart counters.", + "validation" : "`telemetry_identity_canaries.rs` and metric inventory tests structurally assert closed label/span-attribute sets; exact absence of `vm`, `zone`, `zone_id`, `zone_uid`, resource-name-derived keys, and `resource_name_digest`; Device/Service/Binding/Guest/Zone name, UID, ref, and digest canary absence from metrics and spans; retained `d2b.zone` Resource attributes; bounded ring behavior; and correct session/ceremony/restart counters.", "wave" : "W6" }, { @@ -22047,9 +22253,9 @@ { "blockers" : [], "destinations" : [ - "`packages/d2b-telemetry/src/{trace_context.rs,audit_hash.rs,emitter.rs,meter_registry.rs,redaction_guard.rs}`" + "`packages/d2b-telemetry/src/{trace_context.rs,audit_hash.rs,emitter.rs,meter_registry.rs,metric_label_policy.rs,redaction_guard.rs}`" ], - "detailedDesign" : "`d2b-telemetry` provides: (1) `TraceContext` / `AuditHash` / `AuditChainLink` extracted unchanged; (2) `BoundedEmitter`: `tracing`-subscriber layer that serializes span/metric events into compact frames and writes them over a private Unix datagram socket to the `observability-otel` Provider — no `opentelemetry_sdk` dependency; (3) `RedactionGuard` span wrapper that asserts the v3 resource attribute allowlist at span creation. No OTEL SDK in this crate. Primary reuse disposition: `adapt`. Preserved source-plan detail: extract unchanged (`TraceContext`, `AuditHash`, `AuditChainLink`); adapt (`OperationId`/`CorrelationId` for v3 record contract); add bounded emitter.", + "detailedDesign" : "`d2b-telemetry` provides: (1) `TraceContext` / `AuditHash` / `AuditChainLink` extracted unchanged; (2) `BoundedEmitter`: `tracing`-subscriber layer that serializes span/metric events into compact frames and writes them over a private Unix datagram socket to the `observability-otel` Provider — no `opentelemetry_sdk` dependency; (3) the canonical closed `METRIC_LABEL_POLICY`, structural descriptor/data-point/exemplar validator, exact forbidden-key/suffix predicates, and non-serializable resource-identity canary matcher used by emitter defense in depth and the mandatory collector ingress gate; (4) `RedactionGuard` span wrapper that asserts the v3 resource attribute allowlist at span creation. No OTEL SDK in this crate. Primary reuse disposition: `adapt`. Preserved source-plan detail: extract unchanged (`TraceContext`, `AuditHash`, `AuditChainLink`); adapt (`OperationId`/`CorrelationId` for v3 record contract); add bounded emitter.", "entryContracts" : [ "ADR-046-telemetry-audit-and-support" ], @@ -22063,7 +22269,7 @@ ], "specId" : "ADR-046-telemetry-audit-and-support", "topologicalRank" : 10, - "validation" : "Unit test for `RedactionGuard` attribute gate; unit test for `BoundedEmitter` ring-full drop and FIFO drain; `policy_telemetry_redaction.rs::startup_tracing_avoids_host_path_fields` port; assert `config_source = \"realm-controllers\"` absent; assert no `opentelemetry_sdk` dependency in `d2b-telemetry` Cargo.toml", + "validation" : "Unit test for `RedactionGuard` attribute gate; unit test for `BoundedEmitter` ring-full drop and FIFO drain; table tests for `METRIC_LABEL_POLICY` exact keys, suffixes, and `metadata.name`/UID/ResourceRef identity canaries; `policy_telemetry_redaction.rs::startup_tracing_avoids_host_path_fields` port; assert `config_source = \"realm-controllers\"` absent; assert no `opentelemetry_sdk` dependency in `d2b-telemetry` Cargo.toml", "wave" : "W5" }, { @@ -22168,7 +22374,7 @@ "destinations" : [ "`packages/d2b-provider-observability-otel/src/`, `nixos-modules/components/observability/` (adapted files)" ], - "detailedDesign" : "`Provider/observability-otel` is an **ordinary optional non-bootstrap Process** (not counted toward the ≤64 MiB mandatory core aggregate). It owns: (1) per-Zone datagram receiver socket at `$ZONE_STATE/telemetry/emitter.sock` (drains frames from core emitters) and OTLP/gRPC Unix socket at `$ZONE_STATE/telemetry/otlp.sock`; (2) the full OTEL SDK with OTLP exporter — only this process links `opentelemetry_sdk`; (3) OTel Collector pipeline per Zone and per Host; (4) vsock OTLP forwarding to obs Zone (replaces socat-based `OtelHostBridgeArgvInputs`); (5) SigNoz stack Nix adapted from `stack.nix` with per-Zone `ingressSources` replacing per-VM `vmName`; (6) journald scrape (optional, disabled by default); (7) self-metrics endpoint. Zone/controller startup does not wait for this Provider. If absent or unready, Zone health is `Degraded` (not `Failed`). Readiness: socket exists and first drain cycle completes successfully. `d2b.observability.host.identityName` option preserved; `vmName` in `ingressSources` populated from Zone name. Primary reuse disposition: `adapt`. Preserved source-plan detail: adapt Nix pipeline shape (replace per-VM `vmName` with per-Zone naming); adapt `OtelHostBridgeArgvInputs` vsock forwarding to native OTLP/gRPC-over-vsock; adapt readiness gate pattern (`OtelHostBridgeReadiness::Ready` → Provider phase `Ready`); adapt `ingressSources` per-VM → per-Zone.", + "detailedDesign" : "`Provider/observability-otel` is an **ordinary optional non-bootstrap Process** (not counted toward the ≤64 MiB mandatory core aggregate). It owns: (1) per-Zone datagram receiver socket at `$ZONE_STATE/telemetry/emitter.sock` (drains frames from core emitters) and OTLP/gRPC Unix socket at `$ZONE_STATE/telemetry/otlp.sock`; (2) the full OTEL SDK with OTLP exporter — only this process links `opentelemetry_sdk`; (3) OTel Collector pipeline per Zone and per Host; (4) vsock OTLP forwarding to obs Zone (replaces socat-based `OtelHostBridgeArgvInputs`); (5) D096 import-stream ingest; (6) one structural `METRIC_LABEL_POLICY` gate shared by Unix emitter, OTLP Unix, OTLP/vsock, and import-stream metrics before aggregation, queueing, batching, retry, or export, with bounded non-echoing errors/quarantine/backpressure; (7) SigNoz stack Nix adapted from `stack.nix` with per-Zone `ingressSources` replacing per-VM `vmName`; (8) journald scrape (optional, disabled by default); (9) self-metrics endpoint. Trusted producer identity is stamped only into allow-listed OTEL Resource attributes; audit remains separate. Zone/controller startup does not wait for this Provider. If absent or unready, Zone health is `Degraded` (not `Failed`). Readiness: socket exists and first drain cycle completes successfully. `d2b.observability.host.identityName` option preserved; `vmName` in `ingressSources` populated from Zone name. Primary reuse disposition: `adapt`. Preserved source-plan detail: adapt Nix pipeline shape (replace per-VM `vmName` with per-Zone naming); adapt `OtelHostBridgeArgvInputs` vsock forwarding to native OTLP/gRPC-over-vsock; adapt readiness gate pattern (`OtelHostBridgeReadiness::Ready` → Provider phase `Ready`); adapt `ingressSources` per-VM → per-Zone.", "entryContracts" : [ "ADR-046-telemetry-audit-and-support" ], @@ -22184,7 +22390,7 @@ ], "specId" : "ADR-046-telemetry-audit-and-support", "topologicalRank" : 10, - "validation" : "`emitter_socket_receive`, `emitter_ring_drains_on_socket_available`, `emitter_ring_drop_on_overflow`, `no_vm_label_in_metrics`, `zone_startup_proceeds_without_provider` tests; adapted `policy_observability.rs` tests (retain `loki_native_otel_resource_attributes` and SigNoz-only backend assertions); adapted `minijail_relay_otel.rs` shape test for Provider-managed runner", + "validation" : "`emitter_socket_receive`, `emitter_ring_drains_on_socket_available`, `emitter_ring_drop_on_overflow`, table-driven `ingress_metric_policy` across all four ingress adapters, `no_vm_label_in_metrics`, and `zone_startup_proceeds_without_provider` tests; adapted `policy_observability.rs` tests (retain `loki_native_otel_resource_attributes` and SigNoz-only backend assertions); adapted `minijail_relay_otel.rs` shape test for Provider-managed runner", "wave" : "W5" }, { @@ -22215,7 +22421,7 @@ "destinations" : [ "`packages/d2b-contract-tests/tests/policy_telemetry_redaction.rs` (new); updated `policy_observability.rs`; updated `policy_metrics.rs`" ], - "detailedDesign" : "(1) Extend `loki_native_otel_resource_attributes` allowlist to include `d2b.zone`, `d2b.provider`, `d2b.component`, `service.version`. (2) Add redaction lint: scan all v3 instrumentation call sites for `realm`, `workload_id`, `node_id`, `vm` (as label key), `path`, `socket`, `argv`, `pid`, `exe`. (3) Add structural metric-label policy lint: parse every v3 `MetricDescriptor`, require each label key and value domain to exist in the closed `METRIC_LABEL_POLICY`, reject exact keys `vm`, `zone`, `zone_id`, `zone_uid`, `credential_name`, `network`, `network_name`, and `link_name_hash`, reject resource-name-derived key suffixes `*_name`, `*_name_hash`, `*_name_digest`, and `*_uid`, and prove a `metadata.name` canary never enters label values. Fixed semantic labels remain allowed only with closed domains. (4) Assert the `d2b.zone` resource attribute remains present. (5) Add bucket boundary gates for 5 ms and 20 ms. (6) Retain: `startup_tracing_avoids_host_path_fields`; SigNoz-only backend assertion; `tempo_guest_collector_shape`; `config_source = \"realm-controllers\"` absence gate. Primary reuse disposition: `adapt`. Preserved source-plan detail: adapt and extend; keep existing tests; add new policy gates.", + "detailedDesign" : "(1) Extend `loki_native_otel_resource_attributes` allowlist to include `d2b.zone`, `d2b.provider`, `d2b.component`, `service.version`. (2) Add redaction lint: scan all v3 instrumentation call sites for `realm`, `workload_id`, `node_id`, `vm` (as label key), `path`, `socket`, `argv`, `pid`, `exe`. (3) Add structural metric-label policy lint: parse every v3 `MetricDescriptor`, require each label key and value domain to exist in the closed `METRIC_LABEL_POLICY`, reject exact keys `vm`, `zone`, `zone_id`, `zone_uid`, `credential_name`, `network`, `network_name`, and `link_name_hash`, reject resource-name-derived key suffixes `*_name`, `*_name_hash`, `*_name_digest`, and `*_uid`, and prove `metadata.name`, UID, ResourceRef, and resource-identity canaries never enter label values. Fixed semantic labels remain allowed only with closed domains. (4) Prove the observability Provider's Unix-emitter, OTLP-Unix, OTLP/vsock, and import-stream adapters all invoke that validator before queue/batch/export; assert whole-frame rejection, bounded non-echoing error classes/quarantine, and policy-before-capacity backpressure. (5) Assert the `d2b.zone` Resource attribute remains present and audit is unchanged. (6) Add bucket boundary gates for 5 ms and 20 ms. (7) Retain: `startup_tracing_avoids_host_path_fields`; SigNoz-only backend assertion; `tempo_guest_collector_shape`; `config_source = \"realm-controllers\"` absence gate. Primary reuse disposition: `adapt`. Preserved source-plan detail: adapt and extend; keep existing tests; add new policy gates.", "entryContracts" : [ "ADR-046-telemetry-audit-and-support" ], @@ -22334,7 +22540,7 @@ "destinations" : [ "`packages/d2b-provider-transport-azure-relay/src/credential_client.rs`" ], - "detailedDesign" : "Async Credential KK session client for service components (service processes have d2b-bus access, enabling Credential KK; workers do not); acquire listener credential via KK inside the gateway Guest using `config.credentialBindings[listenerCredentialAlias]`; acquire sender credential independently inside the child's gateway Guest via the same KK model using `config.credentialBindings[senderCredentialAlias]`; raw credential bytes held in zeroizing memory inside the gateway Guest, presented to Azure Relay, then immediately zeroized; no credential bytes cross process, network, or Guest boundary; redacted Debug; no credential bytes in logs/audit/OTEL; core ProviderDeployment creates a private persistent Volume (per ADR-046-provider-state) for each component before its Process starts — the transport Provider does not own or create these Volumes; `Provider/volume-local` reconciles them; `migrationPolicy: none` means no migration worker is ever spawned; no relay auth token, WebSocket handle, session key, or credential byte is written to that Volume; all relay session state remains transient in-process memory", + "detailedDesign" : "Async Credential KK session client for service components (service processes have d2b-bus access, enabling Credential KK; workers do not); child core validates the same-Zone refs in `spec.transportCredentials`, selects the unique listener or sender ref by Credential audience, and supplies it to the child-local Provider service inside its gateway Guest; raw credential bytes are held in zeroizing memory inside the gateway Guest, presented to Azure Relay, then immediately zeroized; no Credential ref or byte crosses a Zone or Guest boundary, and byte delivery between the Credential Provider and consuming service remains inside the protected KK session; the parent allocator receives neither refs nor credentials and keeps only sealed route state; redacted Debug; no credential bytes in logs/audit/OTEL; the child Zone's core ProviderDeployment creates a private persistent Volume (per ADR-046-provider-state) for each component before its Process starts — the transport Provider does not own or create these Volumes; `Provider/volume-local` reconciles them; `migrationPolicy: none` means no migration worker is ever spawned; no relay auth token, WebSocket handle, session key, or credential byte is written to that Volume; all relay session state remains transient in-process memory", "entryContracts" : [ "ADR-046-provider-transport-azure-relay" ], @@ -22364,7 +22570,7 @@ "exitGate" : "ADR046-W6 exit criteria (ADR-046-validation-and-delivery §4): every spec/work item in this wave Merged with clean destinations, all validators green, and the ten-role panel seal recorded", "id" : "ADR046-transport-relay-003", "kind" : "work-item", - "owner" : "ADR046-transport-relay-001; reconnect contract; ZoneLink handler", + "owner" : "ADR046-transport-relay-001; reconnect contract; child Zone's ZoneLink handler", "parallelGroup" : "wi:ADR-046-provider-transport-azure-relay", "prerequisites" : [ "ADR-046-provider-transport-azure-relay", @@ -22380,7 +22586,7 @@ "destinations" : [ "`packages/d2b-provider-transport-azure-relay/src/transport_settings.rs`; `docs/reference/schemas/v3/providers/transport-azure-relay.transport-settings.json`" ], - "detailedDesign" : "`AzureRelayTransportSettings` Rust struct with serde; validation against committed JSON Schema; reject `secret`-annotated fields; enforce `^[a-z][a-z0-9-]*$` pattern for `listenerCredentialAlias`/`senderCredentialAlias` alias ID fields (never `Credential/` refs); xtask `gen-provider-transport-schemas` integration", + "detailedDesign" : "`AzureRelayTransportSettings` Rust struct with serde for only `relayNamespaceId` and `relayEntityId`; validation against committed JSON Schema; reject secret-shaped fields/values; generate and admit the exact six-field ZoneLink base; reject legacy provider envelopes and allocator-private fingerprint/capability fields; resolve `spec.transportProviderRef` before schema validation; validate exactly two same-Zone `spec.transportCredentials` refs with one `azure-relay-listen` and one `azure-relay-send` audience; enforce `disabled`/`limits` in child core; xtask `gen-provider-transport-schemas` integration", "entryContracts" : [ "ADR-046-provider-transport-azure-relay" ], @@ -22395,7 +22601,7 @@ ], "specId" : "ADR-046-provider-transport-azure-relay", "topologicalRank" : 12, - "validation" : "`tests/transport_settings_schema.rs`: valid/invalid schema vectors; eval-time Nix assertion coverage from `nix-unit: transport-settings-secret-key` test (see zone-routing spec)", + "validation" : "`tests/transport_settings_schema.rs`: valid/invalid schema vectors; `tests/transport_credentials.rs`: exact canonical ZoneLink field set, same-Zone ref/count/audience/scope checks, and rejection of credential refs inside `transportSettings`; eval-time Nix assertion coverage from `nix-unit: transport-settings-secret-key` test (see zone-routing spec)", "wave" : "W6" }, { @@ -22453,7 +22659,7 @@ "destinations" : [ "`packages/d2b-provider-transport-azure-relay/src/tests/integration/README`" ], - "detailedDesign" : "Required content: fake relay server setup and teardown using the injected fake Relay effect port; how to run hermetic integration tests without a live Azure service; how to configure the injected fake Credential effect port for credential delivery tests; how to run with a real Azure namespace (requires a `Credential` resource declared in `spec.config.credentialBindings`, not environment-variable credential paths); integration test scenarios and expected outcomes; CI/local execution instructions", + "detailedDesign" : "Required content: fake relay server setup and teardown using the injected fake Relay effect port; how to run hermetic integration tests without a live Azure service; how to configure the injected fake Credential effect port for credential delivery tests; the fixture declares compiler-only `k2.parentZone = \"local-root\"` and puts the exact-shape ZoneLink, selected Provider, Network, Credentials, Process, and Endpoint resources only in K2; local-root's store is asserted to contain no reciprocal ZoneLink or Provider; how to run with a real Azure namespace (requires same-child-Zone `Credential` resources listed in `spec.transportCredentials`, not environment-variable credential paths); integration test scenarios and expected outcomes; CI/local execution instructions", "entryContracts" : [ "ADR-046-provider-transport-azure-relay" ], @@ -22635,7 +22841,7 @@ ], "specId" : "ADR-046-provider-transport-unix", "topologicalRank" : 14, - "validation" : "`tests/portal.rs::open_transport_zone_link_validates_and_returns_ownedtransport`; `open_transport_local_portal_seqpacket_with_attachments_accepted`; `open_transport_zone_link_attachments_enabled_rejected`; `close_transport_is_idempotent_after_handle_removed`; `observe_transport_delivers_pollhup_as_peer_disconnected`; `handle_table_rejects_at_max_capacity`; `restart_clears_all_handles`", + "validation" : "`tests/portal.rs::open_transport_zone_link_validates_and_returns_ownedtransport`; `open_transport_uses_child_bootstrap_endpoint_only`; `parent_endpoint_never_enters_provider_portal`; `open_transport_local_portal_seqpacket_with_attachments_accepted`; `open_transport_zone_link_attachments_enabled_rejected`; `close_transport_is_idempotent_after_handle_removed`; `observe_transport_delivers_pollhup_as_peer_disconnected`; `handle_table_rejects_at_max_capacity`; `restart_clears_all_handles`", "wave" : "W6" }, { @@ -22665,9 +22871,9 @@ { "blockers" : [], "destinations" : [ - "`docs/reference/schemas/v3/providers/transport-unix.transport-binding.json`; `nixos-modules/assertions.nix` (assertion additions); generated `nixos-modules/generated/options-zones-ZoneLink.nix` provider-settings submodule" + "`docs/reference/schemas/v3/providers/transport-unix.transport-binding.json`; `nixos-modules/assertions.nix` (assertion additions); generated `nixos-modules/generated/options-zones-ZoneLink.nix` `transportSettings` submodule" ], - "detailedDesign" : "Commit the JSON Schema; run `xtask gen-zone-schemas` and `xtask gen-zone-nix-options` to regenerate committed files; add two assertions to `assertions.nix` (stream+attachments conflict; sensitive key names); `xtask gen-zone-resources` adds provider-specific settings validation step", + "detailedDesign" : "Commit the JSON Schema; run `xtask gen-zone-schemas` and `xtask gen-zone-nix-options` to regenerate committed files; generate the exact six-field ZoneLink base; reject legacy provider envelopes and allocator-private fingerprint/capability fields; add assertions for stream+attachments conflict and sensitive key names; require an empty `transportCredentials` list; reuse the common topology assertions to require the ZoneLink and selected Provider in the same child Zone, self-matching `childZoneName`, compiler-only non-root `parentZone`, and no reciprocal parent resource; `xtask gen-zone-resources` adds Provider-selected `transportSettings` validation", "entryContracts" : [ "ADR-046-provider-transport-unix" ], @@ -22683,7 +22889,7 @@ ], "specId" : "ADR-046-provider-transport-unix", "topologicalRank" : 13, - "validation" : "All seven eval/build tests in the Nix section", + "validation" : "All eval/build tests in the Nix section, including `transport-unix-exact-zonelink-spec`, `transport-unix-child-local-topology`, legacy-field rejection, empty-credential enforcement, and a generated-bundle assertion that the parent store has no reciprocal Provider/ZoneLink row", "wave" : "W6" }, { @@ -22714,7 +22920,7 @@ "destinations" : [ "`packages/d2b-provider-transport-unix/integration/` and `integration/README.md`" ], - "detailedDesign" : "Four scenarios: `transport_open.rs` (fake Zone portal, allocator-socketpair FD attachment in → OwnedTransport attachment out → verify socket kind, CLOEXEC, SO_PASSCRED enabled; p95 latency assertion ≤2 ms); `fd_transfer.rs` (seqpacket `SCM_RIGHTS` transfer through opened transport, credit accounting, scavenge on error injection); `reconnect.rs` (CloseTransport + re-OpenTransport with fresh socketpair, verify previous handle is unknown, verify monitoring dup closed); `observation_stream.rs` (ObserveTransport stream receives `PEER_DISCONNECTED` event when peer closes socketpair end within 5 ms p95). `integration/README.md` documents prerequisites (no KVM required; all scenarios use in-process socketpairs and fake Zone API endpoint stub), invocation (`cargo test -p d2b-provider-transport-unix --test integration`), environment variables, and expected output", + "detailedDesign" : "Four scenarios: `transport_open.rs` (fixture models compiler-only `k1.parentZone = \"local-root\"`; fake selected allocator retains one socketpair endpoint as sealed route state and injects the other into K1 bootstrap; K1 core passes only that child endpoint to its same-Zone Provider → OwnedTransport out; verify socket kind, CLOEXEC, SO_PASSCRED, K1-local Provider/ZoneLink, and absent local-root reciprocal row; p95 latency assertion ≤2 ms); `fd_transfer.rs` (within-Zone local-portal seqpacket `SCM_RIGHTS` transfer, credit accounting, scavenge on error injection; ZoneLink route rejects the same packet); `reconnect.rs` (CloseTransport + re-OpenTransport with a fresh sealed child endpoint, verify previous handle is unknown and monitoring dup closed); `observation_stream.rs` (ObserveTransport stream receives `PEER_DISCONNECTED` when peer closes within 5 ms p95). `integration/README.md` documents prerequisites (no KVM required; all scenarios use in-process socketpairs and fake Zone API endpoint stub), invocation (`cargo test -p d2b-provider-transport-unix --test integration`), environment variables, and expected output", "entryContracts" : [ "ADR-046-provider-transport-unix" ], @@ -22740,7 +22946,7 @@ "destinations" : [ "packages/d2b-contracts/src/usbip_effect_port.rs" ], - "detailedDesign" : "Define UsbipEffectPort and UsbipGuestEffectPort in d2b-contracts with DeviceUid, NetworkUid, UsbBindingUid, LeaseToken, FirewallToken, FirewallObservation, KernelModuleClass, DeviceProbeResult, and UsbipEffectError; export traits/types only with no implementation; keep firewall apply/observe/release Network/busid-scoped, attach/detach Binding-addressed, and all fd/path/busid values private. `TransientDetail` derives `Clone, PartialEq, Eq` while retaining manual redacted Debug/Display so `UsbipEffectError`'s derives compile without disclosure. Primary reuse disposition: `create`. Preserved source-plan detail: net-new trait definition.", + "detailedDesign" : "Define UsbipEffectPort and UsbipGuestEffectPort in d2b-contracts with DeviceUid, NetworkUid, UsbBindingUid, LeaseToken, FirewallToken, FirewallGenerationFence, FirewallObservation, KernelModuleClass, DeviceProbeResult, and UsbipEffectError; export traits/types only with no implementation. `apply_firewall` and `release_firewall` both accept expected Network/Service generations; release also accepts NetworkUid and borrows the token so the controller can retain it until confirmed effect. Keep firewall apply/observe/release Network/busid-scoped, attach/detach Binding-addressed, and all fd/path/busid values private. `TransientDetail` derives `Clone, PartialEq, Eq` while retaining manual redacted Debug/Display so `UsbipEffectError`'s derives compile without disclosure. Primary reuse disposition: `create`. Preserved source-plan detail: net-new trait definition.", "entryContracts" : [ "ADR-046-provider-device-usbip" ], @@ -22754,7 +22960,7 @@ ], "specId" : "ADR-046-provider-device-usbip", "topologicalRank" : 11, - "validation" : "d2b-contracts tests for trait object safety, firewall apply/observe/release signatures, `UsbipEffectError: Clone + PartialEq + Eq`, `TransientDetail` clone/equality, redacted Debug/Display behavior, and no implementation leakage.", + "validation" : "d2b-contracts tests for trait object safety, generation-fenced firewall apply/observe/release signatures (release requires NetworkUid and token), `FirewallGenerationMismatch`, `UsbipEffectError: Clone + PartialEq + Eq`, `TransientDetail` clone/equality, redacted Debug/Display behavior, and no implementation leakage.", "wave" : "W6" }, { @@ -22762,7 +22968,7 @@ "destinations" : [ "packages/d2b-core/src/device_usbip_adapter.rs" ], - "detailedDesign" : "Implement UsbipEffectPort in the core adapter: signed-bundle busid lookup, same-Zone validation, trusted physical-USB identity resolution, mandatory Core-derived `physical-usb-backing/v1` digest and exact `(Host, physical-usb-backing, opaqueKeyDigest)` claim shared with every security-key/USB Provider, exclusive OFD claim, sole ownership of all USBIP TCP/3240 and exact per-Network/per-busid `UsbipBindFirewallRule` effects plus ownership-scoped observe/release, anti-spoof probe, one shared Host module/backend authority, one Core-derived D097 relay Endpoint/firewall authority per Network, D097 authority-index preflight/adoption, and post-effect audit; complete the shared claim before any open, withhold, bind, module, relay, firewall, or attachment effect; never expose raw busid, identity digest, path, fd, bind address, nftables body, audit structs, or broker wire types. Primary reuse disposition: `adapt`. Preserved source-plan detail: extract and adapt into framework-internal adapter.", + "detailedDesign" : "Implement UsbipEffectPort in the core adapter: signed-bundle busid lookup, same-Zone validation, trusted physical-USB identity resolution, mandatory Core-derived `physical-usb-backing/v1` digest and exact `(Host, physical-usb-backing, opaqueKeyDigest)` claim shared with every security-key/USB Provider, exclusive OFD claim, sole ownership of all USBIP TCP/3240 and exact per-Network/per-busid effects through the existing closed `UsbipBindFirewallRule` request with action enum `Ensure|Remove`, ownership-scoped observe/release, anti-spoof probe, one shared Host module/backend authority, one Core-derived D097 relay Endpoint/firewall authority per Network, D097 authority-index preflight/adoption, and post-effect audit. `Ensure` applies; `Remove` uses the same request, binds expected Network/Service generations, validates exact ownership, succeeds idempotently on validated absence, and fails closed on a foreign marker. No separate release op exists. Complete the shared claim before any open, withhold, bind, module, relay, firewall, or attachment effect; never expose raw busid, identity digest, path, fd, bind address, nftables body, audit structs, or broker wire types. Primary reuse disposition: `adapt`. Preserved source-plan detail: extract and adapt into framework-internal adapter.", "entryContracts" : [ "ADR-046-provider-device-usbip" ], @@ -22778,7 +22984,7 @@ ], "specId" : "ADR-046-provider-device-usbip", "topologicalRank" : 14, - "validation" : "Fast packages/d2b-core/tests/device_usbip_adapter.rs covers same-Zone gate, exact shared physical backing tuple derivation, byte-identical keys for USB/security-key views of one fake token, Provider-private-class/digest bypass rejection, `physical-usb-backing-conflict` before effects, separate USBIP module/relay authorities, one-module/one-relay reuse, exact Network/busid firewall scoping, foreign-marker failure, ownership-scoped drift/status, independent per-busid release, no network-local dependency, anti-spoof, redaction, broker mapping, and no digest/busid/path/fd exposure.", + "validation" : "Fast packages/d2b-core/tests/device_usbip_adapter.rs covers same-Zone gate, exact shared physical backing tuple derivation, byte-identical keys for USB/security-key views of one fake token, Provider-private-class/digest bypass rejection, `physical-usb-backing-conflict` before effects, separate USBIP module/relay authorities, one-module/one-relay reuse, exact closed `Ensure|Remove` decoding and same-request broker mapping, unknown-action rejection, expected Network/Service generation binding, exact Network/busid firewall scoping, validated-absence `Remove`, foreign-marker failure without mutation, ownership-scoped drift/status, transient retry with retained token/authority, independent per-busid release, no network-local dependency, anti-spoof, redaction, and no digest/busid/path/fd exposure.", "wave" : "W6" }, { @@ -22810,7 +23016,7 @@ "destinations" : [ "packages/d2b-provider-device-usbip/src/{controller,reconcile,export_import}.rs" ], - "detailedDesign" : "Reconcile provider-neutral authority/projection `UsbService` and per-Guest `UsbBinding` resources through strict USBIP Provider extensions, consuming UsbipEffectPort, the exact shared Host-global physical USB tuple, USBIP-private D097 authorities, and signed D096 ExportAdapter/ImportAdapter. Enforce same-Zone base and provider refs; ResourceExport authority-Service-only target; same-type ResourceImport-owned projection with `providerRef`, semantic base/import fields, no `spec.provider`, and no physical fields/effects; route selection from the signed local descriptor/import record; semantic factory fingerprint independent of separately authenticated adapter identity; exclusive fair Binding admission; encrypted bounded named-stream control/data; D088 layered status; Service/Binding finalizers; restart adoption; declared/explicit modes; no session/transfer resources. Primary reuse disposition: `adapt`. Preserved source-plan detail: extract and adapt step machine into Provider reconcile loop.", + "detailedDesign" : "Reconcile provider-neutral authority/projection `UsbService` and per-Guest `UsbBinding` resources through strict USBIP Provider extensions, consuming UsbipEffectPort, the exact shared Host-global physical USB tuple, USBIP-private D097 authorities, and signed D096 ExportAdapter/ImportAdapter. Enforce same-Zone base and provider refs; ResourceExport authority-Service-only target; same-type ResourceImport-owned projection with `providerRef`, semantic base/import fields, no `spec.provider`, and no physical fields/effects; route selection from the signed local descriptor/import record; semantic factory fingerprint independent of separately authenticated adapter identity; exclusive fair Binding admission; encrypted bounded named-stream control/data; D088 layered status; Service/Binding finalizers; restart adoption; declared/explicit modes; no session/transfer resources. Firewall reconcile maps apply to `UsbipBindFirewallRule` action `Ensure`; finalization maps release to action `Remove` on the same request with expected Network/Service generations. Finalization retains firewall token/status and relay authority until confirmed effect, retries transient failure, refreshes on generation mismatch, and blocks fail-closed on a foreign marker. Primary reuse disposition: `adapt`. Preserved source-plan detail: extract and adapt step machine into Provider reconcile loop.", "entryContracts" : [ "ADR-046-provider-device-usbip" ], @@ -22828,7 +23034,7 @@ ], "specId" : "ADR-046-provider-device-usbip", "topologicalRank" : 15, - "validation" : "Fast tests/controller_state_machine.rs, service_binding_schema.rs, export_import.rs, authority_conflict.rs, async_loop.rs, finalizer.rs, and wrong_zone.rs cover authority/projection/Binding lifecycle, Service-only export, projection `spec.provider` rejection, semantic fingerprint stability under adapter identity changes plus separate descriptor authentication, D088 `status.resource`/`status.provider` placement, shared physical tuple collision before effects, encrypted fake streams, no physical projection effect, exclusivity, restart, and WrongZone degradation.", + "validation" : "Fast tests/controller_state_machine.rs, service_binding_schema.rs, export_import.rs, authority_conflict.rs, async_loop.rs, finalizer.rs, and wrong_zone.rs cover authority/projection/Binding lifecycle, Service-only export, projection `spec.provider` rejection, semantic fingerprint stability under adapter identity changes plus separate descriptor authentication, D088 `status.resource`/`status.provider` placement, shared physical tuple collision before effects, encrypted fake streams, no physical projection effect, exclusivity, restart, WrongZone degradation, exact `Ensure|Remove` mapping, and firewall status/token/authority retention until `Remove` confirmation.", "wave" : "W6" }, { @@ -22859,7 +23065,7 @@ "destinations" : [ "packages/d2b-provider-device-usbip/src/status.rs" ], - "detailedDesign" : "Define provider-neutral `UsbService` `status.resource` with whole-device availability/access counts, authority-only `physical-usb-backing` claim state, and projection-only common import state; define `UsbBinding.status.resource` with generic attachment phase/queue/generation/timestamps. Define separate strict `status.provider` USBIP details: Device probe, owner backend/relay, USBIP-owned `firewallState`/opaque digest/observed generation, imported-route observations, and Binding proxy/private Endpoint/subphase. No common access/import/attachment field appears directly under `status`; no USBIP module, Network, Endpoint, proxy, server/client, port, firewall, or busid field enters base status; no raw backing digest, busid, path, fd, address, session/transfer ID, remote identity, or payload appears anywhere. Primary reuse disposition: `adapt`. Preserved source-plan detail: adapt state fields to typed status.provider.details.", + "detailedDesign" : "Define provider-neutral `UsbService` `status.resource` with whole-device availability/access counts, authority-only `physical-usb-backing` claim state, and projection-only common import state; define `UsbBinding.status.resource` with generic attachment phase/queue/generation/timestamps. Define separate strict `status.provider` USBIP details: Device probe, owner backend/relay, USBIP-owned `firewallState`/opaque digest/observed generation, imported-route observations, and Binding proxy/private Endpoint/subphase. During `UsbipBindFirewallRule` action `Remove`, strict status remains `firewallState: releasing`; the token and relay authority remain held until confirmed success, including validated absence. No common access/import/attachment field appears directly under `status`; no USBIP module, Network, Endpoint, proxy, server/client, port, firewall, or busid field enters base status; no raw backing digest, busid, path, fd, address, session/transfer ID, remote identity, or payload appears anywhere. Primary reuse disposition: `adapt`. Preserved source-plan detail: adapt state fields to typed status.provider.details.", "entryContracts" : [ "ADR-046-provider-device-usbip" ], @@ -22874,7 +23080,7 @@ ], "specId" : "ADR-046-provider-device-usbip", "topologicalRank" : 13, - "validation" : "Fast tests/status_serde.rs covers generic base status plus three strict USBIP detail schemas, exact `status.resource` placement for access/import/attachment/backing claim, exact `status.provider` placement for relay/firewall implementation observations, drift transition and ownership, rejection of semantic fields directly under `status`, mode-dependent omissions, bounded counts/refs, unknown-field denial, and a deny corpus proving USBIP-only fields are rejected from base and Network status.", + "validation" : "Fast tests/status_serde.rs covers generic base status plus three strict USBIP detail schemas, exact `status.resource` placement for access/import/attachment/backing claim, exact `status.provider` placement for relay/firewall implementation observations, drift transition and ownership, `releasing` retention until `Remove` confirmation, rejection of semantic fields directly under `status`, mode-dependent omissions, bounded counts/refs, unknown-field denial, and a deny corpus proving USBIP-only fields are rejected from base and Network status.", "wave" : "W6" }, { @@ -22882,7 +23088,7 @@ "destinations" : [ "packages/d2b-provider-device-usbip/{src,tests,integration/README.md}; tests/host-integration/usbip-service.nix; tests/host-integration/hardware/usbip-service.sh" ], - "detailedDesign" : "Put provider-neutral Service/Binding base-schema separation, strict USBIP extensions, projection `spec.provider` rejection, D088 layered status, semantic factory-fingerprint independence from Provider/adapter identity, exact shared physical backing tuple/conflict, one Core-derived per-Network relay Endpoint/firewall authority, exact per-busid firewall ownership/drift/release, arbitration, same-type export/import, encrypted fake-stream, and least-privilege process/Endpoint shape coverage in fast Layer-1 Rust tests. Include a fake direct-local Provider proving the same base contract has no USBIP dependency. Reserve runNixOSTest for real Linux usbip_host/vhci_hcd, usbipd, namespaces/nftables, TCP 3240, zero network-local carve-out, and Guest checks; reserve the hardware script for an approved physical device. Use existing Make gates only. Primary reuse disposition: `adapt`. Preserved source-plan detail: adapt existing network-scoping assertion and add new scenarios.", + "detailedDesign" : "Put provider-neutral Service/Binding base-schema separation, strict USBIP extensions, projection `spec.provider` rejection, D088 layered status, semantic factory-fingerprint independence from Provider/adapter identity, exact shared physical backing tuple/conflict, one Core-derived per-Network relay Endpoint/firewall authority, exact per-busid firewall ownership/drift/release through one `UsbipBindFirewallRule` request with closed `Ensure|Remove`, generation fencing, idempotent validated-absence `Remove`, foreign-marker fail-closed behavior, and status/token/authority retention until confirmation, arbitration, same-type export/import, encrypted fake-stream, and least-privilege process/Endpoint shape coverage in fast Layer-1 Rust tests. Include a fake direct-local Provider proving the same base contract has no USBIP dependency. Reserve runNixOSTest for real Linux usbip_host/vhci_hcd, usbipd, namespaces/nftables, TCP 3240, zero network-local carve-out, and Guest checks; reserve the hardware script for an approved physical device. Use existing Make gates only. Primary reuse disposition: `adapt`. Preserved source-plan detail: adapt existing network-scoping assertion and add new scenarios.", "entryContracts" : [ "ADR-046-provider-device-usbip" ], @@ -22906,7 +23112,7 @@ "destinations" : [ "nixos-modules/components/usbip.nix, nixos-modules/options-zones.nix, nixos-modules/assertions.nix" ], - "detailedDesign" : "Add Provider config; remove the old per-VM option; emit provider-neutral authority `UsbService`, per-Guest `UsbBinding`, strict USBIP Provider envelopes, and optional ResourceExport/ResourceImport authoring shapes; imported same-type projection Services remain Core-created with no `spec.provider`. Assert same-Zone base/provider refs, projection ownerRef/forbidden implementation and physical fields, Service-only export target, one Host backend/Network relay, exact Core-derived Host-global `(Host, physical-usb-backing, opaqueKeyDigest)` exclusion shared by USB/security-key, Provider-private-class bypass rejection, and retain guest vhci_hcd/tools. Primary reuse disposition: `adapt`. Preserved source-plan detail: adapt guest module, remove host-side option surface, and extend eval assertions.", + "detailedDesign" : "Add Provider config; remove the old per-VM option; emit provider-neutral authority `UsbService`, per-Guest `UsbBinding`, strict USBIP Provider envelopes, and optional ResourceExport/ResourceImport authoring shapes; imported same-type projection Services remain Core-created with no `spec.provider`. Assert same-Zone base/provider refs, projection ownerRef/forbidden implementation and physical fields, Service-only export target, one Host backend/Network relay, exact Core-derived Host-global `(Host, physical-usb-backing, opaqueKeyDigest)` exclusion shared by USB/security-key, Provider-private-class bypass rejection, one `UsbipBindFirewallRule` contract with exact actions `Ensure|Remove` and no separate release operation, and retain guest vhci_hcd/tools. Primary reuse disposition: `adapt`. Preserved source-plan detail: adapt guest module, remove host-side option surface, and extend eval assertions.", "entryContracts" : [ "ADR-046-provider-device-usbip" ], @@ -22923,7 +23129,7 @@ ], "specId" : "ADR-046-provider-device-usbip", "topologicalRank" : 15, - "validation" : "Fast tests/unit/nix/cases/usbip-*.nix cover schema shape, Core projection `spec.provider` rejection, D088 layered status fixtures including USBIP-owned firewall state, all reference/owner/export assertions, byte-identical USB/security-key tuple collision and private-class bypass rejection, one Core-derived 3240 Endpoint/firewall authority per Network, least-privilege consumer policy, absence of network-local 3240 rules, old-option removal, and guest module retention.", + "validation" : "Fast tests/unit/nix/cases/usbip-*.nix cover schema shape, Core projection `spec.provider` rejection, D088 layered status fixtures including USBIP-owned firewall state and `releasing` retention, all reference/owner/export assertions, byte-identical USB/security-key tuple collision and private-class bypass rejection, one Core-derived 3240 Endpoint/firewall authority per Network, exact `Ensure|Remove` broker action surface with unknown values rejected and no second op, least-privilege consumer policy, absence of network-local 3240 rules, old-option removal, and guest module retention.", "wave" : "W6" }, { @@ -22976,9 +23182,9 @@ { "blockers" : [], "destinations" : [ - "`d2b-contracts/src/v3/volume_layout.rs` (LayoutEntry, EntryType, all policy enums, AclGrant, Invariant, SensitivityClass); `d2b-contracts/src/v3/volume_spec.rs` (VolumeSpec, ViewSpec, Attachment, QuotaSpec, SourceKind, `SourcePolicyId` opaque newtype); `d2b-contracts/src/v3/effect_port.rs` (`VolumeEffectPort` trait, opaque ID newtypes `VolumeId`/`LayoutEntryId`/`UserId`/`ViewId` each with custom redacted Debug, and `VolumeMountToken` opaque handle with custom redacted Debug)" + "`d2b-contracts/src/v3/volume_layout.rs` (LayoutEntry, EntryType, all policy enums, AclGrant, Invariant, SensitivityClass); `d2b-contracts/src/v3/volume_spec.rs` (VolumeSpec, ViewSpec, Attachment, QuotaSpec, SourceKind, `SourcePolicyId` opaque newtype); `d2b-contracts/src/v3/effect_port.rs` (`VolumeEffectPort` trait, opaque ID newtypes `VolumeId`/`LayoutEntryId`/`UserId`/`ViewId`/`SealingPolicyId` each with custom redacted Debug, `VolumeMountToken`, and canonical `RotateSealingKeyRequest`/`Result`/`Error` types)" ], - "detailedDesign" : "All LayoutEntry fields as documented in this dossier; enum value names preserved from `StoragePathKind`/policy enums with renames where noted; `User/` ACL principal (no numeric UID); `sourcePolicyId` opaque newtype replaces raw `hostPath` in `SourceKind::LocalPath` and `SourceKind::BlockImage` Primary reuse disposition: `adapt`. Preserved source-plan detail: extract and adapt.", + "detailedDesign" : "All LayoutEntry fields as documented in this dossier; enum value names preserved from `StoragePathKind`/policy enums with renames where noted; `User/` ACL principal (no numeric UID); `sourcePolicyId` opaque newtype replaces raw `hostPath` in `SourceKind::LocalPath` and `SourceKind::BlockImage`; deny-unknown sealing-rotation request contains only opaque Volume/policy/operation IDs and generation/revision preconditions, with no key bytes/path/handle Primary reuse disposition: `adapt`. Preserved source-plan detail: extract and adapt.", "entryContracts" : [ "ADR-046-provider-volume-local" ], @@ -22993,7 +23199,7 @@ ], "specId" : "ADR-046-provider-volume-local", "topologicalRank" : 11, - "validation" : "Schema golden vectors; round-trip serde; ACL principal validation rejects numeric forms; `sourcePolicyId` present; no `hostPath` field in any volume_spec contract", + "validation" : "Schema golden vectors; round-trip serde; ACL principal validation rejects numeric forms; `sourcePolicyId` present; no `hostPath` field in any volume_spec contract; sealing-rotation request deny-unknown/round-trip and redacted-Debug tests; compile-time trait conformance includes `rotate_sealing_key`", "wave" : "W6" }, { @@ -23115,9 +23321,9 @@ { "blockers" : [], "destinations" : [ - "`src/{migration,snapshot}.rs`; `tests/{migration_unit,snapshot_unit}.rs`; `integration/{migration,snapshot}.rs`" + "`src/{migration,snapshot,sealing}.rs`; `tests/{migration_unit,snapshot_unit,sealing_unit}.rs`; `integration/{migration,snapshot,sealing}.rs`" ], - "detailedDesign" : "Schema migration (see §Schema migration) via `volume-migration-worker` EphemeralProcess; Snapshot create/list/expire (see §Snapshots) via `volume-snapshot-worker` EphemeralProcess; controller reports `stateSchemaPhase` and `snapshots` in Volume status. Sealing lifecycle (`sealingStatus`, key-shred) is a core/framework concern dispatched through `VolumeEffectPort` semantic ops, not a Provider-owned EphemeralProcess worker.", + "detailedDesign" : "Schema migration (see §Schema migration) via `volume-migration-worker` EphemeralProcess; Snapshot create/list/expire (see §Snapshots) via `volume-snapshot-worker` EphemeralProcess; controller reports `stateSchemaPhase` and `snapshots` in Volume status. Sealing lifecycle uses the status-first `rotation-pending` transition and only canonical `VolumeEffectPort::rotate_sealing_key`; it persists/resumes the core Operation-ledger fingerprint, classifies exact typed errors, and has no key lease, direct rewrite, generic broker call, or Provider-owned EphemeralProcess worker.", "entryContracts" : [ "ADR-046-provider-volume-local" ], @@ -23136,7 +23342,7 @@ ], "specId" : "ADR-046-provider-volume-local", "topologicalRank" : 14, - "validation" : "All `tests/migration_unit.rs`, `tests/snapshot_unit.rs`, `integration/migration.rs`, `integration/snapshot.rs` scenarios", + "validation" : "All `tests/migration_unit.rs`, `tests/snapshot_unit.rs`, `tests/sealing_unit.rs`, `integration/migration.rs`, `integration/snapshot.rs`, and `integration/sealing.rs` scenarios, including restart/idempotency and status-before-effect assertions", "wave" : "W6" }, { @@ -23168,7 +23374,7 @@ "destinations" : [ "`src/audit.rs`; `src/otel.rs`; `src/error.rs`; `tests/audit_unit.rs`; `integration/audit.rs`" ], - "detailedDesign" : "Event types and Zone audit emission per §Audit events; OTEL metric definitions per §OTEL metrics with closed semantic labels and no Zone/resource-name-derived dimensions; Zone identity remains in `d2b.zone` resource attributes; error catalog per §Error catalog; no-path invariant enforced in all outputs", + "detailedDesign" : "Event types and Zone audit emission per §Audit events, including controller rotation start/failure/commit and exactly-once broker `RotateSealingKey` success; OTEL metric definitions per §OTEL metrics with closed semantic labels and no Zone/resource-name-derived dimensions; Zone identity remains in `d2b.zone` resource attributes; error catalog per §Error catalog; no-path/no-key invariant enforced in all outputs", "entryContracts" : [ "ADR-046-provider-volume-local" ], @@ -23183,7 +23389,7 @@ ], "specId" : "ADR-046-provider-volume-local", "topologicalRank" : 12, - "validation" : "`tests/audit_unit.rs` golden records and structural metric descriptor assertions for exact absence of `vm`, `zone`, `zone_id`, `zone_uid`, and resource-name-derived keys plus resource-name canary absence; `tests/error_messages.rs` bounded messages; `integration/audit.rs` live stream", + "validation" : "`tests/audit_unit.rs` golden records and structural metric descriptor assertions for exact absence of `vm`, `zone`, `zone_id`, `zone_uid`, and resource-name-derived keys plus resource-name canary absence; rotation audit exact-once/digest-only vectors; `tests/error_messages.rs` bounded messages; `integration/audit.rs` live stream", "wave" : "W6" }, { @@ -23237,9 +23443,9 @@ { "blockers" : [], "destinations" : [ - "`packages/d2b-host/src/volume_effect_adapter.rs` (or the equivalent host-runtime crate designated by the Zone broker owner); implements the `VolumeEffectPort` trait defined in `d2b-contracts`" + "`packages/d2b-host/src/volume_effect_adapter.rs` (or the equivalent host-runtime crate designated by the Zone broker owner), implementing the `VolumeEffectPort` trait defined in `d2b-contracts`; planned `d2b-priv-broker/src/ops/rotate_sealing_key.rs` closed operation" ], - "detailedDesign" : "Adapter holds trusted FD table keyed by `VolumeId`; resolves `SourcePolicyId` to host path prefix from private bundle; calls `openat2(RESOLVE_BENEATH)` anchored at retained FD for all FS ops; calls `setfacl`/`acl_set_fd`, `mount`/`umount`, `fallocate` from within adapter only; emits path-free audit records for each op (audit is never atomic with redb write); injected into controller via Zone runtime ComponentSession; blocking filesystem calls run in bounded blocking-thread pool Primary reuse disposition: `adapt`. Preserved source-plan detail: adapt into adapter.", + "detailedDesign" : "Adapter holds trusted FD table keyed by `VolumeId`; resolves `SourcePolicyId` to host path prefix from private bundle; calls `openat2(RESOLVE_BENEATH)` anchored at retained FD for all FS ops; calls `setfacl`/`acl_set_fd`, `mount`/`umount`, `fallocate` from within adapter only; authorizes `volume.rotate-sealing-key`, verifies committed proof, recomputes the canonical idempotency key, and maps `rotate_sealing_key` one-to-one to the closed broker `RotateSealingKey` operation. Broker independently resolves opaque `VolumeId`/`SealingPolicyId`, checks policy/generation/preconditions, performs journaled atomic rewrap and roll-forward recovery, and durably emits exactly one success audit before returning. Neither boundary accepts key bytes, credential bytes, key handles, or paths. Other blocking filesystem calls run in the bounded blocking-thread pool Primary reuse disposition: `adapt`. Preserved source-plan detail: adapt into adapter.", "entryContracts" : [ "ADR-046-provider-volume-local" ], @@ -23255,7 +23461,7 @@ ], "specId" : "ADR-046-provider-volume-local", "topologicalRank" : 13, - "validation" : "Adapter hermetic tests: each effect op called with mock FD table and bundle; no path in any output; anchored-path rejection for RESOLVE_BENEATH violations; `cargo deny check` verifies adapter does not expose raw paths to Provider crate; `integration/provision.rs` exercises full adapter path", + "validation" : "Adapter hermetic tests: each effect op called with mock FD table and bundle; rotation authorization, policy binding, all generation/revision preconditions, canonical idempotency vectors, byte-identical duplicate/retry, different-payload conflict, typed retry classification, and no key/path/handle in wire/Debug/error/audit; broker crash injection at every journal boundary with old-or-target visibility, roll-forward, and exactly-once success audit; anchored-path rejection for RESOLVE_BENEATH violations; `cargo deny check` verifies adapter exposes neither raw paths nor broker implementation to Provider crate; `integration/{provision,sealing}.rs` exercise full adapter paths", "wave" : "W6" }, { @@ -23472,7 +23678,7 @@ "destinations" : [ "`packages/d2b-provider-transport-vsock/src/service.rs`; tests `tests/open_close.rs`, `tests/observe.rs`, and conformance kit." ], - "detailedDesign" : "Implement `VsockTransportService` (OpenTransport/CloseTransport/ObserveTransport). Implement all three service methods in `service.rs`; `open_close.rs` and `observe.rs` test full service API against `FakeVsockEffectPort`; conformance kit passes. Primary reuse disposition: `adapt`. Preserved source-plan detail: net-new service implementation over ComponentSession and fake effect port tests.", + "detailedDesign" : "Implement `VsockTransportService` (OpenTransport/CloseTransport/ObserveTransport). Child core resolves the exact six-field ZoneLink spec, validates `transportSettings` through the Provider selected by `transportProviderRef`, rejects non-empty `transportCredentials`, and passes only derived opaque IDs/deadline to `service.rs`; `open_close.rs`, `observe.rs`, and topology tests cover the service and reject legacy ZoneLink provider/fingerprint/capability fields; conformance kit passes. Primary reuse disposition: `adapt`. Preserved source-plan detail: net-new service implementation over ComponentSession and fake effect port tests.", "entryContracts" : [ "ADR-046-provider-transport-vsock" ], @@ -23488,22 +23694,22 @@ ], "specId" : "ADR-046-provider-transport-vsock", "topologicalRank" : 13, - "validation" : "Proof type: service round-trip test (mock); `tests/open_close.rs`, `tests/observe.rs`, and provider conformance tests.", + "validation" : "Proof type: service round-trip plus exact-shape tests; `tests/open_close.rs`, `tests/observe.rs`, `tests/topology.rs::{canonical_zonelink_spec_fields_are_exact,legacy_zonelink_provider_fields_are_rejected,transport_credentials_must_be_empty}`, and provider conformance tests.", "wave" : "W6" }, { "blockers" : [], "destinations" : [ - "`d2b-core-controller` Zone runtime `LiveVsockEffectPort`; Provider receives it by dependency injection at startup." + "`d2b-core-controller` child Zone runtime `LiveVsockEffectPort`; child-local Provider receives it by dependency injection at startup." ], - "detailedDesign" : "Implement `LiveVsockEffectPort` in Zone runtime. Zone runtime provides `LiveVsockEffectPort` backed by core allocator state; resolves `OpaqueEndpointId` → CID and `OpaqueBindingId` → port; opens AF_VSOCK socket; injects into Provider service at startup; no raw CID/port exposed to Provider. Primary reuse disposition: `adapt`. Preserved source-plan detail: net-new core adapter; keep raw AF_VSOCK syscalls outside Provider crate.", + "detailedDesign" : "Implement `LiveVsockEffectPort` in the child Zone runtime. It consumes the child's sealed binding for the allocator selected by compiler-only `parentZone`, resolves opaque endpoint/binding IDs only inside the effect adapter, opens the child AF_VSOCK endpoint, and injects an opaque stream into the same-Zone Provider service; the selected parent keeps its peer binding and port registry only as sealed allocator/route state; no raw CID/port is exposed to the Provider and no FD or ResourceRef crosses Zones. Primary reuse disposition: `adapt`. Preserved source-plan detail: net-new core adapter; keep raw AF_VSOCK syscalls outside Provider crate.", "entryContracts" : [ "ADR-046-provider-transport-vsock" ], "exitGate" : "ADR046-W6 exit criteria (ADR-046-validation-and-delivery §4): every spec/work item in this wave Merged with clean destinations, all validators green, and the ten-role panel seal recorded", "id" : "ADR046-vsock-004", "kind" : "work-item", - "owner" : "Title: Implement `LiveVsockEffectPort` in Zone runtime; Phase 2; Priority P0; Depends on ADR046-vsock-001 and the Zone allocator (`ADR-046-resources-zone-control`); Owner crate `d2b-core-controller`.", + "owner" : "Title: Implement `LiveVsockEffectPort` in child Zone runtime; Phase 2; Priority P0; Depends on ADR046-vsock-001 and the Zone allocator (`ADR-046-resources-zone-control`); Owner crate `d2b-core-controller`.", "parallelGroup" : "wi:ADR-046-provider-transport-vsock", "prerequisites" : [ "ADR-046-provider-transport-vsock", @@ -23511,7 +23717,7 @@ ], "specId" : "ADR-046-provider-transport-vsock", "topologicalRank" : 12, - "validation" : "Proof type: integration test; `integration/host_guest.rs` exercises live open/close byte round-trip with the injected effect.", + "validation" : "Proof type: integration test; `integration/host_guest.rs` exercises live open/close byte round-trip with the injected effect and proves the selected parent has only sealed allocator/route state, with no parent-store Provider/ZoneLink row.", "wave" : "W6" }, { @@ -23519,14 +23725,14 @@ "destinations" : [ "ProviderDeployment Volume creation/deletion path plus `packages/d2b-provider-transport-vsock/tests/state_volume.rs`." ], - "detailedDesign" : "Core ProviderDeployment creates/deletes service component state Volume. Core ProviderDeployment creates `Volume/transport-vsock--service--empty-state--*` before the component Process and deletes it after the Process finalizer; transport-vsock Provider controller does not own Volume, does not add Volume to exported ResourceTypes, and does not create its prerequisite; Volume spec: empty schema, `kind: state`, `persistenceClass: persistent`, `migrationPolicy: none`, `User/d2b-transport-vsock` owner, minimal nonzero `quota.maxBytes`/`quota.maxInodes` with `enforcement: hard`, `private` sensitivity, `broker-maintained` identity marker; `state_volume.rs` test verifies Volume spec fields against canonical schema; integration test verifies marker written at install and removed at Provider deletion; no operator-authored Volume; component receives dirfd view only. Primary reuse disposition: `create`. Preserved source-plan detail: net-new ProviderDeployment/Volume integration and tests.", + "detailedDesign" : "Child core ProviderDeployment creates/deletes the service component state Volume in the same child Zone. It creates `Volume/transport-vsock--service--empty-state--*` before the component Process and deletes it after the Process finalizer; the transport-vsock service does not own Volume, add Volume to exported ResourceTypes, or create its prerequisite; Volume spec: empty schema, `kind: state`, `persistenceClass: persistent`, `migrationPolicy: none`, `User/d2b-transport-vsock` owner, minimal nonzero `quota.maxBytes`/`quota.maxInodes` with `enforcement: hard`, `private` sensitivity, `broker-maintained` identity marker; `state_volume.rs` tests the canonical schema; installation/removal tests verify marker lifecycle; no operator-authored or parent-store Volume exists; component receives only its child-local dirfd view. Primary reuse disposition: `create`. Preserved source-plan detail: net-new ProviderDeployment/Volume integration and tests.", "entryContracts" : [ "ADR-046-provider-transport-vsock" ], "exitGate" : "ADR046-W6 exit criteria (ADR-046-validation-and-delivery §4): every spec/work item in this wave Merged with clean destinations, all validators green, and the ten-role panel seal recorded", "id" : "ADR046-vsock-005", "kind" : "work-item", - "owner" : "Title: Core ProviderDeployment creates/deletes service component state Volume; Phase 1; Priority P0; Depends on the volume-local Provider (`ADR-046-provider-volume-local`); Owner crate `d2b-provider-transport-vsock`.", + "owner" : "Title: Child core ProviderDeployment creates/deletes service component state Volume; Phase 1; Priority P0; Depends on the volume-local Provider (`ADR-046-provider-volume-local`); Owner crate `d2b-provider-transport-vsock`.", "parallelGroup" : "wi:ADR-046-provider-transport-vsock", "prerequisites" : [ "ADR-046-provider-transport-vsock" @@ -23541,7 +23747,7 @@ "destinations" : [ "`packages/d2b-provider-transport-vsock/integration/host_guest.rs` and `integration/no_fd_transfer.rs`." ], - "detailedDesign" : "Integration test: real vsock socketpair + full ZoneLink open/close. `integration/host_guest.rs`: real vsock socketpair (Linux); `OpenTransport` + byte round-trip + `CloseTransport`; validates bridge throughput ≥ 512 MiB/s; `no_fd_transfer.rs`: structural rejection of attachment packets over vsock transport. Primary reuse disposition: `create`. Preserved source-plan detail: net-new integration coverage with no FD transfer over vsock.", + "detailedDesign" : "Integration test: fixture declares compiler-only `k1.parentZone = \"local-root\"`, puts the selected Provider and an exact six-field ZoneLink (`transportProviderRef`, validated `transportSettings`, empty `transportCredentials`, `disabled`, and `limits`, plus self-matching `childZoneName`) only in K1, and gives local-root only sealed allocator/route state; `integration/host_guest.rs` opens a real Linux vsock path through K1's `LiveVsockEffectPort`, then exercises `OpenTransport` + byte round-trip + `CloseTransport` and validates bridge throughput ≥ 512 MiB/s; `no_fd_transfer.rs` structurally rejects attachment packets and asserts no FD/ResourceRef crosses the Zone boundary. Primary reuse disposition: `create`. Preserved source-plan detail: net-new integration coverage with no FD transfer over vsock.", "entryContracts" : [ "ADR-046-provider-transport-vsock" ], diff --git a/docs/specs/ADR-046-implementation-graph.md b/docs/specs/ADR-046-implementation-graph.md index 1dc6a0b56..0778b179f 100644 --- a/docs/specs/ADR-046-implementation-graph.md +++ b/docs/specs/ADR-046-implementation-graph.md @@ -25,7 +25,7 @@ Each JSON work-item node also embeds the manifest's exact `detailedDesign` and | Spec nodes | 55 | | Work-item nodes | 543 | | Total nodes | 598 | -| Edges | 1908 | +| Edges | 1938 | | Max topological rank | 22 | ## Waves (W0–W7) diff --git a/docs/specs/ADR-046-reset-and-cutover.md b/docs/specs/ADR-046-reset-and-cutover.md index c1c5d1409..2df7a23c6 100644 --- a/docs/specs/ADR-046-reset-and-cutover.md +++ b/docs/specs/ADR-046-reset-and-cutover.md @@ -1749,7 +1749,7 @@ applies here exactly as everywhere else in the repository). | Work item ID | `ADR046-reset-005` | | Dependency/owner | ADR046-reset-004; `d2b-resource-store-redb` owner (`ADR046-store-003`); core-controller owner (`ADR046-core-001`) | | Current source | None (bootstrap sequencing over Zone runtime startup, which is itself ADR-only) | -| Reuse source | None from main | +| Reuse source | None | | Reuse action | create | | Destination | `packages/d2b-cutover/src/{store_bootstrap,provider_sequence}.rs` | | Detailed design | Phase 5 store creation per [Resource-store initialization](#resource-store-initialization); Phase 6 topological Provider install per [Provider install/topological start](#provider-installtopological-start), including the fixed staged default order and cycle-rejection check | @@ -1797,7 +1797,7 @@ applies here exactly as everywhere else in the repository). | Work item ID | `ADR046-reset-008` | | Dependency/owner | ADR046-reset-007; owner of each retiring artifact's ADR 0046 successor work item | | Current source | `ADR-046-cli-and-operations` "Removal notes" (live-successor-before-deletion criterion) | -| Reuse source | None from main | +| Reuse source | None | | Reuse action | create | | Destination | `packages/d2b-cutover/src/finalize.rs` | | Detailed design | Per-candidate independent gate evaluation exactly as tabled in [Old artifact/unit/schema removal gates](#old-artifactunitschema-removal-gates); separate consent phrase from `apply`; never partial-destroys a candidate | @@ -1845,7 +1845,7 @@ applies here exactly as everywhere else in the repository). | Work item ID | `ADR046-reset-011` | | Dependency/owner | ADR046-reset-001 through ADR046-reset-010, fully landed | | Current source | `tests/integration/live/` conventions; `tests/host-integration/hardware/` conventions | -| Reuse source | None from main | +| Reuse source | None | | Reuse action | create | | Destination | `tests/integration/live/cutover-real-host.sh`, `tests/integration/live/cutover-real-host-cloud-guest.sh`, `tests/host-integration/hardware/cutover-real-tpm.sh`, `tests/host-integration/hardware/cutover-real-usbip-security-key.sh` | | Detailed design | Manual, `D2B_LIVE=1`/hardware-gated validation scripts described in [Tests](#tests) Type 11/12 rows; never run in CI; require operator sign-off and an independent out-of-band backup before execution | diff --git a/docs/specs/ADR-046-resources-device.md b/docs/specs/ADR-046-resources-device.md index 0c252fb08..4c9c4da11 100644 --- a/docs/specs/ADR-046-resources-device.md +++ b/docs/specs/ADR-046-resources-device.md @@ -423,7 +423,7 @@ privileged broker for operations that require root: | --- | --- | --- | --- | | `device-tpm` | `PrepareStateDir` (via `PrepareRuntimeDir`/`PrepareSwtpmDir` broker hook) | Provision/harden swtpm state dir, verify tamper marker | Yes | | `device-tpm` | `SpawnRunner` (swtpm role) | Spawn swtpm Process in user namespace | Yes | -| `device-usbip` | `UsbipBindFirewallRule` | Add per-env/bus nftables rule | Yes | +| `device-usbip` | `UsbipBindFirewallRule { action: Ensure \| Remove }` | Ensure or remove the exact per-Network/per-busid nftables rule | Yes | | `device-usbip` | `SpawnRunner` (usbip role) | Spawn usbipd/bind Process | Yes | | `device-security-key` | `SecurityKeyOpenDevice` | Open exact FIDO hidraw node; return fd via SCM_RIGHTS; never a path | Yes | | `device-security-key` | `SecurityKeyApplyUdevRules` | Write udev rules for configured FIDO hidraw nodes | Yes | @@ -506,7 +506,9 @@ These invariants are preserved exactly in the device-tpm Provider. - USBIP per-env/per-busid firewall rules use the ownership-marker pattern: `comment "d2b managed: usbip:env::bus:"`. -- Broker operation `UsbipBindFirewallRule` is audit-logged and destructive. +- Broker operation `UsbipBindFirewallRule { action: Ensure | Remove }` is + audit-logged and destructive; acquisition and release share this one closed + operation. - The bus ID validation is governed by `packages/d2b-contracts/src/usbip.rs`: max 31 chars (`SYSFS_BUS_ID_MAX`), ASCII digits and separators only, no shell metacharacters, no leading zeros on segments. @@ -768,7 +770,8 @@ spec: The per-Device usbipd daemon and proxy Processes are owned by `Provider/device-usbip`. The Network provider supplies only the dependency/status/firewall interface: bridge membership, port allocation, and -the `UsbipBindFirewallRule` broker op surface. Network does not own or supervise +the `UsbipBindFirewallRule { action: Ensure | Remove }` broker op surface. +Network does not own or supervise USBIP Processes. Process resource names follow the `device--` template @@ -793,14 +796,16 @@ step maps to a broker operation or host-side action: | `modprobe` | Load `usbip-host` kernel module | EphemeralProcess (modprobe) | | `lock` | Acquire per-busid OFD lock | — | | `withhold` | Prevent OS auto-claim of device | sysfs write via broker | -| `firewall` | Add nftables rule | `UsbipBindFirewallRule` | +| `firewall` | Ensure on acquisition; Remove on detach/finalize | `UsbipBindFirewallRule { action: Ensure \| Remove }` | | `backend` | Start per-env usbipd daemon | `SpawnRunner` (usbip role) | | `bind` | Bind bus ID to usbip-host | EphemeralProcess (usbip bind) | | `proxy` | Start TCP proxy on env-host IP | `SpawnRunner` (usbip proxy role) | ### Broker operations consumed -- `UsbipBindFirewallRule`: add nftables rule per env/bus; audited/destructive. +- `UsbipBindFirewallRule { action: Ensure | Remove }`: ensure the exact + per-Network/per-busid rule during acquisition and remove it during + detach/finalize; audited/destructive. - `SpawnRunner` (usbip backend role, `usbip-host` device token): spawn per-env usbipd. - `SpawnRunner` (usbip proxy role): spawn TCP proxy. @@ -1227,7 +1232,7 @@ test for each limit is required in `packages/d2b-contract-tests/`): | --- | --- | --- | --- | | `SecurityKeyOpenDevice` | 1 concurrent per device label | 1 | One active hidraw session per Device at a time | | `SecurityKeyApplyUdevRules` | Activation-only | — | One batch per Provider activation; not a hot path | -| `UsbipBindFirewallRule` | One bounded batch per activation | — | Ownership-marker check prevents duplicate rules | +| `UsbipBindFirewallRule { action: Ensure \| Remove }` | One bounded batch per acquisition or release | — | Ownership-marker check prevents duplicate rules; Remove is idempotent | | `SpawnRunner` (swtpm) | 1 per Device per Guest start cycle | — | Idempotent; broker verifies tamper marker | | `SpawnRunner` (gpu/video) | 1 per Device (one GPU worker set per Guest) | — | One GPU worker set per Device | | `OpenDevice` (gpu) | Per-spawn call only | ≤8 per Process launch | Opened before clone; counted per-spawn | @@ -2070,7 +2075,7 @@ error listing the missing paths. There is no opt-out mechanism. | Reuse action | adapt | | Destination | `packages/d2b-provider-device-usbip/src/` (controller, daemon Process, bind/unbind EphemeralProcess, firewall); `packages/d2b-provider-device-usbip/tests/` (hermetic Cargo integration); `packages/d2b-provider-device-usbip/integration/` (container/Host scenarios); `packages/d2b-provider-device-usbip/README.md` | | Detailed design | Device spec/status; bus ID validation; firewall rule ownership-marker; bind/unbind EphemeralProcess; per-Device daemon Process (owned by device-usbip; Network supplies dependency/firewall interface); Nix emitter; all four required crate paths present (see "Provider crate layout") Primary reuse disposition: `adapt`. Preserved source-plan detail: extract and adapt. | -| Integration | Zone resource store; broker `UsbipBindFirewallRule`; nftables marker | +| Integration | Zone resource store; broker `UsbipBindFirewallRule { action: Ensure \| Remove }`; nftables marker | | Data migration | None; full reset | | Validation | `src/`: bus ID corpus, firewall marker format, EphemeralProcess creation; `tests/`: `arbitration_conflict.rs`, `conformance.rs`, `firewall_marker.rs`, `explicit_attach_split.rs`; `integration/`: `arbitration_conflict/`, `busid_bind_cycle/`, `network_firewall_coexistence/`; workspace policy check: `make test-policy` passes with all four paths present | | Removal proof | ProcessRole::Usbip removed after parity | diff --git a/docs/specs/ADR-046-resources-zone-control.md b/docs/specs/ADR-046-resources-zone-control.md index e36465619..5df11eee4 100644 --- a/docs/specs/ADR-046-resources-zone-control.md +++ b/docs/specs/ADR-046-resources-zone-control.md @@ -4268,7 +4268,7 @@ None of the following exist in baseline: | Work item ID | `ADR046-zone-control-009` | | Dependency/owner | ADR046-zone-control-001; Zone store (ADR046-store-001); Quota handler owner | | Current source | `packages/d2b-realm-core/src/ids.rs` (`LABEL_PATTERN`, `MAX_ID_LEN` — ResourceName validation for quotaRef); `packages/d2b-core/src/unsafe_local_workloads.rs:16–164` (`MAX_UNSAFE_LOCAL_WORKLOADS=256`, etc. — bound evidence for quota ceiling defaults); no current quota ResourceType exists (`ADR-only`) | -| Reuse source | None from main; Quota is a new ResourceType with no main-branch analog | +| Reuse source | None | | Reuse action | create | | Destination | `packages/d2b-contracts/src/v3/quota.rs`; `packages/d2b-core-controller/src/quota.rs`; `packages/d2b-resource-api/src/quota_gate.rs` | | Detailed design | Quota resource schema with all spec/status fields from §7; ceiling bounds enforcement at admission (hard policy: reject over-quota with `quota-exceeded`; soft policy: warn); usage index built from resource scan with `quotaRef` field; per-ResourceType ceiling in `perTypeCeilings`; `core.quota-drain` finalizer that blocks Quota deletion until all dependent resources with `spec.quotaRef` pointing to this Quota are reassigned or deleted by authorized owners/operators — the controller never issues spec-updates to clear `quotaRef` on other resources; `dependentCount` status field updated from dependency index; audit event `quota-check` per admission; Nix Quota options per §7.7 | @@ -4284,7 +4284,7 @@ None of the following exist in baseline: | Work item ID | `ADR046-zone-control-010` | | Dependency/owner | ADR046-zone-control-001; Zone store (ADR046-store-001); EmergencyPolicy handler owner | | Current source | `packages/d2bd/src/lib.rs` admission gate (`authorize_peer()` — `implemented-and-reachable`, baseline `b5ddbed6`); `packages/d2b-daemon-access/src/lib.rs` (`DaemonAccessDecision::Denied` — basis for admission rejection pattern); no EmergencyPolicy ResourceType exists (`ADR-only`) | -| Reuse source | None from main; EmergencyPolicy is a new ResourceType | +| Reuse source | None | | Reuse action | create | | Destination | `packages/d2b-contracts/src/v3/emergency_policy.rs`; `packages/d2b-core-controller/src/emergency_policy.rs`; `packages/d2b-resource-api/src/emergency_gate.rs` | | Detailed design | EmergencyPolicy resource schema from §8; union semantics: multiple `enabled=true` policies allowed simultaneously; effective scope = OR of all enabled policies' scope flags; effective `drainDeadlineSeconds` = minimum across enabled policies; scope flag evaluation: `stopNewAdmissions` signal to API admission gate, `disconnectZoneLinks` graceful signal to ZoneLink handler, `stopProviderProcesses` suppresses Process launch and sends stop signal to running Provider component Processes (does NOT set `deletionRequestedAt` on Process resources; reconciliation resumes on deactivation), `drainOngoingOperations` deadline drain; `core.emergency-drain` finalizer for enabled policies; audit events `emergency-policy-activated` / `emergency-policy-deactivated`; `reason` field stored in spec and included in audit record body (never in OTEL metric label values, structured log labels, or status fields); Nix EmergencyPolicy options per §8.7 | @@ -4429,7 +4429,7 @@ Evidence class for all: `main-reuse-source`. | Work item ID | `ADR046-zone-control-014` | | Dependency/owner | ADR046-zone-control-001, ADR046-routing-011; Nix module owner | | Current source | `nixos-modules/options-realms.nix` (option schema/assertion conventions); `nixos-modules/bundle-artifacts.nix` (bundle emit pattern); `nixos-modules/provider-registry-v2-json.nix` (provider-registry JSON emit); `nixos-modules/assertions.nix:730` (assertion style for unsafe-local); `packages/d2b-realm-core/src/ids.rs` (`LABEL_PATTERN`, `MAX_ID_LEN`, `is_label()` — eval-time name validation target) | -| Reuse source | None from main; eval-time validation is Nix-native | +| Reuse source | None | | Reuse action | create | | Destination | `nixos-modules/options-zones.nix`, `nixos-modules/generated/resource-types.nix`, `nixos-modules/generated/options-zones-.nix`, `nixos-modules/resource-type-validators.nix` | | Detailed design | Consume the generator and registry established by ADR046-routing-011 to declare the unified `d2b.zones..resources.` option tree plus the Zone-level compiler-only `parentZone` scalar. The generated standard registry is exactly the canonical 19 types (`Zone`, `ZoneLink`, `Provider`, `Host`, `Guest`, `Process`, `EphemeralProcess`, `Network`, `Volume`, `Credential`, `Device`, `Endpoint`, `User`, `Role`, `RoleBinding`, `Quota`, `EmergencyPolicy`, `ResourceExport`, `ResourceImport`), and every standard type has a strict generated `spec` submodule carrying the schema's Nix types, defaults, bounds, and documentation. Installed Provider artifacts may append only signed qualified ResourceTypes whose strict schema has been verified and generated into the evaluated option set. `type` is selected from that closed combined registry: an unknown standard string, an unqualified extension, or a qualified type without an installed verified schema fails evaluation; there is no unrestricted string or free-form `spec` fallback. Require `parentZone` on every non-root Zone and forbid it on `local-root`; resolve it against declared Zone keys, reject self-parent/conflicting module definitions/cycles, and cap each ancestry path at 16 Zone names. Compile the validated map into sealed allocator bootstrap topology; never emit it into the resource bundle or `Zone.spec`. Declare the global `d2b.artifacts.` catalog with `package` (types.package, required) and `type` (closed enum, required). Provider `spec.artifactId` is a plain catalog ID; the derivation is not a `spec` field. Implement the Phase 1 cross-resource assertions (§14.10 Phase 1 table); retain `credentialRef`, `resourceRef`, and `closedEnum` helpers; reject operator-authored `Zone`; and enforce child-local ZoneLink topology. The runtime creates `Zone/`, `Provider/system-core`, and `Provider/system-minijail` with `managedBy=controller`; none is emitted or inferred from the configuration bundle. `allowUnsafeLocal` maps to the dedicated Host admission gate. Provider manifest-derived fields (`spec.exports`, `spec.components`, `spec.dependencies`, `spec.permissionClaims`, `spec.upgradePolicy`, `spec.restartPolicy`) are read-only and setting one is an eval error. | @@ -4445,7 +4445,7 @@ Evidence class for all: `main-reuse-source`. | Work item ID | `ADR046-zone-control-015` | | Dependency/owner | ADR046-zone-control-014; resource compiler owner | | Current source | `nixos-modules/bundle-artifacts.nix` (artifact emit pattern); `nixos-modules/processes-json.nix` (canonical JSON serialization conventions); `packages/xtask/src/main.rs` `gen-schemas` subcommand (schema-from-derivation pattern); `packages/d2b-core/src/bundle.rs` (current bundle DTO shape for adaptation reference) | -| Reuse source | None from main; the resource compiler is a new pure-Rust Nix derivation | +| Reuse source | None | | Reuse action | create | | Destination | `packages/d2b-resource-compiler/src/{main,bundle,schema,validator,digest,sort,secret_lint,generation}.rs`; exposed as `pkgs.d2b-resource-compiler`; called from `nixos-modules/resource-compiler.nix` | | Detailed design | Implement all Phase 2 build-time checks (§14.10 Phase 2 table): dispatch on `type` to look up ResourceTypeSchema; validate each resource's `spec` canonical JSON against the committed schema (build validation compares canonical rendered JSON against ResourceTypeSchema for each core type); compile the `d2b.artifacts.*` catalog: for each entry, build/include the derivation, verify `type` is a recognized value, compute `digest` over the derivation output, extract and hash manifest and config schema files, validate signature chain and conformance attestation; detect duplicate artifact IDs; for each Provider resource, look up `spec.artifactId` in the compiled catalog (build failure if absent or wrong type), verify `configSchemaDigest` matches schema SHA-256, validate operator `spec.config` against loaded JSON Schema using a pure-Rust validator bundled in the derivation, verify `manifestDigest` and signature chain, load manifest-derived fields (`exports`, `components`, `dependencies`, `permissionClaims`, `upgradePolicy`, `restartPolicy`) into the bundle envelope; emit private integrity-pinned artifact catalog (ID → type/digest/closure metadata) as a separate private file (never merged into the public resource bundle); check `spec.rules[*].resourceTypes` against installed Provider catalogs in the bundle (Role); verify `spec.roleRef` names an existing Role in the bundle (RoleBinding); verify `spec.subjects[*]` names resolve in bundle (RoleBinding); check ResourceType short-name collision across all Zone Providers; RFC 8785 canonical JSON serialization; per-resource `digest` computation; `bundleDigest` computation over sorted `resources` array; inline-secret heuristic lint (`--strict-secrets` flag); `generation` counter persistence in Nix module state; emit `zone-resources.json` bundle with all fields per §14.9 | @@ -4477,7 +4477,7 @@ Evidence class for all: `main-reuse-source`. | Work item ID | `ADR046-pkg-001` | | Dependency/owner | ADR046-zone-control-003; workspace policy owner | | Current source | `packages/d2b-contract-tests/tests/policy_contracts.rs` lines 5–6 (D2B_FIXTURES gate / workspace-checks integration pattern — `implemented-and-reachable`, baseline `b5ddbed6`); `packages/d2b-contract-tests/tests/static_invariants.rs` (hermetic policy test structure — `implemented-and-reachable`); `tests/tools/rust-workspace-checks.sh` (D2B_FIXTURES step shell harness — `implemented-and-reachable`); AGENTS.md "Naming conventions" section (`-` workspace sort rules — `implemented-and-reachable`); `packages/d2b-realm-core/src/ids.rs` `LABEL_PATTERN` / `MAX_ID_LEN` (name regex reused for crate name token validation — `implemented-and-reachable`) | -| Reuse source | None from main; workspace policy tests are repo-specific | +| Reuse source | None | | Reuse action | create | | Destination | `packages/d2b-contract-tests/tests/policy_provider_crate_layout.rs` (new file; gated under D2B_FIXTURES in existing `tests/tools/rust-workspace-checks.sh`) | | Detailed design | Implement `policy_provider_crate_layout.rs` with the following test functions: (1) `every_provider_crate_has_src` — walk `packages/d2b-provider-*/` directories in the workspace, assert each contains `src/`; failure names crate and missing path; (2) `every_provider_crate_has_tests` — assert `tests/` present; (3) `every_provider_crate_has_integration` — assert `integration/` present; (4) `every_provider_crate_has_readme` — assert `README.md` present; (5) `every_provider_readme_has_required_sections` — read `README.md`, check for all nine section headings from §4.8.3 (case-insensitive, after stripping `#` and whitespace); failure names the missing heading(s); (6) `every_integration_file_has_target_declaration` — for each `integration/*.rs` file, scan first 20 lines for exactly one `//! integration-target: (container|host-integration)` declaration; failure names the file and the violation (missing/multiple/invalid value); (7) `non_provider_crates_exempt` — verify the check does not run on non-`d2b-provider-*` crates. All checks are filesystem-only (no compilation). Workspace member list is discovered by parsing `packages/Cargo.toml` `[workspace].members`. Gate: add the new test file to `tests/tools/rust-workspace-checks.sh` D2B_FIXTURES list alongside existing policy tests | @@ -4508,8 +4508,8 @@ Evidence class for all: `main-reuse-source`. | --- | --- | | Work item ID | `ADR046-zone-control-020` | | Dependency/owner | ADR046-zone-control-019; `d2b-core-controller` owner | -| Current source | None — net-new ADR 0046 cross-Zone sharing model (D096) | -| Reuse source | None from main; projection ownership reuses the core owner/child reconcile machinery (§11) | +| Current source | None — net-new ADR 0046 cross-Zone sharing model (D096); the core owner/child reconcile machinery (§11) is prior-art design context only | +| Reuse source | None | | Reuse action | create | | Destination | `packages/d2b-core-controller/src/export_import_projection.rs` (local qualified Service projection lifecycle owned by `ResourceImport`) | | Detailed design | Core creates exactly one same-qualified-type projection Service per `ResourceImport` and keeps it synchronized with the remote Service lease. Operators/Nix separately author same-Zone matching Binding resources with `serviceRef` plus an allowed Guest/User/Zone target; Binding specs hold desired intent only and Binding controllers write observations only to status while owning Process/Endpoint children. On revoke, mark the projection draining/revoked and let Binding controllers stop children. On delete, wait for Bindings to be deleted/retargeted (`BindingReferencesRemain`), release the lease, delete only the projection Service/provider-owned children, then clear the import finalizer. Never create/delete Binding or project Device/Endpoint. | diff --git a/docs/specs/ADR-046-spec-set.json b/docs/specs/ADR-046-spec-set.json index 1bc09eb64..de0eb8e4f 100644 --- a/docs/specs/ADR-046-spec-set.json +++ b/docs/specs/ADR-046-spec-set.json @@ -13,7 +13,7 @@ "ADR-046-terminology-and-identities" ], "path" : "docs/specs/ADR-046-cli-and-operations.md", - "sha256" : "abaa92ab30246ffc400a59ff5502695d23576445a240e1bfd6f606de904a8f3f", + "sha256" : "b325b800dffb929c15368f3c56032dacc5e52251fbdc0b93bc128111a56535c3", "specId" : "ADR-046-cli-and-operations", "status" : "Proposed", "supersedes" : "Current v3 `d2b` CLI contract (`packages/d2b/src/lib.rs` at baseline)", @@ -194,7 +194,7 @@ "ADR-046-telemetry-audit-and-support" ], "path" : "docs/specs/providers/ADR-046-provider-audio-pipewire.md", - "sha256" : "21d97d1131de083390174720eec9ebe7754a6d812edcd94f0df853425301a3bf", + "sha256" : "9cd571617053bfe795e8ea51e6dcef924c9b368861d5167a21c7635983e03667", "specId" : "ADR-046-provider-audio-pipewire", "status" : "Proposed", "supersedes" : "`nixos-modules/components/audio/host.nix`, `nixos-modules/components/audio/guest.nix`, `packages/d2b-core/src/audio_policy.rs`, `packages/d2bd/src/audio_dispatch.rs`, `packages/d2bd/src/audio_host_controller.rs`, `packages/d2b-host/src/audio_argv.rs`", @@ -297,7 +297,7 @@ "ADR-046-telemetry-audit-and-support" ], "path" : "docs/specs/providers/ADR-046-provider-device-gpu.md", - "sha256" : "37552a2ffe27f45eabd43f6b9fc86ecfa3cc3ee5348bf72afc784849a158c453", + "sha256" : "f200f8279c0ea3dc18bbb13b334c7303354edc045830e6b1f35bdc9b452b1c06", "specId" : "ADR-046-provider-device-gpu", "status" : "Proposed", "supersedes" : "`ProcessRole::Gpu`, `ProcessRole::GpuRenderNode`, `ProcessRole::Video` in `packages/d2b-core/src/processes.rs`; Nix `nixos-modules/components/graphics.nix`; Nix `nixos-modules/components/video/guest.nix`; `d2b.vms..graphics.*` options", @@ -321,7 +321,7 @@ "ADR-046-telemetry-audit-and-support" ], "path" : "docs/specs/providers/ADR-046-provider-device-security-key.md", - "sha256" : "85c299f4b747527430de8c319926b6ae32ef28101dd11d3c148032b2a700524d", + "sha256" : "810cc78ce459c862e14835d796e56fe03fc0303b69e8c5e707618e19fdd9f134", "specId" : "ADR-046-provider-device-security-key", "status" : "Proposed", "supersedes" : "`ProcessRole::SecurityKeyFrontend` daemon-internal accept loop (`packages/d2bd/src/security_key.rs`), `nixos-modules/components/security-key-guest.nix` untracked guest `d2b-sk-frontend.service` unit", @@ -367,7 +367,7 @@ "ADR-046-zone-routing" ], "path" : "docs/specs/providers/ADR-046-provider-device-usbip.md", - "sha256" : "b9c4fff8cf7d8a10fe699030b737833f71b94553ff7341fed9029d48d9599cd3", + "sha256" : "ca10fdd00b683319a36742d9f6060ed38ea6f2335fa7ac49c140d8130a23fec8", "specId" : "ADR-046-provider-device-usbip", "status" : "Proposed", "supersedes" : "`nixos-modules/components/usbip.nix` (host-side), per-env usbipd systemd units in `nixos-modules/network.nix`, `ProcessRole::Usbip` / `RunnerRole::Usbip` in current v3 baseline", @@ -429,7 +429,7 @@ "ADR-046-telemetry-audit-and-support" ], "path" : "docs/specs/providers/ADR-046-provider-network-local.md", - "sha256" : "0573cb8efd9ddc30d87cb214995b7ca98bf61a12d635574f4dc052d355e314a9", + "sha256" : "e230d7534b246ed9b002cf21c903559d20c6a24f317506ce832c277dd2ef0e23", "specId" : "ADR-046-provider-network-local", "status" : "Proposed", "supersedes" : "`nixos-modules/network.nix`, `nixos-modules/net.nix`", @@ -474,7 +474,7 @@ "ADR-046-terminology-and-identities" ], "path" : "docs/specs/providers/ADR-046-provider-observability-otel.md", - "sha256" : "8ff7b46472e854b057cec49a7c91fbadd88828e68b246b01bbcae7e91ac4f1d6", + "sha256" : "1a81a48f8bbd04c3b8c05049bc7f04848684cc399c05a3745057229309782fdd", "specId" : "ADR-046-provider-observability-otel", "status" : "Proposed", "supersedes" : "`ProcessRole::OtelHostBridge` / `RunnerRole::OtelHostBridge`; socat-based vsock forwarder in `packages/d2b-host/src/otel_host_bridge_argv.rs`; `packages/d2bd/src/otel_host_bridge_readiness.rs`; hand-rolled per-VM `nixos-modules/components/observability/` pipeline (adapted to per-Zone)", @@ -491,11 +491,10 @@ "ADR-046-provider-state", "ADR-046-resources-credential", "ADR-046-resources-host-guest-process-user", - "ADR-046-telemetry-audit-and-support", - "ADR-046-zone-routing" + "ADR-046-telemetry-audit-and-support" ], "path" : "docs/specs/providers/ADR-046-provider-runtime-azure-container-apps.md", - "sha256" : "774c3cdb40552a2c66fb1386eec72ba2a887e33ce53979b8ba8344922a3a81e2", + "sha256" : "e78e86421b68b7df11f490adeb1b1cbd9fef9e6f3f9577efbc49f7775109af8d", "specId" : "ADR-046-provider-runtime-azure-container-apps", "status" : "Proposed", "supersedes" : "`packages/d2b-provider-aca/` (`AcaWorkloadProvider`, `GuestControlEndpointProvider`), `AcaRelayTransportConfig`, direct vsock guest-control path", @@ -573,7 +572,7 @@ "ADR-046-telemetry-audit-and-support" ], "path" : "docs/specs/providers/ADR-046-provider-runtime-qemu-media.md", - "sha256" : "dee669643caa90526c4f47a66a65870eba76a6a0b40bb6331b2d4435b0b27db6", + "sha256" : "283198af11eb08279e3f2e3a76fd9d80b9d3900e009607171f25d70d687e3a4e", "specId" : "ADR-046-provider-runtime-qemu-media", "status" : "Proposed", "supersedes" : "`docs/adr/0036-qemu-media-runtime.md`", @@ -707,7 +706,7 @@ "ADR-046-zone-routing" ], "path" : "docs/specs/providers/ADR-046-provider-transport-azure-relay.md", - "sha256" : "c90b289271b227a90608f5db64ff391b82af7dfa97bdbefe6495997e5fd64289", + "sha256" : "6ae9c12b64aa11851d699a6b40c947fa528e1143383df7878a1885ad6389b941", "specId" : "ADR-046-provider-transport-azure-relay", "status" : "Proposed", "supersedes" : "`d2b-provider-relay` gateway-display relay path (`AcaWorkloadProvider` + `RelayProvider` traits in `d2b-realm-provider`); `d2b-gateway-runtime/src/bin/d2b-gateway-relay.rs`; `packages/d2b-provider-relay/src/lib.rs` as a first-party transport surface", @@ -731,7 +730,7 @@ "ADR-046-zone-routing" ], "path" : "docs/specs/providers/ADR-046-provider-transport-unix.md", - "sha256" : "57f3e2b23777b7a595edb6444ff8243a1874e6f4723186466ea1e1fa09f0af62", + "sha256" : "52bb5ee85ee18ace64e51ac416527d9f41776d8896a2bc4fdb3dd6cc0e94ed58", "specId" : "ADR-046-provider-transport-unix", "status" : "Proposed", "supersedes" : "v1 of this dossier (ADR-046-provider-transport-unix v1; incorrect ownership model); `InheritedSocketTransport` SD_LISTEN_FDS path (`packages/d2b-session-unix/src/systemd.rs`)", @@ -752,7 +751,7 @@ "ADR-046-zone-routing" ], "path" : "docs/specs/providers/ADR-046-provider-transport-vsock.md", - "sha256" : "824bc597f1c5ee34d6ebd3063a5a75d5b8b6a9ee8f0419357cd87fca0a779df7", + "sha256" : "ab694961e18eebd3f9bdc71c0685f18f9e12e978b4fb66e9ece566daa376ab9e", "specId" : "ADR-046-provider-transport-vsock", "status" : "Proposed", "supersedes" : "`packages/d2bd/src/guest_control_vsock.rs` transport probe (→ allocator-issued endpoint via VsockEffectPort); `packages/d2b-host/src/vsock_relay_argv.rs` socat relay (→ native FramedVsockTransport in Provider service); `NativeVsock`/`CloudHypervisorVsock` in `d2b-session-unix/src/vsock.rs` (→ FramedVsockTransport implementing OwnedTransport adapted as service-Provider transport bridge)", @@ -773,7 +772,7 @@ "ADR-046-telemetry-audit-and-support" ], "path" : "docs/specs/providers/ADR-046-provider-volume-local.md", - "sha256" : "38a439ed5dfe280d7570ee73829fb1d45da8cd237a69ebc3466faaccc010cade", + "sha256" : "d3f4393a4b432d1f990782ba17a353a57be3c5b24368c35daf6c1a590fcb1c06", "specId" : "ADR-046-provider-volume-local", "status" : "Proposed", "supersedes" : "`d2b-priv-broker/src/ops/{state_dir,store_sync,store_sync_audit,store_sync_export,store_view_farm,store_view_posture,swtpm_dir}.rs`; `d2b-core/src/{storage,sync,storage_lifecycle}.rs` StorageJson/SyncJson contract; `nixos-modules/store.nix` per-VM hardlink farm activation", @@ -833,7 +832,7 @@ "ADR-046-zone-routing" ], "path" : "docs/specs/ADR-046-reset-and-cutover.md", - "sha256" : "e317fb89859580c38bce1fec411a96b32150b396fe4cd34017cb5b1a058a3360", + "sha256" : "5df1a458e45ca3ac9b91fadddbadbc4bc2fbac371afb7946a2363c7be82674f5", "specId" : "ADR-046-reset-and-cutover", "status" : "Proposed", "supersedes" : "[ADR 0034](../adr/0034-storage-lifecycle-restart-and-synchronization.md) \"Migration decision\" section for the d2b 3.0 cutover; the current `d2b host migrate-storage` verb (retired per `ADR-046-current-code-migration-map` §6 and `ADR-046-cli-and-operations` \"v2 command surface removed at 3.0 clean break\": it served the one-time v1→v2 storage layout cutover only and has no v3 successor)", @@ -934,7 +933,7 @@ "ADR-046-resource-reconciliation" ], "path" : "docs/specs/ADR-046-resources-device.md", - "sha256" : "9f00cc74331399b3e87cd413d5d0ab1b4fdf7f55480afa7b2416b4a48b3bd431", + "sha256" : "15f8bd34429076a12dffb897ddb5e3cbf00ae74b90ee597581e343ddcc529181", "specId" : "ADR-046-resources-device", "status" : "Proposed", "supersedes" : "Current ProcessRole device sidecars (Swtpm, SwtpmPreStartFlush, Usbip, SecurityKeyFrontend, Gpu, GpuRenderNode, Video) and their Nix components", @@ -975,7 +974,7 @@ "ADR-046-terminology-and-identities" ], "path" : "docs/specs/ADR-046-resources-network.md", - "sha256" : "3d74031c68d030e1bbec8b76eabfee8983c5fe52135b62cf6711dacfa57b70c0", + "sha256" : "100155ed78c0370f01664a75eefc3571bdbea59769a72f3a1d333114382586bf", "specId" : "ADR-046-resources-network", "status" : "Proposed", "supersedes" : "`d2b.envs.` and `d2b.realms..network` Nix surfaces (v3 reset)", @@ -1014,7 +1013,7 @@ "ADR-046-terminology-and-identities" ], "path" : "docs/specs/ADR-046-resources-zone-control.md", - "sha256" : "315a8425c62fded2acc8beff719eac35b695a866917e22b80ed2fa481f8fcde2", + "sha256" : "590419e3dce5c2e32c7e97da658aadaea6fedf151ff22ac59e44d365f19e1064", "specId" : "ADR-046-resources-zone-control", "status" : "Proposed", "supersedes" : null, @@ -1081,7 +1080,7 @@ "ADR-046-zone-routing" ], "path" : "docs/specs/ADR-046-security-and-threat-model.md", - "sha256" : "cc78ea774b8b248c729613ed57e254d4fced2c3881d1dd0521a8b9881331f083", + "sha256" : "96c400a2285e9c36c3ca064b726d483d91b465a5d128221d453f03bf90af2f0b", "specId" : "ADR-046-security-and-threat-model", "status" : "Proposed", "supersedes" : null, @@ -1123,7 +1122,7 @@ "ADR-046-terminology-and-identities" ], "path" : "docs/specs/ADR-046-telemetry-audit-and-support.md", - "sha256" : "0979726e4dfe84e1167628727e93d23caa810d247deb5d54036cff46b955777d", + "sha256" : "1af8dbb4f151e7e76c46a0fb1282afe98b5f07800f1ecac69f3954681b1faea5", "specId" : "ADR-046-telemetry-audit-and-support", "status" : "Proposed", "supersedes" : "Current `d2bd` hand-rolled Prometheus registry; current daemon/broker/gateway JSONL audit paths", @@ -1161,8 +1160,35 @@ "ADR-046-feasibility-and-spikes", "ADR-046-nix-configuration", "ADR-046-primitive-resource-composition", + "ADR-046-provider-activation-nixos", + "ADR-046-provider-audio-pipewire", + "ADR-046-provider-clipboard-wayland", + "ADR-046-provider-credential-entra", + "ADR-046-provider-credential-managed-identity", + "ADR-046-provider-credential-secret-service", + "ADR-046-provider-device-gpu", + "ADR-046-provider-device-security-key", + "ADR-046-provider-device-tpm", + "ADR-046-provider-device-usbip", + "ADR-046-provider-display-wayland", "ADR-046-provider-model-and-packaging", + "ADR-046-provider-network-local", + "ADR-046-provider-notification-desktop", + "ADR-046-provider-observability-otel", + "ADR-046-provider-runtime-azure-container-apps", + "ADR-046-provider-runtime-azure-virtual-machine", + "ADR-046-provider-runtime-cloud-hypervisor", + "ADR-046-provider-runtime-qemu-media", + "ADR-046-provider-shell-terminal", "ADR-046-provider-state", + "ADR-046-provider-system-core", + "ADR-046-provider-system-minijail", + "ADR-046-provider-system-systemd", + "ADR-046-provider-transport-azure-relay", + "ADR-046-provider-transport-unix", + "ADR-046-provider-transport-vsock", + "ADR-046-provider-volume-local", + "ADR-046-provider-volume-virtiofs", "ADR-046-reset-and-cutover", "ADR-046-resource-api-and-authorization", "ADR-046-resource-object-model", @@ -1199,7 +1225,7 @@ "ADR-046-terminology-and-identities" ], "path" : "docs/specs/ADR-046-zone-routing.md", - "sha256" : "71f678a258f8fb39762f625b98ac8e48ce4d7660511c4fa46f6c636e7adb74f8", + "sha256" : "2b2c1f179de89219d8cd0ff1c0d54befbb9ee1c83e1c09f15fa4bf62e6713dea", "specId" : "ADR-046-zone-routing", "status" : "Proposed", "supersedes" : "`RealmEntrypointTable`/`EntrypointMode`/`RealmControllerPlacement` enum (→ compiler-only `parentZone` topology + ZoneLink transport + private Zone runtime bootstrap placement); `RouteTreeEngine`/`routing.rs` route contracts (adapted, not deleted); `RemoteNodeRegistry` (→ ZoneLink controller); `OperationRouter` (→ ZoneLinkIdempotencyKey in d2b-bus); `realm_access_resolver` module (→ ZoneEntrypointResolver); `realm-controllers.json`/`realm-identity.json` bundle artifacts (→ Nix-authored Zone settings/ZoneLink resources); `d2b.realms.*` Nix option namespace (→ `d2b.zones.*`); `WorkloadId` routing targets (→ typed Guest/Host resource refs); `CapabilitySet`-only routing authz (→ per-hop RBAC narrowing + capability ceiling)", diff --git a/docs/specs/ADR-046-work-items.json b/docs/specs/ADR-046-work-items.json index 39a744e5e..2d6124bb4 100644 --- a/docs/specs/ADR-046-work-items.json +++ b/docs/specs/ADR-046-work-items.json @@ -7,14 +7,14 @@ "dataMigration" : "Full d2b 3.0 reset; no v2 provider state compatibility", "dependencyOwner" : "ADR046-provider-001; runtime-aca owner", "destination" : "`packages/d2b-provider-runtime-azure-container-apps/src/controller.rs`", - "detailedDesign" : "Async `Guest` reconcile loop: `describe` → `validateSpec` → `plan` → `reconcile` → `observe` → `finalize`. Adoption before first `RuntimeEnsure`. Operation ledger persisted to Volume inside gateway Guest. Credential lease acquire/revoke per call. Noise KK enrollment via ZoneLink (from gateway Guest to managed ACA sandbox). `providerPhase` and `guestIdentityDigest` in `status.provider.details`; no raw endpoint/path in any status field. **ProviderDeployment creates both static Processes; ACA controller never instantiates its own Processes and never writes Provider status directly. All Processes run inside the gateway Guest. No Host Process, no Host Credential, no Host Azure HTTP socket. Long-running cloud ops return `progressing`/`requeue-at` immediately; never block watch loop.** Primary reuse disposition: `replace`. Preserved source-plan detail: REPLACE (old) + ADAPT (main types/traits).", + "detailedDesign" : "Async `Guest` reconcile loop: `describe` → `validateSpec` → `plan` → `reconcile` → `observe` → `finalize`. Adoption before first `RuntimeEnsure`; operation/requeue truth remains in the core Operation ledger and no Provider state Volume is created. Credential lease acquire/revoke per call. The controller creates a same-Zone Provider-owned semantic sandbox-agent Endpoint (`ownerRef` remains the Guest lifecycle edge), resolves opaque transport carriage, and performs Noise KK enrollment for the ACA Provider service/session. `providerPhase` and `guestIdentityDigest` stay in `status.provider.details`; Endpoint readiness/generation/availability stay only in Endpoint status; no raw endpoint/path, cross-Zone ref, route cursor, transport handle, or authority appears in status. **ProviderDeployment creates both static Processes; ACA controller never instantiates its own Processes and never writes Provider status directly. All Processes run inside the gateway Guest. The managed sandbox remains a Guest in the owning Zone and is not a Zone. No Host Process, no Host Credential, no Host Azure HTTP socket. Long-running cloud ops return `progressing`/`requeue-at` immediately; never block watch loop.** Primary reuse disposition: `replace`. Preserved source-plan detail: REPLACE (old) + ADAPT (main types/traits).", "integration" : "Zone ResourceClient → ProviderDeployment → Process launch inside gateway Guest → d2b-bus → deployment service", "removalProof" : "`packages/d2b-provider-aca/` removed only after conformance suite green", "reuseAction" : "replace", "reuseSource" : null, "specId" : "ADR-046-provider-runtime-azure-container-apps", "specPath" : "docs/specs/providers/ADR-046-provider-runtime-azure-container-apps.md", - "validation" : "Controller conformance suite; adoption/ambiguity tests; deadline/cancellation matrix; redaction coverage; **gateway Guest placement validation: assert no Process has `executionRef: Host/*`**; Process spec field schema tests (`spec.template`, canonical `sandbox`/`budget`/`networkUsage`/`endpoints`/`readiness`/`restartPolicy` fields, `mounts` with `required: true`, `providerRef: Provider/system-minijail`); ProviderDeployment creates both Processes (controller never self-spawns); no raw endpoint/path in Guest status", + "validation" : "Controller conformance suite; adoption/ambiguity tests; Endpoint create/adopt/finalize and generation tests; deadline/cancellation/backpressure matrix; redaction coverage; **gateway Guest placement validation: assert no Process has `executionRef: Host/*`**; Process spec field schema tests (`spec.template`, canonical `sandbox`/`budget`/`networkUsage`/`endpoints`/`readiness`/`restartPolicy` fields, `mounts` with `required: true`, `providerRef: Provider/system-minijail`); ProviderDeployment creates both Processes (controller never self-spawns); no raw endpoint/path or ZoneLink status/cursor/authority in Guest or Endpoint status", "workItemId" : "ADR046-aca-001" }, { @@ -50,16 +50,16 @@ { "currentSource" : "`packages/d2b-provider-relay/src/lib.rs`: `AzureRelayTransportProvider`; `packages/d2b-provider-aca/src/lib.rs`: `AcaRelayTransportConfig`", "dataMigration" : "No relay session compatibility; re-enroll on first `RuntimeAdopt`", - "dependencyOwner" : "ADR046-session-001; ZoneLink transport owner", - "destination" : "ZoneLink resource `spec.transportSettings` (§15.4); `Provider/transport-azure-relay` dossier (separate)", - "detailedDesign" : "`AcaRelayTransportConfig` fields mapped to `transportSettings.relayNamespace`, `.relayHybridConnection`, `.relayCredentialRef`. KK enrollment replaces ZoneLink transport. Primary reuse disposition: `replace`. Preserved source-plan detail: REPLACE (both); ADAPT config fields to ZoneLink transport settings.", - "integration" : "ZoneLink controller resolves transport Provider; ACA controller establishes enrolled KK ComponentSession over relay transport after `GuestProvision`", - "removalProof" : "`packages/d2b-provider-relay/` removed after `transport-azure-relay` Provider conformance", + "dependencyOwner" : "ADR046-session-001; ACA Endpoint/session and transport-capability integration owner", + "destination" : "ACA sandbox-agent Endpoint/session controller (§§7, 12); `Provider/transport-azure-relay` private configuration and carriage dossier (separate)", + "detailedDesign" : "Split `AcaRelayTransportConfig`: relay-private namespace/connection/credential fields move behind `Provider/transport-azure-relay`; this Provider retains only `sandboxTransportAlias`. The ACA controller creates the Provider-owned semantic Endpoint (with the Guest lifecycle edge), resolves an opaque `OwnedTransport`, and owns KK enrollment, authenticated `d2b.aca.v3.sandbox-agent` service/session generation, deadlines, cancel, bounded queues, credits/backpressure, reconcile, adoption, and finalization. The ordinary managed sandbox is explicitly not a Zone; no cross-Zone ResourceRef or ZoneLink status/cursor/authority is accepted or emitted. Primary reuse disposition: `replace`. Preserved source-plan detail: REPLACE (both); ADAPT relay fields only into the carriage Provider's private config.", + "integration" : "ACA controller resolves the semantic Endpoint and transport Provider capability, then establishes the enrolled KK ComponentSession after `GuestProvision`; transport Provider supplies carriage only", + "removalProof" : "`packages/d2b-provider-relay/` removed after `transport-azure-relay` Provider conformance; no ACA schema, Nix emitter, controller, status adapter, fixture, or test retains an ordinary-sandbox ZoneLink path", "reuseAction" : "replace", "reuseSource" : null, "specId" : "ADR-046-provider-runtime-azure-container-apps", "specPath" : "docs/specs/providers/ADR-046-provider-runtime-azure-container-apps.md", - "validation" : "ZoneLink enrollment tests; relay unavailability tests; KK re-enrollment after sandbox restart", + "validation" : "Endpoint ownership/resolution tests; relay unavailability tests; KK re-enrollment after sandbox/controller restart; deadline/cancel/credit-backpressure tests; schema/status tests reject ZoneLink refs, phase, cursors, and authority", "workItemId" : "ADR046-aca-004" }, { @@ -67,14 +67,14 @@ "dataMigration" : "None — no state Volume at v3 `1.0`", "dependencyOwner" : "ADR046-aca-001; state/migration owner", "destination" : "`packages/d2b-provider-runtime-azure-container-apps/src/types.rs`", - "detailedDesign" : "Replace `RealmId`/`WorkloadId` with v3 `Zone`/`Guest` UID types. `AcaResourceBinding` keys the adoption query. The provider declares **no** Provider state Volume: bounded, non-secret sandbox binding/adoption metadata lives in `Guest.status` (latest bounded observed handle digests) and in-flight operation/requeue truth lives in the core Operation ledger (D087). Neither Process mounts a state Volume; there is no `sandbox-state`/`service-state` Volume, no `User/d2b-aca-controller`/`User/d2b-aca-deployment-service` state-layout principal, and no empty identity-only Volume. On restart the controller re-derives observed binding from `Guest.status`, the core Operation ledger, and an external `find_sandboxes` query, treating status as observation, never authority. Host never holds cloud binding, admission, PSK, or operation state. Primary reuse disposition: `adapt`. Preserved source-plan detail: RETAIN+ADAPT.", - "integration" : "No Provider state Volume is created before Processes start; the controller writes its bounded observed binding/adoption metadata to `Guest.status` on material change and reads in-flight operation state from the core Operation ledger adapter", + "detailedDesign" : "Replace `RealmId`/`WorkloadId` with v3 owning-`Zone`/`Guest` UID types. `AcaResourceBinding` keys the adoption query but never names a child Zone. The provider declares **no** Provider state Volume: bounded, non-secret sandbox binding/adoption metadata lives in `Guest.status` (latest bounded observed handle digests) and in-flight operation/requeue truth lives in the core Operation ledger (D087). Neither Process mounts a state Volume; there is no `sandbox-state`/`service-state` Volume, no `User/d2b-aca-controller`/`User/d2b-aca-deployment-service` state-layout principal, and no empty identity-only Volume. On restart the controller re-derives observed binding from `Guest.status`, the core Operation ledger, and an external `find_sandboxes` query, ensures the Provider-owned Endpoint, resolves fresh transport carriage, and authenticates a fresh Provider session, treating all status as observation and never authority. Host never holds cloud binding, admission, PSK, operation, Endpoint, or session state. Primary reuse disposition: `adapt`. Preserved source-plan detail: RETAIN+ADAPT.", + "integration" : "No Provider state Volume is created before Processes start; the controller writes bounded observed binding/adoption metadata to `Guest.status`, writes only standard Endpoint observations to Endpoint status, reads in-flight operation state from the core Operation ledger adapter, and retains no transport or ZoneLink cursor across restart", "removalProof" : "Old in-memory-only operation ledger removed after core Operation ledger adapter passes; `operation-ledger` stateNamespace absent from component descriptor", "reuseAction" : "adapt", "reuseSource" : null, "specId" : "ADR-046-provider-runtime-azure-container-apps", "specPath" : "docs/specs/providers/ADR-046-provider-runtime-azure-container-apps.md", - "validation" : "Controller/service declare empty `stateNamespaces`; no `sandbox-state`/`service-state` Volume created; neither Process mounts a state Volume; `Guest.status` binding/adoption fields are bounded, non-secret, and carry no credential/endpoint/poll-URL bytes; restart re-derivation from status/core ledger/external `find_sandboxes` without a Volume; core Operation ledger adapter integration test", + "validation" : "Controller/service declare empty `stateNamespaces`; no `sandbox-state`/`service-state` Volume created; neither Process mounts a state Volume; `Guest.status` binding/adoption fields and standard Endpoint status are bounded, non-secret, and carry no credential/endpoint/poll-URL/ZoneLink cursor or authority bytes; restart re-derivation from status/core ledger/external `find_sandboxes`, Endpoint ensure, and fresh KK session without a Volume; core Operation ledger adapter integration test", "workItemId" : "ADR046-aca-005" }, { @@ -82,14 +82,14 @@ "dataMigration" : "None — full d2b 3.0 reset; no prior state to migrate", "dependencyOwner" : "ADR046-aca-001; Nix/telemetry owner", "destination" : "`nixos-modules/` (generated Guest resource options); `packages/d2b-provider-runtime-azure-container-apps/src/{audit,metrics}.rs`", - "detailedDesign" : "Eval-time assertions for ACA-specific invariants (§15.6). Closed OTEL label set (§13.4). Audit event schema (§13.3). Tracing target constant `d2b_provider_runtime_azure_container_apps::credential_lease_cleanup` retained. Primary reuse disposition: `replace`. Preserved source-plan detail: REPLACE (Nix emitter) + ADAPT (metric/audit shapes).", + "detailedDesign" : "Eval-time assertions for ACA-specific invariants (§15.7), including rejection of ZoneLink/child-Zone fields for an ordinary sandbox and exact same-Zone sandbox-agent Endpoint template ownership. Closed OTEL label set (§13.4). Audit event schema (§13.3). Tracing target constant `d2b_provider_runtime_azure_container_apps::credential_lease_cleanup` retained. Primary reuse disposition: `replace`. Preserved source-plan detail: REPLACE (Nix emitter) + ADAPT (metric/audit shapes).", "integration" : "Nix eval gate; `observability-otel` Provider OTEL pipeline", "removalProof" : "Old Nix `ProviderManaged` workload options retired after Guest resource Nix emitter parity", "reuseAction" : "replace", "reuseSource" : null, "specId" : "ADR-046-provider-runtime-azure-container-apps", "specPath" : "docs/specs/providers/ADR-046-provider-runtime-azure-container-apps.md", - "validation" : "Label cardinality policy test; audit commit-before-complete test; Nix assertion eval tests", + "validation" : "Label cardinality policy test; audit commit-before-complete test; Nix assertion eval tests; generated resource scan proves no ACA sandbox ZoneLink is emitted", "workItemId" : "ADR046-aca-006" }, { @@ -97,14 +97,14 @@ "dataMigration" : "None — full d2b 3.0 reset; no prior state to migrate", "dependencyOwner" : "ADR046-aca-001; Nix/gateway wiring owner", "destination" : "`nixos-modules/` (gateway Guest declaration, Process template wiring, Credential scope assertion); eval-time validation module", - "detailedDesign" : "Nix eval-time assertions for: (a) `gatewayExecutionRef` resolves to a `Guest` resource, not `Host/*`; (b) Credential `scope.executionRef` matches `gatewayExecutionRef`; (c) all Process templates emitted for this Provider have `executionRef` equal to `gatewayExecutionRef`. No `User` resource or `users.users.*` declarations required — component principals are framework-assigned and not OS accounts. Gateway Guest NixOS closure includes only the two ACA component binaries (§15.6). Assertion error messages name the offending resource and the required `gatewayExecutionRef`.", + "detailedDesign" : "Nix eval-time assertions for: (a) `gatewayExecutionRef` resolves to a `Guest` resource, not `Host/*`; (b) Credential `scope.executionRef` matches `gatewayExecutionRef`; (c) all Process templates emitted for this Provider have `executionRef` equal to `gatewayExecutionRef`; (d) `sandboxTransportAlias` resolves the signed carriage capability; and (e) an ordinary ACA sandbox cannot declare/reference ZoneLink, child-Zone, route-cursor, or ZoneLink-authority fields. The controller, not Nix, creates each same-Zone Provider-owned sandbox-agent Endpoint with the Guest as lifecycle `ownerRef`. No `User` resource or `users.users.*` declarations required — component principals are framework-assigned and not OS accounts. Gateway Guest NixOS closure includes only the two ACA component binaries (§15.6). Assertion error messages name the offending resource and the required `gatewayExecutionRef`; Zone-shaped ACA config fails `aca-sandbox-is-not-zone`.", "integration" : "Nix eval gate; `d2b.zones.*.resources` validation pass; consumer flake usage example", "removalProof" : "n/a — ongoing eval-time constraint", "reuseAction" : "create", "reuseSource" : null, "specId" : "ADR-046-provider-runtime-azure-container-apps", "specPath" : "docs/specs/providers/ADR-046-provider-runtime-azure-container-apps.md", - "validation" : "Nix eval assertion tests (wrong `executionRef` → assertion fires; correct setup → passes); §15.7 assertion coverage tests", + "validation" : "Nix eval assertion tests (wrong `executionRef` → assertion fires; correct setup → passes; ACA ZoneLink/child-Zone fields → `aca-sandbox-is-not-zone`); Endpoint template ownership/shape and §15.7 assertion coverage tests", "workItemId" : "ADR046-aca-007" }, { @@ -311,7 +311,7 @@ "integration" : "Registered with Zone core as a controller under `Provider/audio-pipewire`.", "removalProof" : "Supersedes `audio_dispatch.rs`; `d2bd` audio dispatch deleted after e2e parity test confirms", "reuseAction" : "adapt", - "reuseSource" : "None directly; reconcile flow is new async controller", + "reuseSource" : null, "specId" : "ADR-046-provider-audio-pipewire", "specPath" : "docs/specs/providers/ADR-046-provider-audio-pipewire.md", "validation" : "Fast hermetic `tests/audio_service_controller.rs`: neutral type/provider selection, foreign-provider ignore/deny, owner authority, bounded aggregate microphone status, projection ownerRef/import chain, core-only create/delete, projection no-PipeWire-open, revocation queue cancellation, and D091 propagation. `tests/audio_binding_controller.rs`: neutral type/provider selection, required same-Zone serviceRef, owner/projection dispatch, child Process/private Endpoint state machine, `Applied|Queued|MicQueueFull` status mapping, off/delete/revocation cancellation, grant changes, absence/failures/deletion. Conformance asserts no AudioBinding export/projection, no broker/pidfd/EphemeralProcess/Volume/User ops. ProviderDeployment integration remains fake-only and validates empty ProviderStateSet.", @@ -326,7 +326,7 @@ "integration" : "Second binary in the `d2b-provider-audio-pipewire` package. Registered as a user-session service under `Provider/audio-pipewire`.", "removalProof" : "Supersedes `d2bd`'s `PipeWireHostController` direct session access; `d2bd` audio host controller deleted after e2e parity", "reuseAction" : "create", - "reuseSource" : "None; new component", + "reuseSource" : null, "specId" : "ADR-046-provider-audio-pipewire", "specPath" : "docs/specs/providers/ADR-046-provider-audio-pipewire.md", "validation" : "`tests/mediator.rs`: owner-Service FD handoff and calls; captureAlias; node-id sealing; session-unavailable; concurrent speaker Guest isolation; exclusive microphone no-overlap and mute-before-handoff; teardown; projection Service cannot resolve mediator Endpoint or portal attachment", @@ -830,7 +830,7 @@ { "currentSource" : "None (no completion exists in v3 baseline)", "dataMigration" : "None — full d2b 3.0 reset; no prior state to migrate", - "dependencyOwner" : "ADR046-cli-001; CLI crate owner", + "dependencyOwner" : "ADR046-cli-001, ADR046-routing-012, ADR046-routing-016; CLI crate owner", "destination" : "`packages/d2b/src/complete.rs` (`d2b complete bash/zsh/fish`)", "detailedDesign" : "`d2b complete ` emits completion script; uses clap `CommandFactory::command()` plus dynamic projection fetch (2s per-Provider, 10s total); result bounded at 256 KiB; shell-escaped; newlines stripped Primary reuse disposition: `adapt`. Preserved source-plan detail: copy-unchanged (clap_complete).", "integration" : "Standalone command; no Zone API required for static completion; Zone API used for dynamic Provider projection", @@ -877,14 +877,14 @@ "dataMigration" : "None — full d2b 3.0 reset; no prior state to migrate", "dependencyOwner" : "ADR046-cli-001; CLI crate owner", "destination" : "`packages/d2b/src/zone.rs` (`d2b zone get/list/status`)", - "detailedDesign" : "`d2b zone get []` fetches Zone self resource via `ConnectedClient::invoke`; `d2b zone list` lists ZoneLink resources. v2 commands (`d2b realm list/inspect/enter/run`) are deleted at 3.0; no dispatch wiring. Excluded ADR 0045: `RealmServiceServer`/`RealmServiceProcess` multi-realm service; `RemoteNodeRegistration` constellation routing; `TargetInput::Realm`; `RealmMethod::ResolveRoute`/`AuthorizeShortcut`/`RevokeShortcut`.", - "integration" : "ZoneContext → Zone resource Get/List via `ConnectedClient::invoke`", + "detailedDesign" : "`d2b zone get []` fetches the current Zone self resource only for the local name; another declared name is inspected through the read-only Zone topology projection. `d2b zone list` lists sorted compiler-only `{ childZone, parentZone }` rows joined with authenticated route/projection status. `d2b zone status --watch` watches the corresponding projection revision. None of these commands lists or gets a parent-local ZoneLink: the sole ZoneLink resource and handler are child-local, while the parent owns only sealed allocator topology and authenticated route state. Generated DTO/golden tests reject ZoneLink names/spec/status/provider refs/fingerprints/transport settings in parent-side output. v2 commands (`d2b realm list/inspect/enter/run`) are deleted at 3.0; no dispatch wiring. Excluded ADR 0045: `RealmServiceServer`/`RealmServiceProcess` multi-realm service; `RemoteNodeRegistration` constellation routing; `TargetInput::Realm`; `RealmMethod::ResolveRoute`/`AuthorizeShortcut`/`RevokeShortcut`.", + "integration" : "ZoneContext → local Zone self-resource Get or `ZoneService` topology/route projection via `ConnectedClient::invoke`", "removalProof" : "`cmd_realm_*` and `target_routing.rs` removed only after zone routes pass equivalence tests", "reuseAction" : "adapt", - "reuseSource" : "main `a1cc0b2d` — reference only (no copy): `packages/d2b-realm-router/src/service_v2.rs` `RealmServiceServer`, `RealmServiceProcess`, `RealmMethod::Inspect`, `RealmMethod::ResolveRoute` — server-side multi-realm routing; this is the ADR 0045 multi-Zone topology and is **excluded** from v3 CLI as a direct reuse source; `packages/d2b-realm-router/src/remote_node.rs` `RemoteNodeRegistration`, `RemoteNodeEntry` — constellation remote routing; also excluded; note: `packages/d2b-client/src/daemon_service.rs` `DaemonClient::list_workloads()` and `DaemonMethod::ListRealms` are the closest live list-call patterns, but their zone/workload scoping uses `RealmPath`/`RealmId` types that are ADR 0045-specific; adapt `ConnectedClient::invoke()` with a v3 Zone List request type instead; no main symbols are copied unchanged for cli-009; the zone resource API type design is an ADR-only deliverable pending Zone resource spec", + "reuseSource" : "main `a1cc0b2d` — reference only (no copy): `packages/d2b-realm-router/src/service_v2.rs` `RealmServiceServer`, `RealmServiceProcess`, `RealmMethod::Inspect`, `RealmMethod::ResolveRoute` — server-side multi-realm routing; this is the ADR 0045 multi-Zone topology and is **excluded** from v3 CLI as a direct reuse source; `packages/d2b-realm-router/src/remote_node.rs` `RemoteNodeRegistration`, `RemoteNodeEntry` — constellation remote routing; also excluded; note: `packages/d2b-client/src/daemon_service.rs` `DaemonClient::list_workloads()` and `DaemonMethod::ListRealms` are the closest live list-call patterns, but their zone/workload scoping uses `RealmPath`/`RealmId` types that are ADR 0045-specific; adapt `ConnectedClient::invoke()` with the v3 `ZoneService` topology-projection request instead; no main symbols are copied unchanged for cli-009; the projection type is an ADR-only deliverable owned by zone routing", "specId" : "ADR-046-cli-and-operations", "specPath" : "docs/specs/ADR-046-cli-and-operations.md", - "validation" : "Zone get/list tests; confirm v2 `cmd_realm_*` paths are absent", + "validation" : "Zone self-get plus topology list/get/status/watch tests; disconnected and stale authenticated-route projections; golden output contains `{ childZone, parentZone }` and route/projection status but no ZoneLink fields; parent store has no ZoneLink row or watch; confirm v2 `cmd_realm_*` paths are absent", "workItemId" : "ADR046-cli-009" }, { @@ -911,7 +911,7 @@ "integration" : "Nix build → per-Zone `resource-bundle.json` + global private artifact catalog → `d2b activation switch` → `d2b-core-controller` configuration service → resource API Create/Update/Delete → owner controllers → finalizer cascade → cleanup watcher → Zone status update", "removalProof" : "Old `nixos-modules/manifest.nix`, `nixos-modules/bundle-artifacts.nix` emitters removed only after `bundle-emit.nix` produces equivalent-or-superseding output and all downstream consumers of the old bundle format are migrated", "reuseAction" : "replace", - "reuseSource" : "None (new implementation; no main `a1cc0b2d` reuse — this is the Nix/Zone side, not the CLI client side)", + "reuseSource" : null, "specId" : "ADR-046-cli-and-operations", "specPath" : "docs/specs/ADR-046-cli-and-operations.md", "validation" : "Runtime integration: all CLI-visible cleanup/status/rollback/gc/audit tests (§CLI-visible tests for activation and cleanup), including no force-finalizer path; Nix unit and build tests owned by ADR-046-nix-configuration spec; canonical-example fixture validates every authored resource against the authoritative Host, Guest, and Provider schemas, resolves every artifact/resource reference, rejects unsupported fields, and proves the compiled Guest `spec` is field-for-field identical to the adjacent JSON", @@ -1526,7 +1526,7 @@ "integration" : "`make test-drift` gains a row running this generator and `git diff --exit-code`; every wave's exit criteria (§4) require it committed as the wave's last commit", "removalProof" : "Not applicable", "reuseAction" : "adapt", - "reuseSource" : "none required — this generator is specific to the `docs/specs/ADR-046-*` manifest shape", + "reuseSource" : null, "specId" : "ADR-046-validation-and-delivery", "specPath" : "docs/specs/ADR-046-validation-and-delivery.md", "validation" : "Golden-fixture test against a small synthetic spec directory; drift test against the real `docs/specs/` tree", @@ -1643,7 +1643,7 @@ "dependencyOwner" : "ADR046-device-001; device-usbip provider owner", "destination" : "`packages/d2b-provider-device-usbip/src/` (controller, daemon Process, bind/unbind EphemeralProcess, firewall); `packages/d2b-provider-device-usbip/tests/` (hermetic Cargo integration); `packages/d2b-provider-device-usbip/integration/` (container/Host scenarios); `packages/d2b-provider-device-usbip/README.md`", "detailedDesign" : "Device spec/status; bus ID validation; firewall rule ownership-marker; bind/unbind EphemeralProcess; per-Device daemon Process (owned by device-usbip; Network supplies dependency/firewall interface); Nix emitter; all four required crate paths present (see \"Provider crate layout\") Primary reuse disposition: `adapt`. Preserved source-plan detail: extract and adapt.", - "integration" : "Zone resource store; broker `UsbipBindFirewallRule`; nftables marker", + "integration" : "Zone resource store; broker `UsbipBindFirewallRule { action: Ensure \\| Remove }`; nftables marker", "removalProof" : "ProcessRole::Usbip removed after parity", "reuseAction" : "adapt", "reuseSource" : null, @@ -2366,7 +2366,7 @@ "integration" : "None (standalone; no d2b-bus/ComponentSession/broker dependency)", "removalProof" : "Per SPIKE-01/SPIKE-02 Cleanup rows: deleted once `packages/d2b-resource-store-redb` and `packages/d2b-controller-toolkit/benches/reaction.rs` reproduce equal-or-stricter coverage", "reuseAction" : "adapt", - "reuseSource" : "None (redb is a new external dependency; no main or v3 code implements it)", + "reuseSource" : null, "specId" : "ADR-046-feasibility-and-spikes", "specPath" : "docs/specs/ADR-046-feasibility-and-spikes.md", "validation" : "SPIKE-01 metrics (1)-(5) and SPIKE-02 metrics (1) across all 3 concurrency profiles, per those entries' exact pass/fail thresholds", @@ -2381,7 +2381,7 @@ "integration" : "Consumes `ADR046-feasibility-001`'s hint-bus shape as its watch-receiver input", "removalProof" : "Deleted once `packages/d2b-controller-toolkit/benches/reaction.rs` and the Process Provider integration tests named by `ADR046-reconcile-003` reproduce equal-or-stricter coverage", "reuseAction" : "adapt", - "reuseSource" : "None (the generic async controller loop is ADR-only per `ADR-046-resource-reconciliation`'s own current-code-fit row)", + "reuseSource" : null, "specId" : "ADR-046-feasibility-and-spikes", "specPath" : "docs/specs/ADR-046-feasibility-and-spikes.md", "validation" : "SPIKE-03 metrics (1)-(3) and thresholds", @@ -2396,7 +2396,7 @@ "integration" : "None (standalone)", "removalProof" : "Deleted once `packages/d2b-provider-supervisor` and the volume-domain effect adapter each carry an equal-or-stricter in-tree blocking-adapter regression test", "reuseAction" : "adapt", - "reuseSource" : "None", + "reuseSource" : null, "specId" : "ADR-046-feasibility-and-spikes", "specPath" : "docs/specs/ADR-046-feasibility-and-spikes.md", "validation" : "SPIKE-04 heartbeat-jitter metric and threshold", @@ -2411,7 +2411,7 @@ "integration" : "None (standalone)", "removalProof" : "Deleted once the real Provider-toolkit crate (`ADR046-provider-001` destination) ships equal-or-stricter manifest-parsing/enumeration/workspace-policy coverage", "reuseAction" : "adapt", - "reuseSource" : "None", + "reuseSource" : null, "specId" : "ADR-046-feasibility-and-spikes", "specPath" : "docs/specs/ADR-046-feasibility-and-spikes.md", "validation" : "SPIKE-05 metrics (1)-(4) across 20 repeated randomized-order manifest loads", @@ -2441,7 +2441,7 @@ "integration" : "None between the two spikes beyond sharing the same fake resource-store oracle shape", "removalProof" : "Deleted per each spike's Cleanup row: the real `ADR-046-provider-state` work-item destination for SPIKE-09; `d2b-provider-volume-local`'s own `tests/`/`integration/` suite for SPIKE-10", "reuseAction" : "adapt", - "reuseSource" : "None", + "reuseSource" : null, "specId" : "ADR-046-feasibility-and-spikes", "specPath" : "docs/specs/ADR-046-feasibility-and-spikes.md", "validation" : "SPIKE-09 metrics (1)-(4); SPIKE-10 metrics (1)-(5), zero-tolerance on path leakage", @@ -2456,7 +2456,7 @@ "integration" : "None (standalone; requires a Linux host with `clone3`/`pidfd_open`, and optionally a running `systemd --user` instance behind the `systemd-user` feature)", "removalProof" : "Deleted once `packages/d2b-provider-system-systemd` and `packages/d2b-provider-system-minijail` each carry this exact shared conformance suite in their own `tests/`", "reuseAction" : "adapt", - "reuseSource" : "None", + "reuseSource" : null, "specId" : "ADR-046-feasibility-and-spikes", "specPath" : "docs/specs/ADR-046-feasibility-and-spikes.md", "validation" : "SPIKE-11 metrics (1)-(4), zero-tolerance on false adoption", @@ -2471,7 +2471,7 @@ "integration" : "None (standalone flake; no dependency on the main `flake.nix`)", "removalProof" : "Deleted once the real `nixos-modules/resources.nix` and `packages/xtask` `gen-schemas` implementation reproduce these metrics as part of `make test-drift`/`make test-flake`", "reuseAction" : "adapt", - "reuseSource" : "None", + "reuseSource" : null, "specId" : "ADR-046-feasibility-and-spikes", "specPath" : "docs/specs/ADR-046-feasibility-and-spikes.md", "validation" : "SPIKE-12 metrics (1)-(5), byte-for-byte reproducibility across 3 hermetic builds", @@ -2486,7 +2486,7 @@ "integration" : "None between the two spikes beyond sharing the same fixture command-table shape", "removalProof" : "Deleted per each spike's Cleanup row: the real `d2b` CLI crate's own discovery conformance test for SPIKE-13; the real CLI crate's workspace-policy/lint gate plus the real bootstrap sequence for SPIKE-14", "reuseAction" : "adapt", - "reuseSource" : "None", + "reuseSource" : null, "specId" : "ADR-046-feasibility-and-spikes", "specPath" : "docs/specs/ADR-046-feasibility-and-spikes.md", "validation" : "SPIKE-13 metrics (1)-(7); SPIKE-14 metrics (1)-(3), zero-tolerance on legacy-file access", @@ -2501,7 +2501,7 @@ "integration" : "Depends on and imports the fake shapes from `proofs/redb-resource-store-spike/`, `proofs/process-fastlaunch-spike/`, `proofs/effectport-async-spike/`, `proofs/provider-packaging-spike/`, `proofs/bus-routing-noise-spike/`, `proofs/transport-opaque-streams-spike/`, `proofs/credential-kk-e2e-spike/`, `proofs/provider-state-export-spike/`, `proofs/volume-policy-spike/`, and `proofs/process-provider-conformance-spike/`", "removalProof" : "Deleted once the real integration test suites named by the individual Provider dossiers (`integration/` per D059) collectively reproduce all three compositions against real, non-fake Zone/store/bus/broker code", "reuseAction" : "adapt", - "reuseSource" : "None beyond what `-001` through `-009` already reuse", + "reuseSource" : null, "specId" : "ADR-046-feasibility-and-spikes", "specPath" : "docs/specs/ADR-046-feasibility-and-spikes.md", "validation" : "SPIKE-15 metrics (1)-(4) across all three compositions", @@ -2561,7 +2561,7 @@ "integration" : "`scheduled-observe` trigger from reconcile loop calls `probe::check_drm_device`", "removalProof" : "N/A (new module)", "reuseAction" : "create", - "reuseSource" : "None; probe is `ADR-only`", + "reuseSource" : null, "specId" : "ADR-046-provider-device-gpu", "specPath" : "docs/specs/providers/ADR-046-provider-device-gpu.md", "validation" : "`tests/conformance.rs` contains probe-mock path; `cargo test` passes", @@ -2576,7 +2576,7 @@ "integration" : "Tested by `tests/arbitration_conflict.rs`; integration fixture `render_node_shared/`", "removalProof" : "N/A (new module)", "reuseAction" : "create", - "reuseSource" : "None", + "reuseSource" : null, "specId" : "ADR-046-provider-device-gpu", "specPath" : "docs/specs/providers/ADR-046-provider-device-gpu.md", "validation" : "`cargo test -p d2b-provider-device-gpu --test arbitration_conflict`; `cargo test -p d2b-provider-device-gpu --test render_node_enforcement`", @@ -2636,7 +2636,7 @@ "integration" : "`tests/status_state.rs`; `integration/gpu_worker_start/` verifies controller startup is gated by resource dependencies and status writer authority, not by a Provider state Volume", "removalProof" : "`StorageRoot`/`StoragePathSpec` lifecycle tracking entries for GPU/video roles in `d2b-core/src/storage.rs` removed after Device/Process status-first lifecycle and restart-adoption integration tests pass in a live Zone", "reuseAction" : "create", - "reuseSource" : "None", + "reuseSource" : null, "specId" : "ADR-046-provider-device-gpu", "specPath" : "docs/specs/providers/ADR-046-provider-device-gpu.md", "validation" : "`cargo test -p d2b-provider-device-gpu --test status_state`; component descriptor golden has no Provider state Volume declaration; controller Process template has no `/state` mount; ProviderStateSet query is empty; status/core-ledger fields carry bounded operational observations", @@ -2651,7 +2651,7 @@ "integration" : "Workspace policy checks for `README.md` presence", "removalProof" : "N/A (new file)", "reuseAction" : "create", - "reuseSource" : "None", + "reuseSource" : null, "specId" : "ADR-046-provider-device-gpu", "specPath" : "docs/specs/providers/ADR-046-provider-device-gpu.md", "validation" : "`make test-policy` (workspace crate layout policy check)", @@ -2811,12 +2811,12 @@ "currentSource" : "`packages/d2b-core/src/host.rs` lines 290–520 (`NetEnv`, `IfName`, `ExternalNetworkPolicy`, `NftablesModel`, `BridgePortFlags`, `TapRole`, `Ipv6SysctlEntry`, `IfNameMapping` lines 242–256; **also** `VmRuntimeRow` lines 155–167 with `tap`/`bridge`/`net_vm`/`env` fields — attachment status precursors); `packages/d2b-core/src/processes.rs` lines 98–141 (`ProcessNetworkInterface`, `ProcessNetworkInterfaceType`, `ProcessMacvtapInterface` — current VMM runner network interface DTOs; these are per-Guest VMM fields, not Network-level fields, and migrate to Guest spec under `Provider/runtime-cloud-hypervisor`); `packages/d2b-contracts/src/broker_wire.rs` (authoritative broker op list; network-relevant: `ApplyNftables`, `ApplyNmUnmanaged`, `ApplyRoute`, `ApplySysctl`, `SetBridgePortFlags`, `UpdateHostsFile`, `SeedDnsmasqLease`, `CreatePersistentTap`, `CreateTapFd`); `nixos-modules/lib.nix` lines 396–460 (`subnetIp`, `subnetMask`, `mkMac`, `cidrOverlaps`)", "dataMigration" : "Full d2b 3.0 reset; no env→Network import", "dependencyOwner" : "W0 shared contract root; `d2b-contracts`", - "destination" : "`packages/d2b-contracts/src/v3/network.rs`: NetworkSpec, NetworkStatus, AttachmentSpec, AttachmentStatus, ExternalAttachmentSpec, ExternalAttachmentStatus, PortForwardSpec, NetworkConditionType; `packages/d2b-contracts/src/v3/ifname.rs`: IfName newtype, derivation, collision detection (extracted from `d2b-host/src/ifname.rs`). Also defines `User/net-local-controller` as a proper Resource with explicit lifecycle: `Provider/network-local`'s Nix package/module provisions the reserved `net-local-controller` OS account with a private fixed UID/GID in Host prerequisites and in the generic net-VM nixos-system artifact (same account, same UID/GID inside the Guest); the network-local controller creates and owns the User Resource (`spec.osUsername: net-local-controller`, `ownerRef: Provider/network-local`, `managedBy: controller`); `Provider/system-core` verifies the account via NSS lookup and reconciles the User Resource to Ready — it does not provision the OS account. No numeric UID/GID enters any ResourceSpec field, authz check, or audit record; `User.status` MAY carry diagnostic `uid`/`gid` values discovered by NSS lookup, but those are informational only and are never authorization inputs. The network-local controller waits for `User/net-local-controller` to reach `Ready` before creating any config Volume (reconcile precondition, not a bootstrap side effect).", - "detailedDesign" : "Strict ResourceEnvelope with Network-specific spec/status. IfName newtype: IFNAMSIZ-1 validated, FNV-1a 64-bit derivation, base32 Crockford, 8-char suffix, bridge/tap role prefixes, detect_collisions over IfNameMapping slice. cidrOverlaps: pure Rust IPv4 arithmetic, same algorithm as lib.nix. NetworkSpec validators: /24 lanCidr with .0 base, /30 uplinkCidr, unique attachment indices 2–250, default hostBlocklist enforcement. Primary reuse disposition: `adapt`. Preserved source-plan detail: extract and adapt.", + "destination" : "`packages/d2b-contracts/src/v3/network.rs`: NetworkSpec, NetworkStatus, AttachmentSpec, AttachmentStatus, ExternalAttachmentSpec, ExternalAttachmentStatus, PortForwardSpec, NetworkConditionType, opaque AttachmentHandle, and AttachmentGenerationFence; `packages/d2b-contracts/src/v3/ifname.rs`: IfName newtype, derivation, collision detection (extracted from `d2b-host/src/ifname.rs`). Also defines `User/net-local-controller` as a proper Resource with explicit lifecycle: `Provider/network-local`'s Nix package/module provisions the reserved `net-local-controller` OS account with a private fixed UID/GID in Host prerequisites and in the generic net-VM nixos-system artifact (same account, same UID/GID inside the Guest); the network-local controller creates and owns the User Resource (`spec.osUsername: net-local-controller`, `ownerRef: Provider/network-local`, `managedBy: controller`); `Provider/system-core` verifies the account via NSS lookup and reconciles the User Resource to Ready — it does not provision the OS account. No numeric UID/GID enters any ResourceSpec field, authz check, or audit record; `User.status` MAY carry diagnostic `uid`/`gid` values discovered by NSS lookup, but those are informational only and are never authorization inputs. The network-local controller waits for `User/net-local-controller` to reach `Ready` before creating any config Volume (reconcile precondition, not a bootstrap side effect).", + "detailedDesign" : "Strict ResourceEnvelope with Network-specific spec/status. IfName newtype: IFNAMSIZ-1 validated, FNV-1a 64-bit derivation, base32 Crockford, 8-char suffix, bridge/tap role prefixes, detect_collisions over IfNameMapping slice. cidrOverlaps: pure Rust IPv4 arithmetic, same algorithm as lib.nix. NetworkSpec validators: /24 lanCidr with .0 base, /30 uplinkCidr, unique attachment indices 2–250, default hostBlocklist enforcement. The opaque attachment realization binds Network UID/generation and attachment UID/generation so deletion can supply a non-printable ID plus explicit expected generation fence without an IfName/path. Primary reuse disposition: `adapt`. Preserved source-plan detail: extract and adapt.", "integration" : "Provider dossiers, Nix resource compiler, resource store/API bind these canonical types", "removalProof" : "Old `d2b_core::host::NetEnv` and related types removed only after v3 resource API consumers use `d2b_contracts::v3::network` types", "reuseAction" : "adapt", - "reuseSource" : "None from main; all from v3 baseline", + "reuseSource" : null, "specId" : "ADR-046-resources-network", "specPath" : "docs/specs/ADR-046-resources-network.md", "validation" : "Golden JSON/CBOR vectors; CIDR overlap property tests; IfName collision and derivation determinism tests; default hostBlocklist enforcement; attachment index uniqueness; `User/net-local-controller` User resource lifecycle/readiness test: controller creates User Resource with `spec.osUsername = \"net-local-controller\"` (`ownerRef: Provider/network-local`); controller waits for User resource to reach `Ready` before proceeding; controller aborts with `ConfigVolumeReady=False/user-not-ready` if User resource is not Ready; verifies no numeric UID/GID appears in the Resource spec, authz check, or audit record; verifies that any diagnostic `uid`/`gid` in `User.status` is never used as an authorization input", @@ -2868,26 +2868,26 @@ "workItemId" : "ADR046-network-004" }, { - "currentSource" : "`nixos-modules/network.nix` (tap/sysctl sections); `packages/d2b-host/src/{bridge_port,nftables,netlink,routes}.rs`; broker ops in `packages/d2b-contracts/src/broker_wire.rs`: **real runtime ops** `ApplyNftables`, `ApplyNmUnmanaged`, `ApplyRoute`, `ApplySysctl`, `SetBridgePortFlags`, `UpdateHostsFile`, `SeedDnsmasqLease` (all `implemented-and-reachable`); **new ops to author**: `CreateBridge`, `DeleteBridge` (do not exist in v3 baseline; must be added to `broker_wire.rs` and implemented as `RealBrokerRequest` handlers in `packages/d2b-priv-broker/src/runtime.rs`); **NOT current ops**: `CreateMacvtap` does not exist — macvtap is created inside broker's `SpawnRunner` dispatch (`packages/d2b-priv-broker/src/runtime.rs` line 5097 `live_create_macvtap_fd`)", + "currentSource" : "`nixos-modules/network.nix` (tap/sysctl sections); `packages/d2b-host/src/{bridge_port,nftables,netlink,routes}.rs`; broker ops in `packages/d2b-contracts/src/broker_wire.rs`: **real runtime ops** `ApplyNftables`, `ApplyNmUnmanaged`, `ApplyRoute`, `ApplySysctl`, `CreatePersistentTap`, `SetBridgePortFlags`, `UpdateHostsFile`, `SeedDnsmasqLease` (all `implemented-and-reachable`); **new ops to author**: canonical `DeletePersistentTap` paired with `CreatePersistentTap`, plus `CreateBridge` and `DeleteBridge` (do not exist in v3 baseline; must be added to `broker_wire.rs` and implemented as `RealBrokerRequest` handlers in `packages/d2b-priv-broker/src/runtime.rs`); **NOT current ops**: no unsuffixed tap-deletion alias is valid, and `CreateMacvtap` does not exist — macvtap is created inside broker's `SpawnRunner` dispatch (`packages/d2b-priv-broker/src/runtime.rs` line 5097 `live_create_macvtap_fd`)", "dataMigration" : "None after full reset", "dependencyOwner" : "ADR046-network-001–004; network-local controller owner; D-NETWORK-001, D-NETWORK-002, and D-NETWORK-003 resolved", "destination" : "`packages/d2b-provider-network-local/src/controller.rs`: async NetworkReconciler; `packages/d2b-provider-network-local/src/plan.rs`: ReconcilePlan computation; `packages/d2b-provider-network-local/src/observe.rs`: drift-detection observe loop. Full crate layout required (see [Package and crate boundary](#package-and-crate-boundary)): `src/` (controller/plan/observe + colocated unit tests), `tests/` (hermetic conformance and state-machine tests), `integration/` (provider-system reconcile fixtures), `README.md` (Network ResourceType, controller binary, placement, RBAC, security invariants, build/test/integration commands).", - "detailedDesign" : "Implements full async reconcile interface from `ADR-046-resource-reconciliation`. `plan()` computes desired vs. actual bridge-presence, sysctl, host-side nftables, hosts-file, NM-unmanaged, config Volume content, Guest, guest-agent Process, and mDNS-Process states. `reconcile()` dispatches in order: `CreateBridge` for each bridge not present (broker applies IPv6 sysctls atomically; `CreateBridge` failure sets `FabricReady=False/bridge-create-error` and aborts) → `ApplySysctl` (defense-in-depth IPv6) → `ApplyNftables` (host-side `inet d2b` table) → `ApplyNmUnmanaged` → `ApplyRoute` → `UpdateHostsFile` → `SeedDnsmasqLease` for new reservations → **Volume upsert** (two-phase): Phase 1 — create `Volume/net--config` with `kind: ephemeral`, `source.executionRef: Host/`, `source.settings.kind: tmpfs`, `quota: {maxBytes: 4194304, maxInodes: 128, enforcement: hard}` (tmpfs quota charged to Host memory budget), `layout` entries (root directory with `type: directory` and four config files each with `type: file`, `ownerRef: User/net-local-controller`, `groupRef: User/net-local-controller`, `mode: \"0640\"`, `accessAcl: []`, `defaultAcl: []`, `noFollow: true`, conservative create/repair/cleanup policies), `views: {guest-readonly: {path: \"\", rights: [read, traverse]}}`, `attachments: []` (no Guest attachment); abort on terminal error with `ConfigVolumeReady=False/config-volume-error`. Wait for Volume backing to reach `Ready`; requeue on `Degraded`/`Failed` with `ConfigVolumeReady=False/volume-not-ready`. Write rendered config content through Volume write service (no raw host paths). Phase 2 — create Guest upsert with `systemArtifactId` from REQUIRED `Network.spec.netVmSystemArtifactId`. Wait for Guest `Ready`. Then update Volume with Guest attachment: `attachments: [{executionRef: Guest/, transport: virtiofs, view: guest-readonly, access: read-only, mountPath: \"/run/d2b/net-config\", settings: {posixAcl: false, xattr: false, cache: auto, inodeFileHandles: never, threadPoolSize: null, socketGroup: null}}]`; wait for attachment `Ready`; requeue on `Degraded` with `ConfigVolumeReady=False/attachment-not-ready`. Create or update guest-agent Process `Process/net--agent` with `processClass: worker`, `sandbox: {namespaceClasses: [], capabilityClasses: [network-admin, network-bind, network-raw]}` (inherits Guest network namespace; `network-admin`→`CAP_NET_ADMIN`, `network-bind`→`CAP_NET_BIND_SERVICE`, `network-raw`→`CAP_NET_RAW`, all effective in Guest network namespace only; INV-NET-009), `mounts: [{volumeRef: Volume/net--config, view: guest-readonly, mountPath: \"/run/d2b/net-config\", access: read-only, required: true}]`. mDNS Process upsert when `spec.mdns.enable = true` (D-NETWORK-001) → `SetBridgePortFlags` per tap. Each broker op returns typed audit evidence. `observe()` re-reads `firewallDigest` (host-side), bridge isolation flags, IPv6 sysctls, and guest-agent Process status (`dnsmasq-bound`, `firewall-applied` predicates); queues reconcile on drift. Metrics use only the fixed semantic labels in §OTEL spans and metrics; Zone/Network identity remains in OTEL resource attributes and permitted audit fields and never enters metric labels or span attributes. **Finalizer** (strictly child-first): `NetworkDraining` → delete guest-agent Process and mDNS Processes; wait for each Deleted watch event → update Volume to remove Guest attachment (`attachments: []`); wait for attachment removal confirmed → delete `Guest/`; wait for Deleted watch event → delete `Volume/net--config`; wait for Deleted watch event → `ApplyNftables` (empty) → `ApplyNmUnmanaged` (empty) → `UpdateHostsFile` (empty) → `DeleteBridge` for each bridge (idempotent) → clear finalizer. No USBIP rules installed by Network; `UsbipBindFirewallRule` issued by device-usbip directly (D-NETWORK-002).", + "detailedDesign" : "Implements full async reconcile interface from `ADR-046-resource-reconciliation`. `plan()` computes desired vs. actual bridge-presence, sysctl, host-side nftables, hosts-file, NM-unmanaged, config Volume content, Guest, guest-agent Process, and mDNS-Process states. `reconcile()` dispatches in order: `CreateBridge` for each bridge not present (broker applies IPv6 sysctls atomically; `CreateBridge` failure sets `FabricReady=False/bridge-create-error` and aborts) → `ApplySysctl` (defense-in-depth IPv6) → `ApplyNftables` (host-side `inet d2b` table) → `ApplyNmUnmanaged` → `ApplyRoute` → `UpdateHostsFile` → `SeedDnsmasqLease` for new reservations → **Volume upsert** (two-phase): Phase 1 — create `Volume/net--config` with `kind: ephemeral`, `source.executionRef: Host/`, `source.settings.kind: tmpfs`, `quota: {maxBytes: 4194304, maxInodes: 128, enforcement: hard}` (tmpfs quota charged to Host memory budget), `layout` entries (root directory with `type: directory` and four config files each with `type: file`, `ownerRef: User/net-local-controller`, `groupRef: User/net-local-controller`, `mode: \"0640\"`, `accessAcl: []`, `defaultAcl: []`, `noFollow: true`, conservative create/repair/cleanup policies), `views: {guest-readonly: {path: \"\", rights: [read, traverse]}}`, `attachments: []` (no Guest attachment); abort on terminal error with `ConfigVolumeReady=False/config-volume-error`. Wait for Volume backing to reach `Ready`; requeue on `Degraded`/`Failed` with `ConfigVolumeReady=False/volume-not-ready`. Write rendered config content through Volume write service (no raw host paths). Phase 2 — create Guest upsert with `systemArtifactId` from REQUIRED `Network.spec.netVmSystemArtifactId`. Wait for Guest `Ready`. Then update Volume with Guest attachment: `attachments: [{executionRef: Guest/, transport: virtiofs, view: guest-readonly, access: read-only, mountPath: \"/run/d2b/net-config\", settings: {posixAcl: false, xattr: false, cache: auto, inodeFileHandles: never, threadPoolSize: null, socketGroup: null}}]`; wait for attachment `Ready`; requeue on `Degraded` with `ConfigVolumeReady=False/attachment-not-ready`. Create or update guest-agent Process `Process/net--agent` with `processClass: worker`, `sandbox: {namespaceClasses: [], capabilityClasses: [network-admin, network-bind, network-raw]}` (inherits Guest network namespace; `network-admin`→`CAP_NET_ADMIN`, `network-bind`→`CAP_NET_BIND_SERVICE`, `network-raw`→`CAP_NET_RAW`, all effective in Guest network namespace only; INV-NET-009), `mounts: [{volumeRef: Volume/net--config, view: guest-readonly, mountPath: \"/run/d2b/net-config\", access: read-only, required: true}]`. mDNS Process upsert when `spec.mdns.enable = true` (D-NETWORK-001) → `SetBridgePortFlags` per tap. Removed attachments first wait for Guest/VMM FD ownership to close, then issue `DeletePersistentTap` with the retained opaque attachment ID and current expected Network/attachment generations; the handle remains retained until confirmed effect or validated absence. Stale generation refreshes/requeues, transient kernel error retries, and foreign marker fails closed. Each broker op returns typed audit evidence. `observe()` re-reads `firewallDigest` (host-side), bridge isolation flags, IPv6 sysctls, and guest-agent Process status (`dnsmasq-bound`, `firewall-applied` predicates); queues reconcile on drift. Metrics use only the fixed semantic labels in §OTEL spans and metrics; Zone/Network identity remains in OTEL resource attributes and permitted audit fields and never enters metric labels or span attributes. **Finalizer** (strictly child-first): `NetworkDraining` → stop workload Guests and await VMM FD closure → generation-fenced `DeletePersistentTap` for each retained attachment, awaiting confirmation → delete guest-agent Process and mDNS Processes; wait for each Deleted watch event → update Volume to remove Guest attachment (`attachments: []`); wait for attachment removal confirmed → delete `Guest/`; wait for Deleted watch event → delete `Volume/net--config`; wait for Deleted watch event → `ApplyNftables` (empty) → `ApplyNmUnmanaged` (empty) → `UpdateHostsFile` (empty) → `DeleteBridge` for each bridge (idempotent) → clear finalizer. No USBIP rules installed by Network; device-usbip issues the existing `UsbipBindFirewallRule` request with action `Ensure` for apply or `Remove` for release (D-NETWORK-002).", "integration" : "Controller process registers descriptor, watches `Network` resources via d2b-bus/ComponentSession/ResourceClient. Owned Guest and Process mutations trigger owner reconciliation. Device-usbip watches only Network identity/readiness/generation; its Core adapter privately resolves relay/firewall effects (D-NETWORK-002).", "removalProof" : "Daemon-orchestrated network/bridge lifecycle removed only after controller passes conformance and parity tests", "reuseAction" : "adapt", "reuseSource" : null, "specId" : "ADR-046-resources-network", "specPath" : "docs/specs/ADR-046-resources-network.md", - "validation" : "`ADR046-reconcile-001` toolkit conformance; latency gates (p95 ≤5 ms hint-to-handler); Network-specific: CIDR conflict blocks reconcile, `CreateBridge` failure sets `FabricReady=False`, Volume creation failure sets `ConfigVolumeReady=False/config-volume-error`, `User/net-local-controller` not Ready aborts with `ConfigVolumeReady=False/user-not-ready`, Volume schema round-trip (kind=ephemeral, source.settings.kind=tmpfs, quota.enforcement=hard, layout type=file entries, views.guest-readonly.rights=[read,traverse]), tmpfs quota charged to Host memory budget (test Volume creation fails when Host memory budget exceeded), Guest not created before Volume backing `Ready`, Guest attachment not added before Guest `Ready`, guest-agent Process created after attachment `Ready` (`processClass: worker`, `namespaceClasses: []`, `capabilityClasses: [network-admin, network-bind, network-raw]`, `access: read-only`, `required: true`), host-capability leakage test: no host-netns process gains `CAP_NET_ADMIN`/`CAP_NET_BIND_SERVICE`/`CAP_NET_RAW` as result of guest-agent launch (INV-NET-009; `tests/host-integration/guest-agent-cap-confinement.nix`), `DeleteBridge` called on finalizer, Volume attachment removed before Guest deletion in finalizer (test order: agent Deleted → attachment removed → Guest Deleted → Volume Deleted → bridges), east-west invariant (INV-NET-003), hostBlocklist enforcement (INV-NET-004), macvtap attachment status (delegated to runtime-ch), mDNS Process created/deleted with `spec.mdns.enable` toggle, broker INV-NET-002 tests, config-only spec change updates Volume content and triggers agent reload without Guest restart (INV-NET-008); golden tests updated for v3 IfNames; structural metric descriptor test asserts exact absence of `vm`, `zone`, `zone_id`, `zone_uid`, `network`, and every resource-name-derived label and verifies a Network-name canary is absent from emitted label values", + "validation" : "`ADR046-reconcile-001` toolkit conformance; latency gates (p95 ≤5 ms hint-to-handler); Network-specific: CIDR conflict blocks reconcile, `CreateBridge` failure sets `FabricReady=False`, Volume creation failure sets `ConfigVolumeReady=False/config-volume-error`, `User/net-local-controller` not Ready aborts with `ConfigVolumeReady=False/user-not-ready`, Volume schema round-trip (kind=ephemeral, source.settings.kind=tmpfs, quota.enforcement=hard, layout type=file entries, views.guest-readonly.rights=[read,traverse]), tmpfs quota charged to Host memory budget (test Volume creation fails when Host memory budget exceeded), Guest not created before Volume backing `Ready`, Guest attachment not added before Guest `Ready`, guest-agent Process created after attachment `Ready` (`processClass: worker`, `namespaceClasses: []`, `capabilityClasses: [network-admin, network-bind, network-raw]`, `access: read-only`, `required: true`), host-capability leakage test: no host-netns process gains `CAP_NET_ADMIN`/`CAP_NET_BIND_SERVICE`/`CAP_NET_RAW` as result of guest-agent launch (INV-NET-009; `tests/host-integration/guest-agent-cap-confinement.nix`), removed attachment and finalizer call `DeletePersistentTap` only after Guest/VMM FD closure with opaque ID/current generations, validated absence succeeds, transient failure retains handle/retries, stale generation refreshes, foreign marker blocks without deletion, request/audit contain no IfName/path, `DeleteBridge` called only after tap confirmations, Volume attachment removed before net-VM Guest deletion in finalizer (test order: workload FD closure → persistent taps deleted → agent Deleted → Volume attachment removed → net-VM Guest Deleted → Volume Deleted → bridges), east-west invariant (INV-NET-003), hostBlocklist enforcement (INV-NET-004), macvtap attachment status (delegated to runtime-ch), mDNS Process created/deleted with `spec.mdns.enable` toggle, broker INV-NET-002 tests, config-only spec change updates Volume content and triggers agent reload without Guest restart (INV-NET-008); golden tests updated for v3 IfNames; structural metric descriptor test asserts exact absence of `vm`, `zone`, `zone_id`, `zone_uid`, `network`, and every resource-name-derived label and verifies a Network-name canary is absent from emitted label values", "workItemId" : "ADR046-network-005" }, { "currentSource" : "`tests/unit/nix/cases/net-vm-network.nix`; `tests/golden/pinned/net-vm-bundle-gate.txt`; `tests/golden/pinned/net-canaries.txt`; `tests/golden/pinned/host-prepare-network.txt`; `tests/host-integration/bridge-isolation.nix`; `tests/integration/live/network-isolation.sh`", "dataMigration" : "None — full d2b 3.0 reset; no prior state to migrate", "dependencyOwner" : "ADR046-network-001, ADR046-network-005; test owner", - "destination" : "`tests/unit/nix/cases/net-vm-network.nix` (adapted to v3 resource API); updated golden pins; `tests/host-integration/bridge-isolation.nix` (adapted); `packages/d2b-priv-broker/tests/bridge_lifecycle.rs` (new hermetic broker tests). Provider crate test directories: `packages/d2b-provider-network-local/tests/` — hermetic Cargo integration tests (conformance suite, controller state machine, CIDR validation vectors, IfName determinism, invariant tests INV-NET-001–007, reconcile/observe/finalize with deterministic clock, fault injection); `packages/d2b-provider-network-local/integration/` — container/Host/Guest lifecycle fixtures invoked by `make test-integration` (bridge isolation, east-west double opt-in, nftables drift detection, macvtap lifecycle). Both directories required by package policy.", - "detailedDesign" : "Rust integration tests: NetworkSpec CIDR validation golden vectors; AttachmentSpec index uniqueness; ExternalAttachmentSpec mutual-exclusion validation; IfName derivation determinism; CIDR overlap arithmetic; INV-NET-001 through INV-NET-009 invariant tests; reconcile/observe/finalize state machine (deterministic clock). Broker tests: `create_bridge_applies_ipv6_sysctl` (INV-NET-002 layer 1); `delete_bridge_is_idempotent`; `create_bridge_parameters_match_spec` (MTU, STP disabled, multicast snooping disabled). Controller tests: `reconcile_applies_sysctl_defense_in_depth` (INV-NET-002 layer 2); `volume_created_before_guest`; `guest_not_created_until_volume_ready`; `agent_process_created_after_guest`; `finalizer_order_agent_then_guest_then_volume_then_bridges`; `config_only_spec_change_updates_volume_no_guest_restart` (INV-NET-008); `finalizer_calls_delete_bridge`; `mdns_process_created_on_enable`; `mdns_process_deleted_on_disable`; `host_capability_leakage` (INV-NET-009). nix-unit: INV-NET-001 lib.mkForce assertion; net-VM artifact has no inline mDNS service and no per-Network dnsmasq/nftables data (INV-NET-008); Network emitter CIDR constraint assertions; no `systemd.network.netdevs` bridge entries emitted. Host integration: bridge isolation with east-west opt-in; nftables drift detection; macvtap create/delete lifecycle; config Volume update propagates to guest-agent without Guest restart; `tests/host-integration/guest-agent-cap-confinement.nix` (INV-NET-009 zero leakage to host netns).", + "destination" : "`tests/unit/nix/cases/net-vm-network.nix` (adapted to v3 resource API); updated golden pins; `tests/host-integration/bridge-isolation.nix` (adapted); `packages/d2b-priv-broker/tests/{bridge_lifecycle,persistent_tap_lifecycle}.rs` (new hermetic broker tests). Provider crate test directories: `packages/d2b-provider-network-local/tests/` — hermetic Cargo integration tests (conformance suite, controller state machine, CIDR validation vectors, IfName determinism, invariant tests INV-NET-001–007, reconcile/observe/finalize with deterministic clock, fault injection); `packages/d2b-provider-network-local/integration/` — container/Host/Guest lifecycle fixtures invoked by `make test-integration` (bridge isolation, east-west double opt-in, nftables drift detection, persistent-tap and macvtap lifecycle). Both directories required by package policy.", + "detailedDesign" : "Rust integration tests: NetworkSpec CIDR validation golden vectors; AttachmentSpec index uniqueness; ExternalAttachmentSpec mutual-exclusion validation; IfName derivation determinism; CIDR overlap arithmetic; INV-NET-001 through INV-NET-009 invariant tests; reconcile/observe/finalize state machine (deterministic clock). Broker tests: `create_bridge_applies_ipv6_sysctl` (INV-NET-002 layer 1); `delete_bridge_is_idempotent`; `delete_bridge_never_cascades_attached_tap`; `create_bridge_parameters_match_spec` (MTU, STP disabled, multicast snooping disabled); `delete_persistent_tap_pairs_with_create`; `delete_persistent_tap_absent_is_idempotent_after_ownership_validation`; `delete_persistent_tap_rejects_stale_network_generation`; `delete_persistent_tap_rejects_stale_attachment_generation`; `delete_persistent_tap_foreign_marker_fails_closed`; `delete_persistent_tap_request_and_audit_have_no_ifname_or_path`. Controller tests: `reconcile_applies_sysctl_defense_in_depth` (INV-NET-002 layer 2); `volume_created_before_guest`; `guest_not_created_until_volume_ready`; `agent_process_created_after_guest`; `removed_attachment_waits_for_vmm_then_delete_persistent_tap`; `finalizer_order_vmm_then_taps_then_agent_then_guest_then_volume_then_bridges`; `delete_persistent_tap_transient_retry_retains_handle`; `delete_persistent_tap_generation_mismatch_refreshes`; `delete_persistent_tap_foreign_marker_blocks_finalizer`; `config_only_spec_change_updates_volume_no_guest_restart` (INV-NET-008); `finalizer_calls_delete_bridge`; `mdns_process_created_on_enable`; `mdns_process_deleted_on_disable`; `host_capability_leakage` (INV-NET-009). nix-unit: INV-NET-001 lib.mkForce assertion; net-VM artifact has no inline mDNS service and no per-Network dnsmasq/nftables data (INV-NET-008); Network emitter CIDR constraint assertions; no `systemd.network.netdevs` bridge entries emitted. Host integration: bridge isolation with east-west opt-in; nftables drift detection; persistent-tap and macvtap create/delete lifecycle; config Volume update propagates to guest-agent without Guest restart; `tests/host-integration/guest-agent-cap-confinement.nix` (INV-NET-009 zero leakage to host netns).", "integration" : "Pinned tests registered in `tests/golden/pinned/`; nix-unit cases in `tests/unit/nix/cases/`; host integration in `tests/host-integration/`", "removalProof" : "Not applicable (this work item IS the test successor)", "reuseAction" : "adapt", @@ -2901,15 +2901,15 @@ "currentSource" : "`nixos-modules/network.nix` lines 444–461 (USBIP host firewall); `packages/d2b-core/src/host.rs` lines 324–328 (usbip_backend_port, usbip_busid_locks in NetEnv); `packages/d2b-host/src/` usbip_argv.rs", "dataMigration" : "Current network.nix USBIP carve-out replaced by UsbipBindFirewallRule broker op", "dependencyOwner" : "ADR046-network-005; device-usbip Provider dossier; D-NETWORK-002 resolved", - "destination" : "`Provider/device-usbip` owns one relay Process/Endpoint authority per Network plus the typed EffectPort adapter for `UsbipBindFirewallRule`. The controller watches only the `networkRef` resource's identity/readiness/generation; Core privately resolves Network UID to relay attachment and firewall intent. Network spec/status is not mutated with USBIP fields. Full crate layout required for `packages/d2b-provider-device-usbip/` (see [Package and crate boundary](#package-and-crate-boundary)): `src/` (controller and usbip runner + unit tests), `tests/` (hermetic conformance, dependency-watch state machine, UsbipBindFirewallRule round-trip), `integration/` (Host/Guest USBIP attach/detach lifecycle fixtures), `README.md` (Provider identity, provider-neutral USB Service/Binding types, USBIP Processes/Endpoints, Network least-privilege dependency contract, RBAC, security invariants, standalone-repo path).", - "detailedDesign" : "Device-usbip's typed EffectPort is the sole semantic owner of every USBIP TCP/3240 rule. Its Core adapter resolves the opaque per-Network/per-busid intent and issues `UsbipBindFirewallRule`; its strict provider status owns firewall digest/drift. Network-local emits no generic host or net-VM TCP/3240 allow and ignores device-usbip ownership markers in Network drift. The device Provider owns exactly one multiplexed relay Endpoint authority per Network and supplies Binding proxies only authorized connected streams through LaunchTickets. Primary reuse disposition: `adapt`. Preserved source-plan detail: extract and adapt.", - "integration" : "device-usbip watches Network readiness → Core adapter resolves opaque Network attachment → `UsbipBindFirewallRule` + one relay Endpoint authority → Binding proxy LaunchTicket", + "destination" : "`Provider/device-usbip` owns one relay Process/Endpoint authority per Network plus the typed EffectPort adapter for the existing closed `UsbipBindFirewallRule` request with closed action enum `Ensure|Remove`. The controller watches only the `networkRef` resource's identity/readiness/generation; Core privately resolves Network UID to relay attachment and firewall intent. Network spec/status is not mutated with USBIP fields. Full crate layout required for `packages/d2b-provider-device-usbip/` (see [Package and crate boundary](#package-and-crate-boundary)): `src/` (controller and usbip runner + unit tests), `tests/` (hermetic conformance, dependency-watch state machine, `UsbipBindFirewallRule` `Ensure|Remove` round-trip), `integration/` (Host/Guest USBIP attach/detach lifecycle fixtures), `README.md` (Provider identity, provider-neutral USB Service/Binding types, USBIP Processes/Endpoints, Network least-privilege dependency contract, RBAC, security invariants, standalone-repo path).", + "detailedDesign" : "Device-usbip's typed EffectPort is the sole semantic owner of every USBIP TCP/3240 rule. Its Core adapter resolves the opaque per-Network/per-busid intent and issues the same `UsbipBindFirewallRule` request with action `Ensure` for apply and `Remove` for release; no separate release op exists. `Remove` is generation-bound, ownership-scoped, idempotent after validated absence, and foreign-marker fail-closed. The controller retains firewall token/status and the relay authority reference until the broker confirms `Remove`; its strict provider status owns firewall digest/drift. Network-local emits no generic host or net-VM TCP/3240 allow and ignores device-usbip ownership markers in Network drift. The device Provider owns exactly one multiplexed relay Endpoint authority per Network and supplies Binding proxies only authorized connected streams through LaunchTickets. Primary reuse disposition: `adapt`. Preserved source-plan detail: extract and adapt.", + "integration" : "device-usbip watches Network readiness → Core adapter resolves opaque Network attachment → `UsbipBindFirewallRule { action: Ensure, ... }` for apply or `{ action: Remove, ... }` for release + one relay Endpoint authority → Binding proxy LaunchTicket; release clears status/authority only after confirmed `Remove`", "removalProof" : "Network.nix USBIP sections removed only after UsbipBindFirewallRule mechanism passes conformance", "reuseAction" : "adapt", "reuseSource" : null, "specId" : "ADR-046-resources-network", "specPath" : "docs/specs/ADR-046-resources-network.md", - "validation" : "device-usbip conformance tests cover exact per-Network/per-busid scoping, one relay Endpoint authority, ownership-scoped drift/status, foreign-marker rejection, and release; network-local nftables tests assert no TCP/3240/USBIP rule on host or net VM and prove USBIP rule churn does not change Network `FirewallReady`; the pinned USBIP firewall golden moves to device-usbip ownership", + "validation" : "device-usbip conformance tests cover the exact closed `Ensure|Remove` enum (unknown actions rejected), same-request broker mapping for apply/release, expected Network/Service generation binding, exact per-Network/per-busid scoping, idempotent validated-absence `Remove`, one relay Endpoint authority, ownership-scoped drift/status, foreign-marker rejection, transient retry, and retention of status/token/authority until effect confirmation; network-local nftables tests assert no TCP/3240/USBIP rule on host or net VM and prove USBIP rule churn does not change Network `FirewallReady`; the pinned USBIP firewall golden moves to device-usbip ownership", "workItemId" : "ADR046-network-007" }, { @@ -3472,7 +3472,7 @@ "dataMigration" : "Full d2b 3.0 reset; no v2 state/config import.", "dependencyOwner" : "Core; owns `NetworkEffectPort` contract/versioning in `d2b-contracts` and adapter implementation in `d2b-core`.", "destination" : "`d2b-contracts` trait plus `d2b-core` core adapter; maps to broker wire operations and audit emission.", - "detailedDesign" : "Implement `NetworkEffectPort` core adapter in `d2b-core`; map to broker wire ops; emit audit records. Versioning: minor releases may add methods with default impls; major releases require Provider upgrade. The trait lives in `d2b-contracts`; the adapter in `d2b-core`.", + "detailedDesign" : "Implement `NetworkEffectPort` core adapter in `d2b-core`; map to broker wire ops; emit audit records. `revoke_attachment_tap` accepts only an opaque `AttachmentHandle` plus `AttachmentGenerationFence { expected_network_generation, expected_attachment_generation }` and maps to `DeletePersistentTap`; no IfName/path or caller-authored marker crosses the trait. Versioning: minor releases may add methods with default impls; major releases require Provider upgrade. The trait lives in `d2b-contracts`; the adapter in `d2b-core`.", "integration" : "`Provider/network-local` reconcile calls injected `NetworkEffectPort`; the core adapter resolves opaque Network intents to closed broker wire ops and emits broker-level audit records.", "removalProof" : "None — net-new; no prior owner to remove.", "reuseAction" : "create", @@ -3483,18 +3483,18 @@ "workItemId" : "ADR046-nl-001" }, { - "currentSource" : "Existing broker wire has related ApplyNftables, ApplyRoute, ApplySysctl, ApplyNmUnmanaged, UpdateHostsFile, and SeedDnsmasqLease operations, but no `CreateBridge`, `DeleteBridge`, `ReadNftablesDigest`, `ReadSysctlState`, or `ReadBridgePortFlags` v3 ops.", + "currentSource" : "Existing broker wire has related ApplyNftables, ApplyRoute, ApplySysctl, ApplyNmUnmanaged, UpdateHostsFile, SeedDnsmasqLease, and `CreatePersistentTap` operations, but no paired `DeletePersistentTap`, `CreateBridge`, `DeleteBridge`, `ReadNftablesDigest`, `ReadSysctlState`, or `ReadBridgePortFlags` v3 ops.", "dataMigration" : "Full d2b 3.0 reset; no v2 state/config import.", "dependencyOwner" : "Core; broker/core contract work consumed by ADR046-nl-001.", - "destination" : "Broker wire contract and broker/core adapter operation table for `CreateBridge`, `DeleteBridge`, `ReadNftablesDigest`, `ReadSysctlState`, and `ReadBridgePortFlags`.", - "detailedDesign" : "Add `CreateBridge`, `DeleteBridge`, `ReadNftablesDigest`, `ReadSysctlState`, `ReadBridgePortFlags` broker ops. Primary reuse disposition: `adapt`. Preserved source-plan detail: extend broker wire with net-new operations and reuse existing closed broker-operation dispatch shape.", - "integration" : "`NetworkEffectPort` core adapter invokes these broker ops for bridge lifecycle and observe/drift checks; `Provider/network-local` receives only typed results and opaque digests/handles.", + "destination" : "Broker wire contract and broker/core adapter operation table for `DeletePersistentTap`, `CreateBridge`, `DeleteBridge`, `ReadNftablesDigest`, `ReadSysctlState`, and `ReadBridgePortFlags`.", + "detailedDesign" : "Add canonical closed `DeletePersistentTap` paired with `CreatePersistentTap`, plus `CreateBridge`, `DeleteBridge`, `ReadNftablesDigest`, `ReadSysctlState`, and `ReadBridgePortFlags`. `DeletePersistentTapRequest` contains only an opaque attachment ID and expected Network/attachment generations. The broker resolves trusted realization state, validates generations and ownership marker, treats validated absence as success, rejects foreign markers without deletion, and emits path-free post-effect audit. No request accepts an IfName or path. Primary reuse disposition: `adapt`. Preserved source-plan detail: extend broker wire with net-new operations and reuse existing closed broker-operation dispatch shape.", + "integration" : "`NetworkEffectPort` core adapter invokes these broker ops for attachment/fabric lifecycle and observe/drift checks; `Provider/network-local` receives only typed results and opaque digests/handles. Attachment removal and Network finalization retain the handle until `DeletePersistentTap` confirms deletion or validated absence.", "removalProof" : "None — net-new broker ops; remove only if no Provider consumes them per the removal checklist.", "reuseAction" : "adapt", "reuseSource" : null, "specId" : "ADR-046-provider-network-local", "specPath" : "docs/specs/providers/ADR-046-provider-network-local.md", - "validation" : "`integration/host_fabric.rs` covers bridge create/delete, nftables apply/digest, IPv6 suppression, NetworkManager unmanaged handling, and real `NetworkEffectPort` implementation.", + "validation" : "Broker tests cover `DeletePersistentTap` success, validated already-absent idempotency, stale Network/attachment generations, foreign-marker fail-closed behavior, path-free audit, and rejection of any IfName/path field; `integration/host_fabric.rs` covers persistent-tap deletion, bridge create/delete, nftables apply/digest, IPv6 suppression, NetworkManager unmanaged handling, and real `NetworkEffectPort` implementation.", "workItemId" : "ADR046-nl-002" }, { @@ -3502,14 +3502,14 @@ "dataMigration" : "Full d2b 3.0 reset; no v2 state/config import.", "dependencyOwner" : "Core; handle DTOs are owned by `d2b-contracts` and consumed by `d2b-core` plus `Provider/network-local`.", "destination" : "`d2b-contracts` opaque byte-array newtypes; core-held HMAC key and provider-facing redacted handle types.", - "detailedDesign" : "Implement `AttachmentHandle` and `FabricHandle` as opaque byte-array newtypes (32 bytes of HMAC-SHA-256 over internal identity material; key held by core). Each handle is single-use; revocation is implicit when the owning Network is deleted. These types are declared in `d2b-contracts`, not in the provider crate.", + "detailedDesign" : "Implement `AttachmentHandle` and `FabricHandle` as opaque byte-array newtypes (32 bytes of HMAC-SHA-256 over internal identity material; key held by core). Each attachment handle identifies one generation-fenced realization and is retained until explicit `DeletePersistentTap` confirmation during attachment removal or Network finalization. These types are declared in `d2b-contracts`, not in the provider crate.", "integration" : "Core creates handles from Network and attachment identity, stores them only in internal state/status-resource attachment realization, and supplies resolved tap FDs through LaunchTicket without exposing IfNames or MACs.", "removalProof" : "None — net-new; no prior owner to remove.", "reuseAction" : "create", "reuseSource" : null, "specId" : "ADR-046-provider-network-local", "specPath" : "docs/specs/providers/ADR-046-provider-network-local.md", - "validation" : "`tests/fault_injection.rs` and `tests/controller_state.rs` cover opaque-handle mismatch, revocation-on-delete, and no raw IfName/IP/MAC public surface.", + "validation" : "`tests/fault_injection.rs` and `tests/controller_state.rs` cover opaque-handle mismatch, generation-fenced `DeletePersistentTap`, retained handle until confirmation, and no raw IfName/IP/MAC/path public surface.", "workItemId" : "ADR046-nl-003" }, { @@ -3547,7 +3547,7 @@ "dataMigration" : "Full d2b 3.0 reset; no v2 state/config import.", "dependencyOwner" : "Provider; depends on ADR046-nl-001 through ADR046-nl-005 and owns the Network reconcile/observe/finalize handlers.", "destination" : "`packages/d2b-provider-network-local/src/{controller.rs,metrics.rs}`.", - "detailedDesign" : "Implement `controller.rs` reconcile/observe/finalize handlers with `NetworkEffectPort` injection and the §21 metric descriptors with closed semantic labels. No descriptor may carry `vm`, `zone`, `zone_id`, `zone_uid`, `network`, or another resource-name-derived key; Network/Zone identity stays only in OTEL resource attributes and permitted audit fields. Primary reuse disposition: `adapt`. Preserved source-plan detail: port semantics into provider reconcile state machine; do not reuse static per-env systemd/Nix ownership.", + "detailedDesign" : "Implement `controller.rs` reconcile/observe/finalize handlers with `NetworkEffectPort` injection and the §21 metric descriptors with closed semantic labels. Attachment removal and finalization wait for Guest/VMM FD ownership to close, then call `revoke_attachment_tap` with the retained opaque handle and current Network/attachment generation fence; transient deletion retries retain the handle, stale generations refresh/requeue, and ownership conflict blocks finalizer clearing. No descriptor may carry `vm`, `zone`, `zone_id`, `zone_uid`, `network`, or another resource-name-derived key; Network/Zone identity stays only in OTEL resource attributes and permitted audit fields. Primary reuse disposition: `adapt`. Preserved source-plan detail: port semantics into provider reconcile state machine; do not reuse static per-env systemd/Nix ownership.", "integration" : "Controller watches Network, Guest, Volume, Process, User, Host, and Zone resources; creates child resources, writes status, invokes `NetworkEffectPort`, and drives finalizers.", "removalProof" : "Supersedes static per-env lifecycle in `nixos-modules/network.nix` and `nixos-modules/net.nix`; removal proof is successor controller coverage plus deletion of duplicate old gates when this provider lands.", "reuseAction" : "adapt", @@ -3667,14 +3667,14 @@ "dataMigration" : "None — docs/tooling only; no runtime state.", "dependencyOwner" : "Tests; depends on ADR046-nl-006 controller and fake `NetworkEffectPort` from `d2b-contracts`.", "destination" : "`packages/d2b-provider-network-local/tests/controller_state.rs`.", - "detailedDesign" : "Controller state-machine unit tests with fake `NetworkEffectPort` (from d2b-contracts mock) and deterministic clock.", + "detailedDesign" : "Controller state-machine unit tests with fake `NetworkEffectPort` (from d2b-contracts mock) and deterministic clock, including attachment removal/finalizer calls to generation-fenced `DeletePersistentTap`.", "integration" : "Hermetic fake effect port drives reconcile, observe, finalizer, and adoption transitions without real broker, systemd, container, or network dependencies.", "removalProof" : "None — net-new; no prior owner to remove.", "reuseAction" : "create", "reuseSource" : null, "specId" : "ADR-046-provider-network-local", "specPath" : "docs/specs/providers/ADR-046-provider-network-local.md", - "validation" : "`tests/controller_state.rs` covers normal path, CIDR conflict, User not Ready, Volume error, Guest timeout, agent reload failure, finalizer sequence, adoption, and drift.", + "validation" : "`tests/controller_state.rs` covers normal path, CIDR conflict, User not Ready, Volume error, Guest timeout, agent reload failure, finalizer sequence, `DeletePersistentTap` validated absence, transient retry with retained handle, stale-generation refresh, foreign-marker block, adoption, and drift.", "workItemId" : "ADR046-nl-014" }, { @@ -3682,7 +3682,7 @@ "dataMigration" : "None — docs/tooling only; no runtime state.", "dependencyOwner" : "Tests; integration coverage for the complete Network lifecycle.", "destination" : "`packages/d2b-provider-network-local/integration/host_fabric.rs`, `guest_lifecycle.rs`, `agent_reload.rs`, and `delete_sequence.rs`.", - "detailedDesign" : "Integration tests: full Network lifecycle (create, config update, agent Reload, delete sequence) in container environment. Primary reuse disposition: `adapt`. Preserved source-plan detail: adapt reusable semantic assertions into v3 integration coverage.", + "detailedDesign" : "Integration tests: full Network lifecycle (create, config update, agent Reload, generation-fenced persistent-tap deletion, delete sequence) in container environment. Primary reuse disposition: `adapt`. Preserved source-plan detail: adapt reusable semantic assertions into v3 integration coverage.", "integration" : "Integration tests exercise resource publication, host fabric effects, config Volume updates, ComponentSession reload, Process lifecycle, and finalizer cleanup through the provider stack.", "removalProof" : "Old duplicate tests, shell gates, fixtures, static artifacts, CI jobs, manifests, and pins are deleted once successor coverage and removal proof pass.", "reuseAction" : "adapt", @@ -3906,15 +3906,15 @@ "currentSource" : "`nixos-modules/components/observability/host.nix` (`otelRuntimeDir`, `hostEgressSocket`, `setfacl` ACL pattern, `scrapeJournal` option, `identityName`); `nixos-modules/components/observability/stack.nix` (`ingressSources`, `vmName`, `receiverGrpcPort`, loopback binding, `signoz.listenPort`)", "dataMigration" : "Existing SigNoz data not migrated; v3 starts fresh per Zone", "dependencyOwner" : "ADR046-otel-001 + ADR046-telem-001 + ADR046-provider-001 (Provider toolkit) + ADR046-provider-004 (common telemetry Service/Binding base) + resource/Endpoint/Volume contracts; W2; observability owner", - "destination" : "`packages/d2b-provider-observability-otel/src/{collector_bin,emitter_socket,exporter,controller,service,binding}.rs`; updated Nix observability modules", - "detailedDesign" : "Register the initial implementation of both provider-neutral qualified ResourceTypes by binding the exact ADR046-provider-004 base versions/fingerprints, then define only strict observability-otel Service/Binding spec and status extensions. Reconcile each Binding into an edge collector, private Endpoints, runtime Volume, and optional forwarder. Collector links the full OTEL SDK, resolves `serviceRef`, stamps trusted producer identity, and enforces common signals/quota/policy plus strict batching extension. Write generic Service/Binding observations to `status.resource` and SigNoz/OTLP/OTEL observations only to `status.provider`; no state file or Provider state Volume. Provider root config remains installation-only. Primary reuse disposition: `adapt`. Preserved source-plan detail: adapt Nix pipeline shape (replace per-VM `vmName` with per-Zone name; replace socat runner with vsock-forwarder long-lived Process; adapt `ingressSources` per-Zone entry).", + "destination" : "`packages/d2b-provider-observability-otel/src/{collector_bin,emitter_socket,ingress_policy,exporter,controller,service,binding}.rs`; updated Nix observability modules", + "detailedDesign" : "Register the initial implementation of both provider-neutral qualified ResourceTypes by binding the exact ADR046-provider-004 base versions/fingerprints, then define only strict observability-otel Service/Binding spec and status extensions. Reconcile each Binding into an edge collector, private Endpoints, runtime Volume, and optional forwarder. Collector links the full OTEL SDK, resolves `serviceRef`, stamps trusted producer identity into allow-listed OTEL Resource attributes, and routes Unix-emitter, OTLP-Unix, OTLP/vsock, and import-stream metrics through one structural `METRIC_LABEL_POLICY` gate before aggregation, queueing, batching, retry, or export. The gate rejects exact/suffix identity keys and trusted `metadata.name`/resource-identity canary values, applies bounded non-echoing error classes and connection quarantine, and never consumes audit. Write generic Service/Binding observations to `status.resource` and SigNoz/OTLP/OTEL observations only to `status.provider`; no state file or Provider state Volume. Provider root config remains installation-only. Primary reuse disposition: `adapt`. Preserved source-plan detail: adapt Nix pipeline shape (replace per-VM `vmName` with per-Zone name; replace socat runner with vsock-forwarder long-lived Process; adapt `ingressSources` per-Zone entry).", "integration" : "`BoundedEmitter` → Binding-private Endpoint → edge collector/OTEL SDK → same-Zone authority or projected Service → SigNoz", "removalProof" : "`guest.nix` per-VM guest collector retired after `integration/scenario_obs_zone_forwarding.rs` passes", "reuseAction" : "adapt", "reuseSource" : null, "specId" : "ADR-046-provider-observability-otel", "specPath" : "docs/specs/providers/ADR-046-provider-observability-otel.md", - "validation" : "Common-fixture/fingerprint and canonical-minimal-base conformance (including a fake alternate telemetry Provider); `tests/emitter_socket_receive.rs`; `tests/exporter_outage.rs`; `tests/exporter_backpressure.rs`; `integration/scenario_full_pipeline.rs`; adapted `policy_observability.rs` (retain all existing assertions; add new `d2b.zone`, `d2b.provider` allowlist entries)", + "validation" : "Common-fixture/fingerprint and canonical-minimal-base conformance (including a fake alternate telemetry Provider); `tests/emitter_socket_receive.rs`; table-driven `tests/ingress_metric_policy.rs` across all four ingress adapters; `tests/exporter_outage.rs`; `tests/exporter_backpressure.rs`; `integration/scenario_full_pipeline.rs`; adapted `policy_observability.rs` (retain all existing assertions; add new `d2b.zone`, `d2b.provider` allowlist entries)", "workItemId" : "ADR046-otel-002" }, { @@ -3936,8 +3936,8 @@ "currentSource" : "`packages/d2b-contract-tests/tests/policy_observability.rs` (`loki_native_otel_resource_attributes` allowlist; `tempo_stack_signoz_backend_and_collector`; `startup_tracing_avoids_host_path_fields`); `packages/d2b-contract-tests/tests/policy_metrics.rs` (`EXPECTED_METRICS` table); `packages/d2b-contract-tests/tests/minijail_relay_otel.rs`", "dataMigration" : "None — full d2b 3.0 reset; no prior state to migrate", "dependencyOwner" : "ADR046-otel-002; policy/contract-tests owner", - "destination" : "`packages/d2b-contract-tests/tests/policy_observability.rs` (updated); `packages/d2b-contract-tests/tests/policy_telemetry_redaction.rs` (new, per ADR046-telem-008); `packages/d2b-provider-observability-otel/tests/no_vm_label_in_metrics.rs`", - "detailedDesign" : "(1) Extend `loki_native_otel_resource_attributes` allowlist with `d2b.zone`, `d2b.provider`, `d2b.component`, `service.version`. (2) Add gate: `no_isolation` must not appear in any Provider `MetricDescriptor` label or span attribute catalog. (3) Adapt `minijail_relay_otel.rs` shape test for Provider-managed runner (no broker `RunnerRole::OtelHostBridge`). (4) Add metric inventory gates for `d2b_otel_*` instruments from this spec. (5) Retain: `startup_tracing_avoids_host_path_fields`; SigNoz-only backend assertion; `tempo_guest_collector_shape`; `config_source = \"realm-controllers\"` absence gate. Primary reuse disposition: `adapt`. Preserved source-plan detail: adapt and extend existing tests; keep existing test assertions.", + "destination" : "`packages/d2b-contract-tests/tests/policy_observability.rs` (updated); `packages/d2b-contract-tests/tests/policy_telemetry_redaction.rs` (new, per ADR046-telem-008); `packages/d2b-provider-observability-otel/tests/{no_vm_label_in_metrics,ingress_metric_policy}.rs`", + "detailedDesign" : "(1) Extend `loki_native_otel_resource_attributes` allowlist with `d2b.zone`, `d2b.provider`, `d2b.component`, `service.version`. (2) Add gate: `no_isolation` must not appear in any Provider `MetricDescriptor` label or span attribute catalog. (3) Adapt `minijail_relay_otel.rs` shape test for Provider-managed runner (no broker `RunnerRole::OtelHostBridge`). (4) Add metric inventory gates for `d2b_otel_*` instruments from this spec. (5) Structurally prove that all four metrics ingress adapters call the shared pre-batch policy gate; run exact forbidden-key, suffix, `metadata.name`, ResourceRef, UID, and resource-identity canaries through each adapter; assert bounded non-echoing outcomes/quarantine/backpressure and valid OTEL Resource identity preservation. (6) Retain: `startup_tracing_avoids_host_path_fields`; SigNoz-only backend assertion; `tempo_guest_collector_shape`; `config_source = \"realm-controllers\"` absence gate. Primary reuse disposition: `adapt`. Preserved source-plan detail: adapt and extend existing tests; keep existing test assertions.", "integration" : "Contract-tests run in workspace `make test-drift` and `make test-lint`", "removalProof" : "Not applicable", "reuseAction" : "adapt", @@ -3986,7 +3986,7 @@ "integration" : "`make test-policy` and `make check` both fail if any provider crate violates §4.8; consistent with existing `no-bash-ast-walker` and workspace-sort gates; ADR046-zone-control-003 references §4.8 for Provider package conventions", "removalProof" : "No existing code removed; additive policy test only", "reuseAction" : "create", - "reuseSource" : "None from main; workspace policy tests are repo-specific", + "reuseSource" : null, "specId" : "ADR-046-resources-zone-control", "specPath" : "docs/specs/ADR-046-resources-zone-control.md", "validation" : "§15.3 layout conformance tests: `provider-crate-layout-src-required`, `provider-crate-layout-tests-required`, `provider-crate-layout-integration-required`, `provider-crate-layout-readme-required`, `provider-readme-sections-all-present`, `provider-readme-sections-partial-missing`, `provider-integration-target-declared`, `provider-integration-target-unique`, `provider-integration-target-valid-values`, `provider-crate-naming-convention`, `provider-crate-layout-non-provider-exempt`", @@ -4552,14 +4552,14 @@ "dataMigration" : "None — telemetry-only work; no runtime state import", "dependencyOwner" : "P2; depends on ADR046-qemu-media-013 and ADR046-qemu-media-014; owner: runtime-qemu-media telemetry integration", "destination" : "packages/d2b-provider-runtime-qemu-media/src/telemetry.rs", - "detailedDesign" : "Metrics and OTEL spans: implement all metrics from §18 and OTEL trace spans with structural closed-label enforcement and no Zone/VM/resource name, user identity, path, or other sensitive value in any metric label; retain Zone/resource identity only in bounded OTEL resource attributes and permitted audit fields. Primary reuse disposition: `create`. Preserved source-plan detail: net-new telemetry emission for the Provider metrics and spans in §18.", + "detailedDesign" : "Metrics and OTEL spans: implement all metrics from §18 and OTEL trace spans with structural closed-label enforcement and no Zone/VM/resource name, user identity, path, or other sensitive value in any metric label. Span attributes use only the exact fixed semantic fields and `outcome` listed in §18; no resource name, UID, shortened UID, digest, ref, or derived identity is admitted. Retain identity only in allow-listed OTEL Resource attributes and permitted bounded audit fields. Primary reuse disposition: `create`. Preserved source-plan detail: net-new telemetry emission for the Provider metrics and spans in §18.", "integration" : "Controller, QMP, hotplug, and dependency-watch paths call telemetry helpers; OTEL/metrics exporters consume only closed, bounded labels for support dashboards.", "removalProof" : "None — telemetry helpers are new for this Provider; no prior owner to remove", "reuseAction" : "create", "reuseSource" : null, "specId" : "ADR-046-provider-runtime-qemu-media", "specPath" : "docs/specs/providers/ADR-046-provider-runtime-qemu-media.md", - "validation" : "`tests/metrics_label_cardinality.rs` asserts exact absence of `vm`, `zone`, `zone_id`, `zone_uid`, and resource-name-derived keys plus Guest/Zone-name canary absence; `tests/otel_span_attributes.rs` preserves allowed OTEL resource identity attributes and rejects identity span attributes", + "validation" : "`tests/metrics_label_cardinality.rs` asserts exact absence of `vm`, `zone`, `zone_id`, `zone_uid`, and resource-name-derived keys plus Guest/Zone-name canary absence; `tests/otel_span_attributes.rs` asserts the exact per-span semantic allowlist, preserves allowed OTEL Resource identity attributes, and rejects Zone/Guest/Process/Provider-resource names, refs, UIDs, shortened UIDs, digests, and identity canary values in span attributes", "workItemId" : "ADR046-qemu-media-016" }, { @@ -4661,7 +4661,7 @@ "integration" : "`d2b host cutover preflight`/`plan` CLI commands consume this crate exclusively; no other crate re-implements inventory walking", "removalProof" : "Not applicable (net-new capability)", "reuseAction" : "adapt", - "reuseSource" : "None from main; this is a v3-only cross-cutting concern with no main-branch equivalent", + "reuseSource" : null, "specId" : "ADR-046-reset-and-cutover", "specPath" : "docs/specs/ADR-046-reset-and-cutover.md", "validation" : "`checkpoint_id` determinism property test; snapshot atomic-write crash-injection test; `cutover_preflight_refuses_dirty_flake_check`", @@ -4676,7 +4676,7 @@ "integration" : "Invoked by `preflight` before the snapshot is written; failures block `plan` from being offered", "removalProof" : "Not applicable", "reuseAction" : "adapt", - "reuseSource" : "None from main", + "reuseSource" : null, "specId" : "ADR-046-reset-and-cutover", "specPath" : "docs/specs/ADR-046-reset-and-cutover.md", "validation" : "`cutover-candidate-bundle-validation.nix`; trust-preflight rejection tests for each of digest/publisher/signature/deny/provenance/conformance failure modes", @@ -4691,7 +4691,7 @@ "integration" : "`d2b host cutover apply` orchestrates drain then disposition execution then hands off to Phase 5 (ADR046-reset-005)", "removalProof" : "Not applicable", "reuseAction" : "adapt", - "reuseSource" : "None from main", + "reuseSource" : null, "specId" : "ADR-046-reset-and-cutover", "specPath" : "docs/specs/ADR-046-reset-and-cutover.md", "validation" : "`cutover_apply_requires_exact_consent_phrase`; `cutover_drain_refuses_on_live_process`", @@ -4706,7 +4706,7 @@ "integration" : "Called by ADR046-reset-003's disposition executor for every Adopt row; writes to the state Volumes ADR046-device-tpm-004/ADR046-vl-004/ADR046-vl-006 define", "removalProof" : "Not applicable (the mechanism is retained permanently for later Full/Provider/Guest reset relocation use, not retired after first use)", "reuseAction" : "adapt", - "reuseSource" : "None from main", + "reuseSource" : null, "specId" : "ADR-046-reset-and-cutover", "specPath" : "docs/specs/ADR-046-reset-and-cutover.md", "validation" : "Crash-injection at every step boundary (Type 10 `cutover-crash-resume.nix`); TPM/durable-Volume Destroy-exclusion property test", @@ -4721,7 +4721,7 @@ "integration" : "Invoked immediately after ADR046-reset-003/004 complete; hands off to Phase 7 (ADR046-reset-006)", "removalProof" : "Not applicable", "reuseAction" : "create", - "reuseSource" : "None from main", + "reuseSource" : null, "specId" : "ADR-046-reset-and-cutover", "specPath" : "docs/specs/ADR-046-reset-and-cutover.md", "validation" : "Provider install topological-order determinism test; cycle-rejection test; store-identity mismatch fail-closed test", @@ -4736,7 +4736,7 @@ "integration" : "Consumes Providers installed by ADR046-reset-005; hands off to ADR046-reset-007 (verification)", "removalProof" : "Not applicable", "reuseAction" : "adapt", - "reuseSource" : "None from main", + "reuseSource" : null, "specId" : "ADR-046-reset-and-cutover", "specPath" : "docs/specs/ADR-046-reset-and-cutover.md", "validation" : "Gateway-custody-boundary test asserting the parent inventory never contains a gateway-guest-internal path; child-local ZoneLink test asserting one uplink, self-matching `childZoneName`, and child-store ownership; compiler test asserting `parentZone` selects the allocator but appears only in sealed bootstrap state; no-reciprocal-parent-row/no-parent-handler test; child-local ZoneLink `Degraded/waiting-on-remote` non-blocking test", @@ -4751,7 +4751,7 @@ "integration" : "`d2b host cutover verify`/`doctor` CLI commands; consumed by the Phase 10 finalize gate table", "removalProof" : "Not applicable", "reuseAction" : "adapt", - "reuseSource" : "None from main", + "reuseSource" : null, "specId" : "ADR-046-reset-and-cutover", "specPath" : "docs/specs/ADR-046-reset-and-cutover.md", "validation" : "Injected-digest-mismatch test for TPM/durable-Volume verify checks; audit-genesis-cross-check test; `cutover-full-rehearsal.nix`", @@ -4766,7 +4766,7 @@ "integration" : "`d2b host cutover finalize` CLI command; reads gate status from ADR046-reset-007's verify results plus each named policy-lint/integration test's pass/fail recorded in CI", "removalProof" : "Each candidate's own row in [Old artifact/unit/schema removal gates](#old-artifactunitschema-removal-gates) states its exact removal proof", "reuseAction" : "create", - "reuseSource" : "None from main", + "reuseSource" : null, "specId" : "ADR-046-reset-and-cutover", "specPath" : "docs/specs/ADR-046-reset-and-cutover.md", "validation" : "`policy_no_destroy_without_gate`; `policy_legacy_cli_verbs_absent_after_gate`; `tpm-adopt-retirement.nix`", @@ -4781,7 +4781,7 @@ "integration" : "`d2b host cutover rollback`/`hold` CLI commands; consulted by ADR046-reset-003's disposition executor and ADR046-reset-008's finalize gate before every mutating step", "removalProof" : "Not applicable", "reuseAction" : "adapt", - "reuseSource" : "None from main", + "reuseSource" : null, "specId" : "ADR-046-reset-and-cutover", "specPath" : "docs/specs/ADR-046-reset-and-cutover.md", "validation" : "`cutover_rollback_window_closes_after_phase_5`; incident-hold-blocks-destructive-step test", @@ -4796,7 +4796,7 @@ "integration" : "Standalone from the cutover Phases 0-10 above; usable at any later time as a recovery/maintenance lever once a Zone exists", "removalProof" : "Not applicable", "reuseAction" : "adapt", - "reuseSource" : "None from main", + "reuseSource" : null, "specId" : "ADR-046-reset-and-cutover", "specPath" : "docs/specs/ADR-046-reset-and-cutover.md", "validation" : "`host_reset_scope_isolation`; `zone-provider-guest-reset-isolation.nix`; durable-Volume-preserved-by-default property test for both Provider and Guest scopes", @@ -4811,7 +4811,7 @@ "integration" : "Run manually by an operator against a real host/device before the reset-and-cutover implementation is declared production-ready", "removalProof" : "Not applicable", "reuseAction" : "create", - "reuseSource" : "None from main", + "reuseSource" : null, "specId" : "ADR-046-reset-and-cutover", "specPath" : "docs/specs/ADR-046-reset-and-cutover.md", "validation" : "Manual pass/fail sign-off recorded per the project's existing live-host/hardware validation conventions", @@ -4987,14 +4987,14 @@ "dataMigration" : "None; ZoneLink resources created from Nix configuration at v3 reset", "dependencyOwner" : "ADR046-routing-001, ADR046-routing-002; ZoneLink resource owner", "destination" : "`packages/d2b-contracts/src/v3/zone_link.rs` (ZoneLink spec/status types, intent types, namespace allocation); `packages/d2b-zone-routing/src/resolver.rs` (ZoneEntrypointResolver)", - "detailedDesign" : "Child-local ZoneLink spec/status fields; self-name and one-uplink invariants; ZoneLinkIntent record stored in the child; ZoneLinkNamespaceAllocation issued by the exact parent allocator selected in sealed Nix-compiled `parentZone` topology; ZoneEntrypointResolver with longest-suffix match over ZonePath (adapted from `RealmEntrypointTable::resolve`); no reciprocal parent-store resource; fail-closed on unknown Zone Primary reuse disposition: `adapt`. Preserved source-plan detail: extract and adapt.", - "integration" : "Core-controller ZoneLink handler manages ZoneLink resources; ZoneEntrypointResolver in d2b-bus for per-call dispatch decision", + "detailedDesign" : "Child-local ZoneLink spec/status fields; self-name and one-uplink invariants; ZoneLinkIntent record stored in the child; ZoneLinkNamespaceAllocation issued by the exact parent allocator selected in sealed Nix-compiled `parentZone` topology; ZoneEntrypointResolver with longest-suffix match over ZonePath (adapted from `RealmEntrypointTable::resolve`) driven only by sealed `{ childZone, parentZone }` rows plus authenticated admitted route projections; no reciprocal parent-store resource or parent ZoneLink handler; fail closed on unknown topology, absent/stale projection, or unauthenticated route Primary reuse disposition: `adapt`. Preserved source-plan detail: extract and adapt.", + "integration" : "Child core-controller ZoneLink handler manages child-store ZoneLink resources; parent d2b-bus feeds sealed topology and authenticated `ZoneRouteEngine` projection state to ZoneEntrypointResolver for per-call dispatch", "removalProof" : "`RealmEntrypointTable` retired after all host-daemon routing paths use ZoneEntrypointResolver", "reuseAction" : "adapt", "reuseSource" : "Same v3 baseline commit `b5ddbed6`", "specId" : "ADR-046-zone-routing", "specPath" : "docs/specs/ADR-046-zone-routing.md", - "validation" : "Longest-suffix match vectors; ZoneLink spec validation tests; resolver fail-closed test", + "validation" : "Longest-suffix match vectors over sealed topology; child-local ZoneLink spec validation; resolver rejects unknown/stale/withdrawn/unauthenticated route projections; parent-store fixture contains no ZoneLink row or handler", "workItemId" : "ADR046-routing-003" }, { @@ -5017,7 +5017,7 @@ "dataMigration" : "None — full d2b 3.0 reset; no prior state to migrate", "dependencyOwner" : "ADR046-routing-002, ADR046-routing-007 (from ComponentSession spec); d2b-bus owner", "destination" : "`packages/d2b-bus/src/zone_route.rs` (cross-Zone bus routing), `packages/d2b-bus/src/relay.rs` (per-hop relay handler)", - "detailedDesign" : "Cross-Zone routing path in d2b-bus: ZoneEntrypointResolver → ZoneRouteEngine::decide_route → ZoneLink ComponentSession per hop; hop-counter decrement and enforcement; RBAC `relay` verb check at each intermediate hop; idempotency key namespace (full 6-tuple) in ZoneLinkIdempotencyKey; pinned reverse path tracking; cancellation forwarding; watch cursor forwarding and revision-expired handling; no-FD/credential structural rejection at serialization boundary Primary reuse disposition: `adapt`. Preserved source-plan detail: extract and adapt.", + "detailedDesign" : "Cross-Zone routing path in d2b-bus: ZoneEntrypointResolver consumes sealed topology plus authenticated route projections → ZoneRouteEngine::decide_route → admitted ComponentSession established by each next-hop child's local ZoneLink; hop-counter decrement and enforcement; RBAC `relay` verb check at each intermediate hop; idempotency key namespace (full 6-tuple) in ZoneLinkIdempotencyKey; pinned reverse path tracking; cancellation forwarding; watch cursor forwarding and revision-expired handling; no-FD/credential structural rejection at serialization boundary. No parent route step performs Resource API Get/List/Watch on ZoneLink Primary reuse disposition: `adapt`. Preserved source-plan detail: extract and adapt.", "integration" : "ResourceClient → d2b-bus → ZoneLink CS → intermediate zone → target zone; cancel/watch/stream all use the same routing path", "removalProof" : "Old direct-dispatch and gateway-backed paths retired per bus routing parity", "reuseAction" : "adapt", @@ -5122,14 +5122,14 @@ "dataMigration" : "None; new artifact file", "dependencyOwner" : "ADR046-routing-011, ADR046-routing-001; bundle emitter owner", "destination" : "`nixos-modules/zone-resources-json.nix` (new), private local-root allocator bootstrap compiler/sealer input (not a ResourceSpec or public bundle), `nixos-modules/bundle-artifacts.nix` (new row for per-Zone `resource-bundle.json`), `packages/xtask/src/main.rs` (`gen-zone-schemas` subcommand emitting `docs/reference/schemas/v3/.schema.json` for Zone and ZoneLink; `gen-zone-nix-options` subcommand emitting `nixos-modules/generated/options-zones-.nix`)", - "detailedDesign" : "`zone-resources-json.nix` iterates `d2b.zones..resources.*` to produce the canonical sorted resource list: for each entry, render `{ apiVersion, type, metadata: { name, zone, ownerRef: , labels: , annotations: }, spec: }`. Separately canonicalize sorted `{ childZone, parentZone }` rows from the compiler-only topology and seal them into the private allocator bootstrap input; `parentZone` never enters a resource bundle or `Zone.spec`, and a topology digest change releases/reallocates affected edges independently of resource `generationId`. The bundle JSON omits `managedBy` and `configurationGeneration`; the configuration service/core sets those fields when activating the validated bundle. Sort all resources by `(type, zone, name)`. Compute `generationId` as SHA-256 (lower hex) of the UTF-8 bytes of the sorted `resources` array JSON. Compute `integrity` as SHA-256 (base64url, no padding) of the full bundle JSON with integrity field zeroed. Install at `/etc/d2b/zones//resource-bundle.json` root:d2bd 0640. Canonical form: all object keys sorted lexicographically; order-significant arrays preserved; schema-declared set-like arrays sorted lexicographically; all optional fields emitted with defaults; no field renaming or restructuring. Build-time validation runs in a Nix derivation: (1) validate the complete parent map (non-root required, local-root forbidden, declared target, one scalar parent, not self, acyclic, max 16 names); (2) validate each resource against the committed JSON Schema; (3) validate `transportSettings` for each child-local ZoneLink against its same-Zone Provider's `transportSettingsSchema` — `transportProviderRef` is always explicit, never inferred or defaulted; (4) verify capability ceilings are subsets of the sealed selected-parent allocator grants; (5) verify `childZoneName == metadata.zone`, at most one uplink resource per non-root Zone, and no local-root uplink; (6) check for duplicate `(type, zone, name)` tuples. Providers MUST commit their `transportSettingsSchema` before any ZoneLink can reference them. Drift gates: `xtask gen-zone-schemas && git diff --exit-code` and `xtask gen-zone-nix-options && git diff --exit-code` both wired into `make test-drift`. Add `checks.${system}.zone-schema-drift` to `flake.nix`. Primary reuse disposition: `adapt`. Preserved source-plan detail: extend and adapt.", + "detailedDesign" : "`zone-resources-json.nix` iterates `d2b.zones..resources.*` to produce the canonical sorted resource list: for each entry, render `{ apiVersion, type, metadata: { name, zone, ownerRef: , labels: , annotations: }, spec: }`. Separately canonicalize sorted `{ childZone, parentZone }` rows from the compiler-only topology and seal them into the private allocator bootstrap input; `parentZone` never enters a resource bundle or `Zone.spec`, and a topology digest change releases/reallocates affected edges independently of resource `generationId`. Per-Zone generation is strict: local root's generated bundle contains no ZoneLink; a non-root Zone's enabled uplink and referenced transport Provider appear together only in that child's bundle; no emitter copies either resource into the selected parent's bundle. The bundle JSON omits `managedBy` and `configurationGeneration`; the configuration service/core sets those fields when activating the validated bundle. Sort all resources by `(type, zone, name)`. Compute `generationId` as SHA-256 (lower hex) of the UTF-8 bytes of the sorted `resources` array JSON. Compute `integrity` as SHA-256 (base64url, no padding) of the full bundle JSON with integrity field zeroed. Install at `/etc/d2b/zones//resource-bundle.json` root:d2bd 0640. Canonical form: all object keys sorted lexicographically; order-significant arrays preserved; schema-declared set-like arrays sorted lexicographically; all optional fields emitted with defaults; no field renaming or restructuring. Build-time validation runs in a Nix derivation: (1) validate the complete parent map (non-root required, local-root forbidden, declared target, one scalar parent, not self, acyclic, max 16 names); (2) validate each resource against the committed JSON Schema; (3) validate `transportSettings` for each child-local ZoneLink against its same-Zone Provider's `transportSettingsSchema` — `transportProviderRef` is always explicit, never inferred or defaulted; (4) verify capability ceilings are subsets of the sealed selected-parent allocator grants; (5) verify `childZoneName == metadata.zone`, at most one uplink resource per non-root Zone, and no local-root uplink; (6) check for duplicate `(type, zone, name)` tuples. Providers MUST commit their `transportSettingsSchema` before any ZoneLink can reference them. Drift gates: `xtask gen-zone-schemas && git diff --exit-code` and `xtask gen-zone-nix-options && git diff --exit-code` both wired into `make test-drift`. Add `checks.${system}.zone-schema-drift` to `flake.nix`. Primary reuse disposition: `adapt`. Preserved source-plan detail: extend and adapt.", "integration" : "The local-root allocator consumes sealed parent topology independently of resource bundles; `nixos-modules/bundle-artifacts.nix` installs each per-Zone `resource-bundle.json`; ADR046-routing-013 Zone runtime reads it on startup", "removalProof" : "`realm-controllers.json` artifact retires after Zone runtime is live and all hosts migrated", "reuseAction" : "adapt", "reuseSource" : "`realm-controller-config-json.nix` structural template; `xtask gen-schemas` extension point (main `a1cc0b2d` unchanged in this area)", "specId" : "ADR-046-zone-routing", "specPath" : "docs/specs/ADR-046-zone-routing.md", - "validation" : "`drift: zone-resource-schema`, `drift: zone-nix-options`, `build: zone-bundle-deterministic`, `build: parent-topology-sealed`, `build: transport-settings-unknown-field`, `build: capability-ceiling-superset`, `build: missing-transport-provider`; run `make flake-matrix-pin` after adding flake checks", + "validation" : "`drift: zone-resource-schema`, `drift: zone-nix-options`, `build: zone-bundle-deterministic`, `build: parent-topology-sealed`, `build: child-local-zonelink-bundle` (K0 has no ZoneLink; K1 contains its self-matching ZoneLink and same-Zone transport Provider; neither is copied to K0), `build: transport-settings-unknown-field`, `build: capability-ceiling-superset`, `build: missing-transport-provider`; run `make flake-matrix-pin` after adding flake checks", "workItemId" : "ADR046-routing-012" }, { @@ -5180,16 +5180,16 @@ { "currentSource" : "`packages/d2b-realm-router/src/service_v2.rs` (v3 baseline `b5ddbed6`): `RealmServiceServer` (bootstrap/enroll/resolve_route/authorize_shortcut/revoke_shortcut/report_shortcut_close/inspect/cancel), `RealmServiceProcess`, `RealmSessionAuthority`, `CredentialCustody`, `RealmServiceLimits`, `RealmAuditEvent`/`RealmMethod`/`RealmAuditOutcome`, `BootstrapBinding`/`EnrollmentBinding`/`ShortcutBinding`/`MutationRecord`; constants `DEFAULT_MAX_REALM_BINDINGS=256`, `DEFAULT_MAX_SHORTCUTS=256`, `DEFAULT_MAX_MUTATION_RECORDS=1024`, `DEFAULT_AUDIT_CAPACITY=1024`, `MAX_CONFIGURED_BOUND=4096`, `MAX_DISPATCH_IN_FLIGHT=64`, `SHUTDOWN_TIMEOUT=5s` (evidence: v3 baseline, not main; see Baseline section — **B** from d2bd/CLI perspective, **A** within realm-router display-session use)", "dataMigration" : "None; v3 Zone service is new; no v2 realm-service compatibility", - "dependencyOwner" : "ADR046-routing-007, ADR046-routing-001; Zone service owner", + "dependencyOwner" : "ADR046-routing-007, ADR046-routing-002, ADR046-routing-004, ADR046-routing-012; Zone service owner", "destination" : "`packages/d2b-zone-routing/src/service.rs`", - "detailedDesign" : "Rename `RealmServiceServer` → `ZoneServiceServer`; service wire name `d2b.realm.v2.RealmService` → `d2b.zone.v3.ZoneService`; rename methods (bootstrap→zone-bootstrap, enroll→zone-enroll, resolve_route→resolve-zone-route, authorize_shortcut→authorize-zone-shortcut, revoke_shortcut→revoke-zone-shortcut, report_shortcut_close→report-zone-shortcut-close, inspect→zone-inspect); replace `RealmSessionAuthority` with Zone principal + RBAC binding; replace `BootstrapBinding` with ZoneLink allocator-issued PSK binding; replace `EnrollmentBinding` with ZoneLink KK enrollment record; add `relay` verb RBAC check per hop; adapt shortcut model to ZonePath addressing; `RealmServiceLimits` defaults preserved; `MAX_DISPATCH_IN_FLIGHT=64`, `SHUTDOWN_TIMEOUT=5s` preserved; `CredentialCustody::GatewayGuest` excluded (all ZoneLink sessions are direct KK) Primary reuse disposition: `adapt`. Preserved source-plan detail: copy and adapt.", - "integration" : "Zone runtime instantiates one `ZoneServiceServer` per Zone; d2b-bus routes `d2b.zone.v3.ZoneService` calls to this server; CLI uses `ZoneServiceClient` (from ADR046-routing-010) for zone inspect/enroll/route-resolve", + "detailedDesign" : "Rename `RealmServiceServer` → `ZoneServiceServer`; service wire name `d2b.realm.v2.RealmService` → `d2b.zone.v3.ZoneService`; rename methods (bootstrap→zone-bootstrap, enroll→zone-enroll, resolve_route→resolve-zone-route, authorize_shortcut→authorize-zone-shortcut, revoke_shortcut→revoke-zone-shortcut, report_shortcut_close→report-zone-shortcut-close, inspect→zone-inspect) and add list/watch topology-projection methods. The read-only projection starts from the sealed sorted `{ childZone, parentZone }` compiler input and joins only authenticated, admitted `ZoneRouteEngine` route/projection status. It exposes no ZoneLink resource name, UID, spec, status, Provider ref, fingerprint, transport setting, or handle. Replace `RealmSessionAuthority` with Zone principal + RBAC binding; replace `BootstrapBinding` with allocator-issued PSK binding associated with the child's local ZoneLink; replace `EnrollmentBinding` with the corresponding KK enrollment record; add `relay` verb RBAC check per hop; adapt shortcut model to ZonePath addressing; `RealmServiceLimits` defaults preserved; `MAX_DISPATCH_IN_FLIGHT=64`, `SHUTDOWN_TIMEOUT=5s` preserved; `CredentialCustody::GatewayGuest` excluded (all ZoneLink sessions are direct KK) Primary reuse disposition: `adapt`. Preserved source-plan detail: copy and adapt.", + "integration" : "Zone runtime instantiates one `ZoneServiceServer` per Zone; d2b-bus routes `d2b.zone.v3.ZoneService` calls to this server; CLI uses `ZoneServiceClient` (from ADR046-routing-010) for topology list/inspect/watch, enrollment, and route resolution", "removalProof" : "`RealmServiceServer` on `d2b.realm.v2` retires after `ZoneServiceServer` handles all routing; display-session path migrates separately as part of Provider resource work", "reuseAction" : "adapt", "reuseSource" : null, "specId" : "ADR-046-zone-routing", "specPath" : "docs/specs/ADR-046-zone-routing.md", - "validation" : "Bootstrap/enroll/resolve-route/shortcut integration tests against fake ZoneLink; relay-verb RBAC test; KK enrollment test; shortcut ZonePath addressing test; concurrent dispatch bound test (64 in-flight)", + "validation" : "Bootstrap/enroll/resolve-route/shortcut integration tests against a child-local fake ZoneLink; topology list/inspect/watch golden vectors contain exact `{ childZone, parentZone }` rows plus authenticated status and no ZoneLink fields; stale/withdrawn/unauthenticated projection tests; parent-store no-row/no-handler test; relay-verb RBAC test; KK enrollment test; shortcut ZonePath addressing test; concurrent dispatch bound test (64 in-flight)", "workItemId" : "ADR046-routing-016" }, { @@ -5201,7 +5201,7 @@ "integration" : "Runs as part of `make test-lint`/`make test-rust`; every Provider crate's own redaction test (e.g. `tests/stream_redaction.rs`) is a per-Provider instance of the same closed list", "removalProof" : "Not applicable — this is a permanent gate, not a migration", "reuseAction" : "adapt", - "reuseSource" : "None (new cross-cutting gate; no equivalent exists in main)", + "reuseSource" : null, "specId" : "ADR-046-security-and-threat-model", "specPath" : "docs/specs/ADR-046-security-and-threat-model.md", "validation" : "Hermetic (`cargo test -p d2b-contract-tests policy_telemetry_redaction`); fails the build if a new Provider crate is added without a corresponding redaction test file under its `tests/`", @@ -5231,7 +5231,7 @@ "integration" : "Runs against the real redb-backed resource store test harness, not a mock", "removalProof" : "Not applicable", "reuseAction" : "adapt", - "reuseSource" : "None beyond the verb-table adaptation already tracked by `ADR046-zone-control-004`", + "reuseSource" : null, "specId" : "ADR-046-security-and-threat-model", "specPath" : "docs/specs/ADR-046-security-and-threat-model.md", "validation" : "Hermetic property test (`proptest`/`quickcheck`-style, minimum 10,000 cases per property)", @@ -5246,7 +5246,7 @@ "integration" : "`make test-fuzz`; a companion container test (`tests/integration/containers/zonelink-cross-zone.rs`) runs two real Zone runtime containers connected by a real ZoneLink and asserts the same property end to end over the wire, not just in the frame-serialization unit", "removalProof" : "Not applicable", "reuseAction" : "adapt", - "reuseSource" : "None", + "reuseSource" : null, "specId" : "ADR-046-security-and-threat-model", "specPath" : "docs/specs/ADR-046-security-and-threat-model.md", "validation" : "Fuzz corpus (`cargo fuzz run zonelink_frame -- -runs=1000000`, zero crashes); container test passes in `make test-integration`", @@ -5321,7 +5321,7 @@ "integration" : "`make test-rust`; a host-integration variant (`tests/host-integration/volume-marker-tamper.nix`) repeats the inode-swap scenario against the real broker-maintained marker root on a real filesystem", "removalProof" : "Not applicable", "reuseAction" : "adapt", - "reuseSource" : "None new", + "reuseSource" : null, "specId" : "ADR-046-security-and-threat-model", "specPath" : "docs/specs/ADR-046-security-and-threat-model.md", "validation" : "Hermetic + host/KVM fault-injection test; acceptance is 100% fail-closed across all three tamper scenarios", @@ -5336,7 +5336,7 @@ "integration" : "`make test-lint` (static scan) and `make test-rust` (dynamic property test)", "removalProof" : "Not applicable", "reuseAction" : "adapt", - "reuseSource" : "None", + "reuseSource" : null, "specId" : "ADR-046-security-and-threat-model", "specPath" : "docs/specs/ADR-046-security-and-threat-model.md", "validation" : "Hermetic; the dynamic test additionally runs as a canary-byte test (a unique random marker is embedded in the token and searched for across every observability surface)", @@ -5396,7 +5396,7 @@ "integration" : "`make test-rust` (CLI integration tests); a container test (`tests/integration/containers/support-bundle-quarantined.rs`) runs a real Zone with one quarantined Provider and asserts the bundle correctly reports `partial`", "removalProof" : "Not applicable", "reuseAction" : "adapt", - "reuseSource" : "None", + "reuseSource" : null, "specId" : "ADR-046-security-and-threat-model", "specPath" : "docs/specs/ADR-046-security-and-threat-model.md", "validation" : "Hermetic CLI test asserting no spec byte or `metadata.name` appears in a generated bundle; container test for the quarantined-Provider case", @@ -5426,7 +5426,7 @@ "integration" : "`make test-drift`", "removalProof" : "Not applicable — permanent gate", "reuseAction" : "adapt", - "reuseSource" : "None", + "reuseSource" : null, "specId" : "ADR-046-security-and-threat-model", "specPath" : "docs/specs/ADR-046-security-and-threat-model.md", "validation" : "Hermetic shell-script gate; a negative test adds a scratch Provider dossier missing a Security section and asserts the gate fails", @@ -5437,11 +5437,11 @@ "dataMigration" : "None — full d2b 3.0 reset; no prior state to migrate", "dependencyOwner" : "`ADR046-routing-004`, gateway-custody Provider work items (`ADR046-aca-*`, `ADR046-azure-vm-*`, `ADR046-transport-relay-*`)", "destination" : "`tests/integration/containers/malicious-child-zone.rs`", - "detailedDesign" : "Container-based penetration test running a real parent Zone and a deliberately malicious child Zone container that attempts, over a real ZoneLink: FD smuggling, credential-shaped byte injection, cross-Zone `ownerRef` forgery, capability-ceiling widening claims, and route-advertisement replay. Every attempt must be rejected by the parent with the specific typed error named in §10, and none may reach the parent's resource store, Credential state, or Host substrate Primary reuse disposition: `adapt`. Preserved source-plan detail: extract and adapt.", + "detailedDesign" : "Container-based penetration test running a real parent Zone and a deliberately malicious child Zone container. Before attack injection, assert the one ZoneLink row/handler exists only in the child store/runtime and the parent has only sealed `{ childZone, parentZone }` topology plus authenticated allocator/route projection state. The child then attempts, over that link: FD smuggling, credential-shaped byte injection, cross-Zone `ownerRef` forgery, capability-ceiling widening claims, and route-advertisement replay. Every attempt must be rejected by the parent with the specific typed error named in §10, and none may create a parent ZoneLink row or reach the parent's resource store, Credential state, or Host substrate Primary reuse disposition: `adapt`. Preserved source-plan detail: extract and adapt.", "integration" : "`make test-integration` (requires podman, per `AGENTS.md` \"Local Layer 1 + container integration\")", "removalProof" : "Not applicable", "reuseAction" : "adapt", - "reuseSource" : "None", + "reuseSource" : null, "specId" : "ADR-046-security-and-threat-model", "specPath" : "docs/specs/ADR-046-security-and-threat-model.md", "validation" : "Container integration test; acceptance is zero successful attacks across all five attempted vectors", @@ -5456,7 +5456,7 @@ "integration" : "Run manually before each tagged release touching a cloud/hardware Provider, per the existing `tests/README.md` manual-tier convention", "removalProof" : "Not applicable", "reuseAction" : "adapt", - "reuseSource" : "None", + "reuseSource" : null, "specId" : "ADR-046-security-and-threat-model", "specPath" : "docs/specs/ADR-046-security-and-threat-model.md", "validation" : "Checklist sign-off recorded in the release's validation evidence, not a CI gate (matches `D2b_LIVE=1` manual-tier precedent in `AGENTS.md`)", @@ -5782,7 +5782,7 @@ "dataMigration" : "Full d2b 3.0 reset; no v2 audit import", "dependencyOwner" : "Audit owner for Core device-grant and Service/Binding lifecycle; depends on ADR046-security-key-009, ADR046-security-key-018, and ADR-046-telemetry-audit-and-support.", "destination" : "Core `device-grant` audit and Provider controller Service/Binding ceremony lifecycle audit", - "detailedDesign" : "Path-free authority-grant records from Core and bounded Service/Binding/session digests/outcomes from controller; no path, raw target identity, LeaseId, session content, or CTAP bytes.", + "detailedDesign" : "Path-free authority-grant records from Core and bounded Service/Binding/session digests/outcomes from controller; no path, raw target identity, LeaseId, session content, or CTAP bytes. `resource_name_digest` is admitted only in the Core authority-grant audit after DeviceGrant authorization and is never copied to OTEL.", "integration" : "Core emits grant audit; controller emits Service/Binding lifecycle audit; Zone stream stores bounded records; CLI/support consumes digests/outcomes.", "removalProof" : "None — net-new; no prior owner to remove", "reuseAction" : "create", @@ -5797,14 +5797,14 @@ "dataMigration" : "Full d2b 3.0 reset; no v2 telemetry import", "dependencyOwner" : "Observability owner; depends on ADR046-security-key-010 relay, ADR046-security-key-009 controller, and ADR-046-telemetry-audit-and-support.", "destination" : "Provider/controller bounded telemetry emitter and observability-otel handoff for security-key metrics", - "detailedDesign" : "OTEL metrics: `d2b_device_sk_session_total`, `d2b_device_sk_ceremony_duration_seconds`, `d2b_device_sk_relay_restarts_total` via bounded emitter ring; descriptors use only closed semantic labels and never Zone/resource-name-derived identity, while `d2b.zone` and `d2b.provider` remain OTEL resource attributes", + "detailedDesign" : "OTEL metrics: `d2b_device_sk_session_total`, `d2b_device_sk_ceremony_duration_seconds`, `d2b_device_sk_relay_restarts_total` via bounded emitter ring; descriptors use only closed semantic labels and never Zone/resource-name-derived identity. Provider spans use only fixed operation/phase/outcome/error-class attributes. Neither metrics nor spans admit a resource name, UID, ref, digest (including `resource_name_digest`), session ID, or derived identity token, while `d2b.zone` and `d2b.provider` remain OTEL Resource attributes.", "integration" : "Relay/controller write metric events to the bounded ring; observability-otel Provider drains and exports; dashboards/CLI consume closed labels and bounded histograms.", "removalProof" : "None — net-new; no prior owner to remove", "reuseAction" : "create", "reuseSource" : null, "specId" : "ADR-046-provider-device-security-key", "specPath" : "docs/specs/providers/ADR-046-provider-device-security-key.md", - "validation" : "Metrics tests structurally assert closed label sets, exact absence of `vm`, `zone`, `zone_id`, `zone_uid`, and resource-name-derived keys, Device/Zone-name canary absence, retained `d2b.zone` resource attributes, bounded ring behavior, and correct session/ceremony/restart counters.", + "validation" : "`telemetry_identity_canaries.rs` and metric inventory tests structurally assert closed label/span-attribute sets; exact absence of `vm`, `zone`, `zone_id`, `zone_uid`, resource-name-derived keys, and `resource_name_digest`; Device/Service/Binding/Guest/Zone name, UID, ref, and digest canary absence from metrics and spans; retained `d2b.zone` Resource attributes; bounded ring behavior; and correct session/ceremony/restart counters.", "workItemId" : "ADR046-security-key-022" }, { @@ -6696,15 +6696,15 @@ "currentSource" : "`packages/d2b-realm-core/src/trace_context.rs` (`TraceContext`, `MAX_TRACE_FIELD_LEN`); `packages/d2b-realm-core/src/audit.rs` (`AuditHash`, `AuditHashError`, `AuditChainLink`, `AuditChainRecord`); `packages/d2b-realm-core/src/ids.rs` (`OperationId`, `CorrelationId`); `packages/d2b-realm-codec-protobuf/src/lib.rs` (`encode_trace_context`, `decode_trace_context`); `packages/d2b-contract-tests/tests/policy_observability.rs::startup_tracing_avoids_host_path_fields`", "dataMigration" : "Full d2b 3.0 reset; no v2 state/config import", "dependencyOwner" : "W0/W1a; telemetry crate owner", - "destination" : "`packages/d2b-telemetry/src/{trace_context.rs,audit_hash.rs,emitter.rs,meter_registry.rs,redaction_guard.rs}`", - "detailedDesign" : "`d2b-telemetry` provides: (1) `TraceContext` / `AuditHash` / `AuditChainLink` extracted unchanged; (2) `BoundedEmitter`: `tracing`-subscriber layer that serializes span/metric events into compact frames and writes them over a private Unix datagram socket to the `observability-otel` Provider — no `opentelemetry_sdk` dependency; (3) `RedactionGuard` span wrapper that asserts the v3 resource attribute allowlist at span creation. No OTEL SDK in this crate. Primary reuse disposition: `adapt`. Preserved source-plan detail: extract unchanged (`TraceContext`, `AuditHash`, `AuditChainLink`); adapt (`OperationId`/`CorrelationId` for v3 record contract); add bounded emitter.", + "destination" : "`packages/d2b-telemetry/src/{trace_context.rs,audit_hash.rs,emitter.rs,meter_registry.rs,metric_label_policy.rs,redaction_guard.rs}`", + "detailedDesign" : "`d2b-telemetry` provides: (1) `TraceContext` / `AuditHash` / `AuditChainLink` extracted unchanged; (2) `BoundedEmitter`: `tracing`-subscriber layer that serializes span/metric events into compact frames and writes them over a private Unix datagram socket to the `observability-otel` Provider — no `opentelemetry_sdk` dependency; (3) the canonical closed `METRIC_LABEL_POLICY`, structural descriptor/data-point/exemplar validator, exact forbidden-key/suffix predicates, and non-serializable resource-identity canary matcher used by emitter defense in depth and the mandatory collector ingress gate; (4) `RedactionGuard` span wrapper that asserts the v3 resource attribute allowlist at span creation. No OTEL SDK in this crate. Primary reuse disposition: `adapt`. Preserved source-plan detail: extract unchanged (`TraceContext`, `AuditHash`, `AuditChainLink`); adapt (`OperationId`/`CorrelationId` for v3 record contract); add bounded emitter.", "integration" : "Every v3 core process initializes a `BoundedEmitter` pointing at `$ZONE_STATE/telemetry/emitter.sock`; v3 audit records use `AuditHash`/`AuditChainLink` from this crate", "removalProof" : "None — net-new; no prior owner to remove", "reuseAction" : "adapt", "reuseSource" : null, "specId" : "ADR-046-telemetry-audit-and-support", "specPath" : "docs/specs/ADR-046-telemetry-audit-and-support.md", - "validation" : "Unit test for `RedactionGuard` attribute gate; unit test for `BoundedEmitter` ring-full drop and FIFO drain; `policy_telemetry_redaction.rs::startup_tracing_avoids_host_path_fields` port; assert `config_source = \"realm-controllers\"` absent; assert no `opentelemetry_sdk` dependency in `d2b-telemetry` Cargo.toml", + "validation" : "Unit test for `RedactionGuard` attribute gate; unit test for `BoundedEmitter` ring-full drop and FIFO drain; table tests for `METRIC_LABEL_POLICY` exact keys, suffixes, and `metadata.name`/UID/ResourceRef identity canaries; `policy_telemetry_redaction.rs::startup_tracing_avoids_host_path_fields` port; assert `config_source = \"realm-controllers\"` absent; assert no `opentelemetry_sdk` dependency in `d2b-telemetry` Cargo.toml", "workItemId" : "ADR046-telem-001" }, { @@ -6772,14 +6772,14 @@ "dataMigration" : "Existing SigNoz data not migrated; v3 starts fresh", "dependencyOwner" : "ADR046-process-001 + ADR046-provider-001; `observability-otel` Provider owner", "destination" : "`packages/d2b-provider-observability-otel/src/`, `nixos-modules/components/observability/` (adapted files)", - "detailedDesign" : "`Provider/observability-otel` is an **ordinary optional non-bootstrap Process** (not counted toward the ≤64 MiB mandatory core aggregate). It owns: (1) per-Zone datagram receiver socket at `$ZONE_STATE/telemetry/emitter.sock` (drains frames from core emitters) and OTLP/gRPC Unix socket at `$ZONE_STATE/telemetry/otlp.sock`; (2) the full OTEL SDK with OTLP exporter — only this process links `opentelemetry_sdk`; (3) OTel Collector pipeline per Zone and per Host; (4) vsock OTLP forwarding to obs Zone (replaces socat-based `OtelHostBridgeArgvInputs`); (5) SigNoz stack Nix adapted from `stack.nix` with per-Zone `ingressSources` replacing per-VM `vmName`; (6) journald scrape (optional, disabled by default); (7) self-metrics endpoint. Zone/controller startup does not wait for this Provider. If absent or unready, Zone health is `Degraded` (not `Failed`). Readiness: socket exists and first drain cycle completes successfully. `d2b.observability.host.identityName` option preserved; `vmName` in `ingressSources` populated from Zone name. Primary reuse disposition: `adapt`. Preserved source-plan detail: adapt Nix pipeline shape (replace per-VM `vmName` with per-Zone naming); adapt `OtelHostBridgeArgvInputs` vsock forwarding to native OTLP/gRPC-over-vsock; adapt readiness gate pattern (`OtelHostBridgeReadiness::Ready` → Provider phase `Ready`); adapt `ingressSources` per-VM → per-Zone.", + "detailedDesign" : "`Provider/observability-otel` is an **ordinary optional non-bootstrap Process** (not counted toward the ≤64 MiB mandatory core aggregate). It owns: (1) per-Zone datagram receiver socket at `$ZONE_STATE/telemetry/emitter.sock` (drains frames from core emitters) and OTLP/gRPC Unix socket at `$ZONE_STATE/telemetry/otlp.sock`; (2) the full OTEL SDK with OTLP exporter — only this process links `opentelemetry_sdk`; (3) OTel Collector pipeline per Zone and per Host; (4) vsock OTLP forwarding to obs Zone (replaces socat-based `OtelHostBridgeArgvInputs`); (5) D096 import-stream ingest; (6) one structural `METRIC_LABEL_POLICY` gate shared by Unix emitter, OTLP Unix, OTLP/vsock, and import-stream metrics before aggregation, queueing, batching, retry, or export, with bounded non-echoing errors/quarantine/backpressure; (7) SigNoz stack Nix adapted from `stack.nix` with per-Zone `ingressSources` replacing per-VM `vmName`; (8) journald scrape (optional, disabled by default); (9) self-metrics endpoint. Trusted producer identity is stamped only into allow-listed OTEL Resource attributes; audit remains separate. Zone/controller startup does not wait for this Provider. If absent or unready, Zone health is `Degraded` (not `Failed`). Readiness: socket exists and first drain cycle completes successfully. `d2b.observability.host.identityName` option preserved; `vmName` in `ingressSources` populated from Zone name. Primary reuse disposition: `adapt`. Preserved source-plan detail: adapt Nix pipeline shape (replace per-VM `vmName` with per-Zone naming); adapt `OtelHostBridgeArgvInputs` vsock forwarding to native OTLP/gRPC-over-vsock; adapt readiness gate pattern (`OtelHostBridgeReadiness::Ready` → Provider phase `Ready`); adapt `ingressSources` per-VM → per-Zone.", "integration" : "Core process `BoundedEmitter` → `emitter.sock` → observability-otel collector → `otlp.sock` → vsock → obs Zone SigNoz; Zone startup independent of Provider readiness", "removalProof" : "`otel_host_bridge_argv.rs` socat runner and `otel_host_bridge_readiness.rs` retired after `observability-otel` Provider delivers native OTLP/vsock and passes conformance; `ProcessRole::OtelHostBridge` and `RunnerRole::OtelHostBridge` retired from `d2b-core/src/processes.rs` and `d2b-contracts/src/broker_wire.rs` after Provider migration", "reuseAction" : "adapt", "reuseSource" : null, "specId" : "ADR-046-telemetry-audit-and-support", "specPath" : "docs/specs/ADR-046-telemetry-audit-and-support.md", - "validation" : "`emitter_socket_receive`, `emitter_ring_drains_on_socket_available`, `emitter_ring_drop_on_overflow`, `no_vm_label_in_metrics`, `zone_startup_proceeds_without_provider` tests; adapted `policy_observability.rs` tests (retain `loki_native_otel_resource_attributes` and SigNoz-only backend assertions); adapted `minijail_relay_otel.rs` shape test for Provider-managed runner", + "validation" : "`emitter_socket_receive`, `emitter_ring_drains_on_socket_available`, `emitter_ring_drop_on_overflow`, table-driven `ingress_metric_policy` across all four ingress adapters, `no_vm_label_in_metrics`, and `zone_startup_proceeds_without_provider` tests; adapted `policy_observability.rs` tests (retain `loki_native_otel_resource_attributes` and SigNoz-only backend assertions); adapted `minijail_relay_otel.rs` shape test for Provider-managed runner", "workItemId" : "ADR046-telem-006" }, { @@ -6802,7 +6802,7 @@ "dataMigration" : "Full d2b 3.0 reset; no v2 state/config import", "dependencyOwner" : "ADR046-telem-001; policy/contract-tests owner", "destination" : "`packages/d2b-contract-tests/tests/policy_telemetry_redaction.rs` (new); updated `policy_observability.rs`; updated `policy_metrics.rs`", - "detailedDesign" : "(1) Extend `loki_native_otel_resource_attributes` allowlist to include `d2b.zone`, `d2b.provider`, `d2b.component`, `service.version`. (2) Add redaction lint: scan all v3 instrumentation call sites for `realm`, `workload_id`, `node_id`, `vm` (as label key), `path`, `socket`, `argv`, `pid`, `exe`. (3) Add structural metric-label policy lint: parse every v3 `MetricDescriptor`, require each label key and value domain to exist in the closed `METRIC_LABEL_POLICY`, reject exact keys `vm`, `zone`, `zone_id`, `zone_uid`, `credential_name`, `network`, `network_name`, and `link_name_hash`, reject resource-name-derived key suffixes `*_name`, `*_name_hash`, `*_name_digest`, and `*_uid`, and prove a `metadata.name` canary never enters label values. Fixed semantic labels remain allowed only with closed domains. (4) Assert the `d2b.zone` resource attribute remains present. (5) Add bucket boundary gates for 5 ms and 20 ms. (6) Retain: `startup_tracing_avoids_host_path_fields`; SigNoz-only backend assertion; `tempo_guest_collector_shape`; `config_source = \"realm-controllers\"` absence gate. Primary reuse disposition: `adapt`. Preserved source-plan detail: adapt and extend; keep existing tests; add new policy gates.", + "detailedDesign" : "(1) Extend `loki_native_otel_resource_attributes` allowlist to include `d2b.zone`, `d2b.provider`, `d2b.component`, `service.version`. (2) Add redaction lint: scan all v3 instrumentation call sites for `realm`, `workload_id`, `node_id`, `vm` (as label key), `path`, `socket`, `argv`, `pid`, `exe`. (3) Add structural metric-label policy lint: parse every v3 `MetricDescriptor`, require each label key and value domain to exist in the closed `METRIC_LABEL_POLICY`, reject exact keys `vm`, `zone`, `zone_id`, `zone_uid`, `credential_name`, `network`, `network_name`, and `link_name_hash`, reject resource-name-derived key suffixes `*_name`, `*_name_hash`, `*_name_digest`, and `*_uid`, and prove `metadata.name`, UID, ResourceRef, and resource-identity canaries never enter label values. Fixed semantic labels remain allowed only with closed domains. (4) Prove the observability Provider's Unix-emitter, OTLP-Unix, OTLP/vsock, and import-stream adapters all invoke that validator before queue/batch/export; assert whole-frame rejection, bounded non-echoing error classes/quarantine, and policy-before-capacity backpressure. (5) Assert the `d2b.zone` Resource attribute remains present and audit is unchanged. (6) Add bucket boundary gates for 5 ms and 20 ms. (7) Retain: `startup_tracing_avoids_host_path_fields`; SigNoz-only backend assertion; `tempo_guest_collector_shape`; `config_source = \"realm-controllers\"` absence gate. Primary reuse disposition: `adapt`. Preserved source-plan detail: adapt and extend; keep existing tests; add new policy gates.", "integration" : "Contract-tests run in workspace check and `make test-drift`", "removalProof" : "None — net-new; no prior owner to remove", "reuseAction" : "adapt", @@ -6863,7 +6863,7 @@ "dependencyOwner" : "W0 shared contract root; ComponentSession transport adapter owner", "destination" : "`packages/d2b-provider-transport-azure-relay/src/relay_transport.rs`", "detailedDesign" : "Adapt `RelayStream` as relay transport service process; expose named opaque byte stream on the `transport-service` Unix endpoint; add 2-byte length-prefixed framing; preserve credential redaction; TLS/WebSocket state stays in-process — only Noise record bytes traverse the named stream; register named stream with d2b-bus as `TransportHandle`; transport descriptor: `attachment_support: false`, `locality: Remote`, `atomic: false`; expose `OpenTransport`/`CloseTransport`/`ObserveTransport` interface to core; long-lived service process multiplexes sessions internally Primary reuse disposition: `adapt`. Preserved source-plan detail: extract and adapt.", - "integration" : "Core ZoneLink controller calls `OpenTransport(spec.provider.settings)` → receives named byte stream handle; relay service cannot interpret plaintext bytes; one carriage per call; WebSocket loss closes the named stream", + "integration" : "The child Zone's core ZoneLink controller calls its same-Zone selected Provider's `OpenTransport(spec.transportSettings, roleCredentialRef)` using the role ref selected from `spec.transportCredentials` → receives named byte stream handle; relay service cannot interpret plaintext bytes; one carriage per call; WebSocket loss closes the named stream; the parent has only sealed allocator/route state", "removalProof" : "`d2b-provider-relay/src/lib.rs` relay plumbing retained until ACA display migration completes", "reuseAction" : "adapt", "reuseSource" : null, @@ -6877,8 +6877,8 @@ "dataMigration" : "None — full d2b 3.0 reset; no prior state to migrate", "dependencyOwner" : "ADR046-transport-relay-001; Credential KK session; ComponentSession/d2b-bus owner", "destination" : "`packages/d2b-provider-transport-azure-relay/src/credential_client.rs`", - "detailedDesign" : "Async Credential KK session client for service components (service processes have d2b-bus access, enabling Credential KK; workers do not); acquire listener credential via KK inside the gateway Guest using `config.credentialBindings[listenerCredentialAlias]`; acquire sender credential independently inside the child's gateway Guest via the same KK model using `config.credentialBindings[senderCredentialAlias]`; raw credential bytes held in zeroizing memory inside the gateway Guest, presented to Azure Relay, then immediately zeroized; no credential bytes cross process, network, or Guest boundary; redacted Debug; no credential bytes in logs/audit/OTEL; core ProviderDeployment creates a private persistent Volume (per ADR-046-provider-state) for each component before its Process starts — the transport Provider does not own or create these Volumes; `Provider/volume-local` reconciles them; `migrationPolicy: none` means no migration worker is ever spawned; no relay auth token, WebSocket handle, session key, or credential byte is written to that Volume; all relay session state remains transient in-process memory", - "integration" : "Listener service invokes before each relay connect attempt inside gateway Guest; child Zone's sender service acquires its own credential independently inside its own gateway Guest", + "detailedDesign" : "Async Credential KK session client for service components (service processes have d2b-bus access, enabling Credential KK; workers do not); child core validates the same-Zone refs in `spec.transportCredentials`, selects the unique listener or sender ref by Credential audience, and supplies it to the child-local Provider service inside its gateway Guest; raw credential bytes are held in zeroizing memory inside the gateway Guest, presented to Azure Relay, then immediately zeroized; no Credential ref or byte crosses a Zone or Guest boundary, and byte delivery between the Credential Provider and consuming service remains inside the protected KK session; the parent allocator receives neither refs nor credentials and keeps only sealed route state; redacted Debug; no credential bytes in logs/audit/OTEL; the child Zone's core ProviderDeployment creates a private persistent Volume (per ADR-046-provider-state) for each component before its Process starts — the transport Provider does not own or create these Volumes; `Provider/volume-local` reconciles them; `migrationPolicy: none` means no migration worker is ever spawned; no relay auth token, WebSocket handle, session key, or credential byte is written to that Volume; all relay session state remains transient in-process memory", + "integration" : "The selected Provider's role service invokes acquisition inside the child gateway Guest; the parent allocator has only sealed route state and no Provider/Credential/ZoneLink resource", "removalProof" : "N/A; new module", "reuseAction" : "create", "reuseSource" : null, @@ -6890,7 +6890,7 @@ { "currentSource" : "None (new; core drives reconnect, not the transport Provider)", "dataMigration" : "None — full d2b 3.0 reset; no prior state to migrate", - "dependencyOwner" : "ADR046-transport-relay-001; reconnect contract; ZoneLink handler", + "dependencyOwner" : "ADR046-transport-relay-001; reconnect contract; child Zone's ZoneLink handler", "destination" : "`packages/d2b-provider-transport-azure-relay/src/reconnect.rs`", "detailedDesign" : "Relay service responds to `CloseTransport`+`OpenTransport` cycle from core; core owns reconnect policy and backoff scheduling; relay service tears down the current WebSocket when core calls `CloseTransport` and establishes a new WebSocket connection when core calls `OpenTransport`; relay service does not maintain a backoff state machine or independently retry — it starts a new WebSocket on demand and emits the connect result via `ObserveTransport`; listener and sender are long-lived service processes that do not re-spawn on reconnect", "integration" : "`ObserveTransport` delivers `TransportObservation::Disconnected` to core; core drives reconnect via `CloseTransport` then `OpenTransport` after applying its own backoff", @@ -6907,14 +6907,14 @@ "dataMigration" : "None — full d2b 3.0 reset; no prior state to migrate", "dependencyOwner" : "ADR046-transport-relay-001; transport settings schema; Nix configuration owner", "destination" : "`packages/d2b-provider-transport-azure-relay/src/transport_settings.rs`; `docs/reference/schemas/v3/providers/transport-azure-relay.transport-settings.json`", - "detailedDesign" : "`AzureRelayTransportSettings` Rust struct with serde; validation against committed JSON Schema; reject `secret`-annotated fields; enforce `^[a-z][a-z0-9-]*$` pattern for `listenerCredentialAlias`/`senderCredentialAlias` alias ID fields (never `Credential/` refs); xtask `gen-provider-transport-schemas` integration", + "detailedDesign" : "`AzureRelayTransportSettings` Rust struct with serde for only `relayNamespaceId` and `relayEntityId`; validation against committed JSON Schema; reject secret-shaped fields/values; generate and admit the exact six-field ZoneLink base; reject legacy provider envelopes and allocator-private fingerprint/capability fields; resolve `spec.transportProviderRef` before schema validation; validate exactly two same-Zone `spec.transportCredentials` refs with one `azure-relay-listen` and one `azure-relay-send` audience; enforce `disabled`/`limits` in child core; xtask `gen-provider-transport-schemas` integration", "integration" : "`make test-drift` gate: `xtask gen-provider-transport-schemas && git diff --exit-code`", "removalProof" : "N/A; new contract", "reuseAction" : "create", "reuseSource" : null, "specId" : "ADR-046-provider-transport-azure-relay", "specPath" : "docs/specs/providers/ADR-046-provider-transport-azure-relay.md", - "validation" : "`tests/transport_settings_schema.rs`: valid/invalid schema vectors; eval-time Nix assertion coverage from `nix-unit: transport-settings-secret-key` test (see zone-routing spec)", + "validation" : "`tests/transport_settings_schema.rs`: valid/invalid schema vectors; `tests/transport_credentials.rs`: exact canonical ZoneLink field set, same-Zone ref/count/audience/scope checks, and rejection of credential refs inside `transportSettings`; eval-time Nix assertion coverage from `nix-unit: transport-settings-secret-key` test (see zone-routing spec)", "workItemId" : "ADR046-transport-relay-004" }, { @@ -6952,7 +6952,7 @@ "dataMigration" : "None — full d2b 3.0 reset; no prior state to migrate", "dependencyOwner" : "Provider crate owner; integration test owner", "destination" : "`packages/d2b-provider-transport-azure-relay/src/tests/integration/README`", - "detailedDesign" : "Required content: fake relay server setup and teardown using the injected fake Relay effect port; how to run hermetic integration tests without a live Azure service; how to configure the injected fake Credential effect port for credential delivery tests; how to run with a real Azure namespace (requires a `Credential` resource declared in `spec.config.credentialBindings`, not environment-variable credential paths); integration test scenarios and expected outcomes; CI/local execution instructions", + "detailedDesign" : "Required content: fake relay server setup and teardown using the injected fake Relay effect port; how to run hermetic integration tests without a live Azure service; how to configure the injected fake Credential effect port for credential delivery tests; the fixture declares compiler-only `k2.parentZone = \"local-root\"` and puts the exact-shape ZoneLink, selected Provider, Network, Credentials, Process, and Endpoint resources only in K2; local-root's store is asserted to contain no reciprocal ZoneLink or Provider; how to run with a real Azure namespace (requires same-child-Zone `Credential` resources listed in `spec.transportCredentials`, not environment-variable credential paths); integration test scenarios and expected outcomes; CI/local execution instructions", "integration" : "`make test-integration` invokes `tests/integration/containers/` scenarios which inject the fake relay and credential port implementations from `src/tests/integration/fake_relay_server.rs`", "removalProof" : "N/A; mandatory layout", "reuseAction" : "create", @@ -6984,7 +6984,7 @@ "destination" : "`packages/d2b-provider-transport-unix/src/{seqpacket,identity,socket}.rs`", "detailedDesign" : "Copy transport structs verbatim; adapt `PeerIdentityPolicy` to report `PeerCredentials` upward to ComponentSession for subject mapping (not for direct resource lookup — that is core's responsibility); maintain `SO_PASSCRED` setup and first-packet credential extraction as documented; CLOEXEC enforcement uses `rustix` syscall wrappers over `libc` where available Primary reuse disposition: `adapt`. Preserved source-plan detail: copy and adapt.", "integration" : "`portal.rs::open_transport` calls `SeqpacketSocket::getsockopt(SO_TYPE)` and `setsockopt(SO_PASSCRED)`, constructs `UnixSeqpacketTransport`, hands OwnedTransport FD back to caller", - "removalProof" : "`d2b-realm-transport` seqpacket path retired after ZoneLink sessions migrate", + "removalProof" : "`d2b-realm-transport` seqpacket path retired after ZoneLink sessions migrate to child-local Providers and tests prove no reciprocal parent-store resource or cross-Zone FD transfer remains", "reuseAction" : "adapt", "reuseSource" : "Same; `UnixSeqpacketTransport`, `PeerIdentityPolicy`, `UnixAttachmentPayload`, `OwnedUnixAttachment`, `SeqpacketSocket`, `PeerCredentials`, `ObjectIdentity`, `AcceptedAttachment`, `VerifiedPacket`", "specId" : "ADR-046-provider-transport-unix", @@ -7058,13 +7058,13 @@ "dependencyOwner" : "ADR046-transport-unix-002 through 006; ADR046-bus-001 (d2b-bus ComponentSession method dispatch); ADR046-session-001 (named-stream protocol)", "destination" : "`packages/d2b-provider-transport-unix/src/{portal,service}.rs`", "detailedDesign" : "`portal.rs`: `PortalHandler` struct owns a bounded `HashMap` (capacity `MAX_OPEN_TRANSPORTS=256`); `open_transport(req, attachment_fd)` validates via `admission.rs`, dups FD, allocates handle, stores `MonitorState { dup_fd, observation_senders: Vec }`; `close_transport(handle)` closes dup FD, half-closes all observation senders, removes entry; `observe_transport(handle)` registers a new `NamedStreamSender` and spawns an async epoll-watcher task on the dup FD; `TransportHandle` is a `[u8; 16]` random token; redacted in all Debug impls; `service.rs` is the binary entry: accepts the allocator-issued portal endpoint FD at launch, runs `GeneratedTransportServiceServer` over it, dispatches to `PortalHandler` Primary reuse disposition: `adapt`. Preserved source-plan detail: adapt dispatch pattern; implement portal methods as new.", - "integration" : "Core ZoneLink controller calls the three methods via d2b-bus; portal endpoint FD is supplied by Zone runtime/allocator at Process spawn, not SD_LISTEN_FDS", + "integration" : "The child Zone's core ZoneLink controller calls the three methods via same-Zone d2b-bus; the selected parent allocator retains its peer endpoint as sealed route state and injects the child endpoint through sealed bootstrap; the child runtime supplies that FD locally at Process spawn, not through a parent Provider, a cross-Zone attachment, or `SD_LISTEN_FDS`", "removalProof" : "Ad-hoc IPC stubs in `d2bd/src/` retired after portal migration", "reuseAction" : "adapt", "reuseSource" : "main `a1cc0b2d` `d2b-provider-toolkit/src/server.rs` service dispatch pattern", "specId" : "ADR-046-provider-transport-unix", "specPath" : "docs/specs/providers/ADR-046-provider-transport-unix.md", - "validation" : "`tests/portal.rs::open_transport_zone_link_validates_and_returns_ownedtransport`; `open_transport_local_portal_seqpacket_with_attachments_accepted`; `open_transport_zone_link_attachments_enabled_rejected`; `close_transport_is_idempotent_after_handle_removed`; `observe_transport_delivers_pollhup_as_peer_disconnected`; `handle_table_rejects_at_max_capacity`; `restart_clears_all_handles`", + "validation" : "`tests/portal.rs::open_transport_zone_link_validates_and_returns_ownedtransport`; `open_transport_uses_child_bootstrap_endpoint_only`; `parent_endpoint_never_enters_provider_portal`; `open_transport_local_portal_seqpacket_with_attachments_accepted`; `open_transport_zone_link_attachments_enabled_rejected`; `close_transport_is_idempotent_after_handle_removed`; `observe_transport_delivers_pollhup_as_peer_disconnected`; `handle_table_rejects_at_max_capacity`; `restart_clears_all_handles`", "workItemId" : "ADR046-transport-unix-007" }, { @@ -7086,15 +7086,15 @@ "currentSource" : "`nixos-modules/options-realms.nix` realm options (v3 baseline); `nixos-modules/assertions.nix`", "dataMigration" : "`d2b.realms.*` Nix options superseded by `d2b.zones.*`; no compatibility bridge (v3 reset)", "dependencyOwner" : "ADR046-transport-unix-001; Nix/build integrator", - "destination" : "`docs/reference/schemas/v3/providers/transport-unix.transport-binding.json`; `nixos-modules/assertions.nix` (assertion additions); generated `nixos-modules/generated/options-zones-ZoneLink.nix` provider-settings submodule", - "detailedDesign" : "Commit the JSON Schema; run `xtask gen-zone-schemas` and `xtask gen-zone-nix-options` to regenerate committed files; add two assertions to `assertions.nix` (stream+attachments conflict; sensitive key names); `xtask gen-zone-resources` adds provider-specific settings validation step", - "integration" : "Build emitter validates `spec.provider.settings` against schema before computing `generationId`; drift gate enforces sync", + "destination" : "`docs/reference/schemas/v3/providers/transport-unix.transport-binding.json`; `nixos-modules/assertions.nix` (assertion additions); generated `nixos-modules/generated/options-zones-ZoneLink.nix` `transportSettings` submodule", + "detailedDesign" : "Commit the JSON Schema; run `xtask gen-zone-schemas` and `xtask gen-zone-nix-options` to regenerate committed files; generate the exact six-field ZoneLink base; reject legacy provider envelopes and allocator-private fingerprint/capability fields; add assertions for stream+attachments conflict and sensitive key names; require an empty `transportCredentials` list; reuse the common topology assertions to require the ZoneLink and selected Provider in the same child Zone, self-matching `childZoneName`, compiler-only non-root `parentZone`, and no reciprocal parent resource; `xtask gen-zone-resources` adds Provider-selected `transportSettings` validation", + "integration" : "Build emitter resolves `spec.transportProviderRef`, validates `spec.transportSettings` against that Provider's schema, and validates the empty `spec.transportCredentials` list before computing `generationId`; the topology compiler seals child→parent allocator selection separately and emits only the child-local Provider/ZoneLink resources; drift gate enforces sync", "removalProof" : "`nixos-modules/options-realms.nix` realm wiring retired after Zone resource bundle activation replaces it", "reuseAction" : "create", - "reuseSource" : "None; new schema file", + "reuseSource" : null, "specId" : "ADR-046-provider-transport-unix", "specPath" : "docs/specs/providers/ADR-046-provider-transport-unix.md", - "validation" : "All seven eval/build tests in the Nix section", + "validation" : "All eval/build tests in the Nix section, including `transport-unix-exact-zonelink-spec`, `transport-unix-child-local-topology`, legacy-field rejection, empty-credential enforcement, and a generated-bundle assertion that the parent store has no reciprocal Provider/ZoneLink row", "workItemId" : "ADR046-transport-unix-009" }, { @@ -7106,7 +7106,7 @@ "integration" : "`portal.rs` calls `audit.rs::emit_*` before returning from each portal method; `seqpacket.rs` calls `metrics.rs::record_*` on every accept/packet/attachment", "removalProof" : "`d2bd/src/metrics.rs` hand-rolled registry retired after metric surface migration", "reuseAction" : "create", - "reuseSource" : "None; new per v3 telemetry separation invariant", + "reuseSource" : null, "specId" : "ADR-046-provider-transport-unix", "specPath" : "docs/specs/providers/ADR-046-provider-transport-unix.md", "validation" : "`tests/conformance.rs::audit_records_contain_no_pid_uid_or_handle`; `tests/conformance.rs::metric_labels_are_closed_set`; `tests/conformance.rs::span_attributes_contain_no_sensitive_fields`", @@ -7117,8 +7117,8 @@ "dataMigration" : "None — full d2b 3.0 reset; no prior state to migrate", "dependencyOwner" : "ADR046-transport-unix-007 through 010; test orchestration owner", "destination" : "`packages/d2b-provider-transport-unix/integration/` and `integration/README.md`", - "detailedDesign" : "Four scenarios: `transport_open.rs` (fake Zone portal, allocator-socketpair FD attachment in → OwnedTransport attachment out → verify socket kind, CLOEXEC, SO_PASSCRED enabled; p95 latency assertion ≤2 ms); `fd_transfer.rs` (seqpacket `SCM_RIGHTS` transfer through opened transport, credit accounting, scavenge on error injection); `reconnect.rs` (CloseTransport + re-OpenTransport with fresh socketpair, verify previous handle is unknown, verify monitoring dup closed); `observation_stream.rs` (ObserveTransport stream receives `PEER_DISCONNECTED` event when peer closes socketpair end within 5 ms p95). `integration/README.md` documents prerequisites (no KVM required; all scenarios use in-process socketpairs and fake Zone API endpoint stub), invocation (`cargo test -p d2b-provider-transport-unix --test integration`), environment variables, and expected output", - "integration" : "Invoked by `make test-integration`; no host mutation; each scenario creates its own socketpairs", + "detailedDesign" : "Four scenarios: `transport_open.rs` (fixture models compiler-only `k1.parentZone = \"local-root\"`; fake selected allocator retains one socketpair endpoint as sealed route state and injects the other into K1 bootstrap; K1 core passes only that child endpoint to its same-Zone Provider → OwnedTransport out; verify socket kind, CLOEXEC, SO_PASSCRED, K1-local Provider/ZoneLink, and absent local-root reciprocal row; p95 latency assertion ≤2 ms); `fd_transfer.rs` (within-Zone local-portal seqpacket `SCM_RIGHTS` transfer, credit accounting, scavenge on error injection; ZoneLink route rejects the same packet); `reconnect.rs` (CloseTransport + re-OpenTransport with a fresh sealed child endpoint, verify previous handle is unknown and monitoring dup closed); `observation_stream.rs` (ObserveTransport stream receives `PEER_DISCONNECTED` when peer closes within 5 ms p95). `integration/README.md` documents prerequisites (no KVM required; all scenarios use in-process socketpairs and fake Zone API endpoint stub), invocation (`cargo test -p d2b-provider-transport-unix --test integration`), environment variables, and expected output", + "integration" : "Invoked by `make test-integration`; no host mutation; each scenario creates its own socketpair, keeps parent endpoint state outside the resource stores, and exposes only the child endpoint to the child-local Provider", "removalProof" : "Ad-hoc IPC test stubs retired after scenario parity", "reuseAction" : "adapt", "reuseSource" : "Test scenario shapes from `d2b-session-unix/tests/unix_session.rs` end-to-end test (main `a1cc0b2d`)", @@ -7132,14 +7132,14 @@ "dataMigration" : "None — docs/tooling only; no runtime state", "dependencyOwner" : "d2b-contracts crate shape stabilised by shared root contract; d2b-contracts owner", "destination" : "packages/d2b-contracts/src/usbip_effect_port.rs", - "detailedDesign" : "Define UsbipEffectPort and UsbipGuestEffectPort in d2b-contracts with DeviceUid, NetworkUid, UsbBindingUid, LeaseToken, FirewallToken, FirewallObservation, KernelModuleClass, DeviceProbeResult, and UsbipEffectError; export traits/types only with no implementation; keep firewall apply/observe/release Network/busid-scoped, attach/detach Binding-addressed, and all fd/path/busid values private. `TransientDetail` derives `Clone, PartialEq, Eq` while retaining manual redacted Debug/Display so `UsbipEffectError`'s derives compile without disclosure. Primary reuse disposition: `create`. Preserved source-plan detail: net-new trait definition.", + "detailedDesign" : "Define UsbipEffectPort and UsbipGuestEffectPort in d2b-contracts with DeviceUid, NetworkUid, UsbBindingUid, LeaseToken, FirewallToken, FirewallGenerationFence, FirewallObservation, KernelModuleClass, DeviceProbeResult, and UsbipEffectError; export traits/types only with no implementation. `apply_firewall` and `release_firewall` both accept expected Network/Service generations; release also accepts NetworkUid and borrows the token so the controller can retain it until confirmed effect. Keep firewall apply/observe/release Network/busid-scoped, attach/detach Binding-addressed, and all fd/path/busid values private. `TransientDetail` derives `Clone, PartialEq, Eq` while retaining manual redacted Debug/Display so `UsbipEffectError`'s derives compile without disclosure. Primary reuse disposition: `create`. Preserved source-plan detail: net-new trait definition.", "integration" : "Provider/device-usbip controller depends on this trait for injected semantic effects; the framework core adapter implements it in ADR046-usbip-002.", "removalProof" : "None — net-new; no prior owner to remove", "reuseAction" : "create", "reuseSource" : null, "specId" : "ADR-046-provider-device-usbip", "specPath" : "docs/specs/providers/ADR-046-provider-device-usbip.md", - "validation" : "d2b-contracts tests for trait object safety, firewall apply/observe/release signatures, `UsbipEffectError: Clone + PartialEq + Eq`, `TransientDetail` clone/equality, redacted Debug/Display behavior, and no implementation leakage.", + "validation" : "d2b-contracts tests for trait object safety, generation-fenced firewall apply/observe/release signatures (release requires NetworkUid and token), `FirewallGenerationMismatch`, `UsbipEffectError: Clone + PartialEq + Eq`, `TransientDetail` clone/equality, redacted Debug/Display behavior, and no implementation leakage.", "workItemId" : "ADR046-usbip-001" }, { @@ -7147,14 +7147,14 @@ "dataMigration" : "Full d2b 3.0 reset; adapter resumes from Service/Binding status and authority owner proofs rather than daemon-coupled snapshots", "dependencyOwner" : "ADR046-usbip-001, ADR046-zone-control-024; UsbipBindFirewallRule broker op; d2b-host usbip argv support; framework core adapter owner", "destination" : "packages/d2b-core/src/device_usbip_adapter.rs", - "detailedDesign" : "Implement UsbipEffectPort in the core adapter: signed-bundle busid lookup, same-Zone validation, trusted physical-USB identity resolution, mandatory Core-derived `physical-usb-backing/v1` digest and exact `(Host, physical-usb-backing, opaqueKeyDigest)` claim shared with every security-key/USB Provider, exclusive OFD claim, sole ownership of all USBIP TCP/3240 and exact per-Network/per-busid `UsbipBindFirewallRule` effects plus ownership-scoped observe/release, anti-spoof probe, one shared Host module/backend authority, one Core-derived D097 relay Endpoint/firewall authority per Network, D097 authority-index preflight/adoption, and post-effect audit; complete the shared claim before any open, withhold, bind, module, relay, firewall, or attachment effect; never expose raw busid, identity digest, path, fd, bind address, nftables body, audit structs, or broker wire types. Primary reuse disposition: `adapt`. Preserved source-plan detail: extract and adapt into framework-internal adapter.", + "detailedDesign" : "Implement UsbipEffectPort in the core adapter: signed-bundle busid lookup, same-Zone validation, trusted physical-USB identity resolution, mandatory Core-derived `physical-usb-backing/v1` digest and exact `(Host, physical-usb-backing, opaqueKeyDigest)` claim shared with every security-key/USB Provider, exclusive OFD claim, sole ownership of all USBIP TCP/3240 and exact per-Network/per-busid effects through the existing closed `UsbipBindFirewallRule` request with action enum `Ensure|Remove`, ownership-scoped observe/release, anti-spoof probe, one shared Host module/backend authority, one Core-derived D097 relay Endpoint/firewall authority per Network, D097 authority-index preflight/adoption, and post-effect audit. `Ensure` applies; `Remove` uses the same request, binds expected Network/Service generations, validates exact ownership, succeeds idempotently on validated absence, and fails closed on a foreign marker. No separate release op exists. Complete the shared claim before any open, withhold, bind, module, relay, firewall, or attachment effect; never expose raw busid, identity digest, path, fd, bind address, nftables body, audit structs, or broker wire types. Primary reuse disposition: `adapt`. Preserved source-plan detail: extract and adapt into framework-internal adapter.", "integration" : "Reconcile framework injects the adapter into Provider/device-usbip; D097 authority index gates effects; adapter calls privileged broker and d2b-host argv helpers behind the semantic trait.", "removalProof" : "Old daemon-coupled adapter call sites are removed by ADR046-usbip-009 after Provider wiring and adapter tests pass.", "reuseAction" : "adapt", "reuseSource" : null, "specId" : "ADR-046-provider-device-usbip", "specPath" : "docs/specs/providers/ADR-046-provider-device-usbip.md", - "validation" : "Fast packages/d2b-core/tests/device_usbip_adapter.rs covers same-Zone gate, exact shared physical backing tuple derivation, byte-identical keys for USB/security-key views of one fake token, Provider-private-class/digest bypass rejection, `physical-usb-backing-conflict` before effects, separate USBIP module/relay authorities, one-module/one-relay reuse, exact Network/busid firewall scoping, foreign-marker failure, ownership-scoped drift/status, independent per-busid release, no network-local dependency, anti-spoof, redaction, broker mapping, and no digest/busid/path/fd exposure.", + "validation" : "Fast packages/d2b-core/tests/device_usbip_adapter.rs covers same-Zone gate, exact shared physical backing tuple derivation, byte-identical keys for USB/security-key views of one fake token, Provider-private-class/digest bypass rejection, `physical-usb-backing-conflict` before effects, separate USBIP module/relay authorities, one-module/one-relay reuse, exact closed `Ensure|Remove` decoding and same-request broker mapping, unknown-action rejection, expected Network/Service generation binding, exact Network/busid firewall scoping, validated-absence `Remove`, foreign-marker failure without mutation, ownership-scoped drift/status, transient retry with retained token/authority, independent per-busid release, no network-local dependency, anti-spoof, redaction, and no digest/busid/path/fd exposure.", "workItemId" : "ADR046-usbip-002" }, { @@ -7177,14 +7177,14 @@ "dataMigration" : "Full d2b 3.0 reset; no direct import of d2bd usbip_reconcile_state snapshots", "dependencyOwner" : "ADR046-usbip-001, ADR046-usbip-002, ADR046-usbip-003, and ADR046-zone-control-024; device-usbip controller owner", "destination" : "packages/d2b-provider-device-usbip/src/{controller,reconcile,export_import}.rs", - "detailedDesign" : "Reconcile provider-neutral authority/projection `UsbService` and per-Guest `UsbBinding` resources through strict USBIP Provider extensions, consuming UsbipEffectPort, the exact shared Host-global physical USB tuple, USBIP-private D097 authorities, and signed D096 ExportAdapter/ImportAdapter. Enforce same-Zone base and provider refs; ResourceExport authority-Service-only target; same-type ResourceImport-owned projection with `providerRef`, semantic base/import fields, no `spec.provider`, and no physical fields/effects; route selection from the signed local descriptor/import record; semantic factory fingerprint independent of separately authenticated adapter identity; exclusive fair Binding admission; encrypted bounded named-stream control/data; D088 layered status; Service/Binding finalizers; restart adoption; declared/explicit modes; no session/transfer resources. Primary reuse disposition: `adapt`. Preserved source-plan detail: extract and adapt step machine into Provider reconcile loop.", + "detailedDesign" : "Reconcile provider-neutral authority/projection `UsbService` and per-Guest `UsbBinding` resources through strict USBIP Provider extensions, consuming UsbipEffectPort, the exact shared Host-global physical USB tuple, USBIP-private D097 authorities, and signed D096 ExportAdapter/ImportAdapter. Enforce same-Zone base and provider refs; ResourceExport authority-Service-only target; same-type ResourceImport-owned projection with `providerRef`, semantic base/import fields, no `spec.provider`, and no physical fields/effects; route selection from the signed local descriptor/import record; semantic factory fingerprint independent of separately authenticated adapter identity; exclusive fair Binding admission; encrypted bounded named-stream control/data; D088 layered status; Service/Binding finalizers; restart adoption; declared/explicit modes; no session/transfer resources. Firewall reconcile maps apply to `UsbipBindFirewallRule` action `Ensure`; finalization maps release to action `Remove` on the same request with expected Network/Service generations. Finalization retains firewall token/status and relay authority until confirmed effect, retries transient failure, refreshes on generation mismatch, and blocks fail-closed on a foreign marker. Primary reuse disposition: `adapt`. Preserved source-plan detail: extract and adapt step machine into Provider reconcile loop.", "integration" : "Controller watches Device/Host/Network/Guest/Endpoint/Export/Import dependencies, calls injected EffectPorts, commits ResourceMutationBatch updates, coordinates children from ADR046-usbip-005, and delegates only semantic export/import admission to the Provider adapter while core owns D096 routing/lifecycle.", "removalProof" : "packages/d2bd/src/usbip_state_machine.rs and usbip_reconcile_state.rs are deleted by ADR046-usbip-009 once Provider parity tests pass.", "reuseAction" : "adapt", "reuseSource" : null, "specId" : "ADR-046-provider-device-usbip", "specPath" : "docs/specs/providers/ADR-046-provider-device-usbip.md", - "validation" : "Fast tests/controller_state_machine.rs, service_binding_schema.rs, export_import.rs, authority_conflict.rs, async_loop.rs, finalizer.rs, and wrong_zone.rs cover authority/projection/Binding lifecycle, Service-only export, projection `spec.provider` rejection, semantic fingerprint stability under adapter identity changes plus separate descriptor authentication, D088 `status.resource`/`status.provider` placement, shared physical tuple collision before effects, encrypted fake streams, no physical projection effect, exclusivity, restart, and WrongZone degradation.", + "validation" : "Fast tests/controller_state_machine.rs, service_binding_schema.rs, export_import.rs, authority_conflict.rs, async_loop.rs, finalizer.rs, and wrong_zone.rs cover authority/projection/Binding lifecycle, Service-only export, projection `spec.provider` rejection, semantic fingerprint stability under adapter identity changes plus separate descriptor authentication, D088 `status.resource`/`status.provider` placement, shared physical tuple collision before effects, encrypted fake streams, no physical projection effect, exclusivity, restart, WrongZone degradation, exact `Ensure|Remove` mapping, and firewall status/token/authority retention until `Remove` confirmation.", "workItemId" : "ADR046-usbip-004" }, { @@ -7207,14 +7207,14 @@ "dataMigration" : "Full d2b 3.0 reset; current d2bd reconcile state is not imported", "dependencyOwner" : "ADR046-usbip-003; Device provider details plus `UsbService` and `UsbBinding` base/provider status schema owner", "destination" : "packages/d2b-provider-device-usbip/src/status.rs", - "detailedDesign" : "Define provider-neutral `UsbService` `status.resource` with whole-device availability/access counts, authority-only `physical-usb-backing` claim state, and projection-only common import state; define `UsbBinding.status.resource` with generic attachment phase/queue/generation/timestamps. Define separate strict `status.provider` USBIP details: Device probe, owner backend/relay, USBIP-owned `firewallState`/opaque digest/observed generation, imported-route observations, and Binding proxy/private Endpoint/subphase. No common access/import/attachment field appears directly under `status`; no USBIP module, Network, Endpoint, proxy, server/client, port, firewall, or busid field enters base status; no raw backing digest, busid, path, fd, address, session/transfer ID, remote identity, or payload appears anywhere. Primary reuse disposition: `adapt`. Preserved source-plan detail: adapt state fields to typed status.provider.details.", + "detailedDesign" : "Define provider-neutral `UsbService` `status.resource` with whole-device availability/access counts, authority-only `physical-usb-backing` claim state, and projection-only common import state; define `UsbBinding.status.resource` with generic attachment phase/queue/generation/timestamps. Define separate strict `status.provider` USBIP details: Device probe, owner backend/relay, USBIP-owned `firewallState`/opaque digest/observed generation, imported-route observations, and Binding proxy/private Endpoint/subphase. During `UsbipBindFirewallRule` action `Remove`, strict status remains `firewallState: releasing`; the token and relay authority remain held until confirmed success, including validated absence. No common access/import/attachment field appears directly under `status`; no USBIP module, Network, Endpoint, proxy, server/client, port, firewall, or busid field enters base status; no raw backing digest, busid, path, fd, address, session/transfer ID, remote identity, or payload appears anywhere. Primary reuse disposition: `adapt`. Preserved source-plan detail: adapt state fields to typed status.provider.details.", "integration" : "Controller writes each extension atomically with its resource's common status; dependency/update propagation is Device/Export → Service/projection → Binding.", "removalProof" : "Old d2bd USBIP reconcile-state structs are removed by ADR046-usbip-009 after status extension coverage passes.", "reuseAction" : "adapt", "reuseSource" : null, "specId" : "ADR-046-provider-device-usbip", "specPath" : "docs/specs/providers/ADR-046-provider-device-usbip.md", - "validation" : "Fast tests/status_serde.rs covers generic base status plus three strict USBIP detail schemas, exact `status.resource` placement for access/import/attachment/backing claim, exact `status.provider` placement for relay/firewall implementation observations, drift transition and ownership, rejection of semantic fields directly under `status`, mode-dependent omissions, bounded counts/refs, unknown-field denial, and a deny corpus proving USBIP-only fields are rejected from base and Network status.", + "validation" : "Fast tests/status_serde.rs covers generic base status plus three strict USBIP detail schemas, exact `status.resource` placement for access/import/attachment/backing claim, exact `status.provider` placement for relay/firewall implementation observations, drift transition and ownership, `releasing` retention until `Remove` confirmation, rejection of semantic fields directly under `status`, mode-dependent omissions, bounded counts/refs, unknown-field denial, and a deny corpus proving USBIP-only fields are rejected from base and Network status.", "workItemId" : "ADR046-usbip-006" }, { @@ -7222,7 +7222,7 @@ "dataMigration" : "None — docs/tooling only; no runtime state", "dependencyOwner" : "ADR046-usbip-004 and ADR046-usbip-005; device-usbip integration owner", "destination" : "packages/d2b-provider-device-usbip/{src,tests,integration/README.md}; tests/host-integration/usbip-service.nix; tests/host-integration/hardware/usbip-service.sh", - "detailedDesign" : "Put provider-neutral Service/Binding base-schema separation, strict USBIP extensions, projection `spec.provider` rejection, D088 layered status, semantic factory-fingerprint independence from Provider/adapter identity, exact shared physical backing tuple/conflict, one Core-derived per-Network relay Endpoint/firewall authority, exact per-busid firewall ownership/drift/release, arbitration, same-type export/import, encrypted fake-stream, and least-privilege process/Endpoint shape coverage in fast Layer-1 Rust tests. Include a fake direct-local Provider proving the same base contract has no USBIP dependency. Reserve runNixOSTest for real Linux usbip_host/vhci_hcd, usbipd, namespaces/nftables, TCP 3240, zero network-local carve-out, and Guest checks; reserve the hardware script for an approved physical device. Use existing Make gates only. Primary reuse disposition: `adapt`. Preserved source-plan detail: adapt existing network-scoping assertion and add new scenarios.", + "detailedDesign" : "Put provider-neutral Service/Binding base-schema separation, strict USBIP extensions, projection `spec.provider` rejection, D088 layered status, semantic factory-fingerprint independence from Provider/adapter identity, exact shared physical backing tuple/conflict, one Core-derived per-Network relay Endpoint/firewall authority, exact per-busid firewall ownership/drift/release through one `UsbipBindFirewallRule` request with closed `Ensure|Remove`, generation fencing, idempotent validated-absence `Remove`, foreign-marker fail-closed behavior, and status/token/authority retention until confirmation, arbitration, same-type export/import, encrypted fake-stream, and least-privilege process/Endpoint shape coverage in fast Layer-1 Rust tests. Include a fake direct-local Provider proving the same base contract has no USBIP dependency. Reserve runNixOSTest for real Linux usbip_host/vhci_hcd, usbipd, namespaces/nftables, TCP 3240, zero network-local carve-out, and Guest checks; reserve the hardware script for an approved physical device. Use existing Make gates only. Primary reuse disposition: `adapt`. Preserved source-plan detail: adapt existing network-scoping assertion and add new scenarios.", "integration" : "Layer-2 lanes exercise actual kernel/backend/relay/Guest/device paths and do not duplicate pure controller/schema cases. Cross-Zone protocol logic remains hermetic with fake peers; the runNixOSTest only proves its real-system integration.", "removalProof" : "Old usbip_policy_network_scoping coverage is retired only after the fast wrong-Zone admission test and `tests/host-integration/usbip-service.nix` successor both pass and the migration ledger is updated.", "reuseAction" : "adapt", @@ -7237,14 +7237,14 @@ "dataMigration" : "Full d2b 3.0 reset; operators reauthor old per-VM options as Device + authority/projection Service + per-Guest Binding", "dependencyOwner" : "ADR046-usbip-003, ADR046-zone-control-024; ADR-046-nix-configuration; Nix integrator", "destination" : "nixos-modules/components/usbip.nix, nixos-modules/options-zones.nix, nixos-modules/assertions.nix", - "detailedDesign" : "Add Provider config; remove the old per-VM option; emit provider-neutral authority `UsbService`, per-Guest `UsbBinding`, strict USBIP Provider envelopes, and optional ResourceExport/ResourceImport authoring shapes; imported same-type projection Services remain Core-created with no `spec.provider`. Assert same-Zone base/provider refs, projection ownerRef/forbidden implementation and physical fields, Service-only export target, one Host backend/Network relay, exact Core-derived Host-global `(Host, physical-usb-backing, opaqueKeyDigest)` exclusion shared by USB/security-key, Provider-private-class bypass rejection, and retain guest vhci_hcd/tools. Primary reuse disposition: `adapt`. Preserved source-plan detail: adapt guest module, remove host-side option surface, and extend eval assertions.", + "detailedDesign" : "Add Provider config; remove the old per-VM option; emit provider-neutral authority `UsbService`, per-Guest `UsbBinding`, strict USBIP Provider envelopes, and optional ResourceExport/ResourceImport authoring shapes; imported same-type projection Services remain Core-created with no `spec.provider`. Assert same-Zone base/provider refs, projection ownerRef/forbidden implementation and physical fields, Service-only export target, one Host backend/Network relay, exact Core-derived Host-global `(Host, physical-usb-backing, opaqueKeyDigest)` exclusion shared by USB/security-key, Provider-private-class bypass rejection, one `UsbipBindFirewallRule` contract with exact actions `Ensure|Remove` and no separate release operation, and retain guest vhci_hcd/tools. Primary reuse disposition: `adapt`. Preserved source-plan detail: adapt guest module, remove host-side option surface, and extend eval assertions.", "integration" : "Nix compiler emits Device plus provider-neutral `UsbService`/`UsbBinding` and explicit D096 resources consumed by core/Provider; guest runtime supplies USBIP proxy/attach tools; generated base and provider-extension schemas/fingerprints remain canonical.", "removalProof" : "d2b.vms..usbip.yubikey and host-side USBIP module paths are removed at reset once Zone resource emitter coverage passes.", "reuseAction" : "adapt", "reuseSource" : null, "specId" : "ADR-046-provider-device-usbip", "specPath" : "docs/specs/providers/ADR-046-provider-device-usbip.md", - "validation" : "Fast tests/unit/nix/cases/usbip-*.nix cover schema shape, Core projection `spec.provider` rejection, D088 layered status fixtures including USBIP-owned firewall state, all reference/owner/export assertions, byte-identical USB/security-key tuple collision and private-class bypass rejection, one Core-derived 3240 Endpoint/firewall authority per Network, least-privilege consumer policy, absence of network-local 3240 rules, old-option removal, and guest module retention.", + "validation" : "Fast tests/unit/nix/cases/usbip-*.nix cover schema shape, Core projection `spec.provider` rejection, D088 layered status fixtures including USBIP-owned firewall state and `releasing` retention, all reference/owner/export assertions, byte-identical USB/security-key tuple collision and private-class bypass rejection, one Core-derived 3240 Endpoint/firewall authority per Network, exact `Ensure|Remove` broker action surface with unknown values rejected and no second op, least-privilege consumer policy, absence of network-local 3240 rules, old-option removal, and guest module retention.", "workItemId" : "ADR046-usbip-008" }, { @@ -7281,15 +7281,15 @@ "currentSource" : "`d2b-core/src/storage.rs` (`StoragePathSpec`, `StoragePathKind`, policy enums); `d2b-core/src/sync.rs` (`SyncJson`, `LockSpec`)", "dataMigration" : "Full v3 reset; no row-level import", "dependencyOwner" : "ADR046-primitives-001; v3 contracts owner", - "destination" : "`d2b-contracts/src/v3/volume_layout.rs` (LayoutEntry, EntryType, all policy enums, AclGrant, Invariant, SensitivityClass); `d2b-contracts/src/v3/volume_spec.rs` (VolumeSpec, ViewSpec, Attachment, QuotaSpec, SourceKind, `SourcePolicyId` opaque newtype); `d2b-contracts/src/v3/effect_port.rs` (`VolumeEffectPort` trait, opaque ID newtypes `VolumeId`/`LayoutEntryId`/`UserId`/`ViewId` each with custom redacted Debug, and `VolumeMountToken` opaque handle with custom redacted Debug)", - "detailedDesign" : "All LayoutEntry fields as documented in this dossier; enum value names preserved from `StoragePathKind`/policy enums with renames where noted; `User/` ACL principal (no numeric UID); `sourcePolicyId` opaque newtype replaces raw `hostPath` in `SourceKind::LocalPath` and `SourceKind::BlockImage` Primary reuse disposition: `adapt`. Preserved source-plan detail: extract and adapt.", + "destination" : "`d2b-contracts/src/v3/volume_layout.rs` (LayoutEntry, EntryType, all policy enums, AclGrant, Invariant, SensitivityClass); `d2b-contracts/src/v3/volume_spec.rs` (VolumeSpec, ViewSpec, Attachment, QuotaSpec, SourceKind, `SourcePolicyId` opaque newtype); `d2b-contracts/src/v3/effect_port.rs` (`VolumeEffectPort` trait, opaque ID newtypes `VolumeId`/`LayoutEntryId`/`UserId`/`ViewId`/`SealingPolicyId` each with custom redacted Debug, `VolumeMountToken`, and canonical `RotateSealingKeyRequest`/`Result`/`Error` types)", + "detailedDesign" : "All LayoutEntry fields as documented in this dossier; enum value names preserved from `StoragePathKind`/policy enums with renames where noted; `User/` ACL principal (no numeric UID); `sourcePolicyId` opaque newtype replaces raw `hostPath` in `SourceKind::LocalPath` and `SourceKind::BlockImage`; deny-unknown sealing-rotation request contains only opaque Volume/policy/operation IDs and generation/revision preconditions, with no key bytes/path/handle Primary reuse disposition: `adapt`. Preserved source-plan detail: extract and adapt.", "integration" : "Volume spec and status structs; Provider descriptor component stateNamespace; Nix resource compiler schema validation", "removalProof" : "`d2b-core/src/storage.rs` StoragePathSpec/policy enums removed only after all Provider descriptor consumers are on v3 Volume spec", "reuseAction" : "adapt", "reuseSource" : null, "specId" : "ADR-046-provider-volume-local", "specPath" : "docs/specs/providers/ADR-046-provider-volume-local.md", - "validation" : "Schema golden vectors; round-trip serde; ACL principal validation rejects numeric forms; `sourcePolicyId` present; no `hostPath` field in any volume_spec contract", + "validation" : "Schema golden vectors; round-trip serde; ACL principal validation rejects numeric forms; `sourcePolicyId` present; no `hostPath` field in any volume_spec contract; sealing-rotation request deny-unknown/round-trip and redacted-Debug tests; compile-time trait conformance includes `rotate_sealing_key`", "workItemId" : "ADR046-vl-001" }, { @@ -7371,15 +7371,15 @@ "currentSource" : "`d2b-state/src/atomic.rs` (main); no existing migration/snapshot infrastructure in v3", "dataMigration" : "None (new protocol)", "dependencyOwner" : "ADR046-vl-002; ADR046-vl-003; ADR046-pstate-004 through ADR046-pstate-006", - "destination" : "`src/{migration,snapshot}.rs`; `tests/{migration_unit,snapshot_unit}.rs`; `integration/{migration,snapshot}.rs`", - "detailedDesign" : "Schema migration (see §Schema migration) via `volume-migration-worker` EphemeralProcess; Snapshot create/list/expire (see §Snapshots) via `volume-snapshot-worker` EphemeralProcess; controller reports `stateSchemaPhase` and `snapshots` in Volume status. Sealing lifecycle (`sealingStatus`, key-shred) is a core/framework concern dispatched through `VolumeEffectPort` semantic ops, not a Provider-owned EphemeralProcess worker.", - "integration" : "Controller's reconcile handler dispatches EphemeralProcess via d2b-bus `ResourceClient`; volume-local reports `stateSchemaPhase`, `snapshots` in Volume status", + "destination" : "`src/{migration,snapshot,sealing}.rs`; `tests/{migration_unit,snapshot_unit,sealing_unit}.rs`; `integration/{migration,snapshot,sealing}.rs`", + "detailedDesign" : "Schema migration (see §Schema migration) via `volume-migration-worker` EphemeralProcess; Snapshot create/list/expire (see §Snapshots) via `volume-snapshot-worker` EphemeralProcess; controller reports `stateSchemaPhase` and `snapshots` in Volume status. Sealing lifecycle uses the status-first `rotation-pending` transition and only canonical `VolumeEffectPort::rotate_sealing_key`; it persists/resumes the core Operation-ledger fingerprint, classifies exact typed errors, and has no key lease, direct rewrite, generic broker call, or Provider-owned EphemeralProcess worker.", + "integration" : "Controller's reconcile handler dispatches migration/snapshot EphemeralProcess via d2b-bus `ResourceClient`; sealing reconcile/upgrade commits status before effect and maps typed results to `sealed`/`rotation-failed`; volume-local reports state schema, snapshots, and safe sealing generations in Volume status", "removalProof" : "Not applicable (new)", "reuseAction" : "adapt", "reuseSource" : null, "specId" : "ADR-046-provider-volume-local", "specPath" : "docs/specs/providers/ADR-046-provider-volume-local.md", - "validation" : "All `tests/migration_unit.rs`, `tests/snapshot_unit.rs`, `integration/migration.rs`, `integration/snapshot.rs` scenarios", + "validation" : "All `tests/migration_unit.rs`, `tests/snapshot_unit.rs`, `tests/sealing_unit.rs`, `integration/migration.rs`, `integration/snapshot.rs`, and `integration/sealing.rs` scenarios, including restart/idempotency and status-before-effect assertions", "workItemId" : "ADR046-vl-007" }, { @@ -7402,14 +7402,14 @@ "dataMigration" : "None — full d2b 3.0 reset; no prior state to migrate", "dependencyOwner" : "ADR046-vl-001; Zone audit stream owner; `observability-otel` Provider owner", "destination" : "`src/audit.rs`; `src/otel.rs`; `src/error.rs`; `tests/audit_unit.rs`; `integration/audit.rs`", - "detailedDesign" : "Event types and Zone audit emission per §Audit events; OTEL metric definitions per §OTEL metrics with closed semantic labels and no Zone/resource-name-derived dimensions; Zone identity remains in `d2b.zone` resource attributes; error catalog per §Error catalog; no-path invariant enforced in all outputs", + "detailedDesign" : "Event types and Zone audit emission per §Audit events, including controller rotation start/failure/commit and exactly-once broker `RotateSealingKey` success; OTEL metric definitions per §OTEL metrics with closed semantic labels and no Zone/resource-name-derived dimensions; Zone identity remains in `d2b.zone` resource attributes; error catalog per §Error catalog; no-path/no-key invariant enforced in all outputs", "integration" : "Every lifecycle transition calls `audit::emit_volume_event`; OTEL metrics exported via `observability-otel` Provider", "removalProof" : "Not applicable", "reuseAction" : "adapt", "reuseSource" : null, "specId" : "ADR-046-provider-volume-local", "specPath" : "docs/specs/providers/ADR-046-provider-volume-local.md", - "validation" : "`tests/audit_unit.rs` golden records and structural metric descriptor assertions for exact absence of `vm`, `zone`, `zone_id`, `zone_uid`, and resource-name-derived keys plus resource-name canary absence; `tests/error_messages.rs` bounded messages; `integration/audit.rs` live stream", + "validation" : "`tests/audit_unit.rs` golden records and structural metric descriptor assertions for exact absence of `vm`, `zone`, `zone_id`, `zone_uid`, and resource-name-derived keys plus resource-name canary absence; rotation audit exact-once/digest-only vectors; `tests/error_messages.rs` bounded messages; `integration/audit.rs` live stream", "workItemId" : "ADR046-vl-009" }, { @@ -7446,15 +7446,15 @@ "currentSource" : "`d2b-priv-broker/src/ops/{state_dir,storage_contract,swtpm_dir,store_sync,store_view_posture}.rs`; `d2b-host/src/hardlink_farm.rs`", "dataMigration" : "None (adapter replaces direct broker-op call sites)", "dependencyOwner" : "ADR046-vl-001; ADR046-vl-002; Zone broker/core owner", - "destination" : "`packages/d2b-host/src/volume_effect_adapter.rs` (or the equivalent host-runtime crate designated by the Zone broker owner); implements the `VolumeEffectPort` trait defined in `d2b-contracts`", - "detailedDesign" : "Adapter holds trusted FD table keyed by `VolumeId`; resolves `SourcePolicyId` to host path prefix from private bundle; calls `openat2(RESOLVE_BENEATH)` anchored at retained FD for all FS ops; calls `setfacl`/`acl_set_fd`, `mount`/`umount`, `fallocate` from within adapter only; emits path-free audit records for each op (audit is never atomic with redb write); injected into controller via Zone runtime ComponentSession; blocking filesystem calls run in bounded blocking-thread pool Primary reuse disposition: `adapt`. Preserved source-plan detail: adapt into adapter.", + "destination" : "`packages/d2b-host/src/volume_effect_adapter.rs` (or the equivalent host-runtime crate designated by the Zone broker owner), implementing the `VolumeEffectPort` trait defined in `d2b-contracts`; planned `d2b-priv-broker/src/ops/rotate_sealing_key.rs` closed operation", + "detailedDesign" : "Adapter holds trusted FD table keyed by `VolumeId`; resolves `SourcePolicyId` to host path prefix from private bundle; calls `openat2(RESOLVE_BENEATH)` anchored at retained FD for all FS ops; calls `setfacl`/`acl_set_fd`, `mount`/`umount`, `fallocate` from within adapter only; authorizes `volume.rotate-sealing-key`, verifies committed proof, recomputes the canonical idempotency key, and maps `rotate_sealing_key` one-to-one to the closed broker `RotateSealingKey` operation. Broker independently resolves opaque `VolumeId`/`SealingPolicyId`, checks policy/generation/preconditions, performs journaled atomic rewrap and roll-forward recovery, and durably emits exactly one success audit before returning. Neither boundary accepts key bytes, credential bytes, key handles, or paths. Other blocking filesystem calls run in the bounded blocking-thread pool Primary reuse disposition: `adapt`. Preserved source-plan detail: adapt into adapter.", "integration" : "Zone runtime creates adapter with required FD table and bundle reference at provider startup; passes `Arc` to controller via ComponentSession bootstrap", "removalProof" : "Baseline broker op handlers (`state_dir.rs`, `storage_contract.rs`, `swtpm_dir.rs`, `store_sync.rs`, `store_view_posture.rs`) retired only after Volume controller parity is confirmed and all callers are on the adapter", "reuseAction" : "adapt", "reuseSource" : null, "specId" : "ADR-046-provider-volume-local", "specPath" : "docs/specs/providers/ADR-046-provider-volume-local.md", - "validation" : "Adapter hermetic tests: each effect op called with mock FD table and bundle; no path in any output; anchored-path rejection for RESOLVE_BENEATH violations; `cargo deny check` verifies adapter does not expose raw paths to Provider crate; `integration/provision.rs` exercises full adapter path", + "validation" : "Adapter hermetic tests: each effect op called with mock FD table and bundle; rotation authorization, policy binding, all generation/revision preconditions, canonical idempotency vectors, byte-identical duplicate/retry, different-payload conflict, typed retry classification, and no key/path/handle in wire/Debug/error/audit; broker crash injection at every journal boundary with old-or-target visibility, roll-forward, and exactly-once success audit; anchored-path rejection for RESOLVE_BENEATH violations; `cargo deny check` verifies adapter exposes neither raw paths nor broker implementation to Provider crate; `integration/{provision,sealing}.rs` exercise full adapter paths", "workItemId" : "ADR046-vl-012" }, { @@ -7568,7 +7568,7 @@ "dependencyOwner" : "Title: Implement `VsockEffectPort` trait and `OpaqueEndpointId`/`OpaqueBindingId` newtypes; Phase 1; Priority P0; Depends on ADR046-bus-001 (OwnedTransport in d2b-session); Owner crate `d2b-provider-transport-vsock`.", "destination" : "`packages/d2b-provider-transport-vsock/src/effect_port.rs`; test fake in `tests/effect_port_mock.rs`; redaction checks in `tests/redaction.rs`.", "detailedDesign" : "Implement `VsockEffectPort` trait and `OpaqueEndpointId`/`OpaqueBindingId` newtypes. Define `VsockEffectPort` async trait and opaque ID newtypes in `effect_port.rs`; implement `FakeVsockEffectPort` for tests; `redaction.rs` asserts no raw `u32` in any `Debug`/`Display` output of opaque types; no real vsock socket opened. Primary reuse disposition: `create`. Preserved source-plan detail: net-new trait/newtypes with redaction tests; no real vsock socket opened.", - "integration" : "Core ZoneLink/delegation controller calls the Provider service with opaque IDs; Provider calls injected `VsockEffectPort`; live AF_VSOCK resolution remains in core runtime, not the Provider crate.", + "integration" : "Child Zone's core ZoneLink/delegation controller calls its same-Zone Provider with opaque IDs; Provider calls injected `VsockEffectPort`; live AF_VSOCK child-endpoint resolution remains in child core runtime while the selected parent retains only sealed peer/route state.", "removalProof" : "None — net-new; no prior owner to remove.", "reuseAction" : "create", "reuseSource" : null, @@ -7597,38 +7597,38 @@ "dataMigration" : "Full d2b 3.0 reset; no v2 state/config import.", "dependencyOwner" : "Title: Implement `VsockTransportService` (OpenTransport/CloseTransport/ObserveTransport); Phase 1; Priority P0; Depends on ADR046-vsock-002 and ADR046-bus-001; Owner crate `d2b-provider-transport-vsock`.", "destination" : "`packages/d2b-provider-transport-vsock/src/service.rs`; tests `tests/open_close.rs`, `tests/observe.rs`, and conformance kit.", - "detailedDesign" : "Implement `VsockTransportService` (OpenTransport/CloseTransport/ObserveTransport). Implement all three service methods in `service.rs`; `open_close.rs` and `observe.rs` test full service API against `FakeVsockEffectPort`; conformance kit passes. Primary reuse disposition: `adapt`. Preserved source-plan detail: net-new service implementation over ComponentSession and fake effect port tests.", - "integration" : "Core ZoneLink/delegation controller is the only authorized caller; service opens named stream handles for d2b-bus, releases them on close, and streams transport events for observe.", + "detailedDesign" : "Implement `VsockTransportService` (OpenTransport/CloseTransport/ObserveTransport). Child core resolves the exact six-field ZoneLink spec, validates `transportSettings` through the Provider selected by `transportProviderRef`, rejects non-empty `transportCredentials`, and passes only derived opaque IDs/deadline to `service.rs`; `open_close.rs`, `observe.rs`, and topology tests cover the service and reject legacy ZoneLink provider/fingerprint/capability fields; conformance kit passes. Primary reuse disposition: `adapt`. Preserved source-plan detail: net-new service implementation over ComponentSession and fake effect port tests.", + "integration" : "Child Zone's core ZoneLink/delegation controller is the only authorized caller; service opens named stream handles for child d2b-bus, releases them on close, and streams transport events for observe; no parent-side Provider or handler exists.", "removalProof" : "None — net-new; no prior owner to remove.", "reuseAction" : "adapt", "reuseSource" : null, "specId" : "ADR-046-provider-transport-vsock", "specPath" : "docs/specs/providers/ADR-046-provider-transport-vsock.md", - "validation" : "Proof type: service round-trip test (mock); `tests/open_close.rs`, `tests/observe.rs`, and provider conformance tests.", + "validation" : "Proof type: service round-trip plus exact-shape tests; `tests/open_close.rs`, `tests/observe.rs`, `tests/topology.rs::{canonical_zonelink_spec_fields_are_exact,legacy_zonelink_provider_fields_are_rejected,transport_credentials_must_be_empty}`, and provider conformance tests.", "workItemId" : "ADR046-vsock-003" }, { "currentSource" : "Evidence class `ADR-only`; baseline has guest-control and relay vsock paths, but no allocator-backed `LiveVsockEffectPort` for ZoneLink transport.", "dataMigration" : "Full d2b 3.0 reset; no v2 state/config import.", - "dependencyOwner" : "Title: Implement `LiveVsockEffectPort` in Zone runtime; Phase 2; Priority P0; Depends on ADR046-vsock-001 and the Zone allocator (`ADR-046-resources-zone-control`); Owner crate `d2b-core-controller`.", - "destination" : "`d2b-core-controller` Zone runtime `LiveVsockEffectPort`; Provider receives it by dependency injection at startup.", - "detailedDesign" : "Implement `LiveVsockEffectPort` in Zone runtime. Zone runtime provides `LiveVsockEffectPort` backed by core allocator state; resolves `OpaqueEndpointId` → CID and `OpaqueBindingId` → port; opens AF_VSOCK socket; injects into Provider service at startup; no raw CID/port exposed to Provider. Primary reuse disposition: `adapt`. Preserved source-plan detail: net-new core adapter; keep raw AF_VSOCK syscalls outside Provider crate.", - "integration" : "Zone allocator issues endpoint/binding IDs; core runtime resolves them, opens/accepts AF_VSOCK sockets, returns opaque streams to Provider service, and excludes reserved ports.", + "dependencyOwner" : "Title: Implement `LiveVsockEffectPort` in child Zone runtime; Phase 2; Priority P0; Depends on ADR046-vsock-001 and the Zone allocator (`ADR-046-resources-zone-control`); Owner crate `d2b-core-controller`.", + "destination" : "`d2b-core-controller` child Zone runtime `LiveVsockEffectPort`; child-local Provider receives it by dependency injection at startup.", + "detailedDesign" : "Implement `LiveVsockEffectPort` in the child Zone runtime. It consumes the child's sealed binding for the allocator selected by compiler-only `parentZone`, resolves opaque endpoint/binding IDs only inside the effect adapter, opens the child AF_VSOCK endpoint, and injects an opaque stream into the same-Zone Provider service; the selected parent keeps its peer binding and port registry only as sealed allocator/route state; no raw CID/port is exposed to the Provider and no FD or ResourceRef crosses Zones. Primary reuse disposition: `adapt`. Preserved source-plan detail: net-new core adapter; keep raw AF_VSOCK syscalls outside Provider crate.", + "integration" : "Selected parent allocator issues a sealed endpoint/binding allocation without creating resources; child core resolves its local side, opens/accepts the AF_VSOCK endpoint, returns an opaque stream to its Provider service, and excludes reserved ports.", "removalProof" : "None — net-new core adapter; no prior owner to remove.", "reuseAction" : "adapt", "reuseSource" : null, "specId" : "ADR-046-provider-transport-vsock", "specPath" : "docs/specs/providers/ADR-046-provider-transport-vsock.md", - "validation" : "Proof type: integration test; `integration/host_guest.rs` exercises live open/close byte round-trip with the injected effect.", + "validation" : "Proof type: integration test; `integration/host_guest.rs` exercises live open/close byte round-trip with the injected effect and proves the selected parent has only sealed allocator/route state, with no parent-store Provider/ZoneLink row.", "workItemId" : "ADR046-vsock-004" }, { "currentSource" : "Evidence class `test-only-or-preview`; no operator-authored v3 state Volume exists for transport-vsock in baseline.", "dataMigration" : "Full d2b 3.0 reset; no v2 state/config import; state Volume is created fresh with `migrationPolicy: none`.", - "dependencyOwner" : "Title: Core ProviderDeployment creates/deletes service component state Volume; Phase 1; Priority P0; Depends on the volume-local Provider (`ADR-046-provider-volume-local`); Owner crate `d2b-provider-transport-vsock`.", + "dependencyOwner" : "Title: Child core ProviderDeployment creates/deletes service component state Volume; Phase 1; Priority P0; Depends on the volume-local Provider (`ADR-046-provider-volume-local`); Owner crate `d2b-provider-transport-vsock`.", "destination" : "ProviderDeployment Volume creation/deletion path plus `packages/d2b-provider-transport-vsock/tests/state_volume.rs`.", - "detailedDesign" : "Core ProviderDeployment creates/deletes service component state Volume. Core ProviderDeployment creates `Volume/transport-vsock--service--empty-state--*` before the component Process and deletes it after the Process finalizer; transport-vsock Provider controller does not own Volume, does not add Volume to exported ResourceTypes, and does not create its prerequisite; Volume spec: empty schema, `kind: state`, `persistenceClass: persistent`, `migrationPolicy: none`, `User/d2b-transport-vsock` owner, minimal nonzero `quota.maxBytes`/`quota.maxInodes` with `enforcement: hard`, `private` sensitivity, `broker-maintained` identity marker; `state_volume.rs` test verifies Volume spec fields against canonical schema; integration test verifies marker written at install and removed at Provider deletion; no operator-authored Volume; component receives dirfd view only. Primary reuse disposition: `create`. Preserved source-plan detail: net-new ProviderDeployment/Volume integration and tests.", - "integration" : "Core ProviderDeployment creates Volume before Process, volume-local reconciles it, Provider process receives only a dirfd view, and Provider deletion removes the Process before deleting the Volume/identity marker.", + "detailedDesign" : "Child core ProviderDeployment creates/deletes the service component state Volume in the same child Zone. It creates `Volume/transport-vsock--service--empty-state--*` before the component Process and deletes it after the Process finalizer; the transport-vsock service does not own Volume, add Volume to exported ResourceTypes, or create its prerequisite; Volume spec: empty schema, `kind: state`, `persistenceClass: persistent`, `migrationPolicy: none`, `User/d2b-transport-vsock` owner, minimal nonzero `quota.maxBytes`/`quota.maxInodes` with `enforcement: hard`, `private` sensitivity, `broker-maintained` identity marker; `state_volume.rs` tests the canonical schema; installation/removal tests verify marker lifecycle; no operator-authored or parent-store Volume exists; component receives only its child-local dirfd view. Primary reuse disposition: `create`. Preserved source-plan detail: net-new ProviderDeployment/Volume integration and tests.", + "integration" : "Child core ProviderDeployment creates the Volume before Process, volume-local reconciles it, the Provider process receives only a child-local dirfd view, and Provider deletion removes Process before Volume/identity marker; parent state remains allocator/route-only.", "removalProof" : "Remove the state Volume and its broker-maintained identity marker during Provider deletion; no operator-authored Volume remains.", "reuseAction" : "create", "reuseSource" : null, @@ -7642,8 +7642,8 @@ "dataMigration" : "None — docs/tooling only; no runtime state.", "dependencyOwner" : "Title: Integration test: real vsock socketpair + full ZoneLink open/close; Phase 2; Priority P1; Depends on ADR046-vsock-003 and ADR046-vsock-004; Owner crate `d2b-provider-transport-vsock`.", "destination" : "`packages/d2b-provider-transport-vsock/integration/host_guest.rs` and `integration/no_fd_transfer.rs`.", - "detailedDesign" : "Integration test: real vsock socketpair + full ZoneLink open/close. `integration/host_guest.rs`: real vsock socketpair (Linux); `OpenTransport` + byte round-trip + `CloseTransport`; validates bridge throughput ≥ 512 MiB/s; `no_fd_transfer.rs`: structural rejection of attachment packets over vsock transport. Primary reuse disposition: `create`. Preserved source-plan detail: net-new integration coverage with no FD transfer over vsock.", - "integration" : "Test drives Provider service, LiveVsockEffectPort, d2b-bus `OwnedTransport`, byte bridge, close path, and attachment rejection across the integration lane.", + "detailedDesign" : "Integration test: fixture declares compiler-only `k1.parentZone = \"local-root\"`, puts the selected Provider and an exact six-field ZoneLink (`transportProviderRef`, validated `transportSettings`, empty `transportCredentials`, `disabled`, and `limits`, plus self-matching `childZoneName`) only in K1, and gives local-root only sealed allocator/route state; `integration/host_guest.rs` opens a real Linux vsock path through K1's `LiveVsockEffectPort`, then exercises `OpenTransport` + byte round-trip + `CloseTransport` and validates bridge throughput ≥ 512 MiB/s; `no_fd_transfer.rs` structurally rejects attachment packets and asserts no FD/ResourceRef crosses the Zone boundary. Primary reuse disposition: `create`. Preserved source-plan detail: net-new integration coverage with no FD transfer over vsock.", + "integration" : "Test drives K1-local Provider service, child `LiveVsockEffectPort`, d2b-bus `OwnedTransport`, byte bridge, close path, absent local-root reciprocal resource row, and attachment rejection across the integration lane.", "removalProof" : "None — test coverage net-new; old duplicate vsock tests are retired only after successor assertions migrate.", "reuseAction" : "create", "reuseSource" : null, @@ -7916,7 +7916,7 @@ "integration" : "Resource API admission gate (`packages/d2b-resource-api/src/quota_gate.rs`) called for every `create` verb; Zone controller triggers quota reconcile on resource-created/deleted/quotaRef-changed events; quota handler registered in core-controller process", "removalProof" : "Additive; no existing code removed", "reuseAction" : "create", - "reuseSource" : "None from main; Quota is a new ResourceType with no main-branch analog", + "reuseSource" : null, "specId" : "ADR-046-resources-zone-control", "specPath" : "docs/specs/ADR-046-resources-zone-control.md", "validation" : "`quota-ceiling-hard-reject`, `quota-ceiling-soft-warn`, `quota-ceiling-pertype`, `quota-drain-blocks-on-dependents`, `quota-over-quota-status`, `quota-nix-eval-bounds`, `quota-nix-build-pertype-unknown-type`", @@ -7931,7 +7931,7 @@ "integration" : "API admission gate checks union of enabled EmergencyPolicies before every admitted request; ZoneLink handler subscribes to EmergencyPolicy watch triggers; Provider process lifecycle listens for effective `stopProviderProcesses` and resumes launch on deactivation", "removalProof" : "Replaces the inline `emergencyDisable` field from the proposed Zone.spec option B; that option was not implemented", "reuseAction" : "create", - "reuseSource" : "None from main; EmergencyPolicy is a new ResourceType", + "reuseSource" : null, "specId" : "ADR-046-resources-zone-control", "specPath" : "docs/specs/ADR-046-resources-zone-control.md", "validation" : "`emergency-policy-activates-gate`, `emergency-policy-disconnects-zonelinks`, `emergency-policy-union-most-restrictive`, `emergency-policy-multi-enabled-combined-scope`, `emergency-policy-stop-processes-no-delete`, `emergency-policy-deactivation-restores-gate`, `emergency-policy-drain-finalizer`, `emergency-nix-eval-drain-deadline-bound-tightest`", @@ -7991,7 +7991,7 @@ "integration" : "ADR046-routing-011 supplies the one canonical 19-type registry and generated option family; validated `parentZone` feeds the allocator bootstrap sealer; the closed `d2b.zones..resources.*` tree is consumed by ADR046-zone-control-015; the Zone controller (ADR046-zone-control-001) reads the resulting bundle; Provider package conventions come from ADR046-zone-control-003", "removalProof" : "`nixos-modules/options-realms.nix`, `nixos-modules/realm-controller-config-json.nix`, `nixos-modules/realm-identity-config-json.nix` deleted after Zone controller and resource compiler reach full parity; `nixos-modules/assertions.nix` lines referencing `allowUnsafeLocal`/realm names removed after Host admission validation replaces them", "reuseAction" : "create", - "reuseSource" : "None from main; eval-time validation is Nix-native", + "reuseSource" : null, "specId" : "ADR-046-resources-zone-control", "specPath" : "docs/specs/ADR-046-resources-zone-control.md", "validation" : "All Phase 1 eval tests in §15.8 (`nix-eval-name-regex-enforced`, `nix-eval-verb-closed-enum`, `nix-eval-roleref-format`, `nix-eval-subject-type-restricted`, `nix-eval-no-duplicate-subjects`, `nix-eval-bootstrap-provider-rejected`, `nix-eval-provider-missing-artifact-id`, `nix-eval-artifact-id-not-in-catalog`, `nix-eval-artifact-wrong-type`, `nix-eval-artifact-id-format`, `nix-eval-credentialref-declared`, `nix-eval-dollar-key-rejected`, all five `nix-eval-parent-zone-*` vectors, `nix-eval-zonelink-child-name-mismatch-rejected`, `nix-eval-zonelink-second-uplink-rejected`, `nix-eval-zonelink-limits-maxpendingintents-bound`); drift test asserts the standard registry and generated option modules cover exactly all 19 canonical types; negative evals reject unknown strings, unqualified extensions, unsigned or uninstalled qualified types, and unknown `spec` fields; a positive fixture admits an installed signed qualified type and validates its strict generated schema", @@ -8006,7 +8006,7 @@ "integration" : "Reads from `d2b.zones..resources.*` (ADR046-zone-control-014); emits bundle consumed by ADR046-zone-control-001 configuration publication handler; generation counter stored as Nix module derivation input hash (hermetic) or in a NixOS state file (impure) — exact mechanism is implementation decision", "removalProof" : "No current equivalent; additive only; no prior code removed", "reuseAction" : "create", - "reuseSource" : "None from main; the resource compiler is a new pure-Rust Nix derivation", + "reuseSource" : null, "specId" : "ADR-046-resources-zone-control", "specPath" : "docs/specs/ADR-046-resources-zone-control.md", "validation" : "All Phase 2 build tests in §15.8 (`nix-build-artifact-id-missing-from-catalog`, `nix-build-artifact-wrong-type-rejected`, `nix-build-duplicate-artifact-id`, `nix-build-artifact-store-path-absent-from-bundle`, `nix-build-artifact-store-path-absent-from-config`, `nix-build-config-schema-failure`, `nix-build-schema-digest-mismatch`, `nix-build-manifest-digest-mismatch`, `nix-build-resourcetype-collision`, `nix-build-bundle-sorted`, `nix-build-bundle-digest-stable`, `nix-build-per-resource-digest-correct`, `nix-build-credential-ref-survives-build`, `nix-build-inline-secret-lint-warning`, `nix-build-inline-secret-strict-failure`)", @@ -8073,7 +8073,7 @@ "workItemId" : "ADR046-zone-control-019" }, { - "currentSource" : "None — net-new ADR 0046 cross-Zone sharing model (D096)", + "currentSource" : "None — net-new ADR 0046 cross-Zone sharing model (D096); the core owner/child reconcile machinery (§11) is prior-art design context only", "dataMigration" : "None — full d2b 3.0 reset", "dependencyOwner" : "ADR046-zone-control-019; `d2b-core-controller` owner", "destination" : "`packages/d2b-core-controller/src/export_import_projection.rs` (local qualified Service projection lifecycle owned by `ResourceImport`)", @@ -8081,7 +8081,7 @@ "integration" : "ADR046-zone-control-019 controller; owner/dependency reconcile (§11, ADR046-reconcile-*); local semantic Provider import adapter", "removalProof" : "Not applicable (new surface)", "reuseAction" : "create", - "reuseSource" : "None from main; projection ownership reuses the core owner/child reconcile machinery (§11)", + "reuseSource" : null, "specId" : "ADR-046-resources-zone-control", "specPath" : "docs/specs/ADR-046-resources-zone-control.md", "validation" : "Exactly one same-type Service projection owned by import; no Device/Endpoint/Binding projection; Binding never auto-created/deleted; Binding target allowlist; intent-only spec/status-only observations; owned Process/Endpoint child cleanup; pending finalizer while Binding refs remain; reconnect only after generation/factory/schema revalidation; hermetic fake-adapter + real-stream integration tiers", diff --git a/docs/specs/providers/ADR-046-provider-audio-pipewire.md b/docs/specs/providers/ADR-046-provider-audio-pipewire.md index 6635d3b92..ff15a326c 100644 --- a/docs/specs/providers/ADR-046-provider-audio-pipewire.md +++ b/docs/specs/providers/ADR-046-provider-audio-pipewire.md @@ -2830,7 +2830,7 @@ remote lease are released. | Work item ID | `ADR046-audio-007` | | Dependency/owner | Depends on `ADR046-audio-004`; ComponentSession service (ADR-046-componentsession-and-bus); libpipewire 1.x | | Current source | `packages/d2bd/src/audio_host_controller.rs` PipeWire session access patterns (reference only) | -| Reuse source | None; new component | +| Reuse source | None | | Reuse action | create | | Destination | `packages/d2b-provider-audio-pipewire/src/mediator/mod.rs`; `src/bin/audio_pipewire_mediator.rs` | | Detailed design | Owner AudioService implementation only. Long-lived user-session Process maintains per-AudioBinding nodes under the single owner backing, receives the pre-opened local PipeWire portal FD, and exposes `SetGrant`/`SetLevel` through `Endpoint/audio-pipewire-authority`. It enforces the authority arbiter's single microphone slot and mute-before-handoff result while speaker nodes remain mixed. Projection Services never start/call a local mediator and cannot receive its FD. No EphemeralProcess, wpctl, remote Ref, or node identity in external surfaces. | diff --git a/docs/specs/providers/ADR-046-provider-device-gpu.md b/docs/specs/providers/ADR-046-provider-device-gpu.md index ac63e0243..3f4711996 100644 --- a/docs/specs/providers/ADR-046-provider-device-gpu.md +++ b/docs/specs/providers/ADR-046-provider-device-gpu.md @@ -1664,7 +1664,7 @@ disposition contract test passes. | Work item ID | `ADR046-gpu-003` | | Dependency/owner | ADR046-gpu-002 | | Current source | `nixos-modules/assertions.nix` x86_64-linux guard; `packages/d2b-core/src/processes.rs` ProcessRole::Gpu/GpuRenderNode; no existing sysfs probe module | -| Reuse source | None; probe is `ADR-only` | +| Reuse source | None | | Reuse action | create | | Destination | `packages/d2b-provider-device-gpu/src/probe.rs` | | Detailed design | Call `GpuEffectPort::probe_drm_device(selector)` on each `scheduled-observe` trigger; the effect port resolves device presence against the trusted device table and returns a presence/health result without exposing raw sysfs or device paths to the controller. Three-strike failure counter; `observe_interval_secs` (10–60, default 30); emit `DevicePresent` condition and update `lastProbedAt`. | diff --git a/docs/specs/providers/ADR-046-provider-transport-unix.md b/docs/specs/providers/ADR-046-provider-transport-unix.md index 7640191c7..10b1b7812 100644 --- a/docs/specs/providers/ADR-046-provider-transport-unix.md +++ b/docs/specs/providers/ADR-046-provider-transport-unix.md @@ -1599,7 +1599,7 @@ Old and new suites never run in parallel indefinitely. | Work item ID | `ADR046-transport-unix-009` | | Dependency/owner | ADR046-transport-unix-001; Nix/build integrator | | Current source | `nixos-modules/options-realms.nix` realm options (v3 baseline); `nixos-modules/assertions.nix` | -| Reuse source | None; new schema file | +| Reuse source | None | | Reuse action | create | | Destination | `docs/reference/schemas/v3/providers/transport-unix.transport-binding.json`; `nixos-modules/assertions.nix` (assertion additions); generated `nixos-modules/generated/options-zones-ZoneLink.nix` `transportSettings` submodule | | Detailed design | Commit the JSON Schema; run `xtask gen-zone-schemas` and `xtask gen-zone-nix-options` to regenerate committed files; generate the exact six-field ZoneLink base; reject legacy provider envelopes and allocator-private fingerprint/capability fields; add assertions for stream+attachments conflict and sensitive key names; require an empty `transportCredentials` list; reuse the common topology assertions to require the ZoneLink and selected Provider in the same child Zone, self-matching `childZoneName`, compiler-only non-root `parentZone`, and no reciprocal parent resource; `xtask gen-zone-resources` adds Provider-selected `transportSettings` validation | @@ -1617,7 +1617,7 @@ Old and new suites never run in parallel indefinitely. | Work item ID | `ADR046-transport-unix-010` | | Dependency/owner | ADR046-transport-unix-007; ADR-046-telemetry-audit-and-support | | Current source | v3 baseline `tracing` crate patterns; v3 `d2b-realm-router/src/service_v2.rs` audit field shapes | -| Reuse source | None; new per v3 telemetry separation invariant | +| Reuse source | None | | Reuse action | create | | Destination | `packages/d2b-provider-transport-unix/src/{audit,metrics}.rs` | | Detailed design | `AuditRecordKind` enum with 6 event kinds from Audit section; `AuditRecord` carries only the fields listed (no uid/gid/pid/path/handle/ZoneLink name); emit via Zone runtime `emit_audit_record()` interface; `MetricCounter`/`MetricHistogram` with closed label types per Metrics section; emit via bounded in-process ring to OTEL Provider datagram socket; `tracing::instrument` spans on `PortalHandler` methods with the 3 permitted span attributes only | From bafaea818e558a714390a78792449286d08482b1 Mon Sep 17 00:00:00 2001 From: John Vicondoa Date: Fri, 24 Jul 2026 09:58:34 -0700 Subject: [PATCH 100/115] docs: align zone routing with canonical ZoneLink --- docs/specs/ADR-046-implementation-graph.json | 24 +- docs/specs/ADR-046-spec-set.json | 6 +- docs/specs/ADR-046-work-items.json | 24 +- docs/specs/ADR-046-zone-routing.md | 459 ++++++++---------- .../ADR-046-provider-transport-azure-relay.md | 40 +- 5 files changed, 246 insertions(+), 307 deletions(-) diff --git a/docs/specs/ADR-046-implementation-graph.json b/docs/specs/ADR-046-implementation-graph.json index 07581c677..b2cd49764 100644 --- a/docs/specs/ADR-046-implementation-graph.json +++ b/docs/specs/ADR-046-implementation-graph.json @@ -19497,7 +19497,7 @@ "destinations" : [ "`packages/d2b-contracts/src/v3/zone_routing.rs`" ], - "detailedDesign" : "Rename RealmPath → ZonePath, RealmId → ZoneLabelId, RouteId → ZoneRouteId, ControllerGenerationId → ZoneLinkControllerGeneration; preserve all bounds/validation/serde; add ZoneLink-specific advertisement envelope fields (v3 schema version, capability ceiling field); preserve `RouteFailClosedReason` + add `zone-link-disconnected`, `hop-limit-exceeded`, `relay-denied`, `attachment-not-permitted-over-zone-link`; freeze v3 protobuf numbers separately from v2 Primary reuse disposition: `adapt`. Preserved source-plan detail: extract and adapt.", + "detailedDesign" : "Rename RealmPath → ZonePath, RealmId → ZoneLabelId, RouteId → ZoneRouteId, ControllerGenerationId → ZoneLinkControllerGeneration; preserve all bounds/validation/serde; add ZoneLink-specific advertisement envelope fields (v3 schema version and private allocated-capability field); preserve `RouteFailClosedReason` + add `zone-link-disconnected`, `hop-limit-exceeded`, `relay-denied`, `attachment-not-permitted-over-zone-link`; freeze v3 protobuf numbers separately from v2 Primary reuse disposition: `adapt`. Preserved source-plan detail: extract and adapt.", "entryContracts" : [ "ADR-046-zone-routing" ], @@ -19511,7 +19511,7 @@ ], "specId" : "ADR-046-zone-routing", "topologicalRank" : 7, - "validation" : "Golden advertisement/path/failure vectors shared by Rust/Nix; property tests for NCA/loop/ceiling; replay-window tests; hop-count tests", + "validation" : "Golden advertisement/path/failure vectors shared by Rust/Nix; property tests for NCA/loop/allocated-capability narrowing; replay-window tests; hop-count tests", "wave" : "W2" }, { @@ -19519,7 +19519,7 @@ "destinations" : [ "`packages/d2b-zone-routing/src/engine.rs`" ], - "detailedDesign" : "Adapt RouteTreeEngine to ZoneRouteEngine using ZonePath/ZoneRouteId/ZoneLinkControllerGeneration from ADR046-routing-001; preserve all NCA/loop/multi-parent/capability/replay/capacity logic; add `decide_route` enforcement of `maxHops` parameter; add hop-counter decrement in relay path; expose `ZoneRoutePath` in v3 types Primary reuse disposition: `adapt`. Preserved source-plan detail: extract and adapt.", + "detailedDesign" : "Adapt RouteTreeEngine to ZoneRouteEngine using ZonePath/ZoneRouteId/ZoneLinkControllerGeneration from ADR046-routing-001; preserve all NCA/loop/multi-parent/capability/replay/capacity logic; add `decide_route` enforcement of the fixed protocol hop budget; add hop-counter decrement in relay path; expose `ZoneRoutePath` in v3 types Primary reuse disposition: `adapt`. Preserved source-plan detail: extract and adapt.", "entryContracts" : [ "ADR-046-zone-routing" ], @@ -19540,16 +19540,16 @@ { "blockers" : [], "destinations" : [ - "`packages/d2b-contracts/src/v3/zone_link.rs` (ZoneLink spec/status types, intent types, namespace allocation); `packages/d2b-zone-routing/src/resolver.rs` (ZoneEntrypointResolver)" + "`packages/d2b-zone-routing/src/resolver.rs` (ZoneEntrypointResolver)" ], - "detailedDesign" : "Child-local ZoneLink spec/status fields; self-name and one-uplink invariants; ZoneLinkIntent record stored in the child; ZoneLinkNamespaceAllocation issued by the exact parent allocator selected in sealed Nix-compiled `parentZone` topology; ZoneEntrypointResolver with longest-suffix match over ZonePath (adapted from `RealmEntrypointTable::resolve`) driven only by sealed `{ childZone, parentZone }` rows plus authenticated admitted route projections; no reciprocal parent-store resource or parent ZoneLink handler; fail closed on unknown topology, absent/stale projection, or unauthenticated route Primary reuse disposition: `adapt`. Preserved source-plan detail: extract and adapt.", + "detailedDesign" : "Consume the canonical ZoneLink spec/status/intent types owned by ADR046-zone-control-002; ZoneLinkNamespaceAllocation is issued by the exact parent allocator selected in sealed Nix-compiled `parentZone` topology; ZoneEntrypointResolver uses longest-suffix match over ZonePath (adapted from `RealmEntrypointTable::resolve`) and is driven only by sealed `{ childZone, parentZone }` rows plus authenticated admitted route projections; no reciprocal parent-store resource or parent ZoneLink handler; fail closed on unknown topology, absent/stale projection, or unauthenticated route Primary reuse disposition: `adapt`. Preserved source-plan detail: extract and adapt.", "entryContracts" : [ "ADR-046-zone-routing" ], "exitGate" : "ADR046-W2 exit criteria (ADR-046-validation-and-delivery §4): every spec/work item in this wave Merged with clean destinations, all validators green, and the ten-role panel seal recorded", "id" : "ADR046-routing-003", "kind" : "work-item", - "owner" : "ADR046-routing-001, ADR046-routing-002; ZoneLink resource owner", + "owner" : "ADR046-routing-001, ADR046-routing-002; Zone route resolver owner", "parallelGroup" : "wi:ADR-046-zone-routing", "prerequisites" : [ "ADR-046-zone-routing", @@ -19566,7 +19566,7 @@ "destinations" : [ "`packages/d2b-core-controller/src/zone_links.rs`" ], - "detailedDesign" : "Child-local ZoneLink handler in core-controller: manages local ResourceSpec→allocator-bound session→advertisement lifecycle; session state machine (Pending/Established/Disconnected/Reconnecting/Revoked); reconnect backoff per `reconnectPolicy`; advertisement issuance/renewal/withdrawal using enrolled KK ComponentSession; child-store route cursor and outbound intent queue; capability ceiling change handling; status writer; aggregate metrics use only closed semantic phase/reason/outcome labels and never `link_name_hash` or another ZoneLink/Zone/resource identity label; Nix-compiled `parentZone` selects the parent allocator, which alone owns privileged listeners, placement, and route namespace and creates no reciprocal resource. Primary reuse disposition: `adapt`. Preserved source-plan detail: extract and adapt.", + "detailedDesign" : "Child-local ZoneLink handler in core-controller: consumes the exact six-field ZoneLink schema from ADR046-zone-control-002 and manages local ResourceSpec→allocator-bound session→advertisement lifecycle; session state machine (Pending/Established/Disconnected/Reconnecting/Revoked); Provider-internal reconnect backoff bounded by `spec.limits`; advertisement issuance/renewal/withdrawal using enrolled KK ComponentSession; child-store route cursor and bounded outbound intent queue; private allocator capability-scope changes; D088 `status.resource` writer; aggregate metrics use only closed semantic phase/reason/outcome labels and never `link_name_hash` or another ZoneLink/Zone/resource identity label; Nix-compiled `parentZone` selects the parent allocator, which alone owns privileged listeners, placement, and route namespace and creates no reciprocal resource. Primary reuse disposition: `adapt`. Preserved source-plan detail: extract and adapt.", "entryContracts" : [ "ADR-046-zone-routing" ], @@ -19581,7 +19581,7 @@ ], "specId" : "ADR-046-zone-routing", "topologicalRank" : 10, - "validation" : "Session lifecycle tests; reconnect/revocation/ceiling-change; intent queue drain; cursor resync; advertisement renewal timing; fake-child tests; structural metric descriptor test asserts `vm`, `zone`, `zone_id`, `zone_uid`, and `link_name_hash` are absent and a ZoneLink-name canary never enters label values", + "validation" : "Session lifecycle tests; reconnect/disabled/revocation/allocator-policy-change; intent queue drain; cursor resync; advertisement renewal timing; fake-child tests; structural metric descriptor test asserts `vm`, `zone`, `zone_id`, `zone_uid`, and `link_name_hash` are absent and a ZoneLink-name canary never enters label values", "wave" : "W2" }, { @@ -19728,7 +19728,7 @@ "destinations" : [ "`nixos-modules/options-zones.nix` (new structural base), `nixos-modules/generated/resource-types.nix` (generated registry), `nixos-modules/generated/options-zones-.nix` (generated per ResourceType by `xtask gen-zone-nix-options`), `nixos-modules/assertions.nix` (new Zone assertions)" ], - "detailedDesign" : "Declare compiler-only scalar `d2b.zones..parentZone` plus structural `d2b.zones..resources. = { type = ...; spec = {}; }` as specified in the \"Option schema\" section above. `parentZone` has no default, is required for every non-root Zone, forbidden on `local-root`, resolves to one declared Zone, and never enters a ResourceSpec. Wire `options-zones.nix` and all `generated/options-zones-*.nix` files into `nixos-modules/default.nix`. Add a new `xtask gen-zone-nix-options` command that reads `docs/reference/schemas/v3/.schema.json` for each ResourceType, derives `generated/resource-types.nix`, and emits a generated submodule overlaying typed spec options (types, bounds, enum constraints, defaults, docs) onto `d2b.zones..resources..spec`. The generated registry's standard subset must equal the canonical 19-type registry from `ADR-046-resource-object-model` exactly; installed signed Provider schemas may append qualified types. These generated modules are committed and kept in sync by `xtask gen-zone-nix-options && git diff --exit-code` wired into `make test-drift`. Because the generated options carry field-level type constraints, field-level eval errors (wrong enum, out-of-range int, non-hex fingerprint) are caught without explicit assertions. Explicit assertions in `nixos-modules/assertions.nix` cover cross-resource invariants only: zone/resource key name regex, reserved names, `parentZone` required/forbidden/existence/self/cycle/16-name-depth constraints, child-local `childZoneName == zone`, at most one uplink resource per non-root Zone and none in local root, ref resolution, count limits, and transportSettings secret-key exclusion (listed in the eval-time assertions table). Conflicting parent scalar definitions fail through standard Nix module merging. Each new assertion must have a matching case in `tests/unit/nix/cases/zone-assertions.nix` (nix-unit auto-discovered). `d2b.realms` option namespace is NOT removed in this work item. Primary reuse disposition: `adapt`. Preserved source-plan detail: new module following same pattern.", + "detailedDesign" : "Declare compiler-only scalar `d2b.zones..parentZone` plus structural `d2b.zones..resources. = { type = ...; spec = {}; }` as specified in the \"Option schema\" section above. `parentZone` has no default, is required for every non-root Zone, forbidden on `local-root`, resolves to one declared Zone, and never enters a ResourceSpec. Wire `options-zones.nix` and all `generated/options-zones-*.nix` files into `nixos-modules/default.nix`. Add a new `xtask gen-zone-nix-options` command that reads `docs/reference/schemas/v3/.schema.json` for each ResourceType, derives `generated/resource-types.nix`, and emits a generated submodule overlaying typed spec options (types, bounds, enum constraints, defaults, docs) onto `d2b.zones..resources..spec`. The generated registry's standard subset must equal the canonical 19-type registry from `ADR-046-resource-object-model` exactly; installed signed Provider schemas may append qualified types. These generated modules are committed and kept in sync by `xtask gen-zone-nix-options && git diff --exit-code` wired into `make test-drift`. The ZoneLink module is generated from the exact six-field schema owned by ADR046-zone-control-002 and rejects any seventh field. Because the generated options carry field-level type constraints, field-level eval errors (wrong enum, out-of-range int, malformed ref) are caught without explicit assertions. Explicit assertions in `nixos-modules/assertions.nix` cover cross-resource invariants only: zone/resource key name regex, reserved names, `parentZone` required/forbidden/existence/self/cycle/16-name-depth constraints, child-local `childZoneName == zone`, at most one uplink resource per non-root Zone and none in local root, ref resolution, count limits, and transportSettings secret-key exclusion (listed in the eval-time assertions table). Conflicting parent scalar definitions fail through standard Nix module merging. Each new assertion must have a matching case in `tests/unit/nix/cases/zone-assertions.nix` (nix-unit auto-discovered). `d2b.realms` option namespace is NOT removed in this work item. Primary reuse disposition: `adapt`. Preserved source-plan detail: new module following same pattern.", "entryContracts" : [ "ADR-046-zone-routing" ], @@ -19743,7 +19743,7 @@ ], "specId" : "ADR-046-zone-routing", "topologicalRank" : 8, - "validation" : "`nix-unit: zone-name-regex`, all five `nix-unit: zone-parent-*` vectors, `nix-unit: zone-link-fingerprint`, `nix-unit: zone-link-child-name`, `nix-unit: zone-link-one-uplink`, `nix-unit: capability-ceiling-unknown-verb`, `nix-unit: transport-settings-secret-key`; add `drift: standard-resource-type-registry` asserting the generated standard subset is exactly all 19 canonical types with no omission/addition/duplicate/reordering, plus `drift: zone-nix-options` (`xtask gen-zone-nix-options && git diff --exit-code`); run `make nix-unit-pin` after adding eval cases", + "validation" : "`nix-unit: zone-name-regex`, all five `nix-unit: zone-parent-*` vectors, `nix-unit: zone-link-credential-ref`, `nix-unit: zone-link-child-name`, `nix-unit: zone-link-one-uplink`, `nix-unit: zone-link-closed-spec`, `nix-unit: zone-link-limits`, `nix-unit: transport-settings-secret-key`; add `drift: standard-resource-type-registry` asserting the generated standard subset is exactly all 19 canonical types with no omission/addition/duplicate/reordering, plus `drift: zone-nix-options` (`xtask gen-zone-nix-options && git diff --exit-code`); run `make nix-unit-pin` after adding eval cases", "wave" : "W2" }, { @@ -19751,7 +19751,7 @@ "destinations" : [ "`nixos-modules/zone-resources-json.nix` (new), private local-root allocator bootstrap compiler/sealer input (not a ResourceSpec or public bundle), `nixos-modules/bundle-artifacts.nix` (new row for per-Zone `resource-bundle.json`), `packages/xtask/src/main.rs` (`gen-zone-schemas` subcommand emitting `docs/reference/schemas/v3/.schema.json` for Zone and ZoneLink; `gen-zone-nix-options` subcommand emitting `nixos-modules/generated/options-zones-.nix`)" ], - "detailedDesign" : "`zone-resources-json.nix` iterates `d2b.zones..resources.*` to produce the canonical sorted resource list: for each entry, render `{ apiVersion, type, metadata: { name, zone, ownerRef: , labels: , annotations: }, spec: }`. Separately canonicalize sorted `{ childZone, parentZone }` rows from the compiler-only topology and seal them into the private allocator bootstrap input; `parentZone` never enters a resource bundle or `Zone.spec`, and a topology digest change releases/reallocates affected edges independently of resource `generationId`. Per-Zone generation is strict: local root's generated bundle contains no ZoneLink; a non-root Zone's enabled uplink and referenced transport Provider appear together only in that child's bundle; no emitter copies either resource into the selected parent's bundle. The bundle JSON omits `managedBy` and `configurationGeneration`; the configuration service/core sets those fields when activating the validated bundle. Sort all resources by `(type, zone, name)`. Compute `generationId` as SHA-256 (lower hex) of the UTF-8 bytes of the sorted `resources` array JSON. Compute `integrity` as SHA-256 (base64url, no padding) of the full bundle JSON with integrity field zeroed. Install at `/etc/d2b/zones//resource-bundle.json` root:d2bd 0640. Canonical form: all object keys sorted lexicographically; order-significant arrays preserved; schema-declared set-like arrays sorted lexicographically; all optional fields emitted with defaults; no field renaming or restructuring. Build-time validation runs in a Nix derivation: (1) validate the complete parent map (non-root required, local-root forbidden, declared target, one scalar parent, not self, acyclic, max 16 names); (2) validate each resource against the committed JSON Schema; (3) validate `transportSettings` for each child-local ZoneLink against its same-Zone Provider's `transportSettingsSchema` — `transportProviderRef` is always explicit, never inferred or defaulted; (4) verify capability ceilings are subsets of the sealed selected-parent allocator grants; (5) verify `childZoneName == metadata.zone`, at most one uplink resource per non-root Zone, and no local-root uplink; (6) check for duplicate `(type, zone, name)` tuples. Providers MUST commit their `transportSettingsSchema` before any ZoneLink can reference them. Drift gates: `xtask gen-zone-schemas && git diff --exit-code` and `xtask gen-zone-nix-options && git diff --exit-code` both wired into `make test-drift`. Add `checks.${system}.zone-schema-drift` to `flake.nix`. Primary reuse disposition: `adapt`. Preserved source-plan detail: extend and adapt.", + "detailedDesign" : "`zone-resources-json.nix` iterates `d2b.zones..resources.*` to produce the canonical sorted resource list: for each entry, render `{ apiVersion, type, metadata: { name, zone, ownerRef: , labels: , annotations: }, spec: }`. Separately canonicalize sorted `{ childZone, parentZone }` rows from the compiler-only topology and seal them into the private allocator bootstrap input; `parentZone` never enters a resource bundle or `Zone.spec`, and a topology digest change releases/reallocates affected edges independently of resource `generationId`. Per-Zone generation is strict: local root's generated bundle contains no ZoneLink; a non-root Zone's enabled uplink and referenced transport Provider appear together only in that child's bundle; no emitter copies either resource into the selected parent's bundle. The bundle JSON omits `managedBy` and `configurationGeneration`; the configuration service/core sets those fields when activating the validated bundle. Sort all resources by `(type, zone, name)`. Compute `generationId` as SHA-256 (lower hex) of the UTF-8 bytes of the sorted `resources` array JSON. Compute `integrity` as SHA-256 (base64url, no padding) of the full bundle JSON with integrity field zeroed. Install at `/etc/d2b/zones//resource-bundle.json` root:d2bd 0640. Canonical form: all object keys sorted lexicographically; order-significant arrays preserved; schema-declared set-like arrays sorted lexicographically; all optional fields emitted with defaults; no field renaming or restructuring. Build-time validation runs in a Nix derivation: (1) validate the complete parent map (non-root required, local-root forbidden, declared target, one scalar parent, not self, acyclic, max 16 names); (2) validate each resource against the committed JSON Schema, including the exact six-field ZoneLink schema from ADR046-zone-control-002; (3) validate `transportSettings` for each child-local ZoneLink against its same-Zone Provider's `transportSettingsSchema` — `transportProviderRef` is always explicit, never inferred or defaulted; (4) resolve every same-Zone `transportCredentials` ref; (5) verify `childZoneName == metadata.zone`, at most one uplink resource per non-root Zone, and no local-root uplink; (6) check for duplicate `(type, zone, name)` tuples. Private route capability policy is sealed in allocator bootstrap state and is not a ZoneLink ResourceSpec field. Providers MUST commit their `transportSettingsSchema` before any ZoneLink can reference them. Drift gates: `xtask gen-zone-schemas && git diff --exit-code` and `xtask gen-zone-nix-options && git diff --exit-code` both wired into `make test-drift`. Add `checks.${system}.zone-schema-drift` to `flake.nix`. Primary reuse disposition: `adapt`. Preserved source-plan detail: extend and adapt.", "entryContracts" : [ "ADR-046-zone-routing" ], @@ -19767,7 +19767,7 @@ ], "specId" : "ADR-046-zone-routing", "topologicalRank" : 9, - "validation" : "`drift: zone-resource-schema`, `drift: zone-nix-options`, `build: zone-bundle-deterministic`, `build: parent-topology-sealed`, `build: child-local-zonelink-bundle` (K0 has no ZoneLink; K1 contains its self-matching ZoneLink and same-Zone transport Provider; neither is copied to K0), `build: transport-settings-unknown-field`, `build: capability-ceiling-superset`, `build: missing-transport-provider`; run `make flake-matrix-pin` after adding flake checks", + "validation" : "`drift: zone-resource-schema`, `drift: zone-nix-options`, `build: zone-bundle-deterministic`, `build: parent-topology-sealed`, `build: child-local-zonelink-bundle` (K0 has no ZoneLink; K1 contains its self-matching ZoneLink and same-Zone transport Provider; neither is copied to K0), `build: zone-link-exact-six-fields`, `build: transport-settings-unknown-field`, `build: transport-credential-ref`, `build: missing-transport-provider`; run `make flake-matrix-pin` after adding flake checks", "wave" : "W2" }, { diff --git a/docs/specs/ADR-046-spec-set.json b/docs/specs/ADR-046-spec-set.json index de0eb8e4f..9eb35d9ee 100644 --- a/docs/specs/ADR-046-spec-set.json +++ b/docs/specs/ADR-046-spec-set.json @@ -706,7 +706,7 @@ "ADR-046-zone-routing" ], "path" : "docs/specs/providers/ADR-046-provider-transport-azure-relay.md", - "sha256" : "6ae9c12b64aa11851d699a6b40c947fa528e1143383df7878a1885ad6389b941", + "sha256" : "b6a12dc993e1600d703556525d1eb5305d50a135d519886bf7112eeb2212680b", "specId" : "ADR-046-provider-transport-azure-relay", "status" : "Proposed", "supersedes" : "`d2b-provider-relay` gateway-display relay path (`AcaWorkloadProvider` + `RelayProvider` traits in `d2b-realm-provider`); `d2b-gateway-runtime/src/bin/d2b-gateway-relay.rs`; `packages/d2b-provider-relay/src/lib.rs` as a first-party transport surface", @@ -1225,10 +1225,10 @@ "ADR-046-terminology-and-identities" ], "path" : "docs/specs/ADR-046-zone-routing.md", - "sha256" : "2b2c1f179de89219d8cd0ff1c0d54befbb9ee1c83e1c09f15fa4bf62e6713dea", + "sha256" : "bdecfd3d3122039d6282769e649436123bddc788221ac7d7bb89cbb2d8be8788", "specId" : "ADR-046-zone-routing", "status" : "Proposed", - "supersedes" : "`RealmEntrypointTable`/`EntrypointMode`/`RealmControllerPlacement` enum (→ compiler-only `parentZone` topology + ZoneLink transport + private Zone runtime bootstrap placement); `RouteTreeEngine`/`routing.rs` route contracts (adapted, not deleted); `RemoteNodeRegistry` (→ ZoneLink controller); `OperationRouter` (→ ZoneLinkIdempotencyKey in d2b-bus); `realm_access_resolver` module (→ ZoneEntrypointResolver); `realm-controllers.json`/`realm-identity.json` bundle artifacts (→ Nix-authored Zone settings/ZoneLink resources); `d2b.realms.*` Nix option namespace (→ `d2b.zones.*`); `WorkloadId` routing targets (→ typed Guest/Host resource refs); `CapabilitySet`-only routing authz (→ per-hop RBAC narrowing + capability ceiling)", + "supersedes" : "`RealmEntrypointTable`/`EntrypointMode`/`RealmControllerPlacement` enum (→ compiler-only `parentZone` topology + ZoneLink transport + private Zone runtime bootstrap placement); `RouteTreeEngine`/`routing.rs` route contracts (adapted, not deleted); `RemoteNodeRegistry` (→ ZoneLink controller); `OperationRouter` (→ ZoneLinkIdempotencyKey in d2b-bus); `realm_access_resolver` module (→ ZoneEntrypointResolver); `realm-controllers.json`/`realm-identity.json` bundle artifacts (→ Nix-authored Zone settings/ZoneLink resources); `d2b.realms.*` Nix option namespace (→ `d2b.zones.*`); `WorkloadId` routing targets (→ typed Guest/Host resource refs); `CapabilitySet`-only routing authz (→ per-hop RBAC narrowing + allocator-issued capability scope)", "version" : 1, "workItemPrefixes" : [ "routing" diff --git a/docs/specs/ADR-046-work-items.json b/docs/specs/ADR-046-work-items.json index 2d6124bb4..f9f0546e3 100644 --- a/docs/specs/ADR-046-work-items.json +++ b/docs/specs/ADR-046-work-items.json @@ -4957,14 +4957,14 @@ "dataMigration" : "Full reset; no v2 Realm route compatibility", "dependencyOwner" : "W0/W1a; zone routing contract owner", "destination" : "`packages/d2b-contracts/src/v3/zone_routing.rs`", - "detailedDesign" : "Rename RealmPath → ZonePath, RealmId → ZoneLabelId, RouteId → ZoneRouteId, ControllerGenerationId → ZoneLinkControllerGeneration; preserve all bounds/validation/serde; add ZoneLink-specific advertisement envelope fields (v3 schema version, capability ceiling field); preserve `RouteFailClosedReason` + add `zone-link-disconnected`, `hop-limit-exceeded`, `relay-denied`, `attachment-not-permitted-over-zone-link`; freeze v3 protobuf numbers separately from v2 Primary reuse disposition: `adapt`. Preserved source-plan detail: extract and adapt.", + "detailedDesign" : "Rename RealmPath → ZonePath, RealmId → ZoneLabelId, RouteId → ZoneRouteId, ControllerGenerationId → ZoneLinkControllerGeneration; preserve all bounds/validation/serde; add ZoneLink-specific advertisement envelope fields (v3 schema version and private allocated-capability field); preserve `RouteFailClosedReason` + add `zone-link-disconnected`, `hop-limit-exceeded`, `relay-denied`, `attachment-not-permitted-over-zone-link`; freeze v3 protobuf numbers separately from v2 Primary reuse disposition: `adapt`. Preserved source-plan detail: extract and adapt.", "integration" : "d2b-bus zone route engine and ZoneLink controller consume these types", "removalProof" : "v3 old `RealmPath` route types retired after zone-routing engine is live and all callers switched", "reuseAction" : "adapt", "reuseSource" : "Same v3 baseline commit `b5ddbed6`", "specId" : "ADR-046-zone-routing", "specPath" : "docs/specs/ADR-046-zone-routing.md", - "validation" : "Golden advertisement/path/failure vectors shared by Rust/Nix; property tests for NCA/loop/ceiling; replay-window tests; hop-count tests", + "validation" : "Golden advertisement/path/failure vectors shared by Rust/Nix; property tests for NCA/loop/allocated-capability narrowing; replay-window tests; hop-count tests", "workItemId" : "ADR046-routing-001" }, { @@ -4972,7 +4972,7 @@ "dataMigration" : "None (pure in-memory engine)", "dependencyOwner" : "ADR046-routing-001; zone route engine owner", "destination" : "`packages/d2b-zone-routing/src/engine.rs`", - "detailedDesign" : "Adapt RouteTreeEngine to ZoneRouteEngine using ZonePath/ZoneRouteId/ZoneLinkControllerGeneration from ADR046-routing-001; preserve all NCA/loop/multi-parent/capability/replay/capacity logic; add `decide_route` enforcement of `maxHops` parameter; add hop-counter decrement in relay path; expose `ZoneRoutePath` in v3 types Primary reuse disposition: `adapt`. Preserved source-plan detail: extract and adapt.", + "detailedDesign" : "Adapt RouteTreeEngine to ZoneRouteEngine using ZonePath/ZoneRouteId/ZoneLinkControllerGeneration from ADR046-routing-001; preserve all NCA/loop/multi-parent/capability/replay/capacity logic; add `decide_route` enforcement of the fixed protocol hop budget; add hop-counter decrement in relay path; expose `ZoneRoutePath` in v3 types Primary reuse disposition: `adapt`. Preserved source-plan detail: extract and adapt.", "integration" : "d2b-bus calls `ZoneRouteEngine::decide_route` for every cross-Zone ResourceClient call; ZoneLink controller calls `admit_advertisement`/`admit_withdrawal`", "removalProof" : "`RouteTreeEngine` on v3 RealmPath types retired after ZoneRouteEngine is exercised in all bus routing paths", "reuseAction" : "adapt", @@ -4985,9 +4985,9 @@ { "currentSource" : "`packages/d2b-realm-core/src/routing.rs`: `RouteNamespaceAllocation`; `packages/d2b-realm-core/src/access.rs`: `RealmAccessResolverRequest/Response/Error`, `RealmAccessBinding`, `RealmTransportBinding`, `RealmAccessClientContract`, `UnixSocketPath`, `AccessBindingRef`, all access types (evidence: **B** — complete implementation, no production callers); `packages/d2bd/src/realm_access_resolver.rs`: `resolve_local_root_realm_access()`, `local_root_realm_access_client_contract()` (evidence: **B** — `pub mod` at `d2bd/src/lib.rs:117`, no callers from running daemon); `packages/d2b-realm-router/src/target_resolver.rs`: `RealmEntrypointTable`, `DispatchTarget`, `RealmEntrypoint`, `ResolveError` (evidence: **A**); `packages/d2b/src/lib.rs:5240`: `load_realm_entrypoint_table()` (evidence: **A**); `packages/d2b/src/target_routing.rs`: `Route::Local`/`Route::GatewayBacked` dispatch (evidence: **A**); `packages/d2b-realm-core/src/realm.rs`: `EntrypointMode`, `RealmControllerPlacement` (evidence: **A** as types; routing use **B**); `packages/d2b-core/src/realm_controller_config.rs`: `RealmControllersJson` (evidence: **C**); `nixos-modules/realm-controller-config-json.nix` (evidence: **C**)", "dataMigration" : "None; ZoneLink resources created from Nix configuration at v3 reset", - "dependencyOwner" : "ADR046-routing-001, ADR046-routing-002; ZoneLink resource owner", - "destination" : "`packages/d2b-contracts/src/v3/zone_link.rs` (ZoneLink spec/status types, intent types, namespace allocation); `packages/d2b-zone-routing/src/resolver.rs` (ZoneEntrypointResolver)", - "detailedDesign" : "Child-local ZoneLink spec/status fields; self-name and one-uplink invariants; ZoneLinkIntent record stored in the child; ZoneLinkNamespaceAllocation issued by the exact parent allocator selected in sealed Nix-compiled `parentZone` topology; ZoneEntrypointResolver with longest-suffix match over ZonePath (adapted from `RealmEntrypointTable::resolve`) driven only by sealed `{ childZone, parentZone }` rows plus authenticated admitted route projections; no reciprocal parent-store resource or parent ZoneLink handler; fail closed on unknown topology, absent/stale projection, or unauthenticated route Primary reuse disposition: `adapt`. Preserved source-plan detail: extract and adapt.", + "dependencyOwner" : "ADR046-routing-001, ADR046-routing-002; Zone route resolver owner", + "destination" : "`packages/d2b-zone-routing/src/resolver.rs` (ZoneEntrypointResolver)", + "detailedDesign" : "Consume the canonical ZoneLink spec/status/intent types owned by ADR046-zone-control-002; ZoneLinkNamespaceAllocation is issued by the exact parent allocator selected in sealed Nix-compiled `parentZone` topology; ZoneEntrypointResolver uses longest-suffix match over ZonePath (adapted from `RealmEntrypointTable::resolve`) and is driven only by sealed `{ childZone, parentZone }` rows plus authenticated admitted route projections; no reciprocal parent-store resource or parent ZoneLink handler; fail closed on unknown topology, absent/stale projection, or unauthenticated route Primary reuse disposition: `adapt`. Preserved source-plan detail: extract and adapt.", "integration" : "Child core-controller ZoneLink handler manages child-store ZoneLink resources; parent d2b-bus feeds sealed topology and authenticated `ZoneRouteEngine` projection state to ZoneEntrypointResolver for per-call dispatch", "removalProof" : "`RealmEntrypointTable` retired after all host-daemon routing paths use ZoneEntrypointResolver", "reuseAction" : "adapt", @@ -5002,14 +5002,14 @@ "dataMigration" : "New ZoneLink resources from Nix configuration; no prior enrollment compatibility", "dependencyOwner" : "ADR046-routing-003; core-controller ZoneLink handler owner", "destination" : "`packages/d2b-core-controller/src/zone_links.rs`", - "detailedDesign" : "Child-local ZoneLink handler in core-controller: manages local ResourceSpec→allocator-bound session→advertisement lifecycle; session state machine (Pending/Established/Disconnected/Reconnecting/Revoked); reconnect backoff per `reconnectPolicy`; advertisement issuance/renewal/withdrawal using enrolled KK ComponentSession; child-store route cursor and outbound intent queue; capability ceiling change handling; status writer; aggregate metrics use only closed semantic phase/reason/outcome labels and never `link_name_hash` or another ZoneLink/Zone/resource identity label; Nix-compiled `parentZone` selects the parent allocator, which alone owns privileged listeners, placement, and route namespace and creates no reciprocal resource. Primary reuse disposition: `adapt`. Preserved source-plan detail: extract and adapt.", + "detailedDesign" : "Child-local ZoneLink handler in core-controller: consumes the exact six-field ZoneLink schema from ADR046-zone-control-002 and manages local ResourceSpec→allocator-bound session→advertisement lifecycle; session state machine (Pending/Established/Disconnected/Reconnecting/Revoked); Provider-internal reconnect backoff bounded by `spec.limits`; advertisement issuance/renewal/withdrawal using enrolled KK ComponentSession; child-store route cursor and bounded outbound intent queue; private allocator capability-scope changes; D088 `status.resource` writer; aggregate metrics use only closed semantic phase/reason/outcome labels and never `link_name_hash` or another ZoneLink/Zone/resource identity label; Nix-compiled `parentZone` selects the parent allocator, which alone owns privileged listeners, placement, and route namespace and creates no reciprocal resource. Primary reuse disposition: `adapt`. Preserved source-plan detail: extract and adapt.", "integration" : "Child core-controller process → local transport Provider → sealed binding for the allocator selected by `parentZone` → d2b-bus ComponentSession; child ZoneLink handler exchanges advertisements while that parent ZoneRouteEngine admits/withdraws them", "removalProof" : "`RemoteNodeRegistry` retired after all enrolled peer routing moves to ZoneLink handler", "reuseAction" : "adapt", "reuseSource" : "Same v3 baseline commit `b5ddbed6`", "specId" : "ADR-046-zone-routing", "specPath" : "docs/specs/ADR-046-zone-routing.md", - "validation" : "Session lifecycle tests; reconnect/revocation/ceiling-change; intent queue drain; cursor resync; advertisement renewal timing; fake-child tests; structural metric descriptor test asserts `vm`, `zone`, `zone_id`, `zone_uid`, and `link_name_hash` are absent and a ZoneLink-name canary never enters label values", + "validation" : "Session lifecycle tests; reconnect/disabled/revocation/allocator-policy-change; intent queue drain; cursor resync; advertisement renewal timing; fake-child tests; structural metric descriptor test asserts `vm`, `zone`, `zone_id`, `zone_uid`, and `link_name_hash` are absent and a ZoneLink-name canary never enters label values", "workItemId" : "ADR046-routing-004" }, { @@ -5107,14 +5107,14 @@ "dataMigration" : "None; Zone options are new; Realm options retained until migration PR", "dependencyOwner" : "ADR046-routing-001; Nix module owner", "destination" : "`nixos-modules/options-zones.nix` (new structural base), `nixos-modules/generated/resource-types.nix` (generated registry), `nixos-modules/generated/options-zones-.nix` (generated per ResourceType by `xtask gen-zone-nix-options`), `nixos-modules/assertions.nix` (new Zone assertions)", - "detailedDesign" : "Declare compiler-only scalar `d2b.zones..parentZone` plus structural `d2b.zones..resources. = { type = ...; spec = {}; }` as specified in the \"Option schema\" section above. `parentZone` has no default, is required for every non-root Zone, forbidden on `local-root`, resolves to one declared Zone, and never enters a ResourceSpec. Wire `options-zones.nix` and all `generated/options-zones-*.nix` files into `nixos-modules/default.nix`. Add a new `xtask gen-zone-nix-options` command that reads `docs/reference/schemas/v3/.schema.json` for each ResourceType, derives `generated/resource-types.nix`, and emits a generated submodule overlaying typed spec options (types, bounds, enum constraints, defaults, docs) onto `d2b.zones..resources..spec`. The generated registry's standard subset must equal the canonical 19-type registry from `ADR-046-resource-object-model` exactly; installed signed Provider schemas may append qualified types. These generated modules are committed and kept in sync by `xtask gen-zone-nix-options && git diff --exit-code` wired into `make test-drift`. Because the generated options carry field-level type constraints, field-level eval errors (wrong enum, out-of-range int, non-hex fingerprint) are caught without explicit assertions. Explicit assertions in `nixos-modules/assertions.nix` cover cross-resource invariants only: zone/resource key name regex, reserved names, `parentZone` required/forbidden/existence/self/cycle/16-name-depth constraints, child-local `childZoneName == zone`, at most one uplink resource per non-root Zone and none in local root, ref resolution, count limits, and transportSettings secret-key exclusion (listed in the eval-time assertions table). Conflicting parent scalar definitions fail through standard Nix module merging. Each new assertion must have a matching case in `tests/unit/nix/cases/zone-assertions.nix` (nix-unit auto-discovered). `d2b.realms` option namespace is NOT removed in this work item. Primary reuse disposition: `adapt`. Preserved source-plan detail: new module following same pattern.", + "detailedDesign" : "Declare compiler-only scalar `d2b.zones..parentZone` plus structural `d2b.zones..resources. = { type = ...; spec = {}; }` as specified in the \"Option schema\" section above. `parentZone` has no default, is required for every non-root Zone, forbidden on `local-root`, resolves to one declared Zone, and never enters a ResourceSpec. Wire `options-zones.nix` and all `generated/options-zones-*.nix` files into `nixos-modules/default.nix`. Add a new `xtask gen-zone-nix-options` command that reads `docs/reference/schemas/v3/.schema.json` for each ResourceType, derives `generated/resource-types.nix`, and emits a generated submodule overlaying typed spec options (types, bounds, enum constraints, defaults, docs) onto `d2b.zones..resources..spec`. The generated registry's standard subset must equal the canonical 19-type registry from `ADR-046-resource-object-model` exactly; installed signed Provider schemas may append qualified types. These generated modules are committed and kept in sync by `xtask gen-zone-nix-options && git diff --exit-code` wired into `make test-drift`. The ZoneLink module is generated from the exact six-field schema owned by ADR046-zone-control-002 and rejects any seventh field. Because the generated options carry field-level type constraints, field-level eval errors (wrong enum, out-of-range int, malformed ref) are caught without explicit assertions. Explicit assertions in `nixos-modules/assertions.nix` cover cross-resource invariants only: zone/resource key name regex, reserved names, `parentZone` required/forbidden/existence/self/cycle/16-name-depth constraints, child-local `childZoneName == zone`, at most one uplink resource per non-root Zone and none in local root, ref resolution, count limits, and transportSettings secret-key exclusion (listed in the eval-time assertions table). Conflicting parent scalar definitions fail through standard Nix module merging. Each new assertion must have a matching case in `tests/unit/nix/cases/zone-assertions.nix` (nix-unit auto-discovered). `d2b.realms` option namespace is NOT removed in this work item. Primary reuse disposition: `adapt`. Preserved source-plan detail: new module following same pattern.", "integration" : "ADR046-routing-012 consumes the validated `parentZone` map for private allocator bootstrap sealing and iterates `d2b.zones..resources.*` for resource-bundle emission", "removalProof" : "`nixos-modules/options-realms-workloads.nix` `d2b.realms` namespace retires after all hosts migrate to `d2b.zones`", "reuseAction" : "adapt", "reuseSource" : "Same v3 baseline `b5ddbed6`; `assertions.nix` pattern reused for Zone assertions; `realm-controller-config-json.nix` is the structural template", "specId" : "ADR-046-zone-routing", "specPath" : "docs/specs/ADR-046-zone-routing.md", - "validation" : "`nix-unit: zone-name-regex`, all five `nix-unit: zone-parent-*` vectors, `nix-unit: zone-link-fingerprint`, `nix-unit: zone-link-child-name`, `nix-unit: zone-link-one-uplink`, `nix-unit: capability-ceiling-unknown-verb`, `nix-unit: transport-settings-secret-key`; add `drift: standard-resource-type-registry` asserting the generated standard subset is exactly all 19 canonical types with no omission/addition/duplicate/reordering, plus `drift: zone-nix-options` (`xtask gen-zone-nix-options && git diff --exit-code`); run `make nix-unit-pin` after adding eval cases", + "validation" : "`nix-unit: zone-name-regex`, all five `nix-unit: zone-parent-*` vectors, `nix-unit: zone-link-credential-ref`, `nix-unit: zone-link-child-name`, `nix-unit: zone-link-one-uplink`, `nix-unit: zone-link-closed-spec`, `nix-unit: zone-link-limits`, `nix-unit: transport-settings-secret-key`; add `drift: standard-resource-type-registry` asserting the generated standard subset is exactly all 19 canonical types with no omission/addition/duplicate/reordering, plus `drift: zone-nix-options` (`xtask gen-zone-nix-options && git diff --exit-code`); run `make nix-unit-pin` after adding eval cases", "workItemId" : "ADR046-routing-011" }, { @@ -5122,14 +5122,14 @@ "dataMigration" : "None; new artifact file", "dependencyOwner" : "ADR046-routing-011, ADR046-routing-001; bundle emitter owner", "destination" : "`nixos-modules/zone-resources-json.nix` (new), private local-root allocator bootstrap compiler/sealer input (not a ResourceSpec or public bundle), `nixos-modules/bundle-artifacts.nix` (new row for per-Zone `resource-bundle.json`), `packages/xtask/src/main.rs` (`gen-zone-schemas` subcommand emitting `docs/reference/schemas/v3/.schema.json` for Zone and ZoneLink; `gen-zone-nix-options` subcommand emitting `nixos-modules/generated/options-zones-.nix`)", - "detailedDesign" : "`zone-resources-json.nix` iterates `d2b.zones..resources.*` to produce the canonical sorted resource list: for each entry, render `{ apiVersion, type, metadata: { name, zone, ownerRef: , labels: , annotations: }, spec: }`. Separately canonicalize sorted `{ childZone, parentZone }` rows from the compiler-only topology and seal them into the private allocator bootstrap input; `parentZone` never enters a resource bundle or `Zone.spec`, and a topology digest change releases/reallocates affected edges independently of resource `generationId`. Per-Zone generation is strict: local root's generated bundle contains no ZoneLink; a non-root Zone's enabled uplink and referenced transport Provider appear together only in that child's bundle; no emitter copies either resource into the selected parent's bundle. The bundle JSON omits `managedBy` and `configurationGeneration`; the configuration service/core sets those fields when activating the validated bundle. Sort all resources by `(type, zone, name)`. Compute `generationId` as SHA-256 (lower hex) of the UTF-8 bytes of the sorted `resources` array JSON. Compute `integrity` as SHA-256 (base64url, no padding) of the full bundle JSON with integrity field zeroed. Install at `/etc/d2b/zones//resource-bundle.json` root:d2bd 0640. Canonical form: all object keys sorted lexicographically; order-significant arrays preserved; schema-declared set-like arrays sorted lexicographically; all optional fields emitted with defaults; no field renaming or restructuring. Build-time validation runs in a Nix derivation: (1) validate the complete parent map (non-root required, local-root forbidden, declared target, one scalar parent, not self, acyclic, max 16 names); (2) validate each resource against the committed JSON Schema; (3) validate `transportSettings` for each child-local ZoneLink against its same-Zone Provider's `transportSettingsSchema` — `transportProviderRef` is always explicit, never inferred or defaulted; (4) verify capability ceilings are subsets of the sealed selected-parent allocator grants; (5) verify `childZoneName == metadata.zone`, at most one uplink resource per non-root Zone, and no local-root uplink; (6) check for duplicate `(type, zone, name)` tuples. Providers MUST commit their `transportSettingsSchema` before any ZoneLink can reference them. Drift gates: `xtask gen-zone-schemas && git diff --exit-code` and `xtask gen-zone-nix-options && git diff --exit-code` both wired into `make test-drift`. Add `checks.${system}.zone-schema-drift` to `flake.nix`. Primary reuse disposition: `adapt`. Preserved source-plan detail: extend and adapt.", + "detailedDesign" : "`zone-resources-json.nix` iterates `d2b.zones..resources.*` to produce the canonical sorted resource list: for each entry, render `{ apiVersion, type, metadata: { name, zone, ownerRef: , labels: , annotations: }, spec: }`. Separately canonicalize sorted `{ childZone, parentZone }` rows from the compiler-only topology and seal them into the private allocator bootstrap input; `parentZone` never enters a resource bundle or `Zone.spec`, and a topology digest change releases/reallocates affected edges independently of resource `generationId`. Per-Zone generation is strict: local root's generated bundle contains no ZoneLink; a non-root Zone's enabled uplink and referenced transport Provider appear together only in that child's bundle; no emitter copies either resource into the selected parent's bundle. The bundle JSON omits `managedBy` and `configurationGeneration`; the configuration service/core sets those fields when activating the validated bundle. Sort all resources by `(type, zone, name)`. Compute `generationId` as SHA-256 (lower hex) of the UTF-8 bytes of the sorted `resources` array JSON. Compute `integrity` as SHA-256 (base64url, no padding) of the full bundle JSON with integrity field zeroed. Install at `/etc/d2b/zones//resource-bundle.json` root:d2bd 0640. Canonical form: all object keys sorted lexicographically; order-significant arrays preserved; schema-declared set-like arrays sorted lexicographically; all optional fields emitted with defaults; no field renaming or restructuring. Build-time validation runs in a Nix derivation: (1) validate the complete parent map (non-root required, local-root forbidden, declared target, one scalar parent, not self, acyclic, max 16 names); (2) validate each resource against the committed JSON Schema, including the exact six-field ZoneLink schema from ADR046-zone-control-002; (3) validate `transportSettings` for each child-local ZoneLink against its same-Zone Provider's `transportSettingsSchema` — `transportProviderRef` is always explicit, never inferred or defaulted; (4) resolve every same-Zone `transportCredentials` ref; (5) verify `childZoneName == metadata.zone`, at most one uplink resource per non-root Zone, and no local-root uplink; (6) check for duplicate `(type, zone, name)` tuples. Private route capability policy is sealed in allocator bootstrap state and is not a ZoneLink ResourceSpec field. Providers MUST commit their `transportSettingsSchema` before any ZoneLink can reference them. Drift gates: `xtask gen-zone-schemas && git diff --exit-code` and `xtask gen-zone-nix-options && git diff --exit-code` both wired into `make test-drift`. Add `checks.${system}.zone-schema-drift` to `flake.nix`. Primary reuse disposition: `adapt`. Preserved source-plan detail: extend and adapt.", "integration" : "The local-root allocator consumes sealed parent topology independently of resource bundles; `nixos-modules/bundle-artifacts.nix` installs each per-Zone `resource-bundle.json`; ADR046-routing-013 Zone runtime reads it on startup", "removalProof" : "`realm-controllers.json` artifact retires after Zone runtime is live and all hosts migrated", "reuseAction" : "adapt", "reuseSource" : "`realm-controller-config-json.nix` structural template; `xtask gen-schemas` extension point (main `a1cc0b2d` unchanged in this area)", "specId" : "ADR-046-zone-routing", "specPath" : "docs/specs/ADR-046-zone-routing.md", - "validation" : "`drift: zone-resource-schema`, `drift: zone-nix-options`, `build: zone-bundle-deterministic`, `build: parent-topology-sealed`, `build: child-local-zonelink-bundle` (K0 has no ZoneLink; K1 contains its self-matching ZoneLink and same-Zone transport Provider; neither is copied to K0), `build: transport-settings-unknown-field`, `build: capability-ceiling-superset`, `build: missing-transport-provider`; run `make flake-matrix-pin` after adding flake checks", + "validation" : "`drift: zone-resource-schema`, `drift: zone-nix-options`, `build: zone-bundle-deterministic`, `build: parent-topology-sealed`, `build: child-local-zonelink-bundle` (K0 has no ZoneLink; K1 contains its self-matching ZoneLink and same-Zone transport Provider; neither is copied to K0), `build: zone-link-exact-six-fields`, `build: transport-settings-unknown-field`, `build: transport-credential-ref`, `build: missing-transport-provider`; run `make flake-matrix-pin` after adding flake checks", "workItemId" : "ADR046-routing-012" }, { diff --git a/docs/specs/ADR-046-zone-routing.md b/docs/specs/ADR-046-zone-routing.md index c8911fa4a..57ef43a2e 100644 --- a/docs/specs/ADR-046-zone-routing.md +++ b/docs/specs/ADR-046-zone-routing.md @@ -10,7 +10,7 @@ | Normative | Yes | | Owners | `d2b-zone-routing`, `d2b-bus` Zone route engine, core-controller ZoneLink handler | | Depends on | `ADR-046-terminology-and-identities`, `ADR-046-componentsession-and-bus`, `ADR-046-resource-api-and-authorization`, `ADR-046-resource-object-model`, `ADR-046-resource-reconciliation` | -| Supersedes | `RealmEntrypointTable`/`EntrypointMode`/`RealmControllerPlacement` enum (→ compiler-only `parentZone` topology + ZoneLink transport + private Zone runtime bootstrap placement); `RouteTreeEngine`/`routing.rs` route contracts (adapted, not deleted); `RemoteNodeRegistry` (→ ZoneLink controller); `OperationRouter` (→ ZoneLinkIdempotencyKey in d2b-bus); `realm_access_resolver` module (→ ZoneEntrypointResolver); `realm-controllers.json`/`realm-identity.json` bundle artifacts (→ Nix-authored Zone settings/ZoneLink resources); `d2b.realms.*` Nix option namespace (→ `d2b.zones.*`); `WorkloadId` routing targets (→ typed Guest/Host resource refs); `CapabilitySet`-only routing authz (→ per-hop RBAC narrowing + capability ceiling) | +| Supersedes | `RealmEntrypointTable`/`EntrypointMode`/`RealmControllerPlacement` enum (→ compiler-only `parentZone` topology + ZoneLink transport + private Zone runtime bootstrap placement); `RouteTreeEngine`/`routing.rs` route contracts (adapted, not deleted); `RemoteNodeRegistry` (→ ZoneLink controller); `OperationRouter` (→ ZoneLinkIdempotencyKey in d2b-bus); `realm_access_resolver` module (→ ZoneEntrypointResolver); `realm-controllers.json`/`realm-identity.json` bundle artifacts (→ Nix-authored Zone settings/ZoneLink resources); `d2b.realms.*` Nix option namespace (→ `d2b.zones.*`); `WorkloadId` routing targets (→ typed Guest/Host resource refs); `CapabilitySet`-only routing authz (→ per-hop RBAC narrowing + allocator-issued capability scope) | ## Source and reuse policy @@ -456,19 +456,19 @@ have mock/conformance implementations, and are tested, but: The provider trait family becomes `Provider` ResourceType with controller/service/worker Processes. The capability advertisement fields (`CapabilitySet`, `WorkloadCapabilitySet`, etc.) survive in the v3 -capability ceiling model. The trait boundary itself is replaced by typed +allocator-issued capability-scope model. The trait boundary itself is replaced by typed ResourceSpec/Status fields on the Provider resource. ### CapabilitySet model Current routing authz is entirely `CapabilitySet`-based: the route engine -propagates capability ceilings downward and checks that a requested +propagates allocated capability scopes downward and checks that a requested capability is covered. There is no per-hop RBAC subject/verb check. **ADR 0046 mapping:** -v3 adds a **RBAC layer above the capability ceiling**: at each hop, the +v3 adds a **RBAC layer above the allocated capability scope**: at each hop, the intermediate Zone evaluates a `relay` verb check against the forwarded -principal's RoleBinding. Capability ceiling propagation from +principal's RoleBinding. Capability-scope propagation from `RouteNamespaceAllocation` is preserved. The delta (RBAC relay verb check) is genuinely new; it does not exist anywhere in the current baseline. @@ -495,9 +495,10 @@ trust-session operations based on them today. Nix-authored child-local ZoneLink resources, and the runtime-created Zone self-resource. The `RealmControllersJson` schema retires when sealed allocator topology owns placement/path and ZoneLinks own transport/route state. -- `realm-identity.json` → superseded by ZoneLink - `spec.childStaticKeyFingerprint`. The `RealmIdentityConfigJson` schema - retires when ZoneLink enrollment handles key pinning. +- `realm-identity.json` → superseded by the allocator-sealed ZoneLink bootstrap + identity and the child-local `spec.transportCredentials` references defined + by the canonical ZoneLink schema. The `RealmIdentityConfigJson` schema + retires when ComponentSession enrollment handles key pinning. ### Realm access resolver @@ -596,102 +597,48 @@ authority from prior hops beyond what its own enrolled RoleBinding grants. ## ZoneLink resource -### Spec - -```yaml -apiVersion: resources.d2bus.org/v3 -type: ZoneLink -metadata: - name: k1-uplink - zone: k1 -spec: - # Required: stable local child name; must equal metadata.zone. - childZoneName: k1 - # Required: transport Provider that carries the link ComponentSession. - transportProviderRef: Provider/transport-unix - # Provider-specific transport binding (e.g. socket path class for unix, - # CID range for vsock, relay name for azure-relay). Validated by the - # transport Provider's schema; no raw socket path or credential bytes. - transportSettings: {} - # Child Zone's expected enrolled static public key fingerprint. - # Required for KK ComponentSession profile enforcement. - childStaticKeyFingerprint: - # Maximum capability ceiling propagated to child-local RoleBindings. - # The parent may only grant capabilities it itself holds. - capabilityCeiling: - resourceTypes: [Process, Volume, Network] - verbs: [get, list, watch, create, update-spec] - zones: [] # empty = only the child zone - executionRefs: [] # empty = child chooses - # Optional: preferred reconnect backoff profile (bounded seconds). - reconnectPolicy: - initialDelaySeconds: 1 - maxDelaySeconds: 60 - jitterFactor: 0.25 - # Whether local intents may accumulate while disconnected. - localIntentPolicy: queue # queue | drop | fail - # Maximum queued intents while disconnected (1–4096). - maxQueuedIntents: 256 - # Route advertisement renewal interval (seconds, 10–3600). - routeRenewalSeconds: 300 - # Maximum hop count for forwarded calls (1–16; counted at source). - maxHops: 8 -``` - -Rules: - -- `childZoneName` must satisfy `^[a-z][a-z0-9-]*$` and equal the enclosing - child Zone name. Local root has no uplink; a non-root Zone has at most one - ZoneLink resource, enabled or disabled. -- `transportProviderRef` must resolve to a Ready Provider in this same child - Zone. -- `capabilityCeiling` may only include capabilities granted by the sealed - parent allocator binding; the allocator intersection is enforced - structurally before the link becomes Ready. -- `childStaticKeyFingerprint` is the enrolled KK key fingerprint; the - handshake fails closed if the child presents a different static key. -- The `transportSettings` object is validated against the transport - Provider's JSON Schema before the link is activated. -- `maxHops` is enforced at the source d2b-bus; a call arriving with a - hop count equal to `maxHops` is refused before forwarding. -- `localIntentPolicy: queue` records `UpdateSpec`/`Create`/`Delete` - intents locally until reconnected; `drop` discards them silently; - `fail` returns `zone-link-disconnected` to the caller immediately. - -### Status - -```yaml -status: - observedGeneration: 1 - phase: Ready # Pending | Ready | Degraded | Failed | Unknown - conditions: - - type: SessionEstablished - status: "True" - reason: kk-handshake-ok - observedGeneration: 1 - lastTransitionAt: 2026-07-22T00:00:00Z - - type: ChildZoneReachable - status: "True" - reason: resource-api-probe-ok - observedGeneration: 1 - lastTransitionAt: 2026-07-22T00:00:01Z - - type: AdvertisementCurrent - status: "True" - reason: route-renewed - observedGeneration: 1 - lastTransitionAt: 2026-07-22T00:00:02Z - lastReconciledAt: 2026-07-22T00:01:00Z - # ZoneLink-specific status fields: - zoneLink: - childZoneUid: - sessionGeneration: 3 - reconnectCount: 1 - lastConnectedAt: 2026-07-22T00:00:00Z - lastDisconnectedAt: null - routeRevision: - queuedIntentCount: 0 - advertisedRouteIds: [] -``` +### Authoritative schema and routing interpretation + +[`ADR-046-resources-zone-control.md` §3](ADR-046-resources-zone-control.md) +is the sole normative ZoneLink ResourceType schema. A ZoneLink `spec` has +exactly these six top-level fields: + +1. `childZoneName` +2. `transportProviderRef` +3. `transportSettings` +4. `transportCredentials` +5. `disabled` +6. `limits` + +Unknown fields are rejected. This document does not define a second routing +schema. It only adds these routing interpretations: + +- `childZoneName` identifies the enclosing child Zone and must equal + `metadata.zone`; local root has no uplink and a non-root Zone has at most one + ZoneLink, enabled or disabled. +- `transportProviderRef` resolves to a Ready Provider in that same child Zone. + The Provider validates `transportSettings`, while `transportCredentials` + contains only same-Zone `Credential/` refs. +- `disabled: true` closes the session, withdraws admitted route projections, + and suppresses reconnect until re-enabled. +- `limits.maxPendingIntents`, `limits.maxActiveStreams`, + `limits.reconnectMaxAttempts`, and `limits.reconnectWindowSecs` bound routing + queues, streams, and reconnect activity. Transport-specific backoff remains + Provider-internal and bounded by those limits. +- The allocator-selected parent supplies the sealed ComponentSession identity, + route namespace, and allowed capability scope. None is an additional + ZoneLink spec field. +- The routing wire contract has one fixed, bounded hop budget; it is not + operator-configurable per ZoneLink. + +The canonical D088 status shape is also owned by +`ADR-046-resources-zone-control.md` §3.4. Universal `ResourceStatus` fields +remain directly under `status`; ZoneLink-common observations are under +`status.resource`; optional implementation observations are under +`status.provider`. Routing consumes the universal base plus +`status.resource`, including `childZoneUid`, `connected`, connection +timestamps, revision cursors, `linkEpoch`, `pendingLocalIntents`, and +`childAuthorized`. It never introduces another status container. Status phases: @@ -707,7 +654,7 @@ Status phases: The child-local core-controller ZoneLink handler owns status. It never writes `Ready` until the parent allocator has authenticated the child, acknowledged the route allocation, and successfully probed the child's `d2b.resource.v3` -service within the current session generation. The parent keeps private +service within the current link epoch. The parent keeps private allocation/route state, not a second resource row. ## Zone tree identity and prefix naming @@ -718,7 +665,7 @@ authors its local `ZoneLink/` uplink; the link name need not equal the child's self-name, while `spec.childZoneName` must. The provisioning parent allocator assigns the private tree edge and verifies the child self-name during KK enrollment. The acknowledged UID is recorded as -`status.zoneLink.childZoneUid`. +`status.resource.childZoneUid`. Zone tree positions are described as ordered label paths from the local root, most-specific first, matching the `RealmPath` grammar from @@ -741,7 +688,7 @@ Each Zone runtime maintains a local `RouteTreeEngine` (adapted from tracks: - **Parent entries**: for each child Zone, the immediate parent path, - optional route id, capability ceiling, and expiry. + optional route id, allocated capability scope, and expiry. - **Route entries**: for each descendant Zone reachable through this node, the advertising Zone, next-hop child label, route id, capability set, and expiry. @@ -880,7 +827,7 @@ schemas. and emits a generated `nixos-modules/generated/options-zones-.nix` for each ResourceType, overlaying typed submodule options onto `spec`. These generated files are committed and kept in sync by `make test-drift`. Field-level type errors (wrong -enum value, out-of-range integer, non-hex fingerprint, etc.) are therefore caught +enum value, out-of-range integer, malformed ResourceRef, etc.) are therefore caught at `nix eval` time via the generated option type, not by explicit assertions. #### Generated ZoneLink spec options (illustrative excerpt) @@ -901,44 +848,45 @@ at `nix eval` time via the generated option type, not by explicit assertions. transportSettings = mkOption { # Freeform; validated at build time against the transport Provider's # transportSettingsSchema. No socketPath, hostPath, password, token, - # or key top-level keys permitted. Credential refs use "Credential/". + # or key top-level keys permitted. type = types.attrs; default = {}; }; - childStaticKeyFingerprint = mkOption { - type = types.strMatching "^[0-9a-f]{64}$"; - description = "SHA-256 fingerprint (64 lower-hex chars) of the child's enrolled KK static public key."; + transportCredentials = mkOption { + type = types.listOf (types.strMatching "^Credential/[a-z][a-z0-9-]*$"); + default = []; + description = "Same-Zone Credential refs resolved for ComponentSession establishment."; + }; + disabled = mkOption { + type = types.bool; + default = false; }; - capabilityCeiling = { - resourceTypes = mkOption { - type = types.listOf (types.enum [ - "Credential" "EphemeralProcess" "Guest" "Host" "Network" - "Process" "Provider" "Volume" "Zone" "ZoneLink" - ]); - default = []; + limits = { + maxPendingIntents = mkOption { + type = types.ints.between 0 1024; + default = 256; }; - verbs = mkOption { - type = types.listOf (types.enum [ - "create" "delete" "get" "list" "relay" "update-spec" "watch" - ]); - default = []; + maxActiveStreams = mkOption { + type = types.ints.between 1 128; + default = 32; + }; + reconnectMaxAttempts = mkOption { + type = types.ints.positive; + default = 10; + }; + reconnectWindowSecs = mkOption { + type = types.ints.positive; + default = 300; }; - zones = mkOption { type = types.listOf types.str; default = []; }; - executionRefs = mkOption { type = types.listOf types.str; default = []; }; - }; - reconnectPolicy = { - initialDelaySeconds = mkOption { type = types.ints.between 1 300; default = 1; }; - maxDelaySeconds = mkOption { type = types.ints.between 1 3600; default = 60; }; - jitterFactor = mkOption { type = types.numbers.between 0.0 0.5; default = 0.25; }; }; - localIntentPolicy = mkOption { type = types.enum [ "drop" "fail" "queue" ]; default = "queue"; }; - maxQueuedIntents = mkOption { type = types.ints.between 1 4096; default = 256; }; - routeRenewalSeconds = mkOption { type = types.ints.between 10 3600; default = 300; }; - maxHops = mkOption { type = types.ints.between 1 16; default = 8; }; }; } ``` +This is an exact mirror of the six-field schema owned by +`ADR-046-resources-zone-control.md` §3. The generator rejects any additional +ZoneLink field rather than extending this excerpt. + ### Eval-time assertions These invariants require cross-resource context and cannot be expressed as @@ -987,17 +935,17 @@ d2b.zones.k1.resources.transport-unix = { d2b.zones.k1.resources.k1-uplink = { type = "ZoneLink"; spec = { - childZoneName = "k1"; - transportProviderRef = "Provider/transport-unix"; - transportSettings = {}; # unix transport uses allocator-issued FD; no config needed - childStaticKeyFingerprint = "a3f1e...c4b9"; # 64 lower-hex chars - capabilityCeiling = { - resourceTypes = [ "Network" "Process" "Volume" ]; - verbs = [ "create" "get" "list" "update-spec" "watch" ]; - zones = []; - executionRefs = []; + childZoneName = "k1"; + transportProviderRef = "Provider/transport-unix"; + transportSettings = {}; # allocator-issued FD; no path config + transportCredentials = []; + disabled = false; + limits = { + maxPendingIntents = 256; + maxActiveStreams = 32; + reconnectMaxAttempts = 10; + reconnectWindowSecs = 300; }; - # All remaining fields use schema defaults (reconnectPolicy, localIntentPolicy, etc.) }; }; ``` @@ -1033,32 +981,28 @@ d2b.zones.k2.resources.relay-sas-k2 = { d2b.zones.k2.resources.k2-uplink = { type = "ZoneLink"; spec = { - childZoneName = "k2"; - transportProviderRef = "Provider/transport-azure-relay"; - transportSettings = { - relayName = "d2b-k2"; # relay endpoint name; no key material - sasCredRef = "Credential/relay-sas-k2"; # SAS token via Credential ref + childZoneName = "k2"; + transportProviderRef = "Provider/transport-azure-relay"; + transportSettings = { + relayNamespaceId = "relns-d2b-prod"; + relayEntityId = "hc-d2b-k2"; }; - childStaticKeyFingerprint = "9d2e...7f01"; - capabilityCeiling = { - resourceTypes = [ "Process" ]; - verbs = [ "create" "get" "list" "watch" ]; - zones = []; - executionRefs = []; + transportCredentials = [ "Credential/relay-sas-k2" ]; + disabled = false; + limits = { + maxPendingIntents = 128; + maxActiveStreams = 32; + reconnectMaxAttempts = 10; + reconnectWindowSecs = 300; }; - localIntentPolicy = "queue"; - maxQueuedIntents = 128; - routeRenewalSeconds = 120; - maxHops = 4; }; }; ``` -Note that `spec.transportSettings.sasCredRef` is a `Credential/` ref, not -the SAS token value itself. The `transportSettingsSchema` for `transport-azure-relay` -annotates the `sasCredRef` field with `"credentialRef": true`; the build emitter -rejects any `transportSettings` field annotated `"secret": true` and requires that -secret material arrive only as `Credential/` refs. +The relay SAS token is referenced only through +`spec.transportCredentials`; `transportSettings` contains non-secret Provider +configuration. The build emitter rejects settings annotated `"secret": true` +and resolves every credential ref in the same child Zone. ### Build-time validation @@ -1087,16 +1031,12 @@ The Nix build phase runs `xtask gen-zone-resources` which: Fields annotated `"secret": true` are rejected; those annotated `"credentialRef": true` must be `"Credential/"` strings. -4. **Capability ceiling subset check**: the emitter verifies that every - child-local ZoneLink `capabilityCeiling` is a structural subset of the - provisioning parent's private allocator grant. Capabilities wider than the - sealed grant are a build error. - -5. **Ref resolution**: all `*Ref` and `*ProviderRef` values are resolved +4. **Ref resolution**: all `*Ref` and `*ProviderRef` values, including every + `transportCredentials` entry, are resolved against resources declared in the same `d2b.zones..resources` and fail the build if unresolvable. -6. **Conflict check**: duplicate `(type, zone, name)` tuples across the +5. **Conflict check**: duplicate `(type, zone, name)` tuples across the entire emitted bundle fail the build. ### Canonical ResourceSpec JSON shapes @@ -1140,31 +1080,23 @@ and the Nix-rendered resource bundle. }, "spec": { "childZoneName": "k1", - "transportProviderRef": "Provider/transport-unix", - "transportSettings": {}, - "childStaticKeyFingerprint": "a3f1e...c4b9", - "capabilityCeiling": { - "executionRefs": [], - "resourceTypes": ["Network", "Process", "Volume"], - "verbs": ["create", "get", "list", "update-spec", "watch"], - "zones": [] + "disabled": false, + "limits": { + "maxActiveStreams": 32, + "maxPendingIntents": 256, + "reconnectMaxAttempts": 10, + "reconnectWindowSecs": 300 }, - "reconnectPolicy": { - "initialDelaySeconds": 1, - "jitterFactor": 0.25, - "maxDelaySeconds": 60 - }, - "localIntentPolicy": "queue", - "maxHops": 8, - "maxQueuedIntents": 256, - "routeRenewalSeconds": 300 + "transportCredentials": [], + "transportProviderRef": "Provider/transport-unix", + "transportSettings": {} } } ``` The canonical form sorts all object keys lexicographically. Array ordering depends on the array's schema classification: order-significant arrays -(e.g. `capabilityCeiling.verbs`, named-stream lists) are preserved as authored; +(e.g. `transportCredentials`, named-stream lists) are preserved as authored; schema-declared set-like arrays (e.g. `resourceTypes` in capability specs) are sorted lexicographically. All fields with defaults are always emitted; no key omission. The `status` object is absent from the emitted bundle entirely. @@ -1395,15 +1327,16 @@ Required before ADR046-routing-011 through ADR046-routing-013 are complete: | `nix-unit: zone-parent-one-parent` | nix-unit eval | Conflicting scalar definitions reject through normal Nix module merging | | `nix-unit: zone-parent-cycle` | nix-unit eval | Two-node and longer `parentZone` cycles reject before bootstrap publication | | `nix-unit: zone-parent-depth` | nix-unit eval | Sixteen-name ancestry succeeds and seventeen-name ancestry rejects | -| `nix-unit: zone-link-fingerprint` | nix-unit eval | 63-char and non-hex fingerprints rejected at eval | +| `nix-unit: zone-link-credential-ref` | nix-unit eval | Malformed or cross-Zone `transportCredentials` refs rejected | | `nix-unit: zone-link-child-name` | nix-unit eval | `childZoneName` unequal to enclosing child Zone rejected | | `nix-unit: zone-link-one-uplink` | nix-unit eval | Second uplink (even disabled) and any local-root uplink rejected | -| `nix-unit: capability-ceiling-unknown-verb` | nix-unit eval | Unknown verb in `capabilityCeiling` rejected | +| `nix-unit: zone-link-closed-spec` | nix-unit eval | Any ZoneLink top-level spec field outside the canonical six is rejected | +| `nix-unit: zone-link-limits` | nix-unit eval | Queue, stream, reconnect-attempt, and reconnect-window bounds enforced | | `nix-unit: transport-binding-secret-key` | nix-unit eval | Binding with `key =` rejected at eval | | `drift: zone-resource-schema` | `make test-drift` | `xtask gen-zone-schemas && git diff --exit-code` passes | | `build: zone-bundle-deterministic` | flake check | Two identical configs produce identical `generationId` | | `build: transport-binding-unknown-field` | flake check | Unknown Provider binding key fails build | -| `build: capability-ceiling-superset` | flake check | Ceiling wider than parent grant fails build | +| `build: allocator-capability-scope` | flake check | Route capability scope wider than the sealed parent allocation fails build | | `build: missing-transport-provider` | flake check | Unresolvable `transportProviderRef` fails build | | `build: parent-topology-sealed` | flake check | Valid sorted `parentZone` rows compile only into the sealed allocator bootstrap input and never into `Zone.spec` or resource bundles | | `host-integration: cleanup-removed-zonelink` | NixOS test | Switch removes ZoneLink; assert `deletionRequestedAt` set; store transaction commits `Deleted` revision and removes row/index; audit record appended from committed revision with exactly-once recovery; generation reaches Ready; dynamic route entries deleted by ZoneLink controller teardown, not by generation diff | @@ -1428,7 +1361,7 @@ ZoneLinkRouteAdvertisement { controllerGeneration: // bound to the child controller lease routes: [ { descendant: ZonePath, nextHopChild: ZoneLabelId, routeId: , - capabilities: CapabilitySet } // capabilities narrowed by ceiling + capabilities: CapabilitySet } // narrowed by allocator policy ] // 1–64 routes issuedAtUnixSeconds: u64 expiresAtUnixSeconds: u64 // > issuedAt; max 7200 s @@ -1456,8 +1389,8 @@ Admission rules (adapted from `RouteTreeEngine::admit_advertisement`): 7. Each route's `descendant` must be a strict descendant of `advertisingZone`; next-hop label must be the immediate child of `advertisingZone` toward `descendant`. -8. Each route's `capabilities` must be a subset of the `capabilityCeiling` - in the `ZoneLinkNamespaceAllocation`. +8. Each route's `capabilities` must be a subset of `allowedCapabilities` + in the private `ZoneLinkNamespaceAllocation`. 9. Capacity check: projected physical entries after admission must not exceed `MAX_PARENT_ENTRIES` / `MAX_ROUTE_ENTRIES`; if admission would overflow, prune expired entries first; fail closed with @@ -1485,13 +1418,14 @@ or unknown `routeId` is silently accepted (idempotent). ### Renewal Advertisements expire. The child's ZoneLink controller issues a renewal -advertisement before expiry, at most every `routeRenewalSeconds`. A +advertisement before expiry using the bounded protocol scheduler. Renewal +timing is internal routing behavior, not a ZoneLink spec field. A renewal carries: - a new `issuedAt` / `expiresAt` window; - a new `signatureRef` (distinct from prior); - optionally updated `capabilities` (may narrow but not widen beyond the - parent's `capabilityCeiling`). + parent's private allocation). The engine admits the renewal exactly as a fresh advertisement, replacing the old entry for each `routeId` named in the renewal. The prior replay @@ -1501,8 +1435,8 @@ route is treated as a new advertisement. ### Namespace allocation A parent allocates a `ZoneLinkNamespaceAllocation` when the child-local -ZoneLink requests activation and when that local resource's -`capabilityCeiling` changes: +ZoneLink requests activation and whenever the parent allocator's private +route policy changes: ```text ZoneLinkNamespaceAllocation { @@ -1510,13 +1444,13 @@ ZoneLinkNamespaceAllocation { allocatedToGeneration: allowedPrefixes: [ZonePath] // child zone or descendants; 1–16 maxRoutes: u32 // 1–64 - capabilityCeiling: CapabilitySet + allowedCapabilities: CapabilitySet } ``` The child controller must sign advertisements that exactly match the -allocated edge, generation, allowed prefixes, and capability ceiling. -Allocation changes (e.g. ceiling narrowing) require the child to issue +allocated edge, generation, allowed prefixes, and capability scope. +Allocation changes (e.g. capability narrowing) require the child to issue a new advertisement under the new generation. The `ZoneLinkNamespaceAllocation` above **is** the explicit ZoneLink range @@ -1665,18 +1599,20 @@ The result is immutable route metadata. It carries no transport socket, relay endpoint, credential, or host path. d2b-bus uses the hop list to compose the sequence of ComponentSession calls that forward the request. -## Capability and RBAC ceiling narrowing +## Capability and RBAC narrowing -### Ceiling propagation +### Allocated capability propagation -Each `ZoneLink` spec declares a `capabilityCeiling`. This ceiling is the -maximum `CapabilitySet` the parent will route to the child. It is enforced +The parent allocator's private `ZoneLinkNamespaceAllocation` declares +`allowedCapabilities`, the maximum `CapabilitySet` the parent will route to +the child. It is not part of the ZoneLink ResourceSpec. The scope is enforced at two points: 1. **Advertisement admission**: the child's advertised `capabilities` for - each route must be a subset of `capabilityCeiling` in the namespace + each route must be a subset of `allowedCapabilities` in the namespace allocation (`CapabilitySet::is_subset_of`). A route advertising - capabilities beyond the ceiling is rejected with `namespace-violation`. + capabilities beyond the allocation is rejected with + `namespace-violation`. 2. **Bus route decision**: the `required_capability` for the operation is checked against the route's advertised capabilities at the target Zone. A missing capability returns `MissingCapability`. @@ -1695,7 +1631,7 @@ subject mapped from the ZoneLink's enrolled identity: child-local subject = child RoleBinding that: - names the parent Zone's link principal as its subjectRef - grants only the verbs/resourceTypes/names declared in the binding - - has a capability scope no wider than capabilityCeiling + - has a capability scope no wider than the allocator-issued scope ``` The parent cannot self-assert a subject, verb, or resource name in the @@ -1803,7 +1739,7 @@ be forwarded through a ZoneLink if: descriptor; - the call's `purpose` class is `remote-zone`; - RBAC at each hop grants the `relay` verb for the service name; -- the hop count does not exceed `maxHops`. +- the hop count does not exceed the fixed protocol limit of 16. The forwarded session carries: @@ -1975,23 +1911,26 @@ When a child-local ZoneLink ComponentSession disconnects: established this generation). 2. In-flight operations with a pinned path through this link fail immediately with `zone-link-disconnected`. -3. Child-to-parent intents may accumulate in the child store per - `localIntentPolicy`; parent-to-child operations fail immediately. -4. The child-local controller schedules a reconnect attempt using the - `reconnectPolicy` backoff. +3. Child-to-parent intents may accumulate in the child store up to + `spec.limits.maxPendingIntents`; parent-to-child operations fail + immediately. +4. The child-local controller schedules bounded Provider-internal reconnect + attempts, limited by `spec.limits.reconnectMaxAttempts` within + `spec.limits.reconnectWindowSecs`. 5. On reconnect: - - a new KK handshake is performed; the child static key is - re-verified against `childStaticKeyFingerprint`; - - a new session generation is assigned; - - all pinned-path tracking for the old generation is cleared; + - a new KK handshake is performed against the allocator-sealed enrolled + identity and resolved `spec.transportCredentials`; + - a new link epoch is assigned; + - all pinned-path tracking for the old epoch is cleared; - queued intents are replayed (with original idempotency keys if the queued mutation is younger than the retention window, or as new operations if older); - the child-local ZoneLink handler re-issues parent route/export advertisement watches from the last known parent revision or triggers `List` + reopen if the cursor expired. -6. After reconnect, `status.zoneLink.reconnectCount` is incremented and - `lastConnectedAt` is updated. +6. After reconnect, `status.resource.linkEpoch` is incremented and + `status.resource.lastConnectedAt` is updated atomically with the cursor + fields. During disconnected recovery, the ZoneLink controller updates only its own local status and outbound intent queue. It does not infer or mutate other @@ -2000,12 +1939,12 @@ cleanup or status correction without a live session. ### Revocation -A ZoneLink may be administratively revoked by setting its spec's -`capabilityCeiling` to an empty set and then deleting the resource. +A ZoneLink may be administratively revoked by setting +`spec.disabled: true` and then deleting the resource. Revocation sequence: -1. Spec update: `capabilityCeiling` set to empty. +1. Spec update: `disabled` set to `true`; the child handler stops reconnecting. 2. Child-local ZoneLink controller issues a route withdrawal for all advertised `routeId` values. 3. The parent's `RouteTreeEngine` removes all entries for the child. @@ -2041,8 +1980,8 @@ Additionally, the parent's `RouteTreeEngine` enforces: refers back to its own `advertisingZone` is rejected at admission. The hop counter enforced at source d2b-bus provides a belt-and-suspenders -limit independent of the tree walk. Default `maxHops` is 8; absolute -maximum is 16. +limit independent of the tree walk. The protocol-wide initial budget is 16 +and is not configurable in ZoneLink spec. ### Hop limits @@ -2058,16 +1997,16 @@ Hop counter enforcement: ## Local intents while disconnected -When `localIntentPolicy: queue` is set, the child-local ZoneLink handler may -enqueue `UpdateSpec`, `Create`, and `Delete` intents directed from the child to -parent/ancestor resources while the uplink is disconnected. Parent-to-child -mutations are not queued in the child and fail at the parent route boundary. +The child-local ZoneLink handler may enqueue `UpdateSpec`, `Create`, and +`Delete` intents directed from the child to parent/ancestor resources while +the uplink is disconnected. Parent-to-child mutations are not queued in the +child and fail at the parent route boundary. Behavior: - Intents are stored in the child Zone as bounded `ZoneLinkIntent` records, not resource mutations. No parent resource state is assumed. -- `maxQueuedIntents` limits the queue. When the queue is full, new intents - fail with `zone-link-intent-queue-full`. +- `spec.limits.maxPendingIntents` limits the queue. When the queue is full, + new intents fail with `zone-link-intent-queue-full`. - Intent records carry the original operation/idempotency/correlation IDs and a `queuedAt` timestamp. - On reconnect, intents are replayed in order with their original @@ -2077,12 +2016,8 @@ Behavior: not retried automatically; the caller is notified with the conflict. - A `Get`, `List`, or `Watch` call traversing a disconnected uplink always returns `zone-link-disconnected` immediately; it is never queued. -- `localIntentPolicy: drop` silently discards all intents during - disconnect and returns success to the caller (callers are not informed - of the drop; this mode is for fire-and-forget patterns where staleness - is acceptable). -- `localIntentPolicy: fail` returns `zone-link-disconnected` immediately - for all mutating calls; no intent is queued or dropped. +- If `maxPendingIntents` is zero, mutating calls fail immediately with + `zone-link-disconnected`; intents are never silently dropped. Local intent queueing does not constitute a claim of parent-Zone state. The ZoneLink handler does not infer parent resource phase, condition, or @@ -2232,8 +2167,7 @@ included in span attributes. | --- | --- | | `zone-link-disconnected` | ZoneLink session not established; call rejected | | `zone-link-revoked` | ZoneLink was administratively revoked | -| `zone-link-intent-queue-full` | Intent queue at `maxQueuedIntents` | -| `zone-link-intent-dropped` | Intent dropped due to `localIntentPolicy: drop` | +| `zone-link-intent-queue-full` | Intent queue reached `spec.limits.maxPendingIntents` | | `hop-limit-exceeded` | Forwarded call has no remaining hops | | `malformed-hop-count` | Hop counter in inbound frame claims more hops than allowed | | `relay-denied` | Intermediate Zone's RBAC denied relay | @@ -2242,7 +2176,7 @@ included in span attributes. | `zone-route-capability-denied` | Required capability absent from route | | `zone-route-loop` | NCA algorithm detected a cycle | | `zone-route-multi-parent` | Route table has conflicting parents for one child Zone | -| `zone-advertisement-namespace-violation` | Advertisement exceeds allocated namespace/ceiling | +| `zone-advertisement-namespace-violation` | Advertisement exceeds allocated namespace or capability scope | | `zone-advertisement-replay` | Duplicate advertisement (replay rejection) | | `zone-advertisement-expired` | Advertisement received after expiry | | `zone-advertisement-malformed` | Advertisement fails structural invariants | @@ -2259,9 +2193,10 @@ credentials, or provider diagnostics. Forwarded calls are re-authorized at each hop. 2. A child Zone's resource status is not inferred from local intents; the parent only learns child state from authenticated responses. -3. The KK handshake enforces `childStaticKeyFingerprint`; a child that - presents a different key is refused before any resource exchange. -4. Capability ceilings propagate monotonically downward; a child cannot +3. The KK handshake verifies the allocator-sealed enrolled identity and + resolved transport credentials; a different identity is refused before + any resource exchange. +4. Allocated capability scopes narrow monotonically downward; a child cannot advertise capabilities beyond what its parent allocated. 5. No FD, credential, or host path is forwarded. Transport bindings are provider-schema-validated opaque values. @@ -2341,7 +2276,7 @@ Evidence classes: **A** = implemented-and-reachable from production binary, | --- | --- | --- | --- | --- | | `EnrollmentRecord`, `KeyRotationPlan`, `RevocationRecord`, `SessionTeardownDirective` | `d2b-realm-core/src/enrollment.rs` | **B** | `RealmIdentityStore` only (itself unwired) | → ZoneLink controller session/revocation lifecycle | | `RealmIdentityStore` | `d2b-realm-core/src/identity_store.rs` | **B** | No production callers found | → ZoneLink controller enrollment state machine | -| `RealmIdentityConfigJson` (schema v2) | `d2b-realm-core/src/identity_config.rs` | **A** | Loaded at d2bd startup (lib.rs:1425) and priv-broker startup (runtime.rs:704); logs "runtime trust sessions remain inert" | → ZoneLink `spec.childStaticKeyFingerprint`; artifact retires | +| `RealmIdentityConfigJson` (schema v2) | `d2b-realm-core/src/identity_config.rs` | **A** | Loaded at d2bd startup (lib.rs:1425) and priv-broker startup (runtime.rs:704); logs "runtime trust sessions remain inert" | → allocator-sealed ComponentSession enrollment identity plus ZoneLink `spec.transportCredentials`; artifact retires | ### Allocator engine @@ -2357,7 +2292,7 @@ Evidence classes: **A** = implemented-and-reachable from production binary, | --- | --- | --- | --- | --- | | `WorkloadProvider`, `DisplayProvider`, `RuntimeProvider`, `PersistentShellProvider` traits | `d2b-realm-provider/src/provider.rs` | **B** | `d2b-host-providers` implements (itself unwired); `d2b-provider-aca`/`relay` implement and ARE wired for display session only | → Provider ResourceType controller/service processes | | `AcaWorkloadProvider` | `d2b-provider-aca/src/lib.rs` | **A** (display session path) | d2bd `new_gateway_display_runtime_from_config` (lib.rs:4165); ACA display gateway only | Not Zone routing; display path remains display-specific | -| `CapabilitySet`, `WorkloadCapabilitySet`, `DisplayCapabilitySet` | `d2b-realm-provider/src/capabilities.rs` | **B** | Traits only; d2b-gateway-runtime uses `DisplayCapabilitySet` | → v3 capability ceiling fields | +| `CapabilitySet`, `WorkloadCapabilitySet`, `DisplayCapabilitySet` | `d2b-realm-provider/src/capabilities.rs` | **B** | Traits only; d2b-gateway-runtime uses `DisplayCapabilitySet` | → v3 private route-allocation capability fields | | `ProviderCircuitBreaker` | `d2b-realm-provider/src/rate_limit.rs` | **B** | `d2b-provider-aca` only (display path) | → Provider rate-limit policy | ### Bundle artifacts and Nix options @@ -2376,7 +2311,7 @@ The following transitions are NOT simple textual renames: 1. **`RealmControllerPlacement` enum → private Zone runtime bootstrap placement + explicit parent topology + child-local uplink identity**: 6 variants collapse into per-Zone bootstrap configuration; compiler-only `parentZone` selects the allocator owner, and the child-local ZoneLink's `childZoneName`/`transportProviderRef` supplies transport state. The compiler seals the parent edge into allocator state. Placement and `parentZone` are not public `Zone.spec` fields; `Zone.spec` is `{}`. 2. **`WorkloadId` → Guest/Host split**: VM/sandbox workloads become `Guest`; local/bare-metal become `Host`. Classification is semantic, not mechanical. -3. **`CapabilitySet`-only authz → RBAC + capability ceiling**: current engine checks capability ceiling only. Per-hop `relay` verb RBAC is new. +3. **`CapabilitySet`-only authz → RBAC + allocated capability scope**: current engine checks a route allocation scope only. Per-hop `relay` verb RBAC is new. 4. **`RealmPath` DNS target form → Zone resource path**: grammar preserved; wire address format changes. 5. **`EntrypointMode` enum → child-local ZoneLink transport plus topology projection**: `HostResident`/`GatewayBacked` mode is replaced by the child's transport spec; parent routing and CLI inspection use sealed topology and authenticated route projection state. 6. **`realm-controllers.json`/`realm-identity.json` → sealed parent topology + child-local Zone/ZoneLink state**: data is loaded today but routing/trust sessions are explicitly inert. @@ -2384,8 +2319,8 @@ The following transitions are NOT simple textual renames: | Item | Treatment | | --- | --- | -| Behavior retained | Pure in-memory NCA tree-walk; loop/multi-parent detection; advertisement replay-window; capability ceiling propagation; idempotency dedup full 6-tuple `(realm, principal, node, operation_kind, idempotency_key)`; fail-closed on unknown realm/route; bounded audit label cardinality; `TreeRoutePath`/`TreeRouteHop` already exist (rename to ZoneRoutePath/ZoneRouteHop); `DirectShortcut*` machinery already exists; `RouteAuditEventKind` event set already exists | -| Required delta | ZoneLink ResourceType spec/status/intent queue (ADR-only); compiler-only validated `parentZone` map sealed into allocator bootstrap topology; RBAC `relay` verb check per intermediate hop; watch cursor resync over ZoneLink; named-stream credit forwarding; hop counter byte in wire frames; no-FD/credential structural rejection at serialization boundary; private Zone runtime bootstrap placement (replaces placement enum, not a public spec field); per-hop subject narrowing | +| Behavior retained | Pure in-memory NCA tree-walk; loop/multi-parent detection; advertisement replay-window; allocated capability-scope propagation; idempotency dedup full 6-tuple `(realm, principal, node, operation_kind, idempotency_key)`; fail-closed on unknown realm/route; bounded audit label cardinality; `TreeRoutePath`/`TreeRouteHop` already exist (rename to ZoneRoutePath/ZoneRouteHop); `DirectShortcut*` machinery already exists; `RouteAuditEventKind` event set already exists | +| Required delta | Consume the canonical ZoneLink ResourceType spec/status/intent contract from ADR046-zone-control-002; compiler-only validated `parentZone` map sealed into allocator bootstrap topology; RBAC `relay` verb check per intermediate hop; watch cursor resync over ZoneLink; named-stream credit forwarding; hop counter byte in wire frames; no-FD/credential structural rejection at serialization boundary; private Zone runtime bootstrap placement (replaces placement enum, not a public spec field); per-hop subject narrowing | | Reuse path | Copy and adapt `RouteTreeEngine` (rename RealmPath→ZonePath, RouteId→ZoneRouteId); copy `RouteAdvertisement`/`RouteNamespaceAllocation`/`TreeRoutePath`/`TreeRouteHop`/`RouteFailClosedReason`/`DirectShortcut*`/`RouteAuditEventKind`; copy `OperationRouter` idempotency dedup; extract `RealmEntrypointTable` suffix-match into ZoneEntrypointResolver; adapt KK handshake from `SecurePeerSession`/`PeerSession` | | Replacement/deletion | `RealmEntrypointTable`/`RouteTreeEngine` on RealmPath types retire after ZoneRouteEngine live; `RemoteNodeRegistry` retires when ZoneLink controller live; `WorkloadTargetIndex` retires when Guest/Host resource lookup live; CLI `Route::GatewayBacked` retires when ZoneLink handles all cross-Zone routing; `realm-controllers.json`/`realm-identity.json` retire when sealed `parentZone` topology, runtime Zone identity, and ZoneLink transport state replace them | | Feasibility proof | `route_engine.rs` inline test suite (45 functions at line 1202) proves NCA, advertisement, loop, capability, replay, DirectShortcut; `target_resolver.rs` tests prove suffix match; `lib.rs` tests prove idempotency dedup namespace | @@ -2403,10 +2338,10 @@ The following transitions are NOT simple textual renames: | Reuse source | Same v3 baseline commit `b5ddbed6` | | Reuse action | adapt | | Destination | `packages/d2b-contracts/src/v3/zone_routing.rs` | -| Detailed design | Rename RealmPath → ZonePath, RealmId → ZoneLabelId, RouteId → ZoneRouteId, ControllerGenerationId → ZoneLinkControllerGeneration; preserve all bounds/validation/serde; add ZoneLink-specific advertisement envelope fields (v3 schema version, capability ceiling field); preserve `RouteFailClosedReason` + add `zone-link-disconnected`, `hop-limit-exceeded`, `relay-denied`, `attachment-not-permitted-over-zone-link`; freeze v3 protobuf numbers separately from v2 Primary reuse disposition: `adapt`. Preserved source-plan detail: extract and adapt. | +| Detailed design | Rename RealmPath → ZonePath, RealmId → ZoneLabelId, RouteId → ZoneRouteId, ControllerGenerationId → ZoneLinkControllerGeneration; preserve all bounds/validation/serde; add ZoneLink-specific advertisement envelope fields (v3 schema version and private allocated-capability field); preserve `RouteFailClosedReason` + add `zone-link-disconnected`, `hop-limit-exceeded`, `relay-denied`, `attachment-not-permitted-over-zone-link`; freeze v3 protobuf numbers separately from v2 Primary reuse disposition: `adapt`. Preserved source-plan detail: extract and adapt. | | Integration | d2b-bus zone route engine and ZoneLink controller consume these types | | Data migration | Full reset; no v2 Realm route compatibility | -| Validation | Golden advertisement/path/failure vectors shared by Rust/Nix; property tests for NCA/loop/ceiling; replay-window tests; hop-count tests | +| Validation | Golden advertisement/path/failure vectors shared by Rust/Nix; property tests for NCA/loop/allocated-capability narrowing; replay-window tests; hop-count tests | | Removal proof | v3 old `RealmPath` route types retired after zone-routing engine is live and all callers switched | ### ADR046-routing-002 @@ -2419,7 +2354,7 @@ The following transitions are NOT simple textual renames: | Reuse source | Same v3 baseline commit `b5ddbed6` | | Reuse action | adapt | | Destination | `packages/d2b-zone-routing/src/engine.rs` | -| Detailed design | Adapt RouteTreeEngine to ZoneRouteEngine using ZonePath/ZoneRouteId/ZoneLinkControllerGeneration from ADR046-routing-001; preserve all NCA/loop/multi-parent/capability/replay/capacity logic; add `decide_route` enforcement of `maxHops` parameter; add hop-counter decrement in relay path; expose `ZoneRoutePath` in v3 types Primary reuse disposition: `adapt`. Preserved source-plan detail: extract and adapt. | +| Detailed design | Adapt RouteTreeEngine to ZoneRouteEngine using ZonePath/ZoneRouteId/ZoneLinkControllerGeneration from ADR046-routing-001; preserve all NCA/loop/multi-parent/capability/replay/capacity logic; add `decide_route` enforcement of the fixed protocol hop budget; add hop-counter decrement in relay path; expose `ZoneRoutePath` in v3 types Primary reuse disposition: `adapt`. Preserved source-plan detail: extract and adapt. | | Integration | d2b-bus calls `ZoneRouteEngine::decide_route` for every cross-Zone ResourceClient call; ZoneLink controller calls `admit_advertisement`/`admit_withdrawal` | | Data migration | None (pure in-memory engine) | | Validation | Copy exact `route_engine.rs` test suite adapted to ZonePath; add relay/hop-count/RBAC-narrowing/shortcut integration tests | @@ -2430,12 +2365,12 @@ The following transitions are NOT simple textual renames: | Field | Value | | --- | --- | | Work item ID | `ADR046-routing-003` | -| Dependency/owner | ADR046-routing-001, ADR046-routing-002; ZoneLink resource owner | +| Dependency/owner | ADR046-routing-001, ADR046-routing-002; Zone route resolver owner | | Current source | `packages/d2b-realm-core/src/routing.rs`: `RouteNamespaceAllocation`; `packages/d2b-realm-core/src/access.rs`: `RealmAccessResolverRequest/Response/Error`, `RealmAccessBinding`, `RealmTransportBinding`, `RealmAccessClientContract`, `UnixSocketPath`, `AccessBindingRef`, all access types (evidence: **B** — complete implementation, no production callers); `packages/d2bd/src/realm_access_resolver.rs`: `resolve_local_root_realm_access()`, `local_root_realm_access_client_contract()` (evidence: **B** — `pub mod` at `d2bd/src/lib.rs:117`, no callers from running daemon); `packages/d2b-realm-router/src/target_resolver.rs`: `RealmEntrypointTable`, `DispatchTarget`, `RealmEntrypoint`, `ResolveError` (evidence: **A**); `packages/d2b/src/lib.rs:5240`: `load_realm_entrypoint_table()` (evidence: **A**); `packages/d2b/src/target_routing.rs`: `Route::Local`/`Route::GatewayBacked` dispatch (evidence: **A**); `packages/d2b-realm-core/src/realm.rs`: `EntrypointMode`, `RealmControllerPlacement` (evidence: **A** as types; routing use **B**); `packages/d2b-core/src/realm_controller_config.rs`: `RealmControllersJson` (evidence: **C**); `nixos-modules/realm-controller-config-json.nix` (evidence: **C**) | | Reuse source | Same v3 baseline commit `b5ddbed6` | | Reuse action | adapt | -| Destination | `packages/d2b-contracts/src/v3/zone_link.rs` (ZoneLink spec/status types, intent types, namespace allocation); `packages/d2b-zone-routing/src/resolver.rs` (ZoneEntrypointResolver) | -| Detailed design | Child-local ZoneLink spec/status fields; self-name and one-uplink invariants; ZoneLinkIntent record stored in the child; ZoneLinkNamespaceAllocation issued by the exact parent allocator selected in sealed Nix-compiled `parentZone` topology; ZoneEntrypointResolver with longest-suffix match over ZonePath (adapted from `RealmEntrypointTable::resolve`) driven only by sealed `{ childZone, parentZone }` rows plus authenticated admitted route projections; no reciprocal parent-store resource or parent ZoneLink handler; fail closed on unknown topology, absent/stale projection, or unauthenticated route Primary reuse disposition: `adapt`. Preserved source-plan detail: extract and adapt. | +| Destination | `packages/d2b-zone-routing/src/resolver.rs` (ZoneEntrypointResolver) | +| Detailed design | Consume the canonical ZoneLink spec/status/intent types owned by ADR046-zone-control-002; ZoneLinkNamespaceAllocation is issued by the exact parent allocator selected in sealed Nix-compiled `parentZone` topology; ZoneEntrypointResolver uses longest-suffix match over ZonePath (adapted from `RealmEntrypointTable::resolve`) and is driven only by sealed `{ childZone, parentZone }` rows plus authenticated admitted route projections; no reciprocal parent-store resource or parent ZoneLink handler; fail closed on unknown topology, absent/stale projection, or unauthenticated route Primary reuse disposition: `adapt`. Preserved source-plan detail: extract and adapt. | | Integration | Child core-controller ZoneLink handler manages child-store ZoneLink resources; parent d2b-bus feeds sealed topology and authenticated `ZoneRouteEngine` projection state to ZoneEntrypointResolver for per-call dispatch | | Data migration | None; ZoneLink resources created from Nix configuration at v3 reset | | Validation | Longest-suffix match vectors over sealed topology; child-local ZoneLink spec validation; resolver rejects unknown/stale/withdrawn/unauthenticated route projections; parent-store fixture contains no ZoneLink row or handler | @@ -2448,14 +2383,14 @@ The following transitions are NOT simple textual renames: | Work item ID | `ADR046-routing-004` | | Dependency/owner | ADR046-routing-003; core-controller ZoneLink handler owner | | Current source | `packages/d2b-realm-router/src/remote_node.rs`: `RemoteNodeRegistry`, `RemoteNodeEntry`, `RemoteNodeAvailability`, `RemoteNodeErrorKind`, `RemoteRetryAction`, `ensure_remote_execution_generation` (evidence: **B** — only in `d2bd/src/realm_stubs.rs` dead_code seam); `packages/d2b-realm-router/src/session_lifecycle.rs`: `SessionLifecycle`, `SessionPhase` (evidence: **B** — same seam); `packages/d2bd/src/realm_stubs.rs`: compile-only seam (`#[allow(dead_code)]`, declared at `d2bd/src/lib.rs:249`); `packages/d2b-realm-core/src/enrollment.rs`: `EnrollmentRecord`, `EnrollmentStatus`, `KeyRotationPlan`, `RevocationRecord`, `SessionTeardownDirective`, `RecoveryProcedure`, `IdentityAuditEventKind` (evidence: **B** — consumed by `RealmIdentityStore` which itself has no production callers); `packages/d2b-realm-core/src/identity_store.rs`: `RealmIdentityStore` (evidence: **B** — no production callers); `packages/d2b-realm-core/src/identity_config.rs`: `RealmIdentityConfigJson` (evidence: **A** — loaded at d2bd/priv-broker startup, routing inert); `nixos-modules/realm-identity-config-json.nix` (evidence: **C**); `packages/d2bd/src/workload_target_index.rs`: `WorkloadTargetIndex` (evidence: **A** — called at `d2bd/src/lib.rs:16745`; this is the live bridge from realm metadata to VM-name dispatch; retires with Guest/Host resource lookups) | -| Main reuse source | `packages/d2b-session/src/lifecycle.rs` (`SessionLifecycle`, `SessionPhase`, `KeepaliveAction`, `poll_keepalive`, `disconnect`, `begin_reconnect`, `reconnect_established`, `close`; limits: `MAX_RECONNECT_ATTEMPTS=10`, `MAX_RECONNECT_WINDOW_MS=300000`; test: `lifecycle_keepalive_close_and_reconnect_change_generation`) — adapt as the ZoneLink session state machine inside the ZoneLink handler; generation-increment logic maps to `sessionGeneration` in ZoneLink status; reconnect policy comes from `spec.reconnectPolicy` instead of hard-coded LimitProfile | +| Main reuse source | `packages/d2b-session/src/lifecycle.rs` (`SessionLifecycle`, `SessionPhase`, `KeepaliveAction`, `poll_keepalive`, `disconnect`, `begin_reconnect`, `reconnect_established`, `close`; limits: `MAX_RECONNECT_ATTEMPTS=10`, `MAX_RECONNECT_WINDOW_MS=300000`; test: `lifecycle_keepalive_close_and_reconnect_change_generation`) — adapt as the ZoneLink session state machine inside the ZoneLink handler; generation-increment logic maps to `status.resource.linkEpoch`; reconnect bounds come from `spec.limits.reconnectMaxAttempts` and `spec.limits.reconnectWindowSecs` | | Reuse source | Same v3 baseline commit `b5ddbed6` | | Reuse action | adapt | | Destination | `packages/d2b-core-controller/src/zone_links.rs` | -| Detailed design | Child-local ZoneLink handler in core-controller: manages local ResourceSpec→allocator-bound session→advertisement lifecycle; session state machine (Pending/Established/Disconnected/Reconnecting/Revoked); reconnect backoff per `reconnectPolicy`; advertisement issuance/renewal/withdrawal using enrolled KK ComponentSession; child-store route cursor and outbound intent queue; capability ceiling change handling; status writer; aggregate metrics use only closed semantic phase/reason/outcome labels and never `link_name_hash` or another ZoneLink/Zone/resource identity label; Nix-compiled `parentZone` selects the parent allocator, which alone owns privileged listeners, placement, and route namespace and creates no reciprocal resource. Primary reuse disposition: `adapt`. Preserved source-plan detail: extract and adapt. | +| Detailed design | Child-local ZoneLink handler in core-controller: consumes the exact six-field ZoneLink schema from ADR046-zone-control-002 and manages local ResourceSpec→allocator-bound session→advertisement lifecycle; session state machine (Pending/Established/Disconnected/Reconnecting/Revoked); Provider-internal reconnect backoff bounded by `spec.limits`; advertisement issuance/renewal/withdrawal using enrolled KK ComponentSession; child-store route cursor and bounded outbound intent queue; private allocator capability-scope changes; D088 `status.resource` writer; aggregate metrics use only closed semantic phase/reason/outcome labels and never `link_name_hash` or another ZoneLink/Zone/resource identity label; Nix-compiled `parentZone` selects the parent allocator, which alone owns privileged listeners, placement, and route namespace and creates no reciprocal resource. Primary reuse disposition: `adapt`. Preserved source-plan detail: extract and adapt. | | Integration | Child core-controller process → local transport Provider → sealed binding for the allocator selected by `parentZone` → d2b-bus ComponentSession; child ZoneLink handler exchanges advertisements while that parent ZoneRouteEngine admits/withdraws them | | Data migration | New ZoneLink resources from Nix configuration; no prior enrollment compatibility | -| Validation | Session lifecycle tests; reconnect/revocation/ceiling-change; intent queue drain; cursor resync; advertisement renewal timing; fake-child tests; structural metric descriptor test asserts `vm`, `zone`, `zone_id`, `zone_uid`, and `link_name_hash` are absent and a ZoneLink-name canary never enters label values | +| Validation | Session lifecycle tests; reconnect/disabled/revocation/allocator-policy-change; intent queue drain; cursor resync; advertisement renewal timing; fake-child tests; structural metric descriptor test asserts `vm`, `zone`, `zone_id`, `zone_uid`, and `link_name_hash` are absent and a ZoneLink-name canary never enters label values | | Removal proof | `RemoteNodeRegistry` retired after all enrolled peer routing moves to ZoneLink handler | ### ADR046-routing-005 @@ -2613,10 +2548,10 @@ The following transitions are NOT simple textual renames: | Reuse source | Same v3 baseline `b5ddbed6`; `assertions.nix` pattern reused for Zone assertions; `realm-controller-config-json.nix` is the structural template | | Reuse action | adapt | | Destination | `nixos-modules/options-zones.nix` (new structural base), `nixos-modules/generated/resource-types.nix` (generated registry), `nixos-modules/generated/options-zones-.nix` (generated per ResourceType by `xtask gen-zone-nix-options`), `nixos-modules/assertions.nix` (new Zone assertions) | -| Detailed design | Declare compiler-only scalar `d2b.zones..parentZone` plus structural `d2b.zones..resources. = { type = ...; spec = {}; }` as specified in the "Option schema" section above. `parentZone` has no default, is required for every non-root Zone, forbidden on `local-root`, resolves to one declared Zone, and never enters a ResourceSpec. Wire `options-zones.nix` and all `generated/options-zones-*.nix` files into `nixos-modules/default.nix`. Add a new `xtask gen-zone-nix-options` command that reads `docs/reference/schemas/v3/.schema.json` for each ResourceType, derives `generated/resource-types.nix`, and emits a generated submodule overlaying typed spec options (types, bounds, enum constraints, defaults, docs) onto `d2b.zones..resources..spec`. The generated registry's standard subset must equal the canonical 19-type registry from `ADR-046-resource-object-model` exactly; installed signed Provider schemas may append qualified types. These generated modules are committed and kept in sync by `xtask gen-zone-nix-options && git diff --exit-code` wired into `make test-drift`. Because the generated options carry field-level type constraints, field-level eval errors (wrong enum, out-of-range int, non-hex fingerprint) are caught without explicit assertions. Explicit assertions in `nixos-modules/assertions.nix` cover cross-resource invariants only: zone/resource key name regex, reserved names, `parentZone` required/forbidden/existence/self/cycle/16-name-depth constraints, child-local `childZoneName == zone`, at most one uplink resource per non-root Zone and none in local root, ref resolution, count limits, and transportSettings secret-key exclusion (listed in the eval-time assertions table). Conflicting parent scalar definitions fail through standard Nix module merging. Each new assertion must have a matching case in `tests/unit/nix/cases/zone-assertions.nix` (nix-unit auto-discovered). `d2b.realms` option namespace is NOT removed in this work item. Primary reuse disposition: `adapt`. Preserved source-plan detail: new module following same pattern. | +| Detailed design | Declare compiler-only scalar `d2b.zones..parentZone` plus structural `d2b.zones..resources. = { type = ...; spec = {}; }` as specified in the "Option schema" section above. `parentZone` has no default, is required for every non-root Zone, forbidden on `local-root`, resolves to one declared Zone, and never enters a ResourceSpec. Wire `options-zones.nix` and all `generated/options-zones-*.nix` files into `nixos-modules/default.nix`. Add a new `xtask gen-zone-nix-options` command that reads `docs/reference/schemas/v3/.schema.json` for each ResourceType, derives `generated/resource-types.nix`, and emits a generated submodule overlaying typed spec options (types, bounds, enum constraints, defaults, docs) onto `d2b.zones..resources..spec`. The generated registry's standard subset must equal the canonical 19-type registry from `ADR-046-resource-object-model` exactly; installed signed Provider schemas may append qualified types. These generated modules are committed and kept in sync by `xtask gen-zone-nix-options && git diff --exit-code` wired into `make test-drift`. The ZoneLink module is generated from the exact six-field schema owned by ADR046-zone-control-002 and rejects any seventh field. Because the generated options carry field-level type constraints, field-level eval errors (wrong enum, out-of-range int, malformed ref) are caught without explicit assertions. Explicit assertions in `nixos-modules/assertions.nix` cover cross-resource invariants only: zone/resource key name regex, reserved names, `parentZone` required/forbidden/existence/self/cycle/16-name-depth constraints, child-local `childZoneName == zone`, at most one uplink resource per non-root Zone and none in local root, ref resolution, count limits, and transportSettings secret-key exclusion (listed in the eval-time assertions table). Conflicting parent scalar definitions fail through standard Nix module merging. Each new assertion must have a matching case in `tests/unit/nix/cases/zone-assertions.nix` (nix-unit auto-discovered). `d2b.realms` option namespace is NOT removed in this work item. Primary reuse disposition: `adapt`. Preserved source-plan detail: new module following same pattern. | | Integration | ADR046-routing-012 consumes the validated `parentZone` map for private allocator bootstrap sealing and iterates `d2b.zones..resources.*` for resource-bundle emission | | Data migration | None; Zone options are new; Realm options retained until migration PR | -| Validation | `nix-unit: zone-name-regex`, all five `nix-unit: zone-parent-*` vectors, `nix-unit: zone-link-fingerprint`, `nix-unit: zone-link-child-name`, `nix-unit: zone-link-one-uplink`, `nix-unit: capability-ceiling-unknown-verb`, `nix-unit: transport-settings-secret-key`; add `drift: standard-resource-type-registry` asserting the generated standard subset is exactly all 19 canonical types with no omission/addition/duplicate/reordering, plus `drift: zone-nix-options` (`xtask gen-zone-nix-options && git diff --exit-code`); run `make nix-unit-pin` after adding eval cases | +| Validation | `nix-unit: zone-name-regex`, all five `nix-unit: zone-parent-*` vectors, `nix-unit: zone-link-credential-ref`, `nix-unit: zone-link-child-name`, `nix-unit: zone-link-one-uplink`, `nix-unit: zone-link-closed-spec`, `nix-unit: zone-link-limits`, `nix-unit: transport-settings-secret-key`; add `drift: standard-resource-type-registry` asserting the generated standard subset is exactly all 19 canonical types with no omission/addition/duplicate/reordering, plus `drift: zone-nix-options` (`xtask gen-zone-nix-options && git diff --exit-code`); run `make nix-unit-pin` after adding eval cases | | Removal proof | `nixos-modules/options-realms-workloads.nix` `d2b.realms` namespace retires after all hosts migrate to `d2b.zones` | ### ADR046-routing-012 @@ -2629,10 +2564,10 @@ The following transitions are NOT simple textual renames: | Reuse source | `realm-controller-config-json.nix` structural template; `xtask gen-schemas` extension point (main `a1cc0b2d` unchanged in this area) | | Reuse action | adapt | | Destination | `nixos-modules/zone-resources-json.nix` (new), private local-root allocator bootstrap compiler/sealer input (not a ResourceSpec or public bundle), `nixos-modules/bundle-artifacts.nix` (new row for per-Zone `resource-bundle.json`), `packages/xtask/src/main.rs` (`gen-zone-schemas` subcommand emitting `docs/reference/schemas/v3/.schema.json` for Zone and ZoneLink; `gen-zone-nix-options` subcommand emitting `nixos-modules/generated/options-zones-.nix`) | -| Detailed design | `zone-resources-json.nix` iterates `d2b.zones..resources.*` to produce the canonical sorted resource list: for each entry, render `{ apiVersion, type, metadata: { name, zone, ownerRef: , labels: , annotations: }, spec: }`. Separately canonicalize sorted `{ childZone, parentZone }` rows from the compiler-only topology and seal them into the private allocator bootstrap input; `parentZone` never enters a resource bundle or `Zone.spec`, and a topology digest change releases/reallocates affected edges independently of resource `generationId`. Per-Zone generation is strict: local root's generated bundle contains no ZoneLink; a non-root Zone's enabled uplink and referenced transport Provider appear together only in that child's bundle; no emitter copies either resource into the selected parent's bundle. The bundle JSON omits `managedBy` and `configurationGeneration`; the configuration service/core sets those fields when activating the validated bundle. Sort all resources by `(type, zone, name)`. Compute `generationId` as SHA-256 (lower hex) of the UTF-8 bytes of the sorted `resources` array JSON. Compute `integrity` as SHA-256 (base64url, no padding) of the full bundle JSON with integrity field zeroed. Install at `/etc/d2b/zones//resource-bundle.json` root:d2bd 0640. Canonical form: all object keys sorted lexicographically; order-significant arrays preserved; schema-declared set-like arrays sorted lexicographically; all optional fields emitted with defaults; no field renaming or restructuring. Build-time validation runs in a Nix derivation: (1) validate the complete parent map (non-root required, local-root forbidden, declared target, one scalar parent, not self, acyclic, max 16 names); (2) validate each resource against the committed JSON Schema; (3) validate `transportSettings` for each child-local ZoneLink against its same-Zone Provider's `transportSettingsSchema` — `transportProviderRef` is always explicit, never inferred or defaulted; (4) verify capability ceilings are subsets of the sealed selected-parent allocator grants; (5) verify `childZoneName == metadata.zone`, at most one uplink resource per non-root Zone, and no local-root uplink; (6) check for duplicate `(type, zone, name)` tuples. Providers MUST commit their `transportSettingsSchema` before any ZoneLink can reference them. Drift gates: `xtask gen-zone-schemas && git diff --exit-code` and `xtask gen-zone-nix-options && git diff --exit-code` both wired into `make test-drift`. Add `checks.${system}.zone-schema-drift` to `flake.nix`. Primary reuse disposition: `adapt`. Preserved source-plan detail: extend and adapt. | +| Detailed design | `zone-resources-json.nix` iterates `d2b.zones..resources.*` to produce the canonical sorted resource list: for each entry, render `{ apiVersion, type, metadata: { name, zone, ownerRef: , labels: , annotations: }, spec: }`. Separately canonicalize sorted `{ childZone, parentZone }` rows from the compiler-only topology and seal them into the private allocator bootstrap input; `parentZone` never enters a resource bundle or `Zone.spec`, and a topology digest change releases/reallocates affected edges independently of resource `generationId`. Per-Zone generation is strict: local root's generated bundle contains no ZoneLink; a non-root Zone's enabled uplink and referenced transport Provider appear together only in that child's bundle; no emitter copies either resource into the selected parent's bundle. The bundle JSON omits `managedBy` and `configurationGeneration`; the configuration service/core sets those fields when activating the validated bundle. Sort all resources by `(type, zone, name)`. Compute `generationId` as SHA-256 (lower hex) of the UTF-8 bytes of the sorted `resources` array JSON. Compute `integrity` as SHA-256 (base64url, no padding) of the full bundle JSON with integrity field zeroed. Install at `/etc/d2b/zones//resource-bundle.json` root:d2bd 0640. Canonical form: all object keys sorted lexicographically; order-significant arrays preserved; schema-declared set-like arrays sorted lexicographically; all optional fields emitted with defaults; no field renaming or restructuring. Build-time validation runs in a Nix derivation: (1) validate the complete parent map (non-root required, local-root forbidden, declared target, one scalar parent, not self, acyclic, max 16 names); (2) validate each resource against the committed JSON Schema, including the exact six-field ZoneLink schema from ADR046-zone-control-002; (3) validate `transportSettings` for each child-local ZoneLink against its same-Zone Provider's `transportSettingsSchema` — `transportProviderRef` is always explicit, never inferred or defaulted; (4) resolve every same-Zone `transportCredentials` ref; (5) verify `childZoneName == metadata.zone`, at most one uplink resource per non-root Zone, and no local-root uplink; (6) check for duplicate `(type, zone, name)` tuples. Private route capability policy is sealed in allocator bootstrap state and is not a ZoneLink ResourceSpec field. Providers MUST commit their `transportSettingsSchema` before any ZoneLink can reference them. Drift gates: `xtask gen-zone-schemas && git diff --exit-code` and `xtask gen-zone-nix-options && git diff --exit-code` both wired into `make test-drift`. Add `checks.${system}.zone-schema-drift` to `flake.nix`. Primary reuse disposition: `adapt`. Preserved source-plan detail: extend and adapt. | | Integration | The local-root allocator consumes sealed parent topology independently of resource bundles; `nixos-modules/bundle-artifacts.nix` installs each per-Zone `resource-bundle.json`; ADR046-routing-013 Zone runtime reads it on startup | | Data migration | None; new artifact file | -| Validation | `drift: zone-resource-schema`, `drift: zone-nix-options`, `build: zone-bundle-deterministic`, `build: parent-topology-sealed`, `build: child-local-zonelink-bundle` (K0 has no ZoneLink; K1 contains its self-matching ZoneLink and same-Zone transport Provider; neither is copied to K0), `build: transport-settings-unknown-field`, `build: capability-ceiling-superset`, `build: missing-transport-provider`; run `make flake-matrix-pin` after adding flake checks | +| Validation | `drift: zone-resource-schema`, `drift: zone-nix-options`, `build: zone-bundle-deterministic`, `build: parent-topology-sealed`, `build: child-local-zonelink-bundle` (K0 has no ZoneLink; K1 contains its self-matching ZoneLink and same-Zone transport Provider; neither is copied to K0), `build: zone-link-exact-six-fields`, `build: transport-settings-unknown-field`, `build: transport-credential-ref`, `build: missing-transport-provider`; run `make flake-matrix-pin` after adding flake checks | | Removal proof | `realm-controllers.json` artifact retires after Zone runtime is live and all hosts migrated | ### ADR046-routing-013 diff --git a/docs/specs/providers/ADR-046-provider-transport-azure-relay.md b/docs/specs/providers/ADR-046-provider-transport-azure-relay.md index 8ef727864..99c1aba88 100644 --- a/docs/specs/providers/ADR-046-provider-transport-azure-relay.md +++ b/docs/specs/providers/ADR-046-provider-transport-azure-relay.md @@ -864,7 +864,7 @@ channel. The child-local service assigned the listener role: One `OpenTransport` call creates one carriage. When the relay WebSocket closes for any reason, the named byte stream is closed by the Provider process and core receives the close as a transport loss event. Core then applies its own -reconnect policy and issues a new `OpenTransport` call. +bounded reconnect lifecycle and issues a new `OpenTransport` call. #### `CloseTransport(handle)` @@ -875,9 +875,10 @@ session. #### `ObserveTransport(handle) → Stream` Returns a stream of bounded carriage health observations: Azure auth events, -WebSocket open/close events, and stable error codes. **Core aggregates these -into the ZoneLink transport status sub-object.** The relay service does not -write ZoneLink status directly. +WebSocket open/close events, and stable error codes. **Core aggregates shared +observations into canonical ZoneLink `status.resource` fields and bounded +Azure-specific observations into `status.provider.details`.** The relay service +does not write ZoneLink status directly. ### Core ownership @@ -1059,8 +1060,9 @@ Credential resource. The relay transport service does **not** write ZoneLink status. The child Zone's core controller receives a `Stream` from -`ObserveTransport` and aggregates those observations into its local ZoneLink -transport status sub-object. There is no parent-side ZoneLink status or handler. +`ObserveTransport` and aggregates those observations into the canonical local +ZoneLink `status.resource` fields. There is no parent-side ZoneLink status or +handler. Per D088, child core writes the ZoneLink universal `ResourceStatus` base at top-level `status.*` and cross-provider ZoneLink/transport observation under @@ -1082,24 +1084,26 @@ status: phase: Ready conditions: [...] resource: - zoneLink: - sessionGeneration: 3 - reconnectCount: 1 - lastConnectedAt: 2026-07-22T00:00:00Z - ... - transport: - providerRef: Provider/transport-azure-relay - phase: Connected # Pending | Connected | Reconnecting | Failed | Unknown - lastConnectedAt: 2026-07-22T00:00:00Z - lastDisconnectedAt: null - lastDisconnectReason: null # bounded redacted string; no secret bytes - reconnectAttempt: 0 + childZoneUid: + connected: true + lastConnectedAt: 2026-07-22T00:00:00Z + lastDisconnectedAt: null + lastSentRevision: 14 + lastAckedRevision: 14 + lastReceivedRevision: 27 + lastAppliedRevision: 27 + linkEpoch: 3 + pendingLocalIntents: 0 + childAuthorized: true provider: providerRef: Provider/transport-azure-relay schemaId: transport-azure-relay.d2bus.org/ZoneLink/status schemaVersion: "1.0" observedProviderGeneration: 3 details: + transportPhase: Connected # Pending | Connected | Reconnecting | Failed | Unknown + lastDisconnectReason: null # bounded redacted string; no secret bytes + reconnectAttempt: 0 relayEndpoint: namespaceId: relns-d2b-prod # non-secret; echoed from spec.transportSettings entityId: hc-d2b-k2 # non-secret; echoed from spec.transportSettings From 434b22fd2f619ec5a5c0c0440569a75511c430ff Mon Sep 17 00:00:00 2001 From: John Vicondoa Date: Fri, 24 Jul 2026 11:43:11 -0700 Subject: [PATCH 101/115] docs: keep credential identity out of telemetry --- docs/specs/ADR-046-resources-credential.md | 56 ++++++++++---- ...46-provider-credential-managed-identity.md | 65 +++++++++++----- ...-046-provider-credential-secret-service.md | 76 ++++++++++++------- 3 files changed, 135 insertions(+), 62 deletions(-) diff --git a/docs/specs/ADR-046-resources-credential.md b/docs/specs/ADR-046-resources-credential.md index 37390f748..7be6ca7ef 100644 --- a/docs/specs/ADR-046-resources-credential.md +++ b/docs/specs/ADR-046-resources-credential.md @@ -834,13 +834,21 @@ Audit records for Credential operations: | Event | Fields retained | | --- | --- | -| Credential resource create/update/delete | Zone, subject digest, ResourceRef, verb, revision result, authorization decision | -| `AcquireToken` | Zone, subject digest, credential ResourceRef, operation class, `rotationGeneration`, outcome code, idempotency key digest | -| `RefreshToken` | Zone, subject digest, credential ResourceRef, operation class, `rotationGeneration`, outcome code, idempotency key digest | -| `RevokeToken` | Zone, subject digest, credential ResourceRef, operation class, `rotationGeneration`, revocation result code | -| `SignChallenge` | Zone, subject digest, credential ResourceRef, operation class, outcome code (no signature bytes) | -| Rotation | Zone, credential ResourceRef, trigger reason, old `rotationGeneration`, new `rotationGeneration`, outcome code | -| Provider generation change revocation | Zone, credential ResourceRef, policy applied, outcome code | +| Credential resource create/update/delete | Zone, subject digest, `resource_name_digest`, verb, revision result, authorization decision | +| `AcquireToken` | Zone, subject digest, `resource_name_digest`, operation class, `rotationGeneration`, outcome code, idempotency key digest | +| `RefreshToken` | Zone, subject digest, `resource_name_digest`, operation class, `rotationGeneration`, outcome code, idempotency key digest | +| `RevokeToken` | Zone, subject digest, `resource_name_digest`, operation class, `rotationGeneration`, revocation result code | +| `SignChallenge` | Zone, subject digest, `resource_name_digest`, operation class, outcome code (no signature bytes) | +| Rotation | Zone, `resource_name_digest`, trigger reason, old `rotationGeneration`, new `rotationGeneration`, outcome code | +| Provider generation change revocation | Zone, `resource_name_digest`, policy applied, outcome code | + +`resource_name_digest` is the existing bounded audit identity: SHA-256 of the +Credential resource name, never the raw name. It is emitted only into the +authorization-controlled Zone audit stream and, for caller-initiated +operations, after the authorization decision. Raw Credential name, ResourceRef, +and UID are excluded even from these records. The digest is audit-only and is +never copied to OTEL Resource attributes, span attributes, metric labels, logs, +collector diagnostics, or support summaries. Excluded from all audit records: token bytes, key material, passwords, bearer strings, provider-internal diagnostics, host paths, connection strings, @@ -871,11 +879,23 @@ Required span attributes (closed set): | `d2b.credential.outcome` | Stable closed outcome code | | `d2b.credential.rotation_generation` | Numeric rotation generation | -Zone and Credential identity use the `d2b.zone` and `d2b.credential.name` -OTEL resource attributes only. They are forbidden as span attributes. -Also forbidden from spans/resource attributes: token bytes, audience literals, -provider diagnostics, host paths, resource IDs, tenant/subscription IDs, -endpoint URIs, and correlation IDs that embed secret shapes. +Credential telemetry uses only applicable generic OTEL Resource attributes from +the collector's closed allowlist: + +| Resource attribute | Value | +| --- | --- | +| `d2b.zone` | Zone name, re-stamped at trusted ingress | +| `d2b.provider` | Closed Provider name | +| `d2b.component` | Signed component ID | +| `service.name` | Fixed service name | +| `service.namespace` | Fixed service namespace | +| `service.version` | Build version | + +No OTEL Resource attribute or span attribute carries a Credential resource +name, ResourceRef, UID, digest (including `resource_name_digest`), or derived +identity token. Also forbidden: token bytes, audience literals, provider +diagnostics, host paths, resource IDs, tenant/subscription IDs, endpoint URIs, +and correlation IDs that embed secret shapes. ### Metrics @@ -889,8 +909,12 @@ endpoint URIs, and correlation IDs that embed secret shapes. Label cardinality is bounded and semantic. The expiry gauge reports the minimum seconds remaining across active leases in each provider/placement -aggregate (0 when none); it carries no Credential resource name. Zone and Credential identity remain available in bounded OTEL resource -attributes and permitted audit fields, never metric labels or span attributes. +aggregate (0 when none). Metric labels carry no Credential resource name, +ResourceRef, UID, digest, or derived identity token. Credential identity is +available only as `resource_name_digest` in the authorized bounded audit +records above; it never appears in telemetry. Generic allowlisted OTEL Resource +attributes such as `d2b.zone`, `d2b.provider`, and `d2b.component` remain +available and are not copied into metric labels or span attributes. ## Nix configuration @@ -1998,8 +2022,8 @@ config formalizes this as an `OpaqueAzureRef` with the same charset restriction. | Current source | `packages/d2b-core/src/privileges.rs:SecretAccess` (implemented-and-reachable); `d2b-realm-provider/src/error.rs:ProviderDiagnostic`/`contains_sensitive_shape` (implemented-and-reachable); `packages/d2b-contract-tests/tests/policy_observability.rs` (reachable audit policy tests) | | Reuse action | adapt | | Destination | `packages/d2b-provider-credential-/src/audit.rs`, `telemetry.rs`; `packages/d2b-contract-tests/tests/credential_audit.rs` | -| Detailed design | Implement audit record emission for all credential service methods and controller events using the field set defined in §Audit; implement OTEL span/metric emission using the closed semantic label set in §OTEL and metrics, with expiry reported as a provider/placement aggregate and no Credential/Zone/resource-name-derived label; implement `contains_sensitive_shape` check in all string fields of audit records and metric label values (adapted from `d2b-realm-provider/src/error.rs:contains_sensitive_shape`); add canary-enforcement tests that verify `"secret-canary"`, `"entra-token-canary"`, `"managed-identity-canary"`, Credential `metadata.name`, and Zone name values never appear in any metric label, span attribute, log line, or status field; identity remains only in allowed OTEL resource attributes and permitted audit fields | +| Detailed design | Implement audit record emission for all credential service methods and controller events using the field set defined in §Audit, with Credential identity represented only by the authorized bounded `resource_name_digest`; implement OTEL span/metric emission using the closed semantic label set in §OTEL and metrics, with expiry reported as a provider/placement aggregate and no Credential resource name, ResourceRef, UID, digest, derived identity token, Zone/resource-name-derived label, or non-allowlisted OTEL Resource attribute; retain applicable generic collector-allowlisted Resource attributes (`d2b.zone`, `d2b.provider`, `d2b.component`, and service fields); implement `contains_sensitive_shape` checks in all string fields of audit records and metric label values (adapted from `d2b-realm-provider/src/error.rs:contains_sensitive_shape`); add canary-enforcement tests that verify `"secret-canary"`, `"entra-token-canary"`, `"managed-identity-canary"`, Credential name/ref/UID/digest canaries, and Zone name values never appear in any metric label, span attribute, log line, or status field, and that Credential identity canaries are also absent from OTEL Resource attributes | | Integration | Credential controller and service handlers emit audit records and telemetry through Zone audit/OTEL paths | | Data migration | None — full d2b 3.0 reset; no prior state to migrate | -| Validation | Canary tests across all three Provider crates; audit record field-presence tests; structural metric descriptor tests assert exact absence of `vm`, `zone`, `zone_id`, `zone_uid`, `credential_name`, and every resource-name-derived key plus Credential/Zone-name canary absence; resource attribute tests preserve allowed `d2b.zone`/`d2b.credential.name` identity while span attribute tests reject identity and forbidden fields | +| Validation | Canary tests across all three Provider crates; authorized audit record field-presence tests require `resource_name_digest` and reject raw Credential name/ResourceRef/UID; structural metric descriptor tests assert exact absence of `vm`, `zone`, `zone_id`, `zone_uid`, `credential_name`, `credential_ref`, `credential_uid`, `credential_digest`, `resource_name_digest`, and every resource-name-derived key; Credential name/ref/UID/digest canaries are absent from every OTEL Resource attribute, span attribute, and metric label; Zone-name canaries are absent from spans and labels while resource-attribute tests preserve the generic collector allowlist including `d2b.zone`, `d2b.provider`, `d2b.component`, and service fields; complete Credential metric/span frames pass the shared collector ingress validator, while adding `d2b.credential.name` or any Credential identity key/value rejects the whole frame | | Removal proof | Not applicable | diff --git a/docs/specs/providers/ADR-046-provider-credential-managed-identity.md b/docs/specs/providers/ADR-046-provider-credential-managed-identity.md index 9607c25b0..9eef64cbd 100644 --- a/docs/specs/providers/ADR-046-provider-credential-managed-identity.md +++ b/docs/specs/providers/ADR-046-provider-credential-managed-identity.md @@ -899,7 +899,7 @@ spec: purpose: credential-managed-identity.d2bus.org/credential-service serviceFingerprint: credential.d2bus.org/CredentialService.v3 locality: host-local - visibility: authorized-consumers + visibility: zone attachmentPolicy: component-session consumerPolicy: same-zone-authorized lifecyclePolicy: recycle-with-producer @@ -1281,17 +1281,24 @@ All error messages: | Event | Fields retained | | --- | --- | -| Credential resource create/update/delete | Zone, subject digest, `Credential/`, verb, revision result, authorization decision | -| `AcquireToken` | Zone, subject digest, `Credential/`, `operation_class="acquire-token"`, `rotationGeneration`, outcome code, idempotency key digest | -| `RefreshToken` | Zone, subject digest, `Credential/`, `operation_class="refresh-token"`, `rotationGeneration`, outcome code, idempotency key digest | -| `RevokeToken` | Zone, subject digest, `Credential/`, `operation_class="revoke-token"`, `rotationGeneration`, revocation result code | -| `InspectMetadata` | Zone, subject digest, `Credential/`, `operation_class="inspect-metadata"`, outcome code | -| Proactive rotation | Zone, `Credential/`, trigger `proactive-window`, old `rotationGeneration`, new `rotationGeneration`, outcome code | -| Provider generation change revocation | Zone, `Credential/`, policy applied (`immediate` or `drain-leases`), outcome code | -| IMDS unavailability onset | Zone, `Credential/`, consecutive-failure count, outcome code `credential-provider-unavailable` | -| Agent spawn | Zone, `Credential/`, agent Process name, `executionRef`, outcome code | -| Agent Process failure | Zone, `Credential/`, agent Process name, failure reason (closed code), failure count | -| **Deleted-phase closure** | Zone, `Credential/`, `phase=Deleted`, `finalizer=credential.d2bus.org/provider-revoke`, `cleanupLatencyMs`, final `rotationGeneration`, outcome `resource-deleted` | +| Credential resource create/update/delete | Zone, subject digest, `resource_name_digest`, verb, revision result, authorization decision | +| `AcquireToken` | Zone, subject digest, `resource_name_digest`, `operation_class="acquire-token"`, `rotationGeneration`, outcome code, idempotency key digest | +| `RefreshToken` | Zone, subject digest, `resource_name_digest`, `operation_class="refresh-token"`, `rotationGeneration`, outcome code, idempotency key digest | +| `RevokeToken` | Zone, subject digest, `resource_name_digest`, `operation_class="revoke-token"`, `rotationGeneration`, revocation result code | +| `InspectMetadata` | Zone, subject digest, `resource_name_digest`, `operation_class="inspect-metadata"`, outcome code | +| Proactive rotation | Zone, `resource_name_digest`, trigger `proactive-window`, old `rotationGeneration`, new `rotationGeneration`, outcome code | +| Provider generation change revocation | Zone, `resource_name_digest`, policy applied (`immediate` or `drain-leases`), outcome code | +| IMDS unavailability onset | Zone, `resource_name_digest`, consecutive-failure count, outcome code `credential-provider-unavailable` | +| Agent spawn | Zone, `resource_name_digest`, agent Process name, `executionRef`, outcome code | +| Agent Process failure | Zone, `resource_name_digest`, agent Process name, failure reason (closed code), failure count | +| **Deleted-phase closure** | Zone, `resource_name_digest`, `phase=Deleted`, `finalizer=credential.d2bus.org/provider-revoke`, `cleanupLatencyMs`, final `rotationGeneration`, outcome `resource-deleted` | + +`resource_name_digest` is SHA-256 of the Credential resource name, never the +raw name. It is admitted only to the authorization-controlled bounded Zone +audit stream and, for caller-initiated operations, after the authorization +decision. Raw Credential name, ResourceRef, and UID are excluded. The digest +is never copied to telemetry, logs, collector diagnostics, or support +summaries. The **Deleted-phase closure** audit record is written by the **audit subsystem**, not by the controller. The controller's only deletion-time action is to clear @@ -1342,8 +1349,21 @@ d2b.credential.provider_health_check | `d2b.credential.outcome` | Stable closed outcome code | | `d2b.credential.rotation_generation` | Numeric rotation generation | -Zone and Credential identity use the `d2b.zone` and `d2b.credential.name` -OTEL resource attributes only. They are forbidden as span attributes. +Credential telemetry uses only applicable generic OTEL Resource attributes from +the collector's closed allowlist: + +| Resource attribute | Value | +| --- | --- | +| `d2b.zone` | Zone name, re-stamped at trusted ingress | +| `d2b.provider` | `credential-managed-identity` | +| `d2b.component` | Signed controller/agent component ID | +| `service.name` | Fixed controller or agent service name | +| `service.namespace` | Fixed service namespace | +| `service.version` | Build version | + +No OTEL Resource attribute or span attribute carries a Credential resource +name, ResourceRef, UID, digest (including `resource_name_digest`), or derived +identity token. ### Forbidden from spans and resource attributes @@ -1367,8 +1387,12 @@ embed secret shapes. (`azure-imds`, `azure-imds-aca`); no resolved endpoint URL appears in this label. Label cardinality is bounded and semantic. The expiry gauge reports the minimum seconds remaining across active leases in each provider/placement -aggregate (0 when none). Credential and Zone identity remain in the bounded -OTEL attributes above and permitted audit fields, never metric labels. +aggregate (0 when none). Metric labels carry no Credential resource name, +ResourceRef, UID, digest, or derived identity token. Credential identity is +available only as `resource_name_digest` in authorized bounded audit records, +never telemetry. Generic allowlisted OTEL Resource attributes such as +`d2b.zone`, `d2b.provider`, and `d2b.component` remain available and are not +copied into metric labels or span attributes. --- @@ -1836,10 +1860,10 @@ item. | Current source | `packages/d2b-realm-provider/src/error.rs:contains_sensitive_shape`; `packages/d2b-core/src/realm_workloads_launcher.rs:LauncherMetadataInvariants.no_secrets_or_credentials`; main `a1cc0b2d` managed-identity canary tests listed in §Source reuse | | Reuse action | adapt | | Destination | packages/d2b-provider-credential-managed-identity/src/{audit.rs,telemetry.rs}; packages/d2b-contract-tests/tests/credential_audit.rs | -| Detailed design | Shared audit/OTEL: emit audit records for all methods and controller events per §Audit; emit OTEL spans and metrics per §OTEL and metrics with no Zone/Credential/resource-name-derived label; report expiry as the minimum for each provider/placement aggregate; add `d2b_credential_imds_calls_total` counter with bounded `alias` label; enforce `contains_sensitive_shape` on all string fields in audit records and metric labels; add canary tests for `managed-identity-canary`, `credential_canary`, `imds-endpoint-canary`, Credential `metadata.name`, and Zone name in `canary.rs`. Primary reuse disposition: `adapt`. Preserved source-plan detail: adapt existing sensitive-shape guard and canary pattern to v3 audit, OTEL, and metric surfaces. | +| Detailed design | Shared audit/OTEL: emit audit records for all methods and controller events per §Audit, with Credential identity represented only by the authorized bounded `resource_name_digest`; emit OTEL spans and metrics per §OTEL and metrics with no Credential resource name, ResourceRef, UID, digest, derived identity token, Zone/Credential/resource-name-derived label, or non-allowlisted OTEL Resource attribute; retain applicable generic collector-allowlisted Resource attributes (`d2b.zone`, `d2b.provider`, `d2b.component`, and service fields); report expiry as the minimum for each provider/placement aggregate; add `d2b_credential_imds_calls_total` counter with bounded `alias` label; enforce `contains_sensitive_shape` on all string fields in audit records and metric labels; add canary tests for `managed-identity-canary`, `credential_canary`, `imds-endpoint-canary`, Credential name/ref/UID/digest, and Zone name in `canary.rs`. Primary reuse disposition: `adapt`. Preserved source-plan detail: adapt existing sensitive-shape guard and canary pattern to v3 audit, OTEL, and metric surfaces. | | Integration | Controller and agent service methods call audit/telemetry helpers; audit subsystem and OTEL exporters consume bounded redacted records; contract tests validate credential audit shape across providers. | | Data migration | None — audit/telemetry only; no runtime state import | -| Validation | `packages/d2b-contract-tests/tests/credential_audit.rs`; managed-identity `canary.rs`; audit/OTEL unit tests structurally assert exact absence of `vm`, `zone`, `zone_id`, `zone_uid`, `credential_name`, and every resource-name-derived label key, reject Credential/Zone-name label canaries, preserve allowed OTEL resource identity attributes, reject identity span attributes, and reject sensitive shapes | +| Validation | `packages/d2b-contract-tests/tests/credential_audit.rs` requires `resource_name_digest` in authorized audit records and rejects raw Credential name/ResourceRef/UID; managed-identity `canary.rs` and audit/OTEL unit tests structurally assert exact absence of `vm`, `zone`, `zone_id`, `zone_uid`, `credential_name`, `credential_ref`, `credential_uid`, `credential_digest`, `resource_name_digest`, and every resource-name-derived label key; reject Credential name/ref/UID/digest canaries from all OTEL Resource attributes, span attributes, and metric labels; preserve generic collector-allowlisted Resource attributes including `d2b.zone`, `d2b.provider`, `d2b.component`, and service fields; reject Zone-name span/label canaries and sensitive shapes; pass complete managed-identity metric/span frames through the shared collector ingress validator and prove that adding `d2b.credential.name` or any Credential identity key/value rejects the whole frame | | Removal proof | None — audit/telemetry helpers are additive; no prior owner to remove | --- @@ -1935,13 +1959,14 @@ audit record field set conformance, delivery session binding contract, RBAC | Test | Validates | | --- | --- | -| `"managed-identity-canary"` absent from every `FakeClient` response DTO field, status field, audit record field, metric label value, OTEL span attribute, and delivery record outer header | Zero-secret-bytes invariant | +| `"managed-identity-canary"` absent from every `FakeClient` response DTO field, status field, audit record field, metric label value, OTEL Resource attribute, OTEL span attribute, and delivery record outer header | Zero-secret-bytes invariant | | `"credential_canary"` absent from same surfaces | Cross-provider canary consistency | | `"imds-endpoint-canary"` (a value that looks like an IMDS endpoint URL) absent from all output surfaces | Endpoint URL exclusion | | `clientId` value absent from audit records, metric labels, OTEL spans, error messages, and log lines | Config field exclusion | | `imdsEndpointAlias` value absent from OTEL span attributes, audit records, and error messages | Alias exclusion | | IMDS response-shaped string absent from status, audit, OTEL, and logs | IMDS response content exclusion | -| Metric descriptors contain no `vm`, `zone`, `zone_id`, `zone_uid`, `credential_name`, or resource-name-derived key; Credential/Zone-name canaries are absent from emitted label values while allowed OTEL resource identity attributes remain and identity span attributes are absent | Structural metric identity-label exclusion | +| Metric descriptors contain no `vm`, `zone`, `zone_id`, `zone_uid`, `credential_name`, `credential_ref`, `credential_uid`, `credential_digest`, `resource_name_digest`, or resource-name-derived key; Credential name/ref/UID/digest canaries are absent from OTEL Resource attributes, span attributes, and metric labels; Zone-name canaries are absent from spans and labels while generic collector-allowlisted Resource attributes remain | Structural telemetry identity exclusion | +| Complete metric/span frames with only generic allowlisted Resource attributes are accepted by the shared collector ingress validator; injecting `d2b.credential.name` or any Credential name/ref/UID/digest key or value rejects the whole frame | Closed collector allowlist gate | #### `delivery.rs` diff --git a/docs/specs/providers/ADR-046-provider-credential-secret-service.md b/docs/specs/providers/ADR-046-provider-credential-secret-service.md index 3385a0bab..899f03adb 100644 --- a/docs/specs/providers/ADR-046-provider-credential-secret-service.md +++ b/docs/specs/providers/ADR-046-provider-credential-secret-service.md @@ -355,9 +355,9 @@ finalizer is present. Canonical sequence: revoke/drain → delete scoped Process `expiresAtUnixMs` passes. 3. If `leaseState=Revoked` or `leaseState=Expired`: proceed without further network calls (terminal state satisfies the finalizer). -4. Emit bounded revoke outcome audit record (Zone, credential ResourceRef, - operation class, rotationGeneration, revocation outcome code; no token bytes, - paths, or provider diagnostics). The controller MUST NOT emit a +4. Emit bounded revoke outcome audit record (Zone, `resource_name_digest`, + operation class, rotationGeneration, revocation outcome code; no token + bytes, paths, or provider diagnostics). The controller MUST NOT emit a resource-deleted closure audit; that is appended by the audit subsystem after the core event-only `Deleted` revision with dedup/exactly-once recovery. 5. Issue a controller-initiated Delete for the scoped `Process` resource owned @@ -463,8 +463,8 @@ For `rotation.policy = "proactive"`: `sha256(credential_uid || (rotation_generation + 1).to_le_bytes() || b"acquire")`. 3. On grant: increment `rotationGeneration`; store new `leaseHandle` and `expiresAtUnixMs`; clear `RotationDue`; commit status; emit rotation audit - record (Zone, credential ResourceRef, trigger reason, old rotationGeneration, - new rotationGeneration, outcome code). + record (Zone, `resource_name_digest`, trigger reason, old + rotationGeneration, new rotationGeneration, outcome code). 4. Old lease remains valid until the new lease is confirmed active. 5. A duplicate `issue_lease` with the same idempotency key returns the same grant without double-issuing. @@ -619,7 +619,7 @@ spec: purpose: credential-secret-service.d2bus.org/credential-service serviceFingerprint: credential.d2bus.org/CredentialService.v3 locality: host-local - visibility: authorized-consumers + visibility: zone attachmentPolicy: component-session consumerPolicy: same-zone-authorized lifecyclePolicy: recycle-with-producer @@ -985,15 +985,22 @@ host paths, or connection string shapes. | Event | Retained fields | | --- | --- | -| Credential resource create/update/delete | Zone, subject digest, ResourceRef, verb, revision result, authorization decision | -| `AcquireToken` | Zone, subject digest, credential ResourceRef, operation class, `rotationGeneration`, outcome code, idempotency key digest | -| `RefreshToken` | Zone, subject digest, credential ResourceRef, operation class, `rotationGeneration`, outcome code, idempotency key digest | -| `RevokeToken` | Zone, subject digest, credential ResourceRef, operation class, `rotationGeneration`, revocation result code | -| Rotation | Zone, credential ResourceRef, trigger reason, old `rotationGeneration`, new `rotationGeneration`, outcome code | -| Provider generation change revocation | Zone, credential ResourceRef, policy applied, outcome code | -| Finalize (`provider-revoke`) | Zone, credential ResourceRef, revocation outcome, `revokedAtUnixMs` | +| Credential resource create/update/delete | Zone, subject digest, `resource_name_digest`, verb, revision result, authorization decision | +| `AcquireToken` | Zone, subject digest, `resource_name_digest`, operation class, `rotationGeneration`, outcome code, idempotency key digest | +| `RefreshToken` | Zone, subject digest, `resource_name_digest`, operation class, `rotationGeneration`, outcome code, idempotency key digest | +| `RevokeToken` | Zone, subject digest, `resource_name_digest`, operation class, `rotationGeneration`, revocation result code | +| Rotation | Zone, `resource_name_digest`, trigger reason, old `rotationGeneration`, new `rotationGeneration`, outcome code | +| Provider generation change revocation | Zone, `resource_name_digest`, policy applied, outcome code | +| Finalize (`provider-revoke`) | Zone, `resource_name_digest`, revocation outcome, `revokedAtUnixMs` | | Bundle activated | Zone, `activationGeneration`, digest, create/update/skip/removed counts | -| Cleanup complete *(audit subsystem only; appended post-core-deletion; not emitted by controller)* | Zone, credential ResourceRef, event-only Deleted revision committed and row/indexes removed atomically, `activationGeneration`, `cleanupLatencyMs` | +| Cleanup complete *(audit subsystem only; appended post-core-deletion; not emitted by controller)* | Zone, `resource_name_digest`, event-only Deleted revision committed and row/indexes removed atomically, `activationGeneration`, `cleanupLatencyMs` | + +`resource_name_digest` is SHA-256 of the Credential resource name, never the +raw name. It is admitted only to the authorization-controlled bounded Zone +audit stream and, for caller-initiated operations, after the authorization +decision. Raw Credential name, ResourceRef, and UID are excluded. The digest +is never copied to telemetry, logs, collector diagnostics, or support +summaries. Excluded from all audit records: token bytes, key material, passwords, bearer strings, provider-internal diagnostics, host paths, connection strings, audience @@ -1021,11 +1028,23 @@ Required span attributes (closed set): | `d2b.credential.outcome` | Stable closed outcome code | | `d2b.credential.rotation_generation` | Numeric rotation generation | -Zone and Credential identity use the `d2b.zone` and `d2b.credential.name` -OTEL resource attributes only. They are forbidden as span attributes. Also -forbidden: token bytes, audience literals, provider diagnostics, host paths, -Secret Service object paths, collection names, resource IDs, and correlation -IDs embedding secret shapes. +Credential telemetry uses only applicable generic OTEL Resource attributes from +the collector's closed allowlist: + +| Resource attribute | Value | +| --- | --- | +| `d2b.zone` | Zone name, re-stamped at trusted ingress | +| `d2b.provider` | `credential-secret-service` | +| `d2b.component` | Signed controller/service component ID | +| `service.name` | Fixed controller/service name | +| `service.namespace` | Fixed service namespace | +| `service.version` | Build version | + +No OTEL Resource attribute or span attribute carries a Credential resource +name, ResourceRef, UID, digest (including `resource_name_digest`), or derived +identity token. Also forbidden: token bytes, audience literals, provider +diagnostics, host paths, Secret Service object paths, collection names, +resource IDs, and correlation IDs embedding secret shapes. ### 11.5 Metrics @@ -1039,9 +1058,13 @@ IDs embedding secret shapes. The expiry gauge reports the minimum seconds remaining across active user-agent leases (0 when none). Label cardinality is bounded and semantic; -Credential and Zone identity remain in bounded OTEL resource attributes and -permitted audit fields, never metric labels or span attributes. Secret-shape -assertions run on all label values. +metric labels carry no Credential resource name, ResourceRef, UID, digest, or +derived identity token. Credential identity is available only as +`resource_name_digest` in authorized bounded audit records, never telemetry. +Generic allowlisted OTEL Resource attributes such as `d2b.zone`, +`d2b.provider`, and `d2b.component` remain available and are not copied into +metric labels or span attributes. Secret-shape assertions run on all label +values. --- @@ -1303,10 +1326,10 @@ generation cleanup contract in `nixos-modules/options-resources.nix` and | Current source | `packages/d2b-core/src/realm_workloads_launcher.rs:LauncherMetadataInvariants.no_secrets_or_credentials`; secret-service main reuse canary tests listed in §14 | | Reuse action | adapt | | Destination | packages/d2b-provider-credential-secret-service/src/{audit.rs,telemetry.rs} | -| Detailed design | Audit/OTEL: emit audit records and OTEL spans/metrics for all credential service methods and controller events, with canary enforcement, expiry aggregated across user-agent leases, no Zone/Credential/resource-name-derived metric label, and no token/object-path/lease bytes in status, delivery outer headers, audit, metrics, spans, or logs. Full detail remains in `ADR-046-resources-credential` §Implementation work items. Primary reuse disposition: `adapt`. Preserved source-plan detail: adapt zero-secret invariant and canary test pattern to credential-secret-service audit/OTEL surfaces. | +| Detailed design | Audit/OTEL: emit authorized bounded audit records with Credential identity represented only by `resource_name_digest`, and emit OTEL spans/metrics for all credential service methods and controller events with canary enforcement, expiry aggregated across user-agent leases, no Credential resource name, ResourceRef, UID, digest, derived identity token, Zone/Credential/resource-name-derived metric label, or non-allowlisted OTEL Resource attribute; retain applicable generic collector-allowlisted Resource attributes (`d2b.zone`, `d2b.provider`, `d2b.component`, and service fields); no token/object-path/lease bytes in status, delivery outer headers, audit, metrics, spans, or logs. Full detail remains in `ADR-046-resources-credential` §Implementation work items. Primary reuse disposition: `adapt`. Preserved source-plan detail: adapt zero-secret invariant and canary test pattern to credential-secret-service audit/OTEL surfaces. | | Integration | Controller and service methods call audit/telemetry helpers; audit subsystem and OTEL exporters consume bounded event/span/metric records; canary tests verify every public observable surface stays secret-free. | | Data migration | None — audit/telemetry only; no runtime state migration | -| Validation | Credential audit/OTEL tests from `ADR-046-resources-credential`; `tests/canary.rs` structurally asserts exact absence of `vm`, `zone`, `zone_id`, `zone_uid`, `credential_name`, and every resource-name-derived metric key plus Credential/Zone-name label canary absence while preserving allowed OTEL resource identity attributes and rejecting identity span attributes; `tests/delivery.rs` for credential-secret-service | +| Validation | Credential audit/OTEL tests from `ADR-046-resources-credential` require `resource_name_digest` in authorized audit records and reject raw Credential name/ResourceRef/UID; `tests/canary.rs` structurally asserts exact absence of `vm`, `zone`, `zone_id`, `zone_uid`, `credential_name`, `credential_ref`, `credential_uid`, `credential_digest`, `resource_name_digest`, and every resource-name-derived metric key; Credential name/ref/UID/digest canaries are absent from all OTEL Resource attributes, span attributes, and metric labels; Zone-name canaries are absent from spans and labels while generic collector-allowlisted Resource attributes remain; complete secret-service metric/span frames pass the shared collector ingress validator, while adding `d2b.credential.name` or any Credential identity key/value rejects the whole frame; `tests/delivery.rs` covers credential-secret-service delivery | | Removal proof | None — audit/telemetry helpers are new; no prior owner to remove | Implements audit record and OTEL span/metric emission for all credential @@ -1378,8 +1401,9 @@ All `check_provider_conformance` arms pass for `d2b-provider-credential-secret-s | `canary_absent_status_json` | `credential_canary` absent from status JSON serialization | | `object_path_absent_all_responses` | `object_path_canary` absent from all response DTOs | | `canary_absent_audit_records` | `credential_canary` and `object_path_canary` absent from all audit record JSON | -| `canary_absent_span_attributes` | Neither canary present in any OTEL span attribute captured by test subscriber | -| `metric_identity_labels_absent` | No descriptor key is `vm`, `zone`, `zone_id`, `zone_uid`, `credential_name`, or resource-name-derived; Credential/Zone-name canaries are absent from values; allowed OTEL resource identity attributes remain and identity span attributes are absent | +| `canary_absent_telemetry_attributes` | Neither secret canary nor any Credential name/ref/UID/digest canary is present in OTEL Resource or span attributes; generic collector-allowlisted Resource attributes remain | +| `metric_identity_labels_absent` | No descriptor key is `vm`, `zone`, `zone_id`, `zone_uid`, `credential_name`, `credential_ref`, `credential_uid`, `credential_digest`, `resource_name_digest`, or resource-name-derived; Credential name/ref/UID/digest and Zone-name canaries are absent from label values; Zone-name canaries are also absent from span attributes | +| `collector_allowlist_frame_accepted` | Complete metric/span frames with only generic allowlisted Resource attributes are accepted; injecting `d2b.credential.name` or any Credential name/ref/UID/digest key or value rejects the whole frame | | `canary_absent_delivery_binding` | Neither canary present in delivery session binding parameters | #### `tests/delivery.rs` From 371739fb2a0af7c6e2eaebb3f9ffe12843d18f57 Mon Sep 17 00:00:00 2001 From: John Vicondoa Date: Fri, 24 Jul 2026 11:43:31 -0700 Subject: [PATCH 102/115] docs: route QEMU TAP fds through Core --- docs/specs/ADR-046-resources-network.md | 68 ++++++++--- .../ADR-046-provider-runtime-qemu-media.md | 106 ++++++++++++------ 2 files changed, 121 insertions(+), 53 deletions(-) diff --git a/docs/specs/ADR-046-resources-network.md b/docs/specs/ADR-046-resources-network.md index 077d8203e..0cb796d37 100644 --- a/docs/specs/ADR-046-resources-network.md +++ b/docs/specs/ADR-046-resources-network.md @@ -541,8 +541,10 @@ spec: allowedDomains: [system] budget: { ... } # Core resolves the Network owner relationship and supplies tap/macvtap FDs - # to Provider/runtime-cloud-hypervisor through an authorized LaunchTicket. - # No IfName, address, MAC, parentInterface, or authority key is copied here. + # directly to the selected runtime's ProviderSupervisor through an authorized + # LaunchTicket. Runtime controllers, including runtime-qemu-media, receive + # only opaque Network/Endpoint refs. No IfName, address, MAC, + # parentInterface, fd, broker op, or authority key is copied here. systemArtifactId: # Artifact ID referencing the net-VM nixos-system artifact (type=nixos-system) # in the artifact catalog. Set from the REQUIRED Network.spec.netVmSystemArtifactId; @@ -828,13 +830,32 @@ AND via `ApplySysctl` at each reconcile cycle (defense-in-depth, handling boot-time Nix sysctl entry is required for specific bridge IfNames because bridges are created dynamically and do not exist at host activation. -**Persistent-tap creation** is declared by the network-local controller through -its semantic EffectPort and maps to `CreatePersistentTap`; Core supplies the -resulting FD privately to `Provider/runtime-cloud-hypervisor` through the -LaunchTicket path. `CreateTapFd` remains the runtime's non-persistent FD path. -On attachment removal or Network finalization, network-local waits for the -Guest/VMM FD owner to close and invokes the paired `DeletePersistentTap` -through its EffectPort. +**Persistent-tap creation** uses one operation chain. The network-local +controller declares an opaque attachment realization through +`NetworkEffectPort`. Its Core-owned adapter maps that semantic effect to +`CreatePersistentTap`, applies `SetBridgePortFlags`, and transfers the +already-authorized connected `OwnedFd` directly to ProviderSupervisor for the +selected Guest runtime's Process LaunchTicket. For QEMU, the qemu +Provider/controller receives only opaque Network/Endpoint refs; it receives no +broker operation or fd. The fd is never represented in a ResourceSpec/status, +serialized on d2b-bus, or delivered through a Provider controller +ComponentSession. + +`CreateTapFd` is a distinct baseline operation-scoped, non-persistent +SCM_RIGHTS path. This ADR does not use it for Network attachment realizations; +the retained Network lifecycle and generation-fenced deletion require the +canonical `CreatePersistentTap`/`DeletePersistentTap` pair. + +The Network adapter owns the connected `OwnedFd` with `FD_CLOEXEC` set until +ProviderSupervisor accepts it. ProviderSupervisor keeps its parent copy +CLOEXEC, makes only the declared child fd slot inheritable immediately before +exec, and closes its copy after successful spawn. The Guest VMM owns the child +copy until exit. Cancellation, LaunchTicket rejection, or spawn failure closes +every copy before the adapter invokes generation-fenced +`DeletePersistentTap`; the opaque realization remains retained until deletion +is confirmed. On attachment removal or Network finalization, network-local +likewise waits for the Guest/VMM fd owner to close before invoking +`DeletePersistentTap`. `DeletePersistentTapRequest` contains only an opaque attachment ID, `expectedNetworkGeneration`, and `expectedAttachmentGeneration`. It accepts no @@ -1006,10 +1027,13 @@ attachments: index: 11 ``` -The attachment is owned by the Network, not the Guest. The network-local -controller creates the tap interface and bridge-port configuration when a -listed Guest exists and is in a Ready-or-better phase. The Guest's own spec -references the network for firewall/routing/sandbox purposes: +The attachment is owned by the Network, not the Guest. When a listed Guest +exists and Core validates it as launch-eligible, the network-local controller +declares the opaque attachment effect. The Core adapter performs +`CreatePersistentTap → SetBridgePortFlags` and routes the connected CLOEXEC +`OwnedFd` directly into ProviderSupervisor's LaunchTicket attachment; the +runtime controller never receives the fd or broker operation. The Guest's own +spec references the network for firewall/routing/sandbox purposes: ```yaml # In Guest/corp-vm spec (inline ExecutionPolicy field): @@ -1237,11 +1261,20 @@ The network-local controller implements the full reconciliation contract from (`CAP_NET_ADMIN`/`CAP_NET_BIND_SERVICE`/`CAP_NET_RAW` effective in Guest network namespace only; no host capability; INV-NET-009), and mount `[{volumeRef: Volume/net--config, view: guest-readonly, mountPath: "/run/d2b/net-config", access: read-only, required: true}]`. - Create or update tap IfName records for each attachment entry that has a Ready - Guest. If `spec.mdns.enable = true`, create or update the owned mDNS reflector + Create or update opaque tap realization records for each attachment entry + that has a Core-authorized, launch-eligible Guest. Declare + `CreatePersistentTap` through + `NetworkEffectPort`; after the Core adapter creates/adopts the tap, apply + `SetBridgePortFlags` before it routes the connected CLOEXEC `OwnedFd` + directly to ProviderSupervisor's LaunchTicket attachment. No Provider + controller or d2b-bus payload receives the fd or broker operation. If + `spec.mdns.enable = true`, create or update the owned mDNS reflector `Process` resource (and local DNS bridge `Process` if `dnsmasqLocal = true`) with `executionRef: Guest/`. - j. Set bridge port flags (Isolated, neigh-suppress) for each tap via broker. + j. Confirm bridge port flags (Isolated, neigh-suppress) for every tap before + allowing its LaunchTicket handoff. On ticket rejection, cancellation, or + spawn failure, close every `OwnedFd` copy and invoke + `DeletePersistentTap`; retain the opaque realization until confirmed. k. For each attachment removed from the current spec, wait for its Guest/VMM FD ownership to close, then invoke `DeletePersistentTap` through the EffectPort with the retained opaque attachment ID and current expected @@ -2492,8 +2525,10 @@ bridges at reconcile time). | Destination | `packages/d2b-provider-network-local/src/controller.rs`: async NetworkReconciler; `packages/d2b-provider-network-local/src/plan.rs`: ReconcilePlan computation; `packages/d2b-provider-network-local/src/observe.rs`: drift-detection observe loop. Full crate layout required (see [Package and crate boundary](#package-and-crate-boundary)): `src/` (controller/plan/observe + colocated unit tests), `tests/` (hermetic conformance and state-machine tests), `integration/` (provider-system reconcile fixtures), `README.md` (Network ResourceType, controller binary, placement, RBAC, security invariants, build/test/integration commands). | | Detailed design | Implements full async reconcile interface from `ADR-046-resource-reconciliation`. `plan()` computes desired vs. actual bridge-presence, sysctl, host-side nftables, hosts-file, NM-unmanaged, config Volume content, Guest, guest-agent Process, and mDNS-Process states. `reconcile()` dispatches in order: `CreateBridge` for each bridge not present (broker applies IPv6 sysctls atomically; `CreateBridge` failure sets `FabricReady=False/bridge-create-error` and aborts) → `ApplySysctl` (defense-in-depth IPv6) → `ApplyNftables` (host-side `inet d2b` table) → `ApplyNmUnmanaged` → `ApplyRoute` → `UpdateHostsFile` → `SeedDnsmasqLease` for new reservations → **Volume upsert** (two-phase): Phase 1 — create `Volume/net--config` with `kind: ephemeral`, `source.executionRef: Host/`, `source.settings.kind: tmpfs`, `quota: {maxBytes: 4194304, maxInodes: 128, enforcement: hard}` (tmpfs quota charged to Host memory budget), `layout` entries (root directory with `type: directory` and four config files each with `type: file`, `ownerRef: User/net-local-controller`, `groupRef: User/net-local-controller`, `mode: "0640"`, `accessAcl: []`, `defaultAcl: []`, `noFollow: true`, conservative create/repair/cleanup policies), `views: {guest-readonly: {path: "", rights: [read, traverse]}}`, `attachments: []` (no Guest attachment); abort on terminal error with `ConfigVolumeReady=False/config-volume-error`. Wait for Volume backing to reach `Ready`; requeue on `Degraded`/`Failed` with `ConfigVolumeReady=False/volume-not-ready`. Write rendered config content through Volume write service (no raw host paths). Phase 2 — create Guest upsert with `systemArtifactId` from REQUIRED `Network.spec.netVmSystemArtifactId`. Wait for Guest `Ready`. Then update Volume with Guest attachment: `attachments: [{executionRef: Guest/, transport: virtiofs, view: guest-readonly, access: read-only, mountPath: "/run/d2b/net-config", settings: {posixAcl: false, xattr: false, cache: auto, inodeFileHandles: never, threadPoolSize: null, socketGroup: null}}]`; wait for attachment `Ready`; requeue on `Degraded` with `ConfigVolumeReady=False/attachment-not-ready`. Create or update guest-agent Process `Process/net--agent` with `processClass: worker`, `sandbox: {namespaceClasses: [], capabilityClasses: [network-admin, network-bind, network-raw]}` (inherits Guest network namespace; `network-admin`→`CAP_NET_ADMIN`, `network-bind`→`CAP_NET_BIND_SERVICE`, `network-raw`→`CAP_NET_RAW`, all effective in Guest network namespace only; INV-NET-009), `mounts: [{volumeRef: Volume/net--config, view: guest-readonly, mountPath: "/run/d2b/net-config", access: read-only, required: true}]`. mDNS Process upsert when `spec.mdns.enable = true` (D-NETWORK-001) → `SetBridgePortFlags` per tap. Removed attachments first wait for Guest/VMM FD ownership to close, then issue `DeletePersistentTap` with the retained opaque attachment ID and current expected Network/attachment generations; the handle remains retained until confirmed effect or validated absence. Stale generation refreshes/requeues, transient kernel error retries, and foreign marker fails closed. Each broker op returns typed audit evidence. `observe()` re-reads `firewallDigest` (host-side), bridge isolation flags, IPv6 sysctls, and guest-agent Process status (`dnsmasq-bound`, `firewall-applied` predicates); queues reconcile on drift. Metrics use only the fixed semantic labels in §OTEL spans and metrics; Zone/Network identity remains in OTEL resource attributes and permitted audit fields and never enters metric labels or span attributes. **Finalizer** (strictly child-first): `NetworkDraining` → stop workload Guests and await VMM FD closure → generation-fenced `DeletePersistentTap` for each retained attachment, awaiting confirmation → delete guest-agent Process and mDNS Processes; wait for each Deleted watch event → update Volume to remove Guest attachment (`attachments: []`); wait for attachment removal confirmed → delete `Guest/`; wait for Deleted watch event → delete `Volume/net--config`; wait for Deleted watch event → `ApplyNftables` (empty) → `ApplyNmUnmanaged` (empty) → `UpdateHostsFile` (empty) → `DeleteBridge` for each bridge (idempotent) → clear finalizer. No USBIP rules installed by Network; device-usbip issues the existing `UsbipBindFirewallRule` request with action `Ensure` for apply or `Remove` for release (D-NETWORK-002). | | Integration | Controller process registers descriptor, watches `Network` resources via d2b-bus/ComponentSession/ResourceClient. Owned Guest and Process mutations trigger owner reconciliation. Device-usbip watches only Network identity/readiness/generation; its Core adapter privately resolves relay/firewall effects (D-NETWORK-002). | +| QEMU launch integration | For every authorized QEMU attachment, the controller supplies only the opaque realization to `NetworkEffectPort`. The Core adapter performs `CreatePersistentTap → SetBridgePortFlags`, then transfers the connected CLOEXEC `OwnedFd` directly to ProviderSupervisor's Process LaunchTicket attachment. The qemu Provider/controller receives only opaque Network/Endpoint refs and no broker op/fd; no fd is serialized through ResourceAPI, ComponentSession, or d2b-bus. Adapter/supervisor parent copies remain CLOEXEC and close after spawn; ticket rejection, cancellation, or spawn failure closes all copies before generation-fenced `DeletePersistentTap`, retaining the opaque realization until confirmation. | | Data migration | None after full reset | | Validation | `ADR046-reconcile-001` toolkit conformance; latency gates (p95 ≤5 ms hint-to-handler); Network-specific: CIDR conflict blocks reconcile, `CreateBridge` failure sets `FabricReady=False`, Volume creation failure sets `ConfigVolumeReady=False/config-volume-error`, `User/net-local-controller` not Ready aborts with `ConfigVolumeReady=False/user-not-ready`, Volume schema round-trip (kind=ephemeral, source.settings.kind=tmpfs, quota.enforcement=hard, layout type=file entries, views.guest-readonly.rights=[read,traverse]), tmpfs quota charged to Host memory budget (test Volume creation fails when Host memory budget exceeded), Guest not created before Volume backing `Ready`, Guest attachment not added before Guest `Ready`, guest-agent Process created after attachment `Ready` (`processClass: worker`, `namespaceClasses: []`, `capabilityClasses: [network-admin, network-bind, network-raw]`, `access: read-only`, `required: true`), host-capability leakage test: no host-netns process gains `CAP_NET_ADMIN`/`CAP_NET_BIND_SERVICE`/`CAP_NET_RAW` as result of guest-agent launch (INV-NET-009; `tests/host-integration/guest-agent-cap-confinement.nix`), removed attachment and finalizer call `DeletePersistentTap` only after Guest/VMM FD closure with opaque ID/current generations, validated absence succeeds, transient failure retains handle/retries, stale generation refreshes, foreign marker blocks without deletion, request/audit contain no IfName/path, `DeleteBridge` called only after tap confirmations, Volume attachment removed before net-VM Guest deletion in finalizer (test order: workload FD closure → persistent taps deleted → agent Deleted → Volume attachment removed → net-VM Guest Deleted → Volume Deleted → bridges), east-west invariant (INV-NET-003), hostBlocklist enforcement (INV-NET-004), macvtap attachment status (delegated to runtime-ch), mDNS Process created/deleted with `spec.mdns.enable` toggle, broker INV-NET-002 tests, config-only spec change updates Volume content and triggers agent reload without Guest restart (INV-NET-008); golden tests updated for v3 IfNames; structural metric descriptor test asserts exact absence of `vm`, `zone`, `zone_id`, `zone_uid`, `network`, and every resource-name-derived label and verifies a Network-name canary is absent from emitted label values | +| QEMU launch validation | `qemu_tap_launch_order` proves create → flags → ticket ordering; `qemu_tap_owned_fd_lifetime` proves CLOEXEC parent ownership, one intentional child slot, and closure on success/failure; `qemu_tap_failed_launch_cleanup` proves close-before-`DeletePersistentTap` and handle retention until confirmation; `qemu_tap_no_bus_serialization` proves the qemu Provider/controller and every ResourceAPI/ComponentSession/d2b-bus payload contain only opaque refs, never the fd or broker DTO. | | Removal proof | Daemon-orchestrated network/bridge lifecycle removed only after controller passes conformance and parity tests | ### ADR046-network-006 @@ -2506,6 +2541,7 @@ bridges at reconcile time). | Reuse action | adapt | | Destination | `tests/unit/nix/cases/net-vm-network.nix` (adapted to v3 resource API); updated golden pins; `tests/host-integration/bridge-isolation.nix` (adapted); `packages/d2b-priv-broker/tests/{bridge_lifecycle,persistent_tap_lifecycle}.rs` (new hermetic broker tests). Provider crate test directories: `packages/d2b-provider-network-local/tests/` — hermetic Cargo integration tests (conformance suite, controller state machine, CIDR validation vectors, IfName determinism, invariant tests INV-NET-001–007, reconcile/observe/finalize with deterministic clock, fault injection); `packages/d2b-provider-network-local/integration/` — container/Host/Guest lifecycle fixtures invoked by `make test-integration` (bridge isolation, east-west double opt-in, nftables drift detection, persistent-tap and macvtap lifecycle). Both directories required by package policy. | | Detailed design | Rust integration tests: NetworkSpec CIDR validation golden vectors; AttachmentSpec index uniqueness; ExternalAttachmentSpec mutual-exclusion validation; IfName derivation determinism; CIDR overlap arithmetic; INV-NET-001 through INV-NET-009 invariant tests; reconcile/observe/finalize state machine (deterministic clock). Broker tests: `create_bridge_applies_ipv6_sysctl` (INV-NET-002 layer 1); `delete_bridge_is_idempotent`; `delete_bridge_never_cascades_attached_tap`; `create_bridge_parameters_match_spec` (MTU, STP disabled, multicast snooping disabled); `delete_persistent_tap_pairs_with_create`; `delete_persistent_tap_absent_is_idempotent_after_ownership_validation`; `delete_persistent_tap_rejects_stale_network_generation`; `delete_persistent_tap_rejects_stale_attachment_generation`; `delete_persistent_tap_foreign_marker_fails_closed`; `delete_persistent_tap_request_and_audit_have_no_ifname_or_path`. Controller tests: `reconcile_applies_sysctl_defense_in_depth` (INV-NET-002 layer 2); `volume_created_before_guest`; `guest_not_created_until_volume_ready`; `agent_process_created_after_guest`; `removed_attachment_waits_for_vmm_then_delete_persistent_tap`; `finalizer_order_vmm_then_taps_then_agent_then_guest_then_volume_then_bridges`; `delete_persistent_tap_transient_retry_retains_handle`; `delete_persistent_tap_generation_mismatch_refreshes`; `delete_persistent_tap_foreign_marker_blocks_finalizer`; `config_only_spec_change_updates_volume_no_guest_restart` (INV-NET-008); `finalizer_calls_delete_bridge`; `mdns_process_created_on_enable`; `mdns_process_deleted_on_disable`; `host_capability_leakage` (INV-NET-009). nix-unit: INV-NET-001 lib.mkForce assertion; net-VM artifact has no inline mDNS service and no per-Network dnsmasq/nftables data (INV-NET-008); Network emitter CIDR constraint assertions; no `systemd.network.netdevs` bridge entries emitted. Host integration: bridge isolation with east-west opt-in; nftables drift detection; persistent-tap and macvtap create/delete lifecycle; config Volume update propagates to guest-agent without Guest restart; `tests/host-integration/guest-agent-cap-confinement.nix` (INV-NET-009 zero leakage to host netns). | +| QEMU TAP tests | Add `qemu_tap_launch_order`, `qemu_tap_owned_fd_lifetime`, `qemu_tap_failed_launch_cleanup`, and `qemu_tap_no_bus_serialization` to the network-local/provider-supervisor integration fixture. Assert the exact `CreatePersistentTap → SetBridgePortFlags → LaunchTicket` chain, direct `OwnedFd` handoff, CLOEXEC discipline, close-before-generation-fenced-delete on every failed launch, no operation-scoped `CreateTapFd`, and no fd/broker DTO at the qemu controller or bus boundary. | | Integration | Pinned tests registered in `tests/golden/pinned/`; nix-unit cases in `tests/unit/nix/cases/`; host integration in `tests/host-integration/` | | Data migration | None — full d2b 3.0 reset; no prior state to migrate | | Validation | All listed tests must pass before `nixos-modules/network.nix` removal is eligible | diff --git a/docs/specs/providers/ADR-046-provider-runtime-qemu-media.md b/docs/specs/providers/ADR-046-provider-runtime-qemu-media.md index c38edead8..39f90eb99 100644 --- a/docs/specs/providers/ADR-046-provider-runtime-qemu-media.md +++ b/docs/specs/providers/ADR-046-provider-runtime-qemu-media.md @@ -468,15 +468,29 @@ payloads owned by their respective resources and are retained unchanged. The controller declares a dependency alias `network → Provider/network-local` (bound via `config.networkProviderRef`). For each Guest with a non-empty -`spec.networkAttachments`, the controller watches the referenced Network -resources for `Ready` status. - -When a Network resource is `Ready`, `Provider/network-local` has allocated a -tap fd for the Guest's MAC address and bridge assignment. The controller -requests the tap fd delivery through the network-local ComponentSession -service. The tap fd is then included in the runner Process LaunchTicket's -inherited fd table. QEMU receives the tap interface via a sealed fd slot; no -bridge name, interface name, or host network path crosses the public surface. +`spec.networkAttachments`, the controller watches the referenced opaque +`Network/` resources for `Ready` status. It never requests or receives a +tap fd and never sees a network broker operation. + +At Process launch, Core authorizes the Guest-to-Network attachment. The +network-local controller declares the opaque attachment through +`NetworkEffectPort`; its Core-owned adapter maps that semantic effect to the +canonical `CreatePersistentTap`, applies `SetBridgePortFlags`, and gives the +already-authorized connected `OwnedFd` directly to ProviderSupervisor. +ProviderSupervisor places that fd in the QEMU Process LaunchTicket attachment. +The fd never traverses the qemu Provider/controller, ResourceAPI, +ComponentSession, or d2b-bus serialization. QEMU receives the tap through the +inherited fd table; no bridge name, interface name, or host network path crosses +the public surface. + +The adapter owns the `OwnedFd` with `FD_CLOEXEC` set until ProviderSupervisor +accepts it. ProviderSupervisor retains a CLOEXEC parent copy, creates only the +declared child fd slot immediately before exec, and closes its copy after +successful spawn. QEMU then owns the child copy until exit. Cancellation, +LaunchTicket rejection, or spawn failure closes every copy first and invokes +the generation-fenced `DeletePersistentTap`; the opaque realization remains +retained until deletion is confirmed. Normal teardown likewise waits for QEMU +fd closure before `DeletePersistentTap`. If `spec.networkAttachments` is empty, the runner starts with no network interface (isolated). @@ -644,9 +658,9 @@ spec: limit: 1024 # --- Network --- - # The tap fd is pre-connected and delivered via the LaunchTicket inherited fd - # table (acquired from the network-local ComponentSession before runner - # creation). The runner process holds no live network stack; networkUsage is null. + # Core/ProviderSupervisor resolves the opaque Network ref and inserts the + # pre-authorized tap OwnedFd directly into the LaunchTicket inherited-fd + # table. The controller never receives the fd; networkUsage remains null. networkUsage: null # --- Device --- @@ -731,7 +745,7 @@ spec: purpose: qmp-control serviceFingerprint: runtime-qemu-media.d2bus.org/qmp/v1 locality: host-local - visibility: provider-internal + visibility: provider attachmentPolicy: launch-ticket-only consumerPolicy: [Provider/runtime-qemu-media] lifecyclePolicy: recycle-with-producer @@ -750,7 +764,7 @@ spec: purpose: serial-console serviceFingerprint: runtime-qemu-media.d2bus.org/serial/v1 locality: host-local - visibility: provider-internal + visibility: provider attachmentPolicy: launch-ticket-only consumerPolicy: [Provider/runtime-qemu-media] lifecyclePolicy: recycle-with-producer @@ -758,6 +772,10 @@ spec: Consumers use `Endpoint/media-vm-qmp` and `Endpoint/media-vm-serial`; no raw socket path, fd number, or address appears in spec or status. +`visibility` uses the Endpoint schema's exact closed values +`owner | provider | zone`; these examples use `provider`. +`consumerPolicy` provides the finer restriction to +`Provider/runtime-qemu-media` and does not invent another visibility value. --- @@ -920,9 +938,12 @@ tmpfs is unmounted only after the runner Process pidfd signals exit. 2. **Runtime Volume:** Controller creates `-runtime` Volume if absent. Waits for `Volume.phase = Ready`. -3. **Tap fd acquisition:** Controller calls `network-local` ComponentSession - service to obtain the tap fd for the Guest's Network attachment. The fd is - added to the LaunchTicket. +3. **Network attachment resolution:** Controller supplies only the opaque + `Network/` ref. Core authorizes the attachment; the network-local + `NetworkEffectPort` adapter performs `CreatePersistentTap`, applies + `SetBridgePortFlags`, and transfers the connected CLOEXEC `OwnedFd` directly + to ProviderSupervisor for the LaunchTicket. The controller never receives + the broker operation or fd. 4. **Media fd acquisition:** Volume controller (provider/volume-local) makes the boot media fd available via the Volume's virtio-blk attachment. The @@ -939,14 +960,17 @@ tmpfs is unmounted only after the runner Process pidfd signals exit. backend. 7. **Runner Process creation:** Controller creates (or UpdateSpec) the - `-qemu-runner` Process resource with a fully-sealed - LaunchTicket and all fd slots resolved. + `-qemu-runner` Process resource and supplies only opaque + Network/Endpoint refs to Core's attachment resolver. Core seals the + LaunchTicket only after every private fd attachment is authorized and + resolved. 8. **ProviderSupervisor launch:** `system-minijail` ProviderSupervisor verifies the LaunchTicket, compiles the minijail sandbox plan, and spawns QEMU via `clone3(CLONE_PIDFD)` into the correct cgroup leaf. QEMU receives all required fds via its inherited fd table; no paths cross the supervisor - boundary. + boundary. Launch rejection or spawn failure closes the tap `OwnedFd` before + the Network adapter invokes generation-fenced `DeletePersistentTap`. 9. **QMP readiness:** After spawn, the Process Provider monitors the `qmp` endpoint. When QEMU writes its initial capabilities JSON to the QMP socket, @@ -1054,11 +1078,15 @@ disposition of every current baseline broker op: | --- | --- | --- | | `OpenDevice(kvm)` | Deliver `/dev/kvm` fd to `device-kvm` Provider for LaunchTicket inclusion | Issued by `device-kvm` Provider controller; never by `runtime-qemu-media` directly | | `SpawnRunner` (qemu-media-runner role) | Launch QEMU binary in cgroup leaf via `clone3(CLONE_PIDFD)` | Issued by `system-minijail` ProviderSupervisor on behalf of Process controller | -| `TapFdDeliver` (network-local family) | Deliver tap fd for Guest MAC/bridge assignment | Issued by `network-local` Provider; consumed by `runtime-qemu-media` controller as a fd slot | +| `CreatePersistentTap` | Create/adopt the opaque Guest Network attachment realization | Issued only by the Core-owned NetworkEffectPort adapter after network-local declares the semantic effect | +| `SetBridgePortFlags` | Apply isolation and neighbor-suppression policy before launch | Issued by the same NetworkEffectPort adapter before fd handoff | +| `DeletePersistentTap` | Remove the generation-fenced realization after fd closure | Issued by the NetworkEffectPort adapter on failed launch and normal teardown | -`runtime-qemu-media` does not issue any broker operations directly. It -communicates with `network-local` and `device-kvm` via their ComponentSession -service contracts (d2b-bus), not via broker wire ops. +`runtime-qemu-media` does not issue or receive any broker operation directly. +Its controller supplies only opaque Network and Endpoint refs. The connected +tap `OwnedFd` moves directly from the Core-owned NetworkEffectPort adapter to +ProviderSupervisor and then the child fd table; it is never serialized on +d2b-bus or delivered through the controller's ComponentSession. --- @@ -1215,7 +1243,7 @@ closed; the controller never writes a value outside this table. | `runner-exited-unexpectedly` | Failed | Runner Process exited while Guest desiredPhase was running | | `display-provider-not-configured` | Failed | displayWindow=true but config.displayProviderRef=null | | `wayland-session-failed` | Failed | WaylandSession owned by this Guest is in Failed phase | -| `network-tap-unavailable` | Degraded | network-local could not deliver tap fd | +| `network-tap-unavailable` | Degraded | Core could not authorize or resolve the Network attachment for launch | --- @@ -1669,10 +1697,10 @@ destination in `packages/d2b-provider-runtime-qemu-media/`. | Current source | `packages/d2b-host/src/qemu_media_argv.rs` fd-index arg shape; `packages/d2b-core/src/processes.rs` `ProcessRole::QemuMediaRunner` sandbox/budget baseline | | Reuse action | adapt | | Destination | packages/d2b-provider-runtime-qemu-media/src/controller/process_builder.rs | -| Detailed design | Process spec builder and LaunchTicket assembly: build the canonical `qemu-media-runner` Process ResourceSpec from §10.1 and assemble the LaunchTicket with sealed fd slots for kvm, tap, media, and optional display fds. No raw path, argv, executable path, or principal appears in any public field. Primary reuse disposition: `adapt`. Preserved source-plan detail: adapt fd-slot and sandbox/budget concepts to canonical Process resources and sealed LaunchTickets; do not copy raw argv strings or path construction. | -| Integration | Controller emits Process resources; system-minijail/Process Provider consumes the spec and LaunchTicket; QEMU runner receives only sealed fds; Endpoint resources represent QMP/serial connections. | +| Detailed design | Process spec builder and LaunchTicket attachment resolution: build the canonical `qemu-media-runner` Process ResourceSpec from §10.1 and supply only opaque Network/Endpoint refs to Core's attachment resolver. Core, not the qemu controller, resolves authorized kvm, tap, media, and optional display attachments and seals their fd slots in the LaunchTicket. The qemu Provider/controller receives no broker operation or fd. No raw path, argv, executable path, or principal appears in any public field. Primary reuse disposition: `adapt`. Preserved source-plan detail: adapt sandbox/budget concepts to canonical Process resources and Core-sealed LaunchTickets; do not copy raw argv strings or path construction. | +| Integration | Controller emits Process resources; Core resolves private attachments; system-minijail/Process Provider consumes the sealed LaunchTicket. The NetworkEffectPort adapter transfers its connected tap `OwnedFd` directly to ProviderSupervisor without ResourceAPI, ComponentSession, or d2b-bus serialization; QEMU receives only the declared child fd slot. Endpoint resources represent QMP/serial connections. | | Data migration | Full d2b 3.0 reset; existing QEMU runner process state is not imported and launch state is rebuilt from resources | -| Validation | `tests/process_spec_golden.rs`; `tests/launch_ticket_fd_slots.rs`; `tests/no_raw_argv_in_spec.rs` | +| Validation | `tests/process_spec_golden.rs`; `tests/launch_ticket_fd_slots.rs`; `tests/launch_ticket_tap_fd_lifetime.rs`; `tests/no_controller_fd_or_broker_op.rs`; `tests/no_raw_argv_in_spec.rs` | | Removal proof | `ProcessRole::QemuMediaRunner` and raw qemu-media argv launch surfaces are removable after canonical Process specs and LaunchTickets cover every runner launch | --- @@ -1709,19 +1737,19 @@ destination in `packages/d2b-provider-runtime-qemu-media/`. --- -### ADR046-qemu-media-012 Network tap fd acquisition +### ADR046-qemu-media-012 Network attachment routing | Field | Value | | --- | --- | -| Dependency/owner | P1; depends on ADR046-qemu-media-002 and Provider config `networkProviderRef`; owner: runtime-qemu-media network dependency integration | +| Dependency/owner | P1; depends on ADR046-qemu-media-002, ADR046-network-005, and Provider config `networkProviderRef`; owner: runtime-qemu-media network dependency integration | | Current source | None — net-new v3 work; no pre-ADR45 baseline equivalent | | Reuse action | create | | Destination | packages/d2b-provider-runtime-qemu-media/src/controller/network.rs | -| Detailed design | Network tap fd acquisition: call the `network-local` ComponentSession service to request a tap fd for a Guest MAC/bridge assignment and include the fd in the LaunchTicket. No bridge name or interface name appears in any public field. Primary reuse disposition: `create`. Preserved source-plan detail: net-new against the `network-local` ComponentSession contract. | -| Integration | Guest networkAttachments drive requests to network-local; network-local returns a sealed tap fd; Process LaunchTicket carries the fd to the QEMU runner; Guest conditions report unavailable taps. | +| Detailed design | Network attachment routing: project each Guest network attachment as an opaque `Network/` ref and condition only. The network-local controller declares the opaque semantic effect; its Core-owned NetworkEffectPort adapter maps it to `CreatePersistentTap`, then `SetBridgePortFlags`, and transfers the already-authorized connected `OwnedFd` directly to ProviderSupervisor for the QEMU Process LaunchTicket. The adapter and supervisor keep `FD_CLOEXEC` set on parent copies; only the declared child slot is made inheritable immediately before exec. On cancellation, ticket rejection, or spawn failure, all fd copies close before generation-fenced `DeletePersistentTap`, and the opaque realization is retained until deletion confirmation. The qemu Provider/controller receives no broker operation, fd, bridge name, or interface name, and the fd is never serialized through ResourceAPI, ComponentSession, or d2b-bus. Primary reuse disposition: `create`. | +| Integration | Guest `networkAttachments` drive opaque dependency watches; ADR046-network-005 owns the NetworkEffectPort effect chain and ProviderSupervisor owns fd handoff. Process LaunchTicket carries the fd directly to QEMU; Guest conditions report authorization/resolution failures without exposing the fd or broker operation. | | Data migration | Full d2b 3.0 reset; no v2 state/config import | -| Validation | `tests/tap_fd_acquisition.rs`; `tests/tap_fd_unavailable.rs` | -| Removal proof | None — tap fd acquisition through `network-local` is a new v3 dependency path | +| Validation | `tests/tap_launch_routing.rs` proves `CreatePersistentTap → SetBridgePortFlags → ProviderSupervisor LaunchTicket` ordering; `tests/tap_fd_lifetime.rs` proves CLOEXEC, single child ownership, and close-before-`DeletePersistentTap`; `tests/tap_fd_no_bus_serialization.rs` rejects fd/broker DTOs at the qemu controller boundary; `tests/tap_fd_unavailable.rs` covers authorization and resolution failure | +| Removal proof | None — Core-routed Network attachment resolution is a new v3 dependency path | --- @@ -1874,7 +1902,9 @@ per-test budget. | `wayland_session_attachment_read.rs` | Opaque endpoint attachment consumed from display-wayland status | | `wayland_session_missing_provider.rs` | displayWindow=true + null displayProviderRef → Failed | | `process_spec_golden.rs` | Full canonical Process spec against §10.1 YAML | -| `launch_ticket_fd_slots.rs` | LaunchTicket fd table completeness | +| `launch_ticket_fd_slots.rs` | Core-sealed LaunchTicket fd table completeness; controller supplies only opaque refs | +| `launch_ticket_tap_fd_lifetime.rs` | Tap `OwnedFd` stays CLOEXEC in adapter/supervisor, becomes inheritable only in declared child slot, and closes on all failure paths | +| `no_controller_fd_or_broker_op.rs` | QEMU Provider/controller boundary rejects tap fds and broker DTOs | | `no_raw_argv_in_spec.rs` | No executable path in any Process spec field | | `qmp_capability_negotiation.rs` | QMP greeting exchange | | `qmp_command_dispatch.rs` | All QMP commands in §13.2; success and error paths | @@ -1883,8 +1913,10 @@ per-test budget. | `hotplug_attach_sequence.rs` | blockdev-add + device_add via QMP attachment | | `hotplug_detach_sequence.rs` | device_del + blockdev-del via QMP attachment | | `hotplug_qmp_failure.rs` | QMP error → Degraded + `hotplug-media-failed` | -| `tap_fd_acquisition.rs` | network-local ComponentSession; fd delivery | -| `tap_fd_unavailable.rs` | `network-tap-unavailable` Degraded | +| `tap_launch_routing.rs` | `CreatePersistentTap → SetBridgePortFlags →` direct ProviderSupervisor LaunchTicket attachment ordering | +| `tap_fd_lifetime.rs` | Failed launch closes all copies before generation-fenced `DeletePersistentTap`; normal exit waits for QEMU closure | +| `tap_fd_no_bus_serialization.rs` | Tap fd never appears in ResourceAPI, ComponentSession, or d2b-bus payloads | +| `tap_fd_unavailable.rs` | Authorization/resolution failure → `network-tap-unavailable` Degraded | | `reconcile_dependency_gating.rs` | All dependencies missing/present combinations | | `reconcile_runner_exit_handling.rs` | Runner exit → Failed / re-create logic | | `finalize_sequence.rs` | Finalizer drain order (runner → WaylandSession → Volume) | From 489cec6dc698027ddbc75575fc36109c27b7a687 Mon Sep 17 00:00:00 2001 From: John Vicondoa Date: Fri, 24 Jul 2026 11:48:07 -0700 Subject: [PATCH 103/115] docs: canonicalize Endpoint visibility Keep coarse Endpoint scope closed while moving subject, component, and operation restrictions into consumerPolicy. Bind controller admission and planned drift coverage to that contract so provider examples cannot introduce aliases. --- ...DR-046-components-processes-and-sandbox.md | 17 +++++++++---- docs/specs/ADR-046-feasibility-and-spikes.md | 6 ++--- docs/specs/ADR-046-nix-configuration.md | 9 +++++++ ...R-046-resources-host-guest-process-user.md | 24 +++++++++++++++---- docs/specs/ADR-046-validation-and-delivery.md | 10 +++++++- .../ADR-046-provider-audio-pipewire.md | 21 +++++++++------- .../ADR-046-provider-clipboard-wayland.md | 16 ++++++++----- .../ADR-046-provider-credential-entra.md | 11 +++++---- .../providers/ADR-046-provider-device-gpu.md | 12 ++++++---- .../ADR-046-provider-device-security-key.md | 12 ++++++---- .../providers/ADR-046-provider-device-tpm.md | 12 ++++++---- .../ADR-046-provider-device-usbip.md | 10 +++++--- .../ADR-046-provider-display-wayland.md | 10 ++++---- .../ADR-046-provider-network-local.md | 6 +++-- .../ADR-046-provider-notification-desktop.md | 15 +++++++----- .../ADR-046-provider-observability-otel.md | 15 +++++++----- ...6-provider-runtime-azure-container-apps.md | 12 ++++++---- ...-provider-runtime-azure-virtual-machine.md | 12 ++++++---- ...R-046-provider-runtime-cloud-hypervisor.md | 12 ++++++---- .../ADR-046-provider-shell-terminal.md | 14 +++++++---- .../providers/ADR-046-provider-system-core.md | 9 ++++--- .../ADR-046-provider-system-minijail.md | 6 +++-- .../ADR-046-provider-system-systemd.md | 6 +++-- .../ADR-046-provider-transport-azure-relay.md | 12 ++++++---- .../ADR-046-provider-transport-unix.md | 6 +++-- .../ADR-046-provider-transport-vsock.md | 6 +++-- .../ADR-046-provider-volume-virtiofs.md | 6 +++-- 27 files changed, 209 insertions(+), 98 deletions(-) diff --git a/docs/specs/ADR-046-components-processes-and-sandbox.md b/docs/specs/ADR-046-components-processes-and-sandbox.md index 0bcdc54c4..de522e7cb 100644 --- a/docs/specs/ADR-046-components-processes-and-sandbox.md +++ b/docs/specs/ADR-046-components-processes-and-sandbox.md @@ -164,12 +164,19 @@ LaunchTicket bound to: It: 1. verifies ticket/current resource/controller lease; -2. resolves only trusted package/template/resource outputs; -3. asks the local broker/systemd/user/Host/Guest effect owner to launch; -4. returns provider-specific stable process identity and mandatory pidfd +2. for every Endpoint, rejects any visibility outside + `owner|provider|zone`, applies that coarse scope, authenticates the exact + subject and signed Provider component, evaluates Role/RoleBinding, and + intersects the canonical `consumerPolicy.allowedSubjects`, + `allowedProviderComponents`, and `allowedOperations` allowlists; request + fields cannot select those identities, no visibility alias is accepted, and + any mismatch is `endpoint-resolve-denied`; +3. resolves only trusted package/template/resource outputs; +4. asks the local broker/systemd/user/Host/Guest effect owner to launch; +5. returns provider-specific stable process identity and mandatory pidfd evidence; -5. observes/adopts/signals/stops only that exact identity; -6. reports bounded effects/status to the Process controller. +6. observes/adopts/signals/stops only that exact identity; +7. reports bounded effects/status to the Process controller. It never interprets Provider root settings, chooses dependencies, reads sibling state, or registers services/commands. diff --git a/docs/specs/ADR-046-feasibility-and-spikes.md b/docs/specs/ADR-046-feasibility-and-spikes.md index 3da287a36..6e9d6a548 100644 --- a/docs/specs/ADR-046-feasibility-and-spikes.md +++ b/docs/specs/ADR-046-feasibility-and-spikes.md @@ -566,11 +566,11 @@ performed by this documentation-only spec. | Field | Value | | --- | --- | -| Hypothesis | (D092) A stable endpoint can be modeled as an `Endpoint` resource with `ownerRef` (lifecycle) and `producerRef` (Process/Device/Guest/Host) carrying only closed class/transport/locality/purpose + bounded fingerprints and NO raw locator; a consumer references `Endpoint/` and resolves to a private transport/FD only through an EffectPort/LaunchTicket path under authorization; a producer restart bumps `endpointGeneration`/`status.update` and fires the consumer's dependency trigger; and the promotion test correctly keeps high-churn handles (pidfd, fd index, named stream, `OwnedTransport`, `operationId`) as non-resource opaque IDs. | +| Hypothesis | (D092) A stable endpoint can be modeled as an `Endpoint` resource with `ownerRef` (lifecycle) and `producerRef` (Process/Device/Guest/Host) carrying only closed class/transport/locality/purpose + bounded fingerprints and NO raw locator; `spec.visibility` is exactly `owner\|provider\|zone`, while the strict `consumerPolicy` object is the only finer gate; a consumer references `Endpoint/` and resolves to a private transport/FD only through an EffectPort/LaunchTicket path under authorization; a producer restart bumps `endpointGeneration`/`status.update` and fires the consumer's dependency trigger; and the promotion test correctly keeps high-churn handles (pidfd, fd index, named stream, `OwnedTransport`, `operationId`) as non-resource opaque IDs. | | Minimal disposable artifact | `proofs/endpoint-resource-spike/` — an in-process fake resource store with a `producer_index`, a fake TPM Device producing an `Endpoint`, a fake consumer Process referencing it, and a fake EffectPort that resolves an Endpoint to an opaque private handle only for an authorized consumer; a deterministic clock. | -| Inputs | (a) Device creates an owned `Endpoint` (producerRef=Device, no locator in spec/status); (b) an authorized consumer resolves it via the EffectPort and receives a private handle; (c) an unauthorized consumer resolve; (d) producer restart → `endpointGeneration` bump; (e) child-first deletion when the producer/owner is deleted; (f) a set of high-churn handles fed to the promotion-test lint. | +| Inputs | (a) Device creates an owned `Endpoint` (producerRef=Device, no locator in spec/status); (b) authorized owner-, provider-, and Zone-scope consumers resolve via the EffectPort; (c) invalid visibility aliases and mismatches for each canonical `consumerPolicy` allowlist; (d) producer restart → `endpointGeneration` bump; (e) child-first deletion when the producer/owner is deleted; (f) a set of high-churn handles fed to the promotion-test lint; (g) all Markdown Endpoint examples fed to an Endpoint-only drift parser. | | Command/harness | `cargo test --manifest-path proofs/endpoint-resource-spike/Cargo.toml -- --test-threads=1 endpoint_resource`. | -| Metrics | (1) Endpoint spec/status contain no path/address/CID/port/fd/credential (assert closed-enum/bounded fields only); (2) authorized resolve returns a handle only through the EffectPort; (3) unauthorized resolve → `endpoint-resolve-denied`, no locator returned; (4) producer restart bumps `endpointGeneration` and fires exactly one consumer `dependency-changed` trigger; (5) deleting the producer/owner deletes the Endpoint child-first; (6) the promotion-test lint classifies pidfd/fd-index/named-stream/`OwnedTransport`/`operationId` as permitted opaque (non-resource) and any stable cross-boundary endpoint as requiring promotion. | +| Metrics | (1) Endpoint spec/status contain no path/address/CID/port/fd/credential and visibility accepts exactly `owner\|provider\|zone`; (2) authorized resolve returns a handle only through the EffectPort; (3) an out-of-scope subject or any present `allowedSubjects`/`allowedProviderComponents`/`allowedOperations` mismatch returns `endpoint-resolve-denied`, with no locator; (4) producer restart bumps `endpointGeneration` and fires exactly one consumer `dependency-changed` trigger; (5) deleting the producer/owner deletes the Endpoint child-first; (6) the promotion-test lint classifies pidfd/fd-index/named-stream/`OwnedTransport`/`operationId` as permitted opaque (non-resource) and any stable cross-boundary endpoint as requiring promotion; (7) every Endpoint example uses the exact visibility enum and the object-form `consumerPolicy` as its only finer gate, without inspecting ResourceExport visibility. | | Pass/fail threshold | All metrics binary pass; metrics (1) and (3) are hard zero-tolerance gates (no locator in the resource surface; no unauthorized resolution). | | Expected resource budget | ≤2 minutes wall time; ≤32 MiB RSS (in-memory store/EffectPort/lint). | | Failure interpretation | A raw locator in an Endpoint's spec/status, an unauthorized resolution, or a high-churn handle wrongly promoted (or a stable cross-boundary endpoint left as an opaque ID) is a severity-blocking finding against D092 and must be fixed structurally, never suppressed. | diff --git a/docs/specs/ADR-046-nix-configuration.md b/docs/specs/ADR-046-nix-configuration.md index 35927ea49..9a6532dd9 100644 --- a/docs/specs/ADR-046-nix-configuration.md +++ b/docs/specs/ADR-046-nix-configuration.md @@ -1036,6 +1036,11 @@ d2b.zones.work.resources.entra-login = { transport = "vsock"; # closed class only; no CID/path purpose = "entra-login-token"; locality = "guest-local"; + visibility = "zone"; + consumerPolicy = { + allowedSubjects = [ "Provider/credential-entra" ]; + allowedOperations = [ "resolve" ]; + }; }; }; @@ -1186,6 +1191,10 @@ d2b.zones.dev.resources.wayland-host-socket = { transport = "unix"; purpose = "wayland-host-socket"; locality = "host-local"; + visibility = "zone"; + consumerPolicy = { + allowedOperations = [ "resolve" ]; + }; }; }; ``` diff --git a/docs/specs/ADR-046-resources-host-guest-process-user.md b/docs/specs/ADR-046-resources-host-guest-process-user.md index add4fe016..543e82f81 100644 --- a/docs/specs/ADR-046-resources-host-guest-process-user.md +++ b/docs/specs/ADR-046-resources-host-guest-process-user.md @@ -1643,12 +1643,21 @@ EffectPort/LaunchTicket path. | `purpose` | string | required | Bounded stable purpose label; max 63 chars. | | `serviceFingerprint` | string? | `null` | Bounded service/schema fingerprint or capability class; no raw schema bytes. | | `locality` | `host-local\|guest-local\|cross-domain\|zone-local` | required | Closed locality/visibility class. | -| `visibility` | `owner\|provider\|zone` | `provider` | Closed visibility scope for consumers. | +| `visibility` | `owner\|provider\|zone` | `provider` | Closed visibility scope for consumers. These are the only accepted values; there are no aliases such as `private`, `provider-internal`, `zone-private`, or `authorized-consumers`. | | `attachmentPolicy` | object | `{ supported: false }` | Bounded attachment support/policy (supported, max attachments); no locator. | -| `consumerPolicy` | object | `{}` | Bounded consumer policy / allowed consumer ref bounds. | +| `consumerPolicy` | object | `{}` | The only finer-grained consumer gate: bounded `allowedSubjects` (exact same-Zone ResourceRefs), `allowedProviderComponents` (exact signed component IDs), and `allowedOperations` (`resolve\|attach\|observe`). Omitted lists add no restriction beyond `visibility`; a present list is an allowlist, and all present dimensions must match. | | `lifecyclePolicy` | `pinned\|recycle-with-producer\|recreate-on-generation` | `recycle-with-producer` | Closed lifecycle/recycle policy. | | `provider` | ProviderExtension? | `null` | Optional canonical `spec.provider = { schemaId, schemaVersion, settings }` (D089); implementation-only, no locator. | +`visibility` is only the coarse candidate scope: `owner` admits the exact +`metadata.ownerRef` subject, `provider` admits authenticated Provider subjects +and signed Provider components in the Zone, and `zone` admits all other +same-Zone subjects. It never grants +resolution by itself. Normal Role/RoleBinding authorization and every +`consumerPolicy` allowlist still apply. `consumerPolicy` is strict and +deny-unknown; providers may not add visibility values or string/array policy +aliases. + ### Endpoint base status (Layer 2) On top of the universal base (including `status.update`, D091), the Endpoint @@ -1683,6 +1692,11 @@ Provider-specific implementation extension uses the optional `status.provider` consumer's `dependency-changed` reconcile trigger. - Resolving an `Endpoint` to a live transport/FD requires authorization; an unauthorized resolve is denied with a typed error and no locator is returned. +- Core admission first applies the closed `visibility` scope, then normal + Role/RoleBinding authorization, then every present `consumerPolicy` + allowlist. It authenticates the subject and signed Provider component rather + than accepting either identity from the request body. A mismatch at any + layer returns `endpoint-resolve-denied`. - The virtiofs `Export` resource remains the attachment lifecycle owner and references its `Endpoint` where the exported endpoint is independently consumed (`ADR-046-resources-volume`). @@ -2348,11 +2362,11 @@ A work item whose `Destination` row introduces a new `d2b-provider-*` crate must | Current source | `packages/d2b-core/src/processes.rs`: `ProcessRole` (18 variants), `ProcessNode`, `RoleProfile`, `NamespaceSet`, `MountPolicy`, `CgroupPlacement`, `ReadinessPredicate`; `packages/d2b-core/src/minijail_profile.rs`: `MinijailProfile`, `UserNamespaceProfile`, `NamespaceSet`, `MountPolicy`, `BindMount`, `CgroupPlacement`; `packages/d2b-core/src/storage.rs`: `StoragePathSpec`, `AclGrant`, `CleanupPolicy`, `RepairPolicy`; `packages/d2b-realm-core/src/ids.rs`: `RealmId`, `WorkloadId` (→ GuestRef), `NodeId` (→ HostRef), `ProviderId` (→ Provider ResourceRef), `ExecutionId` (→ EphemeralProcess exec identity), `PrincipalId` (→ User ResourceRef), `AllocatorLeaseId`, `ControllerGenerationId`; `packages/d2b-realm-core/src/workload.rs`: `WorkloadProviderKind` (`LocalVm`→runtime-cloud-hypervisor Provider, `QemuMedia`→runtime-qemu-media Provider, `ProviderManaged`→ACA/relay Providers, `UnsafeLocal`→user-only Host `isolationPosture="none"`), `IsolationPosture` (`VirtualMachine`→Guest, `ProviderManaged`→Guest, `UnsafeLocal`→Host `isolationPosture="none"`), `WorkloadExecutionPosture`, `WorkloadSummary`, `WorkloadState`; `packages/d2b-realm-core/src/target.rs`: `RealmTarget`, `TargetName`, `RealmTargetParser` (current analog for `/` ResourceRef parsing); `packages/d2b-realm-core/src/realm.rs`: `RealmPath`, `RealmControllerPlacement`, `EntrypointMode` (current Zone hierarchy analog); `packages/d2b-core/src/workload_identity.rs`: `WorkloadIdentity`, `WorkloadTarget` (= `RealmTarget`), `WorkloadBackend`, `WorkloadRuntimeIntent` (identity/backend separation reuse model for Host/Guest ResourceType split) | | Reuse source | `packages/d2b-contracts/src/v3/` as destination; no equivalent main source for Host/Guest/Process ResourceType contracts | | Reuse action | adapt | -| Destination | `packages/d2b-contracts/src/v3/host.rs`, `packages/d2b-contracts/src/v3/guest.rs`, `packages/d2b-contracts/src/v3/execution_policy.rs`, `packages/d2b-contracts/src/v3/process.rs`, `packages/d2b-contracts/src/v3/ephemeral_process.rs`, `packages/d2b-contracts/src/v3/user.rs` | -| Detailed design | Implement strict typed Rust structs for HostSpec, GuestSpec, ExecutionPolicy, ExecutionSpec, SandboxSpec, BudgetSpec, MountSpec, NetworkUsageSpec, DeviceUsageSpec, EndpointSpec, TelemetrySpec, ProcessSpec, EphemeralProcessSpec, UserSpec; strict serde deny_unknown_fields; bounds/redaction on all string fields; stable error types; `UserSpec.osUsername` validated as OS username (1..255 bytes, no NUL/control/path-separator), not ResourceName grammar Primary reuse disposition: `adapt`. Preserved source-plan detail: extract and adapt. | +| Destination | `packages/d2b-contracts/src/v3/host.rs`, `packages/d2b-contracts/src/v3/guest.rs`, `packages/d2b-contracts/src/v3/execution_policy.rs`, `packages/d2b-contracts/src/v3/process.rs`, `packages/d2b-contracts/src/v3/ephemeral_process.rs`, `packages/d2b-contracts/src/v3/user.rs`, `packages/d2b-contracts/src/v3/endpoint.rs` | +| Detailed design | Implement strict typed Rust structs for HostSpec, GuestSpec, ExecutionPolicy, ExecutionSpec, SandboxSpec, BudgetSpec, MountSpec, NetworkUsageSpec, DeviceUsageSpec, EndpointSpec, EndpointConsumerPolicy, TelemetrySpec, ProcessSpec, EphemeralProcessSpec, UserSpec; EndpointSpec accepts exactly `owner\|provider\|zone`, and EndpointConsumerPolicy owns the only finer gates (`allowedSubjects`, `allowedProviderComponents`, `allowedOperations`) with no schema aliases; strict serde deny_unknown_fields; bounds/redaction on all string fields; stable error types; `UserSpec.osUsername` validated as OS username (1..255 bytes, no NUL/control/path-separator), not ResourceName grammar Primary reuse disposition: `adapt`. Preserved source-plan detail: extract and adapt. | | Integration | Provider dossiers, controller descriptors, Zone resource API, Nix resource compiler | | Data migration | Full reset; no v2 resource import | -| Validation | Golden JSON vectors for each ResourceType; serde unknown-field rejection; bounds enforcement; `UserSpec.osUsername` OS-username validation (underscore allowed, NUL rejected) | +| Validation | Golden JSON vectors for each ResourceType; Endpoint vectors accept only `owner\|provider\|zone`, reject every legacy/private visibility alias, reject scalar/array `consumerPolicy` aliases, and cover each canonical consumer allowlist; a docs drift test parses every `type: Endpoint` YAML/Nix example and fails unless visibility is canonical and finer gates occur only under `consumerPolicy`; serde unknown-field rejection; bounds enforcement; `UserSpec.osUsername` OS-username validation (underscore allowed, NUL rejected) | | Removal proof | Old DTO types removed only after owning Resource/Provider integrations are live | ### ADR046-exec-002 diff --git a/docs/specs/ADR-046-validation-and-delivery.md b/docs/specs/ADR-046-validation-and-delivery.md index b20c23531..8b40cb52e 100644 --- a/docs/specs/ADR-046-validation-and-delivery.md +++ b/docs/specs/ADR-046-validation-and-delivery.md @@ -720,7 +720,15 @@ Ported verbatim (per `ADR046-session-001/002`) from main's `operationId` are NOT promoted to resources and stay internal; the `ProcessSpec`-has-no-inline-`endpoints` lint; standard ResourceType count is 19 (Endpoint present in the catalog); every retained public `*Id`/`Handle` has a - documented rationale row. + documented rationale row; schema vectors accept exactly + `visibility=owner|provider|zone` and reject aliases; controller admission + tests cover all three scopes plus each + `consumerPolicy.allowedSubjects`/`allowedProviderComponents`/ + `allowedOperations` mismatch; a docs drift test parses every Endpoint + example and fails if visibility is outside the enum, if a finer restriction + is encoded as another visibility value, or if `consumerPolicy` is a + scalar/array alias. ResourceExport visibility is explicitly outside this + Endpoint-only drift check. - Entra identity-Guest login tests (D093), all against a **fake Entrablau Guest login service** (no live Entra in CI; a manual real-Guest login is a separate non-CI check): fake Guest login success → `interactionState: Authenticated` diff --git a/docs/specs/providers/ADR-046-provider-audio-pipewire.md b/docs/specs/providers/ADR-046-provider-audio-pipewire.md index ff15a326c..e87d4d707 100644 --- a/docs/specs/providers/ADR-046-provider-audio-pipewire.md +++ b/docs/specs/providers/ADR-046-provider-audio-pipewire.md @@ -1284,9 +1284,10 @@ spec: purpose: audio-pipewire.d2bus.org/vhost-user-sound serviceFingerprint: audio-pipewire.d2bus.org/vhost-user-sound.v3 locality: host-local - visibility: authorized-consumers + visibility: zone attachmentPolicy: launch-ticket - consumerPolicy: same-zone-authorized + consumerPolicy: + allowedOperations: [resolve] lifecyclePolicy: recycle-with-producer status: readiness: Ready @@ -1312,9 +1313,11 @@ spec: purpose: audio-pipewire.d2bus.org/audio-control serviceFingerprint: audio-pipewire.d2bus.org/AudioMediator.v3 locality: host-local - visibility: authorized-consumers + visibility: zone attachmentPolicy: component-session - consumerPolicy: same-user-authorized + consumerPolicy: + allowedSubjects: [User/alice] + allowedOperations: [resolve] lifecyclePolicy: recycle-with-producer status: readiness: Ready @@ -1340,9 +1343,10 @@ spec: purpose: audio-pipewire.d2bus.org/import-route serviceFingerprint: audio-pipewire.d2bus.org/AudioServiceRoute.v3 locality: zone-local - visibility: authorized-consumers + visibility: zone attachmentPolicy: component-session - consumerPolicy: same-zone-authorized + consumerPolicy: + allowedOperations: [resolve] lifecyclePolicy: recycle-with-producer status: readiness: Ready @@ -1373,9 +1377,10 @@ spec: purpose: audio-pipewire.d2bus.org/guest-audio-set serviceFingerprint: audio-pipewire.d2bus.org/AudioSet.v3 locality: guest-local - visibility: authorized-consumers + visibility: zone attachmentPolicy: component-session - consumerPolicy: same-zone-authorized + consumerPolicy: + allowedOperations: [resolve] lifecyclePolicy: recycle-with-producer status: readiness: Ready diff --git a/docs/specs/providers/ADR-046-provider-clipboard-wayland.md b/docs/specs/providers/ADR-046-provider-clipboard-wayland.md index 7ed56d8a5..410df4b95 100644 --- a/docs/specs/providers/ADR-046-provider-clipboard-wayland.md +++ b/docs/specs/providers/ADR-046-provider-clipboard-wayland.md @@ -564,9 +564,10 @@ spec: purpose: clipboard-wayland.d2bus.org/picker-coordination serviceFingerprint: clipboard-wayland.d2bus.org/picker-coord.v3 locality: host-local - visibility: authorized-consumers + visibility: zone attachmentPolicy: component-session - consumerPolicy: same-zone-authorized + consumerPolicy: + allowedOperations: [resolve] lifecyclePolicy: recycle-with-producer status: readiness: Ready @@ -592,9 +593,10 @@ spec: purpose: clipboard-wayland.d2bus.org/bridge serviceFingerprint: clipboard-wayland.d2bus.org/bridge.v3 locality: host-local - visibility: authorized-consumers + visibility: zone attachmentPolicy: component-session - consumerPolicy: same-zone-authorized + consumerPolicy: + allowedOperations: [resolve] lifecyclePolicy: recycle-with-producer status: readiness: Ready @@ -620,9 +622,11 @@ spec: purpose: clipboard-wayland.d2bus.org/management serviceFingerprint: clipboard-wayland.d2bus.org/management.v3 locality: host-local - visibility: authorized-consumers + visibility: zone attachmentPolicy: component-session - consumerPolicy: operator-authorized + consumerPolicy: + allowedSubjects: [User/alice] + allowedOperations: [resolve] lifecyclePolicy: recycle-with-producer status: readiness: Ready diff --git a/docs/specs/providers/ADR-046-provider-credential-entra.md b/docs/specs/providers/ADR-046-provider-credential-entra.md index 7c31e676b..296981eda 100644 --- a/docs/specs/providers/ADR-046-provider-credential-entra.md +++ b/docs/specs/providers/ADR-046-provider-credential-entra.md @@ -218,9 +218,10 @@ spec: purpose: credential-entra.d2bus.org/entra-login-token serviceFingerprint: credential-entra.d2bus.org/EntrablauLoginTokenService/v1 locality: guest-local - visibility: authorized-consumers + visibility: zone attachmentPolicy: component-session - consumerPolicy: same-zone-authorized + consumerPolicy: + allowedOperations: [resolve] lifecyclePolicy: recycle-with-producer status: readiness: Ready @@ -448,9 +449,11 @@ Endpoint exported by that Guest. d2b core does not import the sibling flake. purpose = "credential-entra.d2bus.org/entra-login-token"; serviceFingerprint = "credential-entra.d2bus.org/EntrablauLoginTokenService/v1"; locality = "guest-local"; - visibility = "authorized-consumers"; + visibility = "zone"; attachmentPolicy = "component-session"; - consumerPolicy = "same-zone-authorized"; + consumerPolicy = { + allowedOperations = [ "resolve" ]; + }; lifecyclePolicy = "recycle-with-producer"; }; }; diff --git a/docs/specs/providers/ADR-046-provider-device-gpu.md b/docs/specs/providers/ADR-046-provider-device-gpu.md index 3f4711996..0ad6c481d 100644 --- a/docs/specs/providers/ADR-046-provider-device-gpu.md +++ b/docs/specs/providers/ADR-046-provider-device-gpu.md @@ -458,9 +458,11 @@ spec: purpose: gpu-sidecar serviceFingerprint: device-gpu.d2bus.org/gpu-sidecar/v1 locality: cross-domain - visibility: provider-internal + visibility: provider attachmentPolicy: launch-ticket-only - consumerPolicy: [Provider/runtime-cloud-hypervisor] + consumerPolicy: + allowedSubjects: [Provider/runtime-cloud-hypervisor] + allowedOperations: [resolve] lifecyclePolicy: recycle-with-producer ``` @@ -767,9 +769,11 @@ spec: purpose: video-sidecar serviceFingerprint: device-gpu.d2bus.org/video-sidecar/v1 locality: cross-domain - visibility: provider-internal + visibility: provider attachmentPolicy: launch-ticket-only - consumerPolicy: [Provider/runtime-cloud-hypervisor] + consumerPolicy: + allowedSubjects: [Provider/runtime-cloud-hypervisor] + allowedOperations: [resolve] lifecyclePolicy: recycle-with-producer ``` diff --git a/docs/specs/providers/ADR-046-provider-device-security-key.md b/docs/specs/providers/ADR-046-provider-device-security-key.md index 4551df98b..369e45790 100644 --- a/docs/specs/providers/ADR-046-provider-device-security-key.md +++ b/docs/specs/providers/ADR-046-provider-device-security-key.md @@ -666,9 +666,11 @@ spec: purpose: device-security-key.d2bus.org/ctaphid-relay serviceFingerprint: device-security-key.d2bus.org/SecurityKeyCtapRelay.v3 locality: cross-domain - visibility: authorized-consumers + visibility: zone attachmentPolicy: component-session - consumerPolicy: device-security-key.d2bus.org/frontend-only + consumerPolicy: + allowedProviderComponents: [device-security-key.d2bus.org/frontend] + allowedOperations: [resolve] lifecyclePolicy: recycle-with-producer status: readiness: Ready @@ -744,9 +746,11 @@ spec: purpose: device-security-key.d2bus.org/ctaphid-frontend serviceFingerprint: device-security-key.d2bus.org/SecurityKeyCtapRelay.v3 locality: zone-local - visibility: provider-internal + visibility: provider attachmentPolicy: launch-ticket-only - consumerPolicy: device-security-key.d2bus.org/service-only + consumerPolicy: + allowedProviderComponents: [device-security-key.d2bus.org/service] + allowedOperations: [resolve] lifecyclePolicy: recycle-with-producer ``` diff --git a/docs/specs/providers/ADR-046-provider-device-tpm.md b/docs/specs/providers/ADR-046-provider-device-tpm.md index e6f01bae1..8c7202ece 100644 --- a/docs/specs/providers/ADR-046-provider-device-tpm.md +++ b/docs/specs/providers/ADR-046-provider-device-tpm.md @@ -622,9 +622,11 @@ spec: purpose: swtpm-tpm-socket serviceFingerprint: device-tpm.d2bus.org/tpm/v1 locality: host-local - visibility: provider-internal + visibility: provider attachmentPolicy: launch-ticket-only - consumerPolicy: [Provider/runtime-cloud-hypervisor] + consumerPolicy: + allowedSubjects: [Provider/runtime-cloud-hypervisor] + allowedOperations: [resolve] lifecyclePolicy: recycle-with-producer --- apiVersion: resources.d2bus.org/v3 @@ -641,9 +643,11 @@ spec: purpose: swtpm-control-socket serviceFingerprint: device-tpm.d2bus.org/control/v1 locality: host-local - visibility: provider-internal + visibility: provider attachmentPolicy: launch-ticket-only - consumerPolicy: [Provider/device-tpm] + consumerPolicy: + allowedSubjects: [Provider/device-tpm] + allowedOperations: [resolve] lifecyclePolicy: recycle-with-producer ``` diff --git a/docs/specs/providers/ADR-046-provider-device-usbip.md b/docs/specs/providers/ADR-046-provider-device-usbip.md index 6d06db85a..7835046f9 100644 --- a/docs/specs/providers/ADR-046-provider-device-usbip.md +++ b/docs/specs/providers/ADR-046-provider-device-usbip.md @@ -500,7 +500,7 @@ The controller reconciles three closed worker classes: | Worker | Cardinality/owner | Endpoint | Network behavior | | --- | --- | --- | --- | -| `usbip-host--backend` | exactly one per Host; Provider-owned Host authority | one provider-internal host-local backend Endpoint | no externally reachable listener; all busids register with this backend | +| `usbip-host--backend` | exactly one per Host; Provider-owned Host authority | one host-local Endpoint with `visibility: provider` and a controller-only `consumerPolicy` | no externally reachable listener; all busids register with this backend | | `usbip-net--relay` | exactly one per Network; Provider-owned relay authority | `Endpoint/-usbip-relay` | binds only that Network's uplink on TCP 3240 and multiplexes all admitted Services | | `usbip-binding--proxy` | one per attached `UsbBinding`; Binding-owned | `Endpoint/-usbip-private` | runs for the exact Guest and exposes only Guest-private loopback TCP 3240 | @@ -529,9 +529,13 @@ spec: purpose: device-usbip.d2bus.org/relay serviceFingerprint: device-usbip.d2bus.org/UsbipRelay.v4 locality: cross-domain - visibility: authorized-consumers + visibility: zone attachmentPolicy: launch-ticket-only - consumerPolicy: device-usbip.d2bus.org/leased-service-and-binding-proxy + consumerPolicy: + allowedProviderComponents: + - device-usbip.d2bus.org/service-controller + - device-usbip.d2bus.org/binding-proxy + allowedOperations: [resolve] lifecyclePolicy: recycle-with-producer authority: authorityScope: host diff --git a/docs/specs/providers/ADR-046-provider-display-wayland.md b/docs/specs/providers/ADR-046-provider-display-wayland.md index 77544a519..33ba79a04 100644 --- a/docs/specs/providers/ADR-046-provider-display-wayland.md +++ b/docs/specs/providers/ADR-046-provider-display-wayland.md @@ -702,9 +702,10 @@ spec: purpose: display-wayland.d2bus.org/wayland-cross-domain serviceFingerprint: display-wayland.d2bus.org/wayland-data.v3 locality: cross-domain - visibility: authorized-consumers + visibility: zone attachmentPolicy: launch-ticket - consumerPolicy: same-zone-authorized + consumerPolicy: + allowedOperations: [resolve] lifecyclePolicy: recycle-with-producer status: readiness: Ready @@ -730,9 +731,10 @@ spec: purpose: display-wayland.d2bus.org/guest-cross-domain serviceFingerprint: display-wayland.d2bus.org/guest-frontend.v3 locality: cross-domain - visibility: authorized-consumers + visibility: zone attachmentPolicy: launch-ticket - consumerPolicy: same-zone-authorized + consumerPolicy: + allowedOperations: [resolve] lifecyclePolicy: recycle-with-producer status: readiness: Ready diff --git a/docs/specs/providers/ADR-046-provider-network-local.md b/docs/specs/providers/ADR-046-provider-network-local.md index cff42a993..493b0872d 100644 --- a/docs/specs/providers/ADR-046-provider-network-local.md +++ b/docs/specs/providers/ADR-046-provider-network-local.md @@ -1001,9 +1001,11 @@ spec: purpose: network-local.d2bus.org/agent serviceFingerprint: d2b.network.v3.agent/v1 locality: cross-domain - visibility: zone-private + visibility: zone attachmentPolicy: none - consumerPolicy: [provider-network-local.d2bus.org/controller] + consumerPolicy: + allowedProviderComponents: [provider-network-local.d2bus.org/controller] + allowedOperations: [resolve] lifecyclePolicy: producer-owned status: phase: Ready diff --git a/docs/specs/providers/ADR-046-provider-notification-desktop.md b/docs/specs/providers/ADR-046-provider-notification-desktop.md index 37e979a9c..a46594e0c 100644 --- a/docs/specs/providers/ADR-046-provider-notification-desktop.md +++ b/docs/specs/providers/ADR-046-provider-notification-desktop.md @@ -492,9 +492,10 @@ spec: purpose: notification-desktop.d2bus.org/host-sink serviceFingerprint: notification-desktop.d2bus.org/DesktopNotificationSink.v3 locality: host-local - visibility: authorized-consumers + visibility: zone attachmentPolicy: component-session - consumerPolicy: same-zone-authorized + consumerPolicy: + allowedOperations: [resolve] lifecyclePolicy: recycle-with-producer status: readiness: Ready @@ -520,9 +521,10 @@ spec: purpose: notification-desktop.d2bus.org/observer serviceFingerprint: notification-desktop.d2bus.org/DesktopNotificationObserver.v3 locality: host-local - visibility: authorized-consumers + visibility: zone attachmentPolicy: component-session - consumerPolicy: observer-authorized + consumerPolicy: + allowedOperations: [resolve] lifecyclePolicy: recycle-with-producer status: readiness: Ready @@ -548,9 +550,10 @@ spec: purpose: notification-desktop.d2bus.org/guest-source serviceFingerprint: notification-desktop.d2bus.org/NotificationSource.v3 locality: cross-domain - visibility: authorized-consumers + visibility: zone attachmentPolicy: component-session - consumerPolicy: same-zone-authorized + consumerPolicy: + allowedOperations: [resolve] lifecyclePolicy: recycle-with-producer status: readiness: Ready diff --git a/docs/specs/providers/ADR-046-provider-observability-otel.md b/docs/specs/providers/ADR-046-provider-observability-otel.md index a41a05185..5f32d782d 100644 --- a/docs/specs/providers/ADR-046-provider-observability-otel.md +++ b/docs/specs/providers/ADR-046-provider-observability-otel.md @@ -841,9 +841,10 @@ spec: purpose: observability-otel.d2bus.org/bounded-emitter-drain serviceFingerprint: observability-otel.d2bus.org/bounded-emitter.v3 locality: host-local - visibility: authorized-consumers + visibility: zone attachmentPolicy: component-session - consumerPolicy: same-zone-authorized + consumerPolicy: + allowedOperations: [resolve] lifecyclePolicy: recycle-with-producer status: readiness: Ready @@ -869,9 +870,10 @@ spec: purpose: observability-otel.d2bus.org/otlp-grpc-ingest serviceFingerprint: observability-otel.d2bus.org/otlp-grpc.v3 locality: host-local - visibility: authorized-consumers + visibility: zone attachmentPolicy: component-session - consumerPolicy: telemetry-producer-authorized + consumerPolicy: + allowedOperations: [resolve] lifecyclePolicy: recycle-with-producer status: readiness: Ready @@ -897,9 +899,10 @@ spec: purpose: observability-otel.d2bus.org/private-guest-ingest serviceFingerprint: observability-otel.d2bus.org/guest-otlp-ingest.v3 locality: cross-domain - visibility: authorized-consumers + visibility: zone attachmentPolicy: launch-ticket - consumerPolicy: same-zone-authorized + consumerPolicy: + allowedOperations: [resolve] lifecyclePolicy: recycle-with-producer status: readiness: Ready diff --git a/docs/specs/providers/ADR-046-provider-runtime-azure-container-apps.md b/docs/specs/providers/ADR-046-provider-runtime-azure-container-apps.md index 3896bb50b..5698ef064 100644 --- a/docs/specs/providers/ADR-046-provider-runtime-azure-container-apps.md +++ b/docs/specs/providers/ADR-046-provider-runtime-azure-container-apps.md @@ -514,9 +514,11 @@ spec: purpose: d2b.aca.v3.deployment serviceFingerprint: runtime-azure-container-apps.d2bus.org/deployment/v1 locality: guest-local - visibility: provider-internal + visibility: provider attachmentPolicy: launch-ticket-only - consumerPolicy: [Provider/runtime-azure-container-apps] + consumerPolicy: + allowedSubjects: [Provider/runtime-azure-container-apps] + allowedOperations: [resolve] lifecyclePolicy: recycle-with-producer ``` @@ -538,9 +540,11 @@ spec: purpose: aca-sandbox-agent serviceFingerprint: runtime-azure-container-apps.d2bus.org/sandbox-agent/v1 locality: cross-domain - visibility: provider-internal + visibility: provider attachmentPolicy: launch-ticket-only - consumerPolicy: [Provider/runtime-azure-container-apps] + consumerPolicy: + allowedSubjects: [Provider/runtime-azure-container-apps] + allowedOperations: [resolve] lifecyclePolicy: recycle-with-producer ``` diff --git a/docs/specs/providers/ADR-046-provider-runtime-azure-virtual-machine.md b/docs/specs/providers/ADR-046-provider-runtime-azure-virtual-machine.md index dd09660e7..760125ad0 100644 --- a/docs/specs/providers/ADR-046-provider-runtime-azure-virtual-machine.md +++ b/docs/specs/providers/ADR-046-provider-runtime-azure-virtual-machine.md @@ -616,9 +616,11 @@ spec: purpose: d2b-bus-controller-endpoint serviceFingerprint: runtime-azure-virtual-machine.d2bus.org/controller/v1 locality: guest-local - visibility: provider-internal + visibility: provider attachmentPolicy: launch-ticket-only - consumerPolicy: [Provider/runtime-azure-virtual-machine] + consumerPolicy: + allowedSubjects: [Provider/runtime-azure-virtual-machine] + allowedOperations: [resolve] lifecyclePolicy: recycle-with-producer --- apiVersion: resources.d2bus.org/v3 @@ -635,9 +637,11 @@ spec: purpose: d2b-bus-service-endpoint serviceFingerprint: runtime-azure-virtual-machine.d2bus.org/bootstrap/v1 locality: guest-local - visibility: provider-internal + visibility: provider attachmentPolicy: launch-ticket-only - consumerPolicy: [Provider/runtime-azure-virtual-machine] + consumerPolicy: + allowedSubjects: [Provider/runtime-azure-virtual-machine] + allowedOperations: [resolve] lifecyclePolicy: recycle-with-producer ``` diff --git a/docs/specs/providers/ADR-046-provider-runtime-cloud-hypervisor.md b/docs/specs/providers/ADR-046-provider-runtime-cloud-hypervisor.md index 6f94c2db8..fea562861 100644 --- a/docs/specs/providers/ADR-046-provider-runtime-cloud-hypervisor.md +++ b/docs/specs/providers/ADR-046-provider-runtime-cloud-hypervisor.md @@ -1073,9 +1073,11 @@ spec: purpose: ch-api-socket serviceFingerprint: runtime-cloud-hypervisor.d2bus.org/ch-api/v1 locality: host-local - visibility: provider-internal + visibility: provider attachmentPolicy: launch-ticket-only - consumerPolicy: [Provider/runtime-cloud-hypervisor] + consumerPolicy: + allowedSubjects: [Provider/runtime-cloud-hypervisor] + allowedOperations: [resolve] lifecyclePolicy: recycle-with-producer --- apiVersion: resources.d2bus.org/v3 @@ -1092,9 +1094,11 @@ spec: purpose: guest-control serviceFingerprint: d2b.guest-control.d2bus.org/kk/v1 locality: cross-domain - visibility: provider-internal + visibility: provider attachmentPolicy: launch-ticket-only - consumerPolicy: [Provider/runtime-cloud-hypervisor] + consumerPolicy: + allowedSubjects: [Provider/runtime-cloud-hypervisor] + allowedOperations: [resolve] lifecyclePolicy: recycle-with-producer ``` diff --git a/docs/specs/providers/ADR-046-provider-shell-terminal.md b/docs/specs/providers/ADR-046-provider-shell-terminal.md index d1def521e..48de48da2 100644 --- a/docs/specs/providers/ADR-046-provider-shell-terminal.md +++ b/docs/specs/providers/ADR-046-provider-shell-terminal.md @@ -705,9 +705,12 @@ d2b.zones.dev.resources.shell-terminal-service-endpoint = { purpose = "shell-terminal.d2bus.org/controller-service"; serviceFingerprint = "shell-terminal.d2bus.org/shell-terminal.v3"; locality = "host-local"; - visibility = "authorized-consumers"; + visibility = "zone"; attachmentPolicy = "component-session"; - consumerPolicy = "operator-authorized"; + consumerPolicy = { + allowedSubjects = [ "User/alice" ]; + allowedOperations = [ "resolve" ]; + }; lifecyclePolicy = "recycle-with-producer"; }; }; @@ -725,9 +728,12 @@ d2b.zones.dev.resources.shell-session-supervisor-endpoint = { purpose = "shell-terminal.d2bus.org/session-supervisor"; serviceFingerprint = "shell-terminal.d2bus.org/shell-session-supervisor.v1"; locality = "guest-local"; - visibility = "authorized-consumers"; + visibility = "zone"; attachmentPolicy = "component-session"; - consumerPolicy = "session-owner-authorized"; + consumerPolicy = { + allowedSubjects = [ "User/alice" ]; + allowedOperations = [ "resolve" ]; + }; lifecyclePolicy = "recycle-with-producer"; }; }; diff --git a/docs/specs/providers/ADR-046-provider-system-core.md b/docs/specs/providers/ADR-046-provider-system-core.md index 6c802bbc8..6da80d7dc 100644 --- a/docs/specs/providers/ADR-046-provider-system-core.md +++ b/docs/specs/providers/ADR-046-provider-system-core.md @@ -598,9 +598,11 @@ spec: purpose: system-core.d2bus.org/host-user-control serviceFingerprint: system-core.d2bus.org/HostUserControl.v3 locality: host-local - visibility: provider-internal + visibility: provider attachmentPolicy: component-session - consumerPolicy: zone-runtime-only + consumerPolicy: + allowedProviderComponents: [system-core.d2bus.org/zone-runtime] + allowedOperations: [resolve] lifecyclePolicy: recycle-with-producer status: readiness: Ready @@ -649,7 +651,8 @@ trusted endpoint policy). They use: | `d2b.resource.v3` | Receive reconcile hints from store post-commit dispatcher | inbound to handlers | No public external service endpoint is registered on d2b-bus. The fixed -host/user control Endpoint above remains provider-internal and does not expose +host/user control Endpoint above has `visibility: provider` and admits only the +Zone-runtime component through `consumerPolicy`; it does not expose provider-specific methods to operators or other controllers. ### 6.1 ResourceClient usage diff --git a/docs/specs/providers/ADR-046-provider-system-minijail.md b/docs/specs/providers/ADR-046-provider-system-minijail.md index 0dcb4faab..3804a73c9 100644 --- a/docs/specs/providers/ADR-046-provider-system-minijail.md +++ b/docs/specs/providers/ADR-046-provider-system-minijail.md @@ -215,9 +215,11 @@ spec: purpose: system-minijail.d2bus.org/process-control serviceFingerprint: system-minijail.d2bus.org/ProcessControl.v3 locality: host-local - visibility: provider-internal + visibility: provider attachmentPolicy: component-session - consumerPolicy: provider-supervisor-only + consumerPolicy: + allowedProviderComponents: [system-core.d2bus.org/provider-supervisor] + allowedOperations: [resolve] lifecyclePolicy: recycle-with-producer status: readiness: Ready diff --git a/docs/specs/providers/ADR-046-provider-system-systemd.md b/docs/specs/providers/ADR-046-provider-system-systemd.md index 3d27afe0a..89148033c 100644 --- a/docs/specs/providers/ADR-046-provider-system-systemd.md +++ b/docs/specs/providers/ADR-046-provider-system-systemd.md @@ -398,9 +398,11 @@ spec: purpose: system-systemd.d2bus.org/process-control serviceFingerprint: system-systemd.d2bus.org/ProcessControl.v3 locality: host-local - visibility: provider-internal + visibility: provider attachmentPolicy: component-session - consumerPolicy: provider-supervisor-only + consumerPolicy: + allowedProviderComponents: [system-core.d2bus.org/provider-supervisor] + allowedOperations: [resolve] lifecyclePolicy: recycle-with-producer status: readiness: Ready diff --git a/docs/specs/providers/ADR-046-provider-transport-azure-relay.md b/docs/specs/providers/ADR-046-provider-transport-azure-relay.md index 99c1aba88..9d0cc61c9 100644 --- a/docs/specs/providers/ADR-046-provider-transport-azure-relay.md +++ b/docs/specs/providers/ADR-046-provider-transport-azure-relay.md @@ -730,9 +730,11 @@ spec: purpose: transport-azure-relay.d2bus.org/listener serviceFingerprint: transport-azure-relay.d2bus.org/listener.v1 locality: cross-domain - visibility: zone-private + visibility: zone attachmentPolicy: none - consumerPolicy: [core-controller.d2bus.org/zonelink] + consumerPolicy: + allowedProviderComponents: [core-controller.d2bus.org/zonelink] + allowedOperations: [resolve] lifecyclePolicy: producer-owned status: phase: Ready @@ -758,9 +760,11 @@ spec: purpose: transport-azure-relay.d2bus.org/sender serviceFingerprint: transport-azure-relay.d2bus.org/sender.v1 locality: cross-domain - visibility: zone-private + visibility: zone attachmentPolicy: none - consumerPolicy: [core-controller.d2bus.org/zonelink] + consumerPolicy: + allowedProviderComponents: [core-controller.d2bus.org/zonelink] + allowedOperations: [resolve] lifecyclePolicy: producer-owned status: phase: Ready diff --git a/docs/specs/providers/ADR-046-provider-transport-unix.md b/docs/specs/providers/ADR-046-provider-transport-unix.md index 10b1b7812..ee1894eb3 100644 --- a/docs/specs/providers/ADR-046-provider-transport-unix.md +++ b/docs/specs/providers/ADR-046-provider-transport-unix.md @@ -661,9 +661,11 @@ spec: purpose: transport-unix.d2bus.org/portal serviceFingerprint: transport-unix.d2bus.org/portal.v1 locality: host-local - visibility: zone-private + visibility: zone attachmentPolicy: none - consumerPolicy: [core-controller.d2bus.org/zonelink] + consumerPolicy: + allowedProviderComponents: [core-controller.d2bus.org/zonelink] + allowedOperations: [resolve] lifecyclePolicy: producer-owned status: phase: Ready diff --git a/docs/specs/providers/ADR-046-provider-transport-vsock.md b/docs/specs/providers/ADR-046-provider-transport-vsock.md index 4ca209015..0166feb82 100644 --- a/docs/specs/providers/ADR-046-provider-transport-vsock.md +++ b/docs/specs/providers/ADR-046-provider-transport-vsock.md @@ -378,9 +378,11 @@ spec: purpose: transport-vsock.d2bus.org/service serviceFingerprint: transport-vsock.d2bus.org/service.v1 locality: cross-domain - visibility: zone-private + visibility: zone attachmentPolicy: none - consumerPolicy: [core-controller.d2bus.org/zonelink] + consumerPolicy: + allowedProviderComponents: [core-controller.d2bus.org/zonelink] + allowedOperations: [resolve] lifecyclePolicy: producer-owned status: phase: Ready diff --git a/docs/specs/providers/ADR-046-provider-volume-virtiofs.md b/docs/specs/providers/ADR-046-provider-volume-virtiofs.md index bd527e37f..666bde726 100644 --- a/docs/specs/providers/ADR-046-provider-volume-virtiofs.md +++ b/docs/specs/providers/ADR-046-provider-volume-virtiofs.md @@ -337,9 +337,11 @@ spec: purpose: volume-virtiofs.d2bus.org/export serviceFingerprint: volume-virtiofs.d2bus.org/export.v1 locality: cross-domain - visibility: zone-private + visibility: zone attachmentPolicy: launch-ticket-only - consumerPolicy: [guest-runtime.d2bus.org/virtiofs] + consumerPolicy: + allowedProviderComponents: [guest-runtime.d2bus.org/virtiofs] + allowedOperations: [resolve] lifecyclePolicy: owner-ref-child status: phase: Ready From 3fe115f948f15fd7b468953cdb5ecac72f59dab7 Mon Sep 17 00:00:00 2001 From: John Vicondoa Date: Fri, 24 Jul 2026 11:52:27 -0700 Subject: [PATCH 104/115] docs: add relay to canonical authorization verbs --- .../specs/ADR-046-componentsession-and-bus.md | 26 ++++- docs/specs/ADR-046-decision-register.md | 2 +- docs/specs/ADR-046-nix-configuration.md | 23 ++++- .../ADR-046-resource-api-and-authorization.md | 38 ++++++-- docs/specs/ADR-046-resources-zone-control.md | 86 +++++++++++++---- .../ADR-046-security-and-threat-model.md | 32 +++++-- docs/specs/ADR-046-zone-routing.md | 96 ++++++++++++------- 7 files changed, 227 insertions(+), 76 deletions(-) diff --git a/docs/specs/ADR-046-componentsession-and-bus.md b/docs/specs/ADR-046-componentsession-and-bus.md index de79028c4..fc5c584f5 100644 --- a/docs/specs/ADR-046-componentsession-and-bus.md +++ b/docs/specs/ADR-046-componentsession-and-bus.md @@ -63,7 +63,7 @@ generated async ttrpc services / named streams d2b-transport provides carriage/evidence only. ComponentSession authenticates and protects a session. d2b-bus resolves an exact service/Zone/Provider/ controller route. Native Role/RoleBinding authorizes connect/invoke/stream/ -resource verbs. No layer may widen the authority established below it. +relay/resource verbs. No layer may widen the authority established below it. ## Noise profiles @@ -114,7 +114,8 @@ The trusted endpoint policy binds: - reconnect generation; - exact limit profile; - attachment policy; -- authorization service/verb requirements. +- authorization service/verb requirements, including the immutable forwarded + target verb and next-hop scope when the session carries a relay. The canonical preface+offer is the Noise prologue. Any mismatch changes the transcript and fails. There is no negotiation down to another service, schema, @@ -153,9 +154,11 @@ Role/RoleBinding evaluator used by the resource API checks: subject Zone session purpose/service -verb = connect | invoke | open-stream | attach | cancel | observe +verb = connect | invoke | open-stream | relay | attach | cancel | observe method/stream kind target ResourceRef/Provider/Host/Guest +forwarded target verb and target Zone (relay only) +authenticated inbound ZoneLink and route-selected next hop (relay only) Provider/controller/session generations ``` @@ -165,9 +168,22 @@ Rules may authorize ComponentSession connect/service/stream attributes in the same Role resource as resource verbs. There is no endpoint-local parallel RBAC language. +`relay` is the closed session verb for one-hop forwarding. It permits only the +already-authenticated inbound ZoneLink/transport subject to pass an +already-admitted invocation or named stream to the route-selected next +authorized hop. The peer cannot supply or replace the subject, target verb, +target Zone, next hop, or authorization result. A relay grant conveys no +resource CRUD, identity mapping, capability widening, attachment, credential, +or local lifecycle authority. + Authorization: - is required for the session itself and each method/stream; +- for every forwarded method/stream, independently requires both `relay` for + the authenticated adjacent-Zone subject and the original target verb under + the same exact local scope; the destination rechecks the target verb; +- fails closed when relay, target-verb authorization, or policy state is + missing; - is revision-bound to Role/RoleBinding/Provider/API/Zone policy; - is cached only under exact attributes/short expiry; - invalidates immediately after relevant durable policy commit; @@ -278,6 +294,8 @@ It: - resolves local/Host/Guest/parent-child Zone transport; - binds authenticated subject and operation/deadline; - checks session connect/service/stream RBAC; +- for a ZoneLink forward, checks the canonical `relay` verb and the immutable + target verb separately before opening the next-hop session; - invokes exact generated ttrpc client/server; - bridges named streams with bounded credit; - preserves pinned reverse route/cancellation; @@ -402,5 +420,5 @@ transcript/session generation digest, route, and fixed outcome. | Detailed design | Exact service/resource routes, RBAC, pinned reverse route, cancellation, named stream bridge, no wildcard pub/sub Primary reuse disposition: `adapt`. Preserved source-plan detail: extract/adapt. | | Integration | Every ResourceClient/controller/Provider/CLI service | | Data migration | None — full d2b 3.0 reset; no prior state to migrate | -| Validation | Message isolation, route/auth revocation, fairness, reconnect, no direct-store path | +| Validation | Message isolation; closed session-verb enum including `relay`; relay missing/target verb missing/provider self-assertion fail-closed vectors; route/auth revocation; fairness; reconnect; no direct-store path | | Removal proof | Old direct dispatch branches removed only after route parity | diff --git a/docs/specs/ADR-046-decision-register.md b/docs/specs/ADR-046-decision-register.md index 401779708..1490874d3 100644 --- a/docs/specs/ADR-046-decision-register.md +++ b/docs/specs/ADR-046-decision-register.md @@ -59,7 +59,7 @@ review metadata, or implementation status. | D037 | The universal status phase enum is `Pending`, `Ready`, `Succeeded`, `Degraded`, `Failed`, `Deleted`, or `Unknown`. | Conditions carry starting/deleting/retrying and other detail without multiplying common phases. | Resource object/API/store, all controllers | | D038 | Final deletion has no retained resource tombstone. | After finalizers complete, core emits a Deleted status revision and removes the resource immediately. revision_log is the only deletion history; GET returns not found. | Resource store/API/reconciliation | | D039 | ComponentSession uses Noise-based authenticated/record-protected profiles. | Copy/adapt the proven `d2b-session` and `d2b-session-unix` implementation from main, then reversion, integrate, and validate it against v3 Zone/resource/RBAC contracts. | ComponentSession/bus/security | -| D040 | ComponentSession and resource control use one authorization system. | Session authentication maps to a canonical resource subject; native Role/RoleBinding authorizes connect, invoke, stream, and resource verbs. Handshakes cannot self-assert roles; revision-bound leases revoke on policy change. | ComponentSession/bus/resource API/RBAC | +| D040 | ComponentSession and resource control use one authorization system. | Session authentication maps to a canonical resource subject; native Role/RoleBinding authorizes the closed session verbs `connect`, `invoke`, `open-stream`, `relay`, `attach`, `cancel`, and `observe`, plus resource verbs. `relay` permits only an authenticated ZoneLink/transport subject to forward an already-admitted invocation/stream one authorized hop; every forwarding hop independently requires relay plus the target verb, and relay grants no CRUD, identity mapping, capability widening, or local lifecycle authority. Relay grants are core-generated/ZoneLink-scoped unless explicit durable local-admin policy permits an exact bounded grant; wildcard/self-asserted grants fail closed. Handshakes cannot self-assert roles; revision-bound leases revoke on policy change. | ComponentSession/bus/resource API/RBAC | | D041 | Main is an unrestricted implementation reuse source, not the v3 baseline. | Work items may copy/adapt any useful main code. Each names the exact main commit/file/symbol/tests, proves the selected behavior, maps it to exact v3 destinations, and excludes unrelated ADR 0045 architecture. | Every implementation work item | | D042 | Unsafe-local is not a separate v3 Provider. | It becomes a user-only Host using Provider/system-core, defaultDomain=user, allowedDomains=[user], and defaultUserRef. Its explicit no-isolation posture/warnings remain in Host status/UI; Processes use normal Process Providers. | Host/system-core/Nix/UI/reset | | D043 | Freeze four Guest Provider families. | runtime-cloud-hypervisor, runtime-qemu-media, runtime-azure-container-apps, and runtime-azure-virtual-machine implement Guest. | Guest/Provider dossiers | diff --git a/docs/specs/ADR-046-nix-configuration.md b/docs/specs/ADR-046-nix-configuration.md index 9a6532dd9..dbb4aaf51 100644 --- a/docs/specs/ADR-046-nix-configuration.md +++ b/docs/specs/ADR-046-nix-configuration.md @@ -866,6 +866,9 @@ the ResourceType schema permits. An authored `Endpoint` spec carries only closed through the effect adapter at runtime). `ProcessSpec` carries no inline `endpoints`; a Process's stable endpoints are owned `Endpoint` resources with `producerRef`, and consumers reference `Endpoint/`. +Endpoint `visibility` accepts only `owner`, `provider`, or `zone`; finer access +belongs in the bounded `consumerPolicy`. Nix examples and generated option help +must not invent additional visibility tokens. **ResourceExport and ResourceImport (D096).** Cross-Zone singleton sharing uses ordinary `d2b.zones..resources.` declarations. The Nix compiler does @@ -1550,9 +1553,22 @@ Eval assertions: membership may narrow User admission at runtime but is not declared as a RoleBinding subject. - A subject referencing a `Provider`, `Host`, `Guest`, `Process`, or `User` - must resolve a declared resource of that type in the same Zone. + must resolve a declared resource of that type in the same Zone. A `Zone` + ResourceRef subject can name only the runtime-created local self Zone; + adjacent enrolled Zone transport subjects use an exact trusted + `externalPrincipalSelector` generated outside untrusted Provider/request + input. - Verbs must be from the closed set published by the resource-api and authz foundation spec; the compiler rejects any verb not in that set at eval time. +- `sessionVerbs` uses the separate closed set `connect`, `invoke`, + `open-stream`, `relay`, `attach`, `cancel`, `observe`. `relay` is valid only + in `sessionVerbs`; generated option help describes it as one-hop + ZoneLink-scoped forwarding, not resource or lifecycle authority. +- A relay-bearing Role/RoleBinding is rejected before bundle activation unless + it has exact Zone/resource/name bounds and core-generated ZoneLink provenance, + or durable explicit local-admin policy permits that exact bounded grant. + Wildcard, empty/all-name, Provider-self-asserted, and ordinary + operator-self-asserted relay grants fail closed. The initial closed subject ResourceType set (`Zone`, `Provider`, `Host`, `Guest`, `Process`, `User`) is extended only by a future foundation spec @@ -2155,7 +2171,8 @@ The Nix compiler detects and rejects at eval time: | Artifact type mismatch | An artifact ID used in a field that expects a different `type` (e.g., `"nixos-system"` where `"provider"` is required) | | Duplicate artifact ID | Two `d2b.artifacts.` entries with the same key | | Missing ref target | Any `*Ref` field whose target is not declared | -| Role verb unknown | A verb not in the initial closed set | +| Role verb unknown | A resource or session verb not in its canonical closed set; `relay` is session-only | +| Relay grant invalid | Relay is wildcard/unbounded, not ZoneLink-scoped, self-asserted, or lacks core/admin-policy provenance | ## Current-code mapping @@ -2351,7 +2368,7 @@ contract work item (ADR046-nix-034/ADR046-nix-035). Cross-reference: | Detailed design | `d2b.zones..resources. = { type = ""; spec = { ... }; }` — single attrset covering all ResourceTypes; `type` discriminates dispatch; `spec` fields mirror exact ResourceTypeSchema field names and nesting; Nix option types/defaults/docs generated from `docs/reference/schemas/v3/.json`; no Nix-only fields inside resource declarations; `metadata.name` derives from attr key; `metadata.zone` derives from enclosing zone attr key; `apiVersion` defaulted; `uid`/`generation`/`revision`/`status`/`managedBy` never in Nix; `resource_name` regex `^[a-z][a-z0-9-]*$`; ref validation assertions; `WorkloadProviderKind` → Guest/Host mapping per disposition table above; `Capability` → Role verb mapping per resource-api/authz foundation spec; Zone self-resource spec is `{}`; `parentZone` is a required non-root/forbidden-root compiler-only plain Zone name compiled into sealed allocator topology; `retainedGenerations`/`trustedPublishers` are likewise Zone-level compiler settings not emitted in Zone spec | | Integration | `nixos-modules/default.nix` imports new options files; old realms options coexist until ADR046-nix-002 | | Data migration | Operator configs migrate `d2b.realms.*` → `d2b.zones.*`; `d2b.vms.*` → `d2b.zones..resources.*` with `type = "Guest"` | -| Validation | nix-unit vectors for each ResourceType; ref-validation rejection vectors; malformed ref error shape; `parentZone` is missing on non-root/forbidden on local-root/unknown/self/cyclic/over-depth rejection; scalar module conflicts prove one parent; child-local ZoneLink `childZoneName` must equal its enclosing Zone key; a second uplink resource (even disabled) and a local-root uplink fail eval; missing/local-unresolved `transportProviderRef` fails eval; `managedBy` in spec rejected at eval; Zone spec is `{}` (no `parentZone`, `parentRef`, `retainedGenerations`, etc.) | +| Validation | nix-unit vectors for each ResourceType; ref-validation rejection vectors; malformed ref error shape; resource/session verb closed-enum vectors accept `relay` only in `sessionVerbs`; relay wildcard/unbounded/Provider-self-asserted fixtures fail before activation while an exact core-generated ZoneLink fixture passes; Endpoint visibility accepts exactly `owner|provider|zone` and uses `consumerPolicy` for finer bounds; `parentZone` is missing on non-root/forbidden on local-root/unknown/self/cyclic/over-depth rejection; scalar module conflicts prove one parent; child-local ZoneLink `childZoneName` must equal its enclosing Zone key; a second uplink resource (even disabled) and a local-root uplink fail eval; missing/local-unresolved `transportProviderRef` fails eval; `managedBy` in spec rejected at eval; Zone spec is `{}` (no `parentZone`, `parentRef`, `retainedGenerations`, etc.) | | Tests | `tests/unit/nix/cases/zones-options.nix`, `tests/unit/nix/cases/zones-ref-validation.nix`, `tests/unit/nix/cases/zones-zonelink.nix` | | Drift pin | `make nix-unit-pin` after adding cases | | Removal proof | `options-realms*.nix` removed after `options-zones*.nix` achieves parity and parity drift test passes | diff --git a/docs/specs/ADR-046-resource-api-and-authorization.md b/docs/specs/ADR-046-resource-api-and-authorization.md index 245da0aa7..b159523ef 100644 --- a/docs/specs/ADR-046-resource-api-and-authorization.md +++ b/docs/specs/ADR-046-resource-api-and-authorization.md @@ -171,6 +171,10 @@ consumer that is not authorized (by RoleBinding and the Endpoint's `consumerPolicy`) to resolve an `Endpoint` is denied with `endpoint-resolve-denied` and receives no locator. A producer restart bumps `status.update`/ `endpointGeneration`, firing the consumer's `dependency-changed` reconcile. +Endpoint `visibility` is the closed enum `owner | provider | zone`; examples and +schemas use no other token. It is only a coarse ceiling. `consumerPolicy` +provides finer exact consumer bounds, and both visibility and consumer policy +must allow resolution. ### ResourceExport and ResourceImport (D096) @@ -271,6 +275,7 @@ Role spec contains bounded rules: rules: - resourceTypes: [Process, Volume] verbs: [get, list, watch, create, update-spec] + sessionVerbs: [] subresources: [] resourceNames: [] zones: [dev] @@ -286,9 +291,9 @@ Zone/Provider/controller structural checks. RoleBinding spec contains: - `roleRef: Role/`; -- exact subjects as canonical same-Zone refs, such as User, Provider, Host, - Guest, - or Process; +- exact subjects as canonical same-Zone refs: Zone, User, Provider, Host, Guest, + or Process; a trusted external-principal selector binds the exact adjacent + enrolled `Zone/*` transport subject for a core-generated relay binding; - optional authenticated external-principal selector generated by trusted enrollment/config; - bounded scope narrowing. @@ -323,8 +328,23 @@ Resource verbs: - update-finalizers; - delete. -Runtime service verbs such as invoke/connect/attach/cancel/observe are mapped -through the same engine but are not resource mutations. +Runtime/session verbs are the exact closed set `connect`, `invoke`, +`open-stream`, `relay`, `attach`, `cancel`, and `observe`. They are mapped +through the same engine but are not resource mutations. `relay` permits only an +already-authenticated ZoneLink/transport subject to forward an already-admitted +invocation or stream to one route-selected next hop. It grants no resource CRUD, +identity mapping, capability widening, attachment, credential, or local +lifecycle authority. + +Every forwarding hop evaluates two independent permissions: `relay` for the +authenticated adjacent-Zone transport subject and the forwarded operation's +target verb under the exact local Role/RoleBinding scope. The final target +evaluates the target verb again. Neither permission implies the other; a missing +grant or unavailable policy state fails closed. Relay-bearing Roles/Bindings +are core-generated and ZoneLink-scoped by default. Admission rejects wildcard, +self-asserted, Provider-authored, or ordinary operator-authored relay grants +unless an already-authorized local administrator explicitly permits the exact +bounded grant through durable admin policy. Native RBAC allow is necessary but not sufficient. Core structural checks also enforce: @@ -422,6 +442,8 @@ A parent: - authenticates over a ZoneLink ComponentSession; - maps to a child-local subject/RoleBinding; +- when it forwards again, requires both `relay` and the requested target verb + for that authenticated adjacent-Zone subject; - calls the child d2b.resource.v3 service; - receives only child-authorized data/status. @@ -475,6 +497,8 @@ Stable classes include: - expedited-reconcile-pending; - upgrade-required; - endpoint-resolve-denied; +- relay-denied; +- role-relay-grant-restricted; - authorization-denied; - revision-expired; - backpressure; @@ -538,8 +562,8 @@ process data, and terminal bytes. | Current source | `d2bd` public admission; `d2b-daemon-access` policy evidence; `d2b-realm-core/src/access.rs`, `audit.rs` | | Reuse action | adapt | | Destination | `packages/d2b-resource-api/src/authz.rs`, `packages/d2b-core-controller/src/rbac.rs` | -| Detailed design | Role/RoleBinding schemas/evaluator/cache/revision invalidation, ComponentSession subject mapping, parent Zone access | +| Detailed design | Role/RoleBinding schemas/evaluator/cache/revision invalidation, canonical resource/session verb enums including ZoneLink-scoped `relay`, ComponentSession subject mapping, parent Zone access, and independent per-hop relay plus target-verb admission | | Integration | Every resource/runtime method invokes one native evaluator before structural checks | | Data migration | Generate initial Roles/Bindings from Nix v3 config | -| Validation | Decision matrix/property tests; revocation/cache/outage/parent-child tests | +| Validation | Decision matrix/property tests; closed-enum and relay-origin/scope rejection; relay-missing and target-verb-missing fail-closed vectors; revocation/cache/outage/parent-child tests | | Removal proof | Legacy auth remains until every v3 route is covered | diff --git a/docs/specs/ADR-046-resources-zone-control.md b/docs/specs/ADR-046-resources-zone-control.md index 5df11eee4..7e915d3c8 100644 --- a/docs/specs/ADR-046-resources-zone-control.md +++ b/docs/specs/ADR-046-resources-zone-control.md @@ -1003,7 +1003,7 @@ status. | Field | Rule | | --- | --- | | `metadata.name` | Zone-unique ResourceName | -| `metadata.ownerRef` | Optional; may be owned by a Provider (for generated Roles) or null for operator-created Roles | +| `metadata.ownerRef` | Optional; may be owned by a Provider (for generated Roles), by a ZoneLink (for core-generated relay Roles), or null for operator-created Roles | | `metadata.finalizers` | Core adds `core.role-binding-drain` when a Role with active RoleBindings is deleted | | `metadata.deletionRequestedAt` | Set on operator delete or owning resource delete | @@ -1085,6 +1085,7 @@ Unknown verbs are rejected at admission. | `connect` | Open a ComponentSession to a service | | `invoke` | Call a ttrpc method | | `open-stream` | Open a named stream | +| `relay` | Forward an already-admitted invocation or stream to one authorized next ZoneLink hop | | `attach` | Transfer a local file descriptor | | `cancel` | Cancel an in-progress operation | | `observe` | Subscribe to service health/event notifications | @@ -1093,6 +1094,31 @@ Session verbs in a Role rule grant ComponentSession/d2b-bus access to the services bound by the same rule's `resourceTypes` and `zones` constraints. They are evaluated by the same native RBAC engine as resource verbs. +`relay` is transport forwarding authority only. It permits the exact +authenticated ZoneLink/transport subject to forward an invocation or named +stream that has already passed admission to the next route-selected hop. The +same hop independently evaluates the invocation's original target verb; a +`relay` allow never satisfies that check. `relay` grants no resource read, +create, update, or delete operation, identity mapping, capability widening, +attachment right, credential access, or local lifecycle authority. + +A relay-bearing rule is admitted only when all of the following hold: + +- `relay` appears in `sessionVerbs`, never `verbs`; +- `resourceTypes`, `resourceNames`, and `zones` exactly bound the forwarded + target; empty/all-name scope and every wildcard form are rejected; +- the Role and RoleBinding are core-generated with `ownerRef` naming the + governing `ZoneLink`, and the binding's trusted external-principal selector + matches the exact enrolled adjacent-`Zone` transport subject; or an + already-authorized local administrator explicitly permits the same bounded + grant through the durable admin-policy path; +- the request payload, Provider descriptor, and Provider/operator-authored + resource cannot assert core-generated or admin-policy provenance. + +Permission to create a Role or RoleBinding is not itself permission to grant +`relay`. Missing `relay`, missing target-verb authority, stale policy, or +unavailable policy state denies forwarding. There is no implicit relay grant. + #### 5.3.3 Explicit wildcard form The foundation specifies: "no implicit wildcard is granted; a reviewed explicit @@ -1125,7 +1151,7 @@ Roles) must be enforced at admission. For non-core-controller Roles, | Rules per Role | 32 | | `resourceTypes` per rule | 16 | | `verbs` per rule | 16 (bounded by verb enum: currently 9 verbs) | -| `sessionVerbs` per rule | 6 (bounded by session verb enum: currently 6 verbs) | +| `sessionVerbs` per rule | 7 (bounded by session verb enum: currently 7 verbs) | | `subresources` per rule | 16 | | `resourceNames` per rule | 64 | | `executionRefs` per rule | 32 | @@ -1223,7 +1249,7 @@ RoleBinding spec or status. | Field | Rule | | --- | --- | | `metadata.name` | Zone-unique ResourceName | -| `metadata.ownerRef` | Optional; may be owned by the Provider that generates this binding | +| `metadata.ownerRef` | Optional; may be owned by the Provider that generates this binding, or by the governing ZoneLink for a core-generated relay binding | | `metadata.finalizers` | No standard finalizer; core clears the binding on deletion without deferral (no downstream finalizer chain) | | `metadata.deletionRequestedAt` | Set on operator delete or owning resource delete | @@ -1289,10 +1315,13 @@ subjects: Rules: - each subject is a `/` ResourceRef in this Zone; -- supported subject ResourceTypes: `User`, `Provider`, `Host`, `Guest`, - `Process`; other ResourceTypes are rejected at admission with +- supported subject ResourceTypes: `Zone`, `User`, `Provider`, `Host`, `Guest`, + `Process`; a ResourceRef `Zone` subject can name only the store's self Zone, + while an enrolled adjacent-Zone transport subject is matched by + `externalPrincipalSelector`; other ResourceTypes are rejected at admission with `resource-schema-invalid`; -- subjects list must be non-empty; +- subjects list must be non-empty except for a core-generated, ZoneLink-owned + relay binding with one exact trusted `externalPrincipalSelector`; - duplicate subjects are rejected at admission; - a subject that does not currently exist as a resource causes `SubjectNotFound` condition (warning only, not Failed; the subject may be created later); @@ -1322,6 +1351,9 @@ Rules: - external selectors are bounded in size (max 512 bytes canonical JSON); - `externalPrincipalSelector` and `subjects` may both be present; a request satisfies the binding if it matches either. +- a core-generated relay binding uses an exact adjacent-Zone enrollment + selector and no broad subject-class selector; a peer cannot supply it in a + request. #### 6.3.4 (removed — no expiry field) @@ -1351,6 +1383,8 @@ Rules: with `resource-schema-invalid`; - narrowed rules are the intersection of the Role rules and the narrowing rules; +- `sessionVerbs`, including `relay`, are intersected exactly like resource + verbs; narrowing cannot add relay or remove its exact target bounds; - `scopeNarrowing: null` means the full Role is granted without restriction; - scope narrowing affects only this RoleBinding; the referenced Role is unchanged. @@ -2524,7 +2558,7 @@ The `authorization` handler reconciles Role and RoleBinding: 1. **Role reconciliation**: a. Validate rules (verbs, ResourceType names, ref formats, explicit wildcard - restriction). + restriction, and relay origin/ZoneLink/target bounds). b. Build index entry: for each (subject, Zone, ResourceType, verb, subresource, resourceNames, executionRefs) tuple, add an allow entry. c. Write `IndexBuilt=True`, `phase=Ready`. @@ -2535,11 +2569,14 @@ The `authorization` handler reconciles Role and RoleBinding: b. Resolve each subject in `subjects` to current UID; note unresolved. c. Resolve `externalPrincipalSelector` if present. d. Apply `scopeNarrowing` as an intersection with Role rules. - e. Honor operator revocation state and any pending deletion request. - f. Build index entry: for each subject × (narrowed rule) add allow entry. - g. Increment Role `activeBindingCount`. - h. Write `phase=Ready`, `IndexBuilt=True`, `roleResolved=true`. - i. Invalidate authorization caches for all covered subjects. + e. For a relay-bearing Role, verify the exact adjacent-Zone enrollment + selector, governing ZoneLink owner, and core-generated or explicit + admin-policy provenance. + f. Honor operator revocation state and any pending deletion request. + g. Build index entry: for each subject × (narrowed rule) add allow entry. + h. Increment Role `activeBindingCount`. + i. Write `phase=Ready`, `IndexBuilt=True`, `roleResolved=true`. + j. Invalidate authorization caches for all covered subjects. 3. **Index swap**: After every batch of Role/RoleBinding commits, the authorization handler atomically swaps the in-memory evaluator to the new @@ -2806,6 +2843,7 @@ Stable error codes for Zone control types: | `provider-dependency-cycle` | Provider dependency alias creates a cycle | | `role-wildcard-denied` | Explicit wildcard attempted by non-core-controller Role | | `role-unknown-verb` | Rule contains an unknown verb token | +| `role-relay-grant-restricted` | Relay is unbounded, self-asserted, lacks ZoneLink ownership/exact adjacent-Zone enrollment selector, or lacks explicit admin-policy provenance | | `role-unknown-resource-type` | Rule names a ResourceType not in Zone API catalog | | `rolebinding-role-not-found` | `roleRef` does not resolve to an existing Role | | `rolebinding-subject-type-invalid` | Subject ResourceType is not a permitted subject type | @@ -3061,6 +3099,11 @@ Eval-time validations: `spec.rules[*].verbs` against the closed verb enum; `spec.rules[*].executionRefs` format if non-empty. `spec.rules[*].resourceTypes` validation against the Zone API catalog is deferred to Phase 2 (catalog requires loading Provider manifests; core types are known at eval time). +Generated option help lists all seven session verbs and describes `relay` as +ZoneLink-scoped forwarding only. Nix recognizes `relay` as a session-verb token, +but Phase 2 admission still rejects an unbounded, wildcard, Provider-asserted, +or ordinary operator-authored relay grant unless explicit admin policy permits +that exact bounded grant. ### 14.5 RoleBinding authoring @@ -3767,6 +3810,11 @@ Rollback atomically: | Test | Assertion | | --- | --- | | `role-unknown-verb-rejected` | Rule with an unknown verb token fails admission | +| `role-relay-core-zonelink-admitted` | Core-generated, ZoneLink-owned Role/RoleBinding with an exact adjacent-Zone enrollment selector and exact target bounds admits `relay` | +| `role-relay-missing-denied` | A forwarding hop without `relay` fails closed even when the target verb is allowed | +| `role-relay-target-verb-required` | `relay` alone cannot authorize the forwarded invocation/stream target verb | +| `role-relay-provider-self-assertion-rejected` | A Provider- or payload-asserted relay grant is rejected | +| `role-relay-wildcard-rejected` | Relay with empty/all-name or wildcard target scope is rejected, including for a core-generated Role | | `role-unknown-resource-type-rejected` | Rule naming a ResourceType not in Zone API catalog is rejected | | `role-wildcard-non-core-rejected` | Non-core-controller Role with `resourceNames: ["*"]` is rejected | | `role-wildcard-core-permitted` | Core-generated Role with `resourceNames: ["*"]` is admitted | @@ -3815,8 +3863,10 @@ Rollback atomically: | `nix-eval-name-length-enforced` | Resource name of 129 characters fails eval | | `nix-eval-verb-closed-enum` | Rule with verb `"delete-all"` (unknown) fails eval | | `nix-eval-session-verb-closed-enum` | Rule with `sessionVerbs=["sudo"]` fails eval | +| `nix-eval-relay-session-verb-known` | `sessionVerbs=["relay"]` reaches Phase 2 as the canonical token; placing `relay` in `verbs` fails eval | +| `nix-build-relay-scope-restricted` | Unbounded, wildcard, or Provider/self-asserted relay configuration fails before bundle activation | | `nix-eval-roleref-format` | `roleRef="role/foo"` (wrong case) fails eval | -| `nix-eval-subject-type-restricted` | Subject `"Process/foo"` (non-permitted type) fails eval | +| `nix-eval-subject-type-restricted` | Subject `"Device/foo"` (non-permitted type) fails eval | | `nix-eval-no-duplicate-subjects` | Two identical subjects in one RoleBinding fails eval | | `nix-eval-no-cross-zone-ref` | Subject `"dev/Role/foo"` (Zone-prefixed) fails eval | | `nix-eval-bootstrap-provider-rejected` | `d2b.zones.dev.resources.system-core = { type = "Provider"; ... }` fails eval with named assertion | @@ -4194,10 +4244,10 @@ None of the following exist in baseline: | Current source | `packages/d2bd/src/admission.rs` (`PeerRole::{Admin, Launcher, HostShutdown}`, `PeerIdentity`, `authorize_peer()`, `verb_requires_admin()`, `verb_allowed_for_host_shutdown()` — `implemented-and-reachable`, baseline `b5ddbed6`); `packages/d2b-daemon-access/src/lib.rs` (`LocalUnixAllowlistRole::{Admin, Launcher}`, `DaemonAccessPolicyRole::RealmAdmin`, `DaemonAccessDecision`, `MappedDaemonAccessPrincipal`, `map_local_unix_daemon_access()` — `implemented-and-reachable`); Role resource schema and RBAC index: `ADR-only` | | Reuse action | adapt | | Destination | `packages/d2b-contracts/src/v3/role.rs`; `packages/d2b-core-controller/src/authz.rs` | -| Detailed design | Role resource schema with rule schema from §5.3 and resolved bounds (32 rules, 16 resourceTypes per rule, 64 resourceNames, 32 executionRefs); verb enum; explicit wildcard enforcement; index builder; phase/conditions; `role-binding-drain` finalizer; Nix Role options; audit/OTEL instrumentation Primary reuse disposition: `adapt`. Preserved source-plan detail: extract and adapt (`verb_requires_admin()` verb table → Role verb enum; `DaemonAccessDecision` error types → Role admission errors; `MappedDaemonAccessPrincipal` → subject identity model). | +| Detailed design | Role resource schema with rule schema from §5.3 and resolved bounds (32 rules, 16 resourceTypes per rule, 64 resourceNames, 32 executionRefs); separate closed resource/session verb enums including `relay`; core-generated ZoneLink ownership, exact adjacent-Zone enrollment selector, exact target bounds, and explicit admin-policy exception admission; explicit wildcard enforcement; index builder; phase/conditions; `role-binding-drain` finalizer; generated Nix Role option help; audit/OTEL instrumentation Primary reuse disposition: `adapt`. Preserved source-plan detail: extract and adapt (`verb_requires_admin()` verb table → Role verb enum; `DaemonAccessDecision` error types → Role admission errors; `MappedDaemonAccessPrincipal` → subject identity model). | | Integration | Authorization evaluator (ADR046-api-002) reads Role index entries; core `authz` handler owns reconcile loop | | Data migration | Initial Roles generated from Nix config; no v2 Role resource import | -| Validation | All §15.4 Role tests including the resolved bounds checks | +| Validation | All §15.4 Role tests including closed-enum, relay origin/scope/target-verb, and resolved-bounds checks | | Removal proof | `daemon-access` capability enum removed after RBAC Role engine covers all access decisions | ### ADR046-zone-control-005 @@ -4239,10 +4289,10 @@ None of the following exist in baseline: | Current source | `nixos-modules/options-realms.nix` (`providerKind = "^[a-z][a-z0-9-]*$"` label regex matching `LABEL_PATTERN` in `ids.rs`; `providerType` submodule with `enable`/`kind`/`placement`/`freeformType` fields; `d2b.realms..providers.*` attrset — `generated-or-eval-contract`, baseline `b5ddbed6`); `nixos-modules/options-realms-workloads.nix` (`d2b.realms..workloads.*` submodule shape — `generated-or-eval-contract`); `d2b.zones.*` Nix options: `ADR-only` | | Reuse action | adapt | | Destination | `nixos-modules/options-zones.nix`; `nixos-modules/resources-zone-control.nix`; extended `nixos-modules/index.nix` | -| Detailed design | `d2b.zones..*` Nix options for Zone/ZoneLink/Provider/Role/RoleBinding/Quota/EmergencyPolicy authoring; compiler-only scalar `parentZone` required on non-root Zones and forbidden on `local-root`; Nix eval-time validation of parent existence, one resolved parent, self/cycle rejection, 16-name ancestry depth, ResourceRefs, verb enums, digest format, subject types, child-local ZoneLink self-name/one-uplink/local transport-ref constraints, and the no-expiry RoleBinding model; canonical JSON serialization; generation-bound resource bundle output; the compiler seals the validated parent map into allocator bootstrap topology without emitting `parentZone` into `Zone.spec` or reciprocal resources Primary reuse disposition: `adapt`. Preserved source-plan detail: adapt (`providerType` submodule → Provider option submodule; label regex `^[a-z][a-z0-9-]*$` retained unchanged; `placement` option → Provider component placement; `d2b.realms.*` option namespace → `d2b.zones.*` option namespace). | +| Detailed design | `d2b.zones..*` Nix options for Zone/ZoneLink/Provider/Role/RoleBinding/Quota/EmergencyPolicy authoring; compiler-only scalar `parentZone` required on non-root Zones and forbidden on `local-root`; Nix eval-time validation of parent existence, one resolved parent, self/cycle rejection, 16-name ancestry depth, ResourceRefs, separate resource/session verb enums (including session-only `relay`), relay origin/scope restrictions, digest format, subject types, child-local ZoneLink self-name/one-uplink/local transport-ref constraints, and the no-expiry RoleBinding model; generated help carries the relay semantics; canonical JSON serialization; generation-bound resource bundle output; the compiler seals the validated parent map into allocator bootstrap topology without emitting `parentZone` into `Zone.spec` or reciprocal resources Primary reuse disposition: `adapt`. Preserved source-plan detail: adapt (`providerType` submodule → Provider option submodule; label regex `^[a-z][a-z0-9-]*$` retained unchanged; `placement` option → Provider component placement; `d2b.realms.*` option namespace → `d2b.zones.*` option namespace). | | Integration | Nix compiler → sealed allocator topology for `parentZone`; resource compiler → configuration publication handler → Zone store; bootstrap Provider records auto-generated | | Data migration | Full reset; Nix realm options (`d2b.realms.*`) remain until purge wave | -| Validation | nix-unit vectors for each Zone control type schema; cross-field constraint tests; rendered JSON contract tests (`make test-drift`) | +| Validation | nix-unit vectors for each Zone control type schema; closed resource/session verb and relay restriction vectors; cross-field constraint tests; rendered JSON contract tests (`make test-drift`) | | Removal proof | `nixos-modules/options-realms.nix` and related realm options removed only after Zone resource Nix integration is live | ### ADR046-zone-control-008 @@ -4435,7 +4485,7 @@ Evidence class for all: `main-reuse-source`. | Detailed design | Consume the generator and registry established by ADR046-routing-011 to declare the unified `d2b.zones..resources.` option tree plus the Zone-level compiler-only `parentZone` scalar. The generated standard registry is exactly the canonical 19 types (`Zone`, `ZoneLink`, `Provider`, `Host`, `Guest`, `Process`, `EphemeralProcess`, `Network`, `Volume`, `Credential`, `Device`, `Endpoint`, `User`, `Role`, `RoleBinding`, `Quota`, `EmergencyPolicy`, `ResourceExport`, `ResourceImport`), and every standard type has a strict generated `spec` submodule carrying the schema's Nix types, defaults, bounds, and documentation. Installed Provider artifacts may append only signed qualified ResourceTypes whose strict schema has been verified and generated into the evaluated option set. `type` is selected from that closed combined registry: an unknown standard string, an unqualified extension, or a qualified type without an installed verified schema fails evaluation; there is no unrestricted string or free-form `spec` fallback. Require `parentZone` on every non-root Zone and forbid it on `local-root`; resolve it against declared Zone keys, reject self-parent/conflicting module definitions/cycles, and cap each ancestry path at 16 Zone names. Compile the validated map into sealed allocator bootstrap topology; never emit it into the resource bundle or `Zone.spec`. Declare the global `d2b.artifacts.` catalog with `package` (types.package, required) and `type` (closed enum, required). Provider `spec.artifactId` is a plain catalog ID; the derivation is not a `spec` field. Implement the Phase 1 cross-resource assertions (§14.10 Phase 1 table); retain `credentialRef`, `resourceRef`, and `closedEnum` helpers; reject operator-authored `Zone`; and enforce child-local ZoneLink topology. The runtime creates `Zone/`, `Provider/system-core`, and `Provider/system-minijail` with `managedBy=controller`; none is emitted or inferred from the configuration bundle. `allowUnsafeLocal` maps to the dedicated Host admission gate. Provider manifest-derived fields (`spec.exports`, `spec.components`, `spec.dependencies`, `spec.permissionClaims`, `spec.upgradePolicy`, `spec.restartPolicy`) are read-only and setting one is an eval error. | | Integration | ADR046-routing-011 supplies the one canonical 19-type registry and generated option family; validated `parentZone` feeds the allocator bootstrap sealer; the closed `d2b.zones..resources.*` tree is consumed by ADR046-zone-control-015; the Zone controller (ADR046-zone-control-001) reads the resulting bundle; Provider package conventions come from ADR046-zone-control-003 | | Data migration | Replace `nixos-modules/options-realms.nix`-derived option trees once Zone controller is live and has reached parity | -| Validation | All Phase 1 eval tests in §15.8 (`nix-eval-name-regex-enforced`, `nix-eval-verb-closed-enum`, `nix-eval-roleref-format`, `nix-eval-subject-type-restricted`, `nix-eval-no-duplicate-subjects`, `nix-eval-bootstrap-provider-rejected`, `nix-eval-provider-missing-artifact-id`, `nix-eval-artifact-id-not-in-catalog`, `nix-eval-artifact-wrong-type`, `nix-eval-artifact-id-format`, `nix-eval-credentialref-declared`, `nix-eval-dollar-key-rejected`, all five `nix-eval-parent-zone-*` vectors, `nix-eval-zonelink-child-name-mismatch-rejected`, `nix-eval-zonelink-second-uplink-rejected`, `nix-eval-zonelink-limits-maxpendingintents-bound`); drift test asserts the standard registry and generated option modules cover exactly all 19 canonical types; negative evals reject unknown strings, unqualified extensions, unsigned or uninstalled qualified types, and unknown `spec` fields; a positive fixture admits an installed signed qualified type and validates its strict generated schema | +| Validation | All Phase 1 eval tests in §15.8 (`nix-eval-name-regex-enforced`, `nix-eval-verb-closed-enum`, `nix-eval-session-verb-closed-enum`, `nix-eval-relay-session-verb-known`, `nix-eval-roleref-format`, `nix-eval-subject-type-restricted`, `nix-eval-no-duplicate-subjects`, `nix-eval-bootstrap-provider-rejected`, `nix-eval-provider-missing-artifact-id`, `nix-eval-artifact-id-not-in-catalog`, `nix-eval-artifact-wrong-type`, `nix-eval-artifact-id-format`, `nix-eval-credentialref-declared`, `nix-eval-dollar-key-rejected`, all five `nix-eval-parent-zone-*` vectors, `nix-eval-zonelink-child-name-mismatch-rejected`, `nix-eval-zonelink-second-uplink-rejected`, `nix-eval-zonelink-limits-maxpendingintents-bound`); Phase 2 runs `nix-build-relay-scope-restricted`; drift test asserts the standard registry and generated option modules cover exactly all 19 canonical types; negative evals reject unknown strings, unqualified extensions, unsigned or uninstalled qualified types, and unknown `spec` fields; a positive fixture admits an installed signed qualified type and validates its strict generated schema | | Removal proof | `nixos-modules/options-realms.nix`, `nixos-modules/realm-controller-config-json.nix`, `nixos-modules/realm-identity-config-json.nix` deleted after Zone controller and resource compiler reach full parity; `nixos-modules/assertions.nix` lines referencing `allowUnsafeLocal`/realm names removed after Host admission validation replaces them | ### ADR046-zone-control-015 diff --git a/docs/specs/ADR-046-security-and-threat-model.md b/docs/specs/ADR-046-security-and-threat-model.md index 99654ba19..77ad2ed0b 100644 --- a/docs/specs/ADR-046-security-and-threat-model.md +++ b/docs/specs/ADR-046-security-and-threat-model.md @@ -511,17 +511,29 @@ principal, or a wider capability merely by writing it into a request. **Closed verb sets.** Resource verbs: `get, list, watch, create, update-spec, update-status, update-metadata, update-finalizers, delete`. Session verbs: -`connect, invoke, open-stream, attach, cancel, observe`. All verb tokens +`connect, invoke, open-stream, relay, attach, cancel, observe`. All verb tokens outside this closed set are rejected with `role-unknown-verb-rejected` (RZC lines 3422-3430, adapted from `verb_requires_admin()` in the baseline `d2bd/src/admission.rs`). +**Relay is narrow transport authority.** `relay` permits the exact +already-authenticated ZoneLink/transport subject to forward one admitted +invocation or stream to the route-selected next hop. It grants no resource +CRUD, identity mapping, capability widening, FD/credential authority, or local +lifecycle permission. It is core-generated and ZoneLink-scoped by default. +Wildcard, self-asserted, Provider-authored, or ordinary operator-authored relay +grants fail admission unless an already-authorized local administrator +explicitly permits the exact bounded grant through durable admin policy. + **Per-hop RBAC.** For every intermediate Zone hop in a ZoneLink chain, the -forwarding d2b-bus performs a `relay` verb check against the local RBAC index -before forwarding the frame — a Zone never grants authority beyond its own -Role/RoleBinding evaluation, and forwarded calls are re-authorized at each -hop (ZR lines 1526-1555, 1574-1601). This closes the historical "one relay -hop is authorized, therefore every downstream hop is trusted" gap. +forwarding d2b-bus performs two independent local checks before forwarding: +`relay` for the authenticated adjacent-Zone transport subject and the immutable +target verb for the invocation/stream. The final target checks the target verb +again. A Zone never grants authority beyond its own Role/RoleBinding +evaluation; missing relay, missing target authority, or unavailable policy +state fails closed. This closes both the historical "one relay hop is +authorized, therefore every downstream hop is trusted" gap and the converse +"relay implies target authority" gap. **Wildcard restriction.** Explicit wildcard (`resourceNames: ["*"]`) is permitted only for core-controller-generated Roles. Operator-authored or @@ -530,6 +542,8 @@ Provider Roles with a wildcard are rejected at admission with is the corresponding conformance test (RZC line 2985). An empty `resourceNames: []` for a non-core Role means "all names of this ResourceType" and is a distinct, narrower grant than `["*"]`. +Relay is stricter: neither `["*"]` nor the empty/all-name form is accepted, +including for core-generated relay Roles. **RoleBinding immutability and atomic deletion.** `spec.roleRef` is immutable after creation. Deletion is one atomic redb write transaction: RBAC index @@ -1161,7 +1175,9 @@ when explicitly required and planned with ownership/state transfer, and full destructive factory reset remains a separate authorized path. **Endpoint resource (D092).** A stable endpoint is the `Endpoint` ResourceType, -never a raw locator in spec/status/CLI: the base carries only closed +never a raw locator in spec/status/CLI: `visibility` is exactly +`owner|provider|zone`, with finer exact access in `consumerPolicy`; the base +carries only closed class/transport/locality/purpose values and bounded fingerprints, and status carries no path/address/CID/port/fd/credential. Core/ProviderSupervisor resolves an `Endpoint` to a live transport/FD only through the EffectPort/LaunchTicket @@ -1966,7 +1982,7 @@ close. Each maps to the attacker class it is scoped against. | Reuse source | None beyond the verb-table adaptation already tracked by `ADR046-zone-control-004` | | Reuse action | adapt | | Destination | `packages/d2b-resource-store/tests/rbac_property.rs` | -| Detailed design | Property test asserting, for a randomly generated Role/RoleBinding/request corpus: (1) no request whose payload sets a subject/role field ever changes the resolved `AuthenticatedSubjectContext.subjectRef`; (2) no non-core Role with a wildcard grant is ever admitted; (3) `scopeNarrowing` never widens beyond the referenced Role; (4) RoleBinding deletion never leaves an observable intermediate state under concurrent readers Primary reuse disposition: `adapt`. Preserved source-plan detail: extract and adapt. | +| Detailed design | Property test asserting, for a randomly generated Role/RoleBinding/request corpus: (1) no request whose payload sets a subject/role field ever changes the resolved `AuthenticatedSubjectContext.subjectRef`; (2) no non-core Role with a wildcard grant is ever admitted; (3) `scopeNarrowing` never widens beyond the referenced Role; (4) RoleBinding deletion never leaves an observable intermediate state under concurrent readers; (5) `relay` is accepted only as the canonical ZoneLink-scoped session verb with core-generated or explicit admin-policy provenance and exact target bounds; and (6) every forwarding hop independently requires relay plus the target verb Primary reuse disposition: `adapt`. Preserved source-plan detail: extract and adapt. | | Integration | Runs against the real redb-backed resource store test harness, not a mock | | Data migration | None — full d2b 3.0 reset; no prior state to migrate | | Validation | Hermetic property test (`proptest`/`quickcheck`-style, minimum 10,000 cases per property) | diff --git a/docs/specs/ADR-046-zone-routing.md b/docs/specs/ADR-046-zone-routing.md index 57ef43a2e..96dbec15e 100644 --- a/docs/specs/ADR-046-zone-routing.md +++ b/docs/specs/ADR-046-zone-routing.md @@ -303,7 +303,8 @@ rename methods (bootstrap→zone-bootstrap, enroll→zone-enroll, resolve_route→resolve-zone-route, authorize_shortcut→authorize-zone-shortcut); replace `RealmSessionAuthority` with Zone principal + RBAC binding; replace `BootstrapBinding`/`EnrollmentBinding` with v3 ZoneLink enrollment -records; add `relay` verb check per hop; adapt shortcut model to ZonePath. +records; add independent `relay` and target-verb checks per forwarding hop; +adapt shortcut model to ZonePath. **Excluded ADR45 assumptions**: - `CredentialCustody::GatewayGuest` maps to the ADR45 constellation gateway @@ -467,10 +468,11 @@ capability is covered. There is no per-hop RBAC subject/verb check. **ADR 0046 mapping:** v3 adds a **RBAC layer above the allocated capability scope**: at each hop, the -intermediate Zone evaluates a `relay` verb check against the forwarded -principal's RoleBinding. Capability-scope propagation from -`RouteNamespaceAllocation` is preserved. The delta (RBAC relay verb check) -is genuinely new; it does not exist anywhere in the current baseline. +intermediate Zone evaluates both the target verb and a separate `relay` verb +against the authenticated adjacent-Zone subject's RoleBinding. +Capability-scope propagation from `RouteNamespaceAllocation` is preserved. +The dual RBAC check is genuinely new; it does not exist anywhere in the current +baseline. ### Realm config artifacts @@ -1629,7 +1631,8 @@ subject mapped from the ZoneLink's enrolled identity: ``` child-local subject = child RoleBinding that: - - names the parent Zone's link principal as its subjectRef + - matches the parent Zone's enrolled link principal with a trusted exact + externalPrincipalSelector - grants only the verbs/resourceTypes/names declared in the binding - has a capability scope no wider than the allocator-issued scope ``` @@ -1641,12 +1644,19 @@ parent identity, not from the forwarded request payload. Authorization at each hop is independent: -- The parent authorizes the outbound forwarded call using parent-local RBAC. -- The intermediate Zone authorizes relay using its own RBAC. -- The target child authorizes the final call using child-local RBAC. +- The source Zone authorizes the caller's target verb before selecting a route. +- Each intermediate Zone authenticates the inbound adjacent-Zone transport + subject, then independently authorizes both `relay` and the immutable target + verb using its own RBAC. +- The target child authenticates its inbound adjacent-Zone subject and + authorizes the final target verb using child-local RBAC. A child that has not bound a matching parent RoleBinding refuses the call -with `authorization-denied`. No ambient cross-Zone authority exists. +with `authorization-denied`. A forwarding Zone without the separate `relay` +grant returns `relay-denied`. `relay` is core-generated/ZoneLink-scoped and +permits only one route-selected next hop; it grants no CRUD, identity mapping, +capability widening, attachment/credential access, or local lifecycle +authority. No ambient cross-Zone authority exists. ### Capability floor at local root @@ -1673,17 +1683,23 @@ Parent ResourceClient At each intermediate hop, d2b-bus: 1. Verifies the inbound ComponentSession's `AuthenticatedSubjectContext`. -2. Evaluates local RBAC for the `relay` verb with the forwarded - ResourceType and target Zone name. -3. Decrements the hop counter; refuses if zero. -4. Opens the allocator-bound ComponentSession represented by the next child +2. Evaluates local RBAC for the forwarded target verb with the immutable + ResourceType/service, resource name, and target Zone. +3. Separately evaluates local RBAC for the `relay` session verb against the + authenticated inbound Zone transport subject, governing ZoneLink, exact + target bounds, and route-selected next hop. +4. Fails closed if either check or policy state is missing; no grant is inferred + from a prior hop. +5. Decrements the hop counter; refuses if zero. +6. Opens the allocator-bound ComponentSession represented by the next child Zone's local uplink. -5. Re-serializes the request with the decremented hop counter; preserves +7. Re-serializes the request with the decremented hop counter; preserves the original operation/idempotency/correlation/trace IDs unchanged. -6. Returns the child response to the inbound caller. +8. Returns the child response to the inbound caller. Relay is a distinct RBAC verb. Intermediate Zones may deny relay without -blocking local resource calls. +blocking local resource calls. A relay allow never supplies the target-verb +allow. ### No cross-Zone resource references @@ -1738,7 +1754,9 @@ be forwarded through a ZoneLink if: - the target service is declared in a child Zone Provider's service descriptor; - the call's `purpose` class is `remote-zone`; -- RBAC at each hop grants the `relay` verb for the service name; +- RBAC at each forwarding hop grants both `relay` for the authenticated + adjacent-Zone subject and the target session verb (`connect`, `invoke`, or + `open-stream`) for the exact service/method/stream; - the hop count does not exceed the fixed protocol limit of 16. The forwarded session carries: @@ -1841,9 +1859,10 @@ more credit than its downstream grants it. A source that exceeds its hop-1 credit budget blocks rather than buffering unboundedly. Named streams inherit the `purpose`, `service`, and `schema fingerprint` -of the originating session. They are not re-authenticated at each hop; -the authentication is carried in the original KK ComponentSession -prologue. +of the originating session. At every hop the adjacent ZoneLink transport +subject is authenticated by that hop's KK ComponentSession and local RBAC +separately requires `relay` plus `open-stream` for the immutable target. No +forwarded payload may self-assert the subject or either authorization result. ### Direct shortcuts @@ -1875,6 +1894,10 @@ Shortcuts are authorized using `ZoneRouteEngine::decide_direct_shortcut` A shortcut is optional optimization metadata. Absence of a shortcut does not break routing; the tree relay path remains authoritative. Shortcuts are not provisioned without explicit policy authorization. +Shortcut establishment does not convert `relay` into target authority: every +tree hop must admit the setup under its bounded relay and target scope, and +every operation on the resulting direct session is authorized for its target +verb at the destination. ## No FD, credential, or host path forwarding @@ -2064,11 +2087,12 @@ RBAC at each hop: ``` K0: subject=User/alice verb=get resourceType=Process zone=K2 - -> K0 Role allows relay to K1 for Process/get in K2 -K1: subject=K0-link-principal verb=relay resourceType=Process zone=K2 - -> K1 Role allows relay to K2 for Process/get -K2: subject=K1-link-principal verb=get resourceType=Process name=web-server - -> K2 Role allows K1-link-principal to get Process/web-server + -> K0 Role admits the target operation before route selection +K1: subject=Zone/k0 verbs=[relay,get] resourceType=Process + name=web-server zone=K2 + -> K1 RoleBinding independently allows one-hop forwarding and target get +K2: subject=Zone/k1 verb=get resourceType=Process name=web-server + -> K2 Role allows the authenticated adjacent Zone to get Process/web-server ``` ### Simple K0/K1 example (local Host + remote Guest) @@ -2170,7 +2194,8 @@ included in span attributes. | `zone-link-intent-queue-full` | Intent queue reached `spec.limits.maxPendingIntents` | | `hop-limit-exceeded` | Forwarded call has no remaining hops | | `malformed-hop-count` | Hop counter in inbound frame claims more hops than allowed | -| `relay-denied` | Intermediate Zone's RBAC denied relay | +| `relay-denied` | Intermediate Zone lacks the exact ZoneLink-scoped relay grant | +| `authorization-denied` | A source, intermediate, or target Zone denied the forwarded operation's target verb | | `attachment-not-permitted-over-zone-link` | FD attachment rejected on ZoneLink transport | | `zone-route-not-found` | No ZoneLink path exists to the target Zone | | `zone-route-capability-denied` | Required capability absent from route | @@ -2190,7 +2215,8 @@ credentials, or provider diagnostics. ### Security invariants 1. A Zone never grants authority beyond its own Role/RoleBinding evaluation. - Forwarded calls are re-authorized at each hop. + Forwarded calls require the target verb at each hop and a separate `relay` + allow at each forwarding hop. 2. A child Zone's resource status is not inferred from local intents; the parent only learns child state from authenticated responses. 3. The KK handshake verifies the allocator-sealed enrolled identity and @@ -2311,7 +2337,7 @@ The following transitions are NOT simple textual renames: 1. **`RealmControllerPlacement` enum → private Zone runtime bootstrap placement + explicit parent topology + child-local uplink identity**: 6 variants collapse into per-Zone bootstrap configuration; compiler-only `parentZone` selects the allocator owner, and the child-local ZoneLink's `childZoneName`/`transportProviderRef` supplies transport state. The compiler seals the parent edge into allocator state. Placement and `parentZone` are not public `Zone.spec` fields; `Zone.spec` is `{}`. 2. **`WorkloadId` → Guest/Host split**: VM/sandbox workloads become `Guest`; local/bare-metal become `Host`. Classification is semantic, not mechanical. -3. **`CapabilitySet`-only authz → RBAC + allocated capability scope**: current engine checks a route allocation scope only. Per-hop `relay` verb RBAC is new. +3. **`CapabilitySet`-only authz → RBAC + allocated capability scope**: current engine checks a route allocation scope only. Independent per-hop target-verb and `relay` RBAC is new. 4. **`RealmPath` DNS target form → Zone resource path**: grammar preserved; wire address format changes. 5. **`EntrypointMode` enum → child-local ZoneLink transport plus topology projection**: `HostResident`/`GatewayBacked` mode is replaced by the child's transport spec; parent routing and CLI inspection use sealed topology and authenticated route projection state. 6. **`realm-controllers.json`/`realm-identity.json` → sealed parent topology + child-local Zone/ZoneLink state**: data is loaded today but routing/trust sessions are explicitly inert. @@ -2320,7 +2346,7 @@ The following transitions are NOT simple textual renames: | Item | Treatment | | --- | --- | | Behavior retained | Pure in-memory NCA tree-walk; loop/multi-parent detection; advertisement replay-window; allocated capability-scope propagation; idempotency dedup full 6-tuple `(realm, principal, node, operation_kind, idempotency_key)`; fail-closed on unknown realm/route; bounded audit label cardinality; `TreeRoutePath`/`TreeRouteHop` already exist (rename to ZoneRoutePath/ZoneRouteHop); `DirectShortcut*` machinery already exists; `RouteAuditEventKind` event set already exists | -| Required delta | Consume the canonical ZoneLink ResourceType spec/status/intent contract from ADR046-zone-control-002; compiler-only validated `parentZone` map sealed into allocator bootstrap topology; RBAC `relay` verb check per intermediate hop; watch cursor resync over ZoneLink; named-stream credit forwarding; hop counter byte in wire frames; no-FD/credential structural rejection at serialization boundary; private Zone runtime bootstrap placement (replaces placement enum, not a public spec field); per-hop subject narrowing | +| Required delta | Consume the canonical ZoneLink ResourceType spec/status/intent contract from ADR046-zone-control-002; compiler-only validated `parentZone` map sealed into allocator bootstrap topology; independent target-verb plus canonical `relay` RBAC checks per intermediate hop; watch cursor resync over ZoneLink; named-stream credit forwarding; hop counter byte in wire frames; no-FD/credential structural rejection at serialization boundary; private Zone runtime bootstrap placement (replaces placement enum, not a public spec field); per-hop subject narrowing | | Reuse path | Copy and adapt `RouteTreeEngine` (rename RealmPath→ZonePath, RouteId→ZoneRouteId); copy `RouteAdvertisement`/`RouteNamespaceAllocation`/`TreeRoutePath`/`TreeRouteHop`/`RouteFailClosedReason`/`DirectShortcut*`/`RouteAuditEventKind`; copy `OperationRouter` idempotency dedup; extract `RealmEntrypointTable` suffix-match into ZoneEntrypointResolver; adapt KK handshake from `SecurePeerSession`/`PeerSession` | | Replacement/deletion | `RealmEntrypointTable`/`RouteTreeEngine` on RealmPath types retire after ZoneRouteEngine live; `RemoteNodeRegistry` retires when ZoneLink controller live; `WorkloadTargetIndex` retires when Guest/Host resource lookup live; CLI `Route::GatewayBacked` retires when ZoneLink handles all cross-Zone routing; `realm-controllers.json`/`realm-identity.json` retire when sealed `parentZone` topology, runtime Zone identity, and ZoneLink transport state replace them | | Feasibility proof | `route_engine.rs` inline test suite (45 functions at line 1202) proves NCA, advertisement, loop, capability, replay, DirectShortcut; `target_resolver.rs` tests prove suffix match; `lib.rs` tests prove idempotency dedup namespace | @@ -2404,10 +2430,10 @@ The following transitions are NOT simple textual renames: | Reuse source | Same v3 baseline commit `b5ddbed6` | | Reuse action | adapt | | Destination | `packages/d2b-bus/src/zone_route.rs` (cross-Zone bus routing), `packages/d2b-bus/src/relay.rs` (per-hop relay handler) | -| Detailed design | Cross-Zone routing path in d2b-bus: ZoneEntrypointResolver consumes sealed topology plus authenticated route projections → ZoneRouteEngine::decide_route → admitted ComponentSession established by each next-hop child's local ZoneLink; hop-counter decrement and enforcement; RBAC `relay` verb check at each intermediate hop; idempotency key namespace (full 6-tuple) in ZoneLinkIdempotencyKey; pinned reverse path tracking; cancellation forwarding; watch cursor forwarding and revision-expired handling; no-FD/credential structural rejection at serialization boundary. No parent route step performs Resource API Get/List/Watch on ZoneLink Primary reuse disposition: `adapt`. Preserved source-plan detail: extract and adapt. | +| Detailed design | Cross-Zone routing path in d2b-bus: ZoneEntrypointResolver consumes sealed topology plus authenticated route projections → ZoneRouteEngine::decide_route → admitted ComponentSession established by each next-hop child's local ZoneLink; hop-counter decrement and enforcement; independent target-verb plus canonical ZoneLink-scoped `relay` checks at each intermediate hop; idempotency key namespace (full 6-tuple) in ZoneLinkIdempotencyKey; pinned reverse path tracking; cancellation forwarding; watch cursor forwarding and revision-expired handling; no-FD/credential structural rejection at serialization boundary. No parent route step performs Resource API Get/List/Watch on ZoneLink Primary reuse disposition: `adapt`. Preserved source-plan detail: extract and adapt. | | Integration | ResourceClient → d2b-bus → ZoneLink CS → intermediate zone → target zone; cancel/watch/stream all use the same routing path | | Data migration | None — full d2b 3.0 reset; no prior state to migrate | -| Validation | End-to-end K0→K1→K2 resource call; relay-denied/hop-limit/FD-rejection tests; idempotency namespace collision tests; cancellation delivery tests; watch resync tests | +| Validation | End-to-end K0→K1→K2 resource call; relay-missing, target-verb-missing, wildcard/self-asserted relay, hop-limit, and FD-rejection tests; prove relay alone grants no CRUD/local lifecycle; idempotency namespace collision tests; cancellation delivery tests; watch resync tests | | Removal proof | Old direct-dispatch and gateway-backed paths retired per bus routing parity | ### ADR046-routing-006 @@ -2503,7 +2529,7 @@ The following transitions are NOT simple textual renames: | Detailed design | Retain `ProviderRegistry`/`ProviderRegistryBuilder`/`ProviderRegistryManager` lifecycle verbatim; adapt `SessionIdentity` to carry `ZonePath` instead of `RealmId`; adapt `AdmissionOptions::peer_role` to v3 Zone principal + RBAC binding; adapt `ProviderDescriptor` schema version to v3; `RegistryLimits` unchanged; `RpcProviderProxy` field adaptations to match v3 session identity; `ProviderInstance` variants retain all 11 types Primary reuse disposition: `adapt`. Preserved source-plan detail: copy and adapt. | | Integration | Zone runtime `ProviderComposition` builds a `ProviderRegistry` per Zone; Provider resource controller admits calls through `ProviderRegistry::admit()` | | Data migration | None (pure runtime) | -| Validation | Port inline registry lifecycle/drain/shutdown tests; add v3 ZonePath routing test; add RBAC relay-verb check at provider admission | +| Validation | Port inline registry lifecycle/drain/shutdown tests; add v3 ZonePath routing test; prove provider admission cannot self-assert relay and each forward requires relay plus the target verb | | Removal proof | Provider registry is v3 core infrastructure; no retirement | ### ADR046-routing-015 @@ -2532,10 +2558,10 @@ The following transitions are NOT simple textual renames: | Main reuse source | `packages/d2b-realm-router/src/service_v2.rs` (commit `a1cc0b2d`): same symbols, unchanged from v3 baseline in the main commit. All evidence class notes apply equally to main. | | Reuse action | adapt | | Destination | `packages/d2b-zone-routing/src/service.rs` | -| Detailed design | Rename `RealmServiceServer` → `ZoneServiceServer`; service wire name `d2b.realm.v2.RealmService` → `d2b.zone.v3.ZoneService`; rename methods (bootstrap→zone-bootstrap, enroll→zone-enroll, resolve_route→resolve-zone-route, authorize_shortcut→authorize-zone-shortcut, revoke_shortcut→revoke-zone-shortcut, report_shortcut_close→report-zone-shortcut-close, inspect→zone-inspect) and add list/watch topology-projection methods. The read-only projection starts from the sealed sorted `{ childZone, parentZone }` compiler input and joins only authenticated, admitted `ZoneRouteEngine` route/projection status. It exposes no ZoneLink resource name, UID, spec, status, Provider ref, fingerprint, transport setting, or handle. Replace `RealmSessionAuthority` with Zone principal + RBAC binding; replace `BootstrapBinding` with allocator-issued PSK binding associated with the child's local ZoneLink; replace `EnrollmentBinding` with the corresponding KK enrollment record; add `relay` verb RBAC check per hop; adapt shortcut model to ZonePath addressing; `RealmServiceLimits` defaults preserved; `MAX_DISPATCH_IN_FLIGHT=64`, `SHUTDOWN_TIMEOUT=5s` preserved; `CredentialCustody::GatewayGuest` excluded (all ZoneLink sessions are direct KK) Primary reuse disposition: `adapt`. Preserved source-plan detail: copy and adapt. | +| Detailed design | Rename `RealmServiceServer` → `ZoneServiceServer`; service wire name `d2b.realm.v2.RealmService` → `d2b.zone.v3.ZoneService`; rename methods (bootstrap→zone-bootstrap, enroll→zone-enroll, resolve_route→resolve-zone-route, authorize_shortcut→authorize-zone-shortcut, revoke_shortcut→revoke-zone-shortcut, report_shortcut_close→report-zone-shortcut-close, inspect→zone-inspect) and add list/watch topology-projection methods. The read-only projection starts from the sealed sorted `{ childZone, parentZone }` compiler input and joins only authenticated, admitted `ZoneRouteEngine` route/projection status. It exposes no ZoneLink resource name, UID, spec, status, Provider ref, fingerprint, transport setting, or handle. Replace `RealmSessionAuthority` with Zone principal + RBAC binding; replace `BootstrapBinding` with allocator-issued PSK binding associated with the child's local ZoneLink; replace `EnrollmentBinding` with the corresponding KK enrollment record; add independent target-verb plus canonical `relay` RBAC checks per forwarding hop; adapt shortcut model to ZonePath addressing; `RealmServiceLimits` defaults preserved; `MAX_DISPATCH_IN_FLIGHT=64`, `SHUTDOWN_TIMEOUT=5s` preserved; `CredentialCustody::GatewayGuest` excluded (all ZoneLink sessions are direct KK) Primary reuse disposition: `adapt`. Preserved source-plan detail: copy and adapt. | | Integration | Zone runtime instantiates one `ZoneServiceServer` per Zone; d2b-bus routes `d2b.zone.v3.ZoneService` calls to this server; CLI uses `ZoneServiceClient` (from ADR046-routing-010) for topology list/inspect/watch, enrollment, and route resolution | | Data migration | None; v3 Zone service is new; no v2 realm-service compatibility | -| Validation | Bootstrap/enroll/resolve-route/shortcut integration tests against a child-local fake ZoneLink; topology list/inspect/watch golden vectors contain exact `{ childZone, parentZone }` rows plus authenticated status and no ZoneLink fields; stale/withdrawn/unauthenticated projection tests; parent-store no-row/no-handler test; relay-verb RBAC test; KK enrollment test; shortcut ZonePath addressing test; concurrent dispatch bound test (64 in-flight) | +| Validation | Bootstrap/enroll/resolve-route/shortcut integration tests against a child-local fake ZoneLink; topology list/inspect/watch golden vectors contain exact `{ childZone, parentZone }` rows plus authenticated status and no ZoneLink fields; stale/withdrawn/unauthenticated projection tests; parent-store no-row/no-handler test; relay-plus-target-verb RBAC tests; KK enrollment test; shortcut ZonePath addressing test; concurrent dispatch bound test (64 in-flight) | | Removal proof | `RealmServiceServer` on `d2b.realm.v2` retires after `ZoneServiceServer` handles all routing; display-session path migrates separately as part of Provider resource work | ### ADR046-routing-011 From d636e74e87e6834a89654a5e99d3754e1687a952 Mon Sep 17 00:00:00 2001 From: John Vicondoa Date: Fri, 24 Jul 2026 12:26:29 -0700 Subject: [PATCH 105/115] docs: regenerate fourth-round integration artifacts --- docs/specs/ADR-046-implementation-graph.json | 66 +++++----- docs/specs/ADR-046-implementation-graph.md | 2 +- docs/specs/ADR-046-spec-set.json | 76 +++++------ docs/specs/ADR-046-work-items.json | 124 +++++++++--------- ...46-provider-credential-managed-identity.md | 3 +- ...-046-provider-credential-secret-service.md | 3 +- .../ADR-046-provider-runtime-qemu-media.md | 8 +- 7 files changed, 147 insertions(+), 135 deletions(-) diff --git a/docs/specs/ADR-046-implementation-graph.json b/docs/specs/ADR-046-implementation-graph.json index b2cd49764..aa7f5c6dd 100644 --- a/docs/specs/ADR-046-implementation-graph.json +++ b/docs/specs/ADR-046-implementation-graph.json @@ -2,7 +2,7 @@ "adr" : "0046", "artifactKind" : "d2b-adr-implementation-graph", "counts" : { - "edges" : 1938, + "edges" : 1939, "maxTopologicalRank" : 22, "nodes" : 598, "specNodes" : 55, @@ -7457,6 +7457,11 @@ "to" : "ADR046-qemu-media-010", "type" : "work-item-depends-on" }, + { + "from" : "ADR046-qemu-media-012", + "to" : "ADR046-network-005", + "type" : "work-item-depends-on" + }, { "from" : "ADR046-qemu-media-012", "to" : "ADR046-qemu-media-002", @@ -12086,7 +12091,7 @@ "destinations" : [ "`packages/d2b-resource-api/src/authz.rs`, `packages/d2b-core-controller/src/rbac.rs`" ], - "detailedDesign" : "Role/RoleBinding schemas/evaluator/cache/revision invalidation, ComponentSession subject mapping, parent Zone access", + "detailedDesign" : "Role/RoleBinding schemas/evaluator/cache/revision invalidation, canonical resource/session verb enums including ZoneLink-scoped `relay`, ComponentSession subject mapping, parent Zone access, and independent per-hop relay plus target-verb admission", "entryContracts" : [ "ADR-046-resource-api-and-authorization" ], @@ -12101,7 +12106,7 @@ ], "specId" : "ADR-046-resource-api-and-authorization", "topologicalRank" : 6, - "validation" : "Decision matrix/property tests; revocation/cache/outage/parent-child tests", + "validation" : "Decision matrix/property tests; closed-enum and relay-origin/scope rejection; relay-missing and target-verb-missing fail-closed vectors; revocation/cache/outage/parent-child tests", "wave" : "W0" }, { @@ -12730,7 +12735,7 @@ ], "specId" : "ADR-046-componentsession-and-bus", "topologicalRank" : 6, - "validation" : "Message isolation, route/auth revocation, fairness, reconnect, no direct-store path", + "validation" : "Message isolation; closed session-verb enum including `relay`; relay missing/target verb missing/provider self-assertion fail-closed vectors; route/auth revocation; fairness; reconnect; no direct-store path", "wave" : "W1" }, { @@ -13672,7 +13677,7 @@ "destinations" : [ "packages/d2b-provider-credential-managed-identity/src/{audit.rs,telemetry.rs}; packages/d2b-contract-tests/tests/credential_audit.rs" ], - "detailedDesign" : "Shared audit/OTEL: emit audit records for all methods and controller events per §Audit; emit OTEL spans and metrics per §OTEL and metrics with no Zone/Credential/resource-name-derived label; report expiry as the minimum for each provider/placement aggregate; add `d2b_credential_imds_calls_total` counter with bounded `alias` label; enforce `contains_sensitive_shape` on all string fields in audit records and metric labels; add canary tests for `managed-identity-canary`, `credential_canary`, `imds-endpoint-canary`, Credential `metadata.name`, and Zone name in `canary.rs`. Primary reuse disposition: `adapt`. Preserved source-plan detail: adapt existing sensitive-shape guard and canary pattern to v3 audit, OTEL, and metric surfaces.", + "detailedDesign" : "Shared audit/OTEL: emit audit records for all methods and controller events per §Audit, with Credential identity represented only by the authorized bounded `resource_name_digest`; emit OTEL spans and metrics per §OTEL and metrics with no Credential resource name, ResourceRef, UID, digest, derived identity token, Zone/Credential/resource-name-derived label, or non-allowlisted OTEL Resource attribute; retain applicable generic collector-allowlisted Resource attributes (`d2b.zone`, `d2b.provider`, `d2b.component`, and service fields); report expiry as the minimum for each provider/placement aggregate; add `d2b_credential_imds_calls_total` counter with bounded `alias` label; enforce `contains_sensitive_shape` on all string fields in audit records and metric labels; add canary tests for `managed-identity-canary`, `credential_canary`, `imds-endpoint-canary`, Credential name/ref/UID/digest, and Zone name in `canary.rs`. Primary reuse disposition: `adapt`. Preserved source-plan detail: adapt existing sensitive-shape guard and canary pattern to v3 audit, OTEL, and metric surfaces.", "entryContracts" : [ "ADR-046-provider-credential-managed-identity" ], @@ -13688,7 +13693,7 @@ ], "specId" : "ADR-046-provider-credential-managed-identity", "topologicalRank" : 13, - "validation" : "`packages/d2b-contract-tests/tests/credential_audit.rs`; managed-identity `canary.rs`; audit/OTEL unit tests structurally assert exact absence of `vm`, `zone`, `zone_id`, `zone_uid`, `credential_name`, and every resource-name-derived label key, reject Credential/Zone-name label canaries, preserve allowed OTEL resource identity attributes, reject identity span attributes, and reject sensitive shapes", + "validation" : "`packages/d2b-contract-tests/tests/credential_audit.rs` requires `resource_name_digest` in authorized audit records and rejects raw Credential name/ResourceRef/UID; managed-identity `canary.rs` and audit/OTEL unit tests structurally assert exact absence of `vm`, `zone`, `zone_id`, `zone_uid`, `credential_name`, `credential_ref`, `credential_uid`, `credential_digest`, `resource_name_digest`, and every resource-name-derived label key; reject Credential name/ref/UID/digest canaries from all OTEL Resource attributes, span attributes, and metric labels; preserve generic collector-allowlisted Resource attributes including `d2b.zone`, `d2b.provider`, `d2b.component`, and service fields; reject Zone-name span/label canaries and sensitive shapes; pass complete managed-identity metric/span frames through the shared collector ingress validator and prove that adding `d2b.credential.name` or any Credential identity key/value rejects the whole frame", "wave" : "W6" }, { @@ -13809,7 +13814,7 @@ "destinations" : [ "packages/d2b-provider-credential-secret-service/src/{audit.rs,telemetry.rs}" ], - "detailedDesign" : "Audit/OTEL: emit audit records and OTEL spans/metrics for all credential service methods and controller events, with canary enforcement, expiry aggregated across user-agent leases, no Zone/Credential/resource-name-derived metric label, and no token/object-path/lease bytes in status, delivery outer headers, audit, metrics, spans, or logs. Full detail remains in `ADR-046-resources-credential` §Implementation work items. Primary reuse disposition: `adapt`. Preserved source-plan detail: adapt zero-secret invariant and canary test pattern to credential-secret-service audit/OTEL surfaces.", + "detailedDesign" : "Audit/OTEL: emit authorized bounded audit records with Credential identity represented only by `resource_name_digest`, and emit OTEL spans/metrics for all credential service methods and controller events with canary enforcement, expiry aggregated across user-agent leases, no Credential resource name, ResourceRef, UID, digest, derived identity token, Zone/Credential/resource-name-derived metric label, or non-allowlisted OTEL Resource attribute; retain applicable generic collector-allowlisted Resource attributes (`d2b.zone`, `d2b.provider`, `d2b.component`, and service fields); no token/object-path/lease bytes in status, delivery outer headers, audit, metrics, spans, or logs. Full detail remains in `ADR-046-resources-credential` §Implementation work items. Primary reuse disposition: `adapt`. Preserved source-plan detail: adapt zero-secret invariant and canary test pattern to credential-secret-service audit/OTEL surfaces.", "entryContracts" : [ "ADR-046-provider-credential-secret-service" ], @@ -13823,7 +13828,7 @@ ], "specId" : "ADR-046-provider-credential-secret-service", "topologicalRank" : 11, - "validation" : "Credential audit/OTEL tests from `ADR-046-resources-credential`; `tests/canary.rs` structurally asserts exact absence of `vm`, `zone`, `zone_id`, `zone_uid`, `credential_name`, and every resource-name-derived metric key plus Credential/Zone-name label canary absence while preserving allowed OTEL resource identity attributes and rejecting identity span attributes; `tests/delivery.rs` for credential-secret-service", + "validation" : "Credential audit/OTEL tests from `ADR-046-resources-credential` require `resource_name_digest` in authorized audit records and reject raw Credential name/ResourceRef/UID; `tests/canary.rs` structurally asserts exact absence of `vm`, `zone`, `zone_id`, `zone_uid`, `credential_name`, `credential_ref`, `credential_uid`, `credential_digest`, `resource_name_digest`, and every resource-name-derived metric key; Credential name/ref/UID/digest canaries are absent from all OTEL Resource attributes, span attributes, and metric labels; Zone-name canaries are absent from spans and labels while generic collector-allowlisted Resource attributes remain; complete secret-service metric/span frames pass the shared collector ingress validator, while adding `d2b.credential.name` or any Credential identity key/value rejects the whole frame; `tests/delivery.rs` covers credential-secret-service delivery", "wave" : "W6" }, { @@ -14007,7 +14012,7 @@ "destinations" : [ "`packages/d2b-provider-credential-/src/audit.rs`, `telemetry.rs`; `packages/d2b-contract-tests/tests/credential_audit.rs`" ], - "detailedDesign" : "Implement audit record emission for all credential service methods and controller events using the field set defined in §Audit; implement OTEL span/metric emission using the closed semantic label set in §OTEL and metrics, with expiry reported as a provider/placement aggregate and no Credential/Zone/resource-name-derived label; implement `contains_sensitive_shape` check in all string fields of audit records and metric label values (adapted from `d2b-realm-provider/src/error.rs:contains_sensitive_shape`); add canary-enforcement tests that verify `\"secret-canary\"`, `\"entra-token-canary\"`, `\"managed-identity-canary\"`, Credential `metadata.name`, and Zone name values never appear in any metric label, span attribute, log line, or status field; identity remains only in allowed OTEL resource attributes and permitted audit fields", + "detailedDesign" : "Implement audit record emission for all credential service methods and controller events using the field set defined in §Audit, with Credential identity represented only by the authorized bounded `resource_name_digest`; implement OTEL span/metric emission using the closed semantic label set in §OTEL and metrics, with expiry reported as a provider/placement aggregate and no Credential resource name, ResourceRef, UID, digest, derived identity token, Zone/resource-name-derived label, or non-allowlisted OTEL Resource attribute; retain applicable generic collector-allowlisted Resource attributes (`d2b.zone`, `d2b.provider`, `d2b.component`, and service fields); implement `contains_sensitive_shape` checks in all string fields of audit records and metric label values (adapted from `d2b-realm-provider/src/error.rs:contains_sensitive_shape`); add canary-enforcement tests that verify `\"secret-canary\"`, `\"entra-token-canary\"`, `\"managed-identity-canary\"`, Credential name/ref/UID/digest canaries, and Zone name values never appear in any metric label, span attribute, log line, or status field, and that Credential identity canaries are also absent from OTEL Resource attributes", "entryContracts" : [ "ADR-046-resources-credential" ], @@ -14023,7 +14028,7 @@ ], "specId" : "ADR-046-resources-credential", "topologicalRank" : 12, - "validation" : "Canary tests across all three Provider crates; audit record field-presence tests; structural metric descriptor tests assert exact absence of `vm`, `zone`, `zone_id`, `zone_uid`, `credential_name`, and every resource-name-derived key plus Credential/Zone-name canary absence; resource attribute tests preserve allowed `d2b.zone`/`d2b.credential.name` identity while span attribute tests reject identity and forbidden fields", + "validation" : "Canary tests across all three Provider crates; authorized audit record field-presence tests require `resource_name_digest` and reject raw Credential name/ResourceRef/UID; structural metric descriptor tests assert exact absence of `vm`, `zone`, `zone_id`, `zone_uid`, `credential_name`, `credential_ref`, `credential_uid`, `credential_digest`, `resource_name_digest`, and every resource-name-derived key; Credential name/ref/UID/digest canaries are absent from every OTEL Resource attribute, span attribute, and metric label; Zone-name canaries are absent from spans and labels while resource-attribute tests preserve the generic collector allowlist including `d2b.zone`, `d2b.provider`, `d2b.component`, and service fields; complete Credential metric/span frames pass the shared collector ingress validator, while adding `d2b.credential.name` or any Credential identity key/value rejects the whole frame", "wave" : "W4" }, { @@ -14888,9 +14893,9 @@ { "blockers" : [], "destinations" : [ - "`packages/d2b-contracts/src/v3/host.rs`, `packages/d2b-contracts/src/v3/guest.rs`, `packages/d2b-contracts/src/v3/execution_policy.rs`, `packages/d2b-contracts/src/v3/process.rs`, `packages/d2b-contracts/src/v3/ephemeral_process.rs`, `packages/d2b-contracts/src/v3/user.rs`" + "`packages/d2b-contracts/src/v3/host.rs`, `packages/d2b-contracts/src/v3/guest.rs`, `packages/d2b-contracts/src/v3/execution_policy.rs`, `packages/d2b-contracts/src/v3/process.rs`, `packages/d2b-contracts/src/v3/ephemeral_process.rs`, `packages/d2b-contracts/src/v3/user.rs`, `packages/d2b-contracts/src/v3/endpoint.rs`" ], - "detailedDesign" : "Implement strict typed Rust structs for HostSpec, GuestSpec, ExecutionPolicy, ExecutionSpec, SandboxSpec, BudgetSpec, MountSpec, NetworkUsageSpec, DeviceUsageSpec, EndpointSpec, TelemetrySpec, ProcessSpec, EphemeralProcessSpec, UserSpec; strict serde deny_unknown_fields; bounds/redaction on all string fields; stable error types; `UserSpec.osUsername` validated as OS username (1..255 bytes, no NUL/control/path-separator), not ResourceName grammar Primary reuse disposition: `adapt`. Preserved source-plan detail: extract and adapt.", + "detailedDesign" : "Implement strict typed Rust structs for HostSpec, GuestSpec, ExecutionPolicy, ExecutionSpec, SandboxSpec, BudgetSpec, MountSpec, NetworkUsageSpec, DeviceUsageSpec, EndpointSpec, EndpointConsumerPolicy, TelemetrySpec, ProcessSpec, EphemeralProcessSpec, UserSpec; EndpointSpec accepts exactly `owner\\|provider\\|zone`, and EndpointConsumerPolicy owns the only finer gates (`allowedSubjects`, `allowedProviderComponents`, `allowedOperations`) with no schema aliases; strict serde deny_unknown_fields; bounds/redaction on all string fields; stable error types; `UserSpec.osUsername` validated as OS username (1..255 bytes, no NUL/control/path-separator), not ResourceName grammar Primary reuse disposition: `adapt`. Preserved source-plan detail: extract and adapt.", "entryContracts" : [ "ADR-046-resources-host-guest-process-user" ], @@ -14904,7 +14909,7 @@ ], "specId" : "ADR-046-resources-host-guest-process-user", "topologicalRank" : 10, - "validation" : "Golden JSON vectors for each ResourceType; serde unknown-field rejection; bounds enforcement; `UserSpec.osUsername` OS-username validation (underscore allowed, NUL rejected)", + "validation" : "Golden JSON vectors for each ResourceType; Endpoint vectors accept only `owner\\|provider\\|zone`, reject every legacy/private visibility alias, reject scalar/array `consumerPolicy` aliases, and cover each canonical consumer allowlist; a docs drift test parses every `type: Endpoint` YAML/Nix example and fails unless visibility is canonical and finer gates occur only under `consumerPolicy`; serde unknown-field rejection; bounds enforcement; `UserSpec.osUsername` OS-username validation (underscore allowed, NUL rejected)", "wave" : "W5" }, { @@ -16351,7 +16356,7 @@ ], "specId" : "ADR-046-nix-configuration", "topologicalRank" : 10, - "validation" : "nix-unit vectors for each ResourceType; ref-validation rejection vectors; malformed ref error shape; `parentZone` is missing on non-root/forbidden on local-root/unknown/self/cyclic/over-depth rejection; scalar module conflicts prove one parent; child-local ZoneLink `childZoneName` must equal its enclosing Zone key; a second uplink resource (even disabled) and a local-root uplink fail eval; missing/local-unresolved `transportProviderRef` fails eval; `managedBy` in spec rejected at eval; Zone spec is `{}` (no `parentZone`, `parentRef`, `retainedGenerations`, etc.)", + "validation" : "nix-unit vectors for each ResourceType; ref-validation rejection vectors; malformed ref error shape; resource/session verb closed-enum vectors accept `relay` only in `sessionVerbs`; relay wildcard/unbounded/Provider-self-asserted fixtures fail before activation while an exact core-generated ZoneLink fixture passes; Endpoint visibility accepts exactly `owner|provider|zone` and uses `consumerPolicy` for finer bounds; `parentZone` is missing on non-root/forbidden on local-root/unknown/self/cyclic/over-depth rejection; scalar module conflicts prove one parent; child-local ZoneLink `childZoneName` must equal its enclosing Zone key; a second uplink resource (even disabled) and a local-root uplink fail eval; missing/local-unresolved `transportProviderRef` fails eval; `managedBy` in spec rejected at eval; Zone spec is `{}` (no `parentZone`, `parentRef`, `retainedGenerations`, etc.)", "wave" : "W5" }, { @@ -18662,7 +18667,7 @@ "destinations" : [ "packages/d2b-provider-runtime-qemu-media/src/controller/process_builder.rs" ], - "detailedDesign" : "Process spec builder and LaunchTicket assembly: build the canonical `qemu-media-runner` Process ResourceSpec from §10.1 and assemble the LaunchTicket with sealed fd slots for kvm, tap, media, and optional display fds. No raw path, argv, executable path, or principal appears in any public field. Primary reuse disposition: `adapt`. Preserved source-plan detail: adapt fd-slot and sandbox/budget concepts to canonical Process resources and sealed LaunchTickets; do not copy raw argv strings or path construction.", + "detailedDesign" : "Process spec builder and LaunchTicket attachment resolution: build the canonical `qemu-media-runner` Process ResourceSpec from §10.1 and supply only opaque Network/Endpoint refs to Core's attachment resolver. Core, not the qemu controller, resolves authorized kvm, tap, media, and optional display attachments and seals their fd slots in the LaunchTicket. The qemu Provider/controller receives no broker operation or fd. No raw path, argv, executable path, or principal appears in any public field. Primary reuse disposition: `adapt`. Preserved source-plan detail: adapt sandbox/budget concepts to canonical Process resources and Core-sealed LaunchTickets; do not copy raw argv strings or path construction.", "entryContracts" : [ "ADR-046-provider-runtime-qemu-media" ], @@ -18682,7 +18687,7 @@ ], "specId" : "ADR-046-provider-runtime-qemu-media", "topologicalRank" : 15, - "validation" : "`tests/process_spec_golden.rs`; `tests/launch_ticket_fd_slots.rs`; `tests/no_raw_argv_in_spec.rs`", + "validation" : "`tests/process_spec_golden.rs`; `tests/launch_ticket_fd_slots.rs`; `tests/launch_ticket_tap_fd_lifetime.rs`; `tests/no_controller_fd_or_broker_op.rs`; `tests/no_raw_argv_in_spec.rs`", "wave" : "W6" }, { @@ -18737,22 +18742,23 @@ "destinations" : [ "packages/d2b-provider-runtime-qemu-media/src/controller/network.rs" ], - "detailedDesign" : "Network tap fd acquisition: call the `network-local` ComponentSession service to request a tap fd for a Guest MAC/bridge assignment and include the fd in the LaunchTicket. No bridge name or interface name appears in any public field. Primary reuse disposition: `create`. Preserved source-plan detail: net-new against the `network-local` ComponentSession contract.", + "detailedDesign" : "Network attachment routing: project each Guest network attachment as an opaque `Network/` ref and condition only. The network-local controller declares the opaque semantic effect; its Core-owned NetworkEffectPort adapter maps it to `CreatePersistentTap`, then `SetBridgePortFlags`, and transfers the already-authorized connected `OwnedFd` directly to ProviderSupervisor for the QEMU Process LaunchTicket. The adapter and supervisor keep `FD_CLOEXEC` set on parent copies; only the declared child slot is made inheritable immediately before exec. On cancellation, ticket rejection, or spawn failure, all fd copies close before generation-fenced `DeletePersistentTap`, and the opaque realization is retained until deletion confirmation. The qemu Provider/controller receives no broker operation, fd, bridge name, or interface name, and the fd is never serialized through ResourceAPI, ComponentSession, or d2b-bus. Primary reuse disposition: `create`.", "entryContracts" : [ "ADR-046-provider-runtime-qemu-media" ], "exitGate" : "ADR046-W6 exit criteria (ADR-046-validation-and-delivery §4): every spec/work item in this wave Merged with clean destinations, all validators green, and the ten-role panel seal recorded", "id" : "ADR046-qemu-media-012", "kind" : "work-item", - "owner" : "P1; depends on ADR046-qemu-media-002 and Provider config `networkProviderRef`; owner: runtime-qemu-media network dependency integration", + "owner" : "P1; depends on ADR046-qemu-media-002, ADR046-network-005, and Provider config `networkProviderRef`; owner: runtime-qemu-media network dependency integration", "parallelGroup" : "wi:ADR-046-provider-runtime-qemu-media", "prerequisites" : [ "ADR-046-provider-runtime-qemu-media", + "ADR046-network-005", "ADR046-qemu-media-002" ], "specId" : "ADR-046-provider-runtime-qemu-media", "topologicalRank" : 13, - "validation" : "`tests/tap_fd_acquisition.rs`; `tests/tap_fd_unavailable.rs`", + "validation" : "`tests/tap_launch_routing.rs` proves `CreatePersistentTap → SetBridgePortFlags → ProviderSupervisor LaunchTicket` ordering; `tests/tap_fd_lifetime.rs` proves CLOEXEC, single child ownership, and close-before-`DeletePersistentTap`; `tests/tap_fd_no_bus_serialization.rs` rejects fd/broker DTOs at the qemu controller boundary; `tests/tap_fd_unavailable.rs` covers authorization and resolution failure", "wave" : "W6" }, { @@ -19589,7 +19595,7 @@ "destinations" : [ "`packages/d2b-bus/src/zone_route.rs` (cross-Zone bus routing), `packages/d2b-bus/src/relay.rs` (per-hop relay handler)" ], - "detailedDesign" : "Cross-Zone routing path in d2b-bus: ZoneEntrypointResolver consumes sealed topology plus authenticated route projections → ZoneRouteEngine::decide_route → admitted ComponentSession established by each next-hop child's local ZoneLink; hop-counter decrement and enforcement; RBAC `relay` verb check at each intermediate hop; idempotency key namespace (full 6-tuple) in ZoneLinkIdempotencyKey; pinned reverse path tracking; cancellation forwarding; watch cursor forwarding and revision-expired handling; no-FD/credential structural rejection at serialization boundary. No parent route step performs Resource API Get/List/Watch on ZoneLink Primary reuse disposition: `adapt`. Preserved source-plan detail: extract and adapt.", + "detailedDesign" : "Cross-Zone routing path in d2b-bus: ZoneEntrypointResolver consumes sealed topology plus authenticated route projections → ZoneRouteEngine::decide_route → admitted ComponentSession established by each next-hop child's local ZoneLink; hop-counter decrement and enforcement; independent target-verb plus canonical ZoneLink-scoped `relay` checks at each intermediate hop; idempotency key namespace (full 6-tuple) in ZoneLinkIdempotencyKey; pinned reverse path tracking; cancellation forwarding; watch cursor forwarding and revision-expired handling; no-FD/credential structural rejection at serialization boundary. No parent route step performs Resource API Get/List/Watch on ZoneLink Primary reuse disposition: `adapt`. Preserved source-plan detail: extract and adapt.", "entryContracts" : [ "ADR-046-zone-routing" ], @@ -19605,7 +19611,7 @@ ], "specId" : "ADR-046-zone-routing", "topologicalRank" : 9, - "validation" : "End-to-end K0→K1→K2 resource call; relay-denied/hop-limit/FD-rejection tests; idempotency namespace collision tests; cancellation delivery tests; watch resync tests", + "validation" : "End-to-end K0→K1→K2 resource call; relay-missing, target-verb-missing, wildcard/self-asserted relay, hop-limit, and FD-rejection tests; prove relay alone grants no CRUD/local lifecycle; idempotency namespace collision tests; cancellation delivery tests; watch resync tests", "wave" : "W2" }, { @@ -19814,7 +19820,7 @@ ], "specId" : "ADR-046-zone-routing", "topologicalRank" : 8, - "validation" : "Port inline registry lifecycle/drain/shutdown tests; add v3 ZonePath routing test; add RBAC relay-verb check at provider admission", + "validation" : "Port inline registry lifecycle/drain/shutdown tests; add v3 ZonePath routing test; prove provider admission cannot self-assert relay and each forward requires relay plus the target verb", "wave" : "W2" }, { @@ -19845,7 +19851,7 @@ "destinations" : [ "`packages/d2b-zone-routing/src/service.rs`" ], - "detailedDesign" : "Rename `RealmServiceServer` → `ZoneServiceServer`; service wire name `d2b.realm.v2.RealmService` → `d2b.zone.v3.ZoneService`; rename methods (bootstrap→zone-bootstrap, enroll→zone-enroll, resolve_route→resolve-zone-route, authorize_shortcut→authorize-zone-shortcut, revoke_shortcut→revoke-zone-shortcut, report_shortcut_close→report-zone-shortcut-close, inspect→zone-inspect) and add list/watch topology-projection methods. The read-only projection starts from the sealed sorted `{ childZone, parentZone }` compiler input and joins only authenticated, admitted `ZoneRouteEngine` route/projection status. It exposes no ZoneLink resource name, UID, spec, status, Provider ref, fingerprint, transport setting, or handle. Replace `RealmSessionAuthority` with Zone principal + RBAC binding; replace `BootstrapBinding` with allocator-issued PSK binding associated with the child's local ZoneLink; replace `EnrollmentBinding` with the corresponding KK enrollment record; add `relay` verb RBAC check per hop; adapt shortcut model to ZonePath addressing; `RealmServiceLimits` defaults preserved; `MAX_DISPATCH_IN_FLIGHT=64`, `SHUTDOWN_TIMEOUT=5s` preserved; `CredentialCustody::GatewayGuest` excluded (all ZoneLink sessions are direct KK) Primary reuse disposition: `adapt`. Preserved source-plan detail: copy and adapt.", + "detailedDesign" : "Rename `RealmServiceServer` → `ZoneServiceServer`; service wire name `d2b.realm.v2.RealmService` → `d2b.zone.v3.ZoneService`; rename methods (bootstrap→zone-bootstrap, enroll→zone-enroll, resolve_route→resolve-zone-route, authorize_shortcut→authorize-zone-shortcut, revoke_shortcut→revoke-zone-shortcut, report_shortcut_close→report-zone-shortcut-close, inspect→zone-inspect) and add list/watch topology-projection methods. The read-only projection starts from the sealed sorted `{ childZone, parentZone }` compiler input and joins only authenticated, admitted `ZoneRouteEngine` route/projection status. It exposes no ZoneLink resource name, UID, spec, status, Provider ref, fingerprint, transport setting, or handle. Replace `RealmSessionAuthority` with Zone principal + RBAC binding; replace `BootstrapBinding` with allocator-issued PSK binding associated with the child's local ZoneLink; replace `EnrollmentBinding` with the corresponding KK enrollment record; add independent target-verb plus canonical `relay` RBAC checks per forwarding hop; adapt shortcut model to ZonePath addressing; `RealmServiceLimits` defaults preserved; `MAX_DISPATCH_IN_FLIGHT=64`, `SHUTDOWN_TIMEOUT=5s` preserved; `CredentialCustody::GatewayGuest` excluded (all ZoneLink sessions are direct KK) Primary reuse disposition: `adapt`. Preserved source-plan detail: copy and adapt.", "entryContracts" : [ "ADR-046-zone-routing" ], @@ -19863,7 +19869,7 @@ ], "specId" : "ADR-046-zone-routing", "topologicalRank" : 11, - "validation" : "Bootstrap/enroll/resolve-route/shortcut integration tests against a child-local fake ZoneLink; topology list/inspect/watch golden vectors contain exact `{ childZone, parentZone }` rows plus authenticated status and no ZoneLink fields; stale/withdrawn/unauthenticated projection tests; parent-store no-row/no-handler test; relay-verb RBAC test; KK enrollment test; shortcut ZonePath addressing test; concurrent dispatch bound test (64 in-flight)", + "validation" : "Bootstrap/enroll/resolve-route/shortcut integration tests against a child-local fake ZoneLink; topology list/inspect/watch golden vectors contain exact `{ childZone, parentZone }` rows plus authenticated status and no ZoneLink fields; stale/withdrawn/unauthenticated projection tests; parent-store no-row/no-handler test; relay-plus-target-verb RBAC tests; KK enrollment test; shortcut ZonePath addressing test; concurrent dispatch bound test (64 in-flight)", "wave" : "W2" }, { @@ -19918,7 +19924,7 @@ "destinations" : [ "`packages/d2b-resource-store/tests/rbac_property.rs`" ], - "detailedDesign" : "Property test asserting, for a randomly generated Role/RoleBinding/request corpus: (1) no request whose payload sets a subject/role field ever changes the resolved `AuthenticatedSubjectContext.subjectRef`; (2) no non-core Role with a wildcard grant is ever admitted; (3) `scopeNarrowing` never widens beyond the referenced Role; (4) RoleBinding deletion never leaves an observable intermediate state under concurrent readers Primary reuse disposition: `adapt`. Preserved source-plan detail: extract and adapt.", + "detailedDesign" : "Property test asserting, for a randomly generated Role/RoleBinding/request corpus: (1) no request whose payload sets a subject/role field ever changes the resolved `AuthenticatedSubjectContext.subjectRef`; (2) no non-core Role with a wildcard grant is ever admitted; (3) `scopeNarrowing` never widens beyond the referenced Role; (4) RoleBinding deletion never leaves an observable intermediate state under concurrent readers; (5) `relay` is accepted only as the canonical ZoneLink-scoped session verb with core-generated or explicit admin-policy provenance and exact target bounds; and (6) every forwarding hop independently requires relay plus the target verb Primary reuse disposition: `adapt`. Preserved source-plan detail: extract and adapt.", "entryContracts" : [ "ADR-046-security-and-threat-model" ], @@ -24059,7 +24065,7 @@ "destinations" : [ "`packages/d2b-contracts/src/v3/role.rs`; `packages/d2b-core-controller/src/authz.rs`" ], - "detailedDesign" : "Role resource schema with rule schema from §5.3 and resolved bounds (32 rules, 16 resourceTypes per rule, 64 resourceNames, 32 executionRefs); verb enum; explicit wildcard enforcement; index builder; phase/conditions; `role-binding-drain` finalizer; Nix Role options; audit/OTEL instrumentation Primary reuse disposition: `adapt`. Preserved source-plan detail: extract and adapt (`verb_requires_admin()` verb table → Role verb enum; `DaemonAccessDecision` error types → Role admission errors; `MappedDaemonAccessPrincipal` → subject identity model).", + "detailedDesign" : "Role resource schema with rule schema from §5.3 and resolved bounds (32 rules, 16 resourceTypes per rule, 64 resourceNames, 32 executionRefs); separate closed resource/session verb enums including `relay`; core-generated ZoneLink ownership, exact adjacent-Zone enrollment selector, exact target bounds, and explicit admin-policy exception admission; explicit wildcard enforcement; index builder; phase/conditions; `role-binding-drain` finalizer; generated Nix Role option help; audit/OTEL instrumentation Primary reuse disposition: `adapt`. Preserved source-plan detail: extract and adapt (`verb_requires_admin()` verb table → Role verb enum; `DaemonAccessDecision` error types → Role admission errors; `MappedDaemonAccessPrincipal` → subject identity model).", "entryContracts" : [ "ADR-046-resources-zone-control" ], @@ -24075,7 +24081,7 @@ ], "specId" : "ADR-046-resources-zone-control", "topologicalRank" : 11, - "validation" : "All §15.4 Role tests including the resolved bounds checks", + "validation" : "All §15.4 Role tests including closed-enum, relay origin/scope/target-verb, and resolved-bounds checks", "wave" : "W5" }, { @@ -24130,7 +24136,7 @@ "destinations" : [ "`nixos-modules/options-zones.nix`; `nixos-modules/resources-zone-control.nix`; extended `nixos-modules/index.nix`" ], - "detailedDesign" : "`d2b.zones..*` Nix options for Zone/ZoneLink/Provider/Role/RoleBinding/Quota/EmergencyPolicy authoring; compiler-only scalar `parentZone` required on non-root Zones and forbidden on `local-root`; Nix eval-time validation of parent existence, one resolved parent, self/cycle rejection, 16-name ancestry depth, ResourceRefs, verb enums, digest format, subject types, child-local ZoneLink self-name/one-uplink/local transport-ref constraints, and the no-expiry RoleBinding model; canonical JSON serialization; generation-bound resource bundle output; the compiler seals the validated parent map into allocator bootstrap topology without emitting `parentZone` into `Zone.spec` or reciprocal resources Primary reuse disposition: `adapt`. Preserved source-plan detail: adapt (`providerType` submodule → Provider option submodule; label regex `^[a-z][a-z0-9-]*$` retained unchanged; `placement` option → Provider component placement; `d2b.realms.*` option namespace → `d2b.zones.*` option namespace).", + "detailedDesign" : "`d2b.zones..*` Nix options for Zone/ZoneLink/Provider/Role/RoleBinding/Quota/EmergencyPolicy authoring; compiler-only scalar `parentZone` required on non-root Zones and forbidden on `local-root`; Nix eval-time validation of parent existence, one resolved parent, self/cycle rejection, 16-name ancestry depth, ResourceRefs, separate resource/session verb enums (including session-only `relay`), relay origin/scope restrictions, digest format, subject types, child-local ZoneLink self-name/one-uplink/local transport-ref constraints, and the no-expiry RoleBinding model; generated help carries the relay semantics; canonical JSON serialization; generation-bound resource bundle output; the compiler seals the validated parent map into allocator bootstrap topology without emitting `parentZone` into `Zone.spec` or reciprocal resources Primary reuse disposition: `adapt`. Preserved source-plan detail: adapt (`providerType` submodule → Provider option submodule; label regex `^[a-z][a-z0-9-]*$` retained unchanged; `placement` option → Provider component placement; `d2b.realms.*` option namespace → `d2b.zones.*` option namespace).", "entryContracts" : [ "ADR-046-resources-zone-control" ], @@ -24152,7 +24158,7 @@ ], "specId" : "ADR-046-resources-zone-control", "topologicalRank" : 14, - "validation" : "nix-unit vectors for each Zone control type schema; cross-field constraint tests; rendered JSON contract tests (`make test-drift`)", + "validation" : "nix-unit vectors for each Zone control type schema; closed resource/session verb and relay restriction vectors; cross-field constraint tests; rendered JSON contract tests (`make test-drift`)", "wave" : "W5" }, { @@ -24318,7 +24324,7 @@ ], "specId" : "ADR-046-resources-zone-control", "topologicalRank" : 11, - "validation" : "All Phase 1 eval tests in §15.8 (`nix-eval-name-regex-enforced`, `nix-eval-verb-closed-enum`, `nix-eval-roleref-format`, `nix-eval-subject-type-restricted`, `nix-eval-no-duplicate-subjects`, `nix-eval-bootstrap-provider-rejected`, `nix-eval-provider-missing-artifact-id`, `nix-eval-artifact-id-not-in-catalog`, `nix-eval-artifact-wrong-type`, `nix-eval-artifact-id-format`, `nix-eval-credentialref-declared`, `nix-eval-dollar-key-rejected`, all five `nix-eval-parent-zone-*` vectors, `nix-eval-zonelink-child-name-mismatch-rejected`, `nix-eval-zonelink-second-uplink-rejected`, `nix-eval-zonelink-limits-maxpendingintents-bound`); drift test asserts the standard registry and generated option modules cover exactly all 19 canonical types; negative evals reject unknown strings, unqualified extensions, unsigned or uninstalled qualified types, and unknown `spec` fields; a positive fixture admits an installed signed qualified type and validates its strict generated schema", + "validation" : "All Phase 1 eval tests in §15.8 (`nix-eval-name-regex-enforced`, `nix-eval-verb-closed-enum`, `nix-eval-session-verb-closed-enum`, `nix-eval-relay-session-verb-known`, `nix-eval-roleref-format`, `nix-eval-subject-type-restricted`, `nix-eval-no-duplicate-subjects`, `nix-eval-bootstrap-provider-rejected`, `nix-eval-provider-missing-artifact-id`, `nix-eval-artifact-id-not-in-catalog`, `nix-eval-artifact-wrong-type`, `nix-eval-artifact-id-format`, `nix-eval-credentialref-declared`, `nix-eval-dollar-key-rejected`, all five `nix-eval-parent-zone-*` vectors, `nix-eval-zonelink-child-name-mismatch-rejected`, `nix-eval-zonelink-second-uplink-rejected`, `nix-eval-zonelink-limits-maxpendingintents-bound`); Phase 2 runs `nix-build-relay-scope-restricted`; drift test asserts the standard registry and generated option modules cover exactly all 19 canonical types; negative evals reject unknown strings, unqualified extensions, unsigned or uninstalled qualified types, and unknown `spec` fields; a positive fixture admits an installed signed qualified type and validates its strict generated schema", "wave" : "W5" }, { diff --git a/docs/specs/ADR-046-implementation-graph.md b/docs/specs/ADR-046-implementation-graph.md index 0778b179f..dbfe60388 100644 --- a/docs/specs/ADR-046-implementation-graph.md +++ b/docs/specs/ADR-046-implementation-graph.md @@ -25,7 +25,7 @@ Each JSON work-item node also embeds the manifest's exact `detailedDesign` and | Spec nodes | 55 | | Work-item nodes | 543 | | Total nodes | 598 | -| Edges | 1938 | +| Edges | 1939 | | Max topological rank | 22 | ## Waves (W0–W7) diff --git a/docs/specs/ADR-046-spec-set.json b/docs/specs/ADR-046-spec-set.json index 9eb35d9ee..15805a8b9 100644 --- a/docs/specs/ADR-046-spec-set.json +++ b/docs/specs/ADR-046-spec-set.json @@ -29,7 +29,7 @@ "ADR-046-provider-model-and-packaging" ], "path" : "docs/specs/ADR-046-components-processes-and-sandbox.md", - "sha256" : "520f1f773f70e546d5566fa21b7981148c39790befa6fca0518d14ebf8b9ba96", + "sha256" : "a52ce5403399596d942f0c65f8f28b22de6b0e57c9623416c7756a5a7ac5be7c", "specId" : "ADR-046-components-processes-and-sandbox", "status" : "Proposed", "supersedes" : "Current ProcessRole/VmProcessDag as the public process model", @@ -44,7 +44,7 @@ "ADR-046-terminology-and-identities" ], "path" : "docs/specs/ADR-046-componentsession-and-bus.md", - "sha256" : "2eca9b7d988406889a0fbd5062abb35e5c56c67c6df4f4385dab3d3e89d21904", + "sha256" : "d0daf4c6ebf921d59489a43087615a48803a6821782e0d41c55008858028e512", "specId" : "ADR-046-componentsession-and-bus", "status" : "Proposed", "supersedes" : "Current v3 Realm PeerSession and ad hoc guest/user/public IPC", @@ -84,7 +84,7 @@ { "dependsOn" : [], "path" : "docs/specs/ADR-046-decision-register.md", - "sha256" : "e61e9d405c772cb305a59cca5391a1f38a0cba66d09fc32c5fe6d12573a5d4b4", + "sha256" : "fae3a475f43023f16a2fc8f01abd2e9b02e840313a992d843f46e0e5af70c4b2", "specId" : "ADR-046-decision-register", "status" : "Proposed", "supersedes" : null, @@ -112,7 +112,7 @@ "ADR-046-zone-routing" ], "path" : "docs/specs/ADR-046-feasibility-and-spikes.md", - "sha256" : "a06ee4f34b96459d014db53f218e6d0dc9e45cf65a63ae32050bc9d6f6df21da", + "sha256" : "7dba615dc1c3a7173d1344a16b3ae0247b4b1158f63c7b217592ad60fd50100e", "specId" : "ADR-046-feasibility-and-spikes", "status" : "Proposed", "supersedes" : null, @@ -130,7 +130,7 @@ "ADR-046-terminology-and-identities" ], "path" : "docs/specs/ADR-046-nix-configuration.md", - "sha256" : "049cfb964da600de8aed072425a870cdec84e749c14f177cc473b1b61d5e611c", + "sha256" : "8742a29435dac5720edd920c5d9859ba9dc2520dca7518d0cea01af3b7d8bc45", "specId" : "ADR-046-nix-configuration", "status" : "Proposed", "supersedes" : "Current `nixos-modules/options-realms*.nix`, `options-envs.nix`, `options-vms.nix`, `index.nix`, `bundle*.nix`, `*-json.nix`, and generated `/etc/d2b/*.json`", @@ -194,7 +194,7 @@ "ADR-046-telemetry-audit-and-support" ], "path" : "docs/specs/providers/ADR-046-provider-audio-pipewire.md", - "sha256" : "9cd571617053bfe795e8ea51e6dcef924c9b368861d5167a21c7635983e03667", + "sha256" : "d55b348dd040ea6874083177adde01868f32cde1e5f30199e3a78e7c21436dd6", "specId" : "ADR-046-provider-audio-pipewire", "status" : "Proposed", "supersedes" : "`nixos-modules/components/audio/host.nix`, `nixos-modules/components/audio/guest.nix`, `packages/d2b-core/src/audio_policy.rs`, `packages/d2bd/src/audio_dispatch.rs`, `packages/d2bd/src/audio_host_controller.rs`, `packages/d2b-host/src/audio_argv.rs`", @@ -215,7 +215,7 @@ "ADR-046-telemetry-audit-and-support" ], "path" : "docs/specs/providers/ADR-046-provider-clipboard-wayland.md", - "sha256" : "bd8d266ecff067d3475718cf434ffc1ce91a6f697d34a76de16776a2c6d90ace", + "sha256" : "8904a31c92495698b240f55a88e3038e4fbb20ab1035ec3abe58d13191afde06", "specId" : "ADR-046-provider-clipboard-wayland", "status" : "Proposed", "supersedes" : "`nixos-modules/clipboard.nix` (v3 migration), ADR-046-provider-clipboard-wayland v1", @@ -235,7 +235,7 @@ "ADR-046-telemetry-audit-and-support" ], "path" : "docs/specs/providers/ADR-046-provider-credential-entra.md", - "sha256" : "348169aa3f7804384902099887620838cb8089fbb3eba24d4fdfdfcf2425f739", + "sha256" : "0ba17f63955ff299d4032b2b9f08baafac49345671fa6ae6cb466d26cd928e81", "specId" : "ADR-046-provider-credential-entra", "status" : "Proposed", "supersedes" : "No Host login/token chains; no direct `EntraCredentialClient` production egress; no `DefaultAzureCredential`, environment, DBus, browser, or path discovery", @@ -254,7 +254,7 @@ "ADR-046-telemetry-audit-and-support" ], "path" : "docs/specs/providers/ADR-046-provider-credential-managed-identity.md", - "sha256" : "5f0017c3f5c78e7504874e060c6d81ef8ab40281f0f8675d2b4e248a792c9984", + "sha256" : "3dfcf08dfae6f6cc5124fe91c06cef65c186d6c16ec32eac0d945655edf5e661", "specId" : "ADR-046-provider-credential-managed-identity", "status" : "Proposed", "supersedes" : "Current v3 `ManagedIdentityRef`, `managed_identity_client_id` ACA config field, `CredentialProvider` trait (status/enrollment-only) in `d2b-realm-provider/src/credential.rs` and `provider.rs`", @@ -276,7 +276,7 @@ "ADR-046-telemetry-audit-and-support" ], "path" : "docs/specs/providers/ADR-046-provider-credential-secret-service.md", - "sha256" : "cb8c810651f3cdc84bf7ac23a9f2d4cdfdda913aace8547373094e74fcc58d5f", + "sha256" : "80708708e45782c01804aed6765a3153f12f43396b490f3f09c19910ceaa8839", "specId" : "ADR-046-provider-credential-secret-service", "status" : "Proposed", "supersedes" : "v2 `SecretServiceCredentialProvider` / `SecretServiceCredentialProviderFactory` in `d2b-realm-provider`; v2 `CredentialProvider` trait", @@ -297,7 +297,7 @@ "ADR-046-telemetry-audit-and-support" ], "path" : "docs/specs/providers/ADR-046-provider-device-gpu.md", - "sha256" : "f200f8279c0ea3dc18bbb13b334c7303354edc045830e6b1f35bdc9b452b1c06", + "sha256" : "f6ba2521edb78e7f573fa3c144f257b74f30796bb9ca31badc53ef51a003690d", "specId" : "ADR-046-provider-device-gpu", "status" : "Proposed", "supersedes" : "`ProcessRole::Gpu`, `ProcessRole::GpuRenderNode`, `ProcessRole::Video` in `packages/d2b-core/src/processes.rs`; Nix `nixos-modules/components/graphics.nix`; Nix `nixos-modules/components/video/guest.nix`; `d2b.vms..graphics.*` options", @@ -321,7 +321,7 @@ "ADR-046-telemetry-audit-and-support" ], "path" : "docs/specs/providers/ADR-046-provider-device-security-key.md", - "sha256" : "810cc78ce459c862e14835d796e56fe03fc0303b69e8c5e707618e19fdd9f134", + "sha256" : "3cf5553f3eb4320ee14d67985be5e629265687328d72f584929e5a0f9869c5c5", "specId" : "ADR-046-provider-device-security-key", "status" : "Proposed", "supersedes" : "`ProcessRole::SecurityKeyFrontend` daemon-internal accept loop (`packages/d2bd/src/security_key.rs`), `nixos-modules/components/security-key-guest.nix` untracked guest `d2b-sk-frontend.service` unit", @@ -342,7 +342,7 @@ "ADR-046-resources-volume" ], "path" : "docs/specs/providers/ADR-046-provider-device-tpm.md", - "sha256" : "559c1f775f75218ed8fa1ff008f7083dcc893f87298dbd459b9d8dd3e7bf9d4c", + "sha256" : "a9fb0308cae40f091623cb7f8e247510bba17cb4dd528756bd061ad951d725c0", "specId" : "ADR-046-provider-device-tpm", "status" : "Proposed", "supersedes" : "Current `nixos-modules/components/tpm.nix` swtpm provisioning and `d2b-priv-broker` swtpm-dir hardening path", @@ -367,7 +367,7 @@ "ADR-046-zone-routing" ], "path" : "docs/specs/providers/ADR-046-provider-device-usbip.md", - "sha256" : "ca10fdd00b683319a36742d9f6060ed38ea6f2335fa7ac49c140d8130a23fec8", + "sha256" : "d4600cee4c450d9ed857155003a61af4c299133eef3a49fbce2940a7315519ee", "specId" : "ADR-046-provider-device-usbip", "status" : "Proposed", "supersedes" : "`nixos-modules/components/usbip.nix` (host-side), per-env usbipd systemd units in `nixos-modules/network.nix`, `ProcessRole::Usbip` / `RunnerRole::Usbip` in current v3 baseline", @@ -390,7 +390,7 @@ "ADR-046-telemetry-audit-and-support" ], "path" : "docs/specs/providers/ADR-046-provider-display-wayland.md", - "sha256" : "e52865faa06261936fad094057072c1e3448610e0b90c5eacba5a43019067065", + "sha256" : "fd57e50f657980881cb2e0f3117cdfc317fe9fbc489f7311f5308653a82f3c89", "specId" : "ADR-046-provider-display-wayland", "status" : "Proposed", "supersedes" : "`ProcessRole::WaylandProxy` in `packages/d2b-core/src/processes.rs`; `LocalCrossDomainWaylandProvider` in `packages/d2b-host-providers/src/lib.rs`; `generate_wayland_proxy_argv` in `packages/d2b-host/src/wayland_proxy_argv.rs`; `nixos-modules/components/graphics.nix` `graphics.waylandProxy.*` options; `nixos-modules/ui-colors.nix` VM border color resolution; current `pkgs/wl-cross-domain-proxy` guest binary", @@ -429,7 +429,7 @@ "ADR-046-telemetry-audit-and-support" ], "path" : "docs/specs/providers/ADR-046-provider-network-local.md", - "sha256" : "e230d7534b246ed9b002cf21c903559d20c6a24f317506ce832c277dd2ef0e23", + "sha256" : "deee9148ceb6db9e698cc944199a0cc44157ceddc424a46d9b3a0420338bc8e0", "specId" : "ADR-046-provider-network-local", "status" : "Proposed", "supersedes" : "`nixos-modules/network.nix`, `nixos-modules/net.nix`", @@ -449,7 +449,7 @@ "ADR-046-telemetry-audit-and-support" ], "path" : "docs/specs/providers/ADR-046-provider-notification-desktop.md", - "sha256" : "d5b9c1e42ed51f70e95193c525331f68a2622fdd1d958985f138766ea2fc41dc", + "sha256" : "3746c29d036e27ea4a7086f6e1f47c5a83b0a54d60973f5e5d3c9e2d4f791bd7", "specId" : "ADR-046-provider-notification-desktop", "status" : "Proposed", "supersedes" : null, @@ -474,7 +474,7 @@ "ADR-046-terminology-and-identities" ], "path" : "docs/specs/providers/ADR-046-provider-observability-otel.md", - "sha256" : "1a81a48f8bbd04c3b8c05049bc7f04848684cc399c05a3745057229309782fdd", + "sha256" : "ed4262392dc6f775c0d85faa270e4480b47136ff2614485496c5681c859a28ab", "specId" : "ADR-046-provider-observability-otel", "status" : "Proposed", "supersedes" : "`ProcessRole::OtelHostBridge` / `RunnerRole::OtelHostBridge`; socat-based vsock forwarder in `packages/d2b-host/src/otel_host_bridge_argv.rs`; `packages/d2bd/src/otel_host_bridge_readiness.rs`; hand-rolled per-VM `nixos-modules/components/observability/` pipeline (adapted to per-Zone)", @@ -494,7 +494,7 @@ "ADR-046-telemetry-audit-and-support" ], "path" : "docs/specs/providers/ADR-046-provider-runtime-azure-container-apps.md", - "sha256" : "e78e86421b68b7df11f490adeb1b1cbd9fef9e6f3f9577efbc49f7775109af8d", + "sha256" : "e92adfce5d1aafce5dbdd1749577da3265c0630b8d7d5099ba3ddef02dab3639", "specId" : "ADR-046-provider-runtime-azure-container-apps", "status" : "Proposed", "supersedes" : "`packages/d2b-provider-aca/` (`AcaWorkloadProvider`, `GuestControlEndpointProvider`), `AcaRelayTransportConfig`, direct vsock guest-control path", @@ -522,7 +522,7 @@ "ADR-046-zone-routing" ], "path" : "docs/specs/providers/ADR-046-provider-runtime-azure-virtual-machine.md", - "sha256" : "d26b90118dcda9a9ca91f174d451ed320ff09362c2662045d0139e3df833ff64", + "sha256" : "03aa87a6b3f6923e863dfd45d17858f0c620bfcdb6a109803bd162372b6b0563", "specId" : "ADR-046-provider-runtime-azure-virtual-machine", "status" : "Proposed", "supersedes" : "Current `InfrastructureProvider` trait (`d2b-realm-provider/src/provider.rs`); `AzureVmForbidden` explicit rejection in `provider_registry.rs`; `AZURE_VM_IMPLEMENTATION_ID` constant; `WorkloadProviderKind::ProviderManaged` paths for Azure VM workloads", @@ -548,7 +548,7 @@ "ADR-046-telemetry-audit-and-support" ], "path" : "docs/specs/providers/ADR-046-provider-runtime-cloud-hypervisor.md", - "sha256" : "b5a48303baa024cc621bd39fd5cdb730bdbb28991c5ea589afa7df82d8016da7", + "sha256" : "1d12e89790ed4c74d73dc2796590f6e75612e32b5298e97a06cf68bb4c143887", "specId" : "ADR-046-provider-runtime-cloud-hypervisor", "status" : "Proposed", "supersedes" : "`packages/d2b-host/src/runtime_provider.rs` `CloudHypervisorRuntimeProvider`; `packages/d2bd/src/` VM lifecycle paths; `d2b-host-providers` adapter; `ProcessRole::CloudHypervisor`, `ProcessRole::Swtpm`, `ProcessRole::NetVm`; systemd unit `d2b--vm.service`; `SwtpmDir` broker op", @@ -572,7 +572,7 @@ "ADR-046-telemetry-audit-and-support" ], "path" : "docs/specs/providers/ADR-046-provider-runtime-qemu-media.md", - "sha256" : "283198af11eb08279e3f2e3a76fd9d80b9d3900e009607171f25d70d687e3a4e", + "sha256" : "52f7bc6e47937040516a885313c313f97db7dbe6757f731e63c53ed08e878bd8", "specId" : "ADR-046-provider-runtime-qemu-media", "status" : "Proposed", "supersedes" : "`docs/adr/0036-qemu-media-runtime.md`", @@ -601,7 +601,7 @@ "ADR-046-zone-routing" ], "path" : "docs/specs/providers/ADR-046-provider-shell-terminal.md", - "sha256" : "6aaa6f3653ce851d2d31a4f1dc00bcb069ad0ef26685e66e63b33b3605171205", + "sha256" : "2fdfc20da92d0ff065d3c0e15c4d15811b6f045ac1b787045a904b580112d2c9", "specId" : "ADR-046-provider-shell-terminal", "status" : "Proposed", "supersedes" : "v1 of this spec; `guestd/src/shell.rs` guest persistent-shell runtime (ADR 0038); `d2b-unsafe-local-helper` shell supervisor and wire protocol v2 (ADR 0044); `ShellOp`/`ShellOpResponse` seqpacket protocol (`d2b-contracts/src/public_wire.rs:1319,1394,1527`)", @@ -641,7 +641,7 @@ "ADR-046-telemetry-audit-and-support" ], "path" : "docs/specs/providers/ADR-046-provider-system-core.md", - "sha256" : "a858d857fee753b704d43a9c6b70c36fdc3093afe9b1cbb01820cb8e6cfcf172", + "sha256" : "cf73df14354c8e1d9a709091578a5e1bc90598f2bf8462faf257f7f9f6248ace", "specId" : "ADR-046-provider-system-core", "status" : "Proposed", "supersedes" : "Current `WorkloadProviderKind::UnsafeLocal`, host/User grouping helpers, `HostJson`/`VmRuntimeRow` host-management paths in `d2bd` and `d2b-realm-core`", @@ -666,7 +666,7 @@ "ADR-046-terminology-and-identities" ], "path" : "docs/specs/providers/ADR-046-provider-system-minijail.md", - "sha256" : "a7685f42c424d14f7da3a190ea40a24313c2a85200d949e688e0ab439318b9e4", + "sha256" : "2553d0727a0b9e48abf39b7cd2cfb7da33b299e658e49ab84dce4eab36ebf3f3", "specId" : "ADR-046-provider-system-minijail", "status" : "Proposed", "supersedes" : "Current `d2b-priv-broker` SpawnRunner, `d2b-core` minijail profile, and `d2bd` supervisor pidfd/wait paths for minijail-spawned processes", @@ -686,7 +686,7 @@ "ADR-046-telemetry-audit-and-support" ], "path" : "docs/specs/providers/ADR-046-provider-system-systemd.md", - "sha256" : "c6bddd9a2f6d1e21700764e9515789277e28680b938730315160d144a452d28d", + "sha256" : "fb726ed676381c6505247d28540ad131634ef8460f7747c6663ae6b2e8d475d7", "specId" : "ADR-046-provider-system-systemd", "status" : "Proposed", "supersedes" : "Current `d2b-unsafe-local-helper` systemd scope runtime; current `d2bd` `supervisor/` VM process management; broker `SpawnRunner` (systemd-owned roles)", @@ -706,7 +706,7 @@ "ADR-046-zone-routing" ], "path" : "docs/specs/providers/ADR-046-provider-transport-azure-relay.md", - "sha256" : "b6a12dc993e1600d703556525d1eb5305d50a135d519886bf7112eeb2212680b", + "sha256" : "a4c9e2ac8c1298adaf23baee6092d8f5e8d912d1076479dd73ade0561a6b72ec", "specId" : "ADR-046-provider-transport-azure-relay", "status" : "Proposed", "supersedes" : "`d2b-provider-relay` gateway-display relay path (`AcaWorkloadProvider` + `RelayProvider` traits in `d2b-realm-provider`); `d2b-gateway-runtime/src/bin/d2b-gateway-relay.rs`; `packages/d2b-provider-relay/src/lib.rs` as a first-party transport surface", @@ -730,7 +730,7 @@ "ADR-046-zone-routing" ], "path" : "docs/specs/providers/ADR-046-provider-transport-unix.md", - "sha256" : "52bb5ee85ee18ace64e51ac416527d9f41776d8896a2bc4fdb3dd6cc0e94ed58", + "sha256" : "cff79bd84e16247446d87af97c97218e4bab4e56af6478c4760015b2d447f048", "specId" : "ADR-046-provider-transport-unix", "status" : "Proposed", "supersedes" : "v1 of this dossier (ADR-046-provider-transport-unix v1; incorrect ownership model); `InheritedSocketTransport` SD_LISTEN_FDS path (`packages/d2b-session-unix/src/systemd.rs`)", @@ -751,7 +751,7 @@ "ADR-046-zone-routing" ], "path" : "docs/specs/providers/ADR-046-provider-transport-vsock.md", - "sha256" : "ab694961e18eebd3f9bdc71c0685f18f9e12e978b4fb66e9ece566daa376ab9e", + "sha256" : "55510f053296ae7cf2e3f64385bcfa3bfc4b0de3d0009c398480d806c119287d", "specId" : "ADR-046-provider-transport-vsock", "status" : "Proposed", "supersedes" : "`packages/d2bd/src/guest_control_vsock.rs` transport probe (→ allocator-issued endpoint via VsockEffectPort); `packages/d2b-host/src/vsock_relay_argv.rs` socat relay (→ native FramedVsockTransport in Provider service); `NativeVsock`/`CloudHypervisorVsock` in `d2b-session-unix/src/vsock.rs` (→ FramedVsockTransport implementing OwnedTransport adapted as service-Provider transport bridge)", @@ -795,7 +795,7 @@ "ADR-046-telemetry-audit-and-support" ], "path" : "docs/specs/providers/ADR-046-provider-volume-virtiofs.md", - "sha256" : "adbd75e715c4d02bb481dd93c34a282540ee4fd4274c9d2617c8ffc566522308", + "sha256" : "4bbd2516c4c401cb1d8e781e61009b554bf478125458e040d44489fd70744b09", "specId" : "ADR-046-provider-volume-virtiofs", "status" : "Proposed", "supersedes" : "`nixos-modules/processes-json.nix` virtiofsdRunner block; `nixos-modules/minijail-profiles.nix` virtiofsdProfiles; `packages/d2b-host/src/virtiofsd_argv.rs`; `ProcessRole::Virtiofsd` dag nodes in `packages/d2bd/src/supervisor/dag.rs`", @@ -848,7 +848,7 @@ "ADR-046-terminology-and-identities" ], "path" : "docs/specs/ADR-046-resource-api-and-authorization.md", - "sha256" : "cc43caa901707dbe4fc1eb1a53fd90a92805d3a55c252fd8ae1c52f08114e14b", + "sha256" : "dd880d5e09a673b44b9515d30733349455895f04aee8cfc7c327b158866a69ff", "specId" : "ADR-046-resource-api-and-authorization", "status" : "Proposed", "supersedes" : null, @@ -914,7 +914,7 @@ "ADR-046-terminology-and-identities" ], "path" : "docs/specs/ADR-046-resources-credential.md", - "sha256" : "50715a0027a760ea454c65245a5a8fa03d5ddd2696533c54c9308ea96d283086", + "sha256" : "1ab6ebd1af197848940dc9b9b386becd2ddc72295d1fc74e66b7001f0a4889e0", "specId" : "ADR-046-resources-credential", "status" : "Proposed", "supersedes" : "Current v3 `CredentialProvider` trait, `CredentialStatus`, `CredentialPlane` model in `d2b-realm-provider/src/credential.rs` and `provider.rs`", @@ -955,7 +955,7 @@ "ADR-046-terminology-and-identities" ], "path" : "docs/specs/ADR-046-resources-host-guest-process-user.md", - "sha256" : "8d6b786fd2a04cfa00fc8beff51bca3c62044adca265969dbf3899eb44847642", + "sha256" : "82d27ef45b6b832c9edabd69c0b187374a30de711e0cec15395b4f7595b63273", "specId" : "ADR-046-resources-host-guest-process-user", "status" : "Proposed", "supersedes" : "Current `ProcessRole`/`VmProcessDag` as the public process model; current Realm workload/unsafe-local/execution DTOs; `WorkloadProviderKind`/`IsolationPosture` posture enums", @@ -974,7 +974,7 @@ "ADR-046-terminology-and-identities" ], "path" : "docs/specs/ADR-046-resources-network.md", - "sha256" : "100155ed78c0370f01664a75eefc3571bdbea59769a72f3a1d333114382586bf", + "sha256" : "f42c4648e38858537711ba8444f72d5cba57a48856da1f1cf74c9edd7f7f56f7", "specId" : "ADR-046-resources-network", "status" : "Proposed", "supersedes" : "`d2b.envs.` and `d2b.realms..network` Nix surfaces (v3 reset)", @@ -1013,7 +1013,7 @@ "ADR-046-terminology-and-identities" ], "path" : "docs/specs/ADR-046-resources-zone-control.md", - "sha256" : "590419e3dce5c2e32c7e97da658aadaea6fedf151ff22ac59e44d365f19e1064", + "sha256" : "132a340c12df4efac7c289aff683d92ce905be7f092e3b5b4032bf88a7ee845d", "specId" : "ADR-046-resources-zone-control", "status" : "Proposed", "supersedes" : null, @@ -1080,7 +1080,7 @@ "ADR-046-zone-routing" ], "path" : "docs/specs/ADR-046-security-and-threat-model.md", - "sha256" : "96c400a2285e9c36c3ca064b726d483d91b465a5d128221d453f03bf90af2f0b", + "sha256" : "66a68f951b259290df1e3bf2b092c4049c0b40fdc79e1c7a798077142de4b80d", "specId" : "ADR-046-security-and-threat-model", "status" : "Proposed", "supersedes" : null, @@ -1207,7 +1207,7 @@ "ADR-046-zone-routing" ], "path" : "docs/specs/ADR-046-validation-and-delivery.md", - "sha256" : "8a872ac456acf0fd29550e366a340dc4c844056b8311d0af9952f5f9e1f58816", + "sha256" : "4f60fd968f973bb029ca5189a110c0d64fb6fe88a2cbf932e4af8cc06d152091", "specId" : "ADR-046-validation-and-delivery", "status" : "Proposed", "supersedes" : "This repository's current `AGENTS.md` \"Panel review\" phase-gate as the *sole* review mechanism for ADR 0046 work (extended, not replaced, per §12); ad hoc per-agent validation ordering for ADR 0046 implementation", @@ -1225,7 +1225,7 @@ "ADR-046-terminology-and-identities" ], "path" : "docs/specs/ADR-046-zone-routing.md", - "sha256" : "bdecfd3d3122039d6282769e649436123bddc788221ac7d7bb89cbb2d8be8788", + "sha256" : "9a480aefc284e0ed3549c638fe31dc9e369bfa467fdf1d1715cc704309cf3e71", "specId" : "ADR-046-zone-routing", "status" : "Proposed", "supersedes" : "`RealmEntrypointTable`/`EntrypointMode`/`RealmControllerPlacement` enum (→ compiler-only `parentZone` topology + ZoneLink transport + private Zone runtime bootstrap placement); `RouteTreeEngine`/`routing.rs` route contracts (adapted, not deleted); `RemoteNodeRegistry` (→ ZoneLink controller); `OperationRouter` (→ ZoneLinkIdempotencyKey in d2b-bus); `realm_access_resolver` module (→ ZoneEntrypointResolver); `realm-controllers.json`/`realm-identity.json` bundle artifacts (→ Nix-authored Zone settings/ZoneLink resources); `d2b.realms.*` Nix option namespace (→ `d2b.zones.*`); `WorkloadId` routing targets (→ typed Guest/Host resource refs); `CapabilitySet`-only routing authz (→ per-hop RBAC narrowing + allocator-issued capability scope)", diff --git a/docs/specs/ADR-046-work-items.json b/docs/specs/ADR-046-work-items.json index f9f0546e3..c39919cee 100644 --- a/docs/specs/ADR-046-work-items.json +++ b/docs/specs/ADR-046-work-items.json @@ -232,14 +232,14 @@ "dataMigration" : "Generate initial Roles/Bindings from Nix v3 config", "dependencyOwner" : "ADR046-api-001; authorization integrator", "destination" : "`packages/d2b-resource-api/src/authz.rs`, `packages/d2b-core-controller/src/rbac.rs`", - "detailedDesign" : "Role/RoleBinding schemas/evaluator/cache/revision invalidation, ComponentSession subject mapping, parent Zone access", + "detailedDesign" : "Role/RoleBinding schemas/evaluator/cache/revision invalidation, canonical resource/session verb enums including ZoneLink-scoped `relay`, ComponentSession subject mapping, parent Zone access, and independent per-hop relay plus target-verb admission", "integration" : "Every resource/runtime method invokes one native evaluator before structural checks", "removalProof" : "Legacy auth remains until every v3 route is covered", "reuseAction" : "adapt", "reuseSource" : null, "specId" : "ADR-046-resource-api-and-authorization", "specPath" : "docs/specs/ADR-046-resource-api-and-authorization.md", - "validation" : "Decision matrix/property tests; revocation/cache/outage/parent-child tests", + "validation" : "Decision matrix/property tests; closed-enum and relay-origin/scope rejection; relay-missing and target-verb-missing fail-closed vectors; revocation/cache/outage/parent-child tests", "workItemId" : "ADR046-api-002" }, { @@ -311,7 +311,7 @@ "integration" : "Registered with Zone core as a controller under `Provider/audio-pipewire`.", "removalProof" : "Supersedes `audio_dispatch.rs`; `d2bd` audio dispatch deleted after e2e parity test confirms", "reuseAction" : "adapt", - "reuseSource" : null, + "reuseSource" : "None directly; reconcile flow is new async controller", "specId" : "ADR-046-provider-audio-pipewire", "specPath" : "docs/specs/providers/ADR-046-provider-audio-pipewire.md", "validation" : "Fast hermetic `tests/audio_service_controller.rs`: neutral type/provider selection, foreign-provider ignore/deny, owner authority, bounded aggregate microphone status, projection ownerRef/import chain, core-only create/delete, projection no-PipeWire-open, revocation queue cancellation, and D091 propagation. `tests/audio_binding_controller.rs`: neutral type/provider selection, required same-Zone serviceRef, owner/projection dispatch, child Process/private Endpoint state machine, `Applied|Queued|MicQueueFull` status mapping, off/delete/revocation cancellation, grant changes, absence/failures/deletion. Conformance asserts no AudioBinding export/projection, no broker/pidfd/EphemeralProcess/Volume/User ops. ProviderDeployment integration remains fake-only and validates empty ProviderStateSet.", @@ -644,7 +644,7 @@ "reuseSource" : "Any useful main d2b-client/provider/session routing symbols named by implementation sub-items", "specId" : "ADR-046-componentsession-and-bus", "specPath" : "docs/specs/ADR-046-componentsession-and-bus.md", - "validation" : "Message isolation, route/auth revocation, fairness, reconnect, no direct-store path", + "validation" : "Message isolation; closed session-verb enum including `relay`; relay missing/target verb missing/provider self-assertion fail-closed vectors; route/auth revocation; fairness; reconnect; no direct-store path", "workItemId" : "ADR046-bus-001" }, { @@ -911,7 +911,7 @@ "integration" : "Nix build → per-Zone `resource-bundle.json` + global private artifact catalog → `d2b activation switch` → `d2b-core-controller` configuration service → resource API Create/Update/Delete → owner controllers → finalizer cascade → cleanup watcher → Zone status update", "removalProof" : "Old `nixos-modules/manifest.nix`, `nixos-modules/bundle-artifacts.nix` emitters removed only after `bundle-emit.nix` produces equivalent-or-superseding output and all downstream consumers of the old bundle format are migrated", "reuseAction" : "replace", - "reuseSource" : null, + "reuseSource" : "None (new implementation; no main `a1cc0b2d` reuse — this is the Nix/Zone side, not the CLI client side)", "specId" : "ADR-046-cli-and-operations", "specPath" : "docs/specs/ADR-046-cli-and-operations.md", "validation" : "Runtime integration: all CLI-visible cleanup/status/rollback/gc/audit tests (§CLI-visible tests for activation and cleanup), including no force-finalizer path; Nix unit and build tests owned by ADR-046-nix-configuration spec; canonical-example fixture validates every authored resource against the authoritative Host, Guest, and Provider schemas, resolves every artifact/resource reference, rejects unsupported fields, and proves the compiled Guest `spec` is field-for-field identical to the adjacent JSON", @@ -1237,14 +1237,14 @@ "dataMigration" : "None — audit/telemetry only; no runtime state import", "dependencyOwner" : "Depends on ADR046-cred-mi-001 and ADR046-mi-topology-001; owner: credential-managed-identity audit/telemetry implementation", "destination" : "packages/d2b-provider-credential-managed-identity/src/{audit.rs,telemetry.rs}; packages/d2b-contract-tests/tests/credential_audit.rs", - "detailedDesign" : "Shared audit/OTEL: emit audit records for all methods and controller events per §Audit; emit OTEL spans and metrics per §OTEL and metrics with no Zone/Credential/resource-name-derived label; report expiry as the minimum for each provider/placement aggregate; add `d2b_credential_imds_calls_total` counter with bounded `alias` label; enforce `contains_sensitive_shape` on all string fields in audit records and metric labels; add canary tests for `managed-identity-canary`, `credential_canary`, `imds-endpoint-canary`, Credential `metadata.name`, and Zone name in `canary.rs`. Primary reuse disposition: `adapt`. Preserved source-plan detail: adapt existing sensitive-shape guard and canary pattern to v3 audit, OTEL, and metric surfaces.", + "detailedDesign" : "Shared audit/OTEL: emit audit records for all methods and controller events per §Audit, with Credential identity represented only by the authorized bounded `resource_name_digest`; emit OTEL spans and metrics per §OTEL and metrics with no Credential resource name, ResourceRef, UID, digest, derived identity token, Zone/Credential/resource-name-derived label, or non-allowlisted OTEL Resource attribute; retain applicable generic collector-allowlisted Resource attributes (`d2b.zone`, `d2b.provider`, `d2b.component`, and service fields); report expiry as the minimum for each provider/placement aggregate; add `d2b_credential_imds_calls_total` counter with bounded `alias` label; enforce `contains_sensitive_shape` on all string fields in audit records and metric labels; add canary tests for `managed-identity-canary`, `credential_canary`, `imds-endpoint-canary`, Credential name/ref/UID/digest, and Zone name in `canary.rs`. Primary reuse disposition: `adapt`. Preserved source-plan detail: adapt existing sensitive-shape guard and canary pattern to v3 audit, OTEL, and metric surfaces.", "integration" : "Controller and agent service methods call audit/telemetry helpers; audit subsystem and OTEL exporters consume bounded redacted records; contract tests validate credential audit shape across providers.", "removalProof" : "None — audit/telemetry helpers are additive; no prior owner to remove", "reuseAction" : "adapt", "reuseSource" : null, "specId" : "ADR-046-provider-credential-managed-identity", "specPath" : "docs/specs/providers/ADR-046-provider-credential-managed-identity.md", - "validation" : "`packages/d2b-contract-tests/tests/credential_audit.rs`; managed-identity `canary.rs`; audit/OTEL unit tests structurally assert exact absence of `vm`, `zone`, `zone_id`, `zone_uid`, `credential_name`, and every resource-name-derived label key, reject Credential/Zone-name label canaries, preserve allowed OTEL resource identity attributes, reject identity span attributes, and reject sensitive shapes", + "validation" : "`packages/d2b-contract-tests/tests/credential_audit.rs` requires `resource_name_digest` in authorized audit records and rejects raw Credential name/ResourceRef/UID; managed-identity `canary.rs` and audit/OTEL unit tests structurally assert exact absence of `vm`, `zone`, `zone_id`, `zone_uid`, `credential_name`, `credential_ref`, `credential_uid`, `credential_digest`, `resource_name_digest`, and every resource-name-derived label key; reject Credential name/ref/UID/digest canaries from all OTEL Resource attributes, span attributes, and metric labels; preserve generic collector-allowlisted Resource attributes including `d2b.zone`, `d2b.provider`, `d2b.component`, and service fields; reject Zone-name span/label canaries and sensitive shapes; pass complete managed-identity metric/span frames through the shared collector ingress validator and prove that adding `d2b.credential.name` or any Credential identity key/value rejects the whole frame", "workItemId" : "ADR046-cred-mi-004" }, { @@ -1327,14 +1327,14 @@ "dataMigration" : "None — audit/telemetry only; no runtime state migration", "dependencyOwner" : "Dependency for ADR046-cred-ss-003; owner: credential-secret-service audit and telemetry implementation", "destination" : "packages/d2b-provider-credential-secret-service/src/{audit.rs,telemetry.rs}", - "detailedDesign" : "Audit/OTEL: emit audit records and OTEL spans/metrics for all credential service methods and controller events, with canary enforcement, expiry aggregated across user-agent leases, no Zone/Credential/resource-name-derived metric label, and no token/object-path/lease bytes in status, delivery outer headers, audit, metrics, spans, or logs. Full detail remains in `ADR-046-resources-credential` §Implementation work items. Primary reuse disposition: `adapt`. Preserved source-plan detail: adapt zero-secret invariant and canary test pattern to credential-secret-service audit/OTEL surfaces.", + "detailedDesign" : "Audit/OTEL: emit authorized bounded audit records with Credential identity represented only by `resource_name_digest`, and emit OTEL spans/metrics for all credential service methods and controller events with canary enforcement, expiry aggregated across user-agent leases, no Credential resource name, ResourceRef, UID, digest, derived identity token, Zone/Credential/resource-name-derived metric label, or non-allowlisted OTEL Resource attribute; retain applicable generic collector-allowlisted Resource attributes (`d2b.zone`, `d2b.provider`, `d2b.component`, and service fields); no token/object-path/lease bytes in status, delivery outer headers, audit, metrics, spans, or logs. Full detail remains in `ADR-046-resources-credential` §Implementation work items. Primary reuse disposition: `adapt`. Preserved source-plan detail: adapt zero-secret invariant and canary test pattern to credential-secret-service audit/OTEL surfaces.", "integration" : "Controller and service methods call audit/telemetry helpers; audit subsystem and OTEL exporters consume bounded event/span/metric records; canary tests verify every public observable surface stays secret-free.", "removalProof" : "None — audit/telemetry helpers are new; no prior owner to remove", "reuseAction" : "adapt", "reuseSource" : null, "specId" : "ADR-046-provider-credential-secret-service", "specPath" : "docs/specs/providers/ADR-046-provider-credential-secret-service.md", - "validation" : "Credential audit/OTEL tests from `ADR-046-resources-credential`; `tests/canary.rs` structurally asserts exact absence of `vm`, `zone`, `zone_id`, `zone_uid`, `credential_name`, and every resource-name-derived metric key plus Credential/Zone-name label canary absence while preserving allowed OTEL resource identity attributes and rejecting identity span attributes; `tests/delivery.rs` for credential-secret-service", + "validation" : "Credential audit/OTEL tests from `ADR-046-resources-credential` require `resource_name_digest` in authorized audit records and reject raw Credential name/ResourceRef/UID; `tests/canary.rs` structurally asserts exact absence of `vm`, `zone`, `zone_id`, `zone_uid`, `credential_name`, `credential_ref`, `credential_uid`, `credential_digest`, `resource_name_digest`, and every resource-name-derived metric key; Credential name/ref/UID/digest canaries are absent from all OTEL Resource attributes, span attributes, and metric labels; Zone-name canaries are absent from spans and labels while generic collector-allowlisted Resource attributes remain; complete secret-service metric/span frames pass the shared collector ingress validator, while adding `d2b.credential.name` or any Credential identity key/value rejects the whole frame; `tests/delivery.rs` covers credential-secret-service delivery", "workItemId" : "ADR046-cred-ss-006" }, { @@ -1447,14 +1447,14 @@ "dataMigration" : "None — full d2b 3.0 reset; no prior state to migrate", "dependencyOwner" : "`ADR046-credential-001`, `ADR046-credential-006`; audit/OTEL integrator", "destination" : "`packages/d2b-provider-credential-/src/audit.rs`, `telemetry.rs`; `packages/d2b-contract-tests/tests/credential_audit.rs`", - "detailedDesign" : "Implement audit record emission for all credential service methods and controller events using the field set defined in §Audit; implement OTEL span/metric emission using the closed semantic label set in §OTEL and metrics, with expiry reported as a provider/placement aggregate and no Credential/Zone/resource-name-derived label; implement `contains_sensitive_shape` check in all string fields of audit records and metric label values (adapted from `d2b-realm-provider/src/error.rs:contains_sensitive_shape`); add canary-enforcement tests that verify `\"secret-canary\"`, `\"entra-token-canary\"`, `\"managed-identity-canary\"`, Credential `metadata.name`, and Zone name values never appear in any metric label, span attribute, log line, or status field; identity remains only in allowed OTEL resource attributes and permitted audit fields", + "detailedDesign" : "Implement audit record emission for all credential service methods and controller events using the field set defined in §Audit, with Credential identity represented only by the authorized bounded `resource_name_digest`; implement OTEL span/metric emission using the closed semantic label set in §OTEL and metrics, with expiry reported as a provider/placement aggregate and no Credential resource name, ResourceRef, UID, digest, derived identity token, Zone/resource-name-derived label, or non-allowlisted OTEL Resource attribute; retain applicable generic collector-allowlisted Resource attributes (`d2b.zone`, `d2b.provider`, `d2b.component`, and service fields); implement `contains_sensitive_shape` checks in all string fields of audit records and metric label values (adapted from `d2b-realm-provider/src/error.rs:contains_sensitive_shape`); add canary-enforcement tests that verify `\"secret-canary\"`, `\"entra-token-canary\"`, `\"managed-identity-canary\"`, Credential name/ref/UID/digest canaries, and Zone name values never appear in any metric label, span attribute, log line, or status field, and that Credential identity canaries are also absent from OTEL Resource attributes", "integration" : "Credential controller and service handlers emit audit records and telemetry through Zone audit/OTEL paths", "removalProof" : "Not applicable", "reuseAction" : "adapt", "reuseSource" : null, "specId" : "ADR-046-resources-credential", "specPath" : "docs/specs/ADR-046-resources-credential.md", - "validation" : "Canary tests across all three Provider crates; audit record field-presence tests; structural metric descriptor tests assert exact absence of `vm`, `zone`, `zone_id`, `zone_uid`, `credential_name`, and every resource-name-derived key plus Credential/Zone-name canary absence; resource attribute tests preserve allowed `d2b.zone`/`d2b.credential.name` identity while span attribute tests reject identity and forbidden fields", + "validation" : "Canary tests across all three Provider crates; authorized audit record field-presence tests require `resource_name_digest` and reject raw Credential name/ResourceRef/UID; structural metric descriptor tests assert exact absence of `vm`, `zone`, `zone_id`, `zone_uid`, `credential_name`, `credential_ref`, `credential_uid`, `credential_digest`, `resource_name_digest`, and every resource-name-derived key; Credential name/ref/UID/digest canaries are absent from every OTEL Resource attribute, span attribute, and metric label; Zone-name canaries are absent from spans and labels while resource-attribute tests preserve the generic collector allowlist including `d2b.zone`, `d2b.provider`, `d2b.component`, and service fields; complete Credential metric/span frames pass the shared collector ingress validator, while adding `d2b.credential.name` or any Credential identity key/value rejects the whole frame", "workItemId" : "ADR046-credential-008" }, { @@ -1526,7 +1526,7 @@ "integration" : "`make test-drift` gains a row running this generator and `git diff --exit-code`; every wave's exit criteria (§4) require it committed as the wave's last commit", "removalProof" : "Not applicable", "reuseAction" : "adapt", - "reuseSource" : null, + "reuseSource" : "none required — this generator is specific to the `docs/specs/ADR-046-*` manifest shape", "specId" : "ADR-046-validation-and-delivery", "specPath" : "docs/specs/ADR-046-validation-and-delivery.md", "validation" : "Golden-fixture test against a small synthetic spec directory; drift test against the real `docs/specs/` tree", @@ -2016,15 +2016,15 @@ "currentSource" : "`packages/d2b-core/src/processes.rs`: `ProcessRole` (18 variants), `ProcessNode`, `RoleProfile`, `NamespaceSet`, `MountPolicy`, `CgroupPlacement`, `ReadinessPredicate`; `packages/d2b-core/src/minijail_profile.rs`: `MinijailProfile`, `UserNamespaceProfile`, `NamespaceSet`, `MountPolicy`, `BindMount`, `CgroupPlacement`; `packages/d2b-core/src/storage.rs`: `StoragePathSpec`, `AclGrant`, `CleanupPolicy`, `RepairPolicy`; `packages/d2b-realm-core/src/ids.rs`: `RealmId`, `WorkloadId` (→ GuestRef), `NodeId` (→ HostRef), `ProviderId` (→ Provider ResourceRef), `ExecutionId` (→ EphemeralProcess exec identity), `PrincipalId` (→ User ResourceRef), `AllocatorLeaseId`, `ControllerGenerationId`; `packages/d2b-realm-core/src/workload.rs`: `WorkloadProviderKind` (`LocalVm`→runtime-cloud-hypervisor Provider, `QemuMedia`→runtime-qemu-media Provider, `ProviderManaged`→ACA/relay Providers, `UnsafeLocal`→user-only Host `isolationPosture=\"none\"`), `IsolationPosture` (`VirtualMachine`→Guest, `ProviderManaged`→Guest, `UnsafeLocal`→Host `isolationPosture=\"none\"`), `WorkloadExecutionPosture`, `WorkloadSummary`, `WorkloadState`; `packages/d2b-realm-core/src/target.rs`: `RealmTarget`, `TargetName`, `RealmTargetParser` (current analog for `/` ResourceRef parsing); `packages/d2b-realm-core/src/realm.rs`: `RealmPath`, `RealmControllerPlacement`, `EntrypointMode` (current Zone hierarchy analog); `packages/d2b-core/src/workload_identity.rs`: `WorkloadIdentity`, `WorkloadTarget` (= `RealmTarget`), `WorkloadBackend`, `WorkloadRuntimeIntent` (identity/backend separation reuse model for Host/Guest ResourceType split)", "dataMigration" : "Full reset; no v2 resource import", "dependencyOwner" : "W0 shared contract root; `d2b-contracts`", - "destination" : "`packages/d2b-contracts/src/v3/host.rs`, `packages/d2b-contracts/src/v3/guest.rs`, `packages/d2b-contracts/src/v3/execution_policy.rs`, `packages/d2b-contracts/src/v3/process.rs`, `packages/d2b-contracts/src/v3/ephemeral_process.rs`, `packages/d2b-contracts/src/v3/user.rs`", - "detailedDesign" : "Implement strict typed Rust structs for HostSpec, GuestSpec, ExecutionPolicy, ExecutionSpec, SandboxSpec, BudgetSpec, MountSpec, NetworkUsageSpec, DeviceUsageSpec, EndpointSpec, TelemetrySpec, ProcessSpec, EphemeralProcessSpec, UserSpec; strict serde deny_unknown_fields; bounds/redaction on all string fields; stable error types; `UserSpec.osUsername` validated as OS username (1..255 bytes, no NUL/control/path-separator), not ResourceName grammar Primary reuse disposition: `adapt`. Preserved source-plan detail: extract and adapt.", + "destination" : "`packages/d2b-contracts/src/v3/host.rs`, `packages/d2b-contracts/src/v3/guest.rs`, `packages/d2b-contracts/src/v3/execution_policy.rs`, `packages/d2b-contracts/src/v3/process.rs`, `packages/d2b-contracts/src/v3/ephemeral_process.rs`, `packages/d2b-contracts/src/v3/user.rs`, `packages/d2b-contracts/src/v3/endpoint.rs`", + "detailedDesign" : "Implement strict typed Rust structs for HostSpec, GuestSpec, ExecutionPolicy, ExecutionSpec, SandboxSpec, BudgetSpec, MountSpec, NetworkUsageSpec, DeviceUsageSpec, EndpointSpec, EndpointConsumerPolicy, TelemetrySpec, ProcessSpec, EphemeralProcessSpec, UserSpec; EndpointSpec accepts exactly `owner\\|provider\\|zone`, and EndpointConsumerPolicy owns the only finer gates (`allowedSubjects`, `allowedProviderComponents`, `allowedOperations`) with no schema aliases; strict serde deny_unknown_fields; bounds/redaction on all string fields; stable error types; `UserSpec.osUsername` validated as OS username (1..255 bytes, no NUL/control/path-separator), not ResourceName grammar Primary reuse disposition: `adapt`. Preserved source-plan detail: extract and adapt.", "integration" : "Provider dossiers, controller descriptors, Zone resource API, Nix resource compiler", "removalProof" : "Old DTO types removed only after owning Resource/Provider integrations are live", "reuseAction" : "adapt", "reuseSource" : "`packages/d2b-contracts/src/v3/` as destination; no equivalent main source for Host/Guest/Process ResourceType contracts", "specId" : "ADR-046-resources-host-guest-process-user", "specPath" : "docs/specs/ADR-046-resources-host-guest-process-user.md", - "validation" : "Golden JSON vectors for each ResourceType; serde unknown-field rejection; bounds enforcement; `UserSpec.osUsername` OS-username validation (underscore allowed, NUL rejected)", + "validation" : "Golden JSON vectors for each ResourceType; Endpoint vectors accept only `owner\\|provider\\|zone`, reject every legacy/private visibility alias, reject scalar/array `consumerPolicy` aliases, and cover each canonical consumer allowlist; a docs drift test parses every `type: Endpoint` YAML/Nix example and fails unless visibility is canonical and finer gates occur only under `consumerPolicy`; serde unknown-field rejection; bounds enforcement; `UserSpec.osUsername` OS-username validation (underscore allowed, NUL rejected)", "workItemId" : "ADR046-exec-001" }, { @@ -2366,7 +2366,7 @@ "integration" : "None (standalone; no d2b-bus/ComponentSession/broker dependency)", "removalProof" : "Per SPIKE-01/SPIKE-02 Cleanup rows: deleted once `packages/d2b-resource-store-redb` and `packages/d2b-controller-toolkit/benches/reaction.rs` reproduce equal-or-stricter coverage", "reuseAction" : "adapt", - "reuseSource" : null, + "reuseSource" : "None (redb is a new external dependency; no main or v3 code implements it)", "specId" : "ADR-046-feasibility-and-spikes", "specPath" : "docs/specs/ADR-046-feasibility-and-spikes.md", "validation" : "SPIKE-01 metrics (1)-(5) and SPIKE-02 metrics (1) across all 3 concurrency profiles, per those entries' exact pass/fail thresholds", @@ -2381,7 +2381,7 @@ "integration" : "Consumes `ADR046-feasibility-001`'s hint-bus shape as its watch-receiver input", "removalProof" : "Deleted once `packages/d2b-controller-toolkit/benches/reaction.rs` and the Process Provider integration tests named by `ADR046-reconcile-003` reproduce equal-or-stricter coverage", "reuseAction" : "adapt", - "reuseSource" : null, + "reuseSource" : "None (the generic async controller loop is ADR-only per `ADR-046-resource-reconciliation`'s own current-code-fit row)", "specId" : "ADR-046-feasibility-and-spikes", "specPath" : "docs/specs/ADR-046-feasibility-and-spikes.md", "validation" : "SPIKE-03 metrics (1)-(3) and thresholds", @@ -2396,7 +2396,7 @@ "integration" : "None (standalone)", "removalProof" : "Deleted once `packages/d2b-provider-supervisor` and the volume-domain effect adapter each carry an equal-or-stricter in-tree blocking-adapter regression test", "reuseAction" : "adapt", - "reuseSource" : null, + "reuseSource" : "None", "specId" : "ADR-046-feasibility-and-spikes", "specPath" : "docs/specs/ADR-046-feasibility-and-spikes.md", "validation" : "SPIKE-04 heartbeat-jitter metric and threshold", @@ -2411,7 +2411,7 @@ "integration" : "None (standalone)", "removalProof" : "Deleted once the real Provider-toolkit crate (`ADR046-provider-001` destination) ships equal-or-stricter manifest-parsing/enumeration/workspace-policy coverage", "reuseAction" : "adapt", - "reuseSource" : null, + "reuseSource" : "None", "specId" : "ADR-046-feasibility-and-spikes", "specPath" : "docs/specs/ADR-046-feasibility-and-spikes.md", "validation" : "SPIKE-05 metrics (1)-(4) across 20 repeated randomized-order manifest loads", @@ -2441,7 +2441,7 @@ "integration" : "None between the two spikes beyond sharing the same fake resource-store oracle shape", "removalProof" : "Deleted per each spike's Cleanup row: the real `ADR-046-provider-state` work-item destination for SPIKE-09; `d2b-provider-volume-local`'s own `tests/`/`integration/` suite for SPIKE-10", "reuseAction" : "adapt", - "reuseSource" : null, + "reuseSource" : "None", "specId" : "ADR-046-feasibility-and-spikes", "specPath" : "docs/specs/ADR-046-feasibility-and-spikes.md", "validation" : "SPIKE-09 metrics (1)-(4); SPIKE-10 metrics (1)-(5), zero-tolerance on path leakage", @@ -2456,7 +2456,7 @@ "integration" : "None (standalone; requires a Linux host with `clone3`/`pidfd_open`, and optionally a running `systemd --user` instance behind the `systemd-user` feature)", "removalProof" : "Deleted once `packages/d2b-provider-system-systemd` and `packages/d2b-provider-system-minijail` each carry this exact shared conformance suite in their own `tests/`", "reuseAction" : "adapt", - "reuseSource" : null, + "reuseSource" : "None", "specId" : "ADR-046-feasibility-and-spikes", "specPath" : "docs/specs/ADR-046-feasibility-and-spikes.md", "validation" : "SPIKE-11 metrics (1)-(4), zero-tolerance on false adoption", @@ -2471,7 +2471,7 @@ "integration" : "None (standalone flake; no dependency on the main `flake.nix`)", "removalProof" : "Deleted once the real `nixos-modules/resources.nix` and `packages/xtask` `gen-schemas` implementation reproduce these metrics as part of `make test-drift`/`make test-flake`", "reuseAction" : "adapt", - "reuseSource" : null, + "reuseSource" : "None", "specId" : "ADR-046-feasibility-and-spikes", "specPath" : "docs/specs/ADR-046-feasibility-and-spikes.md", "validation" : "SPIKE-12 metrics (1)-(5), byte-for-byte reproducibility across 3 hermetic builds", @@ -2486,7 +2486,7 @@ "integration" : "None between the two spikes beyond sharing the same fixture command-table shape", "removalProof" : "Deleted per each spike's Cleanup row: the real `d2b` CLI crate's own discovery conformance test for SPIKE-13; the real CLI crate's workspace-policy/lint gate plus the real bootstrap sequence for SPIKE-14", "reuseAction" : "adapt", - "reuseSource" : null, + "reuseSource" : "None", "specId" : "ADR-046-feasibility-and-spikes", "specPath" : "docs/specs/ADR-046-feasibility-and-spikes.md", "validation" : "SPIKE-13 metrics (1)-(7); SPIKE-14 metrics (1)-(3), zero-tolerance on legacy-file access", @@ -2501,7 +2501,7 @@ "integration" : "Depends on and imports the fake shapes from `proofs/redb-resource-store-spike/`, `proofs/process-fastlaunch-spike/`, `proofs/effectport-async-spike/`, `proofs/provider-packaging-spike/`, `proofs/bus-routing-noise-spike/`, `proofs/transport-opaque-streams-spike/`, `proofs/credential-kk-e2e-spike/`, `proofs/provider-state-export-spike/`, `proofs/volume-policy-spike/`, and `proofs/process-provider-conformance-spike/`", "removalProof" : "Deleted once the real integration test suites named by the individual Provider dossiers (`integration/` per D059) collectively reproduce all three compositions against real, non-fake Zone/store/bus/broker code", "reuseAction" : "adapt", - "reuseSource" : null, + "reuseSource" : "None beyond what `-001` through `-009` already reuse", "specId" : "ADR-046-feasibility-and-spikes", "specPath" : "docs/specs/ADR-046-feasibility-and-spikes.md", "validation" : "SPIKE-15 metrics (1)-(4) across all three compositions", @@ -2816,7 +2816,7 @@ "integration" : "Provider dossiers, Nix resource compiler, resource store/API bind these canonical types", "removalProof" : "Old `d2b_core::host::NetEnv` and related types removed only after v3 resource API consumers use `d2b_contracts::v3::network` types", "reuseAction" : "adapt", - "reuseSource" : null, + "reuseSource" : "None from main; all from v3 baseline", "specId" : "ADR-046-resources-network", "specPath" : "docs/specs/ADR-046-resources-network.md", "validation" : "Golden JSON/CBOR vectors; CIDR overlap property tests; IfName collision and derivation determinism tests; default hostBlocklist enforcement; attachment index uniqueness; `User/net-local-controller` User resource lifecycle/readiness test: controller creates User Resource with `spec.osUsername = \"net-local-controller\"` (`ownerRef: Provider/network-local`); controller waits for User resource to reach `Ready` before proceeding; controller aborts with `ConfigVolumeReady=False/user-not-ready` if User resource is not Ready; verifies no numeric UID/GID appears in the Resource spec, authz check, or audit record; verifies that any diagnostic `uid`/`gid` in `User.status` is never used as an authorization input", @@ -2954,7 +2954,7 @@ "reuseSource" : null, "specId" : "ADR-046-nix-configuration", "specPath" : "docs/specs/ADR-046-nix-configuration.md", - "validation" : "nix-unit vectors for each ResourceType; ref-validation rejection vectors; malformed ref error shape; `parentZone` is missing on non-root/forbidden on local-root/unknown/self/cyclic/over-depth rejection; scalar module conflicts prove one parent; child-local ZoneLink `childZoneName` must equal its enclosing Zone key; a second uplink resource (even disabled) and a local-root uplink fail eval; missing/local-unresolved `transportProviderRef` fails eval; `managedBy` in spec rejected at eval; Zone spec is `{}` (no `parentZone`, `parentRef`, `retainedGenerations`, etc.)", + "validation" : "nix-unit vectors for each ResourceType; ref-validation rejection vectors; malformed ref error shape; resource/session verb closed-enum vectors accept `relay` only in `sessionVerbs`; relay wildcard/unbounded/Provider-self-asserted fixtures fail before activation while an exact core-generated ZoneLink fixture passes; Endpoint visibility accepts exactly `owner|provider|zone` and uses `consumerPolicy` for finer bounds; `parentZone` is missing on non-root/forbidden on local-root/unknown/self/cyclic/over-depth rejection; scalar module conflicts prove one parent; child-local ZoneLink `childZoneName` must equal its enclosing Zone key; a second uplink resource (even disabled) and a local-root uplink fail eval; missing/local-unresolved `transportProviderRef` fails eval; `managedBy` in spec rejected at eval; Zone spec is `{}` (no `parentZone`, `parentRef`, `retainedGenerations`, etc.)", "workItemId" : "ADR046-nix-001" }, { @@ -4447,14 +4447,14 @@ "dataMigration" : "Full d2b 3.0 reset; existing QEMU runner process state is not imported and launch state is rebuilt from resources", "dependencyOwner" : "P1; depends on ADR046-qemu-media-002, ADR046-qemu-media-005, ADR046-qemu-media-006, ADR046-qemu-media-007, ADR046-qemu-media-008, and ADR046-qemu-media-012; owner: runtime-qemu-media Process launch builder", "destination" : "packages/d2b-provider-runtime-qemu-media/src/controller/process_builder.rs", - "detailedDesign" : "Process spec builder and LaunchTicket assembly: build the canonical `qemu-media-runner` Process ResourceSpec from §10.1 and assemble the LaunchTicket with sealed fd slots for kvm, tap, media, and optional display fds. No raw path, argv, executable path, or principal appears in any public field. Primary reuse disposition: `adapt`. Preserved source-plan detail: adapt fd-slot and sandbox/budget concepts to canonical Process resources and sealed LaunchTickets; do not copy raw argv strings or path construction.", - "integration" : "Controller emits Process resources; system-minijail/Process Provider consumes the spec and LaunchTicket; QEMU runner receives only sealed fds; Endpoint resources represent QMP/serial connections.", + "detailedDesign" : "Process spec builder and LaunchTicket attachment resolution: build the canonical `qemu-media-runner` Process ResourceSpec from §10.1 and supply only opaque Network/Endpoint refs to Core's attachment resolver. Core, not the qemu controller, resolves authorized kvm, tap, media, and optional display attachments and seals their fd slots in the LaunchTicket. The qemu Provider/controller receives no broker operation or fd. No raw path, argv, executable path, or principal appears in any public field. Primary reuse disposition: `adapt`. Preserved source-plan detail: adapt sandbox/budget concepts to canonical Process resources and Core-sealed LaunchTickets; do not copy raw argv strings or path construction.", + "integration" : "Controller emits Process resources; Core resolves private attachments; system-minijail/Process Provider consumes the sealed LaunchTicket. The NetworkEffectPort adapter transfers its connected tap `OwnedFd` directly to ProviderSupervisor without ResourceAPI, ComponentSession, or d2b-bus serialization; QEMU receives only the declared child fd slot. Endpoint resources represent QMP/serial connections.", "removalProof" : "`ProcessRole::QemuMediaRunner` and raw qemu-media argv launch surfaces are removable after canonical Process specs and LaunchTickets cover every runner launch", "reuseAction" : "adapt", "reuseSource" : null, "specId" : "ADR-046-provider-runtime-qemu-media", "specPath" : "docs/specs/providers/ADR-046-provider-runtime-qemu-media.md", - "validation" : "`tests/process_spec_golden.rs`; `tests/launch_ticket_fd_slots.rs`; `tests/no_raw_argv_in_spec.rs`", + "validation" : "`tests/process_spec_golden.rs`; `tests/launch_ticket_fd_slots.rs`; `tests/launch_ticket_tap_fd_lifetime.rs`; `tests/no_controller_fd_or_broker_op.rs`; `tests/no_raw_argv_in_spec.rs`", "workItemId" : "ADR046-qemu-media-009" }, { @@ -4490,16 +4490,16 @@ { "currentSource" : "None — net-new v3 work; no pre-ADR45 baseline equivalent", "dataMigration" : "Full d2b 3.0 reset; no v2 state/config import", - "dependencyOwner" : "P1; depends on ADR046-qemu-media-002 and Provider config `networkProviderRef`; owner: runtime-qemu-media network dependency integration", + "dependencyOwner" : "P1; depends on ADR046-qemu-media-002, ADR046-network-005, and Provider config `networkProviderRef`; owner: runtime-qemu-media network dependency integration", "destination" : "packages/d2b-provider-runtime-qemu-media/src/controller/network.rs", - "detailedDesign" : "Network tap fd acquisition: call the `network-local` ComponentSession service to request a tap fd for a Guest MAC/bridge assignment and include the fd in the LaunchTicket. No bridge name or interface name appears in any public field. Primary reuse disposition: `create`. Preserved source-plan detail: net-new against the `network-local` ComponentSession contract.", - "integration" : "Guest networkAttachments drive requests to network-local; network-local returns a sealed tap fd; Process LaunchTicket carries the fd to the QEMU runner; Guest conditions report unavailable taps.", - "removalProof" : "None — tap fd acquisition through `network-local` is a new v3 dependency path", + "detailedDesign" : "Network attachment routing: project each Guest network attachment as an opaque `Network/` ref and condition only. The network-local controller declares the opaque semantic effect; its Core-owned NetworkEffectPort adapter maps it to `CreatePersistentTap`, then `SetBridgePortFlags`, and transfers the already-authorized connected `OwnedFd` directly to ProviderSupervisor for the QEMU Process LaunchTicket. The adapter and supervisor keep `FD_CLOEXEC` set on parent copies; only the declared child slot is made inheritable immediately before exec. On cancellation, ticket rejection, or spawn failure, all fd copies close before generation-fenced `DeletePersistentTap`, and the opaque realization is retained until deletion confirmation. The qemu Provider/controller receives no broker operation, fd, bridge name, or interface name, and the fd is never serialized through ResourceAPI, ComponentSession, or d2b-bus. Primary reuse disposition: `create`.", + "integration" : "Guest `networkAttachments` drive opaque dependency watches; ADR046-network-005 owns the NetworkEffectPort effect chain and ProviderSupervisor owns fd handoff. Process LaunchTicket carries the fd directly to QEMU; Guest conditions report authorization/resolution failures without exposing the fd or broker operation.", + "removalProof" : "None — Core-routed Network attachment resolution is a new v3 dependency path", "reuseAction" : "create", "reuseSource" : null, "specId" : "ADR-046-provider-runtime-qemu-media", "specPath" : "docs/specs/providers/ADR-046-provider-runtime-qemu-media.md", - "validation" : "`tests/tap_fd_acquisition.rs`; `tests/tap_fd_unavailable.rs`", + "validation" : "`tests/tap_launch_routing.rs` proves `CreatePersistentTap → SetBridgePortFlags → ProviderSupervisor LaunchTicket` ordering; `tests/tap_fd_lifetime.rs` proves CLOEXEC, single child ownership, and close-before-`DeletePersistentTap`; `tests/tap_fd_no_bus_serialization.rs` rejects fd/broker DTOs at the qemu controller boundary; `tests/tap_fd_unavailable.rs` covers authorization and resolution failure", "workItemId" : "ADR046-qemu-media-012" }, { @@ -4661,7 +4661,7 @@ "integration" : "`d2b host cutover preflight`/`plan` CLI commands consume this crate exclusively; no other crate re-implements inventory walking", "removalProof" : "Not applicable (net-new capability)", "reuseAction" : "adapt", - "reuseSource" : null, + "reuseSource" : "None from main; this is a v3-only cross-cutting concern with no main-branch equivalent", "specId" : "ADR-046-reset-and-cutover", "specPath" : "docs/specs/ADR-046-reset-and-cutover.md", "validation" : "`checkpoint_id` determinism property test; snapshot atomic-write crash-injection test; `cutover_preflight_refuses_dirty_flake_check`", @@ -4676,7 +4676,7 @@ "integration" : "Invoked by `preflight` before the snapshot is written; failures block `plan` from being offered", "removalProof" : "Not applicable", "reuseAction" : "adapt", - "reuseSource" : null, + "reuseSource" : "None from main", "specId" : "ADR-046-reset-and-cutover", "specPath" : "docs/specs/ADR-046-reset-and-cutover.md", "validation" : "`cutover-candidate-bundle-validation.nix`; trust-preflight rejection tests for each of digest/publisher/signature/deny/provenance/conformance failure modes", @@ -4691,7 +4691,7 @@ "integration" : "`d2b host cutover apply` orchestrates drain then disposition execution then hands off to Phase 5 (ADR046-reset-005)", "removalProof" : "Not applicable", "reuseAction" : "adapt", - "reuseSource" : null, + "reuseSource" : "None from main", "specId" : "ADR-046-reset-and-cutover", "specPath" : "docs/specs/ADR-046-reset-and-cutover.md", "validation" : "`cutover_apply_requires_exact_consent_phrase`; `cutover_drain_refuses_on_live_process`", @@ -4706,7 +4706,7 @@ "integration" : "Called by ADR046-reset-003's disposition executor for every Adopt row; writes to the state Volumes ADR046-device-tpm-004/ADR046-vl-004/ADR046-vl-006 define", "removalProof" : "Not applicable (the mechanism is retained permanently for later Full/Provider/Guest reset relocation use, not retired after first use)", "reuseAction" : "adapt", - "reuseSource" : null, + "reuseSource" : "None from main", "specId" : "ADR-046-reset-and-cutover", "specPath" : "docs/specs/ADR-046-reset-and-cutover.md", "validation" : "Crash-injection at every step boundary (Type 10 `cutover-crash-resume.nix`); TPM/durable-Volume Destroy-exclusion property test", @@ -4736,7 +4736,7 @@ "integration" : "Consumes Providers installed by ADR046-reset-005; hands off to ADR046-reset-007 (verification)", "removalProof" : "Not applicable", "reuseAction" : "adapt", - "reuseSource" : null, + "reuseSource" : "None from main", "specId" : "ADR-046-reset-and-cutover", "specPath" : "docs/specs/ADR-046-reset-and-cutover.md", "validation" : "Gateway-custody-boundary test asserting the parent inventory never contains a gateway-guest-internal path; child-local ZoneLink test asserting one uplink, self-matching `childZoneName`, and child-store ownership; compiler test asserting `parentZone` selects the allocator but appears only in sealed bootstrap state; no-reciprocal-parent-row/no-parent-handler test; child-local ZoneLink `Degraded/waiting-on-remote` non-blocking test", @@ -4751,7 +4751,7 @@ "integration" : "`d2b host cutover verify`/`doctor` CLI commands; consumed by the Phase 10 finalize gate table", "removalProof" : "Not applicable", "reuseAction" : "adapt", - "reuseSource" : null, + "reuseSource" : "None from main", "specId" : "ADR-046-reset-and-cutover", "specPath" : "docs/specs/ADR-046-reset-and-cutover.md", "validation" : "Injected-digest-mismatch test for TPM/durable-Volume verify checks; audit-genesis-cross-check test; `cutover-full-rehearsal.nix`", @@ -4781,7 +4781,7 @@ "integration" : "`d2b host cutover rollback`/`hold` CLI commands; consulted by ADR046-reset-003's disposition executor and ADR046-reset-008's finalize gate before every mutating step", "removalProof" : "Not applicable", "reuseAction" : "adapt", - "reuseSource" : null, + "reuseSource" : "None from main", "specId" : "ADR-046-reset-and-cutover", "specPath" : "docs/specs/ADR-046-reset-and-cutover.md", "validation" : "`cutover_rollback_window_closes_after_phase_5`; incident-hold-blocks-destructive-step test", @@ -4796,7 +4796,7 @@ "integration" : "Standalone from the cutover Phases 0-10 above; usable at any later time as a recovery/maintenance lever once a Zone exists", "removalProof" : "Not applicable", "reuseAction" : "adapt", - "reuseSource" : null, + "reuseSource" : "None from main", "specId" : "ADR-046-reset-and-cutover", "specPath" : "docs/specs/ADR-046-reset-and-cutover.md", "validation" : "`host_reset_scope_isolation`; `zone-provider-guest-reset-isolation.nix`; durable-Volume-preserved-by-default property test for both Provider and Guest scopes", @@ -5017,14 +5017,14 @@ "dataMigration" : "None — full d2b 3.0 reset; no prior state to migrate", "dependencyOwner" : "ADR046-routing-002, ADR046-routing-007 (from ComponentSession spec); d2b-bus owner", "destination" : "`packages/d2b-bus/src/zone_route.rs` (cross-Zone bus routing), `packages/d2b-bus/src/relay.rs` (per-hop relay handler)", - "detailedDesign" : "Cross-Zone routing path in d2b-bus: ZoneEntrypointResolver consumes sealed topology plus authenticated route projections → ZoneRouteEngine::decide_route → admitted ComponentSession established by each next-hop child's local ZoneLink; hop-counter decrement and enforcement; RBAC `relay` verb check at each intermediate hop; idempotency key namespace (full 6-tuple) in ZoneLinkIdempotencyKey; pinned reverse path tracking; cancellation forwarding; watch cursor forwarding and revision-expired handling; no-FD/credential structural rejection at serialization boundary. No parent route step performs Resource API Get/List/Watch on ZoneLink Primary reuse disposition: `adapt`. Preserved source-plan detail: extract and adapt.", + "detailedDesign" : "Cross-Zone routing path in d2b-bus: ZoneEntrypointResolver consumes sealed topology plus authenticated route projections → ZoneRouteEngine::decide_route → admitted ComponentSession established by each next-hop child's local ZoneLink; hop-counter decrement and enforcement; independent target-verb plus canonical ZoneLink-scoped `relay` checks at each intermediate hop; idempotency key namespace (full 6-tuple) in ZoneLinkIdempotencyKey; pinned reverse path tracking; cancellation forwarding; watch cursor forwarding and revision-expired handling; no-FD/credential structural rejection at serialization boundary. No parent route step performs Resource API Get/List/Watch on ZoneLink Primary reuse disposition: `adapt`. Preserved source-plan detail: extract and adapt.", "integration" : "ResourceClient → d2b-bus → ZoneLink CS → intermediate zone → target zone; cancel/watch/stream all use the same routing path", "removalProof" : "Old direct-dispatch and gateway-backed paths retired per bus routing parity", "reuseAction" : "adapt", "reuseSource" : "Same v3 baseline commit `b5ddbed6`", "specId" : "ADR-046-zone-routing", "specPath" : "docs/specs/ADR-046-zone-routing.md", - "validation" : "End-to-end K0→K1→K2 resource call; relay-denied/hop-limit/FD-rejection tests; idempotency namespace collision tests; cancellation delivery tests; watch resync tests", + "validation" : "End-to-end K0→K1→K2 resource call; relay-missing, target-verb-missing, wildcard/self-asserted relay, hop-limit, and FD-rejection tests; prove relay alone grants no CRUD/local lifecycle; idempotency namespace collision tests; cancellation delivery tests; watch resync tests", "workItemId" : "ADR046-routing-005" }, { @@ -5159,7 +5159,7 @@ "reuseSource" : null, "specId" : "ADR-046-zone-routing", "specPath" : "docs/specs/ADR-046-zone-routing.md", - "validation" : "Port inline registry lifecycle/drain/shutdown tests; add v3 ZonePath routing test; add RBAC relay-verb check at provider admission", + "validation" : "Port inline registry lifecycle/drain/shutdown tests; add v3 ZonePath routing test; prove provider admission cannot self-assert relay and each forward requires relay plus the target verb", "workItemId" : "ADR046-routing-014" }, { @@ -5182,14 +5182,14 @@ "dataMigration" : "None; v3 Zone service is new; no v2 realm-service compatibility", "dependencyOwner" : "ADR046-routing-007, ADR046-routing-002, ADR046-routing-004, ADR046-routing-012; Zone service owner", "destination" : "`packages/d2b-zone-routing/src/service.rs`", - "detailedDesign" : "Rename `RealmServiceServer` → `ZoneServiceServer`; service wire name `d2b.realm.v2.RealmService` → `d2b.zone.v3.ZoneService`; rename methods (bootstrap→zone-bootstrap, enroll→zone-enroll, resolve_route→resolve-zone-route, authorize_shortcut→authorize-zone-shortcut, revoke_shortcut→revoke-zone-shortcut, report_shortcut_close→report-zone-shortcut-close, inspect→zone-inspect) and add list/watch topology-projection methods. The read-only projection starts from the sealed sorted `{ childZone, parentZone }` compiler input and joins only authenticated, admitted `ZoneRouteEngine` route/projection status. It exposes no ZoneLink resource name, UID, spec, status, Provider ref, fingerprint, transport setting, or handle. Replace `RealmSessionAuthority` with Zone principal + RBAC binding; replace `BootstrapBinding` with allocator-issued PSK binding associated with the child's local ZoneLink; replace `EnrollmentBinding` with the corresponding KK enrollment record; add `relay` verb RBAC check per hop; adapt shortcut model to ZonePath addressing; `RealmServiceLimits` defaults preserved; `MAX_DISPATCH_IN_FLIGHT=64`, `SHUTDOWN_TIMEOUT=5s` preserved; `CredentialCustody::GatewayGuest` excluded (all ZoneLink sessions are direct KK) Primary reuse disposition: `adapt`. Preserved source-plan detail: copy and adapt.", + "detailedDesign" : "Rename `RealmServiceServer` → `ZoneServiceServer`; service wire name `d2b.realm.v2.RealmService` → `d2b.zone.v3.ZoneService`; rename methods (bootstrap→zone-bootstrap, enroll→zone-enroll, resolve_route→resolve-zone-route, authorize_shortcut→authorize-zone-shortcut, revoke_shortcut→revoke-zone-shortcut, report_shortcut_close→report-zone-shortcut-close, inspect→zone-inspect) and add list/watch topology-projection methods. The read-only projection starts from the sealed sorted `{ childZone, parentZone }` compiler input and joins only authenticated, admitted `ZoneRouteEngine` route/projection status. It exposes no ZoneLink resource name, UID, spec, status, Provider ref, fingerprint, transport setting, or handle. Replace `RealmSessionAuthority` with Zone principal + RBAC binding; replace `BootstrapBinding` with allocator-issued PSK binding associated with the child's local ZoneLink; replace `EnrollmentBinding` with the corresponding KK enrollment record; add independent target-verb plus canonical `relay` RBAC checks per forwarding hop; adapt shortcut model to ZonePath addressing; `RealmServiceLimits` defaults preserved; `MAX_DISPATCH_IN_FLIGHT=64`, `SHUTDOWN_TIMEOUT=5s` preserved; `CredentialCustody::GatewayGuest` excluded (all ZoneLink sessions are direct KK) Primary reuse disposition: `adapt`. Preserved source-plan detail: copy and adapt.", "integration" : "Zone runtime instantiates one `ZoneServiceServer` per Zone; d2b-bus routes `d2b.zone.v3.ZoneService` calls to this server; CLI uses `ZoneServiceClient` (from ADR046-routing-010) for topology list/inspect/watch, enrollment, and route resolution", "removalProof" : "`RealmServiceServer` on `d2b.realm.v2` retires after `ZoneServiceServer` handles all routing; display-session path migrates separately as part of Provider resource work", "reuseAction" : "adapt", "reuseSource" : null, "specId" : "ADR-046-zone-routing", "specPath" : "docs/specs/ADR-046-zone-routing.md", - "validation" : "Bootstrap/enroll/resolve-route/shortcut integration tests against a child-local fake ZoneLink; topology list/inspect/watch golden vectors contain exact `{ childZone, parentZone }` rows plus authenticated status and no ZoneLink fields; stale/withdrawn/unauthenticated projection tests; parent-store no-row/no-handler test; relay-verb RBAC test; KK enrollment test; shortcut ZonePath addressing test; concurrent dispatch bound test (64 in-flight)", + "validation" : "Bootstrap/enroll/resolve-route/shortcut integration tests against a child-local fake ZoneLink; topology list/inspect/watch golden vectors contain exact `{ childZone, parentZone }` rows plus authenticated status and no ZoneLink fields; stale/withdrawn/unauthenticated projection tests; parent-store no-row/no-handler test; relay-plus-target-verb RBAC tests; KK enrollment test; shortcut ZonePath addressing test; concurrent dispatch bound test (64 in-flight)", "workItemId" : "ADR046-routing-016" }, { @@ -5201,7 +5201,7 @@ "integration" : "Runs as part of `make test-lint`/`make test-rust`; every Provider crate's own redaction test (e.g. `tests/stream_redaction.rs`) is a per-Provider instance of the same closed list", "removalProof" : "Not applicable — this is a permanent gate, not a migration", "reuseAction" : "adapt", - "reuseSource" : null, + "reuseSource" : "None (new cross-cutting gate; no equivalent exists in main)", "specId" : "ADR-046-security-and-threat-model", "specPath" : "docs/specs/ADR-046-security-and-threat-model.md", "validation" : "Hermetic (`cargo test -p d2b-contract-tests policy_telemetry_redaction`); fails the build if a new Provider crate is added without a corresponding redaction test file under its `tests/`", @@ -5227,11 +5227,11 @@ "dataMigration" : "None — full d2b 3.0 reset; no prior state to migrate", "dependencyOwner" : "`ADR046-zone-control-004`/`ADR046-zone-control-005` (Role/RoleBinding implementation)", "destination" : "`packages/d2b-resource-store/tests/rbac_property.rs`", - "detailedDesign" : "Property test asserting, for a randomly generated Role/RoleBinding/request corpus: (1) no request whose payload sets a subject/role field ever changes the resolved `AuthenticatedSubjectContext.subjectRef`; (2) no non-core Role with a wildcard grant is ever admitted; (3) `scopeNarrowing` never widens beyond the referenced Role; (4) RoleBinding deletion never leaves an observable intermediate state under concurrent readers Primary reuse disposition: `adapt`. Preserved source-plan detail: extract and adapt.", + "detailedDesign" : "Property test asserting, for a randomly generated Role/RoleBinding/request corpus: (1) no request whose payload sets a subject/role field ever changes the resolved `AuthenticatedSubjectContext.subjectRef`; (2) no non-core Role with a wildcard grant is ever admitted; (3) `scopeNarrowing` never widens beyond the referenced Role; (4) RoleBinding deletion never leaves an observable intermediate state under concurrent readers; (5) `relay` is accepted only as the canonical ZoneLink-scoped session verb with core-generated or explicit admin-policy provenance and exact target bounds; and (6) every forwarding hop independently requires relay plus the target verb Primary reuse disposition: `adapt`. Preserved source-plan detail: extract and adapt.", "integration" : "Runs against the real redb-backed resource store test harness, not a mock", "removalProof" : "Not applicable", "reuseAction" : "adapt", - "reuseSource" : null, + "reuseSource" : "None beyond the verb-table adaptation already tracked by `ADR046-zone-control-004`", "specId" : "ADR-046-security-and-threat-model", "specPath" : "docs/specs/ADR-046-security-and-threat-model.md", "validation" : "Hermetic property test (`proptest`/`quickcheck`-style, minimum 10,000 cases per property)", @@ -5246,7 +5246,7 @@ "integration" : "`make test-fuzz`; a companion container test (`tests/integration/containers/zonelink-cross-zone.rs`) runs two real Zone runtime containers connected by a real ZoneLink and asserts the same property end to end over the wire, not just in the frame-serialization unit", "removalProof" : "Not applicable", "reuseAction" : "adapt", - "reuseSource" : null, + "reuseSource" : "None", "specId" : "ADR-046-security-and-threat-model", "specPath" : "docs/specs/ADR-046-security-and-threat-model.md", "validation" : "Fuzz corpus (`cargo fuzz run zonelink_frame -- -runs=1000000`, zero crashes); container test passes in `make test-integration`", @@ -5321,7 +5321,7 @@ "integration" : "`make test-rust`; a host-integration variant (`tests/host-integration/volume-marker-tamper.nix`) repeats the inode-swap scenario against the real broker-maintained marker root on a real filesystem", "removalProof" : "Not applicable", "reuseAction" : "adapt", - "reuseSource" : null, + "reuseSource" : "None new", "specId" : "ADR-046-security-and-threat-model", "specPath" : "docs/specs/ADR-046-security-and-threat-model.md", "validation" : "Hermetic + host/KVM fault-injection test; acceptance is 100% fail-closed across all three tamper scenarios", @@ -5336,7 +5336,7 @@ "integration" : "`make test-lint` (static scan) and `make test-rust` (dynamic property test)", "removalProof" : "Not applicable", "reuseAction" : "adapt", - "reuseSource" : null, + "reuseSource" : "None", "specId" : "ADR-046-security-and-threat-model", "specPath" : "docs/specs/ADR-046-security-and-threat-model.md", "validation" : "Hermetic; the dynamic test additionally runs as a canary-byte test (a unique random marker is embedded in the token and searched for across every observability surface)", @@ -5396,7 +5396,7 @@ "integration" : "`make test-rust` (CLI integration tests); a container test (`tests/integration/containers/support-bundle-quarantined.rs`) runs a real Zone with one quarantined Provider and asserts the bundle correctly reports `partial`", "removalProof" : "Not applicable", "reuseAction" : "adapt", - "reuseSource" : null, + "reuseSource" : "None", "specId" : "ADR-046-security-and-threat-model", "specPath" : "docs/specs/ADR-046-security-and-threat-model.md", "validation" : "Hermetic CLI test asserting no spec byte or `metadata.name` appears in a generated bundle; container test for the quarantined-Provider case", @@ -5426,7 +5426,7 @@ "integration" : "`make test-drift`", "removalProof" : "Not applicable — permanent gate", "reuseAction" : "adapt", - "reuseSource" : null, + "reuseSource" : "None", "specId" : "ADR-046-security-and-threat-model", "specPath" : "docs/specs/ADR-046-security-and-threat-model.md", "validation" : "Hermetic shell-script gate; a negative test adds a scratch Provider dossier missing a Security section and asserts the gate fails", @@ -5441,7 +5441,7 @@ "integration" : "`make test-integration` (requires podman, per `AGENTS.md` \"Local Layer 1 + container integration\")", "removalProof" : "Not applicable", "reuseAction" : "adapt", - "reuseSource" : null, + "reuseSource" : "None", "specId" : "ADR-046-security-and-threat-model", "specPath" : "docs/specs/ADR-046-security-and-threat-model.md", "validation" : "Container integration test; acceptance is zero successful attacks across all five attempted vectors", @@ -5456,7 +5456,7 @@ "integration" : "Run manually before each tagged release touching a cloud/hardware Provider, per the existing `tests/README.md` manual-tier convention", "removalProof" : "Not applicable", "reuseAction" : "adapt", - "reuseSource" : null, + "reuseSource" : "None", "specId" : "ADR-046-security-and-threat-model", "specPath" : "docs/specs/ADR-046-security-and-threat-model.md", "validation" : "Checklist sign-off recorded in the release's validation evidence, not a CI gate (matches `D2b_LIVE=1` manual-tier precedent in `AGENTS.md`)", @@ -7837,14 +7837,14 @@ "dataMigration" : "Initial Roles generated from Nix config; no v2 Role resource import", "dependencyOwner" : "ADR046-zone-control-001; ADR046-api-002", "destination" : "`packages/d2b-contracts/src/v3/role.rs`; `packages/d2b-core-controller/src/authz.rs`", - "detailedDesign" : "Role resource schema with rule schema from §5.3 and resolved bounds (32 rules, 16 resourceTypes per rule, 64 resourceNames, 32 executionRefs); verb enum; explicit wildcard enforcement; index builder; phase/conditions; `role-binding-drain` finalizer; Nix Role options; audit/OTEL instrumentation Primary reuse disposition: `adapt`. Preserved source-plan detail: extract and adapt (`verb_requires_admin()` verb table → Role verb enum; `DaemonAccessDecision` error types → Role admission errors; `MappedDaemonAccessPrincipal` → subject identity model).", + "detailedDesign" : "Role resource schema with rule schema from §5.3 and resolved bounds (32 rules, 16 resourceTypes per rule, 64 resourceNames, 32 executionRefs); separate closed resource/session verb enums including `relay`; core-generated ZoneLink ownership, exact adjacent-Zone enrollment selector, exact target bounds, and explicit admin-policy exception admission; explicit wildcard enforcement; index builder; phase/conditions; `role-binding-drain` finalizer; generated Nix Role option help; audit/OTEL instrumentation Primary reuse disposition: `adapt`. Preserved source-plan detail: extract and adapt (`verb_requires_admin()` verb table → Role verb enum; `DaemonAccessDecision` error types → Role admission errors; `MappedDaemonAccessPrincipal` → subject identity model).", "integration" : "Authorization evaluator (ADR046-api-002) reads Role index entries; core `authz` handler owns reconcile loop", "removalProof" : "`daemon-access` capability enum removed after RBAC Role engine covers all access decisions", "reuseAction" : "adapt", "reuseSource" : null, "specId" : "ADR-046-resources-zone-control", "specPath" : "docs/specs/ADR-046-resources-zone-control.md", - "validation" : "All §15.4 Role tests including the resolved bounds checks", + "validation" : "All §15.4 Role tests including closed-enum, relay origin/scope/target-verb, and resolved-bounds checks", "workItemId" : "ADR046-zone-control-004" }, { @@ -7882,14 +7882,14 @@ "dataMigration" : "Full reset; Nix realm options (`d2b.realms.*`) remain until purge wave", "dependencyOwner" : "ADR046-identities-002; ADR046-zone-control-001; ADR046-zone-control-002; ADR046-zone-control-003; ADR046-zone-control-004; ADR046-zone-control-005; ADR046-zone-control-009; ADR046-zone-control-010", "destination" : "`nixos-modules/options-zones.nix`; `nixos-modules/resources-zone-control.nix`; extended `nixos-modules/index.nix`", - "detailedDesign" : "`d2b.zones..*` Nix options for Zone/ZoneLink/Provider/Role/RoleBinding/Quota/EmergencyPolicy authoring; compiler-only scalar `parentZone` required on non-root Zones and forbidden on `local-root`; Nix eval-time validation of parent existence, one resolved parent, self/cycle rejection, 16-name ancestry depth, ResourceRefs, verb enums, digest format, subject types, child-local ZoneLink self-name/one-uplink/local transport-ref constraints, and the no-expiry RoleBinding model; canonical JSON serialization; generation-bound resource bundle output; the compiler seals the validated parent map into allocator bootstrap topology without emitting `parentZone` into `Zone.spec` or reciprocal resources Primary reuse disposition: `adapt`. Preserved source-plan detail: adapt (`providerType` submodule → Provider option submodule; label regex `^[a-z][a-z0-9-]*$` retained unchanged; `placement` option → Provider component placement; `d2b.realms.*` option namespace → `d2b.zones.*` option namespace).", + "detailedDesign" : "`d2b.zones..*` Nix options for Zone/ZoneLink/Provider/Role/RoleBinding/Quota/EmergencyPolicy authoring; compiler-only scalar `parentZone` required on non-root Zones and forbidden on `local-root`; Nix eval-time validation of parent existence, one resolved parent, self/cycle rejection, 16-name ancestry depth, ResourceRefs, separate resource/session verb enums (including session-only `relay`), relay origin/scope restrictions, digest format, subject types, child-local ZoneLink self-name/one-uplink/local transport-ref constraints, and the no-expiry RoleBinding model; generated help carries the relay semantics; canonical JSON serialization; generation-bound resource bundle output; the compiler seals the validated parent map into allocator bootstrap topology without emitting `parentZone` into `Zone.spec` or reciprocal resources Primary reuse disposition: `adapt`. Preserved source-plan detail: adapt (`providerType` submodule → Provider option submodule; label regex `^[a-z][a-z0-9-]*$` retained unchanged; `placement` option → Provider component placement; `d2b.realms.*` option namespace → `d2b.zones.*` option namespace).", "integration" : "Nix compiler → sealed allocator topology for `parentZone`; resource compiler → configuration publication handler → Zone store; bootstrap Provider records auto-generated", "removalProof" : "`nixos-modules/options-realms.nix` and related realm options removed only after Zone resource Nix integration is live", "reuseAction" : "adapt", "reuseSource" : null, "specId" : "ADR-046-resources-zone-control", "specPath" : "docs/specs/ADR-046-resources-zone-control.md", - "validation" : "nix-unit vectors for each Zone control type schema; cross-field constraint tests; rendered JSON contract tests (`make test-drift`)", + "validation" : "nix-unit vectors for each Zone control type schema; closed resource/session verb and relay restriction vectors; cross-field constraint tests; rendered JSON contract tests (`make test-drift`)", "workItemId" : "ADR046-zone-control-007" }, { @@ -7994,7 +7994,7 @@ "reuseSource" : null, "specId" : "ADR-046-resources-zone-control", "specPath" : "docs/specs/ADR-046-resources-zone-control.md", - "validation" : "All Phase 1 eval tests in §15.8 (`nix-eval-name-regex-enforced`, `nix-eval-verb-closed-enum`, `nix-eval-roleref-format`, `nix-eval-subject-type-restricted`, `nix-eval-no-duplicate-subjects`, `nix-eval-bootstrap-provider-rejected`, `nix-eval-provider-missing-artifact-id`, `nix-eval-artifact-id-not-in-catalog`, `nix-eval-artifact-wrong-type`, `nix-eval-artifact-id-format`, `nix-eval-credentialref-declared`, `nix-eval-dollar-key-rejected`, all five `nix-eval-parent-zone-*` vectors, `nix-eval-zonelink-child-name-mismatch-rejected`, `nix-eval-zonelink-second-uplink-rejected`, `nix-eval-zonelink-limits-maxpendingintents-bound`); drift test asserts the standard registry and generated option modules cover exactly all 19 canonical types; negative evals reject unknown strings, unqualified extensions, unsigned or uninstalled qualified types, and unknown `spec` fields; a positive fixture admits an installed signed qualified type and validates its strict generated schema", + "validation" : "All Phase 1 eval tests in §15.8 (`nix-eval-name-regex-enforced`, `nix-eval-verb-closed-enum`, `nix-eval-session-verb-closed-enum`, `nix-eval-relay-session-verb-known`, `nix-eval-roleref-format`, `nix-eval-subject-type-restricted`, `nix-eval-no-duplicate-subjects`, `nix-eval-bootstrap-provider-rejected`, `nix-eval-provider-missing-artifact-id`, `nix-eval-artifact-id-not-in-catalog`, `nix-eval-artifact-wrong-type`, `nix-eval-artifact-id-format`, `nix-eval-credentialref-declared`, `nix-eval-dollar-key-rejected`, all five `nix-eval-parent-zone-*` vectors, `nix-eval-zonelink-child-name-mismatch-rejected`, `nix-eval-zonelink-second-uplink-rejected`, `nix-eval-zonelink-limits-maxpendingintents-bound`); Phase 2 runs `nix-build-relay-scope-restricted`; drift test asserts the standard registry and generated option modules cover exactly all 19 canonical types; negative evals reject unknown strings, unqualified extensions, unsigned or uninstalled qualified types, and unknown `spec` fields; a positive fixture admits an installed signed qualified type and validates its strict generated schema", "workItemId" : "ADR046-zone-control-014" }, { diff --git a/docs/specs/providers/ADR-046-provider-credential-managed-identity.md b/docs/specs/providers/ADR-046-provider-credential-managed-identity.md index 9eef64cbd..3e0378983 100644 --- a/docs/specs/providers/ADR-046-provider-credential-managed-identity.md +++ b/docs/specs/providers/ADR-046-provider-credential-managed-identity.md @@ -901,7 +901,8 @@ spec: locality: host-local visibility: zone attachmentPolicy: component-session - consumerPolicy: same-zone-authorized + consumerPolicy: + allowedOperations: [resolve] lifecyclePolicy: recycle-with-producer status: readiness: Ready diff --git a/docs/specs/providers/ADR-046-provider-credential-secret-service.md b/docs/specs/providers/ADR-046-provider-credential-secret-service.md index 899f03adb..a19d89f72 100644 --- a/docs/specs/providers/ADR-046-provider-credential-secret-service.md +++ b/docs/specs/providers/ADR-046-provider-credential-secret-service.md @@ -621,7 +621,8 @@ spec: locality: host-local visibility: zone attachmentPolicy: component-session - consumerPolicy: same-zone-authorized + consumerPolicy: + allowedOperations: [resolve] lifecyclePolicy: recycle-with-producer status: readiness: Ready diff --git a/docs/specs/providers/ADR-046-provider-runtime-qemu-media.md b/docs/specs/providers/ADR-046-provider-runtime-qemu-media.md index 39f90eb99..5f387c562 100644 --- a/docs/specs/providers/ADR-046-provider-runtime-qemu-media.md +++ b/docs/specs/providers/ADR-046-provider-runtime-qemu-media.md @@ -747,7 +747,9 @@ spec: locality: host-local visibility: provider attachmentPolicy: launch-ticket-only - consumerPolicy: [Provider/runtime-qemu-media] + consumerPolicy: + allowedSubjects: [Provider/runtime-qemu-media] + allowedOperations: [resolve] lifecyclePolicy: recycle-with-producer --- apiVersion: resources.d2bus.org/v3 @@ -766,7 +768,9 @@ spec: locality: host-local visibility: provider attachmentPolicy: launch-ticket-only - consumerPolicy: [Provider/runtime-qemu-media] + consumerPolicy: + allowedSubjects: [Provider/runtime-qemu-media] + allowedOperations: [resolve] lifecyclePolicy: recycle-with-producer ``` From 2c3396b6ac82da14148c61d793182bcfbb7f4ff8 Mon Sep 17 00:00:00 2001 From: John Vicondoa Date: Fri, 24 Jul 2026 13:16:45 -0700 Subject: [PATCH 106/115] docs: harden credential authorization and telemetry Keep Credential identity confined to authorized bounded audit records, align service admission with canonical resource verbs and Role subresources, and narrow the Entra login Endpoint example. --- docs/specs/ADR-046-resources-credential.md | 151 ++++++++++++------ .../ADR-046-provider-credential-entra.md | 100 +++++++++--- 2 files changed, 181 insertions(+), 70 deletions(-) diff --git a/docs/specs/ADR-046-resources-credential.md b/docs/specs/ADR-046-resources-credential.md index 7be6ca7ef..893499a59 100644 --- a/docs/specs/ADR-046-resources-credential.md +++ b/docs/specs/ADR-046-resources-credential.md @@ -54,6 +54,18 @@ for those surfaces: - OTEL spans and metrics never label secret bytes, provider-internal identity, or token scope values. +Credential resource identity is non-secret but sensitive, high-cardinality +metadata. Among observable outputs, the only permitted destination for +`metadata.name`, the canonical Credential ResourceRef, the Credential UID, or +any digest derived from one of them is an authorization-controlled, bounded +Zone audit record. The audit schema below is stricter: it retains only +`resource_name_digest`, never the raw name, ResourceRef, or UID. Credential +identity is never copied into status (including `status.update` or +`status.provider`), errors, logs/Debug output, OTEL Resource attributes, span +attributes, or metric labels. This restriction does not remove identity from +the resource envelope or from authenticated internal routing/session bindings +that require the ResourceRef and UID and are not observable output surfaces. + The injected credential client/port in the Credential Provider process acquires the secret from the external service. Token delivery to the authorized consumer Provider Process uses the end-to-end Noise channel defined in @@ -286,6 +298,11 @@ MUST NOT appear in status. Implementation-specific observation belongs only in that implementation's `status.provider.details`; shared fields MUST NOT be duplicated there. +Credential identity remains in the resource envelope, not in status: +`metadata.name`, the Credential ResourceRef/UID, `resource_name_digest`, and any +other value derived from them MUST NOT be copied into the universal status +base, `status.update`, `status.resource`, or `status.provider.details`. + | Field | Rules | | --- | --- | | `leaseHandle` | Opaque, bounded (max 256 chars), non-secret handle assigned by the provider process; stable across refreshes within one rotation generation; suitable for d2b-bus method routing; never a token or partial token | @@ -432,40 +449,64 @@ condition and sets a bounded `requeue-at`. ## RBAC -The Credential ResourceType adds these standard resource verbs to the native -RBAC model: +The Credential ResourceType uses the ordinary resource verbs plus the canonical +Credential-specific resource verbs `use-credential` and `admin-credential`. +They are resource verbs in the native Role engine, not `sessionVerbs` and not +ad hoc service-method verbs: | Verb | Who | Meaning | | --- | --- | --- | | `get` | Any authorized subject | Read Credential metadata/spec/status (no secret bytes present) | | `list` | Any authorized subject | List Credentials matching filters | | `watch` | Any authorized subject | Watch Credential events | -| `create` | Deployer/system-core configuration controller | Create a new Credential resource | -| `update-spec` | Deployer/system-core configuration controller | Replace Credential spec | +| `create` | Deployer/system-core configuration controller | Create a new Credential resource; also requires `admin-credential` on subresource `admin` | +| `update-spec` | Deployer/system-core configuration controller | Replace Credential spec; also requires `admin-credential` on subresource `admin` | | `update-status` | Credential controller only | Update Credential status | | `update-finalizers` | Credential controller, consumerRef controller | Add/remove owned finalizers | -| `delete` | Deployer/system-core configuration controller | Request Credential deletion | -| `use-credential` | Consumer subject authorized via consumerRef | Invoke credential-bound service methods on `d2b.credential.v3` | +| `delete` | Deployer/system-core configuration controller | Request Credential deletion; also requires `admin-credential` on subresource `admin` | +| `use-credential` | Consumer subject authorized via consumerRef | Invoke an admitted credential operation under an exact `acquire-token`, `observe`, or `revoke` subresource | +| `admin-credential` | Authorized operator/configuration controller | Administer Credential lifecycle or an explicit identity reset under the exact `admin` subresource; it does not imply `create`, `update-spec`, or `delete` by itself | -`use-credential` is a runtime service verb evaluated by d2b-bus when a consumer -invokes a credential-bound method. It is checked against the same -Role/RoleBinding engine that governs resource verbs. The Role rule shape: +Credential Roles use the existing `subresources` rule field. They do not add an +`operationClasses` field. A rule granting either Credential-specific verb MUST +name exact, non-empty Credential subresources: ```yaml rules: - resourceTypes: [Credential] verbs: [use-credential] + subresources: [acquire-token, observe] resourceNames: [work-entra] zones: [dev] executionRefs: [Guest/work-vm] - operationClasses: [acquire-token, refresh-token] + sessionVerbs: [] + - resourceTypes: [Credential] + verbs: [admin-credential] + subresources: [admin] + resourceNames: [work-entra] + zones: [dev] + executionRefs: [] + sessionVerbs: [] ``` -`operationClasses` is a Credential-specific rule extension narrowing which -operation classes are authorized in the bind. An empty list means no -operation-class restriction beyond the resource spec's own `allowedOperations`. -The effective operation set is the intersection of the resource `allowedOperations` -and the Role `operationClasses`. +The closed admission mapping is: + +| Credential action | Required resource verb | Required Role `subresources` value | +| --- | --- | --- | +| `AcquireToken`, `RefreshToken`, `SignChallenge`, `BeginLogin` | `use-credential` | `acquire-token` | +| `Status`, `InspectMetadata`, `ObserveLogin` | `use-credential` | `observe` | +| `RevokeToken`, `CancelLogin` | `use-credential` | `revoke` | +| Resource create/spec update/delete and explicit identity reset | `admin-credential` in addition to the ordinary mutation verb | `admin` | + +For each service method, `spec.allowedOperations` remains the independent, +provider-capability ceiling and is checked for the exact operation class +(`acquire-token`, `refresh-token`, `revoke-token`, `sign-challenge`, or +`inspect-metadata`). Effective permission is the intersection of the exact +verb/subresource Role grant, `spec.allowedOperations`, `consumerRef`, scope, and +the structural Provider/component checks. An empty/wildcard subresource grant, +an unknown subresource, `use-credential/admin`, or +`admin-credential/acquire-token|observe|revoke` fails admission. Method names +such as `begin-login` are not RBAC verbs. Status ownership: only the Credential controller's exact registered process generation may call `UpdateStatus` on a Credential resource. @@ -492,14 +533,14 @@ Route key example: ### Service methods -| Method | Required operation class | Outer DTO fields | Token bytes | -| --- | --- | --- | --- | -| `Status` | none (inspect-metadata or RBAC get) | `CredentialStatusResponse`: leaseState, rotationGeneration, sourceVersion, expiresAtUnixMs, placementBinding | none | -| `AcquireToken` | `acquire-token` | `AcquireTokenResponse`: leaseHandle, sourceVersion, rotationGeneration, expiresAtUnixMs | raw token bytes in a dedicated sensitive ComponentSession record (see §Credential-delivery endpoint contract) | -| `RefreshToken` | `refresh-token` | `RefreshTokenResponse`: leaseHandle, sourceVersion, rotationGeneration, new expiresAtUnixMs | raw token bytes in a dedicated sensitive ComponentSession record | -| `RevokeToken` | `revoke-token` | `RevokeTokenResponse`: closed revocation result (Revoked or AlreadyRevoked), revokedAtUnixMs | none | -| `SignChallenge` | `sign-challenge` | `SignChallengeResponse`: outcome code | signature bytes in a dedicated sensitive ComponentSession record (same channel as token delivery) | -| `InspectMetadata` | `inspect-metadata` | `InspectMetadataResponse`: leaseState, rotationGeneration, sourceVersion, expiresAtUnixMs | none | +| Method | Required exact operation class | Required Role permission | Outer DTO fields | Token bytes | +| --- | --- | --- | --- | --- | +| `Status` | none | `use-credential/observe` (or ordinary authorized `get` for the base resource projection) | `CredentialStatusResponse`: leaseState, rotationGeneration, sourceVersion, expiresAtUnixMs, placementBinding | none | +| `AcquireToken` | `acquire-token` | `use-credential/acquire-token` | `AcquireTokenResponse`: leaseHandle, sourceVersion, rotationGeneration, expiresAtUnixMs | raw token bytes in a dedicated sensitive ComponentSession record (see §Credential-delivery endpoint contract) | +| `RefreshToken` | `refresh-token` | `use-credential/acquire-token` | `RefreshTokenResponse`: leaseHandle, sourceVersion, rotationGeneration, new expiresAtUnixMs | raw token bytes in a dedicated sensitive ComponentSession record | +| `RevokeToken` | `revoke-token` | `use-credential/revoke` | `RevokeTokenResponse`: closed revocation result (Revoked or AlreadyRevoked), revokedAtUnixMs | none | +| `SignChallenge` | `sign-challenge` | `use-credential/acquire-token` | `SignChallengeResponse`: outcome code | signature bytes in a dedicated sensitive ComponentSession record (same channel as token delivery) | +| `InspectMetadata` | `inspect-metadata` | `use-credential/observe` | `InspectMetadataResponse`: leaseState, rotationGeneration, sourceVersion, expiresAtUnixMs | none | The `Status`, `RevokeToken`, and `InspectMetadata` response DTOs are non-secret: they carry only opaque identifiers, outcome codes, and timestamps. `AcquireToken`, @@ -508,7 +549,9 @@ dedicated sensitive ComponentSession record (see §Credential-delivery endpoint contract). Every method: - rejects a request where the authenticated subject is not the declared - `consumerRef` (when consumerRef is set) and RBAC `use-credential` is denied; + `consumerRef` (when consumerRef is set); +- rejects before Provider/Endpoint dispatch unless the Role engine allows the + exact resource verb and mapped `subresources` value above; - rejects an operation class not in `spec.allowedOperations`; - returns a stable closed error code rather than provider-internal diagnostics; - carries operation/idempotency/correlation IDs from d2b-bus context; @@ -611,8 +654,9 @@ full binding before accepting records. d2b-bus performs the following checks before authorizing the route: -- RBAC `use-credential` for the authenticated consumer Provider subject, - Credential ResourceRef, and operation class; +- RBAC `use-credential` for the authenticated consumer Provider subject and + Credential ResourceRef under the exact mapped `acquire-token`, `observe`, or + `revoke` Role subresource; - `spec.consumerRef` matches the consumer Provider identity from the signed component descriptor; - `spec.allowedOperations` includes the operation class; @@ -638,8 +682,9 @@ traverse the standard ComponentSession/d2b-bus stack defined in identity evidence mapping the consumer to an exact Zone subject. 2. d2b-bus checks native RBAC `use-credential` for the authenticated subject, - target Credential ResourceRef, operation class, and current - Role/RoleBinding/Provider revisions. + target Credential ResourceRef, exact mapped Role subresource, and current + Role/RoleBinding/Provider revisions, then separately checks the exact + operation class against `spec.allowedOperations`. 3. For non-secret methods, d2b-bus routes the call to the exact credential provider process using an enrolled `Noise_KK_25519_ChaChaPoly_SHA256` @@ -826,7 +871,8 @@ Stable Credential-specific error codes: All error messages are bounded (max 240 UTF-8 chars), stripped of control characters, and must not contain token bytes, URLs, UUIDs, provider diagnostics, -host paths, or connection string shapes. +host paths, connection string shapes, a Credential resource name/ResourceRef/ +UID, or any digest derived from Credential identity. ## Audit @@ -834,11 +880,14 @@ Audit records for Credential operations: | Event | Fields retained | | --- | --- | -| Credential resource create/update/delete | Zone, subject digest, `resource_name_digest`, verb, revision result, authorization decision | -| `AcquireToken` | Zone, subject digest, `resource_name_digest`, operation class, `rotationGeneration`, outcome code, idempotency key digest | -| `RefreshToken` | Zone, subject digest, `resource_name_digest`, operation class, `rotationGeneration`, outcome code, idempotency key digest | -| `RevokeToken` | Zone, subject digest, `resource_name_digest`, operation class, `rotationGeneration`, revocation result code | -| `SignChallenge` | Zone, subject digest, `resource_name_digest`, operation class, outcome code (no signature bytes) | +| Credential resource create/update/delete | Zone, subject digest, `resource_name_digest`, ordinary mutation verb, `admin-credential/admin` decision, revision result | +| `AcquireToken` | Zone, subject digest, `resource_name_digest`, operation class, `use-credential/acquire-token` decision, `rotationGeneration`, outcome code, idempotency key digest | +| `RefreshToken` | Zone, subject digest, `resource_name_digest`, operation class, `use-credential/acquire-token` decision, `rotationGeneration`, outcome code, idempotency key digest | +| `RevokeToken` | Zone, subject digest, `resource_name_digest`, operation class, `use-credential/revoke` decision, `rotationGeneration`, revocation result code | +| `SignChallenge` | Zone, subject digest, `resource_name_digest`, operation class, `use-credential/acquire-token` decision, outcome code (no signature bytes) | +| `Status`, `InspectMetadata`, `ObserveLogin` | Zone, subject digest, `resource_name_digest`, `use-credential/observe` decision, bounded outcome code | +| `BeginLogin` | Zone, subject digest, `resource_name_digest`, `use-credential/acquire-token` decision, bounded outcome code (no challenge/UI bytes) | +| `CancelLogin` | Zone, subject digest, `resource_name_digest`, `use-credential/revoke` decision, bounded outcome code | | Rotation | Zone, `resource_name_digest`, trigger reason, old `rotationGeneration`, new `rotationGeneration`, outcome code | | Provider generation change revocation | Zone, `resource_name_digest`, policy applied, outcome code | @@ -848,7 +897,9 @@ authorization-controlled Zone audit stream and, for caller-initiated operations, after the authorization decision. Raw Credential name, ResourceRef, and UID are excluded even from these records. The digest is audit-only and is never copied to OTEL Resource attributes, span attributes, metric labels, logs, -collector diagnostics, or support summaries. +status, errors, collector diagnostics, or support summaries. Audit schemas cap +record size and field count; failed/unauthorized requests cannot use audit as an +identity oracle. Excluded from all audit records: token bytes, key material, passwords, bearer strings, provider-internal diagnostics, host paths, connection strings, @@ -1508,7 +1559,7 @@ workspace/package policy failure enforced by `make test-policy` (via | Path | Contents | | --- | --- | | `src/` | Provider implementation, controller, service handler, binary entry points, and all colocated unit tests (`#[cfg(test)]` modules within `src/` files). One binary per role declared in the dossier's "Process components" table. | -| `tests/` | Hermetic Cargo integration tests (`#[test]` in `tests/*.rs`, no external process or container): ResourceType/controller lifecycle, provider conformance (`d2b-provider-toolkit::conformance::check_provider_conformance`), fault injection (locked/unavailable/interaction-required/generation-mismatch/oversize), canary enforcement (secret/endpoint/object-path canaries absent from all output), delivery-session binding, and placement rejection tests. Run with `cargo test -p d2b-provider-credential-`. | +| `tests/` | Hermetic Cargo integration tests (`#[test]` in `tests/*.rs`, no external process or container): ResourceType/controller lifecycle, provider conformance (`d2b-provider-toolkit::conformance::check_provider_conformance`), fault injection (locked/unavailable/interaction-required/generation-mismatch/oversize), secret canary enforcement, Credential name/ResourceRef/UID/digest canaries absent from status/errors/logs and every OTEL Resource/span attribute/metric label (with only authorized bounded audit retaining `resource_name_digest`), delivery-session binding, and placement rejection tests. Run with `cargo test -p d2b-provider-credential-`. | | `integration/` | Heavier fixtures and scenarios invoked by existing test orchestration (`make test-integration`, `make test-host-integration`): container-backed Provider service, Host/Guest placement, cross-process d2b-bus routing, provider-system startup/restart/drain, and Nix-generation cleanup/rollback. Files are shell scripts, Nix expressions, or container specs consumed by `tests/integration/containers/` or `tests/host-integration/`; they are NOT run by `cargo test`. | | `README.md` | All sections listed in §Provider README required sections below. | @@ -1894,7 +1945,7 @@ config formalizes this as an `OpaqueAzureRef` with the same charset restriction. | Current anchor | `packages/d2b-realm-provider/src/credential.rs`, `provider.rs`; `packages/d2b-core/src/privileges.rs`, `static_invariants.rs`; `packages/d2b-core/src/realm_workloads_launcher.rs:LauncherMetadataInvariants.no_secrets_or_credentials`; `nixos-modules/assertions.nix` | | Evidence class | `CredentialProvider` trait with status/enrollment is `implemented-and-reachable`; three-plane opaque refs are `implemented-and-reachable`; `ProviderWorkloadIdentity::ManagedIdentity` in `ProviderGuestdBootstrapContract` is `implemented-and-reachable`; `LauncherMetadataInvariants.no_secrets_or_credentials` is `implemented-and-reachable`; v3 `d2b-userd` (`packages/d2b-userd/`) is a guest exec stub only (`test-only-or-preview`; no credential functionality); Credential ResourceType, lease model, operation classes, typed service methods, controller, reconciliation, and async loop are `ADR-only` | | Behavior retained | Zero secret bytes invariant (structurally enforced); `OpaqueAzureRef` charset/length validation; typed capability denial error shape; bounded/redacted error messages; injected-client pattern keeps secret material in the client process | -| Required delta | Credential ResourceType schema; opaque lease/typed operation service; three Credential Provider crates/controllers; d2b-bus routing; async reconciliation integration; Nix resource declaration/assertion; audit/OTEL | +| Required delta | Credential ResourceType schema; opaque lease/typed operation service; canonical `use-credential`/`admin-credential` Role admission through exact existing `subresources`; three Credential Provider crates/controllers; d2b-bus routing; async reconciliation integration; Nix resource declaration/assertion; audit/OTEL | | Reuse path | Copy/adapt three Credential Provider implementations and their test suites from main as named in each dossier; reuse `OpaqueAzureRef` directly from v3 `d2b-realm-provider/src/credential.rs`; adapt `SecretAccess`/privilege audit shape from `d2b-core/src/privileges.rs` | | Replacement/deletion | Old `CredentialProvider` trait removed only after all three v3 Providers have tested replacement controllers; old three-plane types remain in v3 baseline and are not removed until Credential resource integration is live | | Feasibility proof | Main proves three Provider implementations; each has fake-client test suites covering acquire/refresh/revoke/inspect, idempotency, locked/unavailable/interaction-required, canary enforcement, generation validation, and lease cardinality limits | @@ -1912,10 +1963,10 @@ config formalizes this as an `OpaqueAzureRef` with the same charset restriction. | Reuse source | main `a1cc0b2d`: `d2b-provider-credential-secret-service/src/lib.rs` types: `SecretServiceLeaseRequest`, `SecretServiceLeaseRef`, `SecretServiceLeaseGrant`, `SecretServiceLeaseInspection`, `SecretServiceLeaseRenewal`, `SecretServiceLeaseRevocation`, `SecretServiceLeaseState`; parallel entra/managed-identity types; `CredentialLease`, `CredentialLeaseRequest`, `CredentialLeaseState` from `d2b-contracts/src/v2_provider.rs` | | Reuse action | adapt | | Destination | `packages/d2b-contracts/src/v3/credential.rs` | -| Detailed design | Define `CredentialSpec`, `CredentialStatus`, `CredentialLeaseHandle` (opaque bounded newtype), `CredentialRotationPolicy`, `CredentialRevocationPolicy`, `CredentialScope`, `OperationClass` enum, `CredentialLeaseState`, `PlacementBinding`, `CredentialConditionType`, and all serde/validation/redaction helpers; reuse `OpaqueAzureRef` from v3 baseline directly; enforce zero-secret-bytes charset validation on all string fields at construction Primary reuse disposition: `adapt`. Preserved source-plan detail: extract and adapt. | +| Detailed design | Define `CredentialSpec`, `CredentialStatus`, `CredentialLeaseHandle` (opaque bounded newtype), `CredentialRotationPolicy`, `CredentialRevocationPolicy`, `CredentialScope`, `OperationClass` enum, `CredentialLeaseState`, `PlacementBinding`, `CredentialConditionType`, and all serde/validation/redaction helpers; reuse `OpaqueAzureRef` from v3 baseline directly; enforce zero-secret-bytes charset validation on all string fields at construction; prevent Credential name/ResourceRef/UID or any identity-derived digest from serializing into any status layer or typed error Primary reuse disposition: `adapt`. Preserved source-plan detail: extract and adapt. | | Integration | Credential controller, Provider dossier schemas, Nix compiler, and resource store all consume one canonical contract | | Data migration | Full d2b 3.0 reset; no v2 credential import | -| Validation | Schema golden vectors; charset/length tests; serde unknown-field rejection; `OpaqueAzureRef` round-trip and secret-shape rejection parity; `leaseHandle` and `sourceVersion` opaque newtype tests; status redaction tests | +| Validation | Schema golden vectors; charset/length tests; serde unknown-field rejection; `OpaqueAzureRef` round-trip and secret-shape rejection parity; `leaseHandle` and `sourceVersion` opaque newtype tests; status/error redaction tests with Credential name/ResourceRef/UID/digest canaries | | Removal proof | Old `CredentialProvider` trait and `CredentialStatus` enum removed only after all v3 Credential Provider controllers consume this contract | ### ADR046-credential-002 @@ -1928,10 +1979,10 @@ config formalizes this as an `OpaqueAzureRef` with the same charset restriction. | Reuse source | main `a1cc0b2d`: `packages/d2b-contracts/proto/v2/provider_credential.proto` method names | | Reuse action | adapt | | Destination | `packages/d2b-contracts/proto/v3/credential.proto`; `packages/d2b-credential-service/src/{service.rs, client.rs, server.rs}` | -| Detailed design | Define `d2b.credential.v3` protobuf service with methods: `Status`, `AcquireToken`, `RefreshToken`, `RevokeToken`, `SignChallenge`, `InspectMetadata`; each request carries `credential_ref`, `operation_class`, `operation_id`, `idempotency_key`, `requested_expiry_unix_ms`, `deadline_unix_ms`; `Status`, `RevokeToken`, and `InspectMetadata` responses carry only non-secret metadata (leaseHandle digest, rotationGeneration, sourceVersion, expiresAtUnixMs, state, outcome code); `AcquireToken`, `RefreshToken`, and `SignChallenge` responses additionally include a `delivery_session_params` field carrying the binding contract fields required to establish the end-to-end credential-delivery ComponentSession (see §Credential-delivery endpoint contract); the token bytes themselves travel in the separate Noise-encrypted delivery session, never in the outer DTO; strict unknown-field rejection; bounded message sizes; all record wrappers for delivery sessions must be zeroizing types | -| Integration | d2b-bus routes `d2b.credential.v3` service to the exact credential provider Process identified by `Credential.spec.providerRef`; RBAC checks `use-credential` verb before dispatch; for `AcquireToken`/`RefreshToken`/`SignChallenge`, bus additionally authorizes the credential-delivery endpoint route and forwards opaque Noise-encrypted delivery records without terminating or buffering them; bus never stores or inspects delivery record plaintext | +| Detailed design | Define `d2b.credential.v3` protobuf service with methods: `Status`, `AcquireToken`, `RefreshToken`, `RevokeToken`, `SignChallenge`, `InspectMetadata`; each request carries `credential_ref`, `operation_class`, `operation_id`, `idempotency_key`, `requested_expiry_unix_ms`, `deadline_unix_ms`; map methods to the existing Role `subresources` values (`acquire-token`, `observe`, `revoke`) under the canonical `use-credential` resource verb and map administrative lifecycle to `admin-credential/admin`, never to a new Role field or method-name verb; `Status`, `RevokeToken`, and `InspectMetadata` responses carry only non-secret metadata (leaseHandle digest, rotationGeneration, sourceVersion, expiresAtUnixMs, state, outcome code); `AcquireToken`, `RefreshToken`, and `SignChallenge` responses additionally include a `delivery_session_params` field carrying the binding contract fields required to establish the end-to-end credential-delivery ComponentSession (see §Credential-delivery endpoint contract); the token bytes themselves travel in the separate Noise-encrypted delivery session, never in the outer DTO; strict unknown-field rejection; bounded message sizes; all record wrappers for delivery sessions must be zeroizing types | +| Integration | d2b-bus routes `d2b.credential.v3` service to the exact credential provider Process identified by `Credential.spec.providerRef`; before dispatch RBAC checks the exact `use-credential` verb and mapped Role `subresources` value, then separately checks `spec.allowedOperations`; for `AcquireToken`/`RefreshToken`/`SignChallenge`, bus additionally authorizes the credential-delivery endpoint route and forwards opaque Noise-encrypted delivery records without terminating or buffering them; bus never stores or inspects delivery record plaintext | | Data migration | None — full d2b 3.0 reset; no prior state to migrate | -| Validation | Protocol golden vectors for each method; malformed/oversize rejection; `leaseHandle` opacity tests (secret-canary must not appear in outer DTO or delivery routing metadata); locked/unavailable/denied/expired state tests; delivery session binding contract round-trip; zeroizing record type unit tests; delivery channel never materialized in non-delivery method tests | +| Validation | Protocol golden vectors for each method; Role matrix for exact `acquire-token`, `observe`, and `revoke` subresources under `use-credential`, plus `admin-credential/admin` admission; deny empty/wildcard/unknown/mismatched subresources and any `operationClasses`/method-name-verb input before Provider dispatch; malformed/oversize rejection; `leaseHandle` opacity tests (secret-canary must not appear in outer DTO or delivery routing metadata); locked/unavailable/denied/expired state tests; delivery session binding contract round-trip; zeroizing record type unit tests; delivery channel never materialized in non-delivery method tests | | Removal proof | Old v2 `CredentialProviderService` proto removed only after all v3 callers migrate | ### ADR046-credential-003 @@ -1960,10 +2011,10 @@ config formalizes this as an `OpaqueAzureRef` with the same charset restriction. | Reuse source | main `a1cc0b2d`: `packages/d2b-provider-credential-entra/src/lib.rs` (full implementation); `src/tests.rs` (full test suite including `FakeEntraClient`, `credential_canary`/`endpoint_canary`, interaction-required, colocated-consumer, generation-mismatch tests) | | Reuse action | adapt | | Destination | `packages/d2b-provider-credential-entra/src/{lib.rs, controller.rs, service.rs, main.rs}`; `packages/d2b-provider-credential-entra/tests/{lifecycle.rs, conformance.rs, faults.rs, canary.rs, delivery.rs, placement.rs}`; `packages/d2b-provider-credential-entra/integration/{container-service.sh, guest-placement.nix, cleanup-rollback.sh}`; `packages/d2b-provider-credential-entra/README.md` (all §Provider README required sections) | -| Detailed design | Adapt `EntraCredentialProvider` and `EntraCredentialProviderFactory` to v3 service; replace v2 `AgentPlacementBinding` with v3 `PlacementBinding` enum (user-agent, guest-agent only; reject host-system); validate `tenantId` config field using `OpaqueAzureRef::parse` from v3 baseline `d2b-realm-provider/src/credential.rs` (note: current v3 source field is named via `AzureControlPlaneRef`; target field name is `tenantId`); retain `EntraCredentialClient` trait unchanged; map `EntraClientError::InteractionRequired` to `credential-provider-unavailable` (not denied); enforce `EntraCredentialOwner::ExactConsumer` so only the declared `consumerRef` may acquire Primary reuse disposition: `adapt`. Preserved source-plan detail: copy and adapt. | +| Detailed design | Adapt `EntraCredentialProvider` and `EntraCredentialProviderFactory` to v3 service; replace v2 `AgentPlacementBinding` with v3 `PlacementBinding` enum (user-agent, guest-agent only; reject host-system); validate `tenantId` config field using `OpaqueAzureRef::parse` from v3 baseline `d2b-realm-provider/src/credential.rs` (note: current v3 source field is named via `AzureControlPlaneRef`; target field name is `tenantId`); retain `EntraCredentialClient` trait unchanged; map `EntraClientError::InteractionRequired` to `credential-provider-unavailable` (not denied); enforce `EntraCredentialOwner::ExactConsumer` so only the declared `consumerRef` may acquire; enforce canonical Credential verb/subresource admission, provider-visible Endpoint plus exact consumer policy, and audit-only Credential identity as frozen in the dedicated Entra dossier Primary reuse disposition: `adapt`. Preserved source-plan detail: copy and adapt. | | Integration | User-domain or system-domain Process under Guest; d2b-bus routing; Credential controller | | Data migration | Full reset | -| Validation | **`src/` unit**: `EntraCredentialClient` trait API, `OpaqueAzureRef::parse` on `tenantId`, `EntraCredentialOwner::ExactConsumer` guard, `EntraClientState` transitions. **`tests/` Cargo integration**: `lifecycle.rs` (acquire/refresh/revoke/inspect with `FakeEntraClient`); `conformance.rs` (all conformance arms); `faults.rs` (interaction-required → unavailable, generation-mismatch, colocated-consumer rejection); `canary.rs` (`credential_canary` and `endpoint_canary` absent from every response and delivery record); `delivery.rs` (delivery-session binding, zeroizing, replay-safe); `placement.rs` (host-system placement rejected). **`integration/` fixtures**: `container-service.sh`; `guest-placement.nix` (user-domain and system-domain Process on Guest in runNixOSTest); `cleanup-rollback.sh`. | +| Validation | **`src/` unit**: `EntraCredentialClient` trait API, `OpaqueAzureRef::parse` on `tenantId`, `EntraCredentialOwner::ExactConsumer` guard, `EntraClientState` transitions. **`tests/` Cargo integration**: `lifecycle.rs` (acquire/refresh/revoke/inspect with `FakeEntraClient`); `conformance.rs` (all conformance arms, canonical Endpoint visibility/consumer policy, and exact Role verb/subresource mapping); `faults.rs` (interaction-required → unavailable, generation-mismatch, colocated-consumer and mismatched Role-subresource rejection); `canary.rs` (secret plus Credential name/ResourceRef/UID/digest canaries absent from status/errors/logs and every OTEL Resource/span attribute/metric label; authorized audit retains only `resource_name_digest`); `delivery.rs` (delivery-session binding, zeroizing, replay-safe); `placement.rs` (host-system placement rejected). **`integration/` fixtures**: `container-service.sh`; `guest-placement.nix` (user-domain and system-domain Process on Guest in runNixOSTest); `cleanup-rollback.sh`. | | Removal proof | Same as ADR046-credential-003 | ### ADR046-credential-005 @@ -1992,10 +2043,10 @@ config formalizes this as an `OpaqueAzureRef` with the same charset restriction. | Reuse source | main `a1cc0b2d`: `packages/d2b-provider-toolkit/src/conformance.rs` (provider conformance pattern); `packages/d2b-provider-toolkit/src/adapter.rs` (controller toolkit pattern) | | Reuse action | adapt | | Destination | `packages/d2b-provider-credential-/src/controller.rs`; `packages/d2b-contracts/src/v3/credential_controller.rs` | -| Detailed design | Implement Credential controller handler conforming to `ADR-046-resource-reconciliation` async loop; implement `reconcile`, `observe`, `finalize`, `drain`, and `health` handlers; implement rotation state machine (proactive/on-expiry/on-demand policies); implement `provider-revoke` finalizer execution with `revocation.onOwnerDelete` policy; implement provider-generation-change detection and revocation; implement `CredentialReady`, `RotationDue`, `ProviderUnavailable`, `LeaseRevoked` condition logic; implement bounded idempotency key derivation (Credential UID + rotationGeneration + operation class, no secret material); implement `observeInterval=30s` health check calling `InspectMetadata`; bounded retry/backpressure with typed `credential-rotation-failed` outcome; enforce `MAX_LOCAL_LEASES=256` per controller provider instance | +| Detailed design | Implement Credential controller handler conforming to `ADR-046-resource-reconciliation` async loop; implement `reconcile`, `observe`, `finalize`, `drain`, and `health` handlers; implement rotation state machine (proactive/on-expiry/on-demand policies); implement `provider-revoke` finalizer execution with `revocation.onOwnerDelete` policy; implement provider-generation-change detection and revocation; implement `CredentialReady`, `RotationDue`, `ProviderUnavailable`, `LeaseRevoked` condition logic; use controller Roles with exact `acquire-token`, `observe`, and `revoke` subresources under `use-credential` for service calls and structural status/finalizer ownership, never a Credential-specific Role field; implement bounded idempotency key derivation (Credential UID + rotationGeneration + operation class, no secret material); implement `observeInterval=30s` health check calling `InspectMetadata`; bounded retry/backpressure with typed `credential-rotation-failed` outcome; enforce `MAX_LOCAL_LEASES=256` per controller provider instance | | Integration | Provider controller Process → d2b-bus → `d2b.credential.v3` service in provider process → injected client/port; status updates through resource API; watch subscription on Credential, Provider, Host/Guest dependency types | | Data migration | None; v3 reset | -| Validation | Controller state-machine golden vectors; rotation-policy matrix (proactive/on-demand/on-expiry × success/locked/unavailable/expired); finalizer execution tests; provider-generation-change revocation tests; idempotency key derivation tests; observe-interval drift detection test; canary tests confirm zero secret bytes in all controller-written status fields | +| Validation | Controller state-machine golden vectors; rotation-policy matrix (proactive/on-demand/on-expiry × success/locked/unavailable/expired); exact Role-subresource admission matrix; finalizer execution tests; provider-generation-change revocation tests; idempotency key derivation tests; observe-interval drift detection test; canary tests confirm zero secret bytes and no Credential name/ResourceRef/UID/digest in any controller-written status or error | | Removal proof | Not applicable (new controller) | ### ADR046-credential-007 @@ -2008,9 +2059,9 @@ config formalizes this as an `OpaqueAzureRef` with the same charset restriction. | Reuse action | adapt | | Destination | `nixos-modules/options-resources.nix` (generic schema-derived resource options; not type-specific), `nixos-modules/activation-nixos-cleanup.nix` | | Detailed design | **(1) Schema-derived options and eval-time validation**: implement `d2b.zones..resources. = { type = "..."; spec = { ... }; }` as a generic attrset option; an optional `metadata` sub-attr may contain `ownerRef` and/or presentation `labels`/`annotations`. Nix option types, defaults, and inline docs for `spec` fields are generated from the committed `ResourceTypeSchema` JSON (`docs/reference/schemas/v3/credential.json`) and the signed Provider schema — no bespoke options module is maintained separately. `metadata.name` derives from the attr key; `metadata.zone` from the Zone attr key; `apiVersion` defaults to `resources.d2bus.org/v3`; `status`/`uid`/`generation`/`revision`/timestamps/`managedBy`/`configurationGeneration` are not authored. Core assigns `metadata.managedBy = "configuration"` and `metadata.configurationGeneration = ` to all configuration-managed resources at create/update time; these are never authored in Nix. Eval-time assertions (applied to all entries with `type = "Credential"`): `spec.providerRef` resolves a Provider in the same Zone whose `credentialDomains` includes `spec.scope.domainFilter` and whose `spec.artifactId` (a sibling of `spec.config` on the Provider resource, not inside `spec.config`) resolves an artifact catalog entry of `type = "provider"`; `spec.audience` charset (`^[A-Za-z0-9._:/@-]+$`, max 256); `spec.rotation.proactiveWindowMs < maxLeaseLifetimeMs / 2`; `spec.consumerRef`/`scope.executionRef`/`scope.userRef` resolve declared Zone resources; duplicate `(providerRef, executionRef, userRef, audience)` tuple rejected; `contains_sensitive_shape` on all string fields; Provider-specific placement constraints; `allowedOperations` ⊆ `providerRef.supportedOperations`. **(2) Canonical JSON and bundle emission**: render `spec` attr directly to `spec` object in canonical JSON (no field renames/re-nesting); `metadata` in output contains only derived `name`/`zone` and optionally Nix-authored `ownerRef`/`labels`/`annotations`; `apiVersion` is top-level, not inside `metadata`; `finalizers` is omitted from the Nix-rendered input (core manages finalizers, never accepts them from the bundle); no management labels are emitted by Nix; sort bundle by `(type, name)`; write to `/etc/d2b/zones//resource-bundle.json` with digest. **(2b) Artifact catalog emission**: derivation-valued inputs (`d2b.artifacts.`) are compiled separately into an integrity-pinned artifact catalog (`/etc/d2b/zones//artifact-catalog.json`) with its own digest header; each entry records `id`, `type`, `sha256`, and bounded closure metadata; store paths are private catalog implementation data absent from the resource bundle, status, audit, and logs; `activation-nixos` verifies both digests before any create/update; missing or wrong-type `artifactId` references fail the NixOS build. **(3) Build-time schema validation**: validate rendered JSON against `docs/reference/schemas/v3/credential.json` and Provider-specific schema; enforce `secretRef` fields use `Credential/` refs; enforce no store paths in any resource bundle or status output; drift gate (`make test-drift`) regenerates schemas with `cargo xtask gen-schemas` and asserts `git diff --exit-code`; Nix options module drift checked in the same gate. **(4) Generation transition and cleanup contract**: activation-nixos controller verifies SHA-256 digest of both resource bundle and artifact catalog, creates/updates desired-set resources, activates without blocking on cleanup, issues async Delete for absent configuration-managed resources (those with `metadata.managedBy = "configuration"`), sets Degraded/Cleanup=True on removed resources; retains up to `retainedConfigurationMax` (default 3, range 1..16) prior bundles; oldest prune when count exceeded; no time-based rollback window. **(5) Configuration-managed vs controller/API isolation**: `managedBy` and `ownerRef` are orthogonal; configuration-managed and API-created resources may each carry an optional same-Zone `ownerRef` and participate in owner cascade. Cleanup checks `metadata.managedBy = "configuration"` before issuing Delete; resources with `metadata.managedBy = "controller"` or `metadata.managedBy = "api"` are never deleted by this path; API-created resources persist until explicit Delete and are never generation-swept. | -| Integration | `activation-nixos` Provider creates/updates Credential resources from emitted envelopes; Credential controller `provider-revoke` finalizer handles cleanup Deletes; owner controller reconciles children of deleted configuration-managed Credentials | +| Integration | `activation-nixos` Provider creates/updates Credential resources from emitted envelopes with both the ordinary mutation permission and `admin-credential/admin`; Credential controller `provider-revoke` finalizer handles cleanup Deletes; owner controller reconciles children of deleted configuration-managed Credentials | | Data migration | None; v3 reset | -| Validation | **(eval/build)**: nix-unit golden JSON envelope for each example (spec shape, no management labels in Nix output, sort, digest); assertion-failure tests for secret-shaped audience, mismatched providerRef/domainFilter, proactiveWindow > half maxLifetime, duplicate binding tuple, unresolved refs; artifact catalog: assertion-failure for missing `artifactId`, wrong-type `artifactId`, duplicate catalog ID; bundle + artifact catalog digest round-trip; artifact catalog store-path absence from resource bundle and status; Provider-specific schema cross-check; `make test-drift` schema drift gate. **(runtime integration in `tests/host-integration/`)**: `credential-cleanup-basic` (removed resource reaches Deleted); `credential-cleanup-nonblocking` (activation Ready before cleanup finalizer finishes); `credential-cleanup-pending-status` (Cleanup=True on removed resource, PendingCleanup=True on Provider); `credential-cleanup-stalled` (Degraded stall detection and recovery); `credential-cleanup-controller-children-preserved` (ownerRef children cleaned by Credential controller); `credential-cleanup-no-dynamic-deletion` (controller-created Credential with `managedBy = "controller"` not deleted); `credential-retained-generation-count` (up to retainedConfigurationMax bundles retained; rollback re-creates from retained bundle; oldest pruned when count exceeded); `credential-bundle-digest-mismatch` (tampered bundle aborts activation). | +| Validation | **(eval/build)**: nix-unit golden JSON envelope for each example (spec shape, no management labels in Nix output, sort, digest); assertion-failure tests for secret-shaped audience, mismatched providerRef/domainFilter, proactiveWindow > half maxLifetime, duplicate binding tuple, unresolved refs; generated activation Role contains `admin-credential` with exact `subresources = [ "admin" ]` and no undeclared Role field; artifact catalog: assertion-failure for missing `artifactId`, wrong-type `artifactId`, duplicate catalog ID; bundle + artifact catalog digest round-trip; artifact catalog store-path absence from resource bundle and status; Provider-specific schema cross-check; `make test-drift` schema drift gate. **(runtime integration in `tests/host-integration/`)**: `credential-cleanup-basic` (removed resource reaches Deleted); `credential-cleanup-nonblocking` (activation Ready before cleanup finalizer finishes); `credential-cleanup-pending-status` (Cleanup=True on removed resource, PendingCleanup=True on Provider); `credential-cleanup-stalled` (Degraded stall detection and recovery); `credential-cleanup-controller-children-preserved` (ownerRef children cleaned by Credential controller); `credential-cleanup-no-dynamic-deletion` (controller-created Credential with `managedBy = "controller"` not deleted); `credential-retained-generation-count` (up to retainedConfigurationMax bundles retained; rollback re-creates from retained bundle; oldest pruned when count exceeded); `credential-bundle-digest-mismatch` (tampered bundle aborts activation). | | Removal proof | Not applicable (new module) | ### ADR046-credential-008 @@ -2022,8 +2073,8 @@ config formalizes this as an `OpaqueAzureRef` with the same charset restriction. | Current source | `packages/d2b-core/src/privileges.rs:SecretAccess` (implemented-and-reachable); `d2b-realm-provider/src/error.rs:ProviderDiagnostic`/`contains_sensitive_shape` (implemented-and-reachable); `packages/d2b-contract-tests/tests/policy_observability.rs` (reachable audit policy tests) | | Reuse action | adapt | | Destination | `packages/d2b-provider-credential-/src/audit.rs`, `telemetry.rs`; `packages/d2b-contract-tests/tests/credential_audit.rs` | -| Detailed design | Implement audit record emission for all credential service methods and controller events using the field set defined in §Audit, with Credential identity represented only by the authorized bounded `resource_name_digest`; implement OTEL span/metric emission using the closed semantic label set in §OTEL and metrics, with expiry reported as a provider/placement aggregate and no Credential resource name, ResourceRef, UID, digest, derived identity token, Zone/resource-name-derived label, or non-allowlisted OTEL Resource attribute; retain applicable generic collector-allowlisted Resource attributes (`d2b.zone`, `d2b.provider`, `d2b.component`, and service fields); implement `contains_sensitive_shape` checks in all string fields of audit records and metric label values (adapted from `d2b-realm-provider/src/error.rs:contains_sensitive_shape`); add canary-enforcement tests that verify `"secret-canary"`, `"entra-token-canary"`, `"managed-identity-canary"`, Credential name/ref/UID/digest canaries, and Zone name values never appear in any metric label, span attribute, log line, or status field, and that Credential identity canaries are also absent from OTEL Resource attributes | +| Detailed design | Implement audit record emission for all credential service methods and controller events using the field set defined in §Audit, with Credential identity represented only by the authorized bounded `resource_name_digest` after authorization and no identity-bearing record elicited by a denied request; implement OTEL span/metric emission using the closed semantic label set in §OTEL and metrics, with expiry reported as a provider/placement aggregate and no Credential resource name, ResourceRef, UID, digest, derived identity token, Zone/resource-name-derived label, or non-allowlisted OTEL Resource attribute; retain applicable generic collector-allowlisted Resource attributes (`d2b.zone`, `d2b.provider`, `d2b.component`, and service fields); implement `contains_sensitive_shape` checks in all string fields of audit records and metric label values (adapted from `d2b-realm-provider/src/error.rs:contains_sensitive_shape`); add canary-enforcement tests that verify `"secret-canary"`, `"entra-token-canary"`, `"managed-identity-canary"`, Credential name/ref/UID/digest canaries, and Zone name values never appear in any metric label, span attribute, log line, status field, error, or collector diagnostic, and that Credential identity canaries are also absent from OTEL Resource attributes | | Integration | Credential controller and service handlers emit audit records and telemetry through Zone audit/OTEL paths | | Data migration | None — full d2b 3.0 reset; no prior state to migrate | -| Validation | Canary tests across all three Provider crates; authorized audit record field-presence tests require `resource_name_digest` and reject raw Credential name/ResourceRef/UID; structural metric descriptor tests assert exact absence of `vm`, `zone`, `zone_id`, `zone_uid`, `credential_name`, `credential_ref`, `credential_uid`, `credential_digest`, `resource_name_digest`, and every resource-name-derived key; Credential name/ref/UID/digest canaries are absent from every OTEL Resource attribute, span attribute, and metric label; Zone-name canaries are absent from spans and labels while resource-attribute tests preserve the generic collector allowlist including `d2b.zone`, `d2b.provider`, `d2b.component`, and service fields; complete Credential metric/span frames pass the shared collector ingress validator, while adding `d2b.credential.name` or any Credential identity key/value rejects the whole frame | +| Validation | Canary tests across all three Provider crates; authorized audit record field-presence tests require `resource_name_digest` and reject raw Credential name/ResourceRef/UID, while denied-request tests emit no identity-bearing audit; structural metric descriptor tests assert exact absence of `vm`, `zone`, `zone_id`, `zone_uid`, `credential_name`, `credential_ref`, `credential_uid`, `credential_digest`, `resource_name_digest`, and every resource-name-derived key; Credential name/ref/UID/digest canaries are absent from every status field, error, log/Debug line, collector diagnostic, OTEL Resource attribute, span attribute, and metric label; Zone-name canaries are absent from spans and labels while resource-attribute tests preserve the generic collector allowlist including `d2b.zone`, `d2b.provider`, `d2b.component`, and service fields; complete Credential metric/span frames pass the shared collector ingress validator, while adding `d2b.credential.name` or any Credential identity key/value rejects the whole frame | | Removal proof | Not applicable | diff --git a/docs/specs/providers/ADR-046-provider-credential-entra.md b/docs/specs/providers/ADR-046-provider-credential-entra.md index 296981eda..693654b72 100644 --- a/docs/specs/providers/ADR-046-provider-credential-entra.md +++ b/docs/specs/providers/ADR-046-provider-credential-entra.md @@ -218,9 +218,12 @@ spec: purpose: credential-entra.d2bus.org/entra-login-token serviceFingerprint: credential-entra.d2bus.org/EntrablauLoginTokenService/v1 locality: guest-local - visibility: zone + visibility: provider attachmentPolicy: component-session consumerPolicy: + allowedSubjects: + - Provider/credential-entra + - Provider/runtime-azure-container-apps allowedOperations: [resolve] lifecyclePolicy: recycle-with-producer status: @@ -236,20 +239,29 @@ TPM paths, credential file paths, access tokens, refresh tokens, or user PII. Endpoint resolution returns an authorized launch ticket or fails closed with `endpoint-resolve-denied`. +The example deliberately uses the canonical `provider` visibility value. +`owner` would exclude the exact external consumer Provider, while `zone` would +make every same-Zone subject a visibility candidate. `consumerPolicy` then +narrows resolution to the orchestration Provider and the Credential's exact +consumer Provider; ordinary Role/RoleBinding authorization must also allow +`resolve`. Admission accepts only `owner|provider|zone`, denies unknown +visibility aliases, and requires every present `consumerPolicy` dimension to +match. + ### 4.4 Typed service methods Service fingerprint: `credential-entra.d2bus.org/EntrablauLoginTokenService/v1`. All methods are ComponentSession methods. The interactive byte/UI stream is a named stream on the authenticated session, not status text. -| Method | Direction | Secret output? | Purpose | -| --- | --- | --- | --- | -| `BeginLogin` | request/reply + named stream | No status secret; UI bytes stay on stream | Start or resume an Entrablau interactive login session inside `identityGuestRef`. Opens optional named stream `credential-entra.d2bus.org/login-ui`. | -| `ObserveLogin` | request/reply | No | Return bounded non-secret observation for `interactionState`, session generation, deadline, and challenge metadata. | -| `CancelLogin` | request/reply | No | Cancel a pending login session by generation; idempotent. | -| `AcquireAccessTokenLease` | request/reply + sensitive KK record | Yes, only in KK record to consumer | Issue an on-demand access-token lease for `audience`/operation; outer reply carries metadata only. | -| `RevokeAccessTokenLease` | request/reply | No | Revoke a non-secret lease handle or mark it unusable. | -| `InspectAccessTokenLease` | request/reply | No | Return bounded non-secret lease state. | +| Method | Required Role permission | Direction | Secret output? | Purpose | +| --- | --- | --- | --- | --- | +| `BeginLogin` | `use-credential/acquire-token` | request/reply + named stream | No status secret; UI bytes stay on stream | Start or resume an Entrablau interactive login session inside `identityGuestRef`. Opens optional named stream `credential-entra.d2bus.org/login-ui`. | +| `ObserveLogin` | `use-credential/observe` | request/reply | No | Return bounded non-secret observation for `interactionState`, session generation, deadline, and challenge metadata. | +| `CancelLogin` | `use-credential/revoke` | request/reply | No | Cancel a pending login session by generation; idempotent. | +| `AcquireAccessTokenLease` | `use-credential/acquire-token` | request/reply + sensitive KK record | Yes, only in KK record to consumer | Issue an on-demand access-token lease for `audience`/operation; outer reply carries metadata only. | +| `RevokeAccessTokenLease` | `use-credential/revoke` | request/reply | No | Revoke a non-secret lease handle or mark it unusable. | +| `InspectAccessTokenLease` | `use-credential/observe` | request/reply | No | Return bounded non-secret lease state. | `BeginLogin`, `ObserveLogin`, and `CancelLogin` are the Credential base interactive-login operations. The service may conduct browser, device, desktop, @@ -284,7 +296,8 @@ It never duplicates base status fields. ### 5.2 `BeginLogin` 1. The caller must be authorized for the `Credential/` by `consumerRef`, - scope, RBAC, and same-Zone ResourceRefs. + scope, the exact `use-credential/acquire-token` Role verb/subresource pair, + and same-Zone ResourceRefs. 2. The credential-entra helper opens an authenticated ComponentSession to `loginEndpointRef` inside `identityGuestRef`. 3. `BeginLogin` binds the Credential UID/generation, `identityGuestRef`, @@ -404,14 +417,20 @@ Credential status may contain only bounded non-secret observations: | Surface | Allowed | Forbidden | | --- | --- | --- | -| `Credential.status.resource` | interaction state, login session generation, login deadline, bounded challenge metadata, lease state, expiry/issued timestamps | tokens, refresh tokens, device codes, login URLs, cookies, account names, tenant-private IDs, PII | -| `Credential.status.provider.details` | endpoint generation, identity Guest generation digest, retry counters, closed error/outcome enums, non-authorizing lease handle digests | token bytes, Entra response bodies, MSAL cache entries, filesystem paths, DBus names, browser profile paths | -| Audit/OTEL/logs | operation class, result code, ResourceRef, generation, digest of route/session binding | token/refresh/cookie/device code bytes, login URL, user PII, cloud response bodies, host paths | +| `Credential.status.resource` | interaction state, login session generation, login deadline, bounded challenge metadata, lease state, expiry/issued timestamps | Credential name/ResourceRef/UID/identity digest; tokens, refresh tokens, device codes, login URLs, cookies, account names, tenant-private IDs, PII | +| `Credential.status.provider.details` | Endpoint generation, identity Guest generation digest, retry counters, closed error/outcome enums, non-authorizing lease handle digests | Credential name/ResourceRef/UID/identity digest; token bytes, Entra response bodies, MSAL cache entries, filesystem paths, DBus names, browser profile paths | +| Authorized bounded Zone audit | operation class, RBAC verb/subresource, result code, generation, and base `resource_name_digest` after authorization | raw Credential name/ResourceRef/UID; token/refresh/cookie/device-code bytes, login URL, user PII, cloud response bodies, host paths | +| OTEL Resource attributes | only applicable generic collector-allowlisted attributes: `d2b.zone`, `d2b.provider`, `d2b.component`, `service.name`, `service.namespace`, `service.version` | Credential name/ResourceRef/UID/identity digest and every Credential-derived attribute | +| Span attributes, metric labels, logs/Debug, status, and errors | closed provider/operation/outcome values that do not identify a Credential | Credential name/ResourceRef/UID/identity digest; route/session-binding digests derived from Credential identity; all secret/private values forbidden above | The zero-secret invariant applies across spec, status, resource store, audit, OTEL, logs, debug output, process descriptors, launch tickets, Endpoint spec/status, and outer RPC DTOs. Secret bytes are present only inside the Entrablau Guest and, for access tokens, inside the end-to-end KK record and exact consumer process. +Credential identity is a separate observability restriction: it remains in the +resource envelope and authenticated internal routing/session bindings, but +among observable outputs only the authorized bounded audit record may contain +it, and that record uses `resource_name_digest` rather than raw identity. --- @@ -449,9 +468,13 @@ Endpoint exported by that Guest. d2b core does not import the sibling flake. purpose = "credential-entra.d2bus.org/entra-login-token"; serviceFingerprint = "credential-entra.d2bus.org/EntrablauLoginTokenService/v1"; locality = "guest-local"; - visibility = "zone"; + visibility = "provider"; attachmentPolicy = "component-session"; consumerPolicy = { + allowedSubjects = [ + "Provider/credential-entra" + "Provider/runtime-azure-container-apps" + ]; allowedOperations = [ "resolve" ]; }; lifecyclePolicy = "recycle-with-producer"; @@ -490,7 +513,11 @@ Endpoint exported by that Guest. d2b core does not import the sibling flake. Eval-time validation rejects missing `identityGuestRef`/`loginEndpointRef`, any cross-Zone reference, any Endpoint whose producer is not inside the identity Guest, Host-scoped credential placement, an absent `consumerRef`, mismatched -consumer execution scope, and any string that matches known secret/token shapes. +consumer execution scope, any Endpoint visibility outside +`owner|provider|zone`, a `provider`-visible login Endpoint whose +`consumerPolicy.allowedSubjects` omits either `Provider/credential-entra` or the +Credential's exact `consumerRef`, and any string that matches known +secret/token shapes. --- @@ -521,10 +548,36 @@ credential-entra lifecycle changes. | Verb | Who | Purpose | | --- | --- | --- | | `get`, `list`, `watch` | Authorized readers | Inspect non-secret spec/status. | +| `create`, `update-spec`, `delete` plus `admin-credential/admin` | Authorized deployer/configuration controller | Administer Credential resource lifecycle; neither permission implies the other. | | `update-status` | `Provider/credential-entra` controller only | Write projected interaction/lease observations. | | `update-finalizers` | `Provider/credential-entra` controller | Revoke/finalize Credential resources. | -| `begin-login`, `observe-login`, `cancel-login` | Exact `consumerRef` plus RBAC | Drive Credential base interactive login operations. | -| `use-credential` | Exact `consumerRef` plus RBAC | Acquire/revoke/inspect access-token leases. | +| `use-credential/acquire-token` | Exact `consumerRef` plus RBAC | Begin login and acquire/refresh access-token leases permitted by `spec.allowedOperations`. | +| `use-credential/observe` | Exact `consumerRef` plus RBAC | Observe login or inspect bounded lease/status metadata. | +| `use-credential/revoke` | Exact `consumerRef` plus RBAC | Cancel login or revoke an access-token lease. | +| `admin-credential/admin` | Authorized operator/configuration controller | Gate explicit Credential lifecycle and identity-reset administration. | + +The value after `/` is an exact entry in the existing Role rule +`subresources` field. The dossier defines no `operationClasses` Role field and +no method-name verbs such as `begin-login`. A consumer Role is shaped as: + +```yaml +rules: + - resourceTypes: [Credential] + verbs: [use-credential] + subresources: [acquire-token, observe, revoke] + resourceNames: [work-entra] + zones: [work] + executionRefs: [Guest/aca-gateway] + sessionVerbs: [] +``` + +Service admission authenticates the exact consumer, checks the mapped +verb/subresource before Endpoint dispatch, then independently checks the exact +method operation class against `Credential.spec.allowedOperations`. Empty, +wildcard, unknown, or verb/subresource-mismatched Credential grants fail closed. +Administrative create/update/delete additionally require an +`admin-credential` rule with `subresources: [admin]` and the corresponding +ordinary mutation verb. ### Stable error codes @@ -544,7 +597,8 @@ credential-entra lifecycle changes. All error messages are bounded stable text. They do not include login URLs, device codes, user identifiers, tenant-private identifiers, HTTP bodies, paths, -DBus names, tokens, refresh tokens, or cookies. +DBus names, tokens, refresh tokens, cookies, a Credential name/ResourceRef/UID, +or any digest derived from Credential identity. --- @@ -558,7 +612,7 @@ DBus names, tokens, refresh tokens, or cookies. | Current source | `d2b-realm-provider/src/credential.rs:OpaqueAzureRef` remains a bounded opaque identifier reuse source only; no Host login/token implementation is retained | | Reuse action | adapt | | Destination | `packages/d2b-provider-credential-entra/src/{lib.rs,controller.rs,service.rs,controller_main.rs,agent_main.rs,audit.rs,telemetry.rs}` and corresponding tests/integration docs | -| Detailed design | Implement secret-free controller/helper; require Credential base `identityGuestRef` and `loginEndpointRef`; resolve dependency alias `entra-login-token`; validate same-Zone Guest placement and exact `consumerRef`; implement typed `EntraTokenLeaseClient` whose production implementation is the Entrablau Endpoint; implement `BeginLogin`/`ObserveLogin`/`CancelLogin` projection to Credential base interaction status; route access-token leases end-to-end from Entrablau service to exact consumer over Noise_KK; keep all refresh/login/TPM state inside the Entrablau Guest; declare no Provider state Volume; reject Host placement and all ambient fallback chains. Primary reuse disposition: `adapt`. Preserved source-plan detail: Adapt tests/types where non-secret; replace production token acquisition with Entrablau Endpoint client. | +| Detailed design | Implement secret-free controller/helper; require Credential base `identityGuestRef` and `loginEndpointRef`; resolve dependency alias `entra-login-token`; validate same-Zone Guest placement and exact `consumerRef`; implement typed `EntraTokenLeaseClient` whose production implementation is the Entrablau Endpoint; implement `BeginLogin`/`ObserveLogin`/`CancelLogin` projection to Credential base interaction status; admit service calls only through canonical `use-credential` Role rules with exact `acquire-token`/`observe`/`revoke` subresources and administrative lifecycle only through `admin-credential/admin` plus the ordinary mutation verb, with no Credential-specific Role fields; emit the login Endpoint with canonical `visibility = provider` and an exact `consumerPolicy` for the orchestration Provider and configured consumer; route access-token leases end-to-end from Entrablau service to exact consumer over Noise_KK; keep all refresh/login/TPM state inside the Entrablau Guest; declare no Provider state Volume; reject Host placement and all ambient fallback chains; enforce Credential name/ResourceRef/UID/digest as audit-only observable identity, with only `resource_name_digest` retained in authorized bounded audit and no Credential identity in status/errors/logs/OTEL Resource or span attributes/metric labels. Primary reuse disposition: `adapt`. Preserved source-plan detail: Adapt tests/types where non-secret; replace production token acquisition with Entrablau Endpoint client. | | Integration | Consumer composes `inputs.entrablau.nixosModules.default` into the identity Guest; sibling package declares login/token Process and Endpoint; d2b Credential resource binds `identityGuestRef`, `loginEndpointRef`, and `consumerRef` | | Data migration | Full v3 reset of d2b Credential metadata; Entrablau Guest state is preserved unless explicitly destroyed by the sibling-owned reset flow | | Validation | See §13 | @@ -579,10 +633,13 @@ DBus names, tokens, refresh tokens, or cookies. | `test_fake_guest_login_restart_reobserve` | Controller/helper restart reconstructs status by observing Entrablau Endpoint; status is observation, not authority. | | `test_endpoint_unavailable` | Missing/not Ready `loginEndpointRef` yields `credential-endpoint-unavailable` and no fallback. | | `test_endpoint_generation_mismatch` | Stale Endpoint generation invalidates cached sessions and requires reacquire. | +| `test_endpoint_visibility_consumer_policy` | Login Endpoint accepts only `owner`, `provider`, or `zone`, uses `provider`, and requires exact `consumerPolicy.allowedSubjects` for `Provider/credential-entra` plus the configured `consumerRef`; `zone`, omitted consumer, aliases, and mismatches fail the dossier conformance case. | | `test_host_placement_rejected` | Any `Host/*` `identityGuestRef`, `scope.executionRef`, or Host-system placement fails closed. | | `test_same_zone_accepted_cross_zone_rejected` | Same-Zone identity/consumer Guest works; any cross-Zone ResourceRef is rejected at eval/admission. | | `test_exact_consumer_rbac` | Only the authenticated `consumerRef` process can receive token delivery; other callers fail before Endpoint dispatch. | +| `test_credential_role_subresource_matrix` | `use-credential` admits only exact `acquire-token`/`observe`/`revoke` mappings; `admin-credential/admin` plus the ordinary mutation verb gates lifecycle; empty/wildcard/unknown/mismatched subresources and method-name verbs fail before Endpoint dispatch. | | `test_token_refresh_redaction` | Access token, refresh token, cookies, device codes, login URLs, and MSAL cache canaries are absent from spec/status/audit/OTEL/logs/Debug. | +| `test_credential_identity_audit_only` | Credential name, canonical ResourceRef, UID, and derived-digest canaries are absent from status, errors, logs/Debug, every OTEL Resource/span attribute, and every metric label; the authorized bounded audit record retains only `resource_name_digest`, and an unauthorized request cannot elicit identity-bearing audit. Generic allowlisted OTEL Resource attributes remain present. | | `test_e2e_record_only_delivery` | Raw access token appears only in the Entrablau→consumer Noise_KK record; bus/controller/helper see ciphertext only. | | `test_no_ambient_fallbacks` | Production code path never constructs SDK default chains, environment credential sources, DBus/browser/path fallbacks, Host login flows, or direct Entra egress. | | `test_nix_composition_identity_guest` | Consumer-composed `inputs.entrablau.nixosModules.default` in the identity Guest declares the expected Process and Endpoint schema without d2b core importing the sibling flake. | @@ -599,6 +656,9 @@ DBus names, tokens, refresh tokens, or cookies. `credential-entra.d2bus.org/entra-login-token` and producer inside `identityGuestRef`. - `consumerRef` is required and must match the authorized consumer Provider. +- The login Endpoint uses canonical `visibility = "provider"` and an exact + `consumerPolicy.allowedSubjects` containing `Provider/credential-entra` and + the configured `consumerRef`. - Host placement and cross-Zone ResourceRefs are rejected. - No Provider state Volume is emitted for `credential-entra`; Entrablau private state remains guest-local under the sibling package. @@ -638,4 +698,4 @@ Old and new suites never run in parallel indefinitely. | Evidence class | Full Entrablau-backed login/token acquisition is ADR-only in this branch; sibling integration is external. | | Retained concepts | Bounded opaque identifiers, `ExactConsumer` authorization, redacted Debug/canary discipline, D055/D056 Noise_KK token delivery. | | Replaced concepts | No abstract Host-login path, no direct `EntraCredentialClient` production Entra egress, no effect-port proxy through consumer runtime, no Host/user-agent token custody, no ambient fallback chains, and no Provider state Volume for Entra credentials. | -| Zero-secret invariant | Preserved and strengthened: controller/helper are secret-free; refresh/private login state lives only in the Entrablau identity Guest; access tokens go only to exact consumers over end-to-end KK records. | +| Zero-secret invariant | Preserved and strengthened: controller/helper are secret-free; refresh/private login state lives only in the Entrablau identity Guest; access tokens go only to exact consumers over end-to-end KK records; Credential identity is observable only as the authorized bounded audit digest and is absent from status/errors/logs and every OTEL/metric identity surface. | From 6dac27f7797e4b90c8b385a4a9c2a6774b2aaf9f Mon Sep 17 00:00:00 2001 From: John Vicondoa Date: Fri, 24 Jul 2026 13:31:43 -0700 Subject: [PATCH 107/115] docs: align exact credential subresources Map Credential service admission one-for-one to allowed operation classes and require exact supplemental lifecycle subresources for administrative CRUD. --- docs/specs/ADR-046-resources-credential.md | 107 ++++++++++-------- .../ADR-046-provider-credential-entra.md | 66 +++++++---- 2 files changed, 104 insertions(+), 69 deletions(-) diff --git a/docs/specs/ADR-046-resources-credential.md b/docs/specs/ADR-046-resources-credential.md index 893499a59..fbadffbe8 100644 --- a/docs/specs/ADR-046-resources-credential.md +++ b/docs/specs/ADR-046-resources-credential.md @@ -148,6 +148,7 @@ spec: - acquire-token - refresh-token - revoke-token + - inspect-metadata rotation: policy: proactive # on-expiry | proactive | on-demand proactiveWindowMs: 300000 # rotate when remaining lifetime < this value @@ -343,8 +344,9 @@ Operation-class validation: - operation class sets are not extensible at runtime; new classes require a spec generation change and Provider schema update; - the `inspect-metadata` class alone never grants token acquisition; -- each class maps to exactly one credential-bound service method on - `d2b.credential.v3`. +- every credential-bound service method maps to exactly one of these classes; + base interactive methods reuse the matching exact class and introduce no + Role-subresource alias. ## Placement model: User, Host, and Guest @@ -459,30 +461,36 @@ ad hoc service-method verbs: | `get` | Any authorized subject | Read Credential metadata/spec/status (no secret bytes present) | | `list` | Any authorized subject | List Credentials matching filters | | `watch` | Any authorized subject | Watch Credential events | -| `create` | Deployer/system-core configuration controller | Create a new Credential resource; also requires `admin-credential` on subresource `admin` | -| `update-spec` | Deployer/system-core configuration controller | Replace Credential spec; also requires `admin-credential` on subresource `admin` | +| `create` | Deployer/system-core configuration controller | Create a new Credential resource; also requires `admin-credential` on subresource `create` | +| `update-spec` | Deployer/system-core configuration controller | Replace Credential spec; also requires `admin-credential` on subresource `update-spec` | | `update-status` | Credential controller only | Update Credential status | | `update-finalizers` | Credential controller, consumerRef controller | Add/remove owned finalizers | -| `delete` | Deployer/system-core configuration controller | Request Credential deletion; also requires `admin-credential` on subresource `admin` | -| `use-credential` | Consumer subject authorized via consumerRef | Invoke an admitted credential operation under an exact `acquire-token`, `observe`, or `revoke` subresource | -| `admin-credential` | Authorized operator/configuration controller | Administer Credential lifecycle or an explicit identity reset under the exact `admin` subresource; it does not imply `create`, `update-spec`, or `delete` by itself | +| `delete` | Deployer/system-core configuration controller | Request Credential deletion; also requires `admin-credential` on subresource `delete` | +| `use-credential` | Consumer subject authorized via consumerRef | Invoke an admitted credential operation under the exact `allowedOperations` value used as the Role subresource | +| `admin-credential` | Authorized operator/configuration controller | Supplement ordinary Credential CRUD under the exact matching `create`, `update-spec`, or `delete` subresource; it grants no CRUD action by itself | Credential Roles use the existing `subresources` rule field. They do not add an `operationClasses` field. A rule granting either Credential-specific verb MUST -name exact, non-empty Credential subresources: +name exact, non-empty Credential subresources. A consumer Role rule is: ```yaml rules: - resourceTypes: [Credential] verbs: [use-credential] - subresources: [acquire-token, observe] + subresources: [acquire-token, refresh-token] resourceNames: [work-entra] zones: [dev] executionRefs: [Guest/work-vm] sessionVerbs: [] +``` + +A separate deployer Role rule is: + +```yaml +rules: - resourceTypes: [Credential] verbs: [admin-credential] - subresources: [admin] + subresources: [create, update-spec, delete] resourceNames: [work-entra] zones: [dev] executionRefs: [] @@ -493,20 +501,25 @@ The closed admission mapping is: | Credential action | Required resource verb | Required Role `subresources` value | | --- | --- | --- | -| `AcquireToken`, `RefreshToken`, `SignChallenge`, `BeginLogin` | `use-credential` | `acquire-token` | -| `Status`, `InspectMetadata`, `ObserveLogin` | `use-credential` | `observe` | -| `RevokeToken`, `CancelLogin` | `use-credential` | `revoke` | -| Resource create/spec update/delete and explicit identity reset | `admin-credential` in addition to the ordinary mutation verb | `admin` | - -For each service method, `spec.allowedOperations` remains the independent, -provider-capability ceiling and is checked for the exact operation class -(`acquire-token`, `refresh-token`, `revoke-token`, `sign-challenge`, or -`inspect-metadata`). Effective permission is the intersection of the exact -verb/subresource Role grant, `spec.allowedOperations`, `consumerRef`, scope, and -the structural Provider/component checks. An empty/wildcard subresource grant, -an unknown subresource, `use-credential/admin`, or -`admin-credential/acquire-token|observe|revoke` fails admission. Method names -such as `begin-login` are not RBAC verbs. +| `AcquireToken`, `BeginLogin` | `use-credential` | `acquire-token` | +| `RefreshToken` | `use-credential` | `refresh-token` | +| `RevokeToken`, `CancelLogin` | `use-credential` | `revoke-token` | +| `SignChallenge` | `use-credential` | `sign-challenge` | +| `Status`, `InspectMetadata`, `ObserveLogin` | `use-credential` | `inspect-metadata` | +| Resource create | `admin-credential` in addition to ordinary `create` | `create` | +| Resource spec update | `admin-credential` in addition to ordinary `update-spec` | `update-spec` | +| Resource delete | `admin-credential` in addition to ordinary `delete` | `delete` | + +For each service method, the operation class is both the exact +`spec.allowedOperations` value and the exact `use-credential` Role subresource. +Effective permission is their intersection, further narrowed by `consumerRef`, +scope, and structural Provider/component checks. The closed use subresource set +is therefore exactly `acquire-token`, `refresh-token`, `revoke-token`, +`sign-challenge`, and `inspect-metadata`. An empty/wildcard/unknown +subresource, a noncanonical method-name alias, or a verb/subresource mismatch +fails admission. Administrative admission separately requires the matching +ordinary CRUD verb and `admin-credential` subresource; only exact `create`, +`update-spec`, and `delete` pairs are valid. Status ownership: only the Credential controller's exact registered process generation may call `UpdateStatus` on a Credential resource. @@ -535,12 +548,12 @@ Route key example: | Method | Required exact operation class | Required Role permission | Outer DTO fields | Token bytes | | --- | --- | --- | --- | --- | -| `Status` | none | `use-credential/observe` (or ordinary authorized `get` for the base resource projection) | `CredentialStatusResponse`: leaseState, rotationGeneration, sourceVersion, expiresAtUnixMs, placementBinding | none | +| `Status` | `inspect-metadata` | `use-credential/inspect-metadata` | `CredentialStatusResponse`: leaseState, rotationGeneration, sourceVersion, expiresAtUnixMs, placementBinding | none | | `AcquireToken` | `acquire-token` | `use-credential/acquire-token` | `AcquireTokenResponse`: leaseHandle, sourceVersion, rotationGeneration, expiresAtUnixMs | raw token bytes in a dedicated sensitive ComponentSession record (see §Credential-delivery endpoint contract) | -| `RefreshToken` | `refresh-token` | `use-credential/acquire-token` | `RefreshTokenResponse`: leaseHandle, sourceVersion, rotationGeneration, new expiresAtUnixMs | raw token bytes in a dedicated sensitive ComponentSession record | -| `RevokeToken` | `revoke-token` | `use-credential/revoke` | `RevokeTokenResponse`: closed revocation result (Revoked or AlreadyRevoked), revokedAtUnixMs | none | -| `SignChallenge` | `sign-challenge` | `use-credential/acquire-token` | `SignChallengeResponse`: outcome code | signature bytes in a dedicated sensitive ComponentSession record (same channel as token delivery) | -| `InspectMetadata` | `inspect-metadata` | `use-credential/observe` | `InspectMetadataResponse`: leaseState, rotationGeneration, sourceVersion, expiresAtUnixMs | none | +| `RefreshToken` | `refresh-token` | `use-credential/refresh-token` | `RefreshTokenResponse`: leaseHandle, sourceVersion, rotationGeneration, new expiresAtUnixMs | raw token bytes in a dedicated sensitive ComponentSession record | +| `RevokeToken` | `revoke-token` | `use-credential/revoke-token` | `RevokeTokenResponse`: closed revocation result (Revoked or AlreadyRevoked), revokedAtUnixMs | none | +| `SignChallenge` | `sign-challenge` | `use-credential/sign-challenge` | `SignChallengeResponse`: outcome code | signature bytes in a dedicated sensitive ComponentSession record (same channel as token delivery) | +| `InspectMetadata` | `inspect-metadata` | `use-credential/inspect-metadata` | `InspectMetadataResponse`: leaseState, rotationGeneration, sourceVersion, expiresAtUnixMs | none | The `Status`, `RevokeToken`, and `InspectMetadata` response DTOs are non-secret: they carry only opaque identifiers, outcome codes, and timestamps. `AcquireToken`, @@ -655,11 +668,11 @@ full binding before accepting records. d2b-bus performs the following checks before authorizing the route: - RBAC `use-credential` for the authenticated consumer Provider subject and - Credential ResourceRef under the exact mapped `acquire-token`, `observe`, or - `revoke` Role subresource; + Credential ResourceRef under the exact operation-class Role subresource; - `spec.consumerRef` matches the consumer Provider identity from the signed component descriptor; -- `spec.allowedOperations` includes the operation class; +- `spec.allowedOperations` and the Role `subresources` both include the same + exact operation class; - The current lease state permits the operation class (Active/RotationDue for `refresh-token`; no existing active lease required for `acquire-token` subject to `maxLeases`); @@ -682,9 +695,9 @@ traverse the standard ComponentSession/d2b-bus stack defined in identity evidence mapping the consumer to an exact Zone subject. 2. d2b-bus checks native RBAC `use-credential` for the authenticated subject, - target Credential ResourceRef, exact mapped Role subresource, and current - Role/RoleBinding/Provider revisions, then separately checks the exact - operation class against `spec.allowedOperations`. + target Credential ResourceRef, exact operation-class Role subresource, and + current Role/RoleBinding/Provider revisions, then requires that same exact + class in `spec.allowedOperations`. 3. For non-secret methods, d2b-bus routes the call to the exact credential provider process using an enrolled `Noise_KK_25519_ChaChaPoly_SHA256` @@ -880,14 +893,14 @@ Audit records for Credential operations: | Event | Fields retained | | --- | --- | -| Credential resource create/update/delete | Zone, subject digest, `resource_name_digest`, ordinary mutation verb, `admin-credential/admin` decision, revision result | +| Credential resource create/update/delete | Zone, subject digest, `resource_name_digest`, ordinary mutation verb, matching `admin-credential` lifecycle-subresource decision, revision result | | `AcquireToken` | Zone, subject digest, `resource_name_digest`, operation class, `use-credential/acquire-token` decision, `rotationGeneration`, outcome code, idempotency key digest | -| `RefreshToken` | Zone, subject digest, `resource_name_digest`, operation class, `use-credential/acquire-token` decision, `rotationGeneration`, outcome code, idempotency key digest | -| `RevokeToken` | Zone, subject digest, `resource_name_digest`, operation class, `use-credential/revoke` decision, `rotationGeneration`, revocation result code | -| `SignChallenge` | Zone, subject digest, `resource_name_digest`, operation class, `use-credential/acquire-token` decision, outcome code (no signature bytes) | -| `Status`, `InspectMetadata`, `ObserveLogin` | Zone, subject digest, `resource_name_digest`, `use-credential/observe` decision, bounded outcome code | +| `RefreshToken` | Zone, subject digest, `resource_name_digest`, operation class, `use-credential/refresh-token` decision, `rotationGeneration`, outcome code, idempotency key digest | +| `RevokeToken` | Zone, subject digest, `resource_name_digest`, operation class, `use-credential/revoke-token` decision, `rotationGeneration`, revocation result code | +| `SignChallenge` | Zone, subject digest, `resource_name_digest`, operation class, `use-credential/sign-challenge` decision, outcome code (no signature bytes) | +| `Status`, `InspectMetadata`, `ObserveLogin` | Zone, subject digest, `resource_name_digest`, `use-credential/inspect-metadata` decision, bounded outcome code | | `BeginLogin` | Zone, subject digest, `resource_name_digest`, `use-credential/acquire-token` decision, bounded outcome code (no challenge/UI bytes) | -| `CancelLogin` | Zone, subject digest, `resource_name_digest`, `use-credential/revoke` decision, bounded outcome code | +| `CancelLogin` | Zone, subject digest, `resource_name_digest`, `use-credential/revoke-token` decision, bounded outcome code | | Rotation | Zone, `resource_name_digest`, trigger reason, old `rotationGeneration`, new `rotationGeneration`, outcome code | | Provider generation change revocation | Zone, `resource_name_digest`, policy applied, outcome code | @@ -1979,10 +1992,10 @@ config formalizes this as an `OpaqueAzureRef` with the same charset restriction. | Reuse source | main `a1cc0b2d`: `packages/d2b-contracts/proto/v2/provider_credential.proto` method names | | Reuse action | adapt | | Destination | `packages/d2b-contracts/proto/v3/credential.proto`; `packages/d2b-credential-service/src/{service.rs, client.rs, server.rs}` | -| Detailed design | Define `d2b.credential.v3` protobuf service with methods: `Status`, `AcquireToken`, `RefreshToken`, `RevokeToken`, `SignChallenge`, `InspectMetadata`; each request carries `credential_ref`, `operation_class`, `operation_id`, `idempotency_key`, `requested_expiry_unix_ms`, `deadline_unix_ms`; map methods to the existing Role `subresources` values (`acquire-token`, `observe`, `revoke`) under the canonical `use-credential` resource verb and map administrative lifecycle to `admin-credential/admin`, never to a new Role field or method-name verb; `Status`, `RevokeToken`, and `InspectMetadata` responses carry only non-secret metadata (leaseHandle digest, rotationGeneration, sourceVersion, expiresAtUnixMs, state, outcome code); `AcquireToken`, `RefreshToken`, and `SignChallenge` responses additionally include a `delivery_session_params` field carrying the binding contract fields required to establish the end-to-end credential-delivery ComponentSession (see §Credential-delivery endpoint contract); the token bytes themselves travel in the separate Noise-encrypted delivery session, never in the outer DTO; strict unknown-field rejection; bounded message sizes; all record wrappers for delivery sessions must be zeroizing types | -| Integration | d2b-bus routes `d2b.credential.v3` service to the exact credential provider Process identified by `Credential.spec.providerRef`; before dispatch RBAC checks the exact `use-credential` verb and mapped Role `subresources` value, then separately checks `spec.allowedOperations`; for `AcquireToken`/`RefreshToken`/`SignChallenge`, bus additionally authorizes the credential-delivery endpoint route and forwards opaque Noise-encrypted delivery records without terminating or buffering them; bus never stores or inspects delivery record plaintext | +| Detailed design | Define `d2b.credential.v3` protobuf service with methods: `Status`, `AcquireToken`, `RefreshToken`, `RevokeToken`, `SignChallenge`, `InspectMetadata`; each request carries `credential_ref`, `operation_class`, `operation_id`, `idempotency_key`, `requested_expiry_unix_ms`, `deadline_unix_ms`; map every method to the same exact operation class in `spec.allowedOperations` and the existing Role `subresources` field (`acquire-token`, `refresh-token`, `revoke-token`, `sign-challenge`, or `inspect-metadata`) under the canonical `use-credential` resource verb; map administrative lifecycle to matching `admin-credential` subresources `create`, `update-spec`, and `delete`, supplemental to ordinary CRUD, never to a new Role field or method-name alias; `Status`, `RevokeToken`, and `InspectMetadata` responses carry only non-secret metadata (leaseHandle digest, rotationGeneration, sourceVersion, expiresAtUnixMs, state, outcome code); `AcquireToken`, `RefreshToken`, and `SignChallenge` responses additionally include a `delivery_session_params` field carrying the binding contract fields required to establish the end-to-end credential-delivery ComponentSession (see §Credential-delivery endpoint contract); the token bytes themselves travel in the separate Noise-encrypted delivery session, never in the outer DTO; strict unknown-field rejection; bounded message sizes; all record wrappers for delivery sessions must be zeroizing types | +| Integration | d2b-bus routes `d2b.credential.v3` service to the exact credential provider Process identified by `Credential.spec.providerRef`; before dispatch RBAC requires the exact operation class in both the `use-credential` Role `subresources` and `spec.allowedOperations`; for `AcquireToken`/`RefreshToken`/`SignChallenge`, bus additionally authorizes the credential-delivery endpoint route and forwards opaque Noise-encrypted delivery records without terminating or buffering them; bus never stores or inspects delivery record plaintext | | Data migration | None — full d2b 3.0 reset; no prior state to migrate | -| Validation | Protocol golden vectors for each method; Role matrix for exact `acquire-token`, `observe`, and `revoke` subresources under `use-credential`, plus `admin-credential/admin` admission; deny empty/wildcard/unknown/mismatched subresources and any `operationClasses`/method-name-verb input before Provider dispatch; malformed/oversize rejection; `leaseHandle` opacity tests (secret-canary must not appear in outer DTO or delivery routing metadata); locked/unavailable/denied/expired state tests; delivery session binding contract round-trip; zeroizing record type unit tests; delivery channel never materialized in non-delivery method tests | +| Validation | Protocol golden vectors for each method; Role matrix for the five exact allowed-operation subresources under `use-credential` and exact `create`, `update-spec`, and `delete` subresources under `admin-credential`; prove admin permission is supplemental to ordinary CRUD; deny empty/wildcard/unknown/mismatched subresources and any `operationClasses`/method-name-alias input before Provider dispatch; malformed/oversize rejection; `leaseHandle` opacity tests (secret-canary must not appear in outer DTO or delivery routing metadata); locked/unavailable/denied/expired state tests; delivery session binding contract round-trip; zeroizing record type unit tests; delivery channel never materialized in non-delivery method tests | | Removal proof | Old v2 `CredentialProviderService` proto removed only after all v3 callers migrate | ### ADR046-credential-003 @@ -2043,7 +2056,7 @@ config formalizes this as an `OpaqueAzureRef` with the same charset restriction. | Reuse source | main `a1cc0b2d`: `packages/d2b-provider-toolkit/src/conformance.rs` (provider conformance pattern); `packages/d2b-provider-toolkit/src/adapter.rs` (controller toolkit pattern) | | Reuse action | adapt | | Destination | `packages/d2b-provider-credential-/src/controller.rs`; `packages/d2b-contracts/src/v3/credential_controller.rs` | -| Detailed design | Implement Credential controller handler conforming to `ADR-046-resource-reconciliation` async loop; implement `reconcile`, `observe`, `finalize`, `drain`, and `health` handlers; implement rotation state machine (proactive/on-expiry/on-demand policies); implement `provider-revoke` finalizer execution with `revocation.onOwnerDelete` policy; implement provider-generation-change detection and revocation; implement `CredentialReady`, `RotationDue`, `ProviderUnavailable`, `LeaseRevoked` condition logic; use controller Roles with exact `acquire-token`, `observe`, and `revoke` subresources under `use-credential` for service calls and structural status/finalizer ownership, never a Credential-specific Role field; implement bounded idempotency key derivation (Credential UID + rotationGeneration + operation class, no secret material); implement `observeInterval=30s` health check calling `InspectMetadata`; bounded retry/backpressure with typed `credential-rotation-failed` outcome; enforce `MAX_LOCAL_LEASES=256` per controller provider instance | +| Detailed design | Implement Credential controller handler conforming to `ADR-046-resource-reconciliation` async loop; implement `reconcile`, `observe`, `finalize`, `drain`, and `health` handlers; implement rotation state machine (proactive/on-expiry/on-demand policies); implement `provider-revoke` finalizer execution with `revocation.onOwnerDelete` policy; implement provider-generation-change detection and revocation; implement `CredentialReady`, `RotationDue`, `ProviderUnavailable`, `LeaseRevoked` condition logic; use controller Roles with the exact allowed-operation subresource matching each `use-credential` service call, while status/finalizer writes retain their separate structural ownership checks; never add a Credential-specific Role field or coarse alias; implement bounded idempotency key derivation (Credential UID + rotationGeneration + operation class, no secret material); implement `observeInterval=30s` health check calling `InspectMetadata`; bounded retry/backpressure with typed `credential-rotation-failed` outcome; enforce `MAX_LOCAL_LEASES=256` per controller provider instance | | Integration | Provider controller Process → d2b-bus → `d2b.credential.v3` service in provider process → injected client/port; status updates through resource API; watch subscription on Credential, Provider, Host/Guest dependency types | | Data migration | None; v3 reset | | Validation | Controller state-machine golden vectors; rotation-policy matrix (proactive/on-demand/on-expiry × success/locked/unavailable/expired); exact Role-subresource admission matrix; finalizer execution tests; provider-generation-change revocation tests; idempotency key derivation tests; observe-interval drift detection test; canary tests confirm zero secret bytes and no Credential name/ResourceRef/UID/digest in any controller-written status or error | @@ -2059,9 +2072,9 @@ config formalizes this as an `OpaqueAzureRef` with the same charset restriction. | Reuse action | adapt | | Destination | `nixos-modules/options-resources.nix` (generic schema-derived resource options; not type-specific), `nixos-modules/activation-nixos-cleanup.nix` | | Detailed design | **(1) Schema-derived options and eval-time validation**: implement `d2b.zones..resources. = { type = "..."; spec = { ... }; }` as a generic attrset option; an optional `metadata` sub-attr may contain `ownerRef` and/or presentation `labels`/`annotations`. Nix option types, defaults, and inline docs for `spec` fields are generated from the committed `ResourceTypeSchema` JSON (`docs/reference/schemas/v3/credential.json`) and the signed Provider schema — no bespoke options module is maintained separately. `metadata.name` derives from the attr key; `metadata.zone` from the Zone attr key; `apiVersion` defaults to `resources.d2bus.org/v3`; `status`/`uid`/`generation`/`revision`/timestamps/`managedBy`/`configurationGeneration` are not authored. Core assigns `metadata.managedBy = "configuration"` and `metadata.configurationGeneration = ` to all configuration-managed resources at create/update time; these are never authored in Nix. Eval-time assertions (applied to all entries with `type = "Credential"`): `spec.providerRef` resolves a Provider in the same Zone whose `credentialDomains` includes `spec.scope.domainFilter` and whose `spec.artifactId` (a sibling of `spec.config` on the Provider resource, not inside `spec.config`) resolves an artifact catalog entry of `type = "provider"`; `spec.audience` charset (`^[A-Za-z0-9._:/@-]+$`, max 256); `spec.rotation.proactiveWindowMs < maxLeaseLifetimeMs / 2`; `spec.consumerRef`/`scope.executionRef`/`scope.userRef` resolve declared Zone resources; duplicate `(providerRef, executionRef, userRef, audience)` tuple rejected; `contains_sensitive_shape` on all string fields; Provider-specific placement constraints; `allowedOperations` ⊆ `providerRef.supportedOperations`. **(2) Canonical JSON and bundle emission**: render `spec` attr directly to `spec` object in canonical JSON (no field renames/re-nesting); `metadata` in output contains only derived `name`/`zone` and optionally Nix-authored `ownerRef`/`labels`/`annotations`; `apiVersion` is top-level, not inside `metadata`; `finalizers` is omitted from the Nix-rendered input (core manages finalizers, never accepts them from the bundle); no management labels are emitted by Nix; sort bundle by `(type, name)`; write to `/etc/d2b/zones//resource-bundle.json` with digest. **(2b) Artifact catalog emission**: derivation-valued inputs (`d2b.artifacts.`) are compiled separately into an integrity-pinned artifact catalog (`/etc/d2b/zones//artifact-catalog.json`) with its own digest header; each entry records `id`, `type`, `sha256`, and bounded closure metadata; store paths are private catalog implementation data absent from the resource bundle, status, audit, and logs; `activation-nixos` verifies both digests before any create/update; missing or wrong-type `artifactId` references fail the NixOS build. **(3) Build-time schema validation**: validate rendered JSON against `docs/reference/schemas/v3/credential.json` and Provider-specific schema; enforce `secretRef` fields use `Credential/` refs; enforce no store paths in any resource bundle or status output; drift gate (`make test-drift`) regenerates schemas with `cargo xtask gen-schemas` and asserts `git diff --exit-code`; Nix options module drift checked in the same gate. **(4) Generation transition and cleanup contract**: activation-nixos controller verifies SHA-256 digest of both resource bundle and artifact catalog, creates/updates desired-set resources, activates without blocking on cleanup, issues async Delete for absent configuration-managed resources (those with `metadata.managedBy = "configuration"`), sets Degraded/Cleanup=True on removed resources; retains up to `retainedConfigurationMax` (default 3, range 1..16) prior bundles; oldest prune when count exceeded; no time-based rollback window. **(5) Configuration-managed vs controller/API isolation**: `managedBy` and `ownerRef` are orthogonal; configuration-managed and API-created resources may each carry an optional same-Zone `ownerRef` and participate in owner cascade. Cleanup checks `metadata.managedBy = "configuration"` before issuing Delete; resources with `metadata.managedBy = "controller"` or `metadata.managedBy = "api"` are never deleted by this path; API-created resources persist until explicit Delete and are never generation-swept. | -| Integration | `activation-nixos` Provider creates/updates Credential resources from emitted envelopes with both the ordinary mutation permission and `admin-credential/admin`; Credential controller `provider-revoke` finalizer handles cleanup Deletes; owner controller reconciles children of deleted configuration-managed Credentials | +| Integration | `activation-nixos` Provider creates/updates/deletes Credential resources from emitted envelopes with both the ordinary CRUD permission and the matching `admin-credential/create`, `admin-credential/update-spec`, or `admin-credential/delete` permission; Credential controller `provider-revoke` finalizer handles cleanup Deletes; owner controller reconciles children of deleted configuration-managed Credentials | | Data migration | None; v3 reset | -| Validation | **(eval/build)**: nix-unit golden JSON envelope for each example (spec shape, no management labels in Nix output, sort, digest); assertion-failure tests for secret-shaped audience, mismatched providerRef/domainFilter, proactiveWindow > half maxLifetime, duplicate binding tuple, unresolved refs; generated activation Role contains `admin-credential` with exact `subresources = [ "admin" ]` and no undeclared Role field; artifact catalog: assertion-failure for missing `artifactId`, wrong-type `artifactId`, duplicate catalog ID; bundle + artifact catalog digest round-trip; artifact catalog store-path absence from resource bundle and status; Provider-specific schema cross-check; `make test-drift` schema drift gate. **(runtime integration in `tests/host-integration/`)**: `credential-cleanup-basic` (removed resource reaches Deleted); `credential-cleanup-nonblocking` (activation Ready before cleanup finalizer finishes); `credential-cleanup-pending-status` (Cleanup=True on removed resource, PendingCleanup=True on Provider); `credential-cleanup-stalled` (Degraded stall detection and recovery); `credential-cleanup-controller-children-preserved` (ownerRef children cleaned by Credential controller); `credential-cleanup-no-dynamic-deletion` (controller-created Credential with `managedBy = "controller"` not deleted); `credential-retained-generation-count` (up to retainedConfigurationMax bundles retained; rollback re-creates from retained bundle; oldest pruned when count exceeded); `credential-bundle-digest-mismatch` (tampered bundle aborts activation). | +| Validation | **(eval/build)**: nix-unit golden JSON envelope for each example (spec shape, no management labels in Nix output, sort, digest); assertion-failure tests for secret-shaped audience, mismatched providerRef/domainFilter, proactiveWindow > half maxLifetime, duplicate binding tuple, unresolved refs; generated activation Role contains `admin-credential` with exact `subresources = [ "create" "update-spec" "delete" ]`, no coarse alias, and no undeclared Role field; permission tests prove each lifecycle action also requires its ordinary CRUD verb; artifact catalog: assertion-failure for missing `artifactId`, wrong-type `artifactId`, duplicate catalog ID; bundle + artifact catalog digest round-trip; artifact catalog store-path absence from resource bundle and status; Provider-specific schema cross-check; `make test-drift` schema drift gate. **(runtime integration in `tests/host-integration/`)**: `credential-cleanup-basic` (removed resource reaches Deleted); `credential-cleanup-nonblocking` (activation Ready before cleanup finalizer finishes); `credential-cleanup-pending-status` (Cleanup=True on removed resource, PendingCleanup=True on Provider); `credential-cleanup-stalled` (Degraded stall detection and recovery); `credential-cleanup-controller-children-preserved` (ownerRef children cleaned by Credential controller); `credential-cleanup-no-dynamic-deletion` (controller-created Credential with `managedBy = "controller"` not deleted); `credential-retained-generation-count` (up to retainedConfigurationMax bundles retained; rollback re-creates from retained bundle; oldest pruned when count exceeded); `credential-bundle-digest-mismatch` (tampered bundle aborts activation). | | Removal proof | Not applicable (new module) | ### ADR046-credential-008 diff --git a/docs/specs/providers/ADR-046-provider-credential-entra.md b/docs/specs/providers/ADR-046-provider-credential-entra.md index 693654b72..9be8a311d 100644 --- a/docs/specs/providers/ADR-046-provider-credential-entra.md +++ b/docs/specs/providers/ADR-046-provider-credential-entra.md @@ -124,7 +124,7 @@ spec: userRef: null consumerRef: Provider/runtime-azure-container-apps audience: azure-resource-manager - allowedOperations: [acquire-token, refresh-token] + allowedOperations: [acquire-token, refresh-token, revoke-token, inspect-metadata] rotation: policy: proactive proactiveWindowMs: 300000 @@ -257,11 +257,12 @@ named stream on the authenticated session, not status text. | Method | Required Role permission | Direction | Secret output? | Purpose | | --- | --- | --- | --- | --- | | `BeginLogin` | `use-credential/acquire-token` | request/reply + named stream | No status secret; UI bytes stay on stream | Start or resume an Entrablau interactive login session inside `identityGuestRef`. Opens optional named stream `credential-entra.d2bus.org/login-ui`. | -| `ObserveLogin` | `use-credential/observe` | request/reply | No | Return bounded non-secret observation for `interactionState`, session generation, deadline, and challenge metadata. | -| `CancelLogin` | `use-credential/revoke` | request/reply | No | Cancel a pending login session by generation; idempotent. | +| `ObserveLogin` | `use-credential/inspect-metadata` | request/reply | No | Return bounded non-secret observation for `interactionState`, session generation, deadline, and challenge metadata. | +| `CancelLogin` | `use-credential/revoke-token` | request/reply | No | Cancel a pending login session by generation; idempotent. | | `AcquireAccessTokenLease` | `use-credential/acquire-token` | request/reply + sensitive KK record | Yes, only in KK record to consumer | Issue an on-demand access-token lease for `audience`/operation; outer reply carries metadata only. | -| `RevokeAccessTokenLease` | `use-credential/revoke` | request/reply | No | Revoke a non-secret lease handle or mark it unusable. | -| `InspectAccessTokenLease` | `use-credential/observe` | request/reply | No | Return bounded non-secret lease state. | +| `RefreshAccessTokenLease` | `use-credential/refresh-token` | request/reply + sensitive KK record | Yes, only in KK record to consumer | Refresh an existing access-token lease; outer reply carries metadata only. | +| `RevokeAccessTokenLease` | `use-credential/revoke-token` | request/reply | No | Revoke a non-secret lease handle or mark it unusable. | +| `InspectAccessTokenLease` | `use-credential/inspect-metadata` | request/reply | No | Return bounded non-secret lease state. | `BeginLogin`, `ObserveLogin`, and `CancelLogin` are the Credential base interactive-login operations. The service may conduct browser, device, desktop, @@ -386,6 +387,7 @@ trait EntraTokenLeaseClient: Send + Sync { async fn observe_login(&self, request: ObserveLoginRequest) -> Result; async fn cancel_login(&self, request: CancelLoginRequest) -> Result; async fn acquire_access_token_lease(&self, request: TokenLeaseRequest) -> Result; + async fn refresh_access_token_lease(&self, request: TokenLeaseRequest) -> Result; async fn revoke_access_token_lease(&self, request: RevokeLeaseRequest) -> Result; async fn inspect_access_token_lease(&self, request: InspectLeaseRequest) -> Result; } @@ -491,7 +493,12 @@ Endpoint exported by that Guest. d2b core does not import the sibling flake. scope.domainFilter = "system"; consumerRef = "Provider/runtime-azure-container-apps"; audience = "azure-resource-manager"; - allowedOperations = [ "acquire-token" "refresh-token" ]; + allowedOperations = [ + "acquire-token" + "refresh-token" + "revoke-token" + "inspect-metadata" + ]; rotation.policy = "proactive"; rotation.proactiveWindowMs = 300000; rotation.maxLeaseLifetimeMs = 3600000; @@ -548,36 +555,51 @@ credential-entra lifecycle changes. | Verb | Who | Purpose | | --- | --- | --- | | `get`, `list`, `watch` | Authorized readers | Inspect non-secret spec/status. | -| `create`, `update-spec`, `delete` plus `admin-credential/admin` | Authorized deployer/configuration controller | Administer Credential resource lifecycle; neither permission implies the other. | +| `create` plus `admin-credential/create` | Authorized deployer/configuration controller | Create a Credential; neither permission implies the other. | +| `update-spec` plus `admin-credential/update-spec` | Authorized deployer/configuration controller | Update a Credential spec; neither permission implies the other. | +| `delete` plus `admin-credential/delete` | Authorized deployer/configuration controller | Delete a Credential; neither permission implies the other. | | `update-status` | `Provider/credential-entra` controller only | Write projected interaction/lease observations. | | `update-finalizers` | `Provider/credential-entra` controller | Revoke/finalize Credential resources. | -| `use-credential/acquire-token` | Exact `consumerRef` plus RBAC | Begin login and acquire/refresh access-token leases permitted by `spec.allowedOperations`. | -| `use-credential/observe` | Exact `consumerRef` plus RBAC | Observe login or inspect bounded lease/status metadata. | -| `use-credential/revoke` | Exact `consumerRef` plus RBAC | Cancel login or revoke an access-token lease. | -| `admin-credential/admin` | Authorized operator/configuration controller | Gate explicit Credential lifecycle and identity-reset administration. | +| `use-credential/acquire-token` | Exact `consumerRef` plus RBAC | Begin login or acquire an access-token lease when `spec.allowedOperations` includes `acquire-token`. | +| `use-credential/refresh-token` | Exact `consumerRef` plus RBAC | Refresh an access-token lease when `spec.allowedOperations` includes `refresh-token`. | +| `use-credential/revoke-token` | Exact `consumerRef` plus RBAC | Cancel login or revoke an access-token lease when `spec.allowedOperations` includes `revoke-token`. | +| `use-credential/inspect-metadata` | Exact `consumerRef` plus RBAC | Observe login or inspect bounded lease/status metadata when `spec.allowedOperations` includes `inspect-metadata`. | The value after `/` is an exact entry in the existing Role rule `subresources` field. The dossier defines no `operationClasses` Role field and -no method-name verbs such as `begin-login`. A consumer Role is shaped as: +no method-name verbs such as `begin-login`. A consumer Role rule is: ```yaml rules: - resourceTypes: [Credential] verbs: [use-credential] - subresources: [acquire-token, observe, revoke] + subresources: [acquire-token, refresh-token, revoke-token, inspect-metadata] resourceNames: [work-entra] zones: [work] executionRefs: [Guest/aca-gateway] sessionVerbs: [] ``` -Service admission authenticates the exact consumer, checks the mapped -verb/subresource before Endpoint dispatch, then independently checks the exact -method operation class against `Credential.spec.allowedOperations`. Empty, -wildcard, unknown, or verb/subresource-mismatched Credential grants fail closed. -Administrative create/update/delete additionally require an -`admin-credential` rule with `subresources: [admin]` and the corresponding -ordinary mutation verb. +A separate deployer Role rule is: + +```yaml +rules: + - resourceTypes: [Credential] + verbs: [admin-credential] + subresources: [create, update-spec, delete] + resourceNames: [work-entra] + zones: [work] + executionRefs: [] + sessionVerbs: [] +``` + +Service admission authenticates the exact consumer and requires the same exact +operation class in both `Credential.spec.allowedOperations` and the +`use-credential` Role `subresources` before Endpoint dispatch. Empty, wildcard, +unknown, noncanonical aliases, and mismatched classes fail closed. +Administrative create/update/delete additionally require the matching +`admin-credential` subresource (`create`, `update-spec`, or `delete`) and the +corresponding ordinary CRUD verb. ### Stable error codes @@ -612,7 +634,7 @@ or any digest derived from Credential identity. | Current source | `d2b-realm-provider/src/credential.rs:OpaqueAzureRef` remains a bounded opaque identifier reuse source only; no Host login/token implementation is retained | | Reuse action | adapt | | Destination | `packages/d2b-provider-credential-entra/src/{lib.rs,controller.rs,service.rs,controller_main.rs,agent_main.rs,audit.rs,telemetry.rs}` and corresponding tests/integration docs | -| Detailed design | Implement secret-free controller/helper; require Credential base `identityGuestRef` and `loginEndpointRef`; resolve dependency alias `entra-login-token`; validate same-Zone Guest placement and exact `consumerRef`; implement typed `EntraTokenLeaseClient` whose production implementation is the Entrablau Endpoint; implement `BeginLogin`/`ObserveLogin`/`CancelLogin` projection to Credential base interaction status; admit service calls only through canonical `use-credential` Role rules with exact `acquire-token`/`observe`/`revoke` subresources and administrative lifecycle only through `admin-credential/admin` plus the ordinary mutation verb, with no Credential-specific Role fields; emit the login Endpoint with canonical `visibility = provider` and an exact `consumerPolicy` for the orchestration Provider and configured consumer; route access-token leases end-to-end from Entrablau service to exact consumer over Noise_KK; keep all refresh/login/TPM state inside the Entrablau Guest; declare no Provider state Volume; reject Host placement and all ambient fallback chains; enforce Credential name/ResourceRef/UID/digest as audit-only observable identity, with only `resource_name_digest` retained in authorized bounded audit and no Credential identity in status/errors/logs/OTEL Resource or span attributes/metric labels. Primary reuse disposition: `adapt`. Preserved source-plan detail: Adapt tests/types where non-secret; replace production token acquisition with Entrablau Endpoint client. | +| Detailed design | Implement secret-free controller/helper; require Credential base `identityGuestRef` and `loginEndpointRef`; resolve dependency alias `entra-login-token`; validate same-Zone Guest placement and exact `consumerRef`; implement typed `EntraTokenLeaseClient` whose production implementation is the Entrablau Endpoint; implement `BeginLogin`/`ObserveLogin`/`CancelLogin` projection to Credential base interaction status; admit service calls only when the same exact allowed operation (`acquire-token`, `refresh-token`, `revoke-token`, or `inspect-metadata`) is present in both `Credential.spec.allowedOperations` and the canonical `use-credential` Role `subresources`; require matching `create`, `update-spec`, or `delete` under `admin-credential` in addition to ordinary CRUD, with no Credential-specific Role fields or coarse aliases; emit the login Endpoint with canonical `visibility = provider` and an exact `consumerPolicy` for the orchestration Provider and configured consumer; route access-token leases end-to-end from Entrablau service to exact consumer over Noise_KK; keep all refresh/login/TPM state inside the Entrablau Guest; declare no Provider state Volume; reject Host placement and all ambient fallback chains; enforce Credential name/ResourceRef/UID/digest as audit-only observable identity, with only `resource_name_digest` retained in authorized bounded audit and no Credential identity in status/errors/logs/OTEL Resource or span attributes/metric labels. Primary reuse disposition: `adapt`. Preserved source-plan detail: Adapt tests/types where non-secret; replace production token acquisition with Entrablau Endpoint client. | | Integration | Consumer composes `inputs.entrablau.nixosModules.default` into the identity Guest; sibling package declares login/token Process and Endpoint; d2b Credential resource binds `identityGuestRef`, `loginEndpointRef`, and `consumerRef` | | Data migration | Full v3 reset of d2b Credential metadata; Entrablau Guest state is preserved unless explicitly destroyed by the sibling-owned reset flow | | Validation | See §13 | @@ -637,7 +659,7 @@ or any digest derived from Credential identity. | `test_host_placement_rejected` | Any `Host/*` `identityGuestRef`, `scope.executionRef`, or Host-system placement fails closed. | | `test_same_zone_accepted_cross_zone_rejected` | Same-Zone identity/consumer Guest works; any cross-Zone ResourceRef is rejected at eval/admission. | | `test_exact_consumer_rbac` | Only the authenticated `consumerRef` process can receive token delivery; other callers fail before Endpoint dispatch. | -| `test_credential_role_subresource_matrix` | `use-credential` admits only exact `acquire-token`/`observe`/`revoke` mappings; `admin-credential/admin` plus the ordinary mutation verb gates lifecycle; empty/wildcard/unknown/mismatched subresources and method-name verbs fail before Endpoint dispatch. | +| `test_credential_role_subresource_matrix` | Each service action requires the same exact `acquire-token`, `refresh-token`, `revoke-token`, or `inspect-metadata` value in `spec.allowedOperations` and `use-credential` subresources; each lifecycle action requires matching ordinary CRUD plus `admin-credential/create`, `admin-credential/update-spec`, or `admin-credential/delete`; empty/wildcard/unknown/mismatched/coarse aliases fail before Endpoint dispatch. | | `test_token_refresh_redaction` | Access token, refresh token, cookies, device codes, login URLs, and MSAL cache canaries are absent from spec/status/audit/OTEL/logs/Debug. | | `test_credential_identity_audit_only` | Credential name, canonical ResourceRef, UID, and derived-digest canaries are absent from status, errors, logs/Debug, every OTEL Resource/span attribute, and every metric label; the authorized bounded audit record retains only `resource_name_digest`, and an unauthorized request cannot elicit identity-bearing audit. Generic allowlisted OTEL Resource attributes remain present. | | `test_e2e_record_only_delivery` | Raw access token appears only in the Entrablau→consumer Noise_KK record; bus/controller/helper see ciphertext only. | From e222d635a7750280c336696322e4736339f9b13a Mon Sep 17 00:00:00 2001 From: John Vicondoa Date: Fri, 24 Jul 2026 14:18:53 -0700 Subject: [PATCH 108/115] docs: integrate fifth-round authorization fixes --- docs/specs/ADR-046-cli-and-operations.md | 25 ++++++ .../specs/ADR-046-componentsession-and-bus.md | 16 +++- docs/specs/ADR-046-decision-register.md | 2 +- docs/specs/ADR-046-implementation-graph.json | 42 +++++----- docs/specs/ADR-046-nix-configuration.md | 25 +++--- .../ADR-046-resource-api-and-authorization.md | 47 +++++++---- docs/specs/ADR-046-resources-credential.md | 31 ++++--- docs/specs/ADR-046-resources-network.md | 2 + docs/specs/ADR-046-resources-zone-control.md | 59 ++++++++----- .../ADR-046-security-and-threat-model.md | 71 ++++++++++------ docs/specs/ADR-046-spec-set.json | 42 +++++----- .../ADR-046-telemetry-audit-and-support.md | 47 +++++++++-- docs/specs/ADR-046-work-items.json | 82 +++++++++---------- docs/specs/ADR-046-zone-routing.md | 43 ++++++---- .../ADR-046-provider-audio-pipewire.md | 10 +-- .../ADR-046-provider-credential-entra.md | 12 ++- ...46-provider-credential-managed-identity.md | 66 ++++++++------- ...-046-provider-credential-secret-service.md | 43 ++++++---- .../ADR-046-provider-network-local.md | 6 +- ...6-provider-runtime-azure-container-apps.md | 32 ++++++-- ...-provider-runtime-azure-virtual-machine.md | 44 +++++++++- ...R-046-provider-runtime-cloud-hypervisor.md | 4 +- .../ADR-046-provider-runtime-qemu-media.md | 42 ++++++---- .../ADR-046-provider-transport-vsock.md | 35 ++++++-- 24 files changed, 539 insertions(+), 289 deletions(-) diff --git a/docs/specs/ADR-046-cli-and-operations.md b/docs/specs/ADR-046-cli-and-operations.md index d31968808..1c6d5e78a 100644 --- a/docs/specs/ADR-046-cli-and-operations.md +++ b/docs/specs/ADR-046-cli-and-operations.md @@ -1964,6 +1964,31 @@ class: `implemented-and-reachable`. **Retained behavior:** streaming audit lines; `--strict` (exit non-zero on parse error); bounded line lengths; redacted payloads. +### `d2b zone audit export` + +```text +d2b zone audit export [--zone ] + [--after ] + [--before ] +``` + +The command invokes exactly `d2b.audit.v3.AuditService/Export` and requires the +admin-only `audit-export` session verb. CLI help describes it as a diagnostic +service grant, not a Zone resource verb; it supplies no resource `get`, `list`, +or mutation authority. + +### `d2b zone support-bundle` + +```text +d2b zone support-bundle [--zone ] +``` + +The command invokes exactly +`d2b.support.v3.SupportService/GenerateBundle` and requires the admin-only +`support-bundle` session verb. CLI help does not describe it as a resource read +grant: the admitted service performs bounded redacted internal reads and emits +NDJSON. + ## `d2b op` — operation inspection ``` diff --git a/docs/specs/ADR-046-componentsession-and-bus.md b/docs/specs/ADR-046-componentsession-and-bus.md index fc5c584f5..c671818f2 100644 --- a/docs/specs/ADR-046-componentsession-and-bus.md +++ b/docs/specs/ADR-046-componentsession-and-bus.md @@ -155,6 +155,7 @@ subject Zone session purpose/service verb = connect | invoke | open-stream | relay | attach | cancel | observe + | audit-export | support-bundle method/stream kind target ResourceRef/Provider/Host/Guest forwarded target verb and target Zone (relay only) @@ -172,9 +173,16 @@ language. already-authenticated inbound ZoneLink/transport subject to pass an already-admitted invocation or named stream to the route-selected next authorized hop. The peer cannot supply or replace the subject, target verb, -target Zone, next hop, or authorization result. A relay grant conveys no -resource CRUD, identity mapping, capability widening, attachment, credential, -or local lifecycle authority. +target Zone, next hop, named resource or nameless `List`/`Watch` selector, +bounded filters, or authorization result. A relay grant conveys no resource +CRUD, identity mapping, capability widening, attachment, credential, or local +lifecycle authority. + +`audit-export` and `support-bundle` are the two admin-only diagnostic session +verbs. They bind exactly to `d2b.audit.v3.AuditService/Export` and +`d2b.support.v3.SupportService/GenerateBundle`, respectively. Neither may +appear in a rule's resource `verbs`, and neither implies any resource read or +mutation authority. Authorization: @@ -420,5 +428,5 @@ transcript/session generation digest, route, and fixed outcome. | Detailed design | Exact service/resource routes, RBAC, pinned reverse route, cancellation, named stream bridge, no wildcard pub/sub Primary reuse disposition: `adapt`. Preserved source-plan detail: extract/adapt. | | Integration | Every ResourceClient/controller/Provider/CLI service | | Data migration | None — full d2b 3.0 reset; no prior state to migrate | -| Validation | Message isolation; closed session-verb enum including `relay`; relay missing/target verb missing/provider self-assertion fail-closed vectors; route/auth revocation; fairness; reconnect; no direct-store path | +| Validation | Message isolation; closed session-verb enum including `relay`, `audit-export`, and `support-bundle`; exact diagnostic service/method binding with no implied resource grant; relay missing/target verb missing/provider self-assertion fail-closed vectors; named-target and nameless List/Watch selector/filter preservation at every hop; route/auth revocation; fairness; reconnect; no direct-store path | | Removal proof | Old direct dispatch branches removed only after route parity | diff --git a/docs/specs/ADR-046-decision-register.md b/docs/specs/ADR-046-decision-register.md index 1490874d3..02b416e55 100644 --- a/docs/specs/ADR-046-decision-register.md +++ b/docs/specs/ADR-046-decision-register.md @@ -59,7 +59,7 @@ review metadata, or implementation status. | D037 | The universal status phase enum is `Pending`, `Ready`, `Succeeded`, `Degraded`, `Failed`, `Deleted`, or `Unknown`. | Conditions carry starting/deleting/retrying and other detail without multiplying common phases. | Resource object/API/store, all controllers | | D038 | Final deletion has no retained resource tombstone. | After finalizers complete, core emits a Deleted status revision and removes the resource immediately. revision_log is the only deletion history; GET returns not found. | Resource store/API/reconciliation | | D039 | ComponentSession uses Noise-based authenticated/record-protected profiles. | Copy/adapt the proven `d2b-session` and `d2b-session-unix` implementation from main, then reversion, integrate, and validate it against v3 Zone/resource/RBAC contracts. | ComponentSession/bus/security | -| D040 | ComponentSession and resource control use one authorization system. | Session authentication maps to a canonical resource subject; native Role/RoleBinding authorizes the closed session verbs `connect`, `invoke`, `open-stream`, `relay`, `attach`, `cancel`, and `observe`, plus resource verbs. `relay` permits only an authenticated ZoneLink/transport subject to forward an already-admitted invocation/stream one authorized hop; every forwarding hop independently requires relay plus the target verb, and relay grants no CRUD, identity mapping, capability widening, or local lifecycle authority. Relay grants are core-generated/ZoneLink-scoped unless explicit durable local-admin policy permits an exact bounded grant; wildcard/self-asserted grants fail closed. Handshakes cannot self-assert roles; revision-bound leases revoke on policy change. | ComponentSession/bus/resource API/RBAC | +| D040 | ComponentSession and resource control use one authorization system. | Session authentication maps to a canonical resource subject; native Role/RoleBinding authorizes the closed resource verbs `get`, `list`, `watch`, `create`, `update-spec`, `update-status`, `update-metadata`, `update-finalizers`, `delete`, `use-credential`, and `admin-credential`, and the closed session verbs `connect`, `invoke`, `open-stream`, `relay`, `attach`, `cancel`, `observe`, `audit-export`, and `support-bundle`. `use-credential` uses only exact Credential operation subresources; `admin-credential` uses only exact lifecycle subresources and supplements ordinary CRUD. `audit-export` and `support-bundle` bind only their exact admin diagnostic service methods and grant no resource authority. `relay` permits only an authenticated ZoneLink/transport subject to forward an already-admitted invocation/stream one authorized hop; every forwarding hop independently requires relay plus the target verb, preserves named targets or nameless List/Watch filters exactly, and relay grants no CRUD, identity mapping, capability widening, or local lifecycle authority. Relay grants are core-generated/ZoneLink-scoped unless explicit durable local-admin policy permits an exact bounded grant; wildcard/self-asserted grants fail closed. Handshakes cannot self-assert roles; revision-bound leases revoke on policy change. | ComponentSession/bus/resource API/RBAC | | D041 | Main is an unrestricted implementation reuse source, not the v3 baseline. | Work items may copy/adapt any useful main code. Each names the exact main commit/file/symbol/tests, proves the selected behavior, maps it to exact v3 destinations, and excludes unrelated ADR 0045 architecture. | Every implementation work item | | D042 | Unsafe-local is not a separate v3 Provider. | It becomes a user-only Host using Provider/system-core, defaultDomain=user, allowedDomains=[user], and defaultUserRef. Its explicit no-isolation posture/warnings remain in Host status/UI; Processes use normal Process Providers. | Host/system-core/Nix/UI/reset | | D043 | Freeze four Guest Provider families. | runtime-cloud-hypervisor, runtime-qemu-media, runtime-azure-container-apps, and runtime-azure-virtual-machine implement Guest. | Guest/Provider dossiers | diff --git a/docs/specs/ADR-046-implementation-graph.json b/docs/specs/ADR-046-implementation-graph.json index aa7f5c6dd..8b414dbb9 100644 --- a/docs/specs/ADR-046-implementation-graph.json +++ b/docs/specs/ADR-046-implementation-graph.json @@ -12300,7 +12300,7 @@ "destinations" : [ "`packages/d2b-provider-audio-pipewire/src/telemetry.rs`" ], - "detailedDesign" : "Emit closed-label Service and Binding metrics plus post-commit audit. Service events distinguish only `owner\\|projection` and closed outcomes; microphone arbitration emits closed transition/outcome plus bounded aggregate counts only. Metrics expose active count `0|1`, aggregate queue depth, and closed arbitration state without Zone/Binding/handle/position labels. Events omit authority keys, import/export keys, remote identity, stream/session ids, endpoints, client timestamps, and queue entries. Enforcement metrics cover owner-local and projection-routed calls without exposing route identity. ProcessEffect audit remains Process Provider-owned.", + "detailedDesign" : "Emit closed-label Service and Binding metrics plus post-commit audit. Service events distinguish only `owner|projection` and closed outcomes; microphone arbitration emits closed transition/outcome plus bounded aggregate counts only. Metrics expose active count `0|1`, aggregate queue depth, and closed arbitration state without Zone/Binding/handle/position labels. Events omit authority keys, import/export keys, remote identity, stream/session ids, endpoints, client timestamps, and queue entries. Enforcement metrics cover owner-local and projection-routed calls without exposing route identity. ProcessEffect audit remains Process Provider-owned.", "entryContracts" : [ "ADR-046-provider-audio-pipewire" ], @@ -12492,7 +12492,7 @@ "destinations" : [ "`packages/d2b/src/zone_audit.rs` (new `d2b zone audit export` subcommand); `packages/d2b/tests/zone_audit_contract.rs`" ], - "detailedDesign" : "`d2b zone audit export` opens segments read-only (shared flock), streams NDJSON to stdout, validates hash chain inline, reports breaks as inline error records, enforces `audit-export` verb via resource API (admin-only, same `SO_PEERCRED`/Role check as current `ExportBrokerAuditOk`). Assert no `realm`, `node`, `workload_id` fields in exported records. Primary reuse disposition: `adapt`. Preserved source-plan detail: adapt audit CLI contract test (daemon-down/exit behavior); adapt broker export test to new `zone` field and v3 record schema.", + "detailedDesign" : "`d2b zone audit export` opens segments read-only (shared flock), streams NDJSON to stdout, validates hash chain inline, reports breaks as inline error records, and invokes only `d2b.audit.v3.AuditService/Export` under the admin-only `audit-export` session verb (same `SO_PEERCRED`/Role check as current `ExportBrokerAuditOk`). The session grant provides no Zone resource authority. Assert no `realm`, `node`, `workload_id` fields in exported records. Primary reuse disposition: `adapt`. Preserved source-plan detail: adapt audit CLI contract test (daemon-down/exit behavior); adapt broker export test to new `zone` field and v3 record schema.", "entryContracts" : [ "ADR-046-telemetry-audit-and-support" ], @@ -12735,7 +12735,7 @@ ], "specId" : "ADR-046-componentsession-and-bus", "topologicalRank" : 6, - "validation" : "Message isolation; closed session-verb enum including `relay`; relay missing/target verb missing/provider self-assertion fail-closed vectors; route/auth revocation; fairness; reconnect; no direct-store path", + "validation" : "Message isolation; closed session-verb enum including `relay`, `audit-export`, and `support-bundle`; exact diagnostic service/method binding with no implied resource grant; relay missing/target verb missing/provider self-assertion fail-closed vectors; named-target and nameless List/Watch selector/filter preservation at every hop; route/auth revocation; fairness; reconnect; no direct-store path", "wave" : "W1" }, { @@ -13575,7 +13575,7 @@ "destinations" : [ "`packages/d2b-provider-credential-entra/src/{lib.rs,controller.rs,service.rs,controller_main.rs,agent_main.rs,audit.rs,telemetry.rs}` and corresponding tests/integration docs" ], - "detailedDesign" : "Implement secret-free controller/helper; require Credential base `identityGuestRef` and `loginEndpointRef`; resolve dependency alias `entra-login-token`; validate same-Zone Guest placement and exact `consumerRef`; implement typed `EntraTokenLeaseClient` whose production implementation is the Entrablau Endpoint; implement `BeginLogin`/`ObserveLogin`/`CancelLogin` projection to Credential base interaction status; route access-token leases end-to-end from Entrablau service to exact consumer over Noise_KK; keep all refresh/login/TPM state inside the Entrablau Guest; declare no Provider state Volume; reject Host placement and all ambient fallback chains. Primary reuse disposition: `adapt`. Preserved source-plan detail: Adapt tests/types where non-secret; replace production token acquisition with Entrablau Endpoint client.", + "detailedDesign" : "Implement secret-free controller/helper; require Credential base `identityGuestRef` and `loginEndpointRef`; resolve dependency alias `entra-login-token`; validate same-Zone Guest placement and exact `consumerRef`; implement typed `EntraTokenLeaseClient` whose production implementation is the Entrablau Endpoint; implement `BeginLogin`/`ObserveLogin`/`CancelLogin` projection to Credential base interaction status; admit service calls only when the same exact allowed operation (`acquire-token`, `refresh-token`, `revoke-token`, or `inspect-metadata`) is present in both `Credential.spec.allowedOperations` and the canonical `use-credential` Role `subresources`; require matching `create`, `update-spec`, or `delete` under `admin-credential` in addition to ordinary CRUD, with no Credential-specific Role fields or coarse aliases; emit the login Endpoint with canonical `visibility = provider` and an exact `consumerPolicy` for the orchestration Provider and configured consumer; route access-token leases end-to-end from Entrablau service to exact consumer over Noise_KK; keep all refresh/login/TPM state inside the Entrablau Guest; declare no Provider state Volume; reject Host placement and all ambient fallback chains; enforce Credential name/ResourceRef/UID/digest as audit-only observable identity, with only `resource_name_digest` retained in authorized bounded audit and no Credential identity in status/errors/logs/OTEL Resource or span attributes/metric labels. Primary reuse disposition: `adapt`. Preserved source-plan detail: Adapt tests/types where non-secret; replace production token acquisition with Entrablau Endpoint client.", "entryContracts" : [ "ADR-046-provider-credential-entra" ], @@ -13625,7 +13625,7 @@ "destinations" : [ "packages/d2b-provider-credential-managed-identity/src/controller.rs; packages/d2b-contracts/src/v3/credential_controller.rs" ], - "detailedDesign" : "Managed-identity-specific controller design: implement async reconcile and agent spawn/teardown from §Async reconcile; enforce system-only domain; spawn agent on Credential admission plus dependency-ready, not on `phase=Ready`; implement `observeInterval=30s` health-check RPC to the agent, which calls `InspectMetadata` on the injected client; controller never calls IMDS; derive idempotency key as `SHA-256(UID \\|\\| \":\" \\|\\| rotationGeneration.to_le_bytes() \\|\\| \":\" \\|\\| operation_class_byte)`; enforce `MAX_LOCAL_LEASES=256` in the resource store; implement Deleted-phase closure by clearing `provider-revoke` only after agent Process deletion and revocation confirmation while core/audit own Deleted revision and deletion record. Primary reuse disposition: `adapt`. Preserved source-plan detail: adapt shared Credential controller lifecycle to managed-identity controller/agent spawn and teardown.", + "detailedDesign" : "Managed-identity-specific controller design: implement async reconcile and agent spawn/teardown from §Async reconcile; enforce system-only domain; spawn agent on Credential admission plus dependency-ready, not on `phase=Ready`; implement `observeInterval=30s` health-check RPC to the agent, which calls `InspectMetadata` on the injected client; controller never calls IMDS; derive idempotency key as `SHA-256(UID || \":\" || rotationGeneration.to_le_bytes() || \":\" || operation_class_byte)`; enforce `MAX_LOCAL_LEASES=256` in the resource store; implement Deleted-phase closure by clearing `provider-revoke` only after agent Process deletion and revocation confirmation while core/audit own Deleted revision and deletion record. Primary reuse disposition: `adapt`. Preserved source-plan detail: adapt shared Credential controller lifecycle to managed-identity controller/agent spawn and teardown.", "entryContracts" : [ "ADR-046-provider-credential-managed-identity" ], @@ -13836,7 +13836,7 @@ "destinations" : [ "`packages/d2b-contracts/src/v3/credential.rs`" ], - "detailedDesign" : "Define `CredentialSpec`, `CredentialStatus`, `CredentialLeaseHandle` (opaque bounded newtype), `CredentialRotationPolicy`, `CredentialRevocationPolicy`, `CredentialScope`, `OperationClass` enum, `CredentialLeaseState`, `PlacementBinding`, `CredentialConditionType`, and all serde/validation/redaction helpers; reuse `OpaqueAzureRef` from v3 baseline directly; enforce zero-secret-bytes charset validation on all string fields at construction Primary reuse disposition: `adapt`. Preserved source-plan detail: extract and adapt.", + "detailedDesign" : "Define `CredentialSpec`, `CredentialStatus`, `CredentialLeaseHandle` (opaque bounded newtype), `CredentialRotationPolicy`, `CredentialRevocationPolicy`, `CredentialScope`, `OperationClass` enum, `CredentialLeaseState`, `PlacementBinding`, `CredentialConditionType`, and all serde/validation/redaction helpers; reuse `OpaqueAzureRef` from v3 baseline directly; enforce zero-secret-bytes charset validation on all string fields at construction; prevent Credential name/ResourceRef/UID or any identity-derived digest from serializing into any status layer or typed error Primary reuse disposition: `adapt`. Preserved source-plan detail: extract and adapt.", "entryContracts" : [ "ADR-046-resources-credential" ], @@ -13852,7 +13852,7 @@ ], "specId" : "ADR-046-resources-credential", "topologicalRank" : 9, - "validation" : "Schema golden vectors; charset/length tests; serde unknown-field rejection; `OpaqueAzureRef` round-trip and secret-shape rejection parity; `leaseHandle` and `sourceVersion` opaque newtype tests; status redaction tests", + "validation" : "Schema golden vectors; charset/length tests; serde unknown-field rejection; `OpaqueAzureRef` round-trip and secret-shape rejection parity; `leaseHandle` and `sourceVersion` opaque newtype tests; status/error redaction tests with Credential name/ResourceRef/UID/digest canaries", "wave" : "W4" }, { @@ -13860,7 +13860,7 @@ "destinations" : [ "`packages/d2b-contracts/proto/v3/credential.proto`; `packages/d2b-credential-service/src/{service.rs, client.rs, server.rs}`" ], - "detailedDesign" : "Define `d2b.credential.v3` protobuf service with methods: `Status`, `AcquireToken`, `RefreshToken`, `RevokeToken`, `SignChallenge`, `InspectMetadata`; each request carries `credential_ref`, `operation_class`, `operation_id`, `idempotency_key`, `requested_expiry_unix_ms`, `deadline_unix_ms`; `Status`, `RevokeToken`, and `InspectMetadata` responses carry only non-secret metadata (leaseHandle digest, rotationGeneration, sourceVersion, expiresAtUnixMs, state, outcome code); `AcquireToken`, `RefreshToken`, and `SignChallenge` responses additionally include a `delivery_session_params` field carrying the binding contract fields required to establish the end-to-end credential-delivery ComponentSession (see §Credential-delivery endpoint contract); the token bytes themselves travel in the separate Noise-encrypted delivery session, never in the outer DTO; strict unknown-field rejection; bounded message sizes; all record wrappers for delivery sessions must be zeroizing types", + "detailedDesign" : "Define `d2b.credential.v3` protobuf service with exactly five operation methods: `AcquireToken`, `RefreshToken`, `RevokeToken`, `SignChallenge`, and `InspectMetadata`; map those methods one-to-one to the same exact operation class in `spec.allowedOperations` and the existing Role `subresources` field (`acquire-token`, `refresh-token`, `revoke-token`, `sign-challenge`, or `inspect-metadata`) under the canonical `use-credential` resource verb; derive the operation class from the method rather than accepting a caller-selected operation-class field; each request carries `credential_ref`, `operation_id`, `idempotency_key`, `requested_expiry_unix_ms`, `deadline_unix_ms`; map administrative lifecycle to matching `admin-credential` subresources `create`, `update-spec`, and `delete`, supplemental to ordinary CRUD, never to a new Role field or method-name alias; `RevokeToken` and `InspectMetadata` responses carry only non-secret metadata (leaseHandle digest, rotationGeneration, sourceVersion, expiresAtUnixMs, state, outcome code); `AcquireToken`, `RefreshToken`, and `SignChallenge` responses additionally include a `delivery_session_params` field carrying the binding contract fields required to establish the end-to-end credential-delivery ComponentSession (see §Credential-delivery endpoint contract); the token bytes themselves travel in the separate Noise-encrypted delivery session, never in the outer DTO; strict unknown-field rejection; bounded message sizes; all record wrappers for delivery sessions must be zeroizing types", "entryContracts" : [ "ADR-046-resources-credential" ], @@ -13877,7 +13877,7 @@ ], "specId" : "ADR-046-resources-credential", "topologicalRank" : 10, - "validation" : "Protocol golden vectors for each method; malformed/oversize rejection; `leaseHandle` opacity tests (secret-canary must not appear in outer DTO or delivery routing metadata); locked/unavailable/denied/expired state tests; delivery session binding contract round-trip; zeroizing record type unit tests; delivery channel never materialized in non-delivery method tests", + "validation" : "Protocol golden vectors for each method; Role matrix for the five exact allowed-operation subresources under `use-credential` and exact `create`, `update-spec`, and `delete` subresources under `admin-credential`; prove admin permission is supplemental to ordinary CRUD; deny empty/wildcard/unknown/mismatched subresources, alternate Credential-operation Role fields, and method-name aliases before Provider dispatch; malformed/oversize rejection; `leaseHandle` opacity tests (secret-canary must not appear in outer DTO or delivery routing metadata); locked/unavailable/denied/expired state tests; delivery session binding contract round-trip; zeroizing record type unit tests; delivery channel never materialized in non-delivery method tests", "wave" : "W4" }, { @@ -13910,7 +13910,7 @@ "destinations" : [ "`packages/d2b-provider-credential-entra/src/{lib.rs, controller.rs, service.rs, main.rs}`; `packages/d2b-provider-credential-entra/tests/{lifecycle.rs, conformance.rs, faults.rs, canary.rs, delivery.rs, placement.rs}`; `packages/d2b-provider-credential-entra/integration/{container-service.sh, guest-placement.nix, cleanup-rollback.sh}`; `packages/d2b-provider-credential-entra/README.md` (all §Provider README required sections)" ], - "detailedDesign" : "Adapt `EntraCredentialProvider` and `EntraCredentialProviderFactory` to v3 service; replace v2 `AgentPlacementBinding` with v3 `PlacementBinding` enum (user-agent, guest-agent only; reject host-system); validate `tenantId` config field using `OpaqueAzureRef::parse` from v3 baseline `d2b-realm-provider/src/credential.rs` (note: current v3 source field is named via `AzureControlPlaneRef`; target field name is `tenantId`); retain `EntraCredentialClient` trait unchanged; map `EntraClientError::InteractionRequired` to `credential-provider-unavailable` (not denied); enforce `EntraCredentialOwner::ExactConsumer` so only the declared `consumerRef` may acquire Primary reuse disposition: `adapt`. Preserved source-plan detail: copy and adapt.", + "detailedDesign" : "Adapt `EntraCredentialProvider` and `EntraCredentialProviderFactory` to v3 service; replace v2 `AgentPlacementBinding` with v3 `PlacementBinding` enum (user-agent, guest-agent only; reject host-system); validate `tenantId` config field using `OpaqueAzureRef::parse` from v3 baseline `d2b-realm-provider/src/credential.rs` (note: current v3 source field is named via `AzureControlPlaneRef`; target field name is `tenantId`); retain `EntraCredentialClient` trait unchanged; map `EntraClientError::InteractionRequired` to `credential-provider-unavailable` (not denied); enforce `EntraCredentialOwner::ExactConsumer` so only the declared `consumerRef` may acquire; enforce canonical Credential verb/subresource admission, provider-visible Endpoint plus exact consumer policy, and audit-only Credential identity as frozen in the dedicated Entra dossier Primary reuse disposition: `adapt`. Preserved source-plan detail: copy and adapt.", "entryContracts" : [ "ADR-046-resources-credential" ], @@ -13927,7 +13927,7 @@ ], "specId" : "ADR-046-resources-credential", "topologicalRank" : 11, - "validation" : "**`src/` unit**: `EntraCredentialClient` trait API, `OpaqueAzureRef::parse` on `tenantId`, `EntraCredentialOwner::ExactConsumer` guard, `EntraClientState` transitions. **`tests/` Cargo integration**: `lifecycle.rs` (acquire/refresh/revoke/inspect with `FakeEntraClient`); `conformance.rs` (all conformance arms); `faults.rs` (interaction-required → unavailable, generation-mismatch, colocated-consumer rejection); `canary.rs` (`credential_canary` and `endpoint_canary` absent from every response and delivery record); `delivery.rs` (delivery-session binding, zeroizing, replay-safe); `placement.rs` (host-system placement rejected). **`integration/` fixtures**: `container-service.sh`; `guest-placement.nix` (user-domain and system-domain Process on Guest in runNixOSTest); `cleanup-rollback.sh`.", + "validation" : "**`src/` unit**: `EntraCredentialClient` trait API, `OpaqueAzureRef::parse` on `tenantId`, `EntraCredentialOwner::ExactConsumer` guard, `EntraClientState` transitions. **`tests/` Cargo integration**: `lifecycle.rs` (acquire/refresh/revoke/inspect with `FakeEntraClient`); `conformance.rs` (all conformance arms, canonical Endpoint visibility/consumer policy, and exact Role verb/subresource mapping); `faults.rs` (interaction-required → unavailable, generation-mismatch, colocated-consumer and mismatched Role-subresource rejection); `canary.rs` (secret plus Credential name/ResourceRef/UID/digest canaries absent from status/errors/logs and every OTEL Resource/span attribute/metric label; authorized audit retains only `resource_name_digest`); `delivery.rs` (delivery-session binding, zeroizing, replay-safe); `placement.rs` (host-system placement rejected). **`integration/` fixtures**: `container-service.sh`; `guest-placement.nix` (user-domain and system-domain Process on Guest in runNixOSTest); `cleanup-rollback.sh`.", "wave" : "W4" }, { @@ -13960,7 +13960,7 @@ "destinations" : [ "`packages/d2b-provider-credential-/src/controller.rs`; `packages/d2b-contracts/src/v3/credential_controller.rs`" ], - "detailedDesign" : "Implement Credential controller handler conforming to `ADR-046-resource-reconciliation` async loop; implement `reconcile`, `observe`, `finalize`, `drain`, and `health` handlers; implement rotation state machine (proactive/on-expiry/on-demand policies); implement `provider-revoke` finalizer execution with `revocation.onOwnerDelete` policy; implement provider-generation-change detection and revocation; implement `CredentialReady`, `RotationDue`, `ProviderUnavailable`, `LeaseRevoked` condition logic; implement bounded idempotency key derivation (Credential UID + rotationGeneration + operation class, no secret material); implement `observeInterval=30s` health check calling `InspectMetadata`; bounded retry/backpressure with typed `credential-rotation-failed` outcome; enforce `MAX_LOCAL_LEASES=256` per controller provider instance", + "detailedDesign" : "Implement Credential controller handler conforming to `ADR-046-resource-reconciliation` async loop; implement `reconcile`, `observe`, `finalize`, `drain`, and `health` handlers; implement rotation state machine (proactive/on-expiry/on-demand policies); implement `provider-revoke` finalizer execution with `revocation.onOwnerDelete` policy; implement provider-generation-change detection and revocation; implement `CredentialReady`, `RotationDue`, `ProviderUnavailable`, `LeaseRevoked` condition logic; use controller Roles with the exact allowed-operation subresource matching each `use-credential` service call, while status/finalizer writes retain their separate structural ownership checks; never add a Credential-specific Role field or coarse alias; implement bounded idempotency key derivation (Credential UID + rotationGeneration + operation class, no secret material); implement `observeInterval=30s` health check calling `InspectMetadata`; bounded retry/backpressure with typed `credential-rotation-failed` outcome; enforce `MAX_LOCAL_LEASES=256` per controller provider instance", "entryContracts" : [ "ADR-046-resources-credential" ], @@ -13978,7 +13978,7 @@ ], "specId" : "ADR-046-resources-credential", "topologicalRank" : 11, - "validation" : "Controller state-machine golden vectors; rotation-policy matrix (proactive/on-demand/on-expiry × success/locked/unavailable/expired); finalizer execution tests; provider-generation-change revocation tests; idempotency key derivation tests; observe-interval drift detection test; canary tests confirm zero secret bytes in all controller-written status fields", + "validation" : "Controller state-machine golden vectors; rotation-policy matrix (proactive/on-demand/on-expiry × success/locked/unavailable/expired); exact Role-subresource admission matrix; finalizer execution tests; provider-generation-change revocation tests; idempotency key derivation tests; observe-interval drift detection test; canary tests confirm zero secret bytes and no Credential name/ResourceRef/UID/digest in any controller-written status or error", "wave" : "W4" }, { @@ -14004,7 +14004,7 @@ ], "specId" : "ADR-046-resources-credential", "topologicalRank" : 10, - "validation" : "**(eval/build)**: nix-unit golden JSON envelope for each example (spec shape, no management labels in Nix output, sort, digest); assertion-failure tests for secret-shaped audience, mismatched providerRef/domainFilter, proactiveWindow > half maxLifetime, duplicate binding tuple, unresolved refs; artifact catalog: assertion-failure for missing `artifactId`, wrong-type `artifactId`, duplicate catalog ID; bundle + artifact catalog digest round-trip; artifact catalog store-path absence from resource bundle and status; Provider-specific schema cross-check; `make test-drift` schema drift gate. **(runtime integration in `tests/host-integration/`)**: `credential-cleanup-basic` (removed resource reaches Deleted); `credential-cleanup-nonblocking` (activation Ready before cleanup finalizer finishes); `credential-cleanup-pending-status` (Cleanup=True on removed resource, PendingCleanup=True on Provider); `credential-cleanup-stalled` (Degraded stall detection and recovery); `credential-cleanup-controller-children-preserved` (ownerRef children cleaned by Credential controller); `credential-cleanup-no-dynamic-deletion` (controller-created Credential with `managedBy = \"controller\"` not deleted); `credential-retained-generation-count` (up to retainedConfigurationMax bundles retained; rollback re-creates from retained bundle; oldest pruned when count exceeded); `credential-bundle-digest-mismatch` (tampered bundle aborts activation).", + "validation" : "**(eval/build)**: nix-unit golden JSON envelope for each example (spec shape, no management labels in Nix output, sort, digest); assertion-failure tests for secret-shaped audience, mismatched providerRef/domainFilter, proactiveWindow > half maxLifetime, duplicate binding tuple, unresolved refs; generated activation Role contains `admin-credential` with exact `subresources = [ \"create\" \"update-spec\" \"delete\" ]`, no coarse alias, and no undeclared Role field; permission tests prove each lifecycle action also requires its ordinary CRUD verb; artifact catalog: assertion-failure for missing `artifactId`, wrong-type `artifactId`, duplicate catalog ID; bundle + artifact catalog digest round-trip; artifact catalog store-path absence from resource bundle and status; Provider-specific schema cross-check; `make test-drift` schema drift gate. **(runtime integration in `tests/host-integration/`)**: `credential-cleanup-basic` (removed resource reaches Deleted); `credential-cleanup-nonblocking` (activation Ready before cleanup finalizer finishes); `credential-cleanup-pending-status` (Cleanup=True on removed resource, PendingCleanup=True on Provider); `credential-cleanup-stalled` (Degraded stall detection and recovery); `credential-cleanup-controller-children-preserved` (ownerRef children cleaned by Credential controller); `credential-cleanup-no-dynamic-deletion` (controller-created Credential with `managedBy = \"controller\"` not deleted); `credential-retained-generation-count` (up to retainedConfigurationMax bundles retained; rollback re-creates from retained bundle; oldest pruned when count exceeded); `credential-bundle-digest-mismatch` (tampered bundle aborts activation).", "wave" : "W4" }, { @@ -14012,7 +14012,7 @@ "destinations" : [ "`packages/d2b-provider-credential-/src/audit.rs`, `telemetry.rs`; `packages/d2b-contract-tests/tests/credential_audit.rs`" ], - "detailedDesign" : "Implement audit record emission for all credential service methods and controller events using the field set defined in §Audit, with Credential identity represented only by the authorized bounded `resource_name_digest`; implement OTEL span/metric emission using the closed semantic label set in §OTEL and metrics, with expiry reported as a provider/placement aggregate and no Credential resource name, ResourceRef, UID, digest, derived identity token, Zone/resource-name-derived label, or non-allowlisted OTEL Resource attribute; retain applicable generic collector-allowlisted Resource attributes (`d2b.zone`, `d2b.provider`, `d2b.component`, and service fields); implement `contains_sensitive_shape` checks in all string fields of audit records and metric label values (adapted from `d2b-realm-provider/src/error.rs:contains_sensitive_shape`); add canary-enforcement tests that verify `\"secret-canary\"`, `\"entra-token-canary\"`, `\"managed-identity-canary\"`, Credential name/ref/UID/digest canaries, and Zone name values never appear in any metric label, span attribute, log line, or status field, and that Credential identity canaries are also absent from OTEL Resource attributes", + "detailedDesign" : "Implement audit record emission for all credential service methods and controller events using the field set defined in §Audit, with Credential identity represented only by the authorized bounded `resource_name_digest` after authorization and no identity-bearing record elicited by a denied request; implement OTEL span/metric emission using the closed semantic label set in §OTEL and metrics, with expiry reported as a provider/placement aggregate and no Credential resource name, ResourceRef, UID, digest, derived identity token, Zone/resource-name-derived label, or non-allowlisted OTEL Resource attribute; retain applicable generic collector-allowlisted Resource attributes (`d2b.zone`, `d2b.provider`, `d2b.component`, and service fields); implement `contains_sensitive_shape` checks in all string fields of audit records and metric label values (adapted from `d2b-realm-provider/src/error.rs:contains_sensitive_shape`); add canary-enforcement tests that verify `\"secret-canary\"`, `\"entra-token-canary\"`, `\"managed-identity-canary\"`, Credential name/ref/UID/digest canaries, and Zone name values never appear in any metric label, span attribute, log line, status field, error, or collector diagnostic, and that Credential identity canaries are also absent from OTEL Resource attributes", "entryContracts" : [ "ADR-046-resources-credential" ], @@ -14028,7 +14028,7 @@ ], "specId" : "ADR-046-resources-credential", "topologicalRank" : 12, - "validation" : "Canary tests across all three Provider crates; authorized audit record field-presence tests require `resource_name_digest` and reject raw Credential name/ResourceRef/UID; structural metric descriptor tests assert exact absence of `vm`, `zone`, `zone_id`, `zone_uid`, `credential_name`, `credential_ref`, `credential_uid`, `credential_digest`, `resource_name_digest`, and every resource-name-derived key; Credential name/ref/UID/digest canaries are absent from every OTEL Resource attribute, span attribute, and metric label; Zone-name canaries are absent from spans and labels while resource-attribute tests preserve the generic collector allowlist including `d2b.zone`, `d2b.provider`, `d2b.component`, and service fields; complete Credential metric/span frames pass the shared collector ingress validator, while adding `d2b.credential.name` or any Credential identity key/value rejects the whole frame", + "validation" : "Canary tests across all three Provider crates; authorized audit record field-presence tests require `resource_name_digest` and reject raw Credential name/ResourceRef/UID, while denied-request tests emit no identity-bearing audit; structural metric descriptor tests assert exact absence of `vm`, `zone`, `zone_id`, `zone_uid`, `credential_name`, `credential_ref`, `credential_uid`, `credential_digest`, `resource_name_digest`, and every resource-name-derived key; Credential name/ref/UID/digest canaries are absent from every status field, error, log/Debug line, collector diagnostic, OTEL Resource attribute, span attribute, and metric label; Zone-name canaries are absent from spans and labels while resource-attribute tests preserve the generic collector allowlist including `d2b.zone`, `d2b.provider`, `d2b.component`, and service fields; complete Credential metric/span frames pass the shared collector ingress validator, while adding `d2b.credential.name` or any Credential identity key/value rejects the whole frame", "wave" : "W4" }, { @@ -14872,7 +14872,7 @@ "destinations" : [ "`packages/d2b/src/zone_support_bundle.rs`, `packages/d2b/tests/zone_support_bundle_contract.rs`" ], - "detailedDesign" : "`d2b zone support-bundle [--zone ]` requires `support-bundle` verb. Reads bounded resource status snapshots (32 per type, 512 total; metadata + status only; no spec bytes; no `metadata.name`), controller queue depths, schema catalog (names+versions only), audit segment inventory, OTEL collector metrics summary, bounded structured log ring (2000 entries). NDJSON output. On quarantine: `bundle_completeness: \"partial\"`, exit 1. Primary reuse disposition: `adapt`. Preserved source-plan detail: reuse env-redirect sandbox scaffold.", + "detailedDesign" : "`d2b zone support-bundle [--zone ]` invokes only `d2b.support.v3.SupportService/GenerateBundle` under the admin-only `support-bundle` session verb; the caller receives no resource read authority. The service reads bounded resource status snapshots (32 per type, 512 total; metadata + status only; no spec bytes; no `metadata.name`), controller queue depths, schema catalog (names+versions only), audit segment inventory, OTEL collector metrics summary, and a bounded structured log ring (2000 entries). NDJSON output. On quarantine: `bundle_completeness: \"partial\"`, exit 1. Primary reuse disposition: `adapt`. Preserved source-plan detail: reuse env-redirect sandbox scaffold.", "entryContracts" : [ "ADR-046-telemetry-audit-and-support" ], @@ -14895,7 +14895,7 @@ "destinations" : [ "`packages/d2b-contracts/src/v3/host.rs`, `packages/d2b-contracts/src/v3/guest.rs`, `packages/d2b-contracts/src/v3/execution_policy.rs`, `packages/d2b-contracts/src/v3/process.rs`, `packages/d2b-contracts/src/v3/ephemeral_process.rs`, `packages/d2b-contracts/src/v3/user.rs`, `packages/d2b-contracts/src/v3/endpoint.rs`" ], - "detailedDesign" : "Implement strict typed Rust structs for HostSpec, GuestSpec, ExecutionPolicy, ExecutionSpec, SandboxSpec, BudgetSpec, MountSpec, NetworkUsageSpec, DeviceUsageSpec, EndpointSpec, EndpointConsumerPolicy, TelemetrySpec, ProcessSpec, EphemeralProcessSpec, UserSpec; EndpointSpec accepts exactly `owner\\|provider\\|zone`, and EndpointConsumerPolicy owns the only finer gates (`allowedSubjects`, `allowedProviderComponents`, `allowedOperations`) with no schema aliases; strict serde deny_unknown_fields; bounds/redaction on all string fields; stable error types; `UserSpec.osUsername` validated as OS username (1..255 bytes, no NUL/control/path-separator), not ResourceName grammar Primary reuse disposition: `adapt`. Preserved source-plan detail: extract and adapt.", + "detailedDesign" : "Implement strict typed Rust structs for HostSpec, GuestSpec, ExecutionPolicy, ExecutionSpec, SandboxSpec, BudgetSpec, MountSpec, NetworkUsageSpec, DeviceUsageSpec, EndpointSpec, EndpointConsumerPolicy, TelemetrySpec, ProcessSpec, EphemeralProcessSpec, UserSpec; EndpointSpec accepts exactly `owner|provider|zone`, and EndpointConsumerPolicy owns the only finer gates (`allowedSubjects`, `allowedProviderComponents`, `allowedOperations`) with no schema aliases; strict serde deny_unknown_fields; bounds/redaction on all string fields; stable error types; `UserSpec.osUsername` validated as OS username (1..255 bytes, no NUL/control/path-separator), not ResourceName grammar Primary reuse disposition: `adapt`. Preserved source-plan detail: extract and adapt.", "entryContracts" : [ "ADR-046-resources-host-guest-process-user" ], @@ -14909,7 +14909,7 @@ ], "specId" : "ADR-046-resources-host-guest-process-user", "topologicalRank" : 10, - "validation" : "Golden JSON vectors for each ResourceType; Endpoint vectors accept only `owner\\|provider\\|zone`, reject every legacy/private visibility alias, reject scalar/array `consumerPolicy` aliases, and cover each canonical consumer allowlist; a docs drift test parses every `type: Endpoint` YAML/Nix example and fails unless visibility is canonical and finer gates occur only under `consumerPolicy`; serde unknown-field rejection; bounds enforcement; `UserSpec.osUsername` OS-username validation (underscore allowed, NUL rejected)", + "validation" : "Golden JSON vectors for each ResourceType; Endpoint vectors accept only `owner|provider|zone`, reject every legacy/private visibility alias, reject scalar/array `consumerPolicy` aliases, and cover each canonical consumer allowlist; a docs drift test parses every `type: Endpoint` YAML/Nix example and fails unless visibility is canonical and finer gates occur only under `consumerPolicy`; serde unknown-field rejection; bounds enforcement; `UserSpec.osUsername` OS-username validation (underscore allowed, NUL rejected)", "wave" : "W5" }, { @@ -18218,7 +18218,7 @@ "destinations" : [ "`packages/d2b-contracts/src/v3/provider.rs` (component descriptor `stateNamespaces` field)" ], - "detailedDesign" : "Add `stateNamespaces: Vec` to the component descriptor (zero or more entries; a component declares an entry only when a payload passes the storage-need test; stateless components declare none); each entry includes `id`, `kind` (always `state`), `schemaId` (non-null), `schemaVersion`, `schemaDigest`, `persistenceClass` (must be `persistent`; `ephemeral`/`cache` rejected), `sensitivityClass`, `migrationPolicy`, `quotaBytes` (nonzero; minimum 4096), `storageNeed` (`secret` \\| `large-binary` \\| `private-unsafe-for-status` \\| `revision-unsuitable`), `sealingRequired`, `placementMode` (`guest-local` or `host-backed-guest` for Guest-targeted; omitted for Host-targeted), `hostCustodyPermitted` (required `true` for `host-backed-guest`; absent/false for `guest-local`), and `views`; there is no empty-payload (`schemaId: null`) namespace", + "detailedDesign" : "Add `stateNamespaces: Vec` to the component descriptor (zero or more entries; a component declares an entry only when a payload passes the storage-need test; stateless components declare none); each entry includes `id`, `kind` (always `state`), `schemaId` (non-null), `schemaVersion`, `schemaDigest`, `persistenceClass` (must be `persistent`; `ephemeral`/`cache` rejected), `sensitivityClass`, `migrationPolicy`, `quotaBytes` (nonzero; minimum 4096), `storageNeed` (`secret` | `large-binary` | `private-unsafe-for-status` | `revision-unsuitable`), `sealingRequired`, `placementMode` (`guest-local` or `host-backed-guest` for Guest-targeted; omitted for Host-targeted), `hostCustodyPermitted` (required `true` for `host-backed-guest`; absent/false for `guest-local`), and `views`; there is no empty-payload (`schemaId: null`) namespace", "entryContracts" : [ "ADR-046-provider-state" ], @@ -18457,7 +18457,7 @@ "destinations" : [ "`packages/d2b-core-controller/src/optional_state_admission.rs` (storage-need admission: reject a declared namespace whose payload is derivable from spec/status/core ledger/external observation with `component-state-not-justified`; only declared namespaces produce a Volume; stateless components produce none); `packages/d2b-core-controller/tests/optional_state_admission.rs` (hermetic: stateless component → no Volume; declared `storageNeed` variants accepted; unjustified namespace rejected; status-first restart revalidation — controller re-derives observed state from status/core ledger/external observation after restart and never treats a status field as authority); `packages/d2b-provider-volume-local/tests/status_bounds.rs` (hermetic: total canonical serialized status cap and provider-specific detail cap enforced; oversize status write → typed rejection; status carries no secret/path/argv/PID/unit/stream/ring content)" ], - "detailedDesign" : "Optional state-Volume admission is a fixed step in Core ProviderDeployment: for each component, if it declares no `stateNamespaces` entry it gets no Volume; for each declared entry, verify the `storageNeed` justification (`secret` \\| `large-binary` \\| `private-unsafe-for-status` \\| `revision-unsuitable`) and reject a namespace whose payload is fully derivable from spec/status/core ledger/external observation with `component-state-not-justified`. Fixed bootstrap components (`system-core`, `system-minijail`, first `volume-local` instance) declare no state Volume and reach Ready using resource `status`, the core Operation ledger, and external observation only; there is no bootstrap-storage mechanism. Status-bound enforcement: reject a status write whose total canonical serialized size exceeds the single canonical status cap, or whose provider-specific detail exceeds the detail cap, or whose condition/count/list/map entries exceed the bounded limits, with the typed status-oversize rejection; status writes occur only on material change.", + "detailedDesign" : "Optional state-Volume admission is a fixed step in Core ProviderDeployment: for each component, if it declares no `stateNamespaces` entry it gets no Volume; for each declared entry, verify the `storageNeed` justification (`secret` | `large-binary` | `private-unsafe-for-status` | `revision-unsuitable`) and reject a namespace whose payload is fully derivable from spec/status/core ledger/external observation with `component-state-not-justified`. Fixed bootstrap components (`system-core`, `system-minijail`, first `volume-local` instance) declare no state Volume and reach Ready using resource `status`, the core Operation ledger, and external observation only; there is no bootstrap-storage mechanism. Status-bound enforcement: reject a status write whose total canonical serialized size exceeds the single canonical status cap, or whose provider-specific detail exceeds the detail cap, or whose condition/count/list/map entries exceed the bounded limits, with the typed status-oversize rejection; status writes occur only on material change.", "entryContracts" : [ "ADR-046-provider-state" ], diff --git a/docs/specs/ADR-046-nix-configuration.md b/docs/specs/ADR-046-nix-configuration.md index dbb4aaf51..f5f1bff55 100644 --- a/docs/specs/ADR-046-nix-configuration.md +++ b/docs/specs/ADR-046-nix-configuration.md @@ -222,9 +222,9 @@ field merely because of the name. Each value must be individually classified. | Current `Capability` value | Current purpose | v3 target | | --- | --- | --- | | `Lifecycle` | Workload create/start/stop/inspect | Implicit grant from `Role` binding with `Host`/`Guest` verbs | -| `Exec` | Command execution (admin-only) | Role verb `exec` on `Process` or `EphemeralProcess` | -| `Pty` | Interactive pseudo-terminal | Role verb on `Process`/`EphemeralProcess`; exact verb name per resource-api/authz foundation spec | -| `Logs` | Durable execution logs with cursors | Role verb on `EphemeralProcess`; exact verb name per resource-api/authz foundation spec | +| `Exec` | Command execution (admin-only) | Exact service/method selector under session verb `invoke`; there is no `exec` resource verb | +| `Pty` | Interactive pseudo-terminal | Exact terminal service/method or stream selector under session verbs `invoke`, `open-stream`, and, only for local FD transfer, `attach` | +| `Logs` | Durable execution logs with cursors | Exact log service/stream selector under session verbs `invoke`/`open-stream`; no resource-verb alias | | `FileCopy` | Bounded file copy | Not in initial verb set; reimplemented as Volume view copy op if needed | | `PortForward` | One stream per connection | Not in initial verb set | | `PersistentShell` | Named shell operations | Service capability of `Provider/shell-terminal` | @@ -238,7 +238,7 @@ field merely because of the name. Each value must be individually classified. | `Hotplug` | Device hotplug | Typed Provider descriptor capability field; absent means fail closed; no Nix option | | `EphemeralSessions` | Provider-managed ephemeral sessions | Provider descriptor capability field; not a Role verb | | `ProviderManagedIsolation` | Non-host-owned isolation boundary | Typed Provider descriptor capability field; absent means fail closed; no Nix option | -| `ConfiguredLaunch` | Execute a configured launcher item | Role verb on `EphemeralProcess` or `Process`; exact verb name per resource-api/authz foundation spec | +| `ConfiguredLaunch` | Execute a configured launcher item | Exact configured-launch service/method selector under session verb `invoke`; no resource-verb alias | The verb set for `Role.rules[*].verbs` is owned by the resource-api and authz foundation spec. The compiler validates that every declared verb is in the @@ -246,7 +246,7 @@ closed set published by that spec; any verb not in that set is rejected at eval time. Core-reserved verbs (verbs bound to internal controller identity and not grantable to operator principals) cannot be declared in Nix RoleBindings; the compiler rejects them with a structured eval error. Cross-reference: -`ADR-046-resource-api-and-authz`. +`ADR-046-resource-api-and-authorization`. `Capability::Snapshots`, `Hotplug`, and `ProviderManagedIsolation` are retained as typed Provider descriptor capability fields, declared in the Provider's @@ -1538,8 +1538,7 @@ d2b.zones.dev.resources.process-operator-binding = { "Provider/system-systemd" "Provider/system-minijail" ]; - expiresAt = null; - narrowing = null; + scopeNarrowing = null; }; }; ``` @@ -1560,10 +1559,16 @@ Eval assertions: input. - Verbs must be from the closed set published by the resource-api and authz foundation spec; the compiler rejects any verb not in that set at eval time. +- Resource `verbs` uses the exact closed set `get`, `list`, `watch`, `create`, + `update-spec`, `update-status`, `update-metadata`, `update-finalizers`, + `delete`, `use-credential`, and `admin-credential`. - `sessionVerbs` uses the separate closed set `connect`, `invoke`, - `open-stream`, `relay`, `attach`, `cancel`, `observe`. `relay` is valid only - in `sessionVerbs`; generated option help describes it as one-hop - ZoneLink-scoped forwarding, not resource or lifecycle authority. + `open-stream`, `relay`, `attach`, `cancel`, `observe`, `audit-export`, and + `support-bundle`. `relay`, `audit-export`, and `support-bundle` are valid only + in `sessionVerbs`; generated option help describes relay as one-hop + ZoneLink-scoped forwarding and binds the two diagnostic verbs only to + `d2b.audit.v3.AuditService/Export` and + `d2b.support.v3.SupportService/GenerateBundle`, with no resource authority. - A relay-bearing Role/RoleBinding is rejected before bundle activation unless it has exact Zone/resource/name bounds and core-generated ZoneLink provenance, or durable explicit local-admin policy permits that exact bounded grant. diff --git a/docs/specs/ADR-046-resource-api-and-authorization.md b/docs/specs/ADR-046-resource-api-and-authorization.md index b159523ef..ce48a9d5d 100644 --- a/docs/specs/ADR-046-resource-api-and-authorization.md +++ b/docs/specs/ADR-046-resource-api-and-authorization.md @@ -316,7 +316,7 @@ executionRef/domain/userRef scope Provider/controller generation ``` -Resource verbs: +Resource verbs are the exact closed set: - get; - list; @@ -326,25 +326,42 @@ Resource verbs: - update-status; - update-metadata; - update-finalizers; -- delete. +- delete; +- use-credential; and +- admin-credential. + +`use-credential` is valid only for `Credential` rules with one or more exact +operation subresources from the Credential contract. `admin-credential` is +valid only for `Credential` rules with exact `create`, `update-spec`, or +`delete` subresources and is supplemental to the matching ordinary CRUD verb; +it grants no CRUD action by itself. Runtime/session verbs are the exact closed set `connect`, `invoke`, -`open-stream`, `relay`, `attach`, `cancel`, and `observe`. They are mapped -through the same engine but are not resource mutations. `relay` permits only an -already-authenticated ZoneLink/transport subject to forward an already-admitted -invocation or stream to one route-selected next hop. It grants no resource CRUD, -identity mapping, capability widening, attachment, credential, or local -lifecycle authority. +`open-stream`, `relay`, `attach`, `cancel`, `observe`, `audit-export`, and +`support-bundle`. They are mapped through the same engine but are not resource +mutations. `audit-export` binds only +`d2b.audit.v3.AuditService/Export`; `support-bundle` binds only +`d2b.support.v3.SupportService/GenerateBundle`. Both are admin-only, +session-only grants and imply no `get`, `list`, or other resource authority. +`relay` permits only an already-authenticated ZoneLink/transport subject to +forward an already-admitted invocation or stream to one route-selected next +hop. It grants no resource CRUD, identity mapping, capability widening, +attachment, credential, or local lifecycle authority. Every forwarding hop evaluates two independent permissions: `relay` for the authenticated adjacent-Zone transport subject and the forwarded operation's -target verb under the exact local Role/RoleBinding scope. The final target -evaluates the target verb again. Neither permission implies the other; a missing -grant or unavailable policy state fails closed. Relay-bearing Roles/Bindings -are core-generated and ZoneLink-scoped by default. Admission rejects wildcard, -self-asserted, Provider-authored, or ordinary operator-authored relay grants -unless an already-authorized local administrator explicitly permits the exact -bounded grant through durable admin policy. +target verb under the exact local Role/RoleBinding scope. Named methods carry +one immutable resource name. Nameless `List` and `Watch` carry no synthetic +name: their exact ResourceType, non-empty authorized `resourceNames` allowlist, +and bounded filters are evaluated as a set, and every hop preserves those +filters byte-for-byte. A filter that could select a name outside the local +intersection is denied rather than widened. The final target evaluates the +same target verb and selector again. Neither permission implies the other; a +missing grant or unavailable policy state fails closed. Relay-bearing +Roles/Bindings are core-generated and ZoneLink-scoped by default. Admission +rejects wildcard, self-asserted, Provider-authored, or ordinary +operator-authored relay grants unless an already-authorized local administrator +explicitly permits the exact bounded grant through durable admin policy. Native RBAC allow is necessary but not sufficient. Core structural checks also enforce: diff --git a/docs/specs/ADR-046-resources-credential.md b/docs/specs/ADR-046-resources-credential.md index fbadffbe8..2f612be58 100644 --- a/docs/specs/ADR-046-resources-credential.md +++ b/docs/specs/ADR-046-resources-credential.md @@ -469,9 +469,10 @@ ad hoc service-method verbs: | `use-credential` | Consumer subject authorized via consumerRef | Invoke an admitted credential operation under the exact `allowedOperations` value used as the Role subresource | | `admin-credential` | Authorized operator/configuration controller | Supplement ordinary Credential CRUD under the exact matching `create`, `update-spec`, or `delete` subresource; it grants no CRUD action by itself | -Credential Roles use the existing `subresources` rule field. They do not add an -`operationClasses` field. A rule granting either Credential-specific verb MUST -name exact, non-empty Credential subresources. A consumer Role rule is: +Credential Roles use the existing `subresources` rule field. No alternate +Credential-operation Role field exists. A rule granting either +Credential-specific verb MUST name exact, non-empty Credential subresources. A +consumer Role rule is: ```yaml rules: @@ -488,6 +489,13 @@ A separate deployer Role rule is: ```yaml rules: + - resourceTypes: [Credential] + verbs: [create, update-spec, delete] + subresources: [] + resourceNames: [work-entra] + zones: [dev] + executionRefs: [] + sessionVerbs: [] - resourceTypes: [Credential] verbs: [admin-credential] subresources: [create, update-spec, delete] @@ -505,7 +513,7 @@ The closed admission mapping is: | `RefreshToken` | `use-credential` | `refresh-token` | | `RevokeToken`, `CancelLogin` | `use-credential` | `revoke-token` | | `SignChallenge` | `use-credential` | `sign-challenge` | -| `Status`, `InspectMetadata`, `ObserveLogin` | `use-credential` | `inspect-metadata` | +| `InspectMetadata`, `ObserveLogin` | `use-credential` | `inspect-metadata` | | Resource create | `admin-credential` in addition to ordinary `create` | `create` | | Resource spec update | `admin-credential` in addition to ordinary `update-spec` | `update-spec` | | Resource delete | `admin-credential` in addition to ordinary `delete` | `delete` | @@ -548,15 +556,14 @@ Route key example: | Method | Required exact operation class | Required Role permission | Outer DTO fields | Token bytes | | --- | --- | --- | --- | --- | -| `Status` | `inspect-metadata` | `use-credential/inspect-metadata` | `CredentialStatusResponse`: leaseState, rotationGeneration, sourceVersion, expiresAtUnixMs, placementBinding | none | | `AcquireToken` | `acquire-token` | `use-credential/acquire-token` | `AcquireTokenResponse`: leaseHandle, sourceVersion, rotationGeneration, expiresAtUnixMs | raw token bytes in a dedicated sensitive ComponentSession record (see §Credential-delivery endpoint contract) | | `RefreshToken` | `refresh-token` | `use-credential/refresh-token` | `RefreshTokenResponse`: leaseHandle, sourceVersion, rotationGeneration, new expiresAtUnixMs | raw token bytes in a dedicated sensitive ComponentSession record | | `RevokeToken` | `revoke-token` | `use-credential/revoke-token` | `RevokeTokenResponse`: closed revocation result (Revoked or AlreadyRevoked), revokedAtUnixMs | none | | `SignChallenge` | `sign-challenge` | `use-credential/sign-challenge` | `SignChallengeResponse`: outcome code | signature bytes in a dedicated sensitive ComponentSession record (same channel as token delivery) | | `InspectMetadata` | `inspect-metadata` | `use-credential/inspect-metadata` | `InspectMetadataResponse`: leaseState, rotationGeneration, sourceVersion, expiresAtUnixMs | none | -The `Status`, `RevokeToken`, and `InspectMetadata` response DTOs are non-secret: -they carry only opaque identifiers, outcome codes, and timestamps. `AcquireToken`, +The `RevokeToken` and `InspectMetadata` response DTOs are non-secret: they carry +only opaque identifiers, outcome codes, and timestamps. `AcquireToken`, `RefreshToken`, and `SignChallenge` additionally deliver secret bytes in a dedicated sensitive ComponentSession record (see §Credential-delivery endpoint contract). Every method: @@ -685,8 +692,8 @@ or stores the records. ## Noise session binding for credential-bound calls -Non-secret credential operations (`Status`, `RevokeToken`, `InspectMetadata`) -traverse the standard ComponentSession/d2b-bus stack defined in +Non-secret credential operations (`RevokeToken`, `InspectMetadata`) traverse +the standard ComponentSession/d2b-bus stack defined in `ADR-046-componentsession-and-bus`: 1. The consumer process authenticates to d2b-bus using a local @@ -898,7 +905,7 @@ Audit records for Credential operations: | `RefreshToken` | Zone, subject digest, `resource_name_digest`, operation class, `use-credential/refresh-token` decision, `rotationGeneration`, outcome code, idempotency key digest | | `RevokeToken` | Zone, subject digest, `resource_name_digest`, operation class, `use-credential/revoke-token` decision, `rotationGeneration`, revocation result code | | `SignChallenge` | Zone, subject digest, `resource_name_digest`, operation class, `use-credential/sign-challenge` decision, outcome code (no signature bytes) | -| `Status`, `InspectMetadata`, `ObserveLogin` | Zone, subject digest, `resource_name_digest`, `use-credential/inspect-metadata` decision, bounded outcome code | +| `InspectMetadata`, `ObserveLogin` | Zone, subject digest, `resource_name_digest`, `use-credential/inspect-metadata` decision, bounded outcome code | | `BeginLogin` | Zone, subject digest, `resource_name_digest`, `use-credential/acquire-token` decision, bounded outcome code (no challenge/UI bytes) | | `CancelLogin` | Zone, subject digest, `resource_name_digest`, `use-credential/revoke-token` decision, bounded outcome code | | Rotation | Zone, `resource_name_digest`, trigger reason, old `rotationGeneration`, new `rotationGeneration`, outcome code | @@ -1992,10 +1999,10 @@ config formalizes this as an `OpaqueAzureRef` with the same charset restriction. | Reuse source | main `a1cc0b2d`: `packages/d2b-contracts/proto/v2/provider_credential.proto` method names | | Reuse action | adapt | | Destination | `packages/d2b-contracts/proto/v3/credential.proto`; `packages/d2b-credential-service/src/{service.rs, client.rs, server.rs}` | -| Detailed design | Define `d2b.credential.v3` protobuf service with methods: `Status`, `AcquireToken`, `RefreshToken`, `RevokeToken`, `SignChallenge`, `InspectMetadata`; each request carries `credential_ref`, `operation_class`, `operation_id`, `idempotency_key`, `requested_expiry_unix_ms`, `deadline_unix_ms`; map every method to the same exact operation class in `spec.allowedOperations` and the existing Role `subresources` field (`acquire-token`, `refresh-token`, `revoke-token`, `sign-challenge`, or `inspect-metadata`) under the canonical `use-credential` resource verb; map administrative lifecycle to matching `admin-credential` subresources `create`, `update-spec`, and `delete`, supplemental to ordinary CRUD, never to a new Role field or method-name alias; `Status`, `RevokeToken`, and `InspectMetadata` responses carry only non-secret metadata (leaseHandle digest, rotationGeneration, sourceVersion, expiresAtUnixMs, state, outcome code); `AcquireToken`, `RefreshToken`, and `SignChallenge` responses additionally include a `delivery_session_params` field carrying the binding contract fields required to establish the end-to-end credential-delivery ComponentSession (see §Credential-delivery endpoint contract); the token bytes themselves travel in the separate Noise-encrypted delivery session, never in the outer DTO; strict unknown-field rejection; bounded message sizes; all record wrappers for delivery sessions must be zeroizing types | +| Detailed design | Define `d2b.credential.v3` protobuf service with exactly five operation methods: `AcquireToken`, `RefreshToken`, `RevokeToken`, `SignChallenge`, and `InspectMetadata`; map those methods one-to-one to the same exact operation class in `spec.allowedOperations` and the existing Role `subresources` field (`acquire-token`, `refresh-token`, `revoke-token`, `sign-challenge`, or `inspect-metadata`) under the canonical `use-credential` resource verb; derive the operation class from the method rather than accepting a caller-selected operation-class field; each request carries `credential_ref`, `operation_id`, `idempotency_key`, `requested_expiry_unix_ms`, `deadline_unix_ms`; map administrative lifecycle to matching `admin-credential` subresources `create`, `update-spec`, and `delete`, supplemental to ordinary CRUD, never to a new Role field or method-name alias; `RevokeToken` and `InspectMetadata` responses carry only non-secret metadata (leaseHandle digest, rotationGeneration, sourceVersion, expiresAtUnixMs, state, outcome code); `AcquireToken`, `RefreshToken`, and `SignChallenge` responses additionally include a `delivery_session_params` field carrying the binding contract fields required to establish the end-to-end credential-delivery ComponentSession (see §Credential-delivery endpoint contract); the token bytes themselves travel in the separate Noise-encrypted delivery session, never in the outer DTO; strict unknown-field rejection; bounded message sizes; all record wrappers for delivery sessions must be zeroizing types | | Integration | d2b-bus routes `d2b.credential.v3` service to the exact credential provider Process identified by `Credential.spec.providerRef`; before dispatch RBAC requires the exact operation class in both the `use-credential` Role `subresources` and `spec.allowedOperations`; for `AcquireToken`/`RefreshToken`/`SignChallenge`, bus additionally authorizes the credential-delivery endpoint route and forwards opaque Noise-encrypted delivery records without terminating or buffering them; bus never stores or inspects delivery record plaintext | | Data migration | None — full d2b 3.0 reset; no prior state to migrate | -| Validation | Protocol golden vectors for each method; Role matrix for the five exact allowed-operation subresources under `use-credential` and exact `create`, `update-spec`, and `delete` subresources under `admin-credential`; prove admin permission is supplemental to ordinary CRUD; deny empty/wildcard/unknown/mismatched subresources and any `operationClasses`/method-name-alias input before Provider dispatch; malformed/oversize rejection; `leaseHandle` opacity tests (secret-canary must not appear in outer DTO or delivery routing metadata); locked/unavailable/denied/expired state tests; delivery session binding contract round-trip; zeroizing record type unit tests; delivery channel never materialized in non-delivery method tests | +| Validation | Protocol golden vectors for each method; Role matrix for the five exact allowed-operation subresources under `use-credential` and exact `create`, `update-spec`, and `delete` subresources under `admin-credential`; prove admin permission is supplemental to ordinary CRUD; deny empty/wildcard/unknown/mismatched subresources, alternate Credential-operation Role fields, and method-name aliases before Provider dispatch; malformed/oversize rejection; `leaseHandle` opacity tests (secret-canary must not appear in outer DTO or delivery routing metadata); locked/unavailable/denied/expired state tests; delivery session binding contract round-trip; zeroizing record type unit tests; delivery channel never materialized in non-delivery method tests | | Removal proof | Old v2 `CredentialProviderService` proto removed only after all v3 callers migrate | ### ADR046-credential-003 diff --git a/docs/specs/ADR-046-resources-network.md b/docs/specs/ADR-046-resources-network.md index 0cb796d37..548f5bc59 100644 --- a/docs/specs/ADR-046-resources-network.md +++ b/docs/specs/ADR-046-resources-network.md @@ -1446,6 +1446,8 @@ spec: roleRef: Role/network-local-controller subjects: - Provider/network-local + externalPrincipalSelector: null + scopeNarrowing: null ``` Guest controllers may need `get` on `Network` to resolve a `networkRef` in diff --git a/docs/specs/ADR-046-resources-zone-control.md b/docs/specs/ADR-046-resources-zone-control.md index 7e915d3c8..a439917c6 100644 --- a/docs/specs/ADR-046-resources-zone-control.md +++ b/docs/specs/ADR-046-resources-zone-control.md @@ -1051,8 +1051,8 @@ Each rule in `spec.rules` has: | Field | Type | Default | Semantics | | --- | --- | --- | --- | | `resourceTypes` | list of ResourceType names | required, non-empty | ResourceTypes this rule covers; short Zone-unique names or qualified vendor names | -| `verbs` | list of resource verb tokens | required, non-empty | Resource operation verbs (see §5.3.2) | -| `subresources` | list of subresource names | `[]` = no subresource restriction | Empty means all subresources; non-empty means exactly those subresources | +| `verbs` | list of resource verb tokens | `[]` = no resource verbs | Resource operation verbs (see §5.3.2); at least one of `verbs` or `sessionVerbs` must be non-empty | +| `subresources` | list of subresource or exact service/method selectors | `[]` = no subresource restriction | Empty means all subresources; non-empty means exactly those subresources or qualified service/method selectors | | `resourceNames` | list of ResourceName | `[]` = all | Empty means all names; non-empty means exactly those names; max 64 | | `zones` | list of Zone names | `[]` = this Zone only | Empty means only the evaluating Zone; non-empty means any listed Zone | | `executionRefs` | list of ResourceRefs | `[]` = no restriction | Restricts to operations whose target resource has one of these executionRefs; `[]` means unrestricted | @@ -1075,8 +1075,12 @@ All list fields are deduplicated at admission. Order is not significant. | `update-metadata` | Bounded labels/annotations/ownerRef metadata change | | `update-finalizers` | Add/remove finalizers (ownership constrained) | | `delete` | Request resource deletion | +| `use-credential` | Invoke a Credential operation selected by one exact Credential allowed-operation subresource | +| `admin-credential` | Supplement matching ordinary Credential create/update-spec/delete authority using the same exact lifecycle subresource | Unknown verbs are rejected at admission. +`use-credential` and `admin-credential` are valid only for `Credential` rules +with the exact subresources defined by the Credential contract. **Session verbs** (exact closed set): @@ -1089,10 +1093,15 @@ Unknown verbs are rejected at admission. | `attach` | Transfer a local file descriptor | | `cancel` | Cancel an in-progress operation | | `observe` | Subscribe to service health/event notifications | +| `audit-export` | Invoke only `d2b.audit.v3.AuditService/Export` (admin-only) | +| `support-bundle` | Invoke only `d2b.support.v3.SupportService/GenerateBundle` (admin-only) | Session verbs in a Role rule grant ComponentSession/d2b-bus access to the services bound by the same rule's `resourceTypes` and `zones` constraints. They are evaluated by the same native RBAC engine as resource verbs. +`audit-export` and `support-bundle` require exact qualified service/method +selectors in `subresources`, may appear only in `sessionVerbs`, and imply no +resource read or mutation verb. `relay` is transport forwarding authority only. It permits the exact authenticated ZoneLink/transport subject to forward an invocation or named @@ -1106,7 +1115,10 @@ A relay-bearing rule is admitted only when all of the following hold: - `relay` appears in `sessionVerbs`, never `verbs`; - `resourceTypes`, `resourceNames`, and `zones` exactly bound the forwarded - target; empty/all-name scope and every wildcard form are rejected; + target; empty/all-name scope and every wildcard form are rejected. Named + methods match one immutable resource name. Nameless `List`/`Watch` requests + retain a non-empty exact `resourceNames` allowlist and bounded filters whose + possible result set is a subset of that allowlist at every hop; - the Role and RoleBinding are core-generated with `ownerRef` naming the governing `ZoneLink`, and the binding's trusted external-principal selector matches the exact enrolled adjacent-`Zone` transport subject; or an @@ -1150,8 +1162,8 @@ Roles) must be enforced at admission. For non-core-controller Roles, | --- | --- | | Rules per Role | 32 | | `resourceTypes` per rule | 16 | -| `verbs` per rule | 16 (bounded by verb enum: currently 9 verbs) | -| `sessionVerbs` per rule | 7 (bounded by session verb enum: currently 7 verbs) | +| `verbs` per rule | 16 (bounded by verb enum: currently 11 verbs) | +| `sessionVerbs` per rule | 9 (bounded by session verb enum: currently 9 verbs) | | `subresources` per rule | 16 | | `resourceNames` per rule | 64 | | `executionRefs` per rule | 32 | @@ -1383,8 +1395,9 @@ Rules: with `resource-schema-invalid`; - narrowed rules are the intersection of the Role rules and the narrowing rules; -- `sessionVerbs`, including `relay`, are intersected exactly like resource - verbs; narrowing cannot add relay or remove its exact target bounds; +- `sessionVerbs`, including `relay`, `audit-export`, and `support-bundle`, are + intersected exactly like resource verbs; narrowing cannot add one or remove + its exact target or service/method bounds; - `scopeNarrowing: null` means the full Role is granted without restriction; - scope narrowing affects only this RoleBinding; the referenced Role is unchanged. @@ -3099,11 +3112,13 @@ Eval-time validations: `spec.rules[*].verbs` against the closed verb enum; `spec.rules[*].executionRefs` format if non-empty. `spec.rules[*].resourceTypes` validation against the Zone API catalog is deferred to Phase 2 (catalog requires loading Provider manifests; core types are known at eval time). -Generated option help lists all seven session verbs and describes `relay` as -ZoneLink-scoped forwarding only. Nix recognizes `relay` as a session-verb token, -but Phase 2 admission still rejects an unbounded, wildcard, Provider-asserted, -or ordinary operator-authored relay grant unless explicit admin policy permits -that exact bounded grant. +Generated option help lists all nine session verbs, describes `relay` as +ZoneLink-scoped forwarding only, and binds `audit-export` and `support-bundle` +to their exact admin-only service/method selectors without granting resource +authority. Nix recognizes all three as session-verb tokens, but Phase 2 +admission still rejects an unbounded, wildcard, Provider-asserted, or ordinary +operator-authored relay grant unless explicit admin policy permits that exact +bounded grant. ### 14.5 RoleBinding authoring @@ -3111,26 +3126,30 @@ that exact bounded grant. d2b.zones.dev.resources.process-controller-binding = { type = "RoleBinding"; spec = { - roleRef = "Role/process-controller"; - subjects = [ "Provider/system-minijail" ]; - # spec.externalPrincipalSelector: null (default; omit) - # spec.scopeNarrowing: null (full role scope; omit) + roleRef = "Role/process-controller"; + subjects = [ "Provider/system-minijail" ]; + externalPrincipalSelector = null; + scopeNarrowing = null; }; }; d2b.zones.dev.resources.zone-reader-alice = { type = "RoleBinding"; spec = { - roleRef = "Role/zone-reader"; - subjects = [ "User/alice" ]; + roleRef = "Role/zone-reader"; + subjects = [ "User/alice" ]; + externalPrincipalSelector = null; + scopeNarrowing = null; }; }; d2b.zones.dev.resources.zone-reader-bob = { type = "RoleBinding"; spec = { - roleRef = "Role/zone-reader"; - subjects = [ "User/bob" ]; + roleRef = "Role/zone-reader"; + subjects = [ "User/bob" ]; + externalPrincipalSelector = null; + scopeNarrowing = null; }; }; ``` diff --git a/docs/specs/ADR-046-security-and-threat-model.md b/docs/specs/ADR-046-security-and-threat-model.md index 77ad2ed0b..301be5d1e 100644 --- a/docs/specs/ADR-046-security-and-threat-model.md +++ b/docs/specs/ADR-046-security-and-threat-model.md @@ -510,9 +510,14 @@ self-asserted role": a component cannot claim `Admin`, a different principal, or a wider capability merely by writing it into a request. **Closed verb sets.** Resource verbs: `get, list, watch, create, update-spec, -update-status, update-metadata, update-finalizers, delete`. Session verbs: -`connect, invoke, open-stream, relay, attach, cancel, observe`. All verb tokens -outside this closed set are rejected with `role-unknown-verb-rejected` +update-status, update-metadata, update-finalizers, delete, use-credential, +admin-credential`. Session verbs: `connect, invoke, open-stream, relay, attach, +cancel, observe, audit-export, support-bundle`. Credential use/admin grants +require their exact canonical subresources. The two diagnostic session verbs +bind only `d2b.audit.v3.AuditService/Export` and +`d2b.support.v3.SupportService/GenerateBundle` and imply no resource +authority. All verb tokens outside these closed sets are rejected with +`role-unknown-verb-rejected` (RZC lines 3422-3430, adapted from `verb_requires_admin()` in the baseline `d2bd/src/admission.rs`). @@ -528,12 +533,15 @@ explicitly permits the exact bounded grant through durable admin policy. **Per-hop RBAC.** For every intermediate Zone hop in a ZoneLink chain, the forwarding d2b-bus performs two independent local checks before forwarding: `relay` for the authenticated adjacent-Zone transport subject and the immutable -target verb for the invocation/stream. The final target checks the target verb -again. A Zone never grants authority beyond its own Role/RoleBinding -evaluation; missing relay, missing target authority, or unavailable policy -state fails closed. This closes both the historical "one relay hop is -authorized, therefore every downstream hop is trusted" gap and the converse -"relay implies target authority" gap. +target verb for the invocation/stream. Named methods retain one immutable +resource name. Nameless `List`/`Watch` retain the exact non-empty authorized +name set and bounded filters; every hop rejects a selector that could widen the +set and forwards the filters byte-for-byte. The final target checks the same +target verb and selector again. A Zone never grants authority beyond its own +Role/RoleBinding evaluation; missing relay, missing target authority, or +unavailable policy state fails closed. This closes both the historical "one +relay hop is authorized, therefore every downstream hop is trusted" gap and +the converse "relay implies target authority" gap. **Wildcard restriction.** Explicit wildcard (`resourceNames: ["*"]`) is permitted only for core-controller-generated Roles. Operator-authored or @@ -571,9 +579,10 @@ enforces exact-match recipient identity, never a class or wildcard match: requesting session's authenticated subject matches the Credential's `consumerRef` exactly; a mismatch closes the session with `authorization-denied` (`ADR-046-resources-credential` lines 378-383, - 480-491). `operationClasses` on `CredentialSpec` further narrows the - allowed operation set per consumer; a request outside that set is rejected - the same way. + 480-491). Effective permission is the intersection of the exact + `Credential.spec.allowedOperations` value and the exact Role `subresources` + value under `use-credential`, further narrowed by consumer/scope/structural + checks; a request outside that intersection is rejected the same way. - **externalPrincipalSelector.** Bounded at 512 bytes canonical JSON and restricted to opaque enrollment digests — it may not contain credential bytes, so an external-principal RoleBinding cannot be used to smuggle @@ -1327,9 +1336,12 @@ token bytes, URLs, UUIDs, provider diagnostics, host paths, or connection strings (lines 681-683). **RBAC.** `use-credential` is required and checked by d2b-bus before -forwarding delivery; `operationClasses` on `CredentialSpec` narrows the -allowed operation set per consumer; `admin-credential` is required to -create/delete a Credential resource, operator-only (lines 318-336). +forwarding delivery. Each service method maps to exactly one canonical +allowed-operation value and requires that same exact value in both +`Credential.spec.allowedOperations` and Role `subresources`. +`admin-credential` is supplemental to ordinary Credential CRUD and accepts +only matching `create`, `update-spec`, or `delete` subresources; there is no +coarse `admin`, `identity-reset`, `observe`, or `revoke` alias. ## Content secrecy: clipboard, terminal, CTAP, notification @@ -1426,8 +1438,10 @@ digests (ZR line 2098). **Segment lifecycle.** Rotation at 64 MiB or UTC midnight, whichever comes first; 30-day default retention; export requires the admin-only -`audit-export` verb; hash-chain breaks are reported inline in the export -stream, never silently skipped (line 1846). +`audit-export` session verb bound exactly to +`d2b.audit.v3.AuditService/Export`, with no implied Zone resource read; +hash-chain breaks are reported inline in the export stream, never silently +skipped (line 1846). **`observability-otel` is never a bootstrap dependency and never reads audit.** Zone startup does not wait for `observability-otel`; unavailability @@ -1583,19 +1597,24 @@ targeted. `argv`, or PIDs in output; includes an audit hash-chain integrity check (`ADR-046-telemetry-audit-and-support`, `ADR046-doctor-001`). -**`d2b zone support-bundle`.** No spec bytes and no `metadata.name` in the -bundle; metadata and status only. When a Provider is quarantined, the bundle -reports `bundle_completeness: "partial"` rather than silently omitting the -gap or blocking entirely (`ADR046-doctor-002`). This is the concrete tool an -operator or a coordinated-disclosure responder uses to gather evidence +**`d2b zone support-bundle`.** The command requires the admin-only +`support-bundle` session verb bound exactly to +`d2b.support.v3.SupportService/GenerateBundle`; that grant supplies no +resource `get` or `list` authority. No spec bytes and no `metadata.name` appear +in the bundle; metadata and status only. When a Provider is quarantined, the +bundle reports `bundle_completeness: "partial"` rather than silently omitting +the gap or blocking entirely (`ADR046-doctor-002`). This is the concrete tool +an operator or a coordinated-disclosure responder uses to gather evidence without themselves becoming a redaction bypass — it is bound by exactly the same audit/status redaction rules as every other read path in [Audit vs. OTEL](#audit-vs-otel-redaction-cardinality-durability). -**`d2b zone audit export`.** Admin-only (`audit-export` verb); hash-chain -breaks are reported inline in the export stream rather than silently -truncating history; output carries no old field names (`realm`/`node`/ -`workload_id`) and no path/`argv` content (`ADR046-audit-004`). +**`d2b zone audit export`.** Admin-only (`audit-export` session verb, exact +`d2b.audit.v3.AuditService/Export` binding, no resource-authority widening); +hash-chain breaks are reported inline in the export stream rather than +silently truncating history; output carries no old field names +(`realm`/`node`/`workload_id`) and no path/`argv` content +(`ADR046-audit-004`). **Coordination with disclosure policy.** `SECURITY.md`'s GitHub Security Advisory channel, response-time targets (7-day acknowledgment, 30-day diff --git a/docs/specs/ADR-046-spec-set.json b/docs/specs/ADR-046-spec-set.json index 15805a8b9..517b3a9e4 100644 --- a/docs/specs/ADR-046-spec-set.json +++ b/docs/specs/ADR-046-spec-set.json @@ -13,7 +13,7 @@ "ADR-046-terminology-and-identities" ], "path" : "docs/specs/ADR-046-cli-and-operations.md", - "sha256" : "b325b800dffb929c15368f3c56032dacc5e52251fbdc0b93bc128111a56535c3", + "sha256" : "9cb69967240869f2a902d87da25d706bfed580b7a4afefadfec94218c034e07f", "specId" : "ADR-046-cli-and-operations", "status" : "Proposed", "supersedes" : "Current v3 `d2b` CLI contract (`packages/d2b/src/lib.rs` at baseline)", @@ -44,7 +44,7 @@ "ADR-046-terminology-and-identities" ], "path" : "docs/specs/ADR-046-componentsession-and-bus.md", - "sha256" : "d0daf4c6ebf921d59489a43087615a48803a6821782e0d41c55008858028e512", + "sha256" : "1e52660d95859e10fc82decf7757fc54b0c4ea6021dd9967de66c4748b8d9f81", "specId" : "ADR-046-componentsession-and-bus", "status" : "Proposed", "supersedes" : "Current v3 Realm PeerSession and ad hoc guest/user/public IPC", @@ -84,7 +84,7 @@ { "dependsOn" : [], "path" : "docs/specs/ADR-046-decision-register.md", - "sha256" : "fae3a475f43023f16a2fc8f01abd2e9b02e840313a992d843f46e0e5af70c4b2", + "sha256" : "0c1105cf9dafd70cc77526818bb997f64d15a68df02fe96d03a35d4722c95a72", "specId" : "ADR-046-decision-register", "status" : "Proposed", "supersedes" : null, @@ -130,7 +130,7 @@ "ADR-046-terminology-and-identities" ], "path" : "docs/specs/ADR-046-nix-configuration.md", - "sha256" : "8742a29435dac5720edd920c5d9859ba9dc2520dca7518d0cea01af3b7d8bc45", + "sha256" : "fdbe65b4a6b93e8465e971f9651c3ef46cd27b8f3e724d421da5fd784b2ebee1", "specId" : "ADR-046-nix-configuration", "status" : "Proposed", "supersedes" : "Current `nixos-modules/options-realms*.nix`, `options-envs.nix`, `options-vms.nix`, `index.nix`, `bundle*.nix`, `*-json.nix`, and generated `/etc/d2b/*.json`", @@ -194,7 +194,7 @@ "ADR-046-telemetry-audit-and-support" ], "path" : "docs/specs/providers/ADR-046-provider-audio-pipewire.md", - "sha256" : "d55b348dd040ea6874083177adde01868f32cde1e5f30199e3a78e7c21436dd6", + "sha256" : "ca4f477bb5711ee1e74b3f1261e81b5a17cd15744e619ab7e100cf25a7c4f6e8", "specId" : "ADR-046-provider-audio-pipewire", "status" : "Proposed", "supersedes" : "`nixos-modules/components/audio/host.nix`, `nixos-modules/components/audio/guest.nix`, `packages/d2b-core/src/audio_policy.rs`, `packages/d2bd/src/audio_dispatch.rs`, `packages/d2bd/src/audio_host_controller.rs`, `packages/d2b-host/src/audio_argv.rs`", @@ -235,7 +235,7 @@ "ADR-046-telemetry-audit-and-support" ], "path" : "docs/specs/providers/ADR-046-provider-credential-entra.md", - "sha256" : "0ba17f63955ff299d4032b2b9f08baafac49345671fa6ae6cb466d26cd928e81", + "sha256" : "0d83de1ef4e63c361abe5ce0a3bfde365e019efdfcf8d18b1b1d9b5ab9842fda", "specId" : "ADR-046-provider-credential-entra", "status" : "Proposed", "supersedes" : "No Host login/token chains; no direct `EntraCredentialClient` production egress; no `DefaultAzureCredential`, environment, DBus, browser, or path discovery", @@ -254,7 +254,7 @@ "ADR-046-telemetry-audit-and-support" ], "path" : "docs/specs/providers/ADR-046-provider-credential-managed-identity.md", - "sha256" : "3dfcf08dfae6f6cc5124fe91c06cef65c186d6c16ec32eac0d945655edf5e661", + "sha256" : "4bb2c3878f50c24e3726d3ca95ab982284e43d655151221af68df35497694c0b", "specId" : "ADR-046-provider-credential-managed-identity", "status" : "Proposed", "supersedes" : "Current v3 `ManagedIdentityRef`, `managed_identity_client_id` ACA config field, `CredentialProvider` trait (status/enrollment-only) in `d2b-realm-provider/src/credential.rs` and `provider.rs`", @@ -276,7 +276,7 @@ "ADR-046-telemetry-audit-and-support" ], "path" : "docs/specs/providers/ADR-046-provider-credential-secret-service.md", - "sha256" : "80708708e45782c01804aed6765a3153f12f43396b490f3f09c19910ceaa8839", + "sha256" : "a5e2783bc02e74ce629c5922c8b08192f81b0e1c3bdcfe82a004879d56dd8b30", "specId" : "ADR-046-provider-credential-secret-service", "status" : "Proposed", "supersedes" : "v2 `SecretServiceCredentialProvider` / `SecretServiceCredentialProviderFactory` in `d2b-realm-provider`; v2 `CredentialProvider` trait", @@ -429,7 +429,7 @@ "ADR-046-telemetry-audit-and-support" ], "path" : "docs/specs/providers/ADR-046-provider-network-local.md", - "sha256" : "deee9148ceb6db9e698cc944199a0cc44157ceddc424a46d9b3a0420338bc8e0", + "sha256" : "e7e351bd27d7ebfb37d4c47917a0e7f17b4c673b3b7e9fc4ad59170a0ca5cfaf", "specId" : "ADR-046-provider-network-local", "status" : "Proposed", "supersedes" : "`nixos-modules/network.nix`, `nixos-modules/net.nix`", @@ -494,7 +494,7 @@ "ADR-046-telemetry-audit-and-support" ], "path" : "docs/specs/providers/ADR-046-provider-runtime-azure-container-apps.md", - "sha256" : "e92adfce5d1aafce5dbdd1749577da3265c0630b8d7d5099ba3ddef02dab3639", + "sha256" : "6511457a36f7049008eb1a996f0028d5926f6daffcf04e41d782cfb16624c2a4", "specId" : "ADR-046-provider-runtime-azure-container-apps", "status" : "Proposed", "supersedes" : "`packages/d2b-provider-aca/` (`AcaWorkloadProvider`, `GuestControlEndpointProvider`), `AcaRelayTransportConfig`, direct vsock guest-control path", @@ -522,7 +522,7 @@ "ADR-046-zone-routing" ], "path" : "docs/specs/providers/ADR-046-provider-runtime-azure-virtual-machine.md", - "sha256" : "03aa87a6b3f6923e863dfd45d17858f0c620bfcdb6a109803bd162372b6b0563", + "sha256" : "303386a96301cc0b3b3904b8f233263063f7f6b5f3f6c501da277bd51d5ca0c6", "specId" : "ADR-046-provider-runtime-azure-virtual-machine", "status" : "Proposed", "supersedes" : "Current `InfrastructureProvider` trait (`d2b-realm-provider/src/provider.rs`); `AzureVmForbidden` explicit rejection in `provider_registry.rs`; `AZURE_VM_IMPLEMENTATION_ID` constant; `WorkloadProviderKind::ProviderManaged` paths for Azure VM workloads", @@ -548,7 +548,7 @@ "ADR-046-telemetry-audit-and-support" ], "path" : "docs/specs/providers/ADR-046-provider-runtime-cloud-hypervisor.md", - "sha256" : "1d12e89790ed4c74d73dc2796590f6e75612e32b5298e97a06cf68bb4c143887", + "sha256" : "0fa75533c683e40b72c32666434b7185202c24534731fb088a136fe996a31c98", "specId" : "ADR-046-provider-runtime-cloud-hypervisor", "status" : "Proposed", "supersedes" : "`packages/d2b-host/src/runtime_provider.rs` `CloudHypervisorRuntimeProvider`; `packages/d2bd/src/` VM lifecycle paths; `d2b-host-providers` adapter; `ProcessRole::CloudHypervisor`, `ProcessRole::Swtpm`, `ProcessRole::NetVm`; systemd unit `d2b--vm.service`; `SwtpmDir` broker op", @@ -572,7 +572,7 @@ "ADR-046-telemetry-audit-and-support" ], "path" : "docs/specs/providers/ADR-046-provider-runtime-qemu-media.md", - "sha256" : "52f7bc6e47937040516a885313c313f97db7dbe6757f731e63c53ed08e878bd8", + "sha256" : "438f37a0baeb88e31f5e432c6e153ee1d086d7edbacbabdda354316a2eb2e8f8", "specId" : "ADR-046-provider-runtime-qemu-media", "status" : "Proposed", "supersedes" : "`docs/adr/0036-qemu-media-runtime.md`", @@ -751,7 +751,7 @@ "ADR-046-zone-routing" ], "path" : "docs/specs/providers/ADR-046-provider-transport-vsock.md", - "sha256" : "55510f053296ae7cf2e3f64385bcfa3bfc4b0de3d0009c398480d806c119287d", + "sha256" : "056326ac85dc5b1077c2a6e9cf66822dafda8249c4523db021a57b4000e2ff13", "specId" : "ADR-046-provider-transport-vsock", "status" : "Proposed", "supersedes" : "`packages/d2bd/src/guest_control_vsock.rs` transport probe (→ allocator-issued endpoint via VsockEffectPort); `packages/d2b-host/src/vsock_relay_argv.rs` socat relay (→ native FramedVsockTransport in Provider service); `NativeVsock`/`CloudHypervisorVsock` in `d2b-session-unix/src/vsock.rs` (→ FramedVsockTransport implementing OwnedTransport adapted as service-Provider transport bridge)", @@ -848,7 +848,7 @@ "ADR-046-terminology-and-identities" ], "path" : "docs/specs/ADR-046-resource-api-and-authorization.md", - "sha256" : "dd880d5e09a673b44b9515d30733349455895f04aee8cfc7c327b158866a69ff", + "sha256" : "2b6a0c11a8c41c9dbd2725b2b6c8586cc0c7c6de65c43a37c11c36cb31e5f6b1", "specId" : "ADR-046-resource-api-and-authorization", "status" : "Proposed", "supersedes" : null, @@ -914,7 +914,7 @@ "ADR-046-terminology-and-identities" ], "path" : "docs/specs/ADR-046-resources-credential.md", - "sha256" : "1ab6ebd1af197848940dc9b9b386becd2ddc72295d1fc74e66b7001f0a4889e0", + "sha256" : "1ebfcf9219269bbdff9478e83e2d0c83055da0f426a3b90c0af145bfaca2cf3a", "specId" : "ADR-046-resources-credential", "status" : "Proposed", "supersedes" : "Current v3 `CredentialProvider` trait, `CredentialStatus`, `CredentialPlane` model in `d2b-realm-provider/src/credential.rs` and `provider.rs`", @@ -974,7 +974,7 @@ "ADR-046-terminology-and-identities" ], "path" : "docs/specs/ADR-046-resources-network.md", - "sha256" : "f42c4648e38858537711ba8444f72d5cba57a48856da1f1cf74c9edd7f7f56f7", + "sha256" : "541c0dbfe2fe00fd35b8dfaac663160f49c4c69662f6c989cbd6977374fec1f1", "specId" : "ADR-046-resources-network", "status" : "Proposed", "supersedes" : "`d2b.envs.` and `d2b.realms..network` Nix surfaces (v3 reset)", @@ -1013,7 +1013,7 @@ "ADR-046-terminology-and-identities" ], "path" : "docs/specs/ADR-046-resources-zone-control.md", - "sha256" : "132a340c12df4efac7c289aff683d92ce905be7f092e3b5b4032bf88a7ee845d", + "sha256" : "6772bd85ab368501826d468b76633d15474175c7b30350c607451b76ac164d8b", "specId" : "ADR-046-resources-zone-control", "status" : "Proposed", "supersedes" : null, @@ -1080,7 +1080,7 @@ "ADR-046-zone-routing" ], "path" : "docs/specs/ADR-046-security-and-threat-model.md", - "sha256" : "66a68f951b259290df1e3bf2b092c4049c0b40fdc79e1c7a798077142de4b80d", + "sha256" : "58ea74bbda2154e05f5127fdedf0b5ae8f481cc6bc29c4841b3dee77d645c3e2", "specId" : "ADR-046-security-and-threat-model", "status" : "Proposed", "supersedes" : null, @@ -1122,7 +1122,7 @@ "ADR-046-terminology-and-identities" ], "path" : "docs/specs/ADR-046-telemetry-audit-and-support.md", - "sha256" : "1af8dbb4f151e7e76c46a0fb1282afe98b5f07800f1ecac69f3954681b1faea5", + "sha256" : "6e680e6d87a9370d749c5e1c3d3bf62954039deca2494b45c6ce5b62c75061ba", "specId" : "ADR-046-telemetry-audit-and-support", "status" : "Proposed", "supersedes" : "Current `d2bd` hand-rolled Prometheus registry; current daemon/broker/gateway JSONL audit paths", @@ -1225,7 +1225,7 @@ "ADR-046-terminology-and-identities" ], "path" : "docs/specs/ADR-046-zone-routing.md", - "sha256" : "9a480aefc284e0ed3549c638fe31dc9e369bfa467fdf1d1715cc704309cf3e71", + "sha256" : "c2434f0a8f9b7ecadeb2d7b700096bc40f71da6061547b19d0c59818af7ab13d", "specId" : "ADR-046-zone-routing", "status" : "Proposed", "supersedes" : "`RealmEntrypointTable`/`EntrypointMode`/`RealmControllerPlacement` enum (→ compiler-only `parentZone` topology + ZoneLink transport + private Zone runtime bootstrap placement); `RouteTreeEngine`/`routing.rs` route contracts (adapted, not deleted); `RemoteNodeRegistry` (→ ZoneLink controller); `OperationRouter` (→ ZoneLinkIdempotencyKey in d2b-bus); `realm_access_resolver` module (→ ZoneEntrypointResolver); `realm-controllers.json`/`realm-identity.json` bundle artifacts (→ Nix-authored Zone settings/ZoneLink resources); `d2b.realms.*` Nix option namespace (→ `d2b.zones.*`); `WorkloadId` routing targets (→ typed Guest/Host resource refs); `CapabilitySet`-only routing authz (→ per-hop RBAC narrowing + allocator-issued capability scope)", diff --git a/docs/specs/ADR-046-telemetry-audit-and-support.md b/docs/specs/ADR-046-telemetry-audit-and-support.md index 7dfd3e3e2..8fcae078e 100644 --- a/docs/specs/ADR-046-telemetry-audit-and-support.md +++ b/docs/specs/ADR-046-telemetry-audit-and-support.md @@ -399,7 +399,7 @@ Target crate: `d2b-resource-api` (ADR-only). No current analog. | Metric | Type | Labels | Buckets (s) | | --- | --- | --- | --- | -| `d2b_api_request_total` | counter | `verb={get,list,watch,create,update,patch,status,delete,finalize}`, `resource_type`, `outcome={ok,conflict,invalid,denied,not_found,quota,error}` | — | +| `d2b_api_request_total` | counter | `verb={get,list,watch,create,update-spec,update-status,update-metadata,update-finalizers,delete,use-credential,admin-credential}`, `resource_type`, `outcome={ok,conflict,invalid,denied,not_found,quota,error}` | — | | `d2b_api_request_duration_seconds` | histogram | `verb`, `resource_type` | 0.0005, 0.001, 0.005, 0.01, 0.025, 0.05, 0.1, 0.5 | | `d2b_api_watch_active` | gauge | (none) | — | | `d2b_api_admission_rejected_total` | counter | `reason={auth,quota,conflict,invalid,schema}` | — | @@ -1079,7 +1079,7 @@ unchanged. `trace_id` is the `TraceContext.trace_id` field from { "record_class": "resource-mutation", "resource_mutation_fields": { - "verb": "create|update|patch|status|delete|finalize", + "verb": "create|update-spec|update-status|update-metadata|update-finalizers|delete|use-credential|admin-credential", "resource_type": "", "resource_uid": "", "generation": 12, @@ -1142,7 +1142,7 @@ labels (cardinality rules below). { "record_class": "rbac-change", "rbac_change_fields": { - "verb": "create|update|delete", + "verb": "create|update-spec|delete", "resource_type": "Role|RoleBinding", "resource_uid": "", "generation": 4, @@ -1326,12 +1326,28 @@ authoritative Zone audit). `d2b zone audit export [--zone ] [--after ] [--before ]` -- Requires `audit-export` verb on the Zone resource (admin-only). +- Invokes exactly `d2b.audit.v3.AuditService/Export` and requires the + `audit-export` session verb (admin-only). +- The grant is session-only: it implies no `get`, `list`, or other Zone + resource authority. The service itself performs the bounded authoritative + segment read after method admission. - Adapts `ExportBrokerAuditOk` response contract from `packages/d2b-priv-broker/tests/broker_export_audit.rs`. - Hash chain breaks reported inline in output stream. - No plaintext resource names, paths, argv, or credential bytes. +The canonical Role rule is: + +```yaml +resourceTypes: [Zone] +verbs: [] +sessionVerbs: [audit-export] +subresources: [d2b.audit.v3.AuditService/Export] +resourceNames: [] +zones: [] +executionRefs: [] +``` + ## Doctor and support bundles ### `d2b zone doctor [--zone ] [--json]` @@ -1376,7 +1392,20 @@ record content, raw error messages beyond stable codes. ### `d2b zone support-bundle [--zone ]` Bounded redacted diagnostic snapshot for operator/support use. Requires -`support-bundle` verb (admin-only). Output is NDJSON. +the `support-bundle` session verb (admin-only) on exactly +`d2b.support.v3.SupportService/GenerateBundle`. The grant is session-only and +implies no resource `get`/`list` authority; the admitted service performs its +own bounded internal reads. Output is NDJSON. + +```yaml +resourceTypes: [Zone] +verbs: [] +sessionVerbs: [support-bundle] +subresources: [d2b.support.v3.SupportService/GenerateBundle] +resourceNames: [] +zones: [] +executionRefs: [] +``` Contents: @@ -2074,8 +2103,8 @@ New `packages/d2b-core-controller/tests/config_cleanup.rs`: | Current source | `packages/d2b/tests/audit_contract.rs` (`d2b audit --strict` returns 78; `auditResponse` relay; `authz-audit-requires-admin` denial; daemon-down exit 1 without bash fallback); `packages/d2b-priv-broker/tests/broker_export_audit.rs` (`export_audit_requires_admin_and_exports_op_audit_records`: admin-only, path-free, NDJSON `ExportBrokerAuditOk` shape, `peer_uid` field, `ApplyNftables` operation name in records) | | Reuse action | adapt | | Destination | `packages/d2b/src/zone_audit.rs` (new `d2b zone audit export` subcommand); `packages/d2b/tests/zone_audit_contract.rs` | -| Detailed design | `d2b zone audit export` opens segments read-only (shared flock), streams NDJSON to stdout, validates hash chain inline, reports breaks as inline error records, enforces `audit-export` verb via resource API (admin-only, same `SO_PEERCRED`/Role check as current `ExportBrokerAuditOk`). Assert no `realm`, `node`, `workload_id` fields in exported records. Primary reuse disposition: `adapt`. Preserved source-plan detail: adapt audit CLI contract test (daemon-down/exit behavior); adapt broker export test to new `zone` field and v3 record schema. | -| Integration | `d2b` CLI → resource API `audit-export` verb → `d2b-audit` export iterator → stdout | +| Detailed design | `d2b zone audit export` opens segments read-only (shared flock), streams NDJSON to stdout, validates hash chain inline, reports breaks as inline error records, and invokes only `d2b.audit.v3.AuditService/Export` under the admin-only `audit-export` session verb (same `SO_PEERCRED`/Role check as current `ExportBrokerAuditOk`). The session grant provides no Zone resource authority. Assert no `realm`, `node`, `workload_id` fields in exported records. Primary reuse disposition: `adapt`. Preserved source-plan detail: adapt audit CLI contract test (daemon-down/exit behavior); adapt broker export test to new `zone` field and v3 record schema. | +| Integration | `d2b` CLI → ComponentSession `d2b.audit.v3.AuditService/Export` with `sessionVerbs=[audit-export]` → `d2b-audit` export iterator → stdout | | Data migration | Full d2b 3.0 reset; no v2 state/config import | | Validation | `export_audit.rs`: admin-only, hash break inline, no old field names (`realm`/`node`/`workload_id`), no path/argv in output, exit 0 on clean chain | | Removal proof | `d2b audit` legacy command retained until `d2b zone audit export` covers all record classes | @@ -2104,8 +2133,8 @@ New `packages/d2b-core-controller/tests/config_cleanup.rs`: | Current source | `packages/d2b/tests/host_doctor_contract.rs` (env-redirect sandbox scaffold — no current `support-bundle` equivalent exists) | | Reuse action | adapt | | Destination | `packages/d2b/src/zone_support_bundle.rs`, `packages/d2b/tests/zone_support_bundle_contract.rs` | -| Detailed design | `d2b zone support-bundle [--zone ]` requires `support-bundle` verb. Reads bounded resource status snapshots (32 per type, 512 total; metadata + status only; no spec bytes; no `metadata.name`), controller queue depths, schema catalog (names+versions only), audit segment inventory, OTEL collector metrics summary, bounded structured log ring (2000 entries). NDJSON output. On quarantine: `bundle_completeness: "partial"`, exit 1. Primary reuse disposition: `adapt`. Preserved source-plan detail: reuse env-redirect sandbox scaffold. | -| Integration | `d2b` CLI → Zone resource API list (status subresource only) + controller introspection + audit segment reader + OTEL self-metrics | +| Detailed design | `d2b zone support-bundle [--zone ]` invokes only `d2b.support.v3.SupportService/GenerateBundle` under the admin-only `support-bundle` session verb; the caller receives no resource read authority. The service reads bounded resource status snapshots (32 per type, 512 total; metadata + status only; no spec bytes; no `metadata.name`), controller queue depths, schema catalog (names+versions only), audit segment inventory, OTEL collector metrics summary, and a bounded structured log ring (2000 entries). NDJSON output. On quarantine: `bundle_completeness: "partial"`, exit 1. Primary reuse disposition: `adapt`. Preserved source-plan detail: reuse env-redirect sandbox scaffold. | +| Integration | `d2b` CLI → ComponentSession `d2b.support.v3.SupportService/GenerateBundle` with `sessionVerbs=[support-bundle]` → bounded internal resource/controller/audit/OTEL readers | | Data migration | Full d2b 3.0 reset; no v2 state/config import | | Validation | `zone_support_bundle_contract.rs`: complete/partial bundles; no spec/name/path/argv; field completeness | | Removal proof | None — net-new; no prior owner to remove | diff --git a/docs/specs/ADR-046-work-items.json b/docs/specs/ADR-046-work-items.json index c39919cee..3bd8946a3 100644 --- a/docs/specs/ADR-046-work-items.json +++ b/docs/specs/ADR-046-work-items.json @@ -367,7 +367,7 @@ "dataMigration" : "No telemetry/audit data migration; v3 emits new closed-label OTEL/audit records after cutover and old audio_dispatch audit sites are removed.", "dependencyOwner" : "Depends on `ADR046-audio-006`, `ADR046-audio-007`, `ADR046-audio-011`; `d2b-telemetry` lightweight emitter", "destination" : "`packages/d2b-provider-audio-pipewire/src/telemetry.rs`", - "detailedDesign" : "Emit closed-label Service and Binding metrics plus post-commit audit. Service events distinguish only `owner\\|projection` and closed outcomes; microphone arbitration emits closed transition/outcome plus bounded aggregate counts only. Metrics expose active count `0|1`, aggregate queue depth, and closed arbitration state without Zone/Binding/handle/position labels. Events omit authority keys, import/export keys, remote identity, stream/session ids, endpoints, client timestamps, and queue entries. Enforcement metrics cover owner-local and projection-routed calls without exposing route identity. ProcessEffect audit remains Process Provider-owned.", + "detailedDesign" : "Emit closed-label Service and Binding metrics plus post-commit audit. Service events distinguish only `owner|projection` and closed outcomes; microphone arbitration emits closed transition/outcome plus bounded aggregate counts only. Metrics expose active count `0|1`, aggregate queue depth, and closed arbitration state without Zone/Binding/handle/position labels. Events omit authority keys, import/export keys, remote identity, stream/session ids, endpoints, client timestamps, and queue entries. Enforcement metrics cover owner-local and projection-routed calls without exposing route identity. ProcessEffect audit remains Process Provider-owned.", "integration" : "Audio controller and mediator call telemetry/audit emitters after commit or enforcement; d2b-telemetry exporter and policy_observability consume the resulting records.", "removalProof" : "`audio_dispatch.rs` audit call sites deleted after cutover", "reuseAction" : "adapt", @@ -487,8 +487,8 @@ "dataMigration" : "Full d2b 3.0 reset; no v2 state/config import", "dependencyOwner" : "ADR046-audit-001; CLI owner", "destination" : "`packages/d2b/src/zone_audit.rs` (new `d2b zone audit export` subcommand); `packages/d2b/tests/zone_audit_contract.rs`", - "detailedDesign" : "`d2b zone audit export` opens segments read-only (shared flock), streams NDJSON to stdout, validates hash chain inline, reports breaks as inline error records, enforces `audit-export` verb via resource API (admin-only, same `SO_PEERCRED`/Role check as current `ExportBrokerAuditOk`). Assert no `realm`, `node`, `workload_id` fields in exported records. Primary reuse disposition: `adapt`. Preserved source-plan detail: adapt audit CLI contract test (daemon-down/exit behavior); adapt broker export test to new `zone` field and v3 record schema.", - "integration" : "`d2b` CLI → resource API `audit-export` verb → `d2b-audit` export iterator → stdout", + "detailedDesign" : "`d2b zone audit export` opens segments read-only (shared flock), streams NDJSON to stdout, validates hash chain inline, reports breaks as inline error records, and invokes only `d2b.audit.v3.AuditService/Export` under the admin-only `audit-export` session verb (same `SO_PEERCRED`/Role check as current `ExportBrokerAuditOk`). The session grant provides no Zone resource authority. Assert no `realm`, `node`, `workload_id` fields in exported records. Primary reuse disposition: `adapt`. Preserved source-plan detail: adapt audit CLI contract test (daemon-down/exit behavior); adapt broker export test to new `zone` field and v3 record schema.", + "integration" : "`d2b` CLI → ComponentSession `d2b.audit.v3.AuditService/Export` with `sessionVerbs=[audit-export]` → `d2b-audit` export iterator → stdout", "removalProof" : "`d2b audit` legacy command retained until `d2b zone audit export` covers all record classes", "reuseAction" : "adapt", "reuseSource" : null, @@ -644,7 +644,7 @@ "reuseSource" : "Any useful main d2b-client/provider/session routing symbols named by implementation sub-items", "specId" : "ADR-046-componentsession-and-bus", "specPath" : "docs/specs/ADR-046-componentsession-and-bus.md", - "validation" : "Message isolation; closed session-verb enum including `relay`; relay missing/target verb missing/provider self-assertion fail-closed vectors; route/auth revocation; fairness; reconnect; no direct-store path", + "validation" : "Message isolation; closed session-verb enum including `relay`, `audit-export`, and `support-bundle`; exact diagnostic service/method binding with no implied resource grant; relay missing/target verb missing/provider self-assertion fail-closed vectors; named-target and nameless List/Watch selector/filter preservation at every hop; route/auth revocation; fairness; reconnect; no direct-store path", "workItemId" : "ADR046-bus-001" }, { @@ -768,7 +768,7 @@ "workItemId" : "ADR046-cli-001" }, { - "currentSource" : "`packages/d2b/src/lib.rs`: `cmd_vm_start`, `cmd_vm_stop`, `cmd_vm_restart`, `cmd_vm_status`, `cmd_vm_list`, `cmd_list`, `cmd_status`; wire types: `ListResponse { vms: Vec }`, `StatusResponse { entries: Vec }`, `VmLifecycleState` (old: Stopped/Starting/Booted/Running/Stopping/Restarting/Failed/Unknown) from `packages/d2b-contracts/src/public_wire.rs:2152,2158,2605`; `ListEntry.vm: String` = `WorkloadId`; `VmStatus.lifecycle.state: VmLifecycleState` → target Guest `phase` (Pending\\|Ready\\|Succeeded\\|Degraded\\|Failed\\|Deleted\\|Unknown; Starting/Stopping/Restarting → conditions/reasons); `WorkloadPublicSummary.execution_posture: WorkloadExecutionPosture` from `public_wire.rs:267` (carries `IsolationPosture`; unsafe-local entries have `IsolationPosture::UnsafeLocal`)", + "currentSource" : "`packages/d2b/src/lib.rs`: `cmd_vm_start`, `cmd_vm_stop`, `cmd_vm_restart`, `cmd_vm_status`, `cmd_vm_list`, `cmd_list`, `cmd_status`; wire types: `ListResponse { vms: Vec }`, `StatusResponse { entries: Vec }`, `VmLifecycleState` (old: Stopped/Starting/Booted/Running/Stopping/Restarting/Failed/Unknown) from `packages/d2b-contracts/src/public_wire.rs:2152,2158,2605`; `ListEntry.vm: String` = `WorkloadId`; `VmStatus.lifecycle.state: VmLifecycleState` → target Guest `phase` (Pending|Ready|Succeeded|Degraded|Failed|Deleted|Unknown; Starting/Stopping/Restarting → conditions/reasons); `WorkloadPublicSummary.execution_posture: WorkloadExecutionPosture` from `public_wire.rs:267` (carries `IsolationPosture`; unsafe-local entries have `IsolationPosture::UnsafeLocal`)", "dataMigration" : "None — full d2b 3.0 reset; no prior state to migrate", "dependencyOwner" : "ADR046-cli-001, ADR046-api-001; CLI crate owner", "destination" : "`packages/d2b/src/guest.rs` (`d2b guest start/stop/restart/list/status`); unsafe-local workloads go to `packages/d2b/src/host.rs` (`d2b host list/status/get`), NOT guest.rs", @@ -798,7 +798,7 @@ "workItemId" : "ADR046-cli-003" }, { - "currentSource" : "`packages/d2b/src/lib.rs`: `cmd_shell` (`ShellArgs.vm: String` = `WorkloadId` or `RealmTarget`; routes through `route_vm_target()` → `VmTargetRoute::Local\\|Gateway`; gateway `Attach` fails closed via `shell_gateway_attach_failure()` with error class `gateway-shell-attach-unavailable` at lib.rs:1697,1780), `cmd_shell_attach`, `run_shell_fsm`, `ShellOwnerTransport`; wire: `ShellOp`, `ShellOpResponse`, `ShellAttachArgs { vm: String }`, `ShellListEntry`, `ShellSessionState` from `packages/d2b-contracts/src/public_wire.rs:1319,1394,1452,1409`; `exec_client.rs` signal/TTY machinery", + "currentSource" : "`packages/d2b/src/lib.rs`: `cmd_shell` (`ShellArgs.vm: String` = `WorkloadId` or `RealmTarget`; routes through `route_vm_target()` → `VmTargetRoute::Local|Gateway`; gateway `Attach` fails closed via `shell_gateway_attach_failure()` with error class `gateway-shell-attach-unavailable` at lib.rs:1697,1780), `cmd_shell_attach`, `run_shell_fsm`, `ShellOwnerTransport`; wire: `ShellOp`, `ShellOpResponse`, `ShellAttachArgs { vm: String }`, `ShellListEntry`, `ShellSessionState` from `packages/d2b-contracts/src/public_wire.rs:1319,1394,1452,1409`; `exec_client.rs` signal/TTY machinery", "dataMigration" : "None — full d2b 3.0 reset; no prior state to migrate", "dependencyOwner" : "ADR046-cli-001, `shell-terminal` Provider dossier; CLI crate owner", "destination" : "`packages/d2b/src/shell.rs` (`d2b shell open/attach/list/detach/kill/status`)", @@ -1177,7 +1177,7 @@ "dataMigration" : "Full v3 reset of d2b Credential metadata; Entrablau Guest state is preserved unless explicitly destroyed by the sibling-owned reset flow", "dependencyOwner" : "`ADR046-credential-001` (Credential base fields and status), `ADR046-credential-002` (`d2b.credential.v3` service), D092 Endpoint, D093 Entrablau identity Guest decision, credential-entra owner", "destination" : "`packages/d2b-provider-credential-entra/src/{lib.rs,controller.rs,service.rs,controller_main.rs,agent_main.rs,audit.rs,telemetry.rs}` and corresponding tests/integration docs", - "detailedDesign" : "Implement secret-free controller/helper; require Credential base `identityGuestRef` and `loginEndpointRef`; resolve dependency alias `entra-login-token`; validate same-Zone Guest placement and exact `consumerRef`; implement typed `EntraTokenLeaseClient` whose production implementation is the Entrablau Endpoint; implement `BeginLogin`/`ObserveLogin`/`CancelLogin` projection to Credential base interaction status; route access-token leases end-to-end from Entrablau service to exact consumer over Noise_KK; keep all refresh/login/TPM state inside the Entrablau Guest; declare no Provider state Volume; reject Host placement and all ambient fallback chains. Primary reuse disposition: `adapt`. Preserved source-plan detail: Adapt tests/types where non-secret; replace production token acquisition with Entrablau Endpoint client.", + "detailedDesign" : "Implement secret-free controller/helper; require Credential base `identityGuestRef` and `loginEndpointRef`; resolve dependency alias `entra-login-token`; validate same-Zone Guest placement and exact `consumerRef`; implement typed `EntraTokenLeaseClient` whose production implementation is the Entrablau Endpoint; implement `BeginLogin`/`ObserveLogin`/`CancelLogin` projection to Credential base interaction status; admit service calls only when the same exact allowed operation (`acquire-token`, `refresh-token`, `revoke-token`, or `inspect-metadata`) is present in both `Credential.spec.allowedOperations` and the canonical `use-credential` Role `subresources`; require matching `create`, `update-spec`, or `delete` under `admin-credential` in addition to ordinary CRUD, with no Credential-specific Role fields or coarse aliases; emit the login Endpoint with canonical `visibility = provider` and an exact `consumerPolicy` for the orchestration Provider and configured consumer; route access-token leases end-to-end from Entrablau service to exact consumer over Noise_KK; keep all refresh/login/TPM state inside the Entrablau Guest; declare no Provider state Volume; reject Host placement and all ambient fallback chains; enforce Credential name/ResourceRef/UID/digest as audit-only observable identity, with only `resource_name_digest` retained in authorized bounded audit and no Credential identity in status/errors/logs/OTEL Resource or span attributes/metric labels. Primary reuse disposition: `adapt`. Preserved source-plan detail: Adapt tests/types where non-secret; replace production token acquisition with Entrablau Endpoint client.", "integration" : "Consumer composes `inputs.entrablau.nixosModules.default` into the identity Guest; sibling package declares login/token Process and Endpoint; d2b Credential resource binds `identityGuestRef`, `loginEndpointRef`, and `consumerRef`", "removalProof" : "Old abstract Host credential paths and any direct Entra effect client are deleted only after the Entrablau Endpoint-backed provider passes the test matrix", "reuseAction" : "adapt", @@ -1207,7 +1207,7 @@ "dataMigration" : "None — controller lifecycle code only; no runtime state import", "dependencyOwner" : "ADR046-credential-001, ADR046-credential-002; ADR046-reconcile-001, ADR046-reconcile-002; ADR046-mi-topology-001; owner: Credential controller toolkit and managed-identity controller", "destination" : "packages/d2b-provider-credential-managed-identity/src/controller.rs; packages/d2b-contracts/src/v3/credential_controller.rs", - "detailedDesign" : "Managed-identity-specific controller design: implement async reconcile and agent spawn/teardown from §Async reconcile; enforce system-only domain; spawn agent on Credential admission plus dependency-ready, not on `phase=Ready`; implement `observeInterval=30s` health-check RPC to the agent, which calls `InspectMetadata` on the injected client; controller never calls IMDS; derive idempotency key as `SHA-256(UID \\|\\| \":\" \\|\\| rotationGeneration.to_le_bytes() \\|\\| \":\" \\|\\| operation_class_byte)`; enforce `MAX_LOCAL_LEASES=256` in the resource store; implement Deleted-phase closure by clearing `provider-revoke` only after agent Process deletion and revocation confirmation while core/audit own Deleted revision and deletion record. Primary reuse disposition: `adapt`. Preserved source-plan detail: adapt shared Credential controller lifecycle to managed-identity controller/agent spawn and teardown.", + "detailedDesign" : "Managed-identity-specific controller design: implement async reconcile and agent spawn/teardown from §Async reconcile; enforce system-only domain; spawn agent on Credential admission plus dependency-ready, not on `phase=Ready`; implement `observeInterval=30s` health-check RPC to the agent, which calls `InspectMetadata` on the injected client; controller never calls IMDS; derive idempotency key as `SHA-256(UID || \":\" || rotationGeneration.to_le_bytes() || \":\" || operation_class_byte)`; enforce `MAX_LOCAL_LEASES=256` in the resource store; implement Deleted-phase closure by clearing `provider-revoke` only after agent Process deletion and revocation confirmation while core/audit own Deleted revision and deletion record. Primary reuse disposition: `adapt`. Preserved source-plan detail: adapt shared Credential controller lifecycle to managed-identity controller/agent spawn and teardown.", "integration" : "Shared Credential controller contract produces reconcile events; managed-identity controller consumes them, manages agent Process resources, and writes Credential/agent status; generated controller contracts are consumed by all Credential Providers.", "removalProof" : "V2 `CredentialProvider` status/enrollment trait lifecycle is superseded after shared controller reconcile and managed-identity agent lifecycle pass parity", "reuseAction" : "adapt", @@ -1342,14 +1342,14 @@ "dataMigration" : "Full d2b 3.0 reset; no v2 credential import", "dependencyOwner" : "`ADR046-object-001` (resource envelope); `ADR046-identities-001` (types); W0 shared contract root; `d2b-contracts`", "destination" : "`packages/d2b-contracts/src/v3/credential.rs`", - "detailedDesign" : "Define `CredentialSpec`, `CredentialStatus`, `CredentialLeaseHandle` (opaque bounded newtype), `CredentialRotationPolicy`, `CredentialRevocationPolicy`, `CredentialScope`, `OperationClass` enum, `CredentialLeaseState`, `PlacementBinding`, `CredentialConditionType`, and all serde/validation/redaction helpers; reuse `OpaqueAzureRef` from v3 baseline directly; enforce zero-secret-bytes charset validation on all string fields at construction Primary reuse disposition: `adapt`. Preserved source-plan detail: extract and adapt.", + "detailedDesign" : "Define `CredentialSpec`, `CredentialStatus`, `CredentialLeaseHandle` (opaque bounded newtype), `CredentialRotationPolicy`, `CredentialRevocationPolicy`, `CredentialScope`, `OperationClass` enum, `CredentialLeaseState`, `PlacementBinding`, `CredentialConditionType`, and all serde/validation/redaction helpers; reuse `OpaqueAzureRef` from v3 baseline directly; enforce zero-secret-bytes charset validation on all string fields at construction; prevent Credential name/ResourceRef/UID or any identity-derived digest from serializing into any status layer or typed error Primary reuse disposition: `adapt`. Preserved source-plan detail: extract and adapt.", "integration" : "Credential controller, Provider dossier schemas, Nix compiler, and resource store all consume one canonical contract", "removalProof" : "Old `CredentialProvider` trait and `CredentialStatus` enum removed only after all v3 Credential Provider controllers consume this contract", "reuseAction" : "adapt", "reuseSource" : "main `a1cc0b2d`: `d2b-provider-credential-secret-service/src/lib.rs` types: `SecretServiceLeaseRequest`, `SecretServiceLeaseRef`, `SecretServiceLeaseGrant`, `SecretServiceLeaseInspection`, `SecretServiceLeaseRenewal`, `SecretServiceLeaseRevocation`, `SecretServiceLeaseState`; parallel entra/managed-identity types; `CredentialLease`, `CredentialLeaseRequest`, `CredentialLeaseState` from `d2b-contracts/src/v2_provider.rs`", "specId" : "ADR-046-resources-credential", "specPath" : "docs/specs/ADR-046-resources-credential.md", - "validation" : "Schema golden vectors; charset/length tests; serde unknown-field rejection; `OpaqueAzureRef` round-trip and secret-shape rejection parity; `leaseHandle` and `sourceVersion` opaque newtype tests; status redaction tests", + "validation" : "Schema golden vectors; charset/length tests; serde unknown-field rejection; `OpaqueAzureRef` round-trip and secret-shape rejection parity; `leaseHandle` and `sourceVersion` opaque newtype tests; status/error redaction tests with Credential name/ResourceRef/UID/digest canaries", "workItemId" : "ADR046-credential-001" }, { @@ -1357,14 +1357,14 @@ "dataMigration" : "None — full d2b 3.0 reset; no prior state to migrate", "dependencyOwner" : "`ADR046-credential-001`; `ADR046-api-001` (resource API); `ADR046-bus-001` (d2b-bus); Credential service owner", "destination" : "`packages/d2b-contracts/proto/v3/credential.proto`; `packages/d2b-credential-service/src/{service.rs, client.rs, server.rs}`", - "detailedDesign" : "Define `d2b.credential.v3` protobuf service with methods: `Status`, `AcquireToken`, `RefreshToken`, `RevokeToken`, `SignChallenge`, `InspectMetadata`; each request carries `credential_ref`, `operation_class`, `operation_id`, `idempotency_key`, `requested_expiry_unix_ms`, `deadline_unix_ms`; `Status`, `RevokeToken`, and `InspectMetadata` responses carry only non-secret metadata (leaseHandle digest, rotationGeneration, sourceVersion, expiresAtUnixMs, state, outcome code); `AcquireToken`, `RefreshToken`, and `SignChallenge` responses additionally include a `delivery_session_params` field carrying the binding contract fields required to establish the end-to-end credential-delivery ComponentSession (see §Credential-delivery endpoint contract); the token bytes themselves travel in the separate Noise-encrypted delivery session, never in the outer DTO; strict unknown-field rejection; bounded message sizes; all record wrappers for delivery sessions must be zeroizing types", - "integration" : "d2b-bus routes `d2b.credential.v3` service to the exact credential provider Process identified by `Credential.spec.providerRef`; RBAC checks `use-credential` verb before dispatch; for `AcquireToken`/`RefreshToken`/`SignChallenge`, bus additionally authorizes the credential-delivery endpoint route and forwards opaque Noise-encrypted delivery records without terminating or buffering them; bus never stores or inspects delivery record plaintext", + "detailedDesign" : "Define `d2b.credential.v3` protobuf service with exactly five operation methods: `AcquireToken`, `RefreshToken`, `RevokeToken`, `SignChallenge`, and `InspectMetadata`; map those methods one-to-one to the same exact operation class in `spec.allowedOperations` and the existing Role `subresources` field (`acquire-token`, `refresh-token`, `revoke-token`, `sign-challenge`, or `inspect-metadata`) under the canonical `use-credential` resource verb; derive the operation class from the method rather than accepting a caller-selected operation-class field; each request carries `credential_ref`, `operation_id`, `idempotency_key`, `requested_expiry_unix_ms`, `deadline_unix_ms`; map administrative lifecycle to matching `admin-credential` subresources `create`, `update-spec`, and `delete`, supplemental to ordinary CRUD, never to a new Role field or method-name alias; `RevokeToken` and `InspectMetadata` responses carry only non-secret metadata (leaseHandle digest, rotationGeneration, sourceVersion, expiresAtUnixMs, state, outcome code); `AcquireToken`, `RefreshToken`, and `SignChallenge` responses additionally include a `delivery_session_params` field carrying the binding contract fields required to establish the end-to-end credential-delivery ComponentSession (see §Credential-delivery endpoint contract); the token bytes themselves travel in the separate Noise-encrypted delivery session, never in the outer DTO; strict unknown-field rejection; bounded message sizes; all record wrappers for delivery sessions must be zeroizing types", + "integration" : "d2b-bus routes `d2b.credential.v3` service to the exact credential provider Process identified by `Credential.spec.providerRef`; before dispatch RBAC requires the exact operation class in both the `use-credential` Role `subresources` and `spec.allowedOperations`; for `AcquireToken`/`RefreshToken`/`SignChallenge`, bus additionally authorizes the credential-delivery endpoint route and forwards opaque Noise-encrypted delivery records without terminating or buffering them; bus never stores or inspects delivery record plaintext", "removalProof" : "Old v2 `CredentialProviderService` proto removed only after all v3 callers migrate", "reuseAction" : "adapt", "reuseSource" : "main `a1cc0b2d`: `packages/d2b-contracts/proto/v2/provider_credential.proto` method names", "specId" : "ADR-046-resources-credential", "specPath" : "docs/specs/ADR-046-resources-credential.md", - "validation" : "Protocol golden vectors for each method; malformed/oversize rejection; `leaseHandle` opacity tests (secret-canary must not appear in outer DTO or delivery routing metadata); locked/unavailable/denied/expired state tests; delivery session binding contract round-trip; zeroizing record type unit tests; delivery channel never materialized in non-delivery method tests", + "validation" : "Protocol golden vectors for each method; Role matrix for the five exact allowed-operation subresources under `use-credential` and exact `create`, `update-spec`, and `delete` subresources under `admin-credential`; prove admin permission is supplemental to ordinary CRUD; deny empty/wildcard/unknown/mismatched subresources, alternate Credential-operation Role fields, and method-name aliases before Provider dispatch; malformed/oversize rejection; `leaseHandle` opacity tests (secret-canary must not appear in outer DTO or delivery routing metadata); locked/unavailable/denied/expired state tests; delivery session binding contract round-trip; zeroizing record type unit tests; delivery channel never materialized in non-delivery method tests", "workItemId" : "ADR046-credential-002" }, { @@ -1387,14 +1387,14 @@ "dataMigration" : "Full reset", "dependencyOwner" : "`ADR046-credential-001`, `ADR046-credential-002`; `ADR046-reconcile-001`; credential-entra owner", "destination" : "`packages/d2b-provider-credential-entra/src/{lib.rs, controller.rs, service.rs, main.rs}`; `packages/d2b-provider-credential-entra/tests/{lifecycle.rs, conformance.rs, faults.rs, canary.rs, delivery.rs, placement.rs}`; `packages/d2b-provider-credential-entra/integration/{container-service.sh, guest-placement.nix, cleanup-rollback.sh}`; `packages/d2b-provider-credential-entra/README.md` (all §Provider README required sections)", - "detailedDesign" : "Adapt `EntraCredentialProvider` and `EntraCredentialProviderFactory` to v3 service; replace v2 `AgentPlacementBinding` with v3 `PlacementBinding` enum (user-agent, guest-agent only; reject host-system); validate `tenantId` config field using `OpaqueAzureRef::parse` from v3 baseline `d2b-realm-provider/src/credential.rs` (note: current v3 source field is named via `AzureControlPlaneRef`; target field name is `tenantId`); retain `EntraCredentialClient` trait unchanged; map `EntraClientError::InteractionRequired` to `credential-provider-unavailable` (not denied); enforce `EntraCredentialOwner::ExactConsumer` so only the declared `consumerRef` may acquire Primary reuse disposition: `adapt`. Preserved source-plan detail: copy and adapt.", + "detailedDesign" : "Adapt `EntraCredentialProvider` and `EntraCredentialProviderFactory` to v3 service; replace v2 `AgentPlacementBinding` with v3 `PlacementBinding` enum (user-agent, guest-agent only; reject host-system); validate `tenantId` config field using `OpaqueAzureRef::parse` from v3 baseline `d2b-realm-provider/src/credential.rs` (note: current v3 source field is named via `AzureControlPlaneRef`; target field name is `tenantId`); retain `EntraCredentialClient` trait unchanged; map `EntraClientError::InteractionRequired` to `credential-provider-unavailable` (not denied); enforce `EntraCredentialOwner::ExactConsumer` so only the declared `consumerRef` may acquire; enforce canonical Credential verb/subresource admission, provider-visible Endpoint plus exact consumer policy, and audit-only Credential identity as frozen in the dedicated Entra dossier Primary reuse disposition: `adapt`. Preserved source-plan detail: copy and adapt.", "integration" : "User-domain or system-domain Process under Guest; d2b-bus routing; Credential controller", "removalProof" : "Same as ADR046-credential-003", "reuseAction" : "adapt", "reuseSource" : "main `a1cc0b2d`: `packages/d2b-provider-credential-entra/src/lib.rs` (full implementation); `src/tests.rs` (full test suite including `FakeEntraClient`, `credential_canary`/`endpoint_canary`, interaction-required, colocated-consumer, generation-mismatch tests)", "specId" : "ADR-046-resources-credential", "specPath" : "docs/specs/ADR-046-resources-credential.md", - "validation" : "**`src/` unit**: `EntraCredentialClient` trait API, `OpaqueAzureRef::parse` on `tenantId`, `EntraCredentialOwner::ExactConsumer` guard, `EntraClientState` transitions. **`tests/` Cargo integration**: `lifecycle.rs` (acquire/refresh/revoke/inspect with `FakeEntraClient`); `conformance.rs` (all conformance arms); `faults.rs` (interaction-required → unavailable, generation-mismatch, colocated-consumer rejection); `canary.rs` (`credential_canary` and `endpoint_canary` absent from every response and delivery record); `delivery.rs` (delivery-session binding, zeroizing, replay-safe); `placement.rs` (host-system placement rejected). **`integration/` fixtures**: `container-service.sh`; `guest-placement.nix` (user-domain and system-domain Process on Guest in runNixOSTest); `cleanup-rollback.sh`.", + "validation" : "**`src/` unit**: `EntraCredentialClient` trait API, `OpaqueAzureRef::parse` on `tenantId`, `EntraCredentialOwner::ExactConsumer` guard, `EntraClientState` transitions. **`tests/` Cargo integration**: `lifecycle.rs` (acquire/refresh/revoke/inspect with `FakeEntraClient`); `conformance.rs` (all conformance arms, canonical Endpoint visibility/consumer policy, and exact Role verb/subresource mapping); `faults.rs` (interaction-required → unavailable, generation-mismatch, colocated-consumer and mismatched Role-subresource rejection); `canary.rs` (secret plus Credential name/ResourceRef/UID/digest canaries absent from status/errors/logs and every OTEL Resource/span attribute/metric label; authorized audit retains only `resource_name_digest`); `delivery.rs` (delivery-session binding, zeroizing, replay-safe); `placement.rs` (host-system placement rejected). **`integration/` fixtures**: `container-service.sh`; `guest-placement.nix` (user-domain and system-domain Process on Guest in runNixOSTest); `cleanup-rollback.sh`.", "workItemId" : "ADR046-credential-004" }, { @@ -1417,14 +1417,14 @@ "dataMigration" : "None; v3 reset", "dependencyOwner" : "`ADR046-credential-001`, `ADR046-credential-002`; `ADR046-reconcile-001`, `ADR046-reconcile-002`; Credential controller owner", "destination" : "`packages/d2b-provider-credential-/src/controller.rs`; `packages/d2b-contracts/src/v3/credential_controller.rs`", - "detailedDesign" : "Implement Credential controller handler conforming to `ADR-046-resource-reconciliation` async loop; implement `reconcile`, `observe`, `finalize`, `drain`, and `health` handlers; implement rotation state machine (proactive/on-expiry/on-demand policies); implement `provider-revoke` finalizer execution with `revocation.onOwnerDelete` policy; implement provider-generation-change detection and revocation; implement `CredentialReady`, `RotationDue`, `ProviderUnavailable`, `LeaseRevoked` condition logic; implement bounded idempotency key derivation (Credential UID + rotationGeneration + operation class, no secret material); implement `observeInterval=30s` health check calling `InspectMetadata`; bounded retry/backpressure with typed `credential-rotation-failed` outcome; enforce `MAX_LOCAL_LEASES=256` per controller provider instance", + "detailedDesign" : "Implement Credential controller handler conforming to `ADR-046-resource-reconciliation` async loop; implement `reconcile`, `observe`, `finalize`, `drain`, and `health` handlers; implement rotation state machine (proactive/on-expiry/on-demand policies); implement `provider-revoke` finalizer execution with `revocation.onOwnerDelete` policy; implement provider-generation-change detection and revocation; implement `CredentialReady`, `RotationDue`, `ProviderUnavailable`, `LeaseRevoked` condition logic; use controller Roles with the exact allowed-operation subresource matching each `use-credential` service call, while status/finalizer writes retain their separate structural ownership checks; never add a Credential-specific Role field or coarse alias; implement bounded idempotency key derivation (Credential UID + rotationGeneration + operation class, no secret material); implement `observeInterval=30s` health check calling `InspectMetadata`; bounded retry/backpressure with typed `credential-rotation-failed` outcome; enforce `MAX_LOCAL_LEASES=256` per controller provider instance", "integration" : "Provider controller Process → d2b-bus → `d2b.credential.v3` service in provider process → injected client/port; status updates through resource API; watch subscription on Credential, Provider, Host/Guest dependency types", "removalProof" : "Not applicable (new controller)", "reuseAction" : "adapt", "reuseSource" : "main `a1cc0b2d`: `packages/d2b-provider-toolkit/src/conformance.rs` (provider conformance pattern); `packages/d2b-provider-toolkit/src/adapter.rs` (controller toolkit pattern)", "specId" : "ADR-046-resources-credential", "specPath" : "docs/specs/ADR-046-resources-credential.md", - "validation" : "Controller state-machine golden vectors; rotation-policy matrix (proactive/on-demand/on-expiry × success/locked/unavailable/expired); finalizer execution tests; provider-generation-change revocation tests; idempotency key derivation tests; observe-interval drift detection test; canary tests confirm zero secret bytes in all controller-written status fields", + "validation" : "Controller state-machine golden vectors; rotation-policy matrix (proactive/on-demand/on-expiry × success/locked/unavailable/expired); exact Role-subresource admission matrix; finalizer execution tests; provider-generation-change revocation tests; idempotency key derivation tests; observe-interval drift detection test; canary tests confirm zero secret bytes and no Credential name/ResourceRef/UID/digest in any controller-written status or error", "workItemId" : "ADR046-credential-006" }, { @@ -1433,13 +1433,13 @@ "dependencyOwner" : "`ADR046-credential-001`; `ADR046-identities-002` (Nix resource compiler); `ADR046-api-001` (resource API, for create/update/delete); `ADR046-reconcile-001` (activation-nixos controller); Nix integrator", "destination" : "`nixos-modules/options-resources.nix` (generic schema-derived resource options; not type-specific), `nixos-modules/activation-nixos-cleanup.nix`", "detailedDesign" : "**(1) Schema-derived options and eval-time validation**: implement `d2b.zones..resources. = { type = \"...\"; spec = { ... }; }` as a generic attrset option; an optional `metadata` sub-attr may contain `ownerRef` and/or presentation `labels`/`annotations`. Nix option types, defaults, and inline docs for `spec` fields are generated from the committed `ResourceTypeSchema` JSON (`docs/reference/schemas/v3/credential.json`) and the signed Provider schema — no bespoke options module is maintained separately. `metadata.name` derives from the attr key; `metadata.zone` from the Zone attr key; `apiVersion` defaults to `resources.d2bus.org/v3`; `status`/`uid`/`generation`/`revision`/timestamps/`managedBy`/`configurationGeneration` are not authored. Core assigns `metadata.managedBy = \"configuration\"` and `metadata.configurationGeneration = ` to all configuration-managed resources at create/update time; these are never authored in Nix. Eval-time assertions (applied to all entries with `type = \"Credential\"`): `spec.providerRef` resolves a Provider in the same Zone whose `credentialDomains` includes `spec.scope.domainFilter` and whose `spec.artifactId` (a sibling of `spec.config` on the Provider resource, not inside `spec.config`) resolves an artifact catalog entry of `type = \"provider\"`; `spec.audience` charset (`^[A-Za-z0-9._:/@-]+$`, max 256); `spec.rotation.proactiveWindowMs < maxLeaseLifetimeMs / 2`; `spec.consumerRef`/`scope.executionRef`/`scope.userRef` resolve declared Zone resources; duplicate `(providerRef, executionRef, userRef, audience)` tuple rejected; `contains_sensitive_shape` on all string fields; Provider-specific placement constraints; `allowedOperations` ⊆ `providerRef.supportedOperations`. **(2) Canonical JSON and bundle emission**: render `spec` attr directly to `spec` object in canonical JSON (no field renames/re-nesting); `metadata` in output contains only derived `name`/`zone` and optionally Nix-authored `ownerRef`/`labels`/`annotations`; `apiVersion` is top-level, not inside `metadata`; `finalizers` is omitted from the Nix-rendered input (core manages finalizers, never accepts them from the bundle); no management labels are emitted by Nix; sort bundle by `(type, name)`; write to `/etc/d2b/zones//resource-bundle.json` with digest. **(2b) Artifact catalog emission**: derivation-valued inputs (`d2b.artifacts.`) are compiled separately into an integrity-pinned artifact catalog (`/etc/d2b/zones//artifact-catalog.json`) with its own digest header; each entry records `id`, `type`, `sha256`, and bounded closure metadata; store paths are private catalog implementation data absent from the resource bundle, status, audit, and logs; `activation-nixos` verifies both digests before any create/update; missing or wrong-type `artifactId` references fail the NixOS build. **(3) Build-time schema validation**: validate rendered JSON against `docs/reference/schemas/v3/credential.json` and Provider-specific schema; enforce `secretRef` fields use `Credential/` refs; enforce no store paths in any resource bundle or status output; drift gate (`make test-drift`) regenerates schemas with `cargo xtask gen-schemas` and asserts `git diff --exit-code`; Nix options module drift checked in the same gate. **(4) Generation transition and cleanup contract**: activation-nixos controller verifies SHA-256 digest of both resource bundle and artifact catalog, creates/updates desired-set resources, activates without blocking on cleanup, issues async Delete for absent configuration-managed resources (those with `metadata.managedBy = \"configuration\"`), sets Degraded/Cleanup=True on removed resources; retains up to `retainedConfigurationMax` (default 3, range 1..16) prior bundles; oldest prune when count exceeded; no time-based rollback window. **(5) Configuration-managed vs controller/API isolation**: `managedBy` and `ownerRef` are orthogonal; configuration-managed and API-created resources may each carry an optional same-Zone `ownerRef` and participate in owner cascade. Cleanup checks `metadata.managedBy = \"configuration\"` before issuing Delete; resources with `metadata.managedBy = \"controller\"` or `metadata.managedBy = \"api\"` are never deleted by this path; API-created resources persist until explicit Delete and are never generation-swept.", - "integration" : "`activation-nixos` Provider creates/updates Credential resources from emitted envelopes; Credential controller `provider-revoke` finalizer handles cleanup Deletes; owner controller reconciles children of deleted configuration-managed Credentials", + "integration" : "`activation-nixos` Provider creates/updates/deletes Credential resources from emitted envelopes with both the ordinary CRUD permission and the matching `admin-credential/create`, `admin-credential/update-spec`, or `admin-credential/delete` permission; Credential controller `provider-revoke` finalizer handles cleanup Deletes; owner controller reconciles children of deleted configuration-managed Credentials", "removalProof" : "Not applicable (new module)", "reuseAction" : "adapt", "reuseSource" : null, "specId" : "ADR-046-resources-credential", "specPath" : "docs/specs/ADR-046-resources-credential.md", - "validation" : "**(eval/build)**: nix-unit golden JSON envelope for each example (spec shape, no management labels in Nix output, sort, digest); assertion-failure tests for secret-shaped audience, mismatched providerRef/domainFilter, proactiveWindow > half maxLifetime, duplicate binding tuple, unresolved refs; artifact catalog: assertion-failure for missing `artifactId`, wrong-type `artifactId`, duplicate catalog ID; bundle + artifact catalog digest round-trip; artifact catalog store-path absence from resource bundle and status; Provider-specific schema cross-check; `make test-drift` schema drift gate. **(runtime integration in `tests/host-integration/`)**: `credential-cleanup-basic` (removed resource reaches Deleted); `credential-cleanup-nonblocking` (activation Ready before cleanup finalizer finishes); `credential-cleanup-pending-status` (Cleanup=True on removed resource, PendingCleanup=True on Provider); `credential-cleanup-stalled` (Degraded stall detection and recovery); `credential-cleanup-controller-children-preserved` (ownerRef children cleaned by Credential controller); `credential-cleanup-no-dynamic-deletion` (controller-created Credential with `managedBy = \"controller\"` not deleted); `credential-retained-generation-count` (up to retainedConfigurationMax bundles retained; rollback re-creates from retained bundle; oldest pruned when count exceeded); `credential-bundle-digest-mismatch` (tampered bundle aborts activation).", + "validation" : "**(eval/build)**: nix-unit golden JSON envelope for each example (spec shape, no management labels in Nix output, sort, digest); assertion-failure tests for secret-shaped audience, mismatched providerRef/domainFilter, proactiveWindow > half maxLifetime, duplicate binding tuple, unresolved refs; generated activation Role contains `admin-credential` with exact `subresources = [ \"create\" \"update-spec\" \"delete\" ]`, no coarse alias, and no undeclared Role field; permission tests prove each lifecycle action also requires its ordinary CRUD verb; artifact catalog: assertion-failure for missing `artifactId`, wrong-type `artifactId`, duplicate catalog ID; bundle + artifact catalog digest round-trip; artifact catalog store-path absence from resource bundle and status; Provider-specific schema cross-check; `make test-drift` schema drift gate. **(runtime integration in `tests/host-integration/`)**: `credential-cleanup-basic` (removed resource reaches Deleted); `credential-cleanup-nonblocking` (activation Ready before cleanup finalizer finishes); `credential-cleanup-pending-status` (Cleanup=True on removed resource, PendingCleanup=True on Provider); `credential-cleanup-stalled` (Degraded stall detection and recovery); `credential-cleanup-controller-children-preserved` (ownerRef children cleaned by Credential controller); `credential-cleanup-no-dynamic-deletion` (controller-created Credential with `managedBy = \"controller\"` not deleted); `credential-retained-generation-count` (up to retainedConfigurationMax bundles retained; rollback re-creates from retained bundle; oldest pruned when count exceeded); `credential-bundle-digest-mismatch` (tampered bundle aborts activation).", "workItemId" : "ADR046-credential-007" }, { @@ -1447,14 +1447,14 @@ "dataMigration" : "None — full d2b 3.0 reset; no prior state to migrate", "dependencyOwner" : "`ADR046-credential-001`, `ADR046-credential-006`; audit/OTEL integrator", "destination" : "`packages/d2b-provider-credential-/src/audit.rs`, `telemetry.rs`; `packages/d2b-contract-tests/tests/credential_audit.rs`", - "detailedDesign" : "Implement audit record emission for all credential service methods and controller events using the field set defined in §Audit, with Credential identity represented only by the authorized bounded `resource_name_digest`; implement OTEL span/metric emission using the closed semantic label set in §OTEL and metrics, with expiry reported as a provider/placement aggregate and no Credential resource name, ResourceRef, UID, digest, derived identity token, Zone/resource-name-derived label, or non-allowlisted OTEL Resource attribute; retain applicable generic collector-allowlisted Resource attributes (`d2b.zone`, `d2b.provider`, `d2b.component`, and service fields); implement `contains_sensitive_shape` checks in all string fields of audit records and metric label values (adapted from `d2b-realm-provider/src/error.rs:contains_sensitive_shape`); add canary-enforcement tests that verify `\"secret-canary\"`, `\"entra-token-canary\"`, `\"managed-identity-canary\"`, Credential name/ref/UID/digest canaries, and Zone name values never appear in any metric label, span attribute, log line, or status field, and that Credential identity canaries are also absent from OTEL Resource attributes", + "detailedDesign" : "Implement audit record emission for all credential service methods and controller events using the field set defined in §Audit, with Credential identity represented only by the authorized bounded `resource_name_digest` after authorization and no identity-bearing record elicited by a denied request; implement OTEL span/metric emission using the closed semantic label set in §OTEL and metrics, with expiry reported as a provider/placement aggregate and no Credential resource name, ResourceRef, UID, digest, derived identity token, Zone/resource-name-derived label, or non-allowlisted OTEL Resource attribute; retain applicable generic collector-allowlisted Resource attributes (`d2b.zone`, `d2b.provider`, `d2b.component`, and service fields); implement `contains_sensitive_shape` checks in all string fields of audit records and metric label values (adapted from `d2b-realm-provider/src/error.rs:contains_sensitive_shape`); add canary-enforcement tests that verify `\"secret-canary\"`, `\"entra-token-canary\"`, `\"managed-identity-canary\"`, Credential name/ref/UID/digest canaries, and Zone name values never appear in any metric label, span attribute, log line, status field, error, or collector diagnostic, and that Credential identity canaries are also absent from OTEL Resource attributes", "integration" : "Credential controller and service handlers emit audit records and telemetry through Zone audit/OTEL paths", "removalProof" : "Not applicable", "reuseAction" : "adapt", "reuseSource" : null, "specId" : "ADR-046-resources-credential", "specPath" : "docs/specs/ADR-046-resources-credential.md", - "validation" : "Canary tests across all three Provider crates; authorized audit record field-presence tests require `resource_name_digest` and reject raw Credential name/ResourceRef/UID; structural metric descriptor tests assert exact absence of `vm`, `zone`, `zone_id`, `zone_uid`, `credential_name`, `credential_ref`, `credential_uid`, `credential_digest`, `resource_name_digest`, and every resource-name-derived key; Credential name/ref/UID/digest canaries are absent from every OTEL Resource attribute, span attribute, and metric label; Zone-name canaries are absent from spans and labels while resource-attribute tests preserve the generic collector allowlist including `d2b.zone`, `d2b.provider`, `d2b.component`, and service fields; complete Credential metric/span frames pass the shared collector ingress validator, while adding `d2b.credential.name` or any Credential identity key/value rejects the whole frame", + "validation" : "Canary tests across all three Provider crates; authorized audit record field-presence tests require `resource_name_digest` and reject raw Credential name/ResourceRef/UID, while denied-request tests emit no identity-bearing audit; structural metric descriptor tests assert exact absence of `vm`, `zone`, `zone_id`, `zone_uid`, `credential_name`, `credential_ref`, `credential_uid`, `credential_digest`, `resource_name_digest`, and every resource-name-derived key; Credential name/ref/UID/digest canaries are absent from every status field, error, log/Debug line, collector diagnostic, OTEL Resource attribute, span attribute, and metric label; Zone-name canaries are absent from spans and labels while resource-attribute tests preserve the generic collector allowlist including `d2b.zone`, `d2b.provider`, `d2b.component`, and service fields; complete Credential metric/span frames pass the shared collector ingress validator, while adding `d2b.credential.name` or any Credential identity key/value rejects the whole frame", "workItemId" : "ADR046-credential-008" }, { @@ -1643,7 +1643,7 @@ "dependencyOwner" : "ADR046-device-001; device-usbip provider owner", "destination" : "`packages/d2b-provider-device-usbip/src/` (controller, daemon Process, bind/unbind EphemeralProcess, firewall); `packages/d2b-provider-device-usbip/tests/` (hermetic Cargo integration); `packages/d2b-provider-device-usbip/integration/` (container/Host scenarios); `packages/d2b-provider-device-usbip/README.md`", "detailedDesign" : "Device spec/status; bus ID validation; firewall rule ownership-marker; bind/unbind EphemeralProcess; per-Device daemon Process (owned by device-usbip; Network supplies dependency/firewall interface); Nix emitter; all four required crate paths present (see \"Provider crate layout\") Primary reuse disposition: `adapt`. Preserved source-plan detail: extract and adapt.", - "integration" : "Zone resource store; broker `UsbipBindFirewallRule { action: Ensure \\| Remove }`; nftables marker", + "integration" : "Zone resource store; broker `UsbipBindFirewallRule { action: Ensure | Remove }`; nftables marker", "removalProof" : "ProcessRole::Usbip removed after parity", "reuseAction" : "adapt", "reuseSource" : null, @@ -2002,8 +2002,8 @@ "dataMigration" : "Full d2b 3.0 reset; no v2 state/config import", "dependencyOwner" : "ADR046-doctor-001; CLI/doctor owner", "destination" : "`packages/d2b/src/zone_support_bundle.rs`, `packages/d2b/tests/zone_support_bundle_contract.rs`", - "detailedDesign" : "`d2b zone support-bundle [--zone ]` requires `support-bundle` verb. Reads bounded resource status snapshots (32 per type, 512 total; metadata + status only; no spec bytes; no `metadata.name`), controller queue depths, schema catalog (names+versions only), audit segment inventory, OTEL collector metrics summary, bounded structured log ring (2000 entries). NDJSON output. On quarantine: `bundle_completeness: \"partial\"`, exit 1. Primary reuse disposition: `adapt`. Preserved source-plan detail: reuse env-redirect sandbox scaffold.", - "integration" : "`d2b` CLI → Zone resource API list (status subresource only) + controller introspection + audit segment reader + OTEL self-metrics", + "detailedDesign" : "`d2b zone support-bundle [--zone ]` invokes only `d2b.support.v3.SupportService/GenerateBundle` under the admin-only `support-bundle` session verb; the caller receives no resource read authority. The service reads bounded resource status snapshots (32 per type, 512 total; metadata + status only; no spec bytes; no `metadata.name`), controller queue depths, schema catalog (names+versions only), audit segment inventory, OTEL collector metrics summary, and a bounded structured log ring (2000 entries). NDJSON output. On quarantine: `bundle_completeness: \"partial\"`, exit 1. Primary reuse disposition: `adapt`. Preserved source-plan detail: reuse env-redirect sandbox scaffold.", + "integration" : "`d2b` CLI → ComponentSession `d2b.support.v3.SupportService/GenerateBundle` with `sessionVerbs=[support-bundle]` → bounded internal resource/controller/audit/OTEL readers", "removalProof" : "None — net-new; no prior owner to remove", "reuseAction" : "adapt", "reuseSource" : null, @@ -2017,14 +2017,14 @@ "dataMigration" : "Full reset; no v2 resource import", "dependencyOwner" : "W0 shared contract root; `d2b-contracts`", "destination" : "`packages/d2b-contracts/src/v3/host.rs`, `packages/d2b-contracts/src/v3/guest.rs`, `packages/d2b-contracts/src/v3/execution_policy.rs`, `packages/d2b-contracts/src/v3/process.rs`, `packages/d2b-contracts/src/v3/ephemeral_process.rs`, `packages/d2b-contracts/src/v3/user.rs`, `packages/d2b-contracts/src/v3/endpoint.rs`", - "detailedDesign" : "Implement strict typed Rust structs for HostSpec, GuestSpec, ExecutionPolicy, ExecutionSpec, SandboxSpec, BudgetSpec, MountSpec, NetworkUsageSpec, DeviceUsageSpec, EndpointSpec, EndpointConsumerPolicy, TelemetrySpec, ProcessSpec, EphemeralProcessSpec, UserSpec; EndpointSpec accepts exactly `owner\\|provider\\|zone`, and EndpointConsumerPolicy owns the only finer gates (`allowedSubjects`, `allowedProviderComponents`, `allowedOperations`) with no schema aliases; strict serde deny_unknown_fields; bounds/redaction on all string fields; stable error types; `UserSpec.osUsername` validated as OS username (1..255 bytes, no NUL/control/path-separator), not ResourceName grammar Primary reuse disposition: `adapt`. Preserved source-plan detail: extract and adapt.", + "detailedDesign" : "Implement strict typed Rust structs for HostSpec, GuestSpec, ExecutionPolicy, ExecutionSpec, SandboxSpec, BudgetSpec, MountSpec, NetworkUsageSpec, DeviceUsageSpec, EndpointSpec, EndpointConsumerPolicy, TelemetrySpec, ProcessSpec, EphemeralProcessSpec, UserSpec; EndpointSpec accepts exactly `owner|provider|zone`, and EndpointConsumerPolicy owns the only finer gates (`allowedSubjects`, `allowedProviderComponents`, `allowedOperations`) with no schema aliases; strict serde deny_unknown_fields; bounds/redaction on all string fields; stable error types; `UserSpec.osUsername` validated as OS username (1..255 bytes, no NUL/control/path-separator), not ResourceName grammar Primary reuse disposition: `adapt`. Preserved source-plan detail: extract and adapt.", "integration" : "Provider dossiers, controller descriptors, Zone resource API, Nix resource compiler", "removalProof" : "Old DTO types removed only after owning Resource/Provider integrations are live", "reuseAction" : "adapt", "reuseSource" : "`packages/d2b-contracts/src/v3/` as destination; no equivalent main source for Host/Guest/Process ResourceType contracts", "specId" : "ADR-046-resources-host-guest-process-user", "specPath" : "docs/specs/ADR-046-resources-host-guest-process-user.md", - "validation" : "Golden JSON vectors for each ResourceType; Endpoint vectors accept only `owner\\|provider\\|zone`, reject every legacy/private visibility alias, reject scalar/array `consumerPolicy` aliases, and cover each canonical consumer allowlist; a docs drift test parses every `type: Endpoint` YAML/Nix example and fails unless visibility is canonical and finer gates occur only under `consumerPolicy`; serde unknown-field rejection; bounds enforcement; `UserSpec.osUsername` OS-username validation (underscore allowed, NUL rejected)", + "validation" : "Golden JSON vectors for each ResourceType; Endpoint vectors accept only `owner|provider|zone`, reject every legacy/private visibility alias, reject scalar/array `consumerPolicy` aliases, and cover each canonical consumer allowlist; a docs drift test parses every `type: Endpoint` YAML/Nix example and fails unless visibility is canonical and finer gates occur only under `consumerPolicy`; serde unknown-field rejection; bounds enforcement; `UserSpec.osUsername` OS-username validation (underscore allowed, NUL rejected)", "workItemId" : "ADR046-exec-001" }, { @@ -2396,7 +2396,7 @@ "integration" : "None (standalone)", "removalProof" : "Deleted once `packages/d2b-provider-supervisor` and the volume-domain effect adapter each carry an equal-or-stricter in-tree blocking-adapter regression test", "reuseAction" : "adapt", - "reuseSource" : "None", + "reuseSource" : null, "specId" : "ADR-046-feasibility-and-spikes", "specPath" : "docs/specs/ADR-046-feasibility-and-spikes.md", "validation" : "SPIKE-04 heartbeat-jitter metric and threshold", @@ -2411,7 +2411,7 @@ "integration" : "None (standalone)", "removalProof" : "Deleted once the real Provider-toolkit crate (`ADR046-provider-001` destination) ships equal-or-stricter manifest-parsing/enumeration/workspace-policy coverage", "reuseAction" : "adapt", - "reuseSource" : "None", + "reuseSource" : null, "specId" : "ADR-046-feasibility-and-spikes", "specPath" : "docs/specs/ADR-046-feasibility-and-spikes.md", "validation" : "SPIKE-05 metrics (1)-(4) across 20 repeated randomized-order manifest loads", @@ -2441,7 +2441,7 @@ "integration" : "None between the two spikes beyond sharing the same fake resource-store oracle shape", "removalProof" : "Deleted per each spike's Cleanup row: the real `ADR-046-provider-state` work-item destination for SPIKE-09; `d2b-provider-volume-local`'s own `tests/`/`integration/` suite for SPIKE-10", "reuseAction" : "adapt", - "reuseSource" : "None", + "reuseSource" : null, "specId" : "ADR-046-feasibility-and-spikes", "specPath" : "docs/specs/ADR-046-feasibility-and-spikes.md", "validation" : "SPIKE-09 metrics (1)-(4); SPIKE-10 metrics (1)-(5), zero-tolerance on path leakage", @@ -2456,7 +2456,7 @@ "integration" : "None (standalone; requires a Linux host with `clone3`/`pidfd_open`, and optionally a running `systemd --user` instance behind the `systemd-user` feature)", "removalProof" : "Deleted once `packages/d2b-provider-system-systemd` and `packages/d2b-provider-system-minijail` each carry this exact shared conformance suite in their own `tests/`", "reuseAction" : "adapt", - "reuseSource" : "None", + "reuseSource" : null, "specId" : "ADR-046-feasibility-and-spikes", "specPath" : "docs/specs/ADR-046-feasibility-and-spikes.md", "validation" : "SPIKE-11 metrics (1)-(4), zero-tolerance on false adoption", @@ -2471,7 +2471,7 @@ "integration" : "None (standalone flake; no dependency on the main `flake.nix`)", "removalProof" : "Deleted once the real `nixos-modules/resources.nix` and `packages/xtask` `gen-schemas` implementation reproduce these metrics as part of `make test-drift`/`make test-flake`", "reuseAction" : "adapt", - "reuseSource" : "None", + "reuseSource" : null, "specId" : "ADR-046-feasibility-and-spikes", "specPath" : "docs/specs/ADR-046-feasibility-and-spikes.md", "validation" : "SPIKE-12 metrics (1)-(5), byte-for-byte reproducibility across 3 hermetic builds", @@ -2486,7 +2486,7 @@ "integration" : "None between the two spikes beyond sharing the same fixture command-table shape", "removalProof" : "Deleted per each spike's Cleanup row: the real `d2b` CLI crate's own discovery conformance test for SPIKE-13; the real CLI crate's workspace-policy/lint gate plus the real bootstrap sequence for SPIKE-14", "reuseAction" : "adapt", - "reuseSource" : "None", + "reuseSource" : null, "specId" : "ADR-046-feasibility-and-spikes", "specPath" : "docs/specs/ADR-046-feasibility-and-spikes.md", "validation" : "SPIKE-13 metrics (1)-(7); SPIKE-14 metrics (1)-(3), zero-tolerance on legacy-file access", @@ -4162,7 +4162,7 @@ "dataMigration" : "Full reset", "dependencyOwner" : "ADR046-pstate-001; Provider contracts owner", "destination" : "`packages/d2b-contracts/src/v3/provider.rs` (component descriptor `stateNamespaces` field)", - "detailedDesign" : "Add `stateNamespaces: Vec` to the component descriptor (zero or more entries; a component declares an entry only when a payload passes the storage-need test; stateless components declare none); each entry includes `id`, `kind` (always `state`), `schemaId` (non-null), `schemaVersion`, `schemaDigest`, `persistenceClass` (must be `persistent`; `ephemeral`/`cache` rejected), `sensitivityClass`, `migrationPolicy`, `quotaBytes` (nonzero; minimum 4096), `storageNeed` (`secret` \\| `large-binary` \\| `private-unsafe-for-status` \\| `revision-unsuitable`), `sealingRequired`, `placementMode` (`guest-local` or `host-backed-guest` for Guest-targeted; omitted for Host-targeted), `hostCustodyPermitted` (required `true` for `host-backed-guest`; absent/false for `guest-local`), and `views`; there is no empty-payload (`schemaId: null`) namespace", + "detailedDesign" : "Add `stateNamespaces: Vec` to the component descriptor (zero or more entries; a component declares an entry only when a payload passes the storage-need test; stateless components declare none); each entry includes `id`, `kind` (always `state`), `schemaId` (non-null), `schemaVersion`, `schemaDigest`, `persistenceClass` (must be `persistent`; `ephemeral`/`cache` rejected), `sensitivityClass`, `migrationPolicy`, `quotaBytes` (nonzero; minimum 4096), `storageNeed` (`secret` | `large-binary` | `private-unsafe-for-status` | `revision-unsuitable`), `sealingRequired`, `placementMode` (`guest-local` or `host-backed-guest` for Guest-targeted; omitted for Host-targeted), `hostCustodyPermitted` (required `true` for `host-backed-guest`; absent/false for `guest-local`), and `views`; there is no empty-payload (`schemaId: null`) namespace", "integration" : "Provider package build emits component descriptors with state namespaces; Provider controller creates Volumes from descriptors at install time", "removalProof" : "Not applicable (new)", "reuseAction" : "adapt", @@ -4312,7 +4312,7 @@ "dataMigration" : "New; no prior bootstrap artifacts to migrate", "dependencyOwner" : "ADR046-pstate-001, ADR046-pstate-002; Zone runtime owner (`d2b-core-controller`), volume-local Provider owner", "destination" : "`packages/d2b-core-controller/src/optional_state_admission.rs` (storage-need admission: reject a declared namespace whose payload is derivable from spec/status/core ledger/external observation with `component-state-not-justified`; only declared namespaces produce a Volume; stateless components produce none); `packages/d2b-core-controller/tests/optional_state_admission.rs` (hermetic: stateless component → no Volume; declared `storageNeed` variants accepted; unjustified namespace rejected; status-first restart revalidation — controller re-derives observed state from status/core ledger/external observation after restart and never treats a status field as authority); `packages/d2b-provider-volume-local/tests/status_bounds.rs` (hermetic: total canonical serialized status cap and provider-specific detail cap enforced; oversize status write → typed rejection; status carries no secret/path/argv/PID/unit/stream/ring content)", - "detailedDesign" : "Optional state-Volume admission is a fixed step in Core ProviderDeployment: for each component, if it declares no `stateNamespaces` entry it gets no Volume; for each declared entry, verify the `storageNeed` justification (`secret` \\| `large-binary` \\| `private-unsafe-for-status` \\| `revision-unsuitable`) and reject a namespace whose payload is fully derivable from spec/status/core ledger/external observation with `component-state-not-justified`. Fixed bootstrap components (`system-core`, `system-minijail`, first `volume-local` instance) declare no state Volume and reach Ready using resource `status`, the core Operation ledger, and external observation only; there is no bootstrap-storage mechanism. Status-bound enforcement: reject a status write whose total canonical serialized size exceeds the single canonical status cap, or whose provider-specific detail exceeds the detail cap, or whose condition/count/list/map entries exceed the bounded limits, with the typed status-oversize rejection; status writes occur only on material change.", + "detailedDesign" : "Optional state-Volume admission is a fixed step in Core ProviderDeployment: for each component, if it declares no `stateNamespaces` entry it gets no Volume; for each declared entry, verify the `storageNeed` justification (`secret` | `large-binary` | `private-unsafe-for-status` | `revision-unsuitable`) and reject a namespace whose payload is fully derivable from spec/status/core ledger/external observation with `component-state-not-justified`. Fixed bootstrap components (`system-core`, `system-minijail`, first `volume-local` instance) declare no state Volume and reach Ready using resource `status`, the core Operation ledger, and external observation only; there is no bootstrap-storage mechanism. Status-bound enforcement: reject a status write whose total canonical serialized size exceeds the single canonical status cap, or whose provider-specific detail exceeds the detail cap, or whose condition/count/list/map entries exceed the bounded limits, with the typed status-oversize rejection; status writes occur only on material change.", "integration" : "`d2b-core-controller` runs optional state-Volume admission before creating any declared Volume and before launching a component Process; the status-bound check is applied on every status subresource write in the resource store.", "removalProof" : "Not applicable (permanent admission + status-bound enforcement)", "reuseAction" : "adapt", @@ -5246,7 +5246,7 @@ "integration" : "`make test-fuzz`; a companion container test (`tests/integration/containers/zonelink-cross-zone.rs`) runs two real Zone runtime containers connected by a real ZoneLink and asserts the same property end to end over the wire, not just in the frame-serialization unit", "removalProof" : "Not applicable", "reuseAction" : "adapt", - "reuseSource" : "None", + "reuseSource" : null, "specId" : "ADR-046-security-and-threat-model", "specPath" : "docs/specs/ADR-046-security-and-threat-model.md", "validation" : "Fuzz corpus (`cargo fuzz run zonelink_frame -- -runs=1000000`, zero crashes); container test passes in `make test-integration`", @@ -5336,7 +5336,7 @@ "integration" : "`make test-lint` (static scan) and `make test-rust` (dynamic property test)", "removalProof" : "Not applicable", "reuseAction" : "adapt", - "reuseSource" : "None", + "reuseSource" : null, "specId" : "ADR-046-security-and-threat-model", "specPath" : "docs/specs/ADR-046-security-and-threat-model.md", "validation" : "Hermetic; the dynamic test additionally runs as a canary-byte test (a unique random marker is embedded in the token and searched for across every observability surface)", @@ -5396,7 +5396,7 @@ "integration" : "`make test-rust` (CLI integration tests); a container test (`tests/integration/containers/support-bundle-quarantined.rs`) runs a real Zone with one quarantined Provider and asserts the bundle correctly reports `partial`", "removalProof" : "Not applicable", "reuseAction" : "adapt", - "reuseSource" : "None", + "reuseSource" : null, "specId" : "ADR-046-security-and-threat-model", "specPath" : "docs/specs/ADR-046-security-and-threat-model.md", "validation" : "Hermetic CLI test asserting no spec byte or `metadata.name` appears in a generated bundle; container test for the quarantined-Provider case", @@ -5426,7 +5426,7 @@ "integration" : "`make test-drift`", "removalProof" : "Not applicable — permanent gate", "reuseAction" : "adapt", - "reuseSource" : "None", + "reuseSource" : null, "specId" : "ADR-046-security-and-threat-model", "specPath" : "docs/specs/ADR-046-security-and-threat-model.md", "validation" : "Hermetic shell-script gate; a negative test adds a scratch Provider dossier missing a Security section and asserts the gate fails", @@ -5441,7 +5441,7 @@ "integration" : "`make test-integration` (requires podman, per `AGENTS.md` \"Local Layer 1 + container integration\")", "removalProof" : "Not applicable", "reuseAction" : "adapt", - "reuseSource" : "None", + "reuseSource" : null, "specId" : "ADR-046-security-and-threat-model", "specPath" : "docs/specs/ADR-046-security-and-threat-model.md", "validation" : "Container integration test; acceptance is zero successful attacks across all five attempted vectors", @@ -5456,7 +5456,7 @@ "integration" : "Run manually before each tagged release touching a cloud/hardware Provider, per the existing `tests/README.md` manual-tier convention", "removalProof" : "Not applicable", "reuseAction" : "adapt", - "reuseSource" : "None", + "reuseSource" : null, "specId" : "ADR-046-security-and-threat-model", "specPath" : "docs/specs/ADR-046-security-and-threat-model.md", "validation" : "Checklist sign-off recorded in the release's validation evidence, not a CI gate (matches `D2b_LIVE=1` manual-tier precedent in `AGENTS.md`)", @@ -5898,7 +5898,7 @@ "workItemId" : "ADR046-security-key-028" }, { - "currentSource" : "`packages/d2bd/src/security_key.rs` (`CidTranslator`, `SecurityKeyState`, `LeaseId`/`LeaseState`, `CEREMONY_TIMEOUT` 120 s, `QUEUE_WAIT_TIMEOUT` 15 s, `parse_ctaphid_report`/`build_cancel_packet`); `packages/d2b-priv-broker/src/ops/security_key.rs` (`live_open_hidraw_security_key`, double `fstat` + FIDO usage-page 0xF1D0 + HID raw-info revalidation, `O_RDWR\\|O_NONBLOCK\\|O_NOFOLLOW`); `packages/d2b-sk-frontend/src/{main,uhid,vsock,framing}.rs` (UHID FIDO2 CTAPHID frontend, 64-byte report relay)", + "currentSource" : "`packages/d2bd/src/security_key.rs` (`CidTranslator`, `SecurityKeyState`, `LeaseId`/`LeaseState`, `CEREMONY_TIMEOUT` 120 s, `QUEUE_WAIT_TIMEOUT` 15 s, `parse_ctaphid_report`/`build_cancel_packet`); `packages/d2b-priv-broker/src/ops/security_key.rs` (`live_open_hidraw_security_key`, double `fstat` + FIDO usage-page 0xF1D0 + HID raw-info revalidation, `O_RDWR|O_NONBLOCK|O_NOFOLLOW`); `packages/d2b-sk-frontend/src/{main,uhid,vsock,framing}.rs` (UHID FIDO2 CTAPHID frontend, 64-byte report relay)", "dataMigration" : "Full d2b 3.0 reset; no per-session/lease state persisted", "dependencyOwner" : "D097 authority foundation owner; depends on ADR046-security-key-001, ADR046-security-key-002, ADR046-security-key-003, ADR046-security-key-004, ADR046-security-key-018, ADR046-security-key-026, ADR046-zone-control-024, and the D097 authority contract.", "destination" : "`packages/d2b-provider-device-security-key/src/{authority,relay,streams}.rs`; D097 `AuthorityDescriptor` on authority SecurityKeyService", diff --git a/docs/specs/ADR-046-zone-routing.md b/docs/specs/ADR-046-zone-routing.md index 96dbec15e..248ac5eba 100644 --- a/docs/specs/ADR-046-zone-routing.md +++ b/docs/specs/ADR-046-zone-routing.md @@ -1684,7 +1684,10 @@ At each intermediate hop, d2b-bus: 1. Verifies the inbound ComponentSession's `AuthenticatedSubjectContext`. 2. Evaluates local RBAC for the forwarded target verb with the immutable - ResourceType/service, resource name, and target Zone. + ResourceType/service and target Zone. Named methods retain one exact resource + name. Nameless `List`/`Watch` retain an exact non-empty authorized + `resourceNames` set and bounded filters whose possible results are a subset + of that set. 3. Separately evaluates local RBAC for the `relay` session verb against the authenticated inbound Zone transport subject, governing ZoneLink, exact target bounds, and route-selected next hop. @@ -1694,7 +1697,8 @@ At each intermediate hop, d2b-bus: 6. Opens the allocator-bound ComponentSession represented by the next child Zone's local uplink. 7. Re-serializes the request with the decremented hop counter; preserves - the original operation/idempotency/correlation/trace IDs unchanged. + the named target or nameless selector, all filters/pagination/watch cursor, + and the original operation/idempotency/correlation/trace IDs unchanged. 8. Returns the child response to the inbound caller. Relay is a distinct RBAC verb. Intermediate Zones may deny relay without @@ -1728,10 +1732,12 @@ child's own revision cursors: revision token. 3. If the ZoneLink session disconnects, parent route-session state records the last seen child revision; no parent-store ZoneLink row is created. -4. On reconnect, the parent re-issues `Watch(afterRevision=)`. +4. On reconnect, the parent re-issues `Watch` with the identical ResourceType, + authorized name set, and filters plus `afterRevision=`. 5. If the child reports `revision-expired` (cursor too old), the parent - re-issues `List` to obtain a fresh snapshot, then re-opens `Watch` - after the snapshot revision. + re-issues `List` with that identical selector/filter set to obtain a fresh + snapshot, then re-opens `Watch` with those same filters after the snapshot + revision. The parent may not merge child revisions with its own Zone revision namespace. Parent-local watchers for child resources must be driven @@ -1748,15 +1754,20 @@ forwarding; the caller must split it. ## Runtime service calls over ZoneLink -Runtime service calls (ComponentSession connect/invoke/open-stream) may +Runtime service calls (ComponentSession connect/invoke/open-stream and the +exact diagnostic methods authorized by `audit-export` or `support-bundle`) may be forwarded through a ZoneLink if: - the target service is declared in a child Zone Provider's service descriptor; - the call's `purpose` class is `remote-zone`; - RBAC at each forwarding hop grants both `relay` for the authenticated - adjacent-Zone subject and the target session verb (`connect`, `invoke`, or - `open-stream`) for the exact service/method/stream; + adjacent-Zone subject and the target session verb (`connect`, `invoke`, + `open-stream`, `audit-export`, or `support-bundle`) for the exact + service/method/stream. The diagnostic verbs remain bound only to + `d2b.audit.v3.AuditService/Export` and + `d2b.support.v3.SupportService/GenerateBundle` and grant no resource + authority; - the hop count does not exceed the fixed protocol limit of 16. The forwarded session carries: @@ -2088,8 +2099,8 @@ RBAC at each hop: ``` K0: subject=User/alice verb=get resourceType=Process zone=K2 -> K0 Role admits the target operation before route selection -K1: subject=Zone/k0 verbs=[relay,get] resourceType=Process - name=web-server zone=K2 +K1: subject=Zone/k0 sessionVerbs=[relay] verbs=[get] resourceType=Process + resourceNames=[web-server] zone=K2 -> K1 RoleBinding independently allows one-hop forwarding and target get K2: subject=Zone/k1 verb=get resourceType=Process name=web-server -> K2 Role allows the authenticated adjacent Zone to get Process/web-server @@ -2111,14 +2122,18 @@ K1 (Guest/workvm, cloud-hypervisor VM) A watch from K0 on K1 Processes: ``` -1. K0 ResourceClient: Watch(Process, zone=K1, afterRevision=none) +1. K0 ResourceClient: Watch(Process, + resourceNames=[wayland-proxy,shell-session], zone=K1, + afterRevision=none) 2. K0 d2b-bus: route to K1 via the allocation represented by K1's ZoneLink/k1-uplink -3. K1: List snapshot → revision R1; Watch(afterRevision=R1) +3. K1: List with the identical resourceNames filter → revision R1; + Watch with that same filter and afterRevision=R1 4. K1 streams events to K0 named-stream (credit-bounded) 5. Disconnect: K0 records lastRevision=R1 -6. Reconnect: K0 re-opens Watch(afterRevision=R1) -7. K1 delivers events after R1 or returns revision-expired → K0 relists +6. Reconnect: K0 re-opens Watch with the identical filter and afterRevision=R1 +7. K1 delivers matching events after R1 or returns revision-expired → K0 + relists with the identical filter ``` ## Audit, OTEL, errors, and security diff --git a/docs/specs/providers/ADR-046-provider-audio-pipewire.md b/docs/specs/providers/ADR-046-provider-audio-pipewire.md index e87d4d707..5b0a85e2f 100644 --- a/docs/specs/providers/ADR-046-provider-audio-pipewire.md +++ b/docs/specs/providers/ADR-046-provider-audio-pipewire.md @@ -2082,12 +2082,12 @@ dependencySelectors: - resourceType: User resolveFrom: AudioBinding.spec.guestUsers allowedResourceVerbs: - - { type: audio.d2bus.org.AudioService, specProviderRef: Provider/audio-pipewire, verbs: [get, list, watch, updateStatus, updateFinalizers] } - - { type: audio.d2bus.org.AudioBinding, specProviderRef: Provider/audio-pipewire, verbs: [get, list, watch, updateStatus, updateFinalizers] } + - { type: audio.d2bus.org.AudioService, specProviderRef: Provider/audio-pipewire, verbs: [get, list, watch, update-status, update-finalizers] } + - { type: audio.d2bus.org.AudioBinding, specProviderRef: Provider/audio-pipewire, verbs: [get, list, watch, update-status, update-finalizers] } - { type: ResourceImport, verbs: [get, watch] } - - { type: Process, verbs: [get, list, watch, create, updateSpec, delete] } - - { type: Endpoint, verbs: [get, list, watch, create, updateSpec, delete] } - - { type: Guest, verbs: [get, watch, updateSpec] } + - { type: Process, verbs: [get, list, watch, create, update-spec, delete] } + - { type: Endpoint, verbs: [get, list, watch, create, update-spec, delete] } + - { type: Guest, verbs: [get, watch, update-spec] } reconcileConcurrency: 8 maxPendingResources: 512 observePolicy: on-status-change diff --git a/docs/specs/providers/ADR-046-provider-credential-entra.md b/docs/specs/providers/ADR-046-provider-credential-entra.md index 9be8a311d..7ce89c79f 100644 --- a/docs/specs/providers/ADR-046-provider-credential-entra.md +++ b/docs/specs/providers/ADR-046-provider-credential-entra.md @@ -566,8 +566,9 @@ credential-entra lifecycle changes. | `use-credential/inspect-metadata` | Exact `consumerRef` plus RBAC | Observe login or inspect bounded lease/status metadata when `spec.allowedOperations` includes `inspect-metadata`. | The value after `/` is an exact entry in the existing Role rule -`subresources` field. The dossier defines no `operationClasses` Role field and -no method-name verbs such as `begin-login`. A consumer Role rule is: +`subresources` field. The dossier defines no alternate Credential-operation +Role field and no method-name verbs such as `begin-login`. A consumer Role rule +is: ```yaml rules: @@ -584,6 +585,13 @@ A separate deployer Role rule is: ```yaml rules: + - resourceTypes: [Credential] + verbs: [create, update-spec, delete] + subresources: [] + resourceNames: [work-entra] + zones: [work] + executionRefs: [] + sessionVerbs: [] - resourceTypes: [Credential] verbs: [admin-credential] subresources: [create, update-spec, delete] diff --git a/docs/specs/providers/ADR-046-provider-credential-managed-identity.md b/docs/specs/providers/ADR-046-provider-credential-managed-identity.md index 3e0378983..d00f0eb39 100644 --- a/docs/specs/providers/ADR-046-provider-credential-managed-identity.md +++ b/docs/specs/providers/ADR-046-provider-credential-managed-identity.md @@ -420,8 +420,8 @@ and the runtime schema check. The `d2b.credential.v3` protobuf/ttrpc service methods are dispatched across two processes depending on whether IMDS access is required: -- **`managed-identity-controller`** serves `Status` and (optionally) - `InspectMetadata` from stored resource-store state only. It holds no +- **`managed-identity-controller`** optionally serves `InspectMetadata` from + stored resource-store state only. It holds no `ManagedIdentityCredentialClient`, makes no IMDS calls, and never terminates a credential-delivery session. - **`managed-identity-agent`** (one per Credential/consumerRef binding) serves @@ -431,24 +431,24 @@ two processes depending on whether IMDS access is required: The agent is spawned and owned by the controller; its process is co-located at `Credential.spec.scope.executionRef`. -d2b-bus routes each method to the correct endpoint based on the declared -`operationClass` of the request: `inspect-metadata` requests for `Status` go -to the controller; `acquire-token`, `refresh-token`, `revoke-token`, and live -`inspect-metadata` go to the agent. +d2b-bus derives the exact operation class from the method and routes it to the +correct endpoint: stored `InspectMetadata` goes to the controller; +`AcquireToken`, `RefreshToken`, `RevokeToken`, and live `InspectMetadata` go to +the agent. No request carries a caller-selected operation-class field. ### Method table -| Method | Served by | Operation class | Outer DTO fields | Sensitive output | -| --- | --- | --- | --- | --- | -| `Status` | **controller** | `inspect-metadata` | `CredentialStatusResponse`: `leaseState`, `rotationGeneration`, `sourceVersion`, `expiresAtUnixMs`, `placementBinding` | None | -| `AcquireToken` | **agent** | `acquire-token` | `AcquireTokenResponse`: `leaseHandle`, `sourceVersion`, `rotationGeneration`, `expiresAtUnixMs` | Raw token bytes in dedicated `Noise_KK` delivery session (see §Credential-delivery endpoint contract) | -| `RefreshToken` | **agent** | `refresh-token` | `RefreshTokenResponse`: `leaseHandle`, `sourceVersion`, `rotationGeneration`, new `expiresAtUnixMs` | Raw token bytes in dedicated `Noise_KK` delivery session | -| `RevokeToken` | **agent** | `revoke-token` | `RevokeTokenResponse`: closed revocation result (`Revoked` or `AlreadyRevoked`), `revokedAtUnixMs` | None | -| `InspectMetadata` | **agent** (live) / **controller** (stored) | `inspect-metadata` | `InspectMetadataResponse`: `leaseState`, `rotationGeneration`, `sourceVersion`, `expiresAtUnixMs` | None | +| Method | Served by | Exact operation/Role subresource | Required Role permission | Outer DTO fields | Sensitive output | +| --- | --- | --- | --- | --- | --- | +| `AcquireToken` | **agent** | `acquire-token` | `use-credential/acquire-token` | `AcquireTokenResponse`: `leaseHandle`, `sourceVersion`, `rotationGeneration`, `expiresAtUnixMs` | Raw token bytes in dedicated `Noise_KK` delivery session (see §Credential-delivery endpoint contract) | +| `RefreshToken` | **agent** | `refresh-token` | `use-credential/refresh-token` | `RefreshTokenResponse`: `leaseHandle`, `sourceVersion`, `rotationGeneration`, new `expiresAtUnixMs` | Raw token bytes in dedicated `Noise_KK` delivery session | +| `RevokeToken` | **agent** | `revoke-token` | `use-credential/revoke-token` | `RevokeTokenResponse`: closed revocation result (`Revoked` or `AlreadyRevoked`), `revokedAtUnixMs` | None | +| `SignChallenge` | admission only | `sign-challenge` | `use-credential/sign-challenge` | `credential-schema-invalid` because this Provider does not declare the operation | None | +| `InspectMetadata` | **agent** (live) / **controller** (stored) | `inspect-metadata` | `use-credential/inspect-metadata` | `InspectMetadataResponse`: `leaseState`, `rotationGeneration`, `sourceVersion`, `expiresAtUnixMs` | None | -`SignChallenge` is not implemented. Any request carrying `operation_class = -sign-challenge` returns `credential-schema-invalid` immediately, before any -IMDS interaction, and closes the request. +`SignChallenge` is not implemented. A `SignChallenge` call returns +`credential-schema-invalid` immediately, before any IMDS interaction, and +closes the request. Every method: @@ -456,8 +456,9 @@ Every method: subject** (established by the ComponentSession from SO_PEERCRED or the enrolled Noise_KK static key — see §ExactSdkConsumer authentication below) does not match `spec.consumerRef` when set, with `credential-consumer-mismatch`; -- rejects an operation class not in `spec.allowedOperations` with - `credential-operation-denied`; +- rejects before Provider dispatch unless the method's one exact operation is + present in both `spec.allowedOperations` and the Role `subresources` under + `use-credential`, with `credential-operation-denied`; - returns a stable closed error code rather than IMDS response content or provider-internal diagnostics; - carries operation/idempotency/correlation IDs from the d2b-bus context; @@ -666,7 +667,8 @@ The `managed-identity-controller` process: when the Credential spec is admitted and its dependencies are ready (not after Credential `phase=Ready`, which depends on agent readiness); tears it down on Credential deletion; -- serves only `Status` (from resource-store state) over `d2b.credential.v3`; +- may serve `InspectMetadata` from resource-store state over + `d2b.credential.v3`; - monitors agent Process health via `ownerChildTrigger` watch; sets `ProviderUnavailable=True` on sustained agent Process failure. @@ -1026,14 +1028,16 @@ Credential RBAC model defined in `ADR-046-resources-credential`. | `get` | Any authorized subject | Read Credential metadata/spec/status; no secret bytes present | | `list` | Any authorized subject | List managed-identity Credentials | | `watch` | Any authorized subject; `managed-identity-controller` | Watch Credential events (controller watches all; consumers watch own) | -| `create` | `activation-nixos` controller | Create Credential resource from bundle | -| `update-spec` | `activation-nixos` controller | Replace Credential spec | +| `create` plus `admin-credential/create` | `activation-nixos` controller | Create Credential resource from bundle; neither permission implies the other | +| `update-spec` plus `admin-credential/update-spec` | `activation-nixos` controller | Replace Credential spec; neither permission implies the other | | `update-status` | `managed-identity-controller` only | Update Credential status subresource | | `update-finalizers` | `managed-identity-controller`, `consumerRef` controller | Add/remove owned finalizers | -| `delete` | `activation-nixos` controller | Request Credential deletion | -| `use-credential` | Consumer subject authorized via `consumerRef`; dispatched by agent | Invoke `d2b.credential.v3` token-delivery methods | -| `spawn-agent` | `managed-identity-controller` | Create/delete agent Process resources owned by Credential (controller-internal; not RBAC-grantable to consumers) | -| `get-credential` | `managed-identity-agent` | Agent reads own Credential resource to obtain config and lease state at start-up | +| `delete` plus `admin-credential/delete` | `activation-nixos` controller | Request Credential deletion; neither permission implies the other | +| `use-credential` | Consumer subject authorized via `consumerRef`; dispatched by agent | Invoke one admitted `d2b.credential.v3` method under its exact allowed-operation subresource | + +Agent Process creation/deletion uses ordinary `Process` `create`/`delete` +authority plus structural ownership checks. Reading the bound Credential uses +ordinary `Credential` `get`; `spawn-agent` and `get-credential` are not verbs. ### `use-credential` Role rule shape @@ -1041,16 +1045,18 @@ Credential RBAC model defined in `ADR-046-resources-credential`. rules: - resourceTypes: [Credential] verbs: [use-credential] + subresources: [acquire-token, refresh-token] resourceNames: [aca-mi-relay] zones: [dev] executionRefs: [Guest/aca-sandbox] - operationClasses: [acquire-token, refresh-token] + sessionVerbs: [] ``` The effective operation set is the intersection of the Credential resource's -`spec.allowedOperations` and the Role rule's `operationClasses`. An empty -`operationClasses` in the rule means no additional restriction beyond the -resource spec. +`spec.allowedOperations` and the Role rule's exact `subresources`, further +narrowed by `consumerRef`, scope, and structural Provider/component checks. +Empty, wildcard, unknown, and mismatched subresources deny; there is no +alternate Credential-operation Role field or shorthand operation alias. ### Consumer descriptor requirement @@ -2004,7 +2010,7 @@ audit record field set conformance, delivery session binding contract, RBAC | Credential `Delete`: controller sends graceful-stop to agent; agent drains in-flight requests and revokes leases; controller issues delete on agent Process resource; controller observes agent Process deletion watch event (no persisted `phase=Deleted` row for agent Process); controller clears `provider-revoke` finalizer; Core atomically writes event-only Deleted revision and removes Credential row/index; audit subsystem appends closure record with exactly-once dedup — finalizer cleared before Core deletion, Core deletion before audit, all verified in order | Graceful teardown; finalizer-then-Core-deletion-then-audit ordering | | Audit exactly-once: simulate Core Deleted revision committed with no corresponding audit record; audit subsystem on recovery appends exactly once using dedup key bound to committed revision; controller does not re-emit; no duplicate in audit log | Audit subsystem exactly-once / no controller re-emit | | Agent Process `ownerRef` matches Credential UID: controller watch filter correctly associates agent Process events with the owning Credential | ownerRef watch correctness | -| Controller `Status` method: returns stored `leaseState` without calling `FakeClient`; agent `FakeClient` call count unchanged | Controller-side Status | +| Controller `InspectMetadata` path: returns stored `leaseState` without calling `FakeClient`; agent `FakeClient` call count unchanged | Controller-side metadata inspection | | Agent Process spec has `networkUsage.allowEgress=false`; agent receives `ManagedIdentityCredentialClient` via effect port (no direct network calls in agent binary) | Effect-port injection; no ambient egress | | Agent Process template: all required canonical fields present (processClass, template, namespaceClasses `[mount,pid,ipc]`, capabilityClasses `[]`, seccompClass `strict`, startRoot `false`, noNewPrivileges `true`, environmentClass `minimal`, readOnlyRoot `true`, budget nested cpu.request/cpu.limit/memory.request/memory.limit/pids.limit/fds.limit, no inline endpoint fields, owned credential-service Endpoint resource, readiness class `provider-defined`/initialDelay/timeout/failureThreshold/successThreshold, telemetry metricsEnabled/tracingEnabled/logLevel/sensitiveLabels); all excluded fields absent (principalRef, profileRef, endpoint.kind, Process config, telemetry.componentRef, readiness.probe/timeoutMs, network.allowedEffects, budget.class, telemetry.class); agent `networkUsage: {networkRef:null,ports:[],allowEgress:false}` | Canonical template shape | | Controller binary (`controller/main.rs`) constructed with no `ManagedIdentityCredentialClient` import; `FakeClient` inaccessible from controller entry point | Controller secret-free invariant | diff --git a/docs/specs/providers/ADR-046-provider-credential-secret-service.md b/docs/specs/providers/ADR-046-provider-credential-secret-service.md index a19d89f72..f340d9326 100644 --- a/docs/specs/providers/ADR-046-provider-credential-secret-service.md +++ b/docs/specs/providers/ADR-046-provider-credential-secret-service.md @@ -391,22 +391,25 @@ Credential and Process health, never Provider-level status directly. The controller process serves the `d2b.credential.v3` protobuf/ttrpc service routed through d2b-bus. All methods are async. -| Method | Port call | Outer DTO | Sensitive output | -| --- | --- | --- | --- | -| `Status` | `inspect_lease` + `state` | `CredentialStatusResponse` (leaseState, rotationGeneration, sourceVersion, expiresAtUnixMs, placementBinding) | none | -| `AcquireToken` | `issue_lease` | `AcquireTokenResponse` (leaseHandle, sourceVersion, rotationGeneration, expiresAtUnixMs) | raw token bytes in dedicated Noise_KK delivery record | -| `RefreshToken` | `refresh_lease` | `RefreshTokenResponse` (leaseHandle, sourceVersion, rotationGeneration, new expiresAtUnixMs) | raw token bytes in dedicated Noise_KK delivery record | -| `RevokeToken` | `revoke_lease` | `RevokeTokenResponse` (Revoked or AlreadyRevoked, revokedAtUnixMs) | none | -| `InspectMetadata` | `inspect_lease` | `InspectMetadataResponse` (leaseState, rotationGeneration, sourceVersion, expiresAtUnixMs) | none | - -`sign-challenge` is not implemented. Any request with `operationClass = -sign-challenge` returns `credential-schema-invalid` immediately without -consulting the port. +| Method | Port call | Exact operation/Role subresource | Required Role permission | Outer DTO | Sensitive output | +| --- | --- | --- | --- | --- | --- | +| `AcquireToken` | `issue_lease` | `acquire-token` | `use-credential/acquire-token` | `AcquireTokenResponse` (leaseHandle, sourceVersion, rotationGeneration, expiresAtUnixMs) | raw token bytes in dedicated Noise_KK delivery record | +| `RefreshToken` | `refresh_lease` | `refresh-token` | `use-credential/refresh-token` | `RefreshTokenResponse` (leaseHandle, sourceVersion, rotationGeneration, new expiresAtUnixMs) | raw token bytes in dedicated Noise_KK delivery record | +| `RevokeToken` | `revoke_lease` | `revoke-token` | `use-credential/revoke-token` | `RevokeTokenResponse` (Revoked or AlreadyRevoked, revokedAtUnixMs) | none | +| `SignChallenge` | none | `sign-challenge` | `use-credential/sign-challenge` | `credential-schema-invalid` because this Provider does not declare the operation | none | +| `InspectMetadata` | `inspect_lease` | `inspect-metadata` | `use-credential/inspect-metadata` | `InspectMetadataResponse` (leaseState, rotationGeneration, sourceVersion, expiresAtUnixMs) | none | + +`sign-challenge` is not implemented. A `SignChallenge` call returns +`credential-schema-invalid` immediately without consulting the port. The method +selects the operation class; requests do not carry a caller-selected +operation-class field. Every method: - rejects a request where the authenticated subject is not the declared - `consumerRef` (when set) and RBAC `use-credential` is denied; -- rejects an operation class not in `spec.allowedOperations`; + `consumerRef` (when set); +- rejects before Provider dispatch unless the method's one exact operation is + present in both `spec.allowedOperations` and Role `subresources` under + `use-credential`; - returns a stable closed error code, never provider-internal diagnostics; - carries operation/idempotency/correlation IDs from d2b-bus context; - enforces a per-call deadline propagated from the d2b-bus context. @@ -844,11 +847,11 @@ network calls. | `get` | Any authorized subject | | `list` | Any authorized subject | | `watch` | Any authorized subject | -| `create` | Deployer/system-core configuration controller | -| `update-spec` | Deployer/system-core configuration controller | +| `create` plus `admin-credential/create` | Deployer/system-core configuration controller; neither permission implies the other | +| `update-spec` plus `admin-credential/update-spec` | Deployer/system-core configuration controller; neither permission implies the other | | `update-status` | Credential controller (exact registered process generation) only | | `update-finalizers` | Credential controller; `consumerRef` controller | -| `delete` | Deployer/system-core configuration controller | +| `delete` plus `admin-credential/delete` | Deployer/system-core configuration controller; neither permission implies the other | | `use-credential` | Consumer subject authorized via `consumerRef` and Role/RoleBinding | `use-credential` Role rule shape: @@ -857,14 +860,18 @@ network calls. rules: - resourceTypes: [Credential] verbs: [use-credential] + subresources: [acquire-token, refresh-token] resourceNames: [local-keyring] zones: [dev] executionRefs: [Host/host-system] - operationClasses: [acquire-token, refresh-token] + sessionVerbs: [] ``` The effective operation set is the intersection of `spec.allowedOperations` and -the Role `operationClasses`. +the Role's exact `subresources`, further narrowed by `consumerRef`, scope, and +structural Provider/component checks. Empty, wildcard, unknown, or mismatched +subresources deny; there is no alternate Credential-operation Role field or +shorthand operation alias. ### 10.2 Secret isolation invariants diff --git a/docs/specs/providers/ADR-046-provider-network-local.md b/docs/specs/providers/ADR-046-provider-network-local.md index 493b0872d..209bd01a8 100644 --- a/docs/specs/providers/ADR-046-provider-network-local.md +++ b/docs/specs/providers/ADR-046-provider-network-local.md @@ -1832,7 +1832,7 @@ spec: zones: [dev] # Scoped: only Guests with ownerRef resolving to a Network resource - resourceTypes: [Volume] - verbs: [get, list, watch, create, update-spec, delete, write-content] + verbs: [get, list, watch, create, update-spec, delete] zones: [dev] # Scoped: only Volumes with ownerRef resolving to a Network resource - resourceTypes: [Process] @@ -1855,6 +1855,8 @@ spec: roleRef: Role/network-local-controller subjects: - Provider/network-local + externalPrincipalSelector: null + scopeNarrowing: null ``` The controller holds **no** broker role and **no** `network-admin` capability. @@ -1870,7 +1872,7 @@ All host-kernel effects go through the injected `NetworkEffectPort`. | `update-status` | `Network` | `Provider/network-local` | sole status owner | | `create,update-spec` | `Guest` | `Provider/network-local` | scoped to ownerRef=Network | | `create,update-spec` | `Volume` | `Provider/network-local` | creates per-Network config Volume resource objects only; volume-local reconciles; network-local does NOT implement Volume ResourceType | -| `write-content` | `Volume` | `Provider/network-local` | config content writes via Volume service | +| `update-spec` | `Volume` | `Provider/network-local` | declared config content changes use the canonical Volume spec/update path; there is no `write-content` verb | | `create,update-spec` | `Process` | `Provider/network-local` | agent + dnsmasq + mDNS processes | --- diff --git a/docs/specs/providers/ADR-046-provider-runtime-azure-container-apps.md b/docs/specs/providers/ADR-046-provider-runtime-azure-container-apps.md index 5698ef064..81c1c5e6f 100644 --- a/docs/specs/providers/ADR-046-provider-runtime-azure-container-apps.md +++ b/docs/specs/providers/ADR-046-provider-runtime-azure-container-apps.md @@ -864,13 +864,25 @@ spec: rules: - resourceTypes: [Guest] verbs: [get, list, watch, update-spec, update-status, delete] - providerRef: Provider/runtime-azure-container-apps + subresources: [] + resourceNames: [] + zones: [] + executionRefs: [Guest/] + sessionVerbs: [] - resourceTypes: [Credential] - verbs: [get, update-status] - providerRef: Provider/credential-managed-identity + verbs: [get, use-credential] + subresources: [acquire-token, refresh-token, revoke-token, inspect-metadata] + resourceNames: [] + zones: [] + executionRefs: [Guest/] + sessionVerbs: [] - resourceTypes: [Endpoint] verbs: [get, create, update-spec, update-status, delete] - providerRef: Provider/runtime-azure-container-apps + subresources: [] + resourceNames: [] + zones: [] + executionRefs: [Guest/] + sessionVerbs: [] ``` ```yaml @@ -883,12 +895,16 @@ metadata: spec: roleRef: Role/aca-controller-role subjects: - - kind: process - componentRef: aca-controller - providerRef: Provider/runtime-azure-container-apps - executionRef: Guest/ # inside gateway Guest; no Host subject permitted + - Process/aca-controller + externalPrincipalSelector: null + scopeNarrowing: null ``` +The Process subject resolves to the signed `aca-controller` component generation +inside `Guest/`; structural admission rejects a same-name Host +Process. The Credential rule grants no status write and is effective only at the +intersection of the exact `allowedOperations` and Role subresources. + ### 11.2 Deployment service authority The `aca-deployment-service` component may only invoke methods on the `d2b.aca.v3.deployment` service (including `GuestHealth`). It must not hold a `Guest` resource write verb; the controller owns all status and spec mutations. No Role or RoleBinding is required beyond the framework-default service component policy. diff --git a/docs/specs/providers/ADR-046-provider-runtime-azure-virtual-machine.md b/docs/specs/providers/ADR-046-provider-runtime-azure-virtual-machine.md index 760125ad0..e28fc0018 100644 --- a/docs/specs/providers/ADR-046-provider-runtime-azure-virtual-machine.md +++ b/docs/specs/providers/ADR-046-provider-runtime-azure-virtual-machine.md @@ -1024,21 +1024,57 @@ Azure Resource Manager ```yaml type: Role +metadata: + name: azure-vm-controller + zone: spec: rules: - resourceTypes: [Guest] - verbs: [get, list, watch, update-status, add-finalizer, remove-finalizer] - providerSelector: Provider/runtime-azure-virtual-machine + verbs: [get, list, watch, update-status, update-finalizers] + subresources: [] + resourceNames: [] + zones: [] + executionRefs: [Guest/] + sessionVerbs: [] - resourceTypes: [Credential] - verbs: [get, acquire-token, refresh-token] + verbs: [get, use-credential] + subresources: [acquire-token, refresh-token] resourceNames: [] + zones: [] + executionRefs: [Guest/] + sessionVerbs: [] - resourceTypes: [Provider] verbs: [get] + subresources: [] resourceNames: [transport-azure-relay] + zones: [] + executionRefs: [] + sessionVerbs: [] - resourceTypes: [Process] - verbs: [get, create, update-spec, update-status, request-deletion] + verbs: [get, create, update-spec, update-status, delete] + subresources: [] + resourceNames: [] + zones: [] + executionRefs: [Guest/] + sessionVerbs: [] +--- +type: RoleBinding +metadata: + name: azure-vm-controller + zone: +spec: + roleRef: Role/azure-vm-controller + subjects: [Process/azure-vm-controller] + externalPrincipalSelector: null + scopeNarrowing: null ``` +Credential acquisition is not an `acquire-token` resource verb. Each call uses +`use-credential` with the exact matching subresource and is further narrowed by +`Credential.spec.allowedOperations`, `consumerRef`, scope, and structural +component checks. Finalizer changes use `update-finalizers`; there are no +`add-finalizer`, `remove-finalizer`, or `request-deletion` aliases. + ### Azure RBAC | Azure Role | Scope | Purpose | diff --git a/docs/specs/providers/ADR-046-provider-runtime-cloud-hypervisor.md b/docs/specs/providers/ADR-046-provider-runtime-cloud-hypervisor.md index fea562861..c68366bc8 100644 --- a/docs/specs/providers/ADR-046-provider-runtime-cloud-hypervisor.md +++ b/docs/specs/providers/ADR-046-provider-runtime-cloud-hypervisor.md @@ -1147,7 +1147,7 @@ store handle, no ambient route table. | `update-spec` | Process | Drift repair | | `update-status` | Guest | Status write | | `delete` | Process | Finalizer-safe teardown | -| `clear-finalizer` | Guest | After finalize completes | +| `update-finalizers` | Guest | Clear the controller-owned finalizer after finalize completes | ### 14.2 Named streams @@ -1182,7 +1182,7 @@ The controller process must hold a Role binding granting: ```yaml rules: - resourceTypes: [Guest] - verbs: [get, list, watch, update-status, clear-finalizer] + verbs: [get, list, watch, update-status, update-finalizers] - resourceTypes: [Process] verbs: [get, list, watch, create, update-spec, delete] - resourceTypes: [Device, Network, Volume] diff --git a/docs/specs/providers/ADR-046-provider-runtime-qemu-media.md b/docs/specs/providers/ADR-046-provider-runtime-qemu-media.md index 5f387c562..7002fc487 100644 --- a/docs/specs/providers/ADR-046-provider-runtime-qemu-media.md +++ b/docs/specs/providers/ADR-046-provider-runtime-qemu-media.md @@ -1100,35 +1100,45 @@ d2b-bus or delivered through the controller's ComponentSession. | Claim | Target type | Verbs | Purpose | | --- | --- | --- | --- | -| `guest-reconcile` | Guest | get, list, watch, create, update, delete | Own ResourceType | -| `process-manage` | Process | get, list, watch, create, update, delete | Runner process lifecycle | +| `guest-reconcile` | Guest | get, list, watch, create, update-spec, delete | Own ResourceType | +| `process-manage` | Process | get, list, watch, create, update-spec, delete | Runner process lifecycle | | `volume-watch-media` | Volume | get, list, watch | Watch media Volume status | -| `volume-create-runtime` | Volume | get, list, watch, create, update, delete | Create/delete runtime tmpfs Volume | +| `volume-create-runtime` | Volume | get, list, watch, create, update-spec, delete | Create/delete runtime tmpfs Volume | | `network-watch` | Network | get, list, watch | Watch Network readiness | | `device-kvm-watch` | Device | get, list, watch | Watch Device/host-kvm status | -| `waylandsession-manage` | display-wayland.d2bus.org.WaylandSession | get, list, watch, create, update, delete | Create/delete WaylandSession for display | +| `waylandsession-manage` | display-wayland.d2bus.org.WaylandSession | get, list, watch, create, update-spec, delete | Create/delete WaylandSession for display | | `user-watch` | User | get, list, watch | Resolve Guest userRef | -### 15.2 Operator RoleBindings required +### 15.2 Operator Role and RoleBinding required The operator must grant the controller's identity (auto-created Service Account for `Provider/runtime-qemu-media`) at least the following: ```yaml -# Minimum operator RoleBinding for the controller +# Minimum Role rules for the controller rules: - - resources: [Guest] - verbs: [get, list, watch, create, update, delete] - - resources: [Process, EphemeralProcess] - verbs: [get, list, watch, create, update, delete] - - resources: [Volume] - verbs: [get, list, watch, create, update, delete] - - resources: [Network] + - resourceTypes: [Guest] + verbs: [get, list, watch, create, update-spec, delete] + - resourceTypes: [Process, EphemeralProcess] + verbs: [get, list, watch, create, update-spec, delete] + - resourceTypes: [Volume] + verbs: [get, list, watch, create, update-spec, delete] + - resourceTypes: [Network] verbs: [get, list, watch] - - resources: [Device] + - resourceTypes: [Device] verbs: [get, list, watch] - - resources: [display-wayland.d2bus.org.WaylandSession] - verbs: [get, list, watch, create, update, delete] + - resourceTypes: [display-wayland.d2bus.org.WaylandSession] + verbs: [get, list, watch, create, update-spec, delete] +``` + +The matching RoleBinding binds that Role to the Provider identity without +adding expiry or authority: + +```yaml +roleRef: Role/runtime-qemu-media-controller +subjects: [Provider/runtime-qemu-media] +externalPrincipalSelector: null +scopeNarrowing: null ``` ### 15.3 Worker process authority diff --git a/docs/specs/providers/ADR-046-provider-transport-vsock.md b/docs/specs/providers/ADR-046-provider-transport-vsock.md index 0166feb82..894417044 100644 --- a/docs/specs/providers/ADR-046-provider-transport-vsock.md +++ b/docs/specs/providers/ADR-046-provider-transport-vsock.md @@ -626,19 +626,38 @@ parent allocator, other Providers, operators, or end-users) may invoke `OpenTransport`, `CloseTransport`, or `ObserveTransport`. ```yaml -RBACPolicy: - subject: Principal/core-zone-link-controller - zone: +type: Role +metadata: + name: transport-vsock-caller + zone: +spec: rules: - - resource: d2b.transport.vsock.v3.VsockTransportService - verbs: [invoke] - - resource: ComponentSession/transport-vsock-service - verbs: [attach] + - resourceTypes: [Provider] + verbs: [] + sessionVerbs: [invoke, attach] + subresources: + - d2b.transport.vsock.v3.VsockTransportService/OpenTransport + - d2b.transport.vsock.v3.VsockTransportService/CloseTransport + - d2b.transport.vsock.v3.VsockTransportService/ObserveTransport + resourceNames: [transport-vsock] + zones: [] + executionRefs: [] +--- +type: RoleBinding +metadata: + name: transport-vsock-caller + zone: +spec: + roleRef: Role/transport-vsock-caller + subjects: [Process/core-zone-link-controller] + externalPrincipalSelector: null + scopeNarrowing: null ``` ### RBAC grants NOT required by this Provider -`Provider/transport-vsock` requires NO verbs on `ZoneLink`, `Guest`, `Zone`, +`Provider/transport-vsock` requires no resource verbs on `Provider`, +`ZoneLink`, `Guest`, `Zone`, `Route`, `Credential`, `Certificate`, or any other ResourceType. All ZoneLink resource access is performed exclusively by the child Zone's core controller. The Provider service holds no permissions to read, watch, update, From 427df10dc871c37b7f899f37798d81d7e0e1248a Mon Sep 17 00:00:00 2001 From: John Vicondoa Date: Fri, 24 Jul 2026 14:42:28 -0700 Subject: [PATCH 109/115] docs: make managed-identity validation actionable --- .../providers/ADR-046-provider-credential-managed-identity.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/docs/specs/providers/ADR-046-provider-credential-managed-identity.md b/docs/specs/providers/ADR-046-provider-credential-managed-identity.md index d00f0eb39..cc8178e43 100644 --- a/docs/specs/providers/ADR-046-provider-credential-managed-identity.md +++ b/docs/specs/providers/ADR-046-provider-credential-managed-identity.md @@ -162,7 +162,7 @@ is controller-managed and is not present in the Nix-rendered bundle output. | Field | Type | Required | Bounds | Rules | | --- | --- | --- | --- | --- | | `clientId` | string | Yes | 1–128 chars; charset `^[A-Za-z0-9._-]+$` | Opaque Azure Managed Identity client GUID. Validated via `OpaqueAzureRef::parse` (current v3 baseline `d2b-realm-provider/src/credential.rs:OpaqueAzureRef`). The field name is `clientId`, not `clientIdRef`; it is an inline opaque identifier, not a `/` ResourceRef. A secret-shaped value (containing `=`, `+`, `/`, whitespace, or connection-string patterns) fails validation fail-closed. | -| `imdsEndpointAlias` | enum | Yes | closed set | Provider-validated closed alias string resolving to a known IMDS endpoint category. Never an endpoint URL, path, IP address, or hostname. Accepted values and their meanings are defined in §Closed alias set below. | +| `imdsEndpointAlias` | enum | Yes | closed set | Provider-validated closed alias string resolving to a known IMDS endpoint category. Never an endpoint URL, path, IP address, or hostname. Accepted values and their meanings are defined in §Closed alias set for `imdsEndpointAlias`. | | `maxLeases` | u32 | No | 1–256; default 64 | Maximum concurrent active leases this Provider instance may hold. Requests beyond the ceiling return `credential-queue-pressure`. | | `controllerExecutionRef` | ResourceRef | Yes | `Host/` or `Guest/` in same Zone | Execution target for the `managed-identity-controller` Process. Must resolve to a declared system-domain Host or Guest in the same Zone. The controller is secret-free regardless of placement: it holds no `ManagedIdentityCredentialClient`, makes no IMDS calls, and carries no token bytes. | @@ -1824,7 +1824,7 @@ endpoint-role, and provider-factory code is explicitly excluded. | Detailed design | (1) Adapt `ManagedIdentityCredentialProvider` to `d2b.credential.v3` service interface; split controller and agent roles (see `ADR046-mi-topology-001`). (2) Enforce `ManagedIdentityCredentialOwner::ExactSdkConsumer` in agent via `AuthenticatedSubjectContext` from ComponentSession, independently of scope fields. (3) Reject `user-agent` placement: `scope.domainFilter=user` returns `credential-placement-mismatch` before agent spawn. (4) Validate `clientId` using `OpaqueAzureRef::parse` from v3 baseline; artifact IDs match `^[a-z][a-z0-9-]*$`. (5) Validate `imdsEndpointAlias` against closed enum `{azure-imds, azure-imds-aca}`; project into LaunchTicket at spawn time (never into Process spec config or env); co-located runtime Provider constructs `ManagedIdentityCredentialClient` from LaunchTicket projection and supplies via effect port; resolved URL never in any output surface. (6) Agent Process declares `networkUsage.allowEgress=false`; uses canonical Process template shape (see `ADR046-mi-topology-001` design item 6). (7) Reject `sign-challenge` with `credential-schema-invalid` immediately. (8) Map `ManagedIdentityClientState::Unavailable` to `credential-provider-unavailable`; no `InteractionRequired` state. (9) Implement `ManagedIdentityLeaseHandle` as opaque bounded newtype with redacted `Debug`. (10) All token bytes held by injected client in agent; delivered only via agent-terminated `Noise_KK` delivery session. (11) Integrate with Provider resource descriptor and controller toolkit. (12) Confirm `credential_canary` never appears in any service response, status field, delivery record outer header, or audit record. (13) Apply D087 status-first state: declare no Provider state Volume, keep ProviderStateSet empty, and write only bounded non-secret lease observation to `Credential.status` plus the Operation ledger. Primary reuse disposition: `adapt`. Preserved source-plan detail: copy and adapt. | | Integration | Agent `Process` resource under `Host` or `Guest` executionRef; controller `Process` resource at Zone system host; d2b-bus routes `d2b.credential.v3` token-delivery calls to agent; Credential controller reconciles status; ACA `Provider/runtime-azure-container-apps` holds `credentialRef` pointing to a `credential-managed-identity`-backed Credential resource | | Data migration | Full v3 reset. `d2b-provider-aca:managed_identity_client_id` raw field migrated to a Credential resource reference in the v3 ACA Provider config; see removal precondition below. | -| Validation | See §Test matrix | +| Validation | See §Tests. Run `cargo test -p d2b-provider-credential-managed-identity --lib --test lifecycle --test conformance --test faults --test canary --test delivery --test placement --test topology`; run `integration/{container-service.sh,aca-credential-ref.sh,cleanup-rollback.sh}` through `make test-integration` and `integration/host-guest-placement.nix` through `make test-host-integration`. | | Removal proof | `d2b-provider-aca:managed_identity_client_id` raw field removed only after the `credential-managed-identity` controller and agent are integrated and the ACA Provider config uses `credentialRef` exclusively; `ProviderWorkloadIdentity::ManagedIdentity` bootstrap path superseded only after the ACA Provider controller uses the Credential resource for token acquisition | ### ADR046-cred-mi-002 (shared with other Credential Providers) From 1201acaac00c5410f364fd904008afcc35f63be9 Mon Sep 17 00:00:00 2001 From: John Vicondoa Date: Fri, 24 Jul 2026 14:46:17 -0700 Subject: [PATCH 110/115] docs: validate volume rotation wire IDs --- .../ADR-046-provider-volume-local.md | 53 +++++++++++++++++-- 1 file changed, 48 insertions(+), 5 deletions(-) diff --git a/docs/specs/providers/ADR-046-provider-volume-local.md b/docs/specs/providers/ADR-046-provider-volume-local.md index 5079b4a4b..663703697 100644 --- a/docs/specs/providers/ADR-046-provider-volume-local.md +++ b/docs/specs/providers/ADR-046-provider-volume-local.md @@ -870,18 +870,60 @@ crate. The Provider crate depends only on `d2b-contracts`, `d2b-provider`, and `d2b-provider-toolkit`. All opaque ID newtypes carry a custom redacted `Debug` implementation; they must -not derive `Debug` or print internal content: +not derive `Debug` or print internal content. The two IDs carried by +`RotateSealingKeyRequest` use the same newtypes as the trait methods, derive the +wire traits directly, and deserialize through a shared bounded validator. +Their string fields remain crate-private. The canonical encoding is one +non-empty ASCII-graphic string of at most 128 bytes; rejection errors never echo +the input: ```rust // Defined in d2b-contracts::v3::effect_port // All IDs are opaque newtypes with custom redacted Debug. +const MAX_VOLUME_EFFECT_WIRE_ID_BYTES: usize = 128; + +fn validate_volume_effect_wire_id(value: &str) -> Result<(), &'static str> { + if value.is_empty() { + return Err("opaque effect ID must not be empty"); + } + if value.len() > MAX_VOLUME_EFFECT_WIRE_ID_BYTES { + return Err("opaque effect ID exceeds 128 bytes"); + } + if !value.bytes().all(|byte| byte.is_ascii_graphic()) { + return Err("opaque effect ID contains an invalid byte"); + } + Ok(()) +} + +#[derive(Clone, Serialize, Deserialize)] +#[serde(try_from = "String")] pub struct VolumeId(pub(crate) String); pub struct SourcePolicyId(pub(crate) String); pub struct LayoutEntryId(pub(crate) String); pub struct UserId(pub(crate) String); // resolves from User/ resource pub struct ViewId(pub(crate) String); // bounded view name; max 63 chars +#[derive(Clone, Serialize, Deserialize)] +#[serde(try_from = "String")] pub struct SealingPolicyId(pub(crate) String); +impl TryFrom for VolumeId { + type Error = &'static str; + + fn try_from(value: String) -> Result { + validate_volume_effect_wire_id(&value)?; + Ok(Self(value)) + } +} + +impl TryFrom for SealingPolicyId { + type Error = &'static str; + + fn try_from(value: String) -> Result { + validate_volume_effect_wire_id(&value)?; + Ok(Self(value)) + } +} + impl fmt::Debug for VolumeId { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { f.write_str("VolumeId([redacted])") } } impl fmt::Debug for SourcePolicyId{ fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { f.write_str("SourcePolicyId([redacted])") } } impl fmt::Debug for LayoutEntryId { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { f.write_str("LayoutEntryId([redacted])") } } @@ -2408,9 +2450,10 @@ Required test files and minimum coverage: | `tests/symlink.rs` | Valid relative target; `..` component rejected; absolute target rejected; null byte rejected; target resolves outside Volume root rejected | | `tests/domain_isolation.rs` | `private` Volume: concurrent mount from different domain rejected; `internal` Volume: cross-Provider mount rejected; `shared-read` Volume: read-only cross-Provider permitted; `shared-read` write rejected | | `tests/view_rights.rs` | Mount with rights subset of View: admitted; mount with extra right: `volume-view-rights-exceeded`; `read-write` access on `read-only` View: rejected; single-writer constraint: second `read-write` rejected | +| `tests/effect_port_contract.rs` | Compile-time bounds prove `VolumeId` and `SealingPolicyId` are `Clone + Serialize + DeserializeOwned` and `RotateSealingKeyRequest` is `Serialize + DeserializeOwned`; a complete request JSON round-trip is byte-equivalent; each ID uses the canonical string encoding; manual `Debug` is exactly redacted; empty, over-128-byte, non-ASCII, whitespace, and control-byte IDs fail deserialization without echoing input; unknown request fields fail | | `tests/state.rs` | Ported `d2b-state/tests/state.rs` scenarios under v3 StateEnvelope: atomic write, fsync ordering, crash between rename steps, OFD lock acquire/release, quarantine record, generation bound, state-envelope digest | | `tests/migration_unit.rs` | Pre-launch migration dispatch; staging Volume create/destroy; EphemeralProcess succeeded → commit; EphemeralProcess failed → rollback; N-Volume cross-component prepare/commit/rollback protocol; roll-forward on restart detection | -| `tests/sealing_unit.rs` | Initial seal/read without exposing a key lease; status CAS commits `rotation-pending` before the first effect; exact request fields and generation/revision/policy preconditions; operation ID comes from committed proof; deterministic idempotency vector; byte-identical timeout/restart retry; duplicate success → `AlreadyCommitted`; recovered commit → `RecoveredCommitted`; changed bytes under one key → `IdempotencyConflict`; concurrent different rotation → `RotationConflict`; retryable/terminal error table; new generation re-plan; success → `sealed`; integrity failure → Failed; no key/path/handle in DTO, status, error, Debug, log, audit, or OTEL; no direct rewrite/generic broker/EphemeralProcess dispatch | +| `tests/sealing_unit.rs` | Initial seal/read without exposing a key lease; status CAS commits `rotation-pending` before the first effect; exact request fields and generation/revision/policy preconditions using the canonical validated effect-port ID newtypes; operation ID comes from committed proof; deterministic idempotency vector; byte-identical timeout/restart retry; duplicate success → `AlreadyCommitted`; recovered commit → `RecoveredCommitted`; changed bytes under one key → `IdempotencyConflict`; concurrent different rotation → `RotationConflict`; retryable/terminal error table; new generation re-plan; success → `sealed`; integrity failure → Failed; no key/path/handle in DTO, status, error, Debug, log, audit, or OTEL; no direct rewrite/generic broker/EphemeralProcess dispatch | | `tests/snapshot_unit.rs` | `snapshotPolicy` enforcement; retention count; retention TTL; `triggerOnMigration` auto-snapshot; snapshot EphemeralProcess dispatch; list in Volume status | | `tests/relocation_unit.rs` | Finalizer set; EphemeralProcess created; commit: source deleted; failure: source retained; state machine round-trip | | `tests/audit_unit.rs` | Golden audit record for each event kind; no paths in any record; no credential material; structural OTEL label-policy check with exact absence of `vm`, `zone`, `zone_id`, `zone_uid`, and resource-name-derived keys | @@ -2516,11 +2559,11 @@ Documents: | Depends on | `ADR046-pstate-001` (VolumeStateSchema/PersistenceClass/SensitivityClass/StateEnvelope in `d2b-contracts/src/v3/volume_state.rs`) | | Current source | `d2b-core/src/storage.rs` (`StoragePathSpec`, `StoragePathKind`, policy enums); `d2b-core/src/sync.rs` (`SyncJson`, `LockSpec`) | | Reuse action | adapt | -| Destination | `d2b-contracts/src/v3/volume_layout.rs` (LayoutEntry, EntryType, all policy enums, AclGrant, Invariant, SensitivityClass); `d2b-contracts/src/v3/volume_spec.rs` (VolumeSpec, ViewSpec, Attachment, QuotaSpec, SourceKind, `SourcePolicyId` opaque newtype); `d2b-contracts/src/v3/effect_port.rs` (`VolumeEffectPort` trait, opaque ID newtypes `VolumeId`/`LayoutEntryId`/`UserId`/`ViewId`/`SealingPolicyId` each with custom redacted Debug, `VolumeMountToken`, and canonical `RotateSealingKeyRequest`/`Result`/`Error` types) | -| Detailed design | All LayoutEntry fields as documented in this dossier; enum value names preserved from `StoragePathKind`/policy enums with renames where noted; `User/` ACL principal (no numeric UID); `sourcePolicyId` opaque newtype replaces raw `hostPath` in `SourceKind::LocalPath` and `SourceKind::BlockImage`; deny-unknown sealing-rotation request contains only opaque Volume/policy/operation IDs and generation/revision preconditions, with no key bytes/path/handle Primary reuse disposition: `adapt`. Preserved source-plan detail: extract and adapt. | +| Destination | `d2b-contracts/src/v3/volume_layout.rs` (LayoutEntry, EntryType, all policy enums, AclGrant, Invariant, SensitivityClass); `d2b-contracts/src/v3/volume_spec.rs` (VolumeSpec, ViewSpec, Attachment, QuotaSpec, SourceKind, `SourcePolicyId` opaque newtype); `d2b-contracts/src/v3/effect_port.rs` (`VolumeEffectPort` trait, opaque ID newtypes `VolumeId`/`LayoutEntryId`/`UserId`/`ViewId`/`SealingPolicyId` each with custom redacted Debug, `VolumeId` and `SealingPolicyId` with the exact `Clone`/serde wire derives and bounded `TryFrom` deserialization required by the canonical request, `VolumeMountToken`, and canonical `RotateSealingKeyRequest`/`Result`/`Error` types) | +| Detailed design | All LayoutEntry fields as documented in this dossier; enum value names preserved from `StoragePathKind`/policy enums with renames where noted; `User/` ACL principal (no numeric UID); `sourcePolicyId` opaque newtype replaces raw `hostPath` in `SourceKind::LocalPath` and `SourceKind::BlockImage`; deny-unknown sealing-rotation request contains only opaque Volume/policy/operation IDs and generation/revision preconditions, with no key bytes/path/handle; its crate-private `VolumeId` and `SealingPolicyId` strings serialize canonically and deserialize only when non-empty, ASCII-graphic, and at most 128 bytes, while retaining manual redacted Debug Primary reuse disposition: `adapt`. Preserved source-plan detail: extract and adapt. | | Integration | Volume spec and status structs; Provider descriptor component stateNamespace; Nix resource compiler schema validation | | Data migration | Full v3 reset; no row-level import | -| Validation | Schema golden vectors; round-trip serde; ACL principal validation rejects numeric forms; `sourcePolicyId` present; no `hostPath` field in any volume_spec contract; sealing-rotation request deny-unknown/round-trip and redacted-Debug tests; compile-time trait conformance includes `rotate_sealing_key` | +| Validation | Schema golden vectors; round-trip serde; ACL principal validation rejects numeric forms; `sourcePolicyId` present; no `hostPath` field in any volume_spec contract; compile-time assertions for the exact `Clone + Serialize + DeserializeOwned` ID bounds and `Serialize + DeserializeOwned` request bounds; sealing-rotation request canonical-string/round-trip and deny-unknown tests; exact redacted-Debug assertions; deserialization rejects empty, over-128-byte, non-ASCII, whitespace, and control-byte IDs without echoing input; compile-time trait conformance includes `rotate_sealing_key` | | Removal proof | `d2b-core/src/storage.rs` StoragePathSpec/policy enums removed only after all Provider descriptor consumers are on v3 Volume spec | ### ADR046-vl-002 — Crate scaffold and filesystem primitives From 336050f3e29ea129277d63e99b434d5abcdd67da Mon Sep 17 00:00:00 2001 From: John Vicondoa Date: Fri, 24 Jul 2026 14:44:36 -0700 Subject: [PATCH 111/115] docs: align Entra login Endpoint policy --- docs/specs/ADR-046-nix-configuration.md | 53 +++++++++++++++---------- 1 file changed, 31 insertions(+), 22 deletions(-) diff --git a/docs/specs/ADR-046-nix-configuration.md b/docs/specs/ADR-046-nix-configuration.md index f5f1bff55..c8226177f 100644 --- a/docs/specs/ADR-046-nix-configuration.md +++ b/docs/specs/ADR-046-nix-configuration.md @@ -1039,10 +1039,13 @@ d2b.zones.work.resources.entra-login = { transport = "vsock"; # closed class only; no CID/path purpose = "entra-login-token"; locality = "guest-local"; - visibility = "zone"; + visibility = "provider"; consumerPolicy = { - allowedSubjects = [ "Provider/credential-entra" ]; - allowedOperations = [ "resolve" ]; + allowedSubjects = [ + "Provider/credential-entra" + "Provider/runtime-azure-container-apps" + ]; + allowedOperations = [ "resolve" "attach" "observe" ]; }; }; }; @@ -1064,9 +1067,14 @@ d2b.zones.work.resources.work-entra = { Build/eval validation asserts: `identityGuestRef`, `loginEndpointRef`, `consumerRef`, and `scope.executionRef` are all the same Zone; the `Endpoint` `purpose`/schema and `providerRef` match the credential-entra login-service -contract; the Endpoint is Guest-placed (Host placement is rejected); the -Credential scope and consumer placement are consistent; and no store path or -token appears in any emitted spec. +contract; the Endpoint is Guest-placed (Host placement is rejected), uses +`visibility = "provider"` (`"zone"` is rejected), and has an exact +`consumerPolicy` whose `allowedSubjects` contains both +`Provider/credential-entra` and the Credential's exact `consumerRef` +(`Provider/runtime-azure-container-apps`) and whose `allowedOperations` is +exactly `resolve`, `attach`, and `observe`; the Credential scope and consumer +placement are consistent; and no store path or token appears in any emitted +spec. ### Package closures into Guests @@ -1471,14 +1479,15 @@ d2b.zones.dev.resources.work-entra = { type = "Credential"; spec = { providerRef = "Provider/credential-entra"; + identityGuestRef = "Guest/entra-identity"; + loginEndpointRef = "Endpoint/entra-login"; scope = { - executionRef = "Guest/work-vm"; # optional Host or Guest placement restriction - domainFilter = "user"; # optional: system | user - userRef = "User/alice"; # required when domainFilter=user + executionRef = "Guest/aca-gateway"; # optional Host or Guest placement restriction + domainFilter = "system"; # optional: system | user }; audience = "azure-resource-manager"; # Provider-validated opaque audience token; no secrets - consumerRef = "Provider/display-wayland"; # optional; restricts acquiring Provider - allowedOperations = [ "acquire-token" "refresh-token" "revoke-token" ]; + consumerRef = "Provider/runtime-azure-container-apps"; + allowedOperations = [ "acquire-token" "refresh-token" "revoke-token" "inspect-metadata" ]; rotation = { policy = "proactive"; proactiveWindowMs = 300000; @@ -1501,14 +1510,14 @@ d2b.zones.dev.resources.work-entra = { Credential spec fields mirror the `Credential` ResourceTypeSchema exactly: `providerRef`, `scope.{executionRef,domainFilter,userRef}`, `audience`, -`consumerRef`, `allowedOperations`, `rotation`, `expiry`, `revocation`, and -optional canonical `spec.provider` extension (`spec.provider.settings` is -permitted when the Provider schema declares it; validated against signed -Provider schema; no secret bytes). No invented fields (`credentialType`, -`ownerRef`, `domain`). The emitted spec never contains key material, token -bytes, or PEM. Current `identity_config.rs` values that carry credential bytes -are forbidden from the emitted spec; they remain inside the Provider's external -secret service. +`consumerRef`, `allowedOperations`, `rotation`, `expiry`, `revocation`, +conditional `identityGuestRef`/`loginEndpointRef`, and optional canonical +`spec.provider` extension (`spec.provider.settings` is permitted when the +Provider schema declares it; validated against signed Provider schema; no +secret bytes). No invented fields (`credentialType`, `ownerRef`, `domain`). +The emitted spec never contains key material, token bytes, or PEM. Current +`identity_config.rs` values that carry credential bytes are forbidden from the +emitted spec; they remain inside the Provider's external secret service. ## Role and RoleBinding @@ -2498,11 +2507,11 @@ contract work item (ADR046-nix-034/ADR046-nix-035). Cross-reference: | Current source | `nixos-modules/realm-workloads-launcher-v2-json.nix` (`RealmWorkloadsLauncherV2Json`, `LauncherWorkloadSummary`; live); `nixos-modules/realm-identity-config-json.nix` (`RealmIdentityConfigJson`; live, read by d2bd from `/etc/d2b/realm-identity.json`); `packages/d2b-core/src/realm_workloads_launcher.rs`; `packages/d2b-realm-core/src/identity_config.rs` | | Reuse action | adapt | | Destination | Provider/display-wayland and Provider/shell-terminal Process configs in `zones//processes.json`; `Provider/credential-entra` Credential resource; `realm-identity.json` RETAINED during migration | -| Detailed design | Launcher metadata folded into Process resource annotations; identity config → Credential resource fields (`providerRef`, `scope`, `audience`, `allowedOperations`, canonical `spec.provider` extension where Provider schema declares it; no secret bytes); `realm-identity.json` must remain until d2bd `RealmIdentityConfigJson` loading is replaced by Credential resource reader | +| Detailed design | Launcher metadata folded into Process resource annotations; identity config → Credential resource fields (`providerRef`, `identityGuestRef`, `loginEndpointRef`, `scope`, `audience`, `consumerRef`, `allowedOperations`, canonical `spec.provider` extension where Provider schema declares it; no secret bytes); `realm-identity.json` must remain until d2bd `RealmIdentityConfigJson` loading is replaced by Credential resource reader | | Integration | Provider/display-wayland, Provider/shell-terminal, and Provider/credential-entra consume Process/Credential resources; `d2bd` continues reading `realm-identity.json` until the Credential reader lands. | | Data migration | Launcher and identity config are re-emitted as v3 resources; full d2b 3.0 reset; no v2 launcher/identity state import. | -| Validation | Launcher metadata shape regression; no-secret assertion vectors | -| Tests | `tests/unit/nix/cases/zones-launcher-metadata.nix`; no-secret vectors | +| Validation | Launcher metadata shape regression; Entra identity-Guest fixture requires login Endpoint `visibility = "provider"` (and rejects `"zone"`), exact `consumerPolicy.allowedSubjects` containing both `Provider/credential-entra` and the Credential's configured `consumerRef`, and exact canonical `resolve`, `attach`, and `observe` operations; no-secret assertion vectors | +| Tests | `tests/unit/nix/cases/zones-launcher-metadata.nix`; `tests/unit/nix/cases/zones-credential-entra.nix` covers both required subjects, provider visibility, zone-visibility rejection, and exact Endpoint operations; no-secret vectors | | Drift pin | `make nix-unit-pin` | | Removal proof | `realm-workloads-launcher-v2-json.nix`/`realm-identity-config-json.nix` and `/etc/d2b/realm-workloads-launcher-v2.json`/`realm-identity.json` removed ONLY after display/credential Providers read resource configs | From 9fe7ac863972553511bde1995cfd72e25164d78a Mon Sep 17 00:00:00 2001 From: John Vicondoa Date: Fri, 24 Jul 2026 14:48:33 -0700 Subject: [PATCH 112/115] docs: route cloud-hypervisor TAP fds through Core --- ...R-046-provider-runtime-cloud-hypervisor.md | 106 +++++++++++++----- 1 file changed, 76 insertions(+), 30 deletions(-) diff --git a/docs/specs/providers/ADR-046-provider-runtime-cloud-hypervisor.md b/docs/specs/providers/ADR-046-provider-runtime-cloud-hypervisor.md index c68366bc8..a5dda7abe 100644 --- a/docs/specs/providers/ADR-046-provider-runtime-cloud-hypervisor.md +++ b/docs/specs/providers/ADR-046-provider-runtime-cloud-hypervisor.md @@ -304,8 +304,12 @@ controller-created `Guest/-net-vm`. settings), `environmentClass: minimal`. - **pidfd**: mandatory; d2b owns wait/reap for this template. - **networkUsage**: single object `{networkRef: null, ports: [], allowEgress: - false}`. All Network attachment controllers supply ready typed tap Fds in - the LaunchTicket; the VMM itself has no ambient egress. + false}`. The runtime controller supplies only opaque Network attachment refs. + The network-local controller declares each semantic attachment effect; the + Core-owned `NetworkEffectPort` adapter creates and configures the tap, then + transfers its connected CLOEXEC `OwnedFd` directly to `ProviderSupervisor`. + The LaunchTicket inherits that precreated fd; the VMM itself has no ambient + egress. - **deviceUsage**: one entry per `deviceAttachments` entry plus a required `Device/kvm` entry (purpose `kvm-fd`, access `shared` — KVM is safely shareable across multiple VMs). TPM and other exclusive devices use access @@ -340,7 +344,8 @@ The controller enforces this ordering by watching resource statuses through 1. All `Device` resources in `Guest.spec.deviceAttachments` (including `Device/kvm`) must be `Ready` before the VMM Process is created. 2. All `Network` resources in `Guest.spec.networkAttachments` must be `Ready` - (bridges and tap dispatch in place) before the VMM Process is created. + (fabric and opaque attachment realization ready for Core resolution) before + the VMM Process is created. 3. All virtiofs-exported Volumes referenced by the Guest must be `Ready` per `Provider/volume-virtiofs` before the VMM Process is created. @@ -361,12 +366,25 @@ one TAP interface on the host. The TAP name and MAC are derived from the Network resource's `attachments` table, which names the Guest's `executionRef`. The controller does not compute or store TAP names in the Guest spec or status; it reads the provider-neutral `Network.status.resource.attachments[*]` readiness -and opaque handoff record once the Network is Ready. - -TAP creation is a privileged broker effect. The VMM Process supervisor ticket -mediates TAP fd passing to the CH binary using the appropriate net-handoff mode -(`TapFd` or `PersistentTap`) discovered at host-check time. No raw TAP name, -fd number, or broker socket path appears in the Process spec. +and supplies only the opaque attachment ref when the Network is Ready. The +runtime controller never receives a tap fd or network broker operation. + +The network-local controller declares the semantic attachment effect through +`NetworkEffectPort`. Its Core-owned adapter performs +`CreatePersistentTap → SetBridgePortFlags`, owns the connected `OwnedFd` with +`FD_CLOEXEC` set, and transfers it directly to `ProviderSupervisor`. +The Process LaunchTicket attachment inherits this precreated fd; only the +declared child slot is made inheritable immediately before exec. No raw TAP +name, fd number, bridge name, or broker socket path appears in the Guest or +Process spec, status, ResourceAPI, ComponentSession, or d2b-bus payload. + +Adapter and supervisor parent copies remain CLOEXEC and close after successful +spawn. Cancellation, LaunchTicket rejection, or spawn failure closes every fd +copy before the Network adapter invokes generation-fenced +`DeletePersistentTap`; the opaque realization remains retained until deletion +is confirmed. Normal teardown likewise waits for the VMM child copy to close +and confirms all adapter/supervisor copies are closed before issuing +`DeletePersistentTap`. ### 6.2 macvtap (external attachment) @@ -374,8 +392,9 @@ The Network resource declares an optional `ExternalAttachmentSpec` with `mode: macvtap`. When this is present, the network-local Provider creates the macvtap interface and reports the provider-neutral readiness base in `Network.status.resource.externalAttachment`. The `runtime-cloud-hypervisor` -controller receives the private interface handoff from the dependency resolver -and passes it to the VMM supervisor ticket. +controller supplies only the opaque external-attachment ref. The dependency +resolver transfers the private interface handoff directly to +`ProviderSupervisor` for the VMM LaunchTicket. No macvtap interface name, fd, or host interface path appears in the Guest spec or VMM Process spec. @@ -1020,7 +1039,8 @@ spec: seccompClass: vmm-default # required; fixed by signed template; not caller-settable environmentClass: minimal networkUsage: # single object; not a list - networkRef: null # tap Fds supplied by LaunchTicket from Network controllers + # Guest attachment refs resolve to a precreated fd inherited by LaunchTicket. + networkRef: null ports: [] allowEgress: false # VMM has no ambient egress deviceUsage: @@ -1055,6 +1075,14 @@ the signed template's artifact catalog entry and the current resource/dependency state. `Process.spec.artifactId` is not a field in the Process ResourceType; the executable is entirely owned by the template. +For each Network attachment, the runtime controller contributes only the opaque +ref. The network-local controller declares the semantic effect, and the +Core-owned `NetworkEffectPort` adapter completes +`CreatePersistentTap → SetBridgePortFlags` before transferring its connected +CLOEXEC `OwnedFd` directly to `ProviderSupervisor`. The LaunchTicket inherits +that precreated fd; neither the fd nor a broker operation crosses the runtime +controller boundary. + The load-bearing stable endpoints are modeled as resources rather than inline Process fields: @@ -1121,12 +1149,14 @@ unauthorized request fails `endpoint-resolve-denied`. A producer restart bumps ### Retained opaque handles -Permitted opaque values are limited to controller-internal or high-churn data: -`pidfd`/process generation observations, LaunchTicket fd indexes, per-session +Permitted controller opaque values are limited to controller-internal or +high-churn data: `pidfd`/process generation observations, per-session ComponentSession/OwnedTransport handles, operation IDs, and QMP/CH connection -handles. They have no independent resource lifecycle, are not stable managed -endpoint identities, and remain absent from public spec/status/CLI fields except -as bounded non-authorizing diagnostics where already allowed. +handles. ProviderSupervisor-only LaunchTicket fd indexes and `OwnedFd` values +never cross the runtime controller boundary. These values have no independent +resource lifecycle, are not stable managed endpoint identities, and remain +absent from public spec/status/CLI fields except as bounded non-authorizing +diagnostics where already allowed. --- @@ -1201,11 +1231,20 @@ mediated through the `Provider/system-minijail` supervisor ticket. The supervisor ticket covers: - `clone3(CLONE_PIDFD)` spawn with compiled minijail/sandbox arguments; -- TAP fd allocation and passing (via existing `TapBridgeAllocate` / net-handoff - broker chain, translated to supervisor ticket parameters); +- direct inheritance of the precreated tap fd transferred by the Core-owned + `NetworkEffectPort` adapter after + `CreatePersistentTap → SetBridgePortFlags`; the runtime controller supplies + only the opaque attachment ref and receives neither the fd nor either broker + operation; - cgroup placement in the delegated Zone subtree; - pidfd return. +The adapter and supervisor retain CLOEXEC ownership of parent copies and close +them after successful spawn. Ticket rejection, cancellation, or spawn failure +closes all copies before generation-fenced `DeletePersistentTap`; normal +teardown waits for the child copy to close first. The opaque realization is +retained until deletion is confirmed. + The `SwtpmDir` broker op (current: `d2b-priv-broker/src/ops/swtpm_dir.rs`) is owned by `Provider/device-tpm` in v3, not by this Provider. The `store_view_farm` broker op (current: `d2b-priv-broker`) is owned by @@ -1604,7 +1643,7 @@ bundle and are never swept by configuration generation cleanup. | --- | --- | | Current anchor | `packages/d2b-host/src/runtime_provider.rs` (`CloudHypervisorRuntimeProvider`, `CloudHypervisorRuntimeControl`); `packages/d2b-host/src/ch_argv.rs` (`ChArgvInput`, `ChArgvGenerator`); `packages/d2bd/src/provider_shutdown.rs` (`CloudHypervisorShutdown`); `packages/d2b-core/src/processes.rs` (`ProcessRole::CloudHypervisor`, `ProcessRole::Swtpm`, `ProcessRole::NetVm`); `packages/d2b-host-providers/src/lib.rs` (`RuntimeProvider` adapter); `nixos-modules/components/tpm.nix`; `nixos-modules/network.nix`; `nixos-modules/processes-json.nix` (VMM/swtpm/net-VM process node emitters); `nixos-modules/store.nix` | | Evidence class | `production-reachable` for all items above; see migration map §2 | -| Behavior retained | Typed argv generation (pure data, no syscalls); pidfd identity/adoption; direct cgroup placement; fail-closed adoption ambiguity; redacted Debug for paths/argv; process-scoped TAP net-handoff; broker privilege mediation; minijail sandbox with user-NS for virtiofsd; swtpm pre-start flush; watchdog emission; OEM strings for observability | +| Behavior retained | Typed argv generation (pure data, no syscalls); pidfd identity/adoption; direct cgroup placement; fail-closed adoption ambiguity; redacted Debug for paths/argv; process-scoped TAP fd handoff with CLOEXEC ownership; broker privilege mediation; minijail sandbox with user-NS for virtiofsd; swtpm pre-start flush; watchdog emission; OEM strings for observability | | Required delta | Controller as async ResourceReconciler; Guest ResourceSpec validation; VMM Process as single owned child resource; direct dependency-readiness gate via ResourceClient (no EphemeralProcess); ComponentSession guest-control health in observe handler; typed provider descriptor; framework-provisioned ProviderStateSet; bus-only resource access; ResourceMutationBatch status writes; explicit Device/kvm in Guest deviceAttachments; required controllerExecutionRef in Provider config | | Reuse path | See §22.2 | | Replacement/deletion | Current `d2b--vm.service` systemd unit, `SpawnRunner{role: CloudHypervisor}` broker op, `RuntimeProvider` trait calls, and `CloudHypervisorRuntimeProvider` adapter remain until runtime-cloud-hypervisor integration passes full test parity | @@ -1614,7 +1653,7 @@ bundle and are never swept by configuration generation cleanup. | Current symbol / path | Evidence class | Current callers | Reuse action | v3 destination | | --- | --- | --- | --- | --- | -| `d2b-host/src/ch_argv.rs::ChArgvInput`, `generate_ch_argv` | production-reachable | `d2b-host/src/runtime_provider.rs` | EXTRACT and ADAPT | `packages/d2b-provider-runtime-cloud-hypervisor/src/vmm_argv.rs`; `ChArgvInput` fields are renamed to align with `spec.provider.settings` schema; store paths move to private artifact-catalog resolution; no `spec.*` exposure | +| `d2b-host/src/ch_argv.rs::ChArgvInput`, `generate_ch_argv` | production-reachable | `d2b-host/src/runtime_provider.rs` | EXTRACT and ADAPT | `packages/d2b-provider-runtime-cloud-hypervisor/src/vmm_argv.rs`; `ChArgvInput` fields are renamed to align with `spec.provider.settings` schema; store paths move to private artifact-catalog resolution; the v3 builder accepts only the declared LaunchTicket child fd slot for tap argv and has no handoff-mode or tap-name input; no `spec.*` exposure | | `d2b-host/src/runtime_provider.rs::CloudHypervisorRuntimeProvider` | production-reachable | `d2b-host-providers/src/lib.rs`; `d2bd/src/lib.rs` | REPLACE | `packages/d2b-provider-runtime-cloud-hypervisor/src/controller.rs`; new controller owns reconcile loop, not a `RuntimeProvider` trait implementation | | `d2b-host/src/runtime_provider.rs::CloudHypervisorRuntimeControl` trait | production-reachable | `d2bd`; supervisor test seams | REPLACE | Supervisor ticket passed through `Provider/system-minijail` LaunchTicket; no ambient trait | | `d2bd/src/provider_shutdown.rs::CloudHypervisorShutdown` | production-reachable | `d2bd` graceful shutdown | ADAPT | `packages/d2b-provider-runtime-cloud-hypervisor/src/shutdown.rs`; integrates with Process finalizer drain handler | @@ -1668,14 +1707,14 @@ per-test budget. | Field | Value | | --- | --- | -| Dependency/owner | ADR046-ch-001; Volume and Device foundation | +| Dependency/owner | ADR046-ch-001; Volume, Device, and ADR046-network-005 foundations | | Current source | `d2b-core/src/processes.rs`; `nixos-modules/processes-json.nix`; `d2b-priv-broker/src/ops/swtpm_dir.rs`; `d2b-host/src/swtpm_argv.rs` | | Reuse action | replace | | Destination | `packages/d2b-provider-runtime-cloud-hypervisor/src/bootstrap_graph.rs` | -| Detailed design | Single owned VMM Process resource; synchronous ResourceClient dependency check (Device/kvm + all declared Devices, Networks, virtiofs Volumes); immediate Process creation when all deps ready; no EphemeralProcess resources; conditional net-VM Guest creation; per-dependency readiness tracking in reconcile loop Primary reuse disposition: `replace`. Preserved source-plan detail: EXTRACT and REPLACE. | -| Integration | Depends on `Provider/volume-virtiofs`, `Provider/device-tpm`, `Provider/device-kvm`, `Provider/network-local` ResourceType readiness | +| Detailed design | Single owned VMM Process resource; synchronous ResourceClient dependency check (Device/kvm + all declared Devices, Networks, virtiofs Volumes); immediate Process creation when all deps ready; no EphemeralProcess resources; conditional net-VM Guest creation; per-dependency readiness tracking in reconcile loop. For each Network attachment the launch resolution carries only an opaque ref; network-local declares the semantic effect, the Core-owned `NetworkEffectPort` adapter performs `CreatePersistentTap → SetBridgePortFlags`, and `ProviderSupervisor` receives the connected CLOEXEC `OwnedFd` directly for LaunchTicket inheritance. The runtime controller receives no fd or broker operation. Primary reuse disposition: `replace`. Preserved source-plan detail: EXTRACT and REPLACE. | +| Integration | Depends on `Provider/volume-virtiofs`, `Provider/device-tpm`, `Provider/device-kvm`, `Provider/network-local` ResourceType readiness, the ADR046-network-005 effect chain, and direct ProviderSupervisor LaunchTicket fd handoff | | Data migration | v3 reset; no v2 process graph migration | -| Validation | Golden VMM Process spec vectors; dependency-ordering tests; parallel Guest tests (8 concurrent); net-VM creation tests; Device/kvm explicit-ref enforcement | +| Validation | Golden VMM Process spec vectors; dependency-ordering tests; parallel Guest tests (8 concurrent); net-VM creation tests; Device/kvm explicit-ref enforcement; tap launch routing proves `CreatePersistentTap → SetBridgePortFlags → ProviderSupervisor LaunchTicket`; fd-lifetime tests prove CLOEXEC, one inheritable child slot, and close-before-generation-fenced-delete on every failed launch; boundary tests prove the controller and bus payloads contain only opaque refs | | Removal proof | `ProcessRole::CloudHypervisor`, `ProcessRole::Swtpm`, `ProcessRole::NetVm` variant callers deleted; `nixos-modules/processes-json.nix` VMM emitter deleted after parity | ### ADR046-ch-003 (VMM argv builder v3) @@ -1686,11 +1725,11 @@ per-test budget. | Current source | `d2b-host/src/ch_argv.rs::ChArgvInput`, `generate_ch_argv`; `tests/golden/runner-shape/cloud-hypervisor-argv-*.txt` | | Reuse action | adapt | | Destination | `packages/d2b-provider-runtime-cloud-hypervisor/src/vmm_argv.rs`; `tests/vmm_argv_golden_test.rs` | -| Detailed design | `VmmArgvInput` derived from validated `GuestSpec.spec.provider.settings`; kernel/initrd/rootfs paths resolved privately from artifact catalog at dispatch time; no path in spec/status; golden tests for headless/q35/microvm/gpu/video/macvtap variants Primary reuse disposition: `adapt`. Preserved source-plan detail: COPY/ADAPT. | -| Integration | ProviderSupervisor LaunchTicket resolution | +| Detailed design | `VmmArgvInput` derived from validated `GuestSpec.spec.provider.settings`; kernel/initrd/rootfs paths resolved privately from artifact catalog at dispatch time; no path in spec/status; tap argv accepts only the declared child fd slot inherited from the sealed LaunchTicket, with no runtime-selected handoff mode or host tap name; golden tests for headless/q35/microvm/gpu/video/macvtap variants Primary reuse disposition: `adapt`. Preserved source-plan detail: COPY/ADAPT. | +| Integration | ProviderSupervisor LaunchTicket resolution, including direct inheritance of the precreated connected tap fd | | Data migration | None — full d2b 3.0 reset; no prior state to migrate | -| Validation | Golden argv vectors matching `cloud-hypervisor-argv-*.txt` shapes with v3 adaptations; redaction test (no store path in Debug output) | -| Removal proof | `d2b-host/src/ch_argv.rs::generate_ch_argv` callers removed; old golden test files adapted | +| Validation | Golden argv vectors matching `cloud-hypervisor-argv-*.txt` shapes with v3 adaptations; tap vector accepts only the declared LaunchTicket child fd slot; redaction test (no store path in Debug output) | +| Removal proof | `d2b-host/src/ch_argv.rs::generate_ch_argv` callers removed; the old network-handoff mode and tap-name branches have no v3 callers; old golden test files adapted | ### ADR046-ch-004 (Nix resource compiler) @@ -1780,12 +1819,15 @@ packages/d2b-provider-runtime-cloud-hypervisor/ redaction_test.rs # no store path in Debug, status, or audit output state_status_test.rs # status projection round-trip; bound enforcement; # restart re-derivation without a state Volume + tap_fd_no_controller_test.rs # controller/bus accept opaque refs only; reject fd/broker DTOs integration/ README.md # (optional) how to run integration fixtures; prerequisites vmm_boot_test.rs # single Guest boot + guest-control health on real KVM vmm_adoption_test.rs # controller restart + pidfd adoption with running VMM vmm_restart_test.rs # unexpected VMM exit + backoff + restart + re-health - network_attachment_test.rs # TAP allocation, macvtap external attachment + network_attachment_test.rs # CreatePersistentTap -> flags -> direct LaunchTicket; macvtap + tap_fd_lifetime_test.rs # CLOEXEC, one child slot, close-before-generation-fenced-delete + tap_failed_launch_cleanup_test.rs # rejection/cancel/spawn failure; retain opaque realization device_kvm_test.rs # explicit Device/kvm dependency; TCG fallback without it device_tpm_test.rs # swtpm Device dependency + VMM boot with TPM socket device_gpu_test.rs # GPU Device dependency + vhost-user-gpu handoff @@ -1838,12 +1880,16 @@ A work item is **not complete** until: 6. The `d2b-host-providers` adapter crate is empty of `runtime_provider` content and scheduled for deletion once all Provider dossiers migrate (per `ADR046-provider-002`). +7. The v3 runtime controller and its bus DTOs contain no tap fd, network broker + operation, handoff-mode, or host tap-name input; only opaque attachment refs + cross that boundary. The following specific removals are gated on ADR046-ch integration parity: | Current artifact | Removal gate | | --- | --- | | `d2b-host/src/runtime_provider.rs::CloudHypervisorRuntimeProvider` | ADR046-ch-002 parity | +| `d2b-host/src/ch_argv.rs::ChNetHandoff` and tap-name argv branch | ADR046-ch-003 parity; v3 tap argv consumes only the declared LaunchTicket child fd slot | | `d2b-core/src/processes.rs::ProcessRole::{CloudHypervisor, NetVm}` | ADR046-ch-002 parity | | `nixos-modules/processes-json.nix` (VMM/NetVm process node emitters) | ADR046-ch-004 parity | | `nixos-modules/options-vms.nix` | ADR046-ch-004 parity + all VM consumers migrated | From bc28e8f2a14dc44d76044e95a5fc2feae3d60613 Mon Sep 17 00:00:00 2001 From: John Vicondoa Date: Fri, 24 Jul 2026 15:11:01 -0700 Subject: [PATCH 113/115] docs: integrate sixth-round panel fixes --- docs/specs/ADR-046-implementation-graph.json | 22 +++++++-------- docs/specs/ADR-046-nix-configuration.md | 9 +++---- docs/specs/ADR-046-spec-set.json | 8 +++--- docs/specs/ADR-046-work-items.json | 28 ++++++++++---------- 4 files changed, 33 insertions(+), 34 deletions(-) diff --git a/docs/specs/ADR-046-implementation-graph.json b/docs/specs/ADR-046-implementation-graph.json index 8b414dbb9..ed51e8438 100644 --- a/docs/specs/ADR-046-implementation-graph.json +++ b/docs/specs/ADR-046-implementation-graph.json @@ -12765,14 +12765,14 @@ "destinations" : [ "`packages/d2b-provider-runtime-cloud-hypervisor/src/bootstrap_graph.rs`" ], - "detailedDesign" : "Single owned VMM Process resource; synchronous ResourceClient dependency check (Device/kvm + all declared Devices, Networks, virtiofs Volumes); immediate Process creation when all deps ready; no EphemeralProcess resources; conditional net-VM Guest creation; per-dependency readiness tracking in reconcile loop Primary reuse disposition: `replace`. Preserved source-plan detail: EXTRACT and REPLACE.", + "detailedDesign" : "Single owned VMM Process resource; synchronous ResourceClient dependency check (Device/kvm + all declared Devices, Networks, virtiofs Volumes); immediate Process creation when all deps ready; no EphemeralProcess resources; conditional net-VM Guest creation; per-dependency readiness tracking in reconcile loop. For each Network attachment the launch resolution carries only an opaque ref; network-local declares the semantic effect, the Core-owned `NetworkEffectPort` adapter performs `CreatePersistentTap → SetBridgePortFlags`, and `ProviderSupervisor` receives the connected CLOEXEC `OwnedFd` directly for LaunchTicket inheritance. The runtime controller receives no fd or broker operation. Primary reuse disposition: `replace`. Preserved source-plan detail: EXTRACT and REPLACE.", "entryContracts" : [ "ADR-046-provider-runtime-cloud-hypervisor" ], "exitGate" : "ADR046-W6 exit criteria (ADR-046-validation-and-delivery §4): every spec/work item in this wave Merged with clean destinations, all validators green, and the ten-role panel seal recorded", "id" : "ADR046-ch-002", "kind" : "work-item", - "owner" : "ADR046-ch-001; Volume and Device foundation", + "owner" : "ADR046-ch-001; Volume, Device, and ADR046-network-005 foundations", "parallelGroup" : "wi:ADR-046-provider-runtime-cloud-hypervisor", "prerequisites" : [ "ADR-046-provider-runtime-cloud-hypervisor", @@ -12780,7 +12780,7 @@ ], "specId" : "ADR-046-provider-runtime-cloud-hypervisor", "topologicalRank" : 12, - "validation" : "Golden VMM Process spec vectors; dependency-ordering tests; parallel Guest tests (8 concurrent); net-VM creation tests; Device/kvm explicit-ref enforcement", + "validation" : "Golden VMM Process spec vectors; dependency-ordering tests; parallel Guest tests (8 concurrent); net-VM creation tests; Device/kvm explicit-ref enforcement; tap launch routing proves `CreatePersistentTap → SetBridgePortFlags → ProviderSupervisor LaunchTicket`; fd-lifetime tests prove CLOEXEC, one inheritable child slot, and close-before-generation-fenced-delete on every failed launch; boundary tests prove the controller and bus payloads contain only opaque refs", "wave" : "W6" }, { @@ -12788,7 +12788,7 @@ "destinations" : [ "`packages/d2b-provider-runtime-cloud-hypervisor/src/vmm_argv.rs`; `tests/vmm_argv_golden_test.rs`" ], - "detailedDesign" : "`VmmArgvInput` derived from validated `GuestSpec.spec.provider.settings`; kernel/initrd/rootfs paths resolved privately from artifact catalog at dispatch time; no path in spec/status; golden tests for headless/q35/microvm/gpu/video/macvtap variants Primary reuse disposition: `adapt`. Preserved source-plan detail: COPY/ADAPT.", + "detailedDesign" : "`VmmArgvInput` derived from validated `GuestSpec.spec.provider.settings`; kernel/initrd/rootfs paths resolved privately from artifact catalog at dispatch time; no path in spec/status; tap argv accepts only the declared child fd slot inherited from the sealed LaunchTicket, with no runtime-selected handoff mode or host tap name; golden tests for headless/q35/microvm/gpu/video/macvtap variants Primary reuse disposition: `adapt`. Preserved source-plan detail: COPY/ADAPT.", "entryContracts" : [ "ADR-046-provider-runtime-cloud-hypervisor" ], @@ -12803,7 +12803,7 @@ ], "specId" : "ADR-046-provider-runtime-cloud-hypervisor", "topologicalRank" : 13, - "validation" : "Golden argv vectors matching `cloud-hypervisor-argv-*.txt` shapes with v3 adaptations; redaction test (no store path in Debug output)", + "validation" : "Golden argv vectors matching `cloud-hypervisor-argv-*.txt` shapes with v3 adaptations; tap vector accepts only the declared LaunchTicket child fd slot; redaction test (no store path in Debug output)", "wave" : "W6" }, { @@ -13617,7 +13617,7 @@ ], "specId" : "ADR-046-provider-credential-managed-identity", "topologicalRank" : 12, - "validation" : "See §Test matrix", + "validation" : "See §Tests. Run `cargo test -p d2b-provider-credential-managed-identity --lib --test lifecycle --test conformance --test faults --test canary --test delivery --test placement --test topology`; run `integration/{container-service.sh,aca-credential-ref.sh,cleanup-rollback.sh}` through `make test-integration` and `integration/host-guest-placement.nix` through `make test-host-integration`.", "wave" : "W6" }, { @@ -16528,7 +16528,7 @@ "destinations" : [ "Provider/display-wayland and Provider/shell-terminal Process configs in `zones//processes.json`; `Provider/credential-entra` Credential resource; `realm-identity.json` RETAINED during migration" ], - "detailedDesign" : "Launcher metadata folded into Process resource annotations; identity config → Credential resource fields (`providerRef`, `scope`, `audience`, `allowedOperations`, canonical `spec.provider` extension where Provider schema declares it; no secret bytes); `realm-identity.json` must remain until d2bd `RealmIdentityConfigJson` loading is replaced by Credential resource reader", + "detailedDesign" : "Launcher metadata folded into Process resource annotations; identity config → Credential resource fields (`providerRef`, `identityGuestRef`, `loginEndpointRef`, `scope`, `audience`, `consumerRef`, `allowedOperations`, canonical `spec.provider` extension where Provider schema declares it; no secret bytes); `realm-identity.json` must remain until d2bd `RealmIdentityConfigJson` loading is replaced by Credential resource reader", "entryContracts" : [ "ADR-046-nix-configuration" ], @@ -16543,7 +16543,7 @@ ], "specId" : "ADR-046-nix-configuration", "topologicalRank" : 15, - "validation" : "Launcher metadata shape regression; no-secret assertion vectors", + "validation" : "Launcher metadata shape regression; Entra identity-Guest fixture requires login Endpoint `visibility = \"provider\"` (and rejects `\"zone\"`), exact `consumerPolicy.allowedSubjects` containing both `Provider/credential-entra` and the Credential's configured `consumerRef`, and exact canonical `resolve` operation; no-secret assertion vectors", "wave" : "W5" }, { @@ -23188,9 +23188,9 @@ { "blockers" : [], "destinations" : [ - "`d2b-contracts/src/v3/volume_layout.rs` (LayoutEntry, EntryType, all policy enums, AclGrant, Invariant, SensitivityClass); `d2b-contracts/src/v3/volume_spec.rs` (VolumeSpec, ViewSpec, Attachment, QuotaSpec, SourceKind, `SourcePolicyId` opaque newtype); `d2b-contracts/src/v3/effect_port.rs` (`VolumeEffectPort` trait, opaque ID newtypes `VolumeId`/`LayoutEntryId`/`UserId`/`ViewId`/`SealingPolicyId` each with custom redacted Debug, `VolumeMountToken`, and canonical `RotateSealingKeyRequest`/`Result`/`Error` types)" + "`d2b-contracts/src/v3/volume_layout.rs` (LayoutEntry, EntryType, all policy enums, AclGrant, Invariant, SensitivityClass); `d2b-contracts/src/v3/volume_spec.rs` (VolumeSpec, ViewSpec, Attachment, QuotaSpec, SourceKind, `SourcePolicyId` opaque newtype); `d2b-contracts/src/v3/effect_port.rs` (`VolumeEffectPort` trait, opaque ID newtypes `VolumeId`/`LayoutEntryId`/`UserId`/`ViewId`/`SealingPolicyId` each with custom redacted Debug, `VolumeId` and `SealingPolicyId` with the exact `Clone`/serde wire derives and bounded `TryFrom` deserialization required by the canonical request, `VolumeMountToken`, and canonical `RotateSealingKeyRequest`/`Result`/`Error` types)" ], - "detailedDesign" : "All LayoutEntry fields as documented in this dossier; enum value names preserved from `StoragePathKind`/policy enums with renames where noted; `User/` ACL principal (no numeric UID); `sourcePolicyId` opaque newtype replaces raw `hostPath` in `SourceKind::LocalPath` and `SourceKind::BlockImage`; deny-unknown sealing-rotation request contains only opaque Volume/policy/operation IDs and generation/revision preconditions, with no key bytes/path/handle Primary reuse disposition: `adapt`. Preserved source-plan detail: extract and adapt.", + "detailedDesign" : "All LayoutEntry fields as documented in this dossier; enum value names preserved from `StoragePathKind`/policy enums with renames where noted; `User/` ACL principal (no numeric UID); `sourcePolicyId` opaque newtype replaces raw `hostPath` in `SourceKind::LocalPath` and `SourceKind::BlockImage`; deny-unknown sealing-rotation request contains only opaque Volume/policy/operation IDs and generation/revision preconditions, with no key bytes/path/handle; its crate-private `VolumeId` and `SealingPolicyId` strings serialize canonically and deserialize only when non-empty, ASCII-graphic, and at most 128 bytes, while retaining manual redacted Debug Primary reuse disposition: `adapt`. Preserved source-plan detail: extract and adapt.", "entryContracts" : [ "ADR-046-provider-volume-local" ], @@ -23205,7 +23205,7 @@ ], "specId" : "ADR-046-provider-volume-local", "topologicalRank" : 11, - "validation" : "Schema golden vectors; round-trip serde; ACL principal validation rejects numeric forms; `sourcePolicyId` present; no `hostPath` field in any volume_spec contract; sealing-rotation request deny-unknown/round-trip and redacted-Debug tests; compile-time trait conformance includes `rotate_sealing_key`", + "validation" : "Schema golden vectors; round-trip serde; ACL principal validation rejects numeric forms; `sourcePolicyId` present; no `hostPath` field in any volume_spec contract; compile-time assertions for the exact `Clone + Serialize + DeserializeOwned` ID bounds and `Serialize + DeserializeOwned` request bounds; sealing-rotation request canonical-string/round-trip and deny-unknown tests; exact redacted-Debug assertions; deserialization rejects empty, over-128-byte, non-ASCII, whitespace, and control-byte IDs without echoing input; compile-time trait conformance includes `rotate_sealing_key`", "wave" : "W6" }, { diff --git a/docs/specs/ADR-046-nix-configuration.md b/docs/specs/ADR-046-nix-configuration.md index c8226177f..75d6e5f30 100644 --- a/docs/specs/ADR-046-nix-configuration.md +++ b/docs/specs/ADR-046-nix-configuration.md @@ -1045,7 +1045,7 @@ d2b.zones.work.resources.entra-login = { "Provider/credential-entra" "Provider/runtime-azure-container-apps" ]; - allowedOperations = [ "resolve" "attach" "observe" ]; + allowedOperations = [ "resolve" ]; }; }; }; @@ -1072,9 +1072,8 @@ contract; the Endpoint is Guest-placed (Host placement is rejected), uses `consumerPolicy` whose `allowedSubjects` contains both `Provider/credential-entra` and the Credential's exact `consumerRef` (`Provider/runtime-azure-container-apps`) and whose `allowedOperations` is -exactly `resolve`, `attach`, and `observe`; the Credential scope and consumer -placement are consistent; and no store path or token appears in any emitted -spec. +exactly `resolve`; the Credential scope and consumer placement are consistent; +and no store path or token appears in any emitted spec. ### Package closures into Guests @@ -2510,7 +2509,7 @@ contract work item (ADR046-nix-034/ADR046-nix-035). Cross-reference: | Detailed design | Launcher metadata folded into Process resource annotations; identity config → Credential resource fields (`providerRef`, `identityGuestRef`, `loginEndpointRef`, `scope`, `audience`, `consumerRef`, `allowedOperations`, canonical `spec.provider` extension where Provider schema declares it; no secret bytes); `realm-identity.json` must remain until d2bd `RealmIdentityConfigJson` loading is replaced by Credential resource reader | | Integration | Provider/display-wayland, Provider/shell-terminal, and Provider/credential-entra consume Process/Credential resources; `d2bd` continues reading `realm-identity.json` until the Credential reader lands. | | Data migration | Launcher and identity config are re-emitted as v3 resources; full d2b 3.0 reset; no v2 launcher/identity state import. | -| Validation | Launcher metadata shape regression; Entra identity-Guest fixture requires login Endpoint `visibility = "provider"` (and rejects `"zone"`), exact `consumerPolicy.allowedSubjects` containing both `Provider/credential-entra` and the Credential's configured `consumerRef`, and exact canonical `resolve`, `attach`, and `observe` operations; no-secret assertion vectors | +| Validation | Launcher metadata shape regression; Entra identity-Guest fixture requires login Endpoint `visibility = "provider"` (and rejects `"zone"`), exact `consumerPolicy.allowedSubjects` containing both `Provider/credential-entra` and the Credential's configured `consumerRef`, and exact canonical `resolve` operation; no-secret assertion vectors | | Tests | `tests/unit/nix/cases/zones-launcher-metadata.nix`; `tests/unit/nix/cases/zones-credential-entra.nix` covers both required subjects, provider visibility, zone-visibility rejection, and exact Endpoint operations; no-secret vectors | | Drift pin | `make nix-unit-pin` | | Removal proof | `realm-workloads-launcher-v2-json.nix`/`realm-identity-config-json.nix` and `/etc/d2b/realm-workloads-launcher-v2.json`/`realm-identity.json` removed ONLY after display/credential Providers read resource configs | diff --git a/docs/specs/ADR-046-spec-set.json b/docs/specs/ADR-046-spec-set.json index 517b3a9e4..916ccec01 100644 --- a/docs/specs/ADR-046-spec-set.json +++ b/docs/specs/ADR-046-spec-set.json @@ -130,7 +130,7 @@ "ADR-046-terminology-and-identities" ], "path" : "docs/specs/ADR-046-nix-configuration.md", - "sha256" : "fdbe65b4a6b93e8465e971f9651c3ef46cd27b8f3e724d421da5fd784b2ebee1", + "sha256" : "212dc5b240f0f75b32694f4469d181436613f69f5d0f780fe18c7f39c1d92447", "specId" : "ADR-046-nix-configuration", "status" : "Proposed", "supersedes" : "Current `nixos-modules/options-realms*.nix`, `options-envs.nix`, `options-vms.nix`, `index.nix`, `bundle*.nix`, `*-json.nix`, and generated `/etc/d2b/*.json`", @@ -254,7 +254,7 @@ "ADR-046-telemetry-audit-and-support" ], "path" : "docs/specs/providers/ADR-046-provider-credential-managed-identity.md", - "sha256" : "4bb2c3878f50c24e3726d3ca95ab982284e43d655151221af68df35497694c0b", + "sha256" : "a0d61d8fab742709682250d7fb7beb925f79e1fc2344fa6794cc66d3abe77d78", "specId" : "ADR-046-provider-credential-managed-identity", "status" : "Proposed", "supersedes" : "Current v3 `ManagedIdentityRef`, `managed_identity_client_id` ACA config field, `CredentialProvider` trait (status/enrollment-only) in `d2b-realm-provider/src/credential.rs` and `provider.rs`", @@ -548,7 +548,7 @@ "ADR-046-telemetry-audit-and-support" ], "path" : "docs/specs/providers/ADR-046-provider-runtime-cloud-hypervisor.md", - "sha256" : "0fa75533c683e40b72c32666434b7185202c24534731fb088a136fe996a31c98", + "sha256" : "8747902a2af0db8c16ed97fe55d4b87283db0d1c7219ca3f5a9b6cb0a4458c58", "specId" : "ADR-046-provider-runtime-cloud-hypervisor", "status" : "Proposed", "supersedes" : "`packages/d2b-host/src/runtime_provider.rs` `CloudHypervisorRuntimeProvider`; `packages/d2bd/src/` VM lifecycle paths; `d2b-host-providers` adapter; `ProcessRole::CloudHypervisor`, `ProcessRole::Swtpm`, `ProcessRole::NetVm`; systemd unit `d2b--vm.service`; `SwtpmDir` broker op", @@ -772,7 +772,7 @@ "ADR-046-telemetry-audit-and-support" ], "path" : "docs/specs/providers/ADR-046-provider-volume-local.md", - "sha256" : "d3f4393a4b432d1f990782ba17a353a57be3c5b24368c35daf6c1a590fcb1c06", + "sha256" : "e8bd00b95e141dcf00c1808b04acd42d6f2de80d14cd176593d8eec7bdc8097f", "specId" : "ADR-046-provider-volume-local", "status" : "Proposed", "supersedes" : "`d2b-priv-broker/src/ops/{state_dir,store_sync,store_sync_audit,store_sync_export,store_view_farm,store_view_posture,swtpm_dir}.rs`; `d2b-core/src/{storage,sync,storage_lifecycle}.rs` StorageJson/SyncJson contract; `nixos-modules/store.nix` per-VM hardlink farm activation", diff --git a/docs/specs/ADR-046-work-items.json b/docs/specs/ADR-046-work-items.json index 3bd8946a3..a6c638a64 100644 --- a/docs/specs/ADR-046-work-items.json +++ b/docs/specs/ADR-046-work-items.json @@ -665,16 +665,16 @@ { "currentSource" : "`d2b-core/src/processes.rs`; `nixos-modules/processes-json.nix`; `d2b-priv-broker/src/ops/swtpm_dir.rs`; `d2b-host/src/swtpm_argv.rs`", "dataMigration" : "v3 reset; no v2 process graph migration", - "dependencyOwner" : "ADR046-ch-001; Volume and Device foundation", + "dependencyOwner" : "ADR046-ch-001; Volume, Device, and ADR046-network-005 foundations", "destination" : "`packages/d2b-provider-runtime-cloud-hypervisor/src/bootstrap_graph.rs`", - "detailedDesign" : "Single owned VMM Process resource; synchronous ResourceClient dependency check (Device/kvm + all declared Devices, Networks, virtiofs Volumes); immediate Process creation when all deps ready; no EphemeralProcess resources; conditional net-VM Guest creation; per-dependency readiness tracking in reconcile loop Primary reuse disposition: `replace`. Preserved source-plan detail: EXTRACT and REPLACE.", - "integration" : "Depends on `Provider/volume-virtiofs`, `Provider/device-tpm`, `Provider/device-kvm`, `Provider/network-local` ResourceType readiness", + "detailedDesign" : "Single owned VMM Process resource; synchronous ResourceClient dependency check (Device/kvm + all declared Devices, Networks, virtiofs Volumes); immediate Process creation when all deps ready; no EphemeralProcess resources; conditional net-VM Guest creation; per-dependency readiness tracking in reconcile loop. For each Network attachment the launch resolution carries only an opaque ref; network-local declares the semantic effect, the Core-owned `NetworkEffectPort` adapter performs `CreatePersistentTap → SetBridgePortFlags`, and `ProviderSupervisor` receives the connected CLOEXEC `OwnedFd` directly for LaunchTicket inheritance. The runtime controller receives no fd or broker operation. Primary reuse disposition: `replace`. Preserved source-plan detail: EXTRACT and REPLACE.", + "integration" : "Depends on `Provider/volume-virtiofs`, `Provider/device-tpm`, `Provider/device-kvm`, `Provider/network-local` ResourceType readiness, the ADR046-network-005 effect chain, and direct ProviderSupervisor LaunchTicket fd handoff", "removalProof" : "`ProcessRole::CloudHypervisor`, `ProcessRole::Swtpm`, `ProcessRole::NetVm` variant callers deleted; `nixos-modules/processes-json.nix` VMM emitter deleted after parity", "reuseAction" : "replace", "reuseSource" : null, "specId" : "ADR-046-provider-runtime-cloud-hypervisor", "specPath" : "docs/specs/providers/ADR-046-provider-runtime-cloud-hypervisor.md", - "validation" : "Golden VMM Process spec vectors; dependency-ordering tests; parallel Guest tests (8 concurrent); net-VM creation tests; Device/kvm explicit-ref enforcement", + "validation" : "Golden VMM Process spec vectors; dependency-ordering tests; parallel Guest tests (8 concurrent); net-VM creation tests; Device/kvm explicit-ref enforcement; tap launch routing proves `CreatePersistentTap → SetBridgePortFlags → ProviderSupervisor LaunchTicket`; fd-lifetime tests prove CLOEXEC, one inheritable child slot, and close-before-generation-fenced-delete on every failed launch; boundary tests prove the controller and bus payloads contain only opaque refs", "workItemId" : "ADR046-ch-002" }, { @@ -682,14 +682,14 @@ "dataMigration" : "None — full d2b 3.0 reset; no prior state to migrate", "dependencyOwner" : "ADR046-ch-002; artifact catalog foundation", "destination" : "`packages/d2b-provider-runtime-cloud-hypervisor/src/vmm_argv.rs`; `tests/vmm_argv_golden_test.rs`", - "detailedDesign" : "`VmmArgvInput` derived from validated `GuestSpec.spec.provider.settings`; kernel/initrd/rootfs paths resolved privately from artifact catalog at dispatch time; no path in spec/status; golden tests for headless/q35/microvm/gpu/video/macvtap variants Primary reuse disposition: `adapt`. Preserved source-plan detail: COPY/ADAPT.", - "integration" : "ProviderSupervisor LaunchTicket resolution", - "removalProof" : "`d2b-host/src/ch_argv.rs::generate_ch_argv` callers removed; old golden test files adapted", + "detailedDesign" : "`VmmArgvInput` derived from validated `GuestSpec.spec.provider.settings`; kernel/initrd/rootfs paths resolved privately from artifact catalog at dispatch time; no path in spec/status; tap argv accepts only the declared child fd slot inherited from the sealed LaunchTicket, with no runtime-selected handoff mode or host tap name; golden tests for headless/q35/microvm/gpu/video/macvtap variants Primary reuse disposition: `adapt`. Preserved source-plan detail: COPY/ADAPT.", + "integration" : "ProviderSupervisor LaunchTicket resolution, including direct inheritance of the precreated connected tap fd", + "removalProof" : "`d2b-host/src/ch_argv.rs::generate_ch_argv` callers removed; the old network-handoff mode and tap-name branches have no v3 callers; old golden test files adapted", "reuseAction" : "adapt", "reuseSource" : null, "specId" : "ADR-046-provider-runtime-cloud-hypervisor", "specPath" : "docs/specs/providers/ADR-046-provider-runtime-cloud-hypervisor.md", - "validation" : "Golden argv vectors matching `cloud-hypervisor-argv-*.txt` shapes with v3 adaptations; redaction test (no store path in Debug output)", + "validation" : "Golden argv vectors matching `cloud-hypervisor-argv-*.txt` shapes with v3 adaptations; tap vector accepts only the declared LaunchTicket child fd slot; redaction test (no store path in Debug output)", "workItemId" : "ADR046-ch-003" }, { @@ -1199,7 +1199,7 @@ "reuseSource" : "main `a1cc0b2d`: `packages/d2b-provider-credential-managed-identity/src/lib.rs` (full implementation); `src/tests.rs` (full test suite)", "specId" : "ADR-046-provider-credential-managed-identity", "specPath" : "docs/specs/providers/ADR-046-provider-credential-managed-identity.md", - "validation" : "See §Test matrix", + "validation" : "See §Tests. Run `cargo test -p d2b-provider-credential-managed-identity --lib --test lifecycle --test conformance --test faults --test canary --test delivery --test placement --test topology`; run `integration/{container-service.sh,aca-credential-ref.sh,cleanup-rollback.sh}` through `make test-integration` and `integration/host-guest-placement.nix` through `make test-host-integration`.", "workItemId" : "ADR046-cred-mi-001" }, { @@ -3067,14 +3067,14 @@ "dataMigration" : "Launcher and identity config are re-emitted as v3 resources; full d2b 3.0 reset; no v2 launcher/identity state import.", "dependencyOwner" : "ADR046-nix-006; display/credential Provider work items", "destination" : "Provider/display-wayland and Provider/shell-terminal Process configs in `zones//processes.json`; `Provider/credential-entra` Credential resource; `realm-identity.json` RETAINED during migration", - "detailedDesign" : "Launcher metadata folded into Process resource annotations; identity config → Credential resource fields (`providerRef`, `scope`, `audience`, `allowedOperations`, canonical `spec.provider` extension where Provider schema declares it; no secret bytes); `realm-identity.json` must remain until d2bd `RealmIdentityConfigJson` loading is replaced by Credential resource reader", + "detailedDesign" : "Launcher metadata folded into Process resource annotations; identity config → Credential resource fields (`providerRef`, `identityGuestRef`, `loginEndpointRef`, `scope`, `audience`, `consumerRef`, `allowedOperations`, canonical `spec.provider` extension where Provider schema declares it; no secret bytes); `realm-identity.json` must remain until d2bd `RealmIdentityConfigJson` loading is replaced by Credential resource reader", "integration" : "Provider/display-wayland, Provider/shell-terminal, and Provider/credential-entra consume Process/Credential resources; `d2bd` continues reading `realm-identity.json` until the Credential reader lands.", "removalProof" : "`realm-workloads-launcher-v2-json.nix`/`realm-identity-config-json.nix` and `/etc/d2b/realm-workloads-launcher-v2.json`/`realm-identity.json` removed ONLY after display/credential Providers read resource configs", "reuseAction" : "adapt", "reuseSource" : null, "specId" : "ADR-046-nix-configuration", "specPath" : "docs/specs/ADR-046-nix-configuration.md", - "validation" : "Launcher metadata shape regression; no-secret assertion vectors", + "validation" : "Launcher metadata shape regression; Entra identity-Guest fixture requires login Endpoint `visibility = \"provider\"` (and rejects `\"zone\"`), exact `consumerPolicy.allowedSubjects` containing both `Provider/credential-entra` and the Credential's configured `consumerRef`, and exact canonical `resolve` operation; no-secret assertion vectors", "workItemId" : "ADR046-nix-009" }, { @@ -7281,15 +7281,15 @@ "currentSource" : "`d2b-core/src/storage.rs` (`StoragePathSpec`, `StoragePathKind`, policy enums); `d2b-core/src/sync.rs` (`SyncJson`, `LockSpec`)", "dataMigration" : "Full v3 reset; no row-level import", "dependencyOwner" : "ADR046-primitives-001; v3 contracts owner", - "destination" : "`d2b-contracts/src/v3/volume_layout.rs` (LayoutEntry, EntryType, all policy enums, AclGrant, Invariant, SensitivityClass); `d2b-contracts/src/v3/volume_spec.rs` (VolumeSpec, ViewSpec, Attachment, QuotaSpec, SourceKind, `SourcePolicyId` opaque newtype); `d2b-contracts/src/v3/effect_port.rs` (`VolumeEffectPort` trait, opaque ID newtypes `VolumeId`/`LayoutEntryId`/`UserId`/`ViewId`/`SealingPolicyId` each with custom redacted Debug, `VolumeMountToken`, and canonical `RotateSealingKeyRequest`/`Result`/`Error` types)", - "detailedDesign" : "All LayoutEntry fields as documented in this dossier; enum value names preserved from `StoragePathKind`/policy enums with renames where noted; `User/` ACL principal (no numeric UID); `sourcePolicyId` opaque newtype replaces raw `hostPath` in `SourceKind::LocalPath` and `SourceKind::BlockImage`; deny-unknown sealing-rotation request contains only opaque Volume/policy/operation IDs and generation/revision preconditions, with no key bytes/path/handle Primary reuse disposition: `adapt`. Preserved source-plan detail: extract and adapt.", + "destination" : "`d2b-contracts/src/v3/volume_layout.rs` (LayoutEntry, EntryType, all policy enums, AclGrant, Invariant, SensitivityClass); `d2b-contracts/src/v3/volume_spec.rs` (VolumeSpec, ViewSpec, Attachment, QuotaSpec, SourceKind, `SourcePolicyId` opaque newtype); `d2b-contracts/src/v3/effect_port.rs` (`VolumeEffectPort` trait, opaque ID newtypes `VolumeId`/`LayoutEntryId`/`UserId`/`ViewId`/`SealingPolicyId` each with custom redacted Debug, `VolumeId` and `SealingPolicyId` with the exact `Clone`/serde wire derives and bounded `TryFrom` deserialization required by the canonical request, `VolumeMountToken`, and canonical `RotateSealingKeyRequest`/`Result`/`Error` types)", + "detailedDesign" : "All LayoutEntry fields as documented in this dossier; enum value names preserved from `StoragePathKind`/policy enums with renames where noted; `User/` ACL principal (no numeric UID); `sourcePolicyId` opaque newtype replaces raw `hostPath` in `SourceKind::LocalPath` and `SourceKind::BlockImage`; deny-unknown sealing-rotation request contains only opaque Volume/policy/operation IDs and generation/revision preconditions, with no key bytes/path/handle; its crate-private `VolumeId` and `SealingPolicyId` strings serialize canonically and deserialize only when non-empty, ASCII-graphic, and at most 128 bytes, while retaining manual redacted Debug Primary reuse disposition: `adapt`. Preserved source-plan detail: extract and adapt.", "integration" : "Volume spec and status structs; Provider descriptor component stateNamespace; Nix resource compiler schema validation", "removalProof" : "`d2b-core/src/storage.rs` StoragePathSpec/policy enums removed only after all Provider descriptor consumers are on v3 Volume spec", "reuseAction" : "adapt", "reuseSource" : null, "specId" : "ADR-046-provider-volume-local", "specPath" : "docs/specs/providers/ADR-046-provider-volume-local.md", - "validation" : "Schema golden vectors; round-trip serde; ACL principal validation rejects numeric forms; `sourcePolicyId` present; no `hostPath` field in any volume_spec contract; sealing-rotation request deny-unknown/round-trip and redacted-Debug tests; compile-time trait conformance includes `rotate_sealing_key`", + "validation" : "Schema golden vectors; round-trip serde; ACL principal validation rejects numeric forms; `sourcePolicyId` present; no `hostPath` field in any volume_spec contract; compile-time assertions for the exact `Clone + Serialize + DeserializeOwned` ID bounds and `Serialize + DeserializeOwned` request bounds; sealing-rotation request canonical-string/round-trip and deny-unknown tests; exact redacted-Debug assertions; deserialization rejects empty, over-128-byte, non-ASCII, whitespace, and control-byte IDs without echoing input; compile-time trait conformance includes `rotate_sealing_key`", "workItemId" : "ADR046-vl-001" }, { From 1c6167035c51d533b874a4e4e43707aacf61f2af Mon Sep 17 00:00:00 2001 From: John Vicondoa Date: Fri, 24 Jul 2026 15:42:26 -0700 Subject: [PATCH 114/115] docs: close final ADR 0046 panel findings --- docs/specs/ADR-046-implementation-graph.json | 14 ++++++++++---- docs/specs/ADR-046-implementation-graph.md | 2 +- docs/specs/ADR-046-nix-configuration.md | 9 +++++---- docs/specs/ADR-046-spec-set.json | 4 ++-- docs/specs/ADR-046-work-items.json | 4 ++-- .../ADR-046-provider-runtime-cloud-hypervisor.md | 2 +- 6 files changed, 21 insertions(+), 14 deletions(-) diff --git a/docs/specs/ADR-046-implementation-graph.json b/docs/specs/ADR-046-implementation-graph.json index ed51e8438..4e7028585 100644 --- a/docs/specs/ADR-046-implementation-graph.json +++ b/docs/specs/ADR-046-implementation-graph.json @@ -2,7 +2,7 @@ "adr" : "0046", "artifactKind" : "d2b-adr-implementation-graph", "counts" : { - "edges" : 1939, + "edges" : 1940, "maxTopologicalRank" : 22, "nodes" : 598, "specNodes" : 55, @@ -5667,6 +5667,11 @@ "to" : "ADR046-ch-001", "type" : "work-item-depends-on" }, + { + "from" : "ADR046-ch-002", + "to" : "ADR046-network-005", + "type" : "work-item-depends-on" + }, { "from" : "ADR046-ch-003", "to" : "ADR046-ch-002", @@ -12772,11 +12777,12 @@ "exitGate" : "ADR046-W6 exit criteria (ADR-046-validation-and-delivery §4): every spec/work item in this wave Merged with clean destinations, all validators green, and the ten-role panel seal recorded", "id" : "ADR046-ch-002", "kind" : "work-item", - "owner" : "ADR046-ch-001; Volume, Device, and ADR046-network-005 foundations", + "owner" : "Depends on `ADR046-ch-001` and `ADR046-network-005`; owner: cloud-hypervisor bootstrap-graph integration", "parallelGroup" : "wi:ADR-046-provider-runtime-cloud-hypervisor", "prerequisites" : [ "ADR-046-provider-runtime-cloud-hypervisor", - "ADR046-ch-001" + "ADR046-ch-001", + "ADR046-network-005" ], "specId" : "ADR-046-provider-runtime-cloud-hypervisor", "topologicalRank" : 12, @@ -16543,7 +16549,7 @@ ], "specId" : "ADR-046-nix-configuration", "topologicalRank" : 15, - "validation" : "Launcher metadata shape regression; Entra identity-Guest fixture requires login Endpoint `visibility = \"provider\"` (and rejects `\"zone\"`), exact `consumerPolicy.allowedSubjects` containing both `Provider/credential-entra` and the Credential's configured `consumerRef`, and exact canonical `resolve` operation; no-secret assertion vectors", + "validation" : "Launcher metadata shape regression; Entra identity-Guest fixture requires login Endpoint purpose `credential-entra.d2bus.org/entra-login-token`, `visibility = \"provider\"` (and rejects `\"zone\"`), exact `consumerPolicy.allowedSubjects` containing both `Provider/credential-entra` and the Credential's configured `consumerRef`, and exact canonical `resolve` operation; no-secret assertion vectors", "wave" : "W5" }, { diff --git a/docs/specs/ADR-046-implementation-graph.md b/docs/specs/ADR-046-implementation-graph.md index dbfe60388..ded42138b 100644 --- a/docs/specs/ADR-046-implementation-graph.md +++ b/docs/specs/ADR-046-implementation-graph.md @@ -25,7 +25,7 @@ Each JSON work-item node also embeds the manifest's exact `detailedDesign` and | Spec nodes | 55 | | Work-item nodes | 543 | | Total nodes | 598 | -| Edges | 1939 | +| Edges | 1940 | | Max topological rank | 22 | ## Waves (W0–W7) diff --git a/docs/specs/ADR-046-nix-configuration.md b/docs/specs/ADR-046-nix-configuration.md index 75d6e5f30..69e311a9a 100644 --- a/docs/specs/ADR-046-nix-configuration.md +++ b/docs/specs/ADR-046-nix-configuration.md @@ -1037,7 +1037,7 @@ d2b.zones.work.resources.entra-login = { producerRef = "Guest/entra-identity"; # produced inside the identity Guest endpointClass = "service"; transport = "vsock"; # closed class only; no CID/path - purpose = "entra-login-token"; + purpose = "credential-entra.d2bus.org/entra-login-token"; locality = "guest-local"; visibility = "provider"; consumerPolicy = { @@ -1066,8 +1066,9 @@ d2b.zones.work.resources.work-entra = { Build/eval validation asserts: `identityGuestRef`, `loginEndpointRef`, `consumerRef`, and `scope.executionRef` are all the same Zone; the `Endpoint` -`purpose`/schema and `providerRef` match the credential-entra login-service -contract; the Endpoint is Guest-placed (Host placement is rejected), uses +`purpose` is exactly `credential-entra.d2bus.org/entra-login-token`, its schema +and `providerRef` match the credential-entra login-service contract, and the +Endpoint is Guest-placed (Host placement is rejected), uses `visibility = "provider"` (`"zone"` is rejected), and has an exact `consumerPolicy` whose `allowedSubjects` contains both `Provider/credential-entra` and the Credential's exact `consumerRef` @@ -2509,7 +2510,7 @@ contract work item (ADR046-nix-034/ADR046-nix-035). Cross-reference: | Detailed design | Launcher metadata folded into Process resource annotations; identity config → Credential resource fields (`providerRef`, `identityGuestRef`, `loginEndpointRef`, `scope`, `audience`, `consumerRef`, `allowedOperations`, canonical `spec.provider` extension where Provider schema declares it; no secret bytes); `realm-identity.json` must remain until d2bd `RealmIdentityConfigJson` loading is replaced by Credential resource reader | | Integration | Provider/display-wayland, Provider/shell-terminal, and Provider/credential-entra consume Process/Credential resources; `d2bd` continues reading `realm-identity.json` until the Credential reader lands. | | Data migration | Launcher and identity config are re-emitted as v3 resources; full d2b 3.0 reset; no v2 launcher/identity state import. | -| Validation | Launcher metadata shape regression; Entra identity-Guest fixture requires login Endpoint `visibility = "provider"` (and rejects `"zone"`), exact `consumerPolicy.allowedSubjects` containing both `Provider/credential-entra` and the Credential's configured `consumerRef`, and exact canonical `resolve` operation; no-secret assertion vectors | +| Validation | Launcher metadata shape regression; Entra identity-Guest fixture requires login Endpoint purpose `credential-entra.d2bus.org/entra-login-token`, `visibility = "provider"` (and rejects `"zone"`), exact `consumerPolicy.allowedSubjects` containing both `Provider/credential-entra` and the Credential's configured `consumerRef`, and exact canonical `resolve` operation; no-secret assertion vectors | | Tests | `tests/unit/nix/cases/zones-launcher-metadata.nix`; `tests/unit/nix/cases/zones-credential-entra.nix` covers both required subjects, provider visibility, zone-visibility rejection, and exact Endpoint operations; no-secret vectors | | Drift pin | `make nix-unit-pin` | | Removal proof | `realm-workloads-launcher-v2-json.nix`/`realm-identity-config-json.nix` and `/etc/d2b/realm-workloads-launcher-v2.json`/`realm-identity.json` removed ONLY after display/credential Providers read resource configs | diff --git a/docs/specs/ADR-046-spec-set.json b/docs/specs/ADR-046-spec-set.json index 916ccec01..b99b934f0 100644 --- a/docs/specs/ADR-046-spec-set.json +++ b/docs/specs/ADR-046-spec-set.json @@ -130,7 +130,7 @@ "ADR-046-terminology-and-identities" ], "path" : "docs/specs/ADR-046-nix-configuration.md", - "sha256" : "212dc5b240f0f75b32694f4469d181436613f69f5d0f780fe18c7f39c1d92447", + "sha256" : "9ff388089c225d43f6bea705ae4fd095ba203f234226aeeb0a91e869c8400160", "specId" : "ADR-046-nix-configuration", "status" : "Proposed", "supersedes" : "Current `nixos-modules/options-realms*.nix`, `options-envs.nix`, `options-vms.nix`, `index.nix`, `bundle*.nix`, `*-json.nix`, and generated `/etc/d2b/*.json`", @@ -548,7 +548,7 @@ "ADR-046-telemetry-audit-and-support" ], "path" : "docs/specs/providers/ADR-046-provider-runtime-cloud-hypervisor.md", - "sha256" : "8747902a2af0db8c16ed97fe55d4b87283db0d1c7219ca3f5a9b6cb0a4458c58", + "sha256" : "62f6af217aa398e2f801e30157e3d7f124963893d3120c1ca622cd47776cf92a", "specId" : "ADR-046-provider-runtime-cloud-hypervisor", "status" : "Proposed", "supersedes" : "`packages/d2b-host/src/runtime_provider.rs` `CloudHypervisorRuntimeProvider`; `packages/d2bd/src/` VM lifecycle paths; `d2b-host-providers` adapter; `ProcessRole::CloudHypervisor`, `ProcessRole::Swtpm`, `ProcessRole::NetVm`; systemd unit `d2b--vm.service`; `SwtpmDir` broker op", diff --git a/docs/specs/ADR-046-work-items.json b/docs/specs/ADR-046-work-items.json index a6c638a64..bdd2dc3ac 100644 --- a/docs/specs/ADR-046-work-items.json +++ b/docs/specs/ADR-046-work-items.json @@ -665,7 +665,7 @@ { "currentSource" : "`d2b-core/src/processes.rs`; `nixos-modules/processes-json.nix`; `d2b-priv-broker/src/ops/swtpm_dir.rs`; `d2b-host/src/swtpm_argv.rs`", "dataMigration" : "v3 reset; no v2 process graph migration", - "dependencyOwner" : "ADR046-ch-001; Volume, Device, and ADR046-network-005 foundations", + "dependencyOwner" : "Depends on `ADR046-ch-001` and `ADR046-network-005`; owner: cloud-hypervisor bootstrap-graph integration", "destination" : "`packages/d2b-provider-runtime-cloud-hypervisor/src/bootstrap_graph.rs`", "detailedDesign" : "Single owned VMM Process resource; synchronous ResourceClient dependency check (Device/kvm + all declared Devices, Networks, virtiofs Volumes); immediate Process creation when all deps ready; no EphemeralProcess resources; conditional net-VM Guest creation; per-dependency readiness tracking in reconcile loop. For each Network attachment the launch resolution carries only an opaque ref; network-local declares the semantic effect, the Core-owned `NetworkEffectPort` adapter performs `CreatePersistentTap → SetBridgePortFlags`, and `ProviderSupervisor` receives the connected CLOEXEC `OwnedFd` directly for LaunchTicket inheritance. The runtime controller receives no fd or broker operation. Primary reuse disposition: `replace`. Preserved source-plan detail: EXTRACT and REPLACE.", "integration" : "Depends on `Provider/volume-virtiofs`, `Provider/device-tpm`, `Provider/device-kvm`, `Provider/network-local` ResourceType readiness, the ADR046-network-005 effect chain, and direct ProviderSupervisor LaunchTicket fd handoff", @@ -3074,7 +3074,7 @@ "reuseSource" : null, "specId" : "ADR-046-nix-configuration", "specPath" : "docs/specs/ADR-046-nix-configuration.md", - "validation" : "Launcher metadata shape regression; Entra identity-Guest fixture requires login Endpoint `visibility = \"provider\"` (and rejects `\"zone\"`), exact `consumerPolicy.allowedSubjects` containing both `Provider/credential-entra` and the Credential's configured `consumerRef`, and exact canonical `resolve` operation; no-secret assertion vectors", + "validation" : "Launcher metadata shape regression; Entra identity-Guest fixture requires login Endpoint purpose `credential-entra.d2bus.org/entra-login-token`, `visibility = \"provider\"` (and rejects `\"zone\"`), exact `consumerPolicy.allowedSubjects` containing both `Provider/credential-entra` and the Credential's configured `consumerRef`, and exact canonical `resolve` operation; no-secret assertion vectors", "workItemId" : "ADR046-nix-009" }, { diff --git a/docs/specs/providers/ADR-046-provider-runtime-cloud-hypervisor.md b/docs/specs/providers/ADR-046-provider-runtime-cloud-hypervisor.md index a5dda7abe..a803de275 100644 --- a/docs/specs/providers/ADR-046-provider-runtime-cloud-hypervisor.md +++ b/docs/specs/providers/ADR-046-provider-runtime-cloud-hypervisor.md @@ -1707,7 +1707,7 @@ per-test budget. | Field | Value | | --- | --- | -| Dependency/owner | ADR046-ch-001; Volume, Device, and ADR046-network-005 foundations | +| Dependency/owner | Depends on `ADR046-ch-001` and `ADR046-network-005`; owner: cloud-hypervisor bootstrap-graph integration | | Current source | `d2b-core/src/processes.rs`; `nixos-modules/processes-json.nix`; `d2b-priv-broker/src/ops/swtpm_dir.rs`; `d2b-host/src/swtpm_argv.rs` | | Reuse action | replace | | Destination | `packages/d2b-provider-runtime-cloud-hypervisor/src/bootstrap_graph.rs` | From 6078a87f731ca23a44367fa826ab35b4987d1ece Mon Sep 17 00:00:00 2001 From: John Vicondoa Date: Fri, 24 Jul 2026 16:21:47 -0700 Subject: [PATCH 115/115] ci: run pull request gates for v3 --- .github/workflows/eval-with-entra-id.yml | 4 ++-- .github/workflows/pr-eval-shell-tests.yml | 4 ++-- .github/workflows/pr-l1-static-fast.yml | 4 ++-- CHANGELOG.md | 7 ++++++- packages/xtask/tests/policy_ci.rs | 21 +++++++++++++++++++++ tests/ci/layer1-workflow.template.yml | 4 ++-- 6 files changed, 35 insertions(+), 9 deletions(-) diff --git a/.github/workflows/eval-with-entra-id.yml b/.github/workflows/eval-with-entra-id.yml index ba10d72fc..41ae23606 100644 --- a/.github/workflows/eval-with-entra-id.yml +++ b/.github/workflows/eval-with-entra-id.yml @@ -1,9 +1,9 @@ name: eval-with-entra-id on: push: - branches: [main] + branches: [main, v3] pull_request: - branches: [main] + branches: [main, v3] permissions: contents: read diff --git a/.github/workflows/pr-eval-shell-tests.yml b/.github/workflows/pr-eval-shell-tests.yml index c4e42933c..ed36e2a72 100644 --- a/.github/workflows/pr-eval-shell-tests.yml +++ b/.github/workflows/pr-eval-shell-tests.yml @@ -1,9 +1,9 @@ name: pr-eval-shell-tests on: push: - branches: [main] + branches: [main, v3] pull_request: - branches: [main] + branches: [main, v3] permissions: contents: read diff --git a/.github/workflows/pr-l1-static-fast.yml b/.github/workflows/pr-l1-static-fast.yml index be454ca57..1ff095460 100644 --- a/.github/workflows/pr-l1-static-fast.yml +++ b/.github/workflows/pr-l1-static-fast.yml @@ -4,9 +4,9 @@ name: pr-l1-static-fast on: pull_request: - branches: [main] + branches: [main, v3] push: - branches: [main] + branches: [main, v3] permissions: contents: read diff --git a/CHANGELOG.md b/CHANGELOG.md index 515fbcf92..587b066d8 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -18,7 +18,7 @@ deprecations ship one minor release before removal. and closing specs plus a 27-Provider dossier catalog) indexed by `docs/specs/README.md` and `docs/specs/providers/README.md` and bound by the generated `ADR-046-spec-set.json` and `ADR-046-work-items.json` manifests. - It specifies Zone-local resources over the 17 standard ResourceTypes + It specifies Zone-local resources over the 19 standard ResourceTypes (including `Endpoint`), independently packaged multi-process Providers selected by `{ artifactId, config }`, an asynchronous embedded redb resource plane with owner-driven reconciliation and commit-gated expedited reconcile, @@ -31,6 +31,11 @@ deprecations ship one minor release before removal. validation and delivery, and streamline contracts. Everything remains Proposed; no crates, services, controllers, or Providers are created. +### Changed + +- Enabled the required Layer-1, eval-shell, and Entra example PR gates for + changes targeting the `v3` branch as well as `main`. + ## [1.4.1] - 2026-07-12 ### Added diff --git a/packages/xtask/tests/policy_ci.rs b/packages/xtask/tests/policy_ci.rs index 06f287292..eb45dd804 100644 --- a/packages/xtask/tests/policy_ci.rs +++ b/packages/xtask/tests/policy_ci.rs @@ -9,6 +9,12 @@ const ALLOWLISTED_WORKFLOWS: &[&str] = &[ ".github/workflows/release-host-binaries.yml", ]; +const V3_PR_GATE_WORKFLOWS: &[&str] = &[ + ".github/workflows/eval-with-entra-id.yml", + ".github/workflows/pr-eval-shell-tests.yml", + ".github/workflows/pr-l1-static-fast.yml", +]; + const APPROVED_MAKE_TARGETS: &[&str] = &[ "check", "check-ci", @@ -121,3 +127,18 @@ fn ci_uses_make_allowlist_is_intentional_and_bounded() { "workflow make-target exceptions must stay reviewed and bounded" ); } + +#[test] +fn v3_pr_gates_are_enabled() { + for rel in V3_PR_GATE_WORKFLOWS { + let content = read_repo_file(rel); + assert!( + content.contains("pull_request:\n branches: [main, v3]"), + "{rel} must run for pull requests targeting main and v3" + ); + assert!( + content.contains("push:\n branches: [main, v3]"), + "{rel} must run for pushes to main and v3" + ); + } +} diff --git a/tests/ci/layer1-workflow.template.yml b/tests/ci/layer1-workflow.template.yml index 10b1a48e6..319e4cb1e 100644 --- a/tests/ci/layer1-workflow.template.yml +++ b/tests/ci/layer1-workflow.template.yml @@ -4,9 +4,9 @@ name: {{ workflow_name }} on: pull_request: - branches: [main] + branches: [main, v3] push: - branches: [main] + branches: [main, v3] permissions: contents: read